Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 75 additions & 32 deletions .claude/verify-report.json
Original file line number Diff line number Diff line change
@@ -1,36 +1,79 @@
{
"branch": "fix/verify-gate-flags",
"generated": "2026-08-20",
"gates": [
{ "name": "Typecheck", "status": "Passed",
"detail": "npm --prefix src/engine run typecheck -> tsc --noEmit && tsc --noEmit -p tsconfig.scripts.json; exit 0." },
{ "name": "Lint", "status": "Passed",
"detail": "npm --prefix src/engine run lint -> eslint src scripts; exit 0, no findings. Covers the determinism guard." },
{ "name": "Test", "status": "Passed",
"detail": "vitest run: Test Files 79 passed (79), Tests 1101 passed (1101); exit 0." },
{ "name": "Pack package", "status": "Passed",
"detail": "cd src/engine; npm pack --silent -> the-running-dev-game-engine-0.10.0.tgz, 311 entries; exit 0." },
{ "name": "Inspect tarball", "status": "Passed",
"detail": "All four CI assertions re-run locally over the packed tarball: no entries under src/, no tsconfig*.json, no .test.* build artifacts, and package/dist/ present. Zero violations." },
{ "name": "Consumer smoke", "status": "Passed",
"detail": "consumer-smoke rebuilt from a clean tree (node_modules, package-lock.json, dist removed): npm run install:engine, npm run build (tsc), npm run smoke -> node dist/smoke.js; exit 0." },
{ "name": "Parse-check PowerShell scripts", "status": "Passed",
"detail": "Parser::ParseFile over Get-ChildItem -Recurse -Filter *.ps1, matching verify.yml's scope exactly with no exclusions: 28 files parsed, zero parse errors; exit 0." },
{ "name": "Run Pester tests", "status": "Passed",
"detail": "Invoke-Pester -Path tools -Output Detailed -PassThru: Tests Passed: 94, Failed: 0, Skipped: 0, Inconclusive: 0, NotRun: 0." },
{ "name": "Validate Markdown links, terminology, and generated files", "status": "Passed",
"detail": "./build/Test-Documentation.ps1: human-documentation drift check passed across 18 generated engine page(s), compatibility pointers, and the guide. Documentation checks passed across 123 Markdown file(s). Exit 0." },
{ "name": "Build documentation", "status": "DidNotRun",
"reason": "The CI step runs /template/scripts/docs-build.ps1 inside the docs-template container. Its local stand-in, ./docs.ps1 -BuildOnly, is absent from the repository root: docs.ps1 is generated by Invoke-SetupDocs and deliberately not committed, so it does not exist in this checkout. Docker is running, so the missing script is the only blocker. The 'Verify Documentation Build' required check on the pull request is where this answer comes from." },
{ "name": "Build and verify landing page", "status": "Passed",
"detail": "npm --prefix src/engine run build, then npm --prefix site run check: format:check, lint, typecheck, jsdom suite, real-browser suite, test:build (both HTML entry points carry required static metadata; 3 bundles free of Node-only runtime references) and test:merge ('Package-backed merge proven: /, /roadmap/ and the protected docs/ subtree all verified, and the top-level docs/ guard rejects a bad landing build'). Exit 0." },
{ "name": "Merge landing page into documentation build", "status": "DidNotRun",
"reason": "npm --prefix site run merge overlays the landing page onto a completed docs build at artifacts/docs, which does not exist locally because the 'Build documentation' gate above could not run. Note the merge LOGIC is separately covered: test:merge inside 'Build and verify landing page' passed. What is unproven locally is the real overlay against a real docs build; the 'Verify Documentation Build' check on the pull request covers it." },
{ "name": "Test the host", "status": "DidNotRun",
"reason": "dotnet restore cannot authenticate to GitHub Packages. Re-confirmed this run: NUGET_GITHUB_TOKEN is unset and restore exits 1 with 'error NU1301: Failed to retrieve information about SubZeroDev.Platform.Hosting from remote source https://nuget.pkg.github.com/The-Running-Dev/download/subzerodev.platform.hosting/index.json' and 'Response status code does not indicate success: 401 (Unauthorized)'. The package is published from the sibling SubZeroDev.Platform repository and needs the cross-repo REGISTRY_TOKEN that host-image.yml supplies in CI. Nothing compiled or executed, so this is not a test failure." },
{ "name": "Positive route and probe smoke", "status": "DidNotRun",
"reason": "Requires the host image, whose Docker build performs the same SubZeroDev.Platform.Hosting restore that returned 401 above, fed by the nuget_github_token build secret from REGISTRY_TOKEN. host-image.yml also fails its own job up front when REGISTRY_TOKEN is unset. Docker is available locally, so the cross-repo package credential is the sole blocker, not the daemon." },
{ "name": "Negative fixture -- corrupted artifact must fail to start", "status": "DidNotRun",
"reason": "Builds FROM the smoke image via tools/host-smoke/Dockerfile.negative-fixture, so it is blocked by the same missing REGISTRY_TOKEN credential as the image build and the host tests above. Docker is available; the credential is not." }
{
"name": "Typecheck",
"status": "Passed",
"detail": "npm --prefix src/engine run typecheck — tsc --noEmit && tsc --noEmit -p tsconfig.scripts.json — completed with no errors."
},
{
"name": "Lint",
"status": "Passed",
"detail": "npm --prefix src/engine run lint — eslint src scripts — completed with no errors."
},
{
"name": "Test",
"status": "Passed",
"detail": "npm --prefix src/engine test — vitest run — Test Files 79 passed (79), Tests 1121 passed (1121), Duration 7.78s."
},
{
"name": "Pack package",
"status": "Passed",
"detail": "cd src/engine; npm pack --silent — produced the-running-dev-game-engine-0.10.0.tgz with no errors."
},
{
"name": "Inspect tarball",
"status": "Passed",
"detail": "tar -tzf the-running-dev-game-engine-0.10.0.tgz | wc -l => 311 entries. grep -c '^package/src/' => 0. grep -c 'tsconfig' => 0. grep -c '.test.' => 0. grep -c '^package/dist/' => 309. No src/, no tsconfig*.json, no .test.* artifacts, package/dist/ present."
},
{
"name": "Consumer smoke",
"status": "Passed",
"detail": "Cleared consumer-smoke/{node_modules,package-lock.json,dist}; npm run install:engine (packed the-running-dev-game-engine-0.10.0.tgz, added 5 packages); npm run build (tsc -p tsconfig.json, no errors); node dist/smoke.js exited with code 0."
},
{
"name": "Parse-check PowerShell scripts",
"status": "Passed",
"detail": "[System.Management.Automation.Language.Parser]::ParseFile over every *.ps1 outside node_modules/consumer-smoke — Checked 21 files, 0 with errors."
},
{
"name": "Run Pester tests",
"status": "Passed",
"detail": "Invoke-Pester -Path tools -Output Detailed -PassThru — Tests completed in 29.56s. Tests Passed: 94, Failed: 0, Skipped: 0, Inconclusive: 0, NotRun: 0."
},
{
"name": "Validate Markdown links, terminology, and generated files",
"status": "Passed",
"detail": "./build/Test-Documentation.ps1 — 'Human-documentation drift check passed across 18 generated engine page(s), compatibility pointers, and the guide.' 'Documentation checks passed across 123 Markdown file(s).' Exit code 0."
},
{
"name": "Build documentation",
"status": "DidNotRun",
"reason": "docs.ps1 is not present in this checkout (it is generated by Invoke-SetupDocs, not committed). A Docker-based host image build was also attempted for other gates and its docs stage (RUN pwsh /template/scripts/docs-build.ps1) was cancelled when a sibling stage failed, so no local run of this gate's underlying build reached completion. Deferred to the 'Build documentation' step of the docs-ci.yml CI check on the pull request."
},
{
"name": "Build and verify landing page",
"status": "Passed",
"detail": "npm --prefix src/engine run build (already produced by other gates) then npm --prefix site run check — oxlint clean; tsc -b clean; vitest run: Test Files 2 passed (2), Tests 15 passed (15); test:browser: Test Files 1 passed (1), Tests 11 passed (11); test:build: vite build succeeded (8 modules, dist output written), verify-build.mjs reported both entry points contain required static metadata and bundles are free of Node-only runtime references; test:merge: verify-merge.mjs reported 'Package-backed merge proven: /, /roadmap/ and the protected docs/ subtree all verified, and the top-level docs/ guard rejects a bad landing build.'"
},
{
"name": "Merge landing page into documentation build",
"status": "DidNotRun",
"reason": "Requires a completed production docs build in artifacts/docs, which needs docs.ps1 (not present in this checkout, see 'Build documentation'). Deferred to the corresponding docs-ci.yml CI check on the pull request."
},
{
"name": "Test the host",
"status": "DidNotRun",
"reason": "dotnet test src/host/SubZeroDev.GameEngine.Host.Tests/SubZeroDev.GameEngine.Host.Tests.csproj needs NUGET_GITHUB_TOKEN for the sibling-repo (SubZeroDev.Platform.Hosting) NuGet feed; this environment variable is not set locally. Deferred to the 'Test the host' step of host-image.yml on the pull request."
},
{
"name": "Positive route and probe smoke",
"status": "DidNotRun",
"reason": "Requires the built host image (docker build -t subzerodev-gameengine-host:smoke .). Docker daemon is available and the build was attempted, but the host-build stage's dotnet restore of SubZeroDev.Platform.Hosting failed: 'error : Value cannot be null or empty string. (Parameter 'username')' because /run/secrets/nuget_github_actor and /run/secrets/nuget_github_token are not present locally (NUGET_GITHUB_TOKEN not set). Deferred to host-image.yml on the pull request."
},
{
"name": "Negative fixture -- corrupted artifact must fail to start",
"status": "DidNotRun",
"reason": "Requires the base image subzerodev-gameengine-host:smoke as --build-arg BASE_IMAGE, which did not build locally (see 'Positive route and probe smoke' — missing NUGET_GITHUB_TOKEN for the sibling-repo NuGet feed). Deferred to host-image.yml on the pull request."
}
]
}
18 changes: 14 additions & 4 deletions design/20-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -5935,7 +5935,12 @@ relevant canonical comparator. Ordered `unlock`/`lock` or policy writes to the s
last-write-wins. Each producing system groups numeric deltas by target/scalar, sums, then
clamps once before it exits; systems 1, 4, and 11 explicitly defer building-meter deltas to
system 14 so policy/service/litter/staff sources compose there. Systems after 14 apply their own
group locally—effects never wait for the next tick without persisted state.
group locally—effects never wait for the next tick without persisted state. A `wear` delta is
therefore not authorable on a list one of those later systems owns (`objectives.onCompleted`,
`failures.onTriggered`, and `incidents.onResolve` on a duration-bearing incident): it would clamp
independently and could never reach §4.16's broken transition, so validation rejects it rather
than leaving the gap reachable by content. `cleanliness` carries no such transition and stays
legal there, clamped locally like any other late group.
Finance/counter/objective deltas use checked addition. An effect cannot emit another effect
or call a system recursively. If starting an incident must sample
a non-constant duration range, it draws from the owning system's
Expand Down Expand Up @@ -6158,6 +6163,8 @@ rather than summarized.
| `invalid_building_geometry` | 1 | a building declares no entrances or no allowed rotations |
| `invalid_inventory` | 1 | inventory units or capacity are inconsistent |
| `invalid_work_rate` | 1 | a staff work rate is not positive effort per tick |
| `invalid_initial_wear` | 1 | a building's `initialWear` is not positive, so it could never reach the broken transition |
| `undeferrable_building_meter_effect` | 1 | a `wear` delta sits on an effect list owned by a system after 14 (§9.2) |
| `invalid_time_limit_pair` | 1 | a scenario declares a time limit without its failure, or the reverse |
| `disconnected_map` | 2 | the map has no traversable edges |
| `inert_scenario` | 2 | a scenario declares neither objectives nor failures |
Expand All @@ -6168,11 +6175,13 @@ is deliberate reuse of a meaning, not a collision: `ReasonCode` is a flat string
namespaced only by the *message* key (`world-graph.reason.duplicate_id`), so the same failure
reads the same way across kinds and a client switching on it needs no per-kind branch.

