Commit graph

1,031 commits

Author SHA1 Message Date
Meta Vens
5e6c7e22fd Record runs 7 and 8: the first people who speak in their own voices
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 02:35:38 +02:00
Meta Vens
a9d6a1c980 Tell the model why a call was refused, describe the controller, reopen on the SDK lane
Run 7's seed authored six distinct people with memories, reads, and worded
promises, and every declare_subject carried an empty controller: the field
had no description and the tool result said only "recorded as a gap". The
controller variant now describes its two shapes, and a decode failure's
reason goes back to the model in the tool result. The SDK lowering renders a
later user turn into its transcript, so a refusal continuation reaches the
sidecar instead of faulting.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 02:24:28 +02:00
Meta Vens
c31f25415a Let the seed author meaning: persona material, commitment statements, presence
Plan step 12. PersonaMaterial is the ontology's thirteenth row, built:
values, voice, memories, and reads keyed by subject, set whole by
set_persona_material, refused for padded text or a doubled read, no-op when
identical, outside every scope digest. A commitment carries a statement of
what is promised, required at authoring; an affordance-made promise states
the action it came through. The Projector context carries material by label,
each promise's statement, and who else stands in the same place; the
Persona's identity is label, voice, and values. Schemas move to consumer.v3.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 02:12:13 +02:00
Meta Vens
0a6a7216e3 Record runs 4 to 6 and map the seed authoring meaning as proposed step 12
The membrane traced end to end: the Projector's borrowed voice, the
contentless commitment, the unbuilt PersonaMaterial, and the missing
co-presence are recorded as findings; the harness no longer claims to fail
on silence, which is now the honest baseline. The map and handoff point at
the decision the operator owns.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 01:31:20 +02:00
Meta Vens
19609ee06c Render absence as flatness, and due as time
The Projector and Persona prompts asked for what this person remembers,
wants, and fears, with no component to ground any of it, so every seeded
person spoke in the model's one borrowed voice. Both prompts now render only
what the context establishes, say plainly what is unknown, and treat the
word budget as a ceiling. A commitment's due reaches the Projector as
minutes from now, since a bare 1000 was read as a sum of money.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 01:17:40 +02:00
Meta Vens
f3d1904428 Let the smoke show the whole membrane
A test-only TracingInferencePort wraps any inference port and appends each
request's purpose, model, instructions, input turns, tool catalog, and
output to the file GHOSTLIGHT_SMOKE_TRACE names. The logs redact prose by
design and the store is a temp directory, so this is the one way to read
what the Projector, the Persona, and the Interpreter were each given and
what each returned on a road run.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 00:41:27 +02:00
Meta Vens
4297b7c0c2 Project a seeded person's own life to its Persona, by label and never by id
The Projector context carried a name, a clock, and permission to speak, so a
seeded person reasoned correctly from nothing and said "I'm here". It now
carries the acting subject's own place, routes, holdings, dependencies,
authority shape, offices, redress, promises with their counterparties, and
pressure on self, resolved to labels. Another subject is named only where
this subject's own components bind them. Ids, keys, digests, revisions, and
every other subject's state stay out; the leak tests pin it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 00:27:11 +02:00
Meta Vens
52d148788c Record the first SDK road runs and point the Mind past the login gate
Run 3 proves speech cited by quote arrives whole with no quarantine. The
map, handoff, and runbook now say the SDK route is run; the remaining road
item is the live interrupted cell. One distilled evidence record carries
the day's belief-changing findings.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 00:06:17 +02:00
Meta Vens
4f7513bcba Cite Persona prose by quoting it, and make a bad quote a gap rather than a quarantine
The Interpreter's speak and record_gap tools took byte offsets the model
counted itself; on the Claude lane three of eight utterances came through
cut mid-word and one empty span reached invocation as a non-canonical
utterance and quarantined its cell. Both tools now take `source_quote`; the
harness locates the quote in the preserved prose and derives the span. A
quote not in the prose, empty, or padded is a translation gap at capture.
The invariant is unchanged: only words in the prose become speech.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 23:57:17 +02:00
Meta Vens
936aa71b8d Continue the conversation on refusal, and give the claude lane its descriptions
The first SDK-backed seeded runs. Run 1 refused both seed sessions: the
sidecar's schema conversion dropped every field description, `due` was an
undescribed integer against a clock the model never saw, and the subject
tool did not say a grant is required. Run 2 seeded six people in one round
and ticked three times.

