docs: onboarding-wizard contract revision 7 (sol r6 F6 residual + NEW-8)
ci/woodpecker/pr/ci Pipeline was successful

This commit is contained in:
fred
2026-08-26 21:43:57 -05:00
parent b8b257e1ec
commit 84fe8b6ef1
+93 -15
View File
@@ -104,6 +104,24 @@ under two different operations executes both independently
authorization refusal records no fence row so an authorized retry authorization refusal records no fence row so an authorized retry
executes afresh (F7). 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 Scope: the Gateway-backed product onboarding wizard. Out of scope: the
host-local install wizard (`mosaic wizard`, which drives host install and host-local install wizard (`mosaic wizard`, which drives host install and
gateway bootstrap and is not this artifact — audit REPORT.md layer 3); 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 byte-stable across every re-run and resume: the same keys carry
the same payload digests, so already-committed mutations replay the same payload digests, so already-committed mutations replay
(recorded outcomes) rather than collide, regardless of any (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's presence or absence, is what prevents re-seeding; the
parameter is provenance the derivation reads, never a value any parameter is provenance the derivation reads, never a value any
later step may change. 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 as the mutation and its audit event: the key, the operation
identifier, the acting principal, the mutation's authorization identifier, the acting principal, the mutation's authorization
scope (the target company, or the platform scope for top-level 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 a reference to the committed outcome. Fence uniqueness is the
pair (operation identifier, key). pair (operation identifier, key).
- **Replay.** A submission whose (operation, key) pair is recorded - **Replay.** A submission whose (operation, key) pair is recorded
is first authorized exactly as a fresh submission would be — an is first authorized exactly as a fresh submission would be — an
actor who is not authorized receives the authorization refusal, actor who is not authorized receives the authorization refusal,
never the recorded outcome. If authorization passes and the never the recorded outcome. If authorization passes, the
recorded actor, scope, and payload digest all equal the submission's declared replay mode equals the recorded row's, and
submission's, the command executes nothing and returns the the recorded scope and payload digest equal the submission's,
recorded outcome. 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 - **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); refused with a single bounded conflict error (contract 5 §4.2);
it executes nothing and discloses nothing of the recorded it executes nothing and discloses nothing of the recorded
outcome. outcome.
@@ -405,7 +440,15 @@ collects no sensitive category, so v1 ships no custody surface.
The wizard submits deterministic keys derived from the bootstrap The wizard submits deterministic keys derived from the bootstrap
epoch and the seed role (e.g. epoch id + "seed-company") for every 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 linkage and carry no idempotency semantics. Wizard bookkeeping of
seed node ids remains a cache seed node ids remains a cache
(§1.3): losing it cannot cause duplication, because the fence is (§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 rename through the hierarchy command), then re-run — the wizard
renders the current canonical value, not a stored answer. renders the current canonical value, not a stored answer.
**Seed-provenance witnesses (§3.1, §3.4):** rename the seed **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 the re-derived seed sequence carries the same keys and payload
digests as the first run (asserted digest equality), every seed digests as the first run (asserted digest equality), every seed
submission replays with the recorded outcome, zero new nodes are 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 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 settings write to `bootstrap.seed-company-name`, by any actor, is
refused; and a control run that force-mutates a seed payload refused; and a control run that force-mutates a seed payload
digest receives the §4.3 collision refusal, proving the fence digest receives the §4.3 collision refusal, proving the fence
still discriminates. 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 **Second company:** a re-run creating a second company succeeds with
the running user as actor and names the creator as `owner` in the the running user as actor and names the creator as `owner` in the
same audited operation — asserted on the single audit event naming 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 mutation-count and audit-event-count equality); the same key with a
changed payload digest is refused with the single bounded conflict changed payload digest is refused with the single bounded conflict
and executes nothing; the same key and payload submitted by a and executes nothing; the same key and payload submitted by a
different authorized actor is refused with the same single bounded different authorized actor against an **actor-bound** fence row (a
conflict (actor mismatch) and executes nothing; the same key, 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 actor, and payload submitted against a different authorization
scope is likewise refused (scope mismatch) and executes nothing; a scope is likewise refused (scope mismatch) and executes nothing; a
replay by an actor who has since lost replay by an actor who has since lost
eligibility (identity §7.1 ban) receives the authorization refusal, 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 leaves no fence row and its retry executes; two concurrent resumed
runs executing the seed sequence yield exactly one seed set — per runs executing the seed sequence yield exactly one seed set — per
key, exactly one mutation and one audit event exist, and the losing 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 fence, with the §4.3 replay, collision, no-error-replay, and
concurrency rules — an addition to contract 5 §4's command concurrency rules — an addition to contract 5 §4's command
envelope, proposed and ratified here, severable from the rest of 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 5. The presentation-and-submission obligation for applicable mandatory
choices on wizard runs (§3.3). 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),