Commit graph Ghostlight/crates
Author SHA1 Message Date
Meta Vens
855727f8a4 Take a turn's address as its spans in prose order
The Interpreter was citing correctly and the fold was discarding: an
utterance split by narration lost its second half and a turn's second
visible act became an ambiguity gap. Every span cited for a kind now joins
that kind's one statement in prose order; only an overlapping span is an
ambiguity. NarrativeCapture carries the spans, the invocation still carries
one utterance and one display. controller_work.v15.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 14:20:27 +02:00
Meta Vens
81a1f53c2e Report the interrupted cell: subject and both scope digests
A committed turn that re-lowered once carries its pair of scope digests on
the receipt; the cover tick logs it per cell and keeps it on the tick
summary, and the smoke harness prints one "interrupted cell" line per
re-lowered cell after the tick line. The operator feed line shows a display
beside or instead of speech.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 14:00:40 +02:00
Meta Vens
0582321e2c Let a person be seen: nonverbal display by quote
An invocation may carry a display, the actor's own visible sentence, beside
or instead of its utterance. The kernel lowers it as speech's twin: an
AssertClaim and a Display whose room is re-derived at apply, landing
Seen { by } on everyone standing in the actor's place and on nobody else,
never over a channel. A speech-carrying entry given a display alone is an
address, not SpeechRequired; an empty display is EmptyDisplay; a placeless
actor has no room. The Interpreter gains a display tool quoting the visible
clause alone; the narrative capture carries both spans; the fold allows one
of each. Overheard rows and Projector knowledge rows say how a thing was
perceived: said by, seen doing, or known. The journal's event-shape rule
counts both lowerings. Kernel-only lowering: no patch tool, catalog stays
thirty. Schemas: world_state.consumer.v4, world_commit.consumer.v4,
controller_work.v14. Six tests pin the room, the silence, the refusals,
replay, the fold, and the interruption block.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 13:21:53 +02:00
Meta Vens
d4d7bc3c07 Give a world its brief: authored once, guidance on every lane
world_create.v3 carries a required brief, trimmed at ingress, stored on the
state and its snapshot, and projected as the guidance of the Projector,
Persona, Interpreter, operational, and grouped lanes and both authoring
prompts. The two narrative stages that rebuild prompts on resume carry it
(controller_work.v13). The smoke titles its world by the seed root label and
hands its brief to the world rather than to each seed session, so a vow can
no longer name a test fixture.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 03:04:02 +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
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
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
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
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
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
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
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
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
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
Meta Vens
697ed07081 Falsify the seed lane: five reproducers, eight bounds
Soul's pass over the seed producer. Tests only.

Five claims fail and are committed as ignored reproducers, each
naming its defect in the ignore reason:

- `collect_notes` follows a directory junction out of the vault
  root, and `strip_prefix` then dresses the escaped note in a
  vault-relative reference.
- `VaultError::Root` carries the server's absolute path into the
  owner's command receipt.
- `require_answer`'s `declares` reads declarations and evidence and
  not operations, so an operations-only patch passes the Active gate
  through `submit_seed`. The runner's phase check is the only gate,
  not the first of two.
- A `Goal` derives no boundary, so a world seeded to full strength
  out of goals alone activates with nothing for the elaborator.
- The mailbox never re-verifies principal evidence: a revoked
  session's evidence still commits as the owner.

Eight bounds hold and are asserted: an oversized note is never read
and the reader never writes, the receipt cap survives the link
fanout, `require_answer` still owns both phase rules, a session that
never submits stops at the shared round budget and its row stays a
fixed point, one derived id commits once, two landed sessions hold
two ids, `valid_seed_progression` refuses every backward transition,
and the custody probe counts a seed row.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 17:56:13 +02:00
Meta Vens
e73ef5c8f8 Prove the seed lane, and make an unadvertised operation impossible to ship
Eighteen checks. The ones that matter most: an operation the panel emits but
`operation_schema` does not name now fails a test rather than dying silently at
validation — that test fails on the parent commit because of
`world.advance_time`, which is why the schema gap was cut first. A v1 create
payload is refused at validation and a v2 payload missing its scale target is a
payload error; neither defaults, and neither creates a world.

