Rule L1-Q9: resume reads in-flight elaboration state
The operator ruled that an in-flight elaborator session is rediscovered by reading state, not by re-deriving its key. Cut 4 adds one store method listing in-flight elaboration rows, scanned once per sweep. The sweep matches rows by world, jurisdiction and answer, ignoring ancestry, lens and instruction text, and adopts the row and its id before any draw. The command id stays the kernel's idempotency key and stops being the only way to rediscover work. New promises prove the fix, and each fails when adoption is removed: a stranded deficit session is adopted with its refusal and without a repeated round; a boundary session is repaired after an unrelated commit and a clock tick; no orphan row accumulates over five ticks; real-shaped non-matches are never adopted. L1.f10 is resolved by Cut 3 for boundaries and Cut 4 for deficits. Every question L1-Q1 through L1-Q9 now carries a ruling. Estimated total +1,300 lines. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
d3b2c0e464
commit
ff766e8901
1 changed files with 134 additions and 42 deletions
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
Ends are owned by `ghostlight-stock-lenses.md`; this document owns the means.
|
||||
Written 2026-09-16 against Ghostlight `817c9f4` on
|
||||
`codex/ghostlight-dungeon-mvp`; first committed at `b846c68`; refreshed the
|
||||
same day with the operator's rulings on L1-Q4 (twice), L1-Q5, L1-Q6 and
|
||||
L1-Q7, and with the stranding probe. `b846c68` changed only this file, so
|
||||
every line number below is against `817c9f4` = `b846c68` source unless a cut
|
||||
says otherwise. Where a ruling superseded an earlier recommendation, the
|
||||
`codex/ghostlight-dungeon-mvp`; first committed at `b846c68`, refreshed at
|
||||
`d3b2c0e`; refreshed again the same day with the operator's rulings on
|
||||
L1-Q4 (twice), L1-Q5, L1-Q6, L1-Q7 and L1-Q9, and with the stranding probe.
|
||||
Those commits changed only this file, so every line number below is against
|
||||
`817c9f4` = `d3b2c0e` source unless a cut says otherwise. Every question now
|
||||
carries a ruling; Hands start at Cut 1 from `d3b2c0e` (after this refresh is
|
||||
committed, from that commit). Where a ruling superseded an earlier recommendation, the
|
||||
earlier text is kept under "History" inside the question and nowhere else,
|
||||
so there is one live design.
|
||||
|
||||
|
|
@ -524,7 +526,8 @@ not repo artifacts.
|
|||
re-checks the deficit at commit (`lib.rs:1706-1712`), so the old
|
||||
ancestry is informational, and the repair prompt still names the refusal
|
||||
the model saw.
|
||||
- B: **exclude ancestry from the deficit key.** The deficit id becomes
|
||||
- B (history, superseded by the ruling): **exclude ancestry from the
|
||||
deficit key.** The deficit id becomes
|
||||
`(world, jurisdiction, "deficit")`. Then a deficit session that
|
||||
`Committed` leaves a `ReadyToSubmit` row under the one id every later
|
||||
deficit session on that jurisdiction derives; the next session's initial
|
||||
|
|
@ -534,19 +537,26 @@ not repo artifacts.
|
|||
same id with a different body is `CommandIdConflict` (`lib.rs:1438-1447`),
|
||||
and with the same body `AlreadyApplied`. B needs a different
|
||||
disambiguator, which is a session counter in disguise; not viable alone.
|
||||
- C: **equality only.** Compare identity fields (world, jurisdiction,
|
||||
answer, answer digest) at `:404` and adopt the row's ancestry, lens and
|
||||
text. Fixes the boundary stall; deficit rows stay stranded and orphans
|
||||
- C (history as a competitor; live as A's boundary half): **equality
|
||||
only.** Compare identity fields (world, jurisdiction, answer, answer
|
||||
digest) at `:404` and adopt the row's ancestry, lens and text. Fixes
|
||||
the boundary stall; alone, deficit rows stay stranded and orphans
|
||||
accrue one per interrupted deficit session per commit.
|
||||
- **Recommendation: A**, with C inside Cut 3 now (it is A's adoption rule
|
||||
applied to the one row `lookup` already returns) and A's listing method
|
||||
as its own small cut after L1 or, if the operator prefers, inside Cut 4
|
||||
where `demand_entries` is written. A is the root agent's leaning and the
|
||||
probe supports it: the only way to find a moved key is to read the rows.
|
||||
Orphan rows already in a store are a separate hygiene question (a
|
||||
`NoPatch`-style terminal state for abandoned rows, or a scan at open) and
|
||||
are not posed here.
|
||||
- **Depends on it:** Cut 3 (C); Cut 4 or a follow-up cut (A's method).
|
||||
- **Recommendation: A**, with C inside Cut 3 (it is A's adoption rule
|
||||
applied to the one row `lookup` already returns) and A's listing inside
|
||||
Cut 4 where `demand_entries` is written. The probe supports it: the only
|
||||
way to find a moved key is to read the rows. Orphan rows already in a
|
||||
store are a separate hygiene question (a terminal state for abandoned
|
||||
rows, or a scan at open) and are not posed here.
|
||||
- **Depends on it:** Cut 3 (the adoption rule, boundary half); Cut 4 (the
|
||||
listing, deficit half).
|
||||
- **Ruling:** A. The operator: "A, of course". Resume reads state: the
|
||||
in-flight listing rediscovers work by what it answers; the command id
|
||||
stays the kernel's idempotency key and stops being the only way to
|
||||
rediscover work. Cut 3's adoption rule is part of A's design, not a
|
||||
competitor.
|
||||
- **Authority:** operator.
|
||||
- **Ruled:** 2026-09-16.
|
||||
|
||||
## Cut 0. Captures
|
||||
|
||||
|
|
@ -1001,10 +1011,8 @@ not repo artifacts.
|
|||
tests; probe 3's boundary half as a permanent test): `Rejected`, then
|
||||
`submit_clock(60)`, then a fresh runner's step is `Committed` under the
|
||||
same id and its input carries the refusal;
|
||||
`a_deficit_session_is_rediscovered_by_its_answer_after_an_unrelated_commit`
|
||||
(controllers tests; probe 3's deficit half) is written here but marked
|
||||
`#[ignore = "L1-Q9"]` until that ruling lands: it asserts `Committed`
|
||||
under the first id, one inference on resume, and one row;
|
||||
(probe 3's deficit half needs the listing and is Cut 4's
|
||||
`a_stranded_deficit_session_is_adopted_with_its_refusal_intact`);
|
||||
`a_checkpoint_that_changes_the_lens_or_its_text_is_an_illegal_transition`:
|
||||
from a persisted `ElaboratorInFlight`, a next checkpoint identical
|
||||
except `lens` is refused by the real store with the "illegal checkpoint
|
||||
|
|
@ -1107,8 +1115,8 @@ not repo artifacts.
|
|||
- P3.6 Integrity binds the stored invocation to the stored text; a row
|
||||
whose two disagree is refused, and a row whose text is not the current
|
||||
code's still opens (the (a) trade, stated).
|
||||
- P3.7 Deficit rediscovery after an unrelated commit is not promised by
|
||||
this cut (L1-Q9 A); the ignored test names the gap.
|
||||
- P3.7 Deficit rediscovery after an unrelated commit is Cut 4's promise
|
||||
(P4.6), not this cut's; no test in this cut is ignored.
|
||||
- **Landed:**
|
||||
- **Verdicts:**
|
||||
|
||||
|
|
@ -1149,9 +1157,35 @@ not repo artifacts.
|
|||
(pure over the snapshot); a boundary under nested roots appears under
|
||||
each covering root, so the list is deduped by command id keeping first
|
||||
occurrence. The claim set is this `Vec`, alive for one sweep and never
|
||||
persisted. Under L1-Q9 A this is where the in-flight listing is
|
||||
consulted before drawing; until that ruling, `step_session`'s `lookup`
|
||||
by id and the adoption rule are the only rediscovery.
|
||||
persisted. **Rediscovery (L1-Q9 A):** `sweep` calls
|
||||
`self.work.elaboration_in_flight()` once per sweep, before drawing
|
||||
anything, and `demand_entries` takes that list: for each demand entry,
|
||||
if exactly one listed row has the same `world_id`, `jurisdiction` and
|
||||
`answer` (ancestry, lens and text ignored), the entry *is* that row —
|
||||
its session and its command id are adopted and no draw happens; if
|
||||
none, a fresh session is built as above; if more than one (which the
|
||||
dedup and one-sweep-at-a-time make impossible from this code, but a
|
||||
store can hold anything), the oldest by `stored_at` is adopted and the
|
||||
rest are logged at `warn` as orphans. A deficit entry adopted this way
|
||||
keeps its old ancestry and old id; `require_answer` re-checks the
|
||||
deficit at commit (`lib.rs:1706-1712`), so the ancestry is
|
||||
informational, and the repair prompt still carries the refusal the
|
||||
model saw. The command id stays the kernel's idempotency key and stops
|
||||
being the only way to rediscover work; `step_session`'s `lookup` by id
|
||||
and Cut 3's adoption rule remain the second line for the boundary path.
|
||||
- `controllers.rs:1541` region: `ControllerWorkStore::elaboration_in_flight(&self)
|
||||
-> Result<Vec<(CommandId, ElaboratorSession)>, ControllerWorkStoreError>`
|
||||
as a required trait method (no default body: a store that cannot list
|
||||
must say so at compile time). `CultCacheControllerWorkStore` answers it
|
||||
from the in-memory `journal.work` map (`:1662-1667` shape): one pass
|
||||
over the rows, filtering `ControllerWork::Elaboration` in
|
||||
`ElaboratorInFlight` or `ReadyToSubmit` (not `NoPatch`), after
|
||||
`verify_journal_custody` as `lookup` does. Cost O(rows) over a map of
|
||||
tens to hundreds of entries, run once per sweep (every 300 s), never
|
||||
per entry and never per step; no index, no schema, no new row.
|
||||
`RecordingWorkStore` (`:8339`) implements it the same way over its
|
||||
`work` map. Dungeon's test stores (`AlwaysFreshWorkStore` `runtime.rs:3194`
|
||||
and its siblings) return `Ok(Vec::new())`, which is true of them.
|
||||
- `elaboration.rs`: `pub async fn sweep(&self, permits:
|
||||
Arc<tokio::sync::Semaphore>) -> Result<(), ControllerError>`: snapshot;
|
||||
return `Ok(())` unless Active; `demand_entries`; a `tokio::task::JoinSet`;
|
||||
|
|
@ -1195,7 +1229,34 @@ not repo artifacts.
|
|||
two-root world of `:11605` with a boundary inside the inner root runs
|
||||
one session for it; `a_deficit_entry_is_one_per_jurisdiction_per_ancestry`:
|
||||
a world with a deficit and no boundary runs one session per sweep and
|
||||
a second sweep after the commit runs a new id.
|
||||
a second sweep after a `Committed` session runs a new id (a finished
|
||||
row is not in flight and is not adopted).
|
||||
- Library, rediscovery (controllers tests, probe 3 as permanent tests
|
||||
through `sweep` with a pool of 1):
|
||||
`a_stranded_deficit_session_is_adopted_with_its_refusal_intact`: the
|
||||
deficit world of probe 3; sweep one ends `Rejected` (one row, one
|
||||
refusal); `submit_clock(60)`; sweep two ends `Committed` under the
|
||||
first command id, the second invocation's input carries the first
|
||||
refusal, `infer` was called exactly twice across both sweeps (once per
|
||||
sweep: no repeated round), and `custody elaboration_commands == 1`;
|
||||
`a_boundary_session_is_repaired_after_a_commit_and_a_tick`: the
|
||||
boundary world; sweep one `Rejected`; the owner admits an unrelated
|
||||
operations-only patch, then `submit_clock(60)`; sweep two `Committed`
|
||||
under the same id with the refusal in its input, one row;
|
||||
`the_store_gains_no_orphan_per_commit`: the deficit world; after
|
||||
sweep-`Rejected`, five clock ticks each followed by a sweep whose
|
||||
script keeps refusing (bad shed every round, budget permitting), the
|
||||
store holds one elaboration row and `infer` was called once per sweep;
|
||||
`a_row_for_another_answer_is_not_adopted`: rows shaped like real ones
|
||||
that must **not** match — a real `ElaboratorInFlight` row for the same
|
||||
deficit answer under a different jurisdiction (the two-root world of
|
||||
`:11605`, deficit under root B, sweep for root A), a real row whose
|
||||
`world_id` differs (a second world file's row copied into the first
|
||||
store through the raw backing store, with a valid envelope and canonical
|
||||
bytes), and a real `NoPatch` row for the same answer — the sweep builds a
|
||||
fresh session for the entry in each case, and the foreign-world and
|
||||
other-jurisdiction rows are left untouched (their bytes equal before
|
||||
and after).
|
||||
- Dungeon (`runtime.rs` tests): `saturating_one_pool_never_delays_the_other`
|
||||
as specified under L1-Q4: hold all elaboration permits, run
|
||||
`run_cover_tick` on `active_two_cell_world` with a counting port, both
|
||||
|
|
@ -1220,9 +1281,10 @@ not repo artifacts.
|
|||
`:2602`; `docs/architecture/ghostlight-world-ontology.md:668` rewritten
|
||||
as the live rule: "Sessions run concurrently over distinct demand entries
|
||||
— every open boundary and one deficit per jurisdiction — each drawing its
|
||||
lens by the world's weights, each carrying its lens in its identity, and
|
||||
each holding one permit of the elaboration ceiling; the sweep is one at a
|
||||
time." [Hands]; `ghostlight-world-ontology.md:773-775` "Sessions" bullet:
|
||||
lens by the world's weights and recording it, each holding one permit of
|
||||
the elaboration ceiling; a sweep first reads the store's in-flight
|
||||
sessions and resumes any that answer a current demand entry; the sweep is
|
||||
one at a time." [Hands]; `ghostlight-world-ontology.md:773-775` "Sessions" bullet:
|
||||
the "in Draft" clause stays target (L3) [Hands];
|
||||
`ghostlight-dungeon-mvp.md:473-479` names the two pools [Hands];
|
||||
`notes/fresh-workspace-handoff.md:284-290` [steward].
|
||||
|
|
@ -1232,14 +1294,17 @@ not repo artifacts.
|
|||
simulation `Semaphore` owns speak turns and cover cells; the mailbox
|
||||
owns commit order.
|
||||
- Inputs: one snapshot per sweep; the elaboration pool; the store's
|
||||
`lookup` by id inside each `step_session` (Cut 3's adoption rule), and
|
||||
under L1-Q9 A the in-flight listing.
|
||||
in-flight listing once per sweep (L1-Q9 A) and its `lookup` by id
|
||||
inside each `step_session` (Cut 3's adoption rule).
|
||||
- Outputs: one `step_session` per distinct entry.
|
||||
- Derived state: the claim set (never stored).
|
||||
- Forbidden writers: any second `sweep` concurrent with the first (the
|
||||
runtime loop awaits; the library does not guard it — stated); any
|
||||
caller starting `step_session` directly (`pub(super)`); any library-side
|
||||
concurrency number; any lane acquiring the other lane's pool.
|
||||
concurrency number; any lane acquiring the other lane's pool; a fresh
|
||||
draw or a fresh id for an answer that has an in-flight row (the listing
|
||||
is consulted before any draw); the command id as the sole rediscovery
|
||||
path (deleted as such).
|
||||
- Shared paths: every session takes the same permit path and the same
|
||||
`step_session`; a test port and the production port see the same
|
||||
`PreparedInference`; both pools are read at open by the same shape of
|
||||
|
|
@ -1271,7 +1336,20 @@ not repo artifacts.
|
|||
`Retryable` fault must fail on the orphaned task's missing commit. M4.4
|
||||
`runtime.rs:1837`: pass `state.controller_permits` instead of
|
||||
`elaboration_permits`: `saturating_one_pool_never_delays_the_other`'s
|
||||
second half must fail.
|
||||
second half must fail. M4.5 `demand_entries`: skip the listing (always
|
||||
build fresh): `a_stranded_deficit_session_is_adopted_with_its_refusal_intact`
|
||||
must fail (`Committed` under a new id, three `infer` calls, two rows)
|
||||
and `the_store_gains_no_orphan_per_commit` must fail (six rows). M4.6
|
||||
`demand_entries`: match on `world_id` and `answer` only (drop
|
||||
`jurisdiction`): `a_row_for_another_answer_is_not_adopted`'s
|
||||
other-jurisdiction case must fail. M4.7 `demand_entries`: match on
|
||||
`jurisdiction` and `answer` only (drop `world_id`): its foreign-world
|
||||
case must fail. M4.8 `CultCacheControllerWorkStore::elaboration_in_flight`:
|
||||
include `NoPatch` rows: its `NoPatch` case must fail. M4.9 `elaboration.rs`
|
||||
adoption rule (Cut 3's `same_answer`) restored to whole-session
|
||||
equality while the listing stays: `a_boundary_session_is_repaired_after_a_commit_and_a_tick`
|
||||
must fail (`Superseded`), which proves the two halves are separately
|
||||
necessary.
|
||||
- operator: one `GHOSTLIGHT_SMOKE_TRACE` road run (`runtime.rs:3738`,
|
||||
ignored) after this cut with `GHOSTLIGHT_ELABORATION_MAX_CONCURRENT=2`:
|
||||
the trace shows two elaboration sessions in one sweep when the seeded
|
||||
|
|
@ -1290,6 +1368,16 @@ not repo artifacts.
|
|||
stated defaults.
|
||||
- P4.5 A quarantine-class error still ends the sweep with `Err`, after
|
||||
every spawned session has finished.
|
||||
- P4.6 A stranded deficit session is adopted by what it answers: it
|
||||
resumes under its first command id with its refusal in the next
|
||||
invocation and no repeated round.
|
||||
- P4.7 A boundary session is repaired after an unrelated commit and a
|
||||
clock tick, under its id, with its refusal in the next invocation.
|
||||
- P4.8 The store gains no orphan elaboration row per commit; rows for
|
||||
another jurisdiction, another world, or a finished (`NoPatch`) session
|
||||
are never adopted and never touched.
|
||||
- P4.9 `elaboration_in_flight` runs once per sweep and every
|
||||
`ControllerWorkStore` implements it (no default body).
|
||||
- **Landed:**
|
||||
- **Verdicts:**
|
||||
|
||||
|
|
@ -1340,13 +1428,13 @@ not repo artifacts.
|
|||
| 0 | — | — | scratch captures only | |
|
||||
| 1 | 0 | `lens.rs` ~150 + tests ~130; `lib.rs` 2 lines + test helper ~6 | 0 deps; 0 targets | |
|
||||
| 2 | ~6 (doc lines replaced, the v2 denial case) | library: state field, command, effect, two reducer arms, resolve param, mismatch variant, snapshot field, replay check ~90; 19 literal sites × 1 line; tests ~220; Dungeon: payload field, policy fn, Eve control and binding, three schema strings, harness ~40; tests ~60; docs ~8 | 0 deps; `consumer.v4` → `v5`; `world_create.v3` → `v4` | |
|
||||
| 3 | ~4 (the constant as integrity reference, the whole-session supersession equality) | session fields, draw and text in `select_answer`, adoption rule, request param ~60; tests ~220 (one ignored pending L1-Q9); docs ~3 | `controller_work.v15` → `v16` | |
|
||||
| 4 | ~35 (sequential sweep, `Inactive`, doc lines) | `sweep` + `demand_entries` ~80; second pool and its config ~25; tests ~230; docs ~12 | public `sweep` signature gains a `Semaphore`; one new env variable | |
|
||||
| 3 | ~4 (the constant as integrity reference, the whole-session supersession equality) | session fields, draw and text in `select_answer`, adoption rule, request param ~60; tests ~200; docs ~3 | `controller_work.v15` → `v16` | |
|
||||
| 4 | ~35 (sequential sweep, `Inactive`, doc lines) | `sweep` + `demand_entries` with rediscovery ~110; trait method + three store impls ~45; second pool and its config ~25; tests ~360; docs ~14 | public `sweep` signature gains a `Semaphore`; `ControllerWorkStore` gains one required method; one new env variable | |
|
||||
| 5 | ~14 | ~25 | 0 | |
|
||||
|
||||
Net for L1: about +1,150 lines, of which about two thirds are tests, no new
|
||||
Net for L1: about +1,300 lines, of which about two thirds are tests, no new
|
||||
dependency, no new crate, no new binary, four schema strings replaced, one
|
||||
environment variable added. The growth buys the capabilities the target
|
||||
environment variable added, one required method on the store trait. The growth buys the capabilities the target
|
||||
names (lenses, world-owned weights and their owner command, a recorded
|
||||
replayable draw inside the session identity, concurrent sessions under a
|
||||
ceiling that cannot starve simulation, an explicit weights payload) and
|
||||
|
|
@ -1400,7 +1488,9 @@ session on one answer was that nobody wrote one.
|
|||
is the retryable fault it is today. Making simulation win at the
|
||||
connector needs connector-side priority or two caller ids, either of
|
||||
which is outside L1.
|
||||
- **L1.f10 (pre-existing, high, operator — L1-Q9):** an in-flight
|
||||
- **L1.f10 (pre-existing, high, fix — resolved by L1-Q9 A: the deficit
|
||||
half in Cut 4's listing, the boundary half in Cut 3's adoption rule):**
|
||||
an in-flight
|
||||
elaboration session is rediscovered only by re-deriving its command id
|
||||
and looking that id up; there is no listing query. Probe 3: after any
|
||||
unrelated commit, a deficit session's key moves (ancestry is in its
|
||||
|
|
@ -1410,8 +1500,10 @@ session on one answer was that nobody wrote one.
|
|||
reported `Superseded` forever because `ancestry` is in the session
|
||||
equality (`:404-405`). On the road the clock commits every tick, so no
|
||||
`Rejected` session of either kind has been repaired on a later sweep.
|
||||
Cut 3 fixes the boundary half (adoption by identity); the deficit half
|
||||
and the orphan rows are L1-Q9.
|
||||
Cut 3 fixes the boundary half (adoption by identity); Cut 4 fixes the
|
||||
deficit half (rediscovery by answer through `elaboration_in_flight`) and
|
||||
stops new orphans. Orphan rows already on disk from before L1 are not
|
||||
cleaned by either cut.
|
||||
- **L1.f9 (pre-existing, low, recorded):** under Q7 (a) only the elaboration
|
||||
lane binds integrity to stored text. `SEED_INSTRUCTIONS`
|
||||
(`elaboration.rs:1299`), `PERSONA_PROVIDER_INSTRUCTIONS` and the
|
||||
|
|
|
|||
Loading…
Reference in a new issue