A refusal used to reopen the session with an empty draft and a prompt that
named sites from a draft the model could no longer see. It now continues
the same conversation: `Refusal { after_round, mismatches }` on both
authoring checkpoints (controller_work.v12), the refusal as the next user
turn, and the draft rebuilt by one DraftFold shared by evaluator and oracle
from the calls after it. The harness takes the connector only when its
address is set, so an all-claude run needs none.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 23:50:28 +02:00
Meta Vens
463bd8dc14 Name the Linux deploy target so agents stop compiling for Windows
Nothing an agent loads in this repo said Ghostlight Dungeon ships to Linux
on Yggdrasil, so a Windows workstation build looked like reasonable
verification. Record the target, the fact that Idunn builds the release in
pinned Linux containers, and what a local compile is and is not evidence
for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 12:23:24 +02:00
Meta Vens
04db2dafa7 Cut the handoff's chronology down to current authority and the next gate
Item 1 had grown to three hundred lines of per-pass SHAs, line numbers, and
test counts that git and the ledger own. The handoff now carries what is
true of the tree, the constraints, the ordered next moves, the operator's
open decisions, and the deployment gate. Gating lessons move to the
re-entry warnings; the map points there instead of repeating them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 15:55:19 +02:00
Meta Vens
18f2eb96a2 Distill the evidence ledger to twelve records through one compaction path
44 records to 12: supersession chains collapsed to their survivors,
per-commit receipts folded into the decision they served, standing
pre-rebuild decisions merged into one record map.yaml already owns. Every
removed record is archived verbatim. `compact-evidence` is now the one
durable deletion path; the status tool reads folded scalars, and the
compaction helper imports the map readers instead of carrying copies.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 15:03:55 +02:00
Meta Vens
e211075969 Compact the map's next action to working memory before compaction
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 14:55:29 +02:00
Meta Vens
1665620690 Close plan step 11: the SDK port with its follow-ups and lessons
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 02:38:50 +02:00
Meta Vens
d72c246b1f Reclaim the lend on every exit path, and cut three leftovers
`take_oracle` now runs above the caller-identity and expiry gates, so a
refused `infer` reclaims the lend instead of leaving a dead lane's
tool-result owner in the map under a request id nothing will call again.
The port's doc already claimed this; the test that recorded the
divergence now asserts the reclaim.

`fixture_prepared` was `prepare_invocation`'s body a third time, with
the same caller id and the same expiry as `fixture_prepared_inference`
beside it. Its call sites use that one instead.

`ElaboratorSession`, `ElaborationCheckpoint`, `SeedSession`, and
`SeedCheckpoint` go back to `pub(super)`: nothing outside `world` names
them, and `pub(crate)` was reach they never needed. `private_interfaces`
then reports them the way it already reports the grouped, narrative, and
operational checkpoints, which are private under the same `pub(crate)`
`ControllerWork`.

Runtime binds the SDK model prefix from `DEFAULT_SDK_MODEL_PREFIX`
rather than spelling `claude` twice more.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 02:34:19 +02:00
Meta Vens
a791d559d5 Make Rust the one argument validator on the SDK transport too
The sidecar registered a closed Zod schema, so the SDK judged arguments
before Rust ever saw them: an extra key was stripped, a missing required
property was refused with an MCP error, and the call never reached the
handler. The connector path decodes the same argument in Rust, where
`deny_unknown_fields` and required-field decoding turn a bad argument
into the gap the evaluator records.

