Bind provenance label to typed-identity origin (#224) - #251
Conversation
Generalize the proven `Provenance::ProducedBy` lineage so its identity binding is a typed `ProvenanceOrigin` (#222) instead of a bare counterparty-scope `Ulid`, and carry that origin on each classified briefcase item alongside its `disclosure_class`. Why: the deterministic egress gate could tell an item's sensitivity but not *whose* data it is — the Immune-system gap for Bell/Auditor (spec #220, D-174). This lands the binding half; the egress origin-closure and grant caveat are later tickets (#225-#227 / dev-226). - `source_scope: Ulid` -> `ProvenanceOrigin`; field name kept so the JSON path stays `$.produced_by.source_scope`. - AD-140 erasure SQL/triggers re-pointed to the tagged counterparty identity (`.source_scope.identity`), keyed on `.kind = 'counterparty'`. Only counterparty origins are erasable; system/owner/legacy never match. - Triggers switched to DROP-then-CREATE and owned solely by `migrate_provenance_column` (after the row-shape rewrite) so an upgraded DB replaces the stale pre-D-174 trigger text instead of silently disabling closure. Migration rewraps legacy scalar `source_scope`. - `Option<ProvenanceOrigin>` added to `BriefcaseSection` and `ClassifiedBriefcaseItem`, populated in `pack`, top-up, and `provenance_from_sections`; egress coverage still keys only on `disclosure_class`. Origin participates in `PreparedQuery::binding_matches`. - Append-only origin extracted into `establish_or_preserve_origin` with a dedicated invariant test; new migration + stale-trigger tests. Closes #224
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Warning Review limit reached
Next review available in: 27 minutes Limit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?Wait for the limit to reset, then comment An organization admin can change what happens after included review limits in Billing. How do review limits work?CodeRabbit enforces per-developer PR review limits within each organization. For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (24)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…iteral (refs #251/#252) Cross-merge fixup: #251 added the required BriefcaseSection.origin field and updated existing literals; #252 landed a private_section() test literal without it. Their combination on main broke cargo test compilation. Add the missing 'origin: None,' matching the sibling literal in the same file. Coordinator-approved cross-merge fixup unblocking the #244 gate.
What
Generalizes the proven
learned_artifacts::Provenance::ProducedBylineage so its identity binding is the typedProvenanceOrigin(#222) instead of a bare counterparty-scopeUlid, and carries that origin on each classified briefcase item alongside itsdisclosure_class.Why
The deterministic egress gate could tell an item's sensitivity class but not whose data it is — the Immune-system gap for Bell and the Auditor (spec #220, canon D-174). This ticket lands the binding half of the hybrid; the runtime origin-vs-recipient egress closure and the grant caveat allowlist are the following tickets (#225–#227 / dev-226, kept untouched here).
Key decisions
ProvenanceOrigin(Introduce ProvenanceOrigin and IdentityRef typed vocabulary #222) serializes as an internally-tagged object ({"kind":"counterparty","identity":"…"}), not a bareUlid. The field namesource_scopeis kept so the JSON path stays$.produced_by.source_scope, but the value is now an object.$.produced_by.source_scope.identity) and keyed on.kind = 'counterparty'; only counterparty origins are erasable, system/owner/LegacyMigrationnever match.migrate_provenance_columnrewraps legacy scalarsource_scopeinto the tagged form (idempotent).CREATE ... IF NOT EXISTSto DROP-then-CREATE, owned solely bymigrate_provenance_column(run after the row-shape rewrite), so an upgraded DB replaces the pre-D-174 trigger text instead of silently disabling closure enforcement. Guarded by a new test.originisOption<ProvenanceOrigin>onBriefcaseSection/ClassifiedBriefcaseItem, mirroring the existingdisclosure_class: Option<…>precedent — an unresolved counterparty slice yieldsNone(not a wrongSystemlabel). Egress coverage still keys only ondisclosure_class; origin participates inPreparedQuery::binding_matches.establish_or_preserve_origin(the reconfirmation seam) with a dedicated invariant test: a minted origin is never rewritten by a later authorization/reconfirmation.Tests
New: append-only origin invariant, legacy scalar→tagged migration, stale-trigger replacement, and a
binding_matchesorigin regression. All existing counterparty-erasure and disclosure suites stay green.scripts/check.shpasses (fmt, clippy-D warnings, full workspace tests incl. the shell-binary kernel E2E, file-size, claims, capability-map, openspec validate).Scope
Kept to the label/lineage binding; no changes in the grant/caveat area (dev-226 runs there in parallel).
Implementation-notes summary
Provenance::ProducedBy.source_scopefromUlidto the typedProvenanceOrigin(Introduce ProvenanceOrigin and IdentityRef typed vocabulary #222), keeping the field name so the JSON path is stable.source_scope.Option<ProvenanceOrigin>toBriefcaseSectionandClassifiedBriefcaseItem, populated deterministically inpack, top-up, andprovenance_from_sections, without changing egress-coverage semantics.binding_matchesregression) and the append-only invariant has a dedicated unit test.scripts/check.sh) green; all existing erasure/disclosure suites stay green.Closes #224
Summary by cubic
Bind learned-artifact provenance to a typed identity and carry that origin through briefcase sections and prepared-query bindings. Previously
source_scopewas a bare counterpartyUlidand bindings ignored origin; now we serialize a taggedProvenanceOrigin(system/owner/counterparty), include it in binding equality, and update erasure/closure to key only on counterparty identities.$.produced_by.source_scopebut change value to an internally tagged object, for example{"kind":"counterparty","identity":"…"}or{"kind":"system"}.source_scopeinto the tagged form; install closure triggers via DROP-then-CREATE keyed on$.produced_by.source_scope.kind = 'counterparty'and…identity; system/owner never match erasure.origin: Option<ProvenanceOrigin>toBriefcaseSectionandClassifiedBriefcaseItem; pack/top-up set system for kernel-produced sections and counterparty for the slice (unresolved staysNone); egress still keys only ondisclosure_classfor now;PreparedQuery::binding_matchesnow compares origin.establish_or_preserve_origin; reconfirmation establishes system origin forLegacyMigrationand preserves any existingProducedByorigin.Rollout
$.produced_by.source_scopemust accept the new tagged object instead of a scalarUlid.Written for commit fa75900. Summary will update on new commits.