@dzhechkov/harness-cli 0.4.2 → 0.4.4

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/.dz-manifest.json CHANGED
@@ -9,7 +9,7 @@
9
9
  },
10
10
  {
11
11
  "path": "README.md",
12
- "sha256": "4e4049b871ded58ff38fc36b60387a2324610db81dedcb623fb9cce6e77b057b"
12
+ "sha256": "2f053f11ea49780919d0557877302e70adb8f33949299b0b0637ece2809c1b67"
13
13
  },
14
14
  {
15
15
  "path": "coverage/coverage-final.json",
@@ -37,15 +37,15 @@
37
37
  },
38
38
  {
39
39
  "path": "dist/cli.d.ts.map",
40
- "sha256": "a260f0e8d698ac142b38e33ef4d4ed0a21ccee96b179514bcb01bf81c5dce00f"
40
+ "sha256": "5bdc153e789ac180543e9b4955ea36666dc27b7238637b05875e048fc49ae30f"
41
41
  },
42
42
  {
43
43
  "path": "dist/cli.js",
44
- "sha256": "b8a9c6d50cea7eda1065d76adb83e7c853ed9d82db72e7b24f952598e9dbd9d9"
44
+ "sha256": "0183c01a98e232554a7460be48ee5fdf59dd3a7d09c719a31b08acc9c6b3528c"
45
45
  },
46
46
  {
47
47
  "path": "dist/cli.js.map",
48
- "sha256": "4af214e9233bd8f37ab92b95b90fcf05bd46752ecdc0d9491484ee8a7e55c539"
48
+ "sha256": "c5bd1b0b852dd21de328494d7a99db336afbde4397b7c687d416f1a6f3c297b2"
49
49
  },
50
50
  {
51
51
  "path": "dist/index.d.ts",
@@ -69,7 +69,7 @@
69
69
  },
70
70
  {
71
71
  "path": "package.json",
72
- "sha256": "fa591b644f5abda81022a2cfe37996d739a2290ac46e09dcb958faa4e12be9f6"
72
+ "sha256": "5bd26341b4a5885c116432bf621b7fdb4172b2604db47ad3c1c001deb4fe6ed4"
73
73
  },
74
74
  {
75
75
  "path": "src/bin.ts",
@@ -77,7 +77,7 @@
77
77
  },
78
78
  {
79
79
  "path": "src/cli.ts",
80
- "sha256": "c9847974744f5bfd383866eb1832e97cb856e73d6f65cad19abfb91f22998cae"
80
+ "sha256": "0a1097fc3a2294f934fc50f5871b287cfbc5ec85becdab914b7caeca42cb7d1a"
81
81
  },
82
82
  {
83
83
  "path": "src/index.ts",
@@ -189,12 +189,16 @@
189
189
  },
190
190
  {
191
191
  "path": "test/mutation-registry.json",
192
- "sha256": "3dd056f9d05712e45173b0911fc5900f0f52f8deba506d57c0e2b8281eb36a74"
192
+ "sha256": "fe8e5792626cf58678c1e2bcb809f63413655c2e171439157b74deb8b4fd80fb"
193
193
  },
194
194
  {
195
195
  "path": "test/statusline-panel.test.ts",
196
196
  "sha256": "b09b42c4ece1a83e5d3f3765629b0756a8f30a7122f3fc30363f93f092a44c0d"
197
197
  },
198
+ {
199
+ "path": "test/trace-bundle-cli.test.ts",
200
+ "sha256": "fdaad63359ca7ce351e505e613b547bbc653e0847403edfae88d22a8a524b7a0"
201
+ },
198
202
  {
199
203
  "path": "test/workflow-init-lint-clean.test.ts",
200
204
  "sha256": "b7a64462983901adca86ecc29e76b3f95885210e5aaba68352356191e6bfac7f"
@@ -217,5 +221,5 @@
217
221
  }
218
222
  ]
219
223
  },
