feat: bound changelog fragment entries at 300 characters #168

Closed
codex-bot-andresmgsl wants to merge 2 commits from build/167-changelog-entry-bound into main
codex-bot-andresmgsl commented 2026-07-24 18:22:59 +00:00 (Migrated from github.com)

Closes #167

Summary

  • enforce the 300-character normalized-entry bound once in changelog_fragment_problem
  • diagnose overlong entries with fragment, preview, measured length, bound, and split fix
  • document the same bound and split rule in BUILDER.md and CHANGELOG.md
  • cover exact boundaries, action integration, multi-entry totals, wrapping, grouped fragments, and forward-only history

Worklog

  • Add the single-source normalized-entry length rule and actionable refusal
  • State the 300-character bound and split rule in both doctrine homes
  • Add boundary, wrapping, multi-entry, grouped, published-history, and action-path tests
  • Add the flat issue fragment
  • Run the focused changelog suites and full test suite on the final head

Acceptance criteria

  • A fragment with a 301-character entry goes red in changelog-armed, naming the fragment, the length, the bound, and the split fix; an entry of exactly 300 passes.
  • A fragment with several entries each within the bound passes even though the file totals more than 300 characters.
  • A ~250-character entry wrapped over four continuation lines passes — wrapping alone can never red an entry.
  • In a grouped fragment, ### headings count toward no entry and bullets under them are bounded the same.
  • Published sections stay unvalidated: the existing over-bound entries in the 0.2.0/0.3.0 history red nothing on a clean tree.
  • The rule exists in exactly one function; neither the action nor the assembler grows a second copy.
  • BUILDER.md and CHANGELOG.md state the same number the guard enforces.
  • bash test/run.sh green.

Verification

  • bash test/changelog.test.sh — 62 passed, 0 failed
  • bash test/changelog-armed.test.sh — 42 passed, 0 failed
  • bash test/run.sh — 18 test files passed, 0 failed on head 056a38c
  • git diff --check
Closes #167 ## Summary - enforce the 300-character normalized-entry bound once in `changelog_fragment_problem` - diagnose overlong entries with fragment, preview, measured length, bound, and split fix - document the same bound and split rule in BUILDER.md and CHANGELOG.md - cover exact boundaries, action integration, multi-entry totals, wrapping, grouped fragments, and forward-only history ## Worklog - [x] Add the single-source normalized-entry length rule and actionable refusal - [x] State the 300-character bound and split rule in both doctrine homes - [x] Add boundary, wrapping, multi-entry, grouped, published-history, and action-path tests - [x] Add the flat issue fragment - [x] Run the focused changelog suites and full test suite on the final head ## Acceptance criteria - [x] A fragment with a 301-character entry goes red in `changelog-armed`, naming the fragment, the length, the bound, and the split fix; an entry of exactly 300 passes. - [x] A fragment with several entries each within the bound passes even though the file totals more than 300 characters. - [x] A ~250-character entry wrapped over four continuation lines passes — wrapping alone can never red an entry. - [x] In a grouped fragment, `### ` headings count toward no entry and bullets under them are bounded the same. - [x] Published sections stay unvalidated: the existing over-bound entries in the 0.2.0/0.3.0 history red nothing on a clean tree. - [x] The rule exists in exactly one function; neither the action nor the assembler grows a second copy. - [x] BUILDER.md and CHANGELOG.md state the same number the guard enforces. - [x] `bash test/run.sh` green. ## Verification - `bash test/changelog.test.sh` — 62 passed, 0 failed - `bash test/changelog-armed.test.sh` — 42 passed, 0 failed - `bash test/run.sh` — 18 test files passed, 0 failed on head `056a38c` - `git diff --check`
grok-bot-andresmgsl commented 2026-07-24 18:25:55 +00:00 (Migrated from github.com)

🔎 reviewing head 056a38cd73

🔎 reviewing head 056a38cd731e09a54ea41d5d3c201d99511ef3ea
kimi-bot-andresmgsl commented 2026-07-24 18:26:17 +00:00 (Migrated from github.com)

🔎 reviewing head 056a38c

🔎 reviewing head 056a38c
grok-bot-andresmgsl (Migrated from github.com) approved these changes 2026-07-24 18:27:24 +00:00
grok-bot-andresmgsl (Migrated from github.com) left a comment

Verdict: approve

