CreateWorld gains an optional GrammarBinding (schema id, revision, content
digest derived from the kind-sorted verbs, never caller-supplied). Genesis
installs it on the empty state, so resolve_patch checks the genesis patch and
every later AdmitPatch against it: an affordance kind the grammar lacks is
AffordanceKindNotInGrammar, a differing role signature (name, referent kind,
order) is AffordanceRolesDisagreeWithGrammar, and the kernel speak entry is
held to the same check. WorldSnapshot reports the grammar's identity and each
AffordanceSnapshot derives its render path from it. Worlds with no grammar
are unchanged: the field is absent from state and command bytes, pinned by a
journal written at base 367e2eb.
Builds: not verified on Yggdrasil yet at this commit (cargo check clean locally).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seed_vault_root_from becomes seed_vault_root_from_environment(lookup): the
only place naming GHOSTLIGHT_SEED_VAULT_ROOT. Open passes std::env::var_os.
A test pins that the root comes from that variable only. The comment at the
bridge test's no-world.seed assertion names AppState.seed_vault_root.
README notes the root is read once at startup. Builds: all.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Captured by running the drift test's own create/approve/activate/play walk
with a temporary, uncommitted patch that writes the regenerated intents
instead of comparing them (vendor/eve at 672c0c1). Only idempotencyKey and
issuedAt differ from the previous file; the drift test normalizes both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Joining the thread from the test body starved the world mailbox's owner task
and hung the test; spawn_blocking leaves the runtime free. Ignored test,
verified on Starfire.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A second PlayTable over the same store cannot open while the first is alive
(the store takes an exclusive lock), so the first leg now runs on its own
runtime and dies with it, as a crashed process would. Does not run on the
stopgap (ignored test); verified on Starfire.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The previous commit does not compile (E0505 in the ignored bridge test's
closure); this one fixes it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- AppState carries the seed vault root, read once at open through
seed_vault_root_from (blank or whitespace is no configuration); tests no
longer set or remove the environment variable.
- world.create treats a cleared persona or agent label as none, at the one
place the payload becomes the kernel's intent.
- The real-client bridge test types text to open a turn, restarts over the
same store, and continues the Running turn with an untouched Play box.
Builds. Bridge tests are ignored and run separately on Starfire.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The previous commit's walk expected acceptance; with no running turn the table refuses an empty opening, which is the proof the payload arrived. Builds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
world.play.text carries an authored value "" so the lowering sends text "" for a box left untouched. seed_vault_root() is the one reader of GHOSTLIGHT_SEED_VAULT_ROOT; the surface offers the seed controls, card and descriptor only when it answers, and seed_once reads its root through it. The committed bridge fixture drops its world.seed step, which the real client can no longer produce on an unconfigured server.
Not yet compiled when committed; verification follows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shared faulted-turn assertion now takes the expected class and checks the
warn line carries it (Connect, Timeout, BadReply, Status(404), Status(502)),
with the endpoint and reply canaries still absent. Not yet built or tested at
this commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
close_with_fault now takes a TurnFault (detail plus InferenceFaultClass) and
logs the class beside the fixed detail, so a 404 and a 502 read differently
again. absorb_or_close hands the fault's class through; every non-inference
caller is Unclassified. The &'static str comment now says what it does and
does not guarantee (Box::leak defeats it; the per-port canary tests are the
backstop). Not yet built or tested at this commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SidecarLinkError::Spawn and Codec held error text that the fault type now
refuses, leaving two never-read payloads; they are unit variants.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The previous commit (29efabe) does not build: a string literal in the new
malformed-reply test was mis-escaped.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
InferenceFault.detail is a &'static str, so no inference port (local,
connector, SDK) can put an endpoint, request or reply text into a fault. The
connector and SDK details that were built from error text are now fixed per
kind or reason. LocalBinding loses its public response_timeout field; the
short timeout the PlayTable canary needs comes from open_local_port_with_timeout,
which exists only under the ghostlight test-support feature (a dungeon
dev-dependency feature), so a release has one timeout, RESPONSE_TIMEOUT.
Canary tests on the malformed-body path (port and PlayTable), on the SDK and
connector ports, and on the default timeout at both resolving sites.
Not yet built or tested at this commit.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
reqwest's json() reports a body cut mid-stream as a decode error, so the
is_decode branch in send_fault misclassed it BadReply. The body is now read
with bytes() through send_fault (timeout / connect / no complete reply), and
only a whole body that fails serde_json is classed by bad_reply().
Not yet built at this commit; verified by the following job.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- send_fault now classifies the reply-body read too: a body-read timeout is
Timeout, a body cut mid-stream is NoResponse (retryable), a whole body that
does not decode stays BadReply. The response.json() map_err is gone.
- LocalBinding.response_timeout (None = RESPONSE_TIMEOUT) lets a test reach the
timeout path through the full PlayTable path.
- Tests: stalled, cut and malformed bodies against loopback listeners; a
timeout canary at the port and through PlayTable (stored fault, warn line,
card row, Debug); a PlayTable turn that survives N empty replies and faults
past ROUND_RETRY_BUDGET.
- Comments: CREATE_BINDINGS doc reattached, backoff comments state the curve,
close_with_fault no longer claims "endpoint-free by construction".
Not yet built or tested at this commit; verified by the following job.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The table mints the turn id, so the log assertion must read it from the stored turn, not the id the caller passed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
R3: every local send that fails before a status arrives is Retryable except a timeout; the fault carries a typed InferenceFaultClass (Connect, NoResponse, Timeout, Status, BadReply) and a detail without the request URL.
R1: PlayTurnView.fault and a fixed world.play.fault card row.
R2: close_with_fault logs the turn id and the (endpoint-free) detail at warn.
R4: backoff cap 20 s, shift clamp 8: the retry budget now spans about 105 s.
Not yet built or run when committed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ontology:103 lists all three FactStanding variants (patch.rs:298); the Eve
table gains app.auth.logout (eve.rs operation_schema, 9 ids); mvp:466 names
the three RoutedInferencePort routes (sdk_inference.rs:700).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VerificationUnavailable now carries an error class and never the fetch error,
whose text named the key-set URL. fetch_jwks returns the reqwest error so the
class is read from it, not from a string. run_refresh_loop loses its period
parameter: SESSION_REFRESH_PERIOD is decided in one place and a paused-clock
test observes it exactly and over twelve ticks.
Tests drive HeimdallClient::verify_access against a loopback issuer that
counts fetches (cache stored, read, ttl 299 s fresh and 301 s stale), a closed
port and a 503 (kept session and receipt, no URL in the error or the log line),
and nine wrong-algorithm tokens. The moved-session test also pins that the
newer claim is the one spent next.
Builds and passes under ygg-verify (28 of 28 in heimdall:: and refresh_loop_tests).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ComponentOp has 34 variants (table rewritten), Precondition 9, ActionMismatch
26, FactStanding 3, EdgeKind 1; CausalBoundary field shapes, PatchAnswer,
and the live state/commit/controller-work schema generations follow the
code. The consumer page lists the ops a confined consumer is refused. The
smoke note points at the Idunn acceptance step instead of the historical
play-agent page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CommandBody has seven variants, WorldPhase two, EdgeKind one, WorldState
a fixed field set; the page now lists those and marks membership, contract,
archive and general relations as unbuilt. Persistence rows and WorldCommit
fields match journal.rs and lib.rs. The play-agent gate citation points at
the Idunn recipe's acceptance step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ontology: drop the two citations of deleted pre-rebuild pages; Session Zero is
named as an unbuilt consumer. mvp: bounded co-op and the archived phase are
marked design intent, since crates/ has neither.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README no longer calls unbuilt or historical pages current design records:
play-agent is historical, session-zero is unbuilt, public-architecture and
articles are unbuilt design and essays, extraction and stock-lenses are closed
records. The authority architecture page loses its citation of a deleted file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README and AGENTS.md state what exists (sealed kernel, Dungeon, persona
projection, sidecar, browser host) and what does not (the verse, storylets,
cast, episodes). The handoff becomes a pointer to the Eureka mind. The Eve
interface page matches eve.rs (world.create v4, Draft/Active cards, play
card). Live pages cite crates/ghostlight/src/<file> instead of world/*.rs.
The implementation plan gains a historical status line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The refresh pass keeps a spent-upstream receipt when verification could not
fetch the signing keys and verifies it on the next pass instead of refreshing
again; only a positive verification failure revokes. The loop body is
run_refresh_loop, tested for calling the pass; tests pin that a moved session
is not revoked and a higher revision is applied.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A JWKS fetch failure is now VerificationUnavailable, distinct from a receipt that
positively fails verification. The fetched key set is cached for five minutes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Removes state:*, prestate:*, *:validate, schema:validate, images:count and
coverage:status (they called the deleted Python tools); keeps dungeon:* and
sdk:*. Description now names the Rust kernel and Dungeon. Drops .gitignore
lines for __pycache__, *.pyc, .venv, state scratch and experiments/images.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parked at tag parked/ghostlight-pre-rebuild (ruling ghostlight-verse:ruling:park-pre-rebuild-tree).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parked at tag parked/ghostlight-pre-rebuild (ruling ghostlight-verse:ruling:park-pre-rebuild-tree).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parked at tag parked/ghostlight-pre-rebuild (ruling ghostlight-verse:ruling:park-pre-rebuild-tree).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parked at tag parked/ghostlight-pre-rebuild (ruling ghostlight-verse:ruling:park-pre-rebuild-tree).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parked at tag parked/ghostlight-pre-rebuild (ruling ghostlight-verse:ruling:park-pre-rebuild-tree).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parked at tag parked/ghostlight-pre-rebuild (ruling ghostlight-verse:ruling:park-pre-rebuild-tree).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parked at tag parked/ghostlight-pre-rebuild (ruling ghostlight-verse:ruling:park-pre-rebuild-tree).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parked at tag parked/ghostlight-pre-rebuild (ruling ghostlight-verse:ruling:park-pre-rebuild-tree).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parked at tag parked/ghostlight-pre-rebuild (ruling ghostlight-verse:ruling:park-pre-rebuild-tree).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mutation survivors of the first cut: the status comparison inside invoke (now
refuse_denied, tested directly) and the session-or-account custody check
(a receipt for another session, or another account, revokes).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Heimdall's access_revision is a revocation epoch, so a refresh receipt at the
session's revision is the normal case (ruling refresh-revision-is-revocation-epoch).
The loop pass moves behind a SessionRefresher port; a typed HeimdallDenied
revokes the session, a backwards receipt revokes it, every local refusal
names its clause, and the idempotency key derives from the claim being spent.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Why the 2026-09-30 deploy died (Odin's route), what main's build and
tests say, the play-session findings against main, the live refresh
loop, the host's retired v1 body, and where phase 2's verb sets plug in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Operator ruling 2026-09-30: "Nope, I'd rather be on main". main was
fast-forwarded to the codex/ghostlight-dungeon-mvp tip (eeb3a68) and the
Yggdrasil ghostlight binding now admits refs/heads/main (gamecult-ops
45eb584). Historical cut records keep the branch they landed on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>