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 i...

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)

  1. .gitagent/planner/RULES.md — SOD, trace, TMVC, Rule 4.4, break-glass.
  2. .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

ChangeSkill (typical)TMVC
src/cli/** (gantry)gantrysrc/cli/
App logic (if present)logicsrc/lib/, src/utils/
UI (if present)uisrc/components/, src/styles/
.gitagent/, hooks, workflowsPlanner + missionTier-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).

PatternWhenExample
Pass-throughCLI flag names match the command *Options interfaceinit: (options: InitOptions) => runInit(options)
Typed adapterCommander names differ or values need coercionstart (--no-writewriteMission), verify (--reasonbreakGlassReason, --scan-depth string → number)
Positional + optionsVariadic 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)

  1. Triagegantry triage "<intent>" (escalation → Planner legislates).
  2. Legislategantry legislate "<intent>" --msn MSN-NNNN --skill-key gantry (runs gap analysis internally; halts with one finding when operator answers are missing — use MCP gxt_interrogate or --interrogation-file and re-run legislate; or MCP gxt_draft_legislation with complete interrogation[]). Operator answers are quoted verbatim — never fabricated by agents.
  3. Planner commit — subject [MSN-NNNN] …, author email in repo Planner allowlist, mission file under .gitagent/missions/ included in the commit.
  4. Pingantry pin .gitagent/missions/<file>.yaml (or scripts/gxt-pin-mission.sh …). When pinned, verify / scan / attest / runtime env default to that mission and print [gantry] Using pinned mission: …. Run gantry unpin when switching MSNs.
  5. Executor scopesource scripts/gxt-runtime-env.sh (uses pin) or eval "$(gantry runtime env)" when pinned.
  6. Trace — append PASS quotes to repo-root EXECUTOR_LOG.md (see example.verify.yaml).
  7. Verifygantry verify (pinned) or gantry verify --mission .gitagent/missions/<file>.yaml. Optional: gantry verify --receipt then gantry receipt show.

Cursor session (this repo)

OpenGantry dogfoods GXT in Cursor. Other agents: docs/INTEGRATIONS.md.

One-time Cursor enablement

  1. Hooks on — Cursor Settings → Hooks (project hooks from .cursor/hooks.json must be enabled).
  2. MCP on — Cursor Settings → MCP (project server from .cursor/mcp.jsongantry mcp serve).
  3. 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):

  1. Agent: gxt_interrogate (answer findings) → gxt_draft_legislation with complete interrogation[] → human approves → gxt_execute_legislation.
  2. Planner: run returned suggested_human_action (git commit …).
  3. Agent: gxt_check_signaturegxt_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).

LayerMechanismOpenGantry repo
Context.cursor/rules/ + AGENTS.md + sessionStart hookRULES + MANIFEST + pinned mission
LegislationMCP gxt_draft_legislation / gxt_execute_legislationTwo-step chat approval before file write
Shell guardbeforeShellExecution (fallback)Blocks casual writes to foreman / RULES; asks on raw gantry legislate
Terminal envgxt-runtime-env.sh / gxt-pin-mission.shSets GXT_* for integrated terminal
Process trapgantry runtime execHeadless 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:

CheckWhenMechanism
Mission gategantry verifyMission gate_command + optional gate_success_substring
Unit testsgate / CInpm test (node:test)
Compile / typesbuild / CInpm run build (tsc)
Manifest shapeCI / pre-pushgantry check, validate-gxt.sh manifest
Changed-code qualityPR / pre-pushscripts/check-changed-code.sh — complexity, import layers, line budgets on touched src/cli/**/*.ts
Banned importsmission gate / surgeongantry check-imports
KPI thresholdsverify (optional)gantry scan + mission kpi_gate
Perimeter (CI)PRgantry perimeter --ci on protected governance paths
Trace mappingverifyVerbatim 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.

