Commit graph

1,060 commits

Author SHA1 Message Date
Meta Vens
038632d1dd Align remaining doc lines with titled lenses and Session Zero
The memory steward found four leftover lines. The plan and the ontology
doc now describe elaborators as lensed sessions, not eight fixed
instances. The handoff no longer says there is no Session Zero. The
ontology doc notes that Session Zero stops declaring the human at the
genesis commons.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 22:34:39 +02:00
Meta Vens
25ff91f08d Restore the titled elaborators as lenses and design Session Zero
The eight titles (Patina, Charter, Ledger, Hearth, Tangle, Veil, Ember,
Numen) were deleted with their titled verifier and quotas, and no operator
decision covered them. The ontology doc and evidence record 6 now say the
tribunal was the verifier and the quotas, not the titles. A new correction
record carries the history and the 2026-09-15 direction.

Session Zero is the player's path through a world's Draft phase. The player
chooses a Vault, describes the experience, and sets title weights. The seed
lane sketches the world and the player chooses a start. A swarm then draws a
title by weight for each session and elaborates by travel distance from the
player, before and after activation. It is plan step 15, and its fifth pass
closes the playtest gate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 22:33:21 +02:00
Meta Vens
705d298f91 Make the local playtest the next gate; correct stale map claims
The playtest gate goes ahead of the outbound consumer batch. The handoff
defines it and lists the walls found in source. The map no longer claims
co-op for up to eight players on the rebuilt kernel. It points the economy
spec at GameCult/Njordr and says CultCache comes from CultLib, not a
vendored copy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 22:10:52 +02:00
Meta Vens
a306feda4d Clarify connector constraint in map
The Claude SDK sidecar is the only live provider. Backends share one
standardized CultNet connector API with Epiphany; CodexConnector is isolated
and not live here; a generic OpenAI-compatible connector gates third-party use.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 23:06:40 +02:00
Meta Vens
cc016ffb55 Name CultLib as the home of the TypeScript CultNet client
The contract doc still pointed at the standalone GameCult/cultnet-ts repo,
which is being removed. The client is the cultnet-ts package in CultLib.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 16:47:39 +02:00
Meta Vens
59bbe85875 Keep the world elaboration record and first-generation world fixtures in Ghostlight
The elaborator swarm's GitHub branches were removed after verified bundling,
but its output is still wanted: the operator will integrate ideas into the
lore vaults as time allows. The durable record now lives in the repository
that owns the obligation instead of a Codex task folder and deleted branches.

experiments/elaboration/ carries the reviewed additions packet (A2 and D1-D3
approved, A1 demoted, K1/K2/Z1-Z3 pending), the per-world reviews, the
2026-09-05 shortlist with its 20 full candidates, and the 92 per-pass idea
ledgers, with a README naming where the bundled vault additions are and how
to restore one. The four reviewer-accepted first-generation world fixtures
return from their deleted codex/world-* branches to examples/ and
prompts/image-generation/ beside the existing fixtures. The map and handoff
point at the new home.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 16:35:03 +02:00
Meta Vens
22937b71f0 Replace vendor/cultcache-py submodule with published PyPI dependency
CultLib's packages/cultcache-py is now published as cultcache-py==0.2.0 on
PyPI (MIT), so the standalone GameCult/cultcache-py fork that Ghostlight
vendored as a submodule is retired. Symbol-for-symbol and on-disk-format
verification against the pinned submodule commit 9b4bbb8 found no
incompatibility: every symbol tools/ghostlight_state_store.py imports
(CultCache, JsonLinesBackingStore, define_document_type) and every method it
calls kept its signature, and JsonLinesBackingStore's on-disk JSONL envelope
fields and sort order are unchanged, confirmed by a real round-trip against
Ghostlight's production state/ghostlight-state.cultcache.jsonl.

tools/ghostlight_state_store.py no longer pushes vendor/cultcache-py/src onto
sys.path; it imports cultcache_py directly. Since Ghostlight's session
bootstrap previously depended on the submodule needing no install step,
tools/requirements.txt now pins cultcache-py==0.2.0 and npm run
state:bootstrap (pip install -r tools/requirements.txt, idempotent) runs as a
pre-hook ahead of state:status and state:prepare-compaction; AGENTS.md's
Session Bootstrap protocol and Useful Commands section call it out explicitly
for the direct ghostlight_state.py CLI invocations that don't go through npm.

Verified for real: `python tools/ghostlight_state.py status` and `python
tools/ghostlight_prepare_compaction.py`, run from a venv with cultcache-py
installed from PyPI, against Ghostlight's actual production state file (22 ok
findings, 2 pre-existing unrelated warnings, exit 0 both times).

