luciazero 2.0.3 → 2.1.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/CHANGELOG.md CHANGED
@@ -7,6 +7,26 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.1.0] - 2026-08-15
11
+
12
+ ### Added
13
+
14
+ - `/show` turns code relationships, structural changes, and verification
15
+ evidence into the smallest useful traceable visual.
16
+ - `./test.sh --fast` provides a measured intermediate tier while the default
17
+ and `--full` preserve complete CI/closeout coverage.
18
+ - Opt-in Claude hooks use private per-session scratch state to record
19
+ privacy-preserving aggregate turn/merged-Bash wall time and Bash, failed or
20
+ successful verify, and model/user skill counts; `luciazero discipline`
21
+ summarizes them.
22
+
23
+ ### Changed
24
+
25
+ - `/luciazero-bootstrap` is now `/ready`. The old command remains as a
26
+ deprecated compatibility alias for one release.
27
+ - The doctrine reserves full verification for closeout, and `/plan` plus
28
+ `/debug` no longer auto-trigger for routine edits or first obvious failures.
29
+
10
30
  ## [2.0.3] - 2026-08-13
11
31
 
12
32
  ### Fixed
package/README.md CHANGED
@@ -16,7 +16,7 @@ of handing back work they merely believe is finished.
16
16
  > Done is proven by a command, not by my judgment. If no verification command
17
17
  > exists, that is the first bug.
18
18
 
19
- It ships a compact [9-rule doctrine](claude/luciazero.md), 9 on-demand skills,
19
+ It ships a compact [9-rule doctrine](claude/luciazero.md), 10 on-demand skills,
20
20
  verification hooks, a risk-routed reviewer, and an eval harness. It is a
21
21
  discipline layer, not an agent runtime or overnight orchestrator.
22
22
 
@@ -83,7 +83,7 @@ Carries the doctrine, all skills, reviewer, and verify-tracking hooks:
83
83
  /plugin install luciazero@luciazero
84
84
  ```
85
85
 
86
- Start a repository with `/luciazero:luciazero-bootstrap`. Plugin skills use the
86
+ Start a repository with `/luciazero:ready`. Plugin skills use the
87
87
  `/luciazero:` prefix. The plugin has no statusline because Claude Code plugins
88
88
  cannot set one.
89
89
 
@@ -93,7 +93,8 @@ cannot set one.
93
93
  npx skills add ohm41321/luciazero
94
94
  ```
95
95
 
96
- This installs the 9 skills only: no doctrine, reviewer, or hooks.
96
+ This installs the 10 skills plus the temporary `/luciazero-bootstrap`
97
+ compatibility alias: no doctrine, reviewer, or hooks.
97
98
 
98
99
  ### Classic Claude Code and Codex
99
100
 
@@ -140,14 +141,15 @@ Claude Code can auto-update the plugin at startup: open `/plugin` →
140
141
  marketplaces leave this off by default. For release-only notifications, use
141
142
  GitHub **Watch → Custom → Releases**.
142
143
 
143
- ## The 9 skills
144
+ ## The 10 skills
144
145
 
145
- Run `/luciazero-bootstrap` first; the rest activate when their moment arrives.
146
+ Run `/ready` first; the rest activate when their moment arrives.
146
147
 
147
148
  | Moment | Skill | Result |
148
149
  |---|---|---|
149
- | Entering a repository | `/luciazero-bootstrap` | Finds or creates a verify command and proves it can fail |
150
- | Before risky or multi-step work | `/plan` | Fixes scope and observable acceptance evidence |
150
+ | Entering a repository | `/ready` | Finds or creates a verify command and proves it can fail |
151
+ | Structure or evidence is hard to scan | `/show` | Maps connections, changes, and proof into the smallest useful visual |
152
+ | Before risky, ambiguous, or multi-module work | `/plan` | Fixes scope and observable acceptance evidence |
151
153
  | A bug survives the first look | `/debug` | Reproduction, hypothesis ledger, regression test |
152
154
  | Good and bad revisions are known | `/bisect` | Finds the first bad commit in a temporary worktree |
153
155
  | Before claiming completion | `/done` | Full verify, skeptic review, scope report |
@@ -205,6 +207,9 @@ only one run per arm per task. See the [full benchmark](docs/benchmark.md),
205
207
  - Core installers, hooks, helpers, and graders are offline. Real behavioral
206
208
  evals invoke a model CLI and consume API credit or subscription quota.
207
209
  - Hooks run commands on your machine. Read them before enabling them.
210
+ - Hook telemetry stays local in private per-session state and records aggregate
211
+ turn/Bash wall time plus Bash, verify, and model/user skill counts—never raw
212
+ commands, skill names, or paths.
208
213
  - Set `LUCIAZERO_VERIFY_CMD` to the repo's exact fast verify command.
209
214
  - Put `LUCIAZERO_STRICT_VERIFY_CMD` only in personal settings, never in a
210
215
  committed repository config. Strict mode fails open on internal errors.
@@ -214,12 +219,16 @@ See [SECURITY.md](SECURITY.md) for the complete trust boundary.
214
219
  ## Development
215
220
 
216
221
  ```bash
217
- ./test.sh
222
+ ./test.sh --fast # intermediate loop: core doctrine/hooks/report/Relay checks
223
+ ./test.sh # closeout/CI: full eval, packaging, and install coverage
218
224
  ```
219
225
 
220
- The suite covers scripts, hook state, Relay, bisect, plugin/npm manifests,
221
- self-proving eval graders, and sandboxed install reinstall uninstall for
222
- Claude Code and Codex.
226
+ The fast tier is the default intermediate check for this repository; use a
227
+ more targeted command when changing a component it does not cover. The default
228
+ full tier (also `./test.sh --full`) covers scripts, hook state, Relay, bisect,
229
+ plugin/npm manifests, self-proving eval graders, and sandboxed install →
230
+ reinstall → uninstall for Claude Code and Codex. CI and `/done` use the full
231
+ tier.
223
232
 
224
233
  More detail:
225
234
 
package/README.th.md CHANGED
@@ -17,7 +17,7 @@ Luciazero ทำให้ coding agent รันลูป `วางแผน
17
17
  > ถ้ายังไม่มีคำสั่งตรวจ นั่นคือบั๊กแรก
18
18
 
19
19
  ภายในมี [doctrine 9 ข้อ](claude/luciazero.md) ที่สั้น, skill แบบเรียกเมื่อจำเป็น
20
- 9 ตัว, hook ติดตามการ verify, reviewer ที่ route ตามความเสี่ยง และ eval harness
20
+ 10 ตัว, hook ติดตามการ verify, reviewer ที่ route ตามความเสี่ยง และ eval harness
21
21
  นี่คือชั้นวินัย ไม่ใช่ agent runtime หรือระบบ orchestration สำหรับรันงานข้ามคืน
22
22
 
23
23
  ## ดูการทำงานใน 15 วินาที
@@ -81,7 +81,7 @@ fingerprint เก่า (5/6) ตัวเลขเหล่านี้เป
81
81
  /plugin install luciazero@luciazero
82
82
  ```
83
83
 
84
- เริ่ม repo ด้วย `/luciazero:luciazero-bootstrap` ชื่อ skill แบบ plugin มี prefix
84
+ เริ่ม repo ด้วย `/luciazero:ready` ชื่อ skill แบบ plugin มี prefix
85
85
  `/luciazero:` และไม่มี statusline เพราะ Claude Code plugin ตั้งค่านี้ไม่ได้
86
86
 
87
87
  ### เฉพาะ skill — agent ที่รองรับ
@@ -90,7 +90,8 @@ fingerprint เก่า (5/6) ตัวเลขเหล่านี้เป
90
90
  npx skills add ohm41321/luciazero
91
91
  ```
92
92
 
93
- ช่องทางนี้ติดตั้งเฉพาะ skill 9 ตัว ไม่มี doctrine, reviewer หรือ hook
93
+ ช่องทางนี้ติดตั้งเฉพาะ skill 10 ตัว พร้อม alias `/luciazero-bootstrap`
94
+ ชั่วคราว ไม่มี doctrine, reviewer หรือ hook
94
95
 
95
96
  ### Classic Claude Code และ Codex
96
97
 
@@ -136,14 +137,15 @@ Claude Code อัปเดต plugin ตอนเริ่มโปรแกร
136
137
  ภายนอกจะปิดตัวเลือกนี้เป็นค่าเริ่มต้น ถ้าต้องการเพียงการแจ้งเตือน release ให้ใช้
137
138
  GitHub **Watch → Custom → Releases**
138
139
 
139
- ## Skill ทั้ง 9 ตัว
140
+ ## Skill ทั้ง 10 ตัว
140
141
 
141
- รัน `/luciazero-bootstrap` ก่อนหนึ่งครั้ง ที่เหลือใช้เมื่อถึงจังหวะของมัน
142
+ รัน `/ready` ก่อนหนึ่งครั้ง ที่เหลือใช้เมื่อถึงจังหวะของมัน
142
143
 
143
144
  | จังหวะ | Skill | ผลลัพธ์ |
144
145
  |---|---|---|
145
- | เข้า repository | `/luciazero-bootstrap` | หาหรือสร้างคำสั่ง verify และพิสูจน์ว่าแดงได้ |
146
- | ก่อนงานเสี่ยงหรือหลายขั้น | `/plan` | ล็อก scope และหลักฐานยอมรับที่สังเกตได้ |
146
+ | เข้า repository | `/ready` | หาหรือสร้างคำสั่ง verify และพิสูจน์ว่าแดงได้ |
147
+ | โครงสร้างหรือหลักฐานไล่อ่านยาก | `/show` | แสดงความเชื่อมโยง สิ่งที่เปลี่ยน และหลักฐานด้วยภาพที่เล็กที่สุด |
148
+ | ก่อนงานเสี่ยง กำกวม หรือแตะหลาย module | `/plan` | ล็อก scope และหลักฐานยอมรับที่สังเกตได้ |
147
149
  | บั๊กที่มองรอบแรกไม่ออก | `/debug` | Reproduction, hypothesis ledger, regression test |
148
150
  | รู้ revision ดีและเสีย | `/bisect` | หา first bad commit ใน worktree ชั่วคราว |
149
151
  | ก่อนบอกว่าเสร็จ | `/done` | Full verify, skeptic review และรายงาน scope |
@@ -200,6 +202,9 @@ pilot มีเพียง 1 run ต่อ arm ต่อ task ดู [ผลเ
200
202
  - Installer, hook, helper และ grader หลักรัน offline ส่วน behavioral eval จริง
201
203
  เรียก model CLI และใช้เครดิต API หรือโควตา subscription
202
204
  - Hook รันคำสั่งบนเครื่อง ควรอ่านก่อนเปิดใช้
205
+ - Telemetry ของ hook อยู่ใน private state แยกตาม session ภายในเครื่อง เก็บเวลา
206
+ wall time ของ turn/Bash และจำนวน Bash, verify, skill ที่ model/user เรียก
207
+ โดยไม่เก็บ command, ชื่อ skill หรือ path ดิบ
203
208
  - ตั้ง `LUCIAZERO_VERIFY_CMD` เป็นคำสั่ง verify ระดับเร็วที่ exact ของ repo
204
209
  - ใส่ `LUCIAZERO_STRICT_VERIFY_CMD` ใน personal settings เท่านั้น ห้าม commit ลง
205
210
  config ของ repository; strict mode จะ fail open เมื่อเกิด internal error
@@ -209,12 +214,15 @@ pilot มีเพียง 1 run ต่อ arm ต่อ task ดู [ผลเ
209
214
  ## พัฒนา repo นี้
210
215
 
211
216
  ```bash
