fragments: kind-parser silently drops fragments with hyphens in id #9

Closed
opened 2026-06-24 16:50:54 +02:00 by quartermaster · 0 comments

Symptom

A fragment named <id-with-hyphen>.<kind>.md (e.g., 3.fixed-2.md where the author intended id=3, kind=fixed with -2 as a multi-fragment-per-id suffix) is silently dropped during categorize_fragments — never makes it into the CHANGELOG or release notes.

Empirical reproduction (caught during v0.2.0 cut, 2026-06-24)

I named two fragments for v0.2.0: 3.fixed.md and 3.fixed-2.md. After firing release-prep.sh --bump minor:

  • 3.fixed.md → correctly emitted to CHANGELOG, deleted from disk ✓
  • 3.fixed-2.md → NOT emitted, NOT deleted, silently skipped ✗

Verified the cause via source scripts/lib/fragments.sh; fragment_kind_from_path "changelog.d/3.fixed-2.md" → returned 1 (rejection).

Root cause

scripts/lib/fragments.sh::fragment_kind_from_path:

local base="${path##*/}"
base="${base%.md}"        # base = "3.fixed-2"
local kind="${base##*.}"  # kind = "fixed-2" (last dot-segment)

For 3.fixed-2.md, the last dot-segment is fixed-2, not fixed. Since fixed-2 isn't in FRAGMENT_KINDS, the function returns 1 and the fragment gets silently skipped by list_fragments / categorize_fragments / delete_fragments / determine_bump_from_fragments.

Impact

  • Silent data loss: a fragment author who intends <id>.<kind>.md but accidentally adds a hyphen-then-something between kind and .md gets a fragment that's invisible to the toolkit — never emitted, never deleted, never warned about. Pure substrate-of-record corruption.
  • Naming convention ambiguity: <id> was framed as "free-form, but expected to sort sensibly" — hyphens in <id> are natural, but no rule says hyphens MIDWAY through the basename break parsing.
  • Caught manually post-cut: in the v0.2.0 cut I caught it because I noticed the file-count was off, but a less attentive operator might miss it entirely (especially with many fragments).

Proposed dispositions

(A) Strict parsing + loud warning on unrecognized basenames (v0.2.1 patch)

list_fragments should LOG a warning to stderr for *.md files that don't parse as valid fragments (instead of silently skipping). Operator sees "warning: changelog.d/3.fixed-2.md does not match <id>.<kind>.md (unknown kind 'fixed-2')" and can rename or remove before cut.

Doesn't change parsing semantics; just makes the failure visible. Smallest blast-radius fix; covers the immediate "silent data loss" hazard.

(B) Smarter kind matching: scan from KNOWN kinds, not from last dot-segment (v0.3 minor)

Parse the basename by iterating over FRAGMENT_KINDS and checking for any .<kind>.md suffix match (with optional .<suffix> after). Would accept:

  • 3.fixed.md → id=3, kind=fixed
  • 3.fixed-2.md → id=3, kind=fixed, sub-suffix=-2
  • 3.fixed.also.md → id=3, kind=fixed, sub-suffix=.also

More forgiving authoring + supports multi-fragment-per-id naturally.

(C) Both: ship (A) in v0.2.1 + (B) in v0.3

(A) closes the silent-data-loss hazard immediately + buys time to design (B) properly.

My lean

(C) — (A) is a small patch worth shipping fast; (B) is a substrate-care surface-expansion worth taking the time to get right.

Cross-tracker

  • frankenbit/release-toolkit#3 (v0.2.0 umbrella) — fragment silently dropped during the cut; manually folded into the prep PR
  • The manual fold-in is in commit a521998 on release-prep/v0.2.0 branch

— QM, 2026-06-24, surfaced during v0.2.0 cut.

## Symptom A fragment named `<id-with-hyphen>.<kind>.md` (e.g., `3.fixed-2.md` where the author intended `id=3`, `kind=fixed` with `-2` as a multi-fragment-per-id suffix) is silently dropped during `categorize_fragments` — never makes it into the CHANGELOG or release notes. ## Empirical reproduction (caught during v0.2.0 cut, 2026-06-24) I named two fragments for v0.2.0: `3.fixed.md` and `3.fixed-2.md`. After firing `release-prep.sh --bump minor`: - `3.fixed.md` → correctly emitted to CHANGELOG, deleted from disk ✓ - `3.fixed-2.md` → NOT emitted, NOT deleted, silently skipped ✗ Verified the cause via `source scripts/lib/fragments.sh; fragment_kind_from_path "changelog.d/3.fixed-2.md"` → returned 1 (rejection). ## Root cause `scripts/lib/fragments.sh::fragment_kind_from_path`: ```bash local base="${path##*/}" base="${base%.md}" # base = "3.fixed-2" local kind="${base##*.}" # kind = "fixed-2" (last dot-segment) ``` For `3.fixed-2.md`, the last dot-segment is `fixed-2`, not `fixed`. Since `fixed-2` isn't in `FRAGMENT_KINDS`, the function returns 1 and the fragment gets silently skipped by `list_fragments` / `categorize_fragments` / `delete_fragments` / `determine_bump_from_fragments`. ## Impact - **Silent data loss**: a fragment author who intends `<id>.<kind>.md` but accidentally adds a hyphen-then-something between kind and `.md` gets a fragment that's invisible to the toolkit — never emitted, never deleted, never warned about. Pure substrate-of-record corruption. - **Naming convention ambiguity**: `<id>` was framed as "free-form, but expected to sort sensibly" — hyphens in `<id>` are natural, but no rule says hyphens MIDWAY through the basename break parsing. - **Caught manually post-cut**: in the v0.2.0 cut I caught it because I noticed the file-count was off, but a less attentive operator might miss it entirely (especially with many fragments). ## Proposed dispositions ### (A) Strict parsing + loud warning on unrecognized basenames (v0.2.1 patch) `list_fragments` should LOG a warning to stderr for `*.md` files that don't parse as valid fragments (instead of silently skipping). Operator sees "warning: changelog.d/3.fixed-2.md does not match `<id>.<kind>.md` (unknown kind 'fixed-2')" and can rename or remove before cut. Doesn't change parsing semantics; just makes the failure visible. Smallest blast-radius fix; covers the immediate "silent data loss" hazard. ### (B) Smarter kind matching: scan from KNOWN kinds, not from last dot-segment (v0.3 minor) Parse the basename by iterating over `FRAGMENT_KINDS` and checking for any `.<kind>.md` suffix match (with optional `.<suffix>` after). Would accept: - `3.fixed.md` → id=3, kind=fixed - `3.fixed-2.md` → id=3, kind=fixed, sub-suffix=-2 - `3.fixed.also.md` → id=3, kind=fixed, sub-suffix=.also More forgiving authoring + supports multi-fragment-per-id naturally. ### (C) Both: ship (A) in v0.2.1 + (B) in v0.3 (A) closes the silent-data-loss hazard immediately + buys time to design (B) properly. ## My lean (C) — (A) is a small patch worth shipping fast; (B) is a substrate-care surface-expansion worth taking the time to get right. ## Cross-tracker - frankenbit/release-toolkit#3 (v0.2.0 umbrella) — fragment silently dropped during the cut; manually folded into the prep PR - The manual fold-in is in commit a521998 on release-prep/v0.2.0 branch — QM, 2026-06-24, surfaced during v0.2.0 cut.
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#9
No description provided.