Idunn's deployment/idunn/recipe.toml never declared vendor/cultcache-py in
required_gitlinks (only vendor/eve) and its build/test/service steps never
touch Python state tooling, so no recipe change is needed for admission.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-13 16:08:19 +02:00
Meta Vens
9574bf53d7 Point the economy spec at GameCult/Njordr
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 18:29:10 +02:00
Meta Vens
78e54554b8 Njordr: discovery as a producer policy, retooling as a recipe, catalog generation plan
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 18:15:15 +02:00
Meta Vens
4d1040ece0 Njordr: sources are finite deposits, materialized as mining
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 17:58:46 +02:00
Meta Vens
cc85db847c Njordr: roles are filled by lots, properties may be curves over conditions
The heat spreader is a role; copper, a vapour-chamber part, or anything
else fills it by its properties alone. A property value may be a curve over
an operating condition, so a part that fails hot fills a cool recipe and
not a hot one. No optional roles, no alternate recipes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 17:44:08 +02:00
Meta Vens
b6c2ccd697 Njordr: demand as rates, derived intermediate demand, admission over provenance, producer policies
Player activity moves prices both up and down the recipe graph: consumption
is a lot sink so floods find a floor; producers' planned inputs are derived
demand so crashes propagate to inputs; markets admit or refuse lots by
provenance so a diverted shipment re-enters through a black market; corners
are bounded by the scoring rule and answered by supply. Seeding guarantees
every market is served or marked unserved as a fact.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 17:31:42 +02:00
Meta Vens
c077174264 Njordr: market maker settled, no Delvehold surface to cut against
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 17:19:49 +02:00
Meta Vens
e2aeb972bd Njordr owns the economy whole, Delvehold interior included
Greathold recipes, facilities, inventories, orders, and prices are Njordr
lots, recipes, markets, and quotes; Delvehold civic organs author policy as
demand rows and carrier policies. Unity embedding settled as the Rust engine
over a C ABI. The integration note's Delvehold-owner line is marked
superseded.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 15:54:23 +02:00
Meta Vens
d415101fa1 Njordr: engine and shell, seeded single-player mode
The engine is a pure deterministic step function over a state document and
an exogenous event stream it cannot attribute; the shell is the daemon.
Single-player Aetheria hands over the engine state and an event script,
never precalculated prices, so player choices stay live against the story.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 15:45:24 +02:00
Meta Vens
ced6d89b98 Njordr: name the repository spelling
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 15:28:50 +02:00
Meta Vens
b1167cce67 Njordr: shipments, two consumer profiles, dimensions settled
Economic flow is materialized as shipments a player can meet. Aetheria's
interspersed profile adds materialization, interception against a revision,
and per-zone cadence; Delvehold keeps the boundary profile. One ledger, one
quote function; only the subscription grain differs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 15:28:26 +02:00
Meta Vens
6133461d8e Spec the GameCult economy daemon
Dimensions, materials, lots with provenance, recipes as roles, products as
property vectors; markets as Ghostlight mirrors; prices as a pure function
of committed state; both directions through the consumer contract. The
economy owner is the consumer that owns a world's market mirrors.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 15:04:28 +02:00
Meta Vens
d5cbc45a87 Map: name the span rule beside its schema pin
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 14:34:35 +02:00
Meta Vens
4406008b3c Record run 12: every cited span lands
Runbook row 12, evidence record, schema pins in map and handoff moved to
controller_work.v15.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 14:34:20 +02:00
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
d0c047f375 Record run 11: the interrupted cell, logged per cell
Runbook row 11 and the interrupted-cell section, evidence record, map and
handoff advanced: next is the outbound consumer batch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 14:12:26 +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
4ef5f71397 Record run 10: the first displays and the first live interrupted cell
Runbook row 10 and the interrupted-cell section; plan step 14 landed;
evidence record; map, handoff, and scratch reconciled (schemas v4/v14,
thirteen components, next action is the per-cell interrupted log line).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 13:34:09 +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
29ecae7613 Give plan step 13 its own heading
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 12:59:45 +02:00
Meta Vens
5885eff5b6 Map step 14: nonverbal display by quote
A visible act becomes Seen { by } knowledge on the co-located, kernel-lowered
from the invocation beside speech, quoted by the Interpreter as the visible
clause alone. Intent stays private. Authority map, cut line, budgets,
verification, and the three operator decisions are in the plan; map,
handoff, and scratch point at it. Not built.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 12:51:33 +02:00
Meta Vens
b29e7e5b57 Record run 9: a world with a name and a premise
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 03:13:52 +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
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