Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
80abdcb
fix(hooks): skip vet/test/codegen during git rebase
zook-bot Jun 3, 2026
6d7f965
chore(pre-commit): skip test-fast-parallel + dashboard checks in agen…
zook-bot May 26, 2026
44f438d
rework 94c407a5f9c9: rework cfd76288dce3: rework c9878c08459e: test: …
zook-bot Jun 17, 2026
a4bf36d
test(doctor): lock in v2-routed-to-namespace silence for unbound rig-…
zook-bot May 7, 2026
ee86292
test(events): pin bare 'gc events --follow' streaming behavior (gc-4e…
zook-bot May 25, 2026
a76b3fb
rework 24f914981e20: fix(dolt): use ss for listener detection on Linu…
zook-bot May 27, 2026
24276bd
rework 39af0868b9c7: fix(api): skip cache-reconcile events in applyBe…
zook-bot Jun 17, 2026
b6642d1
fix(session): block duplicate pool alias claims at sync
zook-bot May 1, 2026
08bd602
rework 229380f249af: fix(beads): dedup notifyChange emissions by payl…
zook-bot Jun 26, 2026
33fa278
feat(doctor): warn on malformed .beads/config.yaml (gc-0kuep)
zook-bot May 4, 2026
8f25af2
fix(doctor): exclude slash-bearing strings from session bead ID heuri…
zook-bot May 13, 2026
8655b24
fix(doctor): use config-driven prefix set for bead-ID classification …
zook-bot May 4, 2026
d59cd57
rework 11cd7fd6f31e: rework 9c0a9b13d488: feat(city): surface suspend…
zook-bot Jun 7, 2026
eaafe76
rework c4bcca5a1a2d: fix(config): carry agent fields through start_co…
zook-bot May 23, 2026
466b57b
feat(config): qualified-name match in applyPackAgentPatches (gc-tq9ow)
zook-bot May 13, 2026
923f48f
fix(dog-doctor): find -L so backup-freshness traverses symlinked .dol…
zook-bot May 14, 2026
9786662
rework 4894efae7fd6: rework 2e72c12cf070: fix(doctor): bound pack scr…
zook-bot Jun 4, 2026
8e84898
fix(bd): project resolved dolt host in managed_city to kill remote-v …
zook-bot May 19, 2026
f73f3e5
fix(sling): stamp assignee on singleton routes so target's hook surfa…
zook-bot May 19, 2026
6f11fdc
rework aaa96fd26c64: fix(template-resolve): expand agent.toml [env] a…
zook-bot May 23, 2026
e116425
rework ee9ee0e06899: rework ce3513845ce2: feat(config): inherit [agen…
zook-bot Jun 23, 2026
ca3cb4f
rework 288a39ff590b: rework 30d7494e67aa: rework b1af3e57b437: fix(da…
zook-bot Jun 7, 2026
5d326a7
rework 7fa39f71b377: fix(session): close bead before stopping runtime…
zook-bot May 29, 2026
3c481a8
feat(session): default `gc session close` to $GC_SESSION_ID (gc-yzh66…
zook-bot May 21, 2026
136da44
rework 3b064ca68e60: rework 1bd2bf771c80: bd: update sync.remote (#15…
zook-bot Jun 17, 2026
818dd89
rework 02597c511fb1: rework cdb8a3d57c67: rework 42643ab922c4: fix(do…
zook-bot Jun 17, 2026
c7aced3
rework 9530430f7438: rework 71bb7b3c114a: fix(rebase): align upstream…
zook-bot Jun 17, 2026
5947005
bd: commit identity.toml and metadata.json
zook-bot May 23, 2026
98a8ff4
rework 62371cd40438: fix(start): pass SSH_AUTH_SOCK through to agent …
zook-bot May 29, 2026
e9cdc46
feat(api): expose input_tokens on agent/session response (gc-3p9x0f)
zook-bot May 24, 2026
474a687
rework 6a87cb368995: rework ef9efad8344e: fix(api): wire /sessions Hu…
zook-bot Jun 10, 2026
e26283d
rework 89b32c791f92: rework e4af78b47995: feat(orders): declare scope…
zook-bot Jun 10, 2026
f96d79f
rework f1855997e0e1: rework 3e034f39d666: fix(prompt): add ConfigDir …
zook-bot Jun 5, 2026
05b2dff
rework 4f938810c2a8: rework 586d3b767cc4: rework a3fac203a960: rework…
zook-bot Jun 7, 2026
94dbddc
rework 064c716fe05e: fix(apiroute): fall through to supervisor when s…
zook-bot Jun 10, 2026
d99397a
rework 49eb04cd17df: rework f33c53b0fce0: perf(mail): collapse per-re…
zook-bot Jun 5, 2026
fcec541
rework 3b97ac13ea4b: perf(api/mail): wire /mail and /mail/count into …
zook-bot Jun 4, 2026
1c244bc
fix(doctor): floor order-firing staleness so short-cadence orders don…
zook-bot May 29, 2026
ed1faba
fix(reconciler): exempt manual shadow sessions from config-drift drai…
zook-bot Jun 3, 2026
016a71d
test(dolt): fake ss in foreign-managed zombie scan tests (gc-9n4v5n)
zook-bot Jun 5, 2026
62c0850
fix(prime): align buildPrimeContext wrapper with upstream signature (…
zook-bot Jun 5, 2026
75d2a90
test(gastown): isolate host git config in TestRefineryBranchHasRealCh…
zook-bot Jun 5, 2026
636e120
chore(bd): commit canonical .beads/config.yaml (#36)
zook-bot Jun 5, 2026
a547fa7
rework a2af57056c19: fix(convoy): resolve gc convoy create rig scope …
zook-bot Jun 17, 2026
99cd9ed
Proposed check (not prescribed) (gc-c1rpx) (#33)
zook-bot Jun 5, 2026
502e3f5
test(reconciler): regression coverage for start-pending known-state (…
zook-bot Jun 5, 2026
8ba56f0
feat(dashboard): ttyd iframe panel for mayor terminal (gc-lgjze) (#7)
zook-bot Jun 5, 2026
4849a5a
fix(stop): reap owned managed dolt sql-server in shutdownBeadsProvide…
zook-bot Jun 5, 2026
e880b69
fix(dolt): guard gc dolt sync against concurrent runs with flock (gc-…
zook-bot Jun 6, 2026
1e43a02
fix(orders): include failed exec output in order.failed event (gc-2m4…
zook-bot Jun 6, 2026
240bba5
rework f3bb21475cda: supervisor: add view=summary opt-in to GET /sess…
zook-bot Jun 18, 2026
6bacf71
supervisor: warm-cache enriched session fields + flip GET /sessions d…
zook-bot Jun 6, 2026
4504b41
perf(api): warm-cache view=full live fields for fork-free GET /sessio…
zook-bot Jun 7, 2026
e8edaf3
fix(rebase): satisfy fork invariants on new upstream content (gc-k7cex)
zook-bot Jun 7, 2026
f9a9dbf
fix(rebase): satisfy fork invariants on new upstream content (gc-eicuf)
zook-bot Jun 7, 2026
da730b6
fix(maintenance): wire per-database DOLT_GC into the store-maintenanc…
zook-bot Jun 7, 2026
fb73931
fix(maintenance): reconnect after online DOLT_GC so per-db GC doesn't…
zook-bot Jun 9, 2026
4ee8c35
fix(rebase): satisfy fork invariants on new upstream content (gc-vtpf5)
zook-bot Jun 10, 2026
503ab07
rework 0a76e6c6c792: test: unified writable-temp-global-gitconfig iso…
zook-bot Jun 18, 2026
a391df7
rework 29c2a802f96c: rework c32f0b5fad97: fix(order-tracking): event-…
zook-bot Jun 26, 2026
eaf650a
gc-unpyk: wake-budget has no per-session isolation — one spinning ses…
zook-bot Jun 13, 2026
002c546
fix(runtime): deterministic builtin-pack skill fingerprint (gc-155rj)…
zook-bot Jun 13, 2026
3363509
fix(reconciler): wake drained on_demand named session when work assig…
zook-bot Jun 13, 2026
490359d
fix(rebase): align gc-155rj builtin-pack lookup with upstream builtin…
zook-bot Jun 18, 2026
29b9e2e
fix(rebase): satisfy fork invariants on new upstream content (gc-5sacl)
zook-bot Jun 18, 2026
642efcf
feat(config): resolve "<pack>//<subpath>" agent path refs against the…
zook-bot Jun 19, 2026
3f9a84d
fix(bd): remove duplicate dolt: key in .beads/config.yaml (#63)
zook-bot Jun 19, 2026
c899504
fix: stop full-prompt re-injection on resume for nudge-having session…
zook-bot Jun 20, 2026
051e3c8
fix(controller): suppress poke on controller-authored order-tracking …
zook-bot Jun 21, 2026
9b5f9df
Reuse demand snapshot on non-patrol ticks instead of force-rebuilding…
zook-bot Jun 21, 2026
04201a9
fix(beads): reconnect city-store cache reconcile on invalid-connectio…
zook-bot Jun 21, 2026
7e8fe66
fix: clean QF1012 lint fallout from upstream rebase (gc-lcixo)
zook-bot Jun 23, 2026
5615141
build(deps): replace beads with zookanalytics fork main (gc-o4o10) (#84)
zook-bot Jun 24, 2026
6619ff4
fix(sessionlog): resolve 1M context window for bare modern Claude mod…
zook-bot Jun 25, 2026
f2b3f84
build(beads): filesystem-replace beads -> ~/beads (host-only fork bui…
zook-bot Jun 25, 2026
9480dc3
build(beads): restore versioned beads pin -- fixes CI broken by #86 (…
zook-bot Jun 25, 2026
807e1c0
fix(convoy): realign resolveBdScopeTarget to upstream #3410 5-arg sig…
zook-bot Jun 26, 2026
2680084
test(modelwindow): add dedicated testenv import for new gc-os8fn pack…
zook-bot Jun 26, 2026
9031f2f
sling+order-sweep: clear stale assignee on bare pool re-pour; stat-ga…
zook-bot Jul 3, 2026
dddec62
fix(make): default test TMPDIR to /var/tmp to avoid tmpfs ENOSPC (gc-…
zook-bot Jul 3, 2026
9bde3f7
fix(beads): reconnect+retry on invalid connection in CachingStore wri…
zook-bot Jul 3, 2026
b3a0694
feat(doctor): warn on low build-scratch (/tmp) free space (gc-yiqil) …
zook-bot Jul 3, 2026
49896fc
spec(input-area-state): ghost-text-aware input-area inspection (gc-zx…
zook-bot May 10, 2026
fd9f4c0
docs(engdocs): move input-area-state design doc to engdocs/design/ (g…
zook-bot May 11, 2026
bdb7195
feat(tmux): add InputAreaState API for ghost-text-aware input-area in…
zook-bot May 11, 2026
12a6284
test(tmux): parser tests for InputAreaState (gc-8g41r)
zook-bot May 11, 2026
15df61d
docs(review): spec review #2 InputAreaState (gc-9yoqyg)
zook-bot May 28, 2026
4d40f03
docs: review InputAreaState spec (gc-277ni0)
zook-bot May 28, 2026
8397fe1
docs(input-area-state): spec review (Claude polecat #1) [gc-sj4lry]
zook-bot May 28, 2026
e988e69
docs(review): input area state spec review (gc-ce6u81)
zook-bot May 28, 2026
7790f21
docs(input-area-state): apply stage-1b review deltas (gc-8g41r)
zook-bot May 28, 2026
de8c8ad
feat(tmux): classify Claude feedback survey as ready-for-input (gc-8g…
zook-bot Jun 14, 2026
f5cccca
feat(session): add input-area CLI surface + peek --raw (gc-8g41r.5)
zook-bot Jun 14, 2026
ef930ae
test(tmux): add live tmux InputArea integration test (gc-8g41r.6)
zook-bot Jun 14, 2026
d251733
docs(review): codex review input-area-state implementation (gc-8g41r.3)
zook-bot Jun 14, 2026
8878b46
refactor(tmux): lean InputAreaState to faint=ghost + Codex arrow (gc-…
zook-bot Jun 15, 2026
3590093
docs(review): claude stage-4 review input-area-state implementation (…
zook-bot Jun 15, 2026
ee31854
fix(test): align input-area provider mock with current buildSessionPr…
Jul 3, 2026
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
16 changes: 16 additions & 0 deletions .beads/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
issue_prefix: gc
issue-prefix: gc
dolt.auto-start: false
dolt.local-only: true
dolt.auto-push: false
no-push: true
export.auto: false
gc.endpoint_origin: inherited_city
gc.endpoint_status: verified
types.custom: molecule,convoy,message,event,gate,merge-request,agent,role,rig,session,spec,convergence,step
dolt:
disable-event-flush: true
backup.enabled: false
dolt.auto-commit: "batch"
import.auto: false
sync.remote: "git+ssh://git@github.com/zookanalytics/gascity.git"
5 changes: 5 additions & 0 deletions .beads/identity.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# .beads/identity.toml — canonical, git-tracked.
# Edited only at scope creation or by deliberate human/`gc` migration.

[project]
id = "gc-local-76b302ae41577800e6d45fe488752941"
7 changes: 7 additions & 0 deletions .beads/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"backend": "dolt",
"database": "dolt",
"dolt_database": "gc",
"dolt_mode": "server",
"project_id": "gc-local-76b302ae41577800e6d45fe488752941"
}
53 changes: 45 additions & 8 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -1,10 +1,38 @@
#!/usr/bin/env bash
set -euo pipefail

# Skip the two heaviest pre-commit steps (test-fast-parallel and
# dashboard-check + dashboard-smoke) by default in agent contexts.
# Polecats commit at agent pace; running the full per-iteration validation
# N times concurrent saturates FS/CPU/Dolt (see gc-53c8k4 for the 2026-05-24
# supervisor SIGTERM cascade). Refinery's pre-publish review gate and
# GitHub Actions CI run the full validation before the human-visible
# artifact (PR), which is the gate that actually matters.
#
# Override either direction explicitly:
# GC_PRECOMMIT_SKIP_HEAVY=1 → always skip (regardless of GC_AGENT)
# GC_PRECOMMIT_SKIP_HEAVY=0 → always run (force full validation in agent)
# unset → skip iff GC_AGENT is set
if [ "${GC_PRECOMMIT_SKIP_HEAVY:-${GC_AGENT:+1}}" = "1" ]; then
SKIP_HEAVY=1
else
SKIP_HEAVY=0
fi

staged_go_files=$(git diff --cached --name-only --diff-filter=ACM -- '*.go' || true)
staged_web_src=$(git diff --cached --name-only --diff-filter=ACM -- 'cmd/gc/dashboard/web/src/' 'cmd/gc/dashboard/web/index.html' 'cmd/gc/dashboard/web/public/' 'cmd/gc/dashboard/web/package.json' 'cmd/gc/dashboard/web/openapi-ts.config.ts' 'cmd/gc/dashboard/web/vite.config.ts' 'cmd/gc/dashboard/web/tsconfig.json' || true)
staged_docs=$(git diff --cached --name-only --diff-filter=ACM -- '*.md' 'docs/**' 'engdocs/**' 'plans/**' 'specs/**' 'AGENTS.md' 'CONTRIBUTING.md' 'README.md' 'TESTING.md' || true)

# When mid-rebase, skip the heavy gates (test/vet/codegen). The
# formula's post-rebase `test` step is the safety net that runs the
# full suite once on the final state. Per-commit testing during a
# rebase that replays 30+ commits is purely redundant.
git_dir=$(git rev-parse --git-dir)
if [ -d "$git_dir/rebase-merge" ] || [ -d "$git_dir/rebase-apply" ]; then
echo "pre-commit: rebase in progress — skipping vet/test/codegen (post-rebase test step is the gate)" >&2
exit 0
fi

if [ -z "$staged_go_files" ] && [ -z "$staged_web_src" ] && [ -z "$staged_docs" ]; then
exit 0
fi
Expand Down Expand Up @@ -51,14 +79,23 @@ fi
if command -v npm >/dev/null 2>&1; then
spec_changed=$(git diff --cached --name-only --diff-filter=ACM -- 'internal/api/openapi.json' || true)
if [ -n "$spec_changed" ] || [ -n "$staged_web_src" ]; then
# Typecheck BEFORE build: vite's build transpiles TS to JS and
# silently ignores type errors. The Makefile target also runs the
# Vitest suite, builds dist/, and smoke-runs the compiled SPA via
# Vite preview so a bundle that builds but won't serve is caught
# before CI.
make dashboard-check dashboard-smoke
git add -f cmd/gc/dashboard/web/src/generated
git add cmd/gc/dashboard/web/dist
if [ "$SKIP_HEAVY" != "1" ]; then
# Typecheck BEFORE build: vite's build transpiles TS to JS and
# silently ignores type errors. The Makefile target also runs the
# Vitest suite, builds dist/, and smoke-runs the compiled SPA via
# Vite preview so a bundle that builds but won't serve is caught
# before CI.
make dashboard-check dashboard-smoke
git add -f cmd/gc/dashboard/web/src/generated
git add cmd/gc/dashboard/web/dist
fi
# SKIP_HEAVY=1: we did not rebuild the bundle or regenerate types,
# so we must not stage them — staging a stale dist/ or src/generated
# would ship drift in the polecat's commits. The refinery + CI
# (dashboard-ci + the `dashboard` Actions job) catch any drift at
# PR time and bounce the bead back via rejection_reason; the next
# polecat picks it up and either fixes the regen explicitly or
# commits with GC_PRECOMMIT_SKIP_HEAVY=0 to run the full block.
fi
else
echo "warning: npm not on PATH — skipped dashboard SPA typecheck + rebuild. CI will enforce this." >&2
Expand Down
192 changes: 192 additions & 0 deletions .prd-reviews/input-area-state/code-review-claude-gc-8g41r.4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# Code Review (Claude): InputAreaState Implementation (gc-8g41r.4)

Stage 4 review of convoy `gc-8g41r`, against the **current**
`integration/input-area-state` at `e0e90a2ce` (post lean-fix `gc-8g41r.7`).
This is the post-pivot review: it assesses the minimal faint=ghost detector,
not the original gray-range design the Codex review (`gc-8g41r.3`) saw.

## Verdict

**approve — graduation-ready** (squash `integration/input-area-state` → `main`).

The lean-fix resolves all three Codex findings (two of them by a *better*
route than Codex proposed), the parser is correct, the dropped surface is
removed without dangling references, and the test suite is excellent. Every
gate I ran is green (build, vet, unit, live-tmux integration — see Validation).
The three observations below are **non-blocking follow-ups**, not graduation
blockers; none is a defect in a currently-shipping rendering.

## Cross-check of the Codex findings (gc-8g41r.3)

I agree with all three of Codex's original findings *as written against the
pre-lean branch*, and I agree with how `gc-8g41r.7` resolved each — including
the two cases where the resolution diverged from Codex's suggested fix.

1. **Codex #1 — Codex arrow prompt not parsed → RESOLVED, agree.**
`parseCodexInputArea` now binds both shapes bottom-up: the boxed `│ > `
anchor (`input_area.go:269-273`) and the current arrow `› `
(`input_area.go:280-287`). Arrow ghost reuses the shared faint helper —
no Codex-specific ghost code, matching spec §3.2. Tests cover arrow
idle/typed/faint-ghost/queued-followup/bottom-up
(`input_area_test.go:374-404`). This is a complete fix.

2. **Codex #2 — dim-SGR union narrower than spec (240-243 vs 232-245,
missing `ESC[39m`) → RESOLVED BY REFRAME, agree (and this is the stronger
fix).** Rather than widening the gray range, the lean-fix *deletes* the
gray-range heuristic and replaces it with a single rule: faint (`ESC[2m`)
is ghost, everything else is typed (`splitDimSegments`/`updateDimState`,
`input_area.go:396-481`). This is strictly better than the range Codex
asked for: it is LLM-agnostic (no per-provider color table), it is
empirically validated on live Claude (operator+host, 2026-06-15), and it
eliminates the false-positive class in *both* directions Codex worried
about (a gray-but-not-faint themed input is no longer misread as ghost; a
faint suggestion outside the old range is no longer misread as typed).
`ESC[39m` is now a faint terminator (`updateDimState` case `"39"`), and
the `38;5;2`/`38;2;r;g;b` "is the 2 a faint code?" trap Codex implicitly
raised is closed by the extended-color skip (`input_area.go:469-478`),
with a dedicated test (`input_area_test.go:219`). Do not re-flag the old
range — it is intentionally gone.

3. **Codex #3 — missing `shape_variant`/`raw_truncated` → RESOLVED BY DROP,
agree.** Per the operator's "no consumers" decision, the lean-fix removed
the whole raw surface rather than adding the promised discriminators. The
struct is now `{Provider, PromptChar, Busy, Typed, Ghost, Detected}`
(`input_area.go:51-58`), spec §4.2 matches, and the CLI JSON mirrors it
(`cmd_session.go:2183-2191`). This is the right call under YAGNI; I am not
re-flagging the dropped fields.

**Codex's one open architectural note still stands** (see Finding 2 below):
defining the capability in `internal/runtime/tmux` means a non-tmux provider
would have to import `tmux`. The lean-fix did not change this (out of scope),
so it remains an open item for the upstream PR.

## New findings (missed angles) — all non-blocking

1. **Latent ghost→typed misread if a build ever renders the prompt glyph
itself faint.** `splitDimSegments` is always seeded with `dim=false` at the
first byte *after* the prompt char / arrow
(`input_area.go:210` for Claude, `input_area.go:283` for Codex arrow). The
faint state of the bytes *before* the prompt is discarded — for the arrow
path it is explicitly thrown away by the whitespace guard, which only keeps
`stripANSI(raw[:idx])` (`input_area.go:281`). This is correct for every
rendering observed today, because the validated shape is
`❯ ESC[2m…ESC[0m` / `› ESC[2m…ESC[0m` — the glyph sits *outside* the faint
wrapper, so the first `ESC[2m` always lands inside `content`. But if a
future build wraps the entire input row faint
(`ESC[2m❯ suggestion ESC[0m`), the post-prompt ghost would carry no leading
`ESC[2m` in `content` and would be classified as **Typed** — recreating the
exact false-stall class this convoy exists to kill (a ghost suggestion
firing a buffered-input warrant). This is speculative and currently
unobserved; it is sharper for Codex than Claude only because the Codex
arrow ghost path is fixture-tested but not live-validated (Finding 3).
*Concrete hardening if/when wanted:* compute the carried faint state by
running the SGR scan over the pre-prompt slice and seed `splitDimSegments`
with it, instead of starting at `false`. Cheap, removes the assumption.
**Recommend a follow-up bead, not a graduation blocker.**

2. **`InputAreaState` + `InputAreaCapturer` live in package `tmux`
(architecture / upstream-PR note).** A second runtime (exec, k8s, fake)
that wanted input-area support would have to import `internal/runtime/tmux`
to implement the interface or return the type. This is *acceptable today*
and is in fact the correct call under the repo's "No premature abstraction
— don't build interfaces until two implementations exist" rule (there is
one implementation). I raise it only because it is the thing an upstream
reviewer will ask about: when a real second provider appears, the type and
the `InputAreaCapturer` interface should move up to `internal/runtime` as a
runtime-level contract (spec §4 calls this out as unresolved). Worth a
sentence in the upstream PR description so it reads as a known, deliberate
staging choice rather than a layering miss. **Non-blocking.**

3. **Codex-arrow and Gemini paths are not live-validated.** The faint rule is
live-validated end-to-end for Claude (`TestInputAreaLiveTmux` drives real
`capture-pane -e` bytes through capture→parse→classify and passes here on
tmux 3.6b). The Codex *arrow* ghost path is only fixture-tested
(`input_area_test.go:387-392`) — the spec itself hedges to "faint text …
has been observed" (§3.2). Gemini is explicitly provisional and blocked on
a live capture (`tk-mmny1`, noted at `input_area.go:296-298`). Because all
three share one faint helper that *is* live-validated, the risk is low and
this is correctly scoped as later-stage verification, not a blocker. Flag
only so graduation does not silently imply "Codex/Gemini live-proven."
**Non-blocking — already tracked.**

## Per-area review

- **Faint=ghost correctness / robustness.** Correct. `updateDimState`
(`input_area.go:457-481`) handles the load-bearing cases: SGR 2 sets faint;
0/22/39/empty clear it; `38`/`48` extended-color introducers consume their
`5;n` (256) or `2;r;g;b` (truecolor) payload so a color index is never
misread as the faint code. I traced the bound checks
(`i+2 < len`, `i+4 < len`) against `["38","5","2"]`, `["2","38","5","0"]`,
`["38","2","0","0","0"]`, and truncated `["38","5"]` — all classify
correctly, and truncated/malformed groups fail safe (faint unchanged).
Byte/rune handling is correct: `len(codexArrowPrompt)` and
`strings.Index` are both byte-based, so the multibyte `›` slices cleanly.
The only robustness gap is Finding 1 (pre-prompt faint state), which is
out of the currently-observed envelope.

- **Codex-arrow parsing.** Correct and well-guarded. Bottom-up scan returns
the lowest (most recent) prompt row; the "arrow must start the line" guard
(`input_area.go:281`) keeps a `›` inside a queued-follow-up affordance from
being read as the input area; a typed line that itself contains `› ` is fine
because `strings.Index` binds the first (prompt) arrow and the remainder is
content. Matches spec §3.2.4 step-for-step.

- **Drops are clean.** Verified no dangling references to `RawPeeker`,
`CapturePaneRaw`/`CapturePaneAllRaw`, `PeekRaw`/`doSessionPeekRawFallback`,
`--include-raw`, `peek --raw`, `shape_variant`/`raw_truncated`, or a `Raw`
struct field anywhere in `*.go`, in `docs/reference/cli.md`, or in the spec.
The only surviving `streamSessionPeekRaw*` symbols are in `internal/api/`
(the SSE `format=raw` stream) — a *different* mechanism that never used
`tmux.RawPeeker`; the lean-fix correctly left it untouched and it still
builds. `raw_capture_test.go` is replaced by `input_area_adapter_test.go`,
keeping only the delegation test. cli.md is regenerated (input-area entry
present, no `--raw`).

- **Test design quality.** Excellent. Table-driven, fixtures as raw-string
literals with named SGR macros (`dimOn`, `gray256`, `brBlack`) so the wire
bytes are visible inline; each fixture carries a comment tying it to the
real incident it reproduces (e.g. the 2026-05-09 `signal-loom` false
positive, the 2026-05-30 feedback-survey clarification). `TestSplitDimSegments`
pins the load-bearing helper directly, including the `38;5;2` trap and
`bold+faint` precedence; `TestInputAreaLiveTmux` proves the one thing
fixtures cannot — that the host tmux build preserves the SGR bytes — on an
isolated `-L` socket killed on cleanup (tmux-safety compliant). Gap is
intentional: no whole-line-faint fixture (Finding 1) because that rendering
is unobserved.

- **Prompt-consumer ergonomics (agent-prompt-author view).** Strong and
ZFC-aligned. The witness consumer
(`examples/gastown/packs/gastown/agents/witness/prompt.template.md:143-175`)
consumes `gc session input-area` → `jq .typed/.busy` with **no per-provider
branches** — the library carries the rendering knowledge, the prompt carries
the judgment ("is there buffered input the agent ignored?"). The struct
exposes raw facts (typed/ghost/busy/prompt_char) rather than a baked
`ready_for_input` verdict, which is the correct boundary: a "ready" flag
would be a judgment call in Go (a ZFC violation). The three output formats
(json/kv/text) cleanly separate machine vs human consumers. I would ship
this consumer pattern as-is.

- **Graduation readiness.** Ready. Spec ↔ code in sync (struct, faint rule,
Codex shapes); status deliberately stays `Proposed` (an operator call to
flip to `Accepted` on graduation, not a blocker). Net −272 LOC — the change
genuinely shrinks the maintained surface, which is the right direction for a
fork tracking upstream.

## Validation (all run against `e0e90a2ce` in this worktree)

- `go build ./internal/runtime/tmux/... ./cmd/gc/... ./internal/api/...` — OK
(confirms the SSE `format=raw` path still compiles after the tmux drops).
- `go test ./internal/runtime/tmux -run 'InputArea|SplitDim|StripANSI'` — ok
- `go test ./cmd/gc -run 'InputArea'` — ok
- `go vet ./internal/runtime/tmux/... ./cmd/gc/...` — clean
- `go test -tags=integration ./internal/runtime/tmux -run TestInputAreaLiveTmux`
— PASS (tmux 3.6b), faint=ghost validated on real `capture-pane -e` bytes.

## Suggested follow-up beads (none block graduation)

- Harden `splitDimSegments` seeding against a faint-wrapped prompt glyph
(Finding 1).
- When a second non-tmux input-area provider is needed, lift `InputAreaState`
/ `InputAreaCapturer` to `internal/runtime` (Finding 2 / spec §4 open item).
- Live-validate the Codex arrow ghost path and unblock Gemini verification
(`tk-mmny1`) in a later stage (Finding 3).
Loading
Loading