wip(claude:f102018b): session end

This commit is contained in:
sid 2026-04-23 12:49:58 -06:00
parent 21f53f9436
commit 54d32b10e1

View file

@ -0,0 +1,76 @@
# Tasks — planctl Context-Window Token Awareness
Source PRD: `prd.md`
Source design: `design.md`
## Relevant Files
- `cmd/planctl/context.go` — new: `TokenCtx`, `ReadTokenCtx`, `resolveSessionUUID`, `transcriptPath`, `parseLastUsage`, `readLimit`, `Classify`, `formatTokenCount`, `parsePIDFile`.
- `cmd/planctl/context_test.go` — new: unit tests for all pure functions + integration-style `ReadTokenCtx` fixture test.
- `cmd/planctl/proc_darwin.go` — new: `ppidOf`, `processAlive`, `processStartTimeSec` on macOS via `syscall.SysctlKinfoProc` (build tag `//go:build darwin`).
- `cmd/planctl/proc_linux.go` — new: same surface on Linux via safe `/proc/<pid>/stat` parsing (build tag `//go:build linux`).
- `cmd/planctl/proc_test.go` — new: platform-agnostic tests exercising helpers against the calling process PID.
- `cmd/planctl/main.go` — modified: call `ReadTokenCtx()` once in subcommand dispatch and thread `*TokenCtx` into emitters.
- `cmd/planctl/main_test.go` — modified: extend golden-fixture harness for per-fixture env vars and staged transcript JSONL.
- `cmd/planctl/emit.go` — modified: `emitText` / `emitJSON` take `*TokenCtx`; append context line / `context_window` key; extend `jsonSummary` with `*jsonCtxWin`.
- `cmd/planctl/emit_test.go` — modified: update call sites for new signature; add context-emission tests.
- `cmd/planctl/testdata/v2/context-info/` — new: 71% fill fixture (text).
- `cmd/planctl/testdata/v2/context-warn/` — new: 87% fill fixture (text).
- `cmd/planctl/testdata/v2/context-error/` — new: 96% fill fixture (text).
- `cmd/planctl/testdata/v2/context-normal/` — new: 50% fill — no context line emitted.
- `cmd/planctl/testdata/v2/context-no-session/` — new: no env vars — baseline unchanged.
- `cmd/planctl/testdata/v2/context-no-limit/` — new: session set, limit unset — raw count + `(limit unknown)`.
- `cmd/planctl/testdata/v2/context-json-warn/` — new: JSON mode at warn band.
- `cmd/planctl/testdata/v2/context-multi-plan/` — new: single context line after aggregate summary.
- `scripts/jj-hook.sh` — modified: session tracking with UUID validation + `planctl_start_ticks` helper.
- `scripts/test-jj-hooks.sh` — modified: PID-file lifecycle assertion.
- `INTEGRATION.md` — modified: context-window section with hook snippets and env var docs.
### Notes
- Run tests with `go test ./cmd/planctl/...`. Hook integration test: `bash scripts/test-jj-hooks.sh`.
- VCS convention per `AGENTS.md`: jj feature branch off `main`, parent-task commits at each checkpoint.
- Source-code traceability tag format: `// spec:26174-planctl-context-tokens/<ref>`. Example: `// spec:26174-planctl-context-tokens/R2.1`.
## Instructions for Completing Tasks
As you complete each task, flip `[ ]` to `[x]` in this file. Update after each sub-task, not just per parent.
## Tasks
- [ ] 0.0 Create feature branch _Requirements: infra_
- [ ] 0.1 Start jj change on `main`: `jj new main -m "feat: 26174-planctl-context-tokens"` _Requirements: infra_
- [ ] 0.2 Create bookmark: `jj bookmark create feature/26174-planctl-context-tokens` _Requirements: infra_
- [ ] 1.0 Implement `context.go`, platform helpers, and unit tests _Requirements: R1.1-R1.6, R2.1-R2.6, R3.1-R3.3, R3.5, R4.4, R6.1-R6.3_ _Design: D§1, D§3.1-3.9, D§6.1, D§7.1_
- [ ] 1.1 Create `cmd/planctl/context.go` with `TokenCtx` struct and `formatTokenCount(n int64) string` per design §3.1/§3.8. Add `cmd/planctl/context_test.go` with `TestFormatTokenCount` covering n=0→`< 1 k`, n=500→`< 1 k`, n=999→`< 1 k`, n=1000→`1 k`, n=1499→`1 k`, n=1500→`2 k`. _Requirements: R2.2, R2.4, R4.4_ _Design: D§3.1, D§3.8, D§7.1_
- [ ] 1.2 Add `(t *TokenCtx) Classify() (severity, msg string)` per design §3.7. Add `TestClassify_bands` (pct 0/69/70/84/85/94/95/100/nil) and `TestClassify_unknownLimit` (`Limit == 0``("", "")`). _Requirements: R3.1, R3.2, R3.3, R3.5_ _Design: D§3.7, D§7.1_
- [ ] 1.3 Add `readLimit() int64` per design §3.6 — reads `CLAUDE_CODE_MAX_CONTEXT_TOKENS`, returns 0 on unset/invalid. Add `TestReadLimit_set` / `_unset` / `_invalid`. _Requirements: R2.4_ _Design: D§3.6, D§7.1_
- [ ] 1.4 Add `transcriptPath(uuid, home, projectDir string) string` per design §3.4 — replace `/` with `-`, fall back to `os.Getwd()`. Add `TestTranscriptPath_slash`, `_nested`, `_fallbackCwd`. _Requirements: R1.5, R1.6_ _Design: D§3.4, D§7.1_
- [ ] 1.5 Add `parsePIDFile(path string) (uuid string, startStamp int64, err error)` per design §3.9 — two lines, second parses as int64. Add `TestParsePIDFile_valid`, `_missingLine2`, `_malformedInt`. _Requirements: R6.2, R6.3_ _Design: D§3.9, D§4.1, D§7.1_
- [ ] 1.6 Add `parseLastUsage(path string) (int64, error)` per design §3.5 — `bufio.Scanner` 10 MB buffer, forward scan, last `type=assistant` with present `message.usage` wins (presence per PRD R2.1, not non-zero). Skip decode errors. Return `errNoUsageLine` when none. Add `TestParseLastUsage_valid` / `_malformedMid` / `_noAssistant` / `_emptyFile` / `_zeroUsageStillQualifies`. _Requirements: R2.1, R2.2, R2.3, R2.6_ _Design: D§3.5, D§4.2, D§7.1_
- [ ] 1.7 Add `proc_darwin.go` (`//go:build darwin`) using `syscall.SysctlKinfoProc` for `ppidOf`/`processStartTimeSec`. Add `proc_linux.go` (`//go:build linux`): `ppidOf` reads PPid from `/proc/<pid>/status`; `processStartTimeSec` reads starttime from `/proc/<pid>/stat` via safe parsing — `strings.LastIndex` to find last `)`, then field 20 of remainder (raw clock ticks since boot; hook writes ticks too per §5.5/§4.1, so comparison units match). Add `proc_test.go` exercising all three against `os.Getpid()`/`os.Getppid()`. _Requirements: R1.2, R6.3_ _Design: D§3.3, D§7.3_
- [ ] 1.8 Add `resolveSessionUUID() string` per design §3.3 — env var first; then up-to-8-hop PID walk. Use unexported `var pidSessionDir = "/tmp/planctl-sessions"` for testability. Add `TestResolveSessionUUID_envWins` / `_pidWalkFinds` / `_staleSkipped` / `_startTimeMismatch` / `_exhausted`. _Requirements: R1.1, R1.2, R1.3, R1.4, R6.1, R6.3_ _Design: D§3.3, D§6.2, D§6.3, D§7.1_
- [ ] 1.9 Wire `ReadTokenCtx() *TokenCtx` per design §3.2 — compose resolveSessionUUID → transcriptPath → parseLastUsage → readLimit; nil at every failure. Add `TestReadTokenCtx_happyPath` and `_noSession`. No subprocesses invoked (D§6.4). _Requirements: R1.1-R1.6, R2.1-R2.6_ _Design: D§3.2, D§6.4, D§7.1_
- [ ] 2.0 Wire token context into planctl output (`main.go`, `emit.go`, E2E golden tests) _Requirements: R3.4, R4.1-R4.5, R5.1-R5.4_ _Design: D§4.3, D§4.4, D§5.3, D§5.4, D§7.2_
- [ ] 2.1 Extend `emit.go` types: add `jsonCtxWin` struct with `TokensUsed int64`, `TokensLimit *int64`, `Pct *int`, `Severity string`, `Recommendation string` (pointer fields `omitempty`); extend `jsonSummary` with `ContextWin *jsonCtxWin`. _Requirements: R5.1, R5.2, R5.3_ _Design: D§5.4_
- [ ] 2.2 Change `emitText` and `emitJSON` signatures to accept `*TokenCtx`. Text: canonical context line for Info/Warn/Error, unknown-limit form, multi-plan once after aggregate, Normal/nil silent. JSON: `context_window` for Info/Warn/Error, omit for Normal/nil; context never changes error/warning counts. NOTE: 2.1, 2.2, 2.3 must all land in the same parent-task commit — signature change breaks callers immediately. _Requirements: R3.4, R3.5, R4.1-R4.3, R4.5, R5.1-R5.4_ _Design: D§4.3, D§4.4, D§5.3_
- [ ] 2.3 Update `main.go`: call `ReadTokenCtx()` once at entry; thread `ctx` to `emitText`/`emitJSON` for all subcommands. Must land with 2.1/2.2 (see note on 2.2). _Requirements: R3.4_ _Design: D§1, D§2_
- [ ] 2.4 Update `emit_test.go` and `main_test.go` call sites to pass `nil` ctx where no context asserted. Add `emit_test.go` cases: text info/warn/error band, unknown-limit, Normal omission, JSON known+unknown shapes, multi-plan placement. _Requirements: R4.1-R4.5, R5.1-R5.4_ _Design: D§4.3, D§4.4, D§7.2_
- [ ] 2.5 Extend golden-fixture harness: if `env.txt` exists load KEY=VALUE via `t.Setenv`; if `transcript.jsonl` exists stage in temp HOME at `~/.claude/projects/<slug>/<uuid>.jsonl`. _Requirements: R3.4, R4.1_ _Design: D§7.2_
- [ ] 2.6 Create `testdata/v2/context-info/`, `context-warn/`, `context-error/`, `context-normal/`, `context-no-session/`, `context-no-limit/` — each with `prd.md`, `tasks.md`, `env.txt`, `transcript.jsonl` (where applicable), `expected.golden`, `expected.exit`. _Requirements: R3.2, R3.3, R3.5, R4.1, R4.2, R4.5_ _Design: D§4.3, D§7.2_
- [ ] 2.7 Create `testdata/v2/context-json-warn/` (warn band, JSON format) and `testdata/v2/context-multi-plan/` (two plan dirs, context once after aggregate). _Requirements: R4.3, R5.1-R5.4_ _Design: D§4.3, D§4.4, D§7.2_
- [ ] 2.8 Run `go test ./cmd/planctl/...`; all existing fixtures byte-identical, eight new context fixtures pass. _Requirements: R4.5_ _Design: D§7.2_
- [ ] 3.0 Extend `scripts/jj-hook.sh` with session tracking _Requirements: R7.1, R7.2, R7.3_ _Design: D§4.1, D§5.1, D§5.5, D§7.4_
- [ ] 3.1 Add `planctl_start_ticks()` per design §5.5: Linux — safe `/proc/<pid>/stat` parse via `sed 's/.*)//' | awk '{print $20}'`; macOS — `ps -o lstart=``date` epoch seconds. Returns 0 on failure. _Requirements: R6.2, R7.3_ _Design: D§5.5_
- [ ] 3.2 Extend `session-start`: parse `.session_id` via `jq`; validate UUID regex; append `CLAUDE_SESSION_ID` to `$CLAUDE_ENV_FILE`; `mkdir -p /tmp/planctl-sessions` (creates temp dir per §5.1); write two-line temp file via `mktemp` and `mv` atomically. _Requirements: R6.2, R7.1, R7.3_ _Design: D§4.1, D§5.1, D§5.5_
- [ ] 3.3 Extend `session-end`: `rm -f /tmp/planctl-sessions/${PPID}`. _Requirements: R7.2, R7.3_ _Design: D§5.5_
- [ ] 3.4 Extend `scripts/test-jj-hooks.sh`: assert PID file created on start with two lines (UUID + numeric stamp); assert removed on end. _Requirements: R7.1, R7.2_ _Design: D§7.4_
- [ ] 3.5 Manual smoke in a live Claude Code session: PID file appears on start, `planctl status` emits context line at ≥ 70% fill, file removed on exit. _Requirements: R7.1, R7.2_ _Design: D§7.4_
- [ ] 4.0 Document hook integration in `INTEGRATION.md` _Requirements: R7.1, R7.2, R7.4_ _Design: D§5.5_
- [ ] 4.1 Add context-window section: threshold bands, text line format, JSON `context_window` shape. _Requirements: R4.1, R4.2, R5.1_ _Design: D§4.3, D§4.4_
- [ ] 4.2 Copy-paste `session-start`/`session-end` additions and `planctl_start_ticks` helper. _Requirements: R7.1, R7.2, R7.3_ _Design: D§5.5_
- [ ] 4.3 Document `CLAUDE_CODE_MAX_CONTEXT_TOKENS`: raw count when unset; hooks optional; graceful degradation. _Requirements: R2.4, R3.3, R7.4_ _Design: D§5.2_