`toolInputSchema` now builds a loose object whose every property
catches, so nothing is stripped and nothing is refused. The conversion
survives only to advertise: the in-process MCP server derives the schema
the model sees from this object, and that schema still carries every
property, type, bound, and `required` name the emitter produced. What is
deleted is the strictness that made it a second validator -- `.strict()`,
the closed-object requirement, and the every-property-is-required
requirement.

One case cannot be carried through as written: a value outside a
property's own type empties that property, so it reaches Rust as an
absent field rather than a wrong one. Rust still names the gap; the SDK
still does not answer the call.

The undispatched-tool_use protocol violation stays, because no argument
reaches it any more but a call never offered to the handler still does.

`resultFault` is the result-subtype mapping lifted out of `runQuery`, so
`error_max_turns` becoming an ordinary round and every other subtype
becoming its named fault are both asserted without a credential.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 02:34:09 +02:00
Meta Vens
3514d76667 Make the port's source-scanning test line-ending neutral
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 02:15:31 +02:00
Meta Vens
0cbbf77284 Record the Claude SDK inference port in the docs
Two transports behind one port, routed per lane by model prefix; the
sidecar as a child process; the oracle as the evaluator's own fold; the
stopgap's liabilities named with its exit condition; the operator's
credential steps in the smoke runbook.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 02:11:41 +02:00
Meta Vens
4f424a5cfa Close the session's unissued-call gate with a test
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 02:10:33 +02:00
Meta Vens
0fa411bd81 Falsify the SDK port: adversarial oracle equality, gates, and the Zod divergence
Tests only. Per lane, an adversarial call sequence — decode failure, duplicate
terminal, call after terminal, unknown tool name, oversized argument — is
answered by the oracle with exactly the strings the evaluator recomputes for the
same transcript, and the transcripts the oracle refuses are the transcripts the
evaluator refuses. Identity is pinned across every request builder and one
checkpoint variant. The wire's closed enums, the fault disposition table, the
lowering gate table, the dispatch-order gate, the receipt's contents, and the
routing refusal each get a test.

Two recorded findings, characterized and not fixed:

- The emitted grammar's top-level object is not strict in Zod, so a property the
  connector's `deny_unknown_fields` decoders record as a gap is silently
  stripped and the call succeeds; and a missing or mistyped required property is
  answered by the SDK itself, leaving a registered `tool_use` block with no
  dispatch, which `assembleEvents` raises as `protocol_violation` and Rust maps
  to an integrity violation. Both are proven against the real in-process MCP
  server with no credential and no network.
- A lend refused by the caller-identity or expiry gate is never reclaimed: those
  gates run before `take_oracle`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 02:10:33 +02:00
Meta Vens
2ff1d435d8 Add the Claude SDK sidecar: framing, the closed schema converter, the query
`sidecar/claude-sdk` is the repo's second TypeScript package, wired to the
root only through `npm --prefix`. It reads length-prefixed MessagePack
frames on stdin, runs one SDK query, forwards every tool call to Rust, and
returns Rust's answer to the model verbatim. It computes no tool result,
reads no credential, logs no prompt, argument, or result, and writes nothing
outside its own stdout.

The query is opened with a custom system prompt, `settingSources: []`,
`tools: []`, `strictMcpConfig`, `persistSession: false`, and `alwaysLoad` on
every tool, so no CLAUDE.md, settings file, output style, built-in tool, or
persisted transcript reaches a caller-derived prompt.

`schema.ts` converts exactly the grammar `tool_schema.rs` emits and throws on
anything else, so an emitter that outgrows it fails a test rather than
registering a looser tool. Two Rust tests publish the fixtures the sidecar
tests read — the frame bytes and every real `parameters_json` — so the halves
cannot drift apart silently.

Dependencies are exact-pinned: @anthropic-ai/claude-agent-sdk 0.3.261,
@msgpack/msgpack 3.1.3, zod 4.5.4, and dev typescript 5.9.3 with
@types/node 24.13.3. `engines.node` is the installed major.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 02:10:33 +02:00
Meta Vens
b150b5e38e Add the SDK inference port, its oracle, and the model-name routing rule
`SdkInferencePort` runs one Claude Agent SDK query per round through a Node
sidecar and returns it as one `InferenceOutput`. It prepares through the
same `prepare_invocation` the connector port uses, so there is one identity
scheme and every `integrity_is_valid` variant is unchanged.

The sidecar computes no tool result. Every call is answered by
`ToolResultOracle`, which is the lane's own evaluator fold replayed over the
calls made so far — the same function that will recompute the string at
re-derivation. A lane lends its oracle keyed by the prepared request id
immediately before `infer`; the connector port ignores the lend.

Rust owns the transcript lowering, the receipt digest, and the fault
disposition. The sidecar names a closed set of reasons and cannot assert an
integrity violation, a request identity, or a tool result.

`open_inference` routes each configured model: a `GHOSTLIGHT_SDK_MODEL_PREFIX`
model reaches the SDK port, anything else the connector, and a model no
built backend claims fails at open rather than at its first tick. Ghostlight
reads no credential for this transport.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 02:10:33 +02:00
Meta Vens
708d621c0c Collapse the two prepared-identity bodies and the two constructors
`prepare_invocation` is now the one place a `PreparedInference` is built:
the connector port's `prepare_request` and `fixture_prepared_inference`
were the same body twice, and a second transport would have made it three.

`ControllerRunner::open` takes its ports. `with_test_ports` existed only
to bypass the two transport-specific parameters `open` hard-coded, so
removing them deletes the `#[cfg(test)]` twin rather than adding a third
constructor. Runtime binds the transport and the controller-work owner
through `open_inference` and `open_controller_work`, and
`GHOSTLIGHT_CONTROLLER_CREDENTIAL` is no longer unconditionally required.

Each tool lane's per-call body moves whole into a named fold —
`interpreter_tool_result`, `operational_tool_result`,
`grouped_tool_result` — which the batch evaluator now calls. No result
string changes. `apply_tool_call` was already this shape.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 02:10:33 +02:00
Meta Vens
feb603b04b Record the SDK port design as decided
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 00:52:51 +02:00
Meta Vens
634e81f365 Open plan step 11, the Claude SDK inference port; record the SDK research
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 00:25:31 +02:00
Meta Vens
e93ed166f1 Record the overturned connector assumptions
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 00:14:11 +02:00
Meta Vens
fd4edc531b Record the Claude connector task opening
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 00:09:47 +02:00
Meta Vens
06236a4faa Close steps 9 and 10 with their follow-ups
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 00:04:08 +02:00
Meta Vens
651bfb2d34 Delete the unreachable SPEAK_KIND guard; test the driver's Interrupted arm
Fork D was dead code: affordance_grants is insert-only and a checkpoint
that reached ReadyToSubmit/NoProposal already had speech, so a fresh
opportunity on the same scope always carries it too. Delete the guard,
the overtaken(Some(..)) call it fed, and note why in `interrupted`.

Widen InferencePurpose, PreparedInference::purpose, and InferenceEvent
to pub(crate) (plus fixture_inference_events/fixture_recovery_required
test seams) so runtime::tests can script an inference port by purpose.
Add a driver-level end-to-end test: through run_cover_tick, the world's
operational agent speaks once between the narrative cell's Persona turn
and its first submit (a re-lowering) and once more between the
re-lowered submit and its own commit, spending the one re-lowering the
turn is owed. The second change lands on the driver's own Interrupted
arm rather than ControllerRunner::run_narrative directly, and the tick
commits exactly the operational agent's two acts.

WorldMailbox::submit_controller stays pub(super); the test reaches
speech through the already pub(crate) ControllerPort wrapper instead.

Soul's follow-up on step 10 (the witness pass): add a MidTurnCommit::
Witness cause and a controllers.rs test proving a Witness landing
between the actor's bound turn and its submit re-lowers with only the
anonymous "what this person knows changed" line and no Overheard block
- KnowledgeSource::Witnessed writes no spoken_at, so overheard_since
finds nothing.

