Skip to content

chore(infra): tracking-file guard, LWDiD data canary, /push-pr-update range scan + changelog compiler hardening - #808

Open
igerber wants to merge 9 commits into
mainfrom
chore/infra-quick-sweep-2
Open

chore(infra): tracking-file guard, LWDiD data canary, /push-pr-update range scan + changelog compiler hardening#808
igerber wants to merge 9 commits into
mainfrom
chore/infra-quick-sweep-2

Conversation

@igerber

@igerber igerber commented Aug 30, 2026

Copy link
Copy Markdown
Owner

Summary

  • Tracking-file contract guard (tests/test_tracking_files.py): CI-enforces the TODO.md/DEFERRED.md split — no TODO table row points work at DEFERRED.md, rows cross-linking an M-xxx ledger id do not restate lifecycle state (best-effort tripwire, calibrated against every legitimate current row), and the three documented table shapes (TODO Actionable, DEFERRED blocker, DEFERRED Decision record) are enforced with per-row column counts. The parser honors escaped \|, unspaced rows, and GFM no-leading-pipe tables, and fails loudly on malformed (non-rendering) tables. Wired into docs-tests.yml (path filters + dedicated step), excluded from rust-test.yml triggers; CLAUDE.md's Tracking-file map gains the Decision-record shape it enforces.
  • LWDiD real-data canary (.github/workflows/lwdid-data-canary.yml): weekly cron + workflow_dispatch lane that fails loudly when load_prop99/load_walmart fall back to synthetic data (attrs["source"] != "lwdid_ssc_ancillary"), then runs tests/test_methodology_lwdid.py. Today a network/URL/sha regression at the SSC mirror only produces visible-but-green skips. Test docstrings now name the lane instead of the deleted TODO row.
  • /push-pr-update §3b range scan: restores the committed-range pattern scan via premerge_scan.py --range using the command's own COMPARISON_REF resolver; findings stay informational, scan-integrity failures (exit 3/4) are stop-and-report. /pre-merge-check splits premerge_scan and changelog_compile.py check into separate Bash calls so the first command's exit status is not masked.
  • Changelog fragments compiler hardening (follow-up defect fixes to feat(tooling): changelog fragments - conflict-free parallel PRs #806): malformed/duplicate release headers and duplicate link definitions refused on both fresh and exit-4 paths; exit-4 predecessor derived only from a canonical compare//releases/tag/ link; --previous-version cross-check (/bump-version passes it) preserving interrupted-bump recovery; prev-anchor date validation with a documented legacy exemption for historical dateless headers; existing-target sections validated with the full fragment grammar on the exit-4 path; indented ATX headings rejected (CommonMark accepts 1-3 leading spaces, so ## [9.9.9] previously compiled into the changelog as a real H2 invisible to the column-zero header scanners); dirty guard catches deleted-but-committed fragments; compile rolls back changelog + fragment deletions on a mid-flight unlink failure; strict kebab fragment-name grammar.
  • Deletes the three closed TODO.md rows; release note in changelog.d/20260830-infra-process-sweep.md (### Internal).

Methodology references (required if estimator / math changes)

  • Method name(s): N/A — no methodology changes (the LWDiD canary exercises existing replication tests; provenance contract per REGISTRY.md's LWDiD validation notes)
  • Paper / source link(s): N/A
  • Any intentional deviations from the source (and why): None

Validation

  • Tests added/updated: tests/test_tracking_files.py (new, 44 tests), tests/test_changelog_fragments.py (+~25 regressions), tests/test_methodology_lwdid.py (docstring pointers only)
  • 133 tests pass across the two guard suites; changelog_compile.py check: OK on the live tree; canary workflow YAML validated; TODO.md/DEFERRED.md verified well-formed under the stricter parser before enforcement
  • Backtest / simulation / notebook evidence (if applicable): N/A

Security / privacy

  • Confirm no secrets/PII in this PR: Yes

…push-pr-update range scan

Closes three TODO.md infra rows:

- tests/test_tracking_files.py: CI guard for the TODO.md/DEFERRED.md
  split contract - no TODO table row points work at DEFERRED.md, rows
  cross-linking an M-xxx ledger id do not restate lifecycle state, and
  the three documented table shapes (TODO Actionable, DEFERRED blocker,
  DEFERRED Decision record) are enforced with per-row column counts.
  The parser handles escaped pipes, unspaced rows, and GFM
  no-leading-pipe tables, and fails loudly on malformed (non-rendering)
  tables. Wired into docs-tests.yml (path filters + step); excluded from
  rust-test.yml triggers. CLAUDE.md documents the Decision-record shape.

- .github/workflows/lwdid-data-canary.yml: weekly cron + dispatch lane
  that fails loudly if load_prop99/load_walmart fall back to synthetic
  data (source != 'lwdid_ssc_ancillary'), then runs the LWDiD
  replication tests the canary de-gates. Test docstrings now point at
  the lane instead of the deleted TODO row.

- .claude/commands/push-pr-update.md section 3b: restored the
  committed-range pattern scan via premerge_scan.py --range with the
  command's own COMPARISON_REF resolver; findings informational,
  scan-integrity failures (exit 3/4) stop-and-report. pre-merge-check.md
  splits premerge_scan and changelog_compile check into separate Bash
  calls so the first exit status is not masked.

Also hardens the freshly merged changelog fragments compiler (#806),
found during local review of this branch:

- Malformed/duplicate release headers and link definitions refused on
  both fresh and exit-4 paths; exit-4 predecessor derived only from a
  canonical compare/tag link; --previous-version cross-check
  (bump-version.md now passes it) with interrupted-bump recovery kept;
  prev-anchor date validation with a documented legacy exemption.
- Existing-target sections are validated with the full fragment grammar
  on the exit-4 path (orphan bullets / unknown-category bullets refuse).
- Indented ATX headings (CommonMark accepts 1-3 leading spaces) are
  rejected by the fragment grammar - previously ' ## [9.9.9]' compiled
  into the changelog as a real H2 invisible to the header scanners.
- Dirty guard catches deleted-but-committed fragments; compile rolls
  back changelog + fragments on a mid-flight unlink failure; strict
  kebab fragment-name grammar.

Release note in changelog.d/20260830-infra-process-sweep.md (Internal).
@github-actions

Copy link
Copy Markdown

PR Review

Overall assessment

⚠️ Needs changes — two unmitigated P1 correctness gaps remain in the changelog compiler.

Executive summary

  • No estimator, weighting, variance, SE, or identification behavior changes.
  • The LWDiD canary matches the Registry’s documented SSC ancillary-data provenance.
  • CommonMark-indented release headings and link definitions bypass the compiler’s global duplicate checks.
  • The deleted-fragment guard fails open if its new HEAD enumeration command fails.
  • A valid changelog.d/ fragment accompanies the behavior changes.

Methodology

No findings. The affected method is LWDiD validation only. docs/methodology/REGISTRY.md:L2653-L2769 confirms that lw_smoking.dta and lw_walmart.dta are the replication sources. The PR changes only canary coverage and test docstrings, not methodology.

Code Quality

1. Indented changelog constructs bypass duplicate protection

  • Severity: P1
  • Location: .claude/scripts/changelog_compile.py:L299-L333
  • Impact: CommonMark recognizes ATX headings and reference definitions indented by up to three spaces, but these regexes require column zero. An indented ## [1.3.0] ... or [1.3.0]: ... remains invisible, allowing compile to report success, delete fragments, and leave duplicate rendered releases or ambiguous links.
  • Concrete fix: Reject or normalize 1–3 leading spaces in the global release-header and link-definition scans. Add fresh-compile and already-compiled regression tests for indented duplicate headings and links.

2. Deleted-fragment enumeration fails open

  • Severity: P1
  • Location: .claude/scripts/changelog_compile.py:L556-L573
  • Impact: When git ls-tree -r returns nonzero, the compiler silently skips the set-equality check and can compile an incomplete release despite a deleted committed fragment.
  • Concrete fix: Treat a nonzero return as a dirty-guard problem and return before writing. Add a test that forces this specific command to fail and asserts the changelog and fragments remain untouched.

3. Escaped-pipe parsing mishandles even backslash runs

  • Severity: P2
  • Location: tests/test_tracking_files.py:L84-L94
  • Impact: (?<!\\)\| treats every pipe immediately preceded by a backslash as escaped. In Markdown, a pipe after an even number of backslashes is unescaped, so a malformed row can evade the column-count guard.
  • Concrete fix: Count consecutive preceding backslashes and split when the count is even. Test one-, two-, and three-backslash cases.

Performance

No findings.

Maintainability

No additional findings.

Tech Debt

No findings. The removed TODO rows correspond to implemented work, and no new untracked deferral was introduced.

Security

No findings. No secrets were observed, and workflow actions remain SHA-pinned.

Documentation / Tests

Stale TODO reference

  • Severity: P3
  • Location: .github/workflows/lwdid-data-canary.yml:L1-L2
  • Impact: The comment points to a TODO row deleted by this PR.
  • Concrete fix: Remove the parenthetical or label it as the former TODO item.

Test coverage is otherwise substantial. changelog_compile.py check passed; pytest was unavailable in the reviewer environment.

Path to Approval

  1. Make the changelog-wide heading/link checks handle CommonMark indentation and add the two regression shapes above.
  2. Make failed HEAD fragment enumeration fail closed and test that failure path.

…, backslash parity

- changelog_compile.py: refuse CommonMark-indented (1-3 space) ATX
  headings and version-link definitions anywhere in CHANGELOG.md - the
  duplicate/date scans are column-zero anchored, so ' ## [1.3.0]' or
  ' [1.3.0]: ...' rendered as real constructs while staying invisible to
  every safeguard (reproduced: compile succeeded leaving two rendered
  1.3.0 headings + two link definitions). Regressions on both the fresh
  and exit-4 paths.
- changelog_compile.py: the deleted-fragment guard now fails CLOSED when
  'git ls-tree -r HEAD' fails - previously a nonzero return silently
  skipped the set-equality check and could compile an incomplete
  release. Regression forces the enumeration to fail and asserts nothing
  is written or consumed.
- test_tracking_files.py: pipe escaping is parity-based ('\|' literal,
  '\\|' backslash + separator); the fixed-width (?<!\\) lookbehind got
  even backslash runs wrong. Replaced with a backslash-run-counting
  splitter (also fixes the same parity hole in the trailing-pipe trim);
  parity tests for 1-4 backslash runs and trailing forms.
- lwdid-data-canary.yml: reword header comment to name the former
  (now-deleted) TODO row.
@github-actions

Copy link
Copy Markdown

🔁 AI review rerun (requested by @igerber)

Head SHA: 79026febbc421a99e2cff9230e89d3ef14666011


PR Review

Overall assessment

⚠️ Needs changes — two newly identified P1 workflow/compiler defects remain.

Executive summary

  • No estimator, weighting, variance, SE, or identification behavior changes.
  • LWDiD canary provenance matches the Methodology Registry.
  • Both previous P1 findings are resolved.
  • Noncanonical but valid CommonMark release headings can still bypass duplicate detection.
  • /bump-version passes an undefined shell variable.
  • The required changelog fragment is present.

Methodology

No findings. The affected surface is LWDiD validation only. docs/methodology/REGISTRY.md:L2764-L2786 confirms the SSC lw_smoking.dta and lw_walmart.dta sources and corresponding replication targets.

Code Quality

1. [Newly identified] CommonMark heading whitespace still bypasses release scans

  • Severity: P1
  • Location: .claude/scripts/changelog_compile.py:L299-L352
  • Impact: CommonMark accepts tabs or multiple spaces after the opening hashes. Consequently, ## [1.3.0] and ## [1.3.0] render as H2 headings but evade _existing_headers(), the malformed-header scan, and the new indentation check. A fresh compile can add another 1.3.0 section and delete its fragments while leaving duplicate rendered releases.
  • Concrete fix: Detect release-like headings using ^##[ \t]+\[ and reject every match not at a canonical _existing_headers() start. Add fresh-compile and exit-4 tests for tab and multiple-space separators.

2. Divider validation accepts non-rendering tables

  • Severity: P2
  • Location: tests/test_tracking_files.py:L129-L186
  • Impact: _is_divider() only checks the character set and presence of one hyphen. A row such as |---||---| passes when its cell count matches, although an empty delimiter cell prevents GFM table rendering.
  • Concrete fix: Validate every divider cell against the GFM delimiter grammar, such as ^:?-+:?$, and test empty and colon-only cells.

Performance

No findings.

Maintainability

No additional findings.

Tech Debt

No findings. Removed TODO rows correspond to implemented work.

Security

No findings. No secrets were observed; workflow actions remain SHA-pinned.

Documentation / Tests

3. [Newly identified] /bump-version uses an undefined shell variable

  • Severity: P1
  • Location: .claude/commands/bump-version.md:L37-L47
  • Impact: Step 2 only conceptually “stores” OLD_VERSION; it does not define a shell variable. Executing the fenced command in a separate shell expands "$OLD_VERSION" to empty, causing every normal release compile to fail its new cross-check.
  • Concrete fix: Resolve OLD_VERSION in the same Bash block before invoking the compiler, or substitute the literal extracted version rather than using shell expansion.

The focused pytest suites could not run because pytest is unavailable in the reviewer environment. The live compiler check passed.

Path to Approval

  1. Reject tab/multiple-space CommonMark release headings and add fresh/exit-4 regression tests.
  2. Define OLD_VERSION within the /bump-version command’s Bash invocation or pass its literal value.

…r, bump-version variable

- changelog_compile.py: the malformed-header scan now accepts ANY
  CommonMark heading spacing and depth ('##  [x]', a tab after the
  hashes, '### [x]') instead of the single-space '## [' shape, so a
  rendering-but-noncanonical release-like heading can no longer hide
  from the duplicate/date safeguards; exact-canonical release and
  Unreleased headers are the only exemptions. Regressions for
  double-space, tab, and H3 shapes on both fresh and exit-4 paths
  (real CHANGELOG audited: all 85 bracketed headings canonical).
- test_tracking_files.py: _is_divider now enforces the GFM delimiter
  grammar per cell (':?-+:?') - an empty or colon-only delimiter cell
  stops the whole table from rendering, so such a row no longer counts
  as a divider and the well-formedness check raises. Tests for empty,
  colon-only, and space delimiter cells.
- bump-version.md: OLD_VERSION is resolved inside the SAME Bash block
  that passes --previous-version (variables do not persist across Bash
  calls; the prose-only 'store as OLD_VERSION' step would have expanded
  empty and failed every normal release compile).
@github-actions

Copy link
Copy Markdown

🔁 AI review rerun (requested by @igerber)

Head SHA: 3cd3845aced36173f3566534bc422ed60a3322ad


PR Review

Overall assessment

⚠️ Needs changes — one newly identified P1 changelog-compiler defect remains.

Executive summary

  • Both previous P1 findings are resolved.
  • No estimator, weighting, variance, SE, or identification logic changed.
  • LWDiD canary provenance matches the Methodology Registry.
  • A valid CommonMark link-definition form can still bypass duplicate detection.
  • The required changelog fragment is present.

Methodology

No findings. docs/methodology/REGISTRY.md:L2762-L2786 confirms the SSC ancillary sources and Prop 99/Walmart replication targets used by the canary.

Code Quality

1. [Newly identified] No-whitespace link definitions bypass duplicate checks

  • Severity: P1
  • Location: .claude/scripts/changelog_compile.py:L354-L357, L640-L655
  • Impact: CommonMark permits [1.3.0]:https://… without whitespace after the colon. The \s+ scan misses that definition, so compilation can add a duplicate, delete the fragments, and leave an earlier incorrect definition controlling the rendered release link. The exit-4 path can similarly certify one hidden incorrect definition plus one canonical definition.
  • Concrete fix: Detect version labels independently of destination spacing, for example ^\[(\d+\.\d+\.\d+)\]:, and reject duplicate/pre-existing target labels. Add fresh-compile and exit-4 regressions using ]:https://….

Performance

No findings.

Maintainability

No findings.

Tech Debt

No findings. The removed TODO rows correspond to implemented work.

Security

No findings. Workflow permissions are read-only and actions remain SHA-pinned.

Documentation / Tests

No findings. The release fragment follows the project convention. changelog_compile.py check, syntax parsing, and git diff --check passed; pytest was unavailable in the reviewer environment.

Path to Approval

  1. Reject no-whitespace target/duplicate link definitions and add fresh-compile plus exit-4 regression tests.

changelog_compile.py: CommonMark permits '[1.3.0]:https://...' with no
whitespace after the colon (and resolves a label to its FIRST
definition), so the '\s+<dest>' link-definition scan missed such a
definition - a fresh compile could append the canonical definition while
the hidden earlier one kept controlling the rendered link, and the
exit-4 path could certify the pair. The duplicate/already-defined-target
scan now matches on the label alone ('^[X.Y.Z]:'). The predecessor-link
lookups keep their canonical '\]:\s+' form deliberately: a no-space
predecessor definition refuses as not-found (fail closed) since compiler
output always writes the space. Regressions: hidden no-space target
definition blocks the fresh compile (nothing written or consumed);
no-space duplicate blocks exit-4.
@github-actions

Copy link
Copy Markdown

🔁 AI review rerun (requested by @igerber)

Head SHA: c8c1a9e82a6cc7980514f7205daa92027c3933c1


PR Review

Overall assessment

⚠️ Needs changes — one newly identified P1 remains in the changelog compiler.

Executive summary

  • The previous no-whitespace link-definition reproducer is fixed.
  • No estimator, weighting, variance, SE, or identification behavior changed.
  • LWDiD canary provenance agrees with the Methodology Registry.
  • CommonMark definitions inside list items or whitespace-normalized labels still bypass duplicate detection.
  • Tracking contracts, YAML syntax, AST parsing, and changelog_compile.py check pass; pytest was unavailable.

Methodology

No findings. The canary uses the SSC ancillary sources and Prop 99/Walmart targets documented in docs/methodology/REGISTRY.md:L2762-L2786.

Code Quality

1. [Newly identified] CommonMark container definitions bypass link validation

  • Severity: P1
  • Location: .claude/scripts/changelog_compile.py:L354-L371, L646-L656
  • Impact: The scanner only recognizes column-zero [\d…]: lines. CommonMark also registers whitespace-normalized labels and definitions inside list items. For example, a valid fragment containing - [1.3.0]:https://evil.example/wrong passes _parse_fragment(), is omitted from link_versions, and precedes the generated canonical definition. Compilation succeeds and deletes the fragments, but the wrong first definition controls the rendered release-header link.
  • Concrete fix: Normalize/detect CommonMark reference labels across container forms, or conservatively reject version-like definitions outside the canonical link block. Add fresh-compile and exit-4 regressions for [ 1.3.0 ]:…, - [1.3.0]:…, and nested-list definitions.

Performance

No findings.

Maintainability

No findings.

Tech Debt

No findings. The deleted TODO rows correspond to implemented work.

Security

No separate findings. Actions are SHA-pinned and workflow permissions are read-only.

Documentation / Tests

No separate findings. The required changelog.d/ fragment is present.

Path to Approval

  1. Reject all CommonMark-equivalent duplicate/target version-link definitions and add the regressions listed above.

Third same-class CommonMark-smuggling finding (indented defs, no-space
colons, now container/whitespace-normalized labels) - stop chasing
shapes and enforce the invariant directly: a version-label-plus-colon
('[X.Y.Z]:', any label spacing, any container) may exist ONLY as a
canonical column-zero definition, because CommonMark registers
definitions inside list items/blockquotes, whitespace-normalizes labels
('[ 1.3.0 ]' resolves as '1.3.0'), and resolves each label to its FIRST
definition - any smuggled form outranks what the compiler writes.

_noncanonical_version_link() enforces this in three places:
- the pre-branch CHANGELOG scan (covers fresh and exit-4 paths),
- the fragment grammar (a '- [1.3.0]:url' bullet refuses at check time),
- a backstop on the fully assembled text before any write (multi-line
  labels can span the line-based fragment check).

Real CHANGELOG and changelog.d audited: no non-canonical shapes exist.
Regressions: whitespace-normalized label, list-item and nested-list
definitions on both compile paths; three fragment-body forms.
@github-actions

Copy link
Copy Markdown

🔁 AI review rerun (requested by @igerber)

Head SHA: 9ced8f415223e87283fde386ac54d5a1700160ed


PR Review

Overall assessment

⚠️ Needs changes — one newly identified P1 remains in the changelog compiler.

Executive summary

  • The previous CommonMark list-item/whitespace-label finding is resolved.
  • No estimator, inference, weighting, or identification behavior changed.
  • LWDiD canary provenance matches the Methodology Registry.
  • Canonical-looking link lines inside fenced/HTML blocks can still be mistaken for real definitions.
  • AST/YAML validation and changelog_compile.py check pass; pytest is unavailable.

Methodology

No findings. The canary checks the Prop 99 and Walmart sources documented in docs/methodology/REGISTRY.md:L2762-L2786.

Code Quality

1. [Newly identified] Markdown code blocks can masquerade as canonical link definitions

  • Severity: P1
  • Location: .claude/scripts/changelog_compile.py:L258-L274, L409-L418, L520-L551, L706-L721
  • Impact: A column-zero [1.2.0]: URL inside a fenced code or raw-HTML block is accepted as canonical because validation ignores Markdown context. The fresh path then inserts the new definition beside it inside that block, writes the release section, and deletes fragments, although neither definition renders. The exit-4 path can likewise certify an unresolved release-header link.
  • Concrete fix: Restrict definitions and anchor searches to a validated terminal, contiguous comparison-link block. Add fresh and exit-4 regressions with predecessor/target definitions inside fenced-code and HTML blocks.

Performance

No findings.

Maintainability

No findings.

Tech Debt

No findings. The removed TODO rows correspond to implemented work.

Security

No findings. Workflow permissions are read-only and actions are SHA-pinned.

Documentation / Tests

No separate findings. The required changelog.d/ fragment is present.

Path to Approval

  1. Validate that all comparison definitions belong to the terminal link block, then add the fenced-code and HTML-block regressions described above.

Inverse of round 4's hazard: a column-zero '[1.2.0]: url' inside a
fenced code or raw-HTML block LOOKS canonical to the line-based scans
but does not render as a definition - the predecessor-anchor search
could then insert the new comparison link inside that block, and exit-4
could certify a release whose header link never resolves.

Rather than teach the scans Markdown block context, enforce the terminal
invariant the file already satisfies (all 84 real definitions form one
contiguous run at EOF): _link_block_violation() refuses any
canonical-looking definition outside the terminal contiguous link block,
where no fence/HTML context can exist without breaking the run. Runs on
the pre-branch text (fresh + exit-4) and as a backstop on the assembled
text before any write. Regressions: predecessor definition existing only
inside a fenced block and only inside an HTML block, on both paths,
asserting nothing is written or consumed.
@github-actions

Copy link
Copy Markdown

🔁 AI review rerun (requested by @igerber)

Head SHA: 19fdb0c95eaefa79c2d7a037267f45804f893513


PR Review

Overall assessment

⚠️ Needs changes — the previous changelog-compiler P1 is only partially resolved.

Executive summary

  • No estimator, inference, weighting, or identification behavior changed.
  • LWDiD canary provenance agrees with the Methodology Registry.
  • Closed fenced/HTML blocks are now rejected, but unterminated blocks still bypass the terminal-link check.
  • Changelog, Python syntax, and workflow YAML checks pass; pytest is unavailable in this environment.

Methodology

No findings. The canary checks the SSC ancillary sources documented in docs/methodology/REGISTRY.md:L2762-L2786.

Code Quality

1. Unterminated Markdown blocks can still masquerade as comparison links

  • Severity: P1

  • Location: .claude/scripts/changelog_compile.py:L277-L299, .claude/scripts/changelog_compile.py:L744-L788

  • Impact: _link_block_violation() assumes a terminal link run cannot be inside a Markdown block. CommonMark permits fenced code and raw <pre> blocks to remain open through EOF, so:

    
    is accepted as a terminal definition even though it does not render as one. Fresh compilation then inserts the new definition inside that block and deletes the fragments, leaving the release header unresolved; exit 4 can similarly certify an invalid state. The new tests only cover closed wrappers (`tests/test_changelog_fragments.py:L454-L481`).
    
  • Concrete fix: Validate that terminal definitions are outside all Markdown block contexts, then add fresh and exit-4 regressions for unterminated fenced-code and <pre> blocks, asserting exit 1, unchanged changelog, and retained fragments.

Performance

No findings.

Maintainability

No findings.

Tech Debt

No findings. Removed backlog rows correspond to implemented work.

Security

No findings. Workflow permissions remain read-only and actions are SHA-pinned.

Documentation / Tests

No additional findings. The required changelog.d/ fragment is present.

Path to Approval

  1. Reject terminal comparison-link runs within unterminated fenced-code or raw-HTML blocks and add the regressions described above.

…entirely

Round 5's terminal-link-block invariant assumed a block context cannot
reach EOF; CommonMark lets an unterminated fence or <pre> stay open
through EOF, swallowing the terminal link block itself while every line
still matches the scans.

Close the whole question structurally: the changelog may not contain ANY
persistent block-context starter - fenced code (```/~~~ at 0-3 indent)
or raw-HTML block types 1-5 (<pre/script/style/textarea>, processing
instructions, declarations, CDATA), plus HTML comments not closed on the
same line (the Unreleased pointer's closed form stays legal). Type-6/7
HTML blocks end at the first blank line and cannot reach the terminal
block. With no such starter anywhere, no scan ever needs Markdown block
context. Enforced pre-branch (fresh + exit-4) and on the assembled text
before any write (a 1-space-indented fence in a fragment continuation
would land at top level once compiled).

Real CHANGELOG audited: zero starters, all comments self-closing.
Regressions: unterminated fence and unterminated <pre> on both paths.
…e boundary

A blank-line-terminated raw-HTML block (e.g. a bare '<div>') is not a
persistent starter, but one sitting DIRECTLY above the definitions would
swallow them. Requiring the terminal run to be immediately preceded by a
blank line guarantees every type-6/7 block above has ended. Real
CHANGELOG already satisfies it; regression on both compile paths.
@github-actions

Copy link
Copy Markdown

🔁 AI review rerun (requested by @igerber)

Head SHA: 4c487a64ef7a6454f6e8052f2a3c85b96762e6af


PR Review

Overall assessment

⚠️ Needs changes — one new P1 allows CI-approved changelog fragments to block release compilation.

Executive summary

  • No estimator, weighting, variance, inference, or identification logic changed.
  • LWDiD provenance checks agree with docs/methodology/REGISTRY.md:L2762-L2786.
  • The previous unterminated Markdown-block finding is resolved.
  • Fragment validation remains weaker than compilation-time validation.
  • Static Python/YAML checks and the live changelog check passed; pytest is unavailable in this environment.

Methodology

No findings. The canary verifies the SSC ancillary sources specified by the Registry.

Code Quality

1. CI accepts fragments that the release compiler refuses

  • Severity: P1

  • Location: .claude/scripts/changelog_compile.py:L72-L127, .claude/scripts/changelog_compile.py:L203-L210, .claude/scripts/changelog_compile.py:L808-L839

  • Impact: check validates version-link patterns line-by-line and never applies _block_context_violation() to fragments. Consequently, fragments containing an indented fenced/raw-HTML block or multiline numeric link label pass the PR guard but fail later during release compilation. For example:

    ### Documentation
    - Example:
      ```python
      print("ok")
    
    `_parse_fragment()` returns no errors, while assembly reports a block violation. This defers a predictable failure until release time.
    
  • Concrete fix: Apply the whole-text _noncanonical_version_link() and _block_context_violation() checks during fragment validation, then add check regressions for an indented fence, <pre> block, and multiline numeric link label. Document these restrictions in changelog.d/README.md.

Performance

No findings.

Maintainability

No findings.

Tech Debt

No findings. Removed TODO rows correspond to implemented work, and this finding is not tracked elsewhere.

Security

No findings. Workflow permissions are read-only and actions are SHA-pinned.

Documentation / Tests

No additional findings. The required changelog.d/ fragment is present.

Path to Approval

  1. Make changelog_compile.py check reject every fragment construct that the assembled-output guards reject, add the three regressions above, and update the fragment grammar documentation.

A fragment with an indented code fence (or a multi-line link label)
passed 'check' at PR time - the line-based grammar cannot see either -
then refused at RELEASE time via the assembled-output guards: a
CI-approved fragment blocking the release is a fail-late trap.

run_check now applies the same whole-text guards per fragment that the
assembled output must satisfy (_block_context_violation +
_noncanonical_version_link), so everything compile would refuse fails at
PR time instead. changelog.d/README.md documents the restrictions (no
fenced/raw-HTML blocks - use inline code; no version-link-like
constructs). Regressions: indented fence, <pre> block, and multi-line
numeric link label all fail check.
@github-actions

Copy link
Copy Markdown

🔁 AI review rerun (requested by @igerber)

Head SHA: 3bc285fd6af97478bb7e5499d12ea1f1169e9b50


PR Review

Overall assessment

Looks good — no unmitigated P0 or P1 findings.

Executive summary

  • No estimator, weighting, variance, inference, or identification logic changed.
  • LWDiD canary provenance matches the Methodology Registry’s SSC ancillary-data contract.
  • The previous P1 is resolved: PR-time fragment validation now matches compilation-time block/link guards, with regressions and documentation.
  • Static syntax, YAML parsing, tracking contracts, git diff --check, and the live changelog check passed.
  • Full pytest execution was unavailable because pytest is not installed in the review environment.

Methodology

No findings. The canary checks the Prop 99 and Walmart sources and replication suite specified in docs/methodology/REGISTRY.md:L2764-L2786.

Code Quality

No findings. The prior validation-parity issue is addressed in .claude/scripts/changelog_compile.py:L211-L230.

Performance

No findings.

Maintainability

No findings.

Tech Debt

No findings. The removed TODO rows correspond to implemented guards and canary coverage.

Security

No findings. Workflow permissions are read-only and third-party actions are SHA-pinned.

Documentation / Tests

No findings. The prior cases now have regression coverage, the restrictions are documented, and the required changelog.d/ fragment is present.

@igerber igerber added the ready-for-ci Triggers CI test workflows label Aug 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-ci Triggers CI test workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant