@ccoalm/ccl-skills 0.16.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 (43) 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/SKILL.md +6 -4
  9. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/development-completion.md +13 -1
  10. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/staged-review-contract.md +78 -126
  11. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/wording-only-review.md +136 -0
  12. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/review_gate.py +521 -32
  13. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_client_compat.py +25 -2
  14. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_client_order.sh +30 -15
  15. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_gate.sh +439 -19
  16. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_update_review_plan_intent.sh +14 -7
  17. package/dist/assets/marketplace/plugins/ccl-skills/skills/multi-perspective-research/SKILL.md +1 -1
  18. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/SKILL.md +1 -1
  19. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/SKILL.md +6 -6
  20. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/attention-budget-ratchet.md +1 -0
  21. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/dual-track-review-gate.md +48 -119
  22. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/extraction-quickstart.md +9 -9
  23. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/firing-point-placement.md +22 -0
  24. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md +18 -0
  25. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/validation-and-landing.md +2 -2
  26. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check_review_evidence_present.py +122 -0
  27. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/contract-anchors.tsv +4 -1
  28. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/extraction_review_gate.sh +37 -8
  29. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/register-firing-path-resolution.rb +102 -20
  30. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_ai_coding_implementation_gates.sh +16 -27
  31. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_regressions.sh +3 -5
  32. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_review_evidence_present.sh +81 -0
  33. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_extraction_review_gate.sh +137 -279
  34. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_register_firing_path_resolution.sh +41 -1
  35. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_register_firing_path_wiring.sh +49 -3
  36. package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/SKILL.md +3 -2
  37. package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/references/closeout-reread.md +40 -0
  38. package/dist/assets/release.json +72 -52
  39. package/package.json +1 -1
  40. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/review_ledger_binding.py +0 -1242
  41. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_review_ledger_binding.sh +0 -978
  42. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_validate_extraction_review_state.sh +0 -1477
  43. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/validate_extraction_review_state.py +0 -1183
@@ -58,7 +58,12 @@ hand-attested plan (`review_plan_source=implementer-supplied` otherwise).
58
58
  Self-review accumulates stage concerns: explore covers correctness and
59
59
  safety; build adds failure paths, tests, and compatibility; release adds rollout
60
60
  and operations. High-risk input raises depth to release and adds
61
- `high_risk_boundary`.
61
+ `high_risk_boundary`. That set has one owner, and
62
+ `review_gate.sh --print-required-concerns --stage <stage> [--risk-tag <tag>]`
63
+ prints it, so a caller building a plan derives the list instead of keeping a copy
64
+ that silently stops satisfying the gate when the set changes. It prints what the
65
+ PLAN owes: the synthetic challenge slot and the wording-only boundary, which the
66
+ controller adds for the reviewer and never for the plan, are absent.
62
67
 
63
68
  The serialized plan is at most 32,000 bytes and `intent` is 8..4,000
64
69
  characters. Those are validation limits, not permission for a caller to slice a
@@ -168,6 +173,28 @@ top-level agents, commands, hooks, or MCP servers is not loaded.
168
173
  Wrappers keep an explicit selected-owner count instead of testing empty Bash
169
174
  arrays under `set -u`, preserving the no-owner lane on Bash 3.2.
170
175
 
176
+ ### The claim-strength walk, and why the late correction is not cheaper
177
+
178
+ `claim_strength` is a required self-review concern at build and release depth. The
179
+ plan walks the candidate's load-bearing claims — absolutes, universals, causal
180
+ statements, exhaustiveness — and for each one either names evidence that would
181
+ survive a challenge or weakens the claim on the spot. It is owed before round 1
182
+ because that is the only point in a round where correcting a claim is free: once a
183
+ round binds the candidate, an edit inside a selected owner package voids every
184
+ receipt bound to it, so a sentence that claims too much costs exactly what a changed
185
+ predicate costs. The concern also reaches the reviewer, so a claim that survives the
186
+ walk comes back as a round-1 finding — inside the fix batch the round was going to
187
+ pay for anyway — rather than at closeout, where the remaining moves are a fresh chain
188
+ or leaving it standing.
189
+
190
+ There is deliberately no cheap late path. The proof-bound single review
191
+ (`wording-only-review.md`) refuses any changed non-punctuation character, which is
192
+ exactly what weakening a claim is, and an exception keyed on the author's own "this
193
+ edit only weakens a claim" is an assertion the controller cannot re-derive — a waiver
194
+ of that shape was carried here once and removed, because a predicate that approximates
195
+ meaning keeps admitting shapes it did not anticipate. The price stays uniform in both
196
+ directions; the walk is what moves the correction to where the price is zero.
197
+
171
198
  ## Base-derived packet input boundary