The lane: one session commits one Draft patch whose subjects have controllers,
grants, positions, and goals, and a port built on anyone else's evidence is
`Unauthorized` — the owner is the author, and that is the reducer's rule and
not a convention. The deficit falls 6, 4, 2, 0 across a sweep and the fourth
step spends no inference; a commit that does not lower its row returns
`NoProgress` and stops rather than spinning. An Active world refuses the runner
before inference and refuses the port afterwards with `AnswerRequired`. A
citation the Vault did not return is dropped by `filter_evidence` and the
canonical fact naming it is refused; the same fact citing a retrieved reference
commits. A rejection reopens one derived command id with the kernel's complete
mismatch set and a fresh runner resumes it. A v9 row is refused at open beside
the pinned v8 and v7 refusals, and cross-lane writes conflict.

End to end: a two-root world is created, seeded to zero deficit, approved,
activated, elaborated against the boundaries the seed manufactured, ticked, and
replayed from its journal to the same state digest.

The Vault reader is tested with no kernel at all: references are vault-relative
paths, all three wikilink forms resolve to one note, an ambiguous stem takes the
shortest path, and `..`, an absolute scope, and a symlink out are refused at
open.

The ignored live smoke now seeds from a real Vault before it ticks. Its log is
still the deliverable, and it gains the per-round deficit and the subjects that
qualified.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 17:32:00 +02:00
Meta Vens
681f7063b1 Cut the phase clause and the fixed genesis, then land the Draft seed lane
The deletion line first. `world.advance_time` was emitted by the panel and
handled by `execute_world` but absent from `operation_schema`, so every
invocation died at validation; it is named now. `qualifies` loses its
`phase == Active` clause: `require_answer` already owns the Draft-answer
refusal, so the clause protected nothing and only pinned a Draft world's
deficit at the authored intent forever. `WorldScaleIntentRef::default()` and
the comment claiming world ingress authors no scale target are gone, along
with `world_create.v1` and `controller_work.v9`; `build_prompt`'s inlined
evidence/mismatch/tools tail collapses into one `prompt_body` owner.

Then the lane. `world_create.v2` carries a scale target and its jurisdiction
roots, which genesis declares beside the commons so `resolve_patch` can
resolve them. `SeedPort` reads a snapshot and commits one unanswered Draft
patch as `CallerId::Principal(owner)` — it has no approve, no activate, no
clock, no operator log, because those are not methods on the type. `SeedRunner`
is `ElaborationRunner`'s eleven stages with four substitutions: the phase gate,
the row selection, the identity namespace, and the submit port. It shares the
round loop, the tool application, the evidence filter, the cap check, and the
repair prompt verbatim. `world/vault.rs` is a read-only markdown reader over a
configured root: no network, no git, no vault name in core, and a spoiler tier
is a directory and therefore a query scope.

Termination is derived: the Draft deficit falls as seed patches land, so the
runner keeps no counter and a commit that does not lower its row is a fixed
point rather than a retry. `ApproveDraft` and `ActivateWorld` remain the only
path to Active.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 17:00:41 +02:00
Meta Vens
5e52943734 Add the local live smoke harness and record the first road run
An ignored runtime test drives the production tick driver, cover,
elaboration sweep, and clock against a real CodexConnector on a genesis
world and logs what the operator would see; the fixture now takes an
optional live controller. Runbook in notes/local-live-smoke.md; the
first run (three ticks, Persona spoke each tick, elaboration clean) is
recorded as evidence.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 15:42:46 +02:00
Meta Vens
8173c9cb77 Close the pass-10 Soul follow-ups: id-bump, fatal credentials, redaction, evidence cap, named refusals
- Bump controller_work to v9 so a checkpoint from before the
  session_command_id derivation change refuses to open, and document why
  no migration exists.
- Make a malformed GHOSTLIGHT_CONSUMER_CREDENTIALS file fail startup
  instead of warning into an empty registry.
