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).