From 7518f14ca8415bc4827eabc62ad1685c225c6f29 Mon Sep 17 00:00:00 2001 From: garrytan Date: Wed, 28 Oct 2026 01:11:39 +0000 Subject: [PATCH] docs: agent harness registration (GSTA-135) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- bin/gstack-harness-ledger.sh | 5 +-- docs/ADDING_A_HOST.md | 33 ++++++++++++++------ docs/OPENCLAW.md | 14 +++++++-- docs/capy-install.md | 4 ++- docs/hosts.md | 43 ++++++++++++++++++++++++-- docs/install.md | 8 +++++ docs/uninstall.md | 5 +-- test/fixtures/module-size-ratchet.json | 2 ++ 8 files changed, 94 insertions(+), 20 deletions(-) diff --git a/bin/gstack-harness-ledger.sh b/bin/gstack-harness-ledger.sh index 73d0e621f..9b26448d3 100644 --- a/bin/gstack-harness-ledger.sh +++ b/bin/gstack-harness-ledger.sh @@ -2,6 +2,7 @@ # gstack-harness-ledger.sh — sourced; removes what gstack-harness registered, # from its ledger, without bun (bin/gstack-uninstall runs it before it removes # the checkout and the state root). Ledger format: lib/harness-register.ts. +# Callers set GSTACK_STATE_ROOT first (bin/gstack-state-root.sh). # # gstack_harness_unregister_ledger remove one registration # gstack_harness_unregister_all remove every registration under @@ -28,7 +29,7 @@ _gstack_harness_cut_block() { } gstack_harness_unregister_ledger() { - local ledger="$1" root="${GSTACK_STATE_ROOT:-$HOME/.gstack}" stamp kind a b target="" dest="" pack="" backup + local ledger="$1" root="${GSTACK_STATE_ROOT:?gstack-harness-ledger: GSTACK_STATE_ROOT is not set}" stamp kind a b target="" dest="" pack="" backup [ -f "$ledger" ] || return 0 stamp="$(date -u +%Y-%m-%dT%H-%M-%SZ)-$$" backup="$root/backups/harness/$stamp" @@ -54,7 +55,7 @@ gstack_harness_unregister_ledger() { } gstack_harness_unregister_all() { - local root="${GSTACK_STATE_ROOT:-$HOME/.gstack}" l + local root="${GSTACK_STATE_ROOT:?gstack-harness-ledger: GSTACK_STATE_ROOT is not set}" l for l in "$root"/harness/*.ledger; do [ -f "$l" ] || continue gstack_harness_unregister_ledger "$l" && echo "harness: $(basename "$l" .ledger)" diff --git a/docs/ADDING_A_HOST.md b/docs/ADDING_A_HOST.md index ddc7ad2a8..07e12b395 100644 --- a/docs/ADDING_A_HOST.md +++ b/docs/ADDING_A_HOST.md @@ -135,9 +135,11 @@ generator's exact `openai.yaml` shape, byte identity with the checkout) is moved to `$GSTACK_STATE_ROOT/backups/host-renders/-/`; every other file stays where it is, and only directories left empty are removed. The backup's `prune.log` lists every path. A gstack-upgrade migration runs -the same prune for an install upgraded without a setup run. Instruction-only -hosts (Hermes, OpenClaw, GBrain) are never installed by setup, so a user who -renders one by hand inside an install adds its name to the record first. +the same prune for an install upgraded without a setup run. Harness hosts +(OpenClaw, Hermes) render into a scratch directory and copy into the harness's +own folder, so they never add a render to the checkout; GBrain is never +installed by setup, so a user who renders it by hand inside an install adds its +name to the record first. ## Host tiers and capabilities @@ -149,7 +151,7 @@ rows, host errors, and the README host matrix, all checked against | Tier | Meaning | |---|---| | `full` | Installs, renders, passes the conformance kit, and has a dated certification record below. | -| `experimental` | Installs and renders and passes the conformance kit; no certification record yet. | +| `experimental` | Installs and renders and passes the conformance kit; no certification record yet. A harness host (`install.registration: 'harness'`: OpenClaw, Hermes) installs through `gstack-harness register` and is certified by `test/harness-register.test.ts` instead of the per-directory kit. | | `instruction-only` | No install arm. `./setup --host X` prints what to copy and changes nothing. | Rendering alone never certifies `full`. @@ -275,13 +277,24 @@ root's extra markdown files (`ETHOS.md`, `review/*.md`) as skills, the partial-read behavior of `view_file` on long skills, `ask_question` option handling, and an upgrade from an existing install. -## Instruction-only hosts +## Instruction-only hosts and agent harnesses -OpenClaw, Hermes, Slate and GBrain have no install arm. Hermes stays -instruction-only until someone certifies a native install (#2826 measured one -on Hermes 0.21.0): `./setup --host hermes` prints the digest path and the -generator command, and the Hermes render writes `name: gstack-` so -Hermes, which indexes skills by frontmatter name, cannot shadow them (#2825). +Slate and GBrain have no install arm. OpenClaw and Hermes are harness hosts: +`./setup --host openclaw|hermes` (and `./setup` run inside either) calls +`bin/gstack-harness register`, which renders the host into a scratch +directory and copies it into the harness's own skills folder with an ownership +ledger (`lib/harness-register.ts`). The Hermes render writes `name: +gstack-` so Hermes, which indexes skills by frontmatter name, cannot +shadow them (#2825). + +A harness that needs no render of its own (QM, dots, any other) is a row in +`TARGETS` in `lib/harness-register.ts`, not a host file: give it a mechanism, +a default destination, an inside-the-harness marker (an environment variable +the harness sets for the commands its agent runs, never a guess), a presence +check and, when it has one, the command that lists the skills it sees. Add its +reason codes to `HARNESS_CODES` with a `docs/troubleshooting.md` section, a +case to `test/harness-register.test.ts`, and a row to the table in +[hosts.md](hosts.md#agent-harnesses). ## How it works diff --git a/docs/OPENCLAW.md b/docs/OPENCLAW.md index 498484621..325c8d72d 100644 --- a/docs/OPENCLAW.md +++ b/docs/OPENCLAW.md @@ -180,8 +180,16 @@ The agent should: 3. Add dispatch routing to AGENTS.md 4. Verify with a test spawn -For gstack developers: `./setup --host openclaw` outputs this documentation. -The actual artifacts are generated by `bun run gen:skill-docs --host openclaw`. +**Native skills or ACP.** `./setup --host openclaw` (or `./setup` run inside an +OpenClaw session) gives OpenClaw's own agent gstack's skills: one `gstack` +router skill in `$OPENCLAW_STATE_DIR/skills`, rendered with OpenClaw's tool +names and pointing at the installed gstack, so the agent runs gstack's +workflows and helpers itself. Use ACP when the work should happen in a Claude +Code session with the full install (hooks, Claude tool names); use native +skills when the OpenClaw agent should do it. Both can be installed together. +Install, verify and remove: [hosts.md](hosts.md#agent-harnesses). + +The ACP artifacts in `openclaw/` are generated by `bun run gen:skill-docs --host openclaw`. ## What we don't do @@ -190,4 +198,4 @@ The actual artifacts are generated by `bun run gen:skill-docs --host openclaw`. - No bidirectional learnings bridge (brain repo is the knowledge store) - No JSON schemas or protocol versioning - No SOUL.md from gstack (OpenClaw has its own) -- No full skill porting (coding skills stay native to Claude Code) +- No hand-ported coding skills (native registration copies the generated OpenClaw render) diff --git a/docs/capy-install.md b/docs/capy-install.md index eff4169e8..1bed678f9 100644 --- a/docs/capy-install.md +++ b/docs/capy-install.md @@ -261,7 +261,9 @@ keeps the lease. `bin/gstack-lease list` shows `alive|dead|stale`, and up with the same hosts, model and browser flag (recorded in the git dir after a successful setup) and `gstack-doctor --check` passes; a warm rerun takes about a second. Each setup ends with its own `gstack ready ()`; - the installer then prints `gstack ready (claude, codex)`. + the installer then prints `gstack ready (claude, codex)`. Both name a + `--host`, so setup's agent-harness step ([hosts.md](hosts.md#agent-harnesses)) + never runs on a Capy machine. 4. `gstack-doctor --check` for the requested workflow (`--for `, `--only `, or `--require `; default `claude,codex,patch`), run with the launch PATH, so the trailer certifies the Bun workflows run. diff --git a/docs/hosts.md b/docs/hosts.md index 37079db71..97068afb7 100644 --- a/docs/hosts.md +++ b/docs/hosts.md @@ -1,11 +1,50 @@ # Running gstack on other agents The README has the host matrix: one row per agent with its tier, install -command and first invocation. This page has the rest: OpenClaw, how a checkout +command and first invocation. This page has the rest: agent harnesses (OpenClaw, +Hermes, dots, QM, Muse Code, Grok and any other), how a checkout decides which agents it renders for, the per-host notes, outside-review providers, the Codex behavioral profile, model tiers, and the design binary's models. +## Agent harnesses + +Installing gstack inside an agent harness registers its skills the harness's +own way, in the same `./setup` run. `bin/gstack-harness` does the work and +`lib/harness-register.ts` holds one target per harness. + +**When it is automatic.** `./setup` with no `--host` (or `--host auto`) +registers with the harness it runs inside: OpenClaw (its exec tool sets +`OPENCLAW_SHELL=exec`), Hermes (`HERMES_AGENT=true`) or a Dot (`HOME=/home/dot` +on a Dot's computer), or a harness found among setup's parent processes. It +prints every path it wrote and the command that removes them. An explicit +`--host claude` (or any other host) never writes under a harness folder. +`./setup --no-harness-auto` or `GSTACK_HARNESS_AUTO=0` turns the automatic step off. + +**What it writes.** Copies in the harness's own skills folder, never edits to +`openclaw.json`, Hermes's `config.yaml` or any context file you did not name. +Every file goes into a ledger, `$GSTACK_STATE_ROOT/harness/.ledger`, with +its sha256. Registering again changes only what changed; a folder or skill name +gstack did not write stops it (`HARNESS_UNOWNED_ENTRY`); `gstack-uninstall` +and `gstack-harness unregister` remove exactly the ledger's files and move any +file you edited to `$GSTACK_STATE_ROOT/backups/harness/`. `./setup +--refresh-registered` (what `/gstack-upgrade` runs) refreshes every registration. +`gstack-harness status` lists them and runs the harness's own skill list where +it has one (`visible to harness: yes`). Errors: [troubleshooting](troubleshooting.md#agent-harnesses-gstack-harness). + +| Harness | What gstack registers | Install | Verify | Remove | +|---|---|---|---|---| +| OpenClaw | one `gstack` router skill (one description line in every request; its body lists every skill and its path) in `${OPENCLAW_STATE_DIR:-~/.openclaw}/skills/gstack`; `--skills all` registers every skill as its own top-level folder | `./setup --host openclaw`, or `./setup` inside OpenClaw | `openclaw skills list --json` shows `gstack` (source `openclaw-managed`, eligible) | `gstack-harness unregister openclaw` | +| Hermes | every skill, as `gstack-` in one `gstack` category, in `${HERMES_HOME:-~/.hermes}/skills/gstack` (Hermes opens only skills it has indexed) | `./setup --host hermes`, or `./setup` inside Hermes | `hermes skills list` shows the `gstack-*` skills in category `gstack` | `gstack-harness unregister hermes` | +| dots (feder-cr) | the `gstack` router in `/home/dot/skills/gstack`, which the Dot lists every turn | `./setup` on the Dot's computer, or `gstack-harness register dots` | the Dot's Skills page, or `GET /skills` | `gstack-harness unregister dots` | +| QM | an org skill pack: `github.com/garrytan/gstack` at the installed commit, every skill imported (instruction-only there: discovery, no gstack runtime) | an org admin, with `GSTACK_QM_URL` and `GSTACK_QM_ADMIN_TOKEN` set: `gstack-harness register qm --yes` | QM's skill list for the org | `gstack-harness unregister qm --remote` archives the pack; without `--remote` only the local record goes | +| Muse Code, Grok Build | nothing: both read `~/.claude/skills`, so the Claude install (`./setup`) is what they see, with Claude tool names | `./setup` | `gstack-harness status` runs `muse skills list` / `grok inspect` and reports *discovered (Claude-rendered, unverified)* | `gstack-uninstall` | +| Grok Bot, any chat-only skill library | nothing on disk | `gstack-harness register generic --print`, then paste the router and block into the app | ask it for a gstack skill | delete the pasted text | +| any other harness | the router in an Agent Skills folder you name, and/or a 600-byte routing block between `` markers in a context file you name | `gstack-harness register generic --skills-dir ` and/or `--context `; `gstack-harness manifest generic` prints every skill as JSON | the harness's own skill list | `gstack-harness unregister generic` | + +OpenClaw's ACP route below is unchanged: native registration gives OpenClaw's +own agent gstack's skills; ACP spawns Claude Code with the full install. + ## OpenClaw OpenClaw spawns Claude Code sessions via ACP, so every gstack skill just works @@ -79,7 +118,7 @@ Outside reviews require the selected CLI to be installed and authenticated: Clau ## Instruction-only agents -**Instruction-only tier (any rules-reading agent — Zed, Amp, Jules, side projects):** +**Instruction-only tier (any rules-reading agent that loads no skills — Zed, Amp, Jules, side projects):** copy the 2KB digest at [`agents-digest/gstack-AGENTS.md`](../agents-digest/gstack-AGENTS.md) into a location your agent reads (for example, append it to your project's `AGENTS.md`). It carries gstack's ethos, reuse ladder, and voice rules — no install required. The diff --git a/docs/install.md b/docs/install.md index 6d78ed0af..d43258a29 100644 --- a/docs/install.md +++ b/docs/install.md @@ -47,6 +47,14 @@ prune-stale --repoint` removes dead gstack hook entries, re-points stale ones at the stable install, and collapses duplicates, printing one line (and writing a backup beside the file) only when it changed something. +## Agent harnesses + +Run inside OpenClaw, Hermes or a Dot, `./setup` also registers gstack's skills +with that harness, in the harness's own skills folder, and prints every path +it wrote. It never does this when `--host` names another host. Turn it off +with `./setup --no-harness-auto` or `GSTACK_HARNESS_AUTO=0`. Per-harness +details, QM and any other harness: [hosts.md](hosts.md#agent-harnesses). + ## Uninstall See [uninstall.md](uninstall.md). diff --git a/docs/uninstall.md b/docs/uninstall.md index 200f3ee24..efadb2984 100644 --- a/docs/uninstall.md +++ b/docs/uninstall.md @@ -8,7 +8,7 @@ If gstack is installed on your machine: ~/.claude/skills/gstack/bin/gstack-uninstall ``` -This handles skills, symlinks, global state (`~/.gstack/`), project-local state, browse daemons, and temp files. Use `--keep-state` to preserve config and analytics. Use `--force` to skip confirmation. +This handles skills, symlinks, harness registrations (the files `gstack-harness` wrote into OpenClaw, Hermes, a Dot or another harness, from their ledgers), global state (`~/.gstack/`), project-local state, browse daemons, and temp files. Use `--keep-state` to preserve config and analytics. Use `--force` to skip confirmation. ## Option 2: Manual removal (no local repo) @@ -49,7 +49,8 @@ rm -rf ~/.gstack rm -rf "${CODEX_HOME:-$HOME/.codex}/skills/gstack"* 2>/dev/null rm -rf ~/.factory/skills/gstack* 2>/dev/null rm -rf ~/.kiro/skills/gstack* 2>/dev/null -rm -rf ~/.openclaw/skills/gstack* 2>/dev/null +rm -rf "${OPENCLAW_STATE_DIR:-$HOME/.openclaw}/skills/gstack"* 2>/dev/null +rm -rf "${HERMES_HOME:-$HOME/.hermes}/skills/gstack" 2>/dev/null rm -rf ~/.cursor/skills/gstack* 2>/dev/null rm -rf ~/.config/opencode/skills/gstack* 2>/dev/null rm -rf ~/.gemini/antigravity-cli/skills/gstack* 2>/dev/null diff --git a/test/fixtures/module-size-ratchet.json b/test/fixtures/module-size-ratchet.json index 88e1d1a00..40d430758 100644 --- a/test/fixtures/module-size-ratchet.json +++ b/test/fixtures/module-size-ratchet.json @@ -1,5 +1,7 @@ { "newModules": [ + { "file": "lib/harness-register.ts", "movedFrom": "(new: gstack-harness registrar, GSTA-135)" }, + { "file": "lib/harness-qm.ts", "movedFrom": "(new: gstack-harness QM skill-pack target, GSTA-135)" }, { "file": "lib/design-lint/render.ts", "movedFrom": "(new: the bundled design lint, GSTA-105; ported from hallmark sloplint.mjs)" }, { "file": "lib/design-lint/render-probe.ts", "movedFrom": "(new: the bundled design lint, GSTA-105; ported from hallmark sloplint-render.mjs)" }, { "file": "lib/design-lint/render-probe-space.ts", "movedFrom": "(new: the bundled design lint, GSTA-105; ported from hallmark sloplint-render.mjs)" },