Maintaining STAGE
This guide is for changing the STAGE method, plugin, schemas, tools, profiles, or studies in this repository. It is not part of the setup required to use the Safety Kernel in a game.
Read AGENTS.md before editing. It names the authoritative files, editing constraints, and current validation rules.
Sources And Repository Shape
| Path | Responsibility |
|---|---|
SPEC.md | Normative method requirements |
docs/README.md | Canonical documentation navigation and authority map |
docs/glossary.md | Canonical STAGE vocabulary |
docs/adr/ | Cross-project method decisions and supersession history |
docs/ | Explanations and evidence boundaries |
docs/failure-atlas.md | Observed anti-patterns and current corrections; research reference, not a setup checklist |
plugin/skills/ | Concise, self-contained Codex procedures |
plugin/.codex-plugin/ | Codex plugin package metadata |
.agents/plugins/ | Repository-local marketplace entry |
templates/ | Optional project-facing artifact starters |
schemas/ | Supported versioned artifact contracts |
scripts/ | Validation and named maintainer operations |
profiles/ | Non-normative personal and engine workflows |
trials/ | Source-bounded studies and compatibility evidence |
Keep the Safety Kernel separate from this Maintainer Laboratory. A game does not need the schemas, Python scripts, trials, or profile machinery merely to deliver a STAGE-controlled change.
The repository-local marketplace points at plugin/, not the repository root.
Keep that package limited to metadata and operational skills. Skills may link
to sibling packaged references, but must not depend on maintainer documents
outside plugin/; Codex installs only the package subtree.
Development Environment
Use Node.js 24 or newer for repository commit-policy tooling and the derived
documentation site. Install the root maintainer dependencies once per clone;
the prepare script activates the versioned Husky hook:
npm ci
New STAGE commits must use Conventional Commit syntax. Commitlint runs from the
local commit-msg hook and again in hosted CI. Existing history remains
unchanged. GitHub branch rules are not available for this private repository
under its current account plan, so the hosted check detects but cannot prevent
a nonconforming direct push from landing.
Dependabot uses the same contract rather than receiving a CI exception. Every
configured ecosystem prefixes generated commits and pull-request titles with
chore plus the dependency scope. The pip update spans the root maintainer
requirements, canonical project-map validator requirements, and packaged
validator requirements so a grouped update changes all three declarations
together. The repository mirror test remains authoritative and fails closed if
the generated update is incomplete.
Use a Python environment containing requirements-dev.txt. A repository-local
environment keeps the release checks independent from the host Python:
python3 -m venv .venv
.venv/bin/python -m pip install -r requirements-dev.txt
Use the selected environment consistently. If yaml is missing, the gate has
not inspected the repository; fix or select the environment rather than
reporting a project-validation failure.
STAGE's Python source baseline is Ruff. During an intentional Python cleanup, the maintainer may apply deterministic fixes and formatting:
.venv/bin/python -m ruff check scripts tests plugin/skills --fix
.venv/bin/python -m ruff format scripts tests plugin/skills
The aggregate repository gate and hosted CI are check-only:
.venv/bin/python -m ruff check scripts tests plugin/skills
.venv/bin/python -m ruff format --check scripts tests plugin/skills
Do not add a suppression, ignore, baseline rewrite, or weaker rule merely to
make a change green. Treat pyproject.toml, analyzer policy, and workflow gate
selection as human-owned quality configuration. See
Quality Gates And CI for the cross-engine policy.
Change And Release Gates
Run focused tests while editing, then the fast gate:
.venv/bin/python scripts/check_repository.py --scope change
If the Solo Unity profile changes, run its focused suite:
.venv/bin/python scripts/run_test_suite.py --suite solo-unity
Before committing a STAGE release or publishing plugin changes, run every supported contract:
.venv/bin/python scripts/check_repository.py --scope release
git diff --check
The no-argument check_repository.py behavior remains the release gate for
compatibility. The release gate covers Python lint and format status, unit
suites, frozen schema fingerprints, supported maps and studies, self-map paths,
Markdown links, active-document discoverability, skill packages, plugin
metadata, the Solo Unity contracts, and whitespace errors on both the staged
and unstaged sides of the Git index.
The hosted workflow at .github/workflows/quality.yml checks new commit
messages, runs the same complete release gate, and builds the human
documentation from a clean checkout. All jobs have read-only repository
permission, pinned action revisions, bounded runtime, and no formatting,
committing, publishing, or baseline mutation step. A hosted pass establishes
only those deterministic repository and site-build claims; it does not run a
game engine or grant a Director's Call.
Structured YAML and JSON are parsed with duplicate-key and portable-value
checks throughout the repository tooling. A repeated mapping or object key
fails the gate rather than allowing a parser's last-value-wins behavior to hide
an authority, provenance, ownership, operation, receipt, or evidence change.
YAML merge-key overrides are rejected by the same rule. Non-finite numbers and
recursive YAML alias graphs are also rejected. STAGE's shared structured-data
model is deliberately limited to null, booleans, strings, integers, finite
floats, lists, and string-keyed objects. YAML-only timestamps, binary values,
sets, and non-string keys therefore fail before schemas, hashes, receipts, or
cross-tool consumers can interpret them differently.
Production JSON writers disable Python's non-standard NaN and infinity
extensions. Generated YAML is checked against the same portable-value boundary
before publication, so STAGE does not emit artifacts that it later refuses to
read.
Atomic publication preserves an existing artifact's permission mode and uses
0644 for new maps, snapshots, plans, and evidence receipts. Temporary-file
security defaults therefore do not silently become the published interface.
Exact Unity source transfer applies requested modes through the portable path
API. It syncs published file contents on supported hosts and treats containing-
directory syncing as a POSIX durability enhancement rather than a Windows
requirement. A failed source transfer rechecks the target Git state after
rollback; STAGE reports success only when no staged, unstaged, or untracked
residue remains.
Controlled Unity bootstrap apply uses the same clean-target boundary for its single authorized mutation. It retains the exact original package manifest and mode before writing, restores both if any later validation fails, and reports an incomplete rollback when the target Git state is not clean afterward.
The local marketplace copies the physical plugin/ subtree, not only files
known to Git. The release gate therefore runs before and after its test suite
and rejects every physical plugin file that git ls-files plugin does not
declare. Remove generated bytecode, OS metadata, and other local debris before
packaging; .gitignore does not make those files safe to ship.
Gate-managed child processes run with bytecode caching disabled so the gate
does not contaminate the plugin it is inspecting.
When a changed skill or plugin needs direct package validation, also run the current Codex skill/plugin validators available in the installed toolchain. Those validators are the authority for Codex package shape.
Skill Routing
stage-deliver-change is the ordinary, implicitly available route, including
visual implementation and diagnosis. stage-orient, stage-verify, and
stage-map-project are explicit-only companion skills and declare
policy.allow_implicit_invocation: false in their agents/openai.yaml
metadata. Orientation stops after read-only context recovery. Verification is
check-only and never repairs findings. Mapping remains optional adoption,
audit, and maintenance rather than a delivery prerequisite.
Each skill metadata prompt must explicitly name its own $skill-name and stay
a short, one-sentence launcher for that route. It is not a second policy body;
keep authority, stopping, and evidence rules in SKILL.md. Keep skills
procedural, concise, and executable without files outside plugin/, while
extended rationale and historical evidence remain in maintainer documentation.
See ADR 0046.
Use progressive disclosure inside a skill when guidance applies only to a
material claim family. Keep the route, authority boundary, and stopping rule in
SKILL.md; move claim-specific detail into a sibling references/ file and
link it at the exact decision point where it becomes relevant. A reference is
not a place to duplicate the whole method or hide a rule every invocation
needs.
The frontmatter description is always-visible routing metadata. Keep it below
220 characters and use it only to state what the skill does, when it should
trigger, and the minimum boundary needed to distinguish neighboring routes.
Do not copy stopping, preservation, evidence, or execution policy into the
description; that policy belongs in the skill body. See
ADR 0047.
Local Plugin Install And Refresh
The repository is a local Codex marketplace:
codex plugin marketplace add .
codex plugin add stage-game-engineering@stage-local
codex plugin list --marketplace stage-local
Codex installs a versioned cached copy. Repository edits do not update an already-open task, and reinstalling an unchanged package version may preserve a stale cache. During local plugin iteration, use the current Codex plugin creator's cachebuster/reinstall procedure. For a real release, change the package version and reinstall after the release commit.
Always verify that plugin list reports the version declared by
plugin/.codex-plugin/plugin.json, then open a new task. Inspecting only the repository
does not prove which skill snapshot a task loaded.
Use the consumer surface that owns the routing claim. A fresh non-interactive Codex process can confirm the installed version, cache path, and implicitly available primary skill. Explicit plugin skills use their qualified invocation names:
$stage-game-engineering:stage-orient
$stage-game-engineering:stage-verify
$stage-game-engineering:stage-map-project
In the 0.5.7 release probe, a bare companion-skill mention was reported
unavailable, while a plugin-qualified mention injected the installed skill.
The visual companion was retired in 0.5.12 when its useful rules moved into
ordinary delivery. Test explicit mapping from a new task through the actual
skill picker, mention UI,
or a fresh CLI process using the qualified name. Record the Codex product,
model, and version when reporting a routing failure; repository validation and
plugin list alone do not establish task-level invocation behavior.
Human Documentation Site
website/ is a removable Docusaurus consumer of selected canonical repository
Markdown. It owns navigation and presentation only. Do not copy method facts
into generated pages or edit website/build/ and website/.docusaurus/.
Use Node.js 24 or newer:
npm --prefix website ci
npm --prefix website run build
python3 scripts/check_public_site.py --site website/build
npm --prefix website audit --omit=dev
The production build must fail on broken site routes. Repository link validation remains separately authoritative for canonical Markdown, including paths that the curated site sends back to GitHub instead of rendering. The audit command verifies that the generated static site introduces no production Node dependency surface. Docusaurus and its transitive packages are development-only build tools; review their advisories separately instead of misrepresenting them as shipped runtime dependencies.
The initial site intentionally has no frozen documentation versions. Git tags and release records already preserve history.
The public site is https://stage.zoshachi.com.
.github/workflows/docs-mirror.yml runs after a successful
STAGE quality gate workflow for main, checks out the exact verified
revision, rebuilds and screens the static artifact, then replaces the
gh-pages branch of the public
chifunt/stage-game-engineering-docs repository through a repository-scoped
deploy key. Cloudflare Pages watches that public branch and serves it at the
custom domain. A manual dispatch exists for recovery, but manual file uploads
are not the normal publication path.
Initial account setup requires:
- create the public
chifunt/stage-game-engineering-docsrepository and itsgh-pagesbranch; - add its write-enabled public deploy key and store the private half as the
canonical repository's
STAGE_DOCS_DEPLOY_KEYActions secret; - create the Cloudflare Pages project
stage-game-engineering-docs, connect only the public mirror repository, and selectgh-pagesas production; - configure no framework, build command
exit 0, output directory., automatic production deployments, and no preview branch deployments; and - attach
stage.zoshachi.comas the Cloudflare Pages custom domain.
The generated site carries no provider-owned CNAME or .nojekyll file.
Cloudflare owns DNS and TLS, while GitHub Actions owns the screened mirror
commit. GitHub Pages is not required and may be disabled after the owner no
longer needs the cutover rollback.
The public-artifact screen rejects a bounded set of local path and credential shapes. It is defense in depth, not proof that arbitrary prose is suitable for public disclosure. Review newly included document families before publishing them.
Behavior Probes
Use a behavior probe only when a skill change materially affects routing, stopping, authority, or response behavior that static package tests cannot exercise. It is an opt-in Maintainer Laboratory operation, not part of the deterministic release gate.
Capture a fresh consumer-level trace outside the repository. For example:
codex exec --ephemeral -s read-only --json \
-o /tmp/stage-probe-final.txt \
'<exact probe prompt>' > /tmp/stage-probe-trace.jsonl
Then inspect structural limits and explicitly chosen output literals:
.venv/bin/python scripts/inspect_codex_trace.py \
--trace /tmp/stage-probe-trace.jsonl \
--max-action-calls 0 \
--max-input-tokens 20000 \
--require-final-contains 'expected literal' \
--forbid-final-contains 'prohibited literal'
The inspector does not invoke Codex. It reads an existing JSONL trace, counts unique action items conservatively, aggregates reported token usage, and checks case-sensitive literals in the final completed agent message. Literal guards are not semantic proof. Passing output establishes only the declared structural and literal properties of that invocation; semantic judgment remains human-owned.
Record the Codex product and version, model, exact prompt, installed plugin version, and target branch/revision/status with any reported result. If the probe reads a protected target repository, capture its pre/post repository snapshot as well. Treat the evidence as model- and toolchain-bound, preserve failed or surprising probes when they inform a decision, and never turn one successful sample into a deterministic compatibility claim.
Release Metadata
The plugin package version is semantic major.minor.patch. SPEC.md reports
the matching method major.minor. Artifact fields such as stage_version are
separate schema selectors and do not change merely because the method releases.
For a release:
- Let
.github/workflows/release-proposal.ymlopen or update the draft after successfulmainquality, or dispatch it manually for recovery. - Review the proposed SemVer. Reconcile completed
Unreleasedentries with the generated version heading rather than discarding pre-automation work. - Run
npm --prefix website install --package-lock-only --ignore-scriptssowebsite/package-lock.jsonmatches the proposed website version. - Update the method version in
SPEC.mdonly when the release changes the method's major or minor version; a packaging-only patch keeps the existing method version. - Update current-version references in README, STAGE, compatibility, adoption, and evaluation guidance.
- Add
docs/releases/<version>.mdwith changes, compatibility, evidence, and limitations. - Run the full release gate and direct package validators, then mark the proposal ready and merge it after human approval.
- From the accepted merge, create and push the annotated
v<version>tag and create any GitHub Release explicitly. The proposal workflow never does so. - Reinstall the plugin, verify
plugin list, and test from a new task.
tests/test_plugin_packaging.py derives the method version from the plugin
package and prevents the specification, README, changelog, or release note from
silently drifting.
Conventional Commits now feed the draft Release Please proposal. It updates the root automation version, plugin version, website package version, manifest, and changelog. It deliberately leaves the website lockfile, method-version decision, current-version references, compatibility guidance, and required release record for human completion. Tag creation, package refresh, and publication remain human-approved.
The workflow prefers a scoped STAGE_RELEASE_PLEASE_TOKEN secret so opening or
updating the draft can trigger normal PR checks. Without it, the workflow uses
GITHUB_TOKEN; GitHub suppresses the initial PR event, so a subsequent human
completion commit must trigger and pass the quality workflow before merge.
Repository Actions settings keep the default workflow permission read-only and
enable GitHub Actions to create pull requests; GitHub exposes that creation
permission together with approval permission, but this workflow has no review
approval step or authority.
Maps, Schemas, And Studies
Validate a project map and its declared target paths with:
.venv/bin/python scripts/validate_project_map.py \
--manifest path/to/.stage/project.yaml \
--target path/to/game-repository
Preflight one declared operation before inspecting project preconditions or executing it:
.venv/bin/python scripts/preflight_project_operation.py \
--manifest path/to/.stage/project.yaml \
--operation operation-name \
--capability shell-execution
Exit 0 means capability, authority, and safety gates permit proceeding to the
listed project preconditions. Exit 2 is an expected blocked disposition. A
preflight never executes the operation and is not an authorization receipt.
Exit 1 includes malformed or operationally incomplete declarations. In
particular, an omitted capabilities field cannot be preflighted; use an
explicit empty list to declare that no special environment capability is
required. Preflight validates the manifest's schema and cross-record operation
semantics before evaluating the named operation, so contradictory effects,
dangling references, or unsupported exact-result claims fail closed rather
than yielding an execution disposition.
External studies use scripts/repository_snapshot.py to prove that its
captured Git source-state fields did not change and
scripts/validate_case_study.py to validate the study record. The snapshot
does not cover ignored caches, other .git internals, filesystem metadata, or
external tool state. It also requires two consecutive observations to match,
so a repository that changes during capture is rejected rather than recorded
as a mixed-time receipt. Snapshot publication is atomic and preserves a prior
output when replacement fails. A successful write syncs the receipt before
publication and, on POSIX hosts, syncs its containing directory afterward;
non-POSIX hosts retain file syncing and atomic replacement without relying on
unsupported directory descriptors. Keep observations, inferences, proposals,
and normative claims separate. Never alter an external target merely to make
its audit cleaner.
Supported 0.1 maps remain valid; new maps use 0.3. Read Artifact Compatibility before changing or migrating a schema. Versioned schemas are immutable once frozen; incompatible contracts require a new declared artifact version and migration guidance.