Rebased onto origin/codex/ghostlight-dungeon-mvp (step 10 integrated,
5cd8853) after step 9's own follow-ups landed; no conflicts at this
branch's controllers.rs/runtime.rs seams.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 23:59:42 +02:00
Meta Vens
5cd88536ef Record step 10 landed; phrase the operation count as the pinned catalog count
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 23:44:36 +02:00
Meta Vens
12445955d5 Record the witness operation in the docs
One speakerless event over a place subtree: recipients derived at apply
time by the one reach owner, already-holders untouched, an empty subtree
refused at both layers, confinement through the operation's place, an
affordance slot so a world can author a beacon or a blast. Schemas at
consumer.v2; the operation count aligned to the catalog's pinned count.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 23:42:38 +02:00
Meta Vens
70ea6b1323 Falsify the witness pass over the divergences it could hide
Seven tests, no production change. Two lanes, one selection: a
`Reach::Place` channel's `fan_out` and a witness over the same place
agree over every root and every speaker, so the two callers of
`under_place` and `unheld` are pinned by behaviour and not only by grep.
`under_place` covers a place itself, covers below it transitively, stops
at its edge, and never reaches a subject with no position — including
under the widest root in the world.

Two patches built so the resolver's candidate model and the apply arm
could disagree, and do not: a witness whose only recipient is a subject
the same patch declares and places, and a witness whose only recipient
is a subject the same patch forgot the fact. Each is refused at one
layer if the layers part; both land.

What a witness hands the narrative lane, pinned at the kernel because
the lane's fixtures are another pass's: exactly one scope component
moves, `knows`, which is the anonymous line, and the row it writes
carries no `spoken_at`, which is the only gate on an attributable
`Overheard` row.

The no-`Told` pin named its knowledge-touching kinds by example; an
exhaustive match now breaks the build when a `ComponentOpKind` is added
without saying whether it touches knowledge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:41:22 +02:00
Meta Vens
ecdeb9214a Witness: one speakerless event over a place subtree
An event a region saw cost one AcquireKnowledge per witness against a
128-operation patch cap, so a hemisphere did not fit in a patch. `Witness`
lands `Witnessed` knowledge of one fact on everyone standing anywhere under
one place.

The operation stores the place, never the recipients: the subtree is
re-derived at apply from live positions through `under_place`, and holders
are dropped through `unheld`, so a forged effect cannot assert a landing the
world does not have and a repeat cannot reset a knower's credence. It names
no speaker, so `Told` stays unrepresentable outside `Communicate`.

A witnessed event is only its reception, so a witness that reaches nobody is
refused — at resolve over the candidate graph, which now models the fan-out
including this patch's own relocations, and at apply over committed state.
Confinement is by the named place, which is not weaker than confining by the
recipients: a consumer and an uncovered jurisdiction refuse every witness
through the closure that already refuses every place.

Both store schemas move to consumer.v2 and prior stores are refused at open.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:41:22 +02:00
Meta Vens
307d8a5520 One owner for "under a place", one for "already a knower"
`audience`'s `Reach::Place` arm and `fan_out`'s already-holds filter were
inlined bodies. They become `under_place` and `unheld`, so the subtree read
and the never-overwrite rule each have one owner before a second caller
exists. No behaviour change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:41:22 +02:00
Meta Vens
f6cd3b534c Record step 9 integrated and step 10 in flight
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 23:24:30 +02:00
Meta Vens
711f6ec4b0 Record the interruption pass in the docs
The narrative lane re-lowers once on a moved scope with the delta the
subject could perceive; the kernel is the sole detector for that lane;
an author is named only for speech actually heard; a turn is lowered at
most twice by the shape of its binding; SubjectSnapshot carries the
subject's whole components; schemas controller_work.v11 and
persona_turn_receipt.v3. Plan step 9 landed with its undone list.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 23:20:51 +02:00
Meta Vens
d20dc51cca Falsify the interruption pass over its untested causes
Soul review of plan step 9. Tests only; no production code changed.

Five additions, each aimed at a claim the pass makes and does not
check.

