Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .claude/commands/speckit.reviewissue.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,15 +212,15 @@ _<consequence kind>:_ <what a different answer would change, one clause>
- ...
```

**The at-a-glance table.** One row per gap, in the same order as the gaps themselves, spanning both groups β€” so the consequence column is not sorted globally: it restarts at *Changes what gets built* where the Technical group begins. Titles in the `Gap` column match each gap's own title verbatim, and the consequence cell is a compression of the gap's own consequence line, so a row and its gap are unmistakably the same thing and never say different ones. Always emit the table, even for a single gap: the author should never have to check whether it is there.
**The at-a-glance table.** One row per gap, in the same order as the gaps themselves, spanning both groups β€” so the consequence column is not sorted globally: it restarts at *Changes what gets built* where the Technical group begins. Titles in the `Gap` column match each gap's own title verbatim, and the consequence cell is a compression of the gap's own consequence line, so a row and its gap are unmistakably the same thing and never say different ones. Write every `|` in a cell's content as `\|` β€” the column dividers stay bare β€” in the title cell, the consequence cell, and inside inline code spans too, because GitHub splits a row into columns on an unescaped pipe before it renders code, so `` `A | B` `` in a cell breaks the row while `` `A \| B` `` renders as `A | B`. A gap title stays free to contain `|`: its title cell differs from the gap only by that escape and still counts as verbatim. Escape nothing outside the table β€” gap bodies, recommendations and commentary are prose where `|` is harmless. Always emit the table, even for a single gap: the author should never have to check whether it is there.

**Never use the `**N. <title>**` form in the table, and never put a `> _Answer:_` line above the first group.** `/speckit.confirmissue` parses every `**N. <title>**` block in the comment as a gap, ending at its `> _Answer:_` line (its step 2). A row imitating that shape carries no answer slot of its own, so it either hard-stops the fold β€” step 2 refuses to fold anything while a parsed gap looks unanswered β€” or takes the first real gap's answer slot as its own and corrupts the decisions that do land. Table cells carry a bare number and plain text, which matches nothing the parser looks for.

**Length bound β€” 120 words per gap.** Count everything from the `**N. <title>**` line through to its `> _Answer:_` slot: the consequence line, the framing, every sub-bullet, and the recommendation with its reason. Count whitespace-separated words of the prose, taking a markdown link as its link text rather than its URL. The bound applies to the gap as a whole rather than to any one part of it, and to the comment as first composed β€” a refine run's expansion (step 6) may exceed it, where keeping the re-framing tight is the goal rather than the ceiling.

**Splitting is not how you meet the bound.** A 200-word gap broken into two 100-word gaps satisfies nothing β€” the reader faces the same prose and one more decision. Cut instead: drop the restatement of what the issue already says, keep the evidence that makes the gap specific, and let the recommendation carry the detail rather than the framing. Split only where the gap is genuinely two independent questions needing two separate answers β€” and then each half must meet the bound on its own.

**Check the draft before posting.** Composing to a bound is not the same as meeting one β€” count, do not estimate. Before step 5 posts, verify against the draft: one table row per gap, its title matching the gap verbatim and its consequence cell agreeing with the gap's consequence line; every gap carrying a consequence line, a recommendation with its one-sentence reason, and an answer slot; every gap within the bound, counted rather than judged; *Changes what gets built* ahead of *Settles a detail* within each group; and no line above the first group heading matching either `**N. <title>**` or `> _Answer:_`. Fix what fails and re-check. This applies again to a refine run's edit (step 6), minus the bound.
**Check the draft before posting.** Composing to a bound is not the same as meeting one β€” count, do not estimate. Before step 5 posts, verify against the draft: one table row per gap, its title matching the gap verbatim and its consequence cell agreeing with the gap's consequence line; no unescaped `|` anywhere in the table's content, inside inline code included, so every row renders as exactly three columns; every gap carrying a consequence line, a recommendation with its one-sentence reason, and an answer slot; every gap within the bound, counted rather than judged; *Changes what gets built* ahead of *Settles a detail* within each group; and no line above the first group heading matching either `**N. <title>**` or `> _Answer:_`. Fix what fails and re-check. This applies again to a refine run's edit (step 6), minus the bound, and with the pipe check limited to the row it changed β€” every other row passes through as posted.

**Recommendation quality bar:** the recommendation must be a concrete,
actionable default (a value, a library, a field name, an HTTP status, an
Expand Down
8 changes: 8 additions & 0 deletions docs/study/286.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# 286 β€” Escaping rule for a literal pipe in an at-a-glance table cell

| Finding | Level | Fix applied |
| --- | --- | --- |
| Reviewer (comment-analyzer, Important): the self-check clause added in 965c498 required "no unescaped pipe anywhere in the table" and "fix what fails", and the existing last sentence of that paragraph re-applies the whole check to a refine run's edit β€” while step 6 requires every row but the re-framed one to come through byte-for-byte. A review posted before the rule, carrying a bare pipe in an untouched row, would have told a refine run both to rewrite that row and to leave it alone, and resolving it either way migrates an already-posted comment the issue put out of scope. The new clause was checked against the first-run template and not against the refine-run rules the same paragraph extends to. | Execution | Refine-run pipe check limited to the row it changed; every other row passes through as posted (e0a5580) |
| Reviewer (comment-analyzer, Suggestion): "Write every pipe inside the table as `\|`", added in 965c498, also covers the column dividers when read literally β€” a row written with escaped dividers renders as one cell, and the self-check would pass it because every pipe is escaped. | Execution | Reworded to "every pipe in a cell's content", with the column dividers stated to stay bare (e0a5580) |
| The issue cites `docs/study/281.md` as self-demonstrating evidence, but that file exists only in b4f56d6, a commit on local `main` that `git log origin/main..main` shows was never pushed. `/build` branches from `origin/main`, so the file was absent from the branch, and #281's own study record β€” committed after PR #284 merged β€” has not reached the remote at all. | Execution | None; noted as absent in the /build report, and the unpushed commit left on local `main` |
| /raise-pr created PR #288 without the Claude Code attribution footer that the session's standing instructions require on every PR body; the command's own step 8 composes the body without mentioning it, and the omission was caught only after the PR was open. | Execution | Footer appended with `gh pr edit 288` after creation |
Loading