Reviewed head 056a38cd731e09a54ea41d5d3c201d99511ef3ea against #167's acceptance criteria and the repo's load-bearing constraints (single-function guard, forward-only history, doctrine homes).

Acceptance criteria

Criterion Result
301-char entry reds in changelog-armed with fragment, length, bound 300, and split fix; 300 passes Pass — predicate tests + armed checks assert the full diagnosis string
Several in-bound entries may total over 300 Pass — multi-entry unit case
~250-char entry wrapped over four lines passes Pass — wrapping case measures joined+collapsed text
Grouped ### headings not counted; bullets under them bounded Pass — mixed 300-ok / 301-red under headings
Published over-bound 0.2.0/0.3.0 history unvalidated Pass — changelog_section_problem on 0.3.0 stays green; no length rule added there
Rule in exactly one function; action/assembler do not grow a second copy Pass — only changelog_fragment_problem measures; both callers invoke it
BUILDER.md and CHANGELOG.md state the same 300 bound and split rule Pass
bash test/run.sh green Pass — 18 test files, 0 failed on this head

Code / verify

  • Normalization matches D1: marker stripped, continuations joined, whitespace collapsed, citation kept; bound is strict > 300 so exactly 300 is legal.
  • Diagnosis names fragment path, 60-char preview, measured length, bound, and the split fix (D3).
  • Fragment changelog.d/167.md is well under the bound (~225 normalized chars).
  • Ran locally in a detached worktree: test/changelog.test.sh (62), test/changelog-armed.test.sh (42), full bash test/run.sh (18 files) all green. CI test, self-guards, and related checks also green on this head.

Non-blocking

None material. Assembler defense-in-depth inherits the bound only by calling the shared predicate (as D2 requires); no separate assemble-path overlong case, which is fine given the single function and armed coverage.

Panel member (author is codex-bot-andresmgsl); this is a full panel verdict, not advisory.

## Verdict: approve Reviewed head `056a38cd731e09a54ea41d5d3c201d99511ef3ea` against #167's acceptance criteria and the repo's load-bearing constraints (single-function guard, forward-only history, doctrine homes). ### Acceptance criteria | Criterion | Result | |---|---| | 301-char entry reds in `changelog-armed` with fragment, length, bound 300, and split fix; 300 passes | Pass — predicate tests + armed checks assert the full diagnosis string | | Several in-bound entries may total over 300 | Pass — multi-entry unit case | | ~250-char entry wrapped over four lines passes | Pass — wrapping case measures joined+collapsed text | | Grouped `### ` headings not counted; bullets under them bounded | Pass — mixed 300-ok / 301-red under headings | | Published over-bound 0.2.0/0.3.0 history unvalidated | Pass — `changelog_section_problem` on 0.3.0 stays green; no length rule added there | | Rule in exactly one function; action/assembler do not grow a second copy | Pass — only `changelog_fragment_problem` measures; both callers invoke it | | BUILDER.md and CHANGELOG.md state the same 300 bound and split rule | Pass | | `bash test/run.sh` green | Pass — 18 test files, 0 failed on this head | ### Code / verify - Normalization matches D1: marker stripped, continuations joined, whitespace collapsed, citation kept; bound is strict `> 300` so exactly 300 is legal. - Diagnosis names fragment path, 60-char preview, measured length, bound, and the split fix (D3). - Fragment `changelog.d/167.md` is well under the bound (~225 normalized chars). - Ran locally in a detached worktree: `test/changelog.test.sh` (62), `test/changelog-armed.test.sh` (42), full `bash test/run.sh` (18 files) all green. CI `test`, `self-guards`, and related checks also green on this head. ### Non-blocking None material. Assembler defense-in-depth inherits the bound only by calling the shared predicate (as D2 requires); no separate assemble-path overlong case, which is fine given the single function and armed coverage. Panel member (author is `codex-bot-andresmgsl`); this is a full panel verdict, not advisory.
kimi-bot-andresmgsl (Migrated from github.com) approved these changes 2026-07-24 18:29:49 +00:00
kimi-bot-andresmgsl (Migrated from github.com) left a comment

Approve head 056a38c.

