Skip to content

Refuse supersedes into another namespace on live claims (issue 4) - #158

Open
Hafeok wants to merge 2 commits into
claude/great-feynman-zcdsdd-step-03-belongingfrom
claude/great-feynman-zcdsdd-step-04-supersedes
Open

Hafeok wants to merge 2 commits into
claude/great-feynman-zcdsdd-step-03-belongingfrom
claude/great-feynman-zcdsdd-step-04-supersedes

Conversation

@Hafeok

@Hafeok Hafeok commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator

Closes #147 — PRD ledger/prd/namespace-independence-prd.md §6 issue 4 (§3.7; rulings 43 and 75; AC-43).

Stacked on #157 (issue 3). Review the last commit alone, 12123c60.

What lands

Only based_on crosses a namespace. The new verify::crossing stage makes a decision's latest version whose supersedes names a decision of another namespace a SCHEMA fault naming the decision, the edge and the other namespace. Earlier versions are history and are not judged, so a next version that drops the edge repairs the store — the same withdrawal LP-8.21 records for a losing claimant under G005. The writer refuses the supersede verb by the gate it already runs over the store as it would be. No class is added.

Tests (AC-43)

Each failed before the change (the verb succeeded; the gate said nothing).

  • ledger-core/src/verify/crossing_tests.rs: a latest version superseding another namespace is named with both ids; a next version dropping the edge repairs it while history keeps the claim; a supersession within the namespace is not a crossing.
  • ledger-cli/tests/crossing.rs: over a two-namespace store, supersede across namespaces is refused in both directions and writes nothing; a crossing filed by hand past the verb fails the gate, and a hand-filed next version without the edge makes the store conformant again; a supersession at home still stands.

Protocol

LP-3.31 loses its not-implemented mark and its note on the implemented format; LP-8.8 gains the clause; Appendix B row. No Appendix C note (the issue row says none). No digest moves; CANONICAL_FORM stays v1; no such edge in this repository or the fixtures.

Checks

cargo build, cargo t, cargo clippy --workspace -- -D warnings -D clippy::unwrap_used, the quality scripts, and ledger verify --export on this repository (same output as on main).

🤖 Generated with Claude Code

https://claude.ai/code/session_01GgRcHXmnAu43hChNxhnqxU


Generated by Claude Code

Hafeok commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

CI, standing down on one step. The check job fails at the contract-surface gate (ddd diff-contracts <base>..HEAD): 2 undischarged exposed-surface changes, both this PR's — verify/crossing.rs#findings added and verify/mod.rs#crossing added. Every other step of check passed.

The remedy the gate names is ddd bind claude/great-feynman-zcdsdd-step-03-belonging..HEAD (with --verdict), which files seam declarations under .ddd/seams/. The session's instructions forbid me to edit .ddd/, and a declaration's verdict is the author's judgment, so I am not filing them. Please run ddd bind on this branch and push, or tell me to. Recorded in the close-out.


Generated by Claude Code

@Hafeok
Hafeok force-pushed the claude/great-feynman-zcdsdd-step-03-belonging branch from 61bd97e to 74fb858 Compare October 11, 2026 06:14
@Hafeok
Hafeok force-pushed the claude/great-feynman-zcdsdd-step-04-supersedes branch from 12123c6 to ec43481 Compare October 11, 2026 06:15
Hafeok added a commit that referenced this pull request Oct 11, 2026
…tage

Issue #148 (PRD issue 5, §3.2, §3.9; rulings 47, 67, 68 and 50 per
namespace; AC-47 first bullet, AC-48, AC-68). A grant, grant acceptance,
interval, revocation or role belongs to the namespace whose directory
holds it, and nothing in one namespace's authority has effect in another.

`Authority::of(store, ns)` and `Authority::as_of(store, landing, pos, ns)`
read one namespace's records (roles under its `roles/`, the records in
its directory); every role check, genesis lookup and policy verdict goes
through them: the verbs' `authorized`, `A006` over history, the
genesis-role rules of ruling 50, the cross-entry reference rules, the
unbound-genesis notice (one per namespace). A scope is read inside its
own namespace: `*` and `ns:<own>` are the whole of it, and a grant scoped
`ns:<other>` than its directory is a `SCHEMA` fault. The graph stage runs
the shapes over each namespace's graph alone, so `A003` and `A005` count
one trust root per namespace; for that the export selection keeps a
namespace's own authority records only — the `*` reach of LP-9.11 goes.
Roles are per namespace (`Store::role_in`; a duplicate id is judged
within the namespace).

