fix: namespace idempotency keys by principal (#6 item D) #13

Merged
agent-int merged 1 commit from fix/issue-6-idempotency-principal into main 2026-06-29 17:13:05 -07:00
Collaborator

Closes #6 (the deferred Item D; the other four landed in #12).

content_hash (UNIQUE) was the idempotency key as-is, so two principals reusing one key collapsed to first-writer-wins -- the second caller silently got back the first principal's transaction (a write-shadowing hazard).

Fix

Namespace the idempotency key by the posting principal (falling back to the posting role when no principal is supplied) in both postTransaction and editTransaction, via a shared WriterExecutor.namespacedContentHash. The pre-check lookup uses the same namespaced value, so a same-principal retry stays idempotent.

old rows: <key>                (unchanged)
new rows: "<principal>:<key>"  (or "<role>:<key>" when no principal)

Pre-migration rows keep their bare hashes; the namespaced space never collides with them, so the UNIQUE constraint still holds and no data migration is needed.

Test

testIdempotencyKeyNamespacedByPrincipal: two principals reusing one key get distinct transactions, while a same-principal retry returns the same transaction (still idempotent).

Full SwiftPM suite: 189 passing, 0 failures (no regressions in AgentAttributionTests or the MCP propose/commit idempotency tests).

Closes #6 (the deferred Item D; the other four landed in #12). `content_hash` (UNIQUE) was the idempotency key as-is, so two principals reusing one key collapsed to first-writer-wins -- the second caller silently got back the **first** principal's transaction (a write-shadowing hazard). ## Fix Namespace the idempotency key by the posting principal (falling back to the posting role when no principal is supplied) in both `postTransaction` and `editTransaction`, via a shared `WriterExecutor.namespacedContentHash`. The pre-check lookup uses the same namespaced value, so a same-principal retry stays idempotent. ``` old rows: <key> (unchanged) new rows: "<principal>:<key>" (or "<role>:<key>" when no principal) ``` Pre-migration rows keep their bare hashes; the namespaced space never collides with them, so the UNIQUE constraint still holds and no data migration is needed. ## Test `testIdempotencyKeyNamespacedByPrincipal`: two principals reusing one key get **distinct** transactions, while a same-principal retry returns the same transaction (still idempotent). Full SwiftPM suite: **189 passing, 0 failures** (no regressions in `AgentAttributionTests` or the MCP propose/commit idempotency tests).
fix: namespace idempotency keys by principal (#6 item D)
Some checks failed
ci / macos (pull_request) Successful in 4m5s
ci / linux (pull_request) Has been cancelled
ci / proto (pull_request) Has been cancelled
ci / purity (pull_request) Has been cancelled
83c996134e
The content_hash (UNIQUE) was the idempotency key as-is, so two principals
reusing one key collapsed to first-writer-wins: the second caller silently
got back the first principal's transaction. Namespace the key by the posting
principal (falling back to the posting role when none is supplied) in both
postTransaction and editTransaction, and use the namespaced value for the
pre-check lookup too.

Pre-migration rows keep their bare hashes; the namespaced space never
collides with them, so the UNIQUE constraint still holds and no migration
is needed.

Test: two principals reusing one key get distinct transactions while a
same-principal retry stays idempotent. Full suite: 189 passing.

Closes #6
agent-int deleted branch fix/issue-6-idempotency-principal 2026-06-29 17:13:05 -07:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
nic/commons!13
No description provided.