212
- ./test.sh
217
+ ./test.sh --fast # loop ระหว่างทำ: ตรวจ doctrine/hook/report/Relay ส่วนหลัก
218
+ ./test.sh # ปิดงาน/CI: ตรวจ eval, packaging และ install แบบเต็ม
213
219
  ```
214
220
 
215
- ชุดทดสอบครอบคลุม script, state ของ hook, Relay, bisect, manifest ของ plugin/npm,
216
- eval grader ที่พิสูจน์ตัวเองได้ และ install reinstall → uninstall แบบ sandbox
217
- ทั้ง Claude Code และ Codex
221
+ fast tier เป็นคำสั่งระหว่างทำงานของ repo นี้; ถ้าแก้ส่วนที่ fast tier ไม่ครอบคลุม
222
+ ให้ใช้คำสั่ง targeted ของส่วนนั้น ส่วน full tier (`./test.sh` หรือ
223
+ `./test.sh --full`) ครอบคลุม script, state ของ hook, Relay, bisect, manifest ของ
224
+ plugin/npm, eval grader ที่พิสูจน์ตัวเองได้ และ install → reinstall → uninstall
225
+ แบบ sandbox ทั้ง Claude Code และ Codex โดย CI และ `/done` ใช้ full tier
218
226
 
219
227
  อ่านต่อ:
220
228
 
@@ -56,12 +56,20 @@ function parseLine(line) {
56
56
  if (Number.isNaN(timestamp.valueOf()) || typeof row.project_id !== "string" || typeof row.project !== "string") {
57
57
  return { malformed: true };
58
58
  }
59
+ let telemetry = null;
60
+ if (row.telemetry && typeof row.telemetry === "object") {
61
+ const keys = ["turn_ms", "bash_ms", "bash_count", "verify_count", "skill_count"];
62
+ if (keys.every((key) => Number.isSafeInteger(row.telemetry[key]) && row.telemetry[key] >= 0)) {
63
+ telemetry = Object.fromEntries(keys.map((key) => [key, row.telemetry[key]]));
64
+ }
65
+ }
59
66
  return {
60
67
  timestamp,
61
68
  event: row.event,
62
69
  project: row.project,
63
70
  projectId: row.project_id,
64
71
  verifyMode: ["regex", "exact", "strict"].includes(row.verify_mode) ? row.verify_mode : "unknown",
72
+ telemetry,
65
73
  legacy: false,
66
74
  };
67
75
  }
@@ -75,6 +83,7 @@ function parseLine(line) {
75
83
  project: match[3],
76
84
  projectId: `legacy-${crypto.createHash("sha256").update(match[3]).digest("hex").slice(0, 12)}`,
77
85
  verifyMode: "unknown",
86
+ telemetry: null,
78
87
  legacy: true,
79
88
  };
80
89
  }
@@ -111,10 +120,23 @@ if (options.project) {
111
120
 
112
121
  const counts = { "stop-clean": 0, nudge: 0, "strict-block": 0 };
113
122
  const modes = { regex: 0, exact: 0, strict: 0, unknown: 0 };
123
+ const telemetry = {
124
+ measured_turns: 0, turn_ms: 0, bash_ms: 0, non_bash_ms: 0,
125
+ bash_count: 0, verify_count: 0, skill_count: 0,
126
+ };
114
127
  const projects = new Map();
115
128
  for (const row of rows) {
116
129
  counts[row.event] += 1;
117
130
  modes[row.verifyMode] = (modes[row.verifyMode] || 0) + 1;
131
+ if (row.telemetry) {
132
+ telemetry.measured_turns += 1;
133
+ telemetry.turn_ms += row.telemetry.turn_ms;
134
+ telemetry.bash_ms += row.telemetry.bash_ms;
135
+ telemetry.non_bash_ms += Math.max(0, row.telemetry.turn_ms - row.telemetry.bash_ms);
136
+ telemetry.bash_count += row.telemetry.bash_count;
137
+ telemetry.verify_count += row.telemetry.verify_count;
138
+ telemetry.skill_count += row.telemetry.skill_count;
139
+ }
118
140
  const current = projects.get(row.projectId) || {
119
141
  project: row.project,
120
142
  project_id: row.projectId,
@@ -160,6 +182,7 @@ const report = {
160
182
  legacy_records: legacyRecords,
161
183
  outcomes: counts,
162
184
  verify_modes: modes,
185
+ telemetry,
163
186
  top_nudged_projects: topNudged,
164
187
  recommendations,
165
188
  };
@@ -182,6 +205,18 @@ console.log(` Clean stops: ${String(counts["stop-clean"]).padStart(5)} (${per
182
205
  console.log(` Nudges: ${String(counts.nudge).padStart(5)} (${percent(counts.nudge)})`);
183
206
  console.log(` Strict blocks: ${String(counts["strict-block"]).padStart(5)} (${percent(counts["strict-block"])})`);
184
207
  console.log("");
208
+ console.log("Latency Telemetry:");
209
+ if (telemetry.measured_turns === 0) {
210
+ console.log(" No measured turns (new hooks collect this locally).");
211
+ } else {
212
+ const average = (value) => Math.round(value / telemetry.measured_turns);
213
+ console.log(` Measured turns: ${telemetry.measured_turns}`);
214
+ console.log(` Average turn: ${average(telemetry.turn_ms)} ms`);
215
+ console.log(` Average Bash time: ${average(telemetry.bash_ms)} ms`);
216
+ console.log(` Average non-Bash: ${average(telemetry.non_bash_ms)} ms`);
217
+ console.log(` Bash / verify / skill calls: ${telemetry.bash_count} / ${telemetry.verify_count} / ${telemetry.skill_count}`);
218
+ }
219
+ console.log("");
185
220
  console.log("Top Nudged Repositories:");
186
221
  if (topNudged.length === 0) console.log(" None in the selected period.");
187
222
  for (const [index, item] of topNudged.entries()) {
@@ -1,5 +1,27 @@
1
1
  {
2
2
  "hooks": {
3
+ "UserPromptSubmit": [
4
+ {
5
+ "hooks": [
6
+ { "type": "command", "command": "LUCIAZERO_CHANNEL=plugin ${CLAUDE_PLUGIN_ROOT}/claude/hooks/luciazero-verify.sh prompt" }
7
+ ]
8
+ }
9
+ ],
10
+ "UserPromptExpansion": [
11
+ {
12
+ "hooks": [
13
+ { "type": "command", "command": "LUCIAZERO_CHANNEL=plugin ${CLAUDE_PLUGIN_ROOT}/claude/hooks/luciazero-verify.sh skill-prompt" }
14
+ ]
15
+ }
16
+ ],
17
+ "PreToolUse": [
18
+ {
19
+ "matcher": "Bash",
20
+ "hooks": [
21
+ { "type": "command", "command": "LUCIAZERO_CHANNEL=plugin ${CLAUDE_PLUGIN_ROOT}/claude/hooks/luciazero-verify.sh bash-start" }
22
+ ]
23
+ }
24
+ ],
3
25
  "PostToolUse": [
4
26
  {
5
27
  "matcher": "Edit|Write|NotebookEdit",
@@ -12,6 +34,20 @@
12
34
  "hooks": [
13
35
  { "type": "command", "command": "LUCIAZERO_CHANNEL=plugin ${CLAUDE_PLUGIN_ROOT}/claude/hooks/luciazero-verify.sh bash" }
14
36
  ]
37
+ },
38
+ {
39
+ "matcher": "Skill",
40
+ "hooks": [
41
+ { "type": "command", "command": "LUCIAZERO_CHANNEL=plugin ${CLAUDE_PLUGIN_ROOT}/claude/hooks/luciazero-verify.sh skill" }
42
+ ]
43
+ }
44
+ ],
45
+ "PostToolUseFailure": [
46
+ {
47
+ "matcher": "Bash",
48
+ "hooks": [
49
+ { "type": "command", "command": "LUCIAZERO_CHANNEL=plugin ${CLAUDE_PLUGIN_ROOT}/claude/hooks/luciazero-verify.sh bash-failure" }
50
+ ]
15
51
  }
16
52
  ],
17
53
  "Stop": [
@@ -16,7 +16,7 @@ set -u
16
16
  IN="$(cat 2>/dev/null || true)"
17
17
 
18
18
  LINE="$(printf '%s' "${IN}" | python3 -c '
19
- import json, os, sys, time, hashlib
19
+ import json, os, sys, time, hashlib, stat
20
20
 
21
21
  try:
22
22
  d = json.load(sys.stdin)
@@ -27,9 +27,24 @@ model = ((d.get("model") or {}).get("display_name")) or "claude"
27
27
  cwd = ((d.get("workspace") or {}).get("current_dir")) or d.get("cwd") or os.getcwd()
28
28
 
29
29
  key = hashlib.md5(cwd.encode()).hexdigest()[:12]
30
- state = os.path.join(os.environ.get("TMPDIR", "/tmp"), "luciazero-verify-state", key)
30
+ uid = os.getuid() if hasattr(os, "getuid") else "unknown"
31
+ base = os.path.join(os.environ.get("TMPDIR", "/tmp"), f"luciazero-verify-state-{uid}")
32
+ try:
33
+ info = os.lstat(base)
34
+ if stat.S_ISLNK(info.st_mode) or not stat.S_ISDIR(info.st_mode):
35
+ raise OSError("unsafe state base")
36
+ if hasattr(os, "getuid") and info.st_uid != os.getuid():
37
+ raise OSError("wrong state owner")
38
+ if stat.S_IMODE(info.st_mode) & 0o077:
39
+ raise OSError("state base is not private")
40
+ except OSError:
41
+ state = None
42
+ else:
43
+ state = os.path.join(base, key)
31
44
 
32
45
  def mtime(name):
46
+ if state is None:
47
+ return None
33
48
  try:
34
49
  return os.path.getmtime(os.path.join(state, name))
35
50
  except OSError:
@@ -5,8 +5,13 @@
5
5
  # ("done is proven by a command") at the exact moment it is most violated.
6
6
  #
7
7
  # Subcommands (wired in settings.json):
8
+ # prompt — UserPromptSubmit: start privacy-preserving turn telemetry
9
+ # bash-start — PreToolUse on Bash: start shell-command timing
8
10
  # edit — PostToolUse on Edit|Write|NotebookEdit : record "an edit happened"
9
- # bash — PostToolUse on Bash : record verify runs + status
11
+ # bash — PostToolUse on Bash: record duration, verify runs, and status
12
+ # bash-failure — PostToolUseFailure on Bash: record failed commands
13
+ # skill — PostToolUse on Skill: count model-invoked skills
14
+ # skill-prompt — UserPromptExpansion: count user-invoked slash skills
10
15
  # stop — Stop : warn once if edits are unverified
11
16
  # session — SessionStart : point at an existing Lucia Relay
12
17
  # doctrine— SessionStart (plugin installs only) : emit the doctrine as context
@@ -29,8 +34,8 @@
29
34
  # stop hook appends one schema-versioned JSON line per stop outcome
30
35
  # (stop-clean / nudge / strict-block) to luciazero-stats.log in the harness
31
36
  # config dir — local only, capped at ~250 lines, fail-open. It records a
32
- # privacy-preserving project hash and verify mode, never the project path or
33
- # command. Uninstall keeps it (it is learned data).
37
+ # privacy-preserving project hash, verify mode, and aggregate latency/counts;
38
+ # never the project path, command, or skill name. Uninstall keeps it.
34
39
  set -u
35
40
 
36
41
  MODE="${1:-}"
@@ -79,8 +84,54 @@ CWD="$(pyfield "d.get('cwd')")"
79
84
  [ -n "${CWD}" ] || CWD="${PWD}"
80
85
  KEY="$(printf '%s' "${CWD}" | python3 -c 'import sys,hashlib;print(hashlib.md5(sys.stdin.buffer.read()).hexdigest()[:12])' 2>/dev/null)" || exit 0
81
86
  [ -n "${KEY}" ] || exit 0
82
- STATE="${TMPDIR:-/tmp}/luciazero-verify-state/${KEY}"
87
+ BASE="${TMPDIR:-/tmp}/luciazero-verify-state-$(id -u 2>/dev/null || echo unknown)"
88
+ # The base name is predictable, so validate ownership/type before touching it.
89
+ # A hostile pre-created symlink or directory makes the hook fail open.
90
+ python3 - "${BASE}" <<'PY' 2>/dev/null || exit 0
91
+ import os, stat, sys
92
+ path = sys.argv[1]
93
+ try:
94
+ info = os.lstat(path)
95
+ except FileNotFoundError:
96
+ os.mkdir(path, 0o700)
97
+ info = os.lstat(path)
98
+ if not stat.S_ISDIR(info.st_mode) or stat.S_ISLNK(info.st_mode):
99
+ raise SystemExit(1)
100
+ if hasattr(os, "getuid") and info.st_uid != os.getuid():
101
+ raise SystemExit(1)
102
+ os.chmod(path, 0o700)
103
+ PY
104
+ STATE="${BASE}/${KEY}"
83
105
  mkdir -p "${STATE}" 2>/dev/null || exit 0
106
+ chmod 700 "${STATE}" 2>/dev/null || exit 0
107
+ SESSION_RAW="$(pyfield "d.get('session_id')")"
108
+ [ -n "${SESSION_RAW}" ] || SESSION_RAW="parent-${PPID}"
109
+ SESSION_KEY="$(printf '%s' "${SESSION_RAW}" | python3 -c 'import hashlib,sys; print(hashlib.sha256(sys.stdin.buffer.read()).hexdigest()[:16])' 2>/dev/null)" || exit 0
110
+ TELEMETRY="${STATE}/telemetry/${SESSION_KEY}"
111
+
112
+ tool_key() { # stable opaque key; raw tool input never leaves temporary state
113
+ RAW="$(pyfield "d.get('tool_use_id') or d.get('tool_input', {}).get('command') or d.get('tool_input', {}).get('skill') or d.get('command_name') or d.get('prompt') or d.get('command')")"
114
+ [ -n "${RAW}" ] || RAW=unknown
115
+ printf '%s' "${RAW}" | python3 -c 'import hashlib,sys; print(hashlib.sha256(sys.stdin.buffer.read()).hexdigest()[:16])' 2>/dev/null
116
+ }
117
+
118
+ now_ms() {
119
+ python3 -c 'import time; print(int(time.time() * 1000))' 2>/dev/null
120
+ }
121
+
122
+ record_strict_telemetry() { # record_strict_telemetry <start-ms>
123
+ STRICT_END_MS="$(now_ms || true)"
124
+ case "${1:-}:${STRICT_END_MS}" in
125
+ *[!0-9:]*|:|*:|*::* ) return ;;
126
+ esac
127
+ [ "${STRICT_END_MS}" -ge "$1" ] 2>/dev/null || return
128
+ mkdir -p "${TELEMETRY}/bash_count" "${TELEMETRY}/bash_intervals" \
129
+ "${TELEMETRY}/verify_count" 2>/dev/null || return
130
+ : > "${TELEMETRY}/bash_count/strict-gate" 2>/dev/null || true
131
+ : > "${TELEMETRY}/verify_count/strict-gate" 2>/dev/null || true
132
+ printf '%s %s\n' "$1" "${STRICT_END_MS}" \
133
+ > "${TELEMETRY}/bash_intervals/strict-gate" 2>/dev/null || true
134
+ }
84
135
 
85
136
  stat_log() { # stat_log <event> — discipline stats; fail-open, capped
86
137
  SDIR="${CLAUDE_CONFIG_DIR:-${HOME:-}/.claude}"
@@ -88,9 +139,9 @@ stat_log() { # stat_log <event> — discipline stats; fail-open, capped
88
139
  VMODE=regex
89
140
  [ -n "${VERIFY_CMD:-}" ] && VMODE=exact
90
141
  [ -n "${LUCIAZERO_STRICT_VERIFY_CMD:-}" ] && VMODE=strict
91
- python3 - "${SFILE}" "${CWD}" "$1" "${VMODE}" <<'PY' 2>/dev/null || true
142
+ python3 - "${SFILE}" "${CWD}" "$1" "${VMODE}" "${TELEMETRY}" <<'PY' 2>/dev/null || true
92
143
  import datetime, hashlib, json, os, sys
93
- path, cwd, event, mode = sys.argv[1:]
144
+ path, cwd, event, mode, telemetry_dir = sys.argv[1:]
94
145
  os.makedirs(os.path.dirname(path), exist_ok=True)
95
146
  real = os.path.realpath(cwd)
96
147
  row = {
@@ -101,6 +152,47 @@ row = {
101
152
  "project": os.path.basename(real) or "(root)",
102
153
  "verify_mode": mode,
103
154
  }
155
+ def read_int(path):
156
+ try:
157
+ value = int(open(path, encoding="utf-8").read().strip())
158
+ return value if value >= 0 else None
159
+ except (OSError, ValueError):
160
+ return None
161
+ def count_files(name):
162
+ try:
163
+ return sum(os.path.isfile(os.path.join(telemetry_dir, name, item))
164
+ for item in os.listdir(os.path.join(telemetry_dir, name)))
165
+ except OSError:
166
+ return 0
167
+ start = read_int(os.path.join(telemetry_dir, "turn_start_ms"))
168
+ if start is not None:
169
+ now = int(datetime.datetime.now(datetime.timezone.utc).timestamp() * 1000)
170
+ intervals = []
171
+ try:
172
+ interval_dir = os.path.join(telemetry_dir, "bash_intervals")
173
+ for item in os.listdir(interval_dir):
174
+ try:
175
+ a, b = map(int, open(os.path.join(interval_dir, item), encoding="utf-8").read().split())
176
+ except (OSError, ValueError):
177
+ continue
178
+ if 0 <= a <= b:
179
+ intervals.append((max(start, a), min(now, b)))
180
+ except OSError:
181
+ pass
182
+ merged = []
183
+ for a, b in sorted((a, b) for a, b in intervals if a <= b):
184
+ if not merged or a > merged[-1][1]:
185
+ merged.append([a, b])
186
+ else:
187
+ merged[-1][1] = max(merged[-1][1], b)
188
+ bash_ms = sum(b - a for a, b in merged)
189
+ row["telemetry"] = {
190
+ "turn_ms": max(0, now - start),
191
+ "bash_ms": bash_ms,
192
+ "bash_count": count_files("bash_count"),
193
+ "verify_count": count_files("verify_count"),
194
+ "skill_count": count_files("skill_count"),
195
+ }
104
196
  with open(path, "a", encoding="utf-8") as handle:
105
197
  handle.write(json.dumps(row, separators=(",", ":")) + "\n")
106
198
  with open(path, encoding="utf-8", errors="replace") as handle:
@@ -123,6 +215,18 @@ VERIFY_RE="${LUCIAZERO_VERIFY_REGEX:-verify|test\.sh|pytest|npm (run )?test|pnpm
123
215
  VERIFY_CMD="${LUCIAZERO_VERIFY_CMD:-}"
124
216
 
125
217
  case "${MODE}" in
218
+ prompt)
219
+ # Per-turn scratch data is ephemeral. Persistent rows keep aggregates only.
220
+ rm -rf "${TELEMETRY}" 2>/dev/null || exit 0
221
+ mkdir -p "${TELEMETRY}" 2>/dev/null || exit 0
222
+ now_ms > "${TELEMETRY}/turn_start_ms" 2>/dev/null || true
223
+ ;;
224
+ bash-start)
225
+ TK="$(tool_key)" || exit 0
226
+ mkdir -p "${TELEMETRY}/bash_start_ms" "${TELEMETRY}/bash_count" 2>/dev/null || exit 0
227
+ now_ms > "${TELEMETRY}/bash_start_ms/${TK}" 2>/dev/null || true
228
+ : > "${TELEMETRY}/bash_count/${TK}" 2>/dev/null || true
229
+ ;;
126
230
  edit)
127
231
  # Documentation writes do not re-arm the nudge: the closeout skills
128
232
  # Closeout skills write docs AFTER the final green verify. Relay's JSON is
@@ -141,7 +245,20 @@ case "${MODE}" in
141
245
  ;;
142
246
  esac
143
247
  ;;
144
- bash)
248
+ bash|bash-failure)
249
+ TK="$(tool_key)" || TK=unknown
250
+ mkdir -p "${TELEMETRY}/bash_count" "${TELEMETRY}/bash_intervals" 2>/dev/null || true
251
+ : > "${TELEMETRY}/bash_count/${TK}" 2>/dev/null || true
252
+ START_MS="$(cat "${TELEMETRY}/bash_start_ms/${TK}" 2>/dev/null || true)"
253
+ END_MS="$(now_ms || true)"
254
+ case "${START_MS}:${END_MS}" in
255
+ *[!0-9:]*|:|*:|*::* ) : ;;
256
+ *)
257
+ if [ "${END_MS}" -ge "${START_MS}" ] 2>/dev/null; then
258
+ printf '%s %s\n' "${START_MS}" "${END_MS}" > "${TELEMETRY}/bash_intervals/${TK}" 2>/dev/null || true
259
+ fi
260
+ ;;
261
+ esac
145
262
  CMD="$(pyfield "d.get('tool_input', {}).get('command')")"
146
263
  IS_VERIFY=no
147
264
  if [ -n "${CMD}" ]; then
@@ -155,15 +272,31 @@ case "${MODE}" in
155
272
  fi
156
273
  fi
157
274
  if [ "${IS_VERIFY}" = yes ]; then
158
- # Best-effort red/green from the tool response; unknown shape -> "ran"
159
- STATUS="$(pyfield "(lambda r, c=None: (lambda c: 'ok' if c == 0 else ('fail' if isinstance(c, int) else ('fail' if r.get('is_error') is True else 'ran')))(r.get('exit_code', r.get('exitCode'))))(d.get('tool_response') or {})")"
275
+ mkdir -p "${TELEMETRY}/verify_count" 2>/dev/null || true
276
+ : > "${TELEMETRY}/verify_count/${TK}" 2>/dev/null || true
277
+ # Best-effort red/green from the tool response; failure hooks are red.
278
+ if [ "${MODE}" = bash-failure ]; then
279
+ STATUS=fail
280
+ else
281
+ STATUS="$(pyfield "(lambda r, c=None: (lambda c: 'ok' if c == 0 else ('fail' if isinstance(c, int) else ('fail' if r.get('is_error') is True else 'ran')))(r.get('exit_code', r.get('exitCode'))))(d.get('tool_response') or {})")"
282
+ fi
160
283
  printf '%s\n' "${STATUS:-ran}" > "${STATE}/last_verify"
161
- # remember WHICH command produced the state the strict gate's fast
162
- # path trusts a green only when this matches its own command
163
- printf '%s\n' "${CMD}" > "${STATE}/last_verify_cmd" 2>/dev/null || true
284
+ # Keep only an opaque digest for strict-gate equality; raw commands may
285
+ # contain paths or secrets and must never persist in shared state.
286
+ printf '%s' "${CMD}" | python3 -c 'import hashlib,sys; print(hashlib.sha256(sys.stdin.buffer.read()).hexdigest())' \
287
+ > "${STATE}/last_verify_cmd_hash" 2>/dev/null || true
164
288
  rm -f "${STATE}/nudged"
165
289
  fi
166
290
  ;;
291
+ skill|skill-prompt)
292
+ if [ "${MODE}" = skill-prompt ]; then
293
+ EXPANSION_TYPE="$(pyfield "d.get('expansion_type')")"
294
+ [ "${EXPANSION_TYPE}" = slash_command ] || exit 0
295
+ fi
296
+ TK="$(tool_key)" || TK=unknown
297
+ mkdir -p "${TELEMETRY}/skill_count" 2>/dev/null || true
298
+ : > "${TELEMETRY}/skill_count/${TK}" 2>/dev/null || true
299
+ ;;
167
300
  stop)