- Redact ConsumerPatchDocument's secret from its Debug impl.
- Route the elaboration lane's draft through the same check_patch_caps
  the consumer ingress uses, closing the unenforced MAX_PATCH_EVIDENCE gap.
- Give the three item-cap refusals their own ConsumerRefusal variants
  instead of flattening them into TooLarge.
- Delete journal_replay_refuses_a_forged_consumer_row, now covered by
  soul_a_forged_decision_event_on_a_mirror_is_refused_at_replay.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 04:29:29 +02:00
Meta Vens
a1fd64513f Falsify the consumer ingress at the gates the pass named
Soul, pass 10. Nine tests, no production change.

Two claimed proofs were not proofs. `journal_replay_refuses_a_forged_consumer_row`
guards its whole first assertion behind `if let Some(event) = state.events.first()`
and the mirror fixture commits no decision, so that half never ran; it now runs
under a committed decision. The deficit half of the boundary test lands on
`AnswerNotDerived` because its fixture names no scale intent, so the answer rule
refuses before the ground rule is consulted; a scale-intent fixture derives a real
deficit and proves `Unauthorized`.

`session_command_id` moved to `CommandId::derived` and the two spellings hash
different byte streams. The new test pins the fact: a session checkpointed before
this pass resumes under a different command id.