**Audit codes — `StateChange.reason` values (04 §12, §13 below).** All ten ride on
**Audit codes — `StateChange.reason` values (04 §12, §13 below).** All eleven ride on
`visible: true` records, so each owes a resolvable message exactly as a rejection does; there
is no audit namespace exempt from §12's completeness rule. They split by *how the reason
reaches the record*, which is not decoration — it is the distinction that let five of them go
unregistered through three units and one reconciliation pass.
unregistered through three units and one reconciliation pass. W83's `building_broken` was the
second occurrence, caught in review rather than by a gate, which is what the warning below
predicts and why the count above is stated rather than left to be inferred from the rows.

| Code | Emitted by | Arrives as |
|---|---|---|
Expand All @@ -6185,6 +6194,7 @@ unregistered through three units and one reconciliation pass.
| `incident_resolved` | the `staff-work` and `incidents` systems | `EffectContext.reason` |
| `objective_met` | the `objectives` system | `EffectContext.reason` |
| `failure_triggered` | the `failure` system | `EffectContext.reason` |
| `building_broken` | the `cleanliness-wear` system, on the wear-hits-zero transition (§4.16) | a literal at the `record()` call site |

> **The indirect five are the ones to watch, and the reason this table exists.** A reason
> threaded through `EffectContext` is not visible at any call site that also names a
Expand Down Expand Up @@ -6826,7 +6836,7 @@ interface BuildingDefinitionBase<TText> {
constructionWork: number; // effort units; 0 = immediate MVP construction
constructionTaskPriority: number; // signed integer candidate priority
operatingCostCentsPerDay: number; // non-negative integer cents
initialWear: number; // integer 0..100
initialWear: number; // integer 1..100 (0 could never break)
initialCleanliness: number; // integer 0..100
placementRules: readonly PlacementRule[];
adjacencyEffects: readonly AdjacencyEffect[];
Expand Down
Loading
Loading