168
301
  # Never re-block a continuation that a stop hook itself caused
169
302
  ACTIVE="$(pyfield "d.get('stop_hook_active')")"
@@ -178,6 +311,7 @@ case "${MODE}" in
178
311
  # break both the fail-open and the never-re-block guarantees.
179
312
  JSON_OK="$(printf '%s' "${IN}" | python3 -c 'import json,sys; json.load(sys.stdin); print("yes")' 2>/dev/null || echo no)"
180
313
  if [ -n "${STRICT_CMD}" ] && [ "${JSON_OK}" = yes ]; then
314
+ STRICT_START_MS="$(now_ms || true)"
181
315
  OUT="$(python3 -c '
182
316
  import os, subprocess, sys
183
317
  state, cwd, cmd, timeout = sys.argv[1:5]
@@ -192,13 +326,14 @@ def read(name):
192
326
  except OSError:
193
327
  return ""
194
328
  e, v = m("last_edit"), m("last_verify")
195
- # Fast path only for a green that THIS command (or a longer invocation of
196
- # it) produced — a broad-regex green from a mere read of the test file must
329
+ # Fast path only for a green whose command digest exactly matches. A
330
+ # broad-regex green from a mere read of the test file must
197
331
  # not disarm a gate whose promise is "actually runs the command".