The rest: the byte cap ahead of serde, every ingress refusal shape against
snapshot equality and replay, the mirror re-scoped onto a real committed decision
at `apply_effect`, a mixed owner/elaborator/consumer history replayed to the same
world, the pass-9 state schema string refused by name, derived-key determinism and
part unambiguity, and the registry's error and Debug paths carrying no secret.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 04:09:24 +02:00
Meta Vens
b4d1943585 Prove the consumer ingress at every gate it names
Twenty-three tests: the three Delvehold negative proofs (an ordinary
strategic wave cannot select an externally controlled mirror through
opportunities, adjacency, the cover, or a hand-built opportunity; a malformed,
oversize, stale, or structurally broken batch commits nothing; a consumer's
transfer into a Ghostlight subject is refused on the unbound endpoint while
the owner's identical transfer commits), the inbound-bytes-to-receipt run with
its journal replay, idempotent resubmission and key conflict, the capability
pairing, the confinement rows, the phase rule in both directions, the shared
decode bound, the transport gates on the new route, and replay equality with a
prior-schema store refused.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 03:53:37 +02:00
Meta Vens
e0c6f9cb22 Widen the patch author to a confined external consumer
Cut first: the elaboration lane's two draft caps and its own sha256-to-uuid
command-id spelling, `jurisdiction_covers` and `confine_to_jurisdiction` as
jurisdiction-only names and signatures, the flat empty-affordance invariant,
and the two total controller accessors that were keeping a fictional
controller so a reader could stay infallible.

Then widen. `PatchGround` names what a confined author may write: a place
subtree, or every subject bound to one consumer, derived from
`controller_assignments` at decision time and never carried on the wire.
`SystemCapability::Consumer` is admitted for `AdmitPatch` and nothing else, in
the same closed pairing `reduce` decides and `apply_effect` re-decides.
`ControllerAssignment::ExternallyControlled` is a subject the world can see,
relate to, and reason about but never puppet: no controller id, no mode, no
affordance, no opportunity, no adjacency, and a forged history in which it
acted fails replay.

`patch::decode_patch` is the one decode owner for a `WorldPatch` from a byte
source, with one byte cap applied to the frame before `rmp_serde` sees it and
three item caps checked before `resolve_patch`; the elaboration lane reads the
same two item constants it used to own privately.

`world/consumer.rs` owns the ingress and no world truth: one typed document
over one loopback route, canonical MessagePack in and out, a registry that
holds only SHA-256 digests, one derived command key, one port method, one
atomic verdict. The state and commit schemas bump with the caller tag; prior
stores are refused.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 03:25:45 +02:00
Meta Vens
962f7110e3 Close the intra-tick quarantine race: recheck after permit acquisition
Follow-up to the pass-9 follow-ups: deviation 2 flagged that
run_cover_tick's per-cell quarantine check only ran once, before a cell
contended for its permit, so a sibling already parked behind the permit
pool when another cell's fault raised the flag would still run once the
permit freed up. Add a second check right after permits.acquire(): with
the pool sized to one, the second cell cannot be granted the permit
until the first has fully returned, and for a faulting cell that means
the flag is already set by then, so the post-acquire check is
deterministic rather than a race. The top-of-function guard against a
later tick's cells stays as-is.

Renamed and extended
quarantine_raised_mid_tick_stops_every_later_tick_from_reaching_the_port
to quarantine_raised_mid_tick_stops_the_sibling_cell_and_every_later_tick:
same cross-tick assertion as before, plus permit pool 1 and an exact
(not >=1) count of one call in the first tick, proving the sibling cell
never reaches the port once the first cell's fault quarantines the
organ. Ran the new test 8x in isolation with no flakes.

world:: 281 passed, full crate 320 passed (unchanged counts — this
extends an existing test rather than adding one). cargo fmt -p
ghostlight-dungeon -- --check is clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 03:23:50 +02:00
Meta Vens
7aba2b4191 Cover the tick driver's permit gate, quarantine edge, and ordering
Pass 9 follow-ups 1-3 from Soul's review:

- Widen ControllerRunner::with_test_ports, InferencePort, and
  ControllerWorkStore to pub(crate) (still cfg(test)), plus the small
  cfg(test) seam needed to implement them outside world::controllers
  (fixture_prepared_inference, fixture_inference_output,
  InferenceFault::fixture_integrity_violation, and cfg(test) pub(crate)
  re-exports from world::mod). Two new runtime spec tests drive a real
  ControllerRunner over a counting/quarantining InferencePort and an
  in-memory ControllerWorkStore, proving the controller-permit semaphore
  never admits more concurrent run_cell calls than its pool, and that an
  integrity-violating fault quarantines the organ so no later tick's
  cells ever reach the port again.

- Extract drive_one_tick out of drive_cover_tick's loop body, narrowed
  the same way submit_clock_tick narrows the clock port: run_cover and
  submit_clock are opaque closures, never run_cover_tick or
  WorldMailbox::submit_clock by name. A new test drives it with a fake
  cover-runner over a real three-cell Cover (via a new cfg(test)
  fixture_controller_opportunities in world::mod) and a fake clock
  submitter, proving the clock is always submitted after every cell has
  run and that every cell in the tick carries the cover's one tick index.

- Delete cover.rs's merges_are_deterministic_in_subject_order: its name
  overclaimed determinism the test never checked. Its coverage-survives-
  a-permutation assertion was already proven by
  soul_b_the_cover_is_pure_and_the_attention_order_is_a_real_input; its
  other assertion (cell count is stable under permutation) was not, so
  that one assertion moved into soul_b's test instead of surviving under
  a misleading name.

Deviations from the literal brief, both load-bearing:

- Ruling 1 asked for "a cover of several cells" with
  GHOSTLIGHT_CONTROLLER_MAX_CONCURRENT = 2. The production world-create
  intent caps a genesis world at exactly two controller-bearing subjects
  (one narrative persona, one operational agent), so the real cover this
  test can derive tops out at two cells. Reusing that production ceiling
  meant not widening the kernel's private command types (CreateWorld,
  CallerId, CommandEnvelope) past what this pass already opens, which
  seemed like the more coherent boundary to hold. The permit pool is set
  to one instead of two so the cap is still load-bearing: with two cells
  and a pool of two, the assertion would be vacuously true.

- Ruling 1's second scenario wanted "the remaining cells are skipped"
  within one tick. Tracing run_cover_tick's per-cell task shows the
  quarantine check runs once, before a cell ever contends for its
  permit; every cell in one JoinSet is spawned and gets that check
  before any of them can reach the port and set the flag, so no cell of
  the *same* tick is ever actually skipped by it — only a cell of a
  *later* tick is, via run_cover_tick's own top-of-function guard. The
  test proves that real mechanism (a second tick makes zero further
  calls) rather than an intra-tick race the driver does not resolve,
  which would have been flaky either way it landed.