220
- "signature": "nT7OzOloD2DCFR2ZYd4uWJ6isGyyfLiYk7v8hUKp4f0OTP0fTpsVvbDYJF9VvxASNVqwOGaQ7F6WQVu3NrkmBQ=="
224
+ "signature": "dzYono+I+DXsxk1h8k2DAiNYKHmdzhwaUMDvvfFjCqzVFvYZecPnJQCLgT8i5NrpkaOQl2mbwU8bd3tyxQueDg=="
221
225
  }
package/README.md CHANGED
@@ -1141,6 +1141,159 @@ host run there is nothing to read, and it says so rather than inventing a timeli
1141
1141
  follows from the same boundary: on a non-Claude-Code target the authoring and lint verbs work
1142
1142
  unchanged, and only execution is absent.
1143
1143
 
1144
+ ### Build a loop for YOUR scenario — the end-to-end use case
1145
+
1146
+ You have a repeating multi-agent job of your own — say, **a weekly competitor digest: three
1147
+ competitors researched in parallel, one synthesis, and a quality gate that sends a weak digest back
1148
+ for a redo**. Here is the whole path, with real command output. Every command below was executed
1149
+ before being written down; the outputs are captures, not sketches.
1150
+
1151
+ **Install (once):**
1152
+
1153
+ ```bash
1154
+ npm i -g @dzhechkov/harness-cli # gives you the `dz` binary
1155
+ # or zero-install per call: npx @dzhechkov/harness-cli workflow init …
1156
+ ```
1157
+
1158
+ **The division of labour, honestly stated up front:** `dz` AUTHORS, GATES and READS loops — it never
1159
+ RUNS one. Execution belongs to a host with the `Workflow({scriptPath})` runtime, which today means
1160
+ **Claude Code**. So: author anywhere (Codex included), run under Claude Code.
1161
+
1162
+ #### Step 1 — scaffold the plan shape closest to your scenario
1163
+
1164
+ ```bash
1165
+ dz workflow init --name competitor-digest --pattern fanout --o digest.plan.json
1166
+ # → wrote digest.plan.json (pattern: fanout)
1167
+ # → Next: edit the TODO prompts, then `dz workflow validate` + `dz workflow render`.
1168
+ ```
1169
+
1170
+ Patterns: `pipeline` (A→B→C), `barrier` (all A, then B), `fanout` (N parallel lanes + a join),
1171
+ `gate` (a checked step with a redo route). The scaffold is a REAL plan with `TODO` prompts — not a
1172
+ template you fight.
1173
+
1174
+ #### Step 2 — make it yours (edit the JSON)
1175
+
1176
+ Fill the `TODO` prompts, name your lanes in `fanouts[].registry` (`["acme","globex","initech"]`),
1177
+ and add the quality gate with a redo route:
1178
+
1179
+ ```json
1180
+ "steps": [ …, { "stepId": "check", "kind": "gate", "phase": "Gate", "deps": ["synthesize"],
1181
+ "prompt": "…answer strictly GATE: PASS or GATE: FAIL as the last line",
1182
+ "budget": { "maxAgents": 2 } } ],
1183
+ "gates": [ { "stepId": "check", "kind": "quality", "failRoute": "synthesize", "maxRedos": 1 } ]
1184
+ ```
1185
+
1186
+ The plan surface is deliberately NARROW and fully enacted: anything the generated loop would not
1187
+ actually perform is REJECTED at validate time with a named `ENACT-*` diagnostic — never silently
1188
+ accepted and ignored.
1189
+
1190
+ #### Step 3 — validate, render, lint (the three gates before any run)
1191
+
1192
+ ```bash
1193
+ dz workflow validate digest.plan.json
1194
+ # → dz workflow validate: OK (digest sha256:6b6e92fc31b781fa…)
1195
+
1196
+ dz workflow render digest.plan.json --o digest.loop.js
1197
+ # → wrote digest.loop.plan.json then digest.loop.js (exec-fp sha256:a095d12e…, blobs: trace)
1198
+
1199
+ dz workflow-lint digest.loop.js --plan digest.loop.plan.json --require-plan
1200
+ # → dz workflow-lint: PASS (0 fail, 1 warn, 0 inconclusive over 17 rules)
1201
+ ```
1202
+
1203
+ `inconclusive` is never a pass, and the rendered script keeps your hand edits across re-renders
1204
+ (USER regions are preserved).
1205
+
1206
+ #### Step 4 — run it (Claude Code), read it back (anywhere)
1207
+
1208
+ In **Claude Code**, paste exactly this:
1209
+
1210
+ > Запусти мой цикл: `Workflow({ scriptPath: 'digest.loop.js', args: { items: ["acme","globex","initech"], traceDir: '<run dir>', runId: 'digest-2026-08-18' } })` — и когда закончит, покажи `dz workflow-trace` по этому прогону.
1211
+
1212
+ What a real run of THIS plan looks like (captured from an offline harness run of the rendered
1213
+ script): `lane:acme, lane:globex, lane:initech` in parallel → `synthesize` → `check` answers
1214
+ `GATE: FAIL` → the redo route re-runs `synthesize` → `check` answers `GATE: PASS` → `COMPLETED`.
1215
+ 13 agent calls, one ledger row, the trace flushed at every settle. Then:
1216
+
1217
+ ```bash
1218
+ dz workflow-trace <runDir> --html report.html # timeline + SEQ invariants over the loop's own trace
1219
+ ```
1220
+
1221
+ #### How to phrase the ASK — Claude Code vs Codex
1222
+
1223
+ **In Claude Code** (it has both `dz` and the `Workflow` runtime — one message does the whole thing):
1224
+
1225
+ > Собери мне цикл под сценарий: «<опиши свой — источники, что делает каждая полоса, как сводить,
1226
+ > какой критерий качества>». Используй `dz workflow init/validate/render/workflow-lint`, покажи мне
1227
+ > план НА СОГЛАСОВАНИЕ до рендера, потом запусти через `Workflow({scriptPath})` и дай ссылку на
1228
+ > `workflow-trace` отчёт.
1229
+
1230
+ The "покажи план до рендера" clause matters: the plan JSON is the one artifact worth your review —
1231
+ prompts, lanes, gate criteria, budgets — and it is small.
1232
+
1233
+ **In Codex** (or any shell-capable agent — AUTHORING only, honestly):
1234
+
1235
+ > In this repo, run `npx @dzhechkov/harness-cli workflow init --name <x> --pattern fanout --o x.plan.json`,
1236
+ > then edit the plan for this scenario: <describe>. Run `workflow validate` and fix every diagnostic
1237
+ > it names, then `workflow render` and `workflow-lint --require-plan`. Do NOT attempt to execute the
1238
+ > generated loop — it runs under Claude Code's `Workflow({scriptPath})` runtime; hand me the green
1239
+ > plan and the rendered script.
1240
+
1241
+ Codex is a fine plan AUTHOR — the validate/lint diagnostics are named and machine-checkable, so its
1242
+ edit loop converges. What it cannot do is run the result: the generated script calls the host's
1243
+ `agent()`/`parallel()` sandbox, which only the Claude Code Workflow runtime provides. A green lint
1244
+ from Codex + a run under Claude Code is a legitimate two-agent split.
1245
+
1246
+ ### Move a run's telemetry to another machine (`workflow-trace export` / `import`)
1247
+
1248
+ A run leaves traces on the machine that produced it. `export` puts one run's telemetry into a single
1249
+ movable file; `import` reconstructs that run under a root you name.
1250
+
1251
+ ```bash
1252
+ dz workflow-trace export --slug my-feature --o my-feature.bundle.json
1253
+ dz workflow-trace import my-feature.bundle.json --into /other/project
1254
+ ```
1255
+
1256
+ **What a bundle carries** — the raw event lines (`trace.jsonl`, `.fa-state/checkpoints.jsonl`), the
1257
+ ledger rows selected for that run, optionally the training pairs, and `runMeta`: WHO ran each stage,
1258
+ read from the harness's own workflow records. **Events, never aggregates.** The one derived value —
1259
+ `attribution`, "which model ran which stage" — travels ALONGSIDE the records it was folded from,
1260
+ marked `derived`, naming its rule and the record ids, so a consumer that disagrees can recompute it.
1261
+ The rule is stated rather than implied: last-writer-wins by timestamp is a CHOICE — a run whose
1262
+ phases used different models has no single honest answer, and the map reports who ran it *last*.
1263
+
1264
+ **The ledger selector** matches a row by `runId`, or by `slug` when the row has no `runId` — because
1265
+ only `loop-run` rows carry a `runId`, so a `runId`-only filter would select nothing for a feature-adr
1266
+ run. The bundle reports rows scanned vs matched, so an empty slice is visibly empty rather than
1267
+ indistinguishable from an absent ledger.
1268
+
1269
+ **Consent does not travel inside the bundle.** Training pairs may contain target-repo code, so
1270
+ including them needs `--include-pairs --yes` at export AND `--with-pairs` at import; a pairs-bearing
1271
+ bundle imported without the flag writes no pair content.
1272
+
1273
+ **Import is fail-closed.** It reconstructs the run's native layout under `--into`, and REFUSES to
1274
+ write into a run directory that already has content unless it is the bundle's own run and `--force`
1275
+ is given; an identity mismatch refuses even under `--force`. A refused import writes nothing — not
1276
+ one file.
1277
+
1278
+ **Degradation is loud and typed**, and exactly one reason asks for action:
1279
+
1280
+ | reason | meaning | action |
1281
+ |---|---|---|
1282
+ | `records-absent` / `no-match` | no harness records, or none for this run | none |
1283
+ | `predates-model-routing` | a genuine older run, from before per-stage model routing | none — this is history |
1284
+ | `unreadable` | a record could not be parsed | look at that record |
1285
+ | `layout-unrecognised` | records exist and parse, but the fields we read are gone | **the harness record layout CHANGED — update the reader** |
1286
+
1287
+ By default a degraded export still succeeds and prints one named line per degraded member; `--strict`
1288
+ makes it exit non-zero so automation fails closed. The split exists because the actionable reason
1289
+ used to fire on normal data — three of thirty-two runs in a real store were simply older than
1290
+ per-stage routing — and an alarm that sounds on normal operation stops being an alarm.
1291
+
1292
+ **Honest scope:** `runMeta` is read from a store this project does not own, so its shape can change
1293
+ without notice. That is precisely what `layout-unrecognised` exists to announce, and why the reader
1294
+ refuses rather than half-parsing: a partially-read record would report a model-blind run as
1295
+ model-known.
1296
+
1144
1297
  **The v1 plan surface is deliberately NARROW and fully enacted** — `dz workflow validate` REJECTS