172
199
 
173
200
  `--base` freezes the tracked diff plus every non-ignored untracked path in
@@ -187,10 +214,12 @@ and report the remaining packet as the whole candidate.
187
214
  ## The packet and the candidate
188
215
 
189
216
  They are two objects. The **packet** is what the reviewer reads; the **candidate**
190
- is what will land and what `review_ledger_binding.py` recomputes at merge time.
191
- A receipt records both hashes.
217
+ is what will land. A receipt records both hashes; a caller that needs the landing
218
+ tree to equal a reviewed candidate compares against the recorded candidate hash.
192
219
 
193
- They hold the same value when the packet came from `--base` alone. Pass
220
+ They hold the same value when the packet came from `--base` alone and no
221
+ repository contract file was quoted after it (review mode only; the result's
222
+ `repository_contract` field lists what was quoted). Pass
194
223
  `--diff-file` **with** `--base`/`--paths` to widen what the reviewer reads while
195
224
  the round still binds the landing candidate — the shape an evidence-gap finding
196
225
  needs, since editing the candidate would answer an input defect with a candidate
@@ -226,126 +255,9 @@ later round read more than an earlier one.
226
255
 
227
256
  ## Proof-bound wording-only single review
228
257
 
229
- The wording-only exception is one untracked `review` with
230
- `challenge_budget=0`; it is not a chain, challenge, or `complete` checkpoint.
231
- Supply `--wording-only-proof-file` to bind the exception to the exact packet.
232
- Without that proof, an explore/build budget-zero review remains an ordinary
233
- single review and cannot be recorded as the wording-only exception;
234
- release/high-risk budget zero fails before inference.
235
-
236
- At release depth, including depth raised by a high-risk tag, only a
237
- controller-proved `markdown-punctuation-only` check may use this exception.
238
- `markdown-token-replacement` remains available for explore/build budget-zero
239
- review, but it cannot waive the release/high-risk challenge: byte-exact token
240
- replacement does not prove that the old and new tokens have the same meaning.
241
-
242
- The proof is a single-link regular UTF-8 JSON file of at most 16,000 bytes:
243
-
244
- ```json
245
- {"schema_version":1,"candidate_sha256":"<packet-sha256>","check":{"kind":"markdown-punctuation-only"}}
246
- ```
247
-
248
- The other fixed check is
249
- `markdown-token-replacement`, whose `check` also contains `old_token`,
250
- `new_token`, and integer `expected_count` (1..100). The controller never trusts
251
- a caller-supplied pass result. It reparses the frozen packet and derives the
252
- status, files, changed-line count, replacement count, and scope SHA-256.
253
-
254
- The accepted packet is deliberately narrow: a canonical full-context unified
255
- Git diff, LF-terminated, at most 200,000 bytes, changing existing regular
256
- Markdown files inside exactly one existing non-linked skill package. Every
257
- file's first hunk starts at line 1 so frontmatter is inspectable. Adds,
258
- deletes, renames, multi-skill changes, frontmatter or `description` edits,
259
- non-regular Git modes, custom/compact packets, extra context outside the diff,
260
- and files without a final newline fail closed. `markdown-punctuation-only`
261
- accepts only one-for-one plain-prose line replacements whose non-punctuation
262
- characters remain identical; numeric tokens must additionally survive
263
- byte-for-byte (deleting the dot in `5.5` is a threshold change, not
264
- punctuation), and a question mark may not be added or removed (a statement
265
- turned into a question is a meaning change). Lines must start at column zero
266
- and contain prose; line adds/deletes, Markdown headings, lists, block quotes,
267
- links, tables, inline code, fenced or indented code, and raw HTML `pre`/`code`
268
- containers fail closed. `markdown-token-replacement` requires every changed
269
- line pair to differ only by the named whole-token replacement, with the exact
270
- total count, and rejects packets whose changed lines touch a Markdown or HTML
271
- code container.
272
-
273
- This recipe produces the exact packet and proof without a second parser or a
274
- pretend verifier command. Set `WORDING_KIND=markdown-punctuation-only`, or set
275
- `WORDING_KIND=markdown-token-replacement` plus `WORDING_OLD`, `WORDING_NEW`, and
276
- `WORDING_COUNT`:
277
-
278
- ```bash
279
- : "${CODE_REVIEW_SKILL_DIR:?set the installed code-review skill directory}"
280
- : "${REPO_ROOT:?set the absolute repository root}"
281
- : "${REVIEW_BASE:?set the exact base ref}"
282
- : "${SKILL_NAME:?set the one existing skill package name}"
283
- : "${REVIEW_STAGE:?set explore, build, or release}"
284
- : "${IMPLEMENTER_FAMILY:?set the implementer model family}"
285
- : "${REVIEW_PLAN_FILE:?set the absolute review-plan JSON path}"
286
- : "${REVIEW_EVIDENCE_DIR:?set an existing durable private evidence directory}"
287
- : "${WORDING_KIND:?set one supported wording-only check kind}"
288
-
289
- umask 077
290
- WORDING_RUN_DIR="$(mktemp -d "$REVIEW_EVIDENCE_DIR/wording-review.XXXXXX")" || exit 1
291
- WORDING_DIFF="$WORDING_RUN_DIR/candidate.diff"
292
- WORDING_PROOF="$WORDING_RUN_DIR/proof.json"
293
- WORDING_RESULT="$WORDING_RUN_DIR/review.json"
294
-
295
- git -C "$REPO_ROOT" diff --no-color --no-ext-diff --no-textconv --full-index \
296
- --src-prefix=a/ --dst-prefix=b/ --unified=1000000 \
297
- "$REVIEW_BASE" -- "skills/$SKILL_NAME" >"$WORDING_DIFF" || exit 1
298
-
299
- python3 - "$WORDING_DIFF" "$WORDING_PROOF" "$WORDING_KIND" \
300
- "${WORDING_OLD:-}" "${WORDING_NEW:-}" "${WORDING_COUNT:-0}" <<'PY'
301
- import hashlib
302
- import json
303
- import sys
304
- from pathlib import Path
305
-
306
- diff_path, proof_path = map(Path, sys.argv[1:3])
307
- kind, old, new, count = sys.argv[3:]
308
- check = {"kind": kind}
309
- if kind == "markdown-token-replacement":
310
- check.update(old_token=old, new_token=new, expected_count=int(count))
311
- elif kind != "markdown-punctuation-only":
312
- raise SystemExit("unsupported WORDING_KIND")
313
- payload = {
314
- "schema_version": 1,
315
- "candidate_sha256": hashlib.sha256(diff_path.read_bytes()).hexdigest(),
316
- "check": check,
317
- }
318
- proof_path.write_text(
319
- json.dumps(payload, ensure_ascii=False, separators=(",", ":")) + "\n",
320
- encoding="utf-8",
321
- )
322
- PY
323
-
324
- WORDING_RISK_ARGS=()
325
- for tag in ${REVIEW_RISK_TAGS:-}; do WORDING_RISK_ARGS+=(--risk-tag "$tag"); done
326
- if ! bash "$CODE_REVIEW_SKILL_DIR/scripts/review_gate.sh" \
327
- --mode review --stage "$REVIEW_STAGE" --challenge-budget 0 \
328
- --cwd "$REPO_ROOT" --diff-file "$WORDING_DIFF" \
329
- --review-plan-file "$REVIEW_PLAN_FILE" \
330
- --wording-only-proof-file "$WORDING_PROOF" \
331
- ${WORDING_RISK_ARGS[@]+"${WORDING_RISK_ARGS[@]}"} \
332
- --implementer-family "$IMPLEMENTER_FAMILY" >"$WORDING_RESULT"; then
333
- cat "$WORDING_RESULT" >&2
334
- exit 1
335
- fi
336
- cat "$WORDING_RESULT"
337
- ```
338
-
339
- A valid result carries `wording_only_proof_sha256`, controller-derived
340
- `wording_only_scope.status=passed`, and a reviewed
341
- `wording_only_boundary` concern. That concern independently confirms the edit
342
- changes no trigger, scope, routing, validation, acceptance, rule, threshold,
343
- boundary, frontmatter, description, or other meaning. If it is missing,
344
- inconclusive, or reports a possible semantic change, the wording-only exception
345
- does not apply: use the normal challenge and behavioral-evidence path. Any
346
- candidate edit regenerates the packet and proof and requires a new review.
347
- Keep the diff, proof, and result together; a digest whose source artifact was
348
- deleted is not independently auditable evidence.
258
+ The wording-only exception — its depth limits, the proof schema, the accepted
259
+ packet, the recipe that produces both, and how a valid result is read — is
260
+ specified in `wording-only-review.md`.
349
261
 
