Design: the replicator's authority, revision 8 (the seventh review's fixes: a cancellation is conclusive only after the admission writer has returned and a fresh read finds no row; one pending-set contract; the restore wording limited to what the fork's verification chooses)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 file changed,
+4
−4
docs/superpowers/specs/2026-10-03-replicator-pinned-roots-design.md+4 −4
CI passed — run details
diff --git a/docs/superpowers/specs/2026-10-03-replicator-pinned-roots-design.md b/docs/superpowers/specs/2026-10-03-replicator-pinned-roots-design.md| index b1a004b83..fbdf5f855 100644 |
| --- a/docs/superpowers/specs/2026-10-03-replicator-pinned-roots-design.md |
| +++ b/docs/superpowers/specs/2026-10-03-replicator-pinned-roots-design.md |
| @@ -1,6 +1,6 @@ |
| # The replicator's authority: retained descriptors, a durable admission, claims by identity |
| -Date: 2026-10-03. Status: design, revision 7, for review (revision 6 was rejected by Astra on one P1: a discovery whose write failed had no rule for the admissions that settled before its retry, so the replayed interval could lose a settlement; and three P2s: a helper dying before its recorder started left an admission nobody could finish; the transition table lacked the abort edges and the pending set's cleanup; the lineage record cited a durability discipline the shadow state does not have; each answered here. Revision 5 was rejected by Astra on two P1s: the admission's insertion had no ordering point against discovery, so a discovery between the proof and the commit counted nothing and a later send fell outside the closed uncertainty; construction did not backfill already-closed intervals for a successor's new owners before acknowledgement; and P2s: the transition table and the helper-death rule disagreed and the recorder participant was synthesised while its worker could still run; proof-only callers of admit would have created admissions with no terminal; a terminal `.` lost the directory requirement; conflicting summaries; each answered here. Revision 4 was rejected by Astra on four P1s: the helper has several connections of its own and needs SQLite's within-process lock bookkeeping between them; the producer fence did not cover a helper that outlives the agent with an admission in hand; an intermediate fleet closed before acknowledging broke transitive protection; a fleet could be marked closed with a discovery uncommitted; and four P2s: a dead helper's admissions had no terminal in the running agent, the sleeping binding persisted a reusable inode, the lineage predicate named header fields that identify nothing, the walk's grammar refused a `.` target the predicate accepts; each answered here. Revision 3 was rejected by Astra on five P1s: two SQLite VFSs in one process cannot coordinate through POSIX locks, since the library's own per-inode bookkeeping is in-process; a recorder that abandons early could settle an admission before its transport ended; an admission-only barrier does not cover a discovery whose uncertainty write failed after every admission settled; links at acknowledgement contradicted acknowledging only after settlement, and an inherited uncertainty vanished at the next successor; the ledger's lock does not prove the old process gone; and four P2s: closure at zero pending and the margin's extra day, sleeping claims with no binding, absolute and `.` spellings in the walk, `(dev, ino)` as a lineage; each answered here. Revision 2 was rejected by Astra on three P1s: the VFS delegated SQLite's Unix file methods, which open the SHM and check moved files by pathname themselves; every settled admission wrote a permanent hold, so ordinary replication never read complete; a late owner-scoped extension did not reach successors that had already acknowledged; and six P2s: orphan admissions never terminated, `..` and chain edges unspecified, `dup` sharing the directory offset, the platform-link predicate not preserved and terminal links unclassified, the uid producer without a lifecycle, one billing owner not one replicator; each answered here. Revision 1 was rejected by Astra on five P1s: SQLite canonicalises a `/proc` pathname before it opens anything and this driver passes no `nofollow`; `os.Root` confines but re-resolves every name, so a swap between a listing and an inspection still moves the answer; a `Start` row is not an admission, since the meter sends without one; the attempt-derived window lost a request completed before the change was noticed and the days between an admission and a finish; `closed_at` was not a handover; plus the directory open before `O_DIRECTORY`, per-owner holds the ledger cannot store, a uid with no producer, I3 against Part D, and tests). Branch `design-replicator-pinned-root`, off `feat/carloku-metered-replicator-20261001` at `e1280811`. It answers the eleven findings the twentieth review deferred (`DISPOSITIONS-metered-replicator.md`, review twenty) and keeps decisions 10 to 13 and approval H (`DECISIONS-FOR-PAUL.md`, section D, on the launch branch) as parameters. Nothing is built, wired or applied. |
| +Date: 2026-10-03. Status: design, revision 8; revision 7 was accepted with fixes by Astra (no P1; one P2: a negative read-back was not conclusive while the admission writer could still commit, and the pending set had three definitions; one P3: the restore wording made a salt mismatch imply a snapshot), both applied here. Revision 6 was rejected by Astra on one P1: a discovery whose write failed had no rule for the admissions that settled before its retry, so the replayed interval could lose a settlement; and three P2s: a helper dying before its recorder started left an admission nobody could finish; the transition table lacked the abort edges and the pending set's cleanup; the lineage record cited a durability discipline the shadow state does not have; each answered here. Revision 5 was rejected by Astra on two P1s: the admission's insertion had no ordering point against discovery, so a discovery between the proof and the commit counted nothing and a later send fell outside the closed uncertainty; construction did not backfill already-closed intervals for a successor's new owners before acknowledgement; and P2s: the transition table and the helper-death rule disagreed and the recorder participant was synthesised while its worker could still run; proof-only callers of admit would have created admissions with no terminal; a terminal `.` lost the directory requirement; conflicting summaries; each answered here. Revision 4 was rejected by Astra on four P1s: the helper has several connections of its own and needs SQLite's within-process lock bookkeeping between them; the producer fence did not cover a helper that outlives the agent with an admission in hand; an intermediate fleet closed before acknowledging broke transitive protection; a fleet could be marked closed with a discovery uncommitted; and four P2s: a dead helper's admissions had no terminal in the running agent, the sleeping binding persisted a reusable inode, the lineage predicate named header fields that identify nothing, the walk's grammar refused a `.` target the predicate accepts; each answered here. Revision 3 was rejected by Astra on five P1s: two SQLite VFSs in one process cannot coordinate through POSIX locks, since the library's own per-inode bookkeeping is in-process; a recorder that abandons early could settle an admission before its transport ended; an admission-only barrier does not cover a discovery whose uncertainty write failed after every admission settled; links at acknowledgement contradicted acknowledging only after settlement, and an inherited uncertainty vanished at the next successor; the ledger's lock does not prove the old process gone; and four P2s: closure at zero pending and the margin's extra day, sleeping claims with no binding, absolute and `.` spellings in the walk, `(dev, ino)` as a lineage; each answered here. Revision 2 was rejected by Astra on three P1s: the VFS delegated SQLite's Unix file methods, which open the SHM and check moved files by pathname themselves; every settled admission wrote a permanent hold, so ordinary replication never read complete; a late owner-scoped extension did not reach successors that had already acknowledged; and six P2s: orphan admissions never terminated, `..` and chain edges unspecified, `dup` sharing the directory offset, the platform-link predicate not preserved and terminal links unclassified, the uid producer without a lifecycle, one billing owner not one replicator; each answered here. Revision 1 was rejected by Astra on five P1s: SQLite canonicalises a `/proc` pathname before it opens anything and this driver passes no `nofollow`; `os.Root` confines but re-resolves every name, so a swap between a listing and an inspection still moves the answer; a `Start` row is not an admission, since the meter sends without one; the attempt-derived window lost a request completed before the change was noticed and the days between an admission and a finish; `closed_at` was not a handover; plus the directory open before `O_DIRECTORY`, per-owner holds the ledger cannot store, a uid with no producer, I3 against Part D, and tests). Branch `design-replicator-pinned-root`, off `feat/carloku-metered-replicator-20261001` at `e1280811`. It answers the eleven findings the twentieth review deferred (`DISPOSITIONS-metered-replicator.md`, review twenty) and keeps decisions 10 to 13 and approval H (`DECISIONS-FOR-PAUL.md`, section D, on the launch branch) as parameters. Nothing is built, wired or applied. |
| ## What stands today |
| @@ -42,7 +42,7 @@ The VFS is the plan's first spike and it must prove, before anything else of Par |
| ## Part C. The durable admission, two kinds of hold, and a handover on disk (Astra 3, 4) |
| -**Proof and admission are two things.** Fleet construction, `Open` and every watch tick ask the ownership *proof* (today's `admit`: the witness unchanged, the fleet not stale) and create nothing; only the request gate (`lease.RequestGate`, asked by the helper for each physical request) performs an *admission*, which is a proof followed by a row, so an idle healthy fleet has no admission and nothing bars its days. **The admission is a row, written before the send, with one ordering point against discovery.** Under a *discovery mutex* on the fleet (a new requirement: today `admit` and `lookAgain` do not share one, and the admission comments allow a discovery to run unlocked; this design serialises registration, the recheck and the start of a discovery on it) the gate checks the proof and, if the fleet is not stale and no discovery is in progress, registers the admission in the fleet's in-memory *pending set* with its `admitted_at`; it releases the mutex and commits the row in the durability ledger's own database (`admissions`: `token`, `fleet_id`, `owner account and app`, `admitted_at`, `transport_done_at`, `recorder_started_at`, `recorder_done_at`, `settled_at`, `how`), `synchronous=FULL`, within a bounded budget; after the commit it takes the mutex again and, if the fleet went stale or a discovery began since registration, marks the row `unissued` and refuses. Discovery, under the same mutex, sets the in-progress flag and counts the pending set, durable or not yet, as unsettled admissions when it computes its lower bound, and a registered admission that the discovery has counted and that then commits is inside the uncertainty (it was counted); one that commits after the discovery began is refused. So an admission is either counted by every discovery that follows its registration or refused, and Astra's 23:58:10 sequence (proof, discovery, commit, a send on the 4th) ends in a refusal at the recheck, not a send outside the uncertainty. The gate waits for the commit. A commit that does not return within the budget refuses the request (`ErrAdmissionNotDurable`; litestream sees a failed upload and does not advance) and the goroutine that awaited it stays to see the commit land and then marks the row `how = unissued, settled_at = now` in its own transaction, so a late commit never authorises anything and never blocks a day; if the process dies between, recovery classifies the row as below. The meter's `Start` row carries the token (`Start` may still fail or be late, as today; what is now mandatory is the admission, not the `Start`). An admission has two participants, the transport and the recorder, and is settled by whichever finishes last. The transport writes `transport_done_at` from the SDK middleware's finalize step, or `transport-lost` when its helper is proven gone (below). The recorder writes `recorder_done_at` with its outcome: `finished` (`Finish`), `abandoned` (`Abandon`), or `unrecorded` (the request path, when `Start` failed and made no attempt row, which today calls neither; it now writes this terminal); the recorder participant is never synthesised while its worker can still run: today's meter lets recording workers outlive the request path, and the admission stays unsettled (barring) until that worker finishes or abandons. The recorder runs in the agent, fed by the helper's messages over the fleet socket (`Start`, `Finish`, `Abandon` with the token), and the agent *registers* the recorder participant when it receives `Start` (`recorder_started_at`, under an agent-owned lifecycle guard per helper); a `Start` that the agent receives after it has begun the helper's termination handling is refused (the guard is closed first, then the socket drained, so registration and exit handling cannot interleave). The participant that finds the other already done writes `settled_at = max(transport_done_at, recorder_done_at)` and `how`, and performs, in that same transaction, the uncertainty extension or closure below. **The transition table is authoritative and is the plan's model:** states `registered` (in memory only), `admitted` (durable), `transport done`, `recorder done`, `settled`; edges `registered → admitted` (the commit), `admitted → unissued` (the recheck after the commit found the fleet stale or a discovery begun, or the gate's budget elapsed before the answer was returned: the row is rewritten as the terminal and the helper never receives the token), `registered → cancelled` (the commit definitely failed, no row exists: the registration leaves the pending set), `admitted → transport done | recorder done`, `transport done → settled` (the recorder's completion), `recorder done → settled` (the transport's completion or `transport-lost`), and `admitted | transport done | recorder done → recovered-unknown` at process recovery only (all missing timestamps `now`); `transport-lost` is a transport completion, not a terminal, so an admission whose recorder is still running after its helper died stays `recorder pending`. **The pending set and the abort edges.** A registration leaves the pending set only when its terminal, or its row, is durable: after `admitted` the row is the record and the set entry is dropped; after `cancelled` the entry is dropped once the cancellation is decided. A discovery that counted a registration treats its cancellation or its `unissued` as a settlement at that instant with an empty interval, so an uncertainty that waited only on it closes then, and the closure test ("no counted admission unsettled") ranges over rows and pending registrations alike. **An ambiguous commit** (an error or a timeout after the write may have landed) keeps the registration in the pending set, barring, until a read-back by `token` answers: a row found takes the `admitted → unissued` edge; none found takes `registered → cancelled`; the read-back is retried until it answers. Every terminal write is keyed by `token` and idempotent, retried until it commits; nothing is dropped from the pending set before its terminal is durable. Admissions are the ledger's pending state moved to before the send and given an owner. |
| +**Proof and admission are two things.** Fleet construction, `Open` and every watch tick ask the ownership *proof* (today's `admit`: the witness unchanged, the fleet not stale) and create nothing; only the request gate (`lease.RequestGate`, asked by the helper for each physical request) performs an *admission*, which is a proof followed by a row, so an idle healthy fleet has no admission and nothing bars its days. **The admission is a row, written before the send, with one ordering point against discovery.** Under a *discovery mutex* on the fleet (a new requirement: today `admit` and `lookAgain` do not share one, and the admission comments allow a discovery to run unlocked; this design serialises registration, the recheck and the start of a discovery on it) the gate checks the proof and, if the fleet is not stale and no discovery is in progress, registers the admission in the fleet's in-memory *pending set* with its `admitted_at`; it releases the mutex and commits the row in the durability ledger's own database (`admissions`: `token`, `fleet_id`, `owner account and app`, `admitted_at`, `transport_done_at`, `recorder_started_at`, `recorder_done_at`, `settled_at`, `how`), `synchronous=FULL`, within a bounded budget; after the commit it takes the mutex again and, if the fleet went stale or a discovery began since registration, marks the row `unissued` and refuses. Discovery, under the same mutex, sets the in-progress flag and counts the pending set, durable or not yet, as unsettled admissions when it computes its lower bound, and a registered admission that the discovery has counted and that then commits is inside the uncertainty (it was counted); one that commits after the discovery began is refused. So an admission is either counted by every discovery that follows its registration or refused, and Astra's 23:58:10 sequence (proof, discovery, commit, a send on the 4th) ends in a refusal at the recheck, not a send outside the uncertainty. The gate waits for the commit. A commit that does not return within the budget refuses the request (`ErrAdmissionNotDurable`; litestream sees a failed upload and does not advance) and the goroutine that awaited it stays to see the commit land and then marks the row `how = unissued, settled_at = now` in its own transaction, so a late commit never authorises anything and never blocks a day; if the process dies between, recovery classifies the row as below. The meter's `Start` row carries the token (`Start` may still fail or be late, as today; what is now mandatory is the admission, not the `Start`). An admission has two participants, the transport and the recorder, and is settled by whichever finishes last. The transport writes `transport_done_at` from the SDK middleware's finalize step, or `transport-lost` when its helper is proven gone (below). The recorder writes `recorder_done_at` with its outcome: `finished` (`Finish`), `abandoned` (`Abandon`), or `unrecorded` (the request path, when `Start` failed and made no attempt row, which today calls neither; it now writes this terminal); the recorder participant is never synthesised while its worker can still run: today's meter lets recording workers outlive the request path, and the admission stays unsettled (barring) until that worker finishes or abandons. The recorder runs in the agent, fed by the helper's messages over the fleet socket (`Start`, `Finish`, `Abandon` with the token), and the agent *registers* the recorder participant when it receives `Start` (`recorder_started_at`, under an agent-owned lifecycle guard per helper); a `Start` that the agent receives after it has begun the helper's termination handling is refused (the guard is closed first, then the socket drained, so registration and exit handling cannot interleave). The participant that finds the other already done writes `settled_at = max(transport_done_at, recorder_done_at)` and `how`, and performs, in that same transaction, the uncertainty extension or closure below. **The transition table is authoritative and is the plan's model:** states `registered` (in memory only), `admitted` (durable), `transport done`, `recorder done`, `settled`; edges `registered → admitted` (the commit), `admitted → unissued` (the recheck after the commit found the fleet stale or a discovery begun, or the gate's budget elapsed before the answer was returned: the row is rewritten as the terminal and the helper never receives the token), `registered → cancelled` (the admission writer has returned with a definite failure, or the writer's handle is closed and a fresh read then finds no row: the registration leaves the pending set), `admitted → transport done | recorder done`, `transport done → settled` (the recorder's completion), `recorder done → settled` (the transport's completion or `transport-lost`), and `admitted | transport done | recorder done → recovered-unknown` at process recovery only (all missing timestamps `now`); `transport-lost` is a transport completion, not a terminal, so an admission whose recorder is still running after its helper died stays `recorder pending`. **The pending set has one contract.** It holds every registration not yet resolved, and an entry leaves it in exactly two ways, both under the discovery mutex: (a) the admission row is durable, at which point the row carries the responsibility (the removal follows the commit, so a discovery sees the entry or the row and never neither); (b) the cancellation is *conclusive*, at which point the empty-settlement result is recorded for every discovery that counted it (the pending-discovery latch's counted set, or the open uncertainty's closure test, which ranges over rows and pending registrations alike), so an uncertainty that waited only on it closes then. The same empty-settlement rule applies to a counted admission that ends `unissued`. **A cancellation is conclusive only when the admission writer can no longer commit:** the writer is one goroutine owning the transaction, the gate's budget expiring refuses the request but does not resolve the registration, and the registration waits for the writer to return. A writer that returns having committed takes `admitted` (then `unissued`, since the request was refused); one that returns with a definite failure takes `cancelled`; one that cannot tell (the connection lost around the commit) is resolved by a read-back by `token` that begins only after the writer's handle is closed, on a read transaction opened after that close, so nothing it fails to find can be committed afterwards: a row found takes `admitted → unissued`; none found takes `cancelled`; the read is retried until it answers. There is no edge out of `cancelled`. Astra's sequence (the writer still running at the budget's expiry and the discovery's count, a read at 23:58:00.040, the commit at 23:58:00.050) cannot declare a cancellation, because the read waits for the writer; the writer's commit resolves it as `unissued`, and the discovery that counted it is told once. Every terminal write is keyed by `token` and idempotent, retried until it commits. Admissions are the ledger's pending state moved to before the send and given an owner. |
| **Two kinds of hold.** A *barrier* is the ledger's completeness rule: a day is not published complete for an admission's owner while an admission of that owner admitted on or before that day is unsettled (box-wide while Part C2's owner scope is not in use); it is a query over `admissions`, writes nothing, and lifts at settlement. An *uncertainty* is a row (`uncertainties`: `id`, `fleet_id`, `from`, `to` null while open, `reason`) written when a fleet's authority is found changed (by the witness, by `Recheck`, by a changed file), in the transaction that also writes the holds for the days of `[from, now]` for every owner the uncertainty reaches: `from = min(the registry proof, the last clean look's start, the earliest admitted_at of the fleet's admissions then unsettled) - margin`. If discovery finds no admission of the fleet unsettled, the uncertainty is opened and closed in that transaction (`to = discovery + margin`). While an uncertainty is open, settlements of that fleet's admissions extend it: each settlement, in its own transaction, writes the holds for every day of `[its admitted_at, its settled_at]` for every owner the uncertainty reaches, and the last settlement closes it with `to = max(discovery, that settlement) + margin` and materialises every day of the closed interval, the margin's extra day across midnight included; recovery uses the same helper and advances the publication watermark past the days it materialised. An admission that settles while no uncertainty of its fleet is open writes nothing permanent: the ordinary day of an unchanged fleet becomes complete when its barrier lifts. The multiple-midnights sequence (admitted on the 2nd, the change at 23:58:10, sent on the 3rd, finished on the 4th) holds the 2nd, 3rd and 4th through the settlement's interval, whichever participant finished last; the request completed before the change was noticed is inside the lower bound as today. **If the discovery transaction fails** the barrier is not enough (every admission may already be settled): discovery first latches a *pending discovery* in the ledger's in-memory failure overlay (`latchFailure`, extended to carry the lower bound and the owners reached), which the publisher's snapshot already overlays and detects across a drain (today's `snapshotOnce` and `drainFailures` behaviour, extended rather than replaced: a failed drain must not stop the overlay from making the snapshot incomplete), so every day from the bound reads incomplete for those owners until the uncertainty commits; the fleet refuses every request meanwhile and retries the write. The latch carries the lower bound, the owners reached and the tokens of the admissions the discovery counted; settlements of those admissions meanwhile find no uncertainty open and write no hold, but their rows record `settled_at` as always. **The replay is one transaction:** it opens the uncertainty with the original `from`; for each counted token whose row is settled it writes the holds for `[its admitted_at, its settled_at]` for every owner reached (and for a token that was cancelled or unissued, nothing); if any counted token is still unsettled the uncertainty stays open and the usual settlement rule closes it later; otherwise it closes with `to = replay instant + margin` (which is no earlier than the discovery and every counted settlement) and materialises every day of the closed interval; the overlay clears only after that commit, so no snapshot sees neither the latch nor the row. Astra's sequence (T counted at 23:58:10 on the 2nd, the write failing, T settling at 00:02 on the 4th, the replay at 00:03) writes the 2nd, 3rd and 4th through T's interval and closes at 00:04 on the 4th, and a successor constructed after the replay backfills that closed interval for its owners; the fleet cannot be marked `closed` while the discovery is uncommitted (above); a process that dies with the latch held leaves a fleet not `closed`, which recovery covers from the fleet's `opened_at`. |
| @@ -60,7 +60,7 @@ The VFS is the plan's first spike and it must prove, before anything else of Par |
| **Replication authority.** Separately from the owner, each identity has at most one replication authority: the external replicator (the box today), the activator's hibernation path (which replicates a hibernating exec route's database and holds its own stream), or this fleet. The fleet replicates an identity only when no other authority has it: a hibernating exec route whose database resolves to the same identity as a live unit tree's listed file is a conflict of authorities, refused for this fleet with both named, exactly as today's live/hibernating conflict is, until an explicit handover (a route's hibernation path releasing the database to the fleet) exists, which is not designed here. Deduplicating two spellings of one identity within the fleet is allowed only when both claimants are routes this fleet would replicate. |
| -**Lineage.** A replica key today is `(account, path)`, and an inode number is an observation, not a database: a deleted and recreated file can reuse it across a restart. The lineage is therefore a durable record in the shadow state (platform-owned; one record per replica key, written with a protocol this design states, since the shadow state has none to borrow (the fork's LTX writes sync the file and rename it without syncing the directory, and the shadow directory's creation syncs nothing): the record is written to a temporary name in the shadow directory, `fsync`ed, renamed over its final name, the directory `fsync`ed, and every directory created for it `fsync`ed in its parent at creation; replication authority for the key is granted only after the directory's `fsync` returns, so no replica write precedes a lineage whose name and ancestry are durable; the migration that adopts existing prefixes is marked by a record written the same way, and once that marker exists a key whose replica data exists with no record or a corrupt record is a fault, not a first build: its replication authority is withheld and an operator review raised, never re-adopted): `lineage_id` (random), `owner`, the accepted spelling, `(dev, ino)` and the file's birth time as `statx` reports it with `STATX_BTIME` set. Continuity means the same filesystem incarnation of the file, nothing more: within one fleet's life another accepted spelling of the same `(dev, ino)` reuses the key (one stream, one file); across a restart a key is resumed only when `(dev, ino)` and a valid birth time match the record; a filesystem that reports no valid birth time, or a mismatch, rotates to a new lineage (a new prefix) after the hold the change causes. A database replaced in place over the same incarnation (a restore written into the same inode) is not distinguished by this record and is not claimed to be; it is the owner's own replacement; what applies to it is the fork's existing behaviour when a WAL salt or LTX position no longer matches (a new snapshot), and no other in-place restore behaviour is claimed supported. No SQLite header field identifies a database incarnation and none is used. Existing path-based prefixes are adopted as the first lineage of the file found under that path at the first build after this design, recorded then; a later mismatch rotates as above. |
| +**Lineage.** A replica key today is `(account, path)`, and an inode number is an observation, not a database: a deleted and recreated file can reuse it across a restart. The lineage is therefore a durable record in the shadow state (platform-owned; one record per replica key, written with a protocol this design states, since the shadow state has none to borrow (the fork's LTX writes sync the file and rename it without syncing the directory, and the shadow directory's creation syncs nothing): the record is written to a temporary name in the shadow directory, `fsync`ed, renamed over its final name, the directory `fsync`ed, and every directory created for it `fsync`ed in its parent at creation; replication authority for the key is granted only after the directory's `fsync` returns, so no replica write precedes a lineage whose name and ancestry are durable; the migration that adopts existing prefixes is marked by a record written the same way, and once that marker exists a key whose replica data exists with no record or a corrupt record is a fault, not a first build: its replication authority is withheld and an operator review raised, never re-adopted): `lineage_id` (random), `owner`, the accepted spelling, `(dev, ino)` and the file's birth time as `statx` reports it with `STATX_BTIME` set. Continuity means the same filesystem incarnation of the file, nothing more: within one fleet's life another accepted spelling of the same `(dev, ino)` reuses the key (one stream, one file); across a restart a key is resumed only when `(dev, ino)` and a valid birth time match the record; a filesystem that reports no valid birth time, or a mismatch, rotates to a new lineage (a new prefix) after the hold the change causes. A database replaced in place over the same incarnation (a restore written into the same inode) is not distinguished by this record and is not claimed to be; it is the owner's own replacement; what applies to it is the fork's existing verification, which chooses a new snapshot, incremental continuation or an error according to its own checks (a salt change whose last page still matches continues incrementally, for one), and this record neither detects arbitrary in-place replacement nor establishes that any restore is supported. No SQLite header field identifies a database incarnation and none is used. Existing path-based prefixes are adopted as the first lineage of the file found under that path at the first build after this design, recorded then; a later mismatch rotates as above. |
| **Required coverage.** The platform's required databases are a list: the registry this process has open, the console database when the agent's role configuration says the box is a console box, `CONSOLE_DB` when set. A required database unreachable through its root is a problem that makes the inventory incomplete and is written as a durable box-wide hold for the day before and independent of whether a fleet is built. Before any cutover, a read-only operator command lists this inventory beside the external replicator's configuration. |
| @@ -89,7 +89,7 @@ The VFS is the plan's first spike and it must prove, before anything else of Par |
| - **Admission before send.** The middleware order makes an admission-less dispatch impossible: a fake gate that refuses sees no request on the wire; a gate whose commit is held past the budget refuses, the request is not sent, and the late commit's row is marked unissued. (A `Start`-less send remains possible, as today, and is not claimed otherwise.) |
| - **Part A.** A platform ancestry with a tenant-owned, group-writable or linked component refused; a directory swapped between `List` and `Walk` changes nothing; a listed subdirectory replaced by a FIFO fails `Child` with `ENOTDIR` and nothing blocks; a `..` past the root, and a `..` after a named component, refused; a retained child moved under another parent is a changed edge at `Recheck`; `List` twice on one `Dir` gives the same entries; `Child(name, expect)` refuses when the entry changed between the listing and the acquisition; each link class classified as written and none silent. |
| - **Part B.** The spike's own tests, then the fork's suite under the roots in the helper process; a WAL replaced by a link or a FIFO refused; a database replaced within its directory refused at reopen; locks honoured against an ordinary connection in another process, and the same-process arrangement shown unsafe; a helper that dies seen as a change; the SQLite identity asserted by a write through the connection, a checkpoint, and a read through `db.f`. |
| -- **Part C, as a model with the transition table and then against the ledger.** A settled admission on an unchanged fleet writes no permanent hold and its day becomes complete; a change with an unsettled admission opens an uncertainty from the earlier bound; the three-midnight interval in both completion orders (transport last; recorder last, including `abandoned` first and `unrecorded`); discovery with every admission already settled closes at once and materialises the margin's extra day across midnight; a discovery whose write fails with no admission pending bars the day through the pending-discovery latch until the retry commits; a successor linked at construction barred by the predecessor's unsettled admission and held by its late extension; A opens and closes U, healthy B acknowledges A, healthy C acknowledges B, and C's inherited set holds U; the fleet rows across build, stale, closing with a late settlement, closed, and acknowledgement, with a kill at each transition and the ledger reopened by a second process, orphans classified with all three timestamps and later days completing; the fence: a process that lost the ledger's lock but not the fence cannot be recovered over, and one without the fence cannot admit; a `closed` healthy chain contributing nothing; a discovery whose write fails with counted admissions that all settle before the replay (the replayed interval covers their settlements, a successor constructed after the replay inherits it, and no snapshot sees neither latch nor row); a commit that fails definitely, one that is ambiguous and read back both ways, and a recheck refusal after commit, each leaving the pending set only with a durable terminal and each closing an uncertainty that waited only on it; a helper dying before its recorder registered (`unrecorded` after proven exit), a `Start` racing the exit (refused by the closed guard or registered and then abandoned), and a death with a live registered worker (kept until it ends); the lineage record's protocol with a kill after each of its five steps, and a missing record after the migration marker withholding authority. |
| +- **Part C, as a model with the transition table and then against the ledger.** A settled admission on an unchanged fleet writes no permanent hold and its day becomes complete; a change with an unsettled admission opens an uncertainty from the earlier bound; the three-midnight interval in both completion orders (transport last; recorder last, including `abandoned` first and `unrecorded`); discovery with every admission already settled closes at once and materialises the margin's extra day across midnight; a discovery whose write fails with no admission pending bars the day through the pending-discovery latch until the retry commits; a successor linked at construction barred by the predecessor's unsettled admission and held by its late extension; A opens and closes U, healthy B acknowledges A, healthy C acknowledges B, and C's inherited set holds U; the fleet rows across build, stale, closing with a late settlement, closed, and acknowledgement, with a kill at each transition and the ledger reopened by a second process, orphans classified with all three timestamps and later days completing; the fence: a process that lost the ledger's lock but not the fence cannot be recovered over, and one without the fence cannot admit; a `closed` healthy chain contributing nothing; a discovery whose write fails with counted admissions that all settle before the replay (the replayed interval covers their settlements, a successor constructed after the replay inherits it, and no snapshot sees neither latch nor row); a commit that fails definitely, one that is ambiguous and read back both ways, a recheck refusal after commit, and a budget expiry while the writer is still running followed by its commit (one final outcome, `unissued`, and the uncertainty that counted it closed once), each leaving the pending set only with a durable row or a conclusive cancellation and each closing an uncertainty that waited only on it; a helper dying before its recorder registered (`unrecorded` after proven exit), a `Start` racing the exit (refused by the closed guard or registered and then abandoned), and a death with a live registered worker (kept until it ends); the lineage record's protocol with a kill after each of its five steps, and a missing record after the migration marker withholding authority. |
| - **Part D.** The identity cases; the authority conflict (live tree and hibernating exec route on one identity); the lineage record (a file deleted and recreated with a reused inode across a restart gets a new prefix; the same file resumes; an existing path-based prefix adopted once); a required database missing writing the hold without a fleet; a claim refused while the route's invocation is unknown or stale; a sleeping route with no binding, or whose private spelling resolves to another identity, refused; an observation that is not coherent (the pid changing between the two reads) leaving the route unknown. |
| - **Mutants** in a tree copy with the killing test named, per part, after the assertions above exist. |