cc-discipline 2.12.3 → 2.13.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.
package/README.md CHANGED
@@ -17,9 +17,11 @@ Markdown rules help, but Claude can ignore them. **Hooks can't be ignored** —
17
17
 
18
18
  ### Hooks (the enforcer)
19
19
 
20
- Three shell scripts that run automatically before/after Claude's actions:
20
+ Seven shell scripts run automatically before and after Claude's actions. Two of them can stop an operation outright — `git-guard.sh` on destructive git commands, and `streak-breaker.sh` once a file passes its edit ceiling. The rest inject context rather than block. The three below are the ones worth seeing in action:
21
21
 
22
- **`streak-breaker.sh`** — Tracks per-file edit counts. Warns at 3, **hard-blocks at 5**. Forces Claude to stop and find the root cause instead of endlessly patching.
22
+ **`streak-breaker.sh`** — Tracks per-file edit counts. Source files warn at 6 and **hard-block at 10**; config and docs warn at 10 and block at 16. Confirming that a run of edits is planned doubles both thresholds for that file. Forces Claude to stop and find the root cause instead of endlessly patching.
23
+
24
+ > Raised from 3/5 on 2026-07-30. Newer models complete multi-file features end to end rather than one pass per file, so five edits to one source file is now common in legitimate work — the old thresholds fired on progress, not on circling.
23
25
 
24
26
  Here's what it looks like in practice:
25
27
 
@@ -34,25 +36,26 @@ Claude: The regex wasn't enough, let me also add length check.
34
36
 
35
37
  Claude: That broke the existing tests, let me adjust.
36
38
  [Edit src/auth.py — fix test compatibility]
37
- ⚠️ EDIT NOTE: File src/auth.py has been edited 3 times.
39
+ ⚠️ EDIT NOTE: File src/auth.py has been edited 6 times.
38
40
  Quick check: are these edits building toward a goal,
39
41
  or fixing previous edits?
40
42
 
41
43
  Claude: Let me try a different approach to the validation.
42
44
  [Edit src/auth.py — rewrite validation layer]
45
+ [… three more edits …]
43
46
 
44
47
  Claude: Almost there, one more adjustment...
45
48
  [Edit src/auth.py — blocked]
46
49
 
47
50
  🛑 EDIT CHECKPOINT
48
- File src/auth.py has been edited 5 times this session.
51
+ File src/auth.py has been edited 10 times this session.
49
52
  Reflect:
50
53
  1. Are these edits progressing toward a goal,
51
54
  or fixing previous edits?
52
55
  2. If fixing previous edits: pause and look
53
56
  for the root cause.
54
57
 
55
- Claude: You're right. Looking at all 5 edits, I was patching
58
+ Claude: You're right. Looking at all 10 edits, I was patching
56
59
  symptoms. The real issue is that the User model's
57
60
  validate() method is called before sanitization.
58
61
  Let me fix it at the source instead.
@@ -62,7 +65,9 @@ Without the hook, Claude would keep editing `auth.py` indefinitely — each fix
62
65
 
63
66
  ---
64
67
 
65
- **`pre-edit-guard.sh`** — Checks `docs/debug-log.md` for unverified hypotheses. If you're debugging and haven't finished the process, it blocks source code edits until you do.
68
+ **`pre-edit-guard.sh`** — Before a source edit, checks `docs/debug-log.md` for hypotheses still marked pending, flags unusually large diffs, and reminds you to register newly created scripts. These are context notes, not blocks.
69
+
70
+ > Downgraded from a hard block on 2026-07-30. Combined with the debugging rules it had become a trap: write three hypotheses into the log as the rules ask, and you were then barred from editing source until three were confirmed — including for unrelated planned work. The hypothesis discipline stays in the rules; only the enforcement was relaxed.
66
71
 
67
72
  ```
68
73
  You: "The API is returning 500 errors on /users endpoint"
@@ -73,11 +78,12 @@ Claude: Let me investigate.
73
78
 
74
79
  Claude: I think it's a database connection issue. Let me fix
75
80
  the connection pool config.
76
- [Edit src/db.py — blocked]
81
+ [Edit src/db.py]
77
82
 
78
- 🛑 docs/debug-log.md has 2 unverified hypotheses.
79
- Please complete the debugging process (verify or
80
- eliminate hypotheses) before editing source code.
83
+ DEBUG-LOG NOTE: docs/debug-log.md has 2 hypotheses
84
+ still marked pending. If this edit is the fix for one
85
+ of them, update its status first so the log stays
86
+ truthful. If this edit is unrelated work, carry on.
81
87
 
82
88
  Claude: Right, I haven't verified my hypothesis yet.
83
89
  Let me check the actual error logs first.
@@ -188,8 +194,9 @@ CLAUDE.md # Your project info (you fill this in)
188
194
 
189
195
  ```bash
