Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .claude/commands/browser-debugger.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
description: "Open a browser (Chrome/Firefox/Edge) with remote-debugging port 9888 so Claude can inspect/control it — optionally starting `npm run dev` first and pointing the browser at it. Usage: /browser-debugger [--browser=chrome|firefox|edge] [--run-dev]. Read-only dev utility, no code changes, no seal gate."
argument-hint: "[--browser=chrome|firefox|edge] [--run-dev]"
---

# /browser-debugger — open a browser with CDP debugging on port 9888

Local dev-environment utility only — never touches project source code,
never commits/pushes/deploys. `gate: none`, same class as `/hub-tokens`: no
seal gate, no evidence note, no worker identity needed.

## Steps
1. **Parse args.** `--browser=<name>` — one of `chrome`/`chromium`,
`firefox`, `edge` (case-insensitive), default `chrome` if omitted or
malformed — don't fail on a typo'd value, just tell the operator you
fell back to the default. `--run-dev` — boolean flag, no value.
2. **If `--run-dev` was passed:**
a. Check `package.json` at the repo root for a `scripts.dev` entry. Not
present → skip starting anything, note "no `dev` script in
package.json" in the final report, fall through to step 3 with the
default target URL (`http://localhost:3000`).
b. Present → detect the package manager from the lockfile
(`package-lock.json` → `npm`, `yarn.lock` → `yarn`,
`pnpm-lock.yaml` → `pnpm`; default `npm` if none found), then start
`<pm> run dev` in the background (`run_in_background: true`),
capture its output.
c. Poll the dev server's own output for a local URL (common patterns:
"Local:", "http://localhost:", "ready on") for up to ~15s. Found →
use that URL as the target. Not found in time → fall back to
`http://localhost:3000`, note in the report that the URL was
guessed, not read from actual output.
3. **Resolve the browser executable** for `--browser` + the current OS
(macOS/Linux/Windows) — real installed paths, don't assume:
- macOS: `/Applications/Google Chrome.app/...`,
`/Applications/Firefox.app/...`,
`/Applications/Microsoft Edge.app/...`.
- Linux: `google-chrome`/`chromium`, `firefox`, `microsoft-edge` on
`$PATH`.
- Windows (git-bash/WSL interop): standard `Program Files` install
paths for each.
Not found → report the real error (which path/command was tried) and
stop — don't silently fall back to a different browser than requested.
4. **Launch** with remote debugging on port **9888**
(`--remote-debugging-port=9888` for Chrome/Edge/Chromium;
`--start-debugger-server 9888` for Firefox), pointed at the URL from
step 2, as a background process. Capture the PID.
5. **Verify the debug port is actually responding** —
`curl -fsS http://localhost:9888/json/version` (or equivalent), read
the output back. Fails → report the real error, don't claim success.
6. **Report — confirm message, exactly these lines:**
```
🌐 Browser: <chrome|firefox|edge> (PID <pid>)
🔗 Debug port: localhost:9888 — <responding | not responding: reason>
📍 URL: <url opened>
🚀 Dev server: <started via <pm> run dev | already running | no dev script found | skipped (--run-dev not passed)>
```

## Failure branches
| Failure | Handling |
|---|---|
| Requested browser not installed / path not found | Report the exact path/command tried, stop — don't silently substitute another browser |
| Port 9888 already in use | Report it (`lsof -i :9888` output), ask whether to reuse the existing instance or stop — don't kill another process unasked |
| `--run-dev` passed but no `dev` script in `package.json` | Note it in the report, open the browser at the default URL instead of failing the whole command |
| Dev server starts but never prints a detectable local URL | Fall back to `http://localhost:3000`, say so plainly in the report — don't guess a wrong port silently |

## Runtime
`/browser-debugger [--browser=chrome|firefox|edge] [--run-dev]`. Purely
local: no git, no network call other than the local CDP check, no
project-file edits. Safe to re-run.
35 changes: 35 additions & 0 deletions .claude/commands/issues-ls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
description: "List open GitHub issues for this repo, if it's hosted on GitHub. Read-only, no side effects."
argument-hint: "[--state open|closed|all] [gh issue list flags...]"
---

