@ccoalm/ccl-skills 0.3.0 → 0.4.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 (26) hide show
  1. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/classify_envelope.py +34 -3
  2. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_abort_leak_state_helpers.sh +148 -0
  3. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_classify_envelope.sh +28 -0
  4. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_parse_review_json.sh +7 -0
  5. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_gate.sh +13 -0
  6. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_gate_abort_leak.sh +271 -34
  7. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/SKILL.md +8 -8
  8. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/eval-routing.md +7 -8
  9. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/extraction-quickstart.md +3 -2
  10. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/harness-patterns-and-eval.md +7 -6
  11. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/recurring-anti-patterns-checklist.md +18 -0
  12. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md +29 -2
  13. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-to-skill-extraction.md +28 -29
  14. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-ccl-skills.sh +153 -0
  15. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/eval-health.rb +23 -10
  16. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/impact-chain-gate.rb +303 -1
  17. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_impact_chain_refscripts.sh +222 -91
  18. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_regressions.sh +6 -0
  19. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_impact_chain_gate_dateless_host.sh +6 -1
  20. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_impact_chain_gate_verdict_differential.sh +1 -1
  21. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_impact_chain_round_attribution.sh +12 -12
  22. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_impact_chain_self_adjudication.sh +455 -0
  23. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_impact_chain_source_refuted.sh +20 -20
  24. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_liveness_predicate_gate.sh +288 -0
  25. package/dist/assets/release.json +39 -24
  26. package/package.json +1 -1
