feat: Add idea fmt — Explicit Canonicalizer with Automatic Checkbox Adoption#18
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces an explicit, gofmt-style canonicalization command (idea fmt) to rewrite the backlog into canonical form on demand, including automatic adoption of “bare” markdown checkbox tasks into managed idea entries, with a --check mode suitable for CI gating.
Changes:
- Adds
idea fmtCLI subcommand with--check(no-write, exit-1-if-non-canonical) behavior and stderr-only reporting. - Refactors internal parsing/serialization to expose a shared
parseContentandrenderseam, and implements adoption + canonicalization ininternal/idea.Fmt. - Adds comprehensive unit + e2e tests and updates specs/memory docs to document the new contract.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/internal/idea/idea.go | Refactors file parsing (parseContent) and serialization (render) to support fmt/check without duplicating the write path. |
| src/internal/idea/fmt.go | Implements canonicalization + bare-checkbox adoption + --check/skip-write behavior via Fmt. |
| src/internal/idea/fmt_test.go | Adds unit coverage for canonicalization, adoption guards, idempotency, and check mode. |
| src/cmd/idea/main.go | Registers the new fmt subcommand in the root command. |
| src/cmd/idea/help_dump_test.go | Ensures help-dump includes the new fmt node. |
| src/cmd/idea/fmt.go | Adds the idea fmt cobra command wiring and stderr reporting contract. |
| src/cmd/idea/fmt_test.go | Adds end-to-end tests for routing, output channels, idempotency, and --check behavior. |
| docs/specs/overview.md | Documents idea fmt in the command table and behavior overview. |
| docs/specs/backlog-format.md | Specifies the explicit canonicalizer, adoption rules, and the preservation carve-out. |
| docs/memory/cli/structure.md | Updates CLI structure “memory” documentation to include fmt and the new seams. |
| docs/memory/cli/index.md | Updates the CLI memory index row(s) for the structure page. |
| fab/changes/260612-4m3a-add-fmt-canonicalizer-adoption/plan.md | Adds the generated plan/acceptance record for the change. |
| fab/changes/260612-4m3a-add-fmt-canonicalizer-adoption/intake.md | Adds the generated intake/background for the change. |
| fab/changes/260612-4m3a-add-fmt-canonicalizer-adoption/.status.yaml | Adds the generated status/progress metadata for the change. |
| fab/changes/260612-4m3a-add-fmt-canonicalizer-adoption/.history.jsonl | Adds the generated history/event log for the change. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Count what canonicalization will touch among the pre-existing managed | ||
| // lines, comparing each regenerated line against its raw on-disk text. | ||
| today := time.Now().Format("2006-01-02") | ||
| for i, lineIdx := range f.ideaIndices { |
There was a problem hiding this comment.
Fixed — render now takes today as a parameter; SaveFile and Fmt each pass a single value, so one fmt run stamps one consistent date across counting, adoption, and the written bytes. (68dd1ed)
| preserved. Lines whose text starts with a bracket (e.g. "- [ ] [DEV-1011] ...") | ||
| and all other non-idea content are preserved byte-for-byte. A second run is |
There was a problem hiding this comment.
Fixed — help text now says non-idea content keeps its text verbatim while line endings canonicalize file-wide (CRLF becomes LF, single trailing LF). (68dd1ed)
| - [ ] [DEV-1011] external item ← untouched (bracket guard) | ||
| ``` | ||
|
|
||
| The bracket guard keeps inert, byte-for-byte: Shape B lines (the `[issue_ids]` slot — see the next section) and bracket-metadata lines such as `- [ ] [TODO] buy milk` or `- [ ] [ab1] text` — external-looking metadata errs toward preservation. Text-less and whitespace-only checkboxes (`- [ ]` with no text) are not adopted. Headers, prose, and blank lines pass through verbatim as always. |
There was a problem hiding this comment.
Fixed — reworded to text-verbatim preservation with the LF-canonicalization caveat (byte-identical round-trip holds for LF files); also precision-fixed the same claim in the Shape B section for consistency. (68dd1ed)
|
|
||
| > **Format-contract change note.** Earlier versions of this spec declared the date a mandatory part of the line and described an idea-managed "Shape A" that *required* the date. As of the resilient-parser change (`260610-wtmn-resilient-backlog-parser`), the date is **optional on input** and **canonical (always present) on output**, and `idea` additionally accepts `*`/`+` bullets, leading whitespace, and CRLF endings on input. This widens the input contract and was a deliberate, documented change to fix silent-failure on dateless backlogs (e.g. the shll.ai backlog). The **output** contract is unchanged: `idea` still emits exactly one canonical, machine-parseable form. Shape B second-bracket lines remain inert pass-through, exactly as before. | ||
|
|
||
| > **Format-contract change note.** As of the fmt change (`260612-4m3a-add-fmt-canonicalizer-adoption`), adoption **widens which lines `idea` may rewrite**: bare checkbox lines without the 4-char `[id]` anchor were previously guaranteed non-idea pass-through; `idea fmt` (and only `fmt`) now adopts them as managed canonical idea lines (fresh ID, today's date, checked state preserved). No other command's rewrite scope changed, the canonical output form is unchanged, and Shape B second-bracket lines remain inert byte-for-byte pass-through, exactly as before. |
There was a problem hiding this comment.
Fixed — the change note now states text-untouched pass-through with canonical LF endings on rewrite (only LF files round-trip byte-identically). (68dd1ed)
a46affe to
d0be374
Compare
Meta
Pipeline: intake ✓ → apply ✓ → review ✓ → hydrate ✓ → ship → review-pr
Impact: +896/−10 code (excluding
fab/,docs/) · +1386/−22 totalSummary
Canonicalization previously happened only as a side-effect of the first mutating command, so a single
idea doneon a legacy backlog mixed formatting churn into the semantic diff — and bare- [ ]checkbox lines without an[id]anchor were invisible toidea, with no path to adopt an existing markdown task list. This addsidea fmt, a gofmt-style explicit canonicalizer built as a thin verb over the existing parse/format/save machinery: it rewrites the backlog to canonical form, automatically adopts bare checkboxes with fresh 4-char IDs, and ships a--checkmode that serves as both dry-run preview and CI gate.Changes
idea fmt(src/cmd/idea/fmt.go)--checkmode — unified preview + CI gate--file/--main/IDEAS_FILEinherited)backlog-format.mdandoverview.md)🤖 Generated with Claude Code