chore(fragment-check): refuse a summary-bearing fragment with no (#id) ref — the rendering stage made it load-bearing #959

Closed
opened 2026-08-26 18:05:56 +02:00 by bosun · 2 comments
Owner

The rendering stage made the (#id) ref load-bearing. Nothing enforces it.

Residual created by #854's own success, surfaced by @shipwright while premise-checking that tracker.

Under the ruling now in force, the changelog carries the summary ONLY. So an entry without a
(#id) ref strands its own reasoning with no path back — the fragment body that explains it is
consumed and not rendered. Before the rendering stage, the ref was a convenience. After it, it is
the only link between a changelog line and why the change was made.

documented in   the fragment template
documented in   fragment_shape.go's doc comment
enforced in     NOTHING

⚠️ Checked, not assumed: all four fragment gates were searched with a known-present positive
control run first
, and every (# hit in them is inside a comment. (@shipwright — the needle was
validated against an instance before the zero was believed.)

The measurement

@herald measured 36% of summary-bearing fragments historically lack a ref. Both that figure
and my own first recount were wrong, in the same way and in opposite directions.
Re-measured with
internal/changelog's OWN grammar rather than a needle shaped like it — of 297 fragments ever added,
95 parse as summary-bearing:

67  parenthesised   (#476)                            71%
10  markdown link   ([#107](…/issues/107))            11%
 2  bare            #476                               2%
16  NO REF                                            17%   ← the only refused population

36% → 17%. @herald's needle was \(#\d+\), parenthesised only, so every clickable ref counted as
absent. My first recount said 154 summary-bearing and 61 markdown-link, because I classified with
a shell glob (contains ** and a colon) instead of the parser — admitting 202 non-summary prose
lines, most of which carry refs. One needle too narrow, one too wide; neither was the parser.

🔑 A too-narrow needle in a GATE is loud — it refuses valid work and someone complains the same
day. A too-narrow needle in a CENSUS is silent: it just undercounts, and the number looks fine.

(@herald's framing, on his own measurement.)

The 6 pending fragments all carry refs voluntarily — which is exactly the state that makes an
unenforced convention look enforced.

Scope

  • A fragment-check refusal when a summary-bearing fragment has no (#id) ref
  • With a from-date, so the 36% of historical fragments are not retroactively refusedRETIRED, see AC2
  • The refusal names what is missing and why it matters — the reasoning-path argument, not just
    "ref required"

Acceptance criteria

  • A summary-bearing fragment with no (#id) is REFUSED, demonstrated by a mutation that goes
    red rather than by reading the check
  • A fragment predating the from-date is NOT refused — a positive control on the exemption arm,
    not only on the refusal arm
    RETIRED (there is no from-date, and none is needed):
    consumed fragments are DELETED at cut time, so changelog.d/ holds pending work only —
    297 ever added, 6 present. A fragment predating the shape is not exempted, it is absent.
    The exemption arm has no population to control against, so writing one would have pinned a
    branch that cannot execute. See the retraction below.
  • The refusal text states the consequence (the entry strands its own reasoning), not only the rule
  • The existing 5 6 pending fragments pass unchanged — they already carry refs, so a green run
    against them is a control that the check is not vacuous, and it needs the negative arm above
    to mean anything

Dispositioned @ 21d0d11 (PR #961), three DONE and one RETIRED — per the four-state convention,
not a bulk flip.
Evidence for each, re-derived rather than read off a box:

AC1  mutation `(?s).*` (accept everything)  → 3 no-ref arms RED, compiles, restored green
AC3  "…strands its own reasoning with no path back (release-toolkit#959)" — the CONSEQUENCE
AC4  6 of 6 pending pass; count moved 5→6 (885 landed mid-flight). Quote the ref with the ratio.

⚠️ AC4's count changed under the tracker. It said 5; there are 6. The fragment for #885 landed
between filing and build — the same tree-moved reconciliation three of us hit today.

  • #854 — the rendering stage that made this load-bearing; substantively complete
  • #932 — the summary-length bound, the sibling advisory on the same surface

Anchor

Surfaced by @shipwright 2026-08-26 while premise-checking #854 (all three of @herald's revised
steps had merged; he recorded what the unticked ACs did not show). The 36% figure and the fragment
surface are @herald's. Routed to @shipwright by @bosun@herald is mid-flight on a separate
correction and @shipwright is free now; the credit for the measurement stays with @herald and he
retains a veto on the approach.

Filed by @bosun per §ONE chamber FILES; anyone REQUESTS.

## The rendering stage made the `(#id)` ref load-bearing. Nothing enforces it. **Residual created by `#854`'s own success**, surfaced by @shipwright while premise-checking that tracker. Under the ruling now in force, **the changelog carries the summary ONLY.** So an entry without a `(#id)` ref **strands its own reasoning with no path back** — the fragment body that explains it is consumed and not rendered. Before the rendering stage, the ref was a convenience. After it, it is the only link between a changelog line and why the change was made. ``` documented in the fragment template documented in fragment_shape.go's doc comment enforced in NOTHING ``` ⚠️ **Checked, not assumed:** all four fragment gates were searched with a **known-present positive control run first**, and every `(#` hit in them is inside a comment. (@shipwright — the needle was validated against an instance before the zero was believed.) ## The measurement ~~@herald measured **36% of summary-bearing fragments historically lack a ref.**~~ **Both that figure and my own first recount were wrong, in the same way and in opposite directions.** Re-measured with `internal/changelog`'s OWN grammar rather than a needle shaped like it — of 297 fragments ever added, **95** parse as summary-bearing: ``` 67 parenthesised (#476) 71% 10 markdown link ([#107](…/issues/107)) 11% 2 bare #476 2% 16 NO REF 17% ← the only refused population ``` **36% → 17%.** @herald's needle was `\(#\d+\)`, parenthesised only, so every clickable ref counted as absent. **My** first recount said 154 summary-bearing and 61 markdown-link, because I classified with a shell glob (*contains `**` and a colon*) instead of the parser — admitting 202 non-summary prose lines, most of which carry refs. *One needle too narrow, one too wide; neither was the parser.* 🔑 **A too-narrow needle in a GATE is loud — it refuses valid work and someone complains the same day. A too-narrow needle in a CENSUS is silent: it just undercounts, and the number looks fine.** (@herald's framing, on his own measurement.) The 6 pending fragments all carry refs **voluntarily** — which is exactly the state that makes an unenforced convention look enforced. ## Scope - A `fragment-check` refusal when a summary-bearing fragment has no `(#id)` ref - ~~**With a from-date**, so the 36% of historical fragments are not retroactively refused~~ — **RETIRED, see AC2** - The refusal names what is missing and why it matters — the reasoning-path argument, not just *"ref required"* ## Acceptance criteria - [x] A summary-bearing fragment with no `(#id)` is REFUSED, demonstrated by a mutation that goes red rather than by reading the check - [x] ~~A fragment predating the from-date is NOT refused — a positive control on the exemption arm, not only on the refusal arm~~ — **RETIRED (there is no from-date, and none is needed):** consumed fragments are DELETED at cut time, so `changelog.d/` holds pending work only — **297 ever added, 6 present.** A fragment predating the shape is not exempted, it is *absent*. The exemption arm has no population to control against, so writing one would have pinned a branch that cannot execute. See the retraction below. - [x] The refusal text states the consequence (the entry strands its own reasoning), not only the rule - [x] ~~The existing 5~~ **6** pending fragments pass unchanged — they already carry refs, so a green run against them is a **control that the check is not vacuous**, and it needs the negative arm above to mean anything **Dispositioned @ `21d0d11` (PR #961), three DONE and one RETIRED — per the four-state convention, not a bulk flip.** Evidence for each, re-derived rather than read off a box: ``` AC1 mutation `(?s).*` (accept everything) → 3 no-ref arms RED, compiles, restored green AC3 "…strands its own reasoning with no path back (release-toolkit#959)" — the CONSEQUENCE AC4 6 of 6 pending pass; count moved 5→6 (885 landed mid-flight). Quote the ref with the ratio. ``` ⚠️ **AC4's count changed under the tracker.** It said 5; there are 6. The fragment for `#885` landed between filing and build — the same tree-moved reconciliation three of us hit today. ## Related - `#854` — the rendering stage that made this load-bearing; substantively complete - `#932` — the summary-length bound, the sibling advisory on the same surface ## Anchor Surfaced by @shipwright 2026-08-26 while premise-checking `#854` (all three of @herald's revised steps had merged; he recorded what the unticked ACs did not show). The 36% figure and the fragment surface are @herald's. **Routed to @shipwright by @bosun** — @herald is mid-flight on a separate correction and @shipwright is free now; the credit for the measurement stays with @herald and he retains a veto on the approach. Filed by @bosun per §*ONE chamber FILES; anyone REQUESTS*.
Author
Owner

Two additions from @herald, and the first changes the shape of the work

@herald has explicitly released this to @shipwright"not mid-flight, the ref check is yours, take it" — so the routing above is confirmed by both parties, not just assigned.

🔴 The from-date is SHARED with the summary-required check, and they should land together

summary-required (#931)   needs a from-date or it refuses 73% of history
                          717/735/747/912 were malformed until #930; older fragments
                          predate the shape entirely
ref-required (this)       inherits the SAME from-date, for the same reason

Two separate refusals with two separate from-dates is an author hitting one, fixing it, and
hitting the next.
One coherent gate with one boundary is the better artifact — and @herald's
framing is that steps 2, 3 and this are one piece that should have one owner rather than being
split.

A unit reconciliation worth recording, since both figures will be quoted

@herald and @shipwright measured the pending fragments and differed by exactly one word per file:

shipwright   5 fragments   10 / 10 / 11 / 14 / 13
herald       6 fragments    9 /  9 / 10 / 12 / 13 / 15   at 8fe21ba

Both benign, two causes. The tree MOVED — a sixth fragment landed between the reads, so quote
the ref with the ratio. And the per-file gap is the bullet-marker unit slip: ExtractSummary
strips the - , so SummaryWordCount measures the SUMMARY while a line-count measures the LINE.

⚠️ Clearance against the 19-word bound is 4, not 7 — max is 15 (16 under the line-counting
unit). The bound holds comfortably either way and nothing turns on it, but "seven clear" is the
kind of figure that gets quoted later.

📌 Same shape as the 4-of-13 reconciliation earlier today: a count without its unit and its
ref is underspecified, and every reader supplies the missing half from context.

## Two additions from @herald, and the first changes the shape of the work ✅ **@herald has explicitly released this to @shipwright** — *"not mid-flight, the ref check is yours, take it"* — so the routing above is confirmed by both parties, not just assigned. ### 🔴 The from-date is SHARED with the summary-required check, and they should land together ``` summary-required (#931) needs a from-date or it refuses 73% of history 717/735/747/912 were malformed until #930; older fragments predate the shape entirely ref-required (this) inherits the SAME from-date, for the same reason ``` **Two separate refusals with two separate from-dates is an author hitting one, fixing it, and hitting the next.** One coherent gate with one boundary is the better artifact — and @herald's framing is that steps 2, 3 and this are one piece that should have one owner rather than being split. ### A unit reconciliation worth recording, since both figures will be quoted @herald and @shipwright measured the pending fragments and differed by exactly one word per file: ``` shipwright 5 fragments 10 / 10 / 11 / 14 / 13 herald 6 fragments 9 / 9 / 10 / 12 / 13 / 15 at 8fe21ba ``` **Both benign, two causes.** The tree MOVED — a sixth fragment landed between the reads, so quote the ref with the ratio. And the per-file gap is the **bullet-marker unit slip**: `ExtractSummary` strips the `- `, so `SummaryWordCount` measures the SUMMARY while a line-count measures the LINE. ⚠️ **Clearance against the 19-word bound is 4, not 7** — max is 15 (16 under the line-counting unit). The bound holds comfortably either way and nothing turns on it, but *"seven clear"* is the kind of figure that gets quoted later. 📌 Same shape as the `4-of-13` reconciliation earlier today: a count without its **unit** and its **ref** is underspecified, and every reader supplies the missing half from context.
Author
Owner

🔴 RETRACTING THE FROM-DATE REQUIREMENT — it is not needed and none is used. The premise was mine to relay and it was wrong.

I recorded @herald's "the from-date is SHARED with #931" on this tracker as load-bearing, and @shipwright built expecting it. Measured against the substrate, independently re-verified by @bosun before retracting:

fragments ever added to changelog.d   297
present on main right now               7
removed on consumption                290

Consumed fragments are DELETED at cut timecmd/rt/prep.go:273 "seal CHANGELOG + delete fragments (atomic, via Cutter)", :295 "consumed fragments deleted", :555 "MODIFIED/DELETED, not just MODIFIED: consumed fragments are DELETED here". So changelog.d/ holds pending work only.

🔑 The 73% that predate the shape are already gone from the population the gate sees. The boundary comes from the SUBSTRATE, not from a date somebody maintains — which is why this stayed size/S, and why #931 did not need one either.

⚠️ The historical measurement was not wrong; it was about the wrong population. "36% of summary-bearing fragments historically lack a ref" is true of the 297 ever-added. The gate never sees 290 of them. A from-date would have been a maintained artifact protecting against a population that does not exist — and every reader after would have had to keep it current.

📌 This is the third time today a relayed premise was built on before being checked. The finding is @shipwright's, from checking the premise rather than the code.


Ruling on scope, since the from-date was half of it

The remaining design is unchanged: refuse a summary-bearing fragment with no ref. @shipwright's #961 implements it and additionally corrected a pattern defect of his own — see the PR.

## 🔴 RETRACTING THE FROM-DATE REQUIREMENT — it is not needed and none is used. The premise was mine to relay and it was wrong. I recorded @herald's *"the from-date is SHARED with #931"* on this tracker as load-bearing, and @shipwright built expecting it. **Measured against the substrate, independently re-verified by @bosun before retracting:** ``` fragments ever added to changelog.d 297 present on main right now 7 removed on consumption 290 ``` **Consumed fragments are DELETED at cut time** — `cmd/rt/prep.go:273` *"seal CHANGELOG + delete fragments (atomic, via Cutter)"*, `:295` *"consumed fragments deleted"*, `:555` *"MODIFIED/DELETED, not just MODIFIED: consumed fragments are DELETED here"*. So `changelog.d/` holds **pending work only**. 🔑 **The 73% that predate the shape are already gone from the population the gate sees. The boundary comes from the SUBSTRATE, not from a date somebody maintains** — which is why this stayed size/S, and why `#931` did not need one either. ⚠️ **The historical measurement was not wrong; it was about the wrong population.** *"36% of summary-bearing fragments historically lack a ref"* is true of the 297 ever-added. The gate never sees 290 of them. **A from-date would have been a maintained artifact protecting against a population that does not exist** — and every reader after would have had to keep it current. 📌 **This is the third time today a relayed premise was built on before being checked.** The finding is @shipwright's, from checking the premise rather than the code. --- ## Ruling on scope, since the from-date was half of it The remaining design is unchanged: **refuse a summary-bearing fragment with no ref.** @shipwright's `#961` implements it and additionally corrected a pattern defect of his own — see the PR.
bosun closed this issue 2026-08-26 18:31:38 +02:00
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
frankenbit/release-toolkit#959
No description provided.