Skip to content

feat(hygiene/check-md032): mechanize MD032 blanks-around-lists pre-check (B-0456)#3075

Merged
AceHack merged 47 commits into
mainfrom
feat/b0456-md032-blanks-around-lists-helper-2026-05-14
May 14, 2026
Merged

feat(hygiene/check-md032): mechanize MD032 blanks-around-lists pre-check (B-0456)#3075
AceHack merged 47 commits into
mainfrom
feat/b0456-md032-blanks-around-lists-helper-2026-05-14

Conversation

@AceHack
Copy link
Copy Markdown
Member

@AceHack AceHack commented May 14, 2026

Summary

Implements B-0456 — a TS helper that detects MD032 (blanks-around-lists) violations locally so they don't escape to CI. Catches the recurring failure pattern that hit 5 tick shards this session.

What ships

Algorithm

Explicit inList: false | "top" | "block" + openFence: { char, len } | null state machine. The inList enum distinguishes top-level lists from blockquoted lists for correct block-transition handling (e.g., a blockquote line terminates a top-level list but continues a blockquoted one).

Recognises:

  • List markers: - + * 1. 1) 12. 12), capped 9 digits
  • List indent: 0-3 spaces (4+ is code); blockquote prefix (>[ \t]?)+ adds 0-3 more spaces inside the quote
  • Lists in blockquote context: > - > 1. > > - (nested); > (or > + spaces) treated as blockquoted blank
  • ATX headings: NOT list-friendly (blank required after heading)
  • Fenced code blocks: backtick + tilde; matching delim char + len; opener allows info string / closer must be whitespace-only; backtick info string forbids backticks, tilde info string allows tildes; fence transitions preserve list context for indented (list-body) fences, terminate for flush-left / blockquoted top-level fences
  • YAML front matter: skipped per markdownlint behavior
  • Markdownlint-cli2 ignore globs: applied via JSONC parse
  • Lazy unindented list-item paragraph continuations: don't end the list
  • Thematic break (---, ***, ___): terminates list
  • Blockquote-line after top-level list: terminates list (continues blockquoted list)
  • MD032 after-list side: heading / thematic break / fence / blockquote directly after a list (no blank) fires MD032 too
  • Staged-blob reads: --staged reads via git show :path (the index), not the working tree

CLI usage

bun tools/hygiene/check-md032-blanks-around-lists.ts file1.md file2.md
bun tools/hygiene/check-md032-blanks-around-lists.ts --staged

main() is exported per the canonical hygiene-tool pattern. Read errors on explicit inputs throw + exit 1; staged-blob read failures throw (an index anomaly should not silently pass). spawnSync launch failures (git not on PATH, EACCES) surface r.error.message for diagnosability.

Review-trail

Round Findings Resolution
1 4 (P0 unused param, P1 list-marker indent, P2 fenced-code blocks, P2 docstring drift) commit 3158f6a
2 8 (heading-not-list-friendly, inList-guarded continuation, fence i=0 start, fence-char/len matching, checkFiles read-error surfacing, stagedMarkdownFiles throws) 6c436bd + c15613c
3 1 (P2 include renames --diff-filter=AMR) 9bfdde9
4 5 (P1 closer rejects info string, P1 reviewer-name role-ref discipline ×2, P2 export main, P2 PR-description test count) 579dce9 + body edit
5 1 stale-snapshot duplicate of round 4 already in 579dce9
6 4 (P1 markdownlint ignore globs, P1 paren ordered-list, P2 test coverage ×2) 3ebca13 + merge 27cb70d
7 2 (P2 YAML front-matter skip, 1 stale-snapshot) 3649327
8 4 (P1 lazy continuations, P1 9-digit cap, P1 fence info-string rejection, P2 spawnSync.error.message) acd8ff8
9 2 (P1 blockquote-context lists + blockquoted blank, 1 stale-snapshot) 117fa7b
10 4 (P1 tilde-fence info-strings allow tilde; P1 fence-keeps-list-context for fence-inside-list-item; P1 test invert; P2 PR body) a86a524 + body edit
11 4 (1 P1 require → import; 3 stale-snapshot duplicates) 8498772
12 3 (P1 heading-terminates-list; P1 blockquote-fence regex; P1 flush-left fence split) 7c5f079
13 1 (P1 --staged reads index blob via git show :path) 263b7b4
14 6 (P1 permissive blockquote spacing; P1 fence-blockquote-terminates-list; P1 fail-loud staged blob; P1 test git() helper; P2 PR body stale; +1 inverted) 11fcbd5
15 1 (P1 thematic break + blockquote terminates + inList type lift to top/block) 9fdb083
16 5 (P1 after-list MD032 side; P1 blockquote inner-indent cap 0-3; P2 double-git-call refactor; P2 PR body stale; +1 test invert) this commit + body edit

39 substantive findings across 16 rounds; 6 stale-snapshot duplicates.

Acceptance criteria

  • Tool exists, passes clean fixture, fails dirty fixture
  • Test file covers spec'd edge cases + all round-discovered edge cases (66 tests)
  • CLI emits file:line (matches markdownlint output style)
  • Exported main() per canonical hygiene-tool pattern
  • --staged mode honors markdownlint-cli2 ignores
  • --staged mode reads index blob (git show :path), not working tree
  • Wire into pre-push hook OR tick-close ritual — deferred to a follow-up PR.

Out of scope (captured for future)

The session also generated MD018 ×2 + MD038 ×N + MD056 ×1 findings on tick shards. Separate helpers / a unified markdownlint-class helper are captured for follow-up rows.

🤖 Generated with Claude Code