ScriptWhy repo-only
check-changed-code.shDogfood complexity/import-layer gate on PR diff
check-import-layers.mjsLegacy import scanner; dogfood delegates to gantry arch check
check-lib-cycles.mjsSpecimen maintainability gate
dev-validate-core.sh / dev-validate.shFull validation superset for maintainers
gen-asset-catalog.mjs / gen-dogfood.mjs / gen-version.mjsBuild-time generators
validate-mcp-dogfood.shMCP integration dogfood harness
release-gate-publish.sh / poll-npm-version.shnpm 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-pushgantry verify --pre-push for branch-changed missions (legislative stubs pass after git-proof); gantry check if manifest/skills changed; advisory gantry perimeter when governance files change; changed-code gate for touched src/cli/**/*.ts.

LLM evidence + KPI gate

Nondeterministic LLM checks produce committed evidence; merge stays deterministic:

  1. Mission declares optional llm_verifiers + kpi_gate (see .gitagent/planner/MISSION.schema.yaml).
  2. 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 metrics map (Record<string, number | boolean>)
    • optional findings array and exit_code field in JSON (fragment exit_code can raise report exit_code even when the process exits 0)
    • failed optional verifiers set {id}::__verifier_ok: false in the committed report; required verifiers fail the scan when the contract is not met
  3. gantry verify runs shell gate_command, then evaluates kpi_gate.thresholds against .gitagent/kpi/MSN-NNNN.json, then trace mapping.
  4. KPI stale binding mirrors trace evidence: local warnings only; --pre-push / --ci fail-closed when TMVC drifts after the report commit.
  5. Advisory rubric findings (ARCHITECTURE_RUBRIC, PERFORMANCE_RUBRIC) surface as kpi_warnings and structured findings[] on verify PASS — they never flip FAIL→PASS. See ADR-0025 and ADR-0035. Example stub: examples/performance-judge/.
  6. gantry register <dir> proposes skills from AST footprints; Planner still owns manifest edits (Rule 4.4).
  7. gantry perimeter --ci in 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:

  1. gantry verify --fix (interactive or --non-interactive) invokes a registered Code Surgeon when gate output matches a known failure:
    • GXT_BANNED_IMPORT_DETECTEDgantry check-imports stderr
    • GXT_IMPORT_LAYER_VIOLATIONcheck-import-layers.mjs --json structured report
  2. Surgeon quarantines the offending import via TypeScript AST: removes the live import declaration, injects GXT-SURGEON-QUARANTINE markers and lazy Proxy roadblocks (no silent deletion).
  3. On mutation, append [SURGEON-MUTATION] … to EXECUTOR_LOG.md, then rerun the full verify pipeline with --fix disabled.
  4. 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.md trace lines match mission PASS rows (or gantry verify on your mission passes)
  • npm run validate passes
  • Rule 4.4: manifest skill keys ↔ skills/*.md in the same change set when skills change
  • .gitagent/ARCHITECTURE.pointer.json resolved and layer rules respected for src/cli edits
  • When changing agent integration surfaces: bump templates/integrations/compatibility.json verified_date + recipe fragments in the same PR

CI parity

Pull requests run .github/workflows/gxt-validate.yml:

JobWhat it enforces
pr_governancePRs must target the integration branch (main here; default_branch in init template; override via repo variable GXT_INTEGRATION_BRANCH) — blocks stacked mission PRs
manifestgantry check, validate-gxt.sh manifest, unit tests, gantry doctor, gantry perimeter --ci on PRs
code_qualityChanged-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_verifyMission 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 (after npm run build). Policy failures print a one-line error plus Fix: remediation hints — not stack traces.
  • Set GANTRY_DEBUG=1 only when you need a stack trace for an unexpected error.

Smoke checklist (this repo)

  1. npm run build && npm test
  2. ./scripts/check-changed-code.sh origin/main HEAD (when you changed src/cli)
  3. gantry doctor → exit 0 with warnings allowed
  4. Formatter drift: gantry verify passes without --fuzzy-trace
  5. gantry metrics --json identical on two consecutive runs at same ref (including gxt_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.md link 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 (lib must not import commands, 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