Skip to content

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

Merged
AceHack merged 6 commits into
mainfrom
fix/b0409-3way-collision-renumber-amara-and-wallet-2026-05-14
May 14, 2026
Merged

fix(backlog): resolve B-0409 3-way collision — completes B-0451 substrate-hygiene sweep#3073
AceHack merged 6 commits into
mainfrom
fix/b0409-3way-collision-renumber-amara-and-wallet-2026-05-14

Conversation

@AceHack
Copy link
Copy Markdown
Member

@AceHack AceHack commented May 14, 2026

Summary

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 #2709 Wallet immune system spec (L-effort)
P2 amara-persona-bootstrap 2026-05-11 10:34 #2704 B-0118 amara series atomic child
P2 peer-call-ts-audit 2026-05-11 10:58 #2706 B-0120 peer-call series atomic child

Resolution: keep peer-call series at B-0409

Per external-references rule (canonical per #3066 procedure):

  • 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 here)
  • Wallet-immune-system 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 others to B-0462 + B-0463 (bumped past PR #3070's B-0459/0460/0461 reservation for B-0449 slice 5+):

amara-persona-bootstrap B-0409 → B-0462  (completes amara series — B-0410 → B-0457, B-0411 → B-0458 already in PR #3069)
wallet-immune-system B-0409 → B-0463

Chain remap

#3069 left B-0457.depends_on: [B-0409] pointing at the soon-to-be-renumbered amara B-0409. This PR remaps it to [B-0462] (and updates the body-text reference per Codex+Copilot round-1 catch).

B-0118 parent body §Decomposition section updated to point at new IDs.

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 PR What Groups
1 #3053 B-0444 P1+P2 12 → 11
2 #3057 B-0068.1 11 → 10
3 #3058 B-0090.1-4 batch 10 → 6
4 #3065 B-0370-0373 P2 batch 6 → 3
5 #3069 B-0410-B-0411 amara batch 3 → 1
6 (this PR) B-0409 3-way (final) → B-0462/B-0463 1 → 0

Round-1 reviewer catch already addressed

Initial commit on this branch picked B-0459/B-0460 (next-free at the time). PR #3070 then merged and reserved B-0459/0460/0461 for B-0449. Bumped to B-0462/B-0463 in commit 13f285f. Tick shards in this PR document both the initial plan and the bump.

🤖 Generated with Claude Code

…rate-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>
Copilot AI review requested due to automatic review settings May 14, 2026 00:20
@AceHack AceHack enabled auto-merge (squash) May 14, 2026 00:20
…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>
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: cc20e7c95b

ℹ️ 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".

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

Resolves the final B-0451 duplicate backlog-row ID collision by ensuring id: B-0409 is uniquely owned by the peer-call series row (B-0120 child), and renumbering the other two colliding rows to new IDs while updating cross-references and the generated backlog index.

Changes:

  • Renumbered the B-0118 child “amara persona bootstrap” from B-0409B-0459 and recorded renumber provenance in frontmatter.
  • Renumbered the wallet immune system row from B-0409B-0460 and recorded renumber provenance in frontmatter.
  • Updated dependent references (e.g., B-0457 depends_on) and regenerated docs/BACKLOG.md to reflect the new IDs.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
docs/backlog/P2/B-0459-amara-persona-bootstrap-preamble-definition-ts-first-riven-2026-05-11.md Updates id to B-0459 and adds renumber provenance metadata.
docs/backlog/P2/B-0457-amara-ts-core-openai-api-invoke-flag-parity-ts-first-riven-2026-05-11.md Remaps depends_on/composes_with to the renumbered preamble row (B-0459).
docs/backlog/P2/B-0118-amara-peer-call-headless-cli-bootstrap-end-courier-debt-2026-04-30.md Updates decomposition section to reference B-0459 and the new child set.
docs/backlog/P1/B-0460-wallet-immune-system-vaccine-spread-poucc-spec.md Updates id to B-0460, adds last_updated, and records renumber provenance metadata.
docs/BACKLOG.md Regenerates index entries to remove old B-0409 links and add B-0460/B-0459.

AceHack and others added 2 commits May 13, 2026 20:27
…-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>
… 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>
Copilot AI review requested due to automatic review settings May 14, 2026 00:28
@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: 13f285fd59

ℹ️ 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 docs/hygiene-history/ticks/2026/05/14/0223Z.md 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

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

Comment thread docs/hygiene-history/ticks/2026/05/14/0223Z.md Outdated
Comment thread docs/hygiene-history/ticks/2026/05/14/0238Z.md
AceHack and others added 2 commits May 13, 2026 20:36
…nsistency 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>
…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>
Copilot AI review requested due to automatic review settings May 14, 2026 00:37
@AceHack AceHack merged commit 6794dfc into main May 14, 2026
24 checks passed
@AceHack AceHack deleted the fix/b0409-3way-collision-renumber-amara-and-wallet-2026-05-14 branch May 14, 2026 00:39
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 8 out of 8 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

docs/backlog/P1/B-0463-wallet-immune-system-vaccine-spread-poucc-spec.md:14

  • The renumbered_reason states there were "No incoming references to wallet-immune-B-0409 from other rows" (beyond composes_with B-0294/B-0321), but there are still backlog rows that reference B-0409 (e.g., docs/backlog/P2/B-0413-... and docs/backlog/P2/B-0414-... both have composes_with: [B-0409]). After this renumber, those references now resolve to the peer-call audit row, which is likely incorrect. Please either update those referencing rows to point at B-0463 (if they meant the wallet row) and/or adjust this claim so it stays accurate.

AceHack added a commit that referenced this pull request May 14, 2026
…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>
AceHack added a commit that referenced this pull request May 14, 2026
…eck (B-0456) (#3075)

* feat(hygiene/check-md032): mechanize MD032 blanks-around-lists pre-check (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>

* shard(tick): 0252Z — B-0456 MD032 helper shipped + self-validated

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>

* fix(hygiene/check-md032): Codex+Copilot round-1 review (4 findings)

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>

* shard(tick): 0317Z — PR #3075 round-1 review: 4 substantive findings

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>

* shard(tick): 0331Z — PR #3073 merged; substrate-hygiene cascade DURABLE 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>

* fix(check-md032): address 6 P1 Copilot findings from PR #3075 review

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>

* fix(check-md032): track matching fence delimiters (Codex P2 #3075)

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>

* docs(tick): 0101Z — PR #3075 round-2 resolved (8 threads); honor-those-that-came-before

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

* fix(check-md032): include renames in --staged scan (Codex P2 #3075)

`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>

* docs(tick): 0104Z — PR #3074 MERGED; PR #3075 round-3 sibling-absorbed

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

* fix(check-md032): round-4 review fixes (info-string closers, role-refs, exported main)

P1 — Fence closer rejects info string. Per CommonMark, an opening
fence may carry an info string (e.g., ` \`\`\`ts `), but a closing
fence cannot — it must be followed only by spaces. The earlier
`fenceInfo` returned (char, len) for both opener and closer, so an
inner same-delimiter info-string line inside an outer fence was
treated as a closer and the outer block toggled off prematurely.
List-like content that followed inside the outer block was then
falsely flagged. `fenceInfo` now returns an additional `closer:
boolean` set when the line has only whitespace after the run; the
state machine only closes on `fence.closer === true` (plus matching
char + run length >= opener). New test asserts an inner ` \`\`\`ts `
inside an outer triple-backtick fence keeps the outer block open.

P1 — Reviewer-name attribution removed from code comments + test
names. Per docs/AGENT-BEST-PRACTICES.md:671-737, current-state code
surfaces use role-refs; named attribution belongs on history
surfaces only. "Copilot P1 on PR #3075" / "Codex P2 round 2" →
"pre-CI review P1 on PR #3075 round N" / "pre-CI review P2 on PR
 #3075 round N". The substantive review trail is preserved via PR
number + round number (round 1/2/3/4 cluster the findings) without
naming the reviewer service.

P2 — Exported `main()` per the canonical hygiene-tool pattern (cf.
`tools/hygiene/check-no-conflict-markers.ts:123-168` and
`tools/hygiene/check-tick-history-order.ts:94-165`). The `if
(import.meta.main)` guard already existed; `main` is now exported
so CLI branches can be unit-tested directly without spawning a
subprocess.

Tests: 18 pass (+1 covering the info-string closer behavior).

Validation: helper run across all 14/05/14 tick shards (19 files
including the in-flight 0104Z + 0101Z + 0051Z this branch added)
returns clean.

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

* docs(tick): 0111Z — PR #3075 round-4 resolved (5 threads)

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

* docs(tick): 0112Z — round-5 duplicate (Codex info-string closer) resolved; already fixed in 579dce9

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

* docs(tick): 0113Z — real-dependency-wait on PR 3075 CI (4 required in progress)

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

* fix(check-md032): round-6 review fixes (markdownlint ignores, paren ordered-list, test coverage)

P1 — `--staged` mode now respects the `.markdownlint-cli2.jsonc`
`ignores` globs. Previously a hook wired through `--staged` would
reject memory-file or PR-preservation edits (which CI itself ignores)
locally, diverging the gate from CI. Added `loadMarkdownlintIgnores()`
and `globToRegex()`; `stagedMarkdownFiles` now filters by the ignore
list (`memory/**`, `docs/pr-preservation/**`, `docs/history/pr-reviews/**`,
the date-prefixed `docs/research/2026-*-*.md`, etc.). A missing or
unparseable config returns an empty ignore list — graceful, since
CI will surface a real misconfig on its own. `globToRegex` supports
`**` (any depth, consumes trailing `/`), `*` (single segment), `?`
(single non-slash char), and escapes regex specials.

P1 — `isListItemStart` regex extended from `\\d+\\.` to `\\d+[.)]`.
CommonMark + markdownlint treat both `1.` and `1)` as ordered-list
markers; omitting `1)` lets a `Label:` followed by `1) item` pass
locally while still firing MD032 in CI.

P2 — Test coverage for `checkFiles([...], true)` (the explicit-CLI
read-error-surfacing path). The default silent-skip branch already
had a test; the surface-errors branch did not, so a future change
that turned a missing explicit file back into a silent exit-0
would not have been caught.

P2 — Test coverage for `stagedMarkdownFiles` git-failure throw.
Pointing at a non-existent directory causes `git -C` to fail; the
helper must throw rather than silently treat as "no staged .md
files." This was the round-2 fix; the test prevents regression.

Exported `loadMarkdownlintIgnores`, `globToRegex`, and
`stagedMarkdownFiles` for testability per the canonical
hygiene-tool pattern.

Tests: 28 pass (was 18 before round-6; +10 covering the new code
paths). Includes integration test that builds a temp git repo with
staged files in both ignored (`memory/**`) and scanned (`docs/`)
subtrees and asserts only the scanned one comes through.

Validation: helper run across all 14/05/14 tick shards (22 files
now including 0104Z, 0111Z, 0112Z, 0113Z) returns clean.

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

* docs(tick): 0126Z — PR #3075 round-6 resolved (4 threads + DIRTY merge)

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

* fix(check-md032): skip YAML front matter (round-7 Codex P2)

Markdownlint treats the YAML block between leading `---` fences as
metadata, not Markdown content. List-like front-matter keys (e.g.,
`tags:` followed by `- alpha\n- beta`) inside front matter must not
fire MD032 — they're metadata structure, not list markers.

The scanner now detects a leading `---` and skips through the matching
closer `---` before starting the content walk. The `i > startLine`
guard on the list-start prev-line check means a list immediately
after the front matter is treated as start-of-content (no false
positive against the closing `---`).

Defensive: a leading `---` with no closing fence (malformed front
matter, or a thematic-break-only file) falls back to whole-file
scanning — verified by test.

Round-7 thread 2 (Codex P2 on `--staged` honoring markdownlint
ignores) is a duplicate of round-6 thread 1 (already fixed in
commit 3ebca13). Codex reviewed a stale snapshot; thread resolves
without code change.

Tests: 32 pass (was 28). New cases:
- YAML front matter with list-like keys is clean
- Front matter doesn't mask real MD032 in body
- List immediately after `---` is start-of-content
- Leading `---` without closing falls back to whole-file scan

Validation: helper run across all 14/05/14 tick shards (30 files
now including 0126Z + the sibling-merge artifacts) returns clean.

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

* docs(tick): 0130Z — PR #3075 round-7 resolved (YAML front-matter skip)

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

* fix(check-md032): round-8 review fixes (lazy continuations, 9-digit cap, fence info-string validation, spawnSync.error)

P1 — Lazy (unindented) continuations no longer split a list. CommonMark
allows a list-item paragraph to span multiple lines including unindented
continuations:

  - first line
  continued text wraps unindented
  - next item

The earlier algorithm reset `inList = false` on the unindented prose,
then flagged the second bullet as a new list with bad predecessor.
The fix is two-part:

1. The `else` branch (non-list, non-blank, non-heading, non-fence
   line) now leaves `inList` UNCHANGED. The list block ends only on a
   blank line, an ATX heading, or a fence boundary — matching the
   CommonMark spec.
2. The list-item branch only checks the predecessor when `!inList`
   (entering a new list). When `inList === true`, the current line is
   a sibling/nested item of the same list; the previous line (a
   sibling item or a lazy continuation) is not subject to MD032.

P1 — CommonMark caps ordered-list markers at 9 digits. The regex
changes from `\\d+[.)]` to `\\d{1,9}[.)]`. A `Label:` followed by
`1234567890. value` is plain prose, not a list — markdownlint does
not flag it, neither should this helper.

P1 — Fenced-code openers reject info strings containing the fence
character. Per CommonMark, a backtick fence's info string MUST NOT
contain a backtick (otherwise inline-code grammar takes over);
tilde fences likewise. `fenceInfo` now returns `null` when the
trailing run contains the fence character, so such lines are treated
as ordinary prose. New tests confirm:

- ` ```ts\`bad ` followed by a bullet IS flagged (line is prose, not fence)
- ` ~~~text~bad ` likewise
- ` ```ts ` (valid info string, no backticks) still suppresses
  list-like content inside the fence (control test)

P2 — `spawnSync` launch failures surface `r.error.message`. When the
`git` binary can't be launched (missing from PATH, permission denied),
`r.status` is null and `r.stderr` is empty — the prior error was
just "unknown error." Including `r.error.message` makes the failure
diagnosable.

Tests: 38 pass (was 32). New cases:
- lazy unindented continuation does NOT split a list
- 10+ digit numeric prefix is NOT an ordered-list marker
- 9-digit numeric prefix IS still detected (boundary)
- backtick fence with backtick in info string is NOT a valid opener
- tilde fence with tilde in info string is NOT a valid opener
- backtick fence with non-backtick info string IS still valid (control)

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

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

* docs(tick): 0137Z — PR #3075 round-8 resolved (4 CommonMark spec-grade findings)

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

* fix(check-md032): recognize list markers in blockquote context (round-9 Codex P1)

Markdownlint MD032 enforces blanks around lists even inside
blockquotes. A label like `> Label:` immediately followed by
`> - item` fails in CI but the earlier `isListItemStart` regex
only matched markers at the start of a line with optional spaces.
The helper would report `--staged` clean while CI flagged it.

Fix: extend `isListItemStart` to accept optional `>` markers (with
optional single space after each) before the list-marker character.
Handles single-level (`> - item`), nested (`> > - item`), and mixed
ordered/unordered markers.

Extend `isBlank` to treat blockquoted blank lines (`>`, `> `, `>>`)
as blank. Without this, a properly-separated blockquoted list like

  > Label:
  >
  > - item

would falsely fire because the helper saw `>` as non-blank.

Round-9 thread 2 (Codex P2 on 9-digit ordered-list cap) is a
stale-snapshot duplicate of round-8 thread 2 (already fixed in
acd8ff8).

Tests: 43 pass (+5 covering blockquote cases).

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

* docs(tick): 0143Z — PR #3075 round-9 resolved (blockquote-context list detection)

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

* fix(check-md032): round-10 review (tilde-fence info strings, fence-keeps-list-context)

P1 — Tilde fences allow tildes in the info string. CommonMark only
applies the "info string cannot contain the fence char" restriction
to BACKTICK fences (the reason is that otherwise inline-code grammar
would take over and the line wouldn't be a fence). Tilde fences have
no such restriction. `fenceInfo` now rejects fence-char in tail only
when `char === "\`"`; tilde fences accept any info string.

The round-8 test that locked in the (incorrect) tilde-with-tilde
rejection has been inverted: a `~~~text~bad` line now correctly
opens a tilde-fenced block; list-like content inside is NOT flagged.

P1 — Fenced code blocks no longer reset `inList`. A fenced code block
can be a child block inside a list item:

  - item one
    ```
    code inside the item body
    ```
  - item two

Per CommonMark these bullets are siblings of the same list and
markdownlint MD032 does NOT require a blank before `- item two`. The
earlier algorithm reset `inList = false` on fence open AND fence
close, which made the sibling bullet look like a new-list with
non-blank fence-close predecessor — a false MD032 finding that would
block valid code locally.

Trade-off: keeping `inList` across fence transitions means a
top-level fence between two lists (`- a\n- b\n\`\`\`code\`\`\`\n- c`)
won't fire MD032 on `- c` locally even though CI might. False
positives that block valid code are operationally worse than false
negatives that defer to CI; the conservative direction is to keep
the list context.

Tests: 45 pass (was 43). Net: -0 +2 (one test inverted: tilde-with-
tilde is now clean; two new tests: indented fence inside list,
flush-left fence inside list — both clean).

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

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

* docs(tick): 0148Z — PR #3075 round-10 resolved (tilde-fence relaxation + fence-keeps-list-context)

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

* fix(check-md032): replace in-test require() with module-scope import (round-11 P1)

The integration test that builds a temp git repo (round 6) used
`require("node:child_process")` inside the test body. The repo's
ESLint profile enforces `@typescript-eslint/no-require-imports`,
and existing `require` call sites carry explicit suppressions —
this one did not. `bun run lint:typescript` would flag the line.

Fix: hoist `spawnSync` to the existing module-scope `import` block
(other TS tests do this) and call it directly inside the test.
Removed the in-body `require` + the `as typeof import(...)` cast
and the local `spawn` alias.

Round-11 threads 2 (P2 PR description test count), 3 (P1 tilde-fence
info-string allows tilde), and 4 (P1 test fixture invert) are all
stale-snapshot duplicates of round-10 findings already addressed in
commit a86a524 + the round-10 PR-body edit. Resolved without code
change.

Tests: still 45 pass.

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

* docs(tick): 0152Z — PR #3075 round-11 resolved (1 real require fix + 3 stale-snapshot duplicates)

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

* docs(tick): 0154Z — PR #3075 wait-ci after 11 rounds; threads clear; 45/45 tests

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

* fix(tick/0143Z): remove `> ` code span (MD038 — spaces inside code span elements)

The 0143Z tick shard had `\`> \`` (a code span containing `>` then a
space) which markdownlint MD038 flags as "Spaces inside code span
elements." Rewording in prose form preserves the meaning without the
code-span hit.

This is the next markdown-lint class out of scope for B-0456's MD032
helper but worth a follow-up row (alongside the MD018 + MD056 cases
already captured at PR-body level).

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

* docs(tick): 0158Z — PR #3075 MD038 markdownlint fix in 0143Z shard

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

* fix(check-md032): round-12 (heading terminates list + blockquoted fence + flush-left fence split)

P1 — Headings terminate the prior list. `- item a\n## Heading\n- item b`
should fire MD032 on `- item b` because the heading splits the lists.
The earlier algorithm left `inList` unchanged on non-blank, non-fence,
non-list lines (including headings), so the second bullet was treated
as a sibling of the first list (false negative). Added an explicit
`isHeading(line)` check that resets `inList = false` so the next
list-start fires correctly.

P1 — Fenced code blocks inside a blockquote are now recognised.
`fenceInfo` regex extended to accept optional leading `>` markers
(`> \`\`\`` / `> ~~~`). Without this, a blockquoted code sample
containing `> Label:` / `> - item` would fire MD032 even though
markdownlint treats it as code.

P1 — Flush-left fence between bullets DOES split the list (refines
round 10 over-relaxation). Round 10 kept `inList` across all fence
transitions to avoid false-positives on indented-fence-inside-list.
But that over-corrected: a flush-left fence is a top-level block
that terminates the surrounding list per CommonMark. The fix
distinguishes by indent: fences with 1+ leading whitespace OR a
blockquote prefix are child blocks (keep `inList`); fences with
zero leading whitespace are top-level (reset `inList`).

The round-10 test that asserted "flush-left fence between bullets
is clean" was a locked-in false negative; inverted to assert MD032
fires on the second bullet at line 7.

Tests: 49 pass (was 45). New cases:
- ATX heading between two lists terminates the first
- Heading immediately after list, then list, fires on second list
- Fenced code inside blockquote is recognised (no MD032 inside)
- Indented fence inside list-item still keeps list context (preserved)
- Flush-left fence between bullets fires MD032 on second list (inverted)

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

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

* docs(tick): 0202Z — PR #3075 round-12 resolved (heading-terminates + blockquote-fence + flush-left split)

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

* fix(tick/0202Z): remove backtick-rich regex from code span (MD038)

The 0202Z tick shard described the round-12 regex extension with
an inline code span containing backticks (`...\`{3,}\|~{3,}...`).
Markdown's code-span parser doesn't interpret `\\\`` as an escape —
each `\`` opens or closes a span, so the parser saw mismatched
span boundaries and markdownlint flagged the resulting fragment
"; a " as Spaces-Inside-Code-Span (MD038).

Rewording the change description in prose avoids the embedded-
backtick issue while preserving the meaning.

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

* fix(check-md032): --staged reads index blob, not working tree (round-13 P1)

If a user stages a Markdown file (`git add file.md`) and then edits
it again before pushing, `--staged` previously read the working-tree
content, NOT the staged blob. That meant the gate could pass while
the index (what `git commit` records, what CI lints) was actually
violating — or vice versa, gate fail while index was clean.

Fix: new `readStagedBlob(repoRoot, repoRelPath)` reads via
`git show :path` (the `:path` refspec returns the index version).
New `checkStagedFiles(repoRoot)` composes `stagedMarkdownFiles`
(path discovery + ignore-glob filtering) with `readStagedBlob`
(content from the index).

`main()` --staged branch now calls `checkStagedFiles` instead of
`checkFiles`. Explicit-CLI mode still reads filesystem (the user
passed a path, they mean that path).

Both helpers throw on git failure for diagnosability + the
`--staged` mode silently skips unreadable entries per round-2
discipline (push/CI surfaces the I/O error).

Tests: 52 pass (+3). New cases all build a temp git repo:
- readStagedBlob returns staged content, not working-tree content
- checkStagedFiles reports clean when staged is clean even though
  working tree is violating (divergence test)
- checkStagedFiles flags when staged IS violating even though
  working tree is clean (control)

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

* docs(tick): 0211Z — PR #3075 round-13 resolved (staged-blob reads)

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

* docs(tick): 0214Z — DIRTY-gate merge resolved (0154Z add/add); 2nd of session

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

* fix(check-md032): round-14 (permissive blockquote spacing, fence-blockquote-terminates-list, fail-loud staged blob, test suppressions)

P1 — Blockquote prefix now permissive on spacing. CommonMark allows
multiple spaces after `>` (`>   - item` is a valid blockquoted list).
The earlier `>\s?` regex only allowed 0-1 space and missed these.
Changed `(>\s?)*` → `(>[ \t]*)*` in `isListItemStart`, `isBlank`,
and the heading regex.

P1 — Blockquoted blank lines may have arbitrary spaces after `>`.
`>   ` (multiple trailing spaces) IS a blockquoted blank; treating
it as non-blank could false-flag a properly separated blockquoted
list. Same regex fix.

P1 — Flush-left blockquoted fence terminates the surrounding list.
Round-12 used `^[ \t>]` to distinguish "indented or blockquoted"
fences from flush-left, keeping `inList` on both. That was over-
permissive: per CommonMark, a column-0 blockquote opens a NEW
top-level block, terminating an enclosing top-level list. Changed
the heuristic to `^[ \t]` only — blockquote prefix no longer counts
as "indented child block." New test asserts `- item / > \`\`\`code
\`\`\` / - new-item` flags MD032 on `- new-item`.

P1 — `checkStagedFiles` fails loud on `readStagedBlob` failure.
A `git show :path` failure AFTER `git diff --cached` listed the
path is a genuine git/index anomaly — silent-skip would let the
local gate diverge from CI. The round-2 silent-skip discipline
was about working-tree filesystem reads; staged-blob reads are a
different I/O class. Removed the try/catch in `checkStagedFiles`;
the throw propagates to `main()` which exits 1.

P1 — Test file `spawnSync("git", ...)` calls now go through a
single suppressed `git(args, cwd)` helper at module scope. The
helper carries the same `sonarjs/no-os-command-from-path`
suppression rationale as production call sites. Replaces the
per-call `spawnSync` with `git(...)` in all four test helpers.

P2 — Round-14 thread 6 (PR description test count 45) is stale-
snapshot. Already at 52 after round-13; will be 56 after this
commit. PR body refresh deferred to a follow-up edit.

Tests: 56 pass (was 52). New cases:
- blockquote marker with multiple spaces before list marker
- blockquoted blank line with multiple trailing spaces is blank
- flush-left blockquoted fence between bullets terminates list
- checkStagedFiles throws on staged-blob read failure

Validation: helper run across all 14/05/14 tick shards (44 files
including 0211Z + 0214Z) returns clean.

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

* docs(tick): 0222Z — PR #3075 round-14 resolved (5 P1 + 1 stale-snapshot)

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

* fix(check-md032): thematic break + blockquote terminate top-level list (round-15 Codex P1)

CommonMark: thematic breaks (`---`, `***`, `___`) and blockquote-
starts terminate the surrounding list block. The earlier algorithm
kept `inList` true on these as lazy paragraph continuation, so
`- a / --- / - b` and `- a / > quote / - b` passed locally while
markdownlint MD032 fires on the second bullet in CI.

Two fixes:

1. `isThematicBreak(line)` — matches `^ {0,3}(\\*[ \\t]*){3,}$` etc.
   Resets `inList = false` so the subsequent list-start fires MD032.

2. `inList` lifted from boolean to `false | "top" | "block"` to track
   whether the current list is top-level or blockquoted. A blockquote
   line (`> ...`) terminates a "top" list (it opens a new block type)
   but is a CONTINUATION of a "block" list (the `>` prefix is the
   list's own context). New `isBlockquoteLine(line)` helper does the
   detection in the else branch.

The list classification ("top" vs "block") is determined at list
entry by whether the list-item line has a `>` prefix:

  - first         → inList = "top"
  > - first       → inList = "block"

The "block" classification means subsequent `>` lines are
continuations (no terminate). The "top" classification means
subsequent `>` lines terminate (blockquote opens a new block).

Tests: 61 pass (was 56). New cases:
- thematic break (`---`) between two lists terminates the first
- thematic break (`***`) symmetric
- blockquote line after top-level list terminates it
- blockquote continuation in blockquoted list is preserved (control)
- `---` after a list-item line is still a thematic break (regression guard)

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

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

* docs(tick): 0227Z — PR #3075 round-15 resolved (thematic break + blockquote terminate)

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

* fix(tick/0222Z): remove `>   ` code span (MD038 — third hit this PR)

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>

* fix(check-md032): round-16 (after-list MD032 + blockquote indent cap + 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>

* docs(tick): 0239Z — PR #3075 round-16 resolved (after-list MD032 + indent cap)

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

* fix(check-md032): blockquoted fence accepts content-indent (round-17 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>

* fix(check-md032): round-18 (blockquoted thematic break, bare markers, 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>

* fix(check-md032): skip HTML comment regions (round-19 Codex P2)

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>

---------

Co-authored-by: Claude <noreply@anthropic.com>
AceHack added a commit that referenced this pull request May 14, 2026
MD018: escape #3056-#3073 PR refs to avoid false ATX-heading parse.
MD032: add blank line before list after 'Actions:' paragraph.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AceHack added a commit that referenced this pull request May 14, 2026
* chore(backlog): close B-0451 — duplicate row-ID sweep complete, audit exits 0

All 12 collision groups were resolved across PRs #3056#3073.
Verified: bun tools/bg/audit-duplicate-row-ids.ts exits 0 on main
("561 rows with id field, no duplicate IDs"). The row status was never
updated to closed after the sweep completed.

CI-wiring AC remains a documented future-work item (separate slice).

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

* fix(hygiene): correct markdownlint MD018/MD032 in 0521Z tick shard

MD018: escape #3056-#3073 PR refs to avoid false ATX-heading parse.
MD032: add blank line before list after 'Actions:' paragraph.

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AceHack added a commit that referenced this pull request May 14, 2026
* chore(backlog): close B-0451 — duplicate row-ID sweep complete, audit exits 0

All 12 collision groups were resolved across PRs #3056#3073.
Verified: bun tools/bg/audit-duplicate-row-ids.ts exits 0 on main
("561 rows with id field, no duplicate IDs"). The row status was never
updated to closed after the sweep completed.

CI-wiring AC remains a documented future-work item (separate slice).

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

* fix(hygiene): correct markdownlint MD018/MD032 in 0521Z tick shard

MD018: escape #3056-#3073 PR refs to avoid false ATX-heading parse.
MD032: add blank line before list after 'Actions:' paragraph.

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

* docs: lior shadow lesson log for PR 3074

* fix(docs): replace escaped \n sequences with real newlines in shadow lesson log

The file was committed with literal backslash-n sequences instead of
actual newline characters, causing it to render as a single line in
all Markdown renderers. Replace with real newlines so headings and
structure are preserved correctly.

Resolves threads from copilot-pull-request-reviewer and
chatgpt-codex-connector on PR #3102.

Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AceHack added a commit that referenced this pull request May 14, 2026
* chore(backlog): close B-0451 — duplicate row-ID sweep complete, audit exits 0

All 12 collision groups were resolved across PRs #3056#3073.
Verified: bun tools/bg/audit-duplicate-row-ids.ts exits 0 on main
("561 rows with id field, no duplicate IDs"). The row status was never
updated to closed after the sweep completed.

CI-wiring AC remains a documented future-work item (separate slice).

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

* fix(hygiene): correct markdownlint MD018/MD032 in 0521Z tick shard

MD018: escape #3056-#3073 PR refs to avoid false ATX-heading parse.
MD032: add blank line before list after 'Actions:' paragraph.

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

* docs: preserve PR discussions 3095-3099

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AceHack added a commit that referenced this pull request May 14, 2026
* chore(backlog): close B-0451 — duplicate row-ID sweep complete, audit exits 0

All 12 collision groups were resolved across PRs #3056#3073.
Verified: bun tools/bg/audit-duplicate-row-ids.ts exits 0 on main
("561 rows with id field, no duplicate IDs"). The row status was never
updated to closed after the sweep completed.

CI-wiring AC remains a documented future-work item (separate slice).

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

* fix(hygiene): correct markdownlint MD018/MD032 in 0521Z tick shard

MD018: escape #3056-#3073 PR refs to avoid false ATX-heading parse.
MD032: add blank line before list after 'Actions:' paragraph.

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

* docs(lior): antigravity check report and PR preservation for 3074, 3075, 2762

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AceHack added a commit that referenced this pull request May 14, 2026
…3116)

* chore(backlog): close B-0451 — duplicate row-ID sweep complete, audit exits 0

All 12 collision groups were resolved across PRs #3056#3073.
Verified: bun tools/bg/audit-duplicate-row-ids.ts exits 0 on main
("561 rows with id field, no duplicate IDs"). The row status was never
updated to closed after the sweep completed.

CI-wiring AC remains a documented future-work item (separate slice).

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

* fix(hygiene): correct markdownlint MD018/MD032 in 0521Z tick shard

MD018: escape #3056-#3073 PR refs to avoid false ATX-heading parse.
MD032: add blank line before list after 'Actions:' paragraph.

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

* docs(lior): antigravity check - shadow log for Vera and Riven drift

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AceHack added a commit that referenced this pull request May 14, 2026
… exits 0

All 12 collision groups were resolved across PRs #3056#3073.
Verified: bun tools/bg/audit-duplicate-row-ids.ts exits 0 on main
("561 rows with id field, no duplicate IDs"). The row status was never
updated to closed after the sweep completed.

CI-wiring AC remains a documented future-work item (separate slice).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AceHack added a commit that referenced this pull request May 14, 2026
MD018: escape #3056-#3073 PR refs to avoid false ATX-heading parse.
MD032: add blank line before list after 'Actions:' paragraph.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AceHack added a commit that referenced this pull request May 14, 2026
* chore(backlog): close B-0451 — duplicate row-ID sweep complete, audit exits 0

All 12 collision groups were resolved across PRs #3056#3073.
Verified: bun tools/bg/audit-duplicate-row-ids.ts exits 0 on main
("561 rows with id field, no duplicate IDs"). The row status was never
updated to closed after the sweep completed.

CI-wiring AC remains a documented future-work item (separate slice).

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

* fix(hygiene): correct markdownlint MD018/MD032 in 0521Z tick shard

MD018: escape #3056-#3073 PR refs to avoid false ATX-heading parse.
MD032: add blank line before list after 'Actions:' paragraph.

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

* docs: preserve PR discussions for 3112 and 3113

* fix(b-0451): move CI-wiring criterion out of AC checklist into future work

The unchecked AC item was already noted as "does not block closure"
and "separate slice / follow-up row". Moves it out of the checklist
to match the already-documented Future work section, resolving Copilot
reviewer thread on PR #3115.

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.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