2026-09-01 14:38:01 +02:00
|
|
|
|
# Ghostlight Implementation Plan
|
|
|
|
|
|
|
|
|
|
|
|
## Objective
|
|
|
|
|
|
|
|
|
|
|
|
Rebuild Ghostlight Dungeon as one legible world machine: one lifecycle, one
|
|
|
|
|
|
typed ontology, one command ingress, one authority derivation path, and one
|
|
|
|
|
|
atomic persistence owner. Preserve narrative roleplay where it matters and use
|
|
|
|
|
|
direct operational cognition where it is a better fit, without granting either
|
|
|
|
|
|
mode additional authority.
|
|
|
|
|
|
|
|
|
|
|
|
## Current mechanism
|
|
|
|
|
|
|
2026-09-02 16:23:40 +02:00
|
|
|
|
Pushed commit `6bb6869` makes the sealed mailbox/kernel architecture the crate
|
|
|
|
|
|
and executable runtime identity. Pushed commit `13d5136` places app sessions,
|
|
|
|
|
|
the world journal, and controller custody behind the one vendored CultCache
|
|
|
|
|
|
implementation and removes the duplicate persistence dependency. The committed
|
|
|
|
|
|
daemon tree contains no pre-rebuild Session Zero, legacy kernel, scheduler,
|
|
|
|
|
|
assessor, verifier/reconciliation, or legacy-transition module path.
|
|
|
|
|
|
|
|
|
|
|
|
Production has not crossed that source boundary. Yggdrasil still runs legacy
|
|
|
|
|
|
release `a4080d4` under an enabled `Restart=always` unit and a legacy state
|
Re-anchor contract citations on CultLib 85f7024
Every live CultNet contract citation named `f4ab237`, which is not an ancestor
of CultLib main and survives only on the retiring standalone repo's branch.
They now name CultLib `85f7024`, which is both CultLib's current main head and a
permanent ancestor of it, so the citation survives every future advance --
something a branch SHA in a repo queued for deletion could never do.
Verified rather than assumed. At `85f7024`, packages/cultnet-rs carries
IDUNN_LIFECYCLE_BRAKE_* distinct from IDUNN_DEPLOYMENT_BRAKE_*,
IdunnProcessWriteLeaseRecord, GameCultRuntimeCapability, OdinTopologyDisagreement
with disagreements() and into_undisputed_present separating presence from
readiness, a generation-bound IdunnRuntimeActivationRecord with its own
activation-proof signing purpose, and IdunnExpectedRoute refusing non-rudp://
endpoints for RUDP routes. All six cited contracts hold.
Two of the eight citations were instructions, not descriptions, and a mechanical
swap would have been wrong. map.yaml's next action and the handoff's next gate
both said to advance Odin to CultNet `f4ab237` -- advancing it deeper into the
repo we intend to delete. Both now say to repoint Odin off its vendored CultNet
fork onto CultLib `85f7024`, as Ghostlight did at `d0510ac`.
Evidence records keep their pre-consolidation SHAs. They are dated statements of
what was believed, and the new record is the key for reading them.
This closes the citation half of the cultnet-rs retirement gate. Odin's vendored
CultNet gitlink is now the last reader of that repo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 01:51:44 +02:00
|
|
|
|
root. CultNet through CultLib `85f7024` owns generation-bound activation,
|
Land the Idunn bootstrap rules and retire two superseded revisions
The system map and implementation plan carried uncommitted prose describing
Idunn's independent bootstrap, the Odin-outage freeze rule, and the nine-phase
durable transaction. That content is good and is landed here.
It cited CultNet `e542225` and Odin `d527d72`, which map.yaml and the handoff had
already advanced past to `f4ab237` and `65cf2b2`. A bare SHA bump would have been
wrong: both successors own strictly more. `f4ab237` adds routed RUDP incarnation
admission, and `65cf2b2` already lands the durable three-record transaction and
admitted-generation engine that this prose still described as the remaining cut,
along with the systemd isolation, Ready-provider selection, and protected
activation delivery it called uncommitted. The claims are corrected, not just
renumbered.
map.yaml carried the same superseded pair on its Idunn foundation line while its
contract line named the successors, so the canonical map disagreed with itself.
Evidence records keep their original revisions. That ledger says what was
believed when it was written, and editing it would falsify the history it exists
to preserve.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 01:26:08 +02:00
|
|
|
|
separate lifecycle-brake, process-write-lease, observed-capability, explicit
|
|
|
|
|
|
disagreement, and routed RUDP incarnation admission contracts. Odin through
|
|
|
|
|
|
pushed `65cf2b2` owns deterministic recipe and binding admission, exact source
|
|
|
|
|
|
freezing, sealed releases, Expected projection, the narrow native actuator
|
|
|
|
|
|
ports, and the durable three-record transaction and admitted-generation engine.
|
|
|
|
|
|
Remaining Odin work is Idunn projection, bounded RUDP transport, and stable-route
|
|
|
|
|
|
integration; the active move is to finish and prove those before one-way live
|
|
|
|
|
|
cutover.
|
2026-09-01 14:38:01 +02:00
|
|
|
|
|
|
|
|
|
|
## Authority map
|
|
|
|
|
|
|
2026-09-01 15:07:58 +02:00
|
|
|
|
- **Owner:** one sealed per-world `WorldKernel` owns its private aggregate,
|
|
|
|
|
|
reducer, ID allocator, and journal writer. The public crate boundary exposes
|
|
|
|
|
|
only create/open, immutable snapshot, command submission, and typed receipts;
|
|
|
|
|
|
it does not expose mutable `WorldState`, the canonical entity-ID allocator, reducer
|
|
|
|
|
|
entry points, or generic journal writes.
|
|
|
|
|
|
- **Inputs:** an authenticated `CommandEnvelope` with stable command ID, exact
|
|
|
|
|
|
world and expected revision, principal evidence, one closed command body, and
|
|
|
|
|
|
exact source receipts where needed. The kernel privately loads current state,
|
|
|
|
|
|
derives authority, and supplies deterministic clock or entropy. Human,
|
|
|
|
|
|
Interpreter, operational-agent, author, scheduler, and external-owner output
|
|
|
|
|
|
is proposal material only.
|
|
|
|
|
|
- **Outputs:** a rejection receipt changes nothing. An accepted command returns
|
|
|
|
|
|
one `WorldCommit`, including any reducer-issued ID mapping, and atomically
|
|
|
|
|
|
appends one digest-chained revision. A structurally valid command that lowers
|
2026-09-16 15:45:58 +02:00
|
|
|
|
to no factual, speech, lifecycle, or time mutation is refused with
|
|
|
|
|
|
`Mismatch::NoCanonicalChange` inside `PatchRejected` and does
|
2026-09-01 15:07:58 +02:00
|
|
|
|
not advance revision, change either digest, allocate IDs, or append a journal
|
|
|
|
|
|
row.
|
|
|
|
|
|
- **Derived state:** immutable snapshots and subject views, currently executable
|
|
|
|
|
|
affordance catalogs, revision-bound `DecisionOpportunity` values, scheduler
|
|
|
|
|
|
queues, translation gaps, projections, news, transcripts, model receipts,
|
|
|
|
|
|
checkpoints, coverage counts, and evaluations. Translation gaps may be kept
|
|
|
|
|
|
as inference/evaluation telemetry or motivate a later explicit author
|
|
|
|
|
|
proposal; they are not a `WorldState` component, event, or mutation.
|
|
|
|
|
|
- **Forbidden writers:** public state setters or ID issuers; direct reducer
|
|
|
|
|
|
callers; generic CultCache insert/append handles; schedulers or controllers
|
|
|
|
|
|
that assign authority, mint executable affordances, consume opportunities, or
|
|
|
|
|
|
commit; automatic translation-gap lowering; empty commits; the old Session
|
|
|
|
|
|
Zero publication path, aggregate Campaign writer, legacy transition
|
|
|
|
|
|
projection, alternate elaboration and mutation inputs,
|
|
|
|
|
|
verifier/reconciliation agents, recovery repair loops, and external consumer
|
|
|
|
|
|
callbacks.
|
2026-09-01 14:38:01 +02:00
|
|
|
|
- **Shared paths:** draft creation, approval, activation, player action,
|
2026-09-01 15:07:58 +02:00
|
|
|
|
autonomous action, author expansion, time, travel, imports, reload, and
|
|
|
|
|
|
administration use create/open/snapshot/submit and the same private
|
|
|
|
|
|
derive/reduce/commit primitive. Controller harnesses submit an exact typed
|
|
|
|
|
|
proposal as an ordinary command. Recovery opens and verifies the same journal;
|
|
|
|
|
|
it cannot call a repair reducer.
|
|
|
|
|
|
- **Cut line:** the rejected public ontology stays deleted. Translation-gap
|
|
|
|
|
|
records stay outside canonical world state. No public constructor or helper
|
|
|
|
|
|
may issue a canonical ID, mutate an aggregate, invoke the reducer, or write the
|
|
|
|
|
|
journal. Obsolete writers are deleted or reduced to read-only evidence before
|
|
|
|
|
|
their replacement path is called complete.
|
|
|
|
|
|
- **Verification layer:** compile-time visibility proves that an external crate
|
|
|
|
|
|
cannot invoke the ID allocator, mutate `WorldState`, call the reducer, or
|
|
|
|
|
|
obtain the journal writer. Focused black-box tests prove that a caller-supplied
|
|
|
|
|
|
unknown ID cannot become canonical, plus atomic rejection, no-op non-commit,
|
|
|
|
|
|
reducer-only ID allocation, exact restart/digest recovery, translation-gap
|
|
|
|
|
|
non-authority, and opportunity rejection when revision, controller, scope, or
|
|
|
|
|
|
executable affordance does not match.
|
2026-09-01 14:38:01 +02:00
|
|
|
|
|
|
|
|
|
|
`SessionZeroKernel` is no longer a target owner; Draft is a `WorldState` phase.
|
|
|
|
|
|
Aggregate Campaign state is no longer an owner; aggregate views are derived
|
|
|
|
|
|
from typed components. A checkpoint is no longer a repair authority; it is a
|
|
|
|
|
|
projection of committed history.
|
|
|
|
|
|
|
|
|
|
|
|
## Invariants
|
|
|
|
|
|
|
|
|
|
|
|
1. Every world mutation passes through one deterministic reducer and one
|
|
|
|
|
|
atomic compare-and-swap commit.
|
|
|
|
|
|
2. Authority is derived inside the kernel from canonical membership,
|
|
|
|
|
|
governance, custody, jurisdiction, and controller scope.
|
|
|
|
|
|
3. Each autonomous decision scope has exactly one controller. Cognition mode
|
|
|
|
|
|
changes representation only; there is no fallback or double action.
|
|
|
|
|
|
4. Narrative interpretation is total. Persona prose is preserved; faithful
|
|
|
|
|
|
typed proposals and exact translation gaps account for the turn. The
|
|
|
|
|
|
Interpreter does not accept, reject, or commit world state.
|
|
|
|
|
|
5. Structural invalidity is rejected deterministically before persistence and
|
|
|
|
|
|
without a model verifier. A failed batch changes nothing.
|
2026-09-05 23:42:38 +02:00
|
|
|
|
6. Private knowledge remains scoped until an explicit communication command or
|
|
|
|
|
|
a witnessed event under the subject's place changes it.
|
2026-09-01 14:38:01 +02:00
|
|
|
|
7. External consumers retain sovereignty. Ghostlight views, proposals, and
|
|
|
|
|
|
acknowledgements cannot mutate external-owned state.
|
2026-09-04 16:15:58 +02:00
|
|
|
|
8. Sparse causal sufficiency governs structural validity; counts and
|
|
|
|
|
|
qualitative review remain evaluation evidence and never gate admission.
|
|
|
|
|
|
Elaboration additionally pursues the seed's authored `WorldScaleIntent` as
|
|
|
|
|
|
a derived scale deficit; cover budget is the deliberate choke on
|
|
|
|
|
|
simulation attention.
|
2026-09-01 14:38:01 +02:00
|
|
|
|
9. Restart reconstructs the exact committed world and idempotency history; a
|
|
|
|
|
|
recovery loop cannot repair or reinterpret it.
|
2026-09-01 15:07:58 +02:00
|
|
|
|
10. Canonical IDs are allocated only while a complete private reduction is
|
|
|
|
|
|
being admitted. Rejected and no-effect commands cannot consume or reveal
|
|
|
|
|
|
them.
|
|
|
|
|
|
11. Controller assignment and affordance grants are canonical aggregate state.
|
|
|
|
|
|
Current executability and decision opportunities are deterministic
|
2026-09-04 16:03:42 +02:00
|
|
|
|
derivations bound to a scope digest over the components they read; a
|
|
|
|
|
|
proposal whose scope digest is unchanged commits at a later revision, and
|
|
|
|
|
|
one whose scope changed is rejected. A scheduler can order those
|
|
|
|
|
|
opportunities but cannot mint their authority.
|
2026-09-01 14:38:01 +02:00
|
|
|
|
|
|
|
|
|
|
## Implementation sequence
|
|
|
|
|
|
|
2026-09-01 18:11:25 +02:00
|
|
|
|
### 1. Seal the aggregate boundary — landed
|
2026-09-01 14:38:01 +02:00
|
|
|
|
|
2026-09-01 15:07:58 +02:00
|
|
|
|
Establish `WorldKernel` with a private aggregate and private journal child before
|
|
|
|
|
|
publishing ontology types. Expose only create/open, immutable snapshot, submit,
|
|
|
|
|
|
and typed receipts. Make canonical state mutation, ID allocation, reduction,
|
|
|
|
|
|
and CultCache writes unreachable from outside the owning module. Prove the
|
|
|
|
|
|
visibility boundary and the one-writer transaction seam before widening the
|
|
|
|
|
|
state vocabulary.
|
2026-09-01 14:38:01 +02:00
|
|
|
|
|
2026-09-01 19:50:52 +02:00
|
|
|
|
### 2. Add the minimal private reducer — landed
|
|
|
|
|
|
|
|
|
|
|
|
`66ed2ec` implements the state and command vocabulary required for Draft
|
|
|
|
|
|
creation, approval, activation, one human action, and one autonomous action.
|
|
|
|
|
|
The aggregate owns controller assignments and affordance grants and derives
|
|
|
|
|
|
exact revision-bound opportunities. IDs issue only after complete seed
|
|
|
|
|
|
validation; rejected and empty reductions do not commit. Places, relations,
|
|
|
|
|
|
resources, external ownership, and other ontology components wait for a live
|
|
|
|
|
|
typed causal boundary.
|
|
|
|
|
|
|
|
|
|
|
|
### 3. Prove the private authority slice — landed
|
|
|
|
|
|
|
|
|
|
|
|
Eighteen focused tests exercise Draft creation, approval, activation, one human
|
|
|
|
|
|
decision, one NarrativePersona-controlled decision, and one
|
|
|
|
|
|
OperationalAgent-controlled decision through the same aggregate action command.
|
|
|
|
|
|
They cover exact controller scope and affordance matching, revision-bound
|
|
|
|
|
|
opportunity rejection, restart, immutable genesis, idempotency, serialized
|
|
|
|
|
|
mailbox ownership, cancellation, lost reply semantics, and forged-history
|
|
|
|
|
|
rejection. Controller mode changes representation only; it does not create a
|
|
|
|
|
|
second reducer path.
|
|
|
|
|
|
|
|
|
|
|
|
Live `NarrativePersona` integration still requires the Projector to emit prose
|
|
|
|
|
|
context, the Persona to return prose only, and the runner first to persist an
|
|
|
|
|
|
immutable receipt-bound Persona
|
2026-09-01 16:37:41 +02:00
|
|
|
|
turn. The Interpreter returns a completed report containing that exact
|
|
|
|
|
|
noncanonical source prose, zero or more typed proposals, and zero or more
|
|
|
|
|
|
translation gaps. Spoken words require a typed speech proposal. Invalid capture
|
|
|
|
|
|
spans and normal step exhaustion become exact unresolved-source gaps. Any
|
|
|
|
|
|
transport or dispatch fault before explicit finalization discards partial
|
|
|
|
|
|
captures and leaves the immutable source pending for a fresh attempt; it does
|
|
|
|
|
|
not become semantic interpretation failure.
|
2026-09-01 14:38:01 +02:00
|
|
|
|
|
2026-09-01 19:50:52 +02:00
|
|
|
|
Live `OperationalAgent` integration receives only its permissioned typed
|
2026-09-01 14:38:01 +02:00
|
|
|
|
view and tools. Its proposals enter the same command path as narrative
|
|
|
|
|
|
proposals. It does not bypass projection privacy, authority derivation, or the
|
|
|
|
|
|
reducer.
|
|
|
|
|
|
|
2026-09-02 16:23:40 +02:00
|
|
|
|
### 4. Expose, wire, and cut — landed in source
|
|
|
|
|
|
|
|
|
|
|
|
Commits `6bb6869` and `13d5136` expose the replacement facade as the crate and
|
|
|
|
|
|
executable runtime identity while keeping mutable state, ID issuance,
|
|
|
|
|
|
reduction, journal access, and authenticated-caller construction sealed.
|
|
|
|
|
|
Startup creates or opens one replacement owner and spawns one mailbox; runtime
|
|
|
|
|
|
consumers receive only the mailbox-facing command and snapshot surface.
|
|
|
|
|
|
|
|
|
|
|
|
Draft creation, approval, activation, player decisions, and autonomous
|
|
|
|
|
|
controller proposals now enter that boundary. The committed daemon tree no
|
|
|
|
|
|
longer contains the old Session Zero, legacy kernel, scheduler, verifier,
|
|
|
|
|
|
reconciliation, or recovery-writer routes. There is no dual-write or live
|
|
|
|
|
|
compatibility router in source.
|
|
|
|
|
|
|
|
|
|
|
|
Resolution, elaboration, transcript/news projection, and external publication
|
|
|
|
|
|
remain future typed consumers at their causal boundaries. Model transport,
|
|
|
|
|
|
Vault retrieval, Heimdall identity, Eve/CultMesh projection, Idunn health, and
|
|
|
|
|
|
external adapters survive only where their ownership remains clean.
|
|
|
|
|
|
|
2026-09-02 21:15:38 +02:00
|
|
|
|
### 5. Integrate deployment actuation and cut production — active
|
|
|
|
|
|
|
|
|
|
|
|
The deterministic foundation is landed: target recipes and Idunn operator
|
|
|
|
|
|
bindings compile into private plans and sealed releases, and only a sanitized
|
|
|
|
|
|
Expected incarnation may leave that control plane. Shared CultNet contracts own
|
Land the Idunn bootstrap rules and retire two superseded revisions
The system map and implementation plan carried uncommitted prose describing
Idunn's independent bootstrap, the Odin-outage freeze rule, and the nine-phase
durable transaction. That content is good and is landed here.
It cited CultNet `e542225` and Odin `d527d72`, which map.yaml and the handoff had
already advanced past to `f4ab237` and `65cf2b2`. A bare SHA bump would have been
wrong: both successors own strictly more. `f4ab237` adds routed RUDP incarnation
admission, and `65cf2b2` already lands the durable three-record transaction and
admitted-generation engine that this prose still described as the remaining cut,
along with the systemd isolation, Ready-provider selection, and protected
activation delivery it called uncommitted. The claims are corrected, not just
renumbered.
map.yaml carried the same superseded pair on its Idunn foundation line while its
contract line named the successors, so the canonical map disagreed with itself.
Evidence records keep their original revisions. That ledger says what was
believed when it was written, and editing it would falsify the history it exists
to preserve.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 01:26:08 +02:00
|
|
|
|
generation-bound activation, service-signed Present health, observed
|
|
|
|
|
|
capabilities, explicit disagreement, and process-bound write leases. Odin
|
|
|
|
|
|
through pushed `65cf2b2` lands the exact source path, the narrow actuator ports,
|
|
|
|
|
|
dynamic systemd isolation, Ready-provider selection, protected activation
|
|
|
|
|
|
delivery, and the deployment engine that replaced the command-owner stub.
|
|
|
|
|
|
|
|
|
|
|
|
That engine persists exactly three control record types: one command with
|
|
|
|
|
|
a frozen target order, one crash-resumable transaction per target, and one
|
|
|
|
|
|
CAS-owned admitted generation per target. The transaction phases are Sealing,
|
|
|
|
|
|
Starting, Warming, Fencing, Leasing, AwaitingReady, Routing, Committing, and
|
|
|
|
|
|
Complete; stateless targets skip only fencing and leasing. Activation is
|
|
|
|
|
|
prepared without starting a process, its public identity is persisted, and only
|
|
|
|
|
|
then may the prepared process start. A credential or unit with no persisted
|
|
|
|
|
|
activation owner is an orphan and cannot be adopted. Committing atomically
|
|
|
|
|
|
replaces the exact incumbent and completes the transaction.
|
2026-09-02 21:15:38 +02:00
|
|
|
|
|
2026-09-02 21:31:52 +02:00
|
|
|
|
Targets own constrained launch declarations, never raw unit or container
|
|
|
|
|
|
templates. The operator binding selects the workload driver, which alone lowers
|
|
|
|
|
|
that declaration into process-manager configuration. Likewise, deterministic
|
|
|
|
|
|
plan validation retains exact commit, tree, recipe, and Gitlink facts but proves
|
|
|
|
|
|
neither ancestry, signatures, nor object custody; the narrow Idunn-owned source
|
|
|
|
|
|
driver must establish those facts before actuation.
|
|
|
|
|
|
|
2026-09-02 21:15:38 +02:00
|
|
|
|
The integrated path must publish Expected from the sealed plan and release,
|
|
|
|
|
|
record Idunn's observation of the exact runtime activation, and require the
|
|
|
|
|
|
service's signed Present health. A stateful candidate receives its process-bound
|
|
|
|
|
|
write lease before it opens writable state. Odin alone correlates Expected,
|
|
|
|
|
|
activation, and Present into Ready. Only after Ready may Idunn change stable
|
|
|
|
|
|
route membership and drain the incumbent. The deployment brake gates body
|
|
|
|
|
|
changes; same-release continuity remains separately owned and may be stopped
|
|
|
|
|
|
only by an explicit lifecycle brake.
|
2026-09-02 16:23:40 +02:00
|
|
|
|
|
Land the Idunn bootstrap rules and retire two superseded revisions
The system map and implementation plan carried uncommitted prose describing
Idunn's independent bootstrap, the Odin-outage freeze rule, and the nine-phase
durable transaction. That content is good and is landed here.
It cited CultNet `e542225` and Odin `d527d72`, which map.yaml and the handoff had
already advanced past to `f4ab237` and `65cf2b2`. A bare SHA bump would have been
wrong: both successors own strictly more. `f4ab237` adds routed RUDP incarnation
admission, and `65cf2b2` already lands the durable three-record transaction and
admitted-generation engine that this prose still described as the remaining cut,
along with the systemd isolation, Ready-provider selection, and protected
activation delivery it called uncommitted. The claims are corrected, not just
renumbered.
map.yaml carried the same superseded pair on its Idunn foundation line while its
contract line named the successors, so the canonical map disagreed with itself.
Evidence records keep their original revisions. That ledger says what was
believed when it was written, and editing it would falsify the history it exists
to preserve.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 01:26:08 +02:00
|
|
|
|
Idunn starts and recovers from its own durable admitted state. Odin is the first
|
|
|
|
|
|
managed semantic daemon, never an Idunn bootstrap dependency; initial Odin
|
|
|
|
|
|
admission is the sole graph-bootstrap exception and begins from a root-admitted
|
|
|
|
|
|
local binding. During an Odin outage, Idunn may preserve already-admitted
|
|
|
|
|
|
continuity and routes, but it may not start a graph-changing transaction or
|
|
|
|
|
|
promote without the exact frozen Odin Ready receipt for that runtime instance
|
|
|
|
|
|
and presence digest. Idunn never manufactures Ready locally.
|
|
|
|
|
|
|
2026-09-02 16:23:40 +02:00
|
|
|
|
Deploy CodexConnector first, then Ghostlight. Ghostlight's live cut archives the
|
2026-09-05 04:31:40 +02:00
|
|
|
|
entire legacy state root, creates a clean world-v3 root, and validates a complete
|
2026-09-02 16:23:40 +02:00
|
|
|
|
allowed live layout. After route, health, process, write lease, restart, and
|
|
|
|
|
|
negative legacy checks agree, delete the old units, releases, state roots,
|
|
|
|
|
|
acceptance debris, gamecult-ops target deploy programs, and local run
|
|
|
|
|
|
scaffolding.
|
|
|
|
|
|
|
2026-09-05 16:29:04 +02:00
|
|
|
|
### 6. Widen the ontology to the causal boundary — landed
|
Design the elaboration ontology
Run 115 died on a dangling reference, and the vocabulary is why. Legacy
transition.rs took caller-supplied SubjectRef{kind,id:String}, admitted
entities in two phases, and permitted mutations one at a time in a flat
batch, so a plausible-looking id was indistinguishable from a referent until
reduction. Reconciliation existed to repair that class and exhausted after
four steps. Depth lived in free strings, so the previous attempt to fix
shallowness grew model tribunals instead of structure.
The closed vocabulary: reducer-issued typed ID namespaces, draft-handle
references resolved inside one closed WorldPatch before any ID allocates,
twelve components that each constrain a decision, four derived CausalBoundary
kinds, and one AdmitPatch command shared by seed admission and boundary
elaboration. Twenty component kinds become twelve, eighteen mutation variants
become one primitive, five qualification and verification types become none,
and waves and quotas become derived boundaries.
Demotes the transition algebra doc to teardown evidence and slots the stage
into the implementation plan ahead of the deferred deployment cutover.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 15:39:17 +02:00
|
|
|
|
|
|
|
|
|
|
The operator ordered this stage ahead of the deployment cutover. Step 2 deferred
|
|
|
|
|
|
places, relations, resources, and external ownership until "a live typed causal
|
|
|
|
|
|
boundary" existed; this stage builds that boundary.
|
|
|
|
|
|
|
|
|
|
|
|
The closed vocabulary is `docs/architecture/ghostlight-world-ontology.md`: typed
|
|
|
|
|
|
ID namespaces, draft-handle references resolved inside one closed `WorldPatch`,
|
2026-09-05 16:29:04 +02:00
|
|
|
|
twelve decision-constraining components over twenty-seven named operations,
|
2026-09-04 16:01:27 +02:00
|
|
|
|
world-authored affordances (preconditions, effect slots, outcome bands) that
|
|
|
|
|
|
make character action a deterministic precondition-effect transition, four
|
2026-09-05 16:29:04 +02:00
|
|
|
|
derived `CausalBoundary` kinds (a draft `SeedRequest` is representable, not
|
|
|
|
|
|
inhabited: Draft answers nothing), scope-digest binding
|
2026-09-04 16:01:27 +02:00
|
|
|
|
for proposals, and one `AdmitPatch` command shared by seed admission and
|
2026-09-15 22:34:39 +02:00
|
|
|
|
boundary elaboration. An elaborator is one `OperationalAgent` loop whose tool
|
2026-09-16 19:56:20 +02:00
|
|
|
|
catalog is a projection of the operation set, shaped by a lens drawn from the
|
2026-09-15 22:34:39 +02:00
|
|
|
|
world's weights (step 15). The document carries its own
|
2026-09-04 16:01:27 +02:00
|
|
|
|
cut line, subtraction budget, build budget, and eighteen-proof verification
|
|
|
|
|
|
contract.
|
Design the elaboration ontology
Run 115 died on a dangling reference, and the vocabulary is why. Legacy
transition.rs took caller-supplied SubjectRef{kind,id:String}, admitted
entities in two phases, and permitted mutations one at a time in a flat
batch, so a plausible-looking id was indistinguishable from a referent until
reduction. Reconciliation existed to repair that class and exhausted after
four steps. Depth lived in free strings, so the previous attempt to fix
shallowness grew model tribunals instead of structure.
The closed vocabulary: reducer-issued typed ID namespaces, draft-handle
references resolved inside one closed WorldPatch before any ID allocates,
twelve components that each constrain a decision, four derived CausalBoundary
kinds, and one AdmitPatch command shared by seed admission and boundary
elaboration. Twenty component kinds become twelve, eighteen mutation variants
become one primitive, five qualification and verification types become none,
and waves and quotas become derived boundaries.
Demotes the transition algebra doc to teardown evidence and slots the stage
into the implementation plan ahead of the deferred deployment cutover.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 15:39:17 +02:00
|
|
|
|
|
|
|
|
|
|
Implementation order, each pass landing tests before the next begins:
|
|
|
|
|
|
|
2026-09-04 16:01:27 +02:00
|
|
|
|
1. Typed ID namespaces, `Ref<Id>` with draft handles, closed-patch resolution,
|
|
|
|
|
|
and the complete mismatch set; no components yet. Proves the Run 115
|
|
|
|
|
|
rejection shape against an otherwise empty ontology.
|
|
|
|
|
|
2. `Position`, `Route`, containment, topology admission, and scope-digest
|
|
|
|
|
|
binding for opportunities.
|
|
|
|
|
|
3. `Custody`, `Dependency`, conservation, and evidenced admission.
|
|
|
|
|
|
4. Affordance catalog: preconditions, effect slots, kernel-entropy band
|
|
|
|
|
|
selection, and the action pipeline replacing `Speak`-only invocation.
|
|
|
|
|
|
5. `Authority`, `Selection`, `Redress`, and institutional affordances.
|
|
|
|
|
|
6. `Knowledge`, `Channel`, `Fact` standing, and scoped-projection non-leakage.
|
|
|
|
|
|
7. `Commitment`, `Pressure`, obligation → pressure → opportunity flow, and
|
|
|
|
|
|
boundary derivation.
|
2026-09-05 16:29:04 +02:00
|
|
|
|
8. `AdmitPatch` with boundary and scale-deficit binding (Draft answers
|
|
|
|
|
|
nothing; `SeedRequest` stays uninhabited); `WorldScaleIntent` with
|
|
|
|
|
|
structural qualification and per-jurisdiction deficit derivation; and the
|
|
|
|
|
|
derived elaborator tool catalog.
|
Restore the scale target as design, not load fixture
The operator's design is detail first, then a ten-percent choke: generate
goal-bearing actors at every level, then budget cells so attention is scarce
and selection is forced. The mvp doc's teardown-era sentence calling the
2,400-subject profile "test data, not a production elaboration target"
contradicted that and is amended. The ontology now carries an authored
WorldScaleIntent with a structurally counted deficit that elaborators pursue
after boundaries, a budgeted connected cover every tick with singleton detail
cells and grouped coarse cells, partitioned views and per-constituent
attribution in groups, and debt rotation. Gestalt as canonical identity,
arena knowledge union, and semantic qualification stay deleted. Plan step six
is nine passes; two proofs added; evidence records the correction.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 16:14:44 +02:00
|
|
|
|
9. Budgeted connected cover: agency-graph partition under the cell budget,
|
|
|
|
|
|
singleton detail focus, grouped coarse cells with partitioned views and
|
|
|
|
|
|
per-constituent attribution, debt rotation. Scheduler-owned; the kernel
|
|
|
|
|
|
never sees a cell.
|
2026-09-05 04:31:40 +02:00
|
|
|
|
10. Consumer ingress: landed in pass 10. `SystemCapability::Consumer` is the
|
|
|
|
|
|
world's third `AdmitPatch` author, minted only by
|
|
|
|
|
|
`WorldMailbox::submit_consumer` after `world/consumer.rs` authenticates a
|
|
|
|
|
|
`ConsumerPatchDocument` against a `ConsumerRegistry` secret digest.
|
|
|
|
|
|
`require_patch_author`/`confine_to_ground` widen to `PatchGround::Consumer`,
|
|
|
|
|
|
confining a consumer to the subjects declared
|
|
|
|
|
|
`NewController::External { consumer }` (`ControllerAssignment::ExternallyControlled`,
|
|
|
|
|
|
no controller ID, no mode, no opportunity, no affordance). `patch::decode_patch`
|
|
|
|
|
|
is the one decode bound, shared by the elaborator lane and the consumer
|
|
|
|
|
|
lane, under `MAX_PATCH_BYTES`/`_DECLARATIONS`/`_OPERATIONS`/`_EVIDENCE`. The
|
|
|
|
|
|
transport is `POST /cultnet/world-patch`, loopback-only, canonical
|
|
|
|
|
|
MessagePack, schemas `ghostlight.consumer_patch.v0` and
|
|
|
|
|
|
`ghostlight.consumer_receipt.v0`; the state/commit schemas bumped to
|
|
|
|
|
|
`ghostlight.world_state.consumer.v1` / `ghostlight.world_commit.consumer.v1`
|
|
|
|
|
|
(`world-v3`). The outbound consumer response — a consumer reading its own
|
|
|
|
|
|
projection or an attributed proposal — is the next seam, not part of this
|
|
|
|
|
|
pass.
|
Design the elaboration ontology
Run 115 died on a dangling reference, and the vocabulary is why. Legacy
transition.rs took caller-supplied SubjectRef{kind,id:String}, admitted
entities in two phases, and permitted mutations one at a time in a flat
batch, so a plausible-looking id was indistinguishable from a referent until
reduction. Reconciliation existed to repair that class and exhausted after
four steps. Depth lived in free strings, so the previous attempt to fix
shallowness grew model tribunals instead of structure.
The closed vocabulary: reducer-issued typed ID namespaces, draft-handle
references resolved inside one closed WorldPatch before any ID allocates,
twelve components that each constrain a decision, four derived CausalBoundary
kinds, and one AdmitPatch command shared by seed admission and boundary
elaboration. Twenty component kinds become twelve, eighteen mutation variants
become one primitive, five qualification and verification types become none,
and waves and quotas become derived boundaries.
Demotes the transition algebra doc to teardown evidence and slots the stage
into the implementation plan ahead of the deferred deployment cutover.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 15:39:17 +02:00
|
|
|
|
|
|
|
|
|
|
The deployment cutover (step 5) is deferred behind this stage, not cancelled. No
|
|
|
|
|
|
world acceptance run may start while Yggdrasil serves the legacy body.
|
|
|
|
|
|
|
|
|
|
|
|
### 7. Contract verification
|
2026-09-01 14:38:01 +02:00
|
|
|
|
|
2026-09-05 18:32:20 +02:00
|
|
|
|
### 8. Seed producer — landed
|
2026-09-05 16:29:04 +02:00
|
|
|
|
|
|
|
|
|
|
Objective: a world that is alive at genesis rather than three subjects in one
|
|
|
|
|
|
room. The first live smoke (`notes/local-live-smoke.md`) proved the road and
|
|
|
|
|
|
showed that genesis yields thin prose because nothing authored exists to want
|
|
|
|
|
|
or be wanted.
|
|
|
|
|
|
|
2026-09-05 18:32:20 +02:00
|
|
|
|
What landed: `world_create.v2` carries `WorldScaleIntent` (targets and
|
|
|
|
|
|
jurisdiction roots) into the genesis patch itself, write-once; a v1-announcing
|
|
|
|
|
|
invocation is refused before any handler runs. `qualifies` lost its
|
|
|
|
|
|
Draft-excluding phase clause, so a Draft world's scale deficit is live and
|
|
|
|
|
|
falls as structure is authored; the Draft-answer refusal that used to hide
|
|
|
|
|
|
behind that clause now lives only in `require_answer`. The Draft seed lane —
|
|
|
|
|
|
`SeedPort` (two methods, `snapshot` and `submit_seed`), `SeedRunner`,
|
|
|
|
|
|
`SeedSession`/`SeedCheckpoint`, `ControllerWork::Seed` and `WorkLane::Seed` on
|
|
|
|
|
|
`controller_work.v10` — runs one authoring session per `world.seed`
|
|
|
|
|
|
invocation on the existing elaboration repair loop, admitting at most one
|
|
|
|
|
|
Draft patch as `CallerId::Principal(owner)` through the unconfined owner lane;
|
|
|
|
|
|
there is no marker in the journal distinguishing a seed-authored patch from a
|
|
|
|
|
|
hand-authored one. `VaultEvidenceSource` (`world/vault.rs`) is the seed lane's
|
|
|
|
|
|
production `EvidenceSource`: a read-only markdown directory reader whose
|
|
|
|
|
|
evidence reference is a note's vault-relative `.md` path.
|
|
|
|
|
|
`world.advance_time`'s missing `operation_schema` entry — every invocation of
|
|
|
|
|
|
it was silently unreachable — was fixed ahead of `world.seed`, and
|
|
|
|
|
|
`every_operation_the_panel_emits_has_a_schema` is the totality test that makes
|
|
|
|
|
|
that class of gap fail loudly instead of silently.
|
|
|
|
|
|
|
|
|
|
|
|
`SeedRequest` is resolved in the negative: Draft answers nothing, and the
|
|
|
|
|
|
seed lane's port cannot express an answered patch, so nothing ever needed
|
|
|
|
|
|
`SeedRequest` to bind. It stays representable in the `CausalBoundary`
|
|
|
|
|
|
vocabulary and uninhabited in practice.
|
|
|
|
|
|
|
|
|
|
|
|
Left undone: the seeded live run (the extended local live smoke seeds a world
|
|
|
|
|
|
from a real Vault before ticking it, but has not yet been run against a real
|
|
|
|
|
|
connector — see `notes/local-live-smoke.md`); `ControllerWorkCustody::Owned`
|
|
|
|
|
|
gained a `seed_commands` count while `Grouped` still has none, an asymmetry
|
|
|
|
|
|
named and not fixed; the Vault's keyword fallback scores by raw token-count
|
|
|
|
|
|
match rather than any real relevance ranking; and `VaultEvidenceSource::open`
|
|
|
|
|
|
reads its whole configured
|
|
|
|
|
|
scope into memory on every invocation rather than caching across sessions.
|
2026-09-05 16:29:04 +02:00
|
|
|
|
|
|
|
|
|
|
Cut line: no new reducer, ID allocator, conservation check, or
|
|
|
|
|
|
`SystemCapability`; no second tool surface; no compatibility path for the
|
2026-09-05 18:32:20 +02:00
|
|
|
|
three-subject genesis.
|
2026-09-05 16:29:04 +02:00
|
|
|
|
|
|
|
|
|
|
|
2026-09-01 14:38:01 +02:00
|
|
|
|
Required black-box proofs:
|
|
|
|
|
|
|
|
|
|
|
|
1. Draft → Active → player action → autonomous action → restart preserves the
|
|
|
|
|
|
exact digest and commit history.
|
|
|
|
|
|
2. Invalid ontology patches are atomic and model-free; a typed causal boundary
|
|
|
|
|
|
can activate sparse new state.
|
|
|
|
|
|
3. Models, scheduler, projection, and recovery are inert without an explicit
|
|
|
|
|
|
aggregate submission.
|
|
|
|
|
|
4. Private knowledge stays private until explicit communication.
|
|
|
|
|
|
5. External proposals and acknowledgements cannot mutate consumer-owned state;
|
|
|
|
|
|
only a fresh owner snapshot can update the local observation.
|
|
|
|
|
|
6. Narrative and operational controllers have exact disjoint scopes, never
|
|
|
|
|
|
fall through, and never consume one opportunity twice.
|
|
|
|
|
|
7. Interpretation always completes semantically and preserves unlowered
|
|
|
|
|
|
material as translation gaps.
|
2026-09-01 15:07:58 +02:00
|
|
|
|
8. A no-effect command leaves revision, state digest, commit digest, ID
|
|
|
|
|
|
allocation, and journal length unchanged.
|
|
|
|
|
|
9. An external crate cannot obtain mutable aggregate state, invoke the canonical
|
|
|
|
|
|
ID allocator, call the reducer, or write the journal; a caller-supplied
|
|
|
|
|
|
unknown ID is rejected rather than admitted.
|
2026-09-04 20:59:26 +02:00
|
|
|
|
10. A decision proposal is rejected when its opportunity's scope digest has
|
|
|
|
|
|
changed or its controller or currently executable affordance is wrong; the
|
2026-09-01 15:07:58 +02:00
|
|
|
|
scheduler cannot manufacture or consume authority.
|
2026-09-01 14:38:01 +02:00
|
|
|
|
|
|
|
|
|
|
A 1,200-actor synthetic fixture may measure load after these pass. It cannot
|
|
|
|
|
|
serve as an ontology or completeness gate. No live Delvehold acceptance run is
|
|
|
|
|
|
admitted before the focused suite passes and the old writers are structurally
|
|
|
|
|
|
unable to override the aggregate.
|
|
|
|
|
|
|
2026-09-05 23:20:51 +02:00
|
|
|
|
### 9. Interruption — landed
|
2026-09-05 21:37:34 +02:00
|
|
|
|
|
|
|
|
|
|
Objective: a subject whose world moved between forming an intent and
|
|
|
|
|
|
committing it is interrupted, not silently refused, and the interruption is
|
|
|
|
|
|
narrated from what it could perceive.
|
|
|
|
|
|
|
2026-09-05 23:20:51 +02:00
|
|
|
|
Landed mechanism: a proposal binds to the scope digest of the components its
|
|
|
|
|
|
verification reads; when another commit moves that digest before the
|
|
|
|
|
|
proposal lands, the kernel refuses it with `ScopeChanged`. The narrative
|
|
|
|
|
|
lane's own early scope checks are deleted — the kernel's `exact_opportunity`
|
|
|
|
|
|
is the sole detector for the narrative lane, and `ScopeChanged` on submit is
|
|
|
|
|
|
the one event with one handler, `submit_narrative`'s `interrupted` arm. The
|
|
|
|
|
|
Persona is never re-run: on `ScopeChanged` the runner re-lowers the same
|
|
|
|
|
|
prose once through the Interpreter against a fresh opportunity
|
|
|
|
|
|
(`select_fresh`/`select_scope`), with a second input — an `Interruption`
|
|
|
|
|
|
carrying the fresh `ScopeComponents`, the `Overheard` rows later than the
|
|
|
|
|
|
turn's bound revision, and the discarded first-lowering evidence. The
|
|
|
|
|
|
re-lowered turn is `PersonaTurn::record`ed fresh, never mutated, and carries
|
|
|
|
|
|
`interrupted_from` pointing at the binding it replaced; a turn whose binding
|
|
|
|
|
|
already carries `interrupted_from` is not re-lowered again — it ends as
|
|
|
|
|
|
`NarrativeRun::Interrupted`, structurally, because no progression arm gives a
|
|
|
|
|
|
re-lowered binding a second successor. A fresh opportunity whose granted set
|
|
|
|
|
|
has lost `Speak` also ends the turn as `Interrupted` rather than spending an
|
|
|
|
|
|
Interpreter round on an invocation it cannot express. Interruption is
|
|
|
|
|
|
defined by the digest, so a neighbour's act, a clock tick that rolls a
|
|
|
|
|
|
routine's `due`, an elaborator patch, a consumer document, and a world-scale
|
|
|
|
|
|
event are one case, indistinguishable at the handler. Schema bumps:
|
|
|
|
|
|
`controller_work.v11` and `ghostlight.persona_turn_receipt.v3`; prior rows
|
2026-09-22 02:27:59 +02:00
|
|
|
|
are refused, not migrated. The kernel's interruption still carries the
|
|
|
|
|
|
subject, both scope digests, the persona prose and receipt digest, and the
|
|
|
|
|
|
untranslatable gap; the play agent's Cut 1 (`d69e9d4`) deleted the owner's
|
|
|
|
|
|
dispatch surface and `ControllerHttpResult::Interrupted` with it, so nothing
|
|
|
|
|
|
in Dungeon reports an interruption today. Reporting a re-lowered turn to the
|
|
|
|
|
|
play agent's own turn record is Cut 8's.
|
2026-09-05 23:20:51 +02:00
|
|
|
|
|
|
|
|
|
|
Cut line held: no new kernel arm, bundle, or joint command; no second Persona
|
2026-09-05 21:37:34 +02:00
|
|
|
|
turn; no event log reaches a controller; the delta shown to the Interpreter
|
2026-09-05 23:20:51 +02:00
|
|
|
|
is exactly what the membrane already lets the subject perceive
|
|
|
|
|
|
(`Overheard` is built only from `Told` rows with a resolved speaker label,
|
|
|
|
|
|
never a raw knowledge row). The joint-per-room Interpreter was considered and
|
|
|
|
|
|
rejected: the room is not the unit of interruption, the digest is.
|
|
|
|
|
|
|
2026-09-06 00:04:08 +02:00
|
|
|
|
What is undone: no road run has exercised the re-lowering against a live
|
|
|
|
|
|
provider; none exists yet. Closed since landing: fork D's branch was dead by
|
|
|
|
|
|
construction (grants are insert-only, so a fresh opportunity always carries
|
|
|
|
|
|
speech) and is deleted; transfer, route-closing, and grant-revocation causes
|
2026-09-22 02:27:59 +02:00
|
|
|
|
are proven anonymous by Soul; an end-to-end test carried an interruption
|
|
|
|
|
|
through a real tick via `run_cover_tick` and showed the overtaken turn
|
|
|
|
|
|
committing nothing, before the play agent's Cut 1 (`d69e9d4`) deleted
|
|
|
|
|
|
`run_cover_tick` and the tick driver it drove; a witness landing mid-turn
|
|
|
|
|
|
yields the anonymous knowledge line and no overheard row. Still open: the
|
|
|
|
|
|
elaborator-patch cause is proven only by
|
2026-09-06 00:04:08 +02:00
|
|
|
|
the shared code path, not by a fixture that drives the elaborator past its
|
|
|
|
|
|
answer gate; and whether Eve's command-result vocabulary needs a fourth
|
|
|
|
|
|
state for an overtaken turn is the Eve owner's question.
|
2026-09-05 21:37:34 +02:00
|
|
|
|
|
2026-09-05 23:42:38 +02:00
|
|
|
|
### 10. Witnessed events over a place subtree — landed
|
2026-09-05 22:37:56 +02:00
|
|
|
|
|
|
|
|
|
|
Objective: stories that carry regional and global effects. An asteroid is
|
|
|
|
|
|
witnessed by everyone under a region's root; the moon going dark by everyone
|
|
|
|
|
|
under a hemisphere's; a subject standing in a village under both sees both.
|
|
|
|
|
|
|
2026-09-05 23:42:38 +02:00
|
|
|
|
Landed mechanism: `ComponentOp::Witness { fact, place, confidence }` lands
|
2026-09-05 22:37:56 +02:00
|
|
|
|
`Witnessed` knowledge of the fact on every subject positioned anywhere under
|
2026-09-05 23:42:38 +02:00
|
|
|
|
the place. Recipients are never stored; `ResolvedOp::Witness`'s apply arm
|
|
|
|
|
|
derives them at apply time through `under_place`, the same subtree walk
|
|
|
|
|
|
`audience`'s `Reach::Place` arm now calls, so a declared broadcast area and a
|
|
|
|
|
|
witnessed event share one definition of "under a place". Whoever already
|
|
|
|
|
|
holds the fact is dropped through `unheld`, factored out of `fan_out`'s old
|
|
|
|
|
|
inlined filter so `Communicate` and `Witness` cannot disagree about who
|
|
|
|
|
|
already knows. A telling still occurs into an empty room, so `Communicate`'s
|
|
|
|
|
|
empty fan-out stays a legal no-op; a witnessed event is only its reception,
|
|
|
|
|
|
so a `Witness` over an empty subtree, or one where every standing subject
|
|
|
|
|
|
already holds the fact, is refused as `Mismatch::NoOperationEffect` at
|
|
|
|
|
|
resolve (over the candidate graph, including this patch's own relocations)
|
|
|
|
|
|
and `KernelError::Invariant("a witness reaches nobody who does not already
|
|
|
|
|
|
hold the fact")` at apply — one rule, both layers. Confinement runs through
|
|
|
|
|
|
one new `operation_ground` arm returning the named place as the whole
|
|
|
|
|
|
ground: an elaborator may witness only under its own jurisdiction root, and a
|
|
|
|
|
|
`PatchGround::Consumer` patch can never witness, because its ground names no
|
|
|
|
|
|
place. Every recipient's scope digest moves and nobody else's does, which is
|
|
|
|
|
|
what makes a regional or global event interrupt whoever was mid-thought
|
|
|
|
|
|
(step 9).
|
|
|
|
|
|
|
|
|
|
|
|
Beyond the operation itself, an affordance slot,
|
|
|
|
|
|
`ComponentOpKind::Witness { confidence }` with roles `[fact, place]` and no
|
|
|
|
|
|
subject role, was not anticipated by this plan's text but was needed to let
|
|
|
|
|
|
a world-authored affordance (a bell, a beacon) cause a witnessed event
|
|
|
|
|
|
without a proposer ever naming a speaker; the source is `Witnessed` by
|
|
|
|
|
|
construction, since `ComponentOp::Witness` has no field a teller could enter.
|
|
|
|
|
|
One `PATCH_TOOLS` entry, `witness`, rounds out the model-facing surface.
|
|
|
|
|
|
Both store schemas moved to `ghostlight.world_state.consumer.v2` and
|
|
|
|
|
|
`ghostlight.world_commit.consumer.v2`; a store written under the prior schema
|
|
|
|
|
|
is refused, not migrated.
|
|
|
|
|
|
|
|
|
|
|
|
Cut line held: one fan-out owner (`under_place`), shared by `audience` and
|
|
|
|
|
|
`Witness`; one never-overwrite owner (`unheld`), shared by `fan_out` and
|
|
|
|
|
|
`Witness`; no stored recipient list; a witness names no speaker, so `Told`
|
|
|
|
|
|
stays unrepresentable outside `Communicate`; no new `Mismatch`,
|
|
|
|
|
|
`KernelError` variant, `CommandBody`, `SystemCapability`, or
|
|
|
|
|
|
`CompositeShape`; `verify_state_shape`, `can_broadcast`, and
|
|
|
|
|
|
`candidate_in_audience` untouched.
|
|
|
|
|
|
|
|
|
|
|
|
What is undone: the affordance-declaration refusal for a slot whose second
|
|
|
|
|
|
role binds a subject is proved directly through `arity` and
|
|
|
|
|
|
`role_kind_fits`, not by submitting a malformed `AffordanceDeclaration`
|
|
|
|
|
|
through admission — the gate is the one gate either way, but the submission
|
|
|
|
|
|
path itself is untested. The grouped lane's cost is untouched: a
|
|
|
|
|
|
region-wide or global witness still discards a grouped cell's in-flight work
|
|
|
|
|
|
for every constituent it touches, which is the debt this pass explicitly
|
|
|
|
|
|
declined to pay.
|
2026-09-05 22:37:56 +02:00
|
|
|
|
|
2026-09-06 02:11:41 +02:00
|
|
|
|
### 11. Claude SDK inference port — landed
|
2026-09-06 00:25:31 +02:00
|
|
|
|
|
|
|
|
|
|
Objective: run Ghostlight's inference lanes on the Claude subscription while
|
|
|
|
|
|
no Codex subscription or API budget exists, without moving the harness.
|
|
|
|
|
|
|
2026-09-06 02:11:41 +02:00
|
|
|
|
Landed mechanism: two `InferencePort` implementors behind one
|
|
|
|
|
|
`RoutedInferencePort`, chosen per lane by the configured model's prefix
|
|
|
|
|
|
(`GHOSTLIGHT_SDK_MODEL_PREFIX`, default `claude`) — a `claude`-prefixed model
|
|
|
|
|
|
reaches `SdkInferencePort`, anything else reaches the existing
|
|
|
|
|
|
`CodexConnectorInferencePort` unchanged. `SdkInferencePort` spawns a Node
|
|
|
|
|
|
sidecar (`sidecar/claude-sdk/`, a TypeScript package on
|
|
|
|
|
|
`@anthropic-ai/claude-agent-sdk` `0.3.261`, `@msgpack/msgpack` `3.1.3`, and
|
|
|
|
|
|
`zod` `4.5.4`, all exact-pinned) as a child process over stdio, framed as a
|
|
|
|
|
|
4-byte big-endian length prefix plus `rmp_serde::to_vec_named` bytes. One
|
|
|
|
|
|
request is one SDK query with the system prompt replaced, built-ins
|
|
|
|
|
|
stripped, settings sources empty, the request's tools registered as
|
|
|
|
|
|
in-process SDK tools, a derived turn cap, and the request's model.
|
|
|
|
|
|
Execute-through: each tool call the model makes crosses the pipe and is
|
|
|
|
|
|
answered by `ToolResultOracle`, one trait with four implementors
|
|
|
|
|
|
(`InterpreterOracle`, `OperationalOracle`, `GroupedOracle`,
|
|
|
|
|
|
`ElaborationOracle`) — each is the lane's own evaluator fold, extracted
|
|
|
|
|
|
whole into a free function (`interpreter_tool_result`,
|
|
|
|
|
|
`operational_tool_result`, `grouped_tool_result`, and the already-factored
|
|
|
|
|
|
`apply_tool_call`) and replayed over `completed` at construction, so the
|
|
|
|
|
|
string handed to the model is the same one re-derivation will recompute.
|
|
|
|
|
|
The sidecar computes no tool result itself.
|
|
|
|
|
|
|
|
|
|
|
|
One free function, `prepare_invocation`, is the sole owner of prepared
|
|
|
|
|
|
identity for both ports; `ControllerRunner::open` is injection-only —
|
|
|
|
|
|
`with_test_ports` is gone, and `open` takes `Arc<dyn InferencePort>`,
|
|
|
|
|
|
`Arc<dyn ControllerWorkStore>`, and `ControllerModels` and nothing
|
|
|
|
|
|
transport-specific. `runtime::open_controller` builds `ConnectorBinding`
|
|
|
|
|
|
and `SdkBinding` from the environment and calls `open_inference`, then
|
|
|
|
|
|
`open_controller_work`, then `ControllerRunner::open`; `open_inference`
|
|
|
|
|
|
routes every one of the five `ControllerModels` at open time and refuses a
|
|
|
|
|
|
model no configured backend claims (`ControllerOpenError::UnroutableModel`)
|
|
|
|
|
|
or a sidecar entry that is not a file
|
|
|
|
|
|
(`ControllerOpenError::SdkSidecarMissing`) before the first tick, not after.
|
|
|
|
|
|
|
|
|
|
|
|
Environment: `GHOSTLIGHT_SDK_SIDECAR` (the built sidecar entry path; its
|
|
|
|
|
|
absence means no SDK binding, not a default path) and
|
|
|
|
|
|
`GHOSTLIGHT_SDK_MODEL_PREFIX` (default `claude`) are read once in
|
|
|
|
|
|
`runtime::open_controller`. `GHOSTLIGHT_CONTROLLER_CREDENTIAL` is no longer
|
|
|
|
|
|
unconditionally required — an SDK-only configuration reads no credential
|
|
|
|
|
|
path at all. `GHOSTLIGHT_WRITE_SIDECAR_FIXTURES` regenerates the checked-in
|
|
|
|
|
|
schema and frame fixtures the Rust and TypeScript sides pin against each
|
|
|
|
|
|
other; it is a fixture-authoring switch, not a runtime binding.
|
|
|
|
|
|
|
|
|
|
|
|
Cut line held: no second identity scheme (`prepare_invocation` is the one
|
|
|
|
|
|
owner, both ports call it); no daemon, network port, or CultMesh surface for
|
|
|
|
|
|
the sidecar (it is a plain child process, restarted on fault, holding no
|
|
|
|
|
|
query state between queries); no credential handling in Ghostlight (the
|
|
|
|
|
|
sidecar inherits the ambient Claude Code login and this repo reads, copies,
|
|
|
|
|
|
forwards, or logs none of it); the connector port and its `execute` are
|
|
|
|
|
|
untouched; the port is chosen per lane by the configured model, with no mode
|
|
|
|
|
|
flag. Two forks the original design did not anticipate: `ControllerRunner::open`
|
|
|
|
|
|
became injection-only rather than keeping a `#[cfg(test)]` twin, because
|
|
|
|
|
|
removing the two transport-specific parameters made the twin's only purpose
|
|
|
|
|
|
vanish; and the turn cap rides on the oracle (`remaining_rounds`) rather than
|
|
|
|
|
|
on `InferencePurpose`, because the elaboration and seed lanes share one
|
|
|
|
|
|
purpose but carry different round budgets.
|
|
|
|
|
|
|
|
|
|
|
|
Named liabilities, stated where `SdkInferencePort` is defined, not hidden in
|
|
|
|
|
|
the lowering:
|
|
|
|
|
|
|
|
|
|
|
|
- The receipt is the SDK's message, not wire bytes. Its identity half is
|
|
|
|
|
|
computed from the invocation Ghostlight already holds; its provenance half
|
|
|
|
|
|
is a session id and message uuids reported by a child process this port
|
|
|
|
|
|
spawned. It attests that this exact request produced this exact SDK
|
|
|
|
|
|
session; it does not attest against a party Ghostlight does not control,
|
|
|
|
|
|
which is what the connector's receipt does.
|
|
|
|
|
|
- A prior round's conversation reaches the model as prose, not as typed
|
|
|
|
|
|
turns. The SDK owns the assistant side of its own transcript, so typed
|
|
|
|
|
|
`tool_call`/`tool_result` turns from an earlier round cannot be replayed
|
|
|
|
|
|
into it; they are rendered as strings under one fixed header and appended
|
|
|
|
|
|
to the prompt.
|
|
|
|
|
|
- The Zod validation of a tool call's arguments happens in the sidecar
|
|
|
|
|
|
before the handler runs, so a model's undecodable arguments never reach
|
|
|
|
|
|
Rust as an argument string the oracle can fold over; the sidecar reports
|
|
|
|
|
|
a normal dispatched tool call and Rust's own evaluator recomputes the
|
|
|
|
|
|
decode failure as an ordinary gap — on the connector path the same
|
|
|
|
|
|
failure is a gap recorded by the evaluator directly. Both are gaps; the
|
|
|
|
|
|
divergence in exactly which layer detects an undecodable argument can
|
|
|
|
|
|
only reach a later round's prompt through the prose transcript.
|
|
|
|
|
|
- `error_max_turns` handling is inferred from the SDK's own documentation,
|
|
|
|
|
|
not observed: the sidecar is written to catch the throw the TypeScript
|
|
|
|
|
|
SDK performs after yielding the error result, assemble the events already
|
|
|
|
|
|
collected, and emit a normal `Output` whose receipt records
|
|
|
|
|
|
`subtype: "error_max_turns"`, so the lane sees one ordinary round and
|
|
|
|
|
|
proceeds rather than faulting. No live query has exercised this path.
|
|
|
|
|
|
- Because the turn cap is the lane's *remaining* round budget and the lane
|
|
|
|
|
|
still counts rounds even when the cap is not reached, a lane whose model
|
|
|
|
|
|
never terminates in one turn can spend a triangular number of turns across
|
|
|
|
|
|
a round sequence (seed: 24+23+…, bounded by `SEED_ROUND_BUDGET`) against
|
|
|
|
|
|
the connector's flat per-round cost. This is unmeasured: no live run has
|
|
|
|
|
|
produced a number to weigh it against.
|
|
|
|
|
|
|
2026-09-22 02:28:53 +02:00
|
|
|
|
The SDK port has since carried the road runs recorded in the handoff (runs 8
|
|
|
|
|
|
to 12, through the sidecar, the only live provider). The ad-hoc smoke that
|
|
|
|
|
|
drove them was deleted with the tick driver in the play agent's Cut 1
|
|
|
|
|
|
(`d69e9d4`). No live smoke exists until the play agent's gate.
|
2026-09-06 02:11:41 +02:00
|
|
|
|
|
|
|
|
|
|
Exit condition, unchanged from the port's own doc comment: when an
|
|
|
|
|
|
`ANTHROPIC_API_KEY` and a budget exist, a Messages-API port is a closer
|
|
|
|
|
|
structural match to `InferencePort` than the SDK is — inert `tool_use`
|
|
|
|
|
|
blocks, a caller-appended `tool_result`, a real request id, real
|
|
|
|
|
|
concurrency, no subprocess — and `SdkInferencePort` is deleted rather than
|
|
|
|
|
|
extended, keeping `RoutedInferencePort` and swapping what it routes to.
|
|
|
|
|
|
Rejected: a backend inside CodexConnector (a deliberately isolated Codex
|
|
|
|
|
|
fork).
|
2026-09-06 00:25:31 +02:00
|
|
|
|
|
2026-09-06 02:38:50 +02:00
|
|
|
|
One validator on both transports: the sidecar's schema conversion is loose
|
|
|
|
|
|
(every property optional, unknown keys kept), so the arguments the model
|
|
|
|
|
|
emitted reach Rust's decoder unstripped and a refused argument is the gap
|
|
|
|
|
|
the evaluator already records, on the SDK path exactly as on the connector
|
|
|
|
|
|
path. Residual: a property whose value has the wrong type reaches Rust as
|
|
|
|
|
|
absent rather than as the wrong value, because the SDK derives the
|
|
|
|
|
|
advertised tool schema from the same object and refuses a dynamic
|
|
|
|
|
|
pass-through; the decoder reports it as missing. The result-subtype mapping
|
|
|
|
|
|
(`error_max_turns` is an output; every error subtype a named fault) is
|
|
|
|
|
|
tested in the sidecar without a credential.
|
|
|
|
|
|
|
2026-09-10 23:50:28 +02:00
|
|
|
|
Road follow-ups, 2026-09-10, from the first two SDK-backed seeded runs
|
|
|
|
|
|
(`notes/local-live-smoke.md`, "Claude SDK route"): the sidecar's schema
|
|
|
|
|
|
conversion now carries every field `description` through to the advertised
|
|
|
|
|
|
tool schema (it dropped all of them, so the `claude` lane saw bare types);
|
|
|
|
|
|
`due` describes itself as a clock reading and both authoring prompts print
|
|
|
|
|
|
the clock, because the model wrote twelve commitments due at or before a
|
|
|
|
|
|
clock it was never shown; `declare_subject` states the grant rule at the
|
|
|
|
|
|
point of use; `apply_tool_call` logs each raw tool call at debug so a refused
|
|
|
|
|
|
patch is diagnosed from what the model sent. A kernel refusal now continues
|
|
|
|
|
|
the same conversation instead of opening a fresh draft: `Refusal { after_round,
|
|
|
|
|
|
mismatches }` rows on both authoring checkpoints (`controller_work.v12`;
|
|
|
|
|
|
prior rows refused, not migrated), the refusal rendered as the next user turn
|
|
|
|
|
|
after the rounds that earned it, and the draft rebuilt by one `DraftFold`
|
|
|
|
|
|
shared by the evaluator and the oracle from the calls after it. The harness
|
|
|
|
|
|
takes the connector binding only when `GHOSTLIGHT_CONTROLLER_CONNECTOR` is
|
|
|
|
|
|
set, so an all-`claude` run needs no connector.
|
|
|
|
|
|
|
2026-09-10 23:57:17 +02:00
|
|
|
|
From run 2: the Interpreter's `speak` and `record_gap` cite the source by
|
|
|
|
|
|
quoting it (`source_quote`), and the harness locates the quote and derives
|
|
|
|
|
|
the span; the model no longer counts bytes, which cut three of eight
|
|
|
|
|
|
utterances mid-word. The same invariant holds (only words in the preserved
|
|
|
|
|
|
prose become speech; a quote not in the prose is a gap), and a quote that is
|
|
|
|
|
|
not canonical utterance text is a gap at capture rather than a quarantine at
|
|
|
|
|
|
invocation. `RecordGapToolCall` in `ghostlight-persona-projection` changed
|
|
|
|
|
|
shape; Epiphany pins that crate by revision and is unaffected until it moves.
|
|
|
|
|
|
|
2026-09-11 01:31:20 +02:00
|
|
|
|
From runs 4 to 6 (the membrane traced end to end): the Projector context now
|
|
|
|
|
|
carries the acting subject's own life by label (place, routes, holdings,
|
|
|
|
|
|
dependencies, authority shape, offices, redress, promises with counterparty,
|
|
|
|
|
|
pressure on self; due as minutes from now), never an id; the Projector and
|
|
|
|
|
|
Persona prompts render only what the context establishes and treat the word
|
|
|
|
|
|
budget as a ceiling; the smoke's `GHOSTLIGHT_SMOKE_TRACE` writes every
|
|
|
|
|
|
request and output. Silence is a legitimate outcome, not a harness failure.
|
|
|
|
|
|
|
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
|
|
|
|
### 12. The seed authors meaning — landed
|
|
|
|
|
|
|
|
|
|
|
|
Landed 2026-09-11 as mapped, with both recommendations taken: the small typed
|
|
|
|
|
|
material record, and a required commitment statement. `PersonaMaterial` is
|
|
|
|
|
|
`world/patch.rs`'s thirteenth-row component (`values`, `voice`, `memories`,
|
|
|
|
|
|
`reads` keyed by subject), set whole by `set_persona_material` (thirty
|
|
|
|
|
|
operations, thirty-nine tools); a commitment carries `statement`; state and
|
|
|
|
|
|
commit schemas are `.consumer.v3`; an affordance-made promise's statement is
|
|
|
|
|
|
"a promise made through <kind>". The Projector context carries the material
|
|
|
|
|
|
by label, each promise's statement, and `present`, the labels of the subjects
|
|
|
|
|
|
standing in the same place; the Persona's "Who you are" is the label, the
|
|
|
|
|
|
voice, and the values; the typed view carries material and statements by id.
|
|
|
|
|
|
Material is not in `ScopeComponents`, pinned by a test that sets it and
|
2026-09-11 03:04:02 +02:00
|
|
|
|
watches the scope digest stay. The map that led here follows. Objective:
|
2026-09-11 12:59:45 +02:00
|
|
|
|
a seeded person speaks from authored
|
2026-09-11 01:31:20 +02:00
|
|
|
|
meaning rather than the model's default voice, which is the hard boundary
|
|
|
|
|
|
"character intelligence is modeled state, not a default model capability".
|
|
|
|
|
|
|
|
|
|
|
|
Current mechanism: the ontology doc names `PersonaMaterial` (values, voice,
|
|
|
|
|
|
memories, reads) as a component and lists `set(subject, material)` in its
|
|
|
|
|
|
operation table, and none of it exists in the kernel; `Commitment` is kind,
|
|
|
|
|
|
counterparty, due, period, checks, with no statement of what is promised.
|
|
|
|
|
|
The seed can only author identical people with contentless debts.
|
|
|
|
|
|
|
|
|
|
|
|
Cut: (1) `create_commitment` gains a required `statement: Statement`; the
|
|
|
|
|
|
catalog count stays 29. (2) `set_persona_material(subject, material)`, a
|
|
|
|
|
|
whole-value set carrying `values: Vec<Statement>`, `voice: Statement`,
|
|
|
|
|
|
`memories: Vec<Statement>`, `reads: Vec<(SubjectId, Statement)>`; the count
|
|
|
|
|
|
becomes 30 and one tool follows from the catalog. Material is not in
|
|
|
|
|
|
`ScopeComponents` (no precondition reads it), so it cannot interrupt a bound
|
|
|
|
|
|
turn. The Projector gains memories and reads by label and the statement
|
|
|
|
|
|
beside each promise; the Persona identity block becomes label, voice, values;
|
|
|
|
|
|
the Operational typed view gains the same by id. The seed brief asks for a
|
|
|
|
|
|
voice, values, memories, and a read of each counterparty per person.
|
|
|
|
|
|
Forbidden: any `persona_state.v0` document inside `WorldState` (the portable
|
|
|
|
|
|
standard is a boundary projection, a later seam); any prompt-side invention.
|
|
|
|
|
|
Schema bumps: `world_state.consumer.v3`, `world_commit.consumer.v3`. Also
|
|
|
|
|
|
owed from run 6: the Projector says "no one before me" for two people in one
|
|
|
|
|
|
house, because the context carries own place and not who else stands there;
|
|
|
|
|
|
co-presence by label is the same reach the kernel already derives for speech
|
|
|
|
|
|
and belongs in the same cut. Decisions the operator owns: the small typed
|
|
|
|
|
|
material record above versus the full portable standard (recommend small),
|
|
|
|
|
|
and whether a commitment statement is required (recommend required).
|
|
|
|
|
|
|
2026-09-11 12:59:45 +02:00
|
|
|
|
### 13. The world brief — landed
|
|
|
|
|
|
|
|
|
|
|
|
Landed 2026-09-11. Objective: the people of a world know what world they are
|
|
|
|
|
|
in. `world_create.v3` carries a required `brief`, trimmed at ingress and
|
|
|
|
|
|
stored on `WorldState`; it is projected as the guidance of every lane and
|
|
|
|
|
|
printed in both authoring prompts, and the two narrative stages that rebuild
|
|
|
|
|
|
prompts on resume carry it as `guidance` (`controller_work.v13`). The smoke
|
|
|
|
|
|
titles its world by the seed root label unless `GHOSTLIGHT_SMOKE_WORLD_TITLE`
|
|
|
|
|
|
says otherwise, and the seed brief becomes the world's brief rather than a
|
|
|
|
|
|
per-session sentence. Run 9 is the proof: no fixture name in any vow, and
|
|
|
|
|
|
promises made against the deadline the brief set.
|
|
|
|
|
|
|
2026-09-11 13:34:09 +02:00
|
|
|
|
### 14. Nonverbal display by quote — landed
|
|
|
|
|
|
|
|
|
|
|
|
Landed 2026-09-11 as mapped, with all three recommendations taken:
|
|
|
|
|
|
kernel-only lowering (no patch tool, thirty operations, pin unchanged), the
|
|
|
|
|
|
display kept in the actor's own words, and a silent turn invoking the
|
|
|
|
|
|
`speak` entry with no utterance. `DecisionInvocation.display` and
|
|
|
|
|
|
`DecisionEvent.display`; `ResolvedOp::Display { actor, fact }` landing
|
|
|
|
|
|
`KnowledgeSource::Seen { by }` over `Audience::Colocated` through the same
|
|
|
|
|
|
`fan_out`; `ActionMismatch::EmptyDisplay`; `SpeechRequired` only when
|
|
|
|
|
|
neither words nor act; the journal's event-shape rule counts both lowerings;
|
|
|
|
|
|
`minted_at` replaces `spoken_at`. The Interpreter's `display` tool beside
|
|
|
|
|
|
`speak`, one of each per turn, `NarrativeProposal::{Speak, Display}`,
|
|
|
|
|
|
`NarrativeCapture { speech, display }`; `Overheard.from: Perceived::{Said,
|
|
|
|
|
|
Seen, Known}` and the Projector's knowledge rows carry `how` and `by`.
|
|
|
|
|
|
Schemas `world_state.consumer.v4`, `world_commit.consumer.v4`,
|
|
|
|
|
|
`controller_work.v14`. Run 10 proves it on the road (runbook row 10) and
|
2026-09-11 14:20:27 +02:00
|
|
|
|
produced the first live interrupted cell. Follow-up landed the same day: a
|
|
|
|
|
|
turn's utterance and display are each every span cited for them, joined in
|
|
|
|
|
|
prose order, so "So," I say, "does it open" is one statement and four
|
|
|
|
|
|
visible acts are one display; only an overlapping span is an ambiguity
|
|
|
|
|
|
(`controller_work.v15`, proven by run 12). The map that led here follows.
|
2026-09-11 12:51:33 +02:00
|
|
|
|
|
|
|
|
|
|
Ordered 2026-09-11 after run 9. Objective: a person can act without words and
|
|
|
|
|
|
be seen doing it. Run 9's gap column holds "I nod toward the path down", "I
|
|
|
|
|
|
set down whatever's in my hands", "I settle on the warm step", and "I let the
|
|
|
|
|
|
quiet sit a breath longer"; each was seen by everyone in the room and the
|
|
|
|
|
|
world lost it. The false binary is intent versus posture. Intent is a psychic
|
|
|
|
|
|
side channel; posture mechanics force every reader to decode a body. What is
|
|
|
|
|
|
public is neither: it is the display, a visible act at the grain a witness
|
|
|
|
|
|
perceives it, separable from what the actor meant by it. Speech already has
|
|
|
|
|
|
this shape in the kernel: a claim placed before an audience, landed in each
|
|
|
|
|
|
listener's knowledge as `Told { by }`, with the words kept verbatim. A display
|
|
|
|
|
|
is the same act with a different sense: `Seen { by }`, and an audience fixed
|
|
|
|
|
|
by physics rather than by the entry.
|
|
|
|
|
|
|
|
|
|
|
|
Current mechanism: the narrative lane speaks and does nothing else.
|
|
|
|
|
|
`speak_invocation` (`controllers.rs`) finds the granted entry of kind `speak`
|
|
|
|
|
|
and invokes it with `speech: Some(statement)`; `exercise` (`action.rs`)
|
|
|
|
|
|
lowers speech kernel-side into `AssertClaim { fact, statement, by: actor }`
|
|
|
|
|
|
and `Communicate { speaker, fact, to }`, the fact id from the one speech
|
|
|
|
|
|
`derive_id` site with `SPEECH_INDEX = "0"`; `Communicate`'s apply arm fans out
|
|
|
|
|
|
over the entry's audience less the speaker less holders and lands
|
|
|
|
|
|
`Told { by, via }` at `Believed`. `DecisionEvent.speech: Option<EntityId>`
|
|
|
|
|
|
stamps `spoken_at` on every knowledge row at snapshot; `overheard_since`
|
|
|
|
|
|
turns rows newer than a bound turn into the Interpreter's "What was said to
|
|
|
|
|
|
this person since" block; `projector_knowledge` renders each row with a
|
|
|
|
|
|
speaker label for `Told` and none for `Witnessed`/`Evidenced`. The
|
|
|
|
|
|
Interpreter owns two tools, `speak(source_quote)` and `record_gap`; a visible
|
|
|
|
|
|
act is a `missing_affordance` gap, and the fold allows one speech capture.
|
|
|
|
|
|
Interpretation of a display already has a home on the receiving side:
|
|
|
|
|
|
`PersonaMaterial.reads` (Torvin reading Iska's face) is the receiver's own
|
|
|
|
|
|
private reading, rendered only to the receiver. What has no home is the
|
|
|
|
|
|
display itself on the sender's side.
|
|
|
|
|
|
|
|
|
|
|
|
Authority map:
|
|
|
|
|
|
|
|
|
|
|
|
- Owner: the kernel's `exercise` lowering in `action.rs`. It alone turns an
|
|
|
|
|
|
invocation's `display: Option<Statement>` into
|
|
|
|
|
|
`AssertClaim { fact, statement, by: actor }` plus
|
|
|
|
|
|
`ResolvedOp::Display { actor, fact }`, the fact id from the same
|
|
|
|
|
|
`derive_id` site with a second discriminator (`DISPLAY_INDEX = "1"`; the
|
|
|
|
|
|
index exists for exactly this, no new allocation site).
|
|
|
|
|
|
- Inputs: the actor, its position, the quoted visible sentence. Nothing
|
|
|
|
|
|
else. Not the entry's audience: a display cannot go down a horn.
|
|
|
|
|
|
- Outputs: one `Claimed { by: actor }` fact whose statement is the actor's
|
|
|
|
|
|
own visible sentence, and a `Seen { by: actor }` knowledge row at
|
|
|
|
|
|
`Believed` on every subject standing in the actor's place, less the actor,
|
|
|
|
|
|
less anyone who already holds the fact (`unheld`, the never-overwrite rule).
|
|
|
|
|
|
Fan-out is `audience(state, actor, &Audience::Colocated)`, the derivation
|
|
|
|
|
|
speech already uses; `can_broadcast` over the same audience refuses a
|
|
|
|
|
|
placeless actor as it refuses a placeless speaker. A display into an empty
|
|
|
|
|
|
room is a legal no-op, as speaking alone is.
|
|
|
|
|
|
- Derived state: each recipient's `knows` moves, so its scope digest moves,
|
|
|
|
|
|
so a display interrupts whoever was mid-thought in the room through the
|
|
|
|
|
|
interruption mechanism of step 9 unchanged. `spoken_at` becomes
|
|
|
|
|
|
`minted_at`, stamped from `DecisionEvent.speech` or `.display`, and is the
|
|
|
|
|
|
sole gate on an overheard row. `Overheard` carries how the row arrived:
|
|
|
|
|
|
said by, shown by, or known; the Interpreter's interruption block renders
|
|
|
|
|
|
"X said" and "X, seen doing" and the existing "came to know" line. The
|
|
|
|
|
|
Projector's knowledge rows render `Seen { by }` with the actor's label and a
|
|
|
|
|
|
seen marker; the reader's Persona does the social reading itself, in its
|
|
|
|
|
|
own values, and any reading it commits is a `PersonaRead`, already landed.
|
|
|
|
|
|
- Forbidden writers: no `intent`, `meaning`, `mood`, or `reading` field on
|
|
|
|
|
|
the operation, the fact, the knowledge row, or the event; the kernel never
|
|
|
|
|
|
mints a reading for a receiver. The Interpreter never rewrites the display
|
|
|
|
|
|
into third person or into what it meant; the statement is a verbatim quote
|
|
|
|
|
|
of the visible clause and a quote not in the prose is a gap. The Projector
|
|
|
|
|
|
never paraphrases a display. No confidence on the operation: a displayer
|
|
|
|
|
|
cannot choose how much a viewer believes its body. No display over a
|
|
|
|
|
|
channel audience, ever; no `Display` in `PATCH_TOOLS`, so no seed or
|
|
|
|
|
|
elaborator may make a person gesture from outside that person (kernel-only
|
|
|
|
|
|
lowering, the same standing as `AssertClaim`; the catalog stays thirty
|
|
|
|
|
|
operations and the pin stays `(7, 30, 39)`).
|
|
|
|
|
|
- Shared paths: the SDK lane and the connector lane share the one
|
|
|
|
|
|
Interpreter fold; replay re-derives the display fact from the command's
|
|
|
|
|
|
invocation and must land the same rows; a resumed narrative row rebuilds
|
|
|
|
|
|
the same interruption block from the persisted `Overheard`.
|
|
|
|
|
|
- Deletion line: `InterpreterFold.captured_speech: bool` and
|
|
|
|
|
|
`SpeakProposal { text }` are cut for one address proposal carrying
|
|
|
|
|
|
`speech: Option<String>` and `display: Option<String>`, at most one of each
|
|
|
|
|
|
per turn, and `NarrativeCapture.proposal: Option<SourceRange>` becomes the
|
|
|
|
|
|
ranges of both. `ActionMismatch::SpeechRequired` narrows to "a
|
|
|
|
|
|
speech-carrying entry invoked with neither speech nor display"; a silent
|
|
|
|
|
|
turn that only displays invokes the `speak` entry with `speech: None`.
|
|
|
|
|
|
`carries_speech` keeps its name and its meaning: what it gates is the
|
|
|
|
|
|
utterance and its audience; a display accompanies any invocation because a
|
|
|
|
|
|
body is visible whatever it is doing.
|
|
|
|
|
|
|
|
|
|
|
|
Interpreter contract: a third tool, `display(source_quote)`, beside `speak`.
|
|
|
|
|
|
The prompt gains one rule and no list: a visible act another person in the
|
|
|
|
|
|
same place could see (a gesture, a posture, a look, a movement, a silence
|
|
|
|
|
|
held) is a display, quoted as the visible clause alone; what the person
|
|
|
|
|
|
hoped, meant, or felt while doing it is not visible and is not quotable as a
|
|
|
|
|
|
display. A non-canonical quote is a gap; a second display is an `ambiguity`
|
|
|
|
|
|
gap, the same rule as a second speech.
|
|
|
|
|
|
|
|
|
|
|
|
Cut line: the `missing_affordance` gaps for gestures in run 9 are the
|
|
|
|
|
|
measured outcome. Run 10 tallies the gap kinds again; the four gesture gaps
|
|
|
|
|
|
must be displays and the private-reasoning gaps ("I don't say the other
|
|
|
|
|
|
thing") must remain gaps, because they are intent and this cut gives intent
|
|
|
|
|
|
no public home on purpose. Memory formation from a turn's private half is a
|
|
|
|
|
|
different cut and is not in this one.
|
|
|
|
|
|
|
|
|
|
|
|
Subtraction budget: net additive by one `ResolvedOp` arm, one
|
|
|
|
|
|
`KnowledgeSource` arm, one invocation field, one event field, one Interpreter
|
|
|
|
|
|
tool, and one `Overheard` manner; cut against it are `captured_speech`,
|
|
|
|
|
|
`SpeakProposal`, and the four `missing_affordance` gap shapes per run. The
|
|
|
|
|
|
smaller surface, routing gestures through `speak`, was rejected: it would
|
|
|
|
|
|
land a visible act as `Told`, a telling that did not happen, and render it to
|
|
|
|
|
|
the room as words. Schema bumps: `world_state.consumer.v4` (the knowledge
|
|
|
|
|
|
source arm and `minted_at`), `world_commit.consumer.v4` (`display` on the
|
|
|
|
|
|
invocation and the event), `controller_work.v14` (`Overheard` manner and the
|
|
|
|
|
|
capture ranges); the consumer API doc and the ontology's speech paragraph,
|
|
|
|
|
|
operation count sentence (line 459 still says twenty-nine; it is thirty and
|
|
|
|
|
|
stays thirty), and Interpreter paragraph follow.
|
|
|
|
|
|
|
|
|
|
|
|
Build budget: crate `ghostlight-dungeon` and `ghostlight-persona-projection`
|
|
|
|
|
|
tests on the Windows workstation as the proving host, sidecar schema
|
|
|
|
|
|
fixtures regenerated if the Interpreter tool set changes the sidecar's
|
|
|
|
|
|
fixture; the Linux release is Idunn's build and is not touched by this cut.
|
|
|
|
|
|
|
|
|
|
|
|
Verification: kernel tests that a display lands `Seen { by }` on the
|
|
|
|
|
|
co-located only, never on the actor, never on a subject elsewhere or on a
|
|
|
|
|
|
channel audience, never over a holder, is refused for a placeless actor, is a
|
|
|
|
|
|
no-op in an empty room, and replays to equal state; an interruption test that
|
|
|
|
|
|
a display after a neighbour's turn bound yields a "seen doing" overheard row
|
|
|
|
|
|
and the "knows changed" line; fold tests that a non-canonical display quote
|
|
|
|
|
|
is a gap, a second display is an ambiguity gap, and speech plus display in
|
|
|
|
|
|
one turn is one invocation carrying both; a Projector test that a seen row
|
|
|
|
|
|
renders with the actor's label and never as speech.
|
|
|
|
|
|
|
|
|
|
|
|
Decisions the operator owns: kernel-only lowering versus a patch-authorable
|
|
|
|
|
|
`display` operation (recommend kernel-only; a person's body is not
|
|
|
|
|
|
authorable from outside the person, and the count stays thirty); the display
|
|
|
|
|
|
statement kept in the actor's own words versus an Interpreter rewrite
|
|
|
|
|
|
(recommend the actor's words; by-quote discipline, and the receiver reads
|
|
|
|
|
|
first person as a stage direction); a silent turn invoking the `speak` entry
|
|
|
|
|
|
versus a fourth narrative tool for a wordless turn (recommend `speak` with
|
|
|
|
|
|
no utterance; one carrier, one command per turn).
|
|
|
|
|
|
|
2026-09-22 00:53:24 +02:00
|
|
|
|
### 15. Session Zero — L0 and L1 landed; L2–L4 and D1–D3 deferred by ruling
|
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
|
|
|
|
|
2026-09-15 22:46:54 +02:00
|
|
|
|
Adopted 2026-09-15. Two owners, one ordering. Library capabilities,
|
|
|
|
|
|
consumer-neutral, are specified in `docs/architecture/ghostlight-world-ontology.md`
|
2026-09-15 22:55:18 +02:00
|
|
|
|
under "Elaboration lenses and detail rules (target)". Ghostlight Dungeon's
|
|
|
|
|
|
Session Zero consumes them and is specified in
|
2026-09-15 22:46:54 +02:00
|
|
|
|
`docs/architecture/ghostlight-session-zero.md`. This entry only tracks state.
|
|
|
|
|
|
|
2026-09-22 00:53:24 +02:00
|
|
|
|
Deferred by ruling, 2026-09-22 (operator, decision 3): Session Zero is no
|
|
|
|
|
|
longer the playtest gate; step 16, the play agent, is. L2–L4 and D1–D3 are
|
|
|
|
|
|
deferred, not dropped, until a consumer that still runs the offline
|
|
|
|
|
|
machinery needs them; the list, with L1.f16, f1, f13 and f14, is
|
|
|
|
|
|
`docs/architecture/ghostlight-play-agent.md`, "Deferred, by ruling".
|
|
|
|
|
|
|
2026-09-15 22:55:18 +02:00
|
|
|
|
Operator decisions, 2026-09-15: the world kernel is extracted from
|
|
|
|
|
|
`crates/ghostlight-dungeon` into a library crate before any library pass, and
|
|
|
|
|
|
the eight titles ship as the library's stock lens set.
|
|
|
|
|
|
|
Close L0: postmortem, ledger, target and state reconciled
The kernel extraction is finished. The world kernel is the ghostlight
library crate; Dungeon consumes it across a boundary sealed by admission,
proven from outside the crate.
The cut map records the last two fix batches and Q1-10, defaulted: the
minter scanner stays a tripwire, with Clippy recorded as the fix. The
subtraction ledger missed its estimate, +210 net source lines against about
+65. The reasons: the scanner grew chasing evasions, the boundary needed
about 90 more public items than planned, and the rejection tests buy a
ruling made after the estimate.
The postmortem lists what Soul caught, the operator corrections, the
incidents, and the open follow-ups. The target states which invariants
hold and how. The plan, map.yaml and the handoff point at L1. The L0
evidence record is rewritten as its distilled lessons; the previous text is
archived.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 12:24:49 +02:00
|
|
|
|
Library passes: (L0, landed 2026-09-16 at `108b691..ab95d78`; see
|
|
|
|
|
|
`docs/architecture/ghostlight-library-extraction-postmortem.md`) extract the
|
2026-09-16 19:56:20 +02:00
|
|
|
|
kernel into a library crate with a real public boundary; (L1, landed
|
|
|
|
|
|
2026-09-16 at `835ea4d..872ba35`; see
|
|
|
|
|
|
`docs/architecture/ghostlight-stock-lenses-cut.md`) the stock lens set,
|
|
|
|
|
|
weights, sampler, and concurrent sessions over today's demand; (L2)
|
|
|
|
|
|
per-world evidence binding on every elaborator session; (L3) typed detail rules (`Uniform`, `ByDistance`),
|
2026-09-15 22:55:18 +02:00
|
|
|
|
rule-driven demand under the world-target ceiling, and the permille, root,
|
|
|
|
|
|
and genesis-subject cuts; (L4) individuation where a place's level asks for
|
|
|
|
|
|
persons. Dungeon
|
2026-09-16 12:26:38 +02:00
|
|
|
|
passes: (D1) Vault registry and world creation with lens weights and a detail
|
|
|
|
|
|
rule; (D2) the Session Zero Eve surface; (D3) travel affordance and
|
2026-09-22 00:53:24 +02:00
|
|
|
|
per-affordance player controls, which closed the playtest gate until the
|
|
|
|
|
|
2026-09-22 ruling.
|
|
|
|
|
|
|
2026-09-23 03:52:42 +02:00
|
|
|
|
### 16. The play agent — cutting; Cuts 1 through 14 landed or landing
|
2026-09-22 00:53:24 +02:00
|
|
|
|
|
|
|
|
|
|
Adopted 2026-09-22. Target `docs/architecture/ghostlight-play-agent.md`
|
2026-09-23 03:52:42 +02:00
|
|
|
|
(commit `cb0b604`); the means are in
|
|
|
|
|
|
`docs/architecture/ghostlight-play-agent-cut.md`, which owns each cut's
|
|
|
|
|
|
status, findings PA.f1 onward, and every ruling.
|
2026-09-22 00:53:24 +02:00
|
|
|
|
This step is the playtest gate (the target's "Playtest gate"). One
|
|
|
|
|
|
Dungeon-owned operational agent replaces the Interpreter and the
|
|
|
|
|
|
cover/cell/catalog/owner-click orchestration on Dungeon's play path; the
|
|
|
|
|
|
Projector → Persona passes are unchanged; the library keeps the offline
|
|
|
|
|
|
machinery. This entry only tracks state.
|
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
|
|
|
|
|
2026-09-01 14:38:01 +02:00
|
|
|
|
## Subtraction budget
|
|
|
|
|
|
|
|
|
|
|
|
Prefer deletion, collapse, or reuse before adding surfaces. The replacement
|
|
|
|
|
|
buys explicit capabilities: one lifecycle owner, one structural reducer, one
|
|
|
|
|
|
persistence writer, disjoint cognition controllers, total interpretation, and
|
|
|
|
|
|
restart proof. It must retire more writer and stage liability than it adds.
|
|
|
|
|
|
Review each completed pass for modules, binaries, schemas, dependencies,
|
|
|
|
|
|
processes, and tests added or removed.
|
|
|
|
|
|
|
|
|
|
|
|
## Build budget
|
|
|
|
|
|
|
2026-09-02 16:23:40 +02:00
|
|
|
|
- Build host: Yggdrasil through Idunn-configured pinned Linux containers. Do
|
|
|
|
|
|
not build the deployment body on Windows.
|
|
|
|
|
|
- Control plane: `odin-core` library tests plus `idunn-daemon` library/binary
|
|
|
|
|
|
checks; Linux x86_64 release artifacts are `idunn` and `idunn-provision` only.
|
|
|
|
|
|
- Targets: `codex-connector` library tests with and without `daemon`, then the
|
|
|
|
|
|
`codex-connector` release binary with `daemon`; `ghostlight-dungeon` focused
|
|
|
|
|
|
tests, then the `ghostlight-dungeon` release binary. Default target features
|
|
|
|
|
|
remain unchanged unless the target-owned recipe names an existing required
|
|
|
|
|
|
feature.
|
|
|
|
|
|
- Output roots, container digests, cache mounts, current footprints, retention,
|
|
|
|
|
|
and expected deltas are operator-binding inputs and must be measured on
|
|
|
|
|
|
Yggdrasil before the first build. No workspace-wide, all-target, clean, or
|
|
|
|
|
|
cross-platform build is admitted.
|
2026-09-01 14:38:01 +02:00
|
|
|
|
|
|
|
|
|
|
## Deferred research lanes
|
|
|
|
|
|
|
|
|
|
|
|
The 36-case agency corpus, separate-account multiplayer proof, visual/Ink
|
|
|
|
|
|
fixtures, newspaper variation, and direct-tool Persona comparison remain useful
|
|
|
|
|
|
research lanes. They do not steer the authority rebuild and cannot reopen a
|
|
|
|
|
|
retired writer. Historical plans and results remain recoverable through Git,
|
|
|
|
|
|
the evidence ledger, and the frozen pre-rebuild system map.
|