Skip to main content

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

PathResponsibility
SPEC.mdNormative method requirements
docs/README.mdCanonical documentation navigation and authority map
docs/glossary.mdCanonical STAGE vocabulary
docs/adr/Cross-project method decisions and supersession history
docs/Explanations and evidence boundaries
docs/failure-atlas.mdObserved 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.

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:

  1. create the public chifunt/stage-game-engineering-docs repository and its gh-pages branch;
  2. add its write-enabled public deploy key and store the private half as the canonical repository's STAGE_DOCS_DEPLOY_KEY Actions secret;
  3. create the Cloudflare Pages project stage-game-engineering-docs, connect only the public mirror repository, and select gh-pages as production;
  4. configure no framework, build command exit 0, output directory ., automatic production deployments, and no preview branch deployments; and
  5. attach stage.zoshachi.com as 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:

  1. Move completed Unreleased entries under the dated release heading.
  2. Update plugin/.codex-plugin/plugin.json. Update the method version in SPEC.md only when the release changes the method's major or minor version; a packaging-only patch release keeps the existing method version.
  3. Update current-version references in README, STAGE, compatibility, adoption, and evaluation guidance.
  4. Add docs/releases/<version>.md with changes, compatibility, evidence, and limitations.
  5. Run the full release gate and direct package validators.
  6. Commit, create the annotated v<version> tag, and push both.
  7. 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 are now available as structured input for a future release-proposal workflow, but no release automation is installed. Before adopting Release Please or an equivalent, decide how it updates the plugin and site versions, method-version references, compatibility guidance, changelog, and required docs/releases/<version>.md record. A release proposal may prepare those changes, but tag creation, package refresh, and publication remain human-approved.

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.