RS4A-PATCH2-02 — Effect Identity with Authorization Binding Separated — 2026-06-21
RS4A-PATCH2-02 — Effect Identity with Authorization Binding Separated — 2026-06-21
Macro: RS4A-PATCH2 · Mục tiêu A + B (closes Codex re-review residual R1: authority scope/policy still participates in U1 effect identity)
Deliverable: 02 of 6 (under rs4a-patch2/) · design-only · scoped correction addendum
Corrects: RS4A-PATCH1-02 §1 (effect-identity formula still hashed canonical_owner_scope + canonical_authority_policy_ref) and §3 (authority canonicalized into the identity). Does NOT overwrite PATCH1 or RS4A; does NOT create any schema/column/constraint.
Gate: REGISTRATION_HOLD · REGISTRATION_CAN_PROCEED = NO
Status: EFFECT_IDENTITY_BUSINESS_EFFECT_ONLY + AUTHORIZATION_BINDING_SEPARATED — U1 now keys only the business registry effect; authority evidence is bound to the attempt/decision record but excluded from U1.
0. The residual defect this file closes (Codex re-review §3 / §12.1)
PATCH1 correctly excluded run_id, attempt_id/no, nonce, timestamps, TTL, operator/session, and volatile approval-instance ids. But it still hashed canonical_owner_scope and canonical_authority_policy_ref into effect_identity. Codex:
"A reassigned owner scope or policy-version change therefore creates a new U1 key for the same operation, code, artifact identity, and artifact hash. PATCH1 explicitly calls that a 'different effect.' It is not: authority determines whether an effect is authorized; it does not change the registry effect being requested. … A policy change could bypass U1 and attempt a second registration."
Codex's required correction (mandatory):
effect_identity= stable business effect only (operation + canonical target + canonical artifact identity/hash);authorization_binding_digest= owner scope + authority policy + approval/nonce evidence, bound to the attempt/consume record but excluded from U1;- policy/owner changes may authorize or deny a new attempt, but must not mint a new registration effect;
- re-registration under changed lifecycle must use an explicit different operation, not a changed authorization digest.
This file makes exactly that split. No schema, column, or constraint is created.
1. Effect identity v2 (closes R1 — business effect only)
effect_identity = logical_request_key = H(
protocol_version, # versioned hashing/canonicalization contract
operation = "register_dot", # fixed canonical operation
canonical_target_dot_code, # the one scalar DOT code (canonicalized)
canonical_artifact_identity, # canonical_path @ origin (symlink/.. resolved)
canonical_artifact_hash # trusted_attested.artifact_hash from Interface F (NEVER request_proposed)
)
H is a governed hash over a canonicalized field tuple under protocol_version. effect_identity and logical_request_key remain the same single key (PATCH1 retired the replay_key alias; PATCH2 keeps one key and removes the two authority terms PATCH1 had left in it).
1.1 Fields EXPLICITLY EXCLUDED from effect identity (Codex re-review mandatory list)
PATCH2 adds the two authority terms PATCH1 missed (★) to the existing exclusion set:
| Excluded field | Why it is not effect identity |
|---|---|
★ canonical_owner_scope |
authority/accountability, not the requested effect — a reassigned owner does not change what artifact/code is being registered |
★ canonical_authority_policy_ref |
authority policy version, not the requested effect — a policy bump does not change the registry effect |
★ approval instance id (approval_requests.id) |
volatile approval-instance row id |
★ APR row id (apr_approvals.id) |
volatile approval-instance row id |
★ owner row id (governance_object_ownership.id) |
volatile authority row id |
authorization_nonce |
single-use grant credential (a state transition, not identity) |
attempt_id |
execution attempt |
attempt_no |
retry ordinal (the iu_route_attempt anti-pattern) — keying on it is REPLAY_ATTEMPT_NO_BYPASS |
run_id |
observability/execution context |
timestamps / date_created / last_executed |
wall-clock |
| request TTL / freshness window | admissibility gate, not identity |
| operator / session / host / VPS IP | actor/infra context |
Rule (EFFECT_IDENTITY_BUSINESS_EFFECT_DISCIPLINE): effect_identity is a pure function of what registry effect is requested — operation, target code, artifact identity, artifact hash. Any authority/accountability/credential/execution field is excluded. Authority answers may this effect be admitted; it never answers which effect this is.
1.2 Why removing authority from U1 is the correct (and safer) posture
PATCH1 reasoned: "fresh approval under unchanged policy ⇒ unchanged authority terms ⇒ same key ⇒ REPLAY_DUPLICATE." That was right for the unchanged-policy case but wrong for the changed-policy case: a genuine owner reassignment or policy-version bump changed the authority terms ⇒ changed the U1 key ⇒ the same artifact/code could be registered a second time. PATCH2 closes that hole: because authority is no longer in U1, the same operation/code/artifact/hash yields the same U1 key regardless of any authority change, so the duplicate is caught by U1 every time. (See AUTHORIZATION_CHANGED_SAME_EFFECT_DUPLICATE, §3.)
2. Authorization binding digest (closes R1 — authority evidence bound separately)
Authority evidence is preserved in full — it is simply bound to the attempt/consume/decision record, not to U1.
authorization_binding_digest = H(
protocol_version,
effect_identity, # binds the authorization TO the effect, without being part of U1
canonical_owner_scope, # owner-of-record SCOPE (governance_object_ownership accountable head), not a row id
canonical_authority_policy_ref, # the governed register_dot policy VERSION, not an approval-instance id
approval_evidence_ref, # reference to the register_dot APR + decision (instance), as evidence not identity
quorum_evidence_ref, # reference to the quorum/approvals proof
authorization_nonce_issuer, # the authority that minted the single-use grant (not the nonce value as identity)
authorization_window # the validity window of the authorization (admissibility, not identity)
)
2.1 Rules (Codex re-review §3 / §12.1)
authorization_binding_digestis REQUIRED for admission, but is NOT U1. A registration cannot be admitted without a resolved authorization binding; the binding is recorded on the Phase-3 attempt/consume record (PATCH1-05 §2) as non-identity evidence.- Authority changes may authorize or deny an attempt, but do not mint a new registration effect. Two attempts with different
authorization_binding_digestvalues but the sameeffect_identityare two attempts at one effect. - Same operation/code/artifact under changed owner/policy = same U1
effect_identity⇒ must not create a second registration. It isREPLAY_DUPLICATEat U1; the changed authority is recorded as new authorization evidence on the attempt, not as a new effect. - Intentional re-registration is a different OPERATION, not authority drift. If lifecycle genuinely wants to re-register, it must use an explicit, governed, different operation — e.g.
register_dot_revision,supersede_dot_registration, or another governed lifecycle operation — which changesoperation(and therefore legitimately changeseffect_identity). Authority-digest drift must never be the re-registration mechanism.
2.2 Binding location (no new surface invented)
authorization_binding_digest lives where PATCH1-05 §2 already puts authority consumption: the Phase-3 atomic attempt/consume record, alongside the consumed authorization_nonce (U2) and the consumed effect_identity (U1). It is a bound non-identity attribute of that record. No new table/column is created here; like every other carrier in this package, the binding record is REQUIRED_NOT_PRESENT on live surfaces and therefore fails closed today (no owner-of-record, no register_dot policy — see §4).
3. Required reject set (Codex re-review §3)
| Reject code | Trigger |
|---|---|
AUTHORIZATION_CHANGED_SAME_EFFECT_DUPLICATE |
a second attempt presents the same effect_identity with a changed authorization_binding_digest (new owner/policy/approval) — U1 already consumed ⇒ duplicate registration refused; the changed authority is logged as evidence, not honored as a new effect |
AUTHORITY_BINDING_UNRESOLVED |
authorization_binding_digest cannot be canonicalized to a stable scope + governed policy version (only volatile instance ids available, or no governed policy exists) ⇒ admission fail-closed |
AUTHORIZATION_EVIDENCE_MISSING |
no approval_evidence_ref / quorum_evidence_ref / bound nonce issuer present ⇒ admission fail-closed |
REPLAY_DUPLICATE |
the same effect_identity is presented after commit (any authority, fresh run/nonce/approval) ⇒ returns the durable prior decision, mints no second effect |
3.1 Behavior matrix (against the v2 split)
| Scenario | effect_identity |
authorization_binding_digest |
Outcome |
|---|---|---|---|
| Exact retry, same everything | unchanged | unchanged | return durable prior decision (idempotent) |
Same effect, different run_id |
unchanged | unchanged | same effect ⇒ prior decision (run_id non-keying) |
| Same effect, fresh nonce | unchanged | changed (new nonce issuance) | REPLAY_DUPLICATE (nonce non-identity) |
| Same effect, fresh approval instance, same policy | unchanged | changed (new approval evidence) | REPLAY_DUPLICATE (approval instance non-identity) |
| Same effect, changed owner scope / policy version | unchanged | changed | AUTHORIZATION_CHANGED_SAME_EFFECT_DUPLICATE — authority revalidated, but no second registration |
Genuinely new effect (register_dot_revision/supersede_*) |
changed (operation differs) | changed | admissible iff the new operation + authority are governed and lifecycle permits |
The decisive PATCH2 fix is the fifth row: under PATCH1 it would have produced a new U1 key (a second registration); under PATCH2 it is a duplicate at U1.
4. Live posture (read-only query_pg, db directus, 2026-06-21) — overall still fail-closed
The separation does not weaken the gate; it relocates the failure attribution:
governance_object_ownership= 0 rows ⇒canonical_owner_scopeunresolvable ⇒AUTHORITY_BINDING_UNRESOLVED⇒ admission fail-closed (G2).apr_action_types= 14 codes, noregister_dot; register-shaped codeshandler_ref='unimplemented'⇒ no governedcanonical_authority_policy_ref⇒AUTHORITY_BINDING_UNRESOLVED(G3).dot_toolsconstraints (LIVE, re-confirmed): onlydot_tools_pkey PRIMARY KEY (id),chk_dot_tier (A/B),chk_dot_coverage,chk_dot_trigger,fk_dot_tools_domain— no UNIQUE on any effect/grant axis ⇒ U1/U2REQUIRED_NOT_PRESENT.
Net change vs PATCH1: previously the whole effect_identity was fail-closed because authority terms were inside it (AUTHORITY_BINDING_IDENTITY_UNSTABLE). Now effect_identity is computable from business fields alone, and the fail-closed condition moves to admission: no admission without a resolved authorization_binding_digest (AUTHORITY_BINDING_UNRESOLVED). The registration outcome is identical — HOLD / fail-closed — but the contract is now correct: a future authority change can never mint a duplicate registration.
5. What this corrects in PATCH1 (precise, no overwrite)
| PATCH1 location | PATCH1 text | PATCH2 correction |
|---|---|---|
| PATCH1-02 §1 formula | effect_identity = H(…, canonical_owner_scope, canonical_authority_policy_ref) |
remove both authority terms from effect_identity (§1) |
| PATCH1-02 §1.2 / §3.1 | "changed authority policy ⇒ a different effect" | changed authority ⇒ same effect ⇒ AUTHORIZATION_CHANGED_SAME_EFFECT_DUPLICATE; re-registration needs a different operation (§2.1.4, §3) |
| PATCH1-02 §3 | authority canonicalized into identity; AUTHORITY_BINDING_IDENTITY_UNSTABLE |
authority canonicalized into a separate authorization_binding_digest bound to the attempt record; fail-closed becomes AUTHORITY_BINDING_UNRESOLVED at admission (§2, §4) |
U1 axis (PATCH1-02 §2 UNIQUE(effect_identity)) is unchanged in form; only its input set narrows to business-effect fields. U2/U3/U4 are unaffected by this file (U3 is handled in PATCH2-03).
6. Status
EFFECT_IDENTITY_BUSINESS_EFFECT_ONLY— U1 keys operation + target code + artifact identity + artifact hash; all authority/credential/execution fields excluded.AUTHORIZATION_BINDING_SEPARATED— owner scope + policy + approval/quorum/nonce-issuer/window bound to the Phase-3 attempt/consume record as non-identity evidence, required for admission, excluded from U1.- Changed-authority/same-effect ⇒ duplicate (never a new registration); intentional re-registration ⇒ explicit different operation.
- No schema/column/constraint; overall registration fail-closed (now via
AUTHORITY_BINDING_UNRESOLVEDat admission). GateREGISTRATION_HOLD·CAN_PROCEED = NO.