@ccoalm/ccl-skills 0.17.0 → 0.18.0

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 (34) hide show
  1. package/dist/assets/marketplace/plugins/ccl-skills/agent-context/session-start.md +8 -7
  2. package/dist/assets/marketplace/plugins/ccl-skills/hooks/hooks.json +22 -0
  3. package/dist/assets/marketplace/plugins/ccl-skills/hooks/remind-review-covers-head.sh +128 -0
  4. package/dist/assets/marketplace/plugins/ccl-skills/hooks/remind-untracked-background.sh +53 -0
  5. package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_remind_review_covers_head.sh +104 -0
  6. package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_remind_untracked_background.sh +75 -0
  7. package/dist/assets/marketplace/plugins/ccl-skills/packages/opencode-plugin/ccl-skills.ts +8 -0
  8. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/development-completion.md +13 -1
  9. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/staged-review-contract.md +5 -3
  10. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/review_gate.py +401 -21
  11. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_client_compat.py +8 -1
  12. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_gate.sh +230 -7
  13. package/dist/assets/marketplace/plugins/ccl-skills/skills/multi-perspective-research/SKILL.md +1 -1
  14. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/SKILL.md +1 -1
  15. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/SKILL.md +6 -6
  16. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/dual-track-review-gate.md +48 -119
  17. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/extraction-quickstart.md +9 -9
  18. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md +8 -0
  19. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/validation-and-landing.md +2 -2
  20. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check_review_evidence_present.py +122 -0
  21. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/contract-anchors.tsv +0 -1
  22. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/extraction_review_gate.sh +37 -8
  23. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/register-firing-path-resolution.rb +102 -20
  24. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_ai_coding_implementation_gates.sh +16 -27
  25. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_regressions.sh +3 -5
  26. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_review_evidence_present.sh +81 -0
  27. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_extraction_review_gate.sh +130 -310
  28. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_register_firing_path_wiring.sh +49 -3
  29. package/dist/assets/release.json +55 -45
  30. package/package.json +1 -1
  31. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/review_ledger_binding.py +0 -1242
  32. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_review_ledger_binding.sh +0 -978
  33. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_validate_extraction_review_state.sh +0 -1477
  34. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/validate_extraction_review_state.py +0 -1183
@@ -1821,7 +1821,7 @@ diff_alternate = root / "alternate.patch"
1821
1821
  diff_source.write_bytes(original_diff)
1822
1822
  diff_alternate.write_bytes(alternate_diff)
1823
1823
  with replace_after_symlink_check(diff_source, diff_alternate):
