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