198
- vcmd = read("last_verify_cmd")
332
+ vcmd = read("last_verify_cmd_hash")
333
+ cmd_hash = __import__("hashlib").sha256(cmd.encode()).hexdigest()
199
334
  if (v is not None and read("last_verify") == "ok"
200
335
  and (e is None or e <= v)
201
- and (vcmd == cmd or vcmd.startswith(cmd + " "))):
336
+ and vcmd == cmd_hash):
202
337
  print("green"); sys.exit(0)
203
338
  try:
204
339
  r = subprocess.run(cmd, shell=True, cwd=cwd or None, timeout=float(timeout),
@@ -218,14 +353,18 @@ else:
218
353
  case "${OUT%%$'\n'*}" in
219
354
  green) stat_log stop-clean; exit 0 ;;
220
355
  ok)
356
+ record_strict_telemetry "${STRICT_START_MS}"
221
357
  printf 'ok\n' > "${STATE}/last_verify" 2>/dev/null || true
222
- printf '%s\n' "${STRICT_CMD}" > "${STATE}/last_verify_cmd" 2>/dev/null || true
358
+ printf '%s' "${STRICT_CMD}" | python3 -c 'import hashlib,sys; print(hashlib.sha256(sys.stdin.buffer.read()).hexdigest())' \
359
+ > "${STATE}/last_verify_cmd_hash" 2>/dev/null || true
223
360
  rm -f "${STATE}/nudged"
224
361
  stat_log stop-clean
225
362
  exit 0 ;;
226
363
  red)
364
+ record_strict_telemetry "${STRICT_START_MS}"
227
365
  printf 'fail\n' > "${STATE}/last_verify" 2>/dev/null || true
228
- printf '%s\n' "${STRICT_CMD}" > "${STATE}/last_verify_cmd" 2>/dev/null || true
366
+ printf '%s' "${STRICT_CMD}" | python3 -c 'import hashlib,sys; print(hashlib.sha256(sys.stdin.buffer.read()).hexdigest())' \
367
+ > "${STATE}/last_verify_cmd_hash" 2>/dev/null || true
229
368
  stat_log strict-block
230
369
  echo "Strict verify gate: '${STRICT_CMD}' is RED. Fix it before finishing — or say plainly that you are handing back a red state. Failing output:" >&2
231
370
  echo "${OUT#red}" >&2
@@ -1,11 +1,11 @@
1
1
  # Luciazero — default operating mode
2
2
 
3
- Applies to every repo, every session. The loop is plan → change → verify → fix, repeated until verify passes or the blocker is real.
3
+ Applies to every repo, every session. The loop is plan → change → fastest relevant check → fix; run full verification once at closeout.
4
4
 
5
5
  ## Ground truth
6
6
 
7
7
  1. **Done is proven by a command, not by my judgment.** Before saying a change works, run something that returns an exit code — test, lint, type-check, build, or a real invocation — and quote the shortest decisive line of its output. A run that did not happen is reported as exactly that. (closeout procedure: `/done`)
8
- 2. **If no verification command exists, that is the first bug.** Say so and offer to create the smallest one that covers the change (procedure: `/luciazero-bootstrap`). Do not silently proceed on vibes.
8
+ 2. **If no verification command exists, that is the first bug.** Say so and offer to create the smallest one that covers the change (procedure: `/ready`). Do not silently proceed on vibes.
9
9
  3. **Failing test/lint means not done.** Fix the cause. Never delete, skip, weaken, or suppress a check to reach green — if a check is genuinely wrong, say why and ask.
10
10
 
11
11
  ## Loop
package/install-codex.sh CHANGED
@@ -4,7 +4,7 @@
4
4
  #
5
5
  # Mapping (single source of truth stays in claude/):
6
6
  # claude/luciazero.md -> marker block in ~/.codex/AGENTS.md
7
- # skills/catalog.txt entries -> ~/.codex/skills/<each>/
7
+ # skills/catalog.txt + aliases.txt -> ~/.codex/skills/<each>/
8
8
  # claude/agents/catalog.txt entries -> ~/.codex/skills/<agent>/SKILL.md
9
9
  # (Claude-only `tools:`/`model:` lines dropped)
10
10
  # claude/hooks/ (enforcement pack) -> NOT installed: Codex has no hooks/statusline
