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>
56 KiB
Ghostlight library extraction — cut map (plan step 15, L0)
Status: cut map. Ends are owned by ghostlight-library-extraction.md; this
document owns the means. Written 2026-09-15 against d83534c on
codex/ghostlight-dungeon-mvp; first version committed at 02bbf62;
refreshed 2026-09-16 with the operator's rulings.
Cut 1 landed at Ghostlight 108b691..f859d2c. 108b691 is 13 renames with
zero content change; f859d2c builds. Verified by Soul's own runs, not the
Hands report: library 419 passed + 1 ignored with test names identical to the
base world:: set; 10 compile_fail doc-tests pass, also under
--all-features; Dungeon 50 + 1 ignored, all 49 base names present and the
+2 exactly the two new soul tests; no [features], cfg(feature, or
cfg(any(test in the library; no schema string, _NAMESPACE, derive_id,
serde attribute, or Preimage changed; the rewritten tests fail under
mutation of what they pin. Windows only; the Linux release remains Idunn's.
Soul found six defects. Triage:
- F1 seal is name-based (high, open): every
pubID derivesDeserialize(crates/ghostlight/src/lib.rs:186-200), as doScopeDigest(:532) andDecisionOpportunity(:559), so an external crate mints IDs, reproducesScopeDigest::fixture, and forges aDecisionOpportunityintoderive_coverfrom JSON. The reducer still rejects forged opportunities (lib.rs:4192-4201), so kernel integrity holds and the invariant's wording does not. Dungeon's own Eve payloads deserialize these types, so removing the derives is not available. Operator question Q1-9 below. - F2 single-minter test is bypassable (medium, fix): the count at
crates/ghostlight-dungeon/src/app_session.rs:530-555greps a literal and misses<VerifiedPrincipalEvidence>::new(, an alias, a renamed test module, or a subdirectory. - F3 surface wider than bought (low, fix): 71
pubidentifiers are unreferenced by Dungeon;ScopeDigest,WorkLane, andScopeComponentscompile aspub(crate)and were opened only to quietprivate_interfaces. - F4 "Eve" named in library error strings (low, fix):
crates/ghostlight/src/controllers.rs:1494,2576,2582,2584. Pre-existing; invariant 3 is still unmet. - F5 two Hands claims false as stated (cosmetic, recorded):
Cargo.lockdid gain aghostlightpackage entry (no new external package), andfixture_[a-z]has 8 benign hits in Dungeon. - F6 recipe schema drift (pre-existing, recorded):
deployment/idunn/recipe.tomldeclaresworld_state.foundation.v0andcontroller_work.v3against code atconsumer.v4andcontroller_work.v15. Outside L0; it changes no behavior here.
Cut 2 landed at gamecult-ops 6aba281 (text only; nothing live was run).
The library test step is in the runbook, the deploy script, and the wiring
test, ordered as recipe.toml orders it. Two spec defects Hands reported
rather than redesigned: the assertion this map specified was vacuous
(require_text is grep -Fq, and the string given is a substring of the
persona-projection line, so it passed with the library step deleted), so
Hands used the file's existing exact-line helper and proved it fails under
deletion; and the acceptance test path lives in acceptance_test= at
scripts/deploy-ghostlight-yggdrasil.sh:34, a fourth site this map did not
list, pinned by the wiring test and repeated in the runbook prose. Operator
consequence: changing acceptance_test changes the acceptance binding, so
sealed witnesses under /srv/ghostlight/acceptance/<binding-sha>/ from
before this commit will miss and regenerate.
Soul passed on Cut 2 and found the assertions pin text, not invocations:
- C2-F1 (high, fixing): deleting the two lines above the pinned cargo
line in
scripts/deploy-ghostlight-yggdrasil.shleaves it byte-identical as trailing argv on the persona-projection container, so the kernel tests never run and the wiring test still exits 0. Prefixing thedocker runline with:does the same. - C2-F2 (medium, fixing): nothing pins the order of the four cargo steps; the library block moved after the release build and the wiring test passed, which would seal an artifact before the kernel tests ran.
- C2-F3 (medium, fixing): the wiring test never reads the runbook, so only three of the four acceptance sites are enforced to agree.
- C2-F4 (medium, pre-existing, recorded):
deploy-…sh:937takes a reuse branch when the release directory for a commit already exists, so a redeploy of a sealed commit runs no cargo steps at all. This bounds "the deploy runs the kernel tests" to a commit's first build. Operator owns whether that is right. - C2-F5 (low, recorded):
docs/repo-census-2026-09/*still showssrc/world/.... Dated census; no deploy effect.
Held: the step sits in the same branch, image and mounts as its
neighbours, and set -euo pipefail with trap means a library-test
failure does abort the deploy. The acceptance test exists at
crates/ghostlight/src/controllers.rs:8980 under -p ghostlight --lib.
C2-F1, C2-F2 and C2-F3 are fixed at gamecult-ops 803f2c7 (wiring test
only). A new require_invocation helper pins each of the four cargo steps
as a whole contiguous docker invocation and returns its start line; three
numeric comparisons assert the recipe order; the runbook became a checked
file, tying all four acceptance sites together. All four of Soul's
mutations now exit 1 where they previously exited 0, and every
pre-existing assertion still passes. require_order was unusable here
because its last_line resolves a substring and would have bound the
library step to the acceptance invocation.
Soul passed on the fix batch and confirmed it does what it claimed:
whole-invocation pinning survives image swaps, line swaps, re-indentation,
continuation splits and appended flags; the order checks catch a move into
the reuse branch; the runbook is tied in both directions; fail inside a
command substitution does abort, because set -e fires on the
assignment's status; nothing was weakened. It also found the structural
limit, recorded and not fixed:
- C2-F6 (high, recorded): an exact-line pin proves the lines exist,
not that they run. Wrapping the block in
if false, prefixing the opener withtrue || \, or swallowing it in a heredoc keeps every pinned line byte-identical and the checker exits 0. - C2-F7 (medium, recorded): the four steps carry no explicit
|| exit; abort depends entirely onset -euo pipefailatdeploy-…sh:2, which nothing pins. Removing theeleaves the checker green, so a failing kernel suite would be followed by a sealed release. - C2-F8 (low, recorded): a duplicated block passes, since the helper takes the first match and never asserts a count.
These stay recorded rather than fixed: that script is a legacy actuator
under Ghostlight's do-not-invoke warning and is retired at the deployment
gate, and the authority that actually runs the tests is Ghostlight's own
deployment/idunn/recipe.toml. Closing them means control-flow analysis
in a bash checker for a script we intend to delete. Revisit only if the
deployment gate keeps the script.
Follow-ups outside this migration, from the fix batch:
scripts/deploy-ghostlight-yggdrasil.sh:974, the web container, is pinned by substring and survives a:prefix that neuters it.- The Codex Connector's four cargo steps are bare substring pins with no invocation or order assertion: the connector-side C2-F1 and C2-F2.
require_command_optionssearches a fixed 18-line window, so a block that grows past it stops being checked silently.
Cut 1 fix batch landed at Ghostlight 7c1d1e9..2865ea7: a8d3377 external
rejection tests (Q1-9), fac90e6 single-minter check by name (F2),
7cbb9e3 Eve removed from library text (F4), 2865ea7 public surface
262 → 258 (F3). Hands' correction to F3 is confirmed by Soul: Dungeon calls
bound_scope_digest and fresh_scope_digest, and ScopeDigest, WorkLane
and ScopeComponents are held public by KernelError::ScopeChanged,
ControllerWorkCustody::Uncertain::lane and NarrativeCheckpoint variant
fields; closing them means reshaping public enums, which F3 does not
license. fac90e6's commit message describes a replaced brace-counting
scanner; the landed code and its doc comment are correct.
Soul held: the rejection tests are a true external crate with no #[path]
or include!, driving ControllerPort::submit_controller through the
mailbox into reduce with no pre-validation; every commit moves revision
and state_digest, so the unchanged-state assertions are sound; the
positive test goes red when issued opportunities are refused; counts,
the 14 distinct warnings, and both manifests are unchanged.
Soul found:
- S1 (medium, fixing): both forged digests differ in length from a real
sha256:+ 64 hex, so comparing only lengths atcrates/ghostlight/src/lib.rs:4203leaves all seven tests green. The realistic forgery is a stale same-length digest from an earlier revision. - S4 (medium, open, Q1-10): the minter scanner at
crates/ghostlight-dungeon/src/app_session.rs:523-668misses apub typealias, a multi-line alias, a} // testsclosing brace, non-rustfmt indentation, and::newtaken as a function value. - S7 (low, fixing):
controllers.rs:122 provider_modelcloses topub(crate)with no error and no warning change. - S8 (cosmetic, fixing):
mailbox.rs:580namesAppSessionOwnerandcontrollers.rs:2606names AppSession. - S2 (low, recorded): neutering the world_id check at
lib.rs:4193leaves the suite green becauseScopePreimageincludesworld_id, so the scope lookup refuses first. Redundant by construction; not isolable from outside the crate. - S3 (low, recorded): the decline path (
lib.rs:1603) ispub(crate)and unreachable from an integration test. - S5 (low, recorded, pre-existing):
runtime.rs:3584's credential-name test splits on one literal#[cfg(test)]marker and is evadable the same way the minter test was. - S6 (medium, recorded): stable rustc does not enforce
compile_failerror codes;E0603changed toE0999still passes. All ten doc-tests prove "does not compile", not privacy, and the three#[cfg(test)]items (SubjectId::issue,ScopeDigest::fixture, thefixture_*fns) prove non-existence. Enforcing codes needs a snapshot tool such as trybuild; recorded, not added.
Q1-10 how the single minter is enforced (defaulted, 2026-09-16). Asked
how much it mattered, the operator said not at all. The scanner stays as a
tripwire with stated limits: it guards only against the project's own
Dungeon code, and what evades it is deliberate. S4 is a recorded follow-up,
with Clippy's disallowed-methods for
ghostlight::VerifiedPrincipalEvidence::new noted as the fix if a second
minter ever becomes plausible.
S1, S7 and S8 landed at Ghostlight bde05de..af9c95a. S1 submits a
genuinely issued digest made stale by another controller's speech, plus the
current digest with its last digit altered. Soul confirmed the premise
check fails when staleness does not happen and that no earlier check
intercepts the forgery, then found two surviving comparison mutants
(case-insensitive, hex-only after the label). Those, and "player"
vocabulary in library tests, landed at b14507c (uppercased-letter,
bare-hex and relabelled forgeries, all six comparison mutants killed) and
ab95d78 (test-only rename to "human", one test name changed). Soul's
final pass found nothing to fix.
L0 closed, 2026-09-16. Postmortem:
ghostlight-library-extraction-postmortem.md. Follow-ups are listed there.
Q1-9 what sealing means (ruled, operator, 2026-09-16): option A. The
invariant is unforgeable admission, not unconstructible types. External
code may hold a syntactically valid ID, digest, or opportunity; the kernel
must reject any it did not issue, and that is proven by rejection tests
written from outside the crate. The compile_fail set stays for the
genuinely private items. The question as first posed: A: restate invariant 1 as unforgeable
admission — external code may hold a syntactically valid ID or opportunity,
and the kernel must reject any it did not issue — and prove it with rejection
tests from an external crate plus the existing compile_fail set. B: make the
types unconstructible from outside, which means Dungeon stops deserializing
DecisionOpportunity in its Eve payloads and receives an opaque token
instead, a design change L0 excludes. Recommended: A, because the kernel
already enforces it and B moves work into L0 that the target puts out of
scope. Whichever is chosen, F2 and F3 are fixed in the same Hands batch.
Rulings (operator, 2026-09-15):
- Q1-1 crate name: "crate name is
ghostlight." - Q1-2 adapters: "
vault.rsandsdk_inference.rsboth go in the library." - Q1-3 principal seam: "option A, library-owned
VerifiedPrincipalEvidencewith a publicnew, plus the Dungeon count test pinning the single minter." - Q1-4 fixtures: "NOT the
test-supportfeature. The operator takes the alternative: the sealed IDissue()constructors andScopeDigest::fixturestay unreachable from outside the library under every configuration, including feature-enabled builds, and the Dungeon tests that currently need them move into the library instead. Rationale: a sealed constructor exposed behind a feature is still reachable by any crate that turns the feature on, which contradicts invariant 1." (Supersedes thetest-supportdesign in the02bbf62map; that text is history and is not repeated here. The classification below found that none of the affected tests is a kernel test, which raises Q1-7 and Q1-8.) - Q1-5 persona projection: "
ghostlight-persona-projectionstays a separate crate." - Q1-6 gamecult-ops: "edit the five gamecult-ops cargo lines in Cut 2;
do not retire the scripts (retirement belongs to the deployment gate)."
Correction to the
02bbf62map: Ghostlight's handoff does call those helpers unsafe —notes/fresh-workspace-handoff.md:389-391: "Do not invoke the current bounded Connector or Ghostlight redeploy helpers; they fail at the root/Idunn Git boundary and still embody duplicate target deployment authority." The earlier claim that no such doc existed was wrong; the ruling to edit rather than retire stands on the deployment gate owning retirement, not on the scripts being live.
Rulings (2026-09-16), taken by Self under the operator's standing "Go" after Q1-4's classification found no kernel test to move. Either may be reversed by the operator; both are recorded here as the live design:
- Q1-7 port value constructors: option A.
PreparedInference::prepare(renamed fromprepare_invocation),InferenceOutput::new, andInferenceFault::{retryable, recovery_required, integrity_violation}become the port's public contract; the fivefixture_*items are deleted andTracingInferencePortbecomes an ungated decorator. A public trait whose return types only the library can construct is not a port, none of these touches a sealed item, and the change is a net deletion. - Q1-8 cover source: option A. The tick-ordering test derives its
cover from a real world through
derive_coverand asserts 2 cells, with its ordering and shared-tick assertions unchanged;fixture_controller_opportunities, the only outside caller of a sealedissue()/ScopeDigest::fixture, is deleted.
Together these satisfy Q1-4's intent: the sealed ID issuers and
ScopeDigest::fixture stay private #[cfg(test)] items with no feature
gate anywhere, unreachable from outside the library under every
configuration.
Open: nothing blocking Cut 1. Operator checks remain on the Linux release (Idunn's recipe run), which no Windows probe can prove. Follow-ups outside this migration: see "Findings not assigned to a cut".
Probe names versus real names: probes 1–4 built the crate as
ghostlight-world (use ghostlight_world::…); probe 5 rebuilt it as
ghostlight (use ghostlight::…). The difference is the package name, the
directory crates/ghostlight, and the path prefix; every mechanism finding
held in both. This map uses the real name throughout.
What the probes established
All probes ran in scratch worktrees at d83534c (both removed, repo clean)
on the Windows workstation with cargo check and cargo test --doc; they
prove the crate graph and visibility on Windows only. The Linux proof is
Idunn's recipe run on Yggdrasil. Logs are in the session scratchpad
(probe1*-check.log, probe3*-visibility.log, probe5.log,
base-test-list.log); they are not repo artifacts.
- Base test census (
cargo test -p ghostlight-dungeon --bin ghostlight-dungeon -- --list, repo target dir): 469 unit tests in the bin, of which 420 are underworld::(action 34, clock_tests 45, consumer 30, controllers 81, cover 18, custody_tests 17, elaboration 14, journal 24, knowledge_tests 21, mailbox 15, patch 34, sdk_inference 23, soul_knowledge_tests 9, tests 32, vault 6, witness_tests 17) and 49 are Dungeon's own (app_session 7, eve 5, heimdall 5, idunn_health 4, mesh 4, runtime 24).tests/build_provenance.rshas 1.ghostlight-persona- projectionhas 13 unit, 0 doc. Two tests are#[ignore]:world::controllers::tests::real_codex_connector_cognition_modes_commit_speechandruntime::tests::live_smoke_seeds_then_ticks_a_world_against_the_connector. - Base warning:
mod.rs:28-32re-exports nine names nobody in the crate uses (ControllerOpenError,NarrativeCapture,NarrativeDecision,NarrativePending,OperationalCapture,OperationalDecision,OperationalPending,SourceRange,TranslationGapSummary). Their deletion is inside Cut 1. - The move compiles (probe 1d). Moving
src/world/*to the new crate (mod.rs→lib.rs), rewritingcrate::world::→crate::inside it and →ghostlight::in Dungeon, and movingVerifiedPrincipalEvidenceinto the library root:cargo checkon the library, its tests, the Dungeon bin, and the Dungeon tests all pass. Two mechanical faults: the 94pub(super)sites in the formermod.rsare illegal at a crate root (becomepub(crate)), andconsumer.rs:62needsserde_bytesin the library manifest. - What Dungeon needs public (probe 3, four compiler rounds from every
item
pub(crate)): 54 production items, 22 test-only items, 38 fields on 10 structs, 36 methods, the opaque IDsAffordanceId/CommandId/SubjectId, andFictionalMinutes(u64)(one Dungeon test). Nothing sealed appears:WorldKernel,WorldState,reduce,apply_effect,CommandEnvelope,AuthenticatedCaller, the journal,*::issue()andScopeDigest::fixtureare never named by Dungeon. Under the narrowed librarycargo check -p ghostlightemits 646 dead-code warnings — a second enumeration of the same boundary (an item used only by Dungeon is dead inside the library until it ispub). - The sealed set under the Q1-4 ruling (probe 5, crate
ghostlight, no cargo features, fixtures left#[cfg(test)]): tencompile_faildoc-tests on the library root pass undercargo test -p ghostlight --docand again undercargo test -p ghostlight --all-features --doc(the crate declares no features, so the second run is the same build; it is the command Soul re-runs if a feature is ever added).cargo check -p ghostlight-dungeon --bin ghostlight-dungeonpasses: production needs no fixture.cargo check -p ghostlight-dungeon --testsfails on exactly four errors —runtime.rs:3081(ControllerWork…family,Inference…types, threefixture_inference_*),:3570(fixture_controller_opportunities),:2582(TracingInferencePort),:2581(InferencePort, because the base re-export atmod.rs:66iscfg(test)). Those four sites are the whole cross-boundary test surface; they are classified under Cut 1. - Compile-fail mechanism: doc-tests compile as an external crate, which
is exactly the boundary; no
trybuild, no scratch crate. The ten (use ghostlight::WorldKernel,reduce,WorldState,CommandEnvelope,AuthenticatedCaller,journal::WorldJournal,SubjectId::issue(),ScopeDigest::fixture("x"),fixture_controller_opportunities(&[]),fixture_inference_output(..)) are pinned toE0603,E0599,E0599,E0425,E0425. - Negative-grep collisions checked in
src/world/:axum,tower_http,jsonwebtoken,heimdall,eve::,idunn_health,Session Zerohave zero legitimate hits.Dungeoncollides with the vault fixture pathSpoilers/Dungeons/Provenance.md(vault.rs:489,581): use\bDungeon\b.playercollides withtests::player()(consumer.rs:966): do not publish a bareplayerpattern.app_sessionhas 5 hits, all the principal seam, and must reach 0. - Line endings: index and working tree are LF (
git ls-files --eolreportsi/lf w/lf);core.autocrlf=truerewrote every fresh scratch worktree to CRLF. Hands edits inF:\Projects\Ghostlightitself, never in a fresh worktree; if a worktree is unavoidable, rungit ls-files --eol cratesafter the move and refuse anyw/crlf.sdk_inference.rs:1831normalizes\r\nbefore scanning source.
Cut 0. Captures
- Repo/branch: Ghostlight
codex/ghostlight-dungeon-mvpat02bbf62(source identical tod83534c). No commit; output lives in the session scratchpad. - First:
- Test census:
cargo test -p ghostlight-dungeon --bin ghostlight-dungeon -- --list > base-tests.txt(PowerShell>writes UTF-8 BOM + CRLF; compare post-cut with the same redirection andCompare-Objecton trimmed lines after stripping theworld::prefix). Expected 469. - A
world.ccwritten at base. No store exists on disk (F:\Projects\Ghostlight-smoke\ghostlightholds noworld.cc), so produce one: in a scratch copy atd83534c, add an ignored test besidemailbox.rs:966 fixture()that opensWorldMailbox::open(<capture>/world.cc), creates throughcreate_fixture, approves, activates, and ticks three times throughsubmit_clock; recordsnapshot().revision, thestate_digest, and the lastcommit_digestfromoperator_logtocapture.txt. Copyworld.ccandcapture.txtto the scratchpad. After Cut 1 the same test body, in the library'smailboxtests, reopens that file and asserts the three values are equal. Thecontroller-work.cclayout is pinned by the untouchedCONTROLLER_WORK_SCHEMA/CONTROLLER_WORK_ROWconstants (controllers.rs:75-76) and the existing v7/v8 refusal tests (controllers.rs:6487,10654); no separate capture. Get-ChildItem target -Recurse | Measure-Object Length -Sum: 7.5 GiB at the start of this pass.
- Test census:
Cut 1. Extract the library crate
-
Repo/branch: Ghostlight
codex/ghostlight-dungeon-mvpfrom02bbf62. One commit; the crate graph does not build half-moved. Depends on Cut 0 and rulings Q1-7, Q1-8. -
First: Cut 0 captures in hand;
git statusclean; working in the main checkout (LF). -
Deletes first:
crates/ghostlight-dungeon/src/world/mod.rs:28-32— the nine unused re-exported names above (leaveCellRun,ConnectorBinding,ControllerError,ControllerModels,ControllerPendingReason,ControllerRunner,ControllerWorkCustody,NarrativeRun,OperationalRun,SubmissionDisposition,open_controller_work,open_inference).crates/ghostlight-dungeon/src/world/mod.rs:61-72— the#[cfg(test)]re-export block (12 lines). The port traits and their value types in it return as ungated productionpub useunder Adds; the threefixture_inference_*names do not return.crates/ghostlight-dungeon/src/world/mod.rs:483-508—fixture_controller_opportunities(26 lines), per Q1-8 A; andcontrollers.rs:372-382,409-432—fixture_prepared_inference,fixture_inference_output,fixture_inference_events(about 40 lines) andcontrollers.rs:221-237—InferenceFault::fixture_*(17 lines), per Q1-7 A. Under Q1-7 B / Q1-8 B these stay#[cfg(test)]inside the library and the Dungeon tests that used them are deleted instead (see the classification).crates/ghostlight-dungeon/src/app_session.rs:54-91— theVerifiedPrincipalEvidencestruct, accessors, andfixture(38 lines).crates/ghostlight-dungeon/src/main.rs:7—mod world;.crates/ghostlight-dungeon/Cargo.toml:27— the directghostlight-persona-projectiondependency (controllers.rs:35is the only importer and it moves).
-
Keeps and moves:
git mv crates/ghostlight-dungeon/src/world/{action,clock,consumer, controllers,cover,elaboration,journal,mailbox,patch,sdk_inference, tool_schema,vault}.rs crates/ghostlight/src/andgit mv .../world/mod.rs crates/ghostlight/src/lib.rs. Verify withgit diff --cached -M --statthat all 13 show as renames.- Inside the moved files:
crate::world::→crate::(199 sites;super::super::paths need no change);pub(super)→pub(crate)inlib.rsonly (94 sites; submodules keep theirs);crate::app_session::VerifiedPrincipalEvidence→crate::VerifiedPrincipalEvidence(mailbox.rs:10,controllers.rs:10714,10790,10988,11473). vault.rsandsdk_inference.rsmove with the rest (Q1-2). Their fixture writes (sdk_inference.rs:1900,1983) resolve fromcrates/ghostlightat the same depth.build.rs,tests/build_provenance.rs,runtime.rs:375(web/dist) stay in Dungeon;CARGO_MANIFEST_DIR/../..still resolves.- No test moves between crates. Every test under
world::was already a library test and moves with its file (420); every Dungeon test stays in Dungeon (49). See the classification.
-
Adds:
Cargo.toml(workspace): membercrates/ghostlight.crates/ghostlight/Cargo.toml: packageghostlight; no[features]; dependenciesasync-trait,chrono,codex-connector,cultcache-rs,ghostlight-persona-projection = { path = "../ghostlight-persona-projection" },rmp-serde,serde,serde_bytes = "0.11",serde_json,sha2,thiserror,tokio,tracing,uuid,zeroize(all.workspace = trueexceptserde_bytes, which the workspace does not declare); devtempfile = "3". Every one is already inCargo.lock; no new package.crates/ghostlight-dungeon/Cargo.toml:ghostlight = { path = "../ghostlight" }in[dependencies]; nothing in dev-dependencies.Cargo.lock: the new[[package]] name = "ghostlight"entry. Commit it; Idunn builds--locked.crates/ghostlight/src/lib.rs, root:pub struct VerifiedPrincipalEvidence { account_subject_hash: String, valid_until: DateTime<Utc> }withpub fn new,pub fn account_subject_hash,pub fn valid_until, and#[cfg(test)] fn fixturedelegating tonew(Q1-3). Doc comment: the consumer verifies; the library bounds byvalid_untilatmailbox.rs:231.crates/ghostlight/src/lib.rs, the public boundary, one ungated list.pub use clock::{FictionalMinutes, TickMinutes};consumer::{CONSUMER_BODY_LIMIT, CONSUMER_CREDENTIALS_ENVIRONMENT, CONSUMER_PATCH_SCHEMA, CONSUMER_RECEIPT_SCHEMA, ConsumerRegistry, admit_document, encode_receipt};controllers::{CellRun, ConnectorBinding, ControllerError, ControllerModels, ControllerPendingReason, ControllerRunner, ControllerWork, ControllerWorkCustody, ControllerWorkLookup, ControllerWorkStore, ControllerWorkStoreError, ControllerWorkWrite, InferenceEvent, InferenceFault, InferenceOutput, InferencePort, InferencePurpose, InferenceRequest, NarrativeRun, OperationalRun, PreparedInference, SubmissionDisposition, ToolResultOracle, open_controller_work, open_inference}(plusTracingInferencePortunder Q1-7 A);cover::{AgencyGraph, Cell, Cover, CoverBudget, TickIndex, derive_cover};elaboration::{SeedOutcome, select_row};mailbox::{ConsumerPort, ControllerPort, MailboxError, SeedPort, WorldMailbox};patch::{JurisdictionKey, Statement};sdk_inference::{DEFAULT_SDK_MODEL_PREFIX, SdkBinding};vault::VaultEvidenceSource. Root items madepub:STATE_SCHEMA,state_schema_compatibility_tag,AffordanceId,CommandId,SubjectId(theopaque_uuid!macro atmod.rs:98-106emitspub(crate) struct; give it a visibility parameter and emitpubfor these three only — theissue()impls stay private and#[cfg(test)]),PrincipalId,WorldPhase,SubjectKind,ControllerMode,CreateWorldIntent,CreateJurisdictionIntent,DecisionOpportunity,DecisionInvocation,PrincipalCommandIntent,CommandBody,WorldSnapshot,OperatorEvent,CommitReceipt,SubmitReceipt,KernelError,ScaleDeficitRow, and the snapshot row types Dungeon reads throughWorldSnapshotfields (SubjectSnapshot,PlaceSnapshot,AffordanceSnapshot, and whichever others the compiler names; probe 3 stopped at theWorldSnapshotfield layer).GroupedRun(controllers.rs) is reached throughControllerRunnerreturn values and needspubwith its five fields.- How the ports stay public without the fixture constructors becoming
reachable:
InferencePort(controllers.rs:259) andControllerWorkStore(:1541) are traits whose method signatures nameInferenceRequest,PreparedInference,InferenceOutput,InferenceFault,ToolResultOracle,ControllerWork,ControllerWorkLookup,ControllerWorkWrite,ControllerWorkCustody,ControllerWorkStoreError. Those types becomepub; their fields stay private except where listed below. Nothing about them reaches an IDissue()orScopeDigest::fixture, which are private free functions and#[cfg(test)]impls (mod.rs:200-226,448) with nopubpath of any kind and no cargo feature — reachable only from inside the library's own test build. Thefixture_*functions are deleted (Q1-7 A / Q1-8 A) or stay#[cfg(test)](B), and the ten doc-tests pin both outcomes. - Fields that become
pub(38, from probe 3):CommitReceipt.{command_id, commit_digest, resulting_revision, resulting_state_digest},Constituent.subject,Cover.{cells, oversubscribed, tick},CoverBudget.{cells, constituent_cap, urgency_slots},DecisionOpportunity.{controller_mode, world_id},GroupedRun.{cell, needs, pending, resolution, submissions},OperatorEvent.{revision, speech},PreparedInference.purpose,ScaleDeficitRow.{deficit, jurisdiction, kind, target},WorldSnapshot.{affordances, draft_approvals, now, opportunities, owner, phase, places, required_approvers, revision, scale_deficit, subjects, title, world_id}.FictionalMinutes.0stayspub(crate): under Q1-8 A the one Dungeon use (runtime.rs:3579) is replaced bysnapshot.now. Fields of the row structs reached throughWorldSnapshotfollow in the next compiler round. - Methods that become
pub(36, from probe 3, by definition site):WorldMailbox::{open, create, submit_principal, submit_clock, agency_graph, operator_log, snapshot}(mailbox.rs:93,105,226,355,448, 461,474),ConsumerPort::new(:552),SeedPort::new(:594),ElaborationPort::new(:660),ControllerPort::{new, snapshot, submit_controller}(:692,696,721),ControllerRunner::{open, elaborator, seeder, custody_probe, run_narrative, run_operational, run_cell}(controllers.rs:2691,2712,2728,2744,2748,2838,3057), the receipt/pending accessors atcontrollers.rs:2397,2413,2421,2453, 2457,2461,2465,2469,2515,2519,2523,2543,2591,2595(persona_turn,re_lowering,into_parts,subject,bound_scope_digest,fresh_scope_digest,gap,mode,reason,persona_prose),sweep(elaboration.rs:607,1746),name(elaboration.rs:1511),Cover::{singletons, groups, validated}(cover.rs:163,170,198),ConsumerRegistry::{empty, from_secret_file}(consumer.rs:168,176),TickMinutes::{new, minutes}(clock.rs:45,51),CommandId::new(mod.rs:122),PrincipalId::new(mod.rs:239),Statement::new(patch.rs:281). Under Q1-7 A addPreparedInference::prepare(renamed fromprepare_invocation,controllers.rs:388),InferenceOutput::new,InferenceFault::{retryable, recovery_required, integrity_violation},InferenceRequest::provider_model(:120), andTracingInferencePort::new(:285). crates/ghostlight/src/lib.rscrate docs: the tencompile_faildoc-tests from probe 5, each pinned to its error code.crates/ghostlight-dungeon/src/runtime.rs(tests): a ~25-line copy ofsoul_no_credential_name_appears_in_the_ports_own_sourcescanningruntime.rsonly, because the library test cannot read another crate's source (see per-filesdk_inference.rs:1824-1828).crates/ghostlight-dungeon/src/app_session.rs(tests): one test assertingVerifiedPrincipalEvidence::new(occurs exactly once in the crate's production source, ataccount_for_cookie(same shape as thefrom_secret_filecount atsdk_inference.rs:1860). This is the single-minter rule the deleted private fields used to enforce (Q1-3).
-
Cross-boundary test classification (Q1-4). Every Dungeon test that reaches an item which is
#[cfg(test)]in the library today. Method: probe 5's four compile errors plus a read of each test body.# File:line Test Exercises Reaches Class 1 runtime.rs:3270the_tick_driver_never_exceeds_its_controller_permit_poolrun_cover_tickandstate.controller_permits: two singleton cells, pool of one, no overlappinginferfixture_prepared_inference,fixture_inference_output(viaCountingInferencePort,:3241);AlwaysFreshWorkStore(:3194) uses onlypubenum variantsDungeon runtime 2 runtime.rs:3337quarantine_raised_mid_tick_stops_the_sibling_cell_and_every_later_tickthe driver's controller_quarantinedflag across sibling cell and next tickfixture_prepared_inference,InferenceFault::fixture_integrity_violation(QuarantiningInferencePort,:3308)Dungeon runtime 3 runtime.rs:3500a_second_mid_turn_change_reaches_the_drivers_interrupted_armthe driver's Interruptedarm reached throughrun_cover_tick, commits throughControllerPort::submit_controller(production)fixture_prepared_inference,fixture_inference_output,fixture_inference_events,InferenceFault::fixture_recovery_required,InferenceEvent::ToolCall(pub variant),PreparedInference.purpose(InterruptingCoverPort,:3432)Dungeon runtime 4 runtime.rs:3567drive_one_tick_runs_every_cell_before_the_clock_and_all_share_one_tickdrive_one_tickordering: every cell before the clock, all cells carry the cover's tickfixture_controller_opportunities(the only caller of the sealedWorldId/SubjectId/ControllerId::issue()andScopeDigest::fixturefrom outsideworld/),FictionalMinutes(u64),AgencyGraph::default,Cell,TickIndex,CommitReceiptliteral (controller_commit,:2492)Dungeon runtime 5 runtime.rs:3697(ignored)live_smoke_seeds_then_ticks_a_world_against_the_connectorthe road test: seed from a real Vault, activate, tick against a real connector; GHOSTLIGHT_SMOKE_TRACEwrites the whole membrane (notes/local-live-smoke.md:214-246)TracingInferencePort(fixture_with,:2581-2582)Dungeon runtime Counts: 5 tests reach test-only library items; 0 are kernel tests; 5 are Dungeon runtime tests. Two other Dungeon tests touch the boundary but need nothing test-only:
runtime.rs:2502no_proposal_projection_carries_the_canonical_world_commitbuilds aCommitReceiptliteral (fourpubfields, in the boundary list) andruntime.rs:4293soul_verified_evidence_outlives_the_session_that_minted_itusesVerifiedPrincipalEvidence::fixture→::new(Q1-3). Every other Dungeon test (42) uses production ingress only.Consequence: the ruling's "move the tests into the library" has no test to move. Tests 1–4 exercise
run_cover_tick,drive_one_tick, the permit pool, and quarantine — Dungeon's tick driver — and test 5 is Dungeon's road test; moving any of them would move the driver. What they need divides into two kinds, and each is an operator question below rather than a mechanism this map invents:- tests 1, 2, 3, 5 need to construct port values (
PreparedInference,InferenceOutput,InferenceFault) and a port decorator; none of these is a sealed item under invariant 1 (mutable state, ID allocator, reducer, journal, authenticated-caller constructor) — Q1-7; - test 4 needs a
Coverto walk, and today gets it from fabricated opportunities minted with sealed IDs — Q1-8.
Expected post-move counts, under Q1-7 A and Q1-8 A: library 420 unit (the base
world::list, prefix stripped) + 10 doc; Dungeon bin 49 (the same 49 names; tests 1–4 rewritten in place, test 5 unchanged);build_provenance1; persona-projection 13; ignored 1 + 1. Under Q1-7 B tests 1–3 and thetracehalf of test 5 are deleted (Dungeon 46, or 45 if test 4 goes under Q1-8 B); the library count is unchanged either way. - tests 1, 2, 3, 5 need to construct port values (
-
Per-file changes (against
d83534c=02bbf62source):Cargo.toml:3-5: add member.crates/ghostlight-dungeon/Cargo.toml:27: replace with the library path dep.crates/ghostlight-dungeon/src/main.rs:7: delete.crates/ghostlight-dungeon/src/runtime.rs:3-22: split theuse crate::{}block;world::{…}becomesuse ghostlight::{…}(probe edit);:4: importVerifiedPrincipalEvidencefromghostlight, notapp_session.:518,519,535,1084,1088,1092,1201,1325,1621,2492,2493,2581,2582,3081, 3501,3568,3658,3666,3675,3677,3777,3782,4267:crate::world::→ghostlight::.:4313:::fixture(→::new(.:3081-3086: drop the threefixture_inference_*names.:3243,3310,3434:PreparedInference::prepare("ghostlight-controller-test", 4_102_444_800_000, request)(Q1-7 A; the two literals are the onesfixture_prepared_inferencehard-codes atcontrollers.rs:381).:3255,3442,3447:InferenceOutput::new(vec![InferenceEvent::Text(..)], "sha256:<receipt>");:3457:InferenceOutput::new(events, ..);:3318:InferenceFault::integrity_violation(..);:3479:InferenceFault::recovery_required(..).:3567-3644(Q1-8 A): replacefixture_controller_opportunities+FictionalMinutes(..)withfixture().await,active_two_cell_world,snapshot(), andagency_graph(), thenderive_cover(snapshot.world_id, snapshot.now, CLOCK_TICK_MINUTES, &snapshot.opportunities, &graph, budget); the cell count assertion becomes 2 (a genesis world has two controller-bearing subjects —:3089-3095already says this is the mostworld.createcan produce); the ordering and shared-tick assertions are unchanged.:504,2673doc comments nameworld::consumer; reword to the crate.crates/ghostlight-dungeon/src/eve.rs:3-6and:182,335,338,354,386, 405,449,494: same rewrite.crates/ghostlight-dungeon/src/mesh.rs:6-9: same.crates/ghostlight-dungeon/src/idunn_health.rs:8: same.crates/ghostlight-dungeon/src/app_session.rs:54-91: delete;:6: adduse ghostlight::VerifiedPrincipalEvidence;;:232-235: struct literal →VerifiedPrincipalEvidence::new(session.account_subject_hash.clone(), session.access_expires_at).crates/ghostlight/src/controllers.rs:221-237,372-382,409-432(Q1-7 A): delete the fixtures;:388prepare_invocation→pub fn PreparedInference::prepare(keep the body; the two in-library callers at the connector and SDK ports follow);:150addpub fn new(events, receipt_digest);:192,206new→pub fn retryable,integrity_violation→pub, addpub fn recovery_required(detail)(theRecoveryRequireddisposition has a privatenewtoday; make it the named constructor);:277-370drop#[cfg(test)]fromTracingInferencePortand make it andnewpub.crates/ghostlight/src/sdk_inference.rs:1824-1828: the scanned file list becomes["sdk_inference.rs", "controllers.rs"](paths are now crate-root relative);:1860sources[1]still indexescontrollers.rs. Theruntime.rshalf moves to Dungeon (Adds).deployment/idunn/recipe.toml:14-17:test-world-ownerbecomes two steps —test-world-library:["cargo","test","--locked","-p","ghostlight"](runs the 420 unit tests and the 10 doc-tests) andtest-dungeon: the existing-p ghostlight-dungeon --bin ghostlight-dungeonline.:51-53: acceptance argv becomes-p ghostlight --lib controllers::tests::real_codex_connector_cognition_modes_commit_speech(theworld::prefix is gone;--exactstill matches).package.json:9(dungeon:run): unchanged;:7-8use--workspaceand pick the crate up.notes/local-live-smoke.md:64: unchanged; the ignored smoke stays inruntime.rs:3697.- Docs that name the old path (owner in brackets):
README.md:165-166[Hands] add the library line;docs/public-architecture/index.md:46[Hands];docs/architecture/ghostlight-session-zero.md:21-23[Hands] rewrite "Source does not yet match" as the current rule;docs/architecture/ghostlight-world-ontology.md:939-941[Hands] the build-budget sentence now namescrates/ghostlight;notes/ghostlight-current-system-map.md:18,28,289[Mind steward];notes/fresh-workspace-handoff.md:284-286[steward];state/map.yaml:57,389-390[steward].
-
Authority map:
- Owner: the library crate
ghostlightowns the world kernel, its ontology, reducer, journal, mailbox, controllers, elaboration, cover, clock, consumer admission, inference ports (connector, SDK sidecar, and the tracing decorator), and the markdown evidence source. Its public surface is the list under Adds and nothing else. - Inputs:
VerifiedPrincipalEvidencevalues Dungeon constructs fromAppSessionOwner::account_for_cookie;CreateWorldIntent,PrincipalCommandIntent,DecisionInvocation; consumer patch bytes viaadmit_document; store paths viaWorldMailbox::openandopen_controller_work; connector and SDK bindings viaopen_inference; a consumer-implementedInferencePort/ControllerWorkStorewhen the consumer supplies its own (Dungeon's tests do; production uses the library's). - Outputs: immutable
WorldSnapshot,OperatorEvent,AgencyGraph, typed receipts (SubmitReceipt,CommitReceipt,CreationReceiptthroughMailboxError/KernelError), controller runs, cover partitions, encoded consumer receipts. - Derived state: Dungeon's Eve projection, mesh documents, Idunn health
(which reads
STATE_SCHEMAandstate_schema_compatibility_tagand derives, never restates), HTTP responses. - Forbidden writers: any path in Dungeon naming
WorldKernel,WorldState,reduce,apply_effect,CommandEnvelope,AuthenticatedCaller, the journal, anissue(), orScopeDigest::fixture— enforced by visibility with no feature that can lift it, proven by the doc-tests under--all-features. Any second minter ofVerifiedPrincipalEvidence— enforced by the Dungeon count test. Any fixture-shapedDecisionOpportunity— there is no constructor for one outside the reducer. - Shared paths: Dungeon production and Dungeon tests reach the kernel
through the same
pubitems; a Dungeon test port constructs port values through the same constructors the library's own ports use (Q1-7 A). Library-internal tests keeppub(crate)/super::access as today. Consumers other than Dungeon: none yet; Epiphany consumes onlyghostlight-persona-projection, which is unchanged (Q1-5). - Deletion line:
mod world;, the nine unused re-exports, the#[cfg(test)]re-export block, thefixture_*functions (A) and Dungeon's private principal struct are gone before the firstpubis written.
- Owner: the library crate
-
Verification (Windows workstation; proves Windows only):
- builds:
cargo check -p ghostlight(no new warnings; the 646 dead-code warnings of an under-opened boundary must be 0),cargo check -p ghostlight-dungeon --bin ghostlight-dungeon,cargo check -p ghostlight-dungeon --tests,cargo build --locked -p ghostlight-dungeon --bin ghostlight-dungeon(provesCargo.lock). Expected target-dir delta: one extra lib crate in debug; the scratch probe's check-only target was 2.3 GiB from empty, so expect well under 1 GiB incremental on the repo's 7.5 GiB. - tests:
cargo test -p ghostlight= 420 unit + 10 doc (the 420 names equal the baseworld::list with the prefix stripped);cargo test -p ghostlight-dungeon --bin ghostlight-dungeon= 49 (same names as base);cargo test -p ghostlight-dungeon --test build_provenance= 1;cargo test -p ghostlight-persona-projection= 13. Ignored counts unchanged (1 + 1). The reopen test from Cut 0 pins invariant 2 (world.ccwritten at base reopens with equal revision and digests). The doc-tests pin invariant 1. TheVerifiedPrincipalEvidence::newcount pins the single minter. The split credential-name test pins "no credential name in the port's source" on both sides. Tests 1–4 keep their names and assertions (test 4's cell count is 2). - negative:
cargo test -p ghostlight --all-features --doc= 10 passed (same as without; the command is the standing check that no later feature reopens the seal);rg -n '\[features\]' crates/ghostlight/Cargo.toml= 0;rg -n 'cfg\(any\(test' crates/ghostlight/src= 0 (no feature-gated test path exists);rg -n 'fixture_' crates/ghostlight-dungeon/src= 0 outside the Heimdall::fixture(sites (app_session.rs:560,runtime.rs:2552, 2611,4126— userg -n 'fixture_[a-z]' …which they do not match);rg -n 'crate::world::' crates= 0;rg -n '^mod world;' crates= 0;rg -n 'app_session|heimdall|eve::|idunn_health|axum|tower_http|jsonwebtoken|\bDungeon\b|Session Zero' crates/ghostlight/src= 0 (collisions checked:Dungeonsinvault.rsis excluded by\b; do not addplayer);rg -n 'pub\(super\)' crates/ghostlight/src/lib.rs= 0;rg -n 'VerifiedPrincipalEvidence::new\(' crates/ghostlight-dungeon/src= 1 outside#[cfg(test)];cargo tree -p ghostlightnames noaxum,tower-http,jsonwebtoken,cultnet-rs,cultmesh-rs,reqwest,aes-gcm,hmac,rand,base64,libc;git diff -M --stat 02bbf62..HEAD -- cratesshows 13 renames. - operator: Idunn recipe run on Yggdrasil (Linux proof): the
test-world-library,test-dungeon,build-daemonsteps and the acceptance step resolve;/srv/…/ghostlight-dungeon --state-rootreopens the liveworld.cc; health reportsconsumer-v4. OneGHOSTLIGHT_SMOKE_TRACEroad run after the cut shows the membrane trace still writes (test 5).
- builds:
-
Operator questions:
- Q1-7 Port value constructors for consumer-implemented ports. Tests 1,
2, 3 and 5 implement
InferencePortoutside the library and must returnPreparedInference,InferenceOutput,InferenceFault; today they do it through#[cfg(test)]fixture_*wrappers that exist only because those types have private constructors. A: make the constructors the port contract —PreparedInference::prepare(caller_ runtime_id, expires_at_unix_ms, request)(rename of the existingprepare_invocation, "the one place aPreparedInferenceis built", which every in-library port already calls),InferenceOutput::new(events, receipt_digest),InferenceFault::{retryable, recovery_required, integrity_violation},InferenceRequest::provider_model; delete the fivefixture_*items; makeTracingInferencePortan ungatedpubdecorator (it wraps any port and constructs nothing sealed; "production never constructs it" becomes a Dungeon rule, which is where it was enforced anyway — only the ignored smoke names it). B: keep every fixture#[cfg(test)]in the library and delete tests 1–3 and thetracefield ofLiveController(runtime.rs:2538,3726) from Dungeon; the permit-pool, quarantine, and interrupted-arm invariants of the tick driver lose their proofs and the road test loses its membrane trace. Recommended: A. A public trait whose methods must return a type nobody outside the crate can construct is not a port; it is a trait only the library can implement. Invariant 1 seals the kernel, not the inference transport, and none of these constructors mints an ID, mutates state, reduces, or writes the journal. A is a net deletion (about 85 fixture lines out, about 25 constructor lines in, of whichprepareis a rename). - Q1-8 The cover for
drive_one_tick_runs_every_cell_before_the_clock_and_all_share_one_tick. A: derive it from a real world through production ingress — the module's ownfixture()/active_two_cell_world, thensnapshot()andagency_graph()intoderive_cover; deletefixture_controller_opportunitiesand theFictionalMinutes(u64)construction; the test asserts two cells instead of three, and the ordering and shared-tick assertions are unchanged. B: delete the test. C: keepfixture_controller_opportunities#[cfg(test)]in the library and move the test there — not available,drive_one_tickis Dungeon. Recommended: A. It removes the last caller of the sealed constructors from outsideworld/, uses only what tests 1–3 already use, and the test's own doc (:3558-3565) is about ordering, not about three.
- Q1-7 Port value constructors for consumer-implemented ports. Tests 1,
2, 3 and 5 implement
Cut 2. Build and deploy references in gamecult-ops
- Repo/branch: gamecult-ops, current default branch; one commit after Cut 1 is pushed.
- First:
git -C F:\Projects\gamecult-ops statusclean;bash scripts/test-ghostlight-yggdrasil-wiring.shpasses at base. - Deletes first: none (Q1-6: retirement belongs to the deployment gate).
- Keeps and moves: none.
- Adds: none.
- Per-file changes:
runbooks/ghostlight-dungeon-yggdrasil.md:249-251: insertcargo test --locked -p ghostlightbefore the Dungeon test line;:257"one explicit Rust binary" stays true.scripts/deploy-ghostlight-yggdrasil.sh:964-970: add the library test invocation in the same container run;:1308: acceptance becomes-p ghostlight --lib controllers::tests::real_codex_connector_cognition_modes_commit_speech.scripts/test-ghostlight-yggdrasil-wiring.sh:276-278: addrequire_text "${deploy}" 'cargo test --locked -p ghostlight'.idunn/yggdrasil/bindings/ghostlight.toml.in: unchanged; it names the recipe path, and the recipe carries the commands.- The scripts remain, per the Ghostlight handoff (
:389-391), helpers not to invoke: they fail at the root/Idunn Git boundary and hold duplicate deployment authority. Editing them keeps their command list from lying about the crate graph until the deployment gate retires them; it is not an endorsement to run them.
- Authority map:
- Owner:
deployment/idunn/recipe.tomlin Ghostlight owns the build and test commands (binding:11 recipe_path). The runbook describes; the scripts are a second, dormant list pinned to the recipe's strings by the wiring test until the deployment gate removes them. - Inputs: the admitted ref and the recipe.
- Outputs: the same one binary plus
web/. - Derived state: runbook text, wiring-test assertions.
- Forbidden writers: no second list of cargo commands that drifts from
the recipe; the wiring test's
require_textlines are that check. - Shared paths: n/a.
- Deletion line: none in this cut.
- Owner:
- Verification:
- builds: none (shell and Markdown).
- tests:
bash scripts/test-ghostlight-yggdrasil-wiring.shpasses with the newrequire_text. - negative:
rg -n 'ghostlight-dungeon --bin ghostlight-dungeon' F:\Projects\gamecult-ops --glob '!docs/**'lists only lines that also sit beside a-p ghostlightline. - operator: the next Idunn deploy of a post-Cut-1 commit runs the library step.
- Operator questions: none; Q2-1 was Q1-6 and is ruled.
Subtraction ledger (estimate)
| Cut | Removed | Added | Deps / formats / targets |
|---|---|---|---|
| 0 | — | — | scratch captures only |
| 1 | mod world; (1), 9 unused re-exports (~3 lines), cfg(test) re-export block (12), Dungeon principal struct (38), Dungeon's direct persona-projection dep (1), fixture_controller_opportunities (26, Q1-8 A), inference fixtures (~60, Q1-7 A); ~140 lines |
lib manifest (~28), workspace member (1), Dungeon dep (1), lock entry (~20), principal type in lib (~30), pub use boundary (~25), doc-tests (~30), runtime credential test (~25), minter count test (~15), port constructors (~25, Q1-7 A), recipe step (~6); ~205 lines. Visibility edits: 94 pub(super)→pub(crate), ~150 pub(crate)→pub (items, fields, methods), 199 crate::world::→crate::, 32 crate::world::→ghostlight::; test 4 rewritten in place (~20 lines) |
+1 lib crate, 0 features, 0 binaries, 0 new packages in Cargo.lock; 13 files renamed (~52k lines) |
| 2 | — | ~5 lines | 0 |
Net for L0: about +65 source lines, one crate, no feature. The growth is
boundary declaration (an implicit pub(crate) becomes an explicit pub
list plus its compile-fail proofs); the liability it retires is the
convention "Dungeon does not touch kernel internals", which becomes a
compile error with no feature that can lift it, and five fixture wrappers
that existed only to work around private port constructors.
Actual, reconciled 2026-09-16 against 6ddabca (Ghostlight) and 647e57e
(gamecult-ops):
| Area | Estimate | Actual |
|---|---|---|
| Crate sources incl. in-module tests | about +65 net | +1202 / −992 (+210 net) |
| Integration tests | not planned | +479 |
| Manifests and lock | about +50 | +50 / −2 |
| Ghostlight recipe | about +6 | +10 / −5 |
| gamecult-ops | about +5 | +61 / −9 |
The miss is explained, not hidden. The single-minter scanner grew about 110
lines chasing Soul's evasions and remains evadable (S4). The boundary needed
roughly 90 more public items than this map enumerated, each forced by a
compile error or a private_interfaces warning. The integration tests buy
ruling Q1-9, which postdates the estimate. Features and binaries added:
none, as estimated.
Findings not assigned to a cut
deployment/idunn/recipe.toml:142-146,169-171declare the world slot and provided capability asghostlight.world_state.foundation.v0/foundation-v0, and:150-154the controller-work slot asghostlight.controller_work.v3; source isconsumer.v4(mod.rs:84-85, tagconsumer-v4) andcontroller_work.v15(controllers.rs:75-76). Pre-existing drift; L0 changes no schema string, so it stays.tests/build_provenance.rsis never run by the recipe or the scripts:--bin ghostlight-dungeonselects the bin's unit tests only. Acargo test --locked -p ghostlight-dungeon --test build_provenancestep would be a recipe change with its own reason.notes/ghostlight-current-system-map.md:18-19still names the state owner asghostlight.world_state.foundation.v0;:28says "the privateworld::WorldKernel". Both are stale before this cut; the steward owns the rewrite.runtime.rs:2492is aCommitReceiptliteral in Dungeon tests; it is whyCommitReceipt's four fields are in the boundary at all. Under Q1-8 A the same literal servesdrive_one_tick's fake clock submitter.PreparedInference.invocation(controllers.rs:131,CodexTransportInvocationfromcodex-connector) stayspub(crate). A consumer port that talks to a real transport would need it; no such consumer exists in L0, and opening it is a port-contract decision for the pass that brings one.web/references nothing incrates/;sidecar/claude-sdk/test/*is written by library tests underGHOSTLIGHT_WRITE_SIDECAR_FIXTURESand read otherwise — path depth unchanged, nothing to do.