# /issues-ls — list GitHub issues for this repo

Read-only. Lists issues from GitHub if (and only if) this repo's remote is
a GitHub repo — no writes, no approval gate needed.

## Steps
1. **Check the remote is GitHub.** Run `git remote get-url origin` (fall
back to another remote if `origin` doesn't exist). If it doesn't
resolve, or the host isn't `github.com`, stop and report "not a GitHub
repo — skip" — not an error, just nothing to do.
2. **Check `gh` CLI is available and authenticated.** Run `gh auth
status`. If `gh` isn't installed or isn't authenticated, stop and
report the exact output plus a one-line hint (`gh auth login`) — don't
work around it (no calling the GitHub REST API directly with a token).
3. **List issues.** `gh issue list --state open --limit 50` by default.
If `$ARGUMENTS` is given, pass it through verbatim as extra flags to
`gh issue list` instead of the defaults (e.g. `/issues-ls --state all`,
`/issues-ls --label bug --assignee @me`).
4. **Display as a table**: issue number, title, labels, state,
updated-at, URL — whatever `gh issue list` returns is enough, don't
reformat or re-fetch per-issue unless the arguments ask for more detail
(e.g. a `--json` variant).
5. **No writes.** Never close/comment/edit an issue from this command —
that's a separate manual `gh issue` call (or `/release`'s own
issue-closing step), out of scope here.

## Runtime
Requires `gh` CLI authenticated against the project's GitHub remote. If
the repo isn't on GitHub, or `gh` isn't set up, report why and stop — no
fallback to scraping or an unauthenticated API call.
105 changes: 82 additions & 23 deletions .claude/skills/hub-tokens/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
---
name: hub-tokens
description: "Report the token cost of agent-hub/ — how much gets read every worker session (recurring cost) vs cold storage (evidence/, archived diagram rows) that's only opened on demand. Usage: /hub-tokens. Read-only, makes no changes."
description: "Report the token cost of agent-hub/ — how much gets read every worker session (recurring cost) vs cold storage (evidence/, archived diagram/PROJECT.md/log rows) that's only opened on demand. Usage: /hub-tokens. Read-only, makes no changes."
---

# /hub-tokens — measure agent-hub's token cost

Read-only diagnostic. No file changes, no seal gate needed.

## Why this exists
`haven/diagrams/dev-loop.prime-mermaid.md` is read in full by every worker
session (implementer, verifier, and every subagent spawned for a `/todo`
verify pass re-loads it from scratch). Left unchecked it grows forever and
becomes the single biggest recurring token cost in the hub — this is what
the `dev-loop-archive.md` convention (see the diagram file's own header
note) exists to bound. This command measures whether that's actually
happening, instead of guessing.
`haven/diagrams/dev-loop.prime-mermaid.md` and `doctrine/domains/
PROJECT.md` are both read in full by every worker session (implementer,
verifier, and every subagent spawned for a `/todo` verify pass re-loads
them from scratch). Left unchecked either grows forever and becomes the
single biggest recurring token cost in the hub — this is what the
`dev-loop-archive.md` / `PROJECT-archive.md` conventions (see each file's
own header note) exist to bound. This command measures whether that's
actually happening, instead of guessing.