@@ -125,6 +125,7 @@ fast_tests=(
125
125
  test_validate_skill_root_depth.sh
126
126
  test_validate_skill_cross_refs.sh
127
127
  test_git_identity_predicate_gate.sh
128
+ test_liveness_predicate_gate.sh
128
129
  test_regression_runner_registration.sh
129
130
  # Lane-semantics guard for this runner itself (036 challenge P2): proves
130
131
  # --heavy-only / --fast / --full each run exactly their lane against a stub
@@ -164,6 +165,11 @@ heavy_tests=(
164
165
  # 对照表不交代被删内容)必须每轮都跑。clone + 16 分支对 pre-commit 太慢,进 heavy。
165
166
  test_impact_chain_source_refuted.sh
166
167
  test_impact_chain_gate_verdict_differential.sh
168
+ # 045:两个触发器(routing 面改动欠 bank 证据、台账行欠 result-class)的十条决策表
169
+ # 用例。同样是整仓 clone + 一 case 一分支的形态,故与上面两套同进 heavy。它钉的是
170
+ # 「跳过义务会不会留下可检出的缺席」——044 连续五轮 challenge 都停在这一点上,负向
171
+ # 用例(缺证据、非枚举值、降范围无留痕)必须每轮都跑,否则触发器会悄悄退化成声称。
172
+ test_impact_chain_self_adjudication.sh
167
173
  )
168
174
 
169
175
  # Registration self-audit: every sibling test_*.sh must appear in fast_tests or
@@ -76,8 +76,13 @@ git -C "$REPO" branch fixture-base HEAD
76
76
  git -C "$REPO" switch -q -C case-dateless fixture-base
77
77
  git -C "$REPO" branch --set-upstream-to=fixture-base case-dateless >/dev/null 2>&1
78
78
  perl -0pi -e 's/^(description: .+)$/$1 Fixture dateless trigger clause./m' "$REPO/skills/product-rd-workflow/SKILL.md"
79
- printf '| Fixture dateless row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; firing-path: file:skills/product-rd-workflow/SKILL.md#description | `updated` | `product-rd-workflow/SKILL.md` description-only fixture change |\n' \
79
+ printf '| Fixture dateless row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure; bank-evidence: downscoped:DATELESS-FIXTURE-NO-BANK; firing-path: file:skills/product-rd-workflow/SKILL.md#description | `updated` | `product-rd-workflow/SKILL.md` description-only fixture change |\n' \
80
80
  >> "$REPO/skills/skill-extraction-workflow/references/source-register.md"
81
+ mkdir -p "$REPO/specs/fixture-dateless"
82
+ # 045: a description change owes bank evidence. This fixture exercises host date
83
+ # handling, not routing measurement, so it downscopes the run ON THE RECORD — the
84
+ # token has to exist somewhere a reader reaches, which is the point of the rule.
85
+ printf -- '- Downscope downscoped:DATELESS-FIXTURE-NO-BANK is recorded here; this round must not be read as having measured routing.\n' > "$REPO/specs/fixture-dateless/plan.md"
81
86
  git -C "$REPO" add -A
82
87
  git -C "$REPO" commit -qm "description-only change with #description row"
83
88
 
@@ -335,7 +335,7 @@ run_case() { # <name> <expected-baseline-rc> <expected-token-or-empty> <declare:
335
335
  local repo="$TMP/case-$name"
336
336
  seed_case "$repo"
337
337
  if [ "$declare_row" = yes ]; then
338
- printf '| Fixture differential row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; firing-path: file:skills/product-rd-workflow/SKILL.md#DIFFERENTIAL-CASE | `updated` | `product-rd-workflow/SKILL.md` fixture change |\n' \
338
+ printf '| Fixture differential row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure; firing-path: file:skills/product-rd-workflow/SKILL.md#DIFFERENTIAL-CASE | `updated` | `product-rd-workflow/SKILL.md` fixture change |\n' \
339
339
  >> "$repo/$LEDGER_REL"
340
340
  fi
341
341
  git -C "$repo" add -A
@@ -120,7 +120,7 @@ mutate_owner_rule() { # <repo-dir> <owner-slug> <marker>
120
120
  # matching mutate_owner_rule added, and the owner key in the evidence cell.
121
121
  append_valid_row() { # <repo-dir> <owner-slug> <marker> <lesson>
122
122
  local repo="$1" owner="$2" marker="$3" lesson="$4"
123
- printf '| %s | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; firing-path: file:skills/%s/SKILL.md#%s | `updated` | `%s/SKILL.md` fixture change |\n' \
123
+ printf '| %s | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure; firing-path: file:skills/%s/SKILL.md#%s | `updated` | `%s/SKILL.md` fixture change |\n' \
124
124
  "$lesson" "$owner" "$(rule_anchor "$marker")" "$owner" >> "$repo/$LEDGER_REL"
125
125
  }
126
126
 
@@ -276,7 +276,7 @@ mutate_owner_rule "$L" testing-strategy L2
276
276
  # that still merely warns — which is exactly what it must detect.
277
277
  append_valid_row "$L" product-rd-workflow L "Fixture first owner row"
278
278
  append_valid_row "$L" testing-strategy L2 "Fixture second owner row"
279
- printf '| Fixture ambiguous row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; firing-path: file:skills/product-rd-workflow/SKILL.md#%s | `updated` | `product-rd-workflow/SKILL.md` and `testing-strategy/SKILL.md` both changed and are cited in one row |\n' \
279
+ printf '| Fixture ambiguous row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure; firing-path: file:skills/product-rd-workflow/SKILL.md#%s | `updated` | `product-rd-workflow/SKILL.md` and `testing-strategy/SKILL.md` both changed and are cited in one row |\n' \
280
280
  "$(rule_anchor L)" >> "$L/$LEDGER_REL"
281
281
  git -C "$L" add -A
282
282
  git -C "$L" commit -qm "round 1: one row citing two changed selected owners"
@@ -310,7 +310,7 @@ git -C "$M" branch fixture-base HEAD
310
310
  git -C "$M" switch -q -c case-noncurated
311
311
  printf -- '- Rule %s must be recorded before the round lands.\n' "$(rule_anchor M)" \
312
312
  >> "$M/skills/worktree-isolation/SKILL.md"
313
- printf '| Fixture non-curated row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; firing-path: file:skills/worktree-isolation/SKILL.md#%s | `updated` | `worktree-isolation/SKILL.md` changed; this skill is not a curated upstream owner |\n' \
313
+ printf '| Fixture non-curated row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure; firing-path: file:skills/worktree-isolation/SKILL.md#%s | `updated` | `worktree-isolation/SKILL.md` changed; this skill is not a curated upstream owner |\n' \
314
314
  "$(rule_anchor M)" >> "$M/$LEDGER_REL"
315
315
  git -C "$M" add -A
316
316
  git -C "$M" commit -qm "round 1: change a non-curated owner and record it"
@@ -357,7 +357,7 @@ seed_repo "$O"
357
357
  git -C "$O" branch fixture-base HEAD
358
358
  git -C "$O" switch -q -c case-dotslash
359
359
  mutate_owner_rule "$O" product-rd-workflow O
360
- printf '| Fixture dot-slash row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; firing-path: file:skills/product-rd-workflow/SKILL.md#%s | `updated` | see ./skills/product-rd-workflow/references/design-note.md for the rationale |\n' \
360
+ printf '| Fixture dot-slash row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure; firing-path: file:skills/product-rd-workflow/SKILL.md#%s | `updated` | see ./skills/product-rd-workflow/references/design-note.md for the rationale |\n' \
361
361
  "$(rule_anchor O)" >> "$O/$LEDGER_REL"
362
362
  git -C "$O" add -A
363
363
  git -C "$O" commit -qm "round 1: change the owner, cite it with a ./ prefixed path"
@@ -377,7 +377,7 @@ seed_repo "$O2"
377
377
  git -C "$O2" branch fixture-base HEAD
378
378
  git -C "$O2" switch -q -c case-rooted
379
379
  mutate_owner_rule "$O2" product-rd-workflow O2
380
- printf '| Fixture rooted-path row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; firing-path: file:skills/product-rd-workflow/SKILL.md#%s | `updated` | see /skills/product-rd-workflow/references/design-note.md for the rationale |\n' \
380
+ printf '| Fixture rooted-path row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure; firing-path: file:skills/product-rd-workflow/SKILL.md#%s | `updated` | see /skills/product-rd-workflow/references/design-note.md for the rationale |\n' \
381
381
  "$(rule_anchor O2)" >> "$O2/$LEDGER_REL"
382
382
  git -C "$O2" add -A
383
383
  git -C "$O2" commit -qm "round 1: change the owner, cite it with a rooted path"
@@ -399,7 +399,7 @@ seed_repo "$O3"
399
399
  git -C "$O3" branch fixture-base HEAD
400
400
  git -C "$O3" switch -q -c case-remainder
401
401
  mutate_owner_rule "$O3" product-rd-workflow O3
402
- printf '| Fixture remainder row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; firing-path: file:skills/product-rd-workflow/SKILL.md#%s | `updated` | rationale in skills/product-rd-workflow/@design.md and skills/product-rd-workflow/设计说明.md |\n' \
402
+ printf '| Fixture remainder row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure; firing-path: file:skills/product-rd-workflow/SKILL.md#%s | `updated` | rationale in skills/product-rd-workflow/@design.md and skills/product-rd-workflow/设计说明.md |\n' \
403
403
  "$(rule_anchor O3)" >> "$O3/$LEDGER_REL"
404
404
  git -C "$O3" add -A
405
405
  git -C "$O3" commit -qm "round 1: cite the owner with unusual but valid path remainders"
@@ -421,7 +421,7 @@ seed_repo "$O4"
421
421
  git -C "$O4" branch fixture-base HEAD
422
422
  git -C "$O4" switch -q -c case-relative
423
423
  mutate_owner_rule "$O4" product-rd-workflow O4
424
- printf '| Fixture relative-link row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; firing-path: file:skills/product-rd-workflow/SKILL.md#%s | `updated` | rationale in [the design note](../../product-rd-workflow/references/design-note.md) |\n' \
424
+ printf '| Fixture relative-link row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure; firing-path: file:skills/product-rd-workflow/SKILL.md#%s | `updated` | rationale in [the design note](../../product-rd-workflow/references/design-note.md) |\n' \
425
425
  "$(rule_anchor O4)" >> "$O4/$LEDGER_REL"
426
426
  git -C "$O4" add -A
427
427
  git -C "$O4" commit -qm "round 1: cite the owner through a Markdown-relative link"
@@ -471,7 +471,7 @@ S1="$TMP/leg-s"
471
471
  seed_repo "$S1"
472
472
  git -C "$S1" branch fixture-base HEAD
473
473
  git -C "$S1" switch -q -c case-malformed
474
- printf '| Fixture smuggled row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; firing-path: file:skills/product-rd-workflow/SKILL.md#nope | `updated` | rationale in skills/product-rd-workflow/references/note.md, see a|b for the table |\n' \
474
+ printf '| Fixture smuggled row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure; firing-path: file:skills/product-rd-workflow/SKILL.md#nope | `updated` | rationale in skills/product-rd-workflow/references/note.md, see a|b for the table |\n' \
475
475
  >> "$S1/$LEDGER_REL"
476
476
  git -C "$S1" add -A
477
477
  git -C "$S1" commit -qm "round 1: a declaring row for a selected owner with an unescaped pipe"
@@ -504,7 +504,7 @@ S3="$TMP/leg-s3"
504
504
  seed_repo "$S3"
505
505
  git -C "$S3" branch fixture-base HEAD
506
506
  git -C "$S3" switch -q -c case-emptycell
507
- printf '| Fixture empty-cell row | | behavioral-evidence: RED-baseline; observed-failure: yes; firing-path: file:skills/product-rd-workflow/SKILL.md#nope | `updated` | rationale in skills/product-rd-workflow/references/note.md |\n' \
507
+ printf '| Fixture empty-cell row | | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure; firing-path: file:skills/product-rd-workflow/SKILL.md#nope | `updated` | rationale in skills/product-rd-workflow/references/note.md |\n' \
508
508
  >> "$S3/$LEDGER_REL"
509
509
  git -C "$S3" add -A
510
510
  git -C "$S3" commit -qm "round 1: a declaring row for a selected owner with an empty cell"
@@ -527,7 +527,7 @@ git -C "$S4" branch fixture-base HEAD
527
527
  git -C "$S4" switch -q -c case-corrected
528
528
  mutate_owner_rule "$S4" product-rd-workflow S4
529
529
  append_valid_row "$S4" product-rd-workflow S4 "Fixture sound row"
530
- printf '| Fixture to-be-corrected row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; firing-path: file:skills/product-rd-workflow/SKILL.md#%s | `updated` | rationale in skills/product-rd-workflow/references/note.md, see a|b |\n' \
530
+ printf '| Fixture to-be-corrected row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure; firing-path: file:skills/product-rd-workflow/SKILL.md#%s | `updated` | rationale in skills/product-rd-workflow/references/note.md, see a|b |\n' \
531
531
  "$(rule_anchor S4b)" >> "$S4/$LEDGER_REL"
532
532
  git -C "$S4" add -A
533
533
  git -C "$S4" commit -qm "round 1: a sound row plus a malformed declaring row"
@@ -550,7 +550,7 @@ S5="$TMP/leg-s5"
550
550
  seed_repo "$S5"
551
551
  git -C "$S5" branch fixture-base HEAD
552
552
  git -C "$S5" switch -q -c case-noleading
553
- printf 'Fixture no-leading-pipe row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; firing-path: file:skills/product-rd-workflow/SKILL.md#nope | `updated` | rationale in skills/product-rd-workflow/references/note.md |\n' \
553
+ printf 'Fixture no-leading-pipe row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure; firing-path: file:skills/product-rd-workflow/SKILL.md#nope | `updated` | rationale in skills/product-rd-workflow/references/note.md |\n' \
554
554
  >> "$S5/$LEDGER_REL"
555
555
  git -C "$S5" add -A
556
556
  git -C "$S5" commit -qm "round 1: a declaring row without its leading pipe"
@@ -561,7 +561,7 @@ S6="$TMP/leg-s6"
561
561
  seed_repo "$S6"
562
562
  git -C "$S6" branch fixture-base HEAD
563
563
  git -C "$S6" switch -q -c case-notrailing
564
- printf '| Fixture no-trailing-pipe row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; firing-path: file:skills/product-rd-workflow/SKILL.md#nope | `updated` | rationale in skills/product-rd-workflow/references/note.md\n' \
564
+ printf '| Fixture no-trailing-pipe row | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure; firing-path: file:skills/product-rd-workflow/SKILL.md#nope | `updated` | rationale in skills/product-rd-workflow/references/note.md\n' \
565
565
  >> "$S6/$LEDGER_REL"
566
566
  git -C "$S6" add -A
567
567
  git -C "$S6" commit -qm "round 1: a declaring row without its trailing pipe"
@@ -0,0 +1,455 @@
1
+ #!/usr/bin/env bash
2
+ # 045:给「被裁决方自审」的义务装机械触发器的验收用例
3
+ # (specs/045-self-adjudicated-obligation-trigger/plan.md 的 Acceptance matrix)。
4
+ #
5
+ # 044 轮撤回过度声称时,把若干义务降成了咨询项,连续五轮 challenge 返回同一类:
6
+ # 义务的入口条件由被约束方自己陈述,且**跳过时不产生可检测的缺席**。本套件钉的是
7
+ # 那个缺席现在可被机械检出。
8
+ #
9
+ # 两个触发器都键在 diff 的客观事实上,不是散文语义:
10
+ # A 改了 routing 面(SKILL.md 的 frontmatter description,或 eval/routing-tasks.jsonl)
11
+ # ⇒ 该 owner 的台账行必须带 bank-evidence locator,或带留痕的 downscoped token。
12
+ # B 带 behavioral-evidence 的新增台账行 ⇒ 必须带合法 result-class。
13
+ #
14
+ # 期望红的用例都必须点名 WHICH refusal:rc 单独是弱 oracle,为无关原因(fixture 缺陷、
15
+ # 锚点断掉)红同样是 rc=1,会把「形态已关闭」读成绿。
16
+ set -u
17
+ ROOT="$(cd "$(dirname "$0")/../../.." && pwd -P)"
18
+ GATE="$ROOT/skills/skill-extraction-workflow/scripts/impact-chain-gate.rb"
19
+ LEDGER_REL="skills/skill-extraction-workflow/references/source-register.md"
20
+ BANK_REL="eval/routing-tasks.jsonl"
21
+ PLAN_REL="specs/045-self-adjudicated-obligation-trigger/plan.md"
22
+ TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT INT TERM
23
+
24
+ REPO="$TMP/repo"
25
+ git clone -q "$ROOT" "$REPO"
26
+ git -C "$REPO" config user.email test@example.invalid
27
+ git -C "$REPO" config user.name "Test User"
28
+ # A base WITHOUT the declaration, built by REMOVING it rather than by hoping the
29
+ # clone happens not to have it. Once this round is committed, `git clone "$ROOT"`
30
+ # carries the declaration, so a fixture that read the clone's HEAD as "before the
31
+ # grammar" passed from an uncommitted worktree and failed in CI — green meaning
32
+ # "not yet committed", which is the class of false green this suite exists for.
33
+ ORIG_HEAD="$(git -C "$REPO" rev-parse HEAD)"
34
+ git -C "$REPO" switch -q -C nogrammar-base "$ORIG_HEAD"
35
+ python3 - "$REPO/$LEDGER_REL" <<'STRIP'
36
+ import io, sys
37
+ p = sys.argv[1]
38
+ s = io.open(p, encoding="utf-8").read()
39
+ io.open(p, "w", encoding="utf-8").write(
40
+ "".join(l for l in s.splitlines(keepends=True)
41
+ if "result-class: failure|stable-success|insufficient-evidence" not in l))
42
+ STRIP
43
+ git -C "$REPO" add -A >/dev/null
44
+ git -C "$REPO" commit -qm "fixture base predating the 045 declaration"
45
+ PRE_GRAMMAR="$(git -C "$REPO" rev-parse HEAD)"
46
+ git -C "$REPO" switch -q -C fixture-seed "$ORIG_HEAD"
47
+ # Both triggers only fire for a round whose HEAD ledger declares the grammar, so
48
+ # the fixture base must carry that declaration. Seeded explicitly rather than
49
+ # inherited from the clone: `git clone` copies commits, so relying on the working
50
+ # tree would make the suite pass or fail depending on whether the author had
51
+ # committed yet — a green that means "not yet judged".
52
+ # A dedicated package for the rename case, so A18 does not have to rename a REAL
53
+ # owner and drag the whole rename machinery (and its own refusals) into the leg.
54
+ mkdir -p "$REPO/skills/fixture-rename-src"
55
+ printf -- '---\nname: fixture-rename-src\ndescription: A fixture routing surface that a later case renames and rewords at once.\n---\n\n# Fixture\n\n- Baseline rule present before any case.\n' > "$REPO/skills/fixture-rename-src/SKILL.md"
56
+ printf '\n%s\n' 'Fixture grammar declaration: result-class: failure|stable-success|insufficient-evidence' \
57
+ >> "$REPO/$LEDGER_REL"
58
+ git -C "$REPO" add -A >/dev/null
59
+ git -C "$REPO" commit -qm "seed grammar declaration for the 045 triggers"
60
+ git -C "$REPO" branch -f fixture-base HEAD >/dev/null
61
+
62
+ OWNER="product-rd-workflow"
63
+
64
+ new_case() { git -C "$REPO" switch -q -C "$1" fixture-base; }
65
+ commit_case() { git -C "$REPO" add -A >/dev/null; git -C "$REPO" commit -qm "$1"; }
66
+
67
+ anchor() { printf 'SELF-ADJUDICATION-FIXTURE-%s' "$1"; }
68
+
69
+ # A normative list rule the firing path can anchor on. Same shape the attribution
70
+ # suite uses: the anchor must land on a CHANGED list line carrying a normative verb.
71
+ add_owner_rule() { # <marker>
72
+ printf -- '- Rule %s must be recorded before the round lands.\n' "$(anchor "$1")" \
73
+ >> "$REPO/skills/$OWNER/SKILL.md"
74
+ }
75
+
76
+ # Edit the frontmatter `description` — the routing surface trigger A keys on.
77
+ # Appending to the body is NOT this; case A6 pins that distinction.
78
+ # The marker written here is deliberately DISTINCT from the rule anchor: the
79
+ # firing-path locator requires its anchor to occur exactly once in the file, and
80
+ # reusing one token made every A case red on a fixture defect rather than on the
81
+ # behaviour under test — a rc=1 that looked like the gate working. A SUFFIX is not
82
+ # enough either: the locator counts substring hits, so `…-A1-DESC` still contains
83
+ # `…-A1`. The distinguishing part has to come before the shared stem.
84
+ edit_owner_description() { # <marker>
85
+ python3 - "$REPO/skills/$OWNER/SKILL.md" "$(anchor "DESC-$1")" <<'PY'
86
+ import io, re, sys
87
+ path, marker = sys.argv[1], sys.argv[2]
88
+ s = io.open(path, encoding="utf-8").read()
89
+ m = re.match(r"\A---\s*\n(.*?)\n---\s*\n", s, re.S)
90
+ front = m.group(1)
91
+ new_front = re.sub(r"(?m)^(description:.*)$", r"\1 " + marker, front, count=1)
92
+ io.open(path, "w", encoding="utf-8").write(s[:m.start(1)] + new_front + s[m.end(1):])
93
+ PY
94
+ }
95
+
96
+ touch_bank() { printf '{"utterance":"fixture %s","expected_owner":"%s"}\n' "$1" "$OWNER" >> "$REPO/$BANK_REL"; }
97
+
98
+ write_plan() { # <downscope-token or empty>
99
+ mkdir -p "$REPO/$(dirname "$PLAN_REL")"
100
+ {
101
+ printf '# 045 fixture plan\n\n'
102
+ printf -- '- Bank evidence anchor %s must be reachable from the ledger row.\n' "$(anchor BANKEV)"
103
+ # An `[ -n ... ] && printf` tail returns 1 when the token is absent, and the
104
+ # group's status is the script's under errexit — that silently truncated the
105
+ # whole suite after its first leg. Spelled as an if so a no-token call is a
106
+ # normal success, not a run that ends without saying it ended.
107
+ if [ -n "${1:-}" ]; then
108
+ printf -- '- Downscope downscoped:%s is recorded here, so skipping the bank run still leaves an artifact.\n' "$1"
109
+ fi
110
+ } > "$REPO/$PLAN_REL"
111
+ return 0
112
+ }
113
+
114
+ # A row that satisfies every PRE-EXISTING requirement, so each case varies only
115
+ # the field under test. Extra evidence fragments are appended verbatim.
116
+ append_row() { # <marker> <lesson> <extra-behavior-fragments>
117
+ printf '| %s | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; firing-path: file:skills/%s/SKILL.md#%s%s | `updated` | `%s/SKILL.md` fixture change |\n' \
118
+ "$2" "$OWNER" "$(anchor "$1")" "$3" "$OWNER" >> "$REPO/$LEDGER_REL"
119
+ }
120
+
121
+ run_gate() {
122
+ set +e
123
+ out="$(env -u ALIAS_AUDIT_CMD CCL_SKILL_BASE_REF=fixture-base ruby "$GATE" "$REPO" 2>&1)"
124
+ rc=$?
125
+ set -e
126
+ }
127
+
128
+ legs_failed=0
129
+ report_leg() { # <leg-name> <expected-rc> <form-summary> [expected-token]
130
+ local name="$1" want="$2" summary="$3" token="${4:-}"
131
+ if [ "$rc" != "$want" ]; then
132
+ echo " $name: RED (rc=$rc, want $want) — $summary"
133
+ printf '%s\n' "$out" | sed 's/^/ | /'
134
+ legs_failed=$((legs_failed + 1))
135
+ return
136
+ fi
137
+ if [ -n "$token" ] && ! printf '%s' "$out" | grep -qF "$token"; then
138
+ echo " $name: RED (rc matched but refusal is not '$token') — $summary"
139
+ printf '%s\n' "$out" | sed 's/^/ | /'
140
+ legs_failed=$((legs_failed + 1))
141
+ return
142
+ fi
143
+ echo " ok $name — $summary"
144
+ }
145
+
146
+ RC_MISSING="impact_chain_bank_evidence_missing"
147
+ RC_CLASS="impact_chain_result_class_missing"
148
+
149
+ # ---- B:result 分类不可省略 ------------------------------------------------
150
+ # B1 带合法 result-class -> 通过
151
+ new_case case-b1; add_owner_rule B1; write_plan
152
+ append_row B1 'A fixture lesson whose row declares its result class' '; result-class: failure'
153
+ commit_case "B1"; run_gate
154
+ report_leg B1 0 "带合法 result-class 的行通过"
155
+
156
+ # B2 缺 result-class -> 红,且必须是这条拒绝
157
+ new_case case-b2; add_owner_rule B2; write_plan
158
+ append_row B2 'A fixture lesson whose row omits its result class' ''
159
+ commit_case "B2"; run_gate
160
+ report_leg B2 1 "缺 result-class 被拒(关闭「整格省略」这条绕过)" "$RC_CLASS"
161
+
162
+ # B3 result-class 值不在枚举 -> 红
163
+ new_case case-b3; add_owner_rule B3; write_plan
164
+ append_row B3 'A fixture lesson whose row invents a result class' '; result-class: probably-fine'
165
+ commit_case "B3"; run_gate
166
+ report_leg B3 1 "非枚举 result-class 被拒" "$RC_CLASS"
167
+
168
+ # B4 不带 behavioral-evidence 的行 + 无 owner 改动 -> 不触发
169
+ new_case case-b4; write_plan
170
+ printf '| A fixture row that declares nothing | `downstream-executor` | note only | `not-applicable` | supporting note |\n' \
171
+ >> "$REPO/$LEDGER_REL"
172
+ commit_case "B4"; run_gate
173
+ report_leg B4 0 "非本闸的行不被拖进 result-class 义务(保持既有 bifurcation)"
174
+
175
+ # ---- A:routing 面改动必须带 bank 证据 -------------------------------------
176
+ # A1 改 description + 带可解析 bank-evidence -> 通过
177
+ new_case case-a1; add_owner_rule A1; edit_owner_description A1; write_plan
178
+ append_row A1 'A fixture lesson whose routing-surface change carries bank evidence' \
179
+ "; result-class: failure; bank-evidence: file:$PLAN_REL#$(anchor BANKEV)"
180
+ commit_case "A1"; run_gate
181
+ report_leg A1 0 "改 description 且带 bank-evidence 通过"
182
+
183
+ # A2 改 description 但无 bank-evidence -> 红
184
+ new_case case-a2; add_owner_rule A2; edit_owner_description A2; write_plan
185
+ append_row A2 'A fixture lesson whose routing-surface change carries no bank evidence' '; result-class: failure'
186
+ commit_case "A2"; run_gate
187
+ report_leg A2 1 "改 description 却无 bank-evidence 被拒(关闭「不跑就没工件」)" "$RC_MISSING"
188
+
189
+ # A3 改 task-bank 但无 bank-evidence -> 红
190
+ new_case case-a3; add_owner_rule A3; touch_bank A3; write_plan
191
+ append_row A3 'A fixture lesson that edits the task bank without bank evidence' '; result-class: failure'
192
+ commit_case "A3"; run_gate
193
+ report_leg A3 1 "改 task-bank 却无 bank-evidence 被拒" "$RC_MISSING"
194
+
195
+ # A4 显式降范围且 token 留痕在 plan -> 通过
196
+ new_case case-a4; add_owner_rule A4; edit_owner_description A4; write_plan "DOWNSCOPE-A4"
197
+ append_row A4 'A fixture lesson that downscopes the bank run on the record' \
198
+ '; result-class: failure; bank-evidence: downscoped:DOWNSCOPE-A4'
199
+ commit_case "A4"; run_gate
200
+ report_leg A4 0 "降范围带留痕 token 通过(降范围仍产生工件)"
201
+
202
+ # A5 降范围但 token 不在任何 plan -> 红
203
+ new_case case-a5; add_owner_rule A5; edit_owner_description A5; write_plan
204
+ append_row A5 'A fixture lesson that downscopes with no recorded reason' \
205
+ '; result-class: failure; bank-evidence: downscoped:DOWNSCOPE-A5'
206
+ commit_case "A5"; run_gate
207
+ report_leg A5 1 "降范围 token 无留痕被拒(否则 downscoped: 就是新的自审出口)" "$RC_MISSING"
208
+
209
+ # A6 只改 SKILL.md 正文、未动 description -> 不触发
210
+ new_case case-a6; add_owner_rule A6; write_plan
211
+ append_row A6 'A fixture lesson that edits only the body' '; result-class: failure'
212
+ commit_case "A6"; run_gate
213
+ report_leg A6 0 "只改正文不触发 bank 义务(避免把正文编辑拖进来)"
214
+
215
+ # A7 bank-evidence 指回 owner 自己的包 -> 红
216
+ # 「工件存在」不等于「工件是证据」:description-only 的改动若能拿刚写下的那行当自己的
217
+ # bank 证据,闸就只是在核对一个自指的存在性。这条是本轮唯一一次收紧谓词,也正是本轮
218
+ # 要消灭的形态本身。
219
+ new_case case-a7; add_owner_rule A7; edit_owner_description A7; write_plan
220
+ append_row A7 'A fixture lesson citing its own package as bank evidence' \
221
+ "; result-class: failure; bank-evidence: file:skills/$OWNER/SKILL.md#$(anchor A7)"
222
+ commit_case "A7"; run_gate
223
+ report_leg A7 1 "自指 bank-evidence 被拒(改动本身不是关于它的证据)" "$RC_MISSING"
224
+
225
+ # G1 该轮 head 的台账未声明该语法 -> 不被追溯约束
226
+ # 这是非追溯性的承重件:没有它,新增一个必填字段会在任何回放里追溯拒掉全部历史轮
227
+ # (实测 64 个集成点里 34 个),而 verdict-differential 套件会正确地把那报成回归。
228
+ # 判据不是日期也不是 commit id(那些是会漂的代理),是「该轮 head 自己有没有声明」。
229
+ git -C "$REPO" switch -q -C case-g1 "$PRE_GRAMMAR"
230
+ add_owner_rule G1; write_plan
231
+ append_row G1 'A fixture lesson landing before the grammar was declared' ''
232
+ commit_case "G1"
233
+ set +e
234
+ out="$(env -u ALIAS_AUDIT_CMD CCL_SKILL_BASE_REF="$PRE_GRAMMAR" ruby "$GATE" "$REPO" 2>&1)"; rc=$?
235
+ set -e
236
+ report_leg G1 0 "head 未声明该语法的轮不被追溯约束(否则加字段=判死全部历史轮)"
237
+
238
+ # A8 只改 task-bank、不动任何 owner -> 仍必须红
239
+ # 第一版按 owner 迭代,bank-only 的改动根本进不了循环:路由 bank 可以随便动而无任何证据。
240
+ # A3 之所以没抓到,是因为它顺手也改了 owner——洞就藏在那条绿用例后面。
241
+ new_case case-a8; write_plan; touch_bank A8
242
+ printf '| A fixture lesson that moves the routing bank alone | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure | `updated` | supporting note only |\n' \
243
+ >> "$REPO/$LEDGER_REL"
244
+ commit_case "A8"; run_gate
245
+ report_leg A8 1 "只改 task-bank 也必须带 bank 证据(洞曾藏在 A3 的绿后面)" "$RC_MISSING"
246
+
247
+ # G2 声明过又被删掉 -> 红(不是「早于规则」,是关掉规则)
248
+ new_case case-g2; add_owner_rule G2; write_plan
249
+ python3 - "$REPO/$LEDGER_REL" <<'STRIP2'
250
+ import io, sys
251
+ p = sys.argv[1]
252
+ s = io.open(p, encoding="utf-8").read()
253
+ io.open(p, "w", encoding="utf-8").write(
254
+ "".join(l for l in s.splitlines(keepends=True)
255
+ if "result-class: failure|stable-success|insufficient-evidence" not in l))
256
+ STRIP2
257
+ append_row G2 'A fixture lesson that quietly removes the declaration' '; result-class: failure'
258
+ commit_case "G2"; run_gate
259
+ report_leg G2 1 "把声明删掉不等于早于规则,是静默关闭" "impact_chain_grammar_withdrawn"
260
+
261
+ # A9 只改 task-bank 且**一行台账都不加** -> 仍必须红
262
+ # A8 仍然加了行,于是 rows 非空、scope 存在;真正无人守的是「既不动 owner、也不动台账」
263
+ # 那条路径——外层进入条件和按行分组的循环都会放它过去。绿用例第二次挡住了它要覆盖的洞。
264
+ new_case case-a9; touch_bank A9
265
+ commit_case "A9"; run_gate
266
+ report_leg A9 1 "只动 bank、零台账行也必须红(外层进入条件曾漏掉这条路径)" "$RC_MISSING"
267
+
268
+ # A10 两个 owner 各改 description,只有一个带证据 -> 必须红,且点名欠证据的那个
269
+ # 义务由每个移动了自己路由面的 owner 各自承担;按轮求值会让一个 owner 的合法 locator
270
+ # 把整轮标成已满足,另一个就可以完全省略。
271
+ OWNER2="testing-strategy"
272
+ new_case case-a10; add_owner_rule A10; edit_owner_description A10; write_plan
273
+ printf -- '- Rule %s must be recorded before the round lands.\n' "$(anchor A10B)" >> "$REPO/skills/$OWNER2/SKILL.md"
274
+ python3 - "$REPO/skills/$OWNER2/SKILL.md" "$(anchor DESC-A10B)" <<'PY2'
275
+ import io, re, sys
276
+ path, marker = sys.argv[1], sys.argv[2]
277
+ s = io.open(path, encoding="utf-8").read()
278
+ m = re.match(r"\A---\s*\n(.*?)\n---\s*\n", s, re.S)
279
+ front = m.group(1)
280
+ new_front = re.sub(r"(?m)^(description:.*)$", r"\1 " + marker, front, count=1)
281
+ io.open(path, "w", encoding="utf-8").write(s[:m.start(1)] + new_front + s[m.end(1):])
282
+ PY2
283
+ append_row A10 'A fixture lesson whose first owner carries the evidence' \
284
+ "; result-class: failure; bank-evidence: file:$PLAN_REL#$(anchor BANKEV)"
285
+ printf '| A fixture lesson whose second owner carries none | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure; firing-path: file:skills/%s/SKILL.md#%s | `updated` | `%s/SKILL.md` fixture change |\n' \
286
+ "$OWNER2" "$(anchor A10B)" "$OWNER2" >> "$REPO/$LEDGER_REL"
287
+ commit_case "A10"; run_gate
288
+ report_leg A10 1 "多 owner 轮里每个 owner 各自欠证据(一个的 locator 不能替另一个还)" "$OWNER2"
289
+
290
+ # A11 自指的 command: 形态 -> 也必须红
291
+ # 第一版禁令只匹配 `file:skills/<owner>/`。同轮里顺手改了自己包内的一个脚本,就能写成
292
+ # `command:skills/<owner>/scripts/x.sh` 让改动给自己作证。两种 locator 都是在指一条路径,
293
+ # 禁的是那条路径,不是那个前缀拼法。
294
+ new_case case-a11; add_owner_rule A11; edit_owner_description A11; write_plan
295
+ printf '#!/usr/bin/env bash\necho fixture\n' > "$REPO/skills/$OWNER/scripts/fixture_a11.sh"
296
+ chmod +x "$REPO/skills/$OWNER/scripts/fixture_a11.sh"
297
+ append_row A11 'A fixture lesson citing its own package executable as bank evidence' \
298
+ "; result-class: failure; bank-evidence: command:skills/$OWNER/scripts/fixture_a11.sh"
299
+ commit_case "A11"; run_gate
300
+ report_leg A11 1 "自指的 command: 形态同样被拒(禁的是路径不是前缀拼法)" "$RC_MISSING"
301
+
302
+ # A12 跨轮不得串扰:轮1 只动 bank 且带证据,轮2 只动某 owner 的正文 -> 整体必须绿
303
+ # 曾经 routing_surface_touched 在 bank 变更时对**任何** owner 返回 true,于是轮1 会去要求
304
+ # 轮2 那个 owner 的行——把一个不相关的后续提交加进范围,就能改变对前一轮的判定。
305
+ # bank 是它自己的 subject,不是所有人的。
306
+ new_case case-a12; write_plan
307
+ touch_bank A12
308
+ printf '| A fixture round that moves the bank with evidence | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure; bank-evidence: file:%s#%s | `updated` | supporting note only |\n' \
309
+ "$PLAN_REL" "$(anchor BANKEV)" >> "$REPO/$LEDGER_REL"
310
+ commit_case "A12 round 1: bank only, evidenced"
311
+ add_owner_rule A12
312
+ append_row A12 'A later unrelated round that edits only owner body text' '; result-class: failure'
313
+ commit_case "A12 round 2: unrelated owner body"
314
+ run_gate
315
+ report_leg A12 0 "跨轮不串扰(bank 是自己的 subject,不是所有 owner 的)"
316
+
317
+ # A13 退化的 downscope token -> 必须红
318
+ # `\S+` 曾接受 `-`,而 include? 会在任意 bullet 行的连字符上命中:`downscoped:-` 就能
319
+ # 拿一条什么都没说的记录清偿义务。降范围是要被人查到的决定,偶然出现在散文里的字符不是。
320
+ new_case case-a13; add_owner_rule A13; edit_owner_description A13; write_plan
321
+ append_row A13 'A fixture lesson downscoping with a degenerate token' \
322
+ '; result-class: failure; bank-evidence: downscoped:-'
323
+ commit_case "A13"; run_gate
324
+ report_leg A13 1 "退化 token 不算留痕(`-` 会命中任意 bullet 的连字符)" "$RC_MISSING"
325
+
326
+ # B5 只动 bank 的轮里,声明行同样欠 result-class -> 必须红
327
+ # B 曾按变更 owner 收敛,而 bank-only 的轮没有变更 owner:那行声明行压根不被检查,
328
+ # 带着合法 bank-evidence 就整轮放行。非追溯性由 grammar 闸独立承担,B 不该再兼这份差事。
329
+ new_case case-b5; write_plan; touch_bank B5
330
+ printf '| A fixture bank-only round whose declaring row omits its class | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; bank-evidence: file:%s#%s | `updated` | supporting note only |\n' \
331
+ "$PLAN_REL" "$(anchor BANKEV)" >> "$REPO/$LEDGER_REL"
332
+ commit_case "B5"; run_gate
333
+ report_leg B5 1 "bank-only 轮的声明行同样欠 result-class(owner 收敛曾把它整个跳过)" "$RC_CLASS"
334
+
335
+ # A14 自指的等价路径拼法 -> 也必须红
336
+ # 禁令若拿裸 start_with? 比一种拼法,`./skills/<owner>/…` 与 `a/../` 绕路指的是同一个文件
337
+ # 却躲过检查。禁的是位置,就得在位置的规范形上判。
338
+ new_case case-a14; add_owner_rule A14; edit_owner_description A14; write_plan
339
+ append_row A14 'A fixture lesson citing its own package by an equivalent spelling' \
340
+ "; result-class: failure; bank-evidence: file:./skills/$OWNER/SKILL.md#$(anchor A14)"
341
+ commit_case "A14"; run_gate
342
+ report_leg A14 1 "等价路径拼法的自指同样被拒(在规范形上判位置)" "$RC_MISSING"
343
+
344
+ # A15 token 只是碰巧出现在别的行里 -> 必须红
345
+ # 只搜裸 token 时,一个常见英文词就能被无关行命中(`downscoped:evidence` 撞上
346
+ # 「Bank evidence anchor …」)。留痕要留的是那句声明本身,不是那个词。
347
+ new_case case-a15; add_owner_rule A15; edit_owner_description A15; write_plan
348
+ append_row A15 'A fixture lesson downscoping with an incidentally-occurring word' \
349
+ '; result-class: failure; bank-evidence: downscoped:evidence'
350
+ commit_case "A15"; run_gate
351
+ report_leg A15 1 "token 碰巧出现不算留痕(必须逐字记录 downscoped:<token>)" "$RC_MISSING"
352
+
353
+ # A16 新技能的第一版 description -> 必须红(创建也是路由面变更)
354
+ # 「两侧都要存在」曾把创建一并排除,理由写作「保守」。评审点破:一个新技能的第一版
355
+ # description 恰恰决定了哪些请求会到它那里,是最大的一次路由面变更,不是可豁免项。
356
+ new_case case-a16; write_plan
357
+ mkdir -p "$REPO/skills/fixture-new-owner"
358
+ printf -- '---\nname: fixture-new-owner\ndescription: A brand new routing surface that decides which requests reach this skill.\n---\n\n# Fixture\n\n- Rule %s must be recorded before the round lands.\n' "$(anchor A16)" \
359
+ > "$REPO/skills/fixture-new-owner/SKILL.md"
360
+ printf '| A fixture lesson introducing a new routing surface | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure; firing-path: file:skills/fixture-new-owner/SKILL.md#%s | `updated` | `fixture-new-owner/SKILL.md` fixture change |\n' \
361
+ "$(anchor A16)" >> "$REPO/$LEDGER_REL"
362
+ commit_case "A16"; run_gate
363
+ report_leg A16 1 "新技能的第一版 description 也欠 bank 证据(创建不是豁免)" "$RC_MISSING"
364
+
365
+ # A17 降范围 token 的前缀碰撞 -> 必须红
366
+ # `include?` 让 spec 里的 `downscoped:TOKENLONGER` 满足行里的 `downscoped:TOKEN`——
367
+ # 别人的 token 顺手替这一行还了债。声明必须在 token 边界处结束。
368
+ new_case case-a17; add_owner_rule A17; edit_owner_description A17; write_plan "DOWNSCOPE-A17-LONGER"
369
+ append_row A17 'A fixture lesson whose token is a strict prefix of the recorded one' \
370
+ '; result-class: failure; bank-evidence: downscoped:DOWNSCOPE-A17'
371
+ commit_case "A17"; run_gate
372
+ report_leg A17 1 "前缀碰撞不算留痕(声明须在 token 边界结束)" "$RC_MISSING"
373
+
374
+ # G3 删掉声明段、却把 marker 塞进一行台账 -> 仍必须按撤回处理
375
+ # 在整份 blob 上 include? 时,这样两边都读作「已声明」,撤回检测哑掉,之后每一轮
376
+ # 都继续被一个权威已不再陈述的语法判定。G2 抓不到它——它把所有含 marker 的行都删了。
377
+ new_case case-g3; add_owner_rule G3; write_plan
378
+ python3 - "$REPO/$LEDGER_REL" <<'STRIP3'
379
+ import io, sys
380
+ p = sys.argv[1]
381
+ s = io.open(p, encoding="utf-8").read()
382
+ kept = [l for l in s.splitlines(keepends=True)
383
+ if "result-class: failure|stable-success|insufficient-evidence" not in l]
384
+ kept.append("| A row that merely quotes result-class: failure|stable-success|insufficient-evidence | `x` | note | `not-applicable` | quote only |\n")
385
+ io.open(p, "w", encoding="utf-8").write("".join(kept))
386
+ STRIP3
387
+ append_row G3 'A fixture lesson hiding the marker inside a table row' '; result-class: failure'
388
+ commit_case "G3"; run_gate
389
+ report_leg G3 1 "marker 藏进台账行不算声明(行会引用,不会立法)" "impact_chain_grammar_withdrawn"
390
+
391
+ # A18 同一提交里改名 + 改 description -> 必须红
392
+ # 目标路径以「只是改名」被豁免、源路径以「没有 head 条目」被豁免,这次编辑就从两者之间
393
+ # 溜过去了。豁免给的是「description 没变」,不是「路径动过」。
394
+ new_case case-a18; write_plan
395
+ git -C "$REPO" mv skills/fixture-rename-src skills/fixture-rename-dst
396
+ python3 - "$REPO/skills/fixture-rename-dst/SKILL.md" "$(anchor A18)" <<'PY3'
397
+ import io, re, sys
398
+ path, marker = sys.argv[1], sys.argv[2]
399
+ s = io.open(path, encoding="utf-8").read()
400
+ m = re.match(r"\A---\s*\n(.*?)\n---\s*\n", s, re.S)
401
+ front = m.group(1)
402
+ new_front = re.sub(r"(?m)^(description:.*)$", r"\1 " + marker, front, count=1)
403
+ io.open(path, "w", encoding="utf-8").write(s[:m.start(1)] + new_front + s[m.end(1):])
404
+ PY3
405
+ printf -- '- Rule %s must be recorded before the round lands.\n' "$(anchor A18B)" >> "$REPO/skills/fixture-rename-dst/SKILL.md"
406
+ printf '| A fixture lesson renaming an owner and rewording its description at once | `downstream-executor` | behavioral-evidence: RED-baseline; observed-failure: yes; result-class: failure; firing-path: file:skills/fixture-rename-dst/SKILL.md#%s | `updated` | `fixture-rename-dst/SKILL.md` fixture change |\n' \
407
+ "$(anchor A18B)" >> "$REPO/$LEDGER_REL"
408
+ commit_case "A18"; run_gate
409
+ report_leg A18 1 "改名同时改 description 不得从两侧豁免之间溜过去" "$RC_MISSING"
410
+
411
+ # G4 撤回的诊断只能点名本闸真正实现的补救
412
+ # 上一版写着「或由具名风险 owner 承担」,而代码里没有任何解析风险 owner 的路径,exit 1
413
+ # 是无条件的——一条落不了地的补救承诺,等于把合法的有意撤回堵死却假装有出路。
414
+ # 这条钉的是**诊断与实现一致**:不许再出现只存在于措辞里的出口。
415
+ new_case case-g4; add_owner_rule G4; write_plan
416
+ python3 - "$REPO/$LEDGER_REL" <<'STRIP4'
417
+ import io, sys
418
+ p = sys.argv[1]
419
+ s = io.open(p, encoding="utf-8").read()
420
+ io.open(p, "w", encoding="utf-8").write(
421
+ "".join(l for l in s.splitlines(keepends=True)
422
+ if "result-class: failure|stable-success|insufficient-evidence" not in l))
423
+ STRIP4
424
+ append_row G4 'A fixture lesson withdrawing the declaration on purpose' '; result-class: failure'
425
+ commit_case "G4"; run_gate
426
+ if printf '%s' "$out" | grep -qi "named risk owner"; then
427
+ echo " G4: RED — 诊断仍在承诺一条本闸未实现的补救(named risk owner)"
428
+ legs_failed=$((legs_failed + 1))
429
+ else
430
+ report_leg G4 1 "撤回被拒,且诊断只点名真正实现的补救" "no data-side waiver"
431
+ fi
432
+
433
+ # A19 就地删掉 description 条目(文件保留)-> 必须红
434
+ # 「head 没有条目」曾被一律当作删除/改名走。可文件还在、只是不再声明路由面——那和改写它
435
+ # 一样是一次路由面变更,而且是让技能整个从路由里消失的那一种。
436
+ new_case case-a19; add_owner_rule A19; write_plan
437
+ python3 - "$REPO/skills/$OWNER/SKILL.md" <<'PY4'
438
+ import io, re, sys
439
+ path = sys.argv[1]
440
+ s = io.open(path, encoding="utf-8").read()
441
+ m = re.match(r"\A---\s*\n(.*?)\n---\s*\n", s, re.S)
442
+ front = m.group(1)
443
+ kept = [l for l in front.splitlines(keepends=True) if not l.startswith("description:")]
444
+ io.open(path, "w", encoding="utf-8").write(s[:m.start(1)] + "".join(kept).rstrip("\n") + s[m.end(1):])
445
+ PY4
446
+ append_row A19 'A fixture lesson deleting the description entry in place' '; result-class: failure'
447
+ commit_case "A19"; run_gate
448
+ report_leg A19 1 "就地删掉 description 也是路由面变更(文件还在,不是删除)" "$RC_MISSING"
449
+
450
+ echo "impact_chain_self_adjudication: legs_failed=$legs_failed"
451
+ if [ "$legs_failed" -ne 0 ]; then
452
+ echo "impact_chain_self_adjudication_failed=$legs_failed" >&2
453
+ exit 1
454
+ fi
455
+ echo "impact_chain_self_adjudication_ok"