The writer opens every namespace as it opens the first: `init
--namespace` bootstraps the namespace's own genesis grant, root and
accept roles and first policy, and `--external-ref` is required every
time; `join_genesis` goes. This part of PRD issue 8 lands here because
with authority per namespace a later namespace's policy under another
namespace's genesis fails `A006`, so the writer's own gate would refuse
opening any second namespace until issue 8. `grant new --namespace` names
where a grant is filed (inferred from a `ns:` scope, else the store's one
namespace); `grant accept|revoke` and the intervals read the grant's
directory. The grant verbs move to `author/grant_ops.rs` (file length).

Keys stay store-wide in this step — bound, closed and trusted across
namespaces, the genesis holder's key carried into a later namespace by
`init` as before — and `Authority` says so; issue 6 scopes them. AC-47's
second bullet (a rotate in A leaves B unchanged) therefore belongs to
issue 6 and is not claimed here.

Tests (each failing before the change): `ledger-cli/tests/authority_namespaces.rs`
— two namespaces each with their own genesis, roles and policy verify
with no `A003`/`A005`; a later `init` needs its own mandate and a
namespace is opened once; a `*` grant in A authorises in A alone and a
second namespace needs `--namespace`; a grant scoped to another
namespace is refused by the verb and a schema fault by hand; no grant
carries a namespace field and authority records sit apart from decisions
(AC-48); a store whose second namespace was put under the first's
genesis, as the writer filed one before this, fails `A006` on its policy
and D7 on its first binding (AC-68); and the carried test: no agent
identity and no non-interactive session produces an acceptance in a
later namespace either (`authority.rs` keeps the first-namespace one).

Tests rewritten, and why: `graph/export_tests.rs`
`a_namespace_export_carries_its_own_authority_records_only` asserted
that a `*` genesis reaches every namespace's export — it now asserts
that another namespace's `*` grant and same-id role stay out;
`signing/check_tests.rs` and `verify/order_grid.rs` filed `fixture.ledger`
policies and decisions in a `hafeok.ledger` change-set — they now use one
namespace; `ledger-cli/tests/authority.rs`
`a_grantor_below_star_is_refused_a_grant_over_another_scope` used
`ns:fixture.other` as "another scope" — that grant would now be made in
`fixture.other`'s authority, where the role is not declared, so the test
asserts that refusal and keeps `*` and `set:` for the covering rule.
The pre-policy-binding cases (ruled 2026-10-05) rested on a later
namespace whose genesis was older than its policy, which the writer can
no longer file: `init` dates every namespace's genesis with its policy.
A new helper, `hand::found`, founds a namespace by hand with its genesis
ten seconds older than its first policy, and these tests use it:
`pre_policy_binding.rs` (its module note, `a_signed_binding_before_the_first_policy_is_trusted_and_signs`,
`an_unsigned_binding_before_a_signed_first_policy_fails_l011_and_is_never_trusted`,
`under_a_none_first_policy_d7_alone_decides_a_binding_before_it`) and
`binding_accounting.rs` (`first_policy`). Two tests there change meaning
and are renamed: a skewed add dated before `init`'s binding is now before
the namespace's genesis too, so D7 refuses it
(`a_skewed_add_dated_before_inits_genesis_is_refused_by_d7`); and opening
a namespace over a binding filed in it before any policy no longer turns
the binding into `L011` — it stays a schema fault, now D7's
(`…_and_init_does_not_launder_it`). In `closed_key.rs`
`the_genesis_holders_first_binding_in_a_second_namespace_vouched_by_the_closed_key`
dates the binding after the second namespace's policy: dated before it,
the binding is before that namespace's genesis and D7's, whatever signs
it; the test keeps its point, that the closed key vouches for nothing.
Step 4's `crossing.rs` no longer asserts the absence of `G001`.

The graph stage splits: the authority shapes (`A003`, `A005`) run over
each namespace's graph, the decision shapes (`G001`–`G006`) over the
store's graph — PRD §3.9 says the whole stage runs per namespace, but a
`supersedes` edge in history into another namespace would then be a
permanent `G001`, against ruling 75 (live claims only), and `based_on`
crosses by design. Recorded in LP-8.19 and the close-out.