The leak invariant, over the causes the pass leaves untested: a
transfer out of a neighbour's custody, a route closed under the
actor's feet, and the actor's own grant revoked. The existing fixture
world has no route, no resource, and no jurisdiction, so its subjects
can only ever be interrupted through `knows` and `commitments`; a
world carrying that furniture is what makes the other component kinds
reachable at all. Each cause re-lowers once, reports its own anonymous
line, produces no overheard block, and carries no label, id, or value
belonging to anyone.

The membrane's exact boundary. A mid-turn commitment names its
counterparty by subject id, that value really is inside the persisted
`Interruption`, and no prompt byte carries it. That is the difference
between "the renderer takes values rather than a snapshot" and "the
renderer emits no value at all", and only the second is what the
Interpreter sees.

Fork B against the digest rather than against itself. The pinned
preimage fixes the serialization; this reproduces every live
opportunity's scope digest from the snapshot's own `components` field,
which is what "view and digest cannot drift" has to mean.

Fork A at the call sites it deleted. Both detail lanes resume a
persisted in-flight row after the world moved under it. The
operational lane still aborts at its detector and spends nothing; the
narrative lane spends one Interpreter round on a binding already known
to be stale, is refused at submit, re-lowers, and commits. The fork's
accepted cost is one extra round and it is bounded at one.

The receipt bump. A v2 receipt digest, rebuilt from the previous
version's own preimage rather than derived from the live function, does
not rehydrate under v3.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:20:34 +02:00
Meta Vens
ad124eab0d Drop the interruption report's unread capture
Nothing reads it: the driver arm reports the subject, both digests, and
the gap detail, and the Eve receipt carries the prose and the gap. The
capture is still derived where the gap's source span comes from, and is
not stored.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:20:34 +02:00
Meta Vens
1a19fb2231 Prove the interruption over real committed state
Eight tests, each producing its interruption from a real commit that
lands on the world mid-turn rather than a hand-built digest: a
co-located neighbour's speech, and an owner patch that moves a component
naming no actor anywhere.

They hold the leak boundary at the delta surface, which no existing test
covered: the section names a speaker only for a telling the subject
actually received, carries no fact id, revision, digest or `spoken_at`,
and an out-of-reach actor's label and utterance reach no surface of the
turn. They hold the bound: four inferences for one re-lowering, and a
second refusal ends the turn with the overtaken gap having spent nothing
and submitted nothing. They hold the shape: a forged ancestry, a chained
re-lowering, and a row that disagrees with its own turn are each refused
at the gate that owns them, and the re-lowered row rebuilds its request
byte for byte at the derived round with no snapshot.

`the_scope_digest_is_unchanged_by_this_pass` pins the preimage digest so
a future field, order, or serde-attribute change fails there rather than
silently reissuing every bound proposal in every live world.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:20:33 +02:00
Meta Vens
1ef973a861 Interrupt the narrative turn instead of losing it
A scope digest that moved between a subject's prose and its commit is one
event with one detector — the kernel — and one handler, which renews the
turn's binding without re-running the Persona and lowers the same prose a
second time against an anonymous account of what moved.

Cut first. The narrative lane's three `ensure_scope_unchanged` calls are
gone: two detectors reporting different errors for one fact is what made
the pass need an entry point per stage. `exact_opportunity` is now the
sole authority on whether a scope moved, which is where the binding
already lives, and the operational lane keeps its early abort because it
holds nothing to preserve. `SubjectSnapshot` loses `holdings`,
`dependencies`, `incident_routes`, `authority`, and `controls` — each a
projection of a `ScopeComponents` field — and carries the components
whole instead.

The turn is re-recorded, never re-run. `PersonaTurnBinding` gains
`interrupted_from`, and a re-lowered turn's receipt commits to the
receipt it replaced by value; the prose and both inference receipts are
copied. One re-lowering is a property of the persisted shape rather than
a counter: a turn whose binding carries `interrupted_from` has no
successor arm.

What the Interpreter is shown is the typed component diff of the
subject's *own* components, named by meaning with no value, id, count or
actor, plus every statement told to it since its bound revision. The
renderer takes values rather than a snapshot, so a neighbour's state is
a compile error rather than a review note.