190
196
  # In .claude/hooks/streak-breaker.sh
191
- WARN_THRESHOLD=3 # Warn after N edits to same file
192
- STOP_THRESHOLD=5 # Hard block after N edits
197
+ WARN_THRESHOLD=6 # Warn after N edits to same source file
198
+ STOP_THRESHOLD=10 # Hard block after N edits
199
+ # Config and doc files use 10/16; a confirmed run doubles both
193
200
  ```
194
201
 
195
202
  **Add your own rules:**
package/README.zh-CN.md CHANGED
@@ -84,7 +84,7 @@ your-project/
84
84
  │ │ └── mobile.md # 移动端
85
85
  │ ├── hooks/
86
86
  │ │ ├── pre-edit-guard.sh # 编辑前纪律检查
87
- │ │ ├── streak-breaker.sh # 打地鼠检测(同文件编辑≥5次硬停止)
87
+ │ │ ├── streak-breaker.sh # 打地鼠检测(源码同文件编辑≥10次硬停止)
88
88
  │ │ └── post-error-remind.sh # 错误后调试流程提醒
89
89
  │ ├── agents/
90
90
  │ │ ├── reviewer.md # 代码审查员(独立 context)
@@ -127,8 +127,8 @@ Hooks 是在 Claude 执行操作前/后自动运行的 shell 脚本。**exit 2
127
127
 
128
128
  | Hook | 触发时机 | 作用 |
129
129
  |------|---------|------|
130
- | `streak-breaker.sh` | 编辑文件前 | 同文件编辑3 次警告,≥5 次硬停止 |
131
- | `pre-edit-guard.sh` | 编辑文件前 | 检查是否有未完成的调试流程 |
130
+ | `streak-breaker.sh` | 编辑文件前 | 源码同文件编辑6 次警告、**≥10 次硬停止**;配置与文档为 10 / 16。用户确认为计划内改动后,两个阈值翻倍 |
131
+ | `pre-edit-guard.sh` | 编辑文件前 | 检查 debug-log 中仍标 pending 的假设、超大 diff、新建脚本 —— **均为上下文提示,不阻断**(2026-07-30 从硬阻断降级) |
132
132
  | `post-error-remind.sh` | 执行命令后 | 检测到错误输出时提醒遵守调试纪律 |
133
133
 
134
134
  **实际效果演示:**
@@ -146,22 +146,23 @@ Claude: 正则不够,再加个长度检查。
146
146
 
147
147
  Claude: 把之前的测试搞挂了,调整一下。
148
148
  [编辑 src/auth.py — 修复测试兼容性]
149
- ⚠️ 提示:auth.py 已被编辑 3 次。
149
+ ⚠️ 提示:auth.py 已被编辑 6 次。
150
150
  检查一下:这些修改是在推进目标,还是在修前面的修改?
151
151
 
152
152
  Claude: 换个思路重写验证层。
153
153
  [编辑 src/auth.py — 重写验证逻辑]
154
+ [… 中间还有三次编辑 …]
154
155
 
155
156
  Claude: 差不多了,再调一下...
156
157
  [编辑 src/auth.py — 被阻止]
157
158
 
158
159
  🛑 编辑检查点
159
- 文件 auth.py 本次会话已被编辑 5 次。
160
+ 文件 auth.py 本次会话已被编辑 10 次。
160
161
  反思:
161
162
  1. 这些修改是在推进目标,还是在修之前的修改?
162
163
  2. 如果是后者:停下来,寻找根本原因。
163
164
 
164
- Claude: 你说得对。回顾这 5 次编辑,我一直在修症状。
165
+ Claude: 你说得对。回顾这 10 次编辑,我一直在修症状。
165
166
  真正的问题是 User 模型的 validate() 在
166
167
  sanitization 之前被调用了。我应该从源头修。
167
168
  ```
@@ -170,7 +171,7 @@ Claude: 你说得对。回顾这 5 次编辑,我一直在修症状。
170
171
 