1824
- packet_path, digest, _candidate, _n, _, _ = review_gate.freeze_packet(
1824
+ packet_path, digest, _candidate, _n, _, _, _ = review_gate.freeze_packet(
1825
1825
  SimpleNamespace(
1826
1826
  cwd=str(root), diff_file=str(diff_source), base=None, paths=[]
1827
1827
  ),
@@ -1918,9 +1918,9 @@ check "diff, prior, and completion inputs are read once from a bounded opened de
1918
1918
 
1919
1919
  # The reviewer's packet and the landing candidate are two objects. A widened
1920
1920
  # packet exists so a reviewer can judge a claim against code outside the diff;
1921
- # the candidate exists so the merge-side binder can recompute what actually
1921
+ # the candidate exists so a caller can compare a receipt with what actually
1922
1922
  # lands. Aliasing them made the two mutually exclusive: widening produced a
1923
- # receipt the binder could never match. These assert the split and the one
1923
+ # receipt whose candidate no landing could match. These assert the split and the one
1924
1924
  # invariant that replaces the equality -- the candidate appears in the packet
1925
1925
  # verbatim, so nothing lands that its reviewer did not read.
1926
1926
  subject_packet_probe="$(
@@ -1995,7 +1995,7 @@ def expect_refused(label, **kwargs):
1995
1995
 
1996
1996
 
1997
1997
  # The base-derived subject: exactly what the landing binder recomputes.
1998
- subject_path, subject_hash, subject_candidate_hash, subject_n, subject_paths, _ = freeze()
1998
+ subject_path, subject_hash, subject_candidate_hash, subject_n, subject_paths, _, _ = freeze()
1999
1999
  subject_bytes = subject_path.read_bytes()
2000
2000
  subject_path.unlink()
2001
2001
  assert subject_hash == digest(subject_bytes)
@@ -2004,7 +2004,7 @@ assert subject_n == len(subject_bytes)
2004
2004
  assert subject_paths == ["landing.txt"], subject_paths
2005
2005
 
2006
2006
  # A7 -- with no --diff-file the two hashes are the same value, as they are today.
2007
- plain_path, plain_packet_hash, plain_candidate_hash, _plain_n, plain_paths, _ = freeze()
2007
+ plain_path, plain_packet_hash, plain_candidate_hash, _plain_n, plain_paths, _, _ = freeze()
2008
2008
  plain_path.unlink()
2009
2009
  assert plain_packet_hash == subject_hash
2010
2010
  assert plain_candidate_hash == subject_hash
@@ -2019,7 +2019,7 @@ context = (
2019
2019
  )
2020
2020
  widened = outside / "widened.patch"
2021
2021
  widened.write_bytes(subject_bytes + context)
2022
- wide_path, wide_packet_hash, wide_candidate_hash, wide_n, wide_paths, _ = freeze(
2022
+ wide_path, wide_packet_hash, wide_candidate_hash, wide_n, wide_paths, _, _ = freeze(
2023
2023
  diff_file=widened
2024
2024
  )
2025
2025
  try:
@@ -2076,7 +2076,7 @@ expect_refused("an unrelated packet was accepted", diff_file=unrelated)
2076
2076
 
2077
2077
  # A8 -- --diff-file alone keeps today's meaning: no base, so no subject, and
2078
2078
  # the candidate hash stays the packet's own hash.
2079
- alone_path, alone_packet_hash, alone_candidate_hash, _n, _, _ = freeze(
2079
+ alone_path, alone_packet_hash, alone_candidate_hash, _n, _, _, _ = freeze(
2080
2080
  diff_file=widened, base_ref=None
2081
2081
  )
2082
2082
  alone_path.unlink()
@@ -4359,6 +4359,11 @@ post_deadline_ok" ]'
4359
4359
  check "the staged contract declares the current result schema" \
4360
4360
  'grep -q "current result envelope is schema 3" "$DIR/../references/staged-review-contract.md"'
4361
4361
 
4362
+ # The merge-side ledger binder was retired; a contract that still says a script
4363
+ # recomputes the candidate at merge time promises a check nothing performs.
4364
+ check "the staged contract promises no retired merge-side binder" \
4365
+ '! grep -q "review_ledger_binding" "$DIR/../references/staged-review-contract.md"'
4366
+
4362
4367
  reset_case passed unavailable unavailable
4363
4368
  out="$(run_gate --timeout 600 --total-timeout 90)"; rc=$?
4364
4369
  claude_timeout="$(cat "$WORK/state/claude_timeout" 2>/dev/null || true)"
@@ -4737,6 +4742,224 @@ out="$(run_base_gate --allow-fallback-egress)"; rc=$?
4737
4742
  check "base packet freezes tracked and untracked changes once" \
4738
4743
  '[ "$rc" = 0 ] && grep -q tracked.txt "$WORK/state/claude_packet" && grep -q untracked.txt "$WORK/state/claude_packet" && grep -q "Untracked files" "$WORK/state/claude_packet"'
4739
4744
 
4745
+ # A review quotes the repository's own contract files after the candidate: the
4746
+ # tracked AGENTS.override.md-or-AGENTS.md, CLAUDE.md and .claude/CLAUDE.md of
4747
+ # every directory from the root down to a changed path, root first. Untracked
4748
+ # and CLAUDE.local.md files are personal and never quoted; a file off every
4749
+ # changed path is not quoted; a linked file is omitted, not followed.
4750
+ contract_repo="$WORK/contract-repo"
4751
+ rm -rf "$contract_repo"
4752
+ mkdir -p "$contract_repo/sub/deep" "$contract_repo/other" "$contract_repo/.claude" \
4753
+ "$contract_repo/linked" "$contract_repo/big"
4754
+ (
4755
+ cd "$contract_repo"
4756
+ git init -q
4757
+ git config user.email test@example.invalid
4758
+ git config user.name 'Test User'
4759
+ printf 'root agents rule\n' >AGENTS.md
4760
+ printf 'root claude rule\n' >.claude/CLAUDE.md
4761
+ printf 'personal note must not egress\n' >CLAUDE.local.md
4762
+ printf 'sub override rule\n' >sub/AGENTS.override.md
4763
+ printf 'sub shadowed rule\n' >sub/AGENTS.md
4764
+ printf 'sub claude rule\n' >sub/CLAUDE.md
4765
+ printf 'other rule\n' >other/AGENTS.md
4766
+ ln -s ../AGENTS.md linked/AGENTS.md
4767
+ python3 -c 'print("x" * 40000)' >big/AGENTS.md
4768
+ for dir in sub/deep other linked big; do printf 'before\n' >"$dir/code.txt"; done
4769
+ printf 'before\n' >.claude/notes.txt
4770
+ git add -A
4771
+ git commit -q -m initial
4772
+ printf 'untracked rule\n' >sub/deep/CLAUDE.md
4773
+ printf 'after\n' >sub/deep/code.txt
4774
+ )
4775
+ run_contract_gate() {
4776
+ REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
4777
+ --cwd "$contract_repo" --base HEAD --implementer-family openai \
4778
+ --review-plan-file "$WORK/review-plan.json" --allow-fallback-egress "$@"
4779
+ }
4780
+ contract_packet_check() { # <result json> <expected quoted paths, comma-joined> <coverage>
4781
+ JSON_PAYLOAD="$1" python3 - "$WORK/state/claude_packet" "$WORK/state/claude_profile" "$2" "$3" <<'PY' 2>&1
4782
+ import hashlib, json, os, re, sys
4783
+ result = json.loads(os.environ["JSON_PAYLOAD"])
4784
+ packet = open(sys.argv[1], "rb").read()
4785
+ profile = json.load(open(sys.argv[2], encoding="utf-8"))
4786
+ expected = [item for item in sys.argv[3].split(",") if item]
4787
+ quoted = re.findall(rb"^CCL_REPOSITORY_CONTRACT_[0-9a-f]{24}_BEGIN (.+)$", packet, re.M)
4788
+ assert [item.decode() for item in quoted] == expected, quoted
4789
+ assert b"personal note must not egress" not in packet
4790
+ assert hashlib.sha256(packet[: profile["candidate_bytes"]]).hexdigest() == result["candidate_sha256"]
4791
+ assert result["packet_sha256"] != result["candidate_sha256"]
4792
+ assert b"CCL_REPOSITORY_CONTRACT_" not in packet[: profile["candidate_bytes"]]
4793
+ concerns = [item["id"] for item in profile["required_concerns"]]
4794
+ assert "repository_contract" in concerns, concerns
4795
+ contract = result["repository_contract"]
4796
+ assert contract["coverage"] == sys.argv[4], contract
4797
+ assert [item["path"] for item in contract["files"]] == expected, contract
4798
+ print("contract_packet_ok")
4799
+ PY
4800
+ }
4801
+ reset_case passed unavailable unavailable
4802
+ out="$(run_contract_gate --mode review)"; rc=$?
4803
+ check "review quotes the tracked contract files governing the changed path, root first" \
4804
+ '[ "$rc" = 0 ] && contract_packet_check "$out" "AGENTS.md,.claude/CLAUDE.md,sub/AGENTS.override.md,sub/CLAUDE.md" complete | grep -qx contract_packet_ok'
4805
+
4806
+ printf 'after\n' >"$contract_repo/linked/code.txt"
4807
+ printf 'after\n' >"$contract_repo/big/code.txt"
4808
+ # A change under .claude/ reaches .claude/CLAUDE.md twice (root and .claude/);
4809
+ # the expected list below admits it once.
4810
+ printf 'after\n' >"$contract_repo/.claude/notes.txt"
4811
+ reset_case passed unavailable unavailable
4812
+ out="$(run_contract_gate --mode review)"; rc=$?
4813
+ check "a linked or oversized contract file is omitted and reported, never followed or failed on" \
4814
+ '[ "$rc" = 0 ] && contract_packet_check "$out" "AGENTS.md,.claude/CLAUDE.md,sub/AGENTS.override.md,sub/CLAUDE.md" partial | grep -qx contract_packet_ok && json_fields "$out" repository_contract.omitted.0.path=big/AGENTS.md repository_contract.omitted.0.reason=size_budget repository_contract.omitted.1.path=linked/AGENTS.md repository_contract.omitted.1.reason=unreadable'
4815
+
4816
+ reset_case passed unavailable unavailable
4817
+ out="$(run_contract_gate --mode challenge --challenge-budget 1 --challenge-index 1 --focus "contract rules")"; rc=$?
4818
+ check "a challenge keeps its focus: no contract section and no contract concern" \
4819
+ '[ "$rc" = 0 ] && ! grep -q CCL_REPOSITORY_CONTRACT_ "$WORK/state/claude_packet" && python3 -c "import json,sys; p=json.load(open(sys.argv[1])); assert p[\"repository_contract\"] is None and all(c[\"id\"] != \"repository_contract\" for c in p[\"required_concerns\"])" "$WORK/state/claude_profile" && json_fields "$out" repository_contract=None'
4820
+
4821
+ receipt_file="$contract_repo/.git/ccl-code-review/last-review.json"
4822
+ check "a conclusive run leaves a local receipt of the HEAD it reviewed, outside the tree" \
4823
+ '[ "$rc" = 0 ] && [ -f "$receipt_file" ] && [ "$(jq -r .head "$receipt_file")" = "$(git -C "$contract_repo" rev-parse HEAD)" ] && [ "$(jq -r .worktree_clean "$receipt_file")" = false ] && [ "$(jq -r .mode "$receipt_file")" = challenge ]'
4824
+
4825
+ cp "$receipt_file" "$WORK/receipt-before"
4826
+ reset_case passed unavailable unavailable
4827
+ out="$(run_contract_gate --mode challenge --challenge-budget 1 --challenge-index 1)"; rc=$?
4828
+ check "an inconclusive run leaves the last receipt untouched" \
4829
+ '[ "$rc" = 2 ] && cmp -s "$receipt_file" "$WORK/receipt-before"'
4830
+
4831
+ # The receipt directory swapped for a link: the write is refused, never followed.
4832
+ mkdir -p "$WORK/receipt-target"
4833
+ rm -rf "$contract_repo/.git/ccl-code-review"
4834
+ ln -s "$WORK/receipt-target" "$contract_repo/.git/ccl-code-review"
4835
+ reset_case passed unavailable unavailable
4836
+ out="$(run_contract_gate --mode challenge --challenge-budget 1 --challenge-index 1 --focus "contract rules")"; rc=$?
4837
+ check "a linked receipt directory is never written through" \
4838
+ '[ "$rc" = 0 ] && [ -L "$contract_repo/.git/ccl-code-review" ] && [ -z "$(ls -A "$WORK/receipt-target")" ]'
4839
+ rm -f "$contract_repo/.git/ccl-code-review"
4840
+
4841
+ anchor_out="$(PYTHONPATH="$WORK/harness/scripts" python3 - <<'PY' 2>&1
4842
+ import review_gate
4843
+ a = {"git_dir": "/g", "head": "a" * 40, "worktree_clean": True}
4844
+ b = dict(a, head="b" * 40)
4845
+ c = dict(a, worktree_clean=False)
4846
+ assert review_gate.stable_review_anchor(a, dict(a)) == a
4847
+ assert review_gate.stable_review_anchor(a, b) is None
4848
+ assert review_gate.stable_review_anchor(a, c) is None
4849
+ assert review_gate.stable_review_anchor(None, a) is None
4850
+ print("anchor_stability_ok")
4851
+ PY
4852
+ )"
4853
+ check "a freeze that straddles a HEAD or cleanliness change records nothing" \
4854
+ '[ "$anchor_out" = anchor_stability_ok ]'
4855
+
4856
+ # A bare --diff-file packet may cover less than HEAD, so it records no receipt.
4857
+ git -C "$contract_repo" diff HEAD >"$WORK/contract.patch"
4858
+ rm -rf "$contract_repo/.git/ccl-code-review"
4859
+ reset_case passed unavailable unavailable
4860
+ out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
4861
+ --mode review --cwd "$contract_repo" --diff-file "$WORK/contract.patch" --implementer-family openai \
4862
+ --review-plan-file "$WORK/review-plan.json" --allow-fallback-egress)"; rc=$?
4863
+ check "a bare --diff-file review records no receipt" \
4864
+ '[ "$rc" = 0 ] && json_fields "$out" status=passed && [ ! -e "$contract_repo/.git/ccl-code-review" ]'
4865
+
4866
+ swap_out="$(PYTHONPATH="$WORK/harness/scripts" python3 - "$WORK" <<'PY' 2>&1
4867
+ import os, sys, review_gate
4868
+ root = os.path.realpath(os.path.join(sys.argv[1], "gitdir-swap"))
4869
+ os.makedirs(root)
4870
+ def anchor_for(path):
4871
+ st = os.stat(path)
4872
+ return {"git_dir": path, "git_dir_identity": [st.st_dev, st.st_ino], "head": "a" * 40, "worktree_clean": True}
4873
+ def receipt_under(path):
4874
+ return os.path.exists(os.path.join(path, "ccl-code-review", "last-review.json"))
4875
+ result = {"mode": "review", "status": "passed"}
4876
+ # A different directory swapped in under the anchored path.
4877
+ git_dir, other = os.path.join(root, "gitdir"), os.path.join(root, "other")
4878
+ os.mkdir(git_dir)
4879
+ anchor = anchor_for(git_dir)
4880
+ os.rename(git_dir, os.path.join(root, "moved"))
4881
+ os.mkdir(other)
4882
+ os.symlink(other, git_dir)
4883
+ review_gate.record_local_review(anchor, result)
4884
+ assert not receipt_under(other), "wrote through a swapped git directory"
4885
+ # The same directory relocated into a tree, its old path now a link to it.
4886
+ original = os.path.join(root, "original")
4887
+ os.mkdir(original)
4888
+ anchor = anchor_for(original)
4889
+ tree = os.path.join(root, "worktree")
4890
+ os.mkdir(tree)
4891
+ os.rename(original, os.path.join(tree, "relocated"))
4892
+ os.symlink(os.path.join(tree, "relocated"), original)
4893
+ review_gate.record_local_review(anchor, result)
4894
+ assert not receipt_under(os.path.join(tree, "relocated")), "followed a link to the relocated git directory"
4895
+ # A parent component swapped for a link while the directory keeps its inode.
4896
+ parent = os.path.join(root, "parent")
4897
+ os.makedirs(os.path.join(parent, "gd"))
4898
+ anchor = anchor_for(os.path.join(parent, "gd"))
4899
+ os.rename(parent, os.path.join(root, "parent-moved"))
4900
+ os.symlink(os.path.join(root, "parent-moved"), parent)
4901
+ review_gate.record_local_review(anchor, result)
4902
+ assert not receipt_under(os.path.join(root, "parent-moved", "gd")), "followed a linked parent"
4903
+ # Control: an unchanged directory is written.
4904
+ control = os.path.join(root, "control")
4905
+ os.mkdir(control)
4906
+ review_gate.record_local_review(anchor_for(control), result)
4907
+ assert receipt_under(control), "control write missing"
4908
+ print("gitdir_swap_ok")
4909
+ PY
4910
+ )"
4911
+ check "a git directory swapped during the review is never written into" \
4912
+ '[ "$swap_out" = gitdir_swap_ok ]'
4913
+
4914
+ # Repeated failed rooted reads (a linked ancestor, a missing file) must not
4915
+ # leave directory descriptors behind: every omitted contract file is one.
4916
+ fd_out="$(PYTHONPATH="$WORK/harness/scripts" python3 - "$WORK" <<'PY' 2>&1
4917
+ import os, sys, review_gate
4918
+ root = os.path.realpath(os.path.join(sys.argv[1], "fd-leak"))
4919
+ os.makedirs(os.path.join(root, "a", "b"))
4920
+ os.makedirs(os.path.join(root, "target"))
4921
+ os.symlink(os.path.join(root, "target"), os.path.join(root, "a", "linked"))
4922
+ def attempt(relative):
4923
+ try:
4924
+ review_gate.read_bounded_regular_file(relative, root=root, label="probe", maximum=10,
4925
+ regular_error="irregular", oversized_error="big")
4926
+ except review_gate.GateError:
4927
+ pass
4928
+ before = len(os.listdir("/dev/fd"))
4929
+ for _ in range(50):
4930
+ attempt("a/b/missing.md")
4931
+ attempt("a/linked/AGENTS.md")
4932
+ after = len(os.listdir("/dev/fd"))
4933
+ assert after == before, (before, after)
4934
+ print("fd_release_ok")
4935
+ PY
4936
+ )"
4937
+ check "failed rooted reads release every directory descriptor" \
4938
+ '[ "$fd_out" = fd_release_ok ]'
4939
+
4940
+ # A tracked contract file whose ancestor became a link is omitted, and the
4941
+ # linked target's text never reaches the packet.
4942
+ (
4943
+ cd "$contract_repo"
4944
+ git add -A && git commit -q -m "settle earlier cases"
4945
+ mkdir anc hl && printf 'anc rule\n' >anc/AGENTS.md && printf 'before\n' >anc/code.txt
4946
+ printf 'hl rule\n' >hl/AGENTS.md && printf 'before\n' >hl/code.txt
4947
+ git add -A && git commit -q -m "add anc"
4948
+ ln hl/AGENTS.md "$WORK/hl-second-name.md" && printf 'after\n' >hl/code.txt
4949
+ mkdir -p "$WORK/outside-anc" && printf 'outside secret rule\n' >"$WORK/outside-anc/AGENTS.md"
4950
+ printf 'after\n' >"$WORK/outside-anc/code.txt"
4951
+ rm -rf anc && ln -s "$WORK/outside-anc" anc
4952
+ )
4953
+ # Base mode refuses an untracked link as a candidate outright, so the transient
4954
+ # state is reached through a --diff-file packet that names the anc/ paths.
4955
+ git -C "$contract_repo" diff HEAD -- anc hl >"$WORK/anc.patch"
4956
+ reset_case passed unavailable unavailable
4957
+ out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
4958
+ --mode review --cwd "$contract_repo" --diff-file "$WORK/anc.patch" --implementer-family openai \
4959
+ --review-plan-file "$WORK/review-plan.json" --allow-fallback-egress)"; rc=$?
4960
+ check "a contract file behind a linked ancestor or with a second hard link is omitted and never quoted" \
4961
+ '[ "$rc" = 0 ] && ! grep -q "outside secret rule" "$WORK/state/claude_packet" && printf "%s" "$out" | python3 -c "import json,sys; d=json.load(sys.stdin)[\"repository_contract\"]; assert {\"path\": \"anc/AGENTS.md\", \"reason\": \"unreadable\"} in d[\"omitted\"] and {\"path\": \"hl/AGENTS.md\", \"reason\": \"unreadable\"} in d[\"omitted\"], d" && ! grep -q "hl rule" "$WORK/state/claude_packet"'
4962
+
4740
4963
  # The explicit cwd is the only repository identity. Ambient Git variables must
