Repository navigation
Authority per namespace: genesis, roles, grants; scopes read inside it; authority shapes per namespace (issue 5) - #159
Conversation
|
CI, standing down on one step. The The remedy the gate names is Generated by Claude Code |
12123c6 to
ec43481
Compare
4ebe365 to
73f98fb
Compare
…two notices Issue #149 (PRD issue 6, §3.3; rulings 47 and 69; AC-47 second bullet). A key is bound, closed and trusted in one namespace. `Authority::of` and `as_of` now filter key bindings by namespace like every other record; `key_close::same_key` compares the namespace, so a close ends the key in its own namespace only and `refusal`'s three rules (another principal's, closed, already open) look at the binding's namespace; D7 (`filing`) drops the store-wide branch — the genesis holder's first key in a namespace is that namespace's self-bound binding, once per namespace, under its own mandate; the signing check drops `carried_over`, judges a first policy by a key trusted in its own namespace, and the re-acceptance deadline runs from the key's earliest close there; `allowed_signers` ends a line at a close in its own namespace. The writer binds the configured key self-bound at every `init --namespace` (`carry_genesis_key` and `no_live_key` go; `--without-key` stays until issue 13), `identity add`'s self-bound test is per namespace, and signing refuses a key not live in the namespace (`ToSign.any_namespace` goes). Two repository notices, never findings (`verify::key_notices`, in the text and JSON report): a key closed in one namespace and open in another; one key bound to two principals in two namespaces. Tests (each failing before the change): `key_across_namespaces.rs`, rewritten as §3.11 says — bound in A then B, or B then A, a close in A leaves B open: the key still signs there, A's `allowed_signers` line ends and B's does not, the split is a notice, the key signs nothing in A, and a third namespace opens with the same key self-bound; a rotate in A leaves B's acceptances, review items and `allowed_signers` byte-identical (AC-47, second bullet); the within-namespace rules of 2026-10-06 stand (four tests kept); and the carried test, that no agent identity and no non-interactive session produces a key close. The writer's close in every namespace held is issue 8's and is not claimed. `genesis_key.rs`: `init` in a later namespace self-binds the holder's key there under the namespace's own mandate and signs the first policy with it; with every key closed elsewhere `init` binds the key again and `verify` notices the split (JSON `key_split`); with no usable key, `--without-key` initialises unbound and the unbound-genesis notice names the namespace. The prompt assigned these rewrites to issue 8; they could not wait, since the cross-namespace lean they asserted is what this step removes. `key_close_tests.rs`: three tests now assert the namespace reading. The pre-policy cases now bind the holder's first key in the second namespace self-bound: `pre_policy_binding.rs` (`unsigned_before` is self-bound under `hand::found`'s mandate; the signed case signs the first policy with the key bound before it, as LP-4.31 now requires; opening a namespace over a binding filed in it before any policy is refused again, since the holder's own self-bound binding would no longer be the first there — `…_and_init_is_refused_over_it`); `binding_accounting.rs` (`self_bound_by_hand`, one namespace per first binding). `trust.rs`: `init` self-binds in the second namespace and a further key there is the holder's own add (renamed from "once per store"); a hand-written self-bound binding beside `init`'s fails, "first in the namespace". `genesis_key.rs`'s forged-binding test reads the same wording. `key_across_namespaces.rs` keeps its four within-namespace tests. Protocol: LP-4.12, LP-4.13, LP-4.31, LP-4.32, LP-4.37, LP-4.38, LP-4.39 (ruling 101's wording), LP-6.31 (keys note dropped), LP-6.32 (loses its mark; the writer's close-everywhere noted for issue 8), LP-8.31 (two notices, report shape); Appendix B row; Appendix C note "Keys are per namespace". CLAUDE.md updated. No class added; no digest moves (a binding's hashed fields are unchanged and `namespace` was among them). Stacked on #159 (issue 5). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GgRcHXmnAu43hChNxhnqxU
…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
Files the signed bindings the contract-surface gate demands for this step's 18 exposed Rust surface changes over 8 files: a new declaration for author/grant_ops.rs with its verdict_knowledge written, and seven amended — four of them (author/authority_ops.rs, authority/view.rs, authority/structure.rs, store.rs) with their verdict rewritten where per-namespace authority had made the old sentence false or silent. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GgRcHXmnAu43hChNxhnqxU
The declaration over ledger-cli/tests/common/hand.rs now says the bound helper is test-only — the suites' hand-writing harness, no consumer outside ledger-cli/tests/ — and what hand::found founds. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GgRcHXmnAu43hChNxhnqxU
ec43481 to
deb88ef
Compare
73f98fb to
4ee026b
Compare
…two notices Issue #149 (PRD issue 6, §3.3; rulings 47 and 69; AC-47 second bullet). A key is bound, closed and trusted in one namespace. `Authority::of` and `as_of` now filter key bindings by namespace like every other record; `key_close::same_key` compares the namespace, so a close ends the key in its own namespace only and `refusal`'s three rules (another principal's, closed, already open) look at the binding's namespace; D7 (`filing`) drops the store-wide branch — the genesis holder's first key in a namespace is that namespace's self-bound binding, once per namespace, under its own mandate; the signing check drops `carried_over`, judges a first policy by a key trusted in its own namespace, and the re-acceptance deadline runs from the key's earliest close there; `allowed_signers` ends a line at a close in its own namespace. The writer binds the configured key self-bound at every `init --namespace` (`carry_genesis_key` and `no_live_key` go; `--without-key` stays until issue 13), `identity add`'s self-bound test is per namespace, and signing refuses a key not live in the namespace (`ToSign.any_namespace` goes). Two repository notices, never findings (`verify::key_notices`, in the text and JSON report): a key closed in one namespace and open in another; one key bound to two principals in two namespaces. Tests (each failing before the change): `key_across_namespaces.rs`, rewritten as §3.11 says — bound in A then B, or B then A, a close in A leaves B open: the key still signs there, A's `allowed_signers` line ends and B's does not, the split is a notice, the key signs nothing in A, and a third namespace opens with the same key self-bound; a rotate in A leaves B's acceptances, review items and `allowed_signers` byte-identical (AC-47, second bullet); the within-namespace rules of 2026-10-06 stand (four tests kept); and the carried test, that no agent identity and no non-interactive session produces a key close. The writer's close in every namespace held is issue 8's and is not claimed. `genesis_key.rs`: `init` in a later namespace self-binds the holder's key there under the namespace's own mandate and signs the first policy with it; with every key closed elsewhere `init` binds the key again and `verify` notices the split (JSON `key_split`); with no usable key, `--without-key` initialises unbound and the unbound-genesis notice names the namespace. The prompt assigned these rewrites to issue 8; they could not wait, since the cross-namespace lean they asserted is what this step removes. `key_close_tests.rs`: three tests now assert the namespace reading. The pre-policy cases now bind the holder's first key in the second namespace self-bound: `pre_policy_binding.rs` (`unsigned_before` is self-bound under `hand::found`'s mandate; the signed case signs the first policy with the key bound before it, as LP-4.31 now requires; opening a namespace over a binding filed in it before any policy is refused again, since the holder's own self-bound binding would no longer be the first there — `…_and_init_is_refused_over_it`); `binding_accounting.rs` (`self_bound_by_hand`, one namespace per first binding). `trust.rs`: `init` self-binds in the second namespace and a further key there is the holder's own add (renamed from "once per store"); a hand-written self-bound binding beside `init`'s fails, "first in the namespace". `genesis_key.rs`'s forged-binding test reads the same wording. `key_across_namespaces.rs` keeps its four within-namespace tests. Protocol: LP-4.12, LP-4.13, LP-4.31, LP-4.32, LP-4.37, LP-4.38, LP-4.39 (ruling 101's wording), LP-6.31 (keys note dropped), LP-6.32 (loses its mark; the writer's close-everywhere noted for issue 8), LP-8.31 (two notices, report shape); Appendix B row; Appendix C note "Keys are per namespace". CLAUDE.md updated. No class added; no digest moves (a binding's hashed fields are unchanged and `namespace` was among them). Stacked on #159 (issue 5). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GgRcHXmnAu43hChNxhnqxU
Closes #148 — PRD
ledger/prd/namespace-independence-prd.md§6 issue 5 (§3.2, §3.9; rulings 47, 67, 68 and 50 per namespace; AC-47 first bullet, AC-48, AC-68).Stacked on #158 (issue 4). Review the last commit alone,
4ebe3658.What lands
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 (LP-6.31).
Authority::of(store, ns)/Authority::as_of(store, landing, pos, ns)read one namespace's records; every role check, genesis lookup and policy verdict goes through them (the verbs'authorized,A006over history, ruling 50's genesis-role rules, the reference rules, the unbound-genesis notice — now one per namespace).*andns:<own>are the whole of it (ruling 67); a grant scopedns:<other>than its directory is aSCHEMAfault.A003/A005count one trust root per namespace: the authority shapes run over each namespace's graph; the export selection keeps a namespace's own authority only (the*reach of LP-9.11 goes). Roles are per namespace (Store::role_in).init --namespaceopens every namespace with its own genesis grant, root and accept roles and first policy;--external-refis required every time;join_genesisgoes.grant new --namespacenames where a grant is filed (inferred from ans:scope, else the store's one namespace). The grant verbs move toauthor/grant_ops.rs.Three deviations, stated
A006, so the writer's own gate would refuse opening any second namespace between issues 5 and 8. Issue 8 keeps the key-close-in-every-namespace half (AC-47 third bullet).supersedesedge in history into another namespace would be a permanentG001, against ruling 75 (live claims only), andbased_oncrosses by design. SoA003/A005run per namespace,G001–G006over the store. LP-8.19 says so.initstill carries the holder's key into a later namespace).Authoritydocuments it; 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
New
ledger-cli/tests/authority_namespaces.rs(each failing before): two namespaces with their own genesis, roles and policy verify with noA003/A005; a laterinitneeds its own mandate, and a namespace is opened once; a*grant in A authorises in A alone, and with two namespacesgrant newneeds--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, failsA006on 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 (authority.rskeeps the first-namespace one).Rewritten, and why:
graph/export_tests.rs: the*genesis no longer reaches every namespace's export; another namespace's*grant and same-id role stay out.signing/check_tests.rs,verify/order_grid.rs: filedfixture.ledgerentities in ahafeok.ledgerchange-set; now one namespace.ledger-cli/tests/authority.rsgrantor test:ns:fixture.otheras "another scope" would now be a grant made infixture.other's authority, where the role is not declared; asserts that, keeps*andset:.hand::foundfounds a namespace by hand with its genesis ten seconds older than its first policy;pre_policy_binding.rsandbinding_accounting.rsuse it. Two tests change meaning and are renamed: a skewed add beforeinit's binding is now before the genesis too, so D7 refuses it; opening a namespace over a binding filed before any policy no longer turns it intoL011— it stays a schema fault, D7's.closed_key.rs: the binding vouched by the closed key is dated after the second namespace's policy (before it, it is D7's whatever signs it); the point — the closed key vouches for nothing — stands.crossing.rsno longer asserts the absence ofG001.Protocol
LP-6.5, LP-6.16, LP-6.28, LP-6.31 lose their marks and notes (LP-6.31 keeps a note on keys until issue 6); LP-5.19, LP-8.19 (and the
A005row), LP-8.23, LP-8.31 (report shape:genesis_unboundis now one entry per namespace), LP-9.11, LP-8.8; Appendix B row; Appendix C note "Authority is per namespace". CLAUDE.md's authority paragraph updated. No digest moves; no format bump; no class added; no grant payload gains a field.Checks
cargo build,cargo t,cargo clippy --workspace -- -D warnings -D clippy::unwrap_used, the quality scripts, andledger verify --exporton this repository (same output as onmain).🤖 Generated with Claude Code
https://claude.ai/code/session_01GgRcHXmnAu43hChNxhnqxU
Generated by Claude Code