From f033defa9d51d1fb831e2f3fdea3fde0d7d209e3 Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Mon, 6 Jul 2026 19:22:52 +0200 Subject: [PATCH] job4: SPEC-07 doc-shape-removed-envelope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New S11-S13 in lib/tests/run-doc-shape.sh (+4 assertions, 19→23) + truncate_last_n() helper (removes exactly N lines from the END of a committed file — pure removal, 0 added lines, no heading, so the ADDED-envelope and heading checks at doc-shape.sh:70/78 can't fire first). Baseline = 40 plain committed lines, then truncated. S11 remove exactly 20 (== default DOC_SHAPE_MAX_REMOVED, `-gt` boundary) → within (0). S12 remove 30 → exceeds (1), stderr names the path. S13 DOC_SHAPE_MAX_REMOVED=5 override + 6-line removal → exceeds (1). Closes J4-08 (WEAK): the REMOVED branch was never driven over threshold by any existing case (S4 only removes 2 lines) — a regression here mislabels a large doc deletion MINOR and doc-syncer's auto-commit flow would swallow it silently (the exact RISK-1 BDR-040's oracle exists for). Mutation (lean scratch copy — only doc-shape.sh + run-doc-shape.sh, not the whole repo/.git): changed `-gt "$DOC_SHAPE_MAX_REMOVED"` to `-gt 2000` (doc-shape.sh:82). RED: S12 fails both assertions (30 removed no longer exceeds) and S13 fails (the hardcoded literal also kills the env-override contract — DOC_SHAPE_MAX_REMOVED=5 no longer has any effect). S11 stays green (20 removed was always within, mutation-invariant). 3/3 reds land exactly where expected. GREEN: real repo unmutated, 23/23 passed, shellcheck clean. --- lib/tests/run-doc-shape.sh | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lib/tests/run-doc-shape.sh b/lib/tests/run-doc-shape.sh index 3a536e0..1b6f037 100644 --- a/lib/tests/run-doc-shape.sh +++ b/lib/tests/run-doc-shape.sh @@ -51,6 +51,15 @@ append_lines() { for ((i = 1; i <= n; i++)); do printf 'extra line %s\n' "$i" >>"$f"; done } +# Remove exactly N lines from the END of a committed file (pure removal, 0 +# added lines, no heading) — for the REMOVED-envelope tests (S11-S13). +truncate_last_n() { + local f="$1" n="$2" total keep + total=$(wc -l <"$f") + keep=$((total - n)) + head -n "$keep" "$f" >"$f.tmp" && mv "$f.tmp" "$f" +} + # run [ENV=val] → sets RC (exit), OUT (stdout), ERR (stderr). # stdout MUST stay empty: the exit code carries the verdict, reasons go to stderr. run() { @@ -160,6 +169,37 @@ printf ' rc=%s\n' "$RC" if [ "$RC" -eq 3 ]; then ok "not-a-repo → 3"; else ko "expected 3, got $RC"; fi rm -rf "$D" +echo "S11 — remove exactly 20 lines (== threshold, pure removal) → within (0, boundary)" +R="$(new_repo)" +: >"$R/README.md"; append_lines "$R/README.md" 40 +git -C "$R" add README.md; git -C "$R" commit -qm "baseline 40 lines" +truncate_last_n "$R/README.md" 20 +run "$R" check "README.md" +printf ' rc=%s\n' "$RC" +if [ "$RC" -eq 0 ]; then ok "removed 20 (== MAX) → within (0)"; else ko "expected 0, got $RC"; fi +rm -rf "$R" + +echo "S12 — remove 30 lines (pure removal) → exceeds (1, size)" +R="$(new_repo)" +: >"$R/README.md"; append_lines "$R/README.md" 40 +git -C "$R" add README.md; git -C "$R" commit -qm "baseline 40 lines" +truncate_last_n "$R/README.md" 30 +run "$R" check "README.md" +printf ' rc=%s err=%s\n' "$RC" "$(printf '%s' "$ERR" | head -1)" +if [ "$RC" -eq 1 ]; then ok "removed 30 → exceeds (1)"; else ko "expected 1, got $RC"; fi +if printf '%s' "$ERR" | grep -q 'README.md'; then ok "stderr names the offending path"; else ko "offender not named"; fi +rm -rf "$R" + +echo "S13 — DOC_SHAPE_MAX_REMOVED=5 + 6-line removal → exceeds (1, env-tunable)" +R="$(new_repo)" +: >"$R/README.md"; append_lines "$R/README.md" 40 +git -C "$R" add README.md; git -C "$R" commit -qm "baseline 40 lines" +truncate_last_n "$R/README.md" 6 +OUT="$( (cd "$R" && DOC_SHAPE_MAX_REMOVED=5 "$HELPER" check "README.md") 2>"$ERRFILE" )"; RC=$? +printf ' rc=%s\n' "$RC" +if [ "$RC" -eq 1 ]; then ok "override MAX_REMOVED=5, 6 removed → exceeds (1)"; else ko "expected 1, got $RC"; fi +rm -rf "$R" + rm -f "$ERRFILE" echo "" printf 'RESULT: %d passed, %d failed\n' "$PASS" "$FAIL"