Protocol: LP-6.5, LP-6.16, LP-6.28, LP-6.31 lose their marks and notes;
LP-5.19 (roles per namespace), LP-8.19 (graph stage per namespace, `A005`
row), LP-8.23, LP-8.31 (notice per namespace, report shape), LP-9.11
(export reach), LP-8.8 (the `ns:<other>` clause); Appendix B row;
Appendix C note "Authority is per namespace". No digest moves; no format
bump; no class added; no grant payload gains a field.

Stacked on #158 (issue 4).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GgRcHXmnAu43hChNxhnqxU
@Hafeok
Hafeok force-pushed the claude/great-feynman-zcdsdd-step-03-belonging branch from 74fb858 to eb6bf21 Compare October 11, 2026 06:22
Hafeok and others added 2 commits October 11, 2026 06:22
Issue #147 (PRD issue 4, §3.7; rulings 43 and 75; AC-43). Only
`based_on` crosses a namespace. The new `verify::crossing` stage makes a
decision's latest version whose `supersedes` names a decision of another
namespace a `SCHEMA` fault naming the decision, the edge and the other
namespace. Earlier versions are history and are not judged, so a next
version that drops the edge repairs the store — the same withdrawal
LP-8.21 records for a losing claimant. The writer refuses the verb by the
gate it runs over the store as it would be; `G001` keeps the dangling
target, whichever namespace it names. No class is added.

Tests (each failing before the change): `crossing_tests` over in-memory
stores (a latest version superseding another namespace is named with
both ids; a next version dropping the edge repairs it while history keeps
the claim; a supersession within the namespace is not a crossing), and
`ledger-cli/tests/crossing.rs` over a two-namespace store: `supersede`
across namespaces is refused in both directions and writes nothing; a
crossing filed by hand past the verb fails the gate,
and a hand-filed next version without the edge makes the store
conformant again; a supersession at home still stands.

Protocol: LP-3.31 loses its not-implemented mark and its note; LP-8.8
gains the clause; Appendix B row. No Appendix C note (the issue row says
none). No digest moves; no such edge in this repository or the fixtures.

Stacked on #157 (issue 3).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GgRcHXmnAu43hChNxhnqxU
Files the signed bindings the contract-surface gate demands for this
step's two exposed Rust surface changes: a new declaration for
verify/crossing.rs with its verdict_knowledge written, and the
verify/mod.rs declaration amended with the binding for the new module.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GgRcHXmnAu43hChNxhnqxU
@Hafeok
Hafeok force-pushed the claude/great-feynman-zcdsdd-step-04-supersedes branch from ec43481 to deb88ef Compare October 11, 2026 06:22
Hafeok added a commit that referenced this pull request Oct 11, 2026
…tage

Issue #148 (PRD issue 5, §3.2, §3.9; rulings 47, 67, 68 and 50 per
namespace; AC-47 first bullet, AC-48, AC-68). A grant, grant acceptance,
interval, revocation or role belongs to the namespace whose directory
holds it, and nothing in one namespace's authority has effect in another.

`Authority::of(store, ns)` and `Authority::as_of(store, landing, pos, ns)`
read one namespace's records (roles under its `roles/`, the records in
its directory); every role check, genesis lookup and policy verdict goes
through them: the verbs' `authorized`, `A006` over history, the
genesis-role rules of ruling 50, the cross-entry reference rules, the
unbound-genesis notice (one per namespace). A scope is read inside its
own namespace: `*` and `ns:<own>` are the whole of it, and a grant scoped
`ns:<other>` than its directory is a `SCHEMA` fault. The graph stage runs
the shapes over each namespace's graph alone, so `A003` and `A005` count
one trust root per namespace; for that the export selection keeps a
namespace's own authority records only — the `*` reach of LP-9.11 goes.
Roles are per namespace (`Store::role_in`; a duplicate id is judged
within the namespace).

The writer opens every namespace as it opens the first: `init
--namespace` bootstraps the namespace's own genesis grant, root and
accept roles and first policy, and `--external-ref` is required every
time; `join_genesis` goes. This part of PRD issue 8 lands here because
with authority per namespace a later namespace's policy under another
namespace's genesis fails `A006`, so the writer's own gate would refuse
opening any second namespace until issue 8. `grant new --namespace` names
where a grant is filed (inferred from a `ns:` scope, else the store's one
namespace); `grant accept|revoke` and the intervals read the grant's
directory. The grant verbs move to `author/grant_ops.rs` (file length).