4741
4964
  # not redirect discovery, objects, refs, index, or worktree to a clean decoy.
4742
4965
  git clone -q "$WORK/repo" "$WORK/git-env-decoy"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: multi-perspective-research
3
- description: Skip 优先判定:一查便知的单点事实问题 → 直接回答不套流程;请求点名了候选让选、或用裁决措辞要结论(选哪个 / 要不要上 X / 该不该做 / 可不可行)→ 先 `product-rd-workflow`(它可回调本技能产调研底稿);纯主题/领域/写作准备调研留在本技能;拷问已有方案 → `grill-me`;bug 根因 → `defect-diagnosis`;复盘沉淀 → `skill-extraction-workflow`。触发:调研一个主题 / 深度调研 / 多视角研究 / 帮我研究一下 X / 写作前调研 / 领域·组织研究(不喂决策)/ research a topic → run a structured research loop(视角枚举 → 矛盾图 → 合成简报 → 自评)with evidence-grounding discipline, instead of one-question surface answers.
3
+ description: 调研一个主题 / 深度调研 / deep research / 调研某个产品·公司·赛道 / 多视角研究 / 帮我研究一下 X / 写作前调研 / 领域·组织研究(不喂决策)/ research a topic → run a structured research loop(视角枚举 → 矛盾图 → 合成简报 → 自评)with evidence-grounding discipline, instead of one-question surface answers; another installed deep-research skill may serve as a search tool inside this loop, not replace it. Skip:一查便知的单点事实问题 → 直接回答不套流程;请求点名了候选让选、或用裁决措辞要结论(选哪个 / 要不要上 X / 该不该做 / 可不可行)→ 先 `product-rd-workflow`(它可回调本技能产调研底稿);纯主题/领域/产品/写作准备调研留在本技能;拷问已有方案 → `grill-me`;bug 根因 → `defect-diagnosis`;复盘沉淀 → `skill-extraction-workflow`。
4
4
  ---
