Files
gstack/docs/PROJECT_STRUCTURE.md
T
Garry Tanandcapy-ai[bot] 737ba3af40 v1.91.259.0 feat(ubicloud): /ubicloud skill with TTL-named VMs and a reaper, quota preflight, hang watchdog and one committed verify entry point (#3482)
* feat(ubicloud): /ubicloud skill with TTL-named VMs and a reaper, quota preflight, hang watchdog and one committed verify entry point

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs: CLAUDE.md names the ratcheted catalog ceiling

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* ubicloud: UBI_OWNER example is a literal value, not a free-text placeholder (INV-3 fence lint)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* ubicloud: the UBI_OWNER example is a neutral value (no thread codes in product prose)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* ubicloud: verify trusts the checkout only, watched commands keep stdin and end the remote session, quota and reap outcomes

verify: .gstack/ubicloud.json is repository content, so its setup script and
pull destinations must resolve inside --src (absolute, ~ and .. refused before
any API call) and a name it lists under pass is forwarded only when the command
line grants it (--pass NAME or --trust-config); --pass never forwards UBICLOUD_*
and refuses non-names. watched: the caller's stdin reaches the remote command,
a command that finished while the clock was read is not a timeout, a hung setup
exits 124, INT/TERM take the ssh session down, and the watchdog ends the
command's session on the VM (setsid + ~/.ubirun-<phase>.sid + pkill -s). Quota:
a cached refusal maximum is trusted for a day, the refusal names its source and
uses the 'vCPU quota exhausted' marker pregate-remote.sh retries on. reap:
returns 1 on a failed list or destroy; TTL suffixes read in base 10 and
UBI_TTL_HOURS takes 0-999. Tests: a fake API that creates plus a fake ssh run
'run' and 'verify' end to end.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* ubicloud: the remote session file is per runner invocation and setsid is optional on the image

Two watched commands on one VM wrote the same ~/.ubirun-<phase>.sid, so a cancel
could end the other's session; the file now carries the runner's pid and is
removed when the command ends. An image without setsid runs the command through
plain bash instead of failing with exit 127.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* ubicloud: pull destinations are rechecked at pull time, verify parses options by arity, a finished command keeps its status, the remote sid outlives the wrapper; docs audit

Pass-2 (Codex adversarial on the delta): an earlier pull could plant a symlink
where a later config pull lands, so under verify every destination is resolved
again (symlinks followed) right before its pull and a refused pull exits 1;
'--dest --trust-config' made an option value act as the trust flag, so verify
parses by arity; a command that finished in the instant before the watchdog's
kill exited 0 and was still reported as 124; the wrapper removed the sid file
while a background child kept the channel open, so the file now stays until
remote_cancel reads it. Docs audit (document-release, edit mode): TTL suffix and
owner rule in TESTING_INTERNALS, the reaper as the exception to 'no sweep', the
docs/skills.md transcript uses a neutral owner; CHANGELOG and the runner header
corrected on the two points it deferred.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* ubicloud: a refused pull destination creates no directory, inside or outside the checkout

inside_dir checked containment after mkdir -p, so a destination under a planted
symlink (escape/new/sub) created the outside directories before being refused.
The nearest existing ancestor is checked first; only an accepted destination is
created, and it is checked again after.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test: bin/gstack-ubi has a test lane (it execs the runner and forwards arguments)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* ubicloud: pack ships a linked worktree with a self-contained .git

A worktree's .git is a gitdir pointer into the main repository; on the VM git
could not run, so the remote pregate refused with PREGATE_VM_TREE_MISMATCH
vm=unavailable. pack now replaces it with a bare --single-branch clone of the
checked-out branch (core.bare false, the worktree's index copied in), ~45 MB for
gstack; a detached worktree is refused with the fix named.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test: take main's host-conformance C3 fix (#3479) so the remote gate can run on this tree

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* ubicloud: pack skips a split index (its shared half stays in the original git dir)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* CHANGELOG: restore main's released history under the [Unreleased] block (the merge resolution kept only the conflicted span)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* RESTAMP: 1.91.258.0 -> 1.91.259.0 (stamped; queue_mode=stamp-at-merge base=origin/main@7dc2f23192b7 policy=base origin/main@7dc2f23192b7:.gstack/ship-policy.json)
ship, next in queue at 1.91.259.0
STAMP_VERSION: 1.91.259.0
claims: ignored (stamp-at-merge)
stage version: done (wrote 1.91.259.0)
stage release_tool: done (bun scripts/gen-agents-digest.ts)
stage changelog: done (Unreleased -> 1.91.259.0)
stage stamp_paths: skipped (no stamp_paths in policy)
stage mirror: skipped (no mirror in policy)
stage apply: done (4 file(s))
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3c91fdaf8..e4f4d63ec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
 # Changelog

-## [Unreleased]
+## [1.91.259.0] - 2026-10-26

 **New `/ubicloud` skill: the Ubicloud runner moves into gstack with the discipline a shared vCPU quota needs.** Crews ran heavy suites on Ubicloud through a user-scope skill and per-lane wrappers, and paid for it in stray VMs after a machine slept, a project quota at 528 of 512 vCPUs when lanes verified in parallel, verifies that hung for an hour before anyone noticed, and a stale wrapper that skipped a service start. `scripts/ubicloud/ubi-runner.sh` now carries the fixes and `ubicloud/SKILL.md` (entry point `bin/gstack-ubi`) teaches the watched launch; `bun run test:ubicloud` and the remote pregate are unchanged.

diff --git a/VERSION b/VERSION
index c67e9e442..08528ff87 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.91.258.0
+1.91.259.0
diff --git a/agents-digest/gstack-AGENTS.md b/agents-digest/gstack-AGENTS.md
index 4bd43c922..203312cf5 100644
--- a/agents-digest/gstack-AGENTS.md
+++ b/agents-digest/gstack-AGENTS.md
@@ -1,4 +1,4 @@
-# gstack digest v1.91.258.0 — regenerate/re-copy after upgrading gstack
+# gstack digest v1.91.259.0 — regenerate/re-copy after upgrading gstack

 Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
 for agent hosts without a full skill install. The full skills add workflows,
diff --git a/node_modules b/node_modules
new file mode 120000
index 000000000..c7990ad12
--- /dev/null
+++ b/node_modules
@@ -0,0 +1 @@
+/workspace/gstack-ubi/node_modules
\ No newline at end of file
diff --git a/package.json b/package.json
index 5e6d56827..0eca459a0 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "gstack",
-  "version": "1.91.258",
+  "version": "1.91.259",
   "description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
   "license": "MIT",
   "type": "module",
FILES: CHANGELOG.md VERSION agents-digest/gstack-AGENTS.md package.json
MERGE_CONDITION: sha=76e86e08a851fb1a19bef9a947bdf0abeb87956d
next: NEW_VERSION=1.91.259.0; commit and push only the stamp diff above (FILES), with no edit of your own: the train's verify accepts the CHANGELOG only as the base's plus this PR's entry re-headed; merge with the expected head 76e86e08a8 and recompute on a stale queue.
GSTACK_RESULT: skill=restamp status=complete run=/workspace/gstack-ubi/.gstack/tmp/restamp-journal.json

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-26 23:45:45 +00:00

12 KiB

Project structure

Moved verbatim from CLAUDE.md (token-load reduction).

Directory tree

gstack/
├── browse/          # /browse skill + gstack's own headless browser engine (Playwright) — the fallback when Aside is absent
│   ├── SKILL.md.tmpl  # /browse: Aside first ({{ASIDE_SETUP}} + cookbook), $B fallback
│   ├── src/         # CLI + server + commands
│   │   ├── commands.ts  # Command registry (single source of truth)
│   │   ├── routes/      # Daemon HTTP route table (table.ts: entry shape, auth kinds, denials, dispatch) + handlers per area
│   │   └── snapshot.ts  # SNAPSHOT_FLAGS metadata array
│   ├── test/        # Integration tests + fixtures
│   └── dist/        # Compiled binary
├── hosts/           # Typed host configs (one per AI agent)
│   ├── claude.ts    # Primary host config
│   ├── claude/hooks/  # Claude Code lifecycle hooks (AUQ capture + enforcement, spawned-session directive, timeline stop, Memorable recall bridge (opt-in)); hook-log.ts is their one error-log writer
│   ├── codex.ts, factory.ts, kiro.ts  # Existing hosts
│   ├── opencode.ts, slate.ts, cursor.ts, openclaw.ts  # IDE hosts
│   ├── hermes.ts, gbrain.ts  # Agent runtime hosts
│   └── index.ts     # Registry: exports all, derives Host type
├── scripts/         # Build + DX tooling
│   ├── gen-skill-docs.ts  # Template → SKILL.md generator (config-driven)
│   ├── gen-agents-digest.ts  # Generates the budget-capped instruction-tier digest (agents-digest/)
│   ├── host-config.ts     # HostConfig interface + validator
│   ├── host-config-export.ts  # Shell bridge for setup script
│   ├── resolvers/   # Template resolver modules (preamble, aside = the Aside driver contract + research, browse = $B fallback setup + command reference, qa = surface-aware QA/exploration, sections = lazy loading, design, design-checklist = renders review/design-checklist.md from lib/design-catalog.ts, review-dashboard / plan-gates / spec-review / review-scope, outside-voice = outside-voice primitives + the one failure policy, outside-voice-steps = second opinion / adversarial / plan and doc review, gbrain, etc.)
│   ├── lib/shard-engine.ts  # Shared shard engine for both test lanes: spawn, group kill, strict verdicts, per-shard sandbox, logs, duration seeds, flags
│   ├── skill-check.ts     # Health dashboard
│   ├── test-free-shards.ts  # Free-lane policy on the shard engine (GSTACK_FREE_JOBS, opt-in flaky retry)
│   ├── test-paid-shards.ts  # Paid-lane policy on the shard engine (one Bun process per shard)
│   ├── test-strict-output.ts  # Compatibility re-export of lib/shard-engine.ts
│   ├── eval-flake-rank.ts  # Flake-telemetry dial: ranks tests by retried passes across eval runs + the free-lane ledger
│   ├── sandbox-doctor.sh  # One-command cloud-sandbox fixer: makes the free suite run green
│   └── dev-skill.ts       # Watch mode
├── lib/state-root.ts  # State-root owner (resolveStateRoot, readConfigKey); bin/gstack-state-root.sh is its sourced bash twin; docs/state-root.md
├── test/            # Skill validation + eval tests
│   ├── helpers/     # skill-parser.ts, session-runner.ts, llm-judge.ts, eval-store.ts, aside-available.ts (Aside self-skip probe); pty/ = the PTY harness (session.ts owns the runner loop, fake-session.ts the scripted test driver), imported through the claude-pty-runner.ts barrel
│   ├── fixtures/    # Ground truth JSON, planted-bug fixtures, eval baselines, impeccable engine captures (impeccable-*.json, the dumped slop page, fake-impeccable.ts shim)
│   ├── aside-driver.test.ts      # Tier 1: pins the {{ASIDE_SETUP}} contract sentences + the fallback hand-off
│   ├── aside-render.test.ts      # Tier 1 pins + fake-executable runs on both engines + a live Aside render (self-skips without Aside)
│   ├── gstack-render-cli.test.ts # Tier 1: bin/gstack-render.ts argv guards + output contract against a fake daemon
│   ├── skill-validation.test.ts  # Tier 1: static validation (free, <1s)
│   ├── gen-skill-docs.test.ts    # Tier 1: generator quality (free, <1s)
│   ├── setup-*.test.ts, relink.test.ts, hook-scripts.test.ts  # Tier 1: setup linker ownership, retired-skill prune, browser hint, rebuild check + Chromium bootstrap (anchor-sliced from setup), gstack-relink, PreToolUse hooks (free)
│   ├── skill-llm-eval.test.ts   # Tier 3: LLM-as-judge (~$0.15/run)
│   └── skill-e2e-*.test.ts       # Tier 2: E2E via claude -p (~$3.85/run, split by category)
├── qa/, qa-only/    # Surface-aware browser/functional QA; /qa-only reports and proposes tests without product edits
├── plan-design-review/  # /plan-design-review skill (report-only design audit)
├── design-review/    # /design-review skill (design audit + fix loop)
├── ship/            # Ship workflow skill
├── review/          # PR review skill (checklist.md is hand-written; design-checklist.md is GENERATED from lib/design-catalog.ts)
├── deslop-shared-libs/ # Recommendations-only audit for worthwhile shared-code extractions
├── test-audit/      # Report-first sweep for low-value tests (test value bar, audit mode)
├── eval-plan/       # Preregistered paid evals: prereg, arms, $0 dry run, priced pilot (bin/gstack-eval-plan)
├── captain/         # /captain (also /crew): one controller, many lanes; templates/ holds CREW.md, AGENTS.md, NOTES.md, DIGEST.md, the flaky ledger, the lane report, the READY block, slot call, asks and the daily automation prompt
├── plan-ceo-review/ # /plan-ceo-review skill
├── plan-eng-review/ # /plan-eng-review skill
├── autoplan/        # /autoplan skill (auto-review pipeline: CEO → design → DX → eng, eng always last)
├── benchmark/       # /benchmark skill (performance regression detection)
├── ubicloud/        # /ubicloud skill (ephemeral Ubicloud VMs: TTL + reaper, quota preflight, watchdog, verify; bin/gstack-ubi)
├── canary/          # /canary skill (post-deploy monitoring loop)
├── codex/           # /codex skill (multi-AI second opinion via OpenAI Codex CLI)
├── land-and-deploy/ # /land-and-deploy skill (merge → deploy → canary verify)
├── office-hours/    # /office-hours skill (YC Office Hours — startup diagnostic + builder brainstorm)
├── investigate/     # /investigate skill (systematic root-cause debugging)
├── spec/            # /spec skill (five-phase spec → GitHub issue, optional agent spawn, /ship auto-closes)
├── retro/           # Retrospective skill (includes /retro global cross-project mode)
├── careful/         # /careful skill; bin/check-careful.sh (PreToolUse destructive-command hook) + bin/hook-extract.sh (shared hook helpers: payload extraction, deny JSON, gstack_hook_state_root)
├── freeze/          # /freeze skill; bin/check-freeze.sh (PreToolUse edit-boundary hook; sources careful/bin/hook-extract.sh, fails closed)
├── guard/, unfreeze/  # /guard (careful + freeze in one), /unfreeze
├── gstack-upgrade/  # /gstack-upgrade skill + migrations/ (run after ./setup during an upgrade)
├── bin/             # CLI utilities (gstack-render.ts = render a local HTML file through Aside or the engine, gstack-design-detect.ts = probe/scan through a user-installed impeccable engine; gstack-design-md.ts = open DESIGN.md check/convert/tokens/mark; gstack-design-lint = the free deterministic design lint over HTML/CSS/components, --render measures in the browser (docs/design-lint.md); gstack-repo-mode, gstack-slug, gstack-config, gstack-wtree, gstack-evidence, gstack-issue-guard, gstack-relink, gstack-memorable, etc.)
├── document-release/ # /document-release skill (every-ship pre-verification audit; standalone doc updates + Diataxis coverage map)
├── document-generate/ # /document-generate skill (Diataxis doc generator: tutorial/how-to/reference/explanation)
├── cso/             # /cso skill (OWASP Top 10 + STRIDE security audit)
├── design-consultation/ # /design-consultation skill (design system from scratch)
├── design-shotgun/  # /design-shotgun skill (visual design exploration)
├── open-gstack-browser/  # /open-gstack-browser skill (launch GStack Browser)
├── connect-chrome/  # symlink → open-gstack-browser (backwards compat)
├── setup-browser-cookies/, pair-agent/, skillify/  # Fallback-engine skills (cookie import, shared-browser tunnel, codify a /scrape)
├── scrape/          # Browser data extraction (with design-review/, canary/, benchmark/); Aside first, $B fallback
├── make-pdf/        # /make-pdf skill + compiled `pdf` binary (embeds lib/aside-render.ts); test/ = unit tests (cli-exit-codes, setup-smoke, render) + e2e/*-gate.test.ts on whichever engine resolves
├── diagram/         # /diagram skill (mermaid → SVG/PNG/.excalidraw through bin/gstack-render.ts + lib/diagram-render)
├── design/          # Design binary CLI (GPT Image API)
│   ├── src/         # CLI + commands (generate, variants, compare, serve, etc.)
│   ├── test/        # Integration tests
│   └── dist/        # Compiled binary
├── agents-digest/   # Committed 2KB instruction-tier rules digest (gstack-AGENTS.md) for rules-reading hosts
├── extension/       # Chrome extension (side panel + activity feed + CSS inspector)
├── lib/             # Shared libraries (aside-render.ts = local-HTML rendering, Aside first, engine fallback; design-catalog.ts = the typed design anti-pattern catalog every design skill renders from; design-lint/ = the static and render tiers behind bin/gstack-design-lint, ported from hallmark's sloplint; design-detect-contract.ts = detector sentinel vocabulary; design-md.ts = open DESIGN.md reader/writer; dom-dump-script.ts + generated dom-dump.js = rendered-DOM dump for the detector; review-evidence.ts = review-start receipt binding, computed freshness and shared-code snapshot eligibility; frontend-scope.ts; claude-bin.ts, error-handling.ts, worktree.ts, egress-receipt.ts, context-bill.ts, redact-engine.ts, tracker-guard.ts, version-source.ts, code-intelligence/)
│   └── diagram-render/  # Vendored mermaid + excalidraw runtimes, built into one offline bundle the renderer loads
├── patches/         # bun `patchedDependencies` patches (playwright-core windowsHide)
├── docs/designs/    # Design documents (incl. IMPECCABLE_INTEROP.md = the design detector / catalog / open DESIGN.md record, and fork-port-residual-2026-09/ evaluation evidence)
├── setup-deploy/    # /setup-deploy skill (one-time deploy config)
├── .github/         # CI workflows + shared composite actions (.github/actions/) + Docker image (claude CLI pinned)
│   ├── workflows/   # evals.yml (E2E on Ubicloud), quality-gate.yml (secret scan), dependency-review.yml, osv-scanner.yml, skill-docs.yml, actionlint.yml, plus free-tests, Windows, periodic and marathon evals, CSO release, platform qualification and smaller gates
│   └── docker/      # Dockerfile.ci (pre-baked toolchain + Playwright/Chromium)
├── contrib/         # Contributor-only tools (never installed for users)
│   └── add-host/    # /gstack-contrib-add-host skill
├── setup            # One-time setup: build the browse, design + make-pdf binaries (rebuilds when any is missing or browse/make-pdf/design/lib sources changed) + best-effort Chromium bootstrap + link skills + prune retired-skill renders (both ownership-gated)
├── SKILL.md         # Generated from SKILL.md.tmpl (don't edit directly)
├── SKILL.md.tmpl    # Template: edit this, run gen:skill-docs
├── ETHOS.md         # Builder philosophy (Boil the Ocean, Search Before Building)
├── NOTICE.md        # Third-party notices: material derived from impeccable and the DESIGN.md spec (Apache-2.0), Elaya's design skills and hallmark's sloplint (MIT)
├── licenses/        # Verbatim license texts for the notices above (Apache-2.0.txt, MIT-elaya.txt, MIT-hallmark.txt)
└── package.json     # Build scripts for browse