Developing OpenGantry (dogfood the full stack)
This repository is the GXT specimen. Contributors and agents MUST follow the same substrate, hooks, and gantry commands we ship to adopters — not a lighter internal process.
One-time setup
npm ci
npm run build
git config core.hooksPath .githooks
gantry planner set "$(git config user.email)"
Confirm readiness:
gantry doctor
Roadmap / open work: GitHub Issues · Project board #2 · Historical backlog: docs/archive/BACKLOG.md · Release history: docs/CHANGELOG.md
Docs map: docs/index.md · root README § Documentation map.
Law + routing (before you edit)
.gitagent/planner/RULES.md— SOD, trace, TMVC, Rule 4.4, break-glass..gitagent/foreman/MANIFEST.json— skills, roots, forbidden zones.
Specimen routing (this repository only)
jeger-ai/opengantry dogfoods through the gantry skill and TMVC src/cli/ only. The specimen .gitagent/foreman/MANIFEST.json intentionally diverges from templates/.gitagent/foreman/MANIFEST.json: adopters still receive ui/logic skills and example app paths after gantry init, but this CLI-only repo does not list fictional src/components/ or src/lib/ roots.
MSN-enforced paths for commits and PR guards come from fixed substrate paths plus every tmvc_roots entry in MANIFEST (see scripts/gxt-manifest-lib.mjs).
Planner allowlist: committed .gitagent/foreman/PLANNER.allowlist (team emails for CI git-proof). Personal overrides: .gitagent/foreman/PLANNER.allowlist.local (gitignored). Run gantry planner show to confirm.
Where work belongs
| Change | Skill (typical) | TMVC |
|---|---|---|
src/cli/** (gantry) | gantry | src/cli/ |
| App logic (if present) | logic | src/lib/, src/utils/ |
| UI (if present) | ui | src/components/, src/styles/ |
.gitagent/, hooks, workflows | Planner + mission | Tier-3 — legislate first |
CLI registrar conventions (program-core.ts, program-workflow.ts)
Commander .action() handlers MUST extract options from the typed callback parameter — never this.opts() (arrow handlers have no Command this).
| Pattern | When | Example |
|---|---|---|
| Pass-through | CLI flag names match the command *Options interface | init: (options: InitOptions) => runInit(options) |
| Typed adapter | Commander names differ or values need coercion | start (--no-write → writeMission), verify (--reason → breakGlassReason, --scan-depth string → number) |
| Positional + options | Variadic intent args | (intentParts, options, _cmd) — options is the second arg; join positionals before calling run* |
Numeric or transformed flags MUST coerce in .option() parsers or the command adapter — never blind pass-through. See verifyOptionsFromCli in program-workflow.ts.
Mission loop (required for substantive work)
- Triage —
gantry triage "<intent>"(escalation → Planner legislates). - Legislate —
gantry legislate "<intent>" --msn MSN-NNNN --skill-key gantry(runs gap analysis internally; halts with one finding when operator answers are missing — use MCPgxt_interrogateor--interrogation-fileand re-runlegislate; or MCPgxt_draft_legislationwith completeinterrogation[]). Operator answers are quoted verbatim — never fabricated by agents. - Planner commit — subject
[MSN-NNNN] …, author email in repo Planner allowlist, mission file under.gitagent/missions/included in the commit. - Pin —
gantry pin .gitagent/missions/<file>.yaml(orscripts/gxt-pin-mission.sh …). When pinned,verify/scan/attest/runtime envdefault to that mission and print[gantry] Using pinned mission: …. Rungantry unpinwhen switching MSNs. - Executor scope —
source scripts/gxt-runtime-env.sh(uses pin) oreval "$(gantry runtime env)"when pinned. - Trace — append PASS quotes to repo-root
EXECUTOR_LOG.md(see example.verify.yaml). - Verify —
gantry verify(pinned) organtry verify --mission .gitagent/missions/<file>.yaml. Optional:gantry verify --receiptthengantry receipt show.
Cursor session (this repo)
OpenGantry dogfoods GXT in Cursor. Other agents: docs/INTEGRATIONS.md.
One-time Cursor enablement
- Hooks on — Cursor Settings → Hooks (project hooks from
.cursor/hooks.jsonmust be enabled). - MCP on — Cursor Settings → MCP (project server from
.cursor/mcp.json→gantry mcp serve). - Repo setup (same as above):
npm ci && npm run build
git config core.hooksPath .githooks
gantry planner set "$(git config user.email)"
gantry doctor
Restart Cursor after first clone if hooks do not appear (Output → Hooks).
Per-feature closed loop
Cursor MCP (preferred):
- Agent:
gxt_interrogate(answer findings) →gxt_draft_legislationwith completeinterrogation[]→ human approves →gxt_execute_legislation. - Planner: run returned
suggested_human_action(git commit …). - Agent:
gxt_check_signature→gxt_pin_mission→ executor edits →gxt_verify.
CLI fallback:
gantry triage "<intent>"
gantry legislate "<intent>" --msn MSN-NNNN --skill-key gantry # or substrate
# Planner: git commit -m "[MSN-NNNN] legislate …" including mission file
scripts/gxt-pin-mission.sh .gitagent/missions/MSN-NNNN.<slug>.yaml
# or: gantry pin .gitagent/missions/MSN-NNNN.<slug>.yaml
# New Agent chat → sessionStart injects GXT_TMVC_* + mission context automatically
source scripts/gxt-runtime-env.sh # integrated terminal (same pinned mission)
# … Cursor Agent work in src/cli/ or legislated scope …
# Append gate evidence to EXECUTOR_LOG.md
gantry verify # uses pin; prints [gantry] Using pinned mission: …
gantry verify --receipt # optional attestation receipt (gantry receipt show)
npm run validate
git push # pre-push: gantry verify --pre-push on branch-changed missions
Validate MCP flow locally: ./scripts/validate-mcp-dogfood.sh
Substrate upgrade loop (adopters + dogfood)
Tier-3 lifecycle updates use the installed gantry package only (no remote fetch):
npm install @jeger-ai/opengantry@latest # when a newer release exists
gantry upgrade plan --json # preview file changes (stable schema_version 1)
gantry upgrade # stage managed_strict assets + draft MSN-900x mission YAML
# Review .gitagent/.upgrade-tmp/ diff; commit mission YAML only (tmp is gitignored)
git add .gitagent/missions/MSN-9001.upgrade-vX.Y.Z.yaml
git commit -m "[MSN-9001] approve substrate upgrade to vX.Y.Z"
gantry upgrade apply --mission .gitagent/missions/MSN-9001.upgrade-vX.Y.Z.yaml
# Legacy: gantry upgrade --apply --mission … and --dry-run still work
gantry doctor
MCP: gxt_upgrade_plan / gxt_upgrade_apply (same gates as CLI).
| Layer | Mechanism | OpenGantry repo |
|---|---|---|
| Context | .cursor/rules/ + AGENTS.md + sessionStart hook | RULES + MANIFEST + pinned mission |
| Legislation | MCP gxt_draft_legislation / gxt_execute_legislation | Two-step chat approval before file write |
| Shell guard | beforeShellExecution (fallback) | Blocks casual writes to foreman / RULES; asks on raw gantry legislate |
| Terminal env | gxt-runtime-env.sh / gxt-pin-mission.sh | Sets GXT_* for integrated terminal |
| Process trap | gantry runtime exec | Headless Cursor CLI / CI only |
IDE Agent Write/Edit is advisory TMVC — stay within pinned mission scope; use runtime exec when you need manifest-enforced subprocess boundaries. See INTEGRATIONS Enforcement boundary.
Zero-trust gates (sole trust boundary)
All code changes are untrusted — human-typed or IDE-generated. OpenGantry does not deterministically attribute offline IDE provenance. Security relies on result-state checks only:
| Check | When | Mechanism |
|---|---|---|
| Mission gate | gantry verify | Mission gate_command + optional gate_success_substring |
| Unit tests | gate / CI | npm test (node:test) |
| Compile / types | build / CI | npm run build (tsc) |
| Manifest shape | CI / pre-push | gantry check, validate-gxt.sh manifest |
| Changed-code quality | PR / pre-push | scripts/check-changed-code.sh — complexity, import layers, line budgets on touched src/cli/**/*.ts |
| Banned imports | mission gate / surgeon | gantry check-imports |
| KPI thresholds | verify (optional) | gantry scan + mission kpi_gate |
| Perimeter (CI) | PR | gantry perimeter --ci on protected governance paths |
| Trace mapping | verify | Verbatim quotes from EXECUTOR_LOG.md for mission PASS rows |
External IDE skill packs are edge-only (local, gitignored). They must not be wired into .gitagent/ or shipped integration templates. Optional [SKILL-EXEC] lines in EXECUTOR_LOG.md are human triage context only — not verify evidence. See AGENTS.md and .gitagent/planner/RUNTIME.md.
Before push / PR
npm run validate
Runs dev-validate-core.sh (build, gantry check, validate-gxt.sh manifest via Node—no jq, tests, doctor, MCP dogfood, changed-code, MSN vs origin/main) then verify-pr-missions.sh (full gantry verify on branch-changed missions). Mission gates SHOULD use dev-validate-core.sh only—not npm run validate—to avoid verify/gate recursion.
Repo-only scripts: OpenGantry specimen dev gates (check-changed-code.sh, check-import-layers.mjs, dev-validate-core.sh, and related helpers) live under scripts/ but are not in the init asset catalog. scripts/gen-asset-catalog.mjs enforces this via REPO_ONLY_SCRIPTS in scripts/lib/asset-catalog-static.mjs — the generator fails if any repo-only script leaks into templates/integrations/asset-catalog.json.
| Script | Why repo-only |
|---|---|
check-changed-code.sh | Dogfood complexity/import-layer gate on PR diff |
check-import-layers.mjs | Legacy import scanner; dogfood delegates to gantry arch check |
check-lib-cycles.mjs | Specimen maintainability gate |
dev-validate-core.sh / dev-validate.sh | Full validation superset for maintainers |
gen-asset-catalog.mjs / gen-dogfood.mjs / gen-version.mjs | Build-time generators |
validate-mcp-dogfood.sh | MCP integration dogfood harness |
release-gate-publish.sh / poll-npm-version.sh | npm publish orchestration |
Adopters receive scripts/validate-gxt.sh, scripts/gxt-manifest-lib.mjs, and runtime helpers from the init catalog — not the specimen-only gates above.
Hooks (automatic when core.hooksPath=.githooks):
- pre-push —
gantry verify --pre-pushfor branch-changed missions (legislative stubs pass after git-proof);gantry checkif manifest/skills changed; advisorygantry perimeterwhen governance files change; changed-code gate for touchedsrc/cli/**/*.ts.
LLM evidence + KPI gate
Nondeterministic LLM checks produce committed evidence; merge stays deterministic:
- Mission declares optional
llm_verifiers+kpi_gate(see.gitagent/planner/MISSION.schema.yaml). - Executor runs
gantry scan --mission …— each verifier command must print JSON on stdout (trailing whitespace/newlines tolerated). Success contract per verifier:- process exit code
0 - parseable JSON object with a non-empty
metricsmap (Record<string, number | boolean>) - optional
findingsarray andexit_codefield in JSON (fragmentexit_codecan raise reportexit_codeeven when the process exits 0) - failed optional verifiers set
{id}::__verifier_ok: falsein the committed report; required verifiers fail the scan when the contract is not met
- process exit code
gantry verifyruns shellgate_command, then evaluateskpi_gate.thresholdsagainst.gitagent/kpi/MSN-NNNN.json, then trace mapping.- KPI stale binding mirrors trace evidence: local warnings only;
--pre-push/--cifail-closed when TMVC drifts after the report commit. - Advisory rubric findings (
ARCHITECTURE_RUBRIC,PERFORMANCE_RUBRIC) surface askpi_warningsand structuredfindings[]on verify PASS — they never flip FAIL→PASS. See ADR-0025 and ADR-0035. Example stub:examples/performance-judge/. gantry register <dir>proposes skills from AST footprints; Planner still owns manifest edits (Rule 4.4).gantry perimeter --ciin CI enforces verified signatures on protected paths (local mode is advisory).
See ADR-0020.
Code Surgeon (verify --fix, MSN-0047+)
Deterministic quarantine mutations for specific gate failures — an isolation layer, not a pass generator:
gantry verify --fix(interactive or--non-interactive) invokes a registered Code Surgeon when gate output matches a known failure:GXT_BANNED_IMPORT_DETECTED—gantry check-importsstderrGXT_IMPORT_LAYER_VIOLATION—check-import-layers.mjs --jsonstructured report
- Surgeon quarantines the offending import via TypeScript AST: removes the live
importdeclaration, injectsGXT-SURGEON-QUARANTINEmarkers and lazy Proxy roadblocks (no silent deletion). - On mutation, append
[SURGEON-MUTATION] …toEXECUTOR_LOG.md, then rerun the full verify pipeline with--fixdisabled. - Plain
gantry verify(no--fix) remains fail-closed and never mutates TMVC.
Language boundary: verify core phases (git_proof, gate, kpi, trace) are language-agnostic. Native surgeons require TypeScript in the adopter workspace (or gantry package root); unresolved typescript disables surgeon paths only — core verify still runs.
Import-layer JSON contract: node scripts/check-import-layers.mjs --json <files…> emits { schema_version, ok, violations[] } with stable rule_id values. Surgeons parse JSON only — not human stderr.
Implementation: src/cli/lib/surgeons/ registry + orchestration in surgeon-orchestration.ts / verify-present.ts.
Release: see CHANGELOG.md § Maintainers for npm publish and release-squash policy.
Definition of done (OpenGantry repo)
- Mission under
.gitagent/missions/with Planner[MSN-…]commit when GXT paths or behavior changed -
EXECUTOR_LOG.mdtrace lines match mission PASS rows (organtry verifyon your mission passes) -
npm run validatepasses - Rule 4.4: manifest skill keys ↔
skills/*.mdin the same change set when skills change -
.gitagent/ARCHITECTURE.pointer.jsonresolved and layer rules respected forsrc/cliedits - When changing agent integration surfaces: bump
templates/integrations/compatibility.jsonverified_date+ recipe fragments in the same PR
CI parity
Pull requests run .github/workflows/gxt-validate.yml:
| Job | What it enforces |
|---|---|
pr_governance | PRs must target the integration branch (main here; default_branch in init template; override via repo variable GXT_INTEGRATION_BRANCH) — blocks stacked mission PRs |
manifest | gantry check, validate-gxt.sh manifest, unit tests, gantry doctor, gantry perimeter --ci on PRs |
code_quality | Changed-code gates (check-changed-code.sh) on PR diff |
msn_commits | [MSN-NNNN] on commits touching MSN-enforced paths (substrate + MANIFEST tmvc_roots); or valid gxt-bypass note; or repository trusted_automation policy (gxt-manifest-lib.mjs eval-commit) |
mission_verify | Mission purity (one [MSN-NNNN] per ${base}..${head}); full gantry verify on each mission file in triple-dot diff; fails if protected paths change without a mission file unless trusted_automation policy passes (eval-range) |
Local npm run validate is the full superset (includes verify-pr-missions.sh + MSN vs origin/main). Run it before you open a PR.
gantry init ships scripts/verify-pr-missions.sh when CI is enabled. Existing installs: run gantry upgrade apply or re-init managed CI assets.
Troubleshooting verify / hooks
- Run the CLI from the repo root:
npm run gantry -- verify --mission .gitagent/missions/<file>.yaml(afternpm run build). Policy failures print a one-line error plusFix:remediation hints — not stack traces. - Set
GANTRY_DEBUG=1only when you need a stack trace for an unexpected error.
Smoke checklist (this repo)
npm run build && npm test./scripts/check-changed-code.sh origin/main HEAD(when you changedsrc/cli)gantry doctor→ exit 0 with warnings allowed- Formatter drift:
gantry verifypasses without--fuzzy-trace gantry metrics --jsonidentical on two consecutive runs at same ref (includinggxt_extension_metadata)
Code quality (changed files only)
PRs run ./scripts/check-changed-code.sh <base> <head> (also npm run check:changed locally against origin/main).
Deterministic documentation gates (repo-wide, not diff-scoped) run in dev-validate-core:
-
./scripts/assert-docs-deterministic.sh— published doc inventory,docs/index.mdlink integrity, doc-surface legacy naming drift -
./scripts/assert-no-stale-cli-naming.sh— implementation-path legacy naming drift (src/,scripts/, hooks, examples) -
ESLint complexity and function length on touched
src/cli/**/*.ts -
Import layer rules (
libmust not importcommands, etc.) -
File line budgets for non-grandfathered paths (see docs/ARCHITECTURE.md)
If CI fails:
npm run lint -- path/to/changed.ts
./scripts/check-changed-code.sh origin/main HEAD