world:: 281 passed (baseline 282, minus the one deleted test).
Full crate 320 passed (baseline 318, minus one deleted plus three new).
cargo fmt -p ghostlight-dungeon -- --check is clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 03:20:19 +02:00
Meta Vens
8aebfb6811 Falsify pass 9's cover, resume, and replay claims
Tests only. Ten additions across the two organs pass 9 landed.

world/cover.rs, pure over `derive_cover` with no kernel:
- the rotation guarantee at the design profile, not at a toy size: 2,400
  subjects, B=240, C=24, U=36 gives R=110 and the window closes in exactly
  ceil(2400/110)=22 ticks, and not in 21;
- urgency reads the attention order while the window reads ascending id
  order, proved on a fixture whose two rankings are reversed against each
  other, so no third ranking can explain the result;
- the cover is pure for identical inputs, and the attention order is a real
  input rather than an incidental one;
- every cell in one cover carries that cover's tick index;
- derived cell and constituent keys are collision-free across a full sweep
  of ticks;
- the cap yields when component shapes cannot pack inside the budget even
  though token capacity would allow it, and the flag tracks the partition
  rather than the subject count.

world/controllers.rs:
- an uncoupled grouped cell commits every constituent, which is what makes
  the coupled cell's refusal a fact about coupling instead of a per-cell
  quota or a moved world revision;
- the refused constituent's scope digest really did change;
- the other grouped checkpoint resumes: a retryable fault parks the row at
  AgentInFlight, and the resumed run re-derives the same constituent ids,
  infers once, and commits once;
- a grouped tick replays from genesis to an identical state digest and an
  identical event sequence.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 02:48:38 +02:00
Meta Vens
f013c1d22f Trim the cover's unread surfaces and report the tick
Cut the accessors, fields, and re-exports the pass added without a live
consumer: `Cell::id`, `Cell::tick`, `Cell::resolution`, the grouped
capture's receipt vector, and the `CoverBudgetError` re-export. The
resolution a row records is derived from the row's own shape, and the
capture's receipts were a second copy of what its outputs already carry.

In exchange the tick driver now reads what it runs. One debug line per
coarse cell names the cell, its resolution, how many turns it offered,
how many committed, and how many gaps it produced. The gap between
offered and committed is the number that says a batch buys one
inference and never one admission rule.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 02:32:14 +02:00
Meta Vens
71d8c536b0 Give the world a tick
Step 6 pass 9: the budgeted connected cover. Every active subject is in
the cover every tick; the budget decides at what resolution, never
whether. A singleton cell keeps the detail path and its membrane; a
grouped cell is one inference over N labeled, never-unioned constituent
views, each lowered into an ordinary one-opportunity submission verified
by its own opportunity. The kernel never sees a cell.

`world/cover.rs` owns the partition and nothing else: pure over
(world, now, opportunities, agency graph, budget), disposable, dropped at
end of tick. The fairness guarantee is arithmetic over the clock and a
total subject order rather than a second persisted memory. Adjacency is a
kernel-side projection wider than any subject view, reachable from the
tick driver and from no prompt builder.

Cut before adding: the runner's exclusive mutex and its three lock sites,
`controller_slot.take()` as the quarantine mechanism, `select`'s equation
between representation and controller mode, and pass 7's standalone
`advance_world_clock` task, which `drive_cover_tick` absorbs so one owner
holds the tick's cadence.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 02:23:00 +02:00
Meta Vens
b5443d55ea Close pass 8's five Soul follow-ups
- variant_content now spells a payload-less branch, so an effect slot's
  bounds schema offers `none` alongside `quantity` and `cost` and a world
  author can declare an unbounded slot. The falsifying test is inverted
  and renamed.