1145
1298
  (named diagnostics, never a silent no-op) anything the generated loop would not perform: retry
1146
1299
  timing (`initialDelayMs`/`backoffMultiplier`/`maxDelayMs`/`jitter` — v1 retries are immediate;
@@ -1245,6 +1398,8 @@ dz scout [--topics <list>] [--since <date>] [--deep] [--output <file
1245
1398
  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)
1246
1399
  dz workflow-lint <script.js> [--plan <plan.json>] [--require-plan|--legacy] [--json] # 17-rule deterministic gate; exit 0/1/3 — inconclusive is never a pass
1247
1400
  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
1401
+ dz workflow-trace export <run> --o <file> [--include-pairs --yes] [--strict] # one run's telemetry as ONE movable file
1402
+ dz workflow-trace import <bundle> --into <root> [--force] [--with-pairs] # reconstruct that run; fail-closed against clobbering
1248
1403
  dz plugin [--version <ver>]
1249
1404
  dz downloads
1250
1405
  dz migrate [--project <dir>]
@@ -1336,6 +1491,13 @@ exits 1 rather than silently weakening panel arbitration. It is load-bearing in
1336
1491
  24 h. Use `--project <dir>` to pin the panel to a specific project root.
1337
1492
 
1338
1493
  ### Usage estimate (`dz usage`)
1494
+ > **Pin the weekly reset to an ABSOLUTE instant.** `weeklyResetAnchor: "Wed 08:59"` is
1495
+ > server-timezone-relative — measured: the same moment lands a week apart under UTC vs `+03:00`, so
1496
+ > after a real account reset the counter can keep showing the OLD week for hours while printing the
1497
+ > "correct" clock time. Add your offset: `"Wed 08:59 +03:00"` in `.dz/config.json` — the boundary
1498
+ > then never moves with the machine's timezone, and `dz usage` prints the full anchor
1499
+ > (`resets Wed 08:59 +03:00`). Without an offset it warns on every run.
1500
+
1339
1501
 
1340
1502
  `dz usage` prints a READONLY, never-throw ESTIMATE of Claude SESSION and WEEKLY token usage,
1341
1503
  aggregated from your local `~/.claude/projects/**/*.jsonl` transcripts. Weekly counts start at the
@@ -1713,6 +1875,27 @@ dz backlog ship 268d3cb1 --reason "shipped in harness-core 0.3.151"
1713
1875
  # → dz backlog ship: 268d3cb1… new → shipped add a compounding metric …
1714
1876
  # (shipping an already-shipped idea is a SAID no-op, exit 0 — safe in cleanup batches)
1715
1877
  dz backlog drop 9286f5eb --reason "superseded by 268d3cb1" # retire without shipping (→ dropped)
1878
+ dz backlog edit 9286f5eb --text "corrected wording" # rewrite ONE idea's text; every other field
1879
+
1880
+ # feed the learned auto-cost routing from REAL run telemetry, then read its advice
1881
+ dz routing recommend # per-stage args.models suggestion, printed WITH its basis:
1882
+ # n runs, the time window, the grade-floor rule (success ⇔ QE grade ≥ B,
1883
+ # attributed run-level — an inference, and it says so), and every skipped
1884
+ # record with WHY. qe is FORCED to the cross-family of the code pick —
1885
+ # a same-family qe recommendation is unrepresentable, not filtered.
1886
+ dz routing recommend --apply # feed the samples into .dz/routing-outcomes.json (the store the
1887
+ # `auto-cost` plan spec reads) — idempotent by runId: a second apply
1888
+ # feeds 0 and names the skipped runs. Insufficient data is SAID
1889
+ # (cold-start pick + escalation chain), never dressed as a bar-met pick.
1890
+ # (status, effort, goal, uses…) is preserved byte-for-byte,
1891
+ # the previous text lands in .dz/backlog/edits.jsonl, and the
1892
+ # dedup vector is re-embedded in the same bounded form.
1893
+ # If the re-embed FAILS the edit still lands, exits 1, and the
1894
+ # record is MARKED embedStale — dedup then refuses to trust its
1895
+ # similarity (exact-text identity still applies) until
1896
+ # `dz vector reindex` repairs it. The guard sits where the harm
1897
+ # would be (the future duplicate verdict), not in a warning
1898
+ # nobody re-reads. --append adds instead of replacing; --dry-run previews.
1716
1899
  dz backlog reopen 268d3cb1 # changed your mind → back to the pool (new)
1717
1900
  # reopen on an already-new idea is REFUSED (exit 1) — that is almost always the wrong id.
1718
1901
  # Terminal→terminal never happens silently: ship on a dropped idea (or drop on shipped) is refused;
@@ -3222,7 +3405,7 @@ npx @dzhechkov/p-replicator init
3222
3405
 
3223
3406
  ## Status
3224
3407
 
3225
- `v0.4.2` — published on npm. Also available as [Claude Plugin](#claude-plugin). Part of [DZ Harness Hub](https://github.com/djd1m/dz-harness-hub).
3408
+ `v0.4.4` — published on npm. Also available as [Claude Plugin](#claude-plugin). Part of [DZ Harness Hub](https://github.com/djd1m/dz-harness-hub).
3226
3409
 
3227
3410
  ## Claude Plugin
3228
3411
 
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAkbH,2EAA2E;AAC3E,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACjE;AAED,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAAG,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,KACvD;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAyyQ9E,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAoK5E"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAocH,2EAA2E;AAC3E,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACjE;AAED,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAAG,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,KACvD;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AA0lR9E,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAoK5E"}