garrytan/gstack
Read CLAUDE.md first — it is the operational manual (commands, generated-file rules, security invariants). The cardinal rules: edit .tmpl templates never generated SKILL.md; run `bun run test` (the strict shard runner) before commits and `bun run test:evals` before shipping; never run bare `bun test`; keep registry modules side-effect-free; wire egress receipts for any new off-machine send; and respect the token budgets enforced by catalog-budget/context-budget-ratchet tests. Safe edit boundaries: skill prose in .tmpl files, new commands via the commands.ts registry, new hosts via defineHost. Dangerous zones: browse/src/server.ts + extension/ + SSE/CDP paths (read docs/BROWSER_INTERNALS.md first), test/helpers (read docs/TESTING_INTERNALS.md), setup bash invariants.
- First reads for any agent: CLAUDE.md (commands + conventions + security invariants), docs/PROJECT_STRUCTURE.md (annotated tree), ARCHITECTURE.md (why), docs/BROWSER_INTERNALS.md before touching server.ts/extension/SSE/CDP, docs/TESTING_INTERNALS.md before writing E2E or passing env: to runners.
- Generated-file rule: SKILL.md, review/design-checklist.md, lib/dom-dump.js, and agents-digest output are generated — edit the .tmpl or lib source, run `bun run gen:skill-docs`, commit both; never resolve SKILL.md merge conflicts by picking a side.
- Test discipline: `bun run test` (strict shard runner, ~90-100s, ~8,700 tests) before every commit; `bun run test:evals` (paid, diff-based) before shipping; bare `bun test` walks the whole repo including paid files and bypasses the strict classifier — explicitly forbidden.
- Module convention: registry/data modules must have zero side effects on import (commands.ts, token-registry.ts, browser-skills.ts, touchfiles-data.ts are explicitly documented as such) because build scripts and tests import them; literal-only data files (touchfiles-data.ts) must stay importable by old git versions.
- lib/ vs scripts/ boundary: lib/design-catalog.ts documents that bin/ + lib/ travel to every host while scripts/ is never linked — runtime code may not import from scripts/; lib/egress-receipt.ts and browse/src/security.ts are constrained to Node builtins so the compiled binary can embed them.
- New off-machine sends must write an egress receipt (writeReceipt or _receipted_curl/_receipted_git) BEFORE the send, or carry a reasoned SCANNER_EXEMPT entry — test/egress-receipt-wiring.test.ts fails CI on an unreceipted curl/git push/fetch to a non-loopback host.
- Token budgets are ratcheted: new skills must fit catalog-budget (1,150 token-equivalents of frontmatter name+description) and context-budget-ratchet ceilings; legitimate growth requires re-running test/helpers/capture-context-budget.ts and committing the fixture.
- Skill-template conventions: each bash code block in a .tmpl runs in a separate shell (no variable persistence — carry state in prose), never hardcode branch names (use {{BASE_BRANCH_DETECT}}), express conditionals as numbered English steps, and never hardcode project-specific commands (read the target's CLAUDE.md / AskUserQuestion / persist).
- The ONNX classifier can never move into the compiled binary (onnxruntime-node dlopen fails from bun's temp extract dir) — security-classifier.ts stays in security-sidecar-entry.ts; only pure-string pieces live in security.ts.
- State paths must resolve through bin/gstack-paths (eval its output) rather than hardcoding ~/.gstack or ~/.claude — it handles GSTACK_HOME, CLAUDE_PLUGIN_DATA, CLAUDE_PLANS_DIR, and HOME-less containers; claude CLI binary resolution goes through lib/claude-bin.ts with GSTACK_CLAUDE_BIN override.
- Version bumps: VERSION file + package.json version are written by scripts/write-version-files.sh during /ship; detect-bump.ts classifies; the evals CI image deliberately excludes package.json from its hash so version bumps don't rebuild it.
- Known trap: on Windows the Bun-spawned server is broken — cli.ts hard-fails without browse/dist/server-node.mjs built by browse/scripts/build-node-server.sh; and `isProcessAlive` (signal-0, EPERM=alive) is the liveness check while GET /health is the responsiveness check — conflating them caused the busy-daemon bug class.
- PTY/E2E authoring trap: a PTY test typing a /skill slash command must pass seedSkills: true so the hermetic config dir links the LIVE working tree's SKILL.md files (the skills are the subject under test); per-test env: overrides merge last onto a complete hermetic env and are safe.
- Agent-facing safety hooks (careful/freeze/guard) are themselves repo scripts (careful/bin/check-careful.sh, freeze/bin/check-freeze.sh, careful/bin/hook-extract.sh) — editing them changes enforcement semantics; check-freeze fails closed and sources the shared hook-extract helpers.