There's no exact tokenizer available here — the report uses `bytes / 4` as
a documented, consistent proxy (not a real token count). Good enough to
Expand All @@ -32,12 +33,23 @@ HUB="$ROOT/agent-hub"
bytes_glob() { find $1 -maxdepth "${2:-99}" -type f \( -name "*.md" -o -name "*.yaml" -o -name "*.yml" \) 2>/dev/null -exec cat {} + 2>/dev/null | wc -c | tr -d ' '; }
bytes_glob_exclude() { find "$1" -type f \( -name "*.md" -o -name "*.yaml" -o -name "*.yml" \) ! -iname "*archive*" 2>/dev/null -exec cat {} + 2>/dev/null | wc -c | tr -d ' '; }
row() { local label="$1" b="$2"; local t=$(( b / 4 )); printf " %-40s %9d B ~%8d tok\n" "$label" "$b" "$t"; }
check_threshold() {
local file="$1" threshold="$2" hint="$3"
[ -f "$file" ] || return
local b; b=$(wc -c < "$file" | tr -d ' ')
local kb=$(( threshold / 1024 ))
if [ "$b" -gt "$threshold" ]; then
echo " ⚠ $(basename "$file") is ${b}B (>${kb}KB threshold) — $hint"
else
echo " ✓ $(basename "$file") is ${b}B, under the ${kb}KB threshold"
fi
}

