@dzhechkov/harness-cli
v0.8.24
Published
The dz CLI — install AI skills for Claude Code, Codex, OpenCode, Hermes, OpenClaude, GitHub Copilot. 87 commands, 14 presets, 10 platform targets.
Maintainers
Readme
@dzhechkov/harness-cli
The dz CLI — the main entry point to the DZ Harness Hub. Install AI skills for Claude Code, Codex, OpenCode, Hermes, OpenClaude, GitHub Copilot from a single command.
Install in 30 seconds
npm install -g @dzhechkov/harness-cli
dz setup --target claude-code --preset devops
dz doctorYou do NOT need to clone any repository to use dz. Source is for contributors — see From source (contributors only) at the end of this README.
Why dz?
dzis a package manager + cross-compiler for your AI agent harness. Write a skill once in one canonical form;dzinstalls it into any agent's harness, holds it to a quality bar, and lets the harness learn over time.
The problem. You accumulate 260 skills (design-thinking, QE, devops, web3, MCP, academic…). Five pains follow:
- Every agent wants a different layout. Claude Code reads
.claude/skills/, Codex.codex/, OpenCode/Hermes/OpenClaude their own. Hand-maintaining N copies is sync hell. - Skills arrive from many upstream repos — they must be canonicalized (brought to one form) and kept in sync without losing provenance.
- It's hard to know which skill to reach for out of a hundred.
- Quality drifts — there's no single bar.
- Experience doesn't accumulate — the harness doesn't learn from feedback.
The answer — one canon → many platforms. There is a single source of truth (a CanonicalSkill); dz compiles it for each target — so the same skill drops into .claude/skills/, .codex/, etc. without hand-copying.
Every command maps to one of five jobs:
| Job | Commands | What it does |
|-----|----------|--------------|
| Author / canonicalize | auto-canonicalize, sync-upstream, diff, create-skill | pull a skill from any repo into one canonical form + keep it in sync with upstream |
| Install / assemble | init, setup, install, compose, presets, upgrade | deploy the right set of skills into a chosen agent harness (10 targets) |
| Find / recommend | registry, scout, recommend, skill-advisor | for a task, suggest which skill / preset / package to use |
| Guarantee quality | benchmark (L0 A–F), verify, doctor | one bar — 20 deterministic checks per skill |
| Learn | teach, consolidate, recall (hybrid lexical+vector), vector, pretrain, roam (reward-learning) | accumulate patterns, harvest session outcomes, recall ranked memory (semantic when the vector tier is enabled), improve recommendations over time |
(+ ops: publish, bundle (portable export), stats, downloads, dashboard, plugin.)
Analogy: npm for distribution, a compiler / Babel for one source → many targets (adapters for 10 targets), and a linter / CI for a quality bar (benchmark) — but for AI agent skills, not ordinary code.
Install
npm install -g @dzhechkov/harness-cliMCP and hook companion configuration
A skill can request companion configuration through a bounded adjacent INTEGRATIONS.json. The
first integration-aware init prints its aggregate digest; only
--allow-integrations <that-digest> can authorize the exact content. The live registration probe is
non-executing: it uses target-owned list/get surfaces, never the manifest command or URL.
The measured support matrix currently admits Claude Code 2.1.235 project MCP only. Every other
target/component pair is a named refusal, except that the existing Codex hook writer can map to
emitted when its live veto probe reports ready: true. Claude Pending approval is registration
with ready: false. Use --no-integrations for explicit skills-only installation; --no-verify
does not authorize emission.
Note: If you get
EUNSUPPORTEDPROTOCOL workspace:*, you're inside a pnpm/yarn workspace. Run the install from/tmpor~instead.
Updating an already-installed dz
dz does not auto-update — an old global install stays old until you upgrade it explicitly.
Upgrading also refreshes all bundled skill packs to their latest published versions.
# Check installed vs. latest:
npm ls -g @dzhechkov/harness-cli # what you have
npm view @dzhechkov/harness-cli version # latest on npm
# Upgrade (run from /tmp to avoid the workspace:* error above):
cd /tmp && npm install -g @dzhechkov/harness-cli@latestOn an old version? Tell-tale signs:
dz registryshows anotherbucket instead ofproduct/designcategories, ordz init/dz setupfinds no skills. Upgrade as above. A system-wide global on Linux may live in/usr/lib/node_modulesand needsudo.
Using the arsenal: new vs. existing project
Install dz once, globally — it ships all skill packs bundled, so you never install packs
per-project. Then run dz from inside a project to write skills into its agent directory
(.claude/skills/, .codex/, …). Nothing lands until you choose a target + skills.
See what's available (from any directory):
dz registry # browse the full catalog by category
dz registry search <keyword> # search skills
dz registry --category devops # one category
dz stats # totals + full preset & target lists
dz help # all commands; the Presets:/Targets: lines list valid --preset/--target values
dz recommend "<your task>" # task → recommended skills + presetNew project:
mkdir my-app && cd my-app
dz setup --target claude-code --preset devops # skills + hooks + self-learning (.dz/)
dz init --target claude-code --preset devops # or: skills onlyExisting project — same commands from the project root; dz is additive and only touches the
agent directory, never your source:
cd existing-project
dz init --target claude-code --preset qe # add a preset
dz init --target claude-code --select terraform,pr-review # add individual skills
dz install @dzhechkov/skills-web3 # add one pack
dz verify && dz doctor # check healthRe-running is safe (existing files are skipped unless --force). Targets: claude-code, codex,
opencode, hermes, openclaude, copilot, agents-md, cursor, gemini.
Target coverage — which coder/agent maps to which --target
Don't see your tool below? Two fallbacks: if it reads CLAUDE.md/.claude/ use --target claude-code;
otherwise dz bundle --select <ids> --out <dir> gives a portable tree of raw SKILL.md files you can
point any tool at (a skill is just Markdown). Dedicated targets are on the roadmap (contributors — see docs/target-expansion-research.md in the source repo).
✅ Shipped native targets (each compiles to the tool's own layout):
| Coder / agent | --target | Emits |
|---------------|-----------|-------|
| Claude Code | claude-code | CLAUDE.md + .claude/skills/ |
| OpenAI Codex CLI | codex | .agents/skills/ |
| OpenCode | opencode | .opencode/skills/ |
| Hermes Agent | hermes | .hermes/skills/ |
| OpenClaude (incl. the "OpenClaw"/Claw-Code fork — it mirrors Claude Code) | openclaude | .openclaude/skills/ |
| GitHub Copilot | copilot | .github/instructions/*.instructions |
| ~15 tools that read a root AGENTS.md (Zed, Warp, Aider, goose, Gemini CLI, RooCode, Kilo, Junie, Trae, Augment, Devin, pi, Windsurf) | agents-md | root AGENTS.md (merged — preserves user content, owns a fenced block; lossy/flattening, no per-skill frontmatter) |
| Cursor | cursor | .cursor/rules/*.mdc (per-skill, YAML frontmatter description/globs/alwaysApply; plain .md in that dir is ignored) |
| Gemini CLI / Code Assist | gemini | root GEMINI.md (merged — preserves user content, owns a fenced block; lossy/flattening, no per-skill frontmatter — same shape as agents-md) |
| Windsurf | windsurf | .windsurf/rules/*.md (per-skill, YAML trigger frontmatter — model_decision, description, optional globs; cursor's shape with a plain .md extension. .devin/rules/ rebrand is an out-of-scope watch item) |
🗺️ On the roadmap (verified rules-formats — see the research doc):
| Coder(s) / agent | planned --target | reads | priority |
|------------------|--------------------|-------|----------|
| Cline · Continue · Kiro | cline · continue · kiro | .clinerules/ · .continue/rules/ · .kiro/steering/ | mid |
❌ No target applies: nemoclaw (NVIDIA agent-safety runtime — not a code editor, no rules file) ·
v0 (UI-only, no repo file). (pi is a real coder but reads AGENTS.md → covered by agents-md.)
--target aliases and did-you-mean
--target claude used to be rejected outright — the canonical name is claude-code, and the error
just re-printed the list. All eight --target-taking commands (init, verify, install,
compose, setup, upgrade, parity, feature-adr-setup) now resolve the value first.
(This sentence used to say "every" and name only seven: dz parity was missed, and shipped the
original defect verbatim. It is now checked rather than promised — test/target-alias-cli.test.ts
asserts that no command reads --target without reaching the resolver, and that every command whose
--help advertises --target appears in the per-command sweep.)
Accepted aliases (a table, so adding one is a data edit):
| You type | You get | Why it is a row |
|----------|---------|-----------------|
| claude · cc | claude-code | the tool's everyday name |
| agents | agents-md | the everyday name of the AGENTS.md target |
| gpt · openai | codex | the vendor name, not the CLI's |
Case, padding and separators are handled without a row: CLAUDE, Claude_Code, claudecode
and claude-code all resolve to claude-code; agentsmd and agents.md resolve to agents-md.
A typo is SUGGESTED, never silently accepted — installing to the wrong target on a guess is worse than one round-trip:
$ dz init --target clade-code --preset devops > out.txt # the refusal is on STDERR
dz init: unknown --target "clade-code" — did you mean "claude-code"?
--target must be one of: agents-md, claude-code, codex, copilot, cursor, gemini, hermes, openclaude, opencode, windsurf
$ echo $?
1
$ wc -c out.txt
0 out.txt # stdout stays a clean data channel, even on a refusalBoth lines go to stderr — the refusal, like the alias note below, is diagnosis, not data. dz
parity --target <bad> --json likewise writes its structured error to stderr and leaves stdout empty,
so | jq is never handed a diagnostic.
An ambiguous prefix gets no guess at all (--target co could be codex or copilot), and neither
does nonsense (--target totally-bogus prints the plain list). When an alias IS accepted, the
substitution is announced on stderr, so piped stdout stays machine-readable:
$ dz init --target claude --preset devops > installed.txt
dz init: --target "claude" → claude-code (alias)When this matters: you are following a blog post or a teammate's snippet that says --target claude.
It now works, and you are told what it resolved to.
dz list — one broken skill never hides the rest
dz list used to abort on the FIRST unparseable SKILL.md: exit 1, an error naming neither the file
nor a count, and every other skill in the tree invisible. A pack you installed could blank your whole
listing. Now the loadable skills are listed on stdout and the unloadable ones are named on
stderr:
$ dz list --skills-dir .claude/skills
105 skill(s) in /home/you/proj/.claude/skills:
api-design Designs REST and GraphQL APIs with OpenAPI specs…
…103 more…
test-writer Writes focused unit and integration tests…
$ echo $?
1…with the diagnosis kept out of the data:
$ dz list --skills-dir .claude/skills 2>/dev/null # stdout only — clean, pipeable
105 skill(s) in …
$ dz list --skills-dir .claude/skills 1>/dev/null # stderr only — the actionable part
dz list: 105 listed, 1 skipped in /home/you/proj/.claude/skills
⚠ 1 skill(s) skipped (unparseable SKILL.md):
/home/you/proj/.claude/skills/broken-one/SKILL.md
SKILL.md must begin with a "---" frontmatter fence
(line 1: "# Broken One")The full contract:
| valid skills | skipped | stdout | stderr | exit |
|--------------|---------|--------|--------|------|
| >0 | 0 | the listing | empty | 0 |
| >0 | >0 | the listing of the valid ones | named summary | 1 |
| 0 | >0 | nothing | named summary (0 listed, N skipped) | 1 |
| 0 | 0 | nothing | dz list: no skills found in <dir> | 1 |
dz init, dz install and dz sync behave the same way — the good skills are installed, the bad ones
are named, and the command exits 1.
Two failure kinds, two headers, two subjects. A skill that will not PARSE and a skill that will not
WRITE are different accusations, so dz init reports them separately:
$ dz init --target claude-code --skills-dir ./skills --project ./proj
dz init --target claude-code: 1 skill(s), 1 file(s) written, 0 skipped
dz init: 1 installed, 1 failed to write # ← stderr
✗ 1 skill(s) failed to install (compile/write error):
alpha
EEXIST: file already exists, mkdir './proj/.claude/skills/alpha'The header names the target, not alpha/SKILL.md — which is perfectly valid. (It used to print
⚠ 1 skill(s) skipped (unparseable SKILL.md) and quote line 1: "---", a valid frontmatter fence, as
its evidence: a failure that names the wrong artifact is worse than an anonymous one.)
dz install renders the offending path relative to the package
and says so explicitly, because a node_modules/** path is not something you can act on:
$ dz install @someone/skills-pack
dz install @someone/skills-pack: 12 skill(s), 24 file(s) written, 0 skipped
dz install: @someone/skills-pack ships 1 unparseable skill(s) —
⚠ 1 skill(s) skipped (unparseable SKILL.md):
skills/broken-one/SKILL.md
SKILL.md must begin with a "---" frontmatter fence
This is a defect in the package, not in your project.
Workaround: npx -y @someone/skills-pack initWhen this matters: a package you installed shipped a broken skill — you still see everything else, you know exactly which file is at fault, and you know whose defect it is.
dzrequires@dzhechkov/harness-core >= 0.4.7. If you ever seedz: needs @dzhechkov/harness-core >= 0.4.7, found 0.4.1, a stale core was reused from a cache:rm -rf ~/.npm/_npx && npx @dzhechkov/harness-cli@latest --version. That named message replaced a bareSyntaxError: … does not provide an export named 'GRADE_SUCCESS_FLOOR'that used to kill evendz --version.
"Nothing installs" / "no skills found"? Update the CLI:
cd /tmp && npm i -g @dzhechkov/harness-cli@latest. Older global installs couldn't locate their own bundled packs outside the monorepo — now fixed, sodz registry/dz init/dz setupwork from any directory.
User Journey — from install to mastery
All 92 commands (MEASURED — reproducer: node --input-type=module -e "import('./dist/index.js').then(m=>console.log(m.DZ_COMMANDS.length))" from this package; rendered help documents 93 unique top-level names — the 92 plus the built-in help — pinned NAME-FOR-NAME by test/command-inventory-parity.test.ts) mapped to a real workflow:
DISCOVER → INSTALL → USE → CREATE → MAINTAIN → SHAREdz profile — say once who you are, and stop being explained the wrong things
The failure this closes is measured, not hypothetical. On 2026-08-28 an OS pipe buffer was explained
to this repository's owner across three paragraphs of kernel mechanics — he holds a CCIE, and
"tail drop on a full queue with no backpressure signal" would have landed in one line. In the same
session ADR and vitest worker went by unexplained, in a domain where he had said plainly he is not
a professional. Neither failure was ignorance. Both were not knowing who was listening.
dz profile init
# 1/5 Dialogue language (ru, en, …) [ru]: ru
# 2/5 Default register — pro / pro-lite / plain (профи / профи лайт / просто) [pro-lite]: профи лайт
# 3/5 Назовите 2–4 области, где вам НЕ нужно пояснять термины … : networking (CCIE; NSX), cloud architecture
# 4/5 Где наоборот — терминам нужна одна поясняющая фраза? : software architecture, testing internals
# 5/5 Do you teach — must explanations be re-tellable? y/n [y]: y
#
# wrote ~/.dz/profile.json (0600) — register pro-lite (профи лайт), language ru, teaches yes
# deep: networking (CCIE; NSX), cloud-architecture · weak: software-architecture, testing-internals
# synced block into ~/.claude/CLAUDE.mdThat block now loads in every project on the machine, including projects where dz is not installed
— because ~/.claude/CLAUDE.md is read by the runtime itself, not by a hook.
dz profile show # store path, age in days, drift verdict, the rendered block
dz profile set weak add build-toolchains
dz profile set register профи # RU aliases accepted; stored as the neutral `pro`
dz profile sync # after a hand-edit: repairs the block, timestamped backup, foreign content untouchedWhen to use it: once, at onboarding — and again whenever you correct the register twice in one session, which is the signal the profile is wrong rather than the moment to absorb it silently.
What it deliberately does not do. It never changes the FACTS — numbers, caveats, risks and bad
results survive every register, or "simpler please" becomes a hole in the honesty rules. It never
touches artifacts written for future readers: ADRs, commit messages, code comments, QE reports and npm
READMEs keep their own conventions, because their audience is not the current operator. And it is
redacted from training-pair capture, because .dz/fa-training/ records the full prompt and is
deliberately not gitignored — without that, "never write personal data into a project" would be
defeated one path over.
What no test can prove. That the explanation actually landed is a judgement only the reader makes.
The acceptance step is human by design and recorded as such
(features/operator-profile/08_acceptance_cf7.md): the same passage rendered at two registers, and
the owner says which one works. That run found a real defect — a term glossed in one breath and
another assumed in the next — and produced the rule the block now carries: an explanation is
self-contained; every term gets its gloss at first use in THIS passage, because the earlier text has
scrolled away and a new session never had it.
Phase 1: Discover (what's available?)
npm install -g @dzhechkov/harness-cli # install the CLI
dz help # see all commands
dz pretrain # analyze project files → recommend by tech stack
dz recommend "build API and deploy to K8s" # keyword match → skills + toolkits
dz recommend "work on this project" # unmatched? → labels suggestions as PROJECT-STACK, not task-derived
dz stats # 58 packages, 260 skills, 10 targets, 14 presets
dz dashboard # visual panel — packages, adapters, skill packs
dz registry # browse all 260 skills by category
dz registry search kubernetes # find specific skills
dz registry --category devops # filter by domain
dz downloads # npm weekly download statsRussian and English word forms share the same lexical search tier; both the query and the catalogue text are normalized. The current workspace reproducer prints the same count for the two Russian forms (the catalogue count may grow, but the pair must stay equal):
$ dz registry search "анализы"
Search: "анализы" — 8 result(s)
$ dz registry search "анализ"
Search: "анализ" — 8 result(s)
$ dz recommend "пришли анализы крови, хочу разобраться"
║ Topics: healthdz recommend --json reports topicSource as task, project-stack, or none. When no topic
matches, human output explicitly says that any suggestions came from the project stack; if the
stack also yields nothing, it prints that no recommendations were found.
Phase 2: Install (set up your workspace)
# Full setup with self-learning (recommended):
dz setup --target claude-code --preset devops # pretrain + hooks + memory + installs the preset skills
# With AgentDB vector memory (semantic search + self-learning):
dz setup --target claude-code --preset devops --memory agentdb # vector memory + agentdb MCP serverdz setup also installs the destructive-command guard into your project: the hook body at
.claude/hooks/destructive-guard.cjs plus its PreToolUse entry on ^Bash$ in
.claude/settings.json. It refuses a deletion whose target is a LITERAL path into a protected store
(.dz/, .agentic-qe/, a .db/.sqlite/.rvf file) and prints, on every verdict it speaks about,
the four things it does NOT decide — a target held in a shell variable, a relative target after a
directory change, git rm, and quoted text under any head other than the delete verb or sh -c.
dz hooks-sync --target codex installs the same check on the Codex host. Both hook bodies resolve
@dzhechkov/harness-core by path, including the absolute path of the installation that emitted
them, so a global install (npm i -g @dzhechkov/harness-cli) guards a project that has no local
copy of the package. If the guard cannot load it says so on one line and lets the command through —
it never blocks on its own failure, and it is never silent about failing.
Setup is additive here too, and deliberately incurious: if .claude/hooks/destructive-guard.cjs
already exists WITHOUT dz's ownership marker, the file is yours. dz does not overwrite it, does not
touch your settings.json entry for it, and — since a hook can only be proven to work by running it
— does not RUN it either: a file a repository shipped is not something dz setup may execute on
your behalf. You get one line saying so, and dz setup --force replaces the file (keeping a
timestamped backup) if that is what you want.
# Or just install skills (no learning):
dz init --target claude-code --preset devops # 30 DevOps skills
dz init --target openclaude --preset web3 # 12 DeFi skills for OpenClaude
dz init --target codex --preset mcp # 16 MCP skills for Codex
# Or pick individual skills:
dz init --target claude-code --select terraform,kubernetes,docker-compose
# Or install from any npm package:
dz install @dzhechkov/skills-devops # npm install + copy skills
# Verify everything is correct:
dz verify # structural validation
dz doctor # 7 health checks
dz list # show installed skills
dz info --id terraform # detailed info about a skillPhase 3: Use (work with your agent)
# Now use Claude Code / Codex / OpenCode / Hermes normally.
# Skills are auto-discovered from the platform's skills directory.
# Example in Claude Code:
# "Review this PR" → pr-review skill activates
# "Design an API" → api-design skill activates
# "Fix this CI" → ci-fix skill activates
# "Сделай AI-дайджест за февраль" → news-digest (cited report); "what's new since last week" → news-monitor (delta)Phase 4: Create (build your own skills)
# Scaffold a new skill:
dz create-skill --name my-skill --description "What it does" --tier 2
# With BTO-compatible eval templates:
dz create-skill --name my-skill --bto
# Benchmark your skill (aim for Grade A):
dz benchmark .claude/skills/my-skill # single skill — 20 L0 checks
dz benchmark packages/@dzhechkov/skills-devops --all # batch all
dz benchmark skill-a --compare skill-b # A/B compare
# Find skills to canonicalize from the ecosystem:
dz scout # scan 11 sources (GitHub, npm+plugins, HN, ...)
dz scout --deep # deep analysis with SKILL.md parsing
dz auto-canonicalize --source github.com/user/repo --pack packages/@dzhechkov/skills-devopsPhase 5: Maintain (keep skills fresh)
# Check for upstream changes (canonicalized skills):
dz sync-upstream --list # which packages have external sources?
dz sync-upstream --all # check all against upstream
dz sync-upstream --package packages/@dzhechkov/skills-devops # check one
# Check installed skills vs canonical:
dz upgrade # shows which skills need update
dz upgrade --target openclaude # check specific platform
# Sync canonical to legacy layout:
dz sync # canonical → project skills
dz migrate # detect legacy installations
# Author + gate custom Workflow loops (loop-plan/1):
dz workflow init --name my-loop --pattern pipeline --o my-loop.plan.json
dz workflow render my-loop.plan.json --o my-loop.js
dz workflow-lint my-loop.js --plan my-loop.plan.json --require-plan
# Cross-host state sync:
dz roam --apply # sync agent state across machines
# Is the self-learning loop actually paying off?
dz compounding # readiness + payoff report (INSUFFICIENT_DATA is a real answer)
dz deadwood --weeks 8 # advisory deprecation candidates; shallow history → INSUFFICIENT_DATA
dz epoch-replay --mock --n 24 --effect 0.9 # $0 dry run of the cold-vs-warm verdict math
dz epoch-replay --emit # ready? emit the real cold-vs-warm work orderPhase 6: Share (publish to the world)
# Verified release — 4 HARD gates (tests, audit, syntax, smoke-boot) in FRONT of dz publish:
dz release --dry-run # print the full gate plan, execute nothing
dz release --filter skills-devops --tag # gates for one release set; on green: git tag + notes
dz release # gates for the whole workspace; green prints the ready dz publish command
# Publish updated packages to npm:
dz publish --dry-run # preview
dz publish --filter skills-devops # publish specific package
dz publish # publish all changed packages
dz publish --yes --mirror-cmd "node scripts/publish-mirror.mjs" # live publish + required mirror receipt
dz publish --yes --no-mirror # explicit one-run mirror opt-out
# Export portable, self-contained skill bundles for a generic consumer (e.g. a LangGraph app):
dz bundle --preset news --out ./dist # → ./dist/skills/<id>/ (SKILL.md + references/scripts/assets)
dz bundle --select news-digest,goap-research-ed25519 --out ./distFor a live sweep that publishes at least one package, dz publish can run a command-hook epilogue.
Set it for one invocation with --mirror-cmd, or persist the command in .dz/config.json:
{
"publish": {
"mirrorCommand": "node scripts/publish-mirror.mjs"
}
}The CLI exports DZ_PUBLISHED=name@version,… and appends --expect name@version,… --json. It marks
the mirror confirmed only when the command returns JSON containing the live-manifest receipt;
unconfirmed, skipped, and not-configured remain separate report states. If npm publication
failed, the command returns exit 1. If npm publication landed but a configured mirror was not
confirmed, it returns exit 3 and prints a usable re-run: command. --dry-run, --no-mirror,
--bump-only, and a live sweep with zero published packages do not execute the hook. A malformed or
unreadable .dz/config.json leaves the mirror not-configured, returns the ordinary publish exit code,
and prints ⚠ mirror: .dz/config.json unreadable — …; an explicit --mirror-cmd still takes precedence.
The repository wrapper prints human-readable output by default; scripts/publish-mirror.mjs --json
prints exactly one JSON document (the dz publish epilogue always selects this mode).
Three Ways to Install Skills
| | Individual Skill | Preset | npx Package |
|---|---|---|---|
| What | 1 SKILL.md file | Curated list of skill names | Full toolkit with orchestration |
| Contains | Instructions for 1 task | N skill references | Skills + commands + rules + shards + agents + memory |
| Pipeline | No | No | Yes (phases, checkpoints, governance) |
| Self-learning | No | dz setup adds it | Built-in |
| Install | dz init --select X | dz setup --preset X | npx @dzhechkov/X init |
| Example | terraform | devops (30 skills) | keysarium (7-phase research) |
# One skill:
dz init --target claude-code --select design-thinking
# Curated set by topic (recommended):
dz setup --target claude-code --preset meta # 20 development skills + self-learning
# Full toolkit with orchestrated pipeline:
npx @dzhechkov/keysarium init # 7-phase research + commands + memoryWhen to use which:
- Need 1 specific capability →
--select - Need a themed set that works together →
--preset - Need a full pipeline with commands and governance →
npx
dz sign / dz verify-pack — cryptographic tamper-evidence
Ed25519 over a file manifest, plus a CycloneDX SBOM. Zero dependencies (node:crypto).
What it answers: are the bytes I am looking at the bytes the holder of the pinned key signed? What it does NOT answer: whether those bytes are any good. A signature gives provenance and tamper-evidence, never truthfulness.
# One-time: generate the Ed25519 keypair. The PRIVATE key is written OUTSIDE the repo (mode 0600);
# the PUBLIC key is printed — commit it as keys/dz.pub. dz refuses an --out inside the repo tree.
dz sign --init --out ~/.dz/keys/dz.key
# Sign a pack. The private key MUST live outside the repo — dz refuses otherwise.
dz sign --pack packages/@dzhechkov/skills-qe --key ~/.dz/keys/dz.key
# Verify an unpacked artifact. The public key comes from the REPO (keys/dz.pub), never from the pack:
# whoever replaced the artifact would have replaced a key shipped inside it.
dz verify-pack --pack ./unpacked-tarball/package # exit 0 = artifact unmodified
dz verify-pack --pack ./downloaded-pack --pubkey keys/dz.pub # explicit trust root
# The SBOM on its own (CycloneDX 1.5, a file-level bill of materials for the pack):
dz sbom --pack packages/@dzhechkov/skills-qe # print to stdout
dz sbom --pack packages/@dzhechkov/skills-qe --out sbom.json # write to a fileA single flipped byte, a deleted file, or an added file inside the authenticated npm shipment set
fails verification and names the path. Directory segments node_modules, .git, .agentic-qe, and
.dz are unsigned local/dependency/VCS state by design and are absent from both manifest and SBOM; an OK
verdict makes no claim about bytes placed there. A symlink smuggled anywhere else still fails loudly.
sbom.json is required even though it is not self-hashed: after the signature is valid, verify-pack
derives the canonical CycloneDX bytes from the signed manifest and rejects a missing, malformed, duplicated,
renamed, re-hashed, or metadata-modified SBOM. This keeps the trust chain acyclic without leaving the SBOM
as unauthenticated decoration.
CycloneDX SHA-256 fields describe raw bytes only. Because packers may reorder ordinary
package.json metadata, format v3 publishes its semantic canonical digest through explicit
dz:canonical-json-sha256-v2 and dz:digest-basis=package-json-ordered-conditions-v2 properties.
Canonicalisation still sorts packer-noise keys, but preserves every object key order under exports,
imports, and typesVersions; changing first-match condition order therefore changes the signed digest.
Current/v3 signing and verification refuse malformed, duplicate-key, or precision-losing JSON.
Verification retains compatibility-only readers for existing v1/v2 manifests; new evidence emits v3.
dz sign hashes the pnpm tarball because pnpm rewrites package.json and may synthesize or omit files;
the authoring tree is therefore not the signed object. dz publish rebuilds and verifies that exact
artifact, while direct verify-pack is for an already unpacked artifact or installed package.
Re-sign after ANY pack change (the manifest hashes package.json too) — sign is the LAST step before publish. Every degenerate input (no manifest, empty file list, empty signature, no public key)
fails closed — absence never reads as success. dz doctor and dz drift-check run this check
over the installed packs: a tampered pack is fatal (blocks); an unsigned pack or a missing trust
root is reported, not fatal (transitional — the existing packs are not yet signed).
dz publish runs the check before publishing anything. With keys/dz.pub committed, an unsigned or
mismatching pack blocks the release. Until then, packs publish unsigned and dz publish says so on
every run; --require-signing turns that into a refusal today.
From Claude Code, in plain language:
"Sign the QE skill pack with my key at ~/.dz/keys/dz.key, then verify it." "Check whether the pack I just downloaded matches what we published." "Publish, but refuse if anything is unsigned."
Signature checks in dz doctor and dz upgrade
dz doctor verifies every installed skills-* pack against a pinned Ed25519 key, and dz upgrade
verifies what it just installed. A pack that does not match its signed manifest is fatal:
dz doctor # ... signatures: 23 verified, 0 unsigned, 0 TAMPERED (the gate is ARMED: keys/dz.pub committed)
dz doctor --require-signing # an unsigned pack becomes fatal too
dz upgrade # a TAMPERED pack aborts the upgrade
dz doctor --pubkey ./my.pub # verify against a key you pinned yourselfKey precedence: --pubkey > the repository's keys/dz.pub > the key shipped inside harness-cli.
The key never comes from the pack being verified — whoever replaced the pack would have replaced a
key that travelled inside it.
Honest limits. The packaged key lives in the verifier and vouches for other packs; a compromised
harness-cli is outside the threat model, because you have already run its code. And a signature proves
the bytes are unmodified — never that the skill is any good. Today no key is committed, so every pack
reports unverifiable and nothing fails.
From Claude Code, in plain language:
"Run doctor and tell me if any installed pack was modified." "Upgrade, but abort if anything fails its signature."
npm provenance on release
dz publish appends --provenance only where an OIDC token can be minted — GITHUB_ACTIONS=true
plus both ACTIONS_ID_TOKEN_REQUEST_URL and ACTIONS_ID_TOKEN_REQUEST_TOKEN, which GitHub exports under
permissions: id-token: write.
dz publish # auto: provenance in CI, silent no-op locally
dz publish --provenance # force. FAILS before the batch if the environment cannot mint a token
dz publish --no-provenance # escape hatch for a registry outage; prints why it was usedProvenance proves which workflow, at which commit, built this tarball — and there is no private key for
anyone to leak. It does not prove the code is good, and an attacker who can push to main and trigger
the release job gets a perfectly attested malicious package.
A ready-to-install workflow is at features/publish-provenance/07_code_changes/publish.yml; copy it to
.github/workflows/ and add an NPM_TOKEN secret.
Lesson quarantine — a fresh lesson is a hypothesis, not knowledge
Self-learning has a poisoning problem: the moment dz teach stores a lesson, it ranks alongside
patterns proven over months and can ride the auto-inject hook into your next task's context — even
if it is wrong, one-off, or junk. Quarantine (opt-in) closes the gap between COLLECT and RANK:
// .dz/config.json
{ "memory": { "learning": {
"quarantine": true, // fresh lessons start as quarantined hypotheses
"quarantineDamp": 0.5, // rank multiplier for ⚠q hits in recall (0..1]
"quarantineExpireDays": 30 // unconfirmed after N days ⇒ expiry CANDIDATE (report only)
} } }Three surfaces, three strictness levels — and promotion is EARNED, never automatic:
dz teach "..." # → "⚠ quarantined: excluded from auto-inject, damped in recall"
dz teach "..." # the same normalized text reinforces the existing lesson instead of adding a duplicate; quarantine is preserved
dz recall "topic" # ⚠q hits are VISIBLE but marked + rank-damped (never hidden)
# the UserPromptSubmit auto-inject hook EXCLUDES ⚠q lessons entirely (logged, never silent)
dz teach --reinforce "<exact text>" # confirming a lesson IS its promotion
dz recall --promote <dzId> --apply # or promote explicitly (dry-run by default)
dz consolidate --prune-quarantine # report expired unconfirmed lessons (dry-run)
dz consolidate --prune-quarantine --apply # remove them (snapshots first) — a SEPARATE gate,
# never coupled to --prune-noise (unproven ≠ garbage)Absent config = zero behavior change. Existing lessons are grandfathered as promoted; a corrupt quarantine marker reads as promoted (a metadata glitch must never isolate proven knowledge). When to use: any project where subagents teach lessons unattended — the quarantine is the gate between "an agent wrote this down" and "agents now act on it".
Target parity — dz parity (the honest feature × target map)
The harness runs on 10 targets, but not every feature runs everywhere: hooks, MCP (Model Context
Protocol) and the Workflow runtime exist only on some platforms. dz parity answers "what do I
actually get on X?" from a COMPUTED capability model — the matrix is never hand-written (a
hand-maintained table is a drift surface; the model lives next to the code, under tests that
refuse an unclassified 11th target).
dz parity # the full grid: ✓ full / ◐ manual (via which form) / — absent
dz parity --target codex # one target in detail: what works, THROUGH what
dz parity --json # machine contract (targets, capabilities, per-cell level + via)Example (real output, trimmed):
dz parity — codex (capabilities: shell, skills, mcp)
✓ dz CLI (all commands) via shell command
◐ feature-adr pipeline via interactive skill (plain /feature-adr)
◐ Step-10 Delivery Gate via dz delivery-check (CLI 4-plane hand-off protocol)
◐ Integrity claim-check via dz claim-check (CLI) + publish gateWhen to use: before promising a workflow to a teammate on Cursor/Codex/Hermes; when choosing a target for a project; as the requirements input for porting a feature to more targets.
Does the learning loop actually pay? — dz compounding
dz's self-learning has collect, rank and apply legs — this command answers whether they COMPOUND, honestly. Ported from rUv's darwin-mode with a deliberate split: the seeded statistics came over verbatim (deterministic bootstrap, lower-95 promotion, and a minimum of 5 samples per arm — darwin's own calibration measured a 33% false-discovery rate at n=3); darwin's measurement legs did NOT (they score fixtures, not your data). The measurements here run over what your machine actually recorded:
$ dz compounding
POOL PAYOFF: 154 lessons · 27 ever injected by the apply leg · 89 touched by any recall
write-only ratio (strict bar): 82%
GUARD TRAJECTORY (violations, first half vs second half of the audit span):
↓ no-workspace-star: 31 → 0
↓ readme-first: 49 → 4
COLD-VS-WARM REPLAY: 2 unique prompt event(s); 5 needed — queries are recorded as of 2026-07-28
INSTRUMENTATION: last apply-leg record … — live
EVIDENCE CHAIN .dz/recall-usage.jsonl: verified · 3 chained · 133 pre-chain (uncovered)
EVIDENCE CHAIN .dz/guard-audit.jsonl: verified · 3 chained · 82 pre-chain (uncovered)
LESSON → RULE FUNNEL (calendar month, observed traffic only):
2026-08 · eligible/attempted/accepted/executions NOT MEASURED (promotion-history-not-recorded)Four honesty rules are load-bearing:
- a gate without enough data says INSUFFICIENT DATA — never a verdict (the apply-leg log turned out to have been silently dead for 19 days; "no data" is a finding, not a pass);
- readiness is not a result — the replay section says
ready, neverpromote; - improvement is judged by RATE (violations per audit), so a quiet afternoon cannot masquerade as progress;
- zero promotions is not itself a finding — the funnel names a stopped stage only when its
predecessor is non-empty and it stays empty for three consecutive measured UTC months. A missing,
malformed, unreadable, unobserved, or anchorless source prints
NOT MEASURED (<reason>), never 0.
The funnel is prospective: ordinary non-dry dz guard promote runs append bounded candidate/status
observations to the existing .dz/promotion-state.json; --dry-run still writes nothing. Guard
firings join by the opaque digest of the exact effective template+params carried in the audit row, never by a
display rule id alone. Text and --json render the same counts/status/findings model and issue no
overall learning-health verdict.
The prompt queries that make replay possible stay on your machine: .dz/recall-usage.jsonl is
git-ignored, entries are truncated at 200 chars and flagged when truncated (a prefix is not a prompt,
so flagged rows never count as replayable).
dz deadwood --weeks 8 applies the same no-data discipline to harness maintenance: until command
history spans the requested window and sample floor, it prints VERDICT: INSUFFICIENT_DATA. Once
ready, it lists only human-review deprecation candidates, reports safety-net exclusions with their
written reasons, and labels skills no-instrumentation because no skill invocation signal exists.
It never deletes, disables, renames, or deprecates a command, skill, or rule.
Is the evidence itself intact? — the event chain
Every number above is computed from two JSONL files, and a compaction bug already inflated one of
them once (read totals grew 2 → 4 → 6 across three compactions, fixed 2026-07-28). So each record
appended to .dz/recall-usage.jsonl and .dz/guard-audit.jsonl now carries a sequence number and a
hash of the line before it, and both dz compounding and dz doctor verify the chain:
$ dz compounding | grep 'EVIDENCE CHAIN'
EVIDENCE CHAIN .dz/recall-usage.jsonl: verified · 3 chained · 133 pre-chain (uncovered)
$ dz doctor # silent while clean; on damage:
[XX] evidence chain (.dz/guard-audit.jsonl) - 1 defect(s): BrokenLink@L3 —
learning verdicts computed from this log are unsafeDefects are named, not lumped: BrokenLink (an edited or lost record), DuplicateSeq (a duplicated
record, or two writers racing), NonMonotonicSeq (an unrecorded restart), TornTail (a partial
write), DoubleCounted (a rewrite that emits more events than it read — the 2 → 4 → 6 class),
plus three that keep a rewrite from certifying itself: LedgerImbalance, MalformedLedger and
ClaimInterrupted.
Two rules make the check hard to fool, both learned from cross-model review:
- compaction refuses to launder. A rewrite that re-chains a damaged file would turn corruption into a clean chain — so compaction verifies its input first and REFUSES a defective one. The log then grows past its size cap, on purpose: the cap is a convenience, the evidence is the product.
- a rewrite cannot silently eat a concurrent append. The whole-file rewrite takes an exclusive lock and re-reads the live file immediately before the rename; an append that landed in between aborts the attempt and is folded into the retry instead of being overwritten.
Honest scope, stated everywhere it appears: this is corruption detection for our own bugs —
compaction, torn writes, races. FNV-1a is not cryptography and the threat model has no adversary:
anyone who can edit the log can recompute the chain. When to use: read the line before you quote
any dz compounding number, and after any crash that interrupted a write.
Three properties make it safe to run on a per-prompt hook: chain fields come from the last line
only (no full-file scan per append); records written before chaining existed stay valid and are
reported as an uncovered pre-chain prefix rather than flagged; and a tail that cannot be read
never blocks the write — the writer starts a fresh, explicitly marked segment.
Did the lessons actually change outcomes? — dz epoch-replay
dz compounding says a cold-vs-warm replay can be run. dz epoch-replay runs it and reports
what it found: Epoch 0 (cold — the prompt alone) vs Epoch 1 (warm — the same prompt plus exactly the
lessons the apply leg injected), on the same instances, scored into a three-valued verdict.
The test is PAIRED. Each instance is one judgment on one prompt, so the statistic is a single
binomial over decisive pairs: p̂ = warm wins / (warm wins + cold wins), with a 95% Wilson
interval reported on the lift scale 2p̂ − 1. Ties carry no direction — they are excluded from
the denominator and reported separately.
SUPPORTED only when the lift interval lies entirely above zero. FALSIFIED only on harm
(entirely below zero) or on a passed non-superiority test — the lift upper bound below a
pre-registered margin, default 0.05, at 10+ decisive pairs. Everything else is INCONCLUSIVE, a
first-class honest outcome; below 5 decisive pairs there is no verdict at all.
A tie is under-powered, not refuted. 6/12 gives a lift interval of [-0.492, 0.492] — that
excludes nothing, so it reads INCONCLUSIVE. So does a perfectly even 500/500 over 1000 pairs
(lift upper 0.0619): the most uninformative result the protocol can produce must not be reported
as a refutation. A refutation has to clear a bar too.
Try it at $0 first — --mock generates seeded synthetic outcomes at a TRUE effect you choose, and
runs them through the real verdict math:
$ dz epoch-replay --mock --n 24 --effect 0.9 --seed 20260729
COLD (epoch 0, no injected lessons): 1/24 CI95 [0.007, 0.202]
WARM (epoch 1, apply-leg lessons): 23/24 CI95 [0.798, 0.993]
VERDICT: SUPPORTED
warm 23/24 CI [0.798, 0.993] is DISJOINT above cold 1/24 CI [0.007, 0.202]
$ dz epoch-replay --mock --n 24 --effect -0.9 --seed 20260729 # → FALSIFIED (harm)
$ dz epoch-replay --mock --n 24 --effect 0.4 --seed 20260729 # → INCONCLUSIVE (neither separates nor excludes)
$ dz epoch-replay --mock --n 12 --effect 0 --seed 7 # → INCONCLUSIVE (6/6 tie: under-powered, NOT refuted)$ dz epoch-replay --mock --n 24 --effect 0.9 --seed 20260729
SLICE: all · 24 scored instance(s) · 24 DECISIVE pair(s)
COLD (epoch 0, no injected lessons): 1/24 decisive CI95 [0.007, 0.202]
WARM (epoch 1, apply-leg lessons): 23/24 decisive CI95 [0.798, 0.993]
LIFT (paired, 2p−1 over decisive pairs): +0.917 [0.595, 0.985]
VERDICT: SUPPORTED — the lift interval lies ENTIRELY above zeroEvery mock run prints SYNTHETIC … it is NOT evidence about the learning loop, and the same seed
gives byte-identical output — a demo is a reproducer.
Real mode: the runner ORCHESTRATES and SCORES — it never calls a model. Generation and judging happen out of band, so the core stays pure, offline and reproducible:
$ dz epoch-replay --emit # → .dz/epoch-replay/work-order.json (git-ignored: raw prompts)
25 instance(s) · seed 20260729 · blind A/B assignment PRE-REGISTERED
# 1. have an agent fill coldPlan / warmPlan for each item (symmetric length, same task)
$ dz epoch-replay --judge .dz/epoch-replay/work-order.json # → blind judge prompts
# 2. have an EXTERNAL, cross-model judge answer them → [{ "id": …, "winner": "A|B|TIE" }]
$ dz epoch-replay --score judgments.json --work-order .dz/epoch-replay/work-order.json --slice taskThree things make that blind real rather than decorative:
- The judge sees
{id, prompt}and nothing else. No assignment, no arm names, no slice label, not even a path back to the work order. The judge file is byte-identical whichever way the assignment fell, so it cannot be decoded. (Skipped items are reported on stdout, not in the file — their reasons name arms.) - The work order is integrity-checked. It carries a sha256 digest over its pre-registered core
(version, seed, margin, corpus fingerprint, every
[id, warmIsA]);--judgeand--scorerecompute it and re-derive every assignment from the stated seed, refusing on any mismatch. A hand-written order does not buy a verdict.Honest scope: this is an integrity check against accidental corruption and mismatch — not a cryptographic commitment. The hash is self-contained, so a determined operator can re-forge it (seed-searching a matching assignment at n=12 takes a few thousand tries). The threat model is you making a mistake: a hand-edited file, a stale order paired with fresh judgments. The honest-use contract is procedural — emit once, then judge, and keep the emitted file.
--emitrecordsemittedAt+ a corpus fingerprint and--scoreprints the digest, seed and margin, so a reviewer can ask for the original and compare three numbers. - The non-superiority margin is pre-registered.
--marginis accepted only at--emit, is stored in the work order and covered by the digest, and--scorerefuses a--marginflag — a margin chosen once you can see the counts is not a pre-registration, and--margin 99at scoring time would simply buyFALSIFIED. Out of range (outside(0, 0.5]) is refused, not clamped. - Corrupt input is refused, not measured. Duplicate judgement ids exit 1 rather than counting one opinion N times. Unknown ids and unparseable winners are skipped with a reason, never guessed.
The warm arm's only delta is the injected lessons: no gold answers, no verdicts, no outcome labels ever enter a work order.
When to use: after dz compounding reports the replay as READY; before claiming that recall
"works"; and any time you want the claim re-checked as the corpus grows.
Обратный мост QE: Claude-ревьюер из Codex-сессии — dz qe-bridge
The cross-family rule ("the family that writes the code must not review it") was enforceable in one
direction only. When Codex hosts the run there is no Claude agent plane to dispatch from — and
dz reqe's brief admits it: for a claude review family it prints null where the codex branch
prints a ready command. dz qe-bridge is that missing vehicle: a plain-shell command that probes a
Claude model, sends a Step-8-shaped brief over SCOPED extracts, and PARSES the verdict.
# MEASURED 2026-08-19 on this repo — reproducer: the exact command below, reviewing a real shipped feature
$ dz qe-bridge --family claude --slug wave1-scorer-negation --coder-family codex --model opus
dz qe-bridge: GRADE C from claude/opus — 7 finding(s) in 343s
report: features/wave1-scorer-negation/08b_reqe_report.md
signoff: features/wave1-scorer-negation/.fa-state/qe-bridge/signoff-2026-08-19T18-48-45-545Z.json
settle: dz reqe --slug wave1-scorer-negation --done --report features/wave1-scorer-negation/08b_reqe_report.md
the bridge REPORTS (any grade exits 0); gating stays with dz reqe and the host pipeline.
# the same command with a binary that cannot answer — a failed call, and NO report to settle with
$ DZ_QE_BRIDGE_CLAUDE_BIN=/bin/false dz qe-bridge --family claude --slug wave1-scorer-negation --coder-family codex
dz qe-bridge: FAILED — probe-failed
no candidate model answered the liveness probe — opus: exit 1, no `OK` in 0 chars of stdout; sonnet: exit 1, …; haiku: exit 1, …
record: features/wave1-scorer-negation/.fa-state/qe-bridge/failed-2026-08-19T18-48-52-931Z.json
no report was written — an unparseable or absent review is never a passing one.
# with a debt on record, the report settles it through the untouched fail-closed path
$ dz reqe --slug add-x --done --report features/add-x/08b_reqe_report.md
dz reqe: debt settled: re-QE grade C (report …) — settlement appended to features/add-x/08_qe_report.mdWhen to use: you are hosting a run outside Claude Code (Codex, CI, a plain terminal), you have
just written code, and the independent reviewer must be the OTHER family. Also: whenever dz reqe
lists a debt whose coder family is openai.
The reviewer runs isolated. Both calls (probe and review) run from an EMPTY temporary directory
with --safe-mode --strict-mcp-config --tools '' --no-session-persistence, and the verdict is read
from the --output-format json result envelope. Why: customization output lands on the same
stdout — MEASURED on this machine, a session-start plugin prints a banner ahead of the model's
answer — so a crafted hook could otherwise print a complete grade-A signoff and a stream parser
would believe it (reproducer: features/qe-bridge-claude/07_code_changes/mutants/c1-forgery-repro.mjs).
Residue, stated: --safe-mode leaves ADMIN-MANAGED policy settings in force, and no flag proves
which binary answered.
What makes the grade valid. Three channels must EXIST and AGREE, each read LAST-anchored,
and the marker must be the FINAL content of the answer:
the terminal QE-BRIDGE-SIGNOFF grade=<A-F> findings=<n> line, the last fenced qe-bridge-signoff
JSON block, and the report's own line-anchored GRADE: line. Repo content flows into the prompt and
comes back quoted, so a planted earlier verdict must lose — and it does (there is a test whose
fixture plants grade=A early and requires the genuine trailing grade=D to win). Extracts are
DEFANGED on the way in, so quoted content can never mint a verdict. Empty, gradeless, marker-only or
self-contradicting output is a named failure — one of 17 closed reasons (envelope-unparseable,
marker-not-terminal, findings-count-mismatch, grade-mismatch, ambiguous-grade,
audit-write-failed, report-write-failed, … ; closed BOTH ways — every one is produced by a real
run in the suite and leaves a record) — with an audit record under
features/<slug>/.fa-state/qe-bridge/ and the raw stdout beside it, never findings: []. Finding
numbers are the reviewer's: a missing, non-positive or duplicated n fails the call instead of being
renumbered, and a marker whose findings=<n> disagrees with the block is findings-count-mismatch.
The record is auditable, not just a conclusion. Every run writes a runId, the resolved
executable plus binOverride (true whenever DZ_QE_BRIDGE_CLAUDE_BIN was used — the documented TEST
SEAM; there is no --claude-bin flag), the prompt sha256, the byte offsets at which each channel was
found, the requestedOut path and reportWritten: true|false — so "no report was written" is a
stated fact rather than an inference from an absent file. Records and reports are written 0600 in a
0700 directory, through O_EXCL, with realpath containment that refuses a symlinked parent — and
the state directory itself is contained the same way, before anything is created in it. The audit
trail is written BEFORE the report and corrected after it, so reportWritten can only ever
understate; if the trail cannot be written at all, the run FAILS (audit-write-failed) rather than
shipping a verdict nobody can re-derive. The record also carries writeSequence — diagnostic
sequencing metadata: a self-reported, process-local trace of the signoff-write-started /
report-written / record-update-prepared steps with monotonic stamps taken at each named event
(the start of the first record write, the moment the report is on disk, and just before the atomic
record update). It replaces a file-mtime comparison in the tests (a race); the write ORDER itself is
proven by the report-failure test (record exists and says false when the report never lands) and
the failpoint test (reportWritten:true only after the report), not by this field.
Exit codes: 0 a signoff was parsed (ANY grade — a grade F still exits 0: the bridge reports, it
does not gate), 1 a named failure, 2 a usage error. Honest limits: it proves the call was
procedurally sound (a live model was probed, a scoped brief was sent, a self-consistent verdict came
back); it cannot prove which model authored the text, and it cannot classify your secrets — the
extracts you scope are what leaves the machine. RU: мост в обратную сторону — из Codex-сессии
позвать независимого Claude-ревьюера и получить РАЗОБРАННЫЙ вердикт; пустой или безоценочный ответ —
это названная ошибка, а не «чисто».
Строка объявления стадии — что за модель сейчас запустится
Каждый прогон /feature-adr печатает перед каждым диспатчем стадии одну строку — всегда, без
флага:
▸ router · session · routing not requested
▸ adr · fable · budget table cell
▸ code · codex:gpt-5.6-sol:high · coder knob = codex · landed barrier
▸ qe · sonnet · cross-family QE (the coder never self-reviews)
▸ qe · opus · codex refused before dispatching — nothing ranПоследняя строка — про случай, который легко принять за отработавшую ступень: модель Codex ОТВЕТИЛА на пробу, но ступень отказалась строить диспатч (непригодный ref области ревью, небезопасный id). Ни один агент не запускался, и строка говорит именно это, а не «предыдущая ступень ничего не отдала» — та формулировка утверждала бы запуск, которого не было.
Тот же разбор действует, когда рантайм вовсе не знает типа агента codex:codex-rescue: строка
скажет codex unsupported at this dispatch — Claude. И исход принадлежит своему вызову — дизайн-
стадии идут параллельно, поэтому соседняя стадия не может подменить твоей стадии причину.
Три поля: стадия · модель · ветка, которая эту модель выбрала. session означает, что маршрутизация
не запрашивалась и стадия наследует модель сессии. Хвост landed barrier появляется там, где стадия
резолвится на Codex И пишет артефакт вне процесса (code, plan и четыре стадии дизайна): конвейер
после диспатча ЖДЁТ, пока файл ляжет на диск, — если прогон замер, эта пометка говорит, почему.
Строка описывает фактический диспатч, а не намерение: возобновлённая из точки сохранения стадия не
печатает ничего (она не запускается); при coder=codex-fallback первая ступень объявляется как Claude,
вторая — как Codex в момент своего запуска; codex-строка несёт пробированный id модели, тот же, что
попадёт в итоговый modelsUsed. Модель и причина берутся из ОДНОГО возвращённого значения резолвера,
поэтому строка, диспатч и отчёт не могут разойтись.
Пересмотр после аварийного само-ревью — dz reqe
The feature-adr pipeline's cross-model guard says the model that writes code must not review it.
Historical usage-switched runs could suspend that guard under provider-limit pressure: coder AND
Step-8 QE both ran on Codex. The rule used to say "re-review manually after limits reset" — an
instruction nobody remembers. dz reqe turns it into a debt with a lifecycle: the run records
features/<slug>/.fa-state/reqe-due.json, dz usage --json carries the outstanding count as
reqeDue, and settlement is FAIL-CLOSED.
$ dz usage --json
{"sessionPct":null,"weeklyPct":null,"routing":"disabled-by-design","spend":{"days":[...],"total7d":{...},"byModel":{...}},"reqeDue":1}
$ dz reqe # the ledger
dz reqe — 1 unsettled re-QE debt(s):
add-x coder=openai qe=openai grade=B 2026-07-30T10:00:00Z → dz reqe --slug add-x
$ dz reqe --slug add-x # the ready cross-family review brief (the OTHER family than the coder)
$ dz reqe --slug add-x --done --report features/add-x/08b_reqe_report.md
dz reqe: debt settled: re-QE grade C (report …) — settlement appended to features/add-x/08_qe_report.mdWhen to use: any time dz usage --json reports a positive reqeDue, or a feature-adr result
carried reqeDue: true. Fail-closed settlement: the report must exist, be non-trivial, and name exactly
one line-anchored GRADE (the boilerplate phrase GRADE A-F does not count); the run's own
08_qe_report.md can never settle its own debt (real-path AND inode compared — a hard link doesn't
fool it); the settlement epilogue lands in 08_qe_report.md and the due-file rotates to
reqe-settled.json (evidence kept, never deleted). Honest scope (printed by the command):
nothing re-runs QE automatically, and the validator proves the settlement is procedurally sound —
which model authored the report stays with the human running the brief. RU: гард «кодер не ревьюит
сам себя» осознанно снимается под лимитом; dz reqe превращает инструкцию «перепроверь потом»
в долг на диске — виден как reqeDue в dz usage --json, гасится только настоящим кросс-семейным отчётом с грейдом.
Spend you can inspect — dz usage
dz usage reports cost-weighted token spend from local Claude Code and subagent transcripts. It does
not predict provider limits: there is no provider usage API, weekly resets are per-account, and
ad-hoc resets make a local denominator non-measurable. Feature routing is therefore disabled by design.
Two things changed to make it mean something:
- Cost-weighted, not a raw sum. A flat token sum is 89–99.7% cache-read (MEASURED), which grows with conversation length, not with work done — two sessions doing identical work differed by orders of magnitude. Tokens are now input-equivalents (input 1x, cache-write 1.25x — 2x for a 1-hour TTL write, cache-read 0.1x, output 5x).
- Subagent transcripts count.
<session>/subagents/*.jsonlcarry real, non-duplicated usage and were silently excluded. - Symlinked project directories are scanned by real path, each real directory once. Broken links and symlinks to files are skipped.
- The walk is safe and bounded. Only regular files are read, symlinked files and directory components cannot create duplicate walks or loops, and the file cap keeps the NEWEST transcripts so a long history cannot push current usage out of view.
dz usage # seven UTC days + seven-day total + model shares + today-by-model
dz usage --json # sessionPct/weeklyPct stay null; spend carries days, total7d, byModel, daysByModelThe compatibility keys sessionPct and weeklyPct remain present and always null, so existing
feature-adr probes keep parsing the payload without treating a guess as a routing signal.
What unknown means, and which window each block covers. The "by model" table is a
seven-day window total — it is the same seven days as the daily table above it, collapsed
across models, never a per-day figure (a defect this project has hit before: a weekly figure read
as a daily one). A model row named unknown is the aggregation's fallback bucket, literally
event.model ?? 'unknown' in spendReport — it catches BOTH of the two ways an event can carry no
usable model: an event with no model field at all, and one whose model string did not match
any of the four known substrings (fable/opus/sonnet/haiku). (Fix-round-1, Codex review
MEDIUM #3: an earlier revision of this paragraph said unknown was "not an event without a
model" — the opposite of what the code does; corrected to name both causes.) MEASURED on this
machine, the second cause dominates — almost entirely <synthetic> assistant-turn markers, not a
parsing failure — and unknown is always shown, never hidden, because a hidden unknown row would
make the visible rows undercount the real total. Both the "by model" table and the "today by model"
block print a third column: the model's share as a fraction 0..1 (two decimals), not a
percentage — by model shares are of the seven-day total, today by model shares are of
today's own total, and a complete breakdown's column sums to 1.00 in either block (never
100 — that would be the percentage form). The --json payload additionally carries
spend.daysByModel: one entry per day (same dates as spend.days, same order) with that single
day's {model → weightedTokens}, unknown included — read daysByModel.at(-1) for today's
per-model spend, which the plain-text report prints as the today (<date>) by model block right
after the seven-day "by model" table. The invariant this rests on — every model breakdown
reconciles exactly with the totals it was sliced from — is spendInvariantViolations() in
harness-core (relative tolerance max(1e-6, 1e-9·max(|a|,|b|)), and a NaN/Infinity
