From 4af339a312fac23cfae7f307d025c83203fa4e4b Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Sat, 27 Jun 2026 03:22:22 +0200 Subject: [PATCH] =?UTF-8?q?test(lib):=20doc-commit=20behavioral=20check=20?= =?UTF-8?q?=E2=80=94=20coupled=20+=20fail-closed=20scenarios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lib/tests/run-doc-behavioral.md — in-vivo whole-chain check (twin of run-behavioral.md for memory). Scenario A: doc-syncer patches a public doc, the include commits it surgically with dangling code present (coupled + surgical). Scenario B: a forbidden .claude/ path in PATCHED_FILES → helper refuses (rc 4), nothing half-committed, offender named (fail-closed + loud). Complements the 28-assertion deterministic suite (run-doc-commit.sh). Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Ho5EQCFTSvYamuRtVZpp2d --- lib/tests/run-doc-behavioral.md | 66 +++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 lib/tests/run-doc-behavioral.md diff --git a/lib/tests/run-doc-behavioral.md b/lib/tests/run-doc-behavioral.md new file mode 100644 index 0000000..65c6a69 --- /dev/null +++ b/lib/tests/run-doc-behavioral.md @@ -0,0 +1,66 @@ +# Behavioral check — doc-sync coupled, end-to-end + +The deterministic suite (`run-doc-commit.sh`, T1–T7) proves `doc-commit.sh` in +isolation. This is the in-vivo whole-chain check: a real dev-flow shape — code +commit, then doc-syncer patches public docs, then the include commits them — with +dangling code AND a forbidden `.claude/` path present, proving the doc commit is +coupled, surgical, AND fail-closed on an upstream scope violation. + +## Scenario A — coupled + surgical (the happy path) + +```bash +R="$(mktemp -d)"; cd "$R" +git init -q && git config user.email t@t.t && git config user.name t +mkdir -p .claude/memory docs src +printf '# Proj\n' > README.md +printf 'baseline\n' > .claude/memory/decisions.md +git add -A && git commit -qm baseline + +# 1) the flow commits CODE +printf 'feature code\n' > src/feature.txt +git add -- src/feature.txt && git commit -qm "feat: the feature" + +# 2) doc-syncer patches public docs (a modified README + a created docs page). +# It would surface PATCHED_FILES, ONE PATH PER LINE: +# README.md +# docs/usage.md +printf '\n## New feature\nUse --export.\n' >> README.md +printf 'usage guide\n' > docs/usage.md + +# 3) a code file is left dangling (must NOT be embarked) +printf 'WIP do not commit\n' > src/dangling.txt + +# 4) the include passes EACH PATCHED_FILES line as a SEPARATE arg (argv, space-safe) +doc_hash="$(bash "$HOME/.claude/lib/doc-commit.sh" commit "docs: README + usage" "README.md" "docs/usage.md")" +``` + +### Expected (assert) +- Exactly TWO commits after baseline: the code commit, then the doc commit. +- The doc commit (`$doc_hash`) contains ONLY `README.md` + `docs/usage.md` — never + `src/feature.txt` (already committed) or `src/dangling.txt` (WIP). +- `src/dangling.txt` is still untracked after the doc commit. +- No `.claude/**` path in the doc commit (doc-syncer never patches it; the helper + guards it regardless). + +## Scenario B — fail-closed guard (the upstream-anomaly path) + +```bash +# A bug upstream surfaces a forbidden path in PATCHED_FILES (doc-syncer must never +# patch .claude/ — BDR-022). The include passes it through; the helper must REFUSE. +printf 'x\n' >> .claude/memory/decisions.md # make the forbidden path dirty +printf '\n## later\n' >> README.md # a legit doc also changed +bash "$HOME/.claude/lib/doc-commit.sh" commit "docs: mixed" "README.md" ".claude/memory/decisions.md" +echo "rc=$?" +``` + +### Expected (assert) +- `rc=4` (scope violation), NOTHING committed — `README.md` is NOT half-committed. +- stderr is loud (`REFUSED …`) and NAMES the offender (`.claude/memory/decisions.md`). +- The include treats rc 4 as an upstream BDR-022 anomaly to investigate — not a + silent skip. The refusal IS the alarm. + +If Scenario A holds, the chain is coupled (docs committed in the same breath as the +flow) and surgical (no dangling code embarked). If Scenario B holds, the guard is +fail-closed and loud. This mirrors what feat / bugfix / hotfix do at their DOC SYNC +step (inline-branch commit, no FINISH), and what ship-feature / init-project do at +their DOC SYNC step BEFORE FINISH (so the doc commit reaches the merge/PR).