@dzhechkov/harness-cli 0.4.6 → 0.5.1

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
@@ -125,6 +125,140 @@ point any tool at (a skill is just Markdown). Dedicated targets are on the [road
125
125
  **❌ No target applies:** `nemoclaw` (NVIDIA agent-safety runtime — not a code editor, no rules file) ·
126
126
  `v0` (UI-only, no repo file). *(`pi` is a real coder but reads `AGENTS.md` → covered by `agents-md`.)*
127
127
 
128
+ #### `--target` aliases and did-you-mean
129
+
130
+ `--target claude` used to be rejected outright — the canonical name is `claude-code`, and the error
131
+ just re-printed the list. All **eight** `--target`-taking commands (`init`, `verify`, `install`,
132
+ `compose`, `setup`, `upgrade`, `parity`, `feature-adr-setup`) now resolve the value first.
133
+
134
+ *(This sentence used to say "every" and name only seven: `dz parity` was missed, and shipped the
135
+ original defect verbatim. It is now checked rather than promised — `test/target-alias-cli.test.ts`
136
+ asserts that no command reads `--target` without reaching the resolver, and that every command whose
137
+ `--help` advertises `--target` appears in the per-command sweep.)*
138
+
139
+ **Accepted aliases** (a table, so adding one is a data edit):
140
+
141
+ | You type | You get | Why it is a row |
142
+ |----------|---------|-----------------|
143
+ | `claude` · `cc` | `claude-code` | the tool's everyday name |
144
+ | `agents` | `agents-md` | the everyday name of the `AGENTS.md` target |
145
+ | `gpt` · `openai` | `codex` | the vendor name, not the CLI's |
146
+
147
+ Case, padding and separators are handled **without** a row: `CLAUDE`, `Claude_Code`, `claudecode`
148
+ and ` claude-code ` all resolve to `claude-code`; `agentsmd` and `agents.md` resolve to `agents-md`.
149
+
150
+ **A typo is SUGGESTED, never silently accepted** — installing to the wrong target on a guess is worse
151
+ than one round-trip:
152
+
153
+ ```console
154
+ $ dz init --target clade-code --preset devops > out.txt # the refusal is on STDERR
155
+ dz init: unknown --target "clade-code" — did you mean "claude-code"?
156
+ --target must be one of: agents-md, claude-code, codex, copilot, cursor, gemini, hermes, openclaude, opencode, windsurf
157
+ $ echo $?
158
+ 1
159
+ $ wc -c out.txt
160
+ 0 out.txt # stdout stays a clean data channel, even on a refusal
161
+ ```
162
+
163
+ **Both lines go to stderr** — the refusal, like the alias note below, is diagnosis, not data. `dz
164
+ parity --target <bad> --json` likewise writes its structured error to stderr and leaves stdout empty,
165
+ so `| jq` is never handed a diagnostic.
166
+
167
+ An ambiguous prefix gets no guess at all (`--target co` could be `codex` or `copilot`), and neither
168
+ does nonsense (`--target totally-bogus` prints the plain list). When an alias IS accepted, the
169
+ substitution is announced **on stderr**, so piped stdout stays machine-readable:
170
+
171
+ ```console
172
+ $ dz init --target claude --preset devops > installed.txt
173
+ dz init: --target "claude" → claude-code (alias)
174
+ ```
175
+
176
+ **When this matters:** you are following a blog post or a teammate's snippet that says `--target claude`.
177
+ It now works, and you are told what it resolved to.
178
+
179
+ ### `dz list` — one broken skill never hides the rest
180
+
181
+ `dz list` used to abort on the FIRST unparseable `SKILL.md`: exit 1, an error naming neither the file
182
+ nor a count, and every other skill in the tree invisible. A pack you installed could blank your whole
183
+ listing. Now the loadable skills are listed on **stdout** and the unloadable ones are named on
184
+ **stderr**:
185
+
186
+ ```console
187
+ $ dz list --skills-dir .claude/skills
188
+ 105 skill(s) in /home/you/proj/.claude/skills:
189
+
190
+ api-design Designs REST and GraphQL APIs with OpenAPI specs…
191
+ …103 more…
192
+ test-writer Writes focused unit and integration tests…
193
+ $ echo $?
194
+ 1
195
+ ```
196
+
197
+ …with the diagnosis kept out of the data:
198
+
199
+ ```console
200
+ $ dz list --skills-dir .claude/skills 2>/dev/null # stdout only — clean, pipeable
201
+ 105 skill(s) in …
202
+
203
+ $ dz list --skills-dir .claude/skills 1>/dev/null # stderr only — the actionable part
204
+ dz list: 105 listed, 1 skipped in /home/you/proj/.claude/skills
205
+ ⚠ 1 skill(s) skipped (unparseable SKILL.md):
206
+ /home/you/proj/.claude/skills/broken-one/SKILL.md
207
+ SKILL.md must begin with a "---" frontmatter fence
208
+ (line 1: "# Broken One")
209
+ ```
210
+
211
+ The full contract:
212
+
213
+ | valid skills | skipped | stdout | stderr | exit |
214
+ |--------------|---------|--------|--------|------|
215
+ | >0 | 0 | the listing | *empty* | 0 |
216
+ | >0 | >0 | the listing of the valid ones | named summary | 1 |
217
+ | 0 | >0 | *nothing* | named summary (`0 listed, N skipped`) | 1 |
218
+ | 0 | 0 | *nothing* | `dz list: no skills found in <dir>` | 1 |
219
+
220
+ `dz init`, `dz install` and `dz sync` behave the same way — the good skills are installed, the bad ones
221
+ are named, and the command exits 1.
222
+
223
+ **Two failure kinds, two headers, two subjects.** A skill that will not PARSE and a skill that will not
224
+ WRITE are different accusations, so `dz init` reports them separately:
225
+
226
+ ```console
227
+ $ dz init --target claude-code --skills-dir ./skills --project ./proj
228
+ dz init --target claude-code: 1 skill(s), 1 file(s) written, 0 skipped
229
+ dz init: 1 installed, 1 failed to write # ← stderr
230
+ ✗ 1 skill(s) failed to install (compile/write error):
231
+ alpha
232
+ EEXIST: file already exists, mkdir './proj/.claude/skills/alpha'
233
+ ```
234
+
235
+ The header names the **target**, not `alpha/SKILL.md` — which is perfectly valid. (It used to print
236
+ `⚠ 1 skill(s) skipped (unparseable SKILL.md)` and quote `line 1: "---"`, a valid frontmatter fence, as
237
+ its evidence: a failure that names the wrong artifact is worse than an anonymous one.)
238
+
239
+ `dz install` renders the offending path **relative to the package**
240
+ and says so explicitly, because a `node_modules/**` path is not something you can act on:
241
+
242
+ ```console
243
+ $ dz install @someone/skills-pack
244
+ dz install @someone/skills-pack: 12 skill(s), 24 file(s) written, 0 skipped
245
+ dz install: @someone/skills-pack ships 1 unparseable skill(s) —
246
+ ⚠ 1 skill(s) skipped (unparseable SKILL.md):
247
+ skills/broken-one/SKILL.md
248
+ SKILL.md must begin with a "---" frontmatter fence
249
+ This is a defect in the package, not in your project.
250
+ Workaround: npx -y @someone/skills-pack init
251
+ ```
252
+
253
+ **When this matters:** a package you installed shipped a broken skill — you still see everything else,
254
+ you know exactly which file is at fault, and you know whose defect it is.
255
+
256
+ > **`dz` requires `@dzhechkov/harness-core >= 0.4.7`.** If you ever see
257
+ > `dz: needs @dzhechkov/harness-core >= 0.4.7, found 0.4.1`, a stale core was reused from a cache:
258
+ > `rm -rf ~/.npm/_npx && npx @dzhechkov/harness-cli@latest --version`. That named message replaced a
259
+ > bare `SyntaxError: … does not provide an export named 'GRADE_SUCCESS_FLOOR'` that used to kill even
260
+ > `dz --version`.
261
+
128
262
  > **"Nothing installs" / "no skills found"?** Update the CLI:
129
263
  > `cd /tmp && npm i -g @dzhechkov/harness-cli@latest`. Older global installs couldn't locate their
130
264
  > own bundled packs outside the monorepo — now fixed, so `dz registry`/`dz init`/`dz setup` work
@@ -132,7 +266,7 @@ point any tool at (a skill is just Markdown). Dedicated targets are on the [road
132
266
 
133
267
  ## User Journey — from install to mastery
134
268
 
135
- All 66 commands (MEASURED — reproducer: `grep -c "^ case '" src/cli.ts`, the dispatch cases) mapped to a real workflow:
269
+ All 67 commands (MEASURED — reproducer: `grep -c "^ case '" src/cli.ts`, the dispatch cases) mapped to a real workflow:
136
270
 
137
271
  ```
138
272
  DISCOVER → INSTALL → USE → CREATE → MAINTAIN → SHARE
@@ -280,7 +414,7 @@ dz bundle --select news-digest,goap-research-ed25519 --out ./dist
280
414
  dz init --target claude-code --select design-thinking
281
415
 
282
416
  # Curated set by topic (recommended):
283
- dz setup --target claude-code --preset meta # 18 development skills + self-learning
417
+ dz setup --target claude-code --preset meta # 20 development skills + self-learning
284
418
 
285
419
  # Full toolkit with orchestrated pipeline:
286
420
  npx @dzhechkov/keysarium init # 7-phase research + commands + memory
@@ -605,6 +739,82 @@ ever enter a work order.
605
739
  **When to use:** after `dz compounding` reports the replay as READY; before claiming that recall
606
740
  "works"; and any time you want the claim re-checked as the corpus grows.
607
741
 
742
+ ### Обратный мост QE: Claude-ревьюер из Codex-сессии — `dz qe-bridge`
743
+
744
+ The cross-family rule ("the family that writes the code must not review it") was enforceable in one
745
+ direction only. When **Codex hosts** the run there is no Claude agent plane to dispatch from — and
746
+ `dz reqe`'s brief admits it: for a claude review family it prints `null` where the codex branch
747
+ prints a ready command. `dz qe-bridge` is that missing vehicle: a plain-shell command that probes a
748
+ Claude model, sends a Step-8-shaped brief over SCOPED extracts, and PARSES the verdict.
749
+
750
+ ```bash
751
+ # MEASURED 2026-08-19 on this repo — reproducer: the exact command below, reviewing a real shipped feature
752
+ $ dz qe-bridge --family claude --slug wave1-scorer-negation --coder-family codex --model opus
753
+ dz qe-bridge: GRADE C from claude/opus — 7 finding(s) in 343s
754
+ report: features/wave1-scorer-negation/08b_reqe_report.md
755
+ signoff: features/wave1-scorer-negation/.fa-state/qe-bridge/signoff-2026-08-19T18-48-45-545Z.json
756
+ settle: dz reqe --slug wave1-scorer-negation --done --report features/wave1-scorer-negation/08b_reqe_report.md
757
+ the bridge REPORTS (any grade exits 0); gating stays with dz reqe and the host pipeline.
758
+
759
+ # the same command with a binary that cannot answer — a failed call, and NO report to settle with
760
+ $ DZ_QE_BRIDGE_CLAUDE_BIN=/bin/false dz qe-bridge --family claude --slug wave1-scorer-negation --coder-family codex
761
+ dz qe-bridge: FAILED — probe-failed
762
+ no candidate model answered the liveness probe — opus: exit 1, no `OK` in 0 chars of stdout; sonnet: exit 1, …; haiku: exit 1, …
763
+ record: features/wave1-scorer-negation/.fa-state/qe-bridge/failed-2026-08-19T18-48-52-931Z.json
764
+ no report was written — an unparseable or absent review is never a passing one.
765
+
766
+ # with a debt on record, the report settles it through the untouched fail-closed path
767
+ $ dz reqe --slug add-x --done --report features/add-x/08b_reqe_report.md
768
+ dz reqe: debt settled: re-QE grade C (report …) — settlement appended to features/add-x/08_qe_report.md
769
+ ```
770
+
771
+ **When to use:** you are hosting a run outside Claude Code (Codex, CI, a plain terminal), you have
772
+ just written code, and the independent reviewer must be the OTHER family. Also: whenever `dz reqe`
773
+ lists a debt whose coder family is `openai`.
774
+
775
+ **The reviewer runs isolated.** Both calls (probe and review) run from an EMPTY temporary directory
776
+ with `--safe-mode --strict-mcp-config --tools '' --no-session-persistence`, and the verdict is read
777
+ from the `--output-format json` **result envelope**. Why: customization output lands on the same
778
+ stdout — MEASURED on this machine, a session-start plugin prints a banner ahead of the model's
779
+ answer — so a crafted hook could otherwise print a complete grade-A signoff and a stream parser
780
+ would believe it (reproducer: `features/qe-bridge-claude/07_code_changes/mutants/c1-forgery-repro.mjs`).
781
+ Residue, stated: `--safe-mode` leaves ADMIN-MANAGED policy settings in force, and no flag proves
782
+ which binary answered.
783
+
784
+ **What makes the grade valid.** Three channels must EXIST and AGREE, each read **LAST-anchored**,
785
+ and the marker must be the FINAL content of the answer:
786
+ the terminal `QE-BRIDGE-SIGNOFF grade=<A-F> findings=<n>` line, the last fenced `qe-bridge-signoff`
787
+ JSON block, and the report's own line-anchored `GRADE:` line. Repo content flows into the prompt and
788
+ comes back quoted, so a planted earlier verdict must lose — and it does (there is a test whose
789
+ fixture plants `grade=A` early and requires the genuine trailing `grade=D` to win). Extracts are
790
+ DEFANGED on the way in, so quoted content can never mint a verdict. Empty, gradeless, marker-only or
791
+ self-contradicting output is a **named failure** — one of 17 closed reasons (`envelope-unparseable`,
792
+ `marker-not-terminal`, `findings-count-mismatch`, `grade-mismatch`, `ambiguous-grade`,
793
+ `audit-write-failed`, `report-write-failed`, … ; closed BOTH ways — every one is produced by a real
794
+ run in the suite and leaves a record) — with an audit record under
795
+ `features/<slug>/.fa-state/qe-bridge/` and the raw stdout beside it, never `findings: []`. Finding
796
+ numbers are the reviewer's: a missing, non-positive or duplicated `n` fails the call instead of being
797
+ renumbered, and a marker whose `findings=<n>` disagrees with the block is `findings-count-mismatch`.
798
+
799
+ **The record is auditable, not just a conclusion.** Every run writes a `runId`, the resolved
800
+ executable plus `binOverride` (true whenever `DZ_QE_BRIDGE_CLAUDE_BIN` was used — the documented TEST
801
+ SEAM; there is no `--claude-bin` flag), the prompt sha256, the byte offsets at which each channel was
802
+ found, the `requestedOut` path and `reportWritten: true|false` — so "no report was written" is a
803
+ stated fact rather than an inference from an absent file. Records and reports are written `0600` in a
804
+ `0700` directory, through `O_EXCL`, with realpath containment that refuses a symlinked parent — and
805
+ the state directory itself is contained the same way, before anything is created in it. The audit
806
+ trail is written BEFORE the report and corrected after it, so `reportWritten` can only ever
807
+ understate; if the trail cannot be written at all, the run FAILS (`audit-write-failed`) rather than
808
+ shipping a verdict nobody can re-derive.
809
+
810
+ **Exit codes:** `0` a signoff was parsed (ANY grade — a grade F still exits 0: the bridge reports, it
811
+ does not gate), `1` a named failure, `2` a usage error. **Honest limits:** it proves the call was
812
+ procedurally sound (a live model was probed, a scoped brief was sent, a self-consistent verdict came
813
+ back); it cannot prove which model authored the text, and it cannot classify your secrets — the
814
+ extracts you scope are what leaves the machine. RU: мост в обратную сторону — из Codex-сессии
815
+ позвать независимого Claude-ревьюера и получить РАЗОБРАННЫЙ вердикт; пустой или безоценочный ответ —
816
+ это названная ошибка, а не «чисто».
817
+
608
818
  ### Пересмотр после аварийного само-ревью — `dz reqe`
609
819
 
610
820
  The feature-adr pipeline's cross-model guard says *the model that writes code must not review it*.
@@ -1015,7 +1225,7 @@ green: re-sign reminder (`dz sign …` — the publish signature gate is refuse-
1015
1225
  before a multi-package npm release, or whenever a broken dist/bin must be impossible to ship; keep plain
1016
1226
  `dz publish` for routine pushes. `dz publish` itself is byte-identical whether or not release exists.
1017
1227
 
1018
- ### Skill Packs (23 packs · 179 skills)
1228
+ ### Skill Packs (18 packs · 189 skills)
1019
1229
 
1020
1230
  Each pack is an npm package — click through for the **full per-skill documentation** (what each skill does + how to trigger it). Install a whole pack with `dz install <pkg>`, or pick skills with `dz init --select` / a `--preset`.
1021
1231
 
@@ -1027,7 +1237,7 @@ Each pack is an npm package — click through for the **full per-skill documenta
1027
1237
  | [@dzhechkov/skills-qe](https://www.npmjs.com/package/@dzhechkov/skills-qe) | 20 | Quality engineering — test-gen, coverage, chaos, defect intelligence, QCSD swarms |
1028
1238
  | [@dzhechkov/skills-reasoning](https://www.npmjs.com/package/@dzhechkov/skills-reasoning) | 4 | Generic reasoning & code-quality — investigate (root-cause), solid (SOLID/TDD), karpathy-guidelines, agents-md-creator |
1029
1239
  | [@dzhechkov/skills-ecc](https://www.npmjs.com/package/@dzhechkov/skills-ecc) | 20 | Claude-Code engineering craft — agent architecture, autonomous loops, framework patterns |
1030
- | [@dzhechkov/skills-meta](https://www.npmjs.com/package/@dzhechkov/skills-meta) | 19 | Dev-process meta skills — explore, feature-adr, design-thinking, audit, skill-advisor, loop-plan-author |
1240
+ | [@dzhechkov/skills-meta](https://www.npmjs.com/package/@dzhechkov/skills-meta) | 20 | Dev-process meta skills — explore, feature-adr, design-thinking, audit, skill-advisor, loop-plan-author, decision-mockups (vendored mirror of `@dzhechkov/skills-decision-mockups`) |
1031
1241
  | [@dzhechkov/skills-academic](https://www.npmjs.com/package/@dzhechkov/skills-academic) | 5 | Thesis-defense toolkit — dissertation review, questions, doc-check, defense eval |
1032
1242
  | [@dzhechkov/skills-news](https://www.npmjs.com/package/@dzhechkov/skills-news) | 3 | *dz-original* — news digests (`news-digest`) + delta watches (`news-monitor`) + bundled `goap-research-ed25519` verified-research backend (mandatory) |
1033
1243
  | [@dzhechkov/skills-idea2prd](https://www.npmjs.com/package/@dzhechkov/skills-idea2prd) | 1 | *dz-original* — `idea2prd-manual`: idea/problem → PRD+ADR+DDD+C4+Pseudocode+Tests+Completion (9 checkpoints); bundles the analyst trio as a sources.json-tracked vendor ([ADR-0001](https://github.com/djd1m/dz-harness-hub/blob/main/docs/adr/0001-skill-canonicalization-and-dependency-model.md)) |
@@ -1038,12 +1248,13 @@ Each pack is an npm package — click through for the **full per-skill documenta
1038
1248
  | [@dzhechkov/skills-taste](https://www.npmjs.com/package/@dzhechkov/skills-taste) | 1 | *imported (MIT)* — `design-taste-frontend`: anti-slop landing/portfolio/redesign framework (dials + pre-flight + GSAP skeletons); complements frontend-design (`dz init --select design-taste-frontend`) ([ADR-0002](https://github.com/djd1m/dz-harness-hub/blob/main/docs/adr/0002-product-and-design-expansion.md)) |
1039
1249
  | [@dzhechkov/skills-book-digitizer](https://www.npmjs.com/package/@dzhechkov/skills-book-digitizer) | 8 | *dz-original* — book → installable methodology pack: `digitize-book` (orchestrator) + ingest/extract/distill/pack/kb-index + `book-brain-register` (CP6 promote → cross-project brain) + `source-brain-ingest` (repo sibling). Verified provenance, IP-safe, resumable (`dz init --select digitize-book`) ([ADR-001](https://github.com/djd1m/dz-harness-hub/blob/main/features/book-knowledge-digitizer/03_adr/001-book-to-skillpack-pipeline.md)) |
1040
1250
  | [@dzhechkov/skills-12factor](https://www.npmjs.com/package/@dzhechkov/skills-12factor) | 12 | *generated by the digitizer, CC BY 4.0* — The Twelve-Factor App distilled into 12 decision-moment skills (one per factor). The first PUBLIC digitized-book pack; paraphrased (shingling-gated), routing-gated (every factor carries triggers), attributed (`NOTICE`) (`dz init --select 12factor-config-in-environment,…`) |
1251
+ | [@dzhechkov/skills-book-ai-apps](https://www.npmjs.com/package/@dzhechkov/skills-book-ai-apps) | 17 | *generated by the digitizer, CP5-published* — «Building Applications with AI Agents» (Albada, рус. пер.) distilled into 17 decision-moment skills across the whole agent-building arc: agent-fit & model choice, single-vs-multi, orchestration, tool design, knowledge & memory, context engineering, evaluation, probabilistic behaviour checks, release gates, improvement loops, drift, human-in-the-loop, agent UX, governance, security. Ships our page-anchored Knowledge Units, NOT the book text — shingling-gated at 0 uncited verbatim runs >=8 words; publication is the recorded CP5 owner decision; `trust_tier 1` (routing-gated, not human-reviewed) Since 0.2.2 it also ships `brain/ai-apps.sqlite`, the 223-KU knowledge slice: `dz brain add --from-pack @dzhechkov/skills-book-ai-apps` loads it into your `~/.dz/brain`, then `dz brain query --source ai-apps` answers in any project (`dz install @dzhechkov/skills-book-ai-apps --target claude-code`) |
1041
1252
 
1042
1253
  ### Available Presets (14)
1043
1254
 
1044
1255
  | Preset | Skills | Description |
1045
1256
  |--------|--------|-------------|
1046
- | `meta` | 18 | Development process (explore, goap-research, problem-solver, design-thinking, feature-adr, knowledge-extractor, understand-anything-bridge, agentshield-scan, adversarial-verifier, skill-advisor, audit) |
1257
+ | `meta` | 20 | Development process (explore, goap-research, problem-solver, design-thinking, feature-adr, knowledge-extractor, understand-anything-bridge, agentshield-scan, adversarial-verifier, skill-advisor, audit, loop-plan-author, decision-mockups) |
1047
1258
  | `qe-engineer` | 20 | Quality engineering (test-gen, coverage, chaos, defect, ...) |
1048
1259
  | `bto` | 1 | Build-Benchmark-Test-Optimize pipeline |
1049
1260
  | `health` | 8 | Medical AI (diagnostics, drugs, labs, clinical decisions) |
@@ -1073,6 +1284,7 @@ Get the whole set with `dz init --target claude-code --preset meta`, or pick one
1073
1284
  | `reflection-loop` | Standalone critique → revise cycle (≤3 rounds) for code, text, architecture or research | `/reflection-loop` · "critique this" / "review and improve" |
1074
1285
  | `structured-reasoning` | Picks the reasoning strategy (Tree-of-Thought / CoT / compression) and checks the conclusion follows | "reason about…" / "explore options" / "compare approaches" |
1075
1286
  | `skill-crystallizer` | Auto-creates skills from execution traces, combines skills, and repairs broken ones | "create skill from this" / "combine skills" / "fix skill" |
1287
+ | `decision-mockups` | Owner-facing DECISION PAGE — plain-language write-up + browser-frame before/after mockups + clickable option forks + a copy-answers export you paste back into the chat (a one-option fork is deleted as fake, and a deterministic G0–G14 gate blocks the page if it is not) | "объясни понятным языком" / "что сделано, польза, риски, из чего выбираем" / "покажи владельцу развилки и собери решения" / "оформи артефактом" |
1076
1288
 
1077
1289
  ### Standalone Packages (install via npx, no dz CLI needed)
1078
1290
 
@@ -1093,7 +1305,7 @@ Get the whole set with `dz init --target claude-code --preset meta`, or pick one
1093
1305
 
1094
1306
  > **A skill and its npx toolkit are not duplicates — they're a graduation.** Several skills (e.g. `feature-adr`, `design-thinking`) exist BOTH as a skill inside a `dz` preset AND as a standalone `npx` package. The preset's SKILL.md is **fully functional on its own** (the whole methodology — modules + references — travels with it, and it auto-activates by description), and it's the only way to compile that capability to the **non-Claude platforms** (Codex/OpenCode/Hermes/OpenClaude) via `dz`. The npx package adds **project-level runtime governance** around the same skill: a slash command, governance rules, a context shard, and (for feature-adr) reward-learning + `/harvest`. So: pick the **skill/preset** for a working capability across platforms; pick the **npx toolkit** when you want it as a governed, command-driven fixture of one project.
1095
1307
 
1096
- ## Design custom Workflow loops (`workflow` · `workflow-lint` · `workflow-trace`)
1308
+ ## Design custom Workflow loops (`workflow` · `workflow run` · `workflow-lint` · `workflow-trace`)
1097
1309
 
1098
1310
  Custom loops used to be born by copy-pasting a 1470-line workflow script; nothing deterministic
1099
1311
  checked the copy. The loop-designer meta-factory replaces that: a versioned typed plan
@@ -1131,15 +1343,36 @@ rules honestly report `inconclusive` there, never a silent green). `dz workflow
1131
1343
  self-checks the shared-subsystem blob registry (checkpoints, model-resolver, trace, …) that the
1132
1344
  generator injects verbatim — edit the canonical TS in harness-core, regenerate, never the copies.
1133
1345
 
1134
- **Scope, stated plainly: `dz` AUTHORS, GATES and READS loops — it never RUNS one.** There is no
1135
- `dz workflow run`, and step 5 above is not a `dz` command: execution belongs to the Claude Code
1136
- host's `Workflow({scriptPath})` runtime, which owns the agent dispatch the generated script calls
1137
- into. So every claim on this page is about the plan, the generated script, the lint verdict, and the
1138
- run's own trace file not about runtime behaviour `dz` could observe itself. `dz workflow-trace`
1139
- reads what a HOST run already wrote (`trace.jsonl`, seq-ordered by the loop's own counter); with no
1140
- host run there is nothing to read, and it says so rather than inventing a timeline. Portability
1141
- follows from the same boundary: on a non-Claude-Code target the authoring and lint verbs work
1142
- unchanged, and only execution is absent.
1346
+ **Scope, stated plainly (and NARROWED since `dz workflow run` shipped): `dz` AUTHORS, GATES, READS
1347
+ and now RUNS loops but it never runs a RENDERED SCRIPT.** Step 5 above is still not a `dz`
1348
+ command: executing the generated script belongs to the Claude Code host's `Workflow({scriptPath})`
1349
+ runtime, which owns the agent dispatch that script calls into. What `dz workflow run` executes is
1350
+ the PLAN (see "Run a plan WITHOUT the Claude host" below) a second, independent enactor that
1351
+ dispatches to `codex exec` / `claude -p` and writes the SAME trace shape. So a claim on this page is
1352
+ about the plan, the generated script, the lint verdict, and a run's own trace file the two hosts
1353
+ are compared through the same reader, never assumed equivalent. `dz workflow-trace` reads what
1354
+ EITHER host wrote (`trace.jsonl`, seq-ordered by the loop's own counter); with no run there is
1355
+ nothing to read, and it says so rather than inventing a timeline.
1356
+
1357
+ **Who writes the trace, and how far the cross-host claim actually reaches (MEASURED 2026-08-20).**
1358
+ The two enactors do not attest their runs the same way, and the difference is load-bearing:
1359
+
1360
+ | Enactor | Who appends `trace.jsonl` | Evidentiary weight |
1361
+ |---|---|---|
1362
+ | `dz workflow run` (Codex **or** Claude family) | the `dz` process itself, `appendFileSync` in `cli.ts` | **instrument-written** |
1363
+ | the rendered script under the Claude host's `Workflow({scriptPath})` | an AGENT the script asks to run the flush command (`loop-render.ts`) | **agent-attested** |
1364
+
1365
+ The host runtime's own records cannot substitute for the second row. `journal.jsonl` carries four
1366
+ fields (`type`, `key`, `agentId`, `result`) — no `seq`, no `ts`, so it can order nothing; the
1367
+ per-agent `agent-*.jsonl` transcripts do carry `timestamp` and `uuid`/`parentUuid`, so they can
1368
+ order AGENT runs — but a join, a gate redo and a typed pause are steps of the loop, not agents, and
1369
+ leave no record there at all.
1370
+
1371
+ Consequently the cross-host structural equivalence proved by the committed fixture (`pkg-audit-1`)
1372
+ covers a bounded fanout, an all-activated join, a dep chain and a gate. It does **not** cover the
1373
+ gate redo route, the typed terminal route, the typed pause or the file deliverable: those four are
1374
+ what `discrimination.plan.json` adds, and no capture of them exists yet. Read every equivalence
1375
+ statement here as scoped to the first list.
1143
1376
 
1144
1377
  ### Build a loop for YOUR scenario — the end-to-end use case (a real one)
1145
1378
 
@@ -1306,6 +1539,97 @@ edit loop converges. What it cannot do is run the result: the generated script c
1306
1539
  `agent()`/`parallel()` sandbox, which only the Claude Code Workflow runtime provides. A green lint
1307
1540
  from Codex + a run under Claude Code is a legitimate two-agent split.
1308
1541
 
1542
+ ### Run a plan WITHOUT the Claude host (`dz workflow run`)
1543
+
1544
+ Everything above renders a plan into a script that only Claude Code's `Workflow({scriptPath})`
1545
+ runtime can execute. `dz workflow run` is the other half: it **interprets the plan itself**, from a
1546
+ plain shell, dispatching each step to `codex exec` or an isolated `claude -p`.
1547
+
1548
+ **When to use which** — one sentence each:
1549
+
1550
+ | | use it when |
1551
+ |---|---|
1552
+ | `workflow render` + `Workflow({scriptPath})` | you are already inside Claude Code and want the loop to run in that session, with its agents and its context |
1553
+ | `dz workflow run` | you are in a shell, in CI, or on a box with no Claude Code session — and you want the same plan enacted with a trace the same reader can read |
1554
+
1555
+ It interprets the PLAN, never the rendered script (a rendered script is a Claude-host artifact; a
1556
+ second enactor reading it would be reading someone else's implementation). Same `loop-plan/1`, same
1557
+ gate grammar, same join policies, same failure classes — those decisions live in ONE module both
1558
+ enactors consume, not in two lookalike copies.
1559
+
1560
+ ```bash
1561
+ dz workflow run audit.plan.json --run-id pkg-audit-2 --coder-family claude
1562
+ # → dz workflow run: completed (pkg-audit-2) — trace at .dz/loop-trace/pkg-audit-2/trace.jsonl
1563
+ # → {"schema":"wf-run-result/1","runId":"pkg-audit-2","status":"completed","exitCode":0}
1564
+
1565
+ # the run wrote into the addressing the reader already uses, so nothing new is needed to read it:
1566
+ dz workflow-trace --run pkg-audit-2 --invariants audit.plan.json
1567
+ # → INVARIANT PASS seq-monotonic: seq unique and contiguous 1..12 (12 events)
1568
+ # → INVARIANT PASS dispatch-settle-pairing: every dispatch has exactly one settle
1569
+ # → INVARIANT PASS join-coverage:fan: every dispatched branch settled; …
1570
+ ```
1571
+
1572
+ **Exit codes — `run` and `workflow-lint` have DIFFERENT tables. Both, side by side:**
1573
+
1574
+ | | 0 | 1 | 2 | 3 | 75 |
1575
+ |---|---|---|---|---|---|
1576
+ | `dz workflow run` | completed | failed (named reason) | usage / invalid plan | — | **typed pause** |
1577
+ | `dz workflow-lint` | clean | findings | — | inconclusive | — |
1578
+
1579
+ `75` is `EX_TEMPFAIL` ("try again later"), and it is deliberately **not** `3`: `3` collides with
1580
+ lint's inconclusive and reads ignorable, while a pause strands work that is genuinely resumable.
1581
+ On a pause the **last stdout line** is a `wf-pause-envelope/1` JSON object; a FAILURE emits none —
1582
+ so a wrapper distinguishes the two from stdout and the exit code alone, without parsing prose.
1583
+
1584
+ **Pause and resume** (a `kind: 'pause'` step, or the budget ceiling):
1585
+
1586
+ ```bash
1587
+ dz workflow run release.plan.json --run-id rel-7
1588
+ # → dz workflow run: PAUSED (AWAITING_APPROVAL) — resume with: dz workflow run release.plan.json --resume rel-7 --arg approve=<value>
1589
+ # → {"schema":"wf-pause-envelope/1","runId":"rel-7","exitCode":75,"pauseState":"AWAITING_APPROVAL", …}
1590
+ echo $? # 75
1591
+
1592
+ dz workflow run release.plan.json --resume rel-7 --arg approve=yes
1593
+ # → dz workflow run: completed (rel-7) — …
1594
+ ```
1595
+
1596
+ A resume never re-spends work: the cursor comes from checkpoint lines plus artifact probes, and a
1597
+ STALE-INPUT mismatch (plan digest, exec fingerprint, or the run-args hash) refuses to resume at all —
1598
+ there is no override, because the checkpoints describe a different run. Extending a ceiling is the
1599
+ one thing that is not an identity change: `--budget-extra` and `--wall-clock-extra` are recorded and
1600
+ capped, never silent.
1601
+
1602
+ **Budget.** Every boundary reserves its worst case BEFORE it dispatches, so a region that will not
1603
+ fit pauses in front of the region rather than halfway through it. `budget.jsonl` gets one
1604
+ `wf-budget-1` row per dispatch (plus probe rows, which never decrement the ceiling).
1605
+
1606
+ **Cross-model safety.** A step marked `x-role: "qe"` that resolves to the same family as
1607
+ `--coder-family` is REFUSED: the family that wrote the code may not review it. `--allow-same-family-qe`
1608
+ proceeds, and writes a real re-QE debt that `dz reqe` surfaces — a suspension you can see, not a
1609
+ comment nobody reads.
1610
+
1611
+ #### Honest limits — three divergences from the Claude host, named rather than discovered
1612
+
1613
+ 1. **dz-side settle events carry no `wallTime`.** The Claude host stamps it shell-side during its
1614
+ flush; the shared emitter accepts none, and stamping it afterwards would mean editing lines that
1615
+ were already validated — exactly what the buffer discipline forbids. Per-dispatch wall clock
1616
+ lives in `budget.jsonl` instead. `wallTime` was always diagnostic-only (never an operand of an
1617
+ invariant), so no verdict changes.
1618
+ 2. **The runner checkpoints every top-level stage unconditionally**, whatever `plan.checkpointing`
1619
+ says. Its resume cursor is BUILT from those lines, so making them optional would make resume
1620
+ optional. `plan.checkpointing` remains what it always was: the Claude-host opt-in.
1621
+ 3. **A gate `terminal:` route ends the run by plan design, and leaves the trace INCOMPLETE.** This is
1622
+ parity with the rendered script, whose top-level terminal `return` skips the epilogue that writes
1623
+ `run.closed`. The run exits 0 (the plan declared this ending; the Workflow host completes too) and
1624
+ the ledger row names the route — but `dz workflow-trace` will report the trace as incomplete and
1625
+ downgrade window-truncated invariants to `inconclusive`. That is correct: nothing proves the
1626
+ un-run steps would have passed.
1627
+
1628
+ **Deferred, and said so:** budget rows do not appear in the `dz workflow-trace` timeline yet. The
1629
+ condition for adding them was zero reader change for Claude-host runs, and it is not met — a
1630
+ Claude-host run has no `budget.jsonl`, so the timeline would grow a section that is empty for half
1631
+ its inputs. `budget.jsonl` is readable by eye and by the recommender in the meantime.
1632
+
1309
1633
  ### Move a run's telemetry to another machine (`workflow-trace export` / `import`)
1310
1634
 
1311
1635
  A run leaves traces on the machine that produced it. `export` puts one run's telemetry into a single
@@ -1376,9 +1700,9 @@ only at their documented scopes (plan + step), and `fanouts[].registry` items mu
1376
1700
  ItemKey domain the trace plane enforces — `trace.emit` can never decide whether a valid plan runs.
1377
1701
  Deferred options are on the loop-designer roadmap.
1378
1702
 
1379
- ## All Commands (66)
1703
+ ## All Commands (69)
1380
1704
 
1381
- *(66 MEASURED — reproducer: `grep -c "^ case '" src/cli.ts`, the dispatch cases.)*
1705
+ *(69 MEASURED — reproducer: `grep -c "^ case '" src/cli.ts`, the dispatch cases.)*
1382
1706
 
1383
1707
  ```
1384
1708
  dz setup --target <name> [--preset <name>] [--select id,id,...] [--skills-dir <dir>] [--memory agentdb] [--no-memory] [--no-hooks] [--install-driver] [--force]
@@ -1433,7 +1757,7 @@ dz feature-adr-setup [--plan] [--from-spec <f>] [--apply] # guided project onb
1433
1757
  dz challenge --plan <plan.md> [--json] [--context-only] [--author <model>] # adversarial plan-gate (behind the `challenge-panel` skill): assemble a WIDE context pack (plan + vision + testing + map + degradations) + the fixed C1-C8 "break it" brief for a FRESH adversary (≠ plan author); advisory, never blocks
1434
1758
  dz routing [--stage <s>] [--json] # inspect the learned cost-optimal routing store: what `args.models.<stage>='auto-cost'` believes per (stage, complexity-tier, model) — gated attempts/successes/rate (feeds feature-adr model selection)
1435
1759
  dz bto-optimize --split|--plan|--select|--scope-check|--diff [--json] # deterministic engine behind /bto-optimize: hold-out split + hard-capped budget + no-regress-on-holdout winner selection (defeats judge-gaming); prose-only, diff-confirmed, never auto-writes
1436
- dz discrimination-check --test <f[,f]> [--base <ref>] [--name <filter>] [--runner <cmd>] [--json] # §42 test-discrimination gate for feature-adr Step-8: run the ADR's property test in an isolated git worktree at pre-feature base — it MUST go red without the fix; a green is a false green (HIGH finding, advisory, never auto-aborts)
1760
+ dz discrimination-check --test <f[,f]> [--base <ref>] [--name <filter>] [--runner <cmd>] [--timeout <ms>] [--json] # §42 test-discrimination gate for feature-adr Step-8: run the ADR's property test in an isolated git worktree at pre-feature base — it MUST go red without the fix. SEVEN verdicts, each gated on EXECUTION evidence: DISCRIMINATES · DISCRIMINATES_VIA_ERROR · NON_DISCRIMINATING (false green) · TEST_FILE_ABSENT · LOAD_ERROR_AT_BOTH_REVS · FAILS_AT_TIP · CANNOT_ISOLATE (+ typed reason). Advisory, never auto-aborts
1437
1761
  dz mutation-gate [--package <dir>] [--registry <file>] [--test-cmd "<cmd>"] [--only <id[,id]>] [--timeout <ms>] [--rebaseline per-entry|final] [--keep-scratch] [--json] # the mutation gate: for each NAMED protection in a declarative registry, copy the package to a scratch dir (shadow-repo layout, node_modules symlinked, git-initialized), verify the baseline is green, apply the entry's exact {find, replace} mutation, run the suite, REQUIRE red, restore — and require the red to be ATTRIBUTABLE to the protection: a mutated file that no longer parses is MUTATION_UNPARSEABLE, a failing count far above the entry's bound (maxFailing, default from observed) is OVER_FAILING, and a restored tree that does not reproduce green makes the entry INCONCLUSIVE (flaky suite). A mutation that does not apply, a green suite, or an inconclusive run is a FAILURE — never a skip. exit 0 all proven / 1 gate failed / 2 setup error
1438
1762
  dz delivery-check --slug <slug> [--context-only] [--findings <f.json>] [--strict] [--author <model>] [--json] # portable Step-10 Delivery Gate: the `manual` form that travels to every shell target — prints the 4-plane review brief (regressions ‖ security ‖ code-quality ‖ product-honesty) + artifact probes; --findings classifies a fed-back review into a fail-closed ready|blocked hand-off (only cross-validated BLOCKER/HIGH count) and writes features/<slug>/10_delivery_review.md; --strict exits 1 on blocked
1439
1763
  dz skills-verify [--dir <project>] [--expect a,b] [--static] [--strict] [--timeout <s>] [--json] # does .claude/skills/ actually REGISTER? --static = instant layout scan (CI-safe, no session): flags dirs that can never register; default also starts a real session and reads the authoritative system/init listing. exit 0 pass / 1 fail / 2 inconclusive — an unobservable registration is NEVER a pass
@@ -1444,6 +1768,7 @@ dz epoch-replay --judge <filled-work-order.json> [--out <file>] [--json] # bli
1444
1768
  dz epoch-replay --score <judgments.json> --work-order <file> [--slice <name>] [--json] # un-blind against the VERIFIED pre-registered assignment; ONE paired binomial over DECISIVE pairs (ties excluded, reported) → SUPPORTED only when the lift interval (2p−1) lies entirely above zero; FALSIFIED only on harm or a passed non-superiority test (lift upper bound below the margin PRE-REGISTERED in the work order, default 0.05, at 10+ decisive pairs); else INCONCLUSIVE (min 5 decisive pairs). Refuses a forged work order, a --margin flag, or duplicate judgement ids; the verdict is data, not an exit code
1445
1769
  dz score --slug <feature> [--project <dir>] [--json] # process scorecard for ONE feature-adr run, from its artifacts: ADR confirmation, discrimination proof, cross-model QE grade, live verification, README-first, learning loop, amendments — DESCRIPTIVE-ONLY (a low score exits 0); evidence lines are shown so the reader judges the heuristics
1446
1770
  dz reqe [--slug <feature> [--done --report <f>]] [--project <dir>] [--json] # the re-QE debt ledger: a usage-switched feature-adr run whose Step-8 QE ran on the coder's OWN family (cross-model guard suspended, FR-2.9) records a debt; list debts (also surfaced by dz usage), print the cross-family review brief, settle FAIL-CLOSED against an existing GRADED report (the run's own 08_qe_report.md — even hard-linked — can never settle its own debt); settlement lands in 08_qe_report.md, evidence rotates to reqe-settled.json
1771
+ dz qe-bridge --family claude --slug <feature> [--coder-family codex|claude] [--model <id>] [--files a,b] [--out <f>] [--timeout <s>] [--allow-same-family] [--json] # the REVERSE QE bridge (Codex-hosted → Claude reviewer): the reviewer runs ISOLATED (an empty temp cwd + --safe-mode --strict-mcp-config --tools '' --no-session-persistence, so no CLAUDE.md/skills/plugins/hooks/MCP load) and its verdict is read from the --output-format json RESULT ENVELOPE, so text a customization printed onto the same stdout can never become a signoff. Probes the model first; sends SCOPED extracts under a loud 200k-char ceiling; the grade must agree across three LAST-anchored channels AND the marker must be the final content — empty/gradeless/mismatched/miscounted output is a named failure with an audit record under features/<slug>/.fa-state/qe-bridge/, never a clean review. exit 0 signoff parsed (ANY grade — it reports, it does not gate) / 1 named failure / 2 usage. DZ_QE_BRIDGE_CLAUDE_BIN is a TEST SEAM (recorded as binOverride:true)
1447
1772
  dz backlog <sub> add "<idea>" | list | show <id> | goals [--validate] | roulette [--seed n] [--commit <id>] | ship <id…> | drop <id…> | reopen <id…> | enrich <id> | jira <id> | harmonize [--apply] # brain-backed idea backlog: capture an idea → semantic dedup against past ideas/features via the REUSED agentdb vector engine (two-signal: bounded-excerpt cosine DUPLICATE≥0.92 corroborated by shared subject vocabulary — a register-only 0.94 is demoted to RELATED, a length-only re-capture is caught as a subset duplicate; absorbed texts kept in absorbed.jsonl) + GoalMap alignment ("map+compass") → weighted seeded roulette picks one to work on → enrich STAGES an idea2prd hand-off → jira writes an auditable outbox via a configurable MCP adapter seam (jira-mcp|copilot-mcp|none). No 2nd vector store; without agentdb it degrades to exact-text dedup (honest)
1448
1773
  dz sign --init --out <path> | --pack <dir> --key <path> # --init: generate the Ed25519 keypair (private OUTSIDE the repo, prints the public key for keys/dz.pub); else sign a pack's manifest + CycloneDX SBOM
1449
1774
  dz sbom --pack <dir> [--out <file>] # emit the CycloneDX 1.5 SBOM for a pack standalone (file-level bill of materials); print to stdout or write to a file
@@ -1456,9 +1781,12 @@ dz release [--filter <name>] [--affected] [--audit-dev] [--tag] [--pub
1456
1781
  dz auto-canonicalize --source <github-url> --pack <skills-pack>
1457
1782
  dz sync-upstream [--package <dir>] [--list] [--all]
1458
1783
  dz drift-check [--all] [--json] [--project <dir>] # CI gate: exit 1 on NEW shared-skill drift (baseline: .dz/drift-allowlist.json; --all incl .claude dogfood)
1784
+ dz agents-sync [--check] [--json] [--project <dir>] # sync anchored bearing rules into the root AGENTS.md policy fence; exit 0 sync / 1 drift / 3 inconclusive
1785
+ dz hooks-sync --target codex [--check] [--verify|--no-verify] [--project <dir>] [--remove] [--json] # install + ARM the dz veto/recall hooks in $CODEX_HOME/hooks.json and PROVE they fire with a live veto probe; exit 0 armed+trusted+verified / 1 not armed / 3 inconclusive (incl. --no-verify)
1459
1786
  dz sync-canonical <skill> [--check] [--from <dir>] [--auto] [--project <dir>] # heal every copy from skills-meta/<skill> or --from; no canonical + --check = compare copies to each other (exit 1 on drift); no canonical + write = refuse unless --auto (LOUD, picks most-complete copy); --check writes nothing
1460
1787
  dz scout [--topics <list>] [--since <date>] [--deep] [--output <file>] [--diff] [--report]
1461
1788
  dz workflow init --name <n> [--pattern pipeline|barrier|fanout|gate] [--o <plan.json>] | validate <plan.json> [--json] | render <plan.json> --o <script.js> [--check] [--force] | blobs [--check] # loop-plan/1 authoring (the ADR-005 templates are retired)
1789
+ dz workflow run <plan.json> [--run-id <id>] [--resume <runId>] [--arg k=v]... [--coder-family codex|claude] [--default-family codex|claude] [--budget <n>] [--max-wall-clock <s>] [--stage-timeout <s>] [--budget-extra <n>] [--wall-clock-extra <s>] [--run-dir <dir>] [--allow-same-family-qe] [--json] # INTERPRET the plan without the Claude host; exit 0/1/2/75 (75 = typed pause)
1462
1790
  dz workflow-lint <script.js> [--plan <plan.json>] [--require-plan|--legacy] [--json] # 18-rule deterministic gate; exit 0/1/3 — inconclusive is never a pass
1463
1791
  dz workflow-trace <runDir|--slug <s>|--run <id>> [--invariants <plan.json>] [--html <out.html>] [--json] # timeline + SEQ invariant runner over the loop's own trace.jsonl
1464
1792
  dz workflow-trace export <run> --o <file> [--include-pairs --yes] [--strict] # one run's telemetry as ONE movable file
@@ -1477,8 +1805,8 @@ dz help
1477
1805
  ## Global: `dz --version` / `-v` / `dz version`
1478
1806
 
1479
1807
  ```bash
1480
- dz --version # 0.4.6
1481
- dz --version --json # {"name":"dz","version":"0.4.6","node":"v22.22.0","schemas":{"loopPlan":"loop-plan/1"}}
1808
+ dz --version # 0.4.8
1809
+ dz --version --json # {"name":"dz","version":"0.4.8","node":"v22.22.0","schemas":{"loopPlan":"loop-plan/1"}}
1482
1810
  ```
1483
1811
 
1484
1812
  One line, exit 0. Unresolvable ⇒ the literal `unknown` and exit **1** — never a fabricated number,
@@ -2083,6 +2411,96 @@ dz sync-upstream --package packages/@dzhechkov/skills-devops # check one packag
2083
2411
 
2084
2412
  Discovers all skill packs with `sources.json`, fetches SKILL.md from origin repos, reports which skills have upstream changes.
2085
2413
 
2414
+ ### dz agents-sync — Codex starts with the repository's bearing rules
2415
+
2416
+ Use this after changing an anchored policy clause in `CLAUDE.md` or `.claude/rules/*.md`, and run
2417
+ the check form in CI. It updates only the `dz:policies` fence in the root `AGENTS.md`; authored
2418
+ content and the independent `dz:skills` fence remain untouched.
2419
+
2420
+ ```console
2421
+ $ dz agents-sync
2422
+ dz agents-sync: wrote — 9 policy section(s), 8097 bytes (24.71% of 32768)
2423
+
2424
+ $ dz agents-sync --check
2425
+ dz agents-sync: in sync — 9 policy section(s), 8097 bytes (24.71% of 32768)
2426
+ ```
2427
+
2428
+ Exit codes are **0** for synchronized, **1** for drift and **3** when fixed source evidence is
2429
+ missing or unreadable. `--check` never writes. Each section carries a 12-hex hash recomputed from
2430
+ its source anchor; this proves synchronization only — not that Codex read or obeyed the rule. The
2431
+ separate cold-start acceptance probe establishes runtime visibility.
2432
+
2433
+ ### dz hooks-sync — Codex runs the same veto and recall hooks Claude Code does
2434
+
2435
+ **When to use it.** Once per machine, after `npm i -g @dzhechkov/harness-cli`, if you drive Codex as
2436
+ well as Claude Code. It installs two hooks into the **user-global** `$CODEX_HOME/hooks.json`
2437
+ (default `~/.codex/hooks.json`) and **arms** them — Codex hooks are trust-gated, and an untrusted
2438
+ entry is silently never run. Re-run it after a dz upgrade; it is byte-idempotent, so an unchanged
2439
+ install rewrites nothing and the hook keeps its trust.
2440
+
2441
+ ```console
2442
+ $ dz hooks-sync --target codex
2443
+ dz hooks-sync: codex hooks installed and ARMED (trust: trusted) — VERIFIED by a live veto probe — ready
2444
+
2445
+ $ dz hooks-sync --target codex --check # read-only, and it re-proves the guard fires
2446
+ dz hooks-sync: codex hooks installed and ARMED (trust: trusted) — VERIFIED by a live veto probe — ready
2447
+
2448
+ $ dz hooks-sync --target codex --no-verify # skips the probe — and can never say "ready"
2449
+ dz hooks-sync: installed+trusted, NOT verified — ARMED = NO (trust: trusted, executable: true, verify: not verified (no live probe ran))
2450
+
2451
+ $ dz hooks-sync --target codex --remove
2452
+ dz hooks-sync: removed 2 managed entr(ies) from /root/.codex/hooks.json
2453
+ ```
2454
+
2455
+ **"ready" means a command was actually blocked, in this run.** By default `dz hooks-sync` runs a
2456
+ LIVE, nonce-scoped veto probe through `codex exec` in a hermetic workspace: it asks Codex to run one
2457
+ forbidden command and requires BOTH halves of the evidence — dz's `DZ-VETO:` marker in the
2458
+ transcript AND the absence of the command's side effect. `--dangerously-bypass-hook-trust` is never
2459
+ passed, because a bypassed run proves the helper body works and nothing about the installed state.
2460
+ Anything else — a silent transcript, a dead invocation, a timeout, a version mismatch — is
2461
+ **inconclusive**, never ready. `--no-verify` skips the probe and is reported as unverified;
2462
+ `--project <dir>` runs the probe in a project that has already opted into `"shellVeto": "block"`
2463
+ instead of the hermetic workspace.
2464
+
2465
+ Exit codes are **0** for armed **and** trusted **and** verified, **1** for not-armed / drift / a
2466
+ refusal, and **3** when the answer is inconclusive (including "no `codex` binary on PATH", where dz
2467
+ writes **nothing**, and `--no-verify`, where nothing was measured). `--check` writes nothing and is
2468
+ **silent** in a home that never opted in.
2469
+
2470
+ **`dz setup --target codex` and `dz init --target codex` deliver these hooks too**, verify them the
2471
+ same way, and report a failure without aborting the rest of the command. Pass `--no-hooks` for
2472
+ skills only.
2473
+
2474
+ **What the two hooks do.**
2475
+
2476
+ | Hook | Event | Behaviour |
2477
+ |---|---|---|
2478
+ | `dz-codex-veto.cjs` | `PreToolUse` | judges the shell command against one rule, `ssh-explicit-auth-weakening` |
2479
+ | `dz-codex-recall.cjs` | `UserPromptSubmit` | injects matching learned lessons and records the use with `runtime: "codex"` |
2480
+
2481
+ **The veto WARNS by default and never blocks.** A hit prints `DZ-VETO-WARN:` and exits 0. Enforcement
2482
+ is opt-in **per project**:
2483
+
2484
+ ```console
2485
+ $ cat .dz/config.json
2486
+ {"hooks": {"shellVeto": "block"}} # off | warn (default) | block
2487
+ ```
2488
+
2489
+ Only then does a hit exit 2 and stop the command. The rule fires only on tokens by which the command
2490
+ *explicitly* asks for weaker ssh authentication — `sshpass`, `-o PasswordAuthentication=yes`,
2491
+ `-o PubkeyAuthentication=no`, `-o PreferredAuthentications=…password…`. A bare `ssh myhost` whose
2492
+ identity comes from `~/.ssh/config` or `ssh-agent` is **allowed**: this guard is user-global, so a
2493
+ rule that blocks the normal secure case is not a guard, it is an outage.
2494
+
2495
+ **Radius.** Both helpers are INERT outside an opted-in dz project — the activation marker is a `.dz`
2496
+ directory, not `.git`. In a plain git checkout they take no decision, print nothing, and create
2497
+ nothing.
2498
+
2499
+ **Removal is conservative.** `--remove` deletes only entries whose command hash is recorded in dz's
2500
+ own manifest. An entry that merely *looks* like dz's is kept and reported — dz never deletes what it
2501
+ cannot prove it wrote. Foreign entries are preserved byte-for-byte by every operation, and a
2502
+ `hooks.json.bak-<ISO>` copy (newest 3 kept) is taken before each modifying write.
2503
+
2086
2504
  ### dz drift-check / dz sync-canonical — intra-monorepo skill-drift guard
2087
2505
 
2088
2506
  **What.** The same skill is physically duplicated across many packages (`packages/@dzhechkov/*/​<skill>/` + `.claude/skills/<skill>/`). These two commands make that duplication safe:
@@ -2365,7 +2783,7 @@ dz pretrain # detects stack, recommends pres
2365
2783
  dz recommend "work on this Node.js API" # suggests skills + toolkits
2366
2784
 
2367
2785
  # 2. Install skills (choose your level)
2368
- dz setup --target claude-code --preset meta --memory agentdb # 18 skills (includes feature-adr)
2786
+ dz setup --target claude-code --preset meta --memory agentdb # 20 skills (includes feature-adr)
2369
2787
  dz setup --target claude-code --preset qe-engineer # + 20 QE skills
2370
2788
 
2371
2789
  # Want the full feature-adr toolkit with /feature-adr command + governance?
@@ -2756,23 +3174,56 @@ feature-adr Step-8 asserts the ADR's load-bearing safety property **has** a test
2756
3174
  false green — it may never exercise the property, so it would stay green even if the property regressed. The
2757
3175
  `§42` gate (learned from rUv's `cve-bench/evaluate.mjs`) proves the test **discriminates**: it runs the property
2758
3176
  test in a throwaway git worktree at the pre-feature base (no fix) and requires it to go **red**.
3177
+
3178
+ **Use it when** you are about to accept "the property has a test" as evidence — in Step-8 QE, in a review of
3179
+ someone else's fix, or before trusting a regression test you did not write. Run it against `HEAD` while your
3180
+ change is still uncommitted; that is what makes `HEAD` the pre-feature base.
3181
+
2759
3182
  ```bash
2760
3183
  # the Step-7 feature diff is uncommitted mid-pipeline, so HEAD is the pre-feature base
2761
3184
  dz discrimination-check --test packages/x/test/auth-property.test.ts --base HEAD --json
3185
+
3186
+ # human output, a longer suite, and a runner that is not vitest
3187
+ dz discrimination-check --test packages/x/test/auth-property.test.ts \
3188
+ --base HEAD --runner "node --test" --timeout 600000
2762
3189
  ```
2763
3190
  ```
2764
3191
  discrimination-check @ HEAD — verdict: NON_DISCRIMINATING
2765
3192
  ✗ packages/x/test/auth-property.test.ts: NON_DISCRIMINATING
3193
+ measurementValid: true · primaryAction: strengthen-test
2766
3194
 
2767
3195
  [high] non-discriminating property test (false green)
2768
- 1 property test(s) PASS at pre-feature base — they do not exercise the ADR safety property … (Advisory — the pipeline continues; the owner decides.)
3196
+ 1 property test(s) PASS at pre-feature base WITH execution evidence — they do not exercise the ADR safety
3197
+ property and would stay green if the fix regressed … Action: strengthen-test. (Advisory — the pipeline
3198
+ continues; the owner decides.)
2769
3199
  ```
2770
- Verdicts: `DISCRIMINATES` (red by assertion — good) · `DISCRIMINATES_VIA_ERROR` (couldn't load at base, e.g. a
2771
- brand-new module inferred) · `NON_DISCRIMINATING` (green at base a false green, HIGH finding) ·
2772
- `CANNOT_ISOLATE` (no runnable property test folds into "property untested"). It **never auto-aborts** (dz's
2773
- rule: a false gate kills trust)exit 0 on any verdict, exit 2 only on a usage/setup error. The worktree +
2774
- sanitation live in tested CLI code; base ref, paths, name filter, and runner are all injection-checked, and the
2775
- worktree is always removed. Step-8 runs this on the ADR Confirmation's `Required automated check` automatically.
3200
+
3201
+ **Every verdict is gated on EXECUTION evidence.** The gate reads the runner's own output shape (vitest /
3202
+ node --test) and requires it to demonstrate that the *named* test actually ran. An outcome nobody can prove
3203
+ degrades loudly instead of minting trust — MEASURED before this change, `--runner false` (a command that
3204
+ executes nothing and prints nothing) returned `DISCRIMINATES`; it now returns `CANNOT_ISOLATE` with
3205
+ reason `unrecognised-runner-output`.
3206
+
3207
+ | Verdict | Means | ✓/✗ | Action |
3208
+ |---|---|---|---|
3209
+ | `DISCRIMINATES` | assertion-red at base, execution-evidenced | ✓ | none |
3210
+ | `DISCRIMINATES_VIA_ERROR` | evidenced load error at base **and** an evidenced pass at TIP | ✓ | none (inference — note it) |
3211
+ | `NON_DISCRIMINATING` | evidenced pass at base — a proven false green | ✗ | `strengthen-test` |
3212
+ | `TEST_FILE_ABSENT` | the named check is not a regular file (stat+isFile, before any worktree) | ✗ | `create-missing-test` |
3213
+ | `LOAD_ERROR_AT_BOTH_REVS` | could not execute at EITHER rev — zero signal | ✗ | `fix-runner-invocation` |
3214
+ | `FAILS_AT_TIP` | the feature's own test is red **with** the feature present | ✗ | `fix-red-feature-test` |
3215
+ | `CANNOT_ISOLATE` | no established observation; the row carries a typed `reason` | ✗ | `map-a-test` / `fix-runner-invocation` |
3216
+
3217
+ `--json` carries the full reading: `perTest[]` (verdict + `reason`), the per-run `evidence` rows, `findings[]`
3218
+ (**one per distinct non-clean verdict** — the scalar `aggregate` can only name the worst), `measurementValid`
3219
+ (`true | false | 'partial'` — did the instrument measure at all), `primaryAction`, and `tipTree` (the live
3220
+ tree's HEAD + dirty-file count when a tip control ran). The singular `finding` is a **deprecated** alias for
3221
+ `findings[0]`; read `findings[]`. `--timeout <ms>` (default 300000) bounds each run — a timed-out run is
3222
+ `CANNOT_ISOLATE` reason `timeout`, never a pass.
3223
+
3224
+ It **never auto-aborts** (dz's rule: a false gate kills trust) — exit 0 on any verdict, exit 2 only on a
3225
+ usage/setup error. Base ref, paths, name filter, and runner are all injection-checked, and the worktree is
3226
+ always removed. Step-8 runs this on the ADR Confirmation's `Required automated check` automatically.
2776
3227
 
2777
3228
  ### `dz guard` — when a self-mutating operation should be refused, not regretted
2778
3229
 
@@ -3506,9 +3957,29 @@ npx @dzhechkov/p-replicator init
3506
3957
 
3507
3958
  ## Status
3508
3959
 
3509
- `v0.4.6` — published on npm. Also available as [Claude Plugin](#claude-plugin). Part of [DZ Harness Hub](https://github.com/djd1m/dz-harness-hub).
3960
+ `v0.5.1` — **published 2026-08-20**. Ships `dz workflow run`, the portable plan enactor: it INTERPRETS a `loop-plan/1` plan instead of executing a rendered script, dispatching to `codex exec` or an isolated `claude -p`, exit 0/1/2/75 (75 = a typed pause whose last stdout line is a `wf-pause-envelope/1`). Requires `@dzhechkov/harness-core >= 0.5.1` (the compat guard refuses below it by name). See "Who writes the trace" above for the stated scope of the cross-host equivalence claim — it is narrower than "the two hosts agree".
3961
+
3962
+ `v0.5.0` — published. Also available as [Claude Plugin](#claude-plugin). Part of [DZ Harness Hub](https://github.com/djd1m/dz-harness-hub).
3963
+
3964
+ New in 0.5.0 (feature `qe-bridge-claude`, cross-runtime leg 3/4): `dz qe-bridge --family claude`
3965
+ runs an INDEPENDENT Claude reviewer from any host — a Codex session included — and lands a PARSED
3966
+ signoff whose grade must agree across three LAST-anchored channels; an empty or gradeless answer is
3967
+ a named failure with a forensic record, never a clean review. `withNamedLockSync` generalises the
3968
+ store lock and now guards the `$CODEX_HOME/hooks.json` read-merge-write, so two dz processes can no
3969
+ longer lose each other's hook entries.
3970
+
3971
+ Also in 0.4.8 (feature `crossrt-1-agents-md`): `dz agents-sync` ports the fixed registry of bearing
3972
+ rules into an early root-`AGENTS.md` fence, `--check` exposes source drift to CI, and both surfaces
3973
+ report the measured Codex project-doc byte budget. A live cold-start probe, not file presence,
3974
+ remains the runtime acceptance gate.
3975
+
3976
+ New in this change (feature `dz-cli-defects`, slice A — three defects confirmed by RUNNING the CLI, then fixed):
3977
+
3978
+ - **`dz list` skips and collects** — one unparseable `SKILL.md` no longer hides the rest; the broken files are named on stderr and the exit code stays 1. Same for `dz init` / `dz install` / `dz sync`. [Details](#dz-list--one-broken-skill-never-hides-the-rest)
3979
+ - **`--target` aliases + did-you-mean** — `--target claude` (and `cc`, `agents`, `gpt`/`openai`) resolve; typos are suggested, never silently accepted. [Details](#--target-aliases-and-did-you-mean)
3980
+ - **A named refusal instead of a `SyntaxError`** — `dz` now requires `@dzhechkov/harness-core >= 0.4.7` (was `^0.4.0`, which npm could legally resolve to a core too old to link) and says so in words, before anything is imported.
3510
3981
 
3511
- New in this change: the global `dz --version` / `-v` / `dz version` surface (one parseable line), and `dz skills-verify --plugin-dir` / `--expect-commands` so slash-command registration is gate-visible. Both exist for `@dzhechkov/loop-designer-plugin`, which requires `dz` in `^0.4`, verifies it at run time, and falls back to `npx -y @dzhechkov/harness-cli@^0.4` when the `dz` on PATH is stale, unparseable or missing.
3982
+ Previously: the global `dz --version` / `-v` / `dz version` surface (one parseable line), and `dz skills-verify --plugin-dir` / `--expect-commands` so slash-command registration is gate-visible. Both exist for `@dzhechkov/loop-designer-plugin`, which requires `dz` in `^0.4`, verifies it at run time, and falls back to `npx -y @dzhechkov/harness-cli@^0.4` when the `dz` on PATH is stale, unparseable or missing.
3512
3983
 
3513
3984
  ## Claude Plugin
3514
3985