feat(ontology): canonical receipts record whose they are - #850
Merged
Conversation
Migration 0223 gave `ont_action_command_receipts` an `owner` and a `target`,
but no writer set them. `owner` DEFAULTs to `'ontology.action'`, so every
canonical receipt — Company revise, HR appoint, PayRun decide — claimed to
belong to the pre-existing instance-action path.
That is worse than the column not existing. An absent column is a known gap;
a populated one reads as an answer, and this one answered wrongly for six of
the seven writers.
DERIVED FROM THE COMMAND'S QUERY, not from `action_key`. Every canonical
query already implements `dispatch_target()` — the same value the projected
dispatch path uses — so the attribution is a lookup, not a second mapping to
drift:
let receipt_target = command.query.dispatch_target();
let receipt_owner = ReceiptOwner::Canonical(receipt_target.object());
`action_key` cannot do this job, and I tried it first. Its own doc says it is
"unique only per object type": this suite's commands carry a bare `"revise"`,
which names no target at all, and `employment.rs` reassigns org units under
`"internal.reassign_org_unit"`, which is not a dispatch target by
construction. Parsing it would have failed closed on a real production path.
`ontology/rest` is deliberately UNCHANGED. It is the instance-action path —
the rows the widening's DEFAULT was written for — so `'ontology.action'` with
a NULL target is the truth there, and 0223's CHECK requires exactly that
pairing.
Proven against real PostgreSQL:
a real Company port write -> owner='company', target='company.revise'
every DispatchTarget -> storable under the object that owns it,
looped over DispatchTarget::ALL so a
fourteenth target cannot arrive unattributed
no canonical receipt -> falls back to the instance-action default
Mutation-proven: removing the two binds from `company.rs` turns the port test
red with `left: "ontology.action"` — the exact wrong attribution this fixes.
All five canonical port suites, the PayRun port suite and the widening suite
pass.
WHAT THIS DOES NOT DO. The DEFAULT stays. Dropping it belongs with the change
that makes `ontology/rest` pass `owner` explicitly, and dropping it now would
break that writer on the next deploy. Per-row attribution is now TRUE where it
is written, but nothing yet stops a crate writing a row it does not own —
that is the receipt store's writer boundary, bounded to three crates by #829
and still without per-row enforcement.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Collaborator
Author
Pull Request Review: #850 - feat(ontology): canonical receipts record whose they areExecutive Overview
16-Lens Canonical Reasoning Framework Evaluation
Strict Scrutiny Compliance Checklist
🤖 [Reviewed] by Oyatie Anvil |
Collaborator
Author
|
❌ Blocked — 9 finding(s) across 68 gates; 6 gate(s) produced no measurement.
🤖 [Blocked] by Oyatie Anvil |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Migration 0223 gave
ont_action_command_receiptsanownerand atarget, but no writer set them.ownerDEFAULTs to'ontology.action', so every canonical receipt — Company revise, HR appoint, PayRun decide — claimed to belong to the pre-existing instance-action path.That's worse than the column not existing. An absent column is a known gap; a populated one reads as an answer, and this one answered wrongly for six of the seven writers.
Derived from the command's query, not
action_keyEvery canonical query already implements
dispatch_target()— the same value the projected-dispatch path uses — so attribution is a lookup, not a second mapping that can drift:action_keycannot do this job, and I tried it first. Its own doc says it is "unique only per object type": these commands carry a bare"revise", which names no target at all, andemployment.rsreassigns org units under"internal.reassign_org_unit", which is not a dispatch target by construction. Parsing it would have failed closed on a real production path.ontology/restis deliberately unchanged — it is the instance-action path, the rows the DEFAULT was written for, so'ontology.action'with a NULL target is the truth there, and 0223's CHECK requires exactly that pairing.Proven against real PostgreSQL
owner='company',target='company.revise'DispatchTargetDispatchTarget::ALL, so a fourteenth target can't arrive unattributedMutation-proven: removing the two binds from
company.rsturns the port test red withleft: "ontology.action"— the exact wrong attribution this fixes.All five canonical port suites, the PayRun port suite and the widening suite pass.
What this does not do
The DEFAULT stays — dropping it belongs with the change that makes
ontology/restpassownerexplicitly, and dropping it now breaks that writer on the next deploy.Per-row attribution is now true where it is written, but nothing yet stops a crate writing a row it doesn't own. That's the receipt store's writer boundary — bounded to three crates by #829, still without per-row enforcement (
console-yw0).🤖 Generated with Claude Code