test: sentinel rows across the shape predicate, the armed guard, and the assembler

Must-pass and must-fail rows from #182's test plan: the flip shape green
under the sentinel, drift and malformed sentinels red with file named, the
D3 fragment-list assertion, and D5 sentinel survival through consumption.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
claude-bot-andresmgsl 2026-07-25 13:12:30 +00:00
parent 7d29acdeb0
commit 2fd9cb7ded
3 changed files with 202 additions and 0 deletions

View file

@ -362,6 +362,48 @@ check "fragment mode refuses a flat set over a grouped published section" 1 \
"changelog.d/115.md' is flat but newest published section '1.2.3'" \
in_tree fragments-dev-flat-over-grouped
# The declared anchor (#182): the flip tree — a grouped set under a
# 'grouped' sentinel over a flat published section — is green, where the
# same tree minus the sentinel is the all-grouped-over-flat red row above.
fragment_tree fragments-dev-flip 1.2.4-dev <<'EOF'
# Changelog
## 1.2.3 — 2026-07-20
- The shipped entry.
EOF
printf '%s\n' "grouped" >"$TMP/fragments-dev-flip/changelog.d/shape"
cat >"$TMP/fragments-dev-flip/changelog.d/115.md" <<'EOF'
### Fixed
- Grouped fragment.
EOF
check "fragment mode: 'grouped' sentinel admits the flip tree over a flat published section" 0 \
"fragment mode" in_tree fragments-dev-flip
# Post-flip drift is refused on its own PR: a flat probe fragment atop the
# flip tree goes red — beside grouped fragments the mix rule names it first.
printf '%s\n' "- Flat probe." >"$TMP/fragments-dev-flip/changelog.d/116.md"
check "fragment mode: a flat probe atop the flip tree is refused" 1 \
"changelog.d/115.md' is grouped but fragment 'changelog.d/116.md' is not" \
in_tree fragments-dev-flip
rm "$TMP/fragments-dev-flip/changelog.d/116.md"
# And once the grouped fragments are consumed, the sentinel alone still
# holds the shape: an all-flat set under 'grouped' is refused, sentinel
# named — the published-section inference never gets a say.
rm "$TMP/fragments-dev-flip/changelog.d/115.md"
printf '%s\n' "- Flat probe." >"$TMP/fragments-dev-flip/changelog.d/116.md"
check "fragment mode: a flat set under the 'grouped' sentinel refused, sentinel named" 1 \
"changelog.d/116.md' is flat but 'changelog.d/shape' declares grouped" \
in_tree fragments-dev-flip
rm "$TMP/fragments-dev-flip/changelog.d/116.md"
printf '%s\n' "Grouped" >"$TMP/fragments-dev-flip/changelog.d/shape"
check "fragment mode: a malformed sentinel is refused, file named" 1 \
"'changelog.d/shape' declares neither shape" \
in_tree fragments-dev-flip
fragment_tree fragments-dev-no-published 1.2.4-dev <<'EOF'
# Changelog

View file

@ -143,6 +143,51 @@ check "grouped: the written file is exact" 0 "" \
assert_file "$TMP/grouped/CHANGELOG.md" \
$'# Changelog\n\nPreamble prose belongs to no section.\n\n## 0.2.0 — 2026-07-24\n\n'"$GROUPED_BODY"$'\n\n## 0.1.0 — 2026-07-01\n\n### Fixed\n\n- The shipped entry.'
# --- the declared anchor: the first grouped release over a flat history ------
# The flip ceremony (#182): a 'grouped' sentinel admits grouped fragments
# over a flat newest published section, the sentinel is never a stray file,
# and it survives the consumption (D5) — the next -dev tree still declares
# its shape.
tree flip <<EOF
$BASE_CHANGELOG
EOF
printf 'grouped\n' >"$TMP/flip/changelog.d/shape"
frag flip 40.md <<'EOF'
### Added
- Forty landed.
EOF
check "sentinel: the flip release assembles grouped over a flat published section" 0 \
"consumed 1 fragment" in_tree flip 0.2.0 2026-07-24
check "sentinel: the written flip section is exact" 0 "" \
assert_file "$TMP/flip/CHANGELOG.md" \
$'# Changelog\n\nPreamble prose belongs to no section.\n\n## 0.2.0 — 2026-07-24\n\n### Added\n\n- Forty landed.\n\n## 0.1.0 — 2026-07-01\n\n- The shipped entry.'
check "sentinel: changelog.d/shape survives consumption" 0 "" \
test -e "$TMP/flip/changelog.d/shape"
tree flip-flat-frag <<EOF
$BASE_CHANGELOG
EOF
printf 'grouped\n' >"$TMP/flip-flat-frag/changelog.d/shape"
frag flip-flat-frag 41.md <<'EOF'
- Flat forty-one.
EOF
check "sentinel: a flat fragment under 'grouped' refuses, sentinel named" 1 \
"changelog.d/shape' declares grouped" in_tree flip-flat-frag 0.2.0 2026-07-24
tree flip-malformed <<EOF
$BASE_CHANGELOG
EOF
printf 'Grouped\n' >"$TMP/flip-malformed/changelog.d/shape"
frag flip-malformed 42.md <<'EOF'
### Added
- Forty-two.
EOF
check "sentinel: a malformed sentinel refuses, file named" 1 \
"changelog.d/shape' declares neither shape" in_tree flip-malformed 0.2.0 2026-07-24
# --- a changelog holding only its preamble -----------------------------------
tree preamble-only <<'EOF'

View file

