--- id: AUDIT-WP-0010 type: workplan title: "Admit tenant-engine as an attributive sender" domain: infotech repo: audit-core status: ready owner: claude topic_slug: railiance created: "2026-08-29" updated: "2026-08-29" depends_on: - AUDIT-WP-0005 state_hub_workstream_id: "54655357-74da-5f7f-8fa6-647d4c969f21" --- # AUDIT-WP-0010 — Admit tenant-engine as an attributive sender ## Goal Close `AUDIT-IN-0002`. `tenant-engine` emits mutation evidence through a local outbox and **cannot land events in production until audit-core admits the sender**. This is another repository's production blocked on ours, which is why it is a workplan of its own rather than a task inside `AUDIT-WP-0009`. The request is well-formed and arrives with its own bound already declared: class **attributive**, drain non-blocking, documented at `tenant-engine/docs/evidence-emission.md`. Under §9.6 that is a legitimate trade — attributive evidence may deliberately trade away atomicity provided the trade is declared and completeness is never claimed. `tenant-engine` declared it. Nothing here needs renegotiating; it needs admitting. Intake: `AUDIT-IN-0002` (`01a04d8f-b1c1-746a-8007-15221ebf0a48`), origin `TEN-WP-0011-T04`. Onboarding follows INTENT principle 4 — a source is onboarded when ownership, retention, access, export, and evidence policy are declared and validated, not when events start arriving. ## Non-goals - Reclassifying `tenant-engine` as load-bearing. It declared attributive and the declaration is the emitter's to make; §9.6 obligations follow the class. - Carrying any credential value in this repository, in a work record, in State Hub, or in chat. The intake says so and it is right. - Blocking on `AUDIT-WP-0009-T03`. The `evidence_kind` field lands there; admission does not wait for it, and T04 below records the class in the meantime. ## Tasks ```task id: AUDIT-WP-0010-T01 status: todo priority: high state_hub_task_id: "e26170e3-78f1-5d98-bb8f-46cef32244bb" ``` Register the sender identity: name `tenant-engine`, `sources: [tenant-engine]`, `may_write: true`, `may_read: false`, `secret_policy: redact`, tenants scoped as the envelope requires. Follow the existing `user-engine` shape in `deploy/externalsecret-senders.yaml` and `deploy/senders-scope.yaml`. No token value in Git — projected only, per the established lane. ```task id: AUDIT-WP-0010-T02 status: todo priority: high state_hub_task_id: "13855bd9-4ff4-5e8c-ab98-d5ff45103d22" ``` Provide the token lane. Coordinate the projected credential with the owning credential operator through the `ops-warden` route, matching the custody pattern already used for `user-engine`. Verify field presence without disclosure. Do not transport a value through State Hub messages. ```task id: AUDIT-WP-0010-T03 status: todo priority: high state_hub_task_id: "1a75fba2-a625-5e87-bba9-e38f010856fc" ``` Extend the NetworkPolicy. `deploy/networkpolicies.yaml` currently admits `user-engine`; add `tenant-engine`. `tests/test_networkpolicies.py` exists — extend it so the second sender is asserted rather than assumed, and so a future third sender fails the test rather than silently failing in production. ```task id: AUDIT-WP-0010-T04 status: todo priority: medium state_hub_task_id: "56bde19e-6b54-5d9e-a3d7-8b13e810cb51" ``` Validate the envelope against what `tenant-engine` actually sends: `schema_version: audit-core.event.v1alpha1`, `tenant` = affected `tenant_id`, `action` = domain event type, `resource` = `tenant:`, duplicate event ids returning 200. Confirm the duplicate path reconciles rather than minting a second link (`test_replay_reconciles_rather_than_duplicating`). If the envelope needs a field this engine does not send, reply with the correction the intake invited rather than accepting a lossy record. Record the declared class (**attributive**, non-blocking drain, trade declared at `tenant-engine/docs/evidence-emission.md`) alongside the sender registration, so the §9.6 trade is documented here and not only in the emitter. ```task id: AUDIT-WP-0010-T05 status: todo priority: medium state_hub_task_id: "777bb728-4a4d-5c5b-8b62-24fdb6ab31e3" ``` Prove it end to end against the production receiver: a real event from `tenant-engine` accepted, attributed to the right tenant, redacted per policy, and linked into the chain. Record the evidence under `docs/evidence/`. Then close `AUDIT-IN-0002` with outcome `promoted` citing this workplan, and reply to `tenant-engine`. ## Acceptance - `tenant-engine` events land in production, correctly attributed and redacted. - The token never appears in Git, a work record, State Hub, or chat. - The NetworkPolicy admits exactly the intended senders, asserted by test. - The attributive trade is recorded on audit-core's side, and completeness is nowhere claimed for this stream. - `AUDIT-IN-0002` closed and the requester answered. ## Notes `tenant-engine` did this the right way round: it built the outbox, declared the class honestly, documented the trade, and asked before assuming it could emit. The correct response is to move quickly, not to make it wait behind `AUDIT-WP-0009`.