5
5
 
6
6
  # Multi-Perspective Research(多视角主题调研)
@@ -122,7 +122,7 @@ At each stage boundary, walk the per-stage entry-state enumeration in [Stage-Ent
122
122
  - **Bounded mechanical-move exception** — scoped review of a move-dominated diff only under the reference's infeasibility precondition and four conditions.
123
123
  - **Cross-cutting shared-runtime primitives** — explicit adversarial pass over edge paths plus a regression test per path found.
124
124
  - **Implementer self-review row** — persisted before the independent review in a record whose history proves ordering; a late row invalidates the review.
125
- - **Changed candidate = refreshed row + fresh full-scope rerun** — any material post-review change mechanically triggers a full-scope rerun; the author cannot narrow the rerun's scope or self-classify the change as whitespace-only.
125
+ - **Changed candidate = refreshed row + fresh full-scope rerun** — any post-review change, tests/docs included, mechanically triggers a full-scope rerun; the author cannot narrow its scope or call the change immaterial.
126
126
  - **Independent gate surfacing basics = process defect** — repair the self-review/deterministic-gate loop before rerunning, and findings still require disposition, not waiver.
127
127
  - **Green-tests-alone merge is the same defect** as reaching implementation with only spec plus plan.
128
128
  - **Human/team sign-off** — for high-risk money/permission/data paths and any contract/API change with an external consumer, before implementation, merge, or launch.
@@ -26,7 +26,7 @@ Turn observed experience into reusable skills without business-specific details.
26
26
 
27
27
  - **R0 (mandatory clean-landing gate)**: Before marking any skill or reference change `R0-clean`, `landing-clean`, merge-ready, or cleanly landed, run the leakage audit (`audit_cmd` from the maintainer's private alias YAML) and require **zero hits** across all leakage categories — design-source file keys/URLs/node-ids, project/team identifiers, real subproject paths / repo / branch names, contributor names/emails, ticket ids, internal domains/hosts, and any non-distilled business/product noun pointing to one specific organization.
28
28
  - **Probe, don't infer**: a local environment without `ALIAS_AUDIT_CMD` is normal, but you must *observe* that — before recording `interim` / `r0_status`, run `check-ccl-skills.sh` and cite the actual `r0_status=` / final-token line it produced; never infer the branch from "unset is common" or a bare `printenv ALIAS_AUDIT_CMD` pre-check. Skipping straight to the interim fallback is a blocked-verification miss (a set var means the private audit is *configured*; it is *available* only once it runs clean to `alias_audit_ok`, and a set-but-broken var is itself a blocked-verification failure to fix or waive, not a fallback licence).
29
- - **Interim commit semantics**: when `ALIAS_AUDIT_CMD` is genuinely unset, the contributor may commit, push, or open a Draft/WIP MR only as `interim` with `R0 pending maintainer audit` recorded, but must not merge or claim clean landing until R0 passes or an explicit risk-owner waiver is recorded. (Label semantics: `interim` alone does not decide commit permission — this R0-pending `interim` allows commit/push/Draft-MR but blocks merge/clean-landing, while the review/challenge gate's `interim` is an *uncommitted checkpoint* that blocks commit itself; each gate's failure carries its own commit permission, and when both fail the stricter applies.)
29
+ - **Interim commit semantics**: when `ALIAS_AUDIT_CMD` is genuinely unset, the contributor may commit, push, or open a Draft/WIP MR only as `interim` with `R0 pending maintainer audit` recorded, but must not merge or claim clean landing until R0 passes or an explicit risk-owner waiver is recorded. (Label semantics: `interim` alone does not decide commit permission — this R0-pending `interim` allows commit/push/Draft-MR but blocks merge/clean-landing, while the review/challenge gate's `interim` is an *uncommitted checkpoint* that blocks commit itself, except the dual-track local checkpoint commit; each gate's failure carries its own commit permission, and when both fail the stricter applies.)
30
30
  - **Edit-time gates that stay inline**: every new sanitized label MUST already exist in the alias YAML before clean landing (fail-closed); pre-existing leakage may be `known_debt` but new/modified content MUST stay zero-hit; grep cannot catch source-shaped example identifiers (variable/function/class/file/package names lifted verbatim) so adversarial review (codex challenge or equivalent) is the practical safety net.
31
31
  - **False-green guard**: the public fallback is public interim evidence only — the private alias audit (`alias_audit_ok`) has NOT run when those tokens print, so record `interim / R0 pending` and never treat `ccl_skill_check_ok`, `ccl_skill_check_interim_ok`, `generic_r0_leak_scan_ok`, or `alias_audit_unavailable` as clean-landing R0 evidence; the clean-landing signal is `ccl_skill_check_clean_ok` (`r0_status=private-ok`); no project alias is not a waiver (use the generic process-retro profile when no product corpus applies; neither the generic fallback nor an ad-hoc `grep` is the clean-landing gate).
32
32
  - For category definitions, alias-YAML structure, the false-green guard and fallback/token semantics, `known_debt` semantics, example-identifier substitution, the generic process-retro profile, and `ALIAS_AUDIT_CMD` enforcement, read `references/r0-leakage-audit.md`.
@@ -154,20 +154,20 @@ Turn observed experience into reusable skills without business-specific details.
154
154
  - **"The owner skill already states the rule" / "avoid monotonic growth" does NOT license a memory-only or no-op landing when the gate demonstrably failed to fire.** If a rule exists yet the failure still happened (and would recur for another agent or project), adequate *content* is not adequate *enforcement*: land the firing mechanism — the trigger, closeout step, validator, or merged clause that makes the existing rule actually catch this case, in the owning shared skill — or prove it now fires. Retreating to a personal memory or "no change, content is fine" while the gate stays un-fired is the dodge this prevents (memory is supplement only, per the memory-only-insufficient rule).
155
155
  - For changed upstream owners, `check-ccl-skills.sh` (via `scripts/impact-chain-gate.rb`) is the mechanical closeout over every added source-register row; the declaration format (behavioral-evidence / observed-failure / firing-path fragments), anchor rules, wording-only classification, the `RED-baseline` floor, and the author-declaration trust model: `references/external-practice-controls.md#behavioral-evidence-and-attestation`.
156
156
  - For shared-skill changes, classify the diff before finalization: `shared-skill change` is defined in `references/dual-track-review-gate.md` (it also covers the plugin-shipped command/behavior surfaces outside `skills/`); `wording-only` means punctuation, grammar, typo, formatting, or synonym substitution with no change to trigger, scope, routing, validation, condition, example, owner, or acceptance meaning, and touching no frontmatter (any `description`/frontmatter edit, even a pure typo fix, is a routing-surface change, never wording-only); all other changes are non-wording.
157
- - Every shared-skill change, including wording-only edits, must include a recorded independent review row before commit.
157
+ - Every shared-skill change, including wording-only edits, must include a recorded independent review row before commit. A local unpushed checkpoint commit on an isolated worktree branch is allowed so each pass can name the commit it reviewed; push, pull request, readiness claim and landing still wait for the owed passes.
158
158
  - Non-wording shared-skill changes must also include a recorded challenge row AND a recorded behavioral-evidence row before commit (actual behavior/routing deltas require a true `RED-baseline`; unchanged controls use paired `semantic-control`; status rules in `references/dual-track-review-gate.md`). A non-wording owner package must include at least one `RED-baseline` row, so stable-control labels cannot self-clear the package. **For a DESTRUCTIVE/irreversible change, a `RED-baseline` row must show the protected predicates were mutated, not merely that negative cases ran.** Executing the must-NOT-touch cases is necessary but not sufficient: a negative probe that would still pass with its protecting predicate removed is evidence of nothing (recurring shape: a degenerate fixture short-circuits every probe on an unrelated conservative branch, so the safety predicate is never reached and the green suite certifies the hole).
159
159
  - So the row records, per protected predicate, the removal that was **applied** and observed to turn the suite RED **for the right reason** — a bare non-zero exit does not qualify (a mutant that breaks syntax or fixture setup also exits non-zero and would bank a broken build as proof of sensitivity); the failure must be attributable to the named protected assertion, and attribution is **differential** (the owning assertion passes in the unmutated control and fails under the mutant, with no non-owning assertion failing) rather than a substring match on aggregate output. An unapplied "this mutation would fail it" is a hypothesis. `testing-strategy` owns the encoded form of that walk (route, don't copy) — for a destructive artifact the walk belongs inside the suite so a later fixture change cannot silently re-blind it.
160
160
  - A challenge skip row is allowed only for wording-only changes; trivial scope does NOT exempt a non-wording change, and ANY skill `description`/frontmatter edit — including a pure typo fix — is NOT wording-only (it changes the routing surface) — both require the full gate.
161
161
  - If a human explicitly asks to skip independent review or challenge, record the review state and residual risk honestly instead of fabricating a pass. Chat or candidate-local text may authorize an in-scope preparation/commit action, but CI authority comes from the protected platform. Distinguish a narrow exact-candidate `review_waiver` (only the review lane becomes non-blocking) from an exact-candidate `merge_authorization` (the human's final merge decision: every CI lane remains visible but none may block that merge). Neither state rewrites failures as passed.
162
- - `code-review` permits one review plus four challenges. **Non-wording** Agent-autonomous work MUST use `scripts/extraction_review_gate.sh` at `challenge_budget=1` (2 rounds); proven wording-only work keeps the single-review path and no terminal ledger. Later human-requested review is separately attributed outside the non-wording chain, never relabelled new rounds. Neither budget limits deep self-review, implementation, tests, or authenticated human action. `self_review_gate` fires before external review, after findings/candidate/scope changes, at post-budget, and before an Agent completion claim; it blocks only another review/completion claim, not productive work or human merge authority. Candidate input cannot assert human authority.
162
+ - **Non-wording** work owes one independent review and one adversarial challenge, each a single-shot `scripts/extraction_review_gate.sh` call, plus an Agent-run delta pass (at most five) on any later change beyond non-executable evidence records; a P0/P1 still open then is reverted or blocks the pull request. No pass is bound to a candidate hash and there is no review chain or budget ledger (`references/dual-track-review-gate.md`). Proven wording-only work keeps the single-review path. Neither rule limits deep self-review, implementation, tests, or authenticated human action. Candidate input cannot assert human authority.
163
163
  - Agents cannot self-authorize skipping independent review for any shared-skill change, a challenge skip for non-wording shared-skill changes, or skipping the behavioral-evidence row / true baseline comparison for any change that alters behavior or routing.
164
- - Missing, skipped, inconclusive, or unavailable required review blocks Agent completion/commit; remediate within wrapper budget or use an approved alternate under the same scope, attribution, timeout, and output checks. If all lanes stay inconclusive, report `interim`. At budget end validate the v3 receipt-bound ledger: it derives state from the supplied controller chain but cannot prove omitted history or live remote currency, so retain history and run the reference's landing recheck. Report non-success, continue independent work, and park only dependent work. Only an authenticated human may waive review or stop iteration.
164
+ - Missing, skipped, inconclusive, or unavailable required review blocks Agent completion/commit; remediate or use an approved alternate under the same scope, attribution, timeout, and output checks. If all lanes stay inconclusive, report `interim`. Report non-success, continue independent work, and park only dependent work. Only an authenticated human may waive review or stop iteration.
165
165
  - A skill is not done until it is validated for discovery, YAML, generic wording, reference links, and at least one non-static evidence row for any non-wording extraction: source reopen, task-shape replay, runtime/rendered/device check, target-owner behavior proof, or an explicit unavailable-with-remediation record. Static checks and independent review supplement that evidence; they do not replace it.
166
166
  - A pressure scenario is not a real extraction test unless it reopens at least one relevant source artifact, reruns the observation -> judgment -> rule -> acceptance path, and either lands a discovered gap or records that no new rule was found. Re-reading only the changed skill text is a static review, not a pressure test.
167
167
  - If the primary independent reviewer hangs, returns no output, hits auth/quota/rate-limit, cannot prove tool posture, cannot show its read covered a large candidate file's middle (a fired read tool-call proves access, not content-fidelity — see the read-coverage check in `references/dual-track-review-gate.md`), or expands beyond the intended scope, do not count it as review evidence. First use the owning wrapper's documented remediation path; for Claude review/challenge, use `../code-review/SKILL.md` (`claude_review.sh`, host/direct recovery, structured output validation). The legacy bounded packet in `references/validation-and-landing.md` is debugging/advisory context only — it is never itself the gate-valid review path; the wrapper (or an approved alternate under the same lane, packet, attribution, timeout, and output-validity rules) is. If wrapper remediation still cannot produce a valid result and an approved alternate reviewer is available, switch tools while preserving the same lane (review vs challenge), bounded diff/file packet, attribution, timeout, and output-validity requirements. A free-form or hanging alternate run is still inconclusive; it does not satisfy the row.
168
168
  - Independent review is **dual-track** for any non-wording shared-skill change, deep extraction, multi-skill landing, new shared skill, or skill change that ships operational/architectural rules: run BOTH (a) a fact/consistency review (`codex review` or equivalent — catches inaccuracies, contradictions across references, sanitization gaps, over-prescription) AND (b) an adversarial challenge (`codex exec` with adversarial prompt — hunts race conditions, data-loss paths, security holes, algorithm flaws, operational footguns). Skipping the challenge mode is how P0/P1 production-safety issues survive into shared skills. The two are not interchangeable: review catches what's wrong; challenge catches what would break under chaos. See `references/dual-track-review-gate.md` for the runbook.
169
169
  - **Draft-time corollary — pre-cover the recurring first-draft blind-spot AXES before challenge (not security alone).** Sweep EACH applicable axis explicitly before handing off (record ≥1 negative case per axis, or a reasoned `not-applicable`): **(1) security / privacy / authority / data-loss**, **(2) concurrency & lifecycle**, **(3) resource bounds**, **(4) rollout / migration ordering**, **(5) over-broad absolute**, **(6) enumeration-completeness (the mirror of (5) — under-listing, not over-listing)**. A draft that reads clean on the happy path almost always misses ≥1 of these; the challenge is the safety net, not the first line. When drafting any rule/code that touches execution, deletion, optimization, deployment, capability/permission, concurrency, resource lifetime, or a cross-service rollout, self-cover the applicable axes first. "Pre-cover" means recording at least one relevant negative case — or, for axis (6), the set-diff against the complete set — (or a reasoned `not-applicable`) per applicable axis, NOT a "covered" badge — and it never narrows or downgrades the mandatory adversarial challenge. The per-axis instance lists: `references/dual-track-review-gate.md` (pre-cover axis detail).
170
- - **Repeated same-class adversarial-challenge findings across rounds are a design/scope smell, not just more patches** — and the resolution depends on WHAT recurs. When the challenge surfaces NEW instances of the SAME risk class in two or more rounds, evaluate whether that capability should EXIST, not only how to patch this instance: convergence-by-deletion is often cleaner and more complete than convergence-by-patching, and is the right call when the capability serves no real need — judged from the convention's primary source and/or product/process evidence, not from the patch count alone. Record the decision explicitly as `keep / delete / narrow / replace` with the same-class evidence (the rounds and findings), the real user need it serves, a safer alternative, and blast-radius/migration; this complements the challenge-round convergence standard in step 6 (which counts remaining P0/P1 fixes) — same-class recurrence is the cue to question the design, not to open another patch round (observed: an auto-rewrite-of-a-human-file capability spawned a new data-loss footgun every round until it was deleted, after which the gate converged immediately). Two siblings share that decision record: **cross-landing** — the same class returning in production after a previous landing already "fixed" it means the fix shape itself is wrong, the tell being that each prior fix **re-instantiated the same predicate on new inputs**; when the predicate is the **vocabulary of an artifact the control does not own** (a field/value/version list from an upstream tool, format, or API) the second occurrence is already the design signal — re-express it over an **invariant the control does own** (shape, arity, type, or the independently-verified property the check actually needs) or accept the maintenance and say so, recording which invariant replaced the vocabulary and the residual risk the looser predicate accepts; **scope-direction** — a finding whose remediation would implement concerns the design explicitly defers to a later phase is a classification checkpoint, not a cut or another implement round: establish current-phase impact, split a compound finding, and let only a distinct risk owner (never the proposing controller) ratify a scope cut, so a cut defers rather than discards — the failure it prevents is the reviewer silently becoming a scope-expansion engine while the artifact gold-plates (observed: a first-cut state-machine contract ballooned across rounds until the controller reset to the in-scope diff). Failure shapes and mechanics: `references/dual-track-review-gate.md` (`Findings, autonomous budget, and human authority`; design-time operability check) and `references/external-practice-controls.md` (predicates over a vocabulary the control does not own).
170
+ - **Repeated same-class adversarial-challenge findings across rounds are a design/scope smell, not just more patches** — and the resolution depends on WHAT recurs. When the challenge surfaces NEW instances of the SAME risk class in two or more rounds, evaluate whether that capability should EXIST, not only how to patch this instance: convergence-by-deletion is often cleaner and more complete than convergence-by-patching, and is the right call when the capability serves no real need — judged from the convention's primary source and/or product/process evidence, not from the patch count alone. Record the decision explicitly as `keep / delete / narrow / replace` with the same-class evidence (the rounds and findings), the real user need it serves, a safer alternative, and blast-radius/migration; this complements the challenge-round convergence standard in step 6 (which counts remaining P0/P1 fixes) — same-class recurrence is the cue to question the design, not to open another patch round (observed: an auto-rewrite-of-a-human-file capability spawned a new data-loss footgun every round until it was deleted, after which the gate converged immediately). Two siblings share that decision record: **cross-landing** — the same class returning in production after a previous landing already "fixed" it means the fix shape itself is wrong, the tell being that each prior fix **re-instantiated the same predicate on new inputs**; when the predicate is the **vocabulary of an artifact the control does not own** (a field/value/version list from an upstream tool, format, or API) the second occurrence is already the design signal — re-express it over an **invariant the control does own** (shape, arity, type, or the independently-verified property the check actually needs) or accept the maintenance and say so, recording which invariant replaced the vocabulary and the residual risk the looser predicate accepts; **scope-direction** — a finding whose remediation would implement concerns the design explicitly defers to a later phase is a classification checkpoint, not a cut or another implement round: establish current-phase impact, split a compound finding, and let only a distinct risk owner (never the proposing controller) ratify a scope cut, so a cut defers rather than discards — the failure it prevents is the reviewer silently becoming a scope-expansion engine while the artifact gold-plates (observed: a first-cut state-machine contract ballooned across rounds until the controller reset to the in-scope diff). Failure shapes and mechanics: `references/dual-track-review-gate.md` (`Findings and dispositions`; design-time operability check) and `references/external-practice-controls.md` (predicates over a vocabulary the control does not own).
171
171
  - **Global-iteration boundary:** any `STOP`/`revert`/terminal wording in this gate applies to the reviewer lane, readiness claim, or defective dependent slice, never automatically to the overall task. Repeated root cause or no-progress rounds trigger method/design/validation changes or a parked human-decision item while independent runnable work continues. Only an explicit authenticated human stop ends the overall iteration.
172
172
  - **The should-it-exist question also applies at DESIGN TIME for any new mechanical gate, validator, or evidence apparatus, AND for any change to its verdict in EITHER direction — a loosening must be checked hardest, since it removes evidence instead of adding a red — run the check before landing AND before offering a human design options.** Legs: **author-dogfood** (the authoring workflow passes it end-to-end under every base resolution CI uses), **marginal-cost**, **trust-model fit** (for a loosening, which class stops owing evidence), **premise check** when tightening (a clean run on the current corpus is not evidence). Per-leg method and failure shapes: `references/dual-track-review-gate.md` (design-time operability check).
173
173
  - Reference files containing CLI commands, external API calls, or runnable code examples ship operational behavior and are NOT exempt from the dual-track gate; treat them the same as skill core-rule sections. Specifically, any reference file with recommended runnable commands or code that encodes operational behavior, external-state mutation, security-sensitive behavior, deployment/release/audit workflows, or nontrivial reusable recipes — including dangerous anti-patterns a teammate could plausibly copy — must pass both the fact/consistency review and the adversarial challenge before landing. Harmless validation commands and truly non-runnable static illustrative snippets (e.g., pseudocode, redacted/placeholder examples) remain eligible for a trivial-scope challenge skip only when the changed artifact is not a shared-skill change; any non-wording shared-skill change still requires challenge.
@@ -281,7 +281,7 @@ Turn observed experience into reusable skills without business-specific details.
281
281
  - A recorded behavioral-evidence row (`RED-baseline` / `semantic-control` / `not-applicable: docs-only` — see `references/dual-track-review-gate.md`) shows the change moves agent behavior as intended; independent review alone is not behavioral evidence.
282
282
  - If independent review was attempted through Claude Code and the first prompt hung or produced no output, validation must show the wrapper remediation path per the primary-reviewer remediation rule (Core Rules `Validation & the dual-track gate` group): the `code-review` wrapper invocation used (`claude_review.sh`, host/direct recovery), its structured-output validity, and whether the result was `findings applied`, `no blocking findings`, or `review unavailable after remediation`. The legacy bounded review packet is debugging/advisory context only — it does not satisfy this row.
283
283
  - Dual-track closeout record: the gate definition stays in the Core Rules `Validation & the dual-track gate` group; Step 6 records separate rows for fact/consistency review, adversarial challenge or table-permitted `challenge: not-required`, and the behavioral-evidence row required above. Use `references/dual-track-review-gate.md` and `references/description-authoring.md` for trigger/skip/convergence mechanics.
284
- - Challenge convergence means no **undispositioned** P0/P1 and a fresh unbiased full challenge of the exact landing candidate, including the final round; never use a scoped "confirm my prior fixes" pass as the final challenge. Record per-round log and final pass/abort decision per `references/dual-track-review-gate.md`.
284
+ - Challenge convergence means no **undispositioned** P0/P1 after one unprimed full challenge and the delta passes the post-review changes owe; never replace the full challenge with a "confirm my prior fixes" pass. Record the passes and the post-review delta per `references/dual-track-review-gate.md`.
285
285
  - Recurring-anti-patterns checklist: before committing operational/architectural skill changes, run the grep panel in `references/recurring-anti-patterns-checklist.md` against the changed file set; hits must be fixed or recorded as `known_debt` in the project alias map (pre-existing hits only — hits introduced by new/modified content must be fixed before landing, per the R0 rule's zero-hit requirement). The checklist grows over time; add a new pattern when a class appears in 2+ skills.
286
286
  - For fresh extraction wiring, start from `references/extraction-quickstart.md`. For specialized sources, read `references/two-source-extraction-pattern.md` for design+code extraction, `references/incident-postmortem-extraction.md` before landing incident/postmortem-derived rules, and `references/parallel-stack-references-pattern.md` for stack-agnostic + stack-specific sibling references.
287
287
  - Match validation depth to risk: light checks for small updates, strict review/test/install checks for new, shared, high-impact, or generator-backed skills.