- composite_example is now a projection of composite_schema, walked
  generically (first branch of every sum, minimum of every bounded
  integer, a fixed canonical string, an empty list where allowed)
  instead of a hand-written third spelling. The round-trip test checks
  every schema-derived example against serde directly, so the standalone
  tie test pinning the one known disagreement is redundant and deleted.
- The mod.rs test claiming to refuse a forged elaborator row never forged
  anything; journal.rs already carries a real version that rewrites the
  recorded caller and re-seals the commit digest. Deleted the duplicate
  with the lying name.
- Added every_declaration()/every_component_op(), each built from an
  exhaustive match over their enum that fails to compile on a new
  variant, and a test proving every exemplar is accepted by exactly one
  PATCH_TOOLS entry with counts matching the catalog (7, 28, 37) — a new
  variant can no longer ship by mapping onto an existing tool.
- Removed the dead-code-only assert_declarations_exhaustive/
  assert_operations_exhaustive helpers and their unfulfilled
  #[expect(dead_code)]; their logic now lives in the exemplar builders
  above and is actually exercised.

world:: 250 passed (was 252; two tests deleted as redundant/duplicate,
one test replaced net-neutral). cargo fmt clean. No new warnings.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 01:55:41 +02:00
Meta Vens
3ed3868939 Falsify pass 8's boundary elaboration lane
Soul's tests only. Fifteen checks against the landed shapes: the
confinement predicate past its declaration lane, the fifth body the
capability rule refuses, disjoint boundaries committing independently
with a spent answer refused, an unsatisfied answer through the
elaborator lane, canonical provenance surviving the ungating of
`EvidenceRef::new`, a forged elaborator commit row re-sealed against its
own chain digest so replay's authority half must answer, the emitted
tool schema against the decoder branch by branch, the round-trip
examples pinned as a second spelling, the unbounded effect slot the
catalog cannot express, the elaboration runner's resume-and-repair round
trip, a clean jurisdiction spending no inference, `controller_work.v7`
refusing `v6`, and the custody discriminator's lane.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 01:35:02 +02:00
Meta Vens
2252370af3 Let the world grow its own structure
Pass 8: the in-kernel elaborator loop, the Elaborator caller, and the
derived patch-tool catalog.

`SystemCapability` becomes internally tagged and gains
`Elaborator { jurisdiction }`. `require_patch_author` decides who may
`AdmitPatch` and returns the caller's confinement;
`confine_to_jurisdiction` decides what that caller may write. Both are
called from `reduce`'s arm and re-called from `apply_effect`'s, so replay
re-decides authority for free. `require_owner` leaves the `AdmitPatch`
path and survives for `ApproveDraft` and `ActivateWorld`.

Boundaries are covered transitively through `covers_place`; deficits are
covered by exact key equality, because a subject under nested roots counts
toward both roots' targets and a parent answering a child's row would
reduce two targets with one subject.

`patch.rs` gains `PATCH_TOOLS` and its two generators: the tool surface is
the variants a non-genesis patch may carry, with every always-refused
choice removed rather than exposed. `world/tool_schema.rs` is the one
emitter for every model-facing schema in the tree; the interpreter and
action catalogs move onto it and their `json!` literals are deleted.

`world/elaboration.rs` owns the loop: it selects a derived answer,
retrieves evidence, accumulates a draft across tool calls, submits through
`WorldMailbox::submit_elaboration`, and repairs the same draft from the
complete mismatch set. The draft is re-derived from persisted inference
outputs; only `last_mismatches` is stored, because it came from the kernel
rather than the model. Session identity is derived from the answer's
digest, so a crashed loop resumes the same store row.

Schemas bump to `elaboration.v1` and `controller_work.v7`; a prior store
is refused.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 01:10:45 +02:00
Meta Vens
5874fc232a Close pass 7's four Soul follow-ups
- verify_state_shape now enforces the 1..=MAX_ROUTE_COST span bound on a
  routine's period, the same bound TickMinutes::new enforces, closing the
  serde-transparent forgery gap. Inverted and renamed the falsification test
  to soul_a_zero_period_is_refused_by_the_shape_check.