171
172
  ---
172
173
 
173
- **pre-edit-guard** — 阻止跳过诊断直接改代码:
174
+ **pre-edit-guard** — 在跳过诊断直接改代码时提示(2026-07-30 起为提示,不阻断):
174
175
 
175
176
  ```
176
177
  你: "API 的 /users 接口返回 500 错误"
@@ -180,11 +181,12 @@ Claude: 我来排查。
180
181
  → 500 Internal Server Error
181
182
 
182
183
  Claude: 我觉得是数据库连接池的问题,改一下配置。
183
- [编辑 src/db.py — 被阻止]
184
+ [编辑 src/db.py]
184
185
 
185
- 🛑 docs/debug-log.md 中有 2 个未验证的假设。
186
- 请先完成调试流程(验证或排除假设),
187
- 再编辑源代码。
186
+ DEBUG-LOG 提示:docs/debug-log.md 中有 2 个假设
187
+ 仍标记为 pending。如果这次编辑就是其中某条的修复,
188
+ 请先更新它的状态,让日志保持真实;
189
+ 如果是无关的工作,继续即可。
188
190
 
189
191
  Claude: 对,我还没验证我的假设。先看看错误日志。
190
192
  [执行: tail -50 logs/error.log]
@@ -262,8 +264,9 @@ EOF
262
264
 
263
265
  编辑 `.claude/hooks/streak-breaker.sh`:
264
266
  ```bash