@@ -23,6 +23,10 @@ MANAGED_DIR="${CODEX_DIR}/.luciazero-managed"
23
23
  BACKUP_DIR="${CODEX_DIR}/.luciazero-backups"
24
24
 
25
25
  catalog() { sed '/^[[:space:]]*#/d; /^[[:space:]]*$/d' "$1"; }
26
+ skill_inventory() {
27
+ catalog "${SRC}/skills/catalog.txt"
28
+ catalog "${SRC}/skills/aliases.txt"
29
+ }
26
30
 
27
31
  # collision-proof backup path for $1 (two runs in the same second must not overwrite)
28
32
  bakpath() {
@@ -93,7 +97,7 @@ while IFS= read -r SKILL; do
93
97
  "${MANAGED_DIR}/skills/${SKILL}" \
94
98
  "skills/${SKILL}"
95
99
  echo " ok skills/${SKILL}"
96
- done < <(catalog "${SRC}/skills/catalog.txt")
100
+ done < <(skill_inventory)
97
101
 
98
102
  LEGACY_HANDOFF="${CODEX_DIR}/skills/handoff"
99
103
  if [ -f "${LEGACY_HANDOFF}/SKILL.md" ]; then
package/install.sh CHANGED
@@ -29,6 +29,10 @@ MANAGED_DIR="${CLAUDE_DIR}/.luciazero-managed"
29
29
  BACKUP_DIR="${CLAUDE_DIR}/.luciazero-backups"
30
30
 
31
31
  catalog() { sed '/^[[:space:]]*#/d; /^[[:space:]]*$/d' "$1"; }
32
+ skill_inventory() {
33
+ catalog "${SRC}/skills/catalog.txt"
34
+ catalog "${SRC}/skills/aliases.txt"
35
+ }
32
36
 
33
37
  # newest released version in this checkout's CHANGELOG (informational)
34
38
  version_of() {
@@ -50,8 +54,8 @@ if [ "${STATUS_ONLY}" = 1 ]; then
50
54
  check -f "${CLAUDE_DIR}/${DOCTRINE}" "doctrine ${DOCTRINE}"
51
55
  while IFS= read -r SKILL; do
52
56
  check -f "${CLAUDE_DIR}/skills/${SKILL}/SKILL.md" "skill ${SKILL}"
53
- done < <(catalog "${SRC}/skills/catalog.txt")
54
- check -x "${CLAUDE_DIR}/skills/luciazero-bootstrap/scripts/detect.sh" "detect.sh executable"
57
+ done < <(skill_inventory)
58
+ check -x "${CLAUDE_DIR}/skills/ready/scripts/detect.sh" "detect.sh executable"
55
59
  check -x "${CLAUDE_DIR}/skills/done/scripts/revert-probe.sh" "revert-probe.sh executable"
56
60
  check -x "${CLAUDE_DIR}/skills/bisect/scripts/safe-bisect.sh" "safe-bisect.sh executable"
57
61
  check -x "${CLAUDE_DIR}/skills/lucia-relay/scripts/relay.py" "relay.py executable"
@@ -87,12 +91,12 @@ if [ "${STATUS_ONLY}" = 1 ]; then
87
91
  fi
88
92
  done
89
93
  WIRE_MISS=""
90
- for SUB in edit bash stop session; do
91
- grep -qF "${CLAUDE_DIR}/hooks/luciazero-verify.sh ${SUB}" "${CLAUDE_DIR}/settings.json" 2>/dev/null \
94
+ for SUB in prompt skill-prompt bash-start edit bash bash-failure skill stop session; do
95
+ grep -qF "${CLAUDE_DIR}/hooks/luciazero-verify.sh ${SUB}\"" "${CLAUDE_DIR}/settings.json" 2>/dev/null \
92
96
  || WIRE_MISS="${WIRE_MISS} ${SUB}"
93
97
  done
94
98
  if [ -z "${WIRE_MISS}" ]; then
95
- echo " ok hooks wired in settings.json (edit/bash/stop/session)"
99
+ echo " ok hooks wired in settings.json (prompt/skill-prompt/bash-start/edit/bash/bash-failure/skill/stop/session)"
96
100
  else
97
101
  echo " MISS settings.json missing hook entries:${WIRE_MISS} (re-run ./install.sh --with-hooks)"; STATUS_RC=1
98
102
  fi
@@ -187,14 +191,14 @@ install_file "${SRC}/claude/${DOCTRINE}" "${CLAUDE_DIR}/${DOCTRINE}" \
187
191
  "${MANAGED_DIR}/${DOCTRINE}" "${DOCTRINE}"
188
192
  echo " ok ${DOCTRINE}"
189
193
 
190
- # 2. skills catalog.txt is the single install/status/uninstall inventory
194
+ # 2. canonical skills plus temporary compatibility aliases
191
195
  while IFS= read -r SKILL; do
192
196
  install_tree "${SRC}/skills/${SKILL}" \
193
197
  "${CLAUDE_DIR}/skills/${SKILL}" \
194
198
  "${MANAGED_DIR}/skills/${SKILL}" \
195
199
  "skills/${SKILL}"
196
200
  echo " ok skills/${SKILL}"
197
- done < <(catalog "${SRC}/skills/catalog.txt")
201
+ done < <(skill_inventory)
198
202
 
199
203
  # v1.5 migration: remove only an untouched Luciazero /handoff. A customized
200
204
  # skill is user data and stays in place with an explicit warning.
@@ -290,6 +294,11 @@ def ensure(event, matcher, command):
290
294
 
291
295
  ensure("PostToolUse", "Edit|Write|NotebookEdit", verify_cmd + " edit")
292
296
  ensure("PostToolUse", "Bash", verify_cmd + " bash")
297
+ ensure("PostToolUse", "Skill", verify_cmd + " skill")
298
+ ensure("PostToolUseFailure", "Bash", verify_cmd + " bash-failure")
299
+ ensure("PreToolUse", "Bash", verify_cmd + " bash-start")
300
+ ensure("UserPromptSubmit", None, verify_cmd + " prompt")
301
+ ensure("UserPromptExpansion", None, verify_cmd + " skill-prompt")
293
302
  ensure("Stop", None, verify_cmd + " stop")
294
303
  ensure("SessionStart", None, verify_cmd + " session")
295
304
 
@@ -323,6 +332,7 @@ echo
323
332
  SKILL_SUMMARY="$(catalog "${SRC}/skills/catalog.txt" | awk 'BEGIN{s=""} {s=s (s ? ", " : "") "/" $0} END{print s}')"
324
333
  AGENT_SUMMARY="$(catalog "${SRC}/claude/agents/catalog.txt" | awk 'BEGIN{s=""} {s=s (s ? ", " : "") $0} END{print s}')"
325
334
  echo "Skills: ${SKILL_SUMMARY}. Agents: ${AGENT_SUMMARY}."
335
+ echo "Compatibility alias for one release: /luciazero-bootstrap -> /ready."
326
336
  if [ "${WITH_HOOKS}" = 1 ]; then
327
337
  echo "Enforcement pack installed: verify-tracking hooks + statusline (see settings.json)."
328
338
  else
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "luciazero",
3
- "version": "2.0.3",
4
- "description": "Verification-first discipline for coding agents (Claude Code + Codex CLI): 9-rule doctrine, 9 skills, risk-routed reviewer, fail-open enforcement hooks. npx luciazero installs it.",
3
+ "version": "2.1.0",
4
+ "description": "Verification-first discipline for coding agents (Claude Code + Codex CLI): 9-rule doctrine, 10 skills plus a temporary command alias, risk-routed reviewer, fail-open enforcement hooks. npx luciazero installs it.",
5
5
  "repository": { "type": "git", "url": "git+https://github.com/ohm41321/luciazero.git" },
6
6
  "homepage": "https://github.com/ohm41321/luciazero#readme",
7
7
  "bugs": { "url": "https://github.com/ohm41321/luciazero/issues" },
@@ -0,0 +1,2 @@
1
+ # Temporary compatibility aliases. Remove luciazero-bootstrap after one release.
2
+ luciazero-bootstrap
@@ -1,5 +1,6 @@
1
1
  # Canonical install order. Installers, uninstallers, and tests read this file.
2
- luciazero-bootstrap
2
+ ready
3
+ show
3
4
  plan
4
5
  debug
5
6
  bisect
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: debug
3
- description: Hypothesis-driven debugging procedure. Use when a bug is not yet reliably reproduced, when a fix attempt just failed, when debugging has gone two or more iterations without progress, or when the user asks "why is this failing", "debug this properly", "ไล่บั๊ก". Not for trivial errors whose cause is already visible in the message.
3
+ description: Hypothesis-driven debugging procedure. Use when a bug is not yet reliably reproduced, a fix attempt failed, debugging has gone two or more iterations without progress, or the user asks "debug this properly" or "ไล่บั๊ก". Not for a first obvious failure whose cause is already visible; reproduce and fix it directly.
4
4
  ---
5
5
 
6
6
  # Debug — hypothesis before edit
@@ -11,8 +11,10 @@ Run:
11
11
  npx luciazero discipline [--days N] [--project PATH_OR_ID] [--json]
12
12
  ```
13
13
 
14
- The report reads `luciazero-stats.log` from the Claude config directory by default. It accepts current schema-versioned JSON lines and legacy space-delimited records, ignores malformed lines without failing, and never sends data over the network.
14
+ The report reads `luciazero-stats.log` from the Claude config directory by default. It accepts current schema-versioned JSON lines and legacy space-delimited records, ignores malformed lines without failing, and never sends data over the network. New enforcement-pack installs also summarize measured turn/Bash wall-clock milliseconds and Bash, verify, and model/user skill invocation counts. Parallel Bash intervals are merged before subtraction. These are aggregates: raw commands and skill names are never persisted.
15
15
 
16
16
  Treat recorded outcomes as observations, not causes. A `nudge` proves an edit lacked a recognized later verify run; it does not prove why. A `strict-block` proves the configured strict command was red. Recommendations derived from patterns must say `likely` unless the log directly records the cause.
17
17
 
18
+ Latency telemetry separates observed Bash time from the rest of the measured turn. The non-Bash remainder can include model reasoning, non-Bash tools, hook overhead, and harness scheduling, so do not label it as model latency without another measurement.
19
+
18
20
  Use `--project .` to filter by the current repository's privacy-preserving project hash, or `--project <display-name-or-id>` for another entry. Use `--json` when feeding a dashboard or `/retro`.
@@ -12,7 +12,7 @@ The doctrine says: *done is proven by a command, not by my judgment.* This is th
12
12
  Run the **full** tier (`verify-full` if the repo has two tiers, else the verify command). Quote the shortest decisive line of real output.
13
13
 
14
14
  - Red → you are not here yet. Go back to the loop; do not continue this ritual.
15
- - No verify command exists → that is the first bug (`/luciazero-bootstrap`). Say so instead of declaring done.
15
+ - No verify command exists → that is the first bug (`/ready`). Say so instead of declaring done.
16
16
  - The command must actually have run **now**, in this session — a green from an hour ago proves the past, not the present.
17
17
 
18
18
  ## 2. Skeptic diff pass
@@ -28,7 +28,7 @@ git rev-parse --verify --quiet "${BASE}^{commit}" >/dev/null 2>&1 \
28
28
  TOP="$(git rev-parse --show-toplevel 2>/dev/null)" || unassessable "no working tree (bare repo?)"
29
29
  cd "${TOP}"
30
30
 
31
- # test-file patterns mirror luciazero-bootstrap's detect.sh: tests-style dirs
31
+ # test-file patterns mirror ready's detect.sh: tests-style dirs
32
32
  # plus the common root `test.sh` entrypoint and test_*.*, *_test.*, *.test.*,
33
33
  # *.spec.* file names
34
34
  is_test_file() {
@@ -1,109 +1,10 @@
1
1
  ---
2
2
  name: luciazero-bootstrap
3
- description: Make a repository agentic-ready so an agent can run its own plan→change→verify→fix loop without a human checking each step. Use when entering an unfamiliar repo, when the user asks to "set up agentic engineering", "make this repo agent-friendly", "add a verify command", "add smoke tests so you can check your own work", "set up hooks/CLAUDE.md/allowlist" or when a change was requested but no automated way exists to prove it works.
3
+ description: Compatibility alias for /ready. Use only when the user explicitly invokes /luciazero-bootstrap; tell them it was renamed to /ready, then follow the canonical ready procedure completely.
4
4
  ---
5
5
 
6
- # Luciazero Bootstrap
6
+ # Renamed to Ready
7
7
 
8
- Goal: leave the repo with **one command that returns an exit code** and enough guardrails that future agent work self-verifies. Nothing here is language-specific detect, don't assume.
9
-
10
- Bootstrapping is itself work: verify each artifact you add actually runs before reporting it.
11
-
12
- ## Phase 1 — Detect (never assume)
13
-
14
- Run the bundled evidence scan first — it replaces a dozen manual reads with one call:
15
-
16
- ```
17
- <this-skill-dir>/scripts/detect.sh <repo-root>
18
- ```
19
-
20
- (The skill directory is wherever this SKILL.md lives, e.g. `~/.claude/skills/luciazero-bootstrap/` or `~/.codex/skills/luciazero-bootstrap/`.) The script surfaces candidates — **you still decide**. It cannot parse CI matrices or exotic build systems; open anything it flags and read the CI config yourself.
21
-
22
- Sources, in order of trust:
23
-
24
- 1. CI config — the most honest source of truth: `.github/workflows/*`, `.gitlab-ci.yml`, `.circleci/`. **Whatever CI runs is the verify command.**
25
- 2. Manifests: `package.json` scripts, `pyproject.toml` / `tox.ini` / `noxfile.py`, `Makefile`, `justfile`, `Cargo.toml`, `go.mod`, `build.gradle`, `composer.json`
26
- 3. Repo docs: `README*`, `CONTRIBUTING*`, `AGENTS.md`, `CLAUDE.md`, `docs/` — docs go stale; cross-check any doc-claimed command against CI when CI exists. A docs/CI mismatch is itself a finding to record in Phase 5.
27
- 4. Existing test dirs: `tests/`, `test/`, `spec/`, `__tests__/`, `*_test.*`, `test_*.*`
28
-
29
- Report what was found as a short table: run / test / lint / typecheck / build / git repo — command or `MISSING`.
30
-
31
- **If the directory is not under version control**, propose `git init` early (ask first — some dirs are deliberately not repos): without git there is no smallest reversible step, no safe break-and-restore in Phase 6, and no bisect.
32
-
33
- ## Phase 2 — Establish the verify command
34
-
35
- If a verify path exists, **use it** — do not invent a parallel one.
36
-
37
- If none exists, create the smallest real one. Order of preference:
38
-
39
- 1. The project's native runner, already installed (`pytest`, `vitest`, `go test`, `cargo test`, `dotnet test`)
40
- 2. A single entrypoint that chains them, matching the repo's existing convention (`Makefile` target, `package.json` script, `justfile` recipe) — e.g. `make verify` running lint then tests
41
-
42
- Rules:
43
- - Must exit non-zero on failure. A script that always exits 0 is worse than nothing.
44
- - Must run to completion unattended: disable watch/interactive modes (e.g. `CI=1`, `--run`, `--watch=false`) — a command that waits for input or watches files hangs the loop.
45
- - Must run offline, with no credentials. Anything needing GPU/network/secrets belongs in a separate slow target.
46
- - Time the suite once (`time <cmd>`); the measurement, not a guess, decides one tier or two.
47
- - On success, output should be near-silent — prefer quiet flags in the fast tier so failures, not progress spam, fill the context.
48
- - Add it to the repo's own docs so humans find it too.
49
-
50
- **Two tiers when the repo has slow checks.** One `verify` command forces a bad trade: either the loop crawls or coverage gets cut. Split it:
51
-
52
- - `verify` — fast (<~60s), offline: lint, typecheck, unit/smoke tests. Run on **every** loop iteration.
53
- - `verify-full` — everything else: full suite, integration, build, slow checks. Run **before declaring done** and before a PR — "done" means `verify-full` green, not just `verify`.
54
-
55
- Name them by the repo's convention (`make verify` / `make verify-full`, npm scripts, just recipes). A small repo whose whole suite runs in seconds needs only the single tier — do not add ceremony it does not need.
56
-
57
- **Monorepos:** detect the workspace layout (`package.json` `workspaces`, `pnpm-workspace.yaml`, turbo/nx config, `go.work`, Cargo `[workspace]`). Prefer a repo-owned `verify-changed` target backed by the workspace's native dependency graph; `verify-full` remains the root suite. Never make a global hook guess package mappings from path prefixes. Read [references/smart-verification.md](references/smart-verification.md) before creating the target, and record its base-revision/fallback contract in Phase 5 notes.
58
-
59
- **Enforcement pack users (Claude Code, ask first):** if the verify-tracking hooks are active — classic install: `~/.claude/hooks/luciazero-verify.sh` exists; plugin install: the `luciazero` plugin is enabled — offer to record the established command in the repo's *personal* settings so the tracker matches it exactly instead of by broad regex — `.claude/settings.local.json` (gitignored, never committed): `{"env": {"LUCIAZERO_VERIFY_CMD": "<the fast-tier command>"}}`. Derive it from CI (the honest source); it is a cache of that truth, so note it must be updated if CI changes. Show the exact JSON before writing anything.
60
-
61
- ## Phase 3 — Smoke tests, if there are none
62
-
63
- Do **not** attempt coverage. Write 3–6 tests that would catch a catastrophic break. Pick by this heuristic:
64
-
65
- - **Contract shape** — the core data structure in/out: dimensions, keys, types, no NaN/null where impossible
66
- - **Round trip** — serialize→deserialize, encode→decode, save→load returns equal
67
- - **Import/boot** — every package imports, the app answers one request, the CLI runs `--help`. Prefer the framework's test client over binding a real port; any test that starts a process needs a hard timeout and must kill what it started.
68
- - **Artifact loads** — trained model / migration / config parses and does one forward pass or one query
69
- - **The bug you were sent to fix** — a regression test reproducing it, written *before* the fix
70
-
71
- Use fixtures small enough to commit. Never depend on the user's real data paths.
72
-
73
- State plainly that these are smoke tests, not a suite.
74
-
75
- ## Phase 4 — Guardrails (only ones that pay for themselves)
76
-
77
- Hooks, `.claude/settings.json`, and `/fewer-permission-prompts` are **Claude Code mechanisms**. On a harness without them (Codex CLI), skip the hook items and encode the same guardrails as instructions in the project's `AGENTS.md` instead: which files are untouchable, which derived file must be regenerated after editing which source.
78
-
79
- Prefer few and deterministic. Candidates, in value order:
80
-
81
- - **Auto-format/lint on write** — `PostToolUse` hook matching `Edit|Write`, running the repo's own formatter. Only if the repo already has one configured.
82
- - **Regenerate derived files** — if editing source X requires regenerating Y (protobuf, OpenAPI clients, migrations, lockfiles), hook it, scoped inside the command to the relevant paths. This is the highest-value hook in most repos because humans forget it.
83
- - **Protect the untouchables** — `PreToolUse` deny on production config, secrets, live model/deploy pointers.
84
- - **Permission allowlist** — put the repo's read-only and verify commands into `.claude/settings.json` so the loop is not interrupted. `/fewer-permission-prompts` derives this from real transcripts.
85
-
86
- Put project-scoped settings in the repo's `.claude/settings.json` (shared) or `.claude/settings.local.json` (personal, gitignored) — **not** in global settings.
87
-
88
- Hooks execute automatically on the user's machine. Show the exact command before installing it, and never install one that pushes, deploys, deletes, or writes outside the repo.
89
-
90
- ## Phase 5 — Project notes file (`CLAUDE.md` / `AGENTS.md`)
91
-
92
- Extend the notes file the repo already uses; if neither exists, create the one matching the current harness and add a one-line pointer from the other name so both find it. Write only what reading the code cannot tell you:
93
-
94
- - How to run / test / verify — the commands from Phase 2
95
- - Architecture facts that are load-bearing and non-obvious (what serves what, which file is source of truth)
96
- - **Footguns and null results**: "X looks right but breaks Y", "tried A, measured no gain, do not retry", "always rebuild Z after W"
97
- - Where the real docs live
98
-
99
- Do not restate the directory tree, git history, or anything a `grep` answers. Keep it dense; every line costs context on every future session.
100
-
101
- ## Phase 6 — Prove it and report
102
-
103
- 1. **Flake check** — run the fast verify tier twice. A green that does not repeat is a flake, and a flaky verify makes every future red ambiguous; fixing or quarantining the flake comes before relying on the loop. (Skip the double run only when the repo has a single slow tier — say so.)
104
- 2. **Red check** — break a line a smoke test actually covers (flip an expected value or a return), confirm verify goes red, then restore. The break is one deliberate edit: **record file, line, and original text before making it, and restore by reverting exactly that edit.** Only use `git checkout -- <file>` if the file was committed before the break — on a file carrying uncommitted work it silently discards that work too, and it cannot restore the untracked test files this skill just wrote. Never use bare `git stash` here (it sweeps the whole tree and skips untracked files). Breaking an uncovered line and staying green proves nothing. A verify command that cannot fail is not a verify command.
105
-
106
- Report:
107
- - The one command to run (both tiers if split)
108
- - What it does and does not cover
109
- - What was added, and what was deliberately left out
8
+ Tell the user `/luciazero-bootstrap` is deprecated and renamed to `/ready`.
9
+ Then read `../ready/SKILL.md` and follow that procedure completely, resolving
10
+ its relative resource paths from `../ready/`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: plan
3
- description: Build a verification-first implementation plan for new features, major refactors, ambiguous work, or any multi-step change whose acceptance criteria are not yet falsifiable. Use when the user asks for a plan or before risky work; request approval only when ambiguity, high stakes, destructive action, a public-contract choice, or a scope change requires it.
3
+ description: Build a verification-first implementation plan for new features, major refactors, ambiguous work, or risky multi-module changes whose acceptance criteria are not yet falsifiable. Use when the user asks for a plan or material choices remain. Not for routine edits whose scope and proof are already clear.
4
4
  ---
5
5
 
6
6
  # Plan — make the change falsifiable
@@ -0,0 +1,109 @@
1
+ ---
2
+ name: ready
3
+ description: Make a repository agentic-ready so an agent can run its own plan→change→verify→fix loop without a human checking each step. Use when entering an unfamiliar repo, when the user asks to "set up agentic engineering", "make this repo agent-friendly", "add a verify command", "add smoke tests so you can check your own work", "set up hooks/CLAUDE.md/allowlist" — or when a change was requested but no automated way exists to prove it works.
4
+ ---
5
+
6
+ # Ready
7
+
8
+ Goal: leave the repo with **one command that returns an exit code** and enough guardrails that future agent work self-verifies. Nothing here is language-specific — detect, don't assume.
9
+
10
+ Bootstrapping is itself work: verify each artifact you add actually runs before reporting it.
11
+
12
+ ## Phase 1 — Detect (never assume)
13
+
14
+ Run the bundled evidence scan first — it replaces a dozen manual reads with one call:
15
+
16
+ ```
17
+ <this-skill-dir>/scripts/detect.sh <repo-root>
18
+ ```
19
+
20
+ (The skill directory is wherever this SKILL.md lives, e.g. `~/.claude/skills/ready/` or `~/.codex/skills/ready/`.) The script surfaces candidates — **you still decide**. It cannot parse CI matrices or exotic build systems; open anything it flags and read the CI config yourself.
21
+
22
+ Sources, in order of trust:
23
+
24
+ 1. CI config — the most honest source of truth: `.github/workflows/*`, `.gitlab-ci.yml`, `.circleci/`. **Whatever CI runs is the verify command.**
25
+ 2. Manifests: `package.json` scripts, `pyproject.toml` / `tox.ini` / `noxfile.py`, `Makefile`, `justfile`, `Cargo.toml`, `go.mod`, `build.gradle`, `composer.json`
26
+ 3. Repo docs: `README*`, `CONTRIBUTING*`, `AGENTS.md`, `CLAUDE.md`, `docs/` — docs go stale; cross-check any doc-claimed command against CI when CI exists. A docs/CI mismatch is itself a finding to record in Phase 5.
27
+ 4. Existing test dirs: `tests/`, `test/`, `spec/`, `__tests__/`, `*_test.*`, `test_*.*`
28
+
29
+ Report what was found as a short table: run / test / lint / typecheck / build / git repo — command or `MISSING`.
30
+
31
+ **If the directory is not under version control**, propose `git init` early (ask first — some dirs are deliberately not repos): without git there is no smallest reversible step, no safe break-and-restore in Phase 6, and no bisect.
32
+
33
+ ## Phase 2 — Establish the verify command
34
+
35
+ If a verify path exists, **use it** — do not invent a parallel one.
36
+
37
+ If none exists, create the smallest real one. Order of preference:
38
+
39
+ 1. The project's native runner, already installed (`pytest`, `vitest`, `go test`, `cargo test`, `dotnet test`)
40
+ 2. A single entrypoint that chains them, matching the repo's existing convention (`Makefile` target, `package.json` script, `justfile` recipe) — e.g. `make verify` running lint then tests
41
+
42
+ Rules:
43
+ - Must exit non-zero on failure. A script that always exits 0 is worse than nothing.
44
+ - Must run to completion unattended: disable watch/interactive modes (e.g. `CI=1`, `--run`, `--watch=false`) — a command that waits for input or watches files hangs the loop.
45
+ - Must run offline, with no credentials. Anything needing GPU/network/secrets belongs in a separate slow target.
46
+ - Time the suite once (`time <cmd>`); the measurement, not a guess, decides one tier or two.
47
+ - On success, output should be near-silent — prefer quiet flags in the fast tier so failures, not progress spam, fill the context.
48
+ - Add it to the repo's own docs so humans find it too.
49
+
50
+ **Two tiers when the repo has slow checks.** One `verify` command forces a bad trade: either the loop crawls or coverage gets cut. Split it:
51
+
52
+ - `verify` — fast (<~60s), offline: lint, typecheck, unit/smoke tests. Run on **every** loop iteration.
53
+ - `verify-full` — everything else: full suite, integration, build, slow checks. Run **before declaring done** and before a PR — "done" means `verify-full` green, not just `verify`.
54
+
55
+ Name them by the repo's convention (`make verify` / `make verify-full`, npm scripts, just recipes). A small repo whose whole suite runs in seconds needs only the single tier — do not add ceremony it does not need.
56
+
57
+ **Monorepos:** detect the workspace layout (`package.json` `workspaces`, `pnpm-workspace.yaml`, turbo/nx config, `go.work`, Cargo `[workspace]`). Prefer a repo-owned `verify-changed` target backed by the workspace's native dependency graph; `verify-full` remains the root suite. Never make a global hook guess package mappings from path prefixes. Read [references/smart-verification.md](references/smart-verification.md) before creating the target, and record its base-revision/fallback contract in Phase 5 notes.
58
+
59
+ **Enforcement pack users (Claude Code, ask first):** if the verify-tracking hooks are active — classic install: `~/.claude/hooks/luciazero-verify.sh` exists; plugin install: the `luciazero` plugin is enabled — offer to record the established command in the repo's *personal* settings so the tracker matches it exactly instead of by broad regex — `.claude/settings.local.json` (gitignored, never committed): `{"env": {"LUCIAZERO_VERIFY_CMD": "<the fast-tier command>"}}`. Derive it from CI (the honest source); it is a cache of that truth, so note it must be updated if CI changes. Show the exact JSON before writing anything.
60
+
61
+ ## Phase 3 — Smoke tests, if there are none
62
+
63
+ Do **not** attempt coverage. Write 3–6 tests that would catch a catastrophic break. Pick by this heuristic:
64
+
65
+ - **Contract shape** — the core data structure in/out: dimensions, keys, types, no NaN/null where impossible
66
+ - **Round trip** — serialize→deserialize, encode→decode, save→load returns equal
67
+ - **Import/boot** — every package imports, the app answers one request, the CLI runs `--help`. Prefer the framework's test client over binding a real port; any test that starts a process needs a hard timeout and must kill what it started.
68
+ - **Artifact loads** — trained model / migration / config parses and does one forward pass or one query
69
+ - **The bug you were sent to fix** — a regression test reproducing it, written *before* the fix
70
+
71
+ Use fixtures small enough to commit. Never depend on the user's real data paths.
72
+
73
+ State plainly that these are smoke tests, not a suite.
74
+
75
+ ## Phase 4 — Guardrails (only ones that pay for themselves)
76
+
77
+ Hooks, `.claude/settings.json`, and `/fewer-permission-prompts` are **Claude Code mechanisms**. On a harness without them (Codex CLI), skip the hook items and encode the same guardrails as instructions in the project's `AGENTS.md` instead: which files are untouchable, which derived file must be regenerated after editing which source.
78
+
79
+ Prefer few and deterministic. Candidates, in value order:
80
+
81
+ - **Auto-format/lint on write** — `PostToolUse` hook matching `Edit|Write`, running the repo's own formatter. Only if the repo already has one configured.
82
+ - **Regenerate derived files** — if editing source X requires regenerating Y (protobuf, OpenAPI clients, migrations, lockfiles), hook it, scoped inside the command to the relevant paths. This is the highest-value hook in most repos because humans forget it.
83
+ - **Protect the untouchables** — `PreToolUse` deny on production config, secrets, live model/deploy pointers.
84
+ - **Permission allowlist** — put the repo's read-only and verify commands into `.claude/settings.json` so the loop is not interrupted. `/fewer-permission-prompts` derives this from real transcripts.
85
+
86
+ Put project-scoped settings in the repo's `.claude/settings.json` (shared) or `.claude/settings.local.json` (personal, gitignored) — **not** in global settings.
87
+
88
+ Hooks execute automatically on the user's machine. Show the exact command before installing it, and never install one that pushes, deploys, deletes, or writes outside the repo.
89
+
90
+ ## Phase 5 — Project notes file (`CLAUDE.md` / `AGENTS.md`)
91
+
92
+ Extend the notes file the repo already uses; if neither exists, create the one matching the current harness and add a one-line pointer from the other name so both find it. Write only what reading the code cannot tell you:
93
+
94
+ - How to run / test / verify — the commands from Phase 2
95
+ - Architecture facts that are load-bearing and non-obvious (what serves what, which file is source of truth)
96
+ - **Footguns and null results**: "X looks right but breaks Y", "tried A, measured no gain, do not retry", "always rebuild Z after W"
97
+ - Where the real docs live
98
+
99
+ Do not restate the directory tree, git history, or anything a `grep` answers. Keep it dense; every line costs context on every future session.
100
+
101
+ ## Phase 6 — Prove it and report
102
+
103
+ 1. **Flake check** — run the fast verify tier twice. A green that does not repeat is a flake, and a flaky verify makes every future red ambiguous; fixing or quarantining the flake comes before relying on the loop. (Skip the double run only when the repo has a single slow tier — say so.)
104
+ 2. **Red check** — break a line a smoke test actually covers (flip an expected value or a return), confirm verify goes red, then restore. The break is one deliberate edit: **record file, line, and original text before making it, and restore by reverting exactly that edit.** Only use `git checkout -- <file>` if the file was committed before the break — on a file carrying uncommitted work it silently discards that work too, and it cannot restore the untracked test files this skill just wrote. Never use bare `git stash` here (it sweeps the whole tree and skips untracked files). Breaking an uncovered line and staying green proves nothing. A verify command that cannot fail is not a verify command.
105
+
106
+ Report:
107
+ - The one command to run (both tiers if split)
108
+ - What it does and does not cover
109
+ - What was added, and what was deliberately left out
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # Read-only evidence scan for luciazero-bootstrap Phase 1.
2
+ # Read-only evidence scan for ready Phase 1.
3
3
  # Prints what exists in a repo — docs, manifests, script/target names, CI run
4
4
  # lines, test dirs, workspace markers. It surfaces candidates only; it never
5
5
  # picks the verify command. Judgment stays with the agent.
@@ -0,0 +1,133 @@
1
+ ---
2
+ name: show
3
+ description: Turn code structure, changes, and verification evidence into the smallest useful visual. Use when the user invokes /show, asks what connects to what, what changed, how a flow works, or what proves a result; use for compact pseudocode, call or component trees, file maps, structural diffs, Mermaid diagrams, and evidence maps, with focused HTML only when simpler forms cannot carry the information.
4
+ ---
5
+
6
+ # Show — make the evidence visible
7
+
8
+ Answer three questions at a glance:
9
+
10
+ 1. What connects to what?
11
+ 2. What changed?
12
+ 3. What proves it?
13
+
14
+ Build an evidence view, not a decorative diagram. The view summarizes reality;
15
+ source files, diffs, and command results remain the ground truth.
16
+
17
+ ## 1. Set the focus
18
+
19
+ Use the user's question and current task context as the input. Do not ask for
20
+ details that can be discovered from the repository. Narrow broad requests to
21
+ the smallest boundary that answers the question, and state that boundary.
22
+
23
+ Gather only the relevant evidence:
24
+
25
+ - definitions, callers, consumers, configuration, and ownership;
26
+ - the current diff or before/after revisions;
27
+ - verification command, exit code, shortest decisive output, and coverage gaps.
28
+
29
+ Never expose private chain-of-thought. Show observable structure, evidence, and
30
+ concise conclusions instead.
31
+
32
+ ## 2. Normalize the evidence
33
+
34
+ Reduce what was found to five kinds of information:
35
+
36
+ - **Entities** — files, functions, components, services, states, or commands;
37
+ - **Relations** — calls, owns, reads, writes, emits, depends on, or verifies;
38
+ - **Changes** — added, removed, or modified entities and relations;
39
+ - **Proof** — commands and observations that confirm or refute a claim;
40
+ - **Gaps** — unknown, inferred, or unverified parts.
41
+
42
+ Label inference as `? inferred`; never draw a guessed edge as fact.
43
+
44
+ ## 3. Choose the smallest useful view
45
+
46
+ Prefer the first form that carries the relationship clearly:
47
+
48
+ | Question | View |
49
+ |---|---|
50
+ | What does this logic decide? | Compact pseudocode |
51
+ | Who calls what at runtime? | Call tree |
52
+ | Who owns or contains what? | Component or shallow file tree |
53
+ | How do 3+ parts exchange control or data? | Mermaid flow or sequence |
54
+ | What changed structurally? | Before/after structural diff |
55
+ | Why is this considered complete? | Requirement-to-proof evidence map |
56
+ | Is prose already clearer? | One sentence or a short list; draw nothing |
57
+
58
+ Use one primary view. Add a second only when it answers a different question.
59
+ Use focused HTML only for dense UI, layout, or interactive state that text and
60
+ Mermaid cannot show clearly. Keep HTML temporary unless the user asks to keep
61
+ it, and open it only when the harness and user permissions allow.
62
+
63
+ ## 4. Render with a stable grammar
64
+
65
+ Use these marks consistently in text views:
66
+
67
+ ```text
68
+ A --> B calls or moves data to
69
+ A --owns--> B named relationship
70
+ + item added
71
+ - item removed
72
+ ~ item changed
73
+ [+] proven verification passed
74
+ [x] disproven verification failed
75
+ [?] unknown not verified
76
+ [path/to/file:line] source pointer
77
+ ```
78
+
79
+ Keep labels concrete and short. Omit unrelated files, helper calls, props,
80
+ states, and branches. A reader should not need a legend beyond the grammar
81
+ above.
82
+
83
+ For Mermaid, keep node IDs simple, quote labels containing punctuation, and
84
+ put source pointers outside the diagram when they would make nodes noisy.
85
+
86
+ ## 5. Attach evidence
87
+
88
+ Every important node or edge must be traceable to at least one of:
89
+
90
+ - `path/to/file:line` for source structure;
91
+ - a diff hunk or revision for a change;
92
+ - an exact command, exit code, and shortest decisive output for proof.
93
+
94
+ Do not use a green-looking diagram as verification. If no command ran, write
95
+ `not run`. If a check does not cover a shown claim, mark that claim `[?]` and
96
+ name the missing coverage. Failed proof remains visible as `[x]`; do not hide it
97
+ to make the view look complete.
98
+
99
+ ## Output contract
100
+
101
+ Return, in this order:
102
+
103
+ 1. **Answer** — one or two sentences naming the focus and conclusion.
104
+ 2. **View** — the smallest useful visual.
105
+ 3. **Sources** — compact file/line or revision pointers.
106
+ 4. **Proof** — command, exit code, and decisive output; or `not run`.
107
+ 5. **Unknowns** — uncovered or inferred parts; omit only when there are none.
108
+
109
+ For a completed change, an evidence map may look like:
110
+
111
+ ```text
112
+ request
113
+ --> ~ skills/catalog.txt
114
+ --> + skills/show/SKILL.md
115
+ --> ~ README.md / README.th.md
116
+ |
117
+ +--verified by--> [+] ./test.sh (exit 0)
118
+ `PASS all checks green`
119
+
120
+ [?] Real invocation in a fresh agent session was not exercised.
121
+ ```
122
+
123
+ ## Fit into the Luciazero loop
124
+
125
+ - With `/ready`, show the path from CI to the repository verify command.
126
+ - With `/plan`, show the proposed before/after boundary and acceptance proof.
127
+ - With `/debug`, show hypothesis → observation → conclusion without replacing
128
+ the reproduction or hypothesis ledger.
129
+ - With `/done`, show requirement → changed artifact → verification evidence.
130
+ - With `/lucia-relay`, show current state → next action → blocker.
131
+
132
+ The lifecycle skill owns the work and verification. `/show` only makes its
133
+ structure and evidence easier to inspect.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Show"
3
+ short_description: "Map code changes and proof visually"
4
+ default_prompt: "Use $show to map what connects, what changed, and what proves it."
@@ -14,6 +14,10 @@ END='<!-- luciazero:end -->'
14
14
  MANAGED_DIR="${CODEX_DIR}/.luciazero-managed"
15
15
 
16
16
  catalog() { sed '/^[[:space:]]*#/d; /^[[:space:]]*$/d' "$1"; }
17
+ skill_inventory() {
18
+ catalog "${SRC}/skills/catalog.txt"
19
+ catalog "${SRC}/skills/aliases.txt"
20
+ }
17
21
 
18
22
  # collision-proof backup path for $1 (two runs in the same second must not overwrite)
19
23
  bakpath() {
@@ -48,7 +52,7 @@ rm -f "${CODEX_DIR}/.luciazero-version"
48
52
  while IFS= read -r SKILL; do
49
53
  remove_managed_tree "${CODEX_DIR}/skills/${SKILL}" \
50
54
  "${MANAGED_DIR}/skills/${SKILL}" "${SRC}/skills/${SKILL}" "skills/${SKILL}"
51
- done < <(catalog "${SRC}/skills/catalog.txt")
55
+ done < <(skill_inventory)
52
56
 
53
57
  AGENT_STAGE_ROOT="$(mktemp -d)"
54
58
  trap 'rm -rf "${AGENT_STAGE_ROOT}"' EXIT
package/uninstall.sh CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # Remove the Luciazero doctrine + bootstrap skill from ~/.claude/
2
+ # Remove the Luciazero doctrine + skills from ~/.claude/
3
3
  set -euo pipefail
4
4
 
5
5
  for ARG in "$@"; do
@@ -14,6 +14,10 @@ GLOBAL_MD="${CLAUDE_DIR}/CLAUDE.md"
14
14
  MANAGED_DIR="${CLAUDE_DIR}/.luciazero-managed"
15
15
 
16
16
  catalog() { sed '/^[[:space:]]*#/d; /^[[:space:]]*$/d' "$1"; }
17
+ skill_inventory() {
18
+ catalog "${SRC}/skills/catalog.txt"
19
+ catalog "${SRC}/skills/aliases.txt"
20
+ }
17
21
 
18
22
  # collision-proof backup path for $1 (two runs in the same second must not overwrite)
19
23
  bakpath() {
@@ -66,7 +70,7 @@ rm -f "${CLAUDE_DIR}/.luciazero-version"
66
70
  while IFS= read -r SKILL; do
67
71
  remove_managed_tree "${CLAUDE_DIR}/skills/${SKILL}" \
68
72
  "${MANAGED_DIR}/skills/${SKILL}" "${SRC}/skills/${SKILL}" "skills/${SKILL}"
69
- done < <(catalog "${SRC}/skills/catalog.txt")
73
+ done < <(skill_inventory)
70
74
 
71
75
  while IFS= read -r AGENT_NAME; do
72
76
  remove_managed_file "${CLAUDE_DIR}/agents/${AGENT_NAME}.md" \