Pre-PR codex review flagged missing coverage for the 10 MB scanner buffer
called out in design §3.5. Adds:
- TestParseLastUsage_largeLine: 5 MB JSONL line with inert padding still
extracts the usage object correctly (no silent truncation).
- TestParseLastUsage_oversizedLineErrors: 11 MB line surfaces a scanner
error cleanly (no panic, nil usage).
Follow-up to parent 1.0, dev/plans/26174-planctl-context-tokens/tasks.md.
* emit.go: emitText/emitJSON take *TokenCtx; jsonSummary extended with *jsonCtxWin (omitempty) for context_window in JSON output
* main.go: ReadTokenCtx() called once at top of run() before subcommand dispatch; threaded through runLint
* main_test.go: golden-fixture harness extended for env.txt + transcript.jsonl staging in temp HOME
* testdata/v2/context-{info,warn,error,normal,no-session,no-limit,json-warn,multi-plan}: 8 new fixtures exercising all bands + shapes
Task 2.0 from dev/plans/26174-planctl-context-tokens/prd.md
Pre-PR codex review found a gap in the R1.1 annotation grammar handling:
* scanTagAfterOpener treated the first '_' after an opener as the
closing delimiter regardless of paren nesting, so a legitimate
tag like `_Requirements: R1.1 (foo_bar)_` would incorrectly
close at the underscore inside the annotation.
* parseTagBody split on every ', ' regardless of paren context, so
`_Requirements: R1.1 (note, with comma), R2.1_` split into
three bogus refs.
Both are per-spec: PRD R1.1 defines annotation body as 'arbitrary
non-) characters', which includes _ and ,. The state machine and
the ref-splitter are now paren-depth-aware; regression tests added
for both cases.
* .forgejo/workflows/planctl.yml: build/test matrix across
{ubuntu-latest, macos-latest, windows-latest} × {1.22, stable}
per D-3 / PRD R7.4. Dedicated bench job on ubuntu+stable so
ns/op numbers stay comparable across runs. Path filters scope
CI to planctl-adjacent changes only.
* cmd/planctl/bench_test.go: Benchmark_Lint_BigPlan times the full
lint pipeline on testdata/perf/big-plan/. Local M4 Pro: ~720µs,
two orders of magnitude under M3's 100ms target.
* TestLint_BigPlan_SoftCeiling logs (via direct os.Stderr write —
not t.Logf, which is suppressed without -v) when wall time
exceeds 100ms. Soft check per task 8.3 — CI runner variance
means a hard assert would be flaky; the visible stderr line is
the regression signal.
* Windows line-ending handling already landed in parent-7's
harness (CRLF → LF normalisation on both sides of golden
compare), so 8.4 is satisfied by construction.
Parent task 8.0 from dev/plans/26172-planctl/tasks.md.
Codex code-review session: 019db2f3-2f80-7fc0-b7bb-9e1f677f978e (2 rounds).
* main_test.go: TestGoldenFixtures sweeps
testdata/{clean,dirty,inline-code-exclusion,multi}/**; compares
stdout against expected.golden and exit against expected.exit.
--update flag regenerates sidecars. Per-fixture invocation
overrides via optional args.txt (with FIXTURE substitution) and
cwd-rel.txt. Handles flat-layout fixtures (prd.md at class root)
and multi-plan layouts (dev/plans/ subtree).
* CRLF normalisation on both sides of the golden compare per
design §7.4's Windows-CI contract.
* Clean fixtures: simple-plan (prd+tasks), with-design-plan
(prd+design+tasks, full coverage).
* Dirty single-class fixtures: orphan-requirement, orphan-design,
uncovered-requirement, uncovered-design, tag-syntax, tag-unclosed,
ears-violation, missing-closeout-file. Each isolates one
diagnostic class; ears-violation-strict and ears-violation-no-ears
share the plan via args.txt overrides.
* tag-unclosed-with-recovery fixture: codex R1 regression guard for
design §3.3 recovery — the recovery line uniquely cites D§2.2 so
broken recovery would surface as uncovered-design.
* inline-code-exclusion fixture: decoys cite an undeclared R9.9 so
any masking leak would trip orphan-requirement.
* Multi-plan fixtures: case-c (repo root with dev/plans/{alpha,beta,
archive/retired}/, Case C upward walk + archive skip); case-b
(deeply-nested cwd inside a plan dir, Case B resolution).
* Perf fixture testdata/perf/big-plan/ — synthetic 5-file plan
(~90 KB) with 36 R-ids, 18 D§ sections, 72 tasks (all covered).
Parent 8 wires Benchmark_Lint_BigPlan.
Parent task 7.0 from dev/plans/26172-planctl/tasks.md.
Codex code-review session: 019db2ed-d621-7bc0-90f2-05feb5e2fe50 (2 rounds).
* main.go: resolvePlans implementing the R5.1 / design §3.5 decision
tree — Case A (explicit path stat) and a unified upward walk for
Cases B (cwd inside plan dir) and C (cwd ancestor has dev/plans/)
with archive/ filter and lex sort; Case D returns the 3-option
error. No downward WalkDir.
* Hand-rolled parseLintFlags accepting --format={text,json},
--strict, --no-ears, --color={auto,always,never} (no-op per D-1),
both --flag=value and --flag value forms. Unknown flag → error.
* runLint orchestrator: resolvePlans → loadPlan (reads prd/design/
tasks via Scan + os.Stat for close-out markers) → lintPlan
(BuildIndex + 4 rule checkers, missing-PRD short-circuit per R3.2,
PlanDir backfill, sortDiagnostics). Placeholder text emitter —
task 6 replaces with the full emitText/emitJSON from design §3.7.
* computeExit returns 0/1 per R5.4/R5.5; 2 is reserved for outer
discovery/I-O failures (runLint returns 2 directly on those paths).
* main_test.go: TestResolvePlans covers Cases A (valid + invalid), B
(deeply-nested cwd), C (3 plans + archive filter + lex sort), D
(no ancestor); TestParseLintFlags covers every documented flag
plus unknown-flag rejection and last-value-wins. The prior stub
test TestRun_LintStub was replaced with TestRun_LintMissingPRD —
the stub assertion no longer holds now that runLint actually runs
the pipeline. Output-format assertions may need revision when
task 6.6 lands per tasks.md 5.6 note.
Parent task 5.0 from dev/plans/26172-planctl/tasks.md.
Codex code-review session: 019db2ca-721d-7b11-8760-639b2588b114 (1 round, approved on R1).
* lint.go: Severity / Code (full set) / Diagnostic types migrated
here from their temporary home in index.go. Code constants form the
grep-stable public contract per design §4.
* checkTagSyntax (passthrough over idx.MalformedTags).
* checkCrossRef with four diagnostic classes (orphan-requirement,
orphan-design, uncovered-requirement, uncovered-design), R2.8
hasDesign gate, R2.9 infra-sentinel exemption, and sorted-id emission
for stable uncovered-* order.
* checkCloseoutFiles gated on R3.4 fully-closed-out definition
(at-least-one + all-checked); emits Line=0 diagnostics with empty
Path for the emitter to substitute plan-dir.
* newMissingPRDResult: R3.1/R3.2 fatal short-circuit.
* checkEars with the five design §3.4 regex patterns, narrow
`^\*\*[^*]+\*\*` bold-prefix exemption, and --no-ears R4.4 skip.
* sortDiagnostics uses sort.SliceStable with (Path, Line, Code,
Message) tie-breakers so output is deterministic run-to-run per
PRD §6 even when two diagnostics share the first three keys
(missing-closeout-file Line=0 pair).
* Position extended with a Text field populated at extraction time
so checkEars keeps its literal `(idx, skip)` signature without
passing a second *ScanResult.
* lint_test.go: one subtest per checker plus sort stability
regression.
Parent task 4.0 from dev/plans/26172-planctl/tasks.md.
Codex code-review session: 019db2b8-21c8-7d61-85d6-1da5c18d8033 (2 rounds).
* index.go types: Plan, TagRef, Kind (+ KindRequirements/KindDesign),
TaskLine, Index, Position; Diagnostic / Severity / Code stubs parked
here until task 4.1 moves them to lint.go.
* Grammar validators: isValidReqID / isValidDesID / isInfraRef backed
by compile-time regex patterns; infra sentinel handled transparently.
* Tag state machine (extractTagsFromLine) with the design §3.3
"Tag-unclosed recovery" semantics — forward scan that treats an inner
`_Requirements:` / `_Design:` opener as a recovery signal, so
`_Requirements: R3 _Design: D§2_` produces one tag-unclosed + one
well-formed D§2 extraction. extractTags composes mask → state
machine → body-parse.
* PRD R-id extraction via ast.ListItem walk (AST-primary) with regex
fallback for malformed lists.
* Design D§ extraction via ast.Heading level 2-4 walk. Pattern widened
to tolerate the `## 0. Scope` period-space convention used by
level-2 headings in the in-repo design.md.
* Task-line extraction via east.TaskCheckBox walk; Indent from raw
line leading spaces; InCode[] exclusion per R3.3.
* BuildIndex orchestrator + extractTaskTags helper.
* Table-driven index_test.go covering all 10 cases from sub-task 3.9;
assertDiagsExact fails on extras so in-fence / inline-code-span
exclusion and recovery fixtures are tight.
* 39 `// spec:planctl/*` traceability anchors across index.go.
Parent task 3.0 from dev/plans/26172-planctl/tasks.md.
Codex code-review session: 019db26e-629f-7f51-afe2-28dbb1d14ba6 (3 rounds).
* cmd/planctl/scan.go: Scan() / scanBytes() construct a goldmark parser with
the TaskList extension; AST walk populates InCode[] for CodeBlock /
FencedCodeBlock / HTMLBlock and LineMask[] for inline CodeSpan nodes with
+/-1 byte extension to cover the surrounding backticks.
* offsetToLineCol folds '\n' bytes into the preceding line so block segment
Stop-1 maps correctly — a subtle off-by-one that tripped the closing fence
and trailing blank-line tests before the fix landed.
* cmd/planctl/scan_test.go: 7 test functions covering fenced / indented /
HTML blocks, inline code spans (substring + exact-range), nested emphasis
around code, mixed features in one document, EOF-in-fenced-block, and
trailing-newline splitLines variations.
* go.mod: goldmark promoted from indirect to direct (scan.go imports it).
Parent task 2.0 from dev/plans/26172-planctl/tasks.md.
Codex code-review session 019db23c-6c44-7d00-b3a4-6653882f0964 (2 rounds).
- On startup, daemon sweeps orphaned /tmp/jj-claude-*-files from pre-daemon
hook versions and dead-PID tracking files from all repos
- Cross-session file conflicts detected on post-edit and returned in response
(logged as warnings, non-blocking)
- session-start now returns inventory of all other active sessions with their
tracked files, PIDs, and ages
- Stale session reap reduced from 2h to 30min default (matches idle shutdown),
configurable via JJ_HOOK_STALE_MIN env var
- Debounce interval configurable via JJ_HOOK_DEBOUNCE_SEC env var (default 3s,
max 30s) — previously documented but not implemented
- handleEvent response changed from bare bool to structured JSON with ok/error/
sessions/conflicts fields (backward compatible — ok field still present)
- 30 Go tests (up from 13): inventory, conflicts, env vars, legacy cleanup