Keys stay store-wide in this step — bound, closed and trusted across
namespaces, the genesis holder's key carried into a later namespace by
`init` as before — and `Authority` says so; issue 6 scopes them. AC-47's
second bullet (a rotate in A leaves B unchanged) therefore belongs to
issue 6 and is not claimed here.

Tests (each failing before the change): `ledger-cli/tests/authority_namespaces.rs`
— two namespaces each with their own genesis, roles and policy verify
with no `A003`/`A005`; a later `init` needs its own mandate and a
namespace is opened once; a `*` grant in A authorises in A alone and a
second namespace needs `--namespace`; a grant scoped to another
namespace is refused by the verb and a schema fault by hand; no grant
carries a namespace field and authority records sit apart from decisions
(AC-48); a store whose second namespace was put under the first's
genesis, as the writer filed one before this, fails `A006` on its policy
and D7 on its first binding (AC-68); and the carried test: no agent
identity and no non-interactive session produces an acceptance in a
later namespace either (`authority.rs` keeps the first-namespace one).

Tests rewritten, and why: `graph/export_tests.rs`
`a_namespace_export_carries_its_own_authority_records_only` asserted
that a `*` genesis reaches every namespace's export — it now asserts
that another namespace's `*` grant and same-id role stay out;
`signing/check_tests.rs` and `verify/order_grid.rs` filed `fixture.ledger`
policies and decisions in a `hafeok.ledger` change-set — they now use one
namespace; `ledger-cli/tests/authority.rs`
`a_grantor_below_star_is_refused_a_grant_over_another_scope` used
`ns:fixture.other` as "another scope" — that grant would now be made in
`fixture.other`'s authority, where the role is not declared, so the test
asserts that refusal and keeps `*` and `set:` for the covering rule.
The pre-policy-binding cases (ruled 2026-10-05) rested on a later
namespace whose genesis was older than its policy, which the writer can
no longer file: `init` dates every namespace's genesis with its policy.
A new helper, `hand::found`, founds a namespace by hand with its genesis
ten seconds older than its first policy, and these tests use it:
`pre_policy_binding.rs` (its module note, `a_signed_binding_before_the_first_policy_is_trusted_and_signs`,
`an_unsigned_binding_before_a_signed_first_policy_fails_l011_and_is_never_trusted`,
`under_a_none_first_policy_d7_alone_decides_a_binding_before_it`) and
`binding_accounting.rs` (`first_policy`). Two tests there change meaning
and are renamed: a skewed add dated before `init`'s binding is now before
the namespace's genesis too, so D7 refuses it
(`a_skewed_add_dated_before_inits_genesis_is_refused_by_d7`); and opening
a namespace over a binding filed in it before any policy no longer turns
the binding into `L011` — it stays a schema fault, now D7's
(`…_and_init_does_not_launder_it`). In `closed_key.rs`
`the_genesis_holders_first_binding_in_a_second_namespace_vouched_by_the_closed_key`
dates the binding after the second namespace's policy: dated before it,
the binding is before that namespace's genesis and D7's, whatever signs
it; the test keeps its point, that the closed key vouches for nothing.
Step 4's `crossing.rs` no longer asserts the absence of `G001`.

The graph stage splits: the authority shapes (`A003`, `A005`) run over
each namespace's graph, the decision shapes (`G001`–`G006`) over the
store's graph — PRD §3.9 says the whole stage runs per namespace, but a
`supersedes` edge in history into another namespace would then be a
permanent `G001`, against ruling 75 (live claims only), and `based_on`
crosses by design. Recorded in LP-8.19 and the close-out.

Protocol: LP-6.5, LP-6.16, LP-6.28, LP-6.31 lose their marks and notes;
LP-5.19 (roles per namespace), LP-8.19 (graph stage per namespace, `A005`
row), LP-8.23, LP-8.31 (notice per namespace, report shape), LP-9.11
(export reach), LP-8.8 (the `ns:<other>` clause); Appendix B row;
Appendix C note "Authority is per namespace". No digest moves; no format
bump; no class added; no grant payload gains a field.

Stacked on #158 (issue 4).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GgRcHXmnAu43hChNxhnqxU
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant