Files
Agent-System-Backend/.task/feature-rdm-two-trees-project-audit.md
T
weichen a8fa2fe6c9 Bind trusted operation IDs before domain handlers
Why: A domain outbox could not correlate its committed event with the host receipt because the bus generated the operation ID after the handler returned.

Evidence: RED-to-GREEN bus and compiled-handler tests; local go test ./... -count=1 and isolated PostgreSQL operation/link checks passed as SELF.

Impact: Expose a host-generated operation ID to handlers and resolve a unique committed audit offset after dispatch; missing receipts remain retryable and ambiguous rows fail closed.

Refs: Stage 5 W9; approved Stage 6 audit-link design adjustment r1
2026-10-14 04:11:09 +00:00

6.0 KiB

W9 Backend project audit candidate

  • Scope: TT-A01/A02/A04/U04, approved stage 5 W9. Stage 6 local implementation; human review, formal acceptance, PR, CI, tag, and deployment are pending.
  • Branch/worktree: feature/rdm-two-trees-project-audit at /home/weichen/projects/Agent-System-Backend-wt-feature-rdm-two-trees-project-audit.
  • R0: Backend authoritative origin/main 5d5fc9891c0eb8994545ccb8b740535d9b99fb0e; main was clean and equal to the fetched remote before this worktree was created. Core gitlink in this worktree is 5773e74086791731059730bd3726dc8c9cd1381f.
  • Startup receipt: /tmp/backend-w9-precheck-20261014-01.json (SELF, exit 2, SYNC_REQUIRED for the separate Dev checkout feature/rdm-increment-acceptance-note, six commits behind Dev origin/main). This Backend task uses the independent-development path in Backend AGENTS.md; it does not write that Dev checkout or its task card. The receipt is preserved as a failure, not relabeled READY.
  • Approved affected design: rdm-two-trees-stage6-audit-link-design-adjustment-r1.md, SHA256 385d9d7570217ddcce9fe60a60c99dc58c9a4875ad790584a724d09a210a6e52; user approval in the task session covers this local Backend increment, not tag publication or Stage 7 acceptance.

Implemented local slice

  • Optional ProjectAuditHost and domain audit.read registration. A different plugin, absent registration, or unbound actor cannot read.
  • Store write snapshots the project from the owner-matched conversation; query requires the authenticated actor's owner and that immutable project. This deliberately excludes other members' private conversations and unbound historical rows, and does not move old receipts when a conversation changes projects.
  • Command audit stores the current verified account display name at write time; old rows expose not-recorded. Public receipts omit raw payload and parameter digest. Cursor uses project-and-owner scoped source cutoff.
  • Initial commit c8dd169a1fdc1451fab3b475c14f9636a7f5bf6c exposed only the actor-owned project slice. The next local increment fixes button actor propagation and adds a trusted domain-commit link publisher, verifier callback, exact linked metadata read, current audit.read and per-object metadata checks, and durable idempotency. Unlinked personal conversations remain private.
  • Trusted command dispatch now creates an operation ID before calling the compiled handler and passes that ID through extension.Request. The handler can commit the same ID with its domain event/outbox; the final Backend audit/receipt uses it. Client params.operationId is ignored. An outbox link request with HostAuditOffset=0 resolves a unique committed host row by owner, fixed project, and operation ID; a missing row returns retryable ErrProjectAuditPending, while duplicate rows fail closed. The separate idempotency key still controls retry equivalence.
  • Exact-source linked reads do not enumerate hidden links or reveal raw payload, private conversation ID, or content. Body disclosure and a broad project link list are not implemented; the public coverage reports the narrower capability.

Verification and remaining work

  • The session saw RED for the missing store and host interfaces, unscoped cutoff, cross-member private disclosure, and conversation-project reassignment before the respective fixes. These RED outputs were not captured by make evidence, so this card does not use them as formal evidence.
  • SELF receipt /tmp/backend-w9-go-test-20261014-01.json: go test ./..., exit 0, run on the pre-commit worktree based on R0.
  • SELF receipt /tmp/backend-w9-build-20261014-01.json: CGO_ENABLED=0 go build ./cmd/agent-system, exit 0, run on that pre-commit tree. Generated local binary removed after the build.
  • SELF PostgreSQL receipt /tmp/backend-w9-pg-cutoff-20261014-01.json: isolated PostgreSQL 18.6 instance on port 55442 reproduced a low-ID transaction committing after a higher visible ID. /tmp/backend-w9-pg-link-20261014-02.json verifies the new link table, retry/conflict behavior, and the cutoff counterexample against isolated schemas. Both exited 0. The current PG cutoff means only offset-upper-bound-only; it is not a fixed historical visibility snapshot or safe incremental cursor.
  • SELF PostgreSQL receipt /tmp/backend-w9-pg-migrate-20261014-01.json: isolated SQLite→PostgreSQL migration preserved one linked receipt and advanced its target sequence; the same migration ID replayed idempotently. The temporary PG instance was stopped and removed after verification.
  • SELF PostgreSQL receipt /tmp/backend-w9-pg-operation-20261014-01.json: a fresh isolated PG instance reran the link lookup, idempotency/conflict, and low-ID late-commit checks after trusted operation-offset resolution was added. The instance was stopped and removed.
  • Integration constraint: this branch uses schema v31 (audit attribution) and v32 (links), while the independent W6 Backend branch also has a local v31. Neither branch has consumed the other's unmerged work. After the first branch lands, the later branch owner must sync the new main, renumber its unpublished migrations without rewriting an already released version, and rerun fresh/old SQLite and PG migration plus combined regression on the actual merged source. These isolated tests do not prove W6+W9 compatibility.
  • Required next W9 work: add a committed-order waterline or materialize the authorized ID set at the first read, then prove with PG low-ID late commits that history and cross-page incremental reads neither omit nor duplicate operations. The Backend project-event source, actual RDM committed-event verifier/outbox consumer, run ID propagation from command dispatch, model/tool/background receipts, body/detail disclosure, broad authorized list pagination, history/export, and combined product verification remain unfinished. A domain commit followed by a lost Backend audit append leaves the link pending; recovery must retain and reconcile that unknown state without claiming a host receipt. The exact-link slice is not Stage 7 audit acceptance.