350
262
  ## Agent review chain
351
263
 
@@ -362,8 +274,10 @@ index 1; an untracked initial review is single-round and therefore uses budget 0
362
274
  candidate can never be challenged inside it. One succeeding chain may open at
363
275
  index 1 in `challenge` mode by supplying `--predecessor-chain-result-file` — the
364
276
  ended chain's terminal receipt — instead of an in-chain prior result. The
365
- controller accepts it only when that receipt is a tracked challenge at its own
366
- chain's terminal index, carries this chain's `review_scope_sha256` and matching
277
+ controller accepts it only when that receipt is the tracked round its chain ended
278
+ on — a terminal challenge, or a round-1 review whose own
279
+ arithmetic still reports its challenge unspent — carrying this chain's
280
+ `review_scope_sha256` and matching
367
281
  stage/depth/risk-tags/budget, preserves the controller digest, owner-selection
368
282
  source, and selected owner names, and binds a candidate that DIFFERS from this
369
283
  packet: the owner-package digest is the one binding allowed to move, because its
@@ -375,6 +289,24 @@ differ from every focus the ended chain spent. The result records
375
289
  satisfied self-review trigger. Succession carries history rather than resetting
376
290
  it: consumers still sum rounds across both chains.
377
291
 
292
+ A chain ends where the candidate moves, and a fix applied straight after the review
293
+ moves the owner digest exactly as one applied after the challenge does. Requiring a
294
+ challenge receipt here never protected the landing candidate — the succession
295
+ challenge binds that either way — it only forced the challenge to be spent on a
296
+ candidate the author had already decided to replace. The single class that stops
297
+ being owed is a challenge on a candidate that will never land, which carries no
298
+ evidence about the one that does; every other binding is unchanged, the candidate
299
+ must still have moved, succession still does not compose, and this path spends
300
+ fewer rounds than the old one, never more. What bounds it is the receipt's own arithmetic, and that is a
301
+ forgery guard rather than a history check: a genuine round-1 review reads the same
302
+ whether its chain later ran a challenge or not, so a caller who spent the challenge
303
+ and presents only the review is accepted, and the successor inherits no challenge
304
+ focuses — a focus that chain did spend can be spent again. This is the same
305
+ omitted-history boundary the rest of this contract states rather than a new one, and
306
+ the closeout validator's ordered receipt set is where a retained challenge receipt
307
+ would show it; no check at the succession call site can close it, and none is
308
+ claimed.
309
+
378
310
  The chain binds task scope, candidate identity per round, result hashes, mode,