echo "agent-hub token report — $(date +%Y-%m-%d) [$ROOT]"
echo "==================================================================="
echo "READ EVERY WORKER SESSION (this is the recurring cost):"
ROOT_B=$(bytes_glob "$HUB" 1)
DOCTRINE_B=$(bytes_glob "$HUB/doctrine")
DOCTRINE_B=$(bytes_glob_exclude "$HUB/doctrine")
DIAG_ACTIVE_B=$(bytes_glob_exclude "$HUB/haven/diagrams")
IMPL_B=$(bytes_glob "$HUB/haven/workers/implementer")
VERIF_B=$(bytes_glob "$HUB/haven/workers/verifier")
Expand All @@ -52,12 +64,18 @@ echo
echo "COLD STORAGE (opened on demand only, NOT re-read wholesale by"
echo "pick_next/verify_seal — large size here is not a recurring cost):"
ARCHIVE_B=$(find "$HUB/haven/diagrams" -type f -iname "*archive*" 2>/dev/null -exec cat {} + 2>/dev/null | wc -c | tr -d ' ')
DOCTRINE_ARCHIVE_B=$(find "$HUB/doctrine" -type f -iname "*archive*" 2>/dev/null -exec cat {} + 2>/dev/null | wc -c | tr -d ' ')
EVI_I_B=$(bytes_glob "$HUB/evidence/implementer")
EVI_V_B=$(bytes_glob "$HUB/evidence/verifier")
TODO_LOG_B=$([ -f "$HUB/evidence/worker-runs.log" ] && wc -c < "$HUB/evidence/worker-runs.log" | tr -d ' ' || echo 0)
TODO_LOG_ARCHIVE_B=$([ -f "$HUB/evidence/worker-runs-archive.log" ] && wc -c < "$HUB/evidence/worker-runs-archive.log" | tr -d ' ' || echo 0)
row "haven/diagrams/*archive*" "$ARCHIVE_B"
row "doctrine/**/*archive*" "$DOCTRINE_ARCHIVE_B"
row "evidence/implementer/" "$EVI_I_B"
row "evidence/verifier/" "$EVI_V_B"
COLD_B=$(( ARCHIVE_B + EVI_I_B + EVI_V_B ))
row "evidence/worker-runs.log" "$TODO_LOG_B"
row "evidence/worker-runs-archive.log" "$TODO_LOG_ARCHIVE_B"
COLD_B=$(( ARCHIVE_B + DOCTRINE_ARCHIVE_B + EVI_I_B + EVI_V_B + TODO_LOG_B + TODO_LOG_ARCHIVE_B ))
row "= cold storage total" "$COLD_B"
echo
TOTAL_B=$(( SESSION_B + COLD_B ))
Expand All @@ -74,33 +92,74 @@ if [ -f "$DIAG_FILE" ]; then
POINTER_SEALED=$(grep -cE '— archived, see' "$DIAG_FILE" 2>/dev/null || echo 0)
REAL_SEALED=$(( FULL_SEALED - POINTER_SEALED ))
if [ "$DB" -gt 15360 ]; then
echo " ⚠ dev-loop.prime-mermaid.md is ${DB}B (>15KB threshold), $REAL_SEALED full SEALED entries not yet archived — consider moving nodes older than the current work session to haven/diagrams/dev-loop-archive.md"
echo " ⚠ dev-loop.prime-mermaid.md is ${DB}B (>15KB threshold), $REAL_SEALED full SEALED entries not yet archived."
echo " Ready-to-move rows (copy each VERBATIM into dev-loop-archive.md's"
echo " PM status table, then replace it here with a compact pointer row"
echo " '| node | state | date — archived, see dev-loop-archive.md. Evidence: ... |'):"
grep -E '\| SEALED \|' "$DIAG_FILE" 2>/dev/null | grep -vE '— archived, see' | sed 's/^/ /'
else
echo " ✓ dev-loop.prime-mermaid.md is ${DB}B, under the 15KB threshold ($REAL_SEALED full SEALED entries, $POINTER_SEALED archived pointers)"
fi
fi
check_threshold "$HUB/doctrine/domains/PROJECT.md" 15360 \
"consider moving Traps/Decisions rows older than the current work session to doctrine/domains/PROJECT-archive.md"
check_threshold "$HUB/evidence/worker-runs.log" 15360 \
"consider moving lines older than the current work session to evidence/worker-runs-archive.log (see evidence/README.md's archiving convention)"
echo
echo " Static reference files (should stay small by design — no accumulating"
echo " list to archive; growth here likely means misplaced content, not a"
echo " normal archive candidate):"
for f in "$HUB/doctrine/MEMORY.md" "$HUB/doctrine/SOUL.md" "$HUB/doctrine/INDEX.md" \
"$HUB/doctrine/standards/edit-verification.md" "$HUB/doctrine/standards/recipes.md"; do
check_threshold "$f" 8192 \
"unexpected growth for a static file — check for a Correction that belongs in the worker's own MEMORY.md, or a Decision that belongs in PROJECT.md, before creating a dedicated archive file for this one"
done
```

2. Report the output verbatim — don't paraphrase the numbers into prose,
the table is already the report.
3. If the flag fires (active diagram over 15KB), that's a real signal to
do an archive pass (see `haven/diagrams/dev-loop-archive.md`'s own
convention note, or the equivalent section in
`haven/diagrams/dev-loop.prime-mermaid.md`'s PM-status header) — but
this command itself never edits anything. Archiving is a separate,
explicit action.
3. If a flag fires on `dev-loop.prime-mermaid.md`, `PROJECT.md`, or
`worker-runs.log`, that's a real signal to do an archive pass (see
`dev-loop-archive.md` / `PROJECT-archive.md` / `worker-runs-archive.log`'s
own convention notes, or the equivalent header sections in the active
files) — but this command itself never edits anything. Archiving is a
separate, explicit action. [amended 2026-09-02] The diagram flag prints
the exact rows to move (not just "consider moving nodes") — copy-paste
is the whole remaining effort, so there's no excuse to defer it past the
current session the way a vague warning invites.
4. [added 2026-09-05] If a flag fires on one of the 5 static reference
files (`MEMORY.md`, `SOUL.md`, `INDEX.md`, `standards/*.md`), that's
NOT an archive signal — those files have no accumulating list and no
defined archive destination by design. Treat it as an anomaly: read the
file, find what's misplaced (a Correction that belongs in the worker's
own `MEMORY.md`, a Decision that belongs in `PROJECT.md`, a recipe that
belongs in `haven/workers/<wid>/recipes/`), and move it to its one
correct home instead of inventing a new archive file for a file that
was never meant to grow.

## If this hub uses epic sharding [added 2026-09-02]
If `haven/diagrams/index.md` exists (opt-in, see
`kit/agent-hub-templates.md` §9️⃣.3), `DIAG_ACTIVE_B` above sums bytes
across **every** `dev-loop-<epic>.prime-mermaid.md`, not just the one(s)
marked `active: true`. Treat "haven/diagrams/ (active file only)" as an
**upper bound** in that case, not the real per-session cost — `/boot` and
`pick_next` only read the active epic file(s) + `index.md`, per
`boot.md` step 5. This script doesn't parse `index.md`'s `active` column
(keeping it a plain byte-counting script, not a markdown-table parser) —
if you need the real per-session number under sharding, sum
`index.md` + only the active epic file(s) by hand.

## What the numbers mean
- **Recurring per-session cost** — what a fresh implementer or verifier
worker reads before touching any code. This is the number that actually
compounds: every subagent spawned for a verify pass pays it again, from
zero, with no cache reuse across separate agent contexts.
- **Cold storage** — `evidence/` and archived diagram rows. Large here is
normal and not itself a problem: `/boot` and `pick_next` only touch a
handful of the most recent evidence notes, not the whole directory. Only
worth worrying about if something starts reading it in bulk (e.g. a
recipe that globs all of `evidence/` instead of the specific notes it
needs).
- **Cold storage** — `evidence/` and archived rows from the diagram,
`PROJECT.md`, and `worker-runs.log`. Large here is normal and not itself
a problem: `/boot` and `pick_next` only touch a handful of the most
recent evidence notes, not the whole directory. Only worth worrying
about if something starts reading it in bulk (e.g. a recipe that globs
all of `evidence/` instead of the specific notes it needs).

## Runtime
`/hub-tokens`. Read-only — no seal gate, no evidence note, no worker
Expand Down
29 changes: 29 additions & 0 deletions agent-hub/evidence/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
evidence/implementer/<date>/<slug>-plan.md
evidence/implementer/<date>/<slug>-diff.md
evidence/verifier/<date>/<slug>-{seal|reopen}.md
evidence/worker-runs.log
```
Date as `YYYY-mm-dd`, slug kebab-case from the task name.

## Format — implementer note
- Title (date - node) · Worker · Version · Node (points to diagram) · Task
(verbatim prompt)
- `## Hub bytes before` — [added 2026-09-02] byte count measured at
`pick_next` step 7, before the diff starts — the verifier reads this
back when writing `worker-runs.log`, don't skip it
- `## Diff` — files | file | why |
- `## Command` — exact command from `doctrine/MEMORY.md`
- `## Output` — verbatim, no paraphrasing
Expand All @@ -27,6 +31,31 @@ Date as `YYYY-mm-dd`, slug kebab-case from the task name.
(PENDING/SEALED/REOPEN)
- `## Reasoning` — cite evidence for each criterion
- `## Missing` — only present on REOPEN
- `## Re-run` — [added 2026-09-02] `none`/`partial`/`full`, declared
honestly per what was actually done (see "Re-run scope" in
`recipes/verify_seal.md`), with a reason if not `none`. The verifier
reads this back when writing `worker-runs.log` — not decorative.

## Format — worker-runs.log
- [added 2026-09-02] NOT a narrative note like the ones above — an
**append-only file, 1 line per implementer or verifier pass that ends**.
Written by `pick_next.md`/`implement.md`/`verify_seal.md` themselves.
- Two line shapes:
- Implementer (only on `blocked`/`failed`, never reaching the verifier):
`<ISO timestamp> role=implementer outcome=blocked|failed node=<slug>
hub_bytes_before=<N> verifier_rerun=n/a`
- Verifier (every verdict — SEAL or REOPEN):
`<ISO timestamp> role=verifier outcome=SEAL|REOPEN node=<slug>
rerun=none|partial|full hub_bytes_before=<N> hub_bytes_after=<N>`
`hub_bytes_*` use this hub's own `/hub-tokens` "per-session total"
formula.
- One line per round-trip, not one per node's whole lifetime — a node
REOPENed 3 times has 3 verifier lines sharing the same `node=`.
- **Purpose**: real, non-inferred data to spot patterns later — repeated
REOPEN, a verifier re-running despite the audit-only default, an
unusual jump in hub size between two runs. Not a real token count.
- Cold storage — not re-read wholesale every worker session, only opened
when someone audits patterns on purpose. NEVER delete a line.

## The three rules of this directory
1. **VERBATIM, ALWAYS** — no claim without real cited evidence.
Expand Down
Loading
Loading