@ -469,4 +469,119 @@ rm "$SHAPE_DIR/1.md"
check "shape: empty fragment set makes the anchor rule vacuous" 0 "" \
changelog_shape_problem "$SHAPE_CHANGELOG" "$SHAPE_DIR"
# --- the declarable anchor: <dir>/shape (#182) -------------------------------
# The sentinel pins the set's shape and outranks the newest-published-section
# inference — the door a deliberate flip walks through, while the undeclared
# drift rows above stay red, verbatim.
cat >"$SHAPE_CHANGELOG" <<'EOF'
# Changelog
## 2.0.0 — 2026-07-24
- Newest section is flat.
EOF
cat >"$SHAPE_DIR/1.md" <<'EOF'
### Fixed
- Grouped fragment.
EOF
printf 'grouped\n' >"$SHAPE_DIR/shape"
check "shape: 'grouped' sentinel admits a grouped set over a flat published section" 0 "" \
changelog_shape_problem "$SHAPE_CHANGELOG" "$SHAPE_DIR"
check "shape: the sentinel binds with no changelog at all — the assembler's call" 0 "" \
changelog_shape_problem "" "$SHAPE_DIR"
printf -- '- Flat fragment.\n' >"$SHAPE_DIR/1.md"
check "shape: flat fragment under a 'grouped' sentinel refused, fragment and sentinel named" 1 \
"fragment '$SHAPE_DIR/1.md' is flat but '$SHAPE_DIR/shape' declares grouped" \
changelog_shape_problem "$SHAPE_CHANGELOG" "$SHAPE_DIR"
cat >"$SHAPE_CHANGELOG" <<'EOF'
# Changelog
## 2.0.0 — 2026-07-24
### Fixed
- Newest section is grouped.
EOF
printf 'flat\n' >"$SHAPE_DIR/shape"
check "shape: 'flat' sentinel admits a flat set over a grouped published section" 0 "" \
changelog_shape_problem "$SHAPE_CHANGELOG" "$SHAPE_DIR"
cat >"$SHAPE_DIR/1.md" <<'EOF'
### Fixed
- Grouped fragment.
EOF
check "shape: grouped fragment under a 'flat' sentinel refused, fragment and sentinel named" 1 \
"fragment '$SHAPE_DIR/1.md' is grouped but '$SHAPE_DIR/shape' declares flat" \
changelog_shape_problem "$SHAPE_CHANGELOG" "$SHAPE_DIR"
printf 'grouped\n' >"$SHAPE_DIR/shape"
printf -- '- Flat two.\n' >"$SHAPE_DIR/2.md"
check "shape: a mixed set is refused regardless of the sentinel" 1 \
"fragment '$SHAPE_DIR/1.md' is grouped but fragment '$SHAPE_DIR/2.md' is not" \
changelog_shape_problem "$SHAPE_CHANGELOG" "$SHAPE_DIR"
rm "$SHAPE_DIR/1.md" "$SHAPE_DIR/2.md"
check "shape: empty fragment set with a valid sentinel passes" 0 "" \
changelog_shape_problem "$SHAPE_CHANGELOG" "$SHAPE_DIR"
printf 'Grouped\n' >"$SHAPE_DIR/shape"
check "shape: a capitalized sentinel is refused, file named" 1 \
"'$SHAPE_DIR/shape' declares neither shape" \
changelog_shape_problem "$SHAPE_CHANGELOG" "$SHAPE_DIR"
: >"$SHAPE_DIR/shape"
check "shape: an empty sentinel is refused — never a silent fallback" 1 \
"'$SHAPE_DIR/shape' declares neither shape" \
changelog_shape_problem "$SHAPE_CHANGELOG" "$SHAPE_DIR"
printf 'grouped\nflat\n' >"$SHAPE_DIR/shape"
check "shape: a two-line sentinel is refused" 1 \
"'$SHAPE_DIR/shape' declares neither shape" \
changelog_shape_problem "$SHAPE_CHANGELOG" "$SHAPE_DIR"
# The sentinel is not a fragment (#182 D3): the *.md glob is the mechanism,
# but the assertion is on the list itself, so a glob change cannot silently
# start publishing the sentinel.
printf 'grouped\n' >"$SHAPE_DIR/shape"
cat >"$SHAPE_DIR/1.md" <<'EOF'
### Fixed
- Grouped fragment.
EOF
assert_fragments_exclude_sentinel() {
local out
out="$(changelog_fragments "$SHAPE_DIR")"
[ -n "$out" ] || { echo "wanted a non-empty fragment list"; return 1; }
if printf '%s\n' "$out" | grep -q '/shape$'; then
printf 'the sentinel leaked into the fragment list:\n%s\n' "$out"
return 1
fi
}
check "fragments: the shape sentinel never enters the fragment list" 0 "" \
assert_fragments_exclude_sentinel
rm "$SHAPE_DIR/1.md" "$SHAPE_DIR/shape"
AS="$TMP/assemble-sentinel"
mkdir -p "$AS"
printf 'grouped\n' >"$AS/shape"
cat >"$AS/30.md" <<'EOF'
### Fixed
- Fixed thirty.
EOF
cat >"$AS/31.md" <<'EOF'
### Added
- Added thirty-one.
EOF
check "assemble: the sentinel never assembles, and canonical order holds under it" 0 "" \
assert_assemble "$AS" $'### Added\n\n- Added thirty-one.\n\n### Fixed\n\n- Fixed thirty.'
rm "$AS/30.md" "$AS/31.md"
printf -- '- Flat probe.\n' >"$AS/29.md"
check "assemble: a flat set under a 'grouped' sentinel refuses to assemble" 1 \
"declares grouped" \
changelog_assemble "$AS"
summary