379
311
  status, challenge focus, controller, and selected owners. The opaque
380
312
  `review_scope_sha256` always hashes normalized intent, acceptance, stage/depth,
@@ -430,6 +362,26 @@ checkpoint, and before a completion claim. Findings never produce a blind
430
362
  review-fix-review loop: they block another reviewer call, return to implementer
431
363
  triage, and still allow implementation, tests, and independent runnable work.
432
364
 
365
+ **Findings that come back are a design question.** When a round returns findings and
366
+ the history it carries already holds one — an earlier round of this chain, or the
367
+ predecessor chain a succession names — the gate adds
368
+ `recurring_findings_design_check` to the required triggers and
369
+ `decide_keep_delete_narrow_replace` to the allowed actions. It blocks nothing that
370
+ `findings_returned` does not already block; what it adds is the question the next
371
+ patch would walk past: whether the reviewed surface should exist in this shape at
372
+ all, answered as `keep`, `delete`, `narrow`, or `replace`, resting on the rounds and
373
+ findings it recurred across, and ratified by a risk owner other than the one
374
+ proposing it. `../../skill-extraction-workflow/SKILL.md` owns that rule; this is where
375
+ it fires, because the situation arises inside a chain and that skill is usually not
376
+ loaded there. Two findings rounds need not share a class, so the trigger over-fires
377
+ by design — answering an inapplicable question costs a line, and the round it saves
378
+ does not.
379
+
380
+ The count is what the controller can prove, and no more: the rounds of this chain plus
381
+ the predecessor a succession names, which is why the trigger reaches across a chain
382
+ break at all (Chain succession, below). Succession does not compose, so a third chain
383
+ opened fresh carries no history and the recurrence becomes the round's own record.
384
+
433
385
  A passed final external round returns