265
- WARN_THRESHOLD=3 # Warning after N edits to same file
266
- STOP_THRESHOLD=5 # Hard block after N edits to same file
267
+ WARN_THRESHOLD=6 # 同一源码文件编辑 N 次后警告
268
+ STOP_THRESHOLD=10 # 同一源码文件编辑 N 次后硬停止
269
+ # 配置与文档文件为 10/16;用户确认为计划内改动后两者翻倍
267
270
  ```
268
271
 
269
272
  ### 添加新的子代理
package/init.sh CHANGED
@@ -394,13 +394,52 @@ echo -e "${GREEN}Installing skills...${NC}"
394
394
  SKILLS_MANIFEST=".claude/.cc-discipline-skills.manifest"
395
395
  NEW_MANIFEST=$(mktemp 2>/dev/null || echo ".claude/.skills-manifest.tmp")
396
396
  PRESERVED_SKILLS=""
397
-
397
+ RETIRED_SKILLS=""
398
+ RETIRED_PARTIAL=""
399
+ KEPT_RETIRED=""
400
+ # Snapshot the manifest before the install loop overwrites it. The retirement
401
+ # pass further down needs the OLD record to tell "we installed this" apart from
402
+ # "the user wrote it themselves".
403
+ OLD_MANIFEST=$(mktemp 2>/dev/null || echo ".claude/.skills-manifest.old")
404
+ [ -f "$SKILLS_MANIFEST" ] && cp "$SKILLS_MANIFEST" "$OLD_MANIFEST"
405
+
406
+ # Emit "<algo>:<digest>", never a bare digest. The fallback chain below resolves
407
+ # differently depending on what is installed, so a manifest that stores only the
408
+ # digest silently mis-compares the moment tool availability changes — the same
409
+ # class of environment drift that made the jq-only reads fail. When that happens
410
+ # every recorded hash mismatches, every retired skill looks user-modified, and
411
+ # the retirement pass does nothing. (fixed 2026-08-28, before 2.13.0 shipped.)
412
+ # sha256sum and `shasum -a 256` produce identical digests, so both record sha256.
398
413
  _cc_hash() {
399
- if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1
400
- elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | cut -d' ' -f1
401
- elif command -v md5sum >/dev/null 2>&1; then md5sum "$1" | cut -d' ' -f1
402
- elif command -v md5 >/dev/null 2>&1; then md5 -q "$1"
403
- else cksum "$1" | cut -d' ' -f1; fi
414
+ if command -v sha256sum >/dev/null 2>&1; then echo "sha256:$(sha256sum "$1" | cut -d' ' -f1)"
415
+ elif command -v shasum >/dev/null 2>&1; then echo "sha256:$(shasum -a 256 "$1" | cut -d' ' -f1)"
416
+ elif command -v md5sum >/dev/null 2>&1; then echo "md5:$(md5sum "$1" | cut -d' ' -f1)"
417
+ elif command -v md5 >/dev/null 2>&1; then echo "md5:$(md5 -q "$1")"
418
+ else echo "cksum:$(cksum "$1" | cut -d' ' -f1)"; fi
419
+ }
420
+
421
+ # True when manifest entry $2 still describes the current content of file $1.
422
+ # Handles the "<algo>:<digest>" form written from 2.13.0 and the bare digest
423
+ # written by 2.12.2/2.12.3: for a legacy entry the algorithm is unknown, so try
424
+ # each available one and accept any match rather than calling the file modified.
425
+ _cc_hash_matches() {
426
+ _f="$1"; _rec="$2"
427
+ [ -n "$_rec" ] || return 1
428
+ case "$_rec" in
429
+ *:*) [ "$(_cc_hash "$_f")" = "$_rec" ] && return 0; return 1 ;;
430
+ esac
431
+ for _algo in sha256sum shasum md5sum md5 cksum; do
432
+ command -v "$_algo" >/dev/null 2>&1 || continue
433
+ case "$_algo" in
434
+ sha256sum) _got=$(sha256sum "$_f" | cut -d' ' -f1) ;;
435
+ shasum) _got=$(shasum -a 256 "$_f" | cut -d' ' -f1) ;;
436
+ md5sum) _got=$(md5sum "$_f" | cut -d' ' -f1) ;;
437
+ md5) _got=$(md5 -q "$_f") ;;
438
+ cksum) _got=$(cksum "$_f" | cut -d' ' -f1) ;;
439
+ esac
440
+ [ "$_got" = "$_rec" ] && return 0
441
+ done
442
+ return 1
404
443
  }
405
444
 
406
445
  for skill_dir in "$SCRIPT_DIR"/templates/.claude/skills/*/; do
@@ -419,11 +458,10 @@ for skill_dir in "$SCRIPT_DIR"/templates/.claude/skills/*/; do
419
458
  if [ ! -f "$dst" ]; then
420
459
  cp "$src" "$dst" # new file — nothing to protect
421
460
  else
422
- disk_hash=$(_cc_hash "$dst")
423
461
  recorded=$(grep "^$rel " "$SKILLS_MANIFEST" 2>/dev/null | head -1 | cut -d' ' -f2)
424
- if [ "$disk_hash" = "$tpl_hash" ]; then
462
+ if _cc_hash_matches "$dst" "$tpl_hash"; then
425
463
  : # already current
426
- elif [ -n "$recorded" ] && [ "$disk_hash" = "$recorded" ]; then
464
+ elif _cc_hash_matches "$dst" "$recorded"; then
427
465
  cp "$src" "$dst" # pristine older version — safe
428
466
  elif [ -z "$recorded" ]; then
429
467
  # No manifest yet (first run on an existing install) and the file
@@ -449,6 +487,59 @@ done
449
487
 
450
488
  [ -f "$NEW_MANIFEST" ] && mv "$NEW_MANIFEST" "$SKILLS_MANIFEST"
451
489
 
490
+ # ─── Retire skills that upstream removed ───
491
+ # The install loop only ever copies, so a skill deleted from templates/ (as
492
+ # /finish and /retro were in v2.13.0) would otherwise sit in every existing
493
+ # install forever. Same conffile rule as the overwrite guard above: delete only
494
+ # what we installed and the user never touched. Anything modified is kept and
495
+ # reported. An install with no prior manifest is left alone entirely — without
496
+ # the old hashes there is no way to tell our copy from the user's own work.
497
+ if [ -s "$OLD_MANIFEST" ]; then
498
+ for old_skill in $(cut -d' ' -f1 "$OLD_MANIFEST" | cut -d'/' -f1 | sort -u); do
499
+ [ -d "$SCRIPT_DIR/templates/.claude/skills/$old_skill" ] && continue
500
+ [ -d ".claude/skills/$old_skill" ] || continue
501
+ skill_modified=false
502
+ while read -r rel rec_hash; do
503
+ case "$rel" in "$old_skill"/*) ;; *) continue ;; esac
504
+ [ -f ".claude/skills/$rel" ] || continue
505
+ _cc_hash_matches ".claude/skills/$rel" "$rec_hash" || skill_modified=true
506
+ done < "$OLD_MANIFEST"
507
+ if [ "$skill_modified" = true ]; then
508
+ KEPT_RETIRED="$KEPT_RETIRED $old_skill"
509
+ # Carry the old entries into the new manifest. Without this the skill
510
+ # never appears in a manifest again, so a later upgrade cannot tell
511
+ # our copy from the user's and it is frozen in place permanently.
512
+ grep "^$old_skill/" "$OLD_MANIFEST" >> "$SKILLS_MANIFEST" 2>/dev/null
513
+ else
514
+ # Delete only the files the manifest recorded, then the directory if
515
+ # that leaves it empty. A blanket rm -rf would also destroy files a
516
+ # user put inside the skill directory that we never installed and
517
+ # therefore cannot account for.
518
+ while read -r rel _; do
519
+ case "$rel" in "$old_skill"/*) ;; *) continue ;; esac
520
+ rm -f ".claude/skills/$rel"
521
+ done < "$OLD_MANIFEST"
522
+ if rmdir ".claude/skills/$old_skill" 2>/dev/null; then
523
+ RETIRED_SKILLS="$RETIRED_SKILLS $old_skill"
524
+ else
525
+ RETIRED_PARTIAL="$RETIRED_PARTIAL $old_skill"
526
+ fi
527
+ fi
528
+ done
529
+ fi
530
+ rm -f "$OLD_MANIFEST" 2>/dev/null
531
+
532
+ if [ -n "$RETIRED_SKILLS" ]; then
533
+ echo -e " ${YELLOW}Retired upstream, removed:${RETIRED_SKILLS}${NC}"
534
+ fi
535
+ if [ -n "$RETIRED_PARTIAL" ]; then
536
+ echo -e " ${YELLOW}Retired upstream; our files removed but directory kept (it holds files we did not install):${RETIRED_PARTIAL}${NC}"
537
+ fi
538
+ if [ -n "$KEPT_RETIRED" ]; then
539
+ echo -e " ${YELLOW}Retired upstream but kept — you modified them:${KEPT_RETIRED}${NC}"
540
+ echo -e " ${YELLOW}Delete by hand once you no longer need them.${NC}"
541
+ fi
542
+
452
543
  if [ -n "$PRESERVED_SKILLS" ]; then
453
544
  echo -e " ${YELLOW}Locally modified skills were not overwritten:${PRESERVED_SKILLS}${NC}"
454
545
  echo -e " ${YELLOW}Compare with: diff .claude/skills/<name>/SKILL.md{,.new}${NC}"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-discipline",
3
- "version": "2.12.3",
3
+ "version": "2.13.0",
4
4
  "description": "Discipline framework for Claude Code — rules, hooks, and agents that keep AI on track",
5
5
  "bin": {
6
6
  "cc-discipline": "bin/cli.js"
@@ -90,25 +90,14 @@ if [ "$DIFF_LINES" -gt 200 ]; then
90
90
  exit 0
91
91
  fi
92
92
 
93
- # ─── New tool/script detection ───
94
- # When creating a script file via Write, remind to register in CLAUDE.md
95
- # jq-or-sed: the previous jq-only line left TOOL_NAME empty on Windows Git Bash,
96
- # so this detection never fired there. (fixed 2026-07-30)
97
- if command -v jq &>/dev/null; then
98
- TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
99
- else
100
- TOOL_NAME=$(echo "$INPUT" | sed -n -E 's/.*"tool_name"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/p' | head -1)
101
- fi
102
- if [ "$TOOL_NAME" = "Write" ] && echo "$BASENAME" | grep -qiE "\.(sh|py|js|ts|rb|pl)$"; then
103
- # Check if file already exists (new file = tool creation)
104
- FULL_PATH="$FILE_PATH"
105
- if [ ! -f "$FULL_PATH" ]; then
106
- cat <<JSONEOF
107
- {"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":"NEW SCRIPT DETECTED: You are creating $BASENAME. If this is a reusable tool/helper, register it in CLAUDE.md under 'Project Tools' now (path, purpose, usage, date) — capturing it while context is fresh saves time later."}}
108
- JSONEOF
109
- exit 0
110
- fi
111
- fi
93
+ # The new-script registration reminder was removed 2026-08-28. It fired 2,170
94
+ # times in four weeks across two machines (~50 tokens each), almost entirely on
95
+ # one-off transform scripts nobody would ever register. The measured correlation
96
+ # with a growing Project Tools section was confounded: /commit carries the same
97
+ # instruction, and a "does the heading exist" gate was disproven because
98
+ # templates/CLAUDE.md ships that heading so every generated project has it.
99
+ # Removing the branch also drops the TOOL_NAME extraction, which nothing else
100
+ # used, cutting two subprocess calls from the hottest hook.
112
101
 
113
102
  # Normal source edit: stay silent (exit 0, no output).
114
103
  # The "list >=3 causes before a bug fix" guidance already lives in the
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: coplan
3
+ description: Write the plan under discussion to docs/current-plan.md so Codex can review it against the real code. Stacks after /think.
4
+ when_to_use: When a plan is worth a second opinion before building it — "coplan", "落个方案", "写下来给 codex 看", "把方案存一下" — or whenever /coplan is stacked after /think.
5
+ ---
6
+
7
+ Write the plan under discussion to `docs/current-plan.md`, then stop.
8
+
9
+ This skill does not produce the plan and does not decide anything. It takes whatever is on the table — the approaches from `/think`, or a plan just described in conversation — and puts it where Codex can read it. Codex runs read-only from the repo root, so a plan **on disk** is one it can check **against the actual code**. That is the entire point: a plan pasted into a prompt can only be judged on its own terms, while a plan in the tree can be judged against what is really there.
10
+
11
+ If there is no plan on the table yet — no approaches from `/think`, nothing described in conversation — **say so and stop**. Point at `/think` instead. Do not invent a plan just to have something to write: a fabricated plan sent for review wastes a Codex run and produces confident feedback on something nobody proposed.
12
+
13
+ ## What to write
14
+
15
+ **Overwrite the file completely.** One path, current version only — every earlier version survives in `git log -p docs/current-plan.md`, so nothing is lost and the directory never fills with dated files.
16
+
17
+ Include, in this order:
18
+
19
+ 1. **Task** — what is being asked, in a sentence or two, framed the way the user framed it rather than the way you re-scoped it.
20
+ 2. **Approaches** — two or three. For each: how it works, what it costs, what it gives up. If there is genuinely only one reasonable approach, say so and say why. Do not invent alternatives to fill the section.
21
+ 3. **Recommendation** — which one, and the reason. A recommendation, not a survey.
22
+ 4. **Assumptions to verify** — the highest-value section for a reviewer that has the repo. Every claim the plan rests on that you have *not* checked: "assumes this is only called from X", "assumes the migration runs before Y", "assumes no other caller depends on the old shape". Codex can open the code and tell you which of these are false. **A plan usually fails here, not in its logic.**
23
+ 5. **Risks and unknowns** — what could go wrong, and what you could not determine.
24
+ 6. **Out of scope** — what this deliberately does not touch, so a reviewer does not flag choices as omissions.
25
+
26
+ Write it for someone who has the codebase but was not in this conversation. No "as discussed above", no pronouns pointing at chat history.
27
+
28
+ ## Then stop
29
+
30
+ Report the path and hand the review command over verbatim:
31
+
32
+ ```
33
+ Plan written to docs/current-plan.md
34
+
35
+ To have Codex review it:
36
+ /codex:rescue 只评审方案不要修改任何文件,方案见 docs/current-plan.md
37
+ ```
38
+
39
+ **Do not run the review yourself** — it costs time and quota, and a small plan does not need one. That call belongs to the user. **Do not start implementing either**: writing a plan down is not approval to build it.
40
+
41
+ ## When stacked after /think
42
+
43
+ `/think /coplan <task>` is the intended pairing. `/think` produces the approaches and stops for the user's choice; `/coplan` puts them on disk on the way past.
44
+
45
+ Write the file with the approaches **still open**. The review is meant to inform the choice, not rubber-stamp one already made — so do not collapse the section down to your recommendation just because you have one.
@@ -86,9 +86,18 @@ Keep this list curated — remove tools that are obsolete.
86
86
 
87
87
  (Empty — fill in as the project develops scaffolding worth checking periodically.)
88
88
 
89
- ## 7. What's working well?
89
+ ## 7. Did the rules change anything this time?
90
90
 
91
- Before reporting issues, note one thing that's going well a good approach, a clean fix, effective tool use, or strong alignment with the user's direction. Recognizing what works reinforces it.
91
+ Most checks have neither**"none this time" is the normal answer and it is data.** Never manufacture one.
92
+
93
+ - **Save** — a rule or hook *changed the outcome*: caught a real mistake, blocked a real loss, stopped a wrong turn already in motion. "The rules kept me disciplined" is not a save; "git-guard blocked a hard reset with 40 min of uncommitted work in the tree" is.
94
+ - **Friction** — a rule or hook cost time for no benefit. Only log it with a concrete `→ fix:` (raise a threshold, exempt a path, reword a check). "X was annoying" is not actionable.
95
+
96
+ If either is present, append one dated line each to a `## Rule Ledger` table in `docs/progress.md` (create the section at the end if it isn't there), then note it in the status line. **Do this automatically — recording an observation is not a change that needs sign-off.**
97
+
98
+ Saves get logged because they are invisible: a rule that works suppresses the very failure it was written for, so only its cost surfaces on its own. Judge on cost alone and every load-bearing rule eventually looks like overhead.
99
+
100
+ If there was no save, note one thing that went well instead — recognizing what works reinforces it.
92
101
 