`controller_work.v11` and `persona_turn_receipt.v3` refuse prior rows
with no shim. No kernel arm changes: `ScopeComponents` gains
`Deserialize` and no field, so every scope digest is byte-identical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:20:33 +02:00
Meta Vens
a62708fe33 Record steps 9 and 10 in flight; drop the two-call-sites claim
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 22:40:48 +02:00
Meta Vens
e2392ded21 Open plan step 10, witnessed events over a place subtree
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 22:37:56 +02:00
Meta Vens
a7c9b8e0d5 Scope the interruption promise to the narrative lane
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 21:48:04 +02:00
Meta Vens
b77f59ff9b Place the interruption clause where it reads
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 21:37:49 +02:00
Meta Vens
b22536fb11 Open plan step 9, interruption, and retire the re-submission decision
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 21:37:34 +02:00
Meta Vens
bcfd024b34 Record the seeded road run in the handoff and map
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 19:38:45 +02:00
Meta Vens
79206dfce7 Carry the road's rules from the seeded live smoke
The seeded smoke passed on the twelfth run: six persons authored into
Low Sere from the Kalsa vault in one round, the deficit to zero, nine
subjects and six boundaries at activation, eight cells per tick, the
seeded people speaking to each other. Each earlier failure became a rule
in the tree: strict function schemas (anyOf, typed tags) with an offline
totality test over both authoring lanes; a content-addressed provider
request identity so repair rounds do not collide with the connector's
replay record; the connector's expiry skew separated from the response
read timeout; the seed lane's own round budget with parallel tool calls
and a budget-end submit; canonical ids and the placement rule in the
brief; the commitment shape rule in its tool description; inference
faults and cell outcomes logged where the operator can read them; and a
harness that fails on an empty seed or a silent world.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 19:38:01 +02:00
Meta Vens
b7042f16df Record the seed producer and close its Soul follow-ups
Docs describe the seed lane as landed: scale intent at creation, the
phase-free qualification with the Draft refusal in one owner, the owner
port bound to the Draft revision, the Vault reader's containment and
caps, obligations as the source of boundaries, evidence lifetime at
ingress. The session-zero and compiler language leaves the eve-native,
multiplayer, Delvehold, and mvp docs; plan step 8 is landed; the smoke
note's run block targets Kalsa Public at Low Sere. Steward folded the
boundary and cut the stale drift warning.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 18:32:20 +02:00
Meta Vens
491b06e9b0 Land the six seed-lane Soul follow-ups
Vault: collect_notes canonicalizes every directory and note against the
vault's canonical root during the walk and refuses to descend into a
reparse-point directory entry, closing the junction escape. VaultError
no longer carries an absolute filesystem path: Root names no path and
Scope carries only the caller-supplied scope string. The link-fanout
receipt cap is now checked per receipt, not per link.

Mailbox: SeedPort::submit_seed takes its own snapshot at submission
time, refuses anything off Draft with KernelError::WrongPhase, and
binds expected_revision to that fresh snapshot instead of a caller-
supplied one — closing the window between a runner's initial Draft
check and a multi-round session's eventual submit. submit_principal
refuses evidence whose valid_until has passed, read at the mailbox
ingress boundary rather than in a reduce arm.

App session: VerifiedPrincipalEvidence carries valid_until, minted from
the session's own access_expires_at, so a long-lived SeedPort cannot
hold authority past the session that vouched for it.

Elaboration: SEED_INSTRUCTIONS now requires an obligation with a
counterparty from every authored subject, since a goal alone derives no
MissingStructure boundary; the goals-only reproducer stays as a live
test of that structural fact.

Controllers: updated the two require_answer/phase tests and the two
seed-lane spec tests whose expectations changed now that submit_seed
owns its own phase gate ahead of require_answer.

334 world / 379 crate baseline with 5 FALSIFIED reproducers ignored is
now 385 crate tests passing (2 unrelated live-infra ignores remain), no
FALSIFIED ignore left, fmt clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 18:31:31 +02:00