Commit graph

7 commits

Author SHA1 Message Date
Sid
4d865dfd82 feat(planctl): plan-dir discovery + CLI dispatch wiring
* 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).
2026-04-21 19:27:18 -06:00
Sid
77e1ff19b6 feat(planctl): lint rules — tag-syntax, cross-reference, file-presence, EARS
* 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).
2026-04-21 19:11:29 -06:00
Sid
3f63d24c41 feat(planctl): indexer — R-id / D§ id / tag / task-line extraction
* 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).
2026-04-21 17:55:41 -06:00
Sid
aa73e2f727 feat(planctl): scanner — goldmark-backed InCode + LineMask classification
* 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).
2026-04-21 17:02:59 -06:00
Sid
a7a79eab65 feat(planctl): project skeleton — cmd/planctl, goldmark dep, subcommand dispatch
* cmd/planctl/main.go: run() entry, --help (usage + classification codes),
  --version, lint subcommand stub, R6.1 sentinel for v2-reserved subcommands
  (list/next/complete/new-plan), R5.8 exit-2 for unknown subcommand/flag.
* go.mod: require github.com/yuin/goldmark v1.8.2 (will be promoted from
  indirect to direct once the scanner imports it in parent task 2).
* cmd/planctl/main_test.go: 8 subtests covering help/version/v2-reserved/
  unknown/no-args/lint-stub with // spec:planctl/... tags mirroring production.
* .gitignore: add planctl binary so stray go-build artifacts don't trip jj's
  snapshot size limit.

Parent task 1.0 from dev/plans/26172-planctl/tasks.md.
Codex code-review session 019db22d-7978-7e70-ae87-5242d1eedc25 (3 rounds).
2026-04-21 17:02:59 -06:00
Sid
7467ef65f8 feat(planctl): initialize feature branch + migrate plan dir
Planner→implementer handoff per dev/README.md: move the 26172-planctl plan dir (prd, design, tasks, codex-sessions, handoff) from the shared plans commit into this feature branch via jj squash, so tasks.md is visible in the branch's history for /process-task-list to consume.

Parent task 0.0 from dev/plans/26172-planctl/tasks.md.
2026-04-21 17:02:59 -06:00
Sid
ed72ab9d5c dev: add spec-driven workflow, agent skills, and AGENTS.md convention 2026-04-19 23:37:12 -06:00