93
102
  ## 8. Status report
94
103
 
@@ -102,6 +111,7 @@ On track: yes/no/drifted
102
111
  Progressing: yes/circling
103
112
  Progress recorded: yes/updated now/no
104
113
  Scaffolding: [used/skipped/n/a]
114
+ Ledger: [save / friction appended, or "none"]
105
115
  Going well: [one thing]
106
116
  Issues found: [list, or "none"]
107
117
  ```
@@ -93,7 +93,7 @@ If self-review reveals issues, revise your proposals before presenting. Don't sh
93
93
 
94
94
  Do not:
95
95
  - Start coding "while waiting"
96
- - Create files or scaffolding
96
+ - Create files or scaffolding — **except** writing the plan itself to disk, which is not implementation. `/coplan` does exactly that and is designed to stack here.
97
97
  - Run exploratory commands beyond what's needed for Step 0-3
98
98
  - Say "I'll go ahead and start with Approach A" — that's the user's call
99
99
 
@@ -112,6 +112,5 @@ Carry these forward. If scope changes during implementation, pause and re-align
112
112
  The user may steer what happens after alignment by appending a directive to the invocation:
113
113
 
114
114
  - **`/think and plan`** — after alignment, produce the plan (in plan mode, present it for approval via ExitPlanMode).
115
- - **`/think and finish`** (or `and goal`) — after the user approves the approach, transition into **/finish**: drive to completion with its quality bar (solid, comprehensive, fully tested). Do NOT start before approval — 05-phase-discipline still applies; "and finish" declares the post-approval execution stance, it does not bypass the gate.
116
115
 
117
116
  If no directive was appended, stop and wait as usual (Step 5).
@@ -76,7 +76,7 @@
76
76
  ## Rule Ledger
77
77
 
78
78
  <!--
79
- Appended by /retro. Two columns of evidence about the discipline rules themselves:
79
+ Appended by /self-check §7. Two columns of evidence about the discipline rules themselves:
80
80
 
81
81
  - SAVE — a rule or hook changed the outcome: caught a real mistake, blocked a
82
82
  real loss, stopped a wrong turn already in motion.
@@ -1,44 +0,0 @@
1
- ---
2
- name: finish
3
- description: Drive a task to completion with the quality bar — solid, comprehensive, fully tested; "not run" never counts as done. Use when you're ready to execute to the end (standalone, or as the handoff after /think approval).
4
- ---
5
-
6
- You are in **finish mode**: the task is understood and approved — now drive it to completion without stopping early. This is the opposite stance from /think (which aligns and waits). Here you execute, persist, and hold a high quality bar.
7
-
8
- ## Step 1: Lock the completion conditions
9
-
10
- State explicitly — in one short block — what "done" means for this task:
11
- - The concrete deliverables (what must exist / work when finished)
12
- - The quality bar below, applied to each
13
- - Anything you'll treat as "blocked, must report" rather than silently skip
14
-
15
- If invoked standalone (no prior /think) and the scope is genuinely unclear, ask ONE tight round of questions, then commit. Don't turn finish mode into a planning session — the point is to execute.
16
-
17
- ## Step 2: The quality bar (definition of done)
18
-
19
- Hold all of these. They are the standard, not aspirations:
20
-
21
- - **扎实 (solid)** — Fix the root cause, not symptoms. Handle edge cases (empty / null / error paths). No TODO, stub, or workaround left standing in as "the solution."
22
- - **全面 (comprehensive)** — Cover the full scope, not just the happy path. Update related call sites, docs, and `docs/progress.md`. Don't leave half the task for "later."
23
- - **完备测试 (fully tested)** — Write tests AND run them. Per 07-integrity §2, "verified" requires actual execution output — paste the command and result. Untested code is not done.
24
- - **诚实 (honest)** — If something is blocked by an external resource, mark it "⚠️ code ready, verification pending: [reason]" — never ✅ it. Distinguish done from blocked.
25
-
26
- ## Step 3: Drive
27
-
28
- Keep going until the completion conditions are met. While driving:
29
- - Make reasonable decisions and keep moving — note them, don't stop to ask about trivia.
30
- - The discipline rules still apply: 3 consecutive failures → pause and regroup; scope changes → re-align; anything irreversible or outward-facing → confirm first.
31
- - Don't declare partial success to exit early. If you feel the urge to stop before the bar is met, that urge is the signal to push through or report the specific blocker — not to lower the bar.
32
-
33
- ## Step 4: Report on completion
34
-
35
- When the completion conditions are met, give a short close-out:
36
-
37
- ```
38
- FINISH — [task]
39
- Done: [each deliverable + how it was verified — paste key command output]
40
- Quality bar: 扎实 [✓/note] · 全面 [✓/note] · 完备测试 [✓/note]
41
- Blocked (if any): [item — why, what's needed]
42
- ```
43
-
44
- If any part is blocked rather than done, say so plainly. A truthful "90% done, X blocked on Y" beats a false ✅.
@@ -1,49 +0,0 @@
1
- ---
2
- name: retro
3
- description: Find friction, remove friction. Quick post-task review that makes this project's workflow smoother and feeds improvements back to cc-discipline.
4
- ---
5
-
6
- Find friction. Remove friction. Record what the rules actually caught.
7
-
8
- ## What to do
9
-
10
- Quickly scan what just happened — `git log --oneline -10` and any hook triggers you remember. Then output in this format:
11
-
12
- ```
13
- RETRO — [date]
14
-
15
- Friction:
16
- - [what got in the way] → fix: [specific actionable change]
17
-
18
- Saves:
19
- - [rule/hook] caught [the specific real problem] — without it: [what would have shipped]
20
-
21
- Insights:
22
- - [something learned that should survive this session]
23
-
24
- Framework:
25
- - [cc-discipline improvement, if any — skip if none]
26
- ```
27
-
28
- Rules:
29
- - **Only actionable friction** — Every friction item must have a "→ fix:" with a concrete change (adjust a threshold, add to CLAUDE.md, update memory, exempt a path).
30
- - **Only new** — Don't repeat friction that's already been addressed or recorded in memory.
31
- - **Be specific** — "streak-breaker was annoying" is not useful. "streak-breaker triggered 3x on config.yaml during template fill → fix: add config.yaml to docs/ exempt path, or raise config threshold to 10" is useful.
32
- - **Framework items are rare** — Most friction is project-specific. Only flag framework issues if the same problem would hit other projects too.
33
- - **Keep it short** — 3-5 items per section max. If you can't find friction, say "no friction found" and move on. An empty friction list is a good sign.
34
-
35
- Saves — read this before filling that section in:
36
- - **A save is not "what went well."** Log a save only when a rule or hook *changed the outcome*: it caught a real mistake, blocked a real loss, or stopped a wrong turn that was already in motion. "The rules kept me disciplined" is not a save. "git-guard blocked `git reset --hard` while 40 min of uncommitted work was in the tree" is.
37
- - **"no saves" is a real answer, and it is data.** Write it plainly rather than manufacturing one.
38
- - **Why this section exists:** friction is visible and saves are invisible — a rule that works silently suppresses the very failure it was written for, so the only naturally measurable signal is its cost. With cost-only data every rule eventually looks like pure overhead and gets cut, including the ones that are still load-bearing. The save log is what makes it possible to judge, later and with evidence, whether a given rule still earns its place.
39
-
40
- ## After output
41
-
42
- Append both Friction and Saves to a `## Rule Ledger` section in `docs/progress.md` (create the section at the end of the file if it isn't there yet), one dated line each. This accumulates the record across sessions — a single retro proves nothing, twenty of them decide which rules stay.
43
-
44
- Then present the items. User decides:
45
- - "fix it" → apply the changes
46
- - "remember it" → write to memory via /commit
47
- - "skip" → move on
48
-
49
- Do not auto-apply fixes. Do not pad. Do not turn this into a report. (Appending to the ledger is not a fix — do that without asking.)