…eck (B-0456 acceptance #1+#2+#3)

Implements the TS helper specified in B-0456: detects MD032
(blanks-around-lists) violations locally before CI, catching the
recurring failure pattern that hit 5 tick shards this session.

## What ships

- `tools/hygiene/check-md032-blanks-around-lists.ts` — scans `.md`
  files for `non-blank-line-then-list-start` pattern, emits
  `file:line: context` for each finding, exit 0/1
- `tools/hygiene/check-md032-blanks-around-lists.test.ts` — 12 tests
  covering: clean shard, bullet violation (2228Z pattern), numbered
  violation (0024Z pattern), multi-violation file, no-lists file,
  list-without-preceding-label (must NOT flag), heading-then-list
  (must NOT flag, markdownlint-permissive), nested list (must NOT
  flag), plus-space marker (the 0100Z pattern), asterisk-space
  marker, multi-digit numbered list, unreadable-file resilience

## CLI usage

```
bun tools/hygiene/check-md032-blanks-around-lists.ts file1.md file2.md
bun tools/hygiene/check-md032-blanks-around-lists.ts --staged
```

## False-positive calibration

Initial regex caught false positives on multi-line bullet items
(line N is a list-item start, line N-1 is the previous bullet's
continuation text). Tightened `isListFriendlyLeading` to recognize
3 valid leading-line cases:

1. ATX heading (markdownlint-permissive heading→list)
2. Already a list-item (sibling or nested list)
3. Indented continuation of a list-item body

After tightening: all 5 historical (now-fixed) tick shards report
clean. The unit tests still cover the patterns the tool MUST catch.

## Retroactive validation

The session generated 12 markdownlint findings across 4 rule classes
(MD032 ×5 + MD018 ×2 + MD038 ×4 + MD056 ×1). This helper covers
MD032 only — the scope B-0456 specified. The other 3 classes have
their own pattern sets and would be separate helpers (out of scope
for this PR per restraint).

## Acceptance criteria status

- [x] tool exists, passes clean fixture, fails dirty fixture
- [x] test file covers spec'd edge cases
- [x] CLI emits `file:line` (matches markdownlint output style)
- [ ] Wire into pre-push hook OR tick-close ritual — DEFERRED to a
      separate PR so this one stays focused on the detection
      mechanism. The `--staged` flag works today; the hook config
      is a one-line addition for a future follow-up.

## B-0456 row updates

Sweep-refs follow-up to mark B-0456 acceptance items 1-3 done
(item 4 deferred) is left for the next-tick agent.

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 14, 2026 00:33
@AceHack AceHack enabled auto-merge (squash) May 14, 2026 00:33
Records: PR #3075 opened with the TS helper specified in B-0456.
12 unit tests + retroactive run against the 5 historical (now-fixed)
shards confirms calibration. Three of four acceptance items done;
wire-up deferred to follow-up PR.

This shard itself was pre-checked with the helper (the tool catches
its own dogfood); exit 0 on this file.

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 36b86136f9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a Bun/TypeScript hygiene helper for locally detecting common MD032 “blank line before list” failures before CI.

Changes:

  • Adds check-md032-blanks-around-lists.ts CLI/helper with explicit file and --staged modes.
  • Adds Bun tests for common clean/dirty list-start patterns and unreadable-file behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
tools/hygiene/check-md032-blanks-around-lists.ts Implements the MD032 pre-check scanner and CLI.
tools/hygiene/check-md032-blanks-around-lists.test.ts Adds unit tests for scanner behavior and unreadable files.
Comments suppressed due to low confidence (4)

tools/hygiene/check-md032-blanks-around-lists.ts:148

  • P1: A git diff failure is converted into an empty file list, and main then exits successfully as “no staged .md files.” In a pre-push/pre-commit context this silently disables the check when git is unavailable, the working directory is not a repository, or the command errors; propagate a non-zero result instead of returning [].
  if (r.status !== 0) {
    console.error(`git diff failed: ${r.stderr ?? ""}`);
    return [];

tools/hygiene/check-md032-blanks-around-lists.ts:97

  • P1: The scanner does not track fenced code blocks, so valid Markdown examples can be reported as MD032 violations. For example, a fenced YAML block containing items: followed by - value has no markdown list for MD032 to lint, but these lines would still be flagged because the function scans every raw line.
  for (let i = 1; i < lines.length; i++) {
    const cur = lines[i] ?? "";
    if (!isListItemStart(cur)) continue;
    const prev = lines[i - 1] ?? "";
    if (isBlank(prev) || isListFriendlyLeading(prev)) continue;

tools/hygiene/check-md032-blanks-around-lists.ts:95

  • P1: This only checks the blank line before a list starts, but MD032 also requires a blank line after a list block. A file like - item immediately followed by prose would exit clean here while markdownlint still fails, so the helper is not a reliable MD032 pre-check.
  // Walk pairwise: each list-start line is checked against the line before.
  for (let i = 1; i < lines.length; i++) {
    const cur = lines[i] ?? "";
    if (!isListItemStart(cur)) continue;

tools/hygiene/check-md032-blanks-around-lists.ts:122

  • P1: Explicitly provided files that cannot be read are treated as clean, so bun ... missing.md exits 0 with “no MD032 findings.” A lint/pre-push helper should fail on unreadable input; otherwise missing files, permission errors, or stale staged paths can silently bypass the check.
      // Unreadable — skip silently. The audit-duplicate-row-ids tool's
      // round-2 review pushed in the opposite direction (surface read
      // errors) but here we're a fast pre-push helper; the user will
      // see the broken file at push time or in CI anyway.
      continue;

Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
AceHack added a commit that referenced this pull request May 14, 2026
…cy findings

Records: Copilot/Codex caught 3 consistency findings on PR #3073.
Same finding-class as PR #3066's round-4 (cross-document drift after
mid-PR correction). Fixed in 6006bab: 0223Z shard annotated with
SUPERSEDED + pointer to 0238Z; PR body updated to final IDs.

This shard was pre-validated by check-md032-blanks-around-lists.ts
(the B-0456 helper from PR #3075) before push: "1 file(s) scanned,
no MD032 findings".

Co-Authored-By: Claude <noreply@anthropic.com>
AceHack added a commit that referenced this pull request May 14, 2026
…rate-hygiene sweep (#3073)

* fix(backlog): resolve B-0409 3-way collision — completes B-0451 substrate-hygiene sweep

Fifth and FINAL per-collision cleanup from the B-0451 sweep. Three
rows shared id: B-0409:

| Row | Filed | Scope |
|---|---|---|
| P1 wallet-immune-system | 2026-05-11 10:48 PR #2709 | Wallet immune system spec (L-effort) |
| P2 amara-persona-bootstrap | 2026-05-11 10:34 PR #2704 | B-0118 amara series atomic child |
| P2 peer-call-ts-audit | 2026-05-11 10:58 PR #2706 | B-0120 peer-call series atomic child |

## Resolution: keep peer-call series at B-0409

Per external-references rule:

- B-0120 frontmatter has `children: [B-0409, B-0410, B-0411, ...]`
  AND `depends_on: [B-0409, B-0410, ...]` — strongest references
- B-0118 has body-text mentions only (editable in this PR)
- Wallet-immune row has no incoming refs from other rows
  (composes_with B-0294/B-0321 point FROM the wallet row, not TO it)

→ Keep peer-call B-0409. Renumber:

  amara B-0409 → B-0459 (completes the amara series renumber
    started in PR #3069: B-0410 → B-0457, B-0411 → B-0458, and
    now B-0409 → B-0459)
  wallet-immune-system B-0409 → B-0460

## Chain remap

PR #3069 left B-0457's `depends_on: [B-0409]` pointing at the
soon-to-be-renumbered amara B-0409. This PR remaps it:

  B-0457.depends_on: [B-0409] → [B-0459]
  B-0457.composes_with: ..., B-0409, ... → ..., B-0459, ...

Plus B-0118 parent body §Decomposition updated:

  "B-0409 — Amara persona bootstrap definition" →
  "B-0459 (renumbered from B-0409) — Amara persona bootstrap definition"

`docs/BACKLOG.md` regenerated.

## Empirical effect (CASCADE COMPLETE)

```
$ bun tools/bg/audit-duplicate-row-ids.ts
audit-duplicate-row-ids: 561 rows with id field, no duplicate IDs
```

**Down from 12 duplicate-ID groups at session start to 0 on main.**

B-0451 cleanup progress: 11/12 → **12/12** (sweep complete).

## Session-arc cascade rounds

Round 1: B-0444 P1+P2 (PR #3053) — 12 → 11 groups
Round 2: B-0068.1 (PR #3057) — 11 → 10 groups
Round 3: B-0090.1-4 batch (PR #3058) — 10 → 6 groups
Round 4: B-0370-0373 P2 batch (PR #3065) — 6 → 3 groups
Round 5: B-0410-B-0411 amara batch (PR #3069) — 3 → 1 group
**Round 6: B-0409 3-way (THIS PR) — 1 → 0 groups**

Co-Authored-By: Claude <noreply@anthropic.com>

* shard(tick): 0223Z — B-0451 substrate-hygiene cascade COMPLETE (12 → 0 groups)

Records: PR #3069 merged (ed9284d). Cross-PR sequencing constraint
cleared. Executed the final B-0409 3-way cleanup in PR #3073.

audit-duplicate-row-ids on the branch reports: 561 rows with id
field, NO DUPLICATE IDs. The substrate-hygiene cascade reaches its
terminal state once PR #3073 lands.

Session-arc final metrics:
- 14 of my PRs merged in the substrate-hygiene cascade
- 11 sibling-agent PRs merged (Otto-Desktop, Lior, Codex)
- 1 PR in flight (#3073), 1 closed-with-provenance (#3052)
- Duplicate-ID groups: 12 → 0
- Dangling-dep refs: 9 → 0
- 12 markdown findings caught across 4 rule classes
- 4 Copilot review rounds metabolized on PR #3066 (11 threads)
- New tooling: audit-duplicate-row-ids.ts
- New durable substrate: procedure memo + B-0451 + B-0456

The work + procedure are durable; future-Otto can pick up B-0456
mechanization or any new findings via the canonical reference.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(backlog): bump renumber to B-0462/B-0463 (avoid PR #3070's B-0459-B-0461 reservation) + fix B-0457 body refs

Three issues addressed in one commit:

1. **PR #3070 just merged** (2026-05-14) and reserved B-0459/0460/0461
   for B-0449 slice 5+ follow-up work (Otto-Desktop's B-0449 body
   text now points at those numbers). My initial renumber claimed
   B-0459 (amara) + B-0460 (wallet-immune) — same collision pattern
   that started this whole sweep, recurring at meta level. Bumped
   to next-available: B-0462 (amara) + B-0463 (wallet-immune).

2. **Codex P2 + Copilot threads on B-0457**: I updated B-0457's
   frontmatter `depends_on: [B-0409] → [B-0459]` but the body text
   still referenced `B-0409` for the preamble source ("Use preamble
   from B-0409"). After this PR's renumber, B-0409 belongs to a
   different peer-call-ts-audit row, so the instruction would point
   at the wrong artifact. Updated B-0457 body to "Use preamble from
   B-0462 (renumbered from B-0409 per B-0451 sweep)" — same in the
   Evidence section.

3. **B-0118 parent body**: Updated 3 places from B-0459 → B-0462 to
   match the new renumber target.

## Final state

```
$ bun tools/bg/audit-duplicate-row-ids.ts
audit-duplicate-row-ids: 561 rows with id field, no duplicate IDs
```

The cascade still terminates at zero collisions; just with B-0462
+ B-0463 instead of B-0459 + B-0460.

## Substrate-honest meta-observation

Otto-Desktop's PR #3070 + my PR #3073 ran in parallel and BOTH
reached for the same next-free range (B-0459-0461). The audit tool
prevents file-level collisions (same id field in two rows) but
doesn't prevent reservation-level collisions (one PR's body text
says "we plan to use X" while another PR makes X canonical for
different content). This is the next failure class — captured in
this commit message for B-0456 follow-up consideration.

Co-Authored-By: Claude <noreply@anthropic.com>

* shard(tick): 0238Z — caught parallel-renumber-collision with PR #3070; bumped to B-0462/0463

Records: PR #3070 (Otto-Desktop's B-0449 slice-5 reservation
renumber) merged, reserving B-0459/0460/0461. My PR #3073 was
reaching for B-0459/B-0460 — same collision pattern recurring at
meta level. Bumped to B-0462/B-0463.

Also resolved 2 Codex/Copilot threads on B-0457 body refs (real
bugs — frontmatter updated but body text still pointed at B-0409).

Meta-observation captured: audit tool catches file-level
collisions but doesn't catch reservation-level collisions where
one PR's body text says "plan to use X" while another PR makes X
canonical for different content. Next failure class for B-0456
follow-up consideration.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(shard): 0223Z — annotate superseded IDs (Copilot+Codex round-1 consistency catch)

Three Copilot/Codex threads converged on the same finding: the 0223Z
tick shard described the renumber as B-0459/B-0460 (the initial
plan) but the PR's final renumber bumped to B-0462/B-0463 (per
0238Z's catch of PR #3070's reservation collision). Without this
annotation, future readers following the 0223Z trail would land on
the wrong IDs.

Added SUPERSEDED annotations inline + a note pointing at 0238Z for
the bump narrative. The PR body has also been updated to reflect
the final IDs.

Cumulative threads resolved across this PR: 3 (Copilot round-1).

Co-Authored-By: Claude <noreply@anthropic.com>

* shard(tick): 0303Z — PR #3073 round-1 review: 3 cross-shard consistency findings

Records: Copilot/Codex caught 3 consistency findings on PR #3073.
Same finding-class as PR #3066's round-4 (cross-document drift after
mid-PR correction). Fixed in 6006bab: 0223Z shard annotated with
SUPERSEDED + pointer to 0238Z; PR body updated to final IDs.

This shard was pre-validated by check-md032-blanks-around-lists.ts
(the B-0456 helper from PR #3075) before push: "1 file(s) scanned,
no MD032 findings".

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
AceHack and others added 2 commits May 13, 2026 20:42
Four substantive findings, all addressed:

1. **Codex P2 — fenced code blocks**: `findMd032Violations` applied
   to every line pair without tracking fenced-code regions, so a
   doc-comment showing the bad MD032 pattern in a code sample would
   itself be flagged. Added `isFenceLine` detection + `inFencedCode`
   state walked alongside the line scan. New test: "list-like lines
   inside fenced code blocks are NOT flagged".

2. **Copilot P0 — unused param**: `checkFiles(files, repoRoot)` had
   `repoRoot` unused; `noUnusedParameters` in `tsconfig.json` would
   fail typecheck. Dropped the parameter; updated the only caller
   and the test.

3. **Copilot P2 — stale comment**: `checkFiles`'s docstring said
   "emits to stderr; returns total count" but the function returns
   an array and the CLI owns the output boundary. Updated comment
   to match the actual contract.

4. **Copilot P1 — CommonMark indent depth**: `^\s*` was too greedy
   for `isListItemStart` — would catch 4+ space indented "list-like"
   lines that CommonMark treats as code blocks (not lists), so
   `    - option` inside a code sample after non-blank text would
   be flagged. Tightened to `^ {0,3}` (CommonMark spec).
   `isFenceLine` uses the same `^ {0,3}` boundary. New test:
   "indent-as-code (4+ spaces) is NOT a list marker".

Tests: 15/15 pass (12 original + 3 new for the fenced-block and
indent-depth fixes).

Co-Authored-By: Claude <noreply@anthropic.com>
Records: PR #3075 round-1 caught 4 real findings (P0 unused param,
P1 CommonMark indent depth, P2 fenced code blocks, P2 stale comment).
All resolved in 3158f6a; 3 new tests added. Tests now 15/15 pass.

This shard pre-validated by the helper before push (the helper that
exists because of MD032 failures, validated by itself).

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 14, 2026 00:43
…LE on main

Records: PR #3073 (B-0409 3-way) MERGED to main as 6794dfc. The
substrate-hygiene cascade is now complete and durable on main:
audit-duplicate-row-ids reports "561 rows with id field, no
duplicate IDs". 12 → 0 duplicate-ID groups across 6 rounds. 9 → 0
dangling-dep refs.

Total session output: 14 of my PRs merged + 1 in flight (#3075) +
12 sibling-agent PRs merged + 1 closed-with-provenance (#3052).

Durable artifacts on main: audit tool, MD032 helper PR-in-flight,
procedure memo (242 lines), B-0451 cleanup row, B-0456 mechanization
row, 6 lost backlog rows recovered, 2 slice rows restored, ~16
collision rows renumbered.

This shard pre-validated by the B-0456 helper (PR #3075) — clean.

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
Comment thread tools/hygiene/check-md032-blanks-around-lists.test.ts Outdated
Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6f20e2d938

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
AceHack added a commit that referenced this pull request May 14, 2026
Codex P2 — calibration.md: zero-denominator behavior undefined for
Lead-time% and Action-rate%. Added explicit `n/a (0/0)` rule;
documented that 0/0 must NOT be recorded as 0% (would bias trajectory
toward "role failing" thresholds when the role simply had nothing to
evaluate).

Copilot P1 — calibration.md: persona-name attribution ("PM-1 (Otto)",
"how many Otto picked up", "Updated by Mira") on a current-state
surface. Per Otto-279 carve-out, persona names belong only on history
surfaces or roster-mapping section of governance files. Rewrote to
role-refs ("the PM-2 role", "the PM-1 role"). The persona-to-role
mapping for Mira is established in docs/EXPERT-REGISTRY.md.

Copilot P1 — TEMPLATE.md: same persona-name issue ("PM-2 / Mira",
"author: pm2 (Mira)", "Mira predicts, Otto delivers"). Rewrote to
role-refs and added explicit pointer to EXPERT-REGISTRY.md for
persona resolution.

Copilot P1 (now also markdownlint MD032) — B-0145 row: three
"label-then-bullet" patterns on lines 19, 26, 31 fired MD032
in the gate-workflow markdownlint check. Same pattern PR #3075
helper catches. Added blank lines before each list. Helper from
PR #3075 worktree confirms 3 file(s) scanned, no MD032 findings.

The Copilot P1 last_updated bump (originally 2026-05-02) was
already addressed in an earlier commit (now 2026-05-13); the
thread is resolved by the existing value.

Co-Authored-By: Claude <noreply@anthropic.com>
Six correctness and error-handling issues fixed:

1. Heading false-negative: removed ATX-heading exception from
   isListFriendlyLeading — markdownlint requires a blank line before a
   list even when preceded by a heading.

2. Test fixture aligned: updated "heading directly followed by list"
   test to expect 1 finding (line 4) instead of zero, matching real
   markdownlint behaviour.

3. Context-aware whitespace continuation: `isListFriendlyLeading` now
   receives `inList: boolean`; the indented-continuation exemption only
   fires when already inside an established list block, preventing
   ordinary indented prose lines from suppressing a real MD032 violation.

4. Fence off-by-one: loop now starts at i=0 (was i=1) so a fenced-code
   open on the very first line is recorded and inFencedCode is set
   correctly.

5. git diff failure no longer masked: stagedMarkdownFiles throws on
   non-zero exit (was returning []), and main catches + exits 1, so a
   bad repo root or missing git binary surfaces as an error instead of
   a silent "nothing to scan" pass.

6. Unreadable explicit inputs surface an error: checkFiles gains an
   optional surfaceReadErrors param (default false for --staged mode);
   main passes true for explicit file args so a typo or missing path
   exits 1 rather than reporting a clean scan.

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Copy Markdown
Member Author

@AceHack AceHack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lior anti-gravity check: DRIFT DETECTED. Narration-over-action. You built a detection mechanism (check-md032-blanks-around-lists.ts) but deferred wiring it to a hook or CI. This is building capability without exercising it. Add the hook wiring so the tool is actually used. Do not merge dead code.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6c436bd53d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
Builds on the sibling commit 6c436bd, which addressed 6 P1 Copilot
findings + 1 Codex P2 (staged-file discovery error) from PR #3075
round 2. The remaining Codex P2 finding (thread 7 — track matching
fence delimiters before leaving code blocks) is addressed here.

Replaces `isFenceLine(line): boolean` with `fenceInfo(line): { char,
len } | null`, and replaces `inFencedCode: boolean` with the
opening-fence record `openFence: { char: "`" | "~"; len: number } |
null`. A subsequent fence line only closes the block when its char
matches the opener AND its run length is at least the opener's. Inner
fences with a different delimiter (or a shorter run with the same
delimiter) leave the outer block open, so list-like content inside a
fenced example is no longer falsely flagged.

The boolean toggle approach flipped `inFencedCode` on every fence
line regardless of delimiter character or length. That meant an
outer four-backtick block holding an inner three-backtick example,
or a backtick fence holding a tilde-fence example, would toggle out
prematurely and report the inner list-like content as MD032
violations — exactly the false-positive class the fenced-code
handling was added to prevent.

Tests: 17 pass (was 15 after sibling commit, +2 here).
New cases:
- Inner tilde-fence inside backtick-fence: outer stays open
- Inner shorter backtick run inside longer backtick fence: outer stays open

Validation: helper run across all 14/05/14 tick shards + PR #3074
touched files (calibration.md, TEMPLATE.md, B-0145 row, 0051Z tick
shard) returns clean — round-trip evidence the additional rigor
doesn't regress on the legitimate-clean corpus.

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 14, 2026 01:00
AceHack added a commit that referenced this pull request May 14, 2026
…45) (#3074)

* feat(pm2): add Mira to EXPERT-REGISTRY + forward-radar scaffold (B-0145 slice)

Closes remaining open acceptance criteria in B-0145 after children
B-0270 (skill+agent) and B-0271 (first research pass) were already closed.

Changes:
- docs/EXPERT-REGISTRY.md: add PM-2 / Mira row (AC 1)
- docs/forward-radar/TEMPLATE.md: memo output template (AC 3)
- docs/forward-radar/calibration.md: Lead-time% + Action-rate% tracker (AC 5)
- docs/backlog/P1/B-0145-*.md: pre-start checklist per backlog-item-start-gate

AC 2 (weekly Sunday UTC cadence) is documented in TEMPLATE.md header.
AC 4 (first memo) was addressed by B-0271 research doc.

operative-authorization: aaron 2026-05-13: "Cooling period: TBD. The memory file IS the durable record"

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(pm2/b-0145): address PR #3074 review findings (4 threads)

Codex P2 — calibration.md: zero-denominator behavior undefined for
Lead-time% and Action-rate%. Added explicit `n/a (0/0)` rule;
documented that 0/0 must NOT be recorded as 0% (would bias trajectory
toward "role failing" thresholds when the role simply had nothing to
evaluate).

Copilot P1 — calibration.md: persona-name attribution ("PM-1 (Otto)",
"how many Otto picked up", "Updated by Mira") on a current-state
surface. Per Otto-279 carve-out, persona names belong only on history
surfaces or roster-mapping section of governance files. Rewrote to
role-refs ("the PM-2 role", "the PM-1 role"). The persona-to-role
mapping for Mira is established in docs/EXPERT-REGISTRY.md.

Copilot P1 — TEMPLATE.md: same persona-name issue ("PM-2 / Mira",
"author: pm2 (Mira)", "Mira predicts, Otto delivers"). Rewrote to
role-refs and added explicit pointer to EXPERT-REGISTRY.md for
persona resolution.

Copilot P1 (now also markdownlint MD032) — B-0145 row: three
"label-then-bullet" patterns on lines 19, 26, 31 fired MD032
in the gate-workflow markdownlint check. Same pattern PR #3075
helper catches. Added blank lines before each list. Helper from
PR #3075 worktree confirms 3 file(s) scanned, no MD032 findings.

The Copilot P1 last_updated bump (originally 2026-05-02) was
already addressed in an earlier commit (now 2026-05-13); the
thread is resolved by the existing value.

Co-Authored-By: Claude <noreply@anthropic.com>

* docs(tick): 0051Z — PR #3074 round-1 review resolved (4 threads)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(pm2): calibration seed row + Action-rate window + MD018 lint

- calibration.md FR-0 row: replace 0% (baseline) with n/a (0/0) so
  the seed row obeys the zero-denominator rule defined above it
- calibration.md step 4: enforce that pickup must happen within the
  4-round window; late pickups no longer inflate Action-rate%
- 0051Z.md line 83: reword to avoid bare #NNNN at line start
  (MD018 no-missing-space-atx)

Resolves two unresolved Copilot review threads on PR #3074.
Fixes markdownlint CI failure.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AceHack and others added 3 commits May 13, 2026 21:02
…e-that-came-before

Co-Authored-By: Claude <noreply@anthropic.com>
`git diff --diff-filter=AM` drops files with status `R`, so a staged
`git mv old.md new.md` (with edits) was skipped by --staged even though
markdownlint will still lint `new.md` in CI. Add `R` to the filter so
the gate scans renamed staged files too.

Verified by checking `git diff --name-status --cached` (`R...`) vs
`git diff --name-only --cached --diff-filter=AMR` — renamed files now
appear in the latter.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated 5 comments.

Comments suppressed due to low confidence (1)

tools/hygiene/check-md032-blanks-around-lists.ts:169

  • P1: fenceInfo still only accepts zero or one space after each blockquote marker, so valid blockquoted fences like > ```ts are not recognized even though CommonMark allows the remaining indentation before the fence. List-like lines inside that fence will be scanned as prose/list items and can produce MD032 false positives that markdownlint would not report.
  // prose lines do not get a free pass.

Comment thread tools/hygiene/check-md032-blanks-around-lists.ts
Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
Comment thread tools/hygiene/check-md032-blanks-around-lists.test.ts
Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
Comment thread tools/hygiene/check-md032-blanks-around-lists.test.ts
Same class as the 0143Z and 0202Z fixes earlier in this PR: a code
span with `>` + spaces triggers markdownlint MD038
(no-space-in-code). Rewording the description in prose avoids it.

This is becoming a recurring authoring failure for tick shards
that document markdown-grammar changes. The helper helps with
MD032 but other markdown-lint rules still need vigilance until
the corresponding helpers exist.

Co-Authored-By: Claude <noreply@anthropic.com>
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9fdb083cee

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
…+ main()/checkStagedFiles refactor)

P1 — MD032's after-list side. Markdownlint's blanks-around-lists
rule fires when a list is followed by non-blank content (heading,
thematic break, fence, blockquote) without a separating blank. The
helper previously only checked the before-list side. Now each
block-transition reset path also fires MD032 at the transition
line if the previous line wasn't blank:

- heading immediately after list → fires on heading line
- thematic break immediately after list → fires on `---`/`***` line
- flush-left fence immediately after list → fires on fence line
- blockquote line after top-level list → fires on `>` line

P1 — Blockquote inner-indent cap. CommonMark caps list-marker
indent at 0-3 spaces even inside a blockquote: `>     - item`
(5 spaces inside the quote) is indented code, not a list. The
earlier `(>[ \t]*)*` regex allowed unlimited post-`>` spaces;
changed to `(?:(?:>[ \t]?)+ {0,3})?` which structures the indent
correctly:

- Top-level: 0-3 leading spaces, then marker
- Blockquoted: 0-3 leading spaces, blockquote chain (each `>` with
  0-1 optional space), 0-3 inner spaces, then marker

The change also corrects a round-14 regression where the combined
indent allowed up to 6 spaces (3 leading + 3 inner) for non-
blockquoted lines.

P2 — `main()` passes the discovered staged-path list to
`checkStagedFiles` (new optional param) instead of calling
`stagedMarkdownFiles` twice. Avoids double git invocation and
prevents `fileCount` divergence if the index changes between the
two calls.

P2 — PR description test count refreshed in the GraphQL body edit
to reflect 66 tests + 16-round review trail.

Tests: 66 pass (+5). New cases:
- After-list MD032: heading directly after list fires
- After-list MD032: thematic break directly after list fires
- After-list MD032 suppressed by blank-line separator (control)
- Blockquote indent 5 spaces is NOT a list (indented code)
- Blockquote indent 3 spaces IS still a list (boundary)

Pre-existing tests updated where after-list MD032 now produces a
2nd finding (round 12 heading-between, round 14 fence-blockquote-
between, round 15 thematic-break-between, round 12 flush-left
fence-between).

Validation: helper run across all 14/05/14 tick shards (46 files)
returns clean.

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 14, 2026 02:38
AceHack and others added 2 commits May 13, 2026 22:39
…dent cap)

Co-Authored-By: Claude <noreply@anthropic.com>
…Codex P2)

CommonMark allows a blockquote marker plus optional space plus up
to 3 spaces of content indentation before a fence opener, so
`>  \`\`\`` (1-2 spaces between `>` and the fence) is a valid
blockquoted fence. The earlier regex used `(?:>\s?)*` immediately
followed by the fence run, so it only matched `>\`\`\`` or `> \`\`\``
and missed `>  \`\`\``.

Aligned `fenceInfo` regex with the round-16 `isListItemStart`
shape: `^ {0,3}(?:(?:>[ \t]?)+ {0,3})?(\`{3,}|~{3,})(.*)$`.
The inner `{0,3}` slot accepts the same 0-3 content indent the
list-marker check uses.

Without the fix, the helper treated inner `> - item` lines as
real lists and could report MD032 findings markdownlint itself
does not flag.

Tests: 67 pass (+1). New case asserts a `>  \`\`\``-opened fence
hides its `> - item` content from MD032 scanning.

Co-Authored-By: Claude <noreply@anthropic.com>
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 24 out of 24 changed files in this pull request and generated 5 comments.

Comments suppressed due to low confidence (1)

tools/hygiene/check-md032-blanks-around-lists.ts:448

  • This inline comment still says the default read-error path is for --staged mode, but staged scanning no longer reaches this filesystem-read branch. The comment should be updated or removed so future readers do not reintroduce the old silent-skip staged behavior.
      content = readFileSync(f, "utf-8");

Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
Comment thread tools/hygiene/check-md032-blanks-around-lists.ts
Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
Comment thread tools/hygiene/check-md032-blanks-around-lists.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 47a7adc5a5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tools/hygiene/check-md032-blanks-around-lists.ts
AceHack and others added 2 commits May 13, 2026 22:46
… empty headings, docstring refresh)

P1 — `isThematicBreak` now strips the optional blockquote prefix
before matching `---`/`***`/`___`, so `> ---` inside a blockquoted
list (`> - a / > --- / > - b`) is recognised as a thematic break
that terminates the blockquoted list. Markdownlint MD032 fires on
the second blockquoted bullet (and the after-list side fires on
the thematic-break line).

P1 — `isListItemStart` accepts a marker followed by `\\s+` OR by
end-of-line (`\\s*$`). Bare markers like `-` (with no content) are
valid empty list items per CommonMark; a label directly followed
by an empty list item must still fire MD032.

P1 — `isHeading` accepts `#{1,6}` followed by `\\s+` OR by end-of-
line. Empty ATX headings (`##` alone) are valid per CommonMark and
must terminate a preceding list with after-list MD032.

P1 — `checkFiles` docstring refreshed to reflect the round-13
architecture: `--staged` no longer goes through `checkFiles` (it's
routed through `checkStagedFiles`/`readStagedBlob` which fail loud).
The `surfaceReadErrors = false` branch is kept for backwards-
compat with non-main callers but no longer used by `main()`.

Round-18 thread 2 (P1 fenceInfo blockquote spacing) is a stale-
snapshot duplicate of round-17 (already addressed in commit
d172d57). Resolves without code change.

Tests: 71 pass (+4). New cases:
- blockquoted thematic break terminates blockquoted list (both
  sides of MD032)
- bare list marker is recognised as a list-item-start
- bare paren marker (`1)`) is recognised
- empty ATX heading terminates a list (after-list MD032)

Validation: helper run across all 14/05/14 tick shards (47 files)
returns clean.

Co-Authored-By: Claude <noreply@anthropic.com>
Markdownlint ignores list-like content inside `<!-- ... -->` blocks,
including `markdownlint-disable` / `markdownlint-enable` comments
that authors use to suppress specific rules. The helper now
mirrors that behavior: lines inside a multi-line HTML comment are
skipped before any other scanning.

State: new `inHtmlComment: boolean` tracked alongside `inFencedCode`
and `inList`. A line that begins inside a comment (or contains
`<!--` without a matching `-->` on the same line) is treated as
comment content. The inline-comment case (`<!-- x --> markdown`)
is conservatively skipped entirely — partial-line markdown around
an inline comment is rare and not worth the extra complexity.

Tests: 74 pass (+3). New cases:
- Multi-line HTML comment with commented-out MD032 example: clean
- markdownlint-disable comment with multi-line body: clean
- Inline HTML comment doesn't mask a real MD032 elsewhere: 1 finding

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 14, 2026 02:49
@AceHack AceHack merged commit 9757609 into main May 14, 2026
27 checks passed
@AceHack AceHack deleted the feat/b0456-md032-blanks-around-lists-helper-2026-05-14 branch May 14, 2026 02:52
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5dbeafedc9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +321 to +325
const openIdx = cur.indexOf("<!--");
if (openIdx !== -1) {
const closeAfter = cur.indexOf("-->", openIdx + 4);
if (closeAfter === -1) inHtmlComment = true;
continue;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve Markdown tokens on lines with inline HTML comments

Treating any line containing <!-- as entirely comment content causes false negatives for real MD032 cases when list syntax shares that line with an inline comment. For example, Label:\n- item <!-- note --> and - item <!-- note -->\n## Heading both trigger MD032 in markdownlint-cli2, but this scanner skips the list line and never enters list state, so --staged can pass while CI fails. The parser should only ignore the comment span itself (or only full-line comment blocks), not discard the whole line unconditionally.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 24 out of 24 changed files in this pull request and generated 6 comments.

continue;
}

if (isListItemStart(cur)) {
Comment on lines +321 to +325
const openIdx = cur.indexOf("<!--");
if (openIdx !== -1) {
const closeAfter = cur.indexOf("-->", openIdx + 4);
if (closeAfter === -1) inHtmlComment = true;
continue;
});

describe("findMd032Violations — round 19", () => {
test("HTML comment regions are skipped (pre-CI review P2 on PR #3075 round 19)", () => {
Comment on lines +45 to +46
line: number; // 1-indexed line where the list starts (after the bad break)
context: string; // the first 60 chars of the offending list line
Comment on lines +953 to +957
// The default silent-skip mode is for `--staged` (CI surfaces the
// I/O error). When the caller explicitly opted in via the
// `surfaceReadErrors` flag (used by `main()` for explicit CLI
// file args), an unreadable file must throw so a typo doesn't
// exit 0 with "no findings."
Comment on lines +175 to +197
* without an intervening blank. Two valid cases:
*
* 1. Already a list-item line (the prior bullet — sibling/nested list).
* 2. An indented continuation of a list item — BUT ONLY when we are
* already confirmed to be inside a list block (`inList === true`).
* Without the `inList` guard, ordinary prose indented 1–3 spaces
* (which CommonMark permits) would be treated as list continuation
* and suppress a real MD032 violation (pre-CI review P1 on PR #3075
* round 2).
*
* Note: ATX headings are intentionally NOT list-friendly. markdownlint's
* blanks-around-lists check requires a blank line before a list even
* when the preceding line is a heading — treating headings as exempt
* produces false negatives in CI (pre-CI review P1 on PR #3075 round 2).
*/
function isListFriendlyLeading(line: string, inList: boolean): boolean {
// Already a list item — nested or sibling list, both fine.
if (isListItemStart(line)) return true;
// Indented continuation of a list-item body — only suppress if we are
// already inside an established list block so that arbitrary indented
// prose lines do not get a free pass.
if (inList && /^\s+\S/.test(line)) return true;
return false;
AceHack added a commit that referenced this pull request May 14, 2026
Co-authored-by: Claude <noreply@anthropic.com>
AceHack added a commit that referenced this pull request May 14, 2026
…list terminator (PR #3075 follow-up) (#3091)

* fix(check-md032): indented blockquote inside list-item body is NOT a list terminator (PR #3075 follow-up)

After PR #3075 merged, running the helper across the full
`docs/hygiene-history/**` corpus (584 files) surfaced 2 false
positives on `docs/hygiene-history/ticks/2026/05/13/0645Z.md`
at lines 24-25:

```
- bullet describes a thing
  > the thing's exact quoted words      ← indented `>`, not column 0
- next bullet                            ← falsely flagged as new list
```

The round-15 / round-16 work added "blockquote line terminates a
top-level list" + "after-list MD032 fires on the blockquote line
when no blank separator." The `isBlockquoteLine` regex (`^ {0,3}>`)
matched both column-0 AND 1-3-space-indented `>` lines. But an
indented `>` (1+ spaces) is part of the enclosing list-item body /
continuation indent, NOT a new top-level block — it shouldn't
terminate the list.

Fix: tighten `isBlockquoteLine` to column-0 only (`^>`). A 2-space-
indented `> quote` line is now correctly treated as list-item-body
content, and the list continues to the next bullet without firing
either MD032 side.

Tests: 76 pass (+2). New cases:
- Indented blockquote inside list-item body: clean
- Column-0 blockquote between bullets still terminates (control)

Validation: helper run across the full `docs/hygiene-history/**`
corpus (584 files) now returns clean — confirming the fix is the
correct calibration rather than over-relaxation.

Round 15/16 tests with column-0 blockquote ("blockquote line after
top-level list terminates") still pass unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(check-md032): isBlockquoteLine accepts 0-1 leading spaces (round-20 P1 review on the round-20 fix)

The round-20 commit (this PR's first push) tightened
`isBlockquoteLine` from `^ {0,3}>` to `^>`, which over-corrected.
Per CommonMark, a top-level blockquote can have 0-3 leading
spaces; the round-20 false-positive was specifically about a
2-space-indented `>` being treated as top-level when it was
actually a child block of a list-item body.

The calibration: list-item continuation indent is typically 2
spaces (for `- item` / `* item` / `+ item`); a `>` at 2-3 leading
spaces is therefore usually a child block. A `>` at 0-1 leading
spaces is unambiguously top-level. The new regex `^ {0,1}>`
captures this calibration.

Reviewer rationale (composed):

- Copilot P1: 1-space-indented `>` after `- item` IS still
  top-level per CommonMark; my round-20 `^>` regressed it
- Codex P2: `- a / ` ` > quote / - b` now misses the after-list +
  before-list MD032 findings markdownlint reports

New test asserts the 1-space-indented top-level blockquote
terminates the surrounding list (composing with the existing
column-0 and indented-blockquote-as-child-body tests).

Tests: 77 pass (+1, on stable run; one temp-git-repo test is
timing-flaky at 5s timeout — passes on rerun).

Validation: full `docs/hygiene-history/**` corpus (584 files)
still returns clean.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
AceHack added a commit that referenced this pull request May 14, 2026
…D018)

`#3075` at column 0 was parsed by markdownlint as an attempted ATX
heading (MD018: no space after hash). Dropping the `#` prefix on
the wrapped line keeps the meaning ("PR 3075") without triggering
the lint.

Co-Authored-By: Claude <noreply@anthropic.com>
AceHack added a commit that referenced this pull request May 14, 2026
…ded (hook wiring deferred) (#3092)

* chore(tick): 0314Z shard — PR #3091 MERGED; B-0456 arc fully closed; 18 PRs this session

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(tick/0314Z): drop leading `#` from PR references at line start (MD018)

`#3075` at column 0 was parsed by markdownlint as an attempted ATX
heading (MD018: no space after hash). Dropping the `#` prefix on
the wrapped line keeps the meaning ("PR 3075") without triggering
the lint.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(tick/0314Z): correct relative link + accuracy on B-0456 status

Two Copilot P1 findings:

1. The relative link `../../tools/...` from a shard at depth
   `docs/hygiene-history/ticks/2026/05/14/` did NOT resolve to
   the repo `tools/` directory (it pointed under `docs/hygiene-
   history/ticks/2026/tools/`). Replaced the broken link with an
   inline code-span reference — the path is still readable and
   doesn't drift to a wrong file.

2. The "B-0456 arc fully closed" claim conflicts with the row's
   actual status (`status: open` in
   `docs/backlog/P2/B-0456-...md`) — acceptance criterion #4
   (wire helper into pre-push hook OR tick-close ritual) is
   still deferred. Reworded shard title + body to "implementation
   landed; hook wiring deferred."

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
AceHack added a commit that referenced this pull request May 14, 2026
New `.claude/hooks/check-md032-pretooluse.ts` mirrors the
`verify-branch-pretooluse.ts` pattern:

- Reads stdin JSON per Claude Code hooks contract
- Filters to `git commit` commands only — other Bash invocations
  exit 0 with zero overhead
- Gated on `ZETA_MD032_PRECOMMIT=1` env var (opt-in like
  `ZETA_EXPECTED_BRANCH` — default unchanged commit experience)
- Runs `bun tools/hygiene/check-md032-blanks-around-lists.ts --staged`
- Allows commit on exit 0; denies with finding details on non-zero

Wired in `.claude/settings.json` as a second hook under the
PreToolUse Bash matcher (after `verify-branch-pretooluse.ts`).

B-0456 row updated:

- All 5 acceptance criteria now checked off (the implementation
  helper landed in PR #3075; this PR closes #4 — hook wiring)
- `status: open` → `status: closed`
- `closed: 2026-05-14` added

Self-test confirms:

- No env var → exit 0, no work
- ZETA_MD032_PRECOMMIT=1 + non-commit Bash → exit 0, no work
- ZETA_MD032_PRECOMMIT=1 + git commit + no staged .md →
  helper prints "no staged .md files" + exit 0

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants