@ccoalm/ccl-skills 0.18.10 → 0.18.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/assets/marketplace/plugins/ccl-skills/hooks/headless-background-stop.sh +18 -0
  2. package/dist/assets/marketplace/plugins/ccl-skills/hooks/hooks.json +7 -0
  3. package/dist/assets/marketplace/plugins/ccl-skills/hooks/host-input.py +155 -8
  4. package/dist/assets/marketplace/plugins/ccl-skills/hooks/remind-post-merge-cleanup.sh +9 -4
  5. package/dist/assets/marketplace/plugins/ccl-skills/hooks/task-entry.sh +24 -3
  6. package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_headless_background_stop.sh +150 -0
  7. package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_proposed_next.py +71 -0
  8. package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_remind_post_merge_cleanup.sh +47 -0
  9. package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_task_entry.py +63 -0
  10. package/dist/assets/marketplace/plugins/ccl-skills/packages/opencode-plugin/ccl-skills.ts +7 -1
  11. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/development-completion.md +2 -2
  12. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/review_gate.py +77 -6
  13. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_gate.sh +112 -0
  14. package/dist/assets/marketplace/plugins/ccl-skills/skills/multi-agent-delegation/references/multi-agent-delegation-playbook.md +1 -0
  15. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/pre-final-continuation-gate.md +4 -0
  16. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/worktree-mechanics.md +10 -3
  17. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/attention-budget-ratchet.md +2 -2
  18. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/external-practice-controls.md +6 -1
  19. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/extraction-lifecycle-handoff.md +1 -1
  20. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/harness-patterns-and-eval.md +2 -0
  21. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md +15 -0
  22. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-sync-pointers.sh +11 -4
  23. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/skill-paired-eval.py +1546 -0
  24. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_ai_coding_implementation_gates.sh +140 -1
  25. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_regressions.sh +4 -0
  26. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_sync_pointers.sh +13 -4
  27. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_controlled_escalation_pins.sh +12 -1
  28. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_skill_paired_eval.py +1052 -0
  29. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_teardown_guard_pins.sh +354 -0
  30. package/dist/assets/marketplace/plugins/ccl-skills/skills/worktree-isolation/SKILL.md +8 -108
  31. package/dist/assets/marketplace/plugins/ccl-skills/skills/worktree-isolation/references/merge-and-teardown.md +47 -0
  32. package/dist/assets/marketplace/plugins/ccl-skills/skills/worktree-isolation/references/pre-merge-landing-checks.md +73 -0
  33. package/dist/assets/marketplace/plugins/ccl-skills/skills/worktree-isolation/references/shared-branch-rebase.md +1 -1
  34. package/dist/assets/marketplace/plugins/ccl-skills/skills/worktree-isolation/scripts/test_worktree_sweep.sh +1 -1
  35. package/dist/assets/release.json +65 -30
  36. package/package.json +1 -1
@@ -270,7 +270,7 @@ assert_in_section "$PRE_FINAL_REF" '## Gate triggers and outcome contract' 'cite
270
270
  assert_same_line "$REPO_ROOT/AGENTS.md" '授权按用户已明确的交付目标判断' '只授权单项、只问状态、明确停止或限制范围时遵守该边界' "goal authority (root scope and stop)"
271
271
  assert_contains "$REPO_ROOT/docs/npm-release.md" 'Do not ask again for each prerequisite' "goal authority (release follows through)"
272
272
  assert_contains "$REPO_ROOT/skills/release-coordination/SKILL.md" 'Existing host permission checks and resource-owner requirements still apply' "goal authority (host and resource boundary)"
273
- assert_contains "$REPO_ROOT/skills/worktree-isolation/SKILL.md" '目标/批量授权内由 agent 完成的修复、新提交或新建 MR,先刷新检查、评审与状态,不重复请求权限' "goal authority (repair refreshes evidence)"
273
+ assert_contains "$REPO_ROOT/skills/worktree-isolation/references/merge-and-teardown.md" '目标/批量授权内由 agent 完成的修复、新提交或新建 MR,先刷新检查、评审与状态,不重复请求权限' "goal authority (repair refreshes evidence)"
274
274
  assert_contains "$REPO_ROOT/skills/product-rd-workflow/references/refactoring-discipline.md" 'do not ask again merely because it involves refactoring' "quality gate (authorized cleanup)"
275
275
  assert_contains "$REPO_ROOT/skills/product-rd-workflow/references/refactoring-discipline.md" 'Do not abbreviate meaningful names, remove necessary explanations, pack statements, fragment responsibilities arbitrarily, or change the threshold/history just to satisfy a counter.' "quality gate (readability and metric integrity)"
276
276
  assert_contains "$REPO_ROOT/skills/product-rd-workflow/references/refactoring-discipline.md" 'Broader redesign and breaking changes retain their scope and approval checks.' "quality gate (scope and compatibility boundary)"
@@ -871,4 +871,143 @@ assert_contains "$DELEGATION_SKILL" 'method/evidence checkpoint, not renewed tas
871
871
  assert_contains "$DELEGATION_SKILL" 'unknown completion state or missing authority remains after bounded remediation' "delegation preserves unresolved blockers"
872
872
  assert_contains "$DELEGATION_SKILL" 'respecting explicit user limits' "delegation preserves user limits"
873
873
 
874
+ # 9. Worktree teardown guard on every surface that restates removal (round 162).
875
+ # `git worktree remove` without --force deletes gitignored files and exits 0, so
876
+ # the pre-removal scan is the only guard for costly outputs. Each surface that
877
+ # restates the removal carries the scan, its success condition and a pointer to
878
+ # the canonical teardown section; the sweep below requires the same of any
879
+ # Markdown surface added later. Rows are data so test_teardown_guard_pins.sh can
880
+ # apply one mutation per row: kind|repo-relative file|scope|phrase|label, where
881
+ # scope is a section heading (section, order) or the anchor of the line that
882
+ # must carry the phrase (line); an order row's phrase reads `first ⟶ second`,
883
+ # both inside the section and in that order.
884
+ assert_order_in_section() {
885
+ local file="$1" heading="$2" pair="$3" label="$4"
886
+ local first="${pair%% ⟶ *}" second="${pair#* ⟶ }"
887
+ [[ "$first" != "$pair" && -n "$first" && -n "$second" ]] || fail "$label: order row must read 'first ⟶ second'"
888
+ [[ -f "$file" ]] || fail "$label: missing file $file"
889
+ local section a b
890
+ section="$(awk -v h="$heading" '$0 == h {inside = 1; next} inside && /^#/ {exit} inside {print}' "$file")"
891
+ [[ -n "$section" ]] || fail "$label: section not found or empty: $heading"
892
+ a="$(grep -n -F -m1 -- "$first" <<< "$section")" || fail "$label: \`$first\` absent from section $heading"
893
+ b="$(grep -n -F -m1 -- "$second" <<< "$section")" || fail "$label: \`$second\` absent from section $heading"
894
+ (( ${a%%:*} < ${b%%:*} )) || fail "$label: \`$first\` must come before \`$second\` in section $heading"
895
+ }
896
+
897
+ TEARDOWN_PINS="$(cat <<'PINS'
898
+ section|skills/product-rd-workflow/references/worktree-mechanics.md|## Closeout Cleanup|the closeout section (`## 收尾`) of `worktree-isolation/references/merge-and-teardown.md`|product-rd cleanup names the canonical teardown
899
+ section|skills/product-rd-workflow/references/worktree-mechanics.md|## Closeout Cleanup|you must run `git -C <path> status --ignored -s` from the primary checkout|product-rd cleanup scans gitignored outputs first
900
+ section|skills/product-rd-workflow/references/worktree-mechanics.md|## Closeout Cleanup|It must exit 0; a failed scan counts as no scan|product-rd cleanup treats a failed scan as no scan
901
+ section|skills/product-rd-workflow/references/worktree-mechanics.md|## Closeout Cleanup|copy costly ones (long-running results, collected data, trained artifacts) back to the primary checkout before removal|product-rd cleanup rescues costly outputs
902
+ section|skills/product-rd-workflow/references/worktree-mechanics.md|## Closeout Cleanup|When unsure, treat an entry as costly|product-rd cleanup treats uncertain entries as costly
903
+ section|skills/product-rd-workflow/references/worktree-mechanics.md|## Closeout Cleanup|wait for it to finish; never kill it to clean up|product-rd cleanup waits for in-flight side effects
904
+ section|skills/product-rd-workflow/references/worktree-mechanics.md|## Closeout Cleanup|Never pass `--force` to `git worktree remove` or use `git branch -D`|product-rd cleanup forbids forced removal
905
+ section|skills/product-rd-workflow/references/worktree-mechanics.md|## Closeout Cleanup|Never delete a permanent or integration branch, or any branch whose name contains `release`|product-rd cleanup keeps permanent and release branches
906
+ order|skills/product-rd-workflow/references/worktree-mechanics.md|## Closeout Cleanup|status --ignored -s # must exit 0 ⟶ git worktree remove <path>|product-rd recipe scans before it removes
907
+ line|skills/multi-agent-delegation/references/multi-agent-delegation-playbook.md|Every worktree removal below must run the pre-removal scan|`worktree-isolation/references/merge-and-teardown.md`|delegation cleanup names the canonical teardown
908
+ line|skills/multi-agent-delegation/references/multi-agent-delegation-playbook.md|Every worktree removal below must run the pre-removal scan|`git -C <worktree-path> status --ignored -s` must exit 0, and a failed scan counts as no scan|delegation cleanup scans gitignored outputs first
909
+ line|skills/multi-agent-delegation/references/multi-agent-delegation-playbook.md|Every worktree removal below must run the pre-removal scan|Copy costly gitignored outputs the worker produced (long-running results, collected data, trained artifacts) into the target checkout before removal|delegation cleanup rescues costly outputs
910
+ line|skills/multi-agent-delegation/references/multi-agent-delegation-playbook.md|Every worktree removal below must run the pre-removal scan|Record the scan exit status and each kept or dropped entry in `cleanup_proof`|delegation cleanup records the scan in cleanup_proof
911
+ order|skills/multi-agent-delegation/references/multi-agent-delegation-playbook.md|## Handoff-Style Delivery Recipe|Every worktree removal below must run the pre-removal scan ⟶ For local-commit-only handoff|delegation scan precedes the removal paths
912
+ line|skills/skill-extraction-workflow/references/extraction-lifecycle-handoff.md|**Isolate each change with a worktree, not a clone.**|`worktree-isolation/references/merge-and-teardown.md`|extraction worktree names the canonical teardown
913
+ line|skills/skill-extraction-workflow/references/extraction-lifecycle-handoff.md|**Isolate each change with a worktree, not a clone.**|you must scan its gitignored outputs before removal (`git -C <worktree> status --ignored -s`, which must exit 0)|extraction worktree scans before removal
914
+ line|skills/skill-extraction-workflow/references/extraction-lifecycle-handoff.md|**Isolate each change with a worktree, not a clone.**|copy any output that is costly to recreate back to the standing checkout|extraction worktree rescues costly outputs
915
+ line|skills/worktree-isolation/references/merge-and-teardown.md|**任何方式删除单个 worktree 目录之前**|都必须先 `git -C <worktree路径> status --ignored -s` 扫 gitignored 产物|canonical scan precedes any removal
916
+ line|skills/worktree-isolation/references/merge-and-teardown.md|**任何方式删除单个 worktree 目录之前**|① 该命令**必须 exit 0**|canonical scan must succeed
917
+ line|skills/worktree-isolation/references/merge-and-teardown.md|**任何方式删除单个 worktree 目录之前**|② 输出非空即逐条判定保留/丢弃并向用户列出结论|canonical lists each kept or dropped entry
918
+ line|skills/worktree-isolation/references/merge-and-teardown.md|**任何方式删除单个 worktree 目录之前**|③ 判据看**重算代价**|canonical judges by recompute cost
919
+ line|skills/worktree-isolation/references/merge-and-teardown.md|**任何方式删除单个 worktree 目录之前**|宿主原生移除如 Claude Code `ExitWorktree` 的 remove|canonical scan covers host-native removal
920
+ line|skills/worktree-isolation/references/merge-and-teardown.md|**交互式 merge 选项菜单**|删前照样先按上文扫 gitignored 产物|canonical handoff keeps the scan for external finishing skills
921
+ order|skills/worktree-isolation/references/merge-and-teardown.md|## 收尾:worktree 一集成就清理(本地 + 远端,不留垃圾)|git -C <path> status --ignored -s # sweep 之外 ⟶ git worktree remove <path> # 删本地|canonical recipe scans before it removes
922
+ PINS
923
+ )"
924
+ teardown_rows=0
925
+ while IFS='|' read -r kind rel scope phrase label extra; do
926
+ [[ -n "$kind" ]] || continue
927
+ [[ -z "$extra" && -n "$label" ]] || fail "teardown guard: malformed pin row: $kind|$rel|$scope"
928
+ teardown_rows=$((teardown_rows + 1))
929
+ case "$kind" in
930
+ section) assert_in_section "$REPO_ROOT/$rel" "$scope" "$phrase" "teardown guard: $label" ;;
931
+ line) assert_same_line "$REPO_ROOT/$rel" "$scope" "$phrase" "teardown guard: $label" ;;
932
+ order) assert_order_in_section "$REPO_ROOT/$rel" "$scope" "$phrase" "teardown guard: $label" ;;
933
+ *) fail "teardown guard: unknown pin kind $kind in row $label" ;;
934
+ esac
935
+ done <<< "$TEARDOWN_PINS"
936
+ (( teardown_rows >= 20 )) || fail "teardown guard: only $teardown_rows pin rows parsed"
937
+
938
+ # Sweep: a Markdown surface that names `worktree remove` must also carry the
939
+ # ignored-output scan with its exit-0 requirement on the same line, and a
940
+ # pointer to the canonical teardown, `worktree-isolation/references/merge-and-teardown.md`
941
+ # (inside that package the package-relative path; the canonical file is itself
942
+ # the target). It keys on the command because prose wording is not ours to
943
+ # enumerate; prose-only removal steps are pinned per surface above. The set is
944
+ # the repository's own Markdown as git lists it, tracked plus untracked files
945
+ # git does not ignore, so local build output and caches stay out; outside a git
946
+ # work tree there is no such set and the sweep fails rather than guess one.
947
+ # specs/ and eval/ hold round records and evaluation inputs, not guidance, and
948
+ # the append-only source register describes the defects it records, so none of
949
+ # them is scanned. Anything git reports while listing, an unreadable file or a
950
+ # failed classification fails the sweep, since a file it skipped would
951
+ # otherwise pass unseen.
952
+ teardown_list="$(mktemp "${TMPDIR:-/tmp}/teardown-sweep.XXXXXX")"
953
+ trap 'rm -f "$teardown_list"' EXIT
954
+ teardown_fail() { rm -f "$teardown_list"; fail "teardown guard sweep: $1"; }
955
+ [[ "$(git -C "$REPO_ROOT" rev-parse --show-toplevel 2>/dev/null)" == "$REPO_ROOT" ]] ||
956
+ teardown_fail "not a git work tree, so the repository's Markdown cannot be listed"
957
+ teardown_errors="$(git -C "$REPO_ROOT" ls-files -z --cached --others --exclude-standard -- '*.md' 2>&1 >"$teardown_list")" ||
958
+ teardown_fail "could not list the repository's Markdown: $teardown_errors"
959
+ [[ -z "$teardown_errors" ]] || teardown_fail "git could not list every file: $teardown_errors"
960
+ # One pass reads every listed file (a grep per file made this sweep the slowest
961
+ # part of the fixture, which the pin walks run dozens of times). Its exit status
962
+ # carries the verdict; the printed text only names what it found.
963
+ teardown_rc=0
964
+ teardown_report="$(python3 - "$teardown_list" "$REPO_ROOT" <<'PY'
965
+ import sys
966
+ listing, root = sys.argv[1:3]
967
+ package = root + "/skills/worktree-isolation/"
968
+ canonical = package + "references/merge-and-teardown.md"
969
+ register = root + "/skills/skill-extraction-workflow/references/source-register.md"
970
+ def shown(name):
971
+ return "".join(c if c.isprintable() else repr(c)[1:-1] for c in name)
972
+ visited = False
973
+ offenders = []
974
+ for raw in open(listing, "rb").read().split(b"\0"):
975
+ if not raw:
976
+ continue
977
+ rel = raw.decode("utf-8", "surrogateescape")
978
+ path = root + "/" + rel
979
+ if rel.startswith(("specs/", "eval/")) or path == register:
980
+ continue
981
+ try:
982
+ text = open(path, "rb").read().decode("utf-8", "replace")
983
+ except OSError:
984
+ print(shown(rel))
985
+ sys.exit(5)
986
+ if "worktree remove" not in text:
987
+ continue
988
+ visited = visited or path == canonical
989
+ pointer = "references/merge-and-teardown.md" if path.startswith(package) else "worktree-isolation/references/merge-and-teardown.md"
990
+ if "status --ignored" not in text:
991
+ offenders.append(shown(rel) + ": no ignored-output scan")
992
+ elif not any("status --ignored" in line and "exit 0" in line for line in text.splitlines()):
993
+ offenders.append(shown(rel) + ": no exit-0 requirement on the scan line")
994
+ elif path != canonical and pointer not in text:
995
+ offenders.append(shown(rel) + ": no pointer to the canonical teardown")
996
+ if offenders:
997
+ print("; ".join(offenders))
998
+ sys.exit(3)
999
+ if not visited:
1000
+ sys.exit(4)
1001
+ PY
1002
+ )" || teardown_rc=$?
1003
+ rm -f "$teardown_list"
1004
+ trap - EXIT
1005
+ case "$teardown_rc" in
1006
+ 0) ;;
1007
+ 3) fail "teardown guard sweep: $teardown_report" ;;
1008
+ 4) fail "teardown guard sweep: the canonical teardown was never scanned, so the listing is wrong" ;;
1009
+ 5) fail "teardown guard sweep: could not read $teardown_report" ;;
1010
+ *) fail "teardown guard sweep: the classification pass failed" ;;
1011
+ esac
1012
+
874
1013
  echo "test_ai_coding_implementation_gates: ok"
@@ -13,6 +13,7 @@
13
13
  # - test_ai_coding_implementation_gates.sh
14
14
  # - test_locale_independent_gates.sh
15
15
  # - test_controlled_escalation_pins.sh
16
+ # - test_teardown_guard_pins.sh
16
17
  # - test_check_ccl_size_budget.sh
17
18
  # - test_check_ccl_skill_catalog.sh
18
19
  # - test_check_ccl_parallel_stack_parity.sh
@@ -113,6 +114,9 @@ fast_tests=(
113
114
  # family 8 out of the fixture above and proves each pin reds under its own
114
115
  # applied deletion mutation in a throwaway copy (spec 031 review disposition).
115
116
  test_controlled_escalation_pins.sh
117
+ # Applied-mutation walk for the worktree teardown guard rows (family 9 of the
118
+ # fixture above) plus decoy surfaces for its sweep; throwaway copy, seconds.
119
+ test_teardown_guard_pins.sh
116
120
  test_check_ccl_size_budget.sh
117
121
  # Entrypoint guidance-form census (what counts as one rule, a prohibition, a
118
122
  # named baseline failure): synthetic fixtures plus the shipped entrypoint,
@@ -61,7 +61,7 @@ git -C "$REPO" config user.name "Test User"
61
61
  git -C "$REPO" branch -f fixture-base HEAD
62
62
  git -C "$REPO" switch -q -C fixture-work fixture-base
63
63
  git -C "$REPO" branch --set-upstream-to=fixture-base fixture-work >/dev/null 2>&1
64
- WT="$REPO/skills/worktree-isolation/SKILL.md"
64
+ WT="$REPO/skills/worktree-isolation/references/merge-and-teardown.md"
65
65
  SEC4="$REPO/skills/requirement-doc-writer/references/security-four-questions.md"
66
66
  DTREF="$REPO/skills/skill-extraction-workflow/references/dual-track-review-gate.md"
67
67
 
@@ -77,7 +77,7 @@ replace_in_file "$WT" "合并执行协议(canonical" "合并落地协议(can
77
77
  run_sync
78
78
  assert_rc "$rc" 1 "reworded canonical section must block"
79
79
  assert_contains "sync_pointer_block: merge-exec-protocol-section" "$out" "block names the pair"
80
- git -C "$REPO" checkout -- skills/worktree-isolation/SKILL.md
80
+ git -C "$REPO" checkout -- skills/worktree-isolation/references/merge-and-teardown.md
81
81
 
82
82
  # s2 RED: section heading renamed => 收尾节 pointer dangles (the pinned
83
83
  # literal carries the colon so a longer heading cannot substring-satisfy it).
@@ -85,6 +85,15 @@ replace_in_file "$WT" "## 收尾:" "## 收尾清理:"
85
85
  run_sync
86
86
  assert_rc "$rc" 1 "renamed teardown heading must block"
87
87
  assert_contains "sync_pointer_block: worktree-teardown-section" "$out" "block names the pair"
88
+ git -C "$REPO" checkout -- skills/worktree-isolation/references/merge-and-teardown.md
89
+
90
+ # s2b RED: the entrypoint's forwarding sentence removed => the always-on
91
+ # pointer still names the skill, but nothing in SKILL.md leads to the
92
+ # reference that carries the teardown section and the merge protocol.
93
+ replace_in_file "$REPO/skills/worktree-isolation/SKILL.md" "都在 \`references/merge-and-teardown.md\`" "见各 reference"
94
+ run_sync
95
+ assert_rc "$rc" 1 "a removed forwarding sentence must block"
96
+ assert_contains "sync_pointer_block: worktree-reference-forwarding" "$out" "block names the forwarding pair"
88
97
  git -C "$REPO" checkout -- skills/worktree-isolation/SKILL.md
89
98
 
90
99
  # s3 RED: bootstrap side reworded => pointer literal lost on the source side.
@@ -314,7 +323,7 @@ replace_in_file "$WT" "**合并执行协议(canonical——" "(既往)合
314
323
  run_sync
315
324
  assert_rc "$rc" 1 "a demoted-to-narrative section label must block"
316
325
  assert_contains "sync_pointer_block: merge-exec-protocol-section" "$out" "block names the pair"
317
- git -C "$REPO" checkout -- skills/worktree-isolation/SKILL.md
326
+ git -C "$REPO" checkout -- skills/worktree-isolation/references/merge-and-teardown.md
318
327
 
319
328
  # s24 RED: a compliant decoy line inserted before the real reflection makes
320
329
  # the extraction anchor ambiguous (scan requires exactly one hit).
@@ -358,7 +367,7 @@ run_sync
358
367
  assert_rc "$rc" 1 "two-sided drift must block"
359
368
  assert_contains "agent-context/session-start.md lost the registered pointer literal" "$out" "source side reported"
360
369
  assert_contains "canonical target" "$out" "target side reported in the same run"
361
- git -C "$REPO" checkout -- agent-context/session-start.md skills/worktree-isolation/SKILL.md
370
+ git -C "$REPO" checkout -- agent-context/session-start.md skills/worktree-isolation/references/merge-and-teardown.md
362
371
 
363
372
  # s29a: the semantic gate resolves beside the validator, so pruning the IN-TREE
364
373
  # copy changes nothing — on a clean tree the validator still evaluates with its
@@ -11,6 +11,11 @@
11
11
  # tree. The pin list is parsed from the fixture itself so a new pin cannot be
12
12
  # added without automatically entering this walk.
13
13
  set -euo pipefail
14
+ # The copy gets its own repository; inherited git variables (set inside git
15
+ # hooks, for one) would point git at the real one instead.
16
+ unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE GIT_OBJECT_DIRECTORY \
17
+ GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_COMMON_DIR GIT_CEILING_DIRECTORIES \
18
+ GIT_NAMESPACE
14
19
 
15
20
  script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
16
21
  repo_root="$(cd "$script_dir/../../.." && pwd -P)"
@@ -27,6 +32,12 @@ cp -R "$repo_root/skills" "$tmp_root/skills"
27
32
  cp -R "$repo_root/agent-context" "$tmp_root/agent-context"
28
33
  cp -R "$repo_root/docs" "$tmp_root/docs"
29
34
  cp "$repo_root/AGENTS.md" "$tmp_root/AGENTS.md"
35
+ # The fixture's teardown sweep lists the repository's Markdown through git, so
36
+ # the copy needs an index; nothing is committed, which keeps user commit hooks
37
+ # and signing out of the run.
38
+ cp "$repo_root/.gitignore" "$tmp_root/.gitignore"
39
+ git -C "$tmp_root" init -q
40
+ git -C "$tmp_root" add -A
30
41
  copy_fixture="$tmp_root/$fixture_rel"
31
42
  copy_ref="$tmp_root/$ref_rel"
32
43
  [[ -f "$copy_fixture" && -f "$copy_ref" ]] || fail "copy is missing the fixture or the reference"
@@ -63,7 +74,7 @@ pin_count="$(wc -l < "$pins_file" | tr -d ' ')"
63
74
  raw_count="$(grep -c 'assert_in_section "\$EXTRACTION_METHOD_REF" "\$BLOCKED_VERIFICATION_SECTION"' "$repo_root/$fixture_rel")"
64
75
  [[ "$pin_count" == "$raw_count" ]] || fail "parser dropped family-8 assertions: parsed $pin_count of $raw_count calls"
65
76
 
66
- run_copy() { bash "$copy_fixture" 2>&1; }
77
+ run_copy() { TMPDIR="$tmp_root" bash "$copy_fixture" 2>&1; }
67
78
 
68
79
  # Control: the unmutated copy is green, proving the harness reads the copy.
69
80
  control_out="$(run_copy)" || fail "pre-control not green: $control_out"