Commit graph

1,046 commits

Author SHA1 Message Date
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
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