- Extracted advance_world_clock's per-tick body into submit_clock_tick, a
  narrow port over submit_clock, and added a test that captures the
  submitted span without driving tokio's timer.
- Removed the dead top-level PreconditionRef/PressureSourceRef imports in
  world/mod.rs; clock_tests keeps its own.
- Renamed JurisdictionKey::Unplaced to Uncovered and fixed the scale deficit
  to count a positioned subject under no jurisdiction root in that row
  instead of dropping it, with a test asserting the row counts sum to the
  subject count.

cargo test -p ghostlight-dungeon --bin ghostlight-dungeon world:: -> 217 passed
cargo test -p ghostlight-dungeon --bin ghostlight-dungeon -> 251 passed
cargo fmt -p ghostlight-dungeon -- --check -> clean
2026-09-05 00:37:55 +02:00
Meta Vens
1852ddd896 Soul pass 7: bind the boundary digest to its own structure
A tick leaves an UnelaboratedDestination's digest where it is, and
closing the route it names moves it while the predicate still holds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 00:23:00 +02:00
Meta Vens
5771ce6c49 Soul pass 7: falsify the clock, answers, pressure, and the store shape
Ten falsification tests over pass 7's claims: the attention order's id
tiebreak under every input permutation, the Active answer ruling
(component-only commits, evidence must answer, the gate precedes the
resolver, a satisfied answer clears its boundary), pressure saturation
and underflow, a clock-only tick moving no scope digest, scale intent as
genesis-only, discharge of a foreign or discharged key, one routine roll
per command, derive_motion purity plus tick replay identity, the replay
gate over a forged motion, and the TickMinutes store gap.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 00:21:58 +02:00
Meta Vens
2a8cec6b0a Falsify the clock, commitments, pressure, boundaries, and scale
Twenty-two tests over the pass-7 surface: the tick's three stages and its
re-derivation at apply and at replay, the clock capability's single admitted
body, every commitment shape check as one complete sorted set, the attention
order's totality and its refusal to filter, boundary derivation and the
answer-must-be-satisfied rule, and the scale deficit that only admitted
subjects reduce.

Two defects the tests found and this commit fixes: PressureSource mixed struct
and newtype variants under an internal tag, which cannot serialize a scalar
newtype, so it is adjacently tagged; and Motion carried an is_empty no caller
outside a test used.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 00:07:49 +02:00
Meta Vens
d7fba90c71 Give the world a clock, commitments, pressure, boundaries, and scale
Ontology widening pass 7. The world acquires a fictional clock advanced by
exactly one command body; promises acquire a due date; an unmet promise becomes
stored pressure; pressure orders whose turn it is without deciding whether a
turn exists; and the places the world has not grown into become derivable
rather than noticed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 23:54:50 +02:00
Meta Vens
54e9d39113 Close the six pass-6 Soul follow-ups
- audience() no longer folds a channel's controller into its own
  audience: a controller may still can_broadcast() from outside its
  reach, but it gains no fan-out knowledge and is not a CanReach target
  unless it actually stands inside the declared reach.
- Correct the "one home" doc claims: the statement lives in
  facts[fact].statement (the readable home) and is duplicated inside
  the committed AssertClaim effect as a replay witness only; no
  projection reads the event copy.
- ControllerRunner now holds a narrow ControllerPort instead of a whole
  WorldMailbox, forwarding exactly the five requests a controller lane
  makes. operator_log has no method on that type, so it is a compile-time
  boundary rather than a pub(crate) convention.
- Document why resolve_patch's candidate knowledge map safely skips
  modeling Communicate's fan-out (AuthoredSource has no Told variant),
  and pin that invariant with a test on the one ComponentOpKind lowering
  site that could otherwise drift.
- Add a test proving a stale controller_work.v4 row is refused at open.
- Length-prefix derive_id's discriminator so Some("") and None (and any
  other handle/discriminator split) cannot collide; derived IDs change
  as a result, which the v5 schema bump already requires callers to
  accept.

Baseline 178 world / 213 crate; now 181 world / 216 crate, cargo fmt
clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 23:50:47 +02:00