434
386
  `next_action=deep_self_review_before_completion` and remains
435
387
  `completion_gated=true`. `--mode complete` accepts one exact-candidate passed
@@ -0,0 +1,136 @@
1
+ # Proof-bound wording-only single review
2
+
3
+ The wording-only exception to `staged-review-contract.md`: when one review may
4
+ stand in for the review-plus-challenge pair, what the controller re-derives
5
+ before it will say so, and how to produce the packet and proof it accepts.
6
+
7
+ ## The exception and its depth limits
8
+
9
+ The wording-only exception is one untracked `review` with
10
+ `challenge_budget=0`; it is not a chain, challenge, or `complete` checkpoint.
11
+ Supply `--wording-only-proof-file` to bind the exception to the exact packet.
12
+ Without that proof, an explore/build budget-zero review remains an ordinary
13
+ single review and cannot be recorded as the wording-only exception;
14
+ release/high-risk budget zero fails before inference.
15
+
16
+ At release depth, including depth raised by a high-risk tag, only a
17
+ controller-proved `markdown-punctuation-only` check may use this exception.
18
+ `markdown-token-replacement` remains available for explore/build budget-zero
19
+ review, but it cannot waive the release/high-risk challenge: byte-exact token
20
+ replacement does not prove that the old and new tokens have the same meaning.
21
+
22
+ ## The proof
23
+
24
+ The proof is a single-link regular UTF-8 JSON file of at most 16,000 bytes:
25
+
26
+ ```json
27
+ {"schema_version":1,"candidate_sha256":"<packet-sha256>","check":{"kind":"markdown-punctuation-only"}}
28
+ ```
29
+
30
+ The other fixed check is
31
+ `markdown-token-replacement`, whose `check` also contains `old_token`,
32
+ `new_token`, and integer `expected_count` (1..100). The controller never trusts
33
+ a caller-supplied pass result. It reparses the frozen packet and derives the
34
+ status, files, changed-line count, replacement count, and scope SHA-256.
35
+
36
+ ## The accepted packet
37
+
38
+ The accepted packet is deliberately narrow: a canonical full-context unified
39
+ Git diff, LF-terminated, at most 200,000 bytes, changing existing regular
40
+ Markdown files inside exactly one existing non-linked skill package. Every
41
+ file's first hunk starts at line 1 so frontmatter is inspectable. Adds,
42
+ deletes, renames, multi-skill changes, frontmatter or `description` edits,
43
+ non-regular Git modes, custom/compact packets, extra context outside the diff,
44
+ and files without a final newline fail closed. `markdown-punctuation-only`
45
+ accepts only one-for-one plain-prose line replacements whose non-punctuation
46
+ characters remain identical; numeric tokens must additionally survive
47
+ byte-for-byte (deleting the dot in `5.5` is a threshold change, not
48
+ punctuation), and a question mark may not be added or removed (a statement
49
+ turned into a question is a meaning change). Lines must start at column zero
50
+ and contain prose; line adds/deletes, Markdown headings, lists, block quotes,
51
+ links, tables, inline code, fenced or indented code, and raw HTML `pre`/`code`
52
+ containers fail closed. `markdown-token-replacement` requires every changed
53
+ line pair to differ only by the named whole-token replacement, with the exact
54
+ total count, and rejects packets whose changed lines touch a Markdown or HTML
55
+ code container.
56
+
57
+ ## Producing the packet and proof
58
+
59
+ This recipe produces the exact packet and proof without a second parser or a
60
+ pretend verifier command. Set `WORDING_KIND=markdown-punctuation-only`, or set
61
+ `WORDING_KIND=markdown-token-replacement` plus `WORDING_OLD`, `WORDING_NEW`, and
62
+ `WORDING_COUNT`:
63
+
64
+ ```bash
65
+ : "${CODE_REVIEW_SKILL_DIR:?set the installed code-review skill directory}"
66
+ : "${REPO_ROOT:?set the absolute repository root}"
67
+ : "${REVIEW_BASE:?set the exact base ref}"
68
+ : "${SKILL_NAME:?set the one existing skill package name}"
69
+ : "${REVIEW_STAGE:?set explore, build, or release}"
70
+ : "${IMPLEMENTER_FAMILY:?set the implementer model family}"
71
+ : "${REVIEW_PLAN_FILE:?set the absolute review-plan JSON path}"
72
+ : "${REVIEW_EVIDENCE_DIR:?set an existing durable private evidence directory}"
73
+ : "${WORDING_KIND:?set one supported wording-only check kind}"
74
+
75
+ umask 077
76
+ WORDING_RUN_DIR="$(mktemp -d "$REVIEW_EVIDENCE_DIR/wording-review.XXXXXX")" || exit 1
77
+ WORDING_DIFF="$WORDING_RUN_DIR/candidate.diff"
78
+ WORDING_PROOF="$WORDING_RUN_DIR/proof.json"
79
+ WORDING_RESULT="$WORDING_RUN_DIR/review.json"
80
+
81
+ git -C "$REPO_ROOT" diff --no-color --no-ext-diff --no-textconv --full-index \
82
+ --src-prefix=a/ --dst-prefix=b/ --unified=1000000 \
83
+ "$REVIEW_BASE" -- "skills/$SKILL_NAME" >"$WORDING_DIFF" || exit 1
84
+
85
+ python3 - "$WORDING_DIFF" "$WORDING_PROOF" "$WORDING_KIND" \
86
+ "${WORDING_OLD:-}" "${WORDING_NEW:-}" "${WORDING_COUNT:-0}" <<'PY'
87
+ import hashlib
88
+ import json
89
+ import sys
90
+ from pathlib import Path
91
+
92
+ diff_path, proof_path = map(Path, sys.argv[1:3])
93
+ kind, old, new, count = sys.argv[3:]
94
+ check = {"kind": kind}
95
+ if kind == "markdown-token-replacement":
96
+ check.update(old_token=old, new_token=new, expected_count=int(count))
97
+ elif kind != "markdown-punctuation-only":
98
+ raise SystemExit("unsupported WORDING_KIND")
99
+ payload = {
100
+ "schema_version": 1,
101
+ "candidate_sha256": hashlib.sha256(diff_path.read_bytes()).hexdigest(),
102
+ "check": check,
103
+ }
104
+ proof_path.write_text(
105
+ json.dumps(payload, ensure_ascii=False, separators=(",", ":")) + "\n",
106
+ encoding="utf-8",
107
+ )
108
+ PY
109
+
110
+ WORDING_RISK_ARGS=()
111
+ for tag in ${REVIEW_RISK_TAGS:-}; do WORDING_RISK_ARGS+=(--risk-tag "$tag"); done
112
+ if ! bash "$CODE_REVIEW_SKILL_DIR/scripts/review_gate.sh" \
113
+ --mode review --stage "$REVIEW_STAGE" --challenge-budget 0 \
114
+ --cwd "$REPO_ROOT" --diff-file "$WORDING_DIFF" \
115
+ --review-plan-file "$REVIEW_PLAN_FILE" \
116
+ --wording-only-proof-file "$WORDING_PROOF" \
117
+ ${WORDING_RISK_ARGS[@]+"${WORDING_RISK_ARGS[@]}"} \
118
+ --implementer-family "$IMPLEMENTER_FAMILY" >"$WORDING_RESULT"; then
119
+ cat "$WORDING_RESULT" >&2
120
+ exit 1
121
+ fi
122
+ cat "$WORDING_RESULT"
123
+ ```
124
+
125
+ ## Validating the result
126
+
127
+ A valid result carries `wording_only_proof_sha256`, controller-derived
128
+ `wording_only_scope.status=passed`, and a reviewed
129
+ `wording_only_boundary` concern. That concern independently confirms the edit
130
+ changes no trigger, scope, routing, validation, acceptance, rule, threshold,
131
+ boundary, frontmatter, description, or other meaning. If it is missing,
132
+ inconclusive, or reports a possible semantic change, the wording-only exception
133
+ does not apply: use the normal challenge and behavioral-evidence path. Any
134
+ candidate edit regenerates the packet and proof and requires a new review.
135
+ Keep the diff, proof, and result together; a digest whose source artifact was
136
+ deleted is not independently auditable evidence.