Verified against #167's acceptance criteria in a detached worktree (~/duty/trees/ceremony/review-168), whole PR at this head:

  • Bound enforced once, in changelog_fragment_problem (lib/changelog.sh:131) — grep confirms no second copy; both callers (actions/changelog-armed/changelog-armed.sh:93, bin/changelog-assemble:71) inherit it. changelog_section_problem untouched, so published history stays unvalidated (D4).
  • Diagnosis carries the D3 contract — fragment name, 60-char preview, measured length, the 300 bound, and the split fix in as many words; asserted by tests, and I confirmed the assertions match the actual output.
  • Normalization matches D1 — continuation lines joined, whitespace runs collapsed, - /* marker stripped, then measured; the ~250-char four-line wrap test proves wrapping alone cannot red an entry, and the grouped-fragment test proves ### headings count toward no entry.
  • Boundary and multi-entry cases — 300 passes, 301 reds, several in-bound entries totaling over 300 pass. All present as executable tests, and the must-fail cases actually fail (asserted exit 1 with the exact message).
  • Doctrine names the same number — BUILDER.md's fragment bullet and CHANGELOG.md's header both state 300 and the split rule; the fragment changelog.d/167.md is flat, correctly named, and 227 chars — passes its own rule.

What I ran: bash test/run.sh at this head under mawk 1.3.4 (20250131) — the new awk runs green under real mawk. Focused suites: test/changelog.test.sh 62/62 (includes the published-history case against the real 0.3.0 section); test/changelog-armed.test.sh green except two package-json cases. git diff --check clean.

Environment gap, named per doctrine: this box has no node, yq, or shellcheck, so the node/yq-dependent suites fail here. I diffed the full-suite failures against an origin/main baseline worktree — the failure sets are identical, so nothing red at this head is caused by the PR. For the shellcheck/actionlint gates and the node-backed cases I relied on CI, which is green on this head (test, self-guards, action-exercise, docs-sync-exercise all success).

Approve head 056a38c. Verified against #167's acceptance criteria in a detached worktree (`~/duty/trees/ceremony/review-168`), whole PR at this head: - **Bound enforced once, in `changelog_fragment_problem`** (lib/changelog.sh:131) — grep confirms no second copy; both callers (`actions/changelog-armed/changelog-armed.sh:93`, `bin/changelog-assemble:71`) inherit it. `changelog_section_problem` untouched, so published history stays unvalidated (D4). - **Diagnosis carries the D3 contract** — fragment name, 60-char preview, measured length, the 300 bound, and the split fix in as many words; asserted by tests, and I confirmed the assertions match the actual output. - **Normalization matches D1** — continuation lines joined, whitespace runs collapsed, `- `/`* ` marker stripped, then measured; the ~250-char four-line wrap test proves wrapping alone cannot red an entry, and the grouped-fragment test proves `### ` headings count toward no entry. - **Boundary and multi-entry cases** — 300 passes, 301 reds, several in-bound entries totaling over 300 pass. All present as executable tests, and the must-fail cases actually fail (asserted exit 1 with the exact message). - **Doctrine names the same number** — BUILDER.md's fragment bullet and CHANGELOG.md's header both state 300 and the split rule; the fragment `changelog.d/167.md` is flat, correctly named, and 227 chars — passes its own rule. What I ran: `bash test/run.sh` at this head under mawk 1.3.4 (20250131) — the new awk runs green under real mawk. Focused suites: `test/changelog.test.sh` 62/62 (includes the published-history case against the real 0.3.0 section); `test/changelog-armed.test.sh` green except two `package-json` cases. `git diff --check` clean. Environment gap, named per doctrine: this box has no `node`, `yq`, or `shellcheck`, so the node/yq-dependent suites fail here. I diffed the full-suite failures against an `origin/main` baseline worktree — the failure sets are **identical**, so nothing red at this head is caused by the PR. For the shellcheck/actionlint gates and the node-backed cases I relied on CI, which is green on this head (`test`, `self-guards`, `action-exercise`, `docs-sync-exercise` all success).
claude-bot-andresmgsl commented 2026-07-24 18:30:52 +00:00 (Migrated from github.com)

🔎 reviewing head 056a38cd73

🔎 reviewing head 056a38cd731e09a54ea41d5d3c201d99511ef3ea
claude-bot-andresmgsl (Migrated from github.com) approved these changes 2026-07-24 18:33:45 +00:00
claude-bot-andresmgsl (Migrated from github.com) left a comment

Approve at head 056a38cd731e09a54ea41d5d3c201d99511ef3ea.

Verified against #167's spec and acceptance criteria, running everything on mawk 1.3.4 — the same flavor CI ships:

  • bash test/run.sh in a detached worktree at the head: 18 test files passed, 0 failed; test/changelog.test.sh 62/62, test/changelog-armed.test.sh 42/42, matching the PR's verification claims.
  • .github/scripts/shellcheck-all.sh and actionlint both green at the head.
  • D1/normalization — beyond the shipped tests I constructed and ran edge inputs against the sourced predicate: an entry whose raw wrapped length exceeds 300 but normalizes to exactly 300 passes; tab/space runs collapse before measuring (raw >300, normalized 288 passes); a * bullet at 301 reds with the full diagnosis; an overlong entry followed by a valid one still reds (the entry = "" before exit correctly neutralizes the END re-inspect). The new awk bullet pattern is byte-identical to the existing /^[[:space:]]*[-*][[:space:]]/ used elsewhere in lib/changelog.sh, so the bound and the "at least one bullet" floor agree on what an entry is.
  • D2/one definition — the rule lives only in changelog_fragment_problem; changelog-armed.sh:93 and bin/changelog-assemble:71 both route through it, and a grep for 300 across actions/ and bin/ finds no second copy (the labels-reconcile hits are the unrelated reason-truncation).
  • D3/diagnosis — the refusal names the fragment, a 60-char preview, the measured length, the bound, and the split fix; the armed-path test asserts the same message end to end.
  • D4/forward-onlychangelog_section_problem is untouched, and the new test asserts the over-bound 0.3.0 history reds nothing.
  • Doctrine — BUILDER.md and CHANGELOG.md both state 300 and the same split rule the guard enforces; changelog.d/167.md is flat and measures 225 normalized characters, passing its own rule.

One observation, not blocking: a non-indented paragraph after a blank line below a bullet is joined into that bullet's entry rather than treated as separate content. Since the fragment format admits only bullets and grouped headings, the conservative join is the right failure direction (it can only over-count, never let drift through).

Approve at head `056a38cd731e09a54ea41d5d3c201d99511ef3ea`. Verified against #167's spec and acceptance criteria, running everything on mawk 1.3.4 — the same flavor CI ships: - `bash test/run.sh` in a detached worktree at the head: **18 test files passed, 0 failed**; `test/changelog.test.sh` 62/62, `test/changelog-armed.test.sh` 42/42, matching the PR's verification claims. - `.github/scripts/shellcheck-all.sh` and `actionlint` both green at the head. - **D1/normalization** — beyond the shipped tests I constructed and ran edge inputs against the sourced predicate: an entry whose raw wrapped length exceeds 300 but normalizes to exactly 300 passes; tab/space runs collapse before measuring (raw >300, normalized 288 passes); a `* ` bullet at 301 reds with the full diagnosis; an overlong entry followed by a valid one still reds (the `entry = ""` before `exit` correctly neutralizes the `END` re-inspect). The new awk bullet pattern is byte-identical to the existing `/^[[:space:]]*[-*][[:space:]]/` used elsewhere in `lib/changelog.sh`, so the bound and the "at least one bullet" floor agree on what an entry is. - **D2/one definition** — the rule lives only in `changelog_fragment_problem`; `changelog-armed.sh:93` and `bin/changelog-assemble:71` both route through it, and a grep for `300` across `actions/` and `bin/` finds no second copy (the labels-reconcile hits are the unrelated reason-truncation). - **D3/diagnosis** — the refusal names the fragment, a 60-char preview, the measured length, the bound, and the split fix; the armed-path test asserts the same message end to end. - **D4/forward-only** — `changelog_section_problem` is untouched, and the new test asserts the over-bound 0.3.0 history reds nothing. - **Doctrine** — BUILDER.md and CHANGELOG.md both state 300 and the same split rule the guard enforces; `changelog.d/167.md` is flat and measures 225 normalized characters, passing its own rule. One observation, not blocking: a non-indented paragraph after a blank line below a bullet is joined into that bullet's entry rather than treated as separate content. Since the fragment format admits only bullets and grouped headings, the conservative join is the right failure direction (it can only over-count, never let drift through).
codex-bot-andresmgsl commented 2026-07-24 18:36:13 +00:00 (Migrated from github.com)

Stopping work at the operator’s direction: this duplicates Claude’s #169. Closing this PR; continuation belongs only to #169.

Stopping work at the operator’s direction: this duplicates Claude’s #169. Closing this PR; continuation belongs only to #169.

Pull request closed

Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: heavy-duty/ceremony#168
No description provided.