From 1c9a3ddefb04b7ff08a1d9f39f52dd94cb58c0b4 Mon Sep 17 00:00:00 2001 From: fred Date: Wed, 26 Aug 2026 18:46:14 -0500 Subject: [PATCH 01/15] docs: onboarding wizard contract (S2 contract 3) --- docs/requirements/onboarding-wizard.md | 189 +++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 docs/requirements/onboarding-wizard.md diff --git a/docs/requirements/onboarding-wizard.md b/docs/requirements/onboarding-wizard.md new file mode 100644 index 00000000..615cc8f5 --- /dev/null +++ b/docs/requirements/onboarding-wizard.md @@ -0,0 +1,189 @@ +# Onboarding Wizard Contract (D4) + +Status: DRAFT — awaiting ratification (webui-audit S2, contract 3 of 9). +Authority: PRD D4 (Part I §6), D11 v1 slice (Part I §9), D8 architecture +gate (Part I §8), the identity account-lifecycle contract +(`docs/requirements/identity-lifecycle.md`, §§2–4), and the RBAC grant +model contract (`docs/requirements/rbac-grant-model.md`, §4.3). This +document turns D4 into a concrete contract: what the wizard is +architecturally, its v1 step model, mode branching, re-run/idempotency +semantics, and what it seeds under whose authority. + +Scope: the Gateway-backed product onboarding wizard. Out of scope: the +host-local install wizard (`mosaic wizard`, which drives host install and +gateway bootstrap and is not this artifact — audit REPORT.md layer 3); +Enterprise mode conversion (contract 6); custody enforcement semantics +(contract 7); the deferred-beyond-v1 steps themselves (connectors, comms +integrations, voice-matching, M365 — D11 defers them; they bind here only +through the extensibility rule §2.4). + +## 1. Placement and architecture + +1. The wizard is a **product surface over the Gateway command API** — a + web UI flow (and equivalently scriptable command sequence) that + composes existing audited Gateway commands. It is subject to the D8 + hard rule like every other webUI surface: no wizard operation reaches + the database or filesystem directly, and no wizard-only privileged + write path exists. +2. **The wizard introduces no new mutation surface.** Every state change + it performs is an existing command with its own contract: user and + epoch writes under the identity contract §3, hierarchy writes under + contract 1 §5, grant writes under contract 2 §4, settings writes under + their owning command family. The one exception is the bootstrap epoch + transition, where the wizard drives the bootstrap writer defined by + identity §3 — that writer's constraints (§3.1–§3.6) bind unchanged; + this contract adds nothing to them. +3. **Wizard state derives from canonical state.** Each step renders the + system's current configuration (read through the same commands) and + applies deltas; the wizard does not keep an answer file whose contents + can drift from reality. The wizard MAY persist run bookkeeping + (started/completed timestamps, mode) and MUST persist the + explicit-choice records other contracts mandate (registration mode, + identity §2.2; per-provider JIT enablement, identity §4.1) — those are + settings writes, not private wizard state. +4. If a wizard-completion marker is stored, it is presentational only + (which entry screen to show). No authorization or gating decision may + read it: gating state lives where its owning contract puts it + (bootstrap epoch in `bootstrap_state`, registration mode in settings). + +## 2. Modes and extensibility + +1. The wizard **differs by mode** (D4): Standalone and Enterprise share + one skeleton; Enterprise makes personal data optional and moves focus + to business structure, RBAC, and external systems. +2. **v1 ships the Standalone flow only** (D11). The mode branch point and + skeleton MUST still exist in v1 — mode is a property of the flow, not + a fork of it — but no Enterprise-only step ships, and mode conversion + is contract 6. +3. Mode is chosen at bootstrap and recorded as an explicit operator + choice. Changing it later is conversion (contract 6), not a wizard + re-run. +4. **Extensibility:** new wizards attach as tabs (D4). Attaching a wizard + tab is a registration of additional steps against the same skeleton, + inheriting this contract's rules (§1 architecture, §3 step contract, + §4 re-runnability). A tab cannot opt out of them. + +## 3. v1 step model (Standalone) + +The v1 wizard consists of exactly these steps, in order, each backed by +the named authority: + +1. **System and company name.** System name is a settings write. Company + name feeds step 4's company creation. +2. **Component choices.** Mosaic Comms/Matrix vs external; Mosaic + SSO/Authentik vs external; Mosaic DB/PostgreSQL vs external; vector + DB (D4). v1 records the choices as settings and configures what is + installable in-product; component provisioning beyond that is host + tooling, out of scope here. +3. **Initial user.** Email/password or SSO, per identity §3: the + first-admin transition is atomic, one per bootstrap epoch, and the SSO + variant runs inside the bootstrap transaction (identity §3.6). The + wizard also presents registration mode (identity §2.2) and, when SSO + is configured, per-provider JIT with its allowlist warning (identity + §4.1, §4.3) — each recorded as an explicit choice, none silently + defaulted. +4. **Initial hierarchy and seeding.** Creates the first company (naming + the initial `owner` grant in the same audited operation, contract 2 + §4.3), an initial estate, an initial project, and an initial workspace + with seeded example data (D4, D11). All through hierarchy commands + (contract 1 §5.1); seeded examples are ordinary workspace content + created by ordinary commands, attributable in audit to the wizard run + actor. +5. **Minimal agent enrollment.** One harness, API-key login, agent name + and persona (D11). Enrollment specifics belong to the agent-enrollment + command family; this contract binds only that the step exists, is + skippable, and uses that family. + +Steps deferred beyond v1 (user onboarding profile, connectors, comms, +voice-matching) appear in `docs/ROADMAP.md` per D11. Note for the profile +step when it lands: sensitive profile categories are custody-bound to the +user's own brain (D14, contract 7); the v1 wizard collects none of them, +so v1 ships no custody surface. + +## 4. Re-runnability and idempotency (D4 "no lock-in") + +1. **Re-run is a first-class operation.** After completion, running the + wizard again re-opens every step against current state (§1.3) for + reconfiguration. Nothing about completion locks the wizard. +2. **The bootstrap epoch does not re-open** (identity §3.4). On re-run, + step 3 shows the existing admin/registration/JIT configuration and + allows changing the mutable parts through their normal commands; + "setup already completed" is a stable state, and factory reset — a + future, explicitly destructive operation — is the only path to a new + epoch. +3. **Idempotent seeding.** Step 4 on re-run MUST NOT duplicate: the + initial company/estate/project/workspace are created at most once per + bootstrap epoch, keyed by recorded seed identity (the created node + ids), not by name matching. A re-run offers to create _additional_ + hierarchy nodes (PRD: users can create N companies, N estates, N + projects) but never re-creates or resets the originals, and never + touches user content added since. Example-data seeding is likewise + at-most-once per epoch and is skippable. +4. **Interrupted runs strand nothing.** The wizard is resumable at step + granularity: each step's apply either commits through its command (and + is then visible as current state to the resumed run) or leaves nothing + (identity §3.5 for the bootstrap step). There is no wizard-level + transaction spanning steps and no compensating rollback of completed + steps. + +## 5. Seeding authority (resolves contract 2 review NEW-1) + +1. During the bootstrap epoch, the wizard's authority to create the first + company is the bootstrap writer's (identity §3): there is no earlier + user to authorize it. The first company creation names the first admin + (or the operator-designated initial user) as initial `owner` (contract + 2 §4.3). +2. **Post-bootstrap top-level company creation** — the "N companies" flow + — is decided by the ruling below: any ACTIVE platform user MAY create + a top-level company and MUST name an initial `owner` grant in the same + audited operation (contract 2 §4.3); the creator naming themselves is + the default. Until that ruling, deny-by-default holds (contract 2 + §3.1): no implicit creation authority exists. +3. Child-node creation inside the wizard (estate, project, workspace + under the seeded company) follows contract 2 §4.3 unchanged: parent + `owner` authority, no automatic grant needed. + +## 6. Verification requirements + +Binding on the implementing PRs: + +1. **No-new-writers witness:** the wizard modules appear in no + class-table writer allowlist (contract 1 §6.3b) and hold no direct DB + access; a static assertion that wizard code reaches state only through + Gateway command clients. +2. **Re-run witnesses:** complete the wizard, then re-run it to + completion making no changes — assert zero new hierarchy nodes, zero + new users, zero duplicated example content, and the bootstrap epoch + still completed (identity §8's epoch witnesses cover the transition + itself). Then a re-run that adds a second company succeeds and leaves + the first company and its content untouched (row-count and content + assertions). +3. **Seed-audit witness:** every node, grant, and example item created by + a wizard run has its command audit event (contract 1 §5.2, contract 2 + §4.4) attributing the wizard-run actor; the first-company event names + the initial owner grant in the same audited operation. +4. **Explicit-choice witnesses:** after a completed run, registration + mode, per-provider JIT flags, mode (Standalone/Enterprise), and + component choices each read back as recorded values; the identity + contract's defaults (registration `closed`, JIT off) hold for any + choice the run did not explicitly make. +5. **Gating-independence witness:** with any wizard-completion marker + deleted (§1.4), authorization and registration gating behave + identically — proving no gate reads wizard state. +6. **Interrupted-run witness:** kill a run between steps; assert no + stranded partial outcome (identity §8 covers the bootstrap step; for + step 4, either the node set committed with its audit events or is + absent) and that a resumed run completes against the committed state. + +## Ruling request + +Ratify sections 1–6 as written, with one decision embedded: + +- Decision (§5.2): post-bootstrap, any ACTIVE platform user may create a + top-level company, naming an initial `owner` grant (default: self) in + the same audited operation. Basis: PRD Part I §4 "Users can create N + companies, N estates, N projects" read as end-user capability, not + admin-only. Alternative if rejected: top-level creation stays + deny-by-default and becomes a platform-admin-granted capability in a + later contract — nothing in this contract or contract 2 breaks either + way, because deny-by-default is the resting state (contract 2 §3.1). -- 2.54.0 From 666e3dbf2026cefeb53fec2c09e04859b872e6a2 Mon Sep 17 00:00:00 2001 From: fred Date: Wed, 26 Aug 2026 19:13:36 -0500 Subject: [PATCH 02/15] =?UTF-8?q?docs:=20onboarding=20wizard=20revision=20?= =?UTF-8?q?2=20=E2=80=94=20first-company=20authority,=20mode=20as=20input,?= =?UTF-8?q?=20choice/witness=20repairs=20(sol=20F1-F9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/requirements/onboarding-wizard.md | 293 +++++++++++++++++-------- 1 file changed, 201 insertions(+), 92 deletions(-) diff --git a/docs/requirements/onboarding-wizard.md b/docs/requirements/onboarding-wizard.md index 615cc8f5..7af5e097 100644 --- a/docs/requirements/onboarding-wizard.md +++ b/docs/requirements/onboarding-wizard.md @@ -9,11 +9,28 @@ document turns D4 into a concrete contract: what the wizard is architecturally, its v1 step model, mode branching, re-run/idempotency semantics, and what it seeds under whose authority. +Revision 2 (sol review F1–F9): first-company authority repaired — the +bootstrap writer creates only the admin and closes the epoch; step 4 +runs as the new admin through the ordinary top-level company command +(F1). Mode is an install-time input the wizard reads, never writes +(F2). The initial user's `name` restored from the PRD step list, and +enrollment skippability flagged as a §12.1 drafting addition (F3). +Explicit-choice obligations restated as wizard-run presentation rules +that leave identity's defaults untouched, with a submitted-vs-canonical +witness (F4). The D8 witness bound to contract 5's mapping and +authorization-parity probes (F5). Witness coverage extended to the mode +branch, reconfigurability, canonical-state drift, second-company actor +semantics, exact seed sets, tab inheritance, and enrollment (F6). Step +4's at-most-once semantics grounded in same-transaction audit +reconciliation with per-mutation failure injection (F7). The custody +note reduced to a cross-reference (F8). Creation eligibility defined in +identity terms with an actor matrix (F9). + Scope: the Gateway-backed product onboarding wizard. Out of scope: the host-local install wizard (`mosaic wizard`, which drives host install and gateway bootstrap and is not this artifact — audit REPORT.md layer 3); -Enterprise mode conversion (contract 6); custody enforcement semantics -(contract 7); the deferred-beyond-v1 steps themselves (connectors, comms +Enterprise mode conversion (contract 6); custody semantics (contract 7); +the deferred-beyond-v1 steps themselves (connectors, comms integrations, voice-matching, M365 — D11 defers them; they bind here only through the extensibility rule §2.4). @@ -21,10 +38,13 @@ through the extensibility rule §2.4). 1. The wizard is a **product surface over the Gateway command API** — a web UI flow (and equivalently scriptable command sequence) that - composes existing audited Gateway commands. It is subject to the D8 - hard rule like every other webUI surface: no wizard operation reaches - the database or filesystem directly, and no wizard-only privileged - write path exists. + composes Gateway commands. It is subject to the D8 hard rule like + every other webUI surface: no wizard operation reaches the database + or filesystem directly, and no wizard-only privileged write path + exists. Concretely, the wizard is contract 5's rank-6 family, + composing the rank-1 hierarchy commands and the rank-4 enrollment + command (contract 5 §3.1): those families are built first, and every + wizard operation is a row in contract 5's mapping. 2. **The wizard introduces no new mutation surface.** Every state change it performs is an existing command with its own contract: user and epoch writes under the identity contract §3, hierarchy writes under @@ -37,14 +57,14 @@ through the extensibility rule §2.4). system's current configuration (read through the same commands) and applies deltas; the wizard does not keep an answer file whose contents can drift from reality. The wizard MAY persist run bookkeeping - (started/completed timestamps, mode) and MUST persist the - explicit-choice records other contracts mandate (registration mode, - identity §2.2; per-provider JIT enablement, identity §4.1) — those are - settings writes, not private wizard state. + (started/completed timestamps); bookkeeping is a cache, never the + source of truth for any decision (§4.3 defines the reconciliation + rule that makes this concrete for seeding). 4. If a wizard-completion marker is stored, it is presentational only (which entry screen to show). No authorization or gating decision may read it: gating state lives where its owning contract puts it - (bootstrap epoch in `bootstrap_state`, registration mode in settings). + (bootstrap epoch in `bootstrap_state`, registration mode in settings, + mode in the contract-6 mode record). ## 2. Modes and extensibility @@ -55,13 +75,21 @@ through the extensibility rule §2.4). skeleton MUST still exist in v1 — mode is a property of the flow, not a fork of it — but no Enterprise-only step ships, and mode conversion is contract 6. -3. Mode is chosen at bootstrap and recorded as an explicit operator - choice. Changing it later is conversion (contract 6), not a wizard - re-run. +3. **Mode is an install-time input, not a wizard output.** The + deployment mode is recorded canonically at bootstrap and owned by + contract 6 (§2 there): the wizard reads the recorded mode and + branches on it; it never writes mode and never derives it from + feature state. Changing mode later is conversion (contract 6), not a + wizard re-run. In v1 a recorded mode of `enterprise` is refused at + bootstrap (contract 6 §5), so the wizard's Enterprise branch is + unreachable in v1; a wizard invoked against an unsupported or + unreadable mode record produces a single bounded refusal (contract + 5 §4.2 precondition class), never a partial flow. 4. **Extensibility:** new wizards attach as tabs (D4). Attaching a wizard tab is a registration of additional steps against the same skeleton, inheriting this contract's rules (§1 architecture, §3 step contract, - §4 re-runnability). A tab cannot opt out of them. + §4 re-runnability). A tab cannot opt out of them: tab steps are + subject to the same §6 witnesses as the built-in steps. ## 3. v1 step model (Standalone) @@ -75,30 +103,46 @@ the named authority: DB (D4). v1 records the choices as settings and configures what is installable in-product; component provisioning beyond that is host tooling, out of scope here. -3. **Initial user.** Email/password or SSO, per identity §3: the - first-admin transition is atomic, one per bootstrap epoch, and the SSO - variant runs inside the bootstrap transaction (identity §3.6). The - wizard also presents registration mode (identity §2.2) and, when SSO - is configured, per-provider JIT with its allowlist warning (identity - §4.1, §4.3) — each recorded as an explicit choice, none silently - defaulted. -4. **Initial hierarchy and seeding.** Creates the first company (naming - the initial `owner` grant in the same audited operation, contract 2 - §4.3), an initial estate, an initial project, and an initial workspace - with seeded example data (D4, D11). All through hierarchy commands - (contract 1 §5.1); seeded examples are ordinary workspace content - created by ordinary commands, attributable in audit to the wizard run - actor. +3. **Initial user.** Email, password, and display name, or SSO (PRD §6 + step list: "email/password/name/SSO"), per identity §3: the + first-admin transition is atomic, one per bootstrap epoch, and the + SSO variant runs inside the bootstrap transaction (identity §3.6). + The submitted display name lands on the canonical account record + (identity §2); no field of the PRD's step list is dropped. The + wizard also presents the applicable mandatory choices — registration + mode (identity §2.2) and, for each configured SSO provider, that + provider's JIT enablement with its allowlist warning (identity §4.1, + §4.3). The wizard MUST obtain an explicit submission for each + applicable mandatory choice and MUST refuse step completion while + one is unsubmitted. This is a presentation-and-submission obligation + on wizard runs only: identity's own defaults (registration `closed`, + JIT off) continue to govern everything a wizard run never touches, + and this contract creates no new stored-record obligation beyond + those identity itself defines. +4. **Initial hierarchy and seeding.** Runs strictly after step 3's epoch + transition, authenticated as the newly created admin. The bootstrap + writer (identity §3) creates only the first account and closes the + epoch; it has no hierarchy authority and creates no hierarchy node. + The first company is created by the ordinary top-level company + command under §5.2's eligibility policy, with the new admin as actor, + naming the admin as initial `owner` in the same audited operation + (contract 2 §4.3). The initial estate, initial project, and initial + workspace with seeded example data (D4, D11) follow through hierarchy + commands (contract 1 §5.1) under the admin's `owner` authority + (contract 2 §4.3). Seeded examples are ordinary workspace content + created by ordinary commands, attributable in audit to the acting + admin with the wizard run's correlation ids (contract 5 §4.3). 5. **Minimal agent enrollment.** One harness, API-key login, agent name - and persona (D11). Enrollment specifics belong to the agent-enrollment - command family; this contract binds only that the step exists, is - skippable, and uses that family. + and persona (D11). Enrollment specifics belong to the rank-4 + agent-enrollment command family; this contract binds only that the + step exists, uses that family, and is skippable. Skippability is a + drafting addition under PRD §12.1 (the PRD step list does not mark + the step optional); it is ratified with this contract. Steps deferred beyond v1 (user onboarding profile, connectors, comms, -voice-matching) appear in `docs/ROADMAP.md` per D11. Note for the profile -step when it lands: sensitive profile categories are custody-bound to the -user's own brain (D14, contract 7); the v1 wizard collects none of them, -so v1 ships no custody surface. +voice-matching) appear in `docs/ROADMAP.md` per D11. The deferred +profile step's custody semantics are contract 7's (D14); the v1 wizard +collects no sensitive category, so v1 ships no custody surface. ## 4. Re-runnability and idempotency (D4 "no lock-in") @@ -111,34 +155,53 @@ so v1 ships no custody surface. "setup already completed" is a stable state, and factory reset — a future, explicitly destructive operation — is the only path to a new epoch. -3. **Idempotent seeding.** Step 4 on re-run MUST NOT duplicate: the - initial company/estate/project/workspace are created at most once per - bootstrap epoch, keyed by recorded seed identity (the created node - ids), not by name matching. A re-run offers to create _additional_ - hierarchy nodes (PRD: users can create N companies, N estates, N - projects) but never re-creates or resets the originals, and never - touches user content added since. Example-data seeding is likewise - at-most-once per epoch and is skippable. -4. **Interrupted runs strand nothing.** The wizard is resumable at step - granularity: each step's apply either commits through its command (and - is then visible as current state to the resumed run) or leaves nothing - (identity §3.5 for the bootstrap step). There is no wizard-level - transaction spanning steps and no compensating rollback of completed - steps. +3. **Idempotent seeding, reconciled from canonical state.** Step 4 on + re-run MUST NOT duplicate: the initial company/estate/project/ + workspace and the example data are created at most once per bootstrap + epoch. The at-most-once key is a deterministic per-mutation + correlation id derived from the bootstrap epoch and the seed role + (e.g. epoch id + "seed-company"), submitted as the command's + correlation id (contract 5 §4.3) and therefore recorded in the + mutation's audit event in the same transaction as the mutation + itself (contract 1 §5.2). Before applying any seed mutation, the + wizard reconciles against the audit/command log for its epoch's seed + correlation ids and skips every mutation whose event exists. Wizard + bookkeeping of seed node ids is a cache of this record, never the + source of truth: losing it cannot cause duplication. A re-run offers + to create _additional_ hierarchy nodes (PRD: users can create N + companies, N estates, N projects) but never re-creates or resets the + originals, and never touches user content added since. Example-data + seeding is likewise at-most-once per epoch and is skippable. +4. **Interrupted runs strand nothing.** The wizard is resumable at the + granularity of a single command: each mutation either commits through + its command with its same-transaction audit event, or leaves nothing + (identity §3.5 for the bootstrap step). Step 4 is a sequence of + individually atomic commands, not one transaction: an interruption + between them leaves a prefix of committed seed nodes, and the §4.3 + reconciliation rule makes the resumed run complete exactly the + remaining suffix, without duplication and without compensating + rollback of completed commands. There is no wizard-level transaction + spanning steps. ## 5. Seeding authority (resolves contract 2 review NEW-1) -1. During the bootstrap epoch, the wizard's authority to create the first - company is the bootstrap writer's (identity §3): there is no earlier - user to authorize it. The first company creation names the first admin - (or the operator-designated initial user) as initial `owner` (contract - 2 §4.3). +1. During the bootstrap epoch, the bootstrap writer's authority (identity + §3) covers exactly what identity §3 defines: creating the first + account and closing the epoch. It creates no hierarchy node. The + first company is created post-epoch by the first admin through the + ordinary top-level company command (§5.2), naming that admin as + initial `owner` (contract 2 §4.3). No operator-designated third + party, service actor, or wizard-privileged writer exists in this + flow. 2. **Post-bootstrap top-level company creation** — the "N companies" flow - — is decided by the ruling below: any ACTIVE platform user MAY create - a top-level company and MUST name an initial `owner` grant in the same - audited operation (contract 2 §4.3); the creator naming themselves is - the default. Until that ruling, deny-by-default holds (contract 2 - §3.1): no implicit creation authority exists. + — is decided by the ruling below: any **eligible platform user** MAY + create a top-level company and MUST name an initial `owner` grant in + the same audited operation (contract 2 §4.3); the creator naming + themselves is the default. Eligible means, in identity-contract + terms: an authenticated account (identity §2) that is not banned and + not deactivated (identity §5). No further role or grant is required. + Until that ruling, deny-by-default holds (contract 2 §3.1): no + implicit creation authority exists. 3. Child-node creation inside the wizard (estate, project, workspace under the seeded company) follows contract 2 §4.3 unchanged: parent `owner` authority, no automatic grant needed. @@ -147,43 +210,89 @@ so v1 ships no custody surface. Binding on the implementing PRs: -1. **No-new-writers witness:** the wizard modules appear in no - class-table writer allowlist (contract 1 §6.3b) and hold no direct DB - access; a static assertion that wizard code reaches state only through - Gateway command clients. -2. **Re-run witnesses:** complete the wizard, then re-run it to +1. **D8 mapping witness:** every Gateway call the wizard makes resolves + to a contract 5 mapping row (asserted by contract 5 §6.1's inventory + scoped to the wizard surface); wizard modules appear in no + class-table writer allowlist (contract 1 §6.3b) and hold no direct + database or filesystem access (static assertion, plus a runtime + probe that a wizard-context filesystem/database access attempt is + refused). **Authorization parity:** for each mutating wizard + operation, the same actor invoking the underlying command directly + receives the same authorization outcome as through the wizard — + probed for at least one allowed and one refused actor per operation. +2. **Mode witnesses:** the branch point exists (the flow resolves its + step set from the canonical mode record, read through contract 6's + read surface); v1 with mode `standalone` yields the §3 step set; a + simulated unsupported or unreadable mode record yields one bounded + precondition refusal (§2.3) and no partial flow; wizard sources + contain no mode write and no feature-state mode derivation. +3. **Re-run witnesses:** complete the wizard, then re-run it to completion making no changes — assert zero new hierarchy nodes, zero - new users, zero duplicated example content, and the bootstrap epoch - still completed (identity §8's epoch witnesses cover the transition - itself). Then a re-run that adds a second company succeeds and leaves - the first company and its content untouched (row-count and content - assertions). -3. **Seed-audit witness:** every node, grant, and example item created by - a wizard run has its command audit event (contract 1 §5.2, contract 2 - §4.4) attributing the wizard-run actor; the first-company event names - the initial owner grant in the same audited operation. -4. **Explicit-choice witnesses:** after a completed run, registration - mode, per-provider JIT flags, mode (Standalone/Enterprise), and - component choices each read back as recorded values; the identity - contract's defaults (registration `closed`, JIT off) hold for any - choice the run did not explicitly make. -5. **Gating-independence witness:** with any wizard-completion marker + new users, zero new grants, zero duplicated example content, and the + bootstrap epoch still completed (identity §8's epoch witnesses cover + the transition itself). **Reconfigurability:** a re-run changes a + mutable value (registration mode) and the canonical setting reflects + it. **Canonical-state drift:** mutate state outside the wizard (a + rename through the hierarchy command), then re-run — the wizard + renders the current canonical value, not a stored answer. + **Second company:** a re-run creating a second company succeeds with + the running user as actor, names the creator as `owner` by default + (grant row asserted), and leaves the first company's exact node and + content set untouched (row-set and content-digest assertions). +4. **Seed-set witness:** after a first completed run, the created seed + set is exactly the declared set — one company, one estate, one + project, one workspace, the example content (fixed digest), the one + §4.3 `owner` grant, and no other node or grant attributable to the + run's correlation ids. Every seeded node, grant, and example item + has its command audit event (contract 1 §5.2, contract 2 §4.4) + carrying the deterministic seed correlation id (§4.3) and the acting + admin; the first-company event names the initial owner grant in the + same audited operation. +5. **Explicit-choice witness:** a run submitting explicit values + completes, and each canonical value equals the value the run + submitted (asserted against the captured submissions, not by reading + the canonical store twice); a run with an applicable mandatory + choice unsubmitted is refused at step 3 (§3.3); identity's defaults + are asserted for a provider the run never touched (registration + `closed`, JIT off — identity §2.2, §4.1). +6. **Gating-independence witness:** with any wizard-completion marker deleted (§1.4), authorization and registration gating behave identically — proving no gate reads wizard state. -6. **Interrupted-run witness:** kill a run between steps; assert no - stranded partial outcome (identity §8 covers the bootstrap step; for - step 4, either the node set committed with its audit events or is - absent) and that a resumed run completes against the committed state. +7. **Interrupted-run witness:** inject failure after each internal + mutation of a run — after the bootstrap transaction, after each + individual seed command (company, estate, project, workspace, each + example item), and after the bookkeeping cache write — and for each + injection point assert: no stranded partial outcome (every committed + command has its same-transaction audit event; nothing else exists), + and a resumed run completes exactly the remaining mutations with no + duplication (identity §8 covers the bootstrap step; §4.3–§4.4 define + the reconciliation the witness exercises). Deleting the bookkeeping + cache between failure and resume MUST NOT change the outcome. +8. **Actor-matrix witness (§5.2):** post-bootstrap top-level company + creation succeeds for an ordinary authenticated non-admin user + (positive), and is refused for an unauthenticated caller, a banned + account, and a deactivated account (negatives), with contract 2's + error classes. +9. **Tab-inheritance witness:** a registered test tab's steps are + subject to the same assertions — its modules fail the §6.1 static + assertion if they access the database directly, and its mutations + appear in the §6.1 mapping inventory — proving §2.4 is enforced by + machinery, not convention. +10. **Enrollment witness:** the enrollment step exists, invokes only the + rank-4 family, and a run that skips it completes with zero + enrollment-family mutations. ## Ruling request Ratify sections 1–6 as written, with one decision embedded: -- Decision (§5.2): post-bootstrap, any ACTIVE platform user may create a - top-level company, naming an initial `owner` grant (default: self) in - the same audited operation. Basis: PRD Part I §4 "Users can create N - companies, N estates, N projects" read as end-user capability, not - admin-only. Alternative if rejected: top-level creation stays - deny-by-default and becomes a platform-admin-granted capability in a - later contract — nothing in this contract or contract 2 breaks either - way, because deny-by-default is the resting state (contract 2 §3.1). +- Decision (§5.2): post-bootstrap, any eligible platform user + (authenticated, not banned, not deactivated — identity §2/§5) may + create a top-level company, naming an initial `owner` grant (default: + self) in the same audited operation. Basis: PRD Part I §4 "Users can + create N companies, N estates, N projects" read as end-user + capability, not admin-only. Alternative if rejected: top-level + creation stays deny-by-default and becomes a platform-admin-granted + capability in a later contract — nothing in this contract or contract + 2 breaks either way, because deny-by-default is the resting state + (contract 2 §3.1). -- 2.54.0 From eb48d72f67b3f6989d2ea88bd14a2f139a9065c1 Mon Sep 17 00:00:00 2001 From: fred Date: Wed, 26 Aug 2026 19:55:45 -0500 Subject: [PATCH 03/15] =?UTF-8?q?docs(wizard):=20revision=203=20=E2=80=94?= =?UTF-8?q?=20client-side=20composition,=20collect-first=20settings,=20ide?= =?UTF-8?q?mpotency=20fence,=20bound=20name=20sources=20(sol=20r2=20residu?= =?UTF-8?q?als=20+=20N1/N2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/requirements/onboarding-wizard.md | 331 +++++++++++++++++-------- 1 file changed, 231 insertions(+), 100 deletions(-) diff --git a/docs/requirements/onboarding-wizard.md b/docs/requirements/onboarding-wizard.md index 7af5e097..689ced06 100644 --- a/docs/requirements/onboarding-wizard.md +++ b/docs/requirements/onboarding-wizard.md @@ -26,6 +26,28 @@ reconciliation with per-mutation failure injection (F7). The custody note reduced to a cross-reference (F8). Creation eligibility defined in identity terms with an actor matrix (F9). +Revision 3 (sol re-review: residual F2/F3/F5/F6/F7/F9, new N1/N2): the +wizard is now explicitly a client-side composition with no server-side +orchestrator, spanning six named command families, every operation an +ordinary outbound Gateway call inside contract 5 §6.1's inventory scope +(F5). First runs collect steps 1–2 without writing; the collected +settings are applied immediately after the epoch transition under the +first admin's platform authority, so no pre-admin write authority +exists or is invented (N1). Pre-epoch mode branching reads the mode +value from the unauthenticated bootstrap-status response, a disclosed +one-field addition; post-epoch runs use contract 6's authenticated read +(F2). The SSO display-name source is bound to identity §5's mapped name +claim, with a canonical-name witness (F3). At-most-once seeding is +fenced by a caller-supplied idempotency key recorded under a unique +constraint in the same transaction as the mutation; correlation ids +revert to pure trace linkage (F7). Re-run witnesses now cover every +mutable family, assert the second-company grant is same-operation, and +extend tab inheritance to re-run and explicit-choice machinery (F6). +Eligibility is restated in identity §7.1's terms — deactivation is the +better-auth ban, with no separate state (F9). Example-data seeding is +mandatory; the skippability policy is removed (PRD §6.4) (N2). All +drafting additions are consolidated in §7. + Scope: the Gateway-backed product onboarding wizard. Out of scope: the host-local install wizard (`mosaic wizard`, which drives host install and gateway bootstrap and is not this artifact — audit REPORT.md layer 3); @@ -41,10 +63,19 @@ through the extensibility rule §2.4). composes Gateway commands. It is subject to the D8 hard rule like every other webUI surface: no wizard operation reaches the database or filesystem directly, and no wizard-only privileged write path - exists. Concretely, the wizard is contract 5's rank-6 family, - composing the rank-1 hierarchy commands and the rank-4 enrollment - command (contract 5 §3.1): those families are built first, and every - wizard operation is a row in contract 5's mapping. + exists. Concretely, the wizard is contract 5's rank-6 family, and it + is a **client-side composition with no server-side wizard + orchestrator**: every wizard operation is an ordinary outbound + Gateway call issued from the wizard's web modules (or the equivalent + scripted sequence), so contract 5 §6.1's outbound-call inventory, + scoped to those modules, sees the wizard's complete operation set. + The composed operations span six existing families, each already + within contract 5's mapping: the rank-1 hierarchy commands and the + rank-4 enrollment command (contract 5 §3.1 — built first), the + settings command family (steps 1–2), the identity bootstrap and + registration surface (step 3), the mode reads (contract 6 §2.2 + post-epoch; the §2.3 bootstrap-status field pre-epoch), and the + ordinary content commands used for example seeding (step 4). 2. **The wizard introduces no new mutation surface.** Every state change it performs is an existing command with its own contract: user and epoch writes under the identity contract §3, hierarchy writes under @@ -56,10 +87,13 @@ through the extensibility rule §2.4). 3. **Wizard state derives from canonical state.** Each step renders the system's current configuration (read through the same commands) and applies deltas; the wizard does not keep an answer file whose contents - can drift from reality. The wizard MAY persist run bookkeeping + can drift from reality. Values a first run has collected but not yet + applied (the §3.3 application rule) are transient run state, not a + persisted answer file. The wizard MAY persist run bookkeeping (started/completed timestamps); bookkeeping is a cache, never the - source of truth for any decision (§4.3 defines the reconciliation - rule that makes this concrete for seeding). + source of truth for any decision. The §4.3 idempotency fence is + command-layer canonical state, not wizard bookkeeping — losing + bookkeeping never affects it. 4. If a wizard-completion marker is stored, it is presentational only (which entry screen to show). No authorization or gating decision may read it: gating state lives where its owning contract puts it @@ -79,12 +113,20 @@ through the extensibility rule §2.4). deployment mode is recorded canonically at bootstrap and owned by contract 6 (§2 there): the wizard reads the recorded mode and branches on it; it never writes mode and never derives it from - feature state. Changing mode later is conversion (contract 6), not a - wizard re-run. In v1 a recorded mode of `enterprise` is refused at - bootstrap (contract 6 §5), so the wizard's Enterprise branch is - unreachable in v1; a wizard invoked against an unsupported or - unreadable mode record produces a single bounded refusal (contract - 5 §4.2 precondition class), never a partial flow. + feature state. During the bootstrap epoch no authenticated account + exists, so contract 6 §2.2's authenticated read surface is + unreachable there; the pre-epoch flow instead branches on the mode + value reported by the **unauthenticated bootstrap-status response** + of the bootstrap surface (identity §3), which evaluates the mode + record server-side and discloses exactly one field beyond epoch + state: the recorded mode value (drafting addition, §7). Post-epoch + wizard runs read mode through contract 6 §2.2 unchanged. Changing + mode later is conversion (contract 6), not a wizard re-run. In v1 a + recorded mode of `enterprise` is refused at bootstrap (contract 6 + §5), so the wizard's Enterprise branch is unreachable in v1; a + wizard invoked against an unsupported or unreadable mode record + produces a single bounded refusal (contract 5 §4.2 precondition + class), never a partial flow. 4. **Extensibility:** new wizards attach as tabs (D4). Attaching a wizard tab is a registration of additional steps against the same skeleton, inheriting this contract's rules (§1 architecture, §3 step contract, @@ -96,29 +138,44 @@ through the extensibility rule §2.4). The v1 wizard consists of exactly these steps, in order, each backed by the named authority: -1. **System and company name.** System name is a settings write. Company - name feeds step 4's company creation. +1. **System and company name.** System name is a settings value; company + name feeds step 4's company creation. On a first run this step + collects and validates only — application follows step 3's + application rule below. On a re-run (an authenticated admin exists) + it applies directly through the settings commands. 2. **Component choices.** Mosaic Comms/Matrix vs external; Mosaic SSO/Authentik vs external; Mosaic DB/PostgreSQL vs external; vector - DB (D4). v1 records the choices as settings and configures what is - installable in-product; component provisioning beyond that is host - tooling, out of scope here. + DB (D4). v1 records the choices as settings (first run: collected, + applied per step 3's application rule; re-run: applied directly) and + configures what is installable in-product; component provisioning + beyond that is host tooling, out of scope here. 3. **Initial user.** Email, password, and display name, or SSO (PRD §6 step list: "email/password/name/SSO"), per identity §3: the first-admin transition is atomic, one per bootstrap epoch, and the SSO variant runs inside the bootstrap transaction (identity §3.6). - The submitted display name lands on the canonical account record - (identity §2); no field of the PRD's step list is dropped. The - wizard also presents the applicable mandatory choices — registration - mode (identity §2.2) and, for each configured SSO provider, that - provider's JIT enablement with its allowlist warning (identity §4.1, - §4.3). The wizard MUST obtain an explicit submission for each - applicable mandatory choice and MUST refuse step completion while - one is unsubmitted. This is a presentation-and-submission obligation - on wizard runs only: identity's own defaults (registration `closed`, - JIT off) continue to govern everything a wizard run never touches, - and this contract creates no new stored-record obligation beyond - those identity itself defines. + The display-name source is bound per path: the password path uses + the submitted display name; the SSO variant uses the name claim + mapped under identity §5's claim mapping. Canonical `users.name` + MUST equal that source value (witness §6.5); no field of the PRD's + step list is dropped. The wizard also presents the applicable + mandatory choices — registration mode (identity §2.2) and, for each + configured SSO provider, that provider's JIT enablement with its + allowlist warning (identity §4.1, §4.3). The wizard MUST obtain an + explicit submission for each applicable mandatory choice and MUST + refuse step completion while one is unsubmitted. This is a + presentation-and-submission obligation on wizard runs only: + identity's own defaults (registration `closed`, JIT off) continue to + govern everything a wizard run never touches, and this contract + creates no new stored-record obligation beyond those identity itself + defines. **Application rule (first runs):** immediately after the + epoch transition, authenticated as the newly created admin — the + first-admin transition (identity §3) yields the account holding the + RBAC §1.1 platform authority that system and component settings + writes require — the run applies the values collected in steps 1–2 + through the ordinary settings commands. Before the transition the + run holds collected values only: no canonical settings write occurs + pre-epoch, and no pre-admin write authority exists or is invented + (disclosed sequencing addition, §7; witness §6.11). 4. **Initial hierarchy and seeding.** Runs strictly after step 3's epoch transition, authenticated as the newly created admin. The bootstrap writer (identity §3) creates only the first account and closes the @@ -131,13 +188,15 @@ the named authority: commands (contract 1 §5.1) under the admin's `owner` authority (contract 2 §4.3). Seeded examples are ordinary workspace content created by ordinary commands, attributable in audit to the acting - admin with the wizard run's correlation ids (contract 5 §4.3). + admin, carrying the run's trace correlation ids (contract 5 §4.3) + and the §4.3 idempotency keys. 5. **Minimal agent enrollment.** One harness, API-key login, agent name and persona (D11). Enrollment specifics belong to the rank-4 agent-enrollment command family; this contract binds only that the step exists, uses that family, and is skippable. Skippability is a drafting addition under PRD §12.1 (the PRD step list does not mark - the step optional); it is ratified with this contract. + the step optional); it is disclosed in §7 and ratified with this + contract. Steps deferred beyond v1 (user onboarding profile, connectors, comms, voice-matching) appear in `docs/ROADMAP.md` per D11. The deferred @@ -155,33 +214,44 @@ collects no sensitive category, so v1 ships no custody surface. "setup already completed" is a stable state, and factory reset — a future, explicitly destructive operation — is the only path to a new epoch. -3. **Idempotent seeding, reconciled from canonical state.** Step 4 on +3. **Idempotent seeding, fenced at the command layer.** Step 4 on re-run MUST NOT duplicate: the initial company/estate/project/ workspace and the example data are created at most once per bootstrap - epoch. The at-most-once key is a deterministic per-mutation - correlation id derived from the bootstrap epoch and the seed role - (e.g. epoch id + "seed-company"), submitted as the command's - correlation id (contract 5 §4.3) and therefore recorded in the - mutation's audit event in the same transaction as the mutation - itself (contract 1 §5.2). Before applying any seed mutation, the - wizard reconciles against the audit/command log for its epoch's seed - correlation ids and skips every mutation whose event exists. Wizard - bookkeeping of seed node ids is a cache of this record, never the - source of truth: losing it cannot cause duplication. A re-run offers - to create _additional_ hierarchy nodes (PRD: users can create N - companies, N estates, N projects) but never re-creates or resets the - originals, and never touches user content added since. Example-data - seeding is likewise at-most-once per epoch and is skippable. + epoch. The at-most-once mechanism is an **idempotency key**, a + disclosed drafting addition to contract 5's §4 command envelope + ratified with this contract (§7): a mutating command MAY carry a + caller-supplied idempotency key; the command layer records the key + in a uniqueness-constrained fence table **in the same transaction** + as the mutation and its audit event, and a submission whose key is + already recorded executes nothing and returns the recorded outcome. + The wizard submits deterministic keys derived from the bootstrap + epoch and the seed role (e.g. epoch id + "seed-company") for every + seed mutation. Correlation ids (contract 5 §4.3) remain pure trace + linkage and carry no idempotency semantics. Two concurrent resumed + runs serialize on the fence's unique constraint: exactly one + execution per key; the other submission receives the recorded + outcome or a single bounded conflict refusal, never a second + mutation. Wizard bookkeeping of seed node ids remains a cache + (§1.3): losing it cannot cause duplication, because the fence is + canonical command-layer state. A re-run offers to create + _additional_ hierarchy nodes (PRD: users can create N companies, N + estates, N projects) but never re-creates or resets the originals, + and never touches user content added since. Example-data seeding is + likewise at-most-once per epoch; the example set is fixed and its + seeding is not skippable (PRD §6.4: a completed first run contains + the fixed example set). 4. **Interrupted runs strand nothing.** The wizard is resumable at the granularity of a single command: each mutation either commits through - its command with its same-transaction audit event, or leaves nothing - (identity §3.5 for the bootstrap step). Step 4 is a sequence of - individually atomic commands, not one transaction: an interruption - between them leaves a prefix of committed seed nodes, and the §4.3 - reconciliation rule makes the resumed run complete exactly the - remaining suffix, without duplication and without compensating - rollback of completed commands. There is no wizard-level transaction - spanning steps. + its command with its same-transaction audit event and fence row, or + leaves nothing (identity §3.5 for the bootstrap step). Step 4 is a + sequence of individually atomic commands, not one transaction: an + interruption between them leaves a prefix of committed seed nodes, + and the §4.3 fence makes the resumed run complete exactly the + remaining suffix — the run re-submits the full ordered seed sequence + with the same deterministic keys, already-committed mutations return + their recorded outcomes, and only the remainder executes, without + duplication and without compensating rollback of completed commands. + There is no wizard-level transaction spanning steps. ## 5. Seeding authority (resolves contract 2 review NEW-1) @@ -198,10 +268,11 @@ collects no sensitive category, so v1 ships no custody surface. create a top-level company and MUST name an initial `owner` grant in the same audited operation (contract 2 §4.3); the creator naming themselves is the default. Eligible means, in identity-contract - terms: an authenticated account (identity §2) that is not banned and - not deactivated (identity §5). No further role or grant is required. - Until that ruling, deny-by-default holds (contract 2 §3.1): no - implicit creation authority exists. + terms: an authenticated account (identity §2) that is not banned + (identity §7.1 — deactivation on this platform IS the better-auth + ban; no separate deactivated state exists). No further role or grant + is required. Until that ruling, deny-by-default holds (contract 2 + §3.1): no implicit creation authority exists. 3. Child-node creation inside the wizard (estate, project, workspace under the seeded company) follows contract 2 §4.3 unchanged: parent `owner` authority, no automatic grant needed. @@ -211,8 +282,13 @@ collects no sensitive category, so v1 ships no custody surface. Binding on the implementing PRs: 1. **D8 mapping witness:** every Gateway call the wizard makes resolves - to a contract 5 mapping row (asserted by contract 5 §6.1's inventory - scoped to the wizard surface); wizard modules appear in no + to a contract 5 mapping row, asserted by contract 5 §6.1's + outbound-call inventory scoped to the wizard modules — an inventory + that is complete for the wizard because §1.1 forbids a server-side + wizard orchestrator, and the witness statically asserts that + prohibition (no server-side module composes wizard operations). The + inventoried operation set is asserted equal, in both directions, to + the §1.1 declared family list. Wizard modules appear in no class-table writer allowlist (contract 1 §6.3b) and hold no direct database or filesystem access (static assertion, plus a runtime probe that a wizard-context filesystem/database access attempt is @@ -220,9 +296,13 @@ Binding on the implementing PRs: operation, the same actor invoking the underlying command directly receives the same authorization outcome as through the wizard — probed for at least one allowed and one refused actor per operation. -2. **Mode witnesses:** the branch point exists (the flow resolves its - step set from the canonical mode record, read through contract 6's - read surface); v1 with mode `standalone` yields the §3 step set; a +2. **Mode witnesses:** the branch point exists — a pre-epoch flow + resolves its step set from the bootstrap-status mode field (§2.3), + a post-epoch run from contract 6 §2.2's authenticated read; the + bootstrap-status response discloses exactly one field beyond epoch + state (closed-field assertion on the response schema); post-epoch, + an unauthenticated mode read is refused (contract 6 §2.2 holds + unchanged); v1 with mode `standalone` yields the §3 step set; a simulated unsupported or unreadable mode record yields one bounded precondition refusal (§2.3) and no partial flow; wizard sources contain no mode write and no feature-state mode derivation. @@ -230,31 +310,43 @@ Binding on the implementing PRs: completion making no changes — assert zero new hierarchy nodes, zero new users, zero new grants, zero duplicated example content, and the bootstrap epoch still completed (identity §8's epoch witnesses cover - the transition itself). **Reconfigurability:** a re-run changes a - mutable value (registration mode) and the canonical setting reflects - it. **Canonical-state drift:** mutate state outside the wizard (a + the transition itself). **Reconfigurability:** a re-run changes one + value in each mutable family — the system name (settings), a + component choice (settings), registration mode, and a configured + provider's JIT enablement — and each canonical value reflects the + change; a run that skipped enrollment enrolls an agent on re-run + through the rank-4 family and the enrollment exists canonically. + **Canonical-state drift:** mutate state outside the wizard (a rename through the hierarchy command), then re-run — the wizard renders the current canonical value, not a stored answer. **Second company:** a re-run creating a second company succeeds with - the running user as actor, names the creator as `owner` by default - (grant row asserted), and leaves the first company's exact node and - content set untouched (row-set and content-digest assertions). + the running user as actor and names the creator as `owner` in the + same audited operation — asserted on the single audit event naming + both the creation and the grant (contract 2 §4.3–§4.4), not merely + on the grant row's existence — and leaves the first company's exact + node and content set untouched (row-set and content-digest + assertions). 4. **Seed-set witness:** after a first completed run, the created seed set is exactly the declared set — one company, one estate, one project, one workspace, the example content (fixed digest), the one §4.3 `owner` grant, and no other node or grant attributable to the - run's correlation ids. Every seeded node, grant, and example item - has its command audit event (contract 1 §5.2, contract 2 §4.4) - carrying the deterministic seed correlation id (§4.3) and the acting - admin; the first-company event names the initial owner grant in the - same audited operation. + run's idempotency keys or trace correlation ids. Every seeded node, + grant, and example item has its command audit event (contract 1 + §5.2, contract 2 §4.4) committed in the same transaction as its + mutation and §4.3 fence row, attributed to the acting admin; the + first-company event names the initial owner grant in the same + audited operation. 5. **Explicit-choice witness:** a run submitting explicit values completes, and each canonical value equals the value the run submitted (asserted against the captured submissions, not by reading - the canonical store twice); a run with an applicable mandatory - choice unsubmitted is refused at step 3 (§3.3); identity's defaults - are asserted for a provider the run never touched (registration - `closed`, JIT off — identity §2.2, §4.1). + the canonical store twice); canonical `users.name` equals the path's + declared source — the submitted display name for the password path, + the identity §5 mapped name claim for the SSO variant (asserted + against the captured submission or captured assertion); a run with + an applicable mandatory choice unsubmitted is refused at step 3 + (§3.3); identity's defaults are asserted for a provider the run + never touched (registration `closed`, JIT off — identity §2.2, + §4.1). 6. **Gating-independence witness:** with any wizard-completion marker deleted (§1.4), authorization and registration gating behave identically — proving no gate reads wizard state. @@ -263,36 +355,75 @@ Binding on the implementing PRs: individual seed command (company, estate, project, workspace, each example item), and after the bookkeeping cache write — and for each injection point assert: no stranded partial outcome (every committed - command has its same-transaction audit event; nothing else exists), - and a resumed run completes exactly the remaining mutations with no - duplication (identity §8 covers the bootstrap step; §4.3–§4.4 define - the reconciliation the witness exercises). Deleting the bookkeeping - cache between failure and resume MUST NOT change the outcome. + command has its same-transaction audit event and fence row; nothing + else exists), and a resumed run completes exactly the remaining + mutations with no duplication (identity §8 covers the bootstrap + step; §4.3–§4.4 define the fence the witness exercises). Deleting + the bookkeeping cache between failure and resume MUST NOT change the + outcome. **Idempotency-fence witnesses:** re-submitting a committed + seed command with its same key executes nothing and returns the + recorded outcome (asserted by mutation-count and audit-event-count + equality); two concurrent resumed runs executing the seed sequence + yield exactly one seed set — per key, exactly one mutation and one + audit event exist, and the losing submission received the recorded + outcome or the single bounded conflict refusal. 8. **Actor-matrix witness (§5.2):** post-bootstrap top-level company creation succeeds for an ordinary authenticated non-admin user - (positive), and is refused for an unauthenticated caller, a banned - account, and a deactivated account (negatives), with contract 2's - error classes. + (positive), and is refused for an unauthenticated caller and for a + banned account (identity §7.1's deactivation-as-ban) (negatives), + with contract 2's error classes. 9. **Tab-inheritance witness:** a registered test tab's steps are subject to the same assertions — its modules fail the §6.1 static - assertion if they access the database directly, and its mutations - appear in the §6.1 mapping inventory — proving §2.4 is enforced by - machinery, not convention. + assertion if they access the database directly, its mutations + appear in the §6.1 mapping inventory, a tab re-run making no + changes produces zero new mutations (§6.3 style), and a tab + declaring a mandatory choice is refused while it is unsubmitted + (§6.5 style) — proving §2.4 is enforced by machinery, not + convention. 10. **Enrollment witness:** the enrollment step exists, invokes only the rank-4 family, and a run that skips it completes with zero enrollment-family mutations. +11. **Pre-epoch authority witness (§3.3 application rule):** during a + first run, before the epoch transition, canonical settings are + untouched (settings-state comparison against the pre-run capture), + and an injected settings-write attempt from the unauthenticated + pre-epoch flow is refused with contract 5 §4.2's authorization + class; after step 3, the collected values are applied and each + application is attributable in audit to the new admin. + +## 7. Drafting additions (PRD §12.1 disclosure) + +Proposed drafting additions, visible here for ratification, each +severable; the step list, mode branching, re-runnability, and seeding +obligations themselves are traced to PRD D4/D11 and the named sibling +contracts and are not additions: + +1. Enrollment-step skippability (§3.5) — the PRD step list does not + mark the step optional. +2. The unauthenticated bootstrap-status mode disclosure — exactly one + field (the recorded mode value), during the bootstrap epoch only + (§2.3). +3. The collect-first, post-epoch application sequencing for steps 1–2 + (§3.3 application rule). +4. The idempotency-key field and its same-transaction uniqueness fence + (§4.3) — an addition to contract 5 §4's command envelope, proposed + and ratified here, severable from the rest of this contract. +5. The presentation-and-submission obligation for applicable mandatory + choices on wizard runs (§3.3). +6. The no-server-side-orchestrator architectural constraint (§1.1). +7. The wizard-completion-marker presentational-only rule (§1.4). ## Ruling request -Ratify sections 1–6 as written, with one decision embedded: +Ratify sections 1–7 as written, with one decision embedded: - Decision (§5.2): post-bootstrap, any eligible platform user - (authenticated, not banned, not deactivated — identity §2/§5) may - create a top-level company, naming an initial `owner` grant (default: - self) in the same audited operation. Basis: PRD Part I §4 "Users can - create N companies, N estates, N projects" read as end-user - capability, not admin-only. Alternative if rejected: top-level - creation stays deny-by-default and becomes a platform-admin-granted - capability in a later contract — nothing in this contract or contract - 2 breaks either way, because deny-by-default is the resting state - (contract 2 §3.1). + (authenticated, not banned — identity §2/§7.1) may create a top-level + company, naming an initial `owner` grant (default: self) in the same + audited operation. Basis: PRD Part I §4 "Users can create N + companies, N estates, N projects" read as end-user capability, not + admin-only. Alternative if rejected: top-level creation stays + deny-by-default and becomes a platform-admin-granted capability in a + later contract — nothing in this contract or contract 2 breaks + either way, because deny-by-default is the resting state (contract 2 + §3.1). -- 2.54.0 From e58d0a54479b90cf89927663d2b4f83cf8f0851d Mon Sep 17 00:00:00 2001 From: fred Date: Wed, 26 Aug 2026 20:21:55 -0500 Subject: [PATCH 04/15] docs: onboarding-wizard contract revision 4 (sol re-review 2 residuals + N3/N4) - F2: bootstrap-status closed two-field schema (epoch enum + pre-epoch-only mode), post-epoch mode-field absence witnessed, contract 6 \u00a72.2 amendment disclosed (\u00a77.9) - F5/N4: contract 5 \u00a73.1 rank-6 mapping expansion disclosed naming the rows to amend (\u00a77.8); \u00a71.1 states which families are live vs amended - F7: complete idempotency envelope \u2014 fence records operation/actor/scope/ payload digest, re-authorized replay, collision refusal, no error replay, concurrent loser receives winner's recorded outcome; witnesses added - N1/N3: post-epoch application phase replaced by a single bootstrap finalize command (one transaction: admin + epoch close + carried settings/registration/ JIT/seed-parameter writes under the new admin); password-only v1 first admin (\u00a77.10); seed sequence derived from canonical state; \u00a76.11 uses the authentication-failure class --- docs/requirements/onboarding-wizard.md | 349 +++++++++++++++++-------- 1 file changed, 244 insertions(+), 105 deletions(-) diff --git a/docs/requirements/onboarding-wizard.md b/docs/requirements/onboarding-wizard.md index 689ced06..1f51dc68 100644 --- a/docs/requirements/onboarding-wizard.md +++ b/docs/requirements/onboarding-wizard.md @@ -48,6 +48,26 @@ better-auth ban, with no separate state (F9). Example-data seeding is mandatory; the skippability policy is removed (PRD §6.4) (N2). All drafting additions are consolidated in §7. +Revision 4 (sol re-review 2: residual F2/F5/F7/N1, new N3/N4): the +post-epoch application phase is replaced by a single **bootstrap +finalize command** — step 3 submits the first-admin fields and every +collected value (settings, registration mode, applicable JIT choices, +seed parameters) in one command on identity §3's bootstrap surface, +executed server-side in one transaction that creates the admin, closes +the epoch, and commits the carried values under the new admin's +authority; after it, every collected value is canonical state, so an +interrupted run resumes from canonical state alone (N1, N3). The v1 +first admin is password-only; identity §3.6's SSO variant is not +composed by the v1 wizard (N1's SSO ordering). The bootstrap-status +response is a closed two-field schema (epoch state plus a +pre-epoch-only mode field), with the contract 6 §2.2 amendment +disclosed (F2). The idempotency envelope is completed: fence rows +record operation, actor, scope, and payload digest; replay is +re-authorized; changed-payload collision is refused; only committed +outcomes are recorded; the concurrent loser receives the winner's +recorded outcome (F7). §7 now names the contract 5 §3.1 rank-6 +mapping rows the composition expansion amends (F5, N4). + Scope: the Gateway-backed product onboarding wizard. Out of scope: the host-local install wizard (`mosaic wizard`, which drives host install and gateway bootstrap and is not this artifact — audit REPORT.md layer 3); @@ -69,13 +89,17 @@ through the extensibility rule §2.4). Gateway call issued from the wizard's web modules (or the equivalent scripted sequence), so contract 5 §6.1's outbound-call inventory, scoped to those modules, sees the wizard's complete operation set. - The composed operations span six existing families, each already - within contract 5's mapping: the rank-1 hierarchy commands and the - rank-4 enrollment command (contract 5 §3.1 — built first), the - settings command family (steps 1–2), the identity bootstrap and - registration surface (step 3), the mode reads (contract 6 §2.2 - post-epoch; the §2.3 bootstrap-status field pre-epoch), and the - ordinary content commands used for example seeding (step 4). + The composed operations span six existing families: the rank-1 + hierarchy commands and the rank-4 enrollment command (contract 5 + §3.1 — built first), the settings command family (steps 1–2), the + identity bootstrap and registration surface (step 3, including the + §3.3 finalize command), the mode reads (contract 6 §2.2 post-epoch; + the §2.3 bootstrap-status field pre-epoch), and the ordinary + content commands used for example seeding (step 4). Two of these + families (ranks 1 and 4) are in contract 5 §3.1's live rank-6 + composition row; the other four are added by the disclosed §7.8 + mapping amendment, without which their operations are unmapped and + blocked under contract 5 §5. 2. **The wizard introduces no new mutation surface.** Every state change it performs is an existing command with its own contract: user and epoch writes under the identity contract §3, hierarchy writes under @@ -88,8 +112,10 @@ through the extensibility rule §2.4). system's current configuration (read through the same commands) and applies deltas; the wizard does not keep an answer file whose contents can drift from reality. Values a first run has collected but not yet - applied (the §3.3 application rule) are transient run state, not a - persisted answer file. The wizard MAY persist run bookkeeping + committed by the §3.3 finalize command are transient run state, not + a persisted answer file; after finalize every collected value is + canonical state, and no transient value is needed to resume + (§4.4). The wizard MAY persist run bookkeeping (started/completed timestamps); bookkeeping is a cache, never the source of truth for any decision. The §4.3 idempotency fence is command-layer canonical state, not wizard bookkeeping — losing @@ -118,9 +144,18 @@ through the extensibility rule §2.4). unreachable there; the pre-epoch flow instead branches on the mode value reported by the **unauthenticated bootstrap-status response** of the bootstrap surface (identity §3), which evaluates the mode - record server-side and discloses exactly one field beyond epoch - state: the recorded mode value (drafting addition, §7). Post-epoch - wizard runs read mode through contract 6 §2.2 unchanged. Changing + record server-side. The bootstrap-status response is a **closed + two-field schema**: `epoch` — one field whose value set is exactly + the bootstrap epoch states identity §3 defines for + `bootstrap_state`, closed to that enumeration — and `mode` — the + recorded mode value, present **only while the epoch is open** and + absent from the response schema once the epoch has completed. No + other field exists in the response. The unauthenticated pre-epoch + mode disclosure is a disclosed amendment to contract 6 §2.2's + authenticated-read rule (§7.9); post-epoch, §2.2's + authenticated-only rule holds unchanged and the bootstrap-status + response carries no mode field. Post-epoch wizard runs read mode + through contract 6 §2.2 unchanged. Changing mode later is conversion (contract 6), not a wizard re-run. In v1 a recorded mode of `enterprise` is refused at bootstrap (contract 6 §5), so the wizard's Enterprise branch is unreachable in v1; a @@ -140,46 +175,72 @@ the named authority: 1. **System and company name.** System name is a settings value; company name feeds step 4's company creation. On a first run this step - collects and validates only — application follows step 3's - application rule below. On a re-run (an authenticated admin exists) + collects and validates only — the collected values are committed by + step 3's finalize command (§3.3). On a re-run (an authenticated admin exists) it applies directly through the settings commands. 2. **Component choices.** Mosaic Comms/Matrix vs external; Mosaic SSO/Authentik vs external; Mosaic DB/PostgreSQL vs external; vector DB (D4). v1 records the choices as settings (first run: collected, - applied per step 3's application rule; re-run: applied directly) and + committed by step 3's finalize command §3.3; re-run: applied + directly) and configures what is installable in-product; component provisioning beyond that is host tooling, out of scope here. -3. **Initial user.** Email, password, and display name, or SSO (PRD §6 - step list: "email/password/name/SSO"), per identity §3: the - first-admin transition is atomic, one per bootstrap epoch, and the - SSO variant runs inside the bootstrap transaction (identity §3.6). - The display-name source is bound per path: the password path uses - the submitted display name; the SSO variant uses the name claim - mapped under identity §5's claim mapping. Canonical `users.name` - MUST equal that source value (witness §6.5); no field of the PRD's - step list is dropped. The wizard also presents the applicable +3. **Initial user and finalize.** Email, password, and display name + (PRD §6 step list: "email/password/name/SSO"), per identity §3: the + first-admin transition is atomic and one per bootstrap epoch. **The + v1 first admin is password-only.** A step-2 SSO choice cannot yield + a canonically configured provider before the epoch closes, so an + SSO-authenticated first admin is not constructible in this flow; + the v1 wizard does not compose identity §3.6's SSO variant, and the + PRD step list's SSO option is satisfied post-epoch (providers + configured and JIT-enabled after finalize; identity §5 governs + every SSO-created account). This is a disclosed sequencing decision + (§7.10). The password path's canonical `users.name` MUST equal the + submitted display name; any SSO-created account's name is bound to + identity §5's mapped name claim (witness §6.5); no field of the + PRD's step list is dropped. The wizard also presents the applicable mandatory choices — registration mode (identity §2.2) and, for each - configured SSO provider, that provider's JIT enablement with its - allowlist warning (identity §4.1, §4.3). The wizard MUST obtain an - explicit submission for each applicable mandatory choice and MUST - refuse step completion while one is unsubmitted. This is a - presentation-and-submission obligation on wizard runs only: - identity's own defaults (registration `closed`, JIT off) continue to - govern everything a wizard run never touches, and this contract - creates no new stored-record obligation beyond those identity itself - defines. **Application rule (first runs):** immediately after the - epoch transition, authenticated as the newly created admin — the - first-admin transition (identity §3) yields the account holding the - RBAC §1.1 platform authority that system and component settings - writes require — the run applies the values collected in steps 1–2 - through the ordinary settings commands. Before the transition the - run holds collected values only: no canonical settings write occurs - pre-epoch, and no pre-admin write authority exists or is invented - (disclosed sequencing addition, §7; witness §6.11). -4. **Initial hierarchy and seeding.** Runs strictly after step 3's epoch - transition, authenticated as the newly created admin. The bootstrap - writer (identity §3) creates only the first account and closes the - epoch; it has no hierarchy authority and creates no hierarchy node. + SSO provider canonically configured at finalize time, that + provider's JIT enablement with its allowlist warning (identity + §4.1, §4.3); a provider configured after finalize receives its JIT + choice at configuration time under identity §4.1's default. The + wizard MUST obtain an explicit submission for each applicable + mandatory choice and MUST refuse finalize while one is unsubmitted. + This is a presentation-and-submission obligation on wizard runs + only: identity's own defaults (registration `closed`, JIT off) + continue to govern everything a wizard run never touches, and this + contract creates no new stored-record obligation beyond those + identity itself defines. **Finalize rule (first runs):** step 3 + ends in a single **bootstrap finalize command** on identity §3's + bootstrap surface — a disclosed extension of that surface (§7.3) — + carrying the first-admin fields and every collected value: the + step-1 system name, the step-1 company name (recorded as the §3.4 + seed parameter), the step-2 component choices, the + registration-mode submission, and the applicable JIT submissions. + The command executes **in one transaction**: it creates the admin + and closes the epoch exactly as identity §3 defines, then — with + the new admin existing inside the same transaction, holding the + RBAC §1.1 platform authority those writes require — commits every + carried value through the ordinary settings and identity + configuration writes, each attributed in audit to the new admin. + Either the whole transaction commits or none of it exists (identity + §3.5's atomicity extends over the carried writes). Before finalize + the run holds collected values only: no canonical write of any kind + occurs pre-epoch, and no pre-admin write authority exists or is + invented (witness §6.11). After finalize, every collected value is + canonical state; no transient value is needed to resume (§4.4, + witness §6.7). +4. **Initial hierarchy and seeding.** Runs strictly after step 3's + finalize transaction, authenticated as the newly created admin. The + bootstrap writer (identity §3) creates only the first account, + closes the epoch, and commits the §3.3 carried values; it has no + hierarchy authority and creates no hierarchy node. The seed + sequence is derived entirely from canonical state: the seed + parameter the finalize command recorded (the company name) and the + fixed example set (§4.3). A resumed run — including a fresh client + holding none of the original run's transient state — reconstructs + the same ordered sequence and the same deterministic §4.3 keys from + that canonical state alone. The first company is created by the ordinary top-level company command under §5.2's eligibility policy, with the new admin as actor, naming the admin as initial `owner` in the same audited operation @@ -220,18 +281,45 @@ collects no sensitive category, so v1 ships no custody surface. epoch. The at-most-once mechanism is an **idempotency key**, a disclosed drafting addition to contract 5's §4 command envelope ratified with this contract (§7): a mutating command MAY carry a - caller-supplied idempotency key; the command layer records the key - in a uniqueness-constrained fence table **in the same transaction** - as the mutation and its audit event, and a submission whose key is - already recorded executes nothing and returns the recorded outcome. + caller-supplied idempotency key. The complete envelope: + - **Fence row.** The command layer records, in a + uniqueness-constrained fence table **in the same transaction** + as the mutation and its audit event: the key, the operation + identifier, the acting principal, the mutation's authorization + scope (the target company, or the platform scope for top-level + operations), a digest of the canonicalized request payload, and + a reference to the committed outcome. Fence uniqueness is the + pair (operation identifier, key). + - **Replay.** A submission whose (operation, key) pair is recorded + is first authorized exactly as a fresh submission would be — an + actor who is not authorized receives the authorization refusal, + never the recorded outcome. If authorization passes and the + recorded actor, scope, and payload digest all equal the + submission's, the command executes nothing and returns the + recorded outcome. + - **Collision.** A submission whose (operation, key) pair is + recorded but whose actor, scope, or payload digest differs is + refused with a single bounded conflict error (contract 5 §4.2); + it executes nothing and discloses nothing of the recorded + outcome. + - **No error replay.** The fence row commits only with its + mutation, so only committed outcomes are ever recorded. A failed + or refused submission records no fence row; a retry executes + afresh. There is no recorded-error state. + - **Concurrency.** Two submissions with the same (operation, key) + pair serialize on the fence's unique constraint: exactly one + executes. The loser waits for the winner's transaction to + resolve; if it committed, the loser is handled as a replay + (authorization first, then the recorded outcome, or the + collision refusal on mismatch); if it aborted, no fence row + exists and the loser executes. The loser never performs a second + mutation and is never left without a defined response. + The wizard submits deterministic keys derived from the bootstrap epoch and the seed role (e.g. epoch id + "seed-company") for every seed mutation. Correlation ids (contract 5 §4.3) remain pure trace - linkage and carry no idempotency semantics. Two concurrent resumed - runs serialize on the fence's unique constraint: exactly one - execution per key; the other submission receives the recorded - outcome or a single bounded conflict refusal, never a second - mutation. Wizard bookkeeping of seed node ids remains a cache + linkage and carry no idempotency semantics. Wizard bookkeeping of + seed node ids remains a cache (§1.3): losing it cannot cause duplication, because the fence is canonical command-layer state. A re-run offers to create _additional_ hierarchy nodes (PRD: users can create N companies, N @@ -240,18 +328,26 @@ collects no sensitive category, so v1 ships no custody surface. likewise at-most-once per epoch; the example set is fixed and its seeding is not skippable (PRD §6.4: a completed first run contains the fixed example set). + 4. **Interrupted runs strand nothing.** The wizard is resumable at the - granularity of a single command: each mutation either commits through - its command with its same-transaction audit event and fence row, or - leaves nothing (identity §3.5 for the bootstrap step). Step 4 is a - sequence of individually atomic commands, not one transaction: an - interruption between them leaves a prefix of committed seed nodes, - and the §4.3 fence makes the resumed run complete exactly the - remaining suffix — the run re-submits the full ordered seed sequence - with the same deterministic keys, already-committed mutations return - their recorded outcomes, and only the remainder executes, without - duplication and without compensating rollback of completed commands. - There is no wizard-level transaction spanning steps. + granularity of a single command: each mutation either commits + through its command with its same-transaction audit event and fence + row, or leaves nothing (identity §3.5, extended over the carried + writes, for the §3.3 finalize command). A run interrupted before + finalize has written nothing; its collected values are re-entered. + A run interrupted after finalize resumes from canonical state + alone: every collected value was committed by the finalize + transaction, and the remaining work — the step 4–5 command + sequence — is derivable from it (§3.4). Step 4 is a sequence of + individually atomic commands, not one transaction: an interruption + between them leaves a prefix of committed seed nodes, and the §4.3 + fence makes the resumed run complete exactly the remaining suffix — + the run re-derives the full ordered seed sequence from canonical + state and re-submits it with the same deterministic keys, + already-committed mutations return their recorded outcomes, and + only the remainder executes, without duplication and without + compensating rollback of completed commands. There is no + wizard-level transaction spanning steps. ## 5. Seeding authority (resolves contract 2 review NEW-1) @@ -299,10 +395,12 @@ Binding on the implementing PRs: 2. **Mode witnesses:** the branch point exists — a pre-epoch flow resolves its step set from the bootstrap-status mode field (§2.3), a post-epoch run from contract 6 §2.2's authenticated read; the - bootstrap-status response discloses exactly one field beyond epoch - state (closed-field assertion on the response schema); post-epoch, - an unauthenticated mode read is refused (contract 6 §2.2 holds - unchanged); v1 with mode `standalone` yields the §3 step set; a + bootstrap-status response matches §2.3's closed two-field schema + exactly while the epoch is open, and after the epoch completes + contains the epoch field only — the mode field absent from the + response (closed-field assertion on the response schema in both + phases); post-epoch, an unauthenticated mode read through any + surface is refused (contract 6 §2.2, as amended by §7.9, holds); v1 with mode `standalone` yields the §3 step set; a simulated unsupported or unreadable mode record yields one bounded precondition refusal (§2.3) and no partial flow; wizard sources contain no mode write and no feature-state mode derivation. @@ -339,10 +437,12 @@ Binding on the implementing PRs: 5. **Explicit-choice witness:** a run submitting explicit values completes, and each canonical value equals the value the run submitted (asserted against the captured submissions, not by reading - the canonical store twice); canonical `users.name` equals the path's - declared source — the submitted display name for the password path, - the identity §5 mapped name claim for the SSO variant (asserted - against the captured submission or captured assertion); a run with + the canonical store twice); canonical `users.name` equals the + declared source — for the first admin (password path), the + submitted display name; for an SSO-created account (a post-epoch + JIT-provisioned user), the identity §5 mapped name claim (each + asserted against the captured submission or captured assertion); a + run with an applicable mandatory choice unsubmitted is refused at step 3 (§3.3); identity's defaults are asserted for a provider the run never touched (registration `closed`, JIT off — identity §2.2, @@ -350,23 +450,35 @@ Binding on the implementing PRs: 6. **Gating-independence witness:** with any wizard-completion marker deleted (§1.4), authorization and registration gating behave identically — proving no gate reads wizard state. -7. **Interrupted-run witness:** inject failure after each internal - mutation of a run — after the bootstrap transaction, after each - individual seed command (company, estate, project, workspace, each - example item), and after the bookkeeping cache write — and for each - injection point assert: no stranded partial outcome (every committed - command has its same-transaction audit event and fence row; nothing - else exists), and a resumed run completes exactly the remaining - mutations with no duplication (identity §8 covers the bootstrap - step; §4.3–§4.4 define the fence the witness exercises). Deleting - the bookkeeping cache between failure and resume MUST NOT change the - outcome. **Idempotency-fence witnesses:** re-submitting a committed - seed command with its same key executes nothing and returns the - recorded outcome (asserted by mutation-count and audit-event-count - equality); two concurrent resumed runs executing the seed sequence - yield exactly one seed set — per key, exactly one mutation and one - audit event exist, and the losing submission received the recorded - outcome or the single bounded conflict refusal. +7. **Interrupted-run witness:** inject failure at each point of a + run — **inside the finalize transaction** (after admin creation but + before the carried writes commit: assert nothing exists — no + account, the epoch still open, canonical settings untouched, no + fence row, no audit event), after the committed finalize + transaction, after each individual seed command (company, estate, + project, workspace, each example item), and after the bookkeeping + cache write — and for each injection point assert: no stranded + partial outcome (every committed command has its same-transaction + audit event and fence row; nothing else exists), and a resumed run + completes exactly the remaining mutations with no duplication + (identity §8 covers the epoch transition; §4.3–§4.4 define the + fence the witness exercises). The resume after the committed + finalize MUST be performed by a fresh client holding none of the + original run's transient state, deriving the remaining seed + sequence from canonical state alone (§3.4). Deleting the + bookkeeping cache between failure and resume MUST NOT change the + outcome. **Idempotency-fence witnesses (§4.3):** re-submitting a + committed seed command with its same key, actor, and payload + executes nothing and returns the recorded outcome (asserted by + mutation-count and audit-event-count equality); the same key with a + changed payload digest is refused with the single bounded conflict + and executes nothing; a replay by an actor who has since lost + eligibility (identity §7.1 ban) receives the authorization refusal, + not the recorded outcome; a submission that failed before commit + leaves no fence row and its retry executes; two concurrent resumed + runs executing the seed sequence yield exactly one seed set — per + key, exactly one mutation and one audit event exist, and the losing + submission received the winner's recorded outcome. 8. **Actor-matrix witness (§5.2):** post-bootstrap top-level company creation succeeds for an ordinary authenticated non-admin user (positive), and is refused for an unauthenticated caller and for a @@ -383,13 +495,17 @@ Binding on the implementing PRs: 10. **Enrollment witness:** the enrollment step exists, invokes only the rank-4 family, and a run that skips it completes with zero enrollment-family mutations. -11. **Pre-epoch authority witness (§3.3 application rule):** during a - first run, before the epoch transition, canonical settings are - untouched (settings-state comparison against the pre-run capture), - and an injected settings-write attempt from the unauthenticated - pre-epoch flow is refused with contract 5 §4.2's authorization - class; after step 3, the collected values are applied and each - application is attributable in audit to the new admin. +11. **Pre-epoch authority and finalize witness (§3.3):** during a + first run, before finalize, canonical state is untouched + (settings-state comparison against the pre-run capture), and an + injected settings-write attempt from the unauthenticated pre-epoch + flow is refused with contract 5 §4.2's **authentication-failure + class** (the caller is unauthenticated, not an + authorized-but-refused actor); the finalize command commits the + admin, the epoch transition, and every carried value in one + transaction, each carried write attributable in audit to the new + admin; the §6.7 inside-finalize injection covers the atomicity + negative. ## 7. Drafting additions (PRD §12.1 disclosure) @@ -400,18 +516,41 @@ contracts and are not additions: 1. Enrollment-step skippability (§3.5) — the PRD step list does not mark the step optional. -2. The unauthenticated bootstrap-status mode disclosure — exactly one - field (the recorded mode value), during the bootstrap epoch only - (§2.3). -3. The collect-first, post-epoch application sequencing for steps 1–2 - (§3.3 application rule). -4. The idempotency-key field and its same-transaction uniqueness fence - (§4.3) — an addition to contract 5 §4's command envelope, proposed - and ratified here, severable from the rest of this contract. +2. The unauthenticated bootstrap-status mode disclosure — the closed + two-field response schema (epoch state plus the recorded mode + value, the latter present during the bootstrap epoch only) (§2.3). +3. The **bootstrap finalize command** (§3.3) — an extension of + identity §3's bootstrap surface: one transaction carrying the + first-admin fields, the collected settings, registration-mode, and + JIT values, and the §3.4 seed parameter, committing the carried + writes under the new admin's authority with identity §3.5's + atomicity extended over them. Severable from the rest of this + contract. +4. The idempotency-key field and its same-transaction uniqueness + fence, with the §4.3 replay, collision, no-error-replay, and + concurrency rules — an addition to contract 5 §4's command + envelope, proposed and ratified here, severable from the rest of + this contract. 5. The presentation-and-submission obligation for applicable mandatory choices on wizard runs (§3.3). 6. The no-server-side-orchestrator architectural constraint (§1.1). 7. The wizard-completion-marker presentational-only rule (§1.4). +8. The contract 5 §3.1 rank-6 mapping expansion (§1.1): the rank-6 + composition row, which today names only the rank-1 hierarchy and + rank-4 enrollment families, is amended to name all six composed + families — adding the settings command family, the identity + bootstrap and registration surface (including the §3.3 finalize + command), the mode reads (contract 6 §2.2 and the §2.3 + bootstrap-status field), and the ordinary content commands used for + example seeding — with one mapping row per newly named family. + Without this amendment those operations are unmapped and blocked + under contract 5 §5. +9. The contract 6 §2.2 amendment permitting the single unauthenticated + pre-epoch mode disclosure through the §2.3 bootstrap-status field; + post-epoch, §2.2's authenticated-only rule is unchanged. +10. The password-only v1 first admin (§3.3): identity §3.6's SSO + variant is not composed by the v1 wizard; the PRD step list's SSO + option is satisfied post-epoch. ## Ruling request -- 2.54.0 From 0162443a38853241ef4333646c5b92a616808732 Mon Sep 17 00:00:00 2001 From: fred Date: Wed, 26 Aug 2026 20:40:14 -0500 Subject: [PATCH 05/15] docs: onboarding-wizard contract revision 5 (sol re-review 3 residuals + N5/N6) --- docs/requirements/onboarding-wizard.md | 193 +++++++++++++++++++------ 1 file changed, 145 insertions(+), 48 deletions(-) diff --git a/docs/requirements/onboarding-wizard.md b/docs/requirements/onboarding-wizard.md index 1f51dc68..858bb6c6 100644 --- a/docs/requirements/onboarding-wizard.md +++ b/docs/requirements/onboarding-wizard.md @@ -68,6 +68,26 @@ outcomes are recorded; the concurrent loser receives the winner's recorded outcome (F7). §7 now names the contract 5 §3.1 rank-6 mapping rows the composition expansion amends (F5, N4). +Revision 5 (sol re-review 3: residual F2/F7/N1/N3, new N5/N6): the +finalize command is now honestly scoped as the wizard's **single +disclosed server-side composed transaction** — a bounded, severable +amendment to the §1.1 client-side-composition rule, reachable only +while the epoch is open, executing under the bootstrap writer's +amended authority (never by authenticating the caller as the created +admin), with a dedicated finalize-write inventory witness proving its +internal write set equals the §3.3 carried-value list; §1.2 and §5.1 +no longer claim identity §3 binds unchanged (N1, N5). The epoch field's +wire values are pinned to the closed token pair `open`/`completed` +(identity §3 names the states but no wire enumeration; the pin is +disclosed) (F2). The company-name seed parameter gains an owning +record: the declared `bootstrap.seed-company-name` settings value, +committed by finalize and read through the ordinary settings surface +(N3). The §6.7 fence witnesses add the different-actor collision, the +different-scope collision, and the aborted-winner concurrency case +(F7). The password-only first admin is restated as a disclosed PRD +deviation (deferment) of the initial-user SSO option, not as its +satisfaction (N6). + Scope: the Gateway-backed product onboarding wizard. Out of scope: the host-local install wizard (`mosaic wizard`, which drives host install and gateway bootstrap and is not this artifact — audit REPORT.md layer 3); @@ -84,11 +104,19 @@ through the extensibility rule §2.4). every other webUI surface: no wizard operation reaches the database or filesystem directly, and no wizard-only privileged write path exists. Concretely, the wizard is contract 5's rank-6 family, and it - is a **client-side composition with no server-side wizard - orchestrator**: every wizard operation is an ordinary outbound - Gateway call issued from the wizard's web modules (or the equivalent - scripted sequence), so contract 5 §6.1's outbound-call inventory, - scoped to those modules, sees the wizard's complete operation set. + is a **client-side composition with exactly one disclosed + exception**: every wizard operation except step 3's finalize is an + ordinary outbound Gateway call issued from the wizard's web modules + (or the equivalent scripted sequence), and the exception is the + §3.3 **bootstrap finalize command** — one server-side composed + transaction on identity §3's bootstrap surface, reachable only + while the bootstrap epoch is open, disclosed and ratified as the + §7.3 architecture amendment. No other server-side module composes + wizard operations. Contract 5 §6.1's outbound-call inventory, + scoped to the wizard modules, therefore sees the wizard's complete + outbound call set; the finalize command's internally composed + writes are proved complete by the dedicated §6.1 finalize-write + inventory, not by the outbound inventory. The composed operations span six existing families: the rank-1 hierarchy commands and the rank-4 enrollment command (contract 5 §3.1 — built first), the settings command family (steps 1–2), the @@ -104,10 +132,15 @@ through the extensibility rule §2.4). it performs is an existing command with its own contract: user and epoch writes under the identity contract §3, hierarchy writes under contract 1 §5, grant writes under contract 2 §4, settings writes under - their owning command family. The one exception is the bootstrap epoch - transition, where the wizard drives the bootstrap writer defined by - identity §3 — that writer's constraints (§3.1–§3.6) bind unchanged; - this contract adds nothing to them. + their owning command family. The one exception is the bootstrap + surface, where the wizard drives the bootstrap writer defined by + identity §3 **as amended by the disclosed §7.3 finalize + extension**: the writer's constraints (§3.1–§3.6) bind, and this + contract's single change to them — extending the epoch-closing + command to carry the §3.3 value set inside the same transaction — + is exactly the §7.3 amendment, severable and ratified with this + contract. Beyond that amendment, nothing is added to identity §3, + and no undisclosed authority exists. 3. **Wizard state derives from canonical state.** Each step renders the system's current configuration (read through the same commands) and applies deltas; the wizard does not keep an answer file whose contents @@ -147,10 +180,13 @@ through the extensibility rule §2.4). record server-side. The bootstrap-status response is a **closed two-field schema**: `epoch` — one field whose value set is exactly the bootstrap epoch states identity §3 defines for - `bootstrap_state`, closed to that enumeration — and `mode` — the - recorded mode value, present **only while the epoch is open** and - absent from the response schema once the epoch has completed. No - other field exists in the response. The unauthenticated pre-epoch + `bootstrap_state`, closed to the exact wire-token pair **`open`** + and **`completed`** — identity §3 names the two epoch states in + prose but defines no wire enumeration, so this contract pins the + tokens, as the second clause of the disclosed §7.9 amendment — and + `mode` — the recorded mode value, present **only while the epoch + is open** and absent from the response schema once the epoch has + completed. No other field exists in the response. The unauthenticated pre-epoch mode disclosure is a disclosed amendment to contract 6 §2.2's authenticated-read rule (§7.9); post-epoch, §2.2's authenticated-only rule holds unchanged and the bootstrap-status @@ -173,7 +209,12 @@ through the extensibility rule §2.4). The v1 wizard consists of exactly these steps, in order, each backed by the named authority: -1. **System and company name.** System name is a settings value; company +1. **System and company name.** System name and the seed company name + are both settings values: the company name is recorded under the + declared settings key **`bootstrap.seed-company-name`** — the §3.4 + seed parameter, a disclosed drafting addition (§7.11) — committed + by step 3's finalize command among the ordinary settings writes and + readable through the ordinary settings read surface. The company name feeds step 4's company creation. On a first run this step collects and validates only — the collected values are committed by step 3's finalize command (§3.3). On a re-run (an authenticated admin exists) @@ -191,11 +232,16 @@ the named authority: v1 first admin is password-only.** A step-2 SSO choice cannot yield a canonically configured provider before the epoch closes, so an SSO-authenticated first admin is not constructible in this flow; - the v1 wizard does not compose identity §3.6's SSO variant, and the - PRD step list's SSO option is satisfied post-epoch (providers - configured and JIT-enabled after finalize; identity §5 governs - every SSO-created account). This is a disclosed sequencing decision - (§7.10). The password path's canonical `users.name` MUST equal the + the v1 wizard does not compose identity §3.6's SSO variant. The + PRD step list's SSO option **for the initial user is not + implemented in v1 — a disclosed PRD deviation (deferment, §7.10)**: + no v1 step configures a provider before finalize and no v1 step + links the first admin to a provider afterwards, so the initial user + cannot be SSO-created or SSO-linked in v1. SSO first becomes + available to accounts created post-epoch (providers configured and + JIT-enabled after finalize; identity §5 governs every SSO-created + account) — those are different users, not a satisfaction of the + initial-user option. The password path's canonical `users.name` MUST equal the submitted display name; any SSO-created account's name is bound to identity §5's mapped name claim (witness §6.5); no field of the PRD's step list is dropped. The wizard also presents the applicable @@ -218,11 +264,17 @@ the named authority: seed parameter), the step-2 component choices, the registration-mode submission, and the applicable JIT submissions. The command executes **in one transaction**: it creates the admin - and closes the epoch exactly as identity §3 defines, then — with - the new admin existing inside the same transaction, holding the - RBAC §1.1 platform authority those writes require — commits every - carried value through the ordinary settings and identity - configuration writes, each attributed in audit to the new admin. + and closes the epoch exactly as identity §3 defines, then commits + every carried value through the ordinary settings and identity + configuration write implementations — the same code paths the + post-epoch commands use; no parallel write path. The executing + authority is the **bootstrap writer's own authority as amended by + §7.3**, never an authentication of the caller: the finalize + request begins and ends unauthenticated, and creating the admin + row does not authenticate the caller as that admin. Each carried + write is attributed in audit to the newly created admin as the + accountable principal the transaction establishes; the admin + authenticates afterwards by ordinary login. Either the whole transaction commits or none of it exists (identity §3.5's atomicity extends over the carried writes). Before finalize the run holds collected values only: no canonical write of any kind @@ -236,11 +288,15 @@ the named authority: closes the epoch, and commits the §3.3 carried values; it has no hierarchy authority and creates no hierarchy node. The seed sequence is derived entirely from canonical state: the seed - parameter the finalize command recorded (the company name) and the - fixed example set (§4.3). A resumed run — including a fresh client - holding none of the original run's transient state — reconstructs - the same ordered sequence and the same deterministic §4.3 keys from - that canonical state alone. + parameter the finalize command recorded (the company name, read + from the `bootstrap.seed-company-name` settings value, §3.1) and + the fixed example set (§4.3). A resumed run — including a fresh + client holding none of the original run's transient state — + reconstructs the same ordered sequence and the same deterministic + §4.3 keys from that canonical state alone. Once the first company + exists the seed parameter is inert bookkeeping: the §4.3 fence, + not the parameter's presence or absence, is what prevents + re-seeding. The first company is created by the ordinary top-level company command under §5.2's eligibility policy, with the new admin as actor, naming the admin as initial `owner` in the same audited operation @@ -351,9 +407,11 @@ collects no sensitive category, so v1 ships no custody surface. ## 5. Seeding authority (resolves contract 2 review NEW-1) -1. During the bootstrap epoch, the bootstrap writer's authority (identity - §3) covers exactly what identity §3 defines: creating the first - account and closing the epoch. It creates no hierarchy node. The +1. During the bootstrap epoch, the bootstrap writer's authority + (identity §3, as amended by the §7.3 finalize extension) covers + creating the first account, closing the epoch, and committing the + §3.3 carried values inside the finalize transaction — nothing + else. It creates no hierarchy node. The first company is created post-epoch by the first admin through the ordinary top-level company command (§5.2), naming that admin as initial `owner` (contract 2 §4.3). No operator-designated third @@ -380,11 +438,21 @@ Binding on the implementing PRs: 1. **D8 mapping witness:** every Gateway call the wizard makes resolves to a contract 5 mapping row, asserted by contract 5 §6.1's outbound-call inventory scoped to the wizard modules — an inventory - that is complete for the wizard because §1.1 forbids a server-side - wizard orchestrator, and the witness statically asserts that - prohibition (no server-side module composes wizard operations). The - inventoried operation set is asserted equal, in both directions, to - the §1.1 declared family list. Wizard modules appear in no + that is complete for the wizard's outbound calls because §1.1 + forbids server-side wizard composition outside the single declared + §3.3 finalize handler, and the witness statically asserts that + scoped prohibition (no server-side module other than the declared + finalize handler composes wizard operations). The inventoried + operation set is asserted equal, in both directions, to the §1.1 + declared family list. **Finalize-write inventory:** the finalize + handler's internal write set is statically enumerated and asserted + equal, in both directions, to the §3.3 carried-value list — the + admin creation and epoch transition plus the settings writes + (including `bootstrap.seed-company-name`), the registration-mode + write, and the applicable JIT writes, nothing else; each internal + write is asserted to invoke the owning command family's ordinary + write implementation (no parallel write path); and the handler is + asserted refused once the epoch has completed (identity §3.4). Wizard modules appear in no class-table writer allowlist (contract 1 §6.3b) and hold no direct database or filesystem access (static assertion, plus a runtime probe that a wizard-context filesystem/database access attempt is @@ -396,7 +464,8 @@ Binding on the implementing PRs: resolves its step set from the bootstrap-status mode field (§2.3), a post-epoch run from contract 6 §2.2's authenticated read; the bootstrap-status response matches §2.3's closed two-field schema - exactly while the epoch is open, and after the epoch completes + exactly while the epoch is open — the epoch field carrying one of + §2.3's two exact wire tokens — and after the epoch completes contains the epoch field only — the mode field absent from the response (closed-field assertion on the response schema in both phases); post-epoch, an unauthenticated mode read through any @@ -472,13 +541,21 @@ Binding on the implementing PRs: executes nothing and returns the recorded outcome (asserted by mutation-count and audit-event-count equality); the same key with a changed payload digest is refused with the single bounded conflict - and executes nothing; a replay by an actor who has since lost + and executes nothing; the same key and payload submitted by a + different authorized actor is refused with the same single bounded + conflict (actor mismatch) and executes nothing; the same key, + actor, and payload submitted against a different authorization + scope is likewise refused (scope mismatch) and executes nothing; a + replay by an actor who has since lost eligibility (identity §7.1 ban) receives the authorization refusal, not the recorded outcome; a submission that failed before commit leaves no fence row and its retry executes; two concurrent resumed runs executing the seed sequence yield exactly one seed set — per key, exactly one mutation and one audit event exist, and the losing - submission received the winner's recorded outcome. + submission received the winner's recorded outcome; and with the + winner's transaction forced to abort, the waiting loser finds no + fence row, executes, and commits exactly one mutation and one + audit event. 8. **Actor-matrix witness (§5.2):** post-bootstrap top-level company creation succeeds for an ordinary authenticated non-admin user (positive), and is refused for an unauthenticated caller and for a @@ -523,9 +600,14 @@ contracts and are not additions: identity §3's bootstrap surface: one transaction carrying the first-admin fields, the collected settings, registration-mode, and JIT values, and the §3.4 seed parameter, committing the carried - writes under the new admin's authority with identity §3.5's - atomicity extended over them. Severable from the rest of this - contract. + writes under the bootstrap writer's amended authority (never by + authenticating the caller as the created admin; §3.3) with + identity §3.5's atomicity extended over them. This item is also a + disclosed, bounded amendment to the §1.1 client-side-composition + rule: the finalize handler is the wizard's single server-side + composed transaction, reachable only while the epoch is open, + witnessed by §6.1's finalize-write inventory. Severable from the + rest of this contract. 4. The idempotency-key field and its same-transaction uniqueness fence, with the §4.3 replay, collision, no-error-replay, and concurrency rules — an addition to contract 5 §4's command @@ -533,7 +615,8 @@ contracts and are not additions: this contract. 5. The presentation-and-submission obligation for applicable mandatory choices on wizard runs (§3.3). -6. The no-server-side-orchestrator architectural constraint (§1.1). +6. The no-server-side-orchestrator architectural constraint (§1.1), + scoped by the single disclosed §3.3 finalize exception (item 3). 7. The wizard-completion-marker presentational-only rule (§1.4). 8. The contract 5 §3.1 rank-6 mapping expansion (§1.1): the rank-6 composition row, which today names only the rank-1 hierarchy and @@ -547,10 +630,24 @@ contracts and are not additions: under contract 5 §5. 9. The contract 6 §2.2 amendment permitting the single unauthenticated pre-epoch mode disclosure through the §2.3 bootstrap-status field; - post-epoch, §2.2's authenticated-only rule is unchanged. -10. The password-only v1 first admin (§3.3): identity §3.6's SSO - variant is not composed by the v1 wizard; the PRD step list's SSO - option is satisfied post-epoch. + post-epoch, §2.2's authenticated-only rule is unchanged. Second + clause: the pin of the `epoch` field's wire enumeration to exactly + the tokens `open` and `completed` (§2.3) — identity §3 names the + two epoch states in prose but defines no wire values, so this + contract fixes them. +10. The password-only v1 first admin (§3.3) — a disclosed **PRD + deviation (deferment)**: PRD Part I §6's initial-user SSO option + is not implemented in v1. Identity §3.6's SSO variant is not + composed by the v1 wizard, the initial user cannot be SSO-created + or SSO-linked in v1, and SSO first becomes available to accounts + created post-epoch (different users, not a satisfaction of the + initial-user field). The deviation narrows the PRD field for v1 + and is severable. +11. The **`bootstrap.seed-company-name`** settings key (§3.1) — the + owning record of the §3.4 seed parameter: written by the finalize + transaction among the ordinary settings writes, read through the + ordinary settings read surface, inert once the first company + exists (§3.4). ## Ruling request -- 2.54.0 From b8b257e1ec302e452de64f89d2d82b78751b40e7 Mon Sep 17 00:00:00 2001 From: fred Date: Wed, 26 Aug 2026 21:08:33 -0500 Subject: [PATCH 06/15] docs: onboarding-wizard contract revision 6 (sol r5 residual F7 + N7 seed immutability) --- docs/requirements/onboarding-wizard.md | 83 ++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 13 deletions(-) diff --git a/docs/requirements/onboarding-wizard.md b/docs/requirements/onboarding-wizard.md index 858bb6c6..a8dd87cb 100644 --- a/docs/requirements/onboarding-wizard.md +++ b/docs/requirements/onboarding-wizard.md @@ -88,6 +88,22 @@ different-scope collision, and the aborted-winner concurrency case deviation (deferment) of the initial-user SSO option, not as its satisfaction (N6). +Revision 6 (sol re-review 4: residual F7, new N7): the seed parameter +becomes **immutable seed provenance** — `bootstrap.seed-company-name` +is written exactly once by the finalize transaction and any later +write to it is refused, so a re-run's re-derived seed sequence is +byte-stable and replays cleanly against the §4.3 fence instead of +colliding with it; the current company name's canonical home is the +company row (renamed through the ordinary hierarchy command), and the +wizard renders the row, never the provenance value (N7). §6.3 gains +the changed-name re-run and rename-then-re-run witnesses plus the +immutability refusal probe, with a mutated-digest collision control +(N7). §6.7 adds the two missing envelope witnesses: the same raw key +under two different operations executes both independently +(pair-uniqueness, not global key-uniqueness), and a fresh +authorization refusal records no fence row so an authorized retry +executes afresh (F7). + Scope: the Gateway-backed product onboarding wizard. Out of scope: the host-local install wizard (`mosaic wizard`, which drives host install and gateway bootstrap and is not this artifact — audit REPORT.md layer 3); @@ -215,10 +231,21 @@ the named authority: seed parameter, a disclosed drafting addition (§7.11) — committed by step 3's finalize command among the ordinary settings writes and readable through the ordinary settings read surface. The company - name feeds step 4's company creation. On a first run this step - collects and validates only — the collected values are committed by - step 3's finalize command (§3.3). On a re-run (an authenticated admin exists) - it applies directly through the settings commands. + name feeds step 4's company creation. **The seed parameter is + immutable seed provenance:** it is written exactly once, by the + finalize transaction, and every later write to the key — through + any surface, any actor — is refused by the settings family (a + disclosed immutable-key rule, §7.11). It records what the seed + sequence was derived from, not what the company is currently + named: the current name's canonical home is the company row, and + renaming the seed company is the ordinary hierarchy rename + command against that row, never a settings edit. On a first run + this step collects and validates only — the collected values are + committed by step 3's finalize command (§3.3). On a re-run (an + authenticated admin exists) the system name and other settings + apply directly through the settings commands; the company-name + field renders the company row's current canonical name and + applies a change as the hierarchy rename. 2. **Component choices.** Mosaic Comms/Matrix vs external; Mosaic SSO/Authentik vs external; Mosaic DB/PostgreSQL vs external; vector DB (D4). v1 records the choices as settings (first run: collected, @@ -293,10 +320,15 @@ the named authority: the fixed example set (§4.3). A resumed run — including a fresh client holding none of the original run's transient state — reconstructs the same ordered sequence and the same deterministic - §4.3 keys from that canonical state alone. Once the first company - exists the seed parameter is inert bookkeeping: the §4.3 fence, - not the parameter's presence or absence, is what prevents - re-seeding. + §4.3 keys from that canonical state alone. Because the seed + parameter is immutable (§3.1), the re-derived sequence is + byte-stable across every re-run and resume: the same keys carry + the same payload digests, so already-committed mutations replay + (recorded outcomes) rather than collide, regardless of any + hierarchy rename performed since. The §4.3 fence, not the + parameter's presence or absence, is what prevents re-seeding; the + parameter is provenance the derivation reads, never a value any + later step may change. The first company is created by the ordinary top-level company command under §5.2's eligibility policy, with the new admin as actor, naming the admin as initial `owner` in the same audited operation @@ -486,6 +518,18 @@ Binding on the implementing PRs: **Canonical-state drift:** mutate state outside the wizard (a rename through the hierarchy command), then re-run — the wizard renders the current canonical value, not a stored answer. + **Seed-provenance witnesses (§3.1, §3.4):** rename the seed + company through the hierarchy command, then re-run to completion — + the re-derived seed sequence carries the same keys and payload + digests as the first run (asserted digest equality), every seed + submission replays with the recorded outcome, zero new nodes are + created, and the company keeps its renamed name; a re-run + submitting a different company name renames the company row and + commits no settings write to the seed key; a direct post-epoch + settings write to `bootstrap.seed-company-name`, by any actor, is + refused; and a control run that force-mutates a seed payload + digest receives the §4.3 collision refusal, proving the fence + still discriminates. **Second company:** a re-run creating a second company succeeds with the running user as actor and names the creator as `owner` in the same audited operation — asserted on the single audit event naming @@ -555,7 +599,16 @@ Binding on the implementing PRs: submission received the winner's recorded outcome; and with the winner's transaction forced to abort, the waiting loser finds no fence row, executes, and commits exactly one mutation and one - audit event. + audit event. Two envelope-shape witnesses complete the §4.3 + coverage: the same raw key submitted to two DIFFERENT operations + executes both independently, each committing its own mutation, + audit event, and fence row — proving uniqueness is the + (operation, key) pair, so a globally key-unique fence fails this + witness; and a fresh submission refused by authorization records + no fence row — the same actor, made eligible, retries the same + (operation, key, payload) and the command executes afresh, + distinguishing an authorization refusal from a pre-commit failure + and proving refusals record nothing. 8. **Actor-matrix witness (§5.2):** post-bootstrap top-level company creation succeeds for an ordinary authenticated non-admin user (positive), and is refused for an unauthenticated caller and for a @@ -644,10 +697,14 @@ contracts and are not additions: initial-user field). The deviation narrows the PRD field for v1 and is severable. 11. The **`bootstrap.seed-company-name`** settings key (§3.1) — the - owning record of the §3.4 seed parameter: written by the finalize - transaction among the ordinary settings writes, read through the - ordinary settings read surface, inert once the first company - exists (§3.4). + owning record of the §3.4 seed parameter: written exactly once by + the finalize transaction among the ordinary settings writes, read + through the ordinary settings read surface, and **immutable + thereafter** — the settings family refuses every later write to + the key, from any surface and any actor. The key is seed + provenance for §3.4's derivation; the current company name lives + on the company row and changes only through the ordinary + hierarchy rename (§3.1, witnesses §6.3). ## Ruling request -- 2.54.0 From 84fe8b6ef130875fbd10a7567928006c3bc949ee Mon Sep 17 00:00:00 2001 From: fred Date: Wed, 26 Aug 2026 21:43:57 -0500 Subject: [PATCH 07/15] docs: onboarding-wizard contract revision 7 (sol r6 F6 residual + NEW-8) --- docs/requirements/onboarding-wizard.md | 108 +++++++++++++++++++++---- 1 file changed, 93 insertions(+), 15 deletions(-) diff --git a/docs/requirements/onboarding-wizard.md b/docs/requirements/onboarding-wizard.md index a8dd87cb..11d422fa 100644 --- a/docs/requirements/onboarding-wizard.md +++ b/docs/requirements/onboarding-wizard.md @@ -104,6 +104,24 @@ under two different operations executes both independently authorization refusal records no fence row so an authorized retry executes afresh (F7). +Revision 7 (sol re-review 5: residual F6, new N8): the fence gains a +**replay mode** — each keyed submission declares `actor-bound` +(default) or `shared`, recorded in the fence row; a shared row replays +for any freshly authorized actor whose scope and payload digest match, +while an actor-bound row keeps the revision-6 actor-equality rule, and +a declared-mode mismatch is a collision. Every §3.4 seed submission +declares shared, reconciling actor-bound replay with re-runnability: +the seed set is a per-epoch singleton, so a fresh-authorized second +admin's no-change re-run replays the first run's recorded outcomes +instead of colliding (N8). §6.7's different-actor witness splits into +the actor-bound refusal and the shared replay, plus a mode-mismatch +collision and a shared-row lost-eligibility refusal; §6.3 adds the +second-admin re-run witness (N8). §6.3's changed-name case now +continues through step 4 with the bookkeeping cache deleted — original +digests asserted, every seed outcome replayed, zero new nodes, run +completed — proving the dirty form value never enters derivation and +the renamed row resolves cache-independently (F6). + Scope: the Gateway-backed product onboarding wizard. Out of scope: the host-local install wizard (`mosaic wizard`, which drives host install and gateway bootstrap and is not this artifact — audit REPORT.md layer 3); @@ -325,7 +343,10 @@ the named authority: byte-stable across every re-run and resume: the same keys carry the same payload digests, so already-committed mutations replay (recorded outcomes) rather than collide, regardless of any - hierarchy rename performed since. The §4.3 fence, not the + hierarchy rename performed since — and, because every seed + submission declares §4.3's `shared` replay mode, they replay for + whichever currently eligible admin performs the re-run, not only + the actor the fence rows record. The §4.3 fence, not the parameter's presence or absence, is what prevents re-seeding; the parameter is provenance the derivation reads, never a value any later step may change. @@ -375,18 +396,32 @@ collects no sensitive category, so v1 ships no custody surface. as the mutation and its audit event: the key, the operation identifier, the acting principal, the mutation's authorization scope (the target company, or the platform scope for top-level - operations), a digest of the canonicalized request payload, and + operations), a digest of the canonicalized request payload, the + submission's declared **replay mode** — `actor-bound` (the + default) or `shared` (§7.4) — and a reference to the committed outcome. Fence uniqueness is the pair (operation identifier, key). - **Replay.** A submission whose (operation, key) pair is recorded is first authorized exactly as a fresh submission would be — an actor who is not authorized receives the authorization refusal, - never the recorded outcome. If authorization passes and the - recorded actor, scope, and payload digest all equal the - submission's, the command executes nothing and returns the - recorded outcome. + never the recorded outcome. If authorization passes, the + submission's declared replay mode equals the recorded row's, and + the recorded scope and payload digest equal the submission's, + the row's replay mode decides the actor rule: an `actor-bound` + row additionally requires the recorded actor to equal the + submitter; a `shared` row replays for **any** authorized + submitter. On replay the command executes nothing and returns + the recorded outcome. A shared replay adds no audit event and + leaves the fence row untouched: audit attributes each mutation + to the actor who performed it, the recorded acting principal is + immutable, and the recorded outcome discloses nothing beyond + what the replaying actor's just-verified live authorization + already grants — the same mutation that actor could have + performed afresh had no fence row existed. - **Collision.** A submission whose (operation, key) pair is - recorded but whose actor, scope, or payload digest differs is + recorded but whose scope, payload digest, or declared replay + mode differs — or, against an `actor-bound` row, whose actor + differs — is refused with a single bounded conflict error (contract 5 §4.2); it executes nothing and discloses nothing of the recorded outcome. @@ -405,7 +440,15 @@ collects no sensitive category, so v1 ships no custody surface. The wizard submits deterministic keys derived from the bootstrap epoch and the seed role (e.g. epoch id + "seed-company") for every - seed mutation. Correlation ids (contract 5 §4.3) remain pure trace + seed mutation, and every seed submission declares the `shared` + replay mode: the seed set is a per-epoch singleton (§4.4), derived + from immutable provenance (§3.4), so whichever currently eligible + admin performs a re-run or resume must replay the recorded + outcomes rather than collide on actor identity — re-runnability is + not restricted to the original acting admin (witness §6.3). All + other submissions default to `actor-bound`; nothing in this + contract declares `shared` outside the seed sequence. Correlation + ids (contract 5 §4.3) remain pure trace linkage and carry no idempotency semantics. Wizard bookkeeping of seed node ids remains a cache (§1.3): losing it cannot cause duplication, because the fence is @@ -519,17 +562,36 @@ Binding on the implementing PRs: rename through the hierarchy command), then re-run — the wizard renders the current canonical value, not a stored answer. **Seed-provenance witnesses (§3.1, §3.4):** rename the seed - company through the hierarchy command, then re-run to completion — + company through the hierarchy command, delete the wizard's + seed-node bookkeeping cache, then re-run to completion — the re-derived seed sequence carries the same keys and payload digests as the first run (asserted digest equality), every seed submission replays with the recorded outcome, zero new nodes are - created, and the company keeps its renamed name; a re-run + created, the run completes, and the company keeps its renamed + name — the deleted cache proving the renamed row was resolved + from canonical state alone (§1.3), not from a stored id. A re-run submitting a different company name renames the company row and - commits no settings write to the seed key; a direct post-epoch + commits no settings write to the seed key, **and that same run**, + with the bookkeeping cache likewise deleted beforehand, continues + through step 4 deriving the seed sequence from the immutable seed + parameter — asserted to carry the first run's keys and payload + digests, not digests of the newly submitted name — replays every + seed outcome, creates zero new nodes, zero new users, and zero + new grants, and completes; a direct post-epoch settings write to `bootstrap.seed-company-name`, by any actor, is refused; and a control run that force-mutates a seed payload digest receives the §4.3 collision refusal, proving the fence still discriminates. + **Second-admin re-run witness (§4.3 shared replay):** after admin + A's completed first run, create a second account B and grant it + platform-admin and the hierarchy authority the seed commands + require (B is freshly authorized, and no text restricts re-runs + to A); delete the bookkeeping cache; B re-runs the wizard to + completion making no changes — every seed submission replays with + A's recorded outcome (asserted by mutation-count and + audit-event-count equality across the run, the fence rows still + recording A as acting principal), zero new hierarchy nodes, + users, grants, or example content exist, and the run completes. **Second company:** a re-run creating a second company succeeds with the running user as actor and names the creator as `owner` in the same audited operation — asserted on the single audit event naming @@ -586,13 +648,21 @@ Binding on the implementing PRs: mutation-count and audit-event-count equality); the same key with a changed payload digest is refused with the single bounded conflict and executes nothing; the same key and payload submitted by a - different authorized actor is refused with the same single bounded - conflict (actor mismatch) and executes nothing; the same key, + different authorized actor against an **actor-bound** fence row (a + control row recorded outside the seed sequence) is refused with + the same single bounded conflict (actor mismatch) and executes + nothing, while against a **shared** row (every §3.4 seed fence) it + replays with the recorded outcome, executing nothing and adding no + audit event, the fence row still recording the original actor; a + submission whose declared replay mode differs from the recorded + row's is refused with the single bounded conflict and executes + nothing; the same key, actor, and payload submitted against a different authorization scope is likewise refused (scope mismatch) and executes nothing; a replay by an actor who has since lost eligibility (identity §7.1 ban) receives the authorization refusal, - not the recorded outcome; a submission that failed before commit + not the recorded outcome — asserted against a shared seed fence, + proving shared replay never bypasses fresh authorization; a submission that failed before commit leaves no fence row and its retry executes; two concurrent resumed runs executing the seed sequence yield exactly one seed set — per key, exactly one mutation and one audit event exist, and the losing @@ -665,7 +735,15 @@ contracts and are not additions: fence, with the §4.3 replay, collision, no-error-replay, and concurrency rules — an addition to contract 5 §4's command envelope, proposed and ratified here, severable from the rest of - this contract. + this contract. Including the **replay mode**: each keyed + submission declares `actor-bound` (default) or `shared`, the + declaration is recorded in the fence row, a shared row replays + for any freshly authorized actor whose scope and payload digest + match while an actor-bound row also requires recorded-actor + equality, and a declared-mode mismatch is a collision. This + contract declares `shared` for exactly the §3.4 seed sequence — + deterministic, epoch-scoped singletons any eligible admin must be + able to re-run (§4.1) — and for nothing else. 5. The presentation-and-submission obligation for applicable mandatory choices on wizard runs (§3.3). 6. The no-server-side-orchestrator architectural constraint (§1.1), -- 2.54.0 From eff3b914784c255d03f3e070876e767b0eb7feaa Mon Sep 17 00:00:00 2001 From: fred Date: Wed, 26 Aug 2026 22:30:22 -0500 Subject: [PATCH 08/15] docs: onboarding-wizard contract revision 8 (sol r7 NEW-9: shared replay target-result authorization, seed-only boundary, replay access event) --- docs/requirements/onboarding-wizard.md | 213 ++++++++++++++++++++----- 1 file changed, 177 insertions(+), 36 deletions(-) diff --git a/docs/requirements/onboarding-wizard.md b/docs/requirements/onboarding-wizard.md index 11d422fa..57ed7aa2 100644 --- a/docs/requirements/onboarding-wizard.md +++ b/docs/requirements/onboarding-wizard.md @@ -122,6 +122,36 @@ digests asserted, every seed outcome replayed, zero new nodes, run completed — proving the dirty form value never enters derivation and the renamed row resolves cache-independently (F6). +Revision 8 (sol re-review 6: NEW-9): shared replay gains a +**target-result authorization boundary** — returning a recorded +outcome is a read of the canonical records it references, authorized +separately from fresh-mutation authorization: a shared replay +requires the submitter to hold, at replay time, read authority on +every record the recorded outcome references, and a submitter who +passes the fresh-mutation check but lacks target read authority is +refused with the same constant-shape single bounded conflict as a +§4.3 collision, identifying no record — the response confirms no +tenant target's existence, preserving RBAC §7's no-existence-oracle +rule (§4.3). `shared` becomes server-verified seed-only policy: the +command layer re-derives the epoch's **canonical seed tuple set** +from canonical state alone and refuses any shared declaration whose +(operation, key, scope, digest) tuple is not a member, and a missing +seed fence is originated only by a submission passing the full +fresh-mutation authorization for that seed command — a wrong actor +cannot win an unrecorded seed key, and no shared row can exist +outside the seed sequence (§4.3). An authorized shared replay appends +a **replay access event** — a distinct non-mutation audit event class +recording the replaying actor, the current request's correlation ids, +and the fence row whose outcome was returned — so every access to a +recorded outcome is attributed without a duplicate semantic mutation +event, while the fence row and the mutation event immutably retain +the original actor (§4.3, §7.4). §6.7 adds the unauthorized-target +replay refusal, the non-seed shared declaration refusal, and the +unrecorded-seed-key race; §6.3's second-admin witness grants B +explicit read authority on the recorded seed targets and asserts B's +replay access events; replay-affected count equalities are scoped to +the mutation event class (NEW-9). + Scope: the Gateway-backed product onboarding wizard. Out of scope: the host-local install wizard (`mosaic wizard`, which drives host install and gateway bootstrap and is not this artifact — audit REPORT.md layer 3); @@ -345,8 +375,9 @@ the named authority: (recorded outcomes) rather than collide, regardless of any hierarchy rename performed since — and, because every seed submission declares §4.3's `shared` replay mode, they replay for - whichever currently eligible admin performs the re-run, not only - the actor the fence rows record. The §4.3 fence, not the + whichever currently eligible admin holding §4.3 target-result + read authority on the recorded seed targets performs the re-run, + not only the actor the fence rows record. The §4.3 fence, not the parameter's presence or absence, is what prevents re-seeding; the parameter is provenance the derivation reads, never a value any later step may change. @@ -409,15 +440,37 @@ collects no sensitive category, so v1 ships no custody surface. the recorded scope and payload digest equal the submission's, the row's replay mode decides the actor rule: an `actor-bound` row additionally requires the recorded actor to equal the - submitter; a `shared` row replays for **any** authorized - submitter. On replay the command executes nothing and returns - the recorded outcome. A shared replay adds no audit event and - leaves the fence row untouched: audit attributes each mutation - to the actor who performed it, the recorded acting principal is - immutable, and the recorded outcome discloses nothing beyond - what the replaying actor's just-verified live authorization - already grants — the same mutation that actor could have - performed afresh had no fence row existed. + submitter; a `shared` row replays for an authorized submitter + who also passes **target-result authorization**. Returning a + recorded outcome is a read of the canonical records that + outcome references, and fresh-mutation authorization is not + read authorization on records another actor already created: a + shared replay therefore additionally requires the submitter to + hold, at replay time, read authority on every canonical record + the recorded outcome references, under each record's owning + contract's read-authorization rules (for the seed targets, the + contract 2 grant model — RBAC §1.1 platform-admin standing + confers none of it). A submitter who passes the fresh-mutation + check but fails target-result authorization is refused with the + same single bounded conflict error as a collision below — + constant in shape, identifying no record, disclosing nothing of + the recorded outcome or its targets — so the refusal confirms + no tenant target's existence or identity and RBAC §7's + no-existence-oracle rule is preserved. On replay the command + executes nothing and returns the recorded outcome. A shared + replay adds no **mutation** audit event and leaves the fence + row untouched — audit attributes each mutation to the actor who + performed it, and the recorded acting principal is immutable — + but it appends a **replay access event**: a distinct + non-mutation audit event class recording the replaying actor as + accessing principal, the current request's correlation ids + (contract 5 §4.3), and a reference to the fence row whose + outcome was returned. The current submission's correlation + travels only in that access event and the trace layer; the + recorded outcome and its original mutation event are returned + and retained unchanged. Every access to a recorded outcome is + thereby attributed to its accessing actor without a duplicate + semantic mutation event ever existing. - **Collision.** A submission whose (operation, key) pair is recorded but whose scope, payload digest, or declared replay mode differs — or, against an `actor-bound` row, whose actor @@ -425,6 +478,30 @@ collects no sensitive category, so v1 ships no custody surface. refused with a single bounded conflict error (contract 5 §4.2); it executes nothing and discloses nothing of the recorded outcome. + - **Shared-declaration boundary.** `shared` is a server-verified, + seed-only policy, never a caller privilege. The command layer + re-derives, from canonical state alone, the **canonical seed + tuple set** of the current bootstrap epoch: the exact + (operation identifier, key, authorization scope, payload + digest) tuples of §3.4's ordered seed sequence, derived from + the epoch id, the immutable `bootstrap.seed-company-name` + provenance, and the fixed example set — the same derivation + §3.4 requires of a fresh client. A submission declaring + `shared` whose tuple is not a member of that set is refused + with a single bounded refusal (contract 5 §4.2 validation + class) that executes nothing and records no fence row. No + other operation can carry a shared declaration, so no shared + fence row can exist outside the seed sequence — the seed-only + rule is enforced by the command layer, not by wizard + convention. **Origination.** A shared fence row not yet + recorded is originated only by executing its seed mutation: + the originating submission passes the full fresh-mutation + authorization for that seed command (§5.2 eligibility plus the + hierarchy authority the command itself requires), and a + refused submission records no row (no-error replay below). An + actor without the seed mutation's authority therefore cannot + win an unrecorded seed key, and the recorded actor of every + shared row was authorized for the mutation the row fences. - **No error replay.** The fence row commits only with its mutation, so only committed outcomes are ever recorded. A failed or refused submission records no fence row; a retry executes @@ -433,8 +510,9 @@ collects no sensitive category, so v1 ships no custody surface. pair serialize on the fence's unique constraint: exactly one executes. The loser waits for the winner's transaction to resolve; if it committed, the loser is handled as a replay - (authorization first, then the recorded outcome, or the - collision refusal on mismatch); if it aborted, no fence row + (fresh-mutation authorization first — and, for a shared row, + target-result authorization — then the recorded outcome, or + the collision refusal on mismatch); if it aborted, no fence row exists and the loser executes. The loser never performs a second mutation and is never left without a defined response. @@ -443,13 +521,19 @@ collects no sensitive category, so v1 ships no custody surface. seed mutation, and every seed submission declares the `shared` replay mode: the seed set is a per-epoch singleton (§4.4), derived from immutable provenance (§3.4), so whichever currently eligible - admin performs a re-run or resume must replay the recorded + admin holding target-result read authority on the recorded seed + targets performs a re-run or resume must replay the recorded outcomes rather than collide on actor identity — re-runnability is not restricted to the original acting admin (witness §6.3). All other submissions default to `actor-bound`; nothing in this - contract declares `shared` outside the seed sequence. Correlation + contract declares `shared` outside the seed sequence, and the + shared-declaration boundary makes that exclusivity + server-enforced: a shared declaration outside the canonical seed + tuple set is refused, not merely unconventional. Correlation ids (contract 5 §4.3) remain pure trace - linkage and carry no idempotency semantics. Wizard bookkeeping of + linkage and carry no idempotency semantics; a shared replay's + current correlation is carried by its §4.3 replay access event, + never by rewriting the recorded outcome or its mutation event. Wizard bookkeeping of seed node ids remains a cache (§1.3): losing it cannot cause duplication, because the fence is canonical command-layer state. A re-run offers to create @@ -584,14 +668,21 @@ Binding on the implementing PRs: still discriminates. **Second-admin re-run witness (§4.3 shared replay):** after admin A's completed first run, create a second account B and grant it - platform-admin and the hierarchy authority the seed commands - require (B is freshly authorized, and no text restricts re-runs - to A); delete the bookkeeping cache; B re-runs the wizard to - completion making no changes — every seed submission replays with - A's recorded outcome (asserted by mutation-count and - audit-event-count equality across the run, the fence rows still - recording A as acting principal), zero new hierarchy nodes, - users, grants, or example content exist, and the run completes. + platform-admin, the hierarchy authority the seed commands + require, and **explicit target-result read authority on every + recorded seed target** — a contract 2 grant conferring read on + the seed company and its seeded children (B is freshly + authorized with explicit target authority, and no text restricts + re-runs to A); delete the bookkeeping cache; B re-runs the + wizard to completion making no changes — every seed submission + replays with A's recorded outcome (asserted by mutation-count + and mutation-audit-event-count equality across the run, the + fence rows still recording A as acting principal), exactly one + §4.3 replay access event exists per replayed submission, each + naming B as accessing principal and carrying B's run's + correlation ids while referencing A's fence row, zero new + hierarchy nodes, users, grants, or example content exist, and + the run completes. **Second company:** a re-run creating a second company succeeds with the running user as actor and names the creator as `owner` in the same audited operation — asserted on the single audit event naming @@ -645,15 +736,19 @@ Binding on the implementing PRs: outcome. **Idempotency-fence witnesses (§4.3):** re-submitting a committed seed command with its same key, actor, and payload executes nothing and returns the recorded outcome (asserted by - mutation-count and audit-event-count equality); the same key with a + mutation-count and mutation-audit-event-count equality); the same + key with a changed payload digest is refused with the single bounded conflict and executes nothing; the same key and payload submitted by a different authorized actor against an **actor-bound** fence row (a control row recorded outside the seed sequence) is refused with the same single bounded conflict (actor mismatch) and executes - nothing, while against a **shared** row (every §3.4 seed fence) it - replays with the recorded outcome, executing nothing and adding no - audit event, the fence row still recording the original actor; a + nothing, while against a **shared** row (every §3.4 seed fence) a + different authorized actor holding target-result read authority + replays with the recorded outcome, executing nothing, adding no + mutation audit event, and appending exactly one replay access + event naming the replaying actor and its correlation ids, the + fence row still recording the original actor; a submission whose declared replay mode differs from the recorded row's is refused with the single bounded conflict and executes nothing; the same key, @@ -662,14 +757,42 @@ Binding on the implementing PRs: replay by an actor who has since lost eligibility (identity §7.1 ban) receives the authorization refusal, not the recorded outcome — asserted against a shared seed fence, - proving shared replay never bypasses fresh authorization; a submission that failed before commit + proving shared replay never bypasses fresh authorization. + **Target-authorization and boundary witnesses (§4.3, NEW-9):** + an **unauthorized-target shared replay** — a platform admin + holding §5.2 fresh-create eligibility but no contract 2 grant on + the seed company derives the canonical seed tuple per §3.4 and + submits it with the recorded `shared` mode — is refused with the + single bounded conflict, and the refusal response is asserted + byte-shape-identical to the changed-digest collision refusal + above (same error class, same fields, no record identifier, no + scope detail beyond the caller's own submission), executes + nothing, changes no fence row, and appends no mutation audit + event — proving the recorded target is neither returned nor + confirmed to exist and RBAC §7's no-existence-oracle rule holds + at the fence; a **non-seed shared declaration** — an ordinary + hierarchy or content command whose tuple is outside the + canonical seed tuple set, submitted with `shared` declared by an + actor fully authorized for the mutation — is refused with the + §4.3 validation refusal, executes nothing, and records no fence + row (asserted absent); an **unrecorded-seed-key race** — with + one seed tuple deliberately left unrecorded, an actor lacking + that seed command's hierarchy authority races the authorized + resume for the same (operation, key): the unauthorized + submission receives the authorization refusal and records no + fence row, the authorized submission executes afresh, and the + resulting fence row records the authorized actor — proving + origination of a missing seed fence is bound to full + fresh-mutation authorization; a submission that failed before commit leaves no fence row and its retry executes; two concurrent resumed runs executing the seed sequence yield exactly one seed set — per - key, exactly one mutation and one audit event exist, and the losing - submission received the winner's recorded outcome; and with the + key, exactly one mutation and one mutation audit event exist, and + the losing + submission received the winner's recorded outcome (its access + attributed by the §4.3 replay access event); and with the winner's transaction forced to abort, the waiting loser finds no fence row, executes, and commits exactly one mutation and one - audit event. Two envelope-shape witnesses complete the §4.3 + mutation audit event. Two envelope-shape witnesses complete the §4.3 coverage: the same raw key submitted to two DIFFERENT operations executes both independently, each committing its own mutation, audit event, and fence row — proving uniqueness is the @@ -738,11 +861,29 @@ contracts and are not additions: this contract. Including the **replay mode**: each keyed submission declares `actor-bound` (default) or `shared`, the declaration is recorded in the fence row, a shared row replays - for any freshly authorized actor whose scope and payload digest - match while an actor-bound row also requires recorded-actor - equality, and a declared-mode mismatch is a collision. This + for a freshly authorized actor whose scope and payload digest + match and who additionally passes the §4.3 target-result + authorization — read authority on every canonical record the + recorded outcome references, refused otherwise with the + constant-shape conflict that preserves RBAC §7's + no-existence-oracle rule — while an actor-bound row also + requires recorded-actor + equality, and a declared-mode mismatch is a collision. Including + the **shared-declaration boundary**: `shared` is server-verified + against the epoch's canonical seed tuple set re-derived from + canonical state, a shared declaration outside that set is + refused recording nothing, and origination of a missing seed + fence requires the full fresh-mutation authorization for that + seed command (§4.3). Including the **replay access event**: a + distinct non-mutation audit event class, appended on every + shared replay, recording the accessing actor, the current + request's correlation ids, and the fence row returned — the + disclosed mechanism by which a replay's access is attributed + without a duplicate semantic mutation event, the fence row and + mutation event immutably retaining the original actor. This contract declares `shared` for exactly the §3.4 seed sequence — - deterministic, epoch-scoped singletons any eligible admin must be + deterministic, epoch-scoped singletons any eligible, + target-authorized admin must be able to re-run (§4.1) — and for nothing else. 5. The presentation-and-submission obligation for applicable mandatory choices on wizard runs (§3.3). -- 2.54.0 From 535ac2d8603f7e7c984e001a7d49010fc06ee968 Mon Sep 17 00:00:00 2001 From: fred Date: Wed, 26 Aug 2026 22:56:59 -0500 Subject: [PATCH 09/15] docs: onboarding-wizard contract revision 9 (sol r8 NEW-9 residual: prefix-derived seed tuples + seed-origin gate; NEW-10: target-result authorization on every replay mode) --- docs/requirements/onboarding-wizard.md | 262 +++++++++++++++++++------ 1 file changed, 204 insertions(+), 58 deletions(-) diff --git a/docs/requirements/onboarding-wizard.md b/docs/requirements/onboarding-wizard.md index 57ed7aa2..187846ee 100644 --- a/docs/requirements/onboarding-wizard.md +++ b/docs/requirements/onboarding-wizard.md @@ -152,6 +152,33 @@ explicit read authority on the recorded seed targets and asserts B's replay access events; replay-affected count equalities are scoped to the mutation event class (NEW-9). +Revision 9 (sol re-review 7: NEW-9 residual, NEW-10): the seed +boundary becomes server-complete and oracle-free. The **canonical +seed key set** is fence-independent (epoch id plus the fixed +seed-role list), while the full tuple set is **prefix-derived**: each +position's scope and payload digest derive from the committed +predecessors' recorded fence outcomes, the derivable members at any +state are the committed prefix plus the next unrecorded position, +the pre-first-company state derives exactly the seed-company tuple, +and an out-of-order shared declaration is refused recording nothing +(§4.3, §3.4). The epoch gains one immutable **seed-origin account** +— the first admin the finalize transaction created — and a +**seed-boundary gate** evaluated after fresh-mutation authorization +and before fence presence can affect the response: a submitter on a +canonical seed key who is neither the seed-origin account nor holds +target-result read authority on the position's recorded targets +receives the constant-shape bounded conflict, identical across the +recorded and unrecorded worlds, creating nothing in either — so +§5.2 eligibility alone can never originate a seed fence and the +eligible-wrong-actor race on the top-level seed-company key is +closed (NEW-9; witnesses §6.7: the two-world control and the +top-level origination race). Target-result authorization extends to +**every replay mode**: an `actor-bound` replay requires the recorded +actor to hold live read authority on every record the recorded +outcome references, actor equality remaining an additional +condition, never a substitute — a creator whose grants were revoked +is refused, not replayed (NEW-10; §6.7 lost-target-grant witness). + Scope: the Gateway-backed product onboarding wizard. Out of scope: the host-local install wizard (`mosaic wizard`, which drives host install and gateway bootstrap and is not this artifact — audit REPORT.md layer 3); @@ -368,7 +395,11 @@ the named authority: the fixed example set (§4.3). A resumed run — including a fresh client holding none of the original run's transient state — reconstructs the same ordered sequence and the same deterministic - §4.3 keys from that canonical state alone. Because the seed + §4.3 keys from that canonical state alone, prefix-wise: each + position's scope and payload derive from the committed + predecessors' recorded outcomes, so the run walks the order and + never precomputes a tuple past the next unrecorded position + (§4.3 shared-declaration boundary). Because the seed parameter is immutable (§3.1), the re-derived sequence is byte-stable across every re-run and resume: the same keys carry the same payload digests, so already-committed mutations replay @@ -382,9 +413,13 @@ the named authority: parameter is provenance the derivation reads, never a value any later step may change. The first company is created by the ordinary top-level company - command under §5.2's eligibility policy, with the new admin as actor, + command under §5.2's eligibility policy, with the new admin — the + epoch's §4.3 seed-origin account — as actor, naming the admin as initial `owner` in the same audited operation - (contract 2 §4.3). The initial estate, initial project, and initial + (contract 2 §4.3); the §4.3 seed-boundary gate reserves + origination of the canonical seed tuples to that account, while + the same command outside the canonical seed key set follows §5.2 + unchanged. The initial estate, initial project, and initial workspace with seeded example data (D4, D11) follow through hierarchy commands (contract 1 §5.1) under the admin's `owner` authority (contract 2 §4.3). Seeded examples are ordinary workspace content @@ -438,19 +473,24 @@ collects no sensitive category, so v1 ships no custody surface. never the recorded outcome. If authorization passes, the submission's declared replay mode equals the recorded row's, and the recorded scope and payload digest equal the submission's, - the row's replay mode decides the actor rule: an `actor-bound` - row additionally requires the recorded actor to equal the - submitter; a `shared` row replays for an authorized submitter - who also passes **target-result authorization**. Returning a + the submitter must pass **target-result authorization** — in + every replay mode. Returning a recorded outcome is a read of the canonical records that - outcome references, and fresh-mutation authorization is not - read authorization on records another actor already created: a - shared replay therefore additionally requires the submitter to + outcome references, and neither fresh-mutation authorization + nor recorded-actor identity is + read authorization on those records: every replay, `shared` or + `actor-bound`, requires the submitter to hold, at replay time, read authority on every canonical record the recorded outcome references, under each record's owning contract's read-authorization rules (for the seed targets, the contract 2 grant model — RBAC §1.1 platform-admin standing - confers none of it). A submitter who passes the fresh-mutation + confers none of it). Actor equality is the additional + `actor-bound` condition, never a substitute for target-result + authorization: an `actor-bound` row replays only for the + recorded actor, and only while that actor holds live + target-result read authority — an original actor whose grants + on the referenced records were since revoked is refused, not + replayed (witness §6.7). A submitter who passes the fresh-mutation check but fails target-result authorization is refused with the same single bounded conflict error as a collision below — constant in shape, identifying no record, disclosing nothing of @@ -479,29 +519,77 @@ collects no sensitive category, so v1 ships no custody surface. it executes nothing and discloses nothing of the recorded outcome. - **Shared-declaration boundary.** `shared` is a server-verified, - seed-only policy, never a caller privilege. The command layer - re-derives, from canonical state alone, the **canonical seed - tuple set** of the current bootstrap epoch: the exact - (operation identifier, key, authorization scope, payload - digest) tuples of §3.4's ordered seed sequence, derived from - the epoch id, the immutable `bootstrap.seed-company-name` - provenance, and the fixed example set — the same derivation - §3.4 requires of a fresh client. A submission declaring - `shared` whose tuple is not a member of that set is refused - with a single bounded refusal (contract 5 §4.2 validation - class) that executes nothing and records no fence row. No - other operation can carry a shared declaration, so no shared - fence row can exist outside the seed sequence — the seed-only - rule is enforced by the command layer, not by wizard - convention. **Origination.** A shared fence row not yet - recorded is originated only by executing its seed mutation: - the originating submission passes the full fresh-mutation - authorization for that seed command (§5.2 eligibility plus the - hierarchy authority the command itself requires), and a - refused submission records no row (no-error replay below). An - actor without the seed mutation's authority therefore cannot - win an unrecorded seed key, and the recorded actor of every - shared row was authorized for the mutation the row fences. + seed-only policy, never a caller privilege. The boundary has a + fence-independent part and a prefix-derived part. The + **canonical seed key set** — the (operation identifier, key) + pairs of §3.4's ordered seed sequence — is derived from the + epoch id and the fixed seed-role list alone: the keys are + deterministic and depend on no generated id, so membership is + decidable before any seed mutation has run and without + consulting the fence table. The full **canonical seed tuple + set** is prefix-derived, because later seed tuples embed + generated ids: the tuple at seed position k — its + authorization scope and payload digest — is derived from the + epoch id, the immutable `bootstrap.seed-company-name` + provenance, the fixed example set, and the canonical recorded + outcomes of positions 1 through k−1 (the ids the committed + predecessor fence rows reference). At any canonical state + exactly these members are derivable: every committed-prefix + tuple (read back from its fence row) and the next unrecorded + tuple in order. Before the first company exists, the derivable + set is exactly the seed-company tuple. A fresh client derives + the same way — §3.4's derivation is this walk: submit the + sequence in order, learning each generated id from the + returned recorded outcome or the submission's own execution, + never precomputing a tuple past the next position. A + submission declaring `shared` whose (operation, key) is + outside the canonical seed key set, whose tuple does not equal + its position's derived tuple, or whose position lies past the + next unrecorded position (out of order) is refused with a + single bounded refusal (contract 5 §4.2 validation class) that + executes nothing and records no fence row. No other operation + can carry a shared declaration, so no shared fence row can + exist outside the seed sequence — the seed-only rule is + enforced by the command layer, not by wizard convention. + - **Seed-boundary gate and origination.** The epoch has one + immutable **seed-origin account**: the account the §3.3 + finalize transaction created as the epoch's first admin + (identity §3) — provenance the epoch record fixes at finalize + and nothing changes afterwards. Every mutating submission + whose (operation, key) is in the canonical seed key set — + whatever replay mode it declares — passes, after + fresh-mutation authorization and before the fence table is + consulted, the **seed-boundary gate**: the submitter is the + seed-origin account, or holds §4.3 target-result read + authority on the canonical records the position's recorded + outcome references. A submitter satisfying neither is refused + with the same single bounded conflict error as a collision — + and because the gate is evaluated without consulting fence + presence, the refusal is byte-shape-identical whether the seed + fence and its targets exist or not: the recorded and + unrecorded worlds are indistinguishable to that submitter, no + mutation or fence row is created in either, and RBAC §7's + no-existence-oracle rule holds at the seed boundary itself, + not merely at an existing fence (witness §6.7). For an + unrecorded position no recorded outcome exists to hold read + authority on, so only the seed-origin account can proceed to + origination: §5.2 eligibility alone never originates a seed + fence, which closes the eligible-wrong-actor race on the + top-level seed-company key. **Origination.** A seed fence row + not yet recorded is originated only by the seed-origin account + executing its seed mutation in order, passing the full + fresh-mutation authorization for that seed command (§5.2 + eligibility plus the hierarchy authority the command itself + requires); a refused submission records no row (no-error + replay below). The recorded actor of every shared row is + therefore the seed-origin account, authorized for the mutation + the row fences. If the seed-origin account is banned or + deleted while seed positions remain unoriginated, those + positions are unoriginatable for this epoch — an accepted + consequence of the immutable boundary; factory reset (§4.2), + which opens a new epoch with a new first admin, is the + recovery path. Recorded positions are unaffected: any admin + holding target-result read authority replays them (§6.3). - **No error replay.** The fence row commits only with its mutation, so only committed outcomes are ever recorded. A failed or refused submission records no fence row; a retry executes @@ -510,8 +598,9 @@ collects no sensitive category, so v1 ships no custody surface. pair serialize on the fence's unique constraint: exactly one executes. The loser waits for the winner's transaction to resolve; if it committed, the loser is handled as a replay - (fresh-mutation authorization first — and, for a shared row, - target-result authorization — then the recorded outcome, or + (fresh-mutation authorization, the seed-boundary gate where + the key is a seed key, and target-result authorization first — + then the recorded outcome, or the collision refusal on mismatch); if it aborted, no fence row exists and the loser executes. The loser never performs a second mutation and is never left without a defined response. @@ -770,20 +859,64 @@ Binding on the implementing PRs: nothing, changes no fence row, and appends no mutation audit event — proving the recorded target is neither returned nor confirmed to exist and RBAC §7's no-existence-oracle rule holds - at the fence; a **non-seed shared declaration** — an ordinary + at the fence; the **two-world seed-boundary control (§4.3 + seed-boundary gate)** — the SAME eligible non-originator actor + submits the exact canonical seed-company tuple in two prepared + worlds: one where the seed fence and its targets exist (the + recorded world) and one freshly finalized epoch where they do + not (the unrecorded world, before any seed command has run) — + and in both worlds receives the identical bounded conflict + refusal (asserted byte-shape-identical across the two worlds and + to the collision refusal), executes nothing, and creates no + mutation, fence row, or hierarchy node in either — proving the + response is computed without consulting fence presence, the + recorded and unrecorded worlds are indistinguishable to that + actor, and no eligible wrong actor can originate the top-level + seed fence; a **non-seed shared declaration** — an ordinary hierarchy or content command whose tuple is outside the canonical seed tuple set, submitted with `shared` declared by an actor fully authorized for the mutation — is refused with the §4.3 validation refusal, executes nothing, and records no fence - row (asserted absent); an **unrecorded-seed-key race** — with - one seed tuple deliberately left unrecorded, an actor lacking - that seed command's hierarchy authority races the authorized + row (asserted absent); an **out-of-order shared declaration** — + with the seed prefix committed through position k, the + seed-origin account submits the position k+2 tuple (past the + next unrecorded position), constructed by the test harness from + ids it obtained out of band — and is refused with the §4.3 + validation refusal, executes nothing, and records no fence row, + proving the prefix-aware derivation rejects positions the + canonical walk cannot yet derive; an **unrecorded-seed-key + race** in two variants — (child variant) with one child seed + tuple deliberately left unrecorded, an actor lacking that seed + command's hierarchy authority races the seed-origin account's resume for the same (operation, key): the unauthorized submission receives the authorization refusal and records no - fence row, the authorized submission executes afresh, and the - resulting fence row records the authorized actor — proving - origination of a missing seed fence is bound to full - fresh-mutation authorization; a submission that failed before commit + fence row, the origin account's submission executes afresh, and + the resulting fence row records the seed-origin account; + (top-level variant, NEW-9) with the seed-company tuple + unrecorded, an eligible platform admin who is not the + seed-origin account — passing §5.2 fresh-mutation authorization + in full, since the top-level command requires no hierarchy + authority — races the seed-origin account for the seed-company + (operation, key): the non-originator receives the constant + bounded conflict refusal from the seed-boundary gate and + records no fence row and no company, the seed-origin account + executes afresh, and the resulting fence row records the + seed-origin account — proving origination of a missing seed + fence is bound to the seed-origin account, not to eligibility + alone; an **actor-bound lost-target-grant replay (NEW-10)** — an + actor creates a non-seed top-level company under an + `actor-bound` key, grants a second account `owner`, and the + second account then revokes every grant the creator held on the + company; the creator, still authenticated and eligible, + resubmits the exact recorded (operation, key, scope, payload, + mode) tuple — actor equality holds but live target-result + authorization fails, so the submission is refused with the + single bounded conflict, executes nothing, returns nothing of + the recorded outcome, and appends no access or mutation event; + re-granting the creator read authority and resubmitting returns + the recorded outcome — proving actor equality is never a + substitute for live target-result read authority on any replay + mode; a submission that failed before commit leaves no fence row and its retry executes; two concurrent resumed runs executing the seed sequence yield exactly one seed set — per key, exactly one mutation and one mutation audit event exist, and @@ -860,21 +993,34 @@ contracts and are not additions: envelope, proposed and ratified here, severable from the rest of this contract. Including the **replay mode**: each keyed submission declares `actor-bound` (default) or `shared`, the - declaration is recorded in the fence row, a shared row replays - for a freshly authorized actor whose scope and payload digest - match and who additionally passes the §4.3 target-result - authorization — read authority on every canonical record the - recorded outcome references, refused otherwise with the - constant-shape conflict that preserves RBAC §7's - no-existence-oracle rule — while an actor-bound row also - requires recorded-actor - equality, and a declared-mode mismatch is a collision. Including + declaration is recorded in the fence row, and every replay — in + either mode — requires the submitter, freshly authorized with + matching scope and payload digest, to pass the §4.3 + target-result authorization: read authority, live at replay + time, on every canonical record the recorded outcome + references, refused otherwise with the constant-shape conflict + that preserves RBAC §7's no-existence-oracle rule. An + actor-bound row additionally requires recorded-actor equality — + never as a substitute for target-result authority — and a + declared-mode mismatch is a collision. Including the **shared-declaration boundary**: `shared` is server-verified - against the epoch's canonical seed tuple set re-derived from - canonical state, a shared declaration outside that set is - refused recording nothing, and origination of a missing seed - fence requires the full fresh-mutation authorization for that - seed command (§4.3). Including the **replay access event**: a + against the epoch's canonical seed key set (fence-independent, + derived from the epoch id and the fixed seed-role list) and its + prefix-derived canonical seed tuple set — each position's scope + and digest derived from the committed predecessors' recorded + outcomes — with a shared declaration outside the key set, off + its position's derived tuple, or past the next unrecorded + position refused recording nothing (§4.3). Including the + **seed-boundary gate**: the epoch's immutable seed-origin + account is the first admin the finalize transaction created; + every submission on a canonical seed key must be that account + or hold target-result read authority on the position's recorded + targets, refused otherwise with the constant-shape conflict + evaluated before fence presence — so recorded and unrecorded + worlds are indistinguishable to the refused submitter — and + origination of a missing seed fence is reserved to the + seed-origin account passing the full fresh-mutation + authorization for that seed command (§4.3). Including the **replay access event**: a distinct non-mutation audit event class, appended on every shared replay, recording the accessing actor, the current request's correlation ids, and the fence row returned — the -- 2.54.0 From 3824fc6a37ab68972bb29d8462bb6594dfcefddc Mon Sep 17 00:00:00 2001 From: fred Date: Wed, 26 Aug 2026 23:21:33 -0500 Subject: [PATCH 10/15] =?UTF-8?q?docs:=20onboarding-wizard=20contract=20re?= =?UTF-8?q?vision=2010=20(sol=20r9=20NEW-11:=20seed-origin=20becomes=20a?= =?UTF-8?q?=20designation=20with=20a=20disclosed=20succession=20command=20?= =?UTF-8?q?=E2=80=94=20origin=20loss=20recoverable=20without=20factory=20r?= =?UTF-8?q?eset,=20no-oracle=20shape=20preserved)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/requirements/onboarding-wizard.md | 133 +++++++++++++++++++++---- 1 file changed, 114 insertions(+), 19 deletions(-) diff --git a/docs/requirements/onboarding-wizard.md b/docs/requirements/onboarding-wizard.md index 187846ee..e82de41c 100644 --- a/docs/requirements/onboarding-wizard.md +++ b/docs/requirements/onboarding-wizard.md @@ -179,6 +179,24 @@ outcome references, actor equality remaining an additional condition, never a substitute — a creator whose grants were revoked is refused, not replayed (NEW-10; §6.7 lost-target-grant witness). +Revision 10 (sol re-review 8: NEW-11): the seed-origin boundary becomes +recoverable without breaking the no-oracle result. The epoch's +seed-origin is a **designation** the finalize fixes to the first admin, +changed through exactly one disclosed command — **seed-origin +succession** (§4.3, §7 item 12): an eligible platform admin holding +target-result read authority on the committed seed prefix succeeds only +while the current origin fails identity §7.1 eligibility; refusals keep +the constant conflict shape whatever condition failed and whatever is +recorded, concurrent successions serialize on the epoch record, the +change is one audited epoch-record mutation, and recorded fence rows +immutably retain their original actor. The seed-boundary gate and the +origination rule read the epoch record's current designation, so a +banned or deleted origin no longer strands the unoriginated suffix — +the successor resumes and completes it with no factory reset and no new +epoch, restoring D4 re-runnability and §4.4's strand-nothing rule +(NEW-11; witnesses §6.7: succession recovery, origin-available refusal, +succession race). + Scope: the Gateway-backed product onboarding wizard. Out of scope: the host-local install wizard (`mosaic wizard`, which drives host install and gateway bootstrap and is not this artifact — audit REPORT.md layer 3); @@ -417,7 +435,9 @@ the named authority: epoch's §4.3 seed-origin account — as actor, naming the admin as initial `owner` in the same audited operation (contract 2 §4.3); the §4.3 seed-boundary gate reserves - origination of the canonical seed tuples to that account, while + origination of the canonical seed tuples to the epoch's current + seed-origin account — initially this admin, thereafter changed + only by §4.3 seed-origin succession — while the same command outside the canonical seed key set follows §5.2 unchanged. The initial estate, initial project, and initial workspace with seeded example data (D4, D11) follow through hierarchy @@ -551,11 +571,13 @@ collects no sensitive category, so v1 ships no custody surface. can carry a shared declaration, so no shared fence row can exist outside the seed sequence — the seed-only rule is enforced by the command layer, not by wizard convention. - - **Seed-boundary gate and origination.** The epoch has one - immutable **seed-origin account**: the account the §3.3 - finalize transaction created as the epoch's first admin - (identity §3) — provenance the epoch record fixes at finalize - and nothing changes afterwards. Every mutating submission + - **Seed-boundary gate and origination.** The epoch record holds + one **seed-origin designation**: at finalize it names the + account the §3.3 finalize transaction created as the epoch's + first admin (identity §3), and afterwards it changes through + exactly one path — the **seed-origin succession** command below + — never by any other write. The epoch's **seed-origin account** + is the account the designation currently names. Every mutating submission whose (operation, key) is in the canonical seed key set — whatever replay mode it declares — passes, after fresh-mutation authorization and before the fence table is @@ -582,14 +604,46 @@ collects no sensitive category, so v1 ships no custody surface. eligibility plus the hierarchy authority the command itself requires); a refused submission records no row (no-error replay below). The recorded actor of every shared row is - therefore the seed-origin account, authorized for the mutation - the row fences. If the seed-origin account is banned or - deleted while seed positions remain unoriginated, those - positions are unoriginatable for this epoch — an accepted - consequence of the immutable boundary; factory reset (§4.2), - which opens a new epoch with a new first admin, is the - recovery path. Recorded positions are unaffected: any admin - holding target-result read authority replays them (§6.3). + therefore the account that was the epoch's seed-origin at that + position's origination, authorized for the mutation the row + fences. Recorded positions are replayable by any admin holding + target-result read authority (§6.3). + - **Seed-origin succession.** Loss of the seed-origin account + does not strand the epoch (D4, §4.4). The designation changes + through exactly one mutating command, an amendment to identity + §3's epoch surface disclosed in §7 item 12: an eligible + platform admin — passing §5.2 fresh-mutation authorization in + full — submits succession naming itself the epoch's + seed-origin. The command succeeds only when, evaluated against + canonical state inside the succession transaction itself: + (a) the current seed-origin account fails identity §7.1 + eligibility (banned, deleted, or disabled) — succession while + the current origin remains eligible is refused — and (b) the + submitter holds §4.3 target-result read authority on every + canonical record referenced by the recorded outcomes of the + committed seed prefix (vacuously satisfied while no position + is committed). A submission failing either condition is + refused with the same single constant-shape bounded conflict + as the seed-boundary gate, byte-shape-identical whichever + condition failed and whether any seed fence exists — so + succession adds no existence oracle (witness §6.7) — executes + nothing, and appends no event. Successful succession updates + the designation in the epoch record and appends one ordinary + mutation audit event recording the prior designation, the new + designation, and the acting principal; it rewrites no fence + row and no recorded outcome — rows already recorded immutably + retain their original actor. Concurrent successions serialize + on the epoch record: exactly one submitter commits and becomes + the seed-origin, and the loser, re-evaluated against the + committed winner, fails condition (a) — the now-current origin + is eligible — and receives the constant-shape refusal. The + seed-boundary gate and the origination rule always read the + epoch record's current designation: after succession the + successor originates the remaining suffix in order under its + own full fresh-mutation authorization, and the fence rows it + originates record the successor. Factory reset (§4.2) remains + the only path to a new epoch; it is never required to complete + an interrupted seed sequence. - **No error replay.** The fence row commits only with its mutation, so only committed outcomes are ever recorded. A failed or refused submission records no fence row; a retry executes @@ -651,7 +705,11 @@ collects no sensitive category, so v1 ships no custody surface. already-committed mutations return their recorded outcomes, and only the remainder executes, without duplication and without compensating rollback of completed commands. There is no - wizard-level transaction spanning steps. + wizard-level transaction spanning steps. Loss of the seed-origin + account mid-sequence is likewise recoverable without a new epoch: + §4.3 seed-origin succession designates an eligible successor, and + the resumed run completes the remaining suffix under the successor + — interrupted runs strand nothing even across origin-account loss. ## 5. Seeding authority (resolves contract 2 review NEW-1) @@ -916,7 +974,30 @@ Binding on the implementing PRs: re-granting the creator read authority and resubmitting returns the recorded outcome — proving actor equality is never a substitute for live target-result read authority on any replay - mode; a submission that failed before commit + mode; **seed-origin succession witnesses (§4.3, NEW-11):** a + **succession recovery** — the seed-origin account commits a + proper seed prefix and is then banned (identity §7.1); an + eligible platform admin holding target-result read authority on + the committed prefix submits succession, the epoch record's + designation changes to the successor with exactly one mutation + audit event recording the prior designation, the new + designation, and the acting principal, and the successor's + resumed run replays the committed prefix (its access attributed + by §4.3 replay access events) and originates the remaining + suffix in order — the new fence rows record the successor, the + pre-succession rows immutably retain the original origin, and + the full seed set completes with no factory reset and no new + epoch; an **origin-available succession refusal** — the same + eligible admin submits succession while the current origin + remains §7.1-eligible — is refused with the single + constant-shape bounded conflict (asserted byte-shape-identical + to the collision refusal), changes no epoch record, and appends + no audit event; a **succession race** — with the origin banned, + two eligible, prefix-authorized admins submit succession + concurrently: the epoch record serializes them, exactly one + commits and becomes the designated origin with one audit event, + the loser receives the constant-shape refusal, and the seed + sequence completes exactly once under the winner; a submission that failed before commit leaves no fence row and its retry executes; two concurrent resumed runs executing the seed sequence yield exactly one seed set — per key, exactly one mutation and one mutation audit event exist, and @@ -1011,9 +1092,11 @@ contracts and are not additions: outcomes — with a shared declaration outside the key set, off its position's derived tuple, or past the next unrecorded position refused recording nothing (§4.3). Including the - **seed-boundary gate**: the epoch's immutable seed-origin - account is the first admin the finalize transaction created; - every submission on a canonical seed key must be that account + **seed-boundary gate**: the epoch's seed-origin designation + initially names the first admin the finalize transaction + created and changes only through item 12's succession command; + every submission on a canonical seed key must be the currently + designated account or hold target-result read authority on the position's recorded targets, refused otherwise with the constant-shape conflict evaluated before fence presence — so recorded and unrecorded @@ -1070,6 +1153,18 @@ contracts and are not additions: provenance for §3.4's derivation; the current company name lives on the company row and changes only through the ordinary hierarchy rename (§3.1, witnesses §6.3). +12. The **seed-origin succession command** (§4.3) — an amendment to + identity §3's epoch surface, proposed and ratified here, + severable: one mutating command that redesignates the epoch's + seed-origin to an eligible platform admin holding target-result + read authority on the committed seed prefix, valid only while + the current origin fails identity §7.1 eligibility; refusals + are the constant-shape §4.3 conflict regardless of which + condition failed, the change is one audited epoch-record write + serialized on the epoch record, and recorded fence rows are + never rewritten. Without this amendment a banned or deleted + seed-origin account strands the unoriginated seed suffix, + contradicting PRD D4's no-lock-in requirement (§4.4). ## Ruling request -- 2.54.0 From 83142e1b7930730ff9824ed7d527430c6b783d07 Mon Sep 17 00:00:00 2001 From: fred Date: Thu, 27 Aug 2026 02:50:32 -0500 Subject: [PATCH 11/15] =?UTF-8?q?docs:=20onboarding-wizard=20contract=20re?= =?UTF-8?q?vision=2011=20(sol=20r10=20NEW-12/13/14/15:=20world-independent?= =?UTF-8?q?=20succession=20with=20conferred=20position-1=20authority,=20un?= =?UTF-8?q?available-or-unable=20re-succession,=20=C2=A77.1=20predicate=20?= =?UTF-8?q?collapse=20with=20forward=20constraint,=20dual=20identity-surfa?= =?UTF-8?q?ce=20disclosure)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/requirements/onboarding-wizard.md | 319 ++++++++++++++++++------- 1 file changed, 236 insertions(+), 83 deletions(-) diff --git a/docs/requirements/onboarding-wizard.md b/docs/requirements/onboarding-wizard.md index e82de41c..4d1fed64 100644 --- a/docs/requirements/onboarding-wizard.md +++ b/docs/requirements/onboarding-wizard.md @@ -197,6 +197,38 @@ epoch, restoring D4 re-runnability and §4.4's strand-nothing rule (NEW-11; witnesses §6.7: succession recovery, origin-available refusal, succession race). +Revision 11 (sol re-review 9: NEW-12, NEW-13, NEW-14, NEW-15): succession +itself becomes oracle-free and strand-proof. Succession's conditions are +now **fence-independent**: the submitter's side is exactly platform-level +eligibility plus §5.2 top-level-create eligibility — never any authority +over, or reference to, committed seed records — and the origin's side is +the disjunction _unavailable or unable_: the current origin fails +identity §7.1's single account-unavailability predicate (today exactly +the better-auth ban), or lacks the seed-completion authority defined in +§4.3. Because every input is evaluated without consulting fence rows, +recorded outcomes, or the committed prefix, the command's outcome is +identical across the recorded and unrecorded worlds — the vacuous-prefix +oracle is gone (NEW-12). Successful succession **confers, in the same +transaction and inside its single audit event, the position-1 +initial-owner authority on the seed company where that position is +already originated** — exactly the authority origination of position 1 +would have self-conferred — so every successful successor holds +seed-completion authority by construction and no read-only capture can +strand the suffix; the _unable_ disjunct makes re-succession available +when a later designation loses that authority while staying +identity-eligible, and succession is repeatable across successive losses +(NEW-13). Condition (a)'s eligibility failure collapses to the one +predicate identity §7.1 actually defines, with a forward constraint +binding any future account-removal or account-disable contract to extend +that predicate and to preserve the epoch designation as a stable +reference (NEW-14). §1.2 now discloses both identity-surface additions — +the §7.3 finalize extension and the §7 item 12 succession command — and +succession appears in the §1.1 composed-surface inventory and the §7.8 +mapping amendment (NEW-15). Witnesses §6.7: the succession two-world +control, tenant-unprivileged successor completion, post-succession +grant-revocation re-succession, repeat succession, and empty-prefix +succession. + Scope: the Gateway-backed product onboarding wizard. Out of scope: the host-local install wizard (`mosaic wizard`, which drives host install and gateway bootstrap and is not this artifact — audit REPORT.md layer 3); @@ -230,26 +262,32 @@ through the extensibility rule §2.4). hierarchy commands and the rank-4 enrollment command (contract 5 §3.1 — built first), the settings command family (steps 1–2), the identity bootstrap and registration surface (step 3, including the - §3.3 finalize command), the mode reads (contract 6 §2.2 post-epoch; + §3.3 finalize command and the §4.3 seed-origin succession + command), the mode reads (contract 6 §2.2 post-epoch; the §2.3 bootstrap-status field pre-epoch), and the ordinary content commands used for example seeding (step 4). Two of these families (ranks 1 and 4) are in contract 5 §3.1's live rank-6 composition row; the other four are added by the disclosed §7.8 mapping amendment, without which their operations are unmapped and blocked under contract 5 §5. -2. **The wizard introduces no new mutation surface.** Every state change - it performs is an existing command with its own contract: user and - epoch writes under the identity contract §3, hierarchy writes under - contract 1 §5, grant writes under contract 2 §4, settings writes under - their owning command family. The one exception is the bootstrap - surface, where the wizard drives the bootstrap writer defined by - identity §3 **as amended by the disclosed §7.3 finalize - extension**: the writer's constraints (§3.1–§3.6) bind, and this - contract's single change to them — extending the epoch-closing - command to carry the §3.3 value set inside the same transaction — - is exactly the §7.3 amendment, severable and ratified with this - contract. Beyond that amendment, nothing is added to identity §3, - and no undisclosed authority exists. +2. **The wizard introduces no undisclosed mutation surface.** Every + state change it performs is an existing command with its own + contract: user and epoch writes under the identity contract §3, + hierarchy writes under contract 1 §5, grant writes under contract 2 + §4, settings writes under their owning command family. The + exceptions are exactly the **two disclosed amendments to identity + §3's epoch surface**, each severable and ratified with this + contract: the **§7.3 finalize extension** — the wizard drives the + bootstrap writer defined by identity §3, whose constraints + (§3.1–§3.6) bind, with this contract's single change extending the + epoch-closing command to carry the §3.3 value set inside the same + transaction — and the **§7 item 12 seed-origin succession command** + (§4.3), one mutating epoch-record command with its conferred-grant + clause. Beyond those two amendments, nothing is added to identity + §3, and no undisclosed authority exists; both surfaces appear in + the §1.1 composed-family inventory, the §7.8 mapping amendment, + and §6.1's inventories, so the D8 mapping and authorization-parity + witnesses cannot omit them. 3. **Wizard state derives from canonical state.** Each step renders the system's current configuration (read through the same commands) and applies deltas; the wizard does not keep an answer file whose contents @@ -419,10 +457,19 @@ the named authority: never precomputes a tuple past the next unrecorded position (§4.3 shared-declaration boundary). Because the seed parameter is immutable (§3.1), the re-derived sequence is - byte-stable across every re-run and resume: the same keys carry - the same payload digests, so already-committed mutations replay - (recorded outcomes) rather than collide, regardless of any - hierarchy rename performed since — and, because every seed + byte-stable across every re-run and resume **under an unchanged + seed-origin designation**: the same keys carry the same payload + digests, so already-committed mutations replay (recorded + outcomes) rather than collide, regardless of any hierarchy rename + performed since. Where a payload field names the acting seed + origin — position 1's initial-owner field below — the derivation + reads the epoch record's **current designation at origination + time**: a committed position's tuple is pinned by its recorded + outcome and fence digest forever (replays compare against the + recorded digest, never a re-derived one), while an unrecorded + position's derived tuple names the current designation, so a §4.3 + succession changes derived payloads only for positions not yet + originated and can never collide a committed row — and, because every seed submission declares §4.3's `shared` replay mode, they replay for whichever currently eligible admin holding §4.3 target-result read authority on the recorded seed targets performs the re-run, @@ -431,10 +478,12 @@ the named authority: parameter is provenance the derivation reads, never a value any later step may change. The first company is created by the ordinary top-level company - command under §5.2's eligibility policy, with the new admin — the - epoch's §4.3 seed-origin account — as actor, - naming the admin as initial `owner` in the same audited operation - (contract 2 §4.3); the §4.3 seed-boundary gate reserves + command under §5.2's eligibility policy, with the epoch's §4.3 + seed-origin account — the account the designation names at + origination time, initially the new admin — as actor, naming that + same account as initial `owner` in the same audited operation + (contract 2 §4.3): the initial-owner field is bound to the current + designation, not to the historical first admin; the §4.3 seed-boundary gate reserves origination of the canonical seed tuples to the epoch's current seed-origin account — initially this admin, thereafter changed only by §4.3 seed-origin succession — while @@ -611,39 +660,81 @@ collects no sensitive category, so v1 ships no custody surface. - **Seed-origin succession.** Loss of the seed-origin account does not strand the epoch (D4, §4.4). The designation changes through exactly one mutating command, an amendment to identity - §3's epoch surface disclosed in §7 item 12: an eligible - platform admin — passing §5.2 fresh-mutation authorization in - full — submits succession naming itself the epoch's - seed-origin. The command succeeds only when, evaluated against - canonical state inside the succession transaction itself: - (a) the current seed-origin account fails identity §7.1 - eligibility (banned, deleted, or disabled) — succession while - the current origin remains eligible is refused — and (b) the - submitter holds §4.3 target-result read authority on every - canonical record referenced by the recorded outcomes of the - committed seed prefix (vacuously satisfied while no position - is committed). A submission failing either condition is + §3's epoch surface disclosed in §7 item 12: a platform admin + submits succession naming itself the epoch's seed-origin. The + command's outcome is **world-independent by construction**: + the submitter-side condition (b) is evaluated against + identity, platform-eligibility, and epoch-record state alone — + never against fence rows, recorded outcomes, the committed + prefix, or any grant attached to a seed record — and the + origin-side condition (a)'s only seed-scope input is the + origin's seed-completion authority, whose recorded-world + component is exactly the authority origination and succession + themselves confer (below), so in any two worlds differing only + in seed existence every condition evaluates identically and + succession carries no existence oracle; the evaluation + consults no fence row, so its timing is fence-independent too + (witness §6.7 two-world control). **Seed-completion authority** means: §5.2 top-level + create eligibility (the full fresh-mutation authorization + position 1 requires) plus, for an account currently designated + while position 1 stands originated, the position-1 + initial-owner authority on the seed company — exactly the + authority whose origination self-confers it (§3.4). The + command succeeds only when BOTH: (a) the current seed-origin + account is **unavailable or unable** — it fails identity + §7.1's account-unavailability predicate (today exactly the + better-auth ban; §5.2 records that no separate deactivated + state exists and identity §7.3 defers hard deletion), or it + lacks seed-completion authority — succession while the current + origin is both available and able is refused; and (b) the + submitter is an identity-§7.1-eligible platform admin holding + §5.2 top-level create eligibility. Condition (b) references no + seed record and no prefix: a tenant-unprivileged platform + admin passes or fails it identically whether or not any seed + position is committed. Any future contract adding an + account-removal or account-disable mechanism MUST extend + identity §7.1's single unavailability predicate to cover it + and MUST preserve the epoch record's designation as a stable + reference across it (a retained identifier or tombstone — + never a cascade that rewrites or nulls the designation outside + this command). A submission failing either condition is refused with the same single constant-shape bounded conflict as the seed-boundary gate, byte-shape-identical whichever - condition failed and whether any seed fence exists — so - succession adds no existence oracle (witness §6.7) — executes - nothing, and appends no event. Successful succession updates - the designation in the epoch record and appends one ordinary - mutation audit event recording the prior designation, the new - designation, and the acting principal; it rewrites no fence - row and no recorded outcome — rows already recorded immutably - retain their original actor. Concurrent successions serialize - on the epoch record: exactly one submitter commits and becomes - the seed-origin, and the loser, re-evaluated against the - committed winner, fails condition (a) — the now-current origin - is eligible — and receives the constant-shape refusal. The - seed-boundary gate and the origination rule always read the - epoch record's current designation: after succession the - successor originates the remaining suffix in order under its - own full fresh-mutation authorization, and the fence rows it - originates record the successor. Factory reset (§4.2) remains - the only path to a new epoch; it is never required to complete - an interrupted seed sequence. + condition failed and whether any seed fence exists, executes + nothing, and appends no event. Successful succession, in one + transaction, updates the designation in the epoch record, + **confers on the successor the position-1 initial-owner + authority on the seed company where position 1 stands + originated** — no more than originating position 1 from an + empty prefix would have self-conferred (§3.4), so succession + escalates nothing beyond the origination role it transfers — + and appends exactly one ordinary mutation audit event + recording the prior designation, the new designation, the + acting principal, and the conferred grant where one was + written; it rewrites no fence row and no recorded outcome — + rows already recorded immutably retain their original actor. + Every successful successor therefore holds seed-completion + authority at commit: a read-only or tenant-unprivileged + capture that strands the suffix cannot exist, and if a later + designation loses that authority while staying + identity-eligible, the _unable_ disjunct of condition (a) + makes re-succession available — succession is repeatable + across successive origin losses, by inability as well as by + unavailability (witnesses §6.7). Concurrent successions + serialize on the epoch record: exactly one submitter commits + and becomes the seed-origin, and the loser, re-evaluated + against the committed winner, fails condition (a) — the + now-current origin is identity-eligible and, holding the + just-conferred seed-completion authority, able — and receives + the constant-shape refusal. The seed-boundary gate and the + origination rule always read the epoch record's current + designation: after succession the successor originates the + remaining suffix in order under its own full fresh-mutation + authorization (supplied by the conferred authority plus its + own eligibility), and the fence rows it originates record the + successor. Factory reset (§4.2) remains the only path to a new + epoch; it is never required to complete an interrupted seed + sequence. - **No error replay.** The fence row commits only with its mutation, so only committed outcomes are ever recorded. A failed or refused submission records no fence row; a retry executes @@ -706,10 +797,14 @@ collects no sensitive category, so v1 ships no custody surface. only the remainder executes, without duplication and without compensating rollback of completed commands. There is no wizard-level transaction spanning steps. Loss of the seed-origin - account mid-sequence is likewise recoverable without a new epoch: - §4.3 seed-origin succession designates an eligible successor, and - the resumed run completes the remaining suffix under the successor - — interrupted runs strand nothing even across origin-account loss. + account mid-sequence — by unavailability (identity §7.1) or by + loss of seed-completion authority — is likewise recoverable + without a new epoch: §4.3 seed-origin succession designates an + eligible successor, confers the position-1 authority where the + seed company exists, and the resumed run completes the remaining + suffix under the successor; succession is repeatable, so + interrupted runs strand nothing even across successive origin + losses. ## 5. Seeding authority (resolves contract 2 review NEW-1) @@ -974,30 +1069,72 @@ Binding on the implementing PRs: re-granting the creator read authority and resubmitting returns the recorded outcome — proving actor equality is never a substitute for live target-result read authority on any replay - mode; **seed-origin succession witnesses (§4.3, NEW-11):** a - **succession recovery** — the seed-origin account commits a - proper seed prefix and is then banned (identity §7.1); an - eligible platform admin holding target-result read authority on - the committed prefix submits succession, the epoch record's - designation changes to the successor with exactly one mutation - audit event recording the prior designation, the new - designation, and the acting principal, and the successor's - resumed run replays the committed prefix (its access attributed - by §4.3 replay access events) and originates the remaining + mode; **seed-origin succession witnesses (§4.3, NEW-11 through + NEW-14):** a **succession recovery** — the seed-origin account + commits a proper seed prefix and is then banned (identity §7.1); + an eligible platform admin holding §5.2 top-level create + eligibility submits succession, the epoch record's designation + changes to the successor with exactly one mutation audit event + recording the prior designation, the new designation, the acting + principal, and the conferred position-1 grant, and the + successor's resumed run replays the committed prefix (its access + attributed by §4.3 replay access events, its read authority + supplied by the conferred grant) and originates the remaining suffix in order — the new fence rows record the successor, the pre-succession rows immutably retain the original origin, and the full seed set completes with no factory reset and no new epoch; an **origin-available succession refusal** — the same eligible admin submits succession while the current origin - remains §7.1-eligible — is refused with the single - constant-shape bounded conflict (asserted byte-shape-identical - to the collision refusal), changes no epoch record, and appends - no audit event; a **succession race** — with the origin banned, - two eligible, prefix-authorized admins submit succession - concurrently: the epoch record serializes them, exactly one - commits and becomes the designated origin with one audit event, - the loser receives the constant-shape refusal, and the seed - sequence completes exactly once under the winner; a submission that failed before commit + remains §7.1-eligible and holds seed-completion authority — is + refused with the single constant-shape bounded conflict + (asserted byte-shape-identical to the collision refusal), + changes no epoch record, and appends no audit event; a + **succession two-world control (NEW-12)** — the SAME + identity-eligible, §5.2-create-eligible platform admin holding + no grant on any seed record submits succession in two prepared + worlds with the current origin banned in both: one where a + proper seed prefix stands committed and one freshly finalized + epoch with no position committed — and in BOTH worlds the + command succeeds, the designation changes to the submitter, and + exactly one succession audit event appends, with the response + asserted equal in shape and error/success class across the + worlds and the evaluation asserted within a fence-independent + timing bound (it consults no fence row); the control is repeated + with a submitter failing condition (b) — refused + byte-shape-identically in both worlds, no record change, no + event in either — proving succession's outcome is a function of + fence-independent inputs only; a **tenant-unprivileged successor + completion (NEW-13)** — the successor of the two-world control's + recorded world, who held no seed-record authority before + succeeding, completes the entire remaining suffix using only the + conferred position-1 authority plus its own eligibility, proving + no read-only capture can strand the suffix; a **post-succession + grant-revocation re-succession (NEW-13)** — after an A→B + succession and further committed progress, B's conferred + position-1 authority is revoked while B remains + identity-eligible; an eligible, §5.2-create-eligible admin C + submits succession and succeeds through condition (a)'s _unable_ + disjunct, receives the conferred authority, and completes the + suffix — no factory reset, exactly one audit event for C's + succession; a **repeat succession (NEW-14)** — A is banned, B + succeeds and commits further prefix, B is then banned, C + succeeds and completes the suffix: each succession appends + exactly one event, every fence row records its true originator + (A's rows, B's rows, C's rows), and the seed set completes; an + **empty-prefix succession (NEW-13)** — the origin is banned + after finalize but before any seed position commits; the + successor succeeds (condition (b) evaluated with no seed record + in existence), originates the ENTIRE sequence, and position 1's + committed payload names the successor as initial owner — + asserting §3.4's derivation reads the current designation for + unrecorded positions; a **succession race** — with the origin + banned, two eligible, §5.2-create-eligible admins submit + succession concurrently: the epoch record serializes them, + exactly one commits and becomes the designated origin with one + audit event and the conferred authority, the loser receives the + constant-shape refusal (re-evaluated: the now-current origin is + eligible and able), and the seed sequence completes exactly once + under the winner; a submission that failed before commit leaves no fence row and its retry executes; two concurrent resumed runs executing the seed sequence yield exactly one seed set — per key, exactly one mutation and one mutation audit event exist, and @@ -1124,7 +1261,9 @@ contracts and are not additions: rank-4 enrollment families, is amended to name all six composed families — adding the settings command family, the identity bootstrap and registration surface (including the §3.3 finalize - command), the mode reads (contract 6 §2.2 and the §2.3 + command and the §4.3 seed-origin succession command, so the + official-tool mapping and §6.1's authorization-parity witnesses + cannot omit either), the mode reads (contract 6 §2.2 and the §2.3 bootstrap-status field), and the ordinary content commands used for example seeding — with one mapping row per newly named family. Without this amendment those operations are unmapped and blocked @@ -1156,13 +1295,27 @@ contracts and are not additions: 12. The **seed-origin succession command** (§4.3) — an amendment to identity §3's epoch surface, proposed and ratified here, severable: one mutating command that redesignates the epoch's - seed-origin to an eligible platform admin holding target-result - read authority on the committed seed prefix, valid only while - the current origin fails identity §7.1 eligibility; refusals + seed-origin to the submitter, valid only while the current + origin is **unavailable or unable** — it fails identity §7.1's + account-unavailability predicate (today exactly the better-auth + ban; future removal or disable contracts must extend that one + predicate and preserve the designation, §4.3) or lacks + seed-completion authority — and the submitter is an + identity-§7.1-eligible platform admin holding §5.2 top-level + create eligibility, a condition evaluated against identity, + platform-eligibility, and epoch-record state only, never + against fence rows or seed-record grants, so its outcome and + timing are fence-independent (no existence oracle). Refusals are the constant-shape §4.3 conflict regardless of which - condition failed, the change is one audited epoch-record write - serialized on the epoch record, and recorded fence rows are - never rewritten. Without this amendment a banned or deleted + condition failed; success is one audited epoch-record write + serialized on the epoch record that also confers, in the same + transaction and audit event, the position-1 initial-owner + authority on the seed company where position 1 has originated + — exactly the authority position-1 origination itself confers — + so every successor holds seed-completion authority at commit; + the command is repeatable (a later unavailable-or-unable + successor is succeeded the same way), and recorded fence rows + are never rewritten. Without this amendment a banned seed-origin account strands the unoriginated seed suffix, contradicting PRD D4's no-lock-in requirement (§4.4). -- 2.54.0 From 10e82d05c0487005a453d0875d38590f860cf355 Mon Sep 17 00:00:00 2001 From: fred Date: Thu, 27 Aug 2026 03:38:06 -0500 Subject: [PATCH 12/15] contract 7 rev 12: pure designation transfer, epoch-derived account-free seed tuples, designation-derived origination authority MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Answers sol re-review 10 (NEW-12/NEW-13/NEW-14 residuals, NEW-16, NEW-17): - Canonical seed tuples fully epoch-derived and account-free: no generated id or account identifier in any canonical payload or scope; child positions reference parents by epoch-scoped canonical seed role, resolved server-side at execution (canonical-reference resolution); position 1 carries no initial-owner field — the contract 2 §4.3 default binds owner to the acting designation as a recorded outcome. Byte-stability absolute; post-succession replay compares equal by construction (NEW-13). - Succession reduced to a pure designation transfer: condition (a) = identity §7.1 unavailability alone (unable disjunct removed), no grant conferred, reads identity/platform/epoch state only, closed write set = designation update + one audit event. World-independent unconditionally, self-revocation pair included (NEW-12); post- completion succession confers nothing (NEW-16). - Designation-derived origination authority: scoped contract 2 §4 amendment (§7 item 12) — the current designation satisfies the hierarchy-authority component for fresh origination of unoriginated canonical positions only; no read/replay/standing authority. - §6.1 succession-write inventory closed in both directions; no-seed- input static assertion (NEW-17). - Revision-10 preamble vocabulary corrected to the banned state identity defines (NEW-14). - §6.7 reworked: strengthened two-world control (event content, grant- table delta, full-command timing), new self-revocation two-world refusal, designation-derived completion, post-completion harmlessness, post-succession replay digest-equality, empty-prefix digest-equality witnesses; out-of-order origination witness. --- docs/requirements/onboarding-wizard.md | 635 +++++++++++++++---------- 1 file changed, 392 insertions(+), 243 deletions(-) diff --git a/docs/requirements/onboarding-wizard.md b/docs/requirements/onboarding-wizard.md index 4d1fed64..6b2c68cc 100644 --- a/docs/requirements/onboarding-wizard.md +++ b/docs/requirements/onboarding-wizard.md @@ -191,7 +191,9 @@ recorded, concurrent successions serialize on the epoch record, the change is one audited epoch-record mutation, and recorded fence rows immutably retain their original actor. The seed-boundary gate and the origination rule read the epoch record's current designation, so a -banned or deleted origin no longer strands the unoriginated suffix — +banned origin — identity §7.1's sole unavailability state; deletion +does not exist under identity §7.3 — no longer strands the +unoriginated suffix — the successor resumes and completes it with no factory reset and no new epoch, restoring D4 re-runnability and §4.4's strand-nothing rule (NEW-11; witnesses §6.7: succession recovery, origin-available refusal, @@ -227,7 +229,55 @@ succession appears in the §1.1 composed-surface inventory and the §7.8 mapping amendment (NEW-15). Witnesses §6.7: the succession two-world control, tenant-unprivileged successor completion, post-succession grant-revocation re-succession, repeat succession, and empty-prefix -succession. +succession. (Revision 12 removes the conferral and the _unable_ +disjunct introduced here; it closes the same findings without them.) + +Revision 12 (sol re-review 10: NEW-12/NEW-13/NEW-14 residuals, NEW-16, +NEW-17): succession is reduced to a pure designation transfer and the +canonical seed tuples become fully epoch-derived, superseding revision +9's prefix-derived tuple set. Canonical seed payloads and scopes now +contain **no generated id and no account identifier**: a child +position references its parent by epoch-scoped canonical seed role, +resolved to the actual row server-side at execution +(canonical-reference resolution, §4.3), and position 1 carries no +initial-owner field — the ordinary top-level command's default (the +creator names itself, contract 2 §4.3) binds the initial `owner` to +the acting designation as a recorded outcome, never as payload +content. The full canonical seed tuple set is therefore +fence-independent and byte-stable absolutely: every client — fresh, +resuming, or a successor — derives identical bytes from the epoch id, +the immutable provenance, and the fixed example set alone, so a +replay after succession compares equal against every committed digest +and the historical-payload gap is closed (NEW-13). Succession itself +now writes exactly the designation update and its single audit event +— **no grant is conferred** — and condition (a) collapses to identity +§7.1 unavailability alone: the _unable_ disjunct and the +conferred-grant clause are removed, so the command reads identity, +platform-eligibility, and epoch-record state only, writes nothing +conditional, and is world-independent unconditionally — no allowed +grant transition, including an origin revoking its own position-1 +`owner` grant, can make its outcome or observables differ (NEW-12) — +and a platform admin can never parlay succession into authority over +an existing seed company: after full completion, succeeding confers +nothing at all (NEW-16). The origination authority succession +transfers is **designation-derived**: for exactly the fresh +origination of an unoriginated canonical seed position, the +hierarchy-authority component of fresh-mutation authorization is +satisfied by the current designation itself — a disclosed, scoped +amendment to contract 2 §4's evaluation (§7 item 12) conferring no +read authority, no replay authority, no standing grant, and nothing +outside the canonical seed key set — so a successor completes the +suffix by direct in-order origination, never replaying the committed +prefix, and the revocation lock that motivated the _unable_ disjunct +cannot arise (completion authority never depends on a revocable +grant). §6.1 gains the closed succession-write inventory and its +no-seed-input static assertion (NEW-17), and the Revision-10 +paragraph's origin-loss vocabulary is corrected to the banned state +identity actually defines (NEW-14). §6.7 reworked: the two-world +control now asserts equal event content, an empty grant-table delta, +and a full-command timing bound; new self-revocation two-world +refusal, designation-derived completion, post-completion +harmlessness, and post-succession replay witnesses. Scope: the Gateway-backed product onboarding wizard. Out of scope: the host-local install wizard (`mosaic wizard`, which drives host install and @@ -282,9 +332,14 @@ through the extensibility rule §2.4). (§3.1–§3.6) bind, with this contract's single change extending the epoch-closing command to carry the §3.3 value set inside the same transaction — and the **§7 item 12 seed-origin succession command** - (§4.3), one mutating epoch-record command with its conferred-grant - clause. Beyond those two amendments, nothing is added to identity - §3, and no undisclosed authority exists; both surfaces appear in + (§4.3), one mutating epoch-record command whose closed write set + is the designation update and its single audit event. Beyond those + two amendments, nothing is added to identity §3; the one further + disclosed authorization amendment is §7 item 12's + **designation-derived origination authority** — a scoped change to + contract 2 §4's evaluation for canonical seed origination only, + conferring no grant and no read authority — and no undisclosed + authority exists; all three surfaces appear in the §1.1 composed-family inventory, the §7.8 mapping amendment, and §6.1's inventories, so the D8 mapping and authorization-parity witnesses cannot omit them. @@ -448,29 +503,34 @@ the named authority: sequence is derived entirely from canonical state: the seed parameter the finalize command recorded (the company name, read from the `bootstrap.seed-company-name` settings value, §3.1) and - the fixed example set (§4.3). A resumed run — including a fresh - client holding none of the original run's transient state — - reconstructs the same ordered sequence and the same deterministic - §4.3 keys from that canonical state alone, prefix-wise: each - position's scope and payload derive from the committed - predecessors' recorded outcomes, so the run walks the order and - never precomputes a tuple past the next unrecorded position - (§4.3 shared-declaration boundary). Because the seed + the fixed example set (§4.3). **Canonical seed tuples are fully + epoch-derived and account-free.** No canonical seed payload or + scope contains a generated id or an account identifier: a child + position references its parent by the epoch-scoped canonical seed + role ("the seed company of this epoch", "the seed estate of this + epoch"), which the command layer resolves to the actual canonical + row server-side at execution (§4.3 canonical-reference + resolution), and position 1 carries no initial-owner field — it + takes the ordinary top-level command's default, the creator + naming itself (contract 2 §4.3), so the initial `owner` binding + is a property of the recorded outcome (the acting designation at + origination time), never of payload bytes. A resumed run — + including a fresh client holding none of the original run's + transient state, and a §4.3 successor holding no authority over + any committed record — reconstructs the same ordered sequence, + the same deterministic §4.3 keys, and byte-identical payloads + from the epoch id, the immutable seed parameter, and the fixed + example set alone, reading no recorded outcome. Because the seed parameter is immutable (§3.1), the re-derived sequence is - byte-stable across every re-run and resume **under an unchanged - seed-origin designation**: the same keys carry the same payload - digests, so already-committed mutations replay (recorded - outcomes) rather than collide, regardless of any hierarchy rename - performed since. Where a payload field names the acting seed - origin — position 1's initial-owner field below — the derivation - reads the epoch record's **current designation at origination - time**: a committed position's tuple is pinned by its recorded - outcome and fence digest forever (replays compare against the - recorded digest, never a re-derived one), while an unrecorded - position's derived tuple names the current designation, so a §4.3 - succession changes derived payloads only for positions not yet - originated and can never collide a committed row — and, because every seed - submission declares §4.3's `shared` replay mode, they replay for + **byte-stable absolutely** — across every re-run, resume, and + §4.3 succession: the same keys carry the same payload digests + whoever derives them, so already-committed mutations replay + (recorded outcomes) rather than collide, regardless of any + hierarchy rename performed since, and a replay after succession + compares equal against every committed digest by construction — + no payload ever named the origin. Because every seed + submission declares §4.3's `shared` replay mode, committed + positions replay for whichever currently eligible admin holding §4.3 target-result read authority on the recorded seed targets performs the re-run, not only the actor the fence rows record. The §4.3 fence, not the @@ -480,18 +540,22 @@ the named authority: The first company is created by the ordinary top-level company command under §5.2's eligibility policy, with the epoch's §4.3 seed-origin account — the account the designation names at - origination time, initially the new admin — as actor, naming that - same account as initial `owner` in the same audited operation - (contract 2 §4.3): the initial-owner field is bound to the current - designation, not to the historical first admin; the §4.3 seed-boundary gate reserves + origination time, initially the new admin — as actor; the + command's default names the actor as initial `owner` in the same + audited operation (contract 2 §4.3), so the owner binding follows + the acting designation through the outcome, not through any + payload field; the §4.3 seed-boundary gate reserves origination of the canonical seed tuples to the epoch's current seed-origin account — initially this admin, thereafter changed only by §4.3 seed-origin succession — while the same command outside the canonical seed key set follows §5.2 unchanged. The initial estate, initial project, and initial workspace with seeded example data (D4, D11) follow through hierarchy - commands (contract 1 §5.1) under the admin's `owner` authority - (contract 2 §4.3). Seeded examples are ordinary workspace content + commands (contract 1 §5.1) under the parent-`owner` authority + contract 2 §4.3 requires — for canonical seed origination, + satisfied by the §4.3 designation-derived authority, which the + original admin holds as designation exactly as a successor + would. Seeded examples are ordinary workspace content created by ordinary commands, attributable in audit to the acting admin, carrying the run's trace correlation ids (contract 5 §4.3) and the §4.3 idempotency keys. @@ -587,35 +651,38 @@ collects no sensitive category, so v1 ships no custody surface. refused with a single bounded conflict error (contract 5 §4.2); it executes nothing and discloses nothing of the recorded outcome. - - **Shared-declaration boundary.** `shared` is a server-verified, - seed-only policy, never a caller privilege. The boundary has a - fence-independent part and a prefix-derived part. The - **canonical seed key set** — the (operation identifier, key) - pairs of §3.4's ordered seed sequence — is derived from the - epoch id and the fixed seed-role list alone: the keys are - deterministic and depend on no generated id, so membership is - decidable before any seed mutation has run and without - consulting the fence table. The full **canonical seed tuple - set** is prefix-derived, because later seed tuples embed - generated ids: the tuple at seed position k — its - authorization scope and payload digest — is derived from the - epoch id, the immutable `bootstrap.seed-company-name` - provenance, the fixed example set, and the canonical recorded - outcomes of positions 1 through k−1 (the ids the committed - predecessor fence rows reference). At any canonical state - exactly these members are derivable: every committed-prefix - tuple (read back from its fence row) and the next unrecorded - tuple in order. Before the first company exists, the derivable - set is exactly the seed-company tuple. A fresh client derives - the same way — §3.4's derivation is this walk: submit the - sequence in order, learning each generated id from the - returned recorded outcome or the submission's own execution, - never precomputing a tuple past the next position. A - submission declaring `shared` whose (operation, key) is - outside the canonical seed key set, whose tuple does not equal - its position's derived tuple, or whose position lies past the - next unrecorded position (out of order) is refused with a - single bounded refusal (contract 5 §4.2 validation class) that + - **Shared-declaration boundary and canonical-reference + resolution.** `shared` is a server-verified, seed-only policy, + never a caller privilege. The whole boundary is + fence-independent. The **canonical seed key set** — the + (operation identifier, key) pairs of §3.4's ordered seed + sequence — is derived from the epoch id and the fixed + seed-role list alone: the keys are deterministic and depend on + no generated id, so membership is decidable before any seed + mutation has run and without consulting the fence table. The + full **canonical seed tuple set** — every position's + authorization scope and payload digest — is likewise derived + from the epoch id, the immutable `bootstrap.seed-company-name` + provenance, and the fixed example set alone: no canonical seed + payload or scope contains a generated id or an account + identifier (§3.4). Where a child position must name its + parent, the payload carries the parent's **epoch-scoped + canonical seed role** ("the seed company of this epoch"), and + the command layer resolves that reference to the actual + canonical row server-side at execution — **canonical-reference + resolution**, available to exactly the canonical seed tuples + and nothing else, with the resolved rows recorded in the + position's outcome. Every tuple is therefore derivable at any + canonical state by any party from canonical settings state + alone, but **origination stays in order**: a canonical + position is originated only when every predecessor is + committed, and resolution of a reference to an uncommitted + predecessor fails closed. A submission declaring `shared` + whose (operation, key) is outside the canonical seed key set + or whose tuple does not equal its position's derived tuple — + and any origination submission for a position past the next + unrecorded one (out of order) — is refused with a single + bounded refusal (contract 5 §4.2 validation class) that executes nothing and records no fence row. No other operation can carry a shared declaration, so no shared fence row can exist outside the seed sequence — the seed-only rule is @@ -649,9 +716,18 @@ collects no sensitive category, so v1 ships no custody surface. top-level seed-company key. **Origination.** A seed fence row not yet recorded is originated only by the seed-origin account executing its seed mutation in order, passing the full - fresh-mutation authorization for that seed command (§5.2 - eligibility plus the hierarchy authority the command itself - requires); a refused submission records no row (no-error + fresh-mutation authorization for that seed command — §5.2 + eligibility and identity §7.1 eligibility evaluated fresh + against the submitter, with the hierarchy-authority component + satisfied by the **designation-derived authority**: for + exactly the fresh origination of a currently-unoriginated + canonical seed position, the current designation itself + satisfies the parent-`owner` (or top-level) authority contract + 2 §4 requires, a disclosed scoped amendment (§7 item 12) that + confers no read authority, no replay authority, no standing + grant, no authority over any committed record, and nothing + outside the canonical seed key set — evaluated per submission, + writes nothing; a refused submission records no row (no-error replay below). The recorded actor of every shared row is therefore the account that was the epoch's seed-origin at that position's origination, authorized for the mutation the row @@ -661,80 +737,83 @@ collects no sensitive category, so v1 ships no custody surface. does not strand the epoch (D4, §4.4). The designation changes through exactly one mutating command, an amendment to identity §3's epoch surface disclosed in §7 item 12: a platform admin - submits succession naming itself the epoch's seed-origin. The - command's outcome is **world-independent by construction**: - the submitter-side condition (b) is evaluated against - identity, platform-eligibility, and epoch-record state alone — - never against fence rows, recorded outcomes, the committed - prefix, or any grant attached to a seed record — and the - origin-side condition (a)'s only seed-scope input is the - origin's seed-completion authority, whose recorded-world - component is exactly the authority origination and succession - themselves confer (below), so in any two worlds differing only - in seed existence every condition evaluates identically and - succession carries no existence oracle; the evaluation - consults no fence row, so its timing is fence-independent too - (witness §6.7 two-world control). **Seed-completion authority** means: §5.2 top-level - create eligibility (the full fresh-mutation authorization - position 1 requires) plus, for an account currently designated - while position 1 stands originated, the position-1 - initial-owner authority on the seed company — exactly the - authority whose origination self-confers it (§3.4). The - command succeeds only when BOTH: (a) the current seed-origin - account is **unavailable or unable** — it fails identity - §7.1's account-unavailability predicate (today exactly the + submits succession naming itself the epoch's seed-origin. + Succession is a **pure designation transfer**. It reads + identity state, platform eligibility, and the epoch record + only — never fence rows, recorded outcomes, grant rows, or + hierarchy records — and its complete write set is the epoch + record's designation update plus one audit event (closed + inventory, §6.1). It confers no grant of any kind. The command + succeeds only when BOTH: (a) the current seed-origin account + is **unavailable** — it fails identity §7.1's + account-unavailability predicate (today exactly the better-auth ban; §5.2 records that no separate deactivated - state exists and identity §7.3 defers hard deletion), or it - lacks seed-completion authority — succession while the current - origin is both available and able is refused; and (b) the - submitter is an identity-§7.1-eligible platform admin holding - §5.2 top-level create eligibility. Condition (b) references no - seed record and no prefix: a tenant-unprivileged platform - admin passes or fails it identically whether or not any seed - position is committed. Any future contract adding an - account-removal or account-disable mechanism MUST extend - identity §7.1's single unavailability predicate to cover it - and MUST preserve the epoch record's designation as a stable - reference across it (a retained identifier or tombstone — - never a cascade that rewrites or nulls the designation outside - this command). A submission failing either condition is - refused with the same single constant-shape bounded conflict - as the seed-boundary gate, byte-shape-identical whichever - condition failed and whether any seed fence exists, executes - nothing, and appends no event. Successful succession, in one - transaction, updates the designation in the epoch record, - **confers on the successor the position-1 initial-owner - authority on the seed company where position 1 stands - originated** — no more than originating position 1 from an - empty prefix would have self-conferred (§3.4), so succession - escalates nothing beyond the origination role it transfers — - and appends exactly one ordinary mutation audit event - recording the prior designation, the new designation, the - acting principal, and the conferred grant where one was - written; it rewrites no fence row and no recorded outcome — - rows already recorded immutably retain their original actor. - Every successful successor therefore holds seed-completion - authority at commit: a read-only or tenant-unprivileged - capture that strands the suffix cannot exist, and if a later - designation loses that authority while staying - identity-eligible, the _unable_ disjunct of condition (a) - makes re-succession available — succession is repeatable - across successive origin losses, by inability as well as by - unavailability (witnesses §6.7). Concurrent successions - serialize on the epoch record: exactly one submitter commits - and becomes the seed-origin, and the loser, re-evaluated - against the committed winner, fails condition (a) — the - now-current origin is identity-eligible and, holding the - just-conferred seed-completion authority, able — and receives - the constant-shape refusal. The seed-boundary gate and the - origination rule always read the epoch record's current - designation: after succession the successor originates the - remaining suffix in order under its own full fresh-mutation - authorization (supplied by the conferred authority plus its - own eligibility), and the fence rows it originates record the - successor. Factory reset (§4.2) remains the only path to a new - epoch; it is never required to complete an interrupted seed - sequence. + state exists and identity §7.3 defers hard deletion) — and no + other origin-side condition exists: the origin's grant state + is never read, so no allowed grant change (including an origin + revoking its own position-1 `owner` grant) can make + succession's outcome differ between two worlds whose identity, + platform, and epoch state agree; and (b) the submitter is an + identity-§7.1-eligible platform admin holding §5.2 top-level + create eligibility. Condition (b) references no seed record + and no prefix: a tenant-unprivileged platform admin passes or + fails it identically whether or not any seed position is + committed. Because no input and no write depends on seed + state, the command's observables — response bytes, event count + and content, grant-table state — and its timing are identical + across any two worlds whose identity, platform, and epoch + state agree, whatever seed state either holds: succession + carries no existence oracle, unconditionally (witness §6.7 + two-world controls, self-revocation pair included). Any future + contract adding an account-removal or account-disable + mechanism MUST extend identity §7.1's single unavailability + predicate to cover it and MUST preserve the epoch record's + designation as a stable reference across it (a retained + identifier or tombstone — never a cascade that rewrites or + nulls the designation outside this command). A submission + failing either condition is refused with the same single + constant-shape bounded conflict as the seed-boundary gate, + byte-shape-identical whichever condition failed and whether + any seed fence exists, executes nothing, and appends no event. + Successful succession, in one transaction, updates the + designation in the epoch record and appends exactly one + ordinary mutation audit event recording the prior designation, + the new designation, and the acting principal — nothing else; + it writes no grant, rewrites no fence row and no recorded + outcome — rows already recorded immutably retain their + original actor. What succession transfers is the origination + role alone: the §4.3 designation-derived authority to + originate the unoriginated suffix, and nothing else. Because + that authority derives from the designation itself, never from + a revocable grant, no inability state exists — a designation + that lost every grant still completes the suffix — and + succession over a fully completed sequence is harmless by + construction: there is nothing left to originate, so the + successor gains no authority over any existing record. A + successor who originates position 1 becomes the seed company's + initial owner exactly as any §5.2-eligible creator of a + top-level company would (contract 2 §4.3 default) — an + authority §5.2 already grants, not an escalation. Succession + recovers **seed-sequence completion**, never tenant ownership: + recovering ownership of an existing company whose owners are + all unavailable is an account-administration surface of the + identity and RBAC contracts, expressly out of this contract's + scope. Concurrent successions serialize on the epoch record: + exactly one submitter commits and becomes the seed-origin, and + the loser, re-evaluated against the committed winner, fails + condition (a) — the now-current origin is identity-eligible — + and receives the constant-shape refusal. The seed-boundary + gate and the origination rule always read the epoch record's + current designation: after succession the successor originates + the remaining suffix in order under its own full + fresh-mutation authorization (§4.3 designation-derived + authority plus its own eligibility), deriving every tuple from + canonical settings state alone — no replay of any committed + position is needed or implied — and the fence rows it + originates record the successor. Succession is repeatable + across successive origin losses. Factory reset (§4.2) remains + the only path to a new epoch; it is never required to complete + an interrupted seed sequence. - **No error replay.** The fence row commits only with its mutation, so only committed outcomes are ever recorded. A failed or refused submission records no fence row; a retry executes @@ -797,14 +876,18 @@ collects no sensitive category, so v1 ships no custody surface. only the remainder executes, without duplication and without compensating rollback of completed commands. There is no wizard-level transaction spanning steps. Loss of the seed-origin - account mid-sequence — by unavailability (identity §7.1) or by - loss of seed-completion authority — is likewise recoverable - without a new epoch: §4.3 seed-origin succession designates an - eligible successor, confers the position-1 authority where the - seed company exists, and the resumed run completes the remaining - suffix under the successor; succession is repeatable, so - interrupted runs strand nothing even across successive origin - losses. + account mid-sequence — identity §7.1 unavailability — is likewise + recoverable without a new epoch: §4.3 seed-origin succession + designates an eligible successor, and the resumed run completes + the remaining suffix under the successor's designation-derived + authority, deriving every tuple from canonical settings state + alone — no grant is conferred and no committed position is + replayed. Succession is repeatable, so interrupted runs strand + nothing even across successive origin losses. Origination + authority is designation-derived (§4.3), never grant-dependent, + so no revocation — including an origin revoking its own + position-1 `owner` grant — can strand the suffix while the + origin remains available. ## 5. Seeding authority (resolves contract 2 review NEW-1) @@ -829,8 +912,11 @@ collects no sensitive category, so v1 ships no custody surface. is required. Until that ruling, deny-by-default holds (contract 2 §3.1): no implicit creation authority exists. 3. Child-node creation inside the wizard (estate, project, workspace - under the seeded company) follows contract 2 §4.3 unchanged: parent - `owner` authority, no automatic grant needed. + under the seeded company) follows contract 2 §4.3: parent + `owner` authority, no automatic grant needed — for canonical seed + origination, the parent-authority component is satisfied by the + §4.3 designation-derived authority (§7 item 12); outside the + canonical seed key set the rule is unchanged. ## 6. Verification requirements @@ -853,7 +939,17 @@ Binding on the implementing PRs: write, and the applicable JIT writes, nothing else; each internal write is asserted to invoke the owning command family's ordinary write implementation (no parallel write path); and the handler is - asserted refused once the epoch has completed (identity §3.4). Wizard modules appear in no + asserted refused once the epoch has completed (identity §3.4). + **Succession-write inventory:** the §4.3 succession handler's + internal write set is statically enumerated and asserted equal, in + both directions, to: the epoch record's designation update plus + its single audit event (and the audit family's required outbox + record where one exists) — no `hierarchy_grants` write, no + fence-table write, no settings write, no hierarchy or content + write, nothing else; and the handler is statically asserted to + read no fence table, no recorded outcome, no grant table, and no + hierarchy record — its inputs are identity state, platform + eligibility, and the epoch record only (§4.3). Wizard modules appear in no class-table writer allowlist (contract 1 §6.3b) and hold no direct database or filesystem access (static assertion, plus a runtime probe that a wizard-context filesystem/database access attempt is @@ -1030,14 +1126,13 @@ Binding on the implementing PRs: canonical seed tuple set, submitted with `shared` declared by an actor fully authorized for the mutation — is refused with the §4.3 validation refusal, executes nothing, and records no fence - row (asserted absent); an **out-of-order shared declaration** — + row (asserted absent); an **out-of-order origination** — with the seed prefix committed through position k, the - seed-origin account submits the position k+2 tuple (past the - next unrecorded position), constructed by the test harness from - ids it obtained out of band — and is refused with the §4.3 - validation refusal, executes nothing, and records no fence row, - proving the prefix-aware derivation rejects positions the - canonical walk cannot yet derive; an **unrecorded-seed-key + seed-origin account submits the position k+2 canonical tuple + (past the next unrecorded position) — and is refused with the + §4.3 validation refusal, executes nothing, and records no fence + row, proving in-order origination is enforced even though every + tuple is derivable at any state; an **unrecorded-seed-key race** in two variants — (child variant) with one child seed tuple deliberately left unrecorded, an actor lacking that seed command's hierarchy authority races the seed-origin account's @@ -1070,71 +1165,104 @@ Binding on the implementing PRs: the recorded outcome — proving actor equality is never a substitute for live target-result read authority on any replay mode; **seed-origin succession witnesses (§4.3, NEW-11 through - NEW-14):** a **succession recovery** — the seed-origin account + NEW-17):** a **succession recovery** — the seed-origin account commits a proper seed prefix and is then banned (identity §7.1); an eligible platform admin holding §5.2 top-level create eligibility submits succession, the epoch record's designation changes to the successor with exactly one mutation audit event - recording the prior designation, the new designation, the acting - principal, and the conferred position-1 grant, and the - successor's resumed run replays the committed prefix (its access - attributed by §4.3 replay access events, its read authority - supplied by the conferred grant) and originates the remaining - suffix in order — the new fence rows record the successor, the + recording the prior designation, the new designation, and the + acting principal — asserted to write no grant row and touch no + fence row — and the successor's resumed run derives the + remaining tuples from canonical settings state alone and + originates the suffix in order, never submitting a committed + position: zero replay access events name the successor, zero + grant rows name the successor, the mutation count equals exactly + the suffix length, the new fence rows record the successor, the pre-succession rows immutably retain the original origin, and the full seed set completes with no factory reset and no new epoch; an **origin-available succession refusal** — the same eligible admin submits succession while the current origin - remains §7.1-eligible and holds seed-completion authority — is - refused with the single constant-shape bounded conflict - (asserted byte-shape-identical to the collision refusal), - changes no epoch record, and appends no audit event; a - **succession two-world control (NEW-12)** — the SAME - identity-eligible, §5.2-create-eligible platform admin holding - no grant on any seed record submits succession in two prepared - worlds with the current origin banned in both: one where a - proper seed prefix stands committed and one freshly finalized - epoch with no position committed — and in BOTH worlds the - command succeeds, the designation changes to the submitter, and - exactly one succession audit event appends, with the response - asserted equal in shape and error/success class across the - worlds and the evaluation asserted within a fence-independent - timing bound (it consults no fence row); the control is repeated + remains §7.1-eligible — is refused with the single + constant-shape bounded conflict (asserted byte-shape-identical + to the collision refusal), changes no epoch record, and appends + no audit event; a **succession two-world control (NEW-12)** — + the SAME identity-eligible, §5.2-create-eligible platform admin + holding no grant on any seed record submits succession in two + prepared worlds with the current origin banned in both: one + where a proper seed prefix stands committed and one freshly + finalized epoch with no position committed — and in BOTH worlds + the command succeeds, the designation changes to the submitter, + and exactly one succession audit event appends, with the worlds + asserted equal in response bytes (modulo run-scoped + identifiers), in audit event content (the same field set and + classes, no seed reference and no grant reference in either), + and in grant-table delta (empty in both), and the full command + execution asserted within a fence-independent timing bound (it + consults no fence row and no grant row); the control is repeated with a submitter failing condition (b) — refused byte-shape-identically in both worlds, no record change, no - event in either — proving succession's outcome is a function of - fence-independent inputs only; a **tenant-unprivileged successor - completion (NEW-13)** — the successor of the two-world control's - recorded world, who held no seed-record authority before - succeeding, completes the entire remaining suffix using only the - conferred position-1 authority plus its own eligibility, proving - no read-only capture can strand the suffix; a **post-succession - grant-revocation re-succession (NEW-13)** — after an A→B - succession and further committed progress, B's conferred - position-1 authority is revoked while B remains - identity-eligible; an eligible, §5.2-create-eligible admin C - submits succession and succeeds through condition (a)'s _unable_ - disjunct, receives the conferred authority, and completes the - suffix — no factory reset, exactly one audit event for C's - succession; a **repeat succession (NEW-14)** — A is banned, B - succeeds and commits further prefix, B is then banned, C - succeeds and completes the suffix: each succession appends - exactly one event, every fence row records its true originator - (A's rows, B's rows, C's rows), and the seed set completes; an - **empty-prefix succession (NEW-13)** — the origin is banned - after finalize but before any seed position commits; the - successor succeeds (condition (b) evaluated with no seed record - in existence), originates the ENTIRE sequence, and position 1's - committed payload names the successor as initial owner — - asserting §3.4's derivation reads the current designation for - unrecorded positions; a **succession race** — with the origin - banned, two eligible, §5.2-create-eligible admins submit - succession concurrently: the epoch record serializes them, - exactly one commits and becomes the designated origin with one - audit event and the conferred authority, the loser receives the - constant-shape refusal (re-evaluated: the now-current origin is - eligible and able), and the seed sequence completes exactly once - under the winner; a submission that failed before commit + event in either — proving succession's outcome and observables + are a function of identity, platform, and epoch state only; a + **self-revocation two-world refusal (NEW-12)** — world U holds a + freshly finalized epoch with no seed position committed; in + world R the origin A originates position 1 (the outcome names A + initial owner by the contract 2 §4.3 default) and then, still + eligible, revokes its own `owner` grant on the seed company + (RBAC §4.1 authorizes it); identity, platform, and epoch state + now agree across U and R; an eligible platform admin B submits + succession in both worlds and is refused byte-shape-identically + in BOTH (the origin is available; grant state is not an input), + with no event and no record change in either — proving no + allowed grant change opens succession or leaks seed state; a + **designation-derived completion (NEW-12/NEW-13)** — origin A + originates a prefix, revokes its own position-1 `owner` grant, + and resumes: every remaining origination succeeds under the + designation-derived authority, zero new grant rows are written + beyond each command's own §5.2/§4.3 defaults, and the seed set + completes — proving completion never depends on a revocable + grant; a **tenant-unprivileged successor completion (NEW-13)** — + the successor of the two-world control's recorded world, who + held no seed-record authority before succeeding and receives + none by succeeding, completes the entire remaining suffix by + direct in-order origination using only the designation-derived + authority plus its own eligibility; a subsequent read attempt by + that successor against a committed pre-succession seed record is + refused — proving succession recovers completion without + conferring read or tenant authority; a **post-completion + harmlessness control (NEW-16)** — with the seed sequence fully + complete, the origin is banned and an eligible platform admin B + submits succession: it succeeds with exactly one audit event, + zero grant rows written, and zero hierarchy or content + mutations; B's subsequent attempts to read the seed company, its + children, or their content are refused — proving succession over + a completed sequence confers nothing; a **post-succession replay + (NEW-13)** — after an A→B succession, an admin holding explicit + target-result read authority on the recorded seed targets + derives position 1's tuple afresh from canonical settings state: + the derived payload is asserted byte-identical to the committed + one (digest equality — no payload field ever named the origin) + and the submission replays, returning the recorded outcome; a + **repeat succession (NEW-14)** — A is banned, B succeeds and + commits further prefix, B is then banned, C succeeds and + completes the suffix: each succession appends exactly one event, + every fence row records its true originator (A's rows, B's rows, + C's rows), and the seed set completes; an **empty-prefix + succession (NEW-13)** — the origin is banned after finalize but + before any seed position commits; the successor succeeds + (condition (b) evaluated with no seed record in existence) and + originates the ENTIRE sequence: position 1's committed payload + is asserted byte-identical to a control derivation performed + before the succession (digest equality — the payload names no + account), and the recorded outcome and its same-operation + initial `owner` grant name the successor — asserting the owner + binding follows the acting designation through the outcome, not + the payload; a **succession race** — with the origin banned, two + eligible, §5.2-create-eligible admins submit succession + concurrently: the epoch record serializes them, exactly one + commits and becomes the designated origin with one audit event, + the loser receives the constant-shape refusal (re-evaluated: the + now-current origin is identity-eligible), and the seed sequence + completes exactly once under the winner; a submission that failed before commit leaves no fence row and its retry executes; two concurrent resumed runs executing the seed sequence yield exactly one seed set — per key, exactly one mutation and one mutation audit event exist, and @@ -1221,14 +1349,18 @@ contracts and are not additions: actor-bound row additionally requires recorded-actor equality — never as a substitute for target-result authority — and a declared-mode mismatch is a collision. Including - the **shared-declaration boundary**: `shared` is server-verified - against the epoch's canonical seed key set (fence-independent, - derived from the epoch id and the fixed seed-role list) and its - prefix-derived canonical seed tuple set — each position's scope - and digest derived from the committed predecessors' recorded - outcomes — with a shared declaration outside the key set, off - its position's derived tuple, or past the next unrecorded - position refused recording nothing (§4.3). Including the + the **shared-declaration boundary and canonical-reference + resolution**: `shared` is server-verified against the epoch's + canonical seed key set and its canonical seed tuple set, both + fully derived from the epoch id, the immutable seed-parameter + provenance, and the fixed example set alone — no canonical seed + payload or scope contains a generated id or an account + identifier; child positions reference parents by epoch-scoped + canonical seed role, resolved to canonical rows server-side at + execution, available to exactly the canonical seed tuples — with + a shared declaration outside the key set or off its position's + derived tuple, and any origination past the next unrecorded + position (out of order), refused recording nothing (§4.3). Including the **seed-boundary gate**: the epoch's seed-origin designation initially names the first admin the finalize transaction created and changes only through item 12's succession command; @@ -1240,7 +1372,9 @@ contracts and are not additions: worlds are indistinguishable to the refused submitter — and origination of a missing seed fence is reserved to the seed-origin account passing the full fresh-mutation - authorization for that seed command (§4.3). Including the **replay access event**: a + authorization for that seed command, its hierarchy-authority + component satisfied by item 12's designation-derived authority + (§4.3). Including the **replay access event**: a distinct non-mutation audit event class, appended on every shared replay, recording the accessing actor, the current request's correlation ids, and the fence row returned — the @@ -1292,32 +1426,47 @@ contracts and are not additions: provenance for §3.4's derivation; the current company name lives on the company row and changes only through the ordinary hierarchy rename (§3.1, witnesses §6.3). -12. The **seed-origin succession command** (§4.3) — an amendment to - identity §3's epoch surface, proposed and ratified here, - severable: one mutating command that redesignates the epoch's - seed-origin to the submitter, valid only while the current - origin is **unavailable or unable** — it fails identity §7.1's +12. The **seed-origin succession command and the + designation-derived origination authority** (§4.3) — two + coupled amendments, proposed and ratified here, severable + together. First, an amendment to identity §3's epoch surface: + one mutating command that redesignates the epoch's seed-origin + to the submitter, valid only while the current origin is + **unavailable** — it fails identity §7.1's account-unavailability predicate (today exactly the better-auth ban; future removal or disable contracts must extend that one - predicate and preserve the designation, §4.3) or lacks - seed-completion authority — and the submitter is an - identity-§7.1-eligible platform admin holding §5.2 top-level - create eligibility, a condition evaluated against identity, - platform-eligibility, and epoch-record state only, never - against fence rows or seed-record grants, so its outcome and - timing are fence-independent (no existence oracle). Refusals - are the constant-shape §4.3 conflict regardless of which - condition failed; success is one audited epoch-record write - serialized on the epoch record that also confers, in the same - transaction and audit event, the position-1 initial-owner - authority on the seed company where position 1 has originated - — exactly the authority position-1 origination itself confers — - so every successor holds seed-completion authority at commit; - the command is repeatable (a later unavailable-or-unable + predicate and preserve the designation, §4.3) — and the + submitter is an identity-§7.1-eligible platform admin holding + §5.2 top-level create eligibility; both conditions are + evaluated against identity, platform-eligibility, and + epoch-record state only, never against fence rows, recorded + outcomes, grant rows, or hierarchy records, so the command's + outcome, observables, and timing are world-independent + unconditionally (no existence oracle). Refusals are the + constant-shape §4.3 conflict regardless of which condition + failed; success is one audited epoch-record write serialized on + the epoch record, whose closed write set (§6.1) is the + designation update plus its single audit event — it confers no + grant of any kind and grants no tenant authority; ownership + recovery for existing records is expressly out of scope + (§4.3). The command is repeatable (a later unavailable successor is succeeded the same way), and recorded fence rows - are never rewritten. Without this amendment a banned - seed-origin account strands the unoriginated seed suffix, - contradicting PRD D4's no-lock-in requirement (§4.4). + are never rewritten. Second, a scoped amendment to contract 2 + §4's authorization evaluation: the **designation-derived + origination authority** — for exactly the fresh origination of + a currently-unoriginated canonical seed position, the epoch + record's current designation itself satisfies the + parent-`owner` (or top-level) hierarchy-authority component of + fresh-mutation authorization; it confers no read authority, no + replay authority, no standing grant, no authority over any + committed record, and nothing outside the canonical seed key + set, is evaluated per submission, and writes nothing; every + other authorization component (§5.2 eligibility, identity §7.1 + eligibility) is evaluated fresh and unchanged. Together these + make origin loss recoverable with no escalation surface: + without them a banned seed-origin account strands the + unoriginated seed suffix, contradicting PRD D4's no-lock-in + requirement (§4.4). ## Ruling request -- 2.54.0 From 552650a69b71fe51c410708c4e2519e8a569f150 Mon Sep 17 00:00:00 2001 From: fred Date: Thu, 27 Aug 2026 04:12:05 -0500 Subject: [PATCH 13/15] =?UTF-8?q?contract(onboarding-wizard):=20revision?= =?UTF-8?q?=2013=20=E2=80=94=20seed-progress=20query,=20generalized=20desi?= =?UTF-8?q?gnation-derived=20authority?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses sol r12 verdict (NEW-18, NEW-19): - NEW-18: new mapped, designation-only seed-progress query returning exactly the next unrecorded canonical position index (or completion marker); screening evaluated before any fence state, non-designated submitters refused byte-shape-identically across recorded and unrecorded worlds; closed read set (position-committed existence flags + designation, §6.1); origin fresh-client resume and successor completion both query first and originate from the returned index. - NEW-19: designation-derived authority generalized to the actor-authorization component of every canonical position's owning family, each surface named expressly (contract 2 §4; RBAC §§2-3 workspace-content authorization; native-kanban SOT REQ-TEN-001 / A1 §8.1.3) as coupled severable-together amendments under one mechanically decidable scope, with a defined result-disclosure boundary (canonical outcome fields only). - §6.7: seed-progress two-world refusal, entitlement witness with the actual fresh client run against both worlds, content-position completion in both recovery variants, non-canonical content refusal, result-disclosure witness. - §7 item 12 now three coupled amendments; §1.1/§1.2/§5.3-5.4/§6.1 disclosures updated; preamble Revision 13 paragraph. --- docs/requirements/onboarding-wizard.md | 372 ++++++++++++++++++++----- 1 file changed, 303 insertions(+), 69 deletions(-) diff --git a/docs/requirements/onboarding-wizard.md b/docs/requirements/onboarding-wizard.md index 6b2c68cc..d50a3ab9 100644 --- a/docs/requirements/onboarding-wizard.md +++ b/docs/requirements/onboarding-wizard.md @@ -277,7 +277,55 @@ identity actually defines (NEW-14). §6.7 reworked: the two-world control now asserts equal event content, an empty grant-table delta, and a full-command timing bound; new self-revocation two-world refusal, designation-derived completion, post-completion -harmlessness, and post-succession replay witnesses. +harmlessness, and post-succession replay witnesses. (Revision 13 +generalizes the designation-derived authority — which this revision +scoped to contract 2 §4 only — across every canonical position's +owning family, and adds the seed-progress query that locates the +suffix.) + +Revision 13 (sol re-review 11: NEW-18, NEW-19): the designated +client can locate the suffix, and can originate all of it. +**Seed-progress query** (NEW-18): revision 12 made every canonical +tuple derivable but left the committed-prefix length underivable — +a fresh successor's permitted inputs were identical across an +empty-prefix world and a k-committed world, so §6.7's required +never-submit-a-committed-position walk was impossible without +hidden fixture state, and blind submission could not distinguish a +committed prefix from a real collision or authorization loss. §4.3 +now defines one mapped, designation-only read surface returning +exactly the epoch's next unrecorded canonical position index (or a +completion marker): the designation screening is evaluated before +any fence state, every non-designated submitter — former +designations and platform admins included — is refused +byte-shape-identically across recorded and unrecorded worlds, the +read set is closed to position-committed existence flags plus the +designation (§6.1), and the state-dependent answer for the +designated account is the entitled disclosure that is the query's +purpose. The original origin's fresh-client resume and the +successor's completion both query first and originate in order +from the returned index; no client infers progress from refusal +shapes. **Generalized designation-derived authority** (NEW-19): +revision 12's amendment reached only contract 2 §4's +hierarchy-authority component, so a grant-less designation — an +origin that revoked its own position-1 `owner` grant, or a +tenant-unprivileged successor — could originate hierarchy +positions but not the ordinary workspace-content example +positions, whose authorization lives in RBAC §§2–3 and the +native-kanban SOT. §7 item 12 now amends the actor-authorization +component of every canonical position's owning family expressly — +contract 2 §4; RBAC §§2–3; native-kanban SOT REQ-TEN-001 / +Amendment A1 §8.1.3 — coupled and severable together, under one +mechanically decidable scope (current designation, next unrecorded +canonical position, exact server-derived tuple, in-order fresh +origination) and a defined **result-disclosure boundary**: a +successful origination's response and recorded outcome carry +exactly the command's canonical outcome fields, and a +designation-only actor's non-canonical content command is refused +by the owning contract. Witnesses §6.7: seed-progress two-world +refusal, seed-progress entitlement with the actual fresh client +run against both worlds, content-position completion in both +recovery variants, non-canonical content refusal, and the +result-disclosure witness. Scope: the Gateway-backed product onboarding wizard. Out of scope: the host-local install wizard (`mosaic wizard`, which drives host install and @@ -312,8 +360,9 @@ through the extensibility rule §2.4). hierarchy commands and the rank-4 enrollment command (contract 5 §3.1 — built first), the settings command family (steps 1–2), the identity bootstrap and registration surface (step 3, including the - §3.3 finalize command and the §4.3 seed-origin succession - command), the mode reads (contract 6 §2.2 post-epoch; + §3.3 finalize command, the §4.3 seed-origin succession + command, and the §4.3 seed-progress query), the mode reads + (contract 6 §2.2 post-epoch; the §2.3 bootstrap-status field pre-epoch), and the ordinary content commands used for example seeding (step 4). Two of these families (ranks 1 and 4) are in contract 5 §3.1's live rank-6 @@ -334,12 +383,19 @@ through the extensibility rule §2.4). transaction — and the **§7 item 12 seed-origin succession command** (§4.3), one mutating epoch-record command whose closed write set is the designation update and its single audit event. Beyond those - two amendments, nothing is added to identity §3; the one further - disclosed authorization amendment is §7 item 12's - **designation-derived origination authority** — a scoped change to - contract 2 §4's evaluation for canonical seed origination only, - conferring no grant and no read authority — and no undisclosed - authority exists; all three surfaces appear in + two amendments, nothing is added to identity §3; the further + disclosed amendments are §7 item 12's + **designation-derived origination authority** — a scoped change, + for canonical seed origination only, to the actor-authorization + component of each canonical position's owning family (contract 2 + §4's hierarchy authority; RBAC §§2–3's workspace-content + authorization; the native-kanban SOT's workspace-scoped command + authorization, REQ-TEN-001 / Amendment A1 §8.1.3), conferring no + grant and no read authority — and §7 item 12's **seed-progress + query** (§4.3), one designation-only mapped read surface + disclosing exactly the epoch's next unrecorded canonical seed + position index (or a completion marker) and nothing else; no + undisclosed authority exists; all of these surfaces appear in the §1.1 composed-family inventory, the §7.8 mapping amendment, and §6.1's inventories, so the D8 mapping and authorization-parity witnesses cannot omit them. @@ -558,7 +614,17 @@ the named authority: would. Seeded examples are ordinary workspace content created by ordinary commands, attributable in audit to the acting admin, carrying the run's trace correlation ids (contract 5 §4.3) - and the §4.3 idempotency keys. + and the §4.3 idempotency keys; their actor-authorization + component — RBAC §§2–3's workspace-content authorization, and + the native-kanban SOT's workspace-scoped command authorization + (REQ-TEN-001, Amendment A1 §8.1.3) for the kanban example + positions — is, for canonical seed origination only, likewise + satisfied by the §4.3 designation-derived authority, under the + same mechanical scope and the §4.3 result-disclosure boundary + (§7 item 12); every other component of each content command's + evaluation (validation, eligibility, collision and replay + rules) is unchanged, and outside the canonical seed key set + every content command's authorization is untouched. 5. **Minimal agent enrollment.** One harness, API-key login, agent name and persona (D11). Enrollment specifics belong to the rank-4 agent-enrollment command family; this contract binds only that the @@ -718,17 +784,43 @@ collects no sensitive category, so v1 ships no custody surface. executing its seed mutation in order, passing the full fresh-mutation authorization for that seed command — §5.2 eligibility and identity §7.1 eligibility evaluated fresh - against the submitter, with the hierarchy-authority component + against the submitter, with the actor-authorization component satisfied by the **designation-derived authority**: for exactly the fresh origination of a currently-unoriginated canonical seed position, the current designation itself - satisfies the parent-`owner` (or top-level) authority contract - 2 §4 requires, a disclosed scoped amendment (§7 item 12) that - confers no read authority, no replay authority, no standing - grant, no authority over any committed record, and nothing - outside the canonical seed key set — evaluated per submission, - writes nothing; a refused submission records no row (no-error - replay below). The recorded actor of every shared row is + satisfies the actor-authorization component of the position's + **owning command family** — for the top-level and child + hierarchy positions, the parent-`owner` (or top-level) + authority contract 2 §4 requires; for the ordinary + workspace-content example positions, the workspace-content + authorization RBAC §§2–3 require and the workspace-scoped + command authorization the native-kanban SOT requires + (REQ-TEN-001, Amendment A1 §8.1.3) — coupled disclosed + amendments to each named surface (§7 item 12), severable + together, that confer no read authority, no replay authority, + no standing grant, no authority over any committed record, + and nothing outside the canonical seed key set. The + authority's scope is mechanically decidable per submission: + the submitter is the epoch's current designation, the + (operation, key) is the next unrecorded canonical position, + the payload and scope equal that position's server-derived + tuple exactly, and the submission is a fresh in-order + origination; every other component of the owning family's + evaluation (validation, §5.2 eligibility, identity §7.1 + eligibility, collision and replay rules) is evaluated fresh + and unchanged; the authority is evaluated per submission and + writes nothing beyond the command's own canonical writes; a + refused submission records no row (no-error replay below). + **Result-disclosure boundary.** A successful origination's + response and recorded outcome contain exactly the command's + canonical outcome fields — the created row id, any + canonical-reference-resolved parent id, the canonical key, + and the payload digest — and nothing else: no listing, no + attribute of any pre-existing record beyond the resolved + parent id the outcome must record, and no grant- or + fence-table content. Refusals on canonical seed keys are + screened by the seed-boundary gate first and keep its + constant shape. The recorded actor of every shared row is therefore the account that was the epoch's seed-origin at that position's origination, authorized for the mutation the row fences. Recorded positions are replayable by any admin holding @@ -807,13 +899,47 @@ collects no sensitive category, so v1 ships no custody surface. current designation: after succession the successor originates the remaining suffix in order under its own full fresh-mutation authorization (§4.3 designation-derived - authority plus its own eligibility), deriving every tuple from - canonical settings state alone — no replay of any committed - position is needed or implied — and the fence rows it + authority plus its own eligibility), locating that suffix + through the seed-progress query below and deriving every + tuple from canonical settings state alone — no replay of any + committed position and no read of any recorded outcome is + needed or implied — and the fence rows it originates record the successor. Succession is repeatable across successive origin losses. Factory reset (§4.2) remains the only path to a new epoch; it is never required to complete an interrupted seed sequence. + - **Seed-progress query.** Locating the unoriginated suffix is a + disclosed, mapped read surface, never an inference from + refusal shapes: the **seed-progress query** returns exactly + one value — the index of the epoch's next unrecorded + canonical seed position, or a completion marker when every + position is recorded — and nothing else. It is authorized for + exactly the account the epoch's designation currently names, + and that screening is evaluated first, before any fence state + is consulted: every other submitter — a former designation, a + platform admin, and an admin holding target-result read + authority included — is refused with the same single + constant-shape bounded conflict as the seed-boundary gate, + byte-shape-identical whether any seed fence exists or not, so + the recorded and unrecorded worlds stay indistinguishable to + every non-designated actor and RBAC §7's no-existence-oracle + rule holds on this surface too. For the designated account + the returned index is intentionally state-dependent — that + disclosure is the query's purpose, and it is bounded: the + query reads only which canonical positions have committed + fence rows (position-committed existence) plus the epoch + record's designation, never a recorded outcome, a payload or + digest, a resolved id, an acting principal, or any grant + state, and its closed read set is statically asserted (§6.1). + The query writes nothing and appends no event. A resuming + designated client — the original origin on a fresh client and + a §4.3 successor alike — first submits the query and then + originates in order from the returned index, deriving each + tuple from canonical settings state alone; an admin holding + target-result read authority may instead walk the sequence + and receive recorded outcomes as replays (§6.3). No client is + required to infer progress from refusals, and no + non-designated actor can. - **No error replay.** The fence row commits only with its mutation, so only committed outcomes are ever recorded. A failed or refused submission records no fence row; a retry executes @@ -870,24 +996,30 @@ collects no sensitive category, so v1 ships no custody surface. individually atomic commands, not one transaction: an interruption between them leaves a prefix of committed seed nodes, and the §4.3 fence makes the resumed run complete exactly the remaining suffix — - the run re-derives the full ordered seed sequence from canonical - state and re-submits it with the same deterministic keys, - already-committed mutations return their recorded outcomes, and - only the remainder executes, without duplication and without - compensating rollback of completed commands. There is no + the resumed designated client locates that suffix through the + §4.3 seed-progress query and originates in order from the + returned index, re-deriving every tuple from canonical state; a + target-authorized admin's re-run may instead re-submit the full + ordered seed sequence with the same deterministic keys, + already-committed mutations returning their recorded outcomes — + either way only the remainder executes, without duplication and + without compensating rollback of completed commands. There is no wizard-level transaction spanning steps. Loss of the seed-origin account mid-sequence — identity §7.1 unavailability — is likewise recoverable without a new epoch: §4.3 seed-origin succession - designates an eligible successor, and the resumed run completes - the remaining suffix under the successor's designation-derived - authority, deriving every tuple from canonical settings state - alone — no grant is conferred and no committed position is - replayed. Succession is repeatable, so interrupted runs strand - nothing even across successive origin losses. Origination - authority is designation-derived (§4.3), never grant-dependent, - so no revocation — including an origin revoking its own - position-1 `owner` grant — can strand the suffix while the - origin remains available. + designates an eligible successor, and the resumed run locates + the remaining suffix through the §4.3 seed-progress query and + completes it under the successor's designation-derived + authority — the ordinary workspace-content example positions + included (§4.3, §7 item 12) — deriving every tuple from + canonical settings state alone: no grant is conferred and no + committed position is replayed. Succession is repeatable, so + interrupted runs strand nothing even across successive origin + losses. Origination authority — and suffix location, the §4.3 + seed-progress query — is designation-derived (§4.3), never + grant-dependent, so no revocation — including an origin + revoking its own position-1 `owner` grant — can strand the + suffix while the origin remains available. ## 5. Seeding authority (resolves contract 2 review NEW-1) @@ -917,6 +1049,18 @@ collects no sensitive category, so v1 ships no custody surface. origination, the parent-authority component is satisfied by the §4.3 designation-derived authority (§7 item 12); outside the canonical seed key set the rule is unchanged. +4. Example-content creation inside the wizard (the fixed §3.4 + example set) follows each item's owning content contract — RBAC + §§2–3's workspace-content authorization and, for the kanban + examples, the native-kanban SOT's workspace-scoped command + authorization (REQ-TEN-001, Amendment A1 §8.1.3) — with the + same canonical-seed-origination exception: for exactly those + positions, the actor-authorization component is satisfied by + the §4.3 designation-derived authority under §7 item 12's + mechanical scope and the §4.3 result-disclosure boundary. + Outside the canonical seed key set every content rule is + unchanged, and a designation-only actor's non-canonical content + command is refused by the owning contract (witness §6.7). ## 6. Verification requirements @@ -949,7 +1093,17 @@ Binding on the implementing PRs: write, nothing else; and the handler is statically asserted to read no fence table, no recorded outcome, no grant table, and no hierarchy record — its inputs are identity state, platform - eligibility, and the epoch record only (§4.3). Wizard modules appear in no + eligibility, and the epoch record only (§4.3). + **Seed-progress-query inventory:** the §4.3 seed-progress query + handler's read set is statically enumerated and asserted equal, + in both directions, to: the epoch record's current designation + (its authorization input, screened first) and the + position-committed existence flags of the epoch's canonical + seed keys — no recorded outcome, no payload or digest, no + resolved id, no acting principal, no grant table, no hierarchy + or content record; its write set is asserted empty (no event, + no row), and its response schema is closed to the single + index-or-completion value. Wizard modules appear in no class-table writer allowlist (contract 1 §6.3b) and hold no direct database or filesystem access (static assertion, plus a runtime probe that a wizard-context filesystem/database access attempt is @@ -1172,9 +1326,12 @@ Binding on the implementing PRs: changes to the successor with exactly one mutation audit event recording the prior designation, the new designation, and the acting principal — asserted to write no grant row and touch no - fence row — and the successor's resumed run derives the + fence row — and the successor's resumed run submits the §4.3 + seed-progress query — asserted to return exactly the first + unrecorded index — derives the remaining tuples from canonical settings state alone and - originates the suffix in order, never submitting a committed + originates the suffix in order from that index, never + submitting a committed position: zero replay access events name the successor, zero grant rows name the successor, the mutation count equals exactly the suffix length, the new fence rows record the successor, the @@ -1214,21 +1371,66 @@ Binding on the implementing PRs: in BOTH (the origin is available; grant state is not an input), with no event and no record change in either — proving no allowed grant change opens succession or leaks seed state; a - **designation-derived completion (NEW-12/NEW-13)** — origin A - originates a prefix, revokes its own position-1 `owner` grant, - and resumes: every remaining origination succeeds under the + **seed-progress two-world refusal (NEW-18)** — worlds U (no + seed position committed) and P (positions 1..k committed) agree + on identity, platform, and epoch state; a non-designated actor + — an eligible platform admin, a former designation after + succession, and an admin holding target-result read authority + each probed — submits the §4.3 seed-progress query in both + worlds and is refused byte-shape-identically in both, with no + event appended in either — proving the query surface discloses + nothing to anyone but the current designation; a + **seed-progress entitlement witness (NEW-18)** — the SAME + designated account submits the query in worlds U and P: it + returns 1 in U and k+1 in P (the intentional, entitled + disclosure), reading no outcome payload and no grant state + (asserted by the §6.1 closed read set); the actual fresh + client — holding no transient state, no bookkeeping cache, and + no seed read authority — is then run to completion against + BOTH worlds, and in each it originates first exactly the + returned index and completes the sequence without submitting + any committed position and without receiving a single refusal + in the walk — proving progress location is a disclosed entitled + read, never an inference from refusal shapes; a + **designation-derived completion (NEW-12/NEW-13/NEW-19)** — + origin A originates a prefix extending past workspace creation + — so the remaining suffix consists of ordinary + workspace-content example positions — revokes its own + position-1 `owner` grant, and resumes: the seed-progress query + returns the first unrecorded index and every remaining + origination, the content positions included, succeeds under the designation-derived authority, zero new grant rows are written beyond each command's own §5.2/§4.3 defaults, and the seed set - completes — proving completion never depends on a revocable - grant; a **tenant-unprivileged successor completion (NEW-13)** — - the successor of the two-world control's recorded world, who + completes — proving completion, content positions included, + never depends on a revocable grant; a **tenant-unprivileged + successor completion (NEW-13/NEW-19)** — + the successor of the two-world control's recorded world, whose + recorded prefix likewise extends past workspace creation, who held no seed-record authority before succeeding and receives - none by succeeding, completes the entire remaining suffix by - direct in-order origination using only the designation-derived + none by succeeding, completes the entire remaining suffix — + its workspace-content positions included — by + direct in-order origination from the seed-progress query's + returned index, using only the designation-derived authority plus its own eligibility; a subsequent read attempt by that successor against a committed pre-succession seed record is refused — proving succession recovers completion without - conferring read or tenant authority; a **post-completion + conferring read or tenant authority; a **non-canonical content + refusal (NEW-19)** — a designation holding no grant (the + self-revocation world, post-completion) submits, against the + seed workspace, an ordinary content command whose (operation, + key) is outside the canonical seed key set — a second, + non-canonical kanban item — and is refused by the owning + content contract's authorization with nothing written — proving + the designation-derived authority stops at the canonical set + and confers no general workspace authority; a + **result-disclosure witness (NEW-19)** — a successor's + content-position origination response and recorded outcome are + asserted to contain exactly the canonical outcome fields + (created row id, resolved parent id, canonical key, payload + digest) and no attribute of any pre-existing record beyond the + resolved parent id — proving origination under the + designation-derived authority discloses nothing further; a + **post-completion harmlessness control (NEW-16)** — with the seed sequence fully complete, the origin is banned and an eligible platform admin B submits succession: it succeeds with exactly one audit event, @@ -1395,9 +1597,9 @@ contracts and are not additions: rank-4 enrollment families, is amended to name all six composed families — adding the settings command family, the identity bootstrap and registration surface (including the §3.3 finalize - command and the §4.3 seed-origin succession command, so the - official-tool mapping and §6.1's authorization-parity witnesses - cannot omit either), the mode reads (contract 6 §2.2 and the §2.3 + command, the §4.3 seed-origin succession command, and the §4.3 + seed-progress query, so the official-tool mapping and §6.1's + witnesses can omit none of them), the mode reads (contract 6 §2.2 and the §2.3 bootstrap-status field), and the ordinary content commands used for example seeding — with one mapping row per newly named family. Without this amendment those operations are unmapped and blocked @@ -1426,9 +1628,9 @@ contracts and are not additions: provenance for §3.4's derivation; the current company name lives on the company row and changes only through the ordinary hierarchy rename (§3.1, witnesses §6.3). -12. The **seed-origin succession command and the - designation-derived origination authority** (§4.3) — two - coupled amendments, proposed and ratified here, severable +12. The **seed-origin succession command, the designation-derived + origination authority, and the seed-progress query** (§4.3) — + three coupled amendments, proposed and ratified here, severable together. First, an amendment to identity §3's epoch surface: one mutating command that redesignates the epoch's seed-origin to the submitter, valid only while the current origin is @@ -1451,21 +1653,53 @@ contracts and are not additions: recovery for existing records is expressly out of scope (§4.3). The command is repeatable (a later unavailable successor is succeeded the same way), and recorded fence rows - are never rewritten. Second, a scoped amendment to contract 2 - §4's authorization evaluation: the **designation-derived - origination authority** — for exactly the fresh origination of - a currently-unoriginated canonical seed position, the epoch - record's current designation itself satisfies the - parent-`owner` (or top-level) hierarchy-authority component of - fresh-mutation authorization; it confers no read authority, no - replay authority, no standing grant, no authority over any - committed record, and nothing outside the canonical seed key - set, is evaluated per submission, and writes nothing; every - other authorization component (§5.2 eligibility, identity §7.1 - eligibility) is evaluated fresh and unchanged. Together these - make origin loss recoverable with no escalation surface: - without them a banned seed-origin account strands the - unoriginated seed suffix, contradicting PRD D4's no-lock-in + are never rewritten. Second, the **designation-derived + origination authority** — a scoped amendment to the + actor-authorization component of every canonical seed + position's owning command family, each amended surface named + expressly: contract 2 §4's parent-`owner` and top-level + hierarchy-authority evaluation (the company, estate, project, + and workspace positions); RBAC §§2–3's workspace-content + authorization evaluation (the ordinary example-content + positions); and the native-kanban SOT's workspace-scoped + command authorization (REQ-TEN-001, Amendment A1 §8.1.3) for + the kanban example positions. For exactly the fresh + origination of a currently-unoriginated canonical seed + position, the epoch record's current designation itself + satisfies that one component; the scope is mechanically + decidable per submission — current designation, next + unrecorded canonical position, exact server-derived tuple, + in-order fresh origination — and every other component of each + family's evaluation (validation, §5.2 eligibility, identity + §7.1 eligibility, collision and replay rules) is evaluated + fresh and unchanged. It confers no read authority, no replay + authority, no standing grant, no authority over any committed + record, and nothing outside the canonical seed key set; it is + evaluated per submission and writes nothing beyond the + command's own canonical writes; and a successful origination's + response and recorded outcome are bounded to exactly the + command's canonical outcome fields (created row id, resolved + parent id, canonical key, payload digest) — the §4.3 + result-disclosure boundary. Third, the **seed-progress query** + (§4.3) — an addition to contract 5 §4's command-envelope + surface, the owning contract of the item 4 fence table whose + recording existence it reads: + one mapped read surface returning only the epoch's + next unrecorded canonical position index or a completion + marker, authorized for exactly the current designation, the + screening evaluated before any fence state with every + non-designated submitter refused with the constant-shape + conflict byte-shape-identically across recorded and unrecorded + worlds; its closed read set (the designation plus + position-committed existence flags, §6.1) covers no recorded + outcome, no grant state, and no content, and it writes + nothing. Together these make origin loss recoverable with no + escalation surface and no oracle: without the succession + command a banned seed-origin account strands the unoriginated + seed suffix; without the generalized authority the suffix's + content positions refuse for a grant-less designation; and + without the query no fresh designated client can locate the + suffix at all — each contradicting PRD D4's no-lock-in requirement (§4.4). ## Ruling request -- 2.54.0 From f97d7220e315227e7d3f7444dbbc680c5f46e38a Mon Sep 17 00:00:00 2001 From: fred Date: Thu, 27 Aug 2026 04:44:57 -0500 Subject: [PATCH 14/15] =?UTF-8?q?contract(onboarding-wizard):=20revision?= =?UTF-8?q?=2014=20=E2=80=94=20query/originate=20loop,=20RBAC=20=C2=A71=20?= =?UTF-8?q?exception=20ratified,=20contract=205=20envelope=20reconciled,?= =?UTF-8?q?=20one=20refusal=20order?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses sol re-review 13 (NEW-18/NEW-19 residuals, NEW-20, NEW-21): - NEW-18: seed-progress query response is a closed discriminated union (next index | typed complete variant); normative query/originate loop — stop on complete, re-query on canonical refusal, continue only on a strictly-later result, surface a fault on an unchanged index. New witnesses: same-designation race (with unchanged-index fault variant) and completed-world stop run of the actual fresh client; cross-surface refusal-shape control added to the two-world query witness. - NEW-19: RBAC §1 expressly named among amended surfaces — narrow ratified exception making the designation a fourth authority source inside the mechanical origination scope only (§1.2, §4.3, §7 item 12). New active-window boundary witness: canonical tuple succeeds while non-canonical and non-seed-workspace commands refuse in the SAME incomplete state; predecessor- and successor-created content reads refused post-origination. - NEW-20: state-derived disclosure split from mandatory envelope metadata everywhere — origination and query responses remain ordinary contract 5 §4 result DTOs carrying the correlation envelope; contract 5 not amended. §6.1 closed-schema assertion covers both halves. - NEW-21: result-disclosure paragraph restates the operative order — fresh-mutation authorization first (owning family's refusal), then seed-boundary gate before fence presence and canonical-reference resolution, constant shape scoped to callers that reached the gate; child-race control asserts the refusal class, pinning the order. --- docs/requirements/onboarding-wizard.md | 277 ++++++++++++++++++++----- 1 file changed, 230 insertions(+), 47 deletions(-) diff --git a/docs/requirements/onboarding-wizard.md b/docs/requirements/onboarding-wizard.md index d50a3ab9..fcb67804 100644 --- a/docs/requirements/onboarding-wizard.md +++ b/docs/requirements/onboarding-wizard.md @@ -325,7 +325,53 @@ by the owning contract. Witnesses §6.7: seed-progress two-world refusal, seed-progress entitlement with the actual fresh client run against both worlds, content-position completion in both recovery variants, non-canonical content refusal, and the -result-disclosure witness. +result-disclosure witness. (Revision 14 completes this revision's +query with a full client protocol and typed completion, ratifies +the RBAC §1 exception this revision left implicit, and reconciles +the closed result schemas with contract 5's envelope.) + +Revision 14 (sol re-review 13: NEW-18/NEW-19 residuals, NEW-20, +NEW-21): the query's client protocol, the authority's ratified +boundary, the result envelope, and the refusal order are made +exact. **Query/originate loop** (NEW-18): the walk's complete +protocol is now normative in §4.3 — the query's state-derived +content is a closed discriminated union (`next` index or a typed +`complete` variant that is a result value, never an error); the +client stops on `complete`; on a refused canonical origination it +draws nothing from the refusal and re-queries, continuing only on +a strictly-later result (the benign stale-query race, where a +same-designation peer committed the position first) and stopping +to surface a genuine fault when the index is unchanged — so +progress derives from the entitled query alone under any number +of same-designation clients. Witnesses: same-designation race +(with its unchanged-index fault variant) and the completed-world +stop run of the actual fresh client. **RBAC §1 exception +ratified** (NEW-19): RBAC §1 declares its three authority layers +exhaustive, so §4.3 and §7 item 12 now name §1 itself among the +expressly amended surfaces — a narrow ratified exception making +the designation a fourth, disclosed authority source inside +exactly the mechanical origination scope and substitutable +nowhere else. The active-window boundary witness discriminates +canonical-key scope from completion-state scope: while a content +position is still next, the exact canonical tuple succeeds while +a second non-canonical item and a non-seed-workspace command +refuse in the SAME state, and post-origination reads of +predecessor- and successor-created seed content stay refused. +**Envelope reconciliation** (NEW-20): every closed response — +origination outcome and query alike — bounds its STATE-DERIVED +content while remaining an ordinary contract 5 §4 result DTO +carrying the mandated non-state correlation envelope, which +echoes the request and discloses no state; contract 5 is not +amended by the disclosure boundary. **One refusal order** +(NEW-21): the result-disclosure paragraph now restates the +operative §4.3 order instead of contradicting it — fresh-mutation +authorization first (its refusal is the owning family's), then +the seed-boundary gate before fence presence and +canonical-reference resolution, the constant shape scoped to +callers that reached the gate — and the child-race control +asserts the refusal class, pinning the order mechanically, with +the cross-surface shape control asserting one constant conflict +shape across the gate, succession, and query surfaces. Scope: the Gateway-backed product onboarding wizard. Out of scope: the host-local install wizard (`mosaic wizard`, which drives host install and @@ -390,11 +436,16 @@ through the extensibility rule §2.4). component of each canonical position's owning family (contract 2 §4's hierarchy authority; RBAC §§2–3's workspace-content authorization; the native-kanban SOT's workspace-scoped command - authorization, REQ-TEN-001 / Amendment A1 §8.1.3), conferring no + authorization, REQ-TEN-001 / Amendment A1 §8.1.3; and, because + RBAC §1 declares its three authority layers exhaustive, a + narrow ratified exception to RBAC §1 itself, scoped to exactly + that origination and substitutable nowhere else), conferring no grant and no read authority — and §7 item 12's **seed-progress - query** (§4.3), one designation-only mapped read surface - disclosing exactly the epoch's next unrecorded canonical seed - position index (or a completion marker) and nothing else; no + query** (§4.3), one designation-only mapped read surface whose + entire state-derived content is the closed §4.3 discriminated + union — the epoch's next unrecorded canonical seed position + index, or a typed completion variant — inside the ordinary + contract 5 §4 result envelope, and nothing else; no undisclosed authority exists; all of these surfaces appear in the §1.1 composed-family inventory, the §7.8 mapping amendment, and §6.1's inventories, so the D8 mapping and authorization-parity @@ -799,7 +850,16 @@ collects no sensitive category, so v1 ships no custody surface. amendments to each named surface (§7 item 12), severable together, that confer no read authority, no replay authority, no standing grant, no authority over any committed record, - and nothing outside the canonical seed key set. The + and nothing outside the canonical seed key set. Because RBAC + §1 states its three authority layers exhaustively ("none + substitutable"), this authority is also, expressly, a narrow + ratified exception to RBAC §1's exhaustive-layer rule (§7 + item 12): within exactly the mechanical scope below — the + current designation, the next unrecorded canonical position, + the exact server-derived tuple, a fresh in-order + origination — the designation is a fourth, disclosed + authority source; outside that scope it substitutes for no + layer, anywhere, ever. The authority's scope is mechanically decidable per submission: the submitter is the epoch's current designation, the (operation, key) is the next unrecorded canonical position, @@ -812,15 +872,30 @@ collects no sensitive category, so v1 ships no custody surface. writes nothing beyond the command's own canonical writes; a refused submission records no row (no-error replay below). **Result-disclosure boundary.** A successful origination's - response and recorded outcome contain exactly the command's - canonical outcome fields — the created row id, any + response and recorded outcome carry, as their entire + STATE-DERIVED content, exactly the command's canonical + outcome fields — the created row id, any canonical-reference-resolved parent id, the canonical key, - and the payload digest — and nothing else: no listing, no - attribute of any pre-existing record beyond the resolved - parent id the outcome must record, and no grant- or - fence-table content. Refusals on canonical seed keys are - screened by the seed-boundary gate first and keep its - constant shape. The recorded actor of every shared row is + and the payload digest — and nothing else state-derived: no + listing, no attribute of any pre-existing record beyond the + resolved parent id the outcome must record, and no grant- or + fence-table content. The response remains an ordinary + contract 5 §4 result DTO: it additionally carries exactly + the non-state envelope metadata contract 5 §4.1/§4.3 mandate + for every mapped result — the request's own correlation ids + in the fixed envelope frame — which echoes the caller's + request, derives from no stored record, and discloses + nothing about any world's state; the boundary bounds + disclosure and amends nothing in contract 5. Refusals on + canonical seed keys follow the evaluation order this section + fixes and no other: fresh-mutation authorization is + evaluated first — a submitter it refuses receives the owning + command family's ordinary authorization refusal — and the + seed-boundary gate is evaluated next, before fence presence + and before canonical-reference resolution; the gate's + constant-shape conflict is the refusal of exactly the + callers that passed fresh-mutation authorization and failed + the gate (witness §6.7). The recorded actor of every shared row is therefore the account that was the epoch's seed-origin at that position's origination, authorized for the mutation the row fences. Recorded positions are replayable by any admin holding @@ -910,10 +985,18 @@ collects no sensitive category, so v1 ships no custody surface. an interrupted seed sequence. - **Seed-progress query.** Locating the unoriginated suffix is a disclosed, mapped read surface, never an inference from - refusal shapes: the **seed-progress query** returns exactly - one value — the index of the epoch's next unrecorded - canonical seed position, or a completion marker when every - position is recorded — and nothing else. It is authorized for + refusal shapes: the **seed-progress query** returns, as its + entire state-derived content, exactly one value from a + closed discriminated union of two result variants — `next`, + carrying the index of the epoch's next unrecorded canonical + seed position, or `complete`, carrying no index, when every + position is recorded — and nothing else state-derived. The + `complete` variant is a typed result value on the success + path, never an error shape and never a sentinel index; the + response is an ordinary contract 5 §4 result DTO whose only + other content is the mandated non-state envelope metadata + (the request's own correlation ids), which echoes the + request and discloses no state. It is authorized for exactly the account the epoch's designation currently names, and that screening is evaluated first, before any fence state is consulted: every other submitter — a former designation, a @@ -933,13 +1016,32 @@ collects no sensitive category, so v1 ships no custody surface. state, and its closed read set is statically asserted (§6.1). The query writes nothing and appends no event. A resuming designated client — the original origin on a fresh client and - a §4.3 successor alike — first submits the query and then - originates in order from the returned index, deriving each - tuple from canonical settings state alone; an admin holding - target-result read authority may instead walk the sequence - and receive recorded outcomes as replays (§6.3). No client is - required to infer progress from refusals, and no - non-designated actor can. + a §4.3 successor alike — runs the **query/originate loop**, + the walk's complete protocol: submit the query; on + `complete`, STOP — the walk is finished and the client + submits no origination and no other seed-key command; on + `next` index i, originate position i, deriving its tuple + from canonical settings state alone, and on success continue + the loop from a fresh query or the next index. If an + origination on a canonical seed key is refused, the client + draws NO conclusion from the refusal itself — refusal shapes + are not progress signals — and re-submits the entitled + query: a result strictly beyond i (a greater index or + `complete`) means another client of the same designation + committed position i between query and origination — the + **stale-query race**, benign by construction — and the loop + continues from the fresh result; a result that still names i + means the refusal was a genuine fault, and the client stops + and surfaces the refusal without submitting further. The + loop's only progress source is the query, so the walk is + race-safe under any number of same-designation clients: each + position commits exactly once (Concurrency below), and every + client either advances on its own successes or + re-synchronizes through the query it is entitled to. An + admin holding target-result read authority may instead walk + the sequence and receive recorded outcomes as replays + (§6.3). No client is required to infer progress from + refusals, and no non-designated actor can. - **No error replay.** The fence row commits only with its mutation, so only committed outcomes are ever recorded. A failed or refused submission records no fence row; a retry executes @@ -996,9 +1098,11 @@ collects no sensitive category, so v1 ships no custody surface. individually atomic commands, not one transaction: an interruption between them leaves a prefix of committed seed nodes, and the §4.3 fence makes the resumed run complete exactly the remaining suffix — - the resumed designated client locates that suffix through the - §4.3 seed-progress query and originates in order from the - returned index, re-deriving every tuple from canonical state; a + the resumed designated client runs the §4.3 query/originate + loop — querying, originating the returned index, stopping on + the typed completion variant, and re-querying (never + interpreting the refusal) if a canonical origination is + refused — re-deriving every tuple from canonical state; a target-authorized admin's re-run may instead re-submit the full ordered seed sequence with the same deterministic keys, already-committed mutations returning their recorded outcomes — @@ -1008,8 +1112,8 @@ collects no sensitive category, so v1 ships no custody surface. account mid-sequence — identity §7.1 unavailability — is likewise recoverable without a new epoch: §4.3 seed-origin succession designates an eligible successor, and the resumed run locates - the remaining suffix through the §4.3 seed-progress query and - completes it under the successor's designation-derived + the remaining suffix through the same §4.3 query/originate + loop and completes it under the successor's designation-derived authority — the ordinary workspace-content example positions included (§4.3, §7 item 12) — deriving every tuple from canonical settings state alone: no grant is conferred and no @@ -1102,8 +1206,13 @@ Binding on the implementing PRs: seed keys — no recorded outcome, no payload or digest, no resolved id, no acting principal, no grant table, no hierarchy or content record; its write set is asserted empty (no event, - no row), and its response schema is closed to the single - index-or-completion value. Wizard modules appear in no + no row), and its response DTO is asserted closed in both + halves: its state-derived content is exactly the §4.3 + discriminated next-index-or-complete union, its only other + content is contract 5 §4's non-state envelope metadata (the + request's correlation ids), and no further field exists — the + closed-schema assertion covers the full wire shape, both + variants included. Wizard modules appear in no class-table writer allowlist (contract 1 §6.3b) and hold no direct database or filesystem access (static assertion, plus a runtime probe that a wizard-context filesystem/database access attempt is @@ -1291,7 +1400,11 @@ Binding on the implementing PRs: tuple deliberately left unrecorded, an actor lacking that seed command's hierarchy authority races the seed-origin account's resume for the same (operation, key): the unauthorized - submission receives the authorization refusal and records no + submission receives the owning command family's ordinary + authorization refusal — asserted NOT the seed-boundary gate's + constant-shape conflict, pinning the §4.3 evaluation order in + which fresh-mutation authorization refuses before the gate is + reached — and records no fence row, the origin account's submission executes afresh, and the resulting fence row records the seed-origin account; (top-level variant, NEW-9) with the seed-company tuple @@ -1378,7 +1491,12 @@ Binding on the implementing PRs: succession, and an admin holding target-result read authority each probed — submits the §4.3 seed-progress query in both worlds and is refused byte-shape-identically in both, with no - event appended in either — proving the query surface discloses + event appended in either, and the refusal bytes are + additionally asserted byte-shape-identical to the seed-boundary + gate's conflict as returned to a gate-refused mutating + submitter — the cross-surface control proving one constant + shape spans the gate, succession, and query surfaces — and the + query surface therefore discloses nothing to anyone but the current designation; a **seed-progress entitlement witness (NEW-18)** — the SAME designated account submits the query in worlds U and P: it @@ -1392,6 +1510,30 @@ Binding on the implementing PRs: any committed position and without receiving a single refusal in the walk — proving progress location is a disclosed entitled read, never an inference from refusal shapes; a + **same-designation race witness (NEW-18)** — two fresh clients + C1 and C2 of the same grant-less designation both submit the + query and both receive the same `next` index i; C1 originates + position i and commits; C2 then submits the identical tuple + and is refused; C2 is asserted to follow the §4.3 + query/originate loop exactly — it does not retry, does not + advance to i+1 on its own, and interprets nothing from the + refusal — and re-submits the query, receiving a result + strictly beyond i (a greater `next` or `complete`), from which + it continues the loop to completion; per canonical key exactly + one mutation and one fence row exist; a fault variant of the + same witness refuses C2's origination while position i is + STILL unrecorded and asserts the re-query returns the SAME + index i, whereupon the client stops and surfaces the refusal + rather than looping — proving the loop distinguishes the + benign stale-query race from a genuine fault through the + entitled query alone; a **completed-world stop witness + (NEW-18)** — the actual fresh designated client is run against + a world whose canonical sequence is fully recorded: the query + returns the typed `complete` variant (asserted against the + closed DTO — a result value, not an error and not an index), + and the client stops, submitting zero originations and zero + other seed-key commands — proving the terminal branch is + defined, typed, and honored; a **designation-derived completion (NEW-12/NEW-13/NEW-19)** — origin A originates a prefix extending past workspace creation — so the remaining suffix consists of ordinary @@ -1414,8 +1556,29 @@ Binding on the implementing PRs: authority plus its own eligibility; a subsequent read attempt by that successor against a committed pre-succession seed record is refused — proving succession recovers completion without - conferring read or tenant authority; a **non-canonical content - refusal (NEW-19)** — a designation holding no grant (the + conferring read or tenant authority; an **active-window + boundary witness (NEW-19)** — in a world whose next unrecorded + canonical position IS an ordinary workspace-content example + position, a designation holding no grant submits, in that SAME + state: (a) that position's exact canonical tuple, which + succeeds under the designation-derived authority; (b) a + non-canonical content command against the seed workspace — a + second kanban item — which is refused by the owning content + contract's authorization with nothing written; and (c) a + content command against a workspace outside the seed set, + refused likewise; this witness is discriminating where the + post-completion refusal below is not — an incorrect evaluator + scoped by completion state rather than by canonical key (one + granting the designation all seed-workspace writes while + seeding is incomplete) passes the post-completion test but + fails branches (b) and (c) here, because they refuse while + seeding is still incomplete; after branch (a) commits, read + attempts by the acting designation and by a later successor + against BOTH predecessor-created and successor-created seed + content are refused — the bounded origination response is the + only disclosure either ever receives; a **non-canonical content + refusal (NEW-19)** — the post-completion variant of the same + boundary: a designation holding no grant (the self-revocation world, post-completion) submits, against the seed workspace, an ordinary content command whose (operation, key) is outside the canonical seed key set — a second, @@ -1423,13 +1586,18 @@ Binding on the implementing PRs: content contract's authorization with nothing written — proving the designation-derived authority stops at the canonical set and confers no general workspace authority; a - **result-disclosure witness (NEW-19)** — a successor's + **result-disclosure witness (NEW-19/NEW-20)** — a successor's content-position origination response and recorded outcome are - asserted to contain exactly the canonical outcome fields + asserted to carry, as their entire state-derived content, + exactly the canonical outcome fields (created row id, resolved parent id, canonical key, payload digest) and no attribute of any pre-existing record beyond the - resolved parent id — proving origination under the - designation-derived authority discloses nothing further; a + resolved parent id, while the response's envelope carries the + request's own correlation ids per contract 5 §4.3 — asserted + present, asserted equal to the request's, and derived from no + stored record — proving origination under the + designation-derived authority discloses nothing further and + still conforms to contract 5's result envelope; a **post-completion harmlessness control (NEW-16)** — with the seed sequence fully complete, the origin is banned and an eligible platform admin B @@ -1661,9 +1829,17 @@ contracts and are not additions: hierarchy-authority evaluation (the company, estate, project, and workspace positions); RBAC §§2–3's workspace-content authorization evaluation (the ordinary example-content - positions); and the native-kanban SOT's workspace-scoped + positions); the native-kanban SOT's workspace-scoped command authorization (REQ-TEN-001, Amendment A1 §8.1.3) for - the kanban example positions. For exactly the fresh + the kanban example positions; and — because RBAC §1 declares + its three authority layers (platform role, hierarchy grants, + workspace membership) exhaustive and "none substitutable" — + RBAC §1 itself, amended with a narrow ratified exception: for + exactly the mechanical scope below, the epoch's current + designation is a fourth, disclosed authority source, and + outside that scope §1's exhaustive rule stands unchanged, the + designation substituting for no layer on any other surface, + key, or command. For exactly the fresh origination of a currently-unoriginated canonical seed position, the epoch record's current designation itself satisfies that one component; the scope is mechanically @@ -1677,16 +1853,23 @@ contracts and are not additions: record, and nothing outside the canonical seed key set; it is evaluated per submission and writes nothing beyond the command's own canonical writes; and a successful origination's - response and recorded outcome are bounded to exactly the + response and recorded outcome are bounded, in their + state-derived content, to exactly the command's canonical outcome fields (created row id, resolved parent id, canonical key, payload digest) — the §4.3 - result-disclosure boundary. Third, the **seed-progress query** + result-disclosure boundary, whose responses remain ordinary + contract 5 §4 result DTOs carrying the mandated non-state + correlation envelope, amending nothing in contract 5. Third, + the **seed-progress query** (§4.3) — an addition to contract 5 §4's command-envelope surface, the owning contract of the item 4 fence table whose recording existence it reads: - one mapped read surface returning only the epoch's - next unrecorded canonical position index or a completion - marker, authorized for exactly the current designation, the + one mapped read surface whose result DTO conforms to contract + 5 §4.1/§4.3 (explicit DTO, correlation echoed) and whose + state-derived content is only the closed §4.3 discriminated + union — the epoch's next unrecorded canonical position index + (`next`) or the typed completion variant (`complete`) — + authorized for exactly the current designation, the screening evaluated before any fence state with every non-designated submitter refused with the constant-shape conflict byte-shape-identically across recorded and unrecorded -- 2.54.0 From 1e069946ffdf502931de064d050ed469baa311d5 Mon Sep 17 00:00:00 2001 From: fred Date: Thu, 27 Aug 2026 05:19:30 -0500 Subject: [PATCH 15/15] =?UTF-8?q?contract(onboarding-wizard):=20revision?= =?UTF-8?q?=2015=20=E2=80=94=20bootstrap-status=20envelope=20split=20(NEW-?= =?UTF-8?q?20=20residual),=20active-window=20mutant=20attribution=20(NEW-2?= =?UTF-8?q?2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sol r14 re-review left two findings. NEW-20 residual (BLOCKER): §2.3 closed the bootstrap-status response to epoch/mode with no other field, conflicting with contract 5 §4.3's mandatory correlation echo on mapped operations (§§1.1, 7 item 8). Rev 14's state-derived/envelope split is now applied to bootstrap-status: §2.3 bounds state-derived content only and carries the contract 5 §4 envelope; §6 item 2's closed-field assertion and §7 item 2's description follow. Contract 5 not amended. NEW-22 (MAJOR): the active-window witness claimed its seed-workspace- scoped mutant fails branches (b) and (c); it passes (c). The witness now attributes (b) to that mutant, names the separate all-workspace mutant branch (c) catches, and states branches (a)/(b)/(c) start from isolated copies of the same incomplete pre-state. Preamble: Revision 15 paragraph; Revision 14 superseding note. --- docs/requirements/onboarding-wizard.md | 75 +++++++++++++++++++++----- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/docs/requirements/onboarding-wizard.md b/docs/requirements/onboarding-wizard.md index fcb67804..67153aa2 100644 --- a/docs/requirements/onboarding-wizard.md +++ b/docs/requirements/onboarding-wizard.md @@ -371,7 +371,31 @@ canonical-reference resolution, the constant shape scoped to callers that reached the gate — and the child-race control asserts the refusal class, pinning the order mechanically, with the cross-surface shape control asserting one constant conflict -shape across the gate, succession, and query surfaces. +shape across the gate, succession, and query surfaces. (Revision 15 +extends this revision's envelope reconciliation to the +bootstrap-status response and corrects the active-window witness's +mutant attribution.) + +Revision 15 (sol re-review 14: NEW-20 residual, NEW-22): the +envelope reconciliation reaches the last closed response, and the +active-window witness names its mutants correctly. +**Bootstrap-status envelope** (NEW-20 residual): §2.3's closed +two-field response now bounds its STATE-DERIVED content only — +pre-epoch `epoch` plus `mode`, post-epoch `epoch` alone, no other +state-derived field — while the response remains an ordinary +contract 5 §4 result DTO whose fixed envelope frame carries the +request's own correlation ids, echoing the caller's request and +deriving from no stored record; §6 item 2's closed-field assertion +and §7 item 2's disclosure description follow. Contract 5 is not +amended. **Mutant attribution** (NEW-22): the active-window witness +now states that branches (a), (b), and (c) each start from an +isolated copy of the same incomplete pre-state (branch (a) commits +a state change), attributes branch (b)'s detection to the +seed-workspace-scoped incorrect evaluator, and names the separate +all-workspace incorrect evaluator that branch (c) catches — the +seed-workspace-scoped mutant, correctly refusing outside the seed +set, passes branch (c), so the two branches detect distinct +mutants. No other change. Scope: the Gateway-backed product onboarding wizard. Out of scope: the host-local install wizard (`mosaic wizard`, which drives host install and @@ -486,16 +510,23 @@ through the extensibility rule §2.4). unreachable there; the pre-epoch flow instead branches on the mode value reported by the **unauthenticated bootstrap-status response** of the bootstrap surface (identity §3), which evaluates the mode - record server-side. The bootstrap-status response is a **closed - two-field schema**: `epoch` — one field whose value set is exactly + record server-side. The bootstrap-status response's **state-derived + content is a closed two-field schema**: `epoch` — one field whose + value set is exactly the bootstrap epoch states identity §3 defines for `bootstrap_state`, closed to the exact wire-token pair **`open`** and **`completed`** — identity §3 names the two epoch states in prose but defines no wire enumeration, so this contract pins the tokens, as the second clause of the disclosed §7.9 amendment — and `mode` — the recorded mode value, present **only while the epoch - is open** and absent from the response schema once the epoch has - completed. No other field exists in the response. The unauthenticated pre-epoch + is open** and absent from the state-derived content once the epoch + has completed. No other state-derived field exists in the + response, and the response remains an ordinary contract 5 §4 + result DTO: its fixed envelope frame carries the request's own + correlation ids — echoing the caller's request, deriving from no + stored record, and disclosing no state — so the closed schema + bounds disclosure of state and amends nothing in + contract 5. The unauthenticated pre-epoch mode disclosure is a disclosed amendment to contract 6 §2.2's authenticated-read rule (§7.9); post-epoch, §2.2's authenticated-only rule holds unchanged and the bootstrap-status @@ -1223,12 +1254,17 @@ Binding on the implementing PRs: 2. **Mode witnesses:** the branch point exists — a pre-epoch flow resolves its step set from the bootstrap-status mode field (§2.3), a post-epoch run from contract 6 §2.2's authenticated read; the - bootstrap-status response matches §2.3's closed two-field schema + bootstrap-status response's state-derived content matches §2.3's + closed two-field schema exactly while the epoch is open — the epoch field carrying one of §2.3's two exact wire tokens — and after the epoch completes - contains the epoch field only — the mode field absent from the - response (closed-field assertion on the response schema in both - phases); post-epoch, an unauthenticated mode read through any + contains the epoch field only — the mode field absent + (closed-field assertion on the state-derived content in both + phases, with the response's contract 5 §4 envelope asserted to + carry the request's own correlation ids — present, equal to the + request's, derived from no stored record — and no further + state-derived field in either + phase); post-epoch, an unauthenticated mode read through any surface is refused (contract 6 §2.2, as amended by §7.9, holds); v1 with mode `standalone` yields the §3 step set; a simulated unsupported or unreadable mode record yields one bounded precondition refusal (§2.3) and no partial flow; wizard sources @@ -1566,13 +1602,22 @@ Binding on the implementing PRs: second kanban item — which is refused by the owning content contract's authorization with nothing written; and (c) a content command against a workspace outside the seed set, - refused likewise; this witness is discriminating where the + refused likewise; branches (a), (b), and (c) each start from + an isolated copy of the same incomplete pre-state — branch (a) + commits a state change, so no branch runs in another's + post-state; this witness is discriminating where the post-completion refusal below is not — an incorrect evaluator scoped by completion state rather than by canonical key (one granting the designation all seed-workspace writes while seeding is incomplete) passes the post-completion test but - fails branches (b) and (c) here, because they refuse while - seeding is still incomplete; after branch (a) commits, read + fails branch (b), which refuses while seeding is still + incomplete, and a second, broader incorrect evaluator (one + granting the designation writes in EVERY workspace while + seeding is incomplete) passes the post-completion test but + fails branch (c) — the control against that all-workspace + bypass, which the seed-workspace-scoped evaluator, correctly + refusing outside the seed set, would pass; after branch (a) + commits, read attempts by the acting designation and by a later successor against BOTH predecessor-created and successor-created seed content are refused — the bounded origination response is the @@ -1689,8 +1734,10 @@ contracts and are not additions: 1. Enrollment-step skippability (§3.5) — the PRD step list does not mark the step optional. 2. The unauthenticated bootstrap-status mode disclosure — the closed - two-field response schema (epoch state plus the recorded mode - value, the latter present during the bootstrap epoch only) (§2.3). + two-field state-derived response content (epoch state plus the + recorded mode value, the latter present during the bootstrap + epoch only), carried in an ordinary contract 5 §4 result DTO + whose envelope echoes the request's correlation ids (§2.3). 3. The **bootstrap finalize command** (§3.3) — an extension of identity §3's bootstrap surface: one transaction carrying the first-admin fields, the collected settings, registration-mode, and -- 2.54.0