@dzhechkov/harness-cli 0.7.6 → 0.7.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"known-flags.d.ts","sourceRoot":"","sources":["../src/known-flags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAwR5C,CAAC"}
1
+ {"version":3,"file":"known-flags.d.ts","sourceRoot":"","sources":["../src/known-flags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAyR5C,CAAC"}
@@ -23,6 +23,7 @@ export const KNOWN_CLI_FLAGS = [
23
23
  'audit-dev',
24
24
  'author',
25
25
  'auto',
26
+ 'backfill',
26
27
  'base',
27
28
  'baseline',
28
29
  'body',
@@ -1 +1 @@
1
- {"version":3,"file":"known-flags.js","sourceRoot":"","sources":["../src/known-flags.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"known-flags.js","sourceRoot":"","sources":["../src/known-flags.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dzhechkov/harness-cli",
3
- "version": "0.7.6",
3
+ "version": "0.7.8",
4
4
  "description": "The dz CLI — install AI skills for Claude Code, Codex, OpenCode, Hermes, OpenClaude, GitHub Copilot. 67 commands, 14 presets, 10 platform targets.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -41,22 +41,22 @@
41
41
  "sbom.json"
42
42
  ],
43
43
  "dependencies": {
44
- "@dzhechkov/harness-core": "^0.7.7",
44
+ "@dzhechkov/harness-core": "^0.7.10",
45
+ "@dzhechkov/harness-presets": "^0.5.14",
45
46
  "@dzhechkov/scout": "^0.8.7",
46
47
  "@dzhechkov/skills-devops": "^0.3.13",
47
48
  "@dzhechkov/skills-mcp": "^0.3.6",
48
- "@dzhechkov/harness-presets": "^0.5.14",
49
49
  "@dzhechkov/skills-web3": "^0.2.5",
50
- "@dzhechkov/skills-meta": "^0.9.50",
51
50
  "@dzhechkov/skills-qe": "^0.3.10",
52
- "@dzhechkov/skills-ecc": "^0.1.8",
51
+ "@dzhechkov/skills-meta": "^0.9.51",
53
52
  "@dzhechkov/skills-academic": "^0.3.3",
53
+ "@dzhechkov/skills-ecc": "^0.1.8",
54
54
  "@dzhechkov/skills-news": "^0.1.12",
55
55
  "@dzhechkov/skills-pm": "^0.1.1",
56
56
  "@dzhechkov/skills-taste": "^0.1.1",
57
57
  "@dzhechkov/skills-idea2prd": "^0.1.11",
58
- "@dzhechkov/skills-reverse-engineering": "^0.1.9",
59
58
  "@dzhechkov/skills-presentation-storyteller": "^0.1.10",
59
+ "@dzhechkov/skills-reverse-engineering": "^0.1.9",
60
60
  "@dzhechkov/skills-website-cloner": "^0.1.1"
61
61
  },
62
62
  "devDependencies": {
package/sbom.json CHANGED
@@ -25,7 +25,7 @@
25
25
  "hashes": [
26
26
  {
27
27
  "alg": "SHA-256",
28
- "content": "ba98f7078023a6202a8bf809b83ae20ef44b3d2e1ca14ce0ae5aac83ffd8d51e"
28
+ "content": "a4000a6a9a99f570f9f8955b135fe4742ca74ab9e8c12e7feee0d859c6ab0576"
29
29
  }
30
30
  ]
31
31
  },
@@ -85,7 +85,7 @@
85
85
  "hashes": [
86
86
  {
87
87
  "alg": "SHA-256",
88
- "content": "a8309c4a78c90a1c9db9518a6d07c5fa800c4d39710941a379635a65012e208d"
88
+ "content": "bf5eadb6ea3ed4761fbc8206167c86119467d7dfd28d7e0eee7cd21d30099da6"
89
89
  }
90
90
  ]
91
91
  },
@@ -95,7 +95,7 @@
95
95
  "hashes": [
96
96
  {
97
97
  "alg": "SHA-256",
98
- "content": "8cf050f1bc0b69be1088e58cc5f88f034d2e847fba9ffc87f3cc01121d7d9d74"
98
+ "content": "2cee996fe21ed692135c98b35bf5e56a89f2dc157f681b36c65020fd062b857b"
99
99
  }
100
100
  ]
101
101
  },
@@ -105,7 +105,7 @@
105
105
  "hashes": [
106
106
  {
107
107
  "alg": "SHA-256",
108
- "content": "442b8bbb2ec9f7701ddc2232fc8662956966097d6f46ee1fce49bd06c20db97f"
108
+ "content": "68a41f08452af263f2a378b54e813567b6ba52376f3d0e13567a7eef7dff09e0"
109
109
  }
110
110
  ]
111
111
  },
@@ -125,7 +125,7 @@
125
125
  "hashes": [
126
126
  {
127
127
  "alg": "SHA-256",
128
- "content": "3c0a60e8d8528e1b7df6bf5678388ce14d3ac3d0362bde42bc8e7e712d914dbd"
128
+ "content": "c7856b79fdf6683fe855268191805ed07b1c68f2ff7ac544d46ac3d93e4c94ef"
129
129
  }
130
130
  ]
131
131
  },
@@ -135,7 +135,7 @@
135
135
  "hashes": [
136
136
  {
137
137
  "alg": "SHA-256",
138
- "content": "f117e676f0a120e1314f3cc6416901931d9b389d97052985e1150fa75322b221"
138
+ "content": "27741d68ac996a060df7161b49e372f68e31b990a406f0792c79c474ff17eeca"
139
139
  }
140
140
  ]
141
141
  },
@@ -145,7 +145,7 @@
145
145
  "hashes": [
146
146
  {
147
147
  "alg": "SHA-256",
148
- "content": "718f0380983cb995fad2a30b87af79feb3b0c85656449957bada67956e7e8854"
148
+ "content": "bea844c256e8d947464a4690d2224e4f28d61f433d6e50e5fe26bed3b415ae26"
149
149
  }
150
150
  ]
151
151
  },
@@ -285,7 +285,7 @@
285
285
  "hashes": [
286
286
  {
287
287
  "alg": "SHA-256",
288
- "content": "93d7a002824ed631f1459e922ac2cad08772e06a21777b64eed095933dd0e889"
288
+ "content": "1e6f9fd92734216f3cc392fba9e2ec7899492a6892e5891197bc582c886a38fc"
289
289
  }
290
290
  ]
291
291
  },
@@ -295,7 +295,7 @@
295
295
  "hashes": [
296
296
  {
297
297
  "alg": "SHA-256",
298
- "content": "64928f5f20cc647f86323b4d179c614613635eeb6a553fd7733bf5ebad653d70"
298
+ "content": "fea029550cb557c73eea2d53d94ef09ec99ba353126a5090a658dc219e8ae5df"
299
299
  }
300
300
  ]
301
301
  },
@@ -305,7 +305,7 @@
305
305
  "hashes": [
306
306
  {
307
307
  "alg": "SHA-256",
308
- "content": "3cb9b072c57a3d9562cfb919c11c2b242db215169a4675b9940de46b67e0ccfa"
308
+ "content": "1b46a6093d0ff5713c870b5b7566623c98a33bb212c7f1ba0c56f336620961a3"
309
309
  }
310
310
  ]
311
311
  },
@@ -335,7 +335,7 @@
335
335
  "hashes": [
336
336
  {
337
337
  "alg": "SHA-256",
338
- "content": "4831c7b0121cb2a62464bc4d4844e6037a80f9e2d1e6e0934836bde14a1bffdb"
338
+ "content": "1b1e7ae1af22dd91a6bc92fe1070a5b1d1710e50434abbd8e707f95bbb8f3b14"
339
339
  }
340
340
  ]
341
341
  },
@@ -355,7 +355,7 @@
355
355
  "hashes": [
356
356
  {
357
357
  "alg": "SHA-256",
358
- "content": "c3ef929a729f53ca612ac67b1571d2e8da3681ac5060a1cd16e453e0bb1d4e00"
358
+ "content": "3ea2dd881198881f94675ce14e465ce007e5d987e25b7786f67d9af13e880e22"
359
359
  }
360
360
  ]
361
361
  },
@@ -365,7 +365,7 @@
365
365
  "hashes": [
366
366
  {
367
367
  "alg": "SHA-256",
368
- "content": "f30fb7c6b0795758578a90de19b870c4cde42eb2009fa246051023f499374c09"
368
+ "content": "d8288240b5420ddca2cabdb7b358c7c18ba59724defda951d0dbc61439b6fc4c"
369
369
  }
370
370
  ]
371
371
  },
@@ -405,7 +405,7 @@
405
405
  "hashes": [
406
406
  {
407
407
  "alg": "SHA-256",
408
- "content": "b08dc5d28795419f642df56fbf5cd036a43a06bb1bec5e864f4e538b0a025b13"
408
+ "content": "d123f4f88f51a6b6d283a4fe6d2502bb297ad0de4eb1ef6f99f01b86a7da98a0"
409
409
  }
410
410
  ]
411
411
  }
@@ -20,6 +20,7 @@
20
20
  */
21
21
  export const BOOLEAN_CLI_FLAGS: ReadonlySet<string> = new Set([
22
22
  'affected',
23
+ 'backfill',
23
24
  'all',
24
25
  'allow-same-family',
25
26
  'allow-same-family-qe',
package/src/cli.ts CHANGED
@@ -142,6 +142,12 @@ import {
142
142
  writeFeatureAdrState,
143
143
  computeUsage,
144
144
  deriveCostLedger,
145
+ planLedgerBackfill,
146
+ listCostLedgerRuns,
147
+ resolveLedgerRunId,
148
+ AMBIGUOUS,
149
+ stampCheckpointLine,
150
+ LEDGER_FILL_SOURCE,
145
151
  renderCostLedger,
146
152
  verifyCostLedgerReport,
147
153
  writeCostLedgerJsonl,
@@ -10547,8 +10553,106 @@ async function cmdRecap(options: Map<string, string>, flags: Set<string>, cwd: s
10547
10553
  return 0;
10548
10554
  }
10549
10555
 
10556
+ /**
10557
+ * `dz feature-adr-record --backfill` — fill the run-cost ledger from the host workflow records.
10558
+ *
10559
+ * MEASURED 2026-08-25: of 87 ledger rows, all 20 written automatically carried
10560
+ * `tokens:null, minutes:null, agents:null`, so cost-per-feature was answerable only for the 66 rows
10561
+ * a human retyped. The numbers exist in the host record; the sandboxed workflow simply cannot reach
10562
+ * them. This is that join, run afterwards, on the dz side.
10563
+ */
10564
+ function cmdLedgerBackfill(options: Map<string, string>, flags: Set<string>, cwd: string, write: Write): number {
10565
+ const json = flags.has('json');
10566
+ const apply = flags.has('yes');
10567
+ const ledgerPath = resolve(options.get('project') ?? cwd, '.dz', 'feature-adr', 'run-cost-ledger.jsonl');
10568
+ if (!existsSync(ledgerPath)) {
10569
+ const msg = `dz feature-adr-record --backfill: no ledger at ${ledgerPath}`;
10570
+ write(json ? JSON.stringify({ ok: false, reason: 'no-ledger', path: ledgerPath }) : msg);
10571
+ return 1;
10572
+ }
10573
+ const raw = readFileSync(ledgerPath, 'utf-8');
10574
+ const lines = raw.split('\n').filter((l) => l !== '');
10575
+
10576
+ // One host-record lookup per runId, cached: deriveCostLedger walks the transcript tree.
10577
+ type Facts = { tokens: number | null; minutes: number | null; agents: number | null };
10578
+ const cache = new Map<string, Facts | typeof AMBIGUOUS | null>();
10579
+
10580
+ // Only `tokens` is derived, and the scope is narrow on purpose:
10581
+ // • minutes — CostLedgerReport exposes startedTs but no run duration; there is nothing to divide.
10582
+ // • agents — the ledger column means TOTAL agent invocations including infra agents, while the
10583
+ // union of row.agentIds counts only agents that SPENT. Writing the second into the first would
10584
+ // quietly redefine the column, which that column's own writer explicitly warns against.
10585
+ const factsFor = (runId: string): Facts | null => {
10586
+ try {
10587
+ const report = deriveCostLedger({ runId });
10588
+ if (report === null) return null;
10589
+ const tokens = typeof report.recordTotalTokens === 'number' && Number.isFinite(report.recordTotalTokens)
10590
+ ? report.recordTotalTokens
10591
+ : null;
10592
+ return { tokens, minutes: null, agents: null };
10593
+ } catch { return null; }
10594
+ };
10595
+
10596
+ const facts = (key: 'runId' | 'slug', value: string): Facts | typeof AMBIGUOUS | null => {
10597
+ const ck = key + ':' + value;
10598
+ const hit = cache.get(ck);
10599
+ if (hit !== undefined) return hit;
10600
+ let out: Facts | typeof AMBIGUOUS | null = null;
10601
+ if (key === 'runId') {
10602
+ out = factsFor(value);
10603
+ } else {
10604
+ // The slug is the fallback because the sandboxed workflow cannot know its own run id. It is
10605
+ // only usable when it names exactly ONE host run: deriveCostLedger resolves a slug with
10606
+ // `runs.find(...)`, which takes the FIRST match SILENTLY, so a feature run twice would have
10607
+ // one run's spend written into the other run's row and called measured. Count first, refuse
10608
+ // on more than one.
10609
+ try {
10610
+ const matches = listCostLedgerRuns().filter((r) => r.slug === value);
10611
+ if (matches.length > 1) out = AMBIGUOUS;
10612
+ else if (matches.length === 1 && matches[0] !== undefined) out = factsFor(matches[0].runId);
10613
+ } catch { out = null; }
10614
+ }
10615
+ cache.set(ck, out);
10616
+ return out;
10617
+ };
10618
+
10619
+ const plan = planLedgerBackfill({ lines, facts });
10620
+ const unfilled = plan.rows.filter((r) => r.skipped !== null && r.skipped !== 'already-complete');
10621
+
10622
+ if (apply && plan.filledRows > 0) {
10623
+ // Atomic replace: a torn ledger is worse than an unfilled one.
10624
+ const tmp = ledgerPath + '.backfill-tmp';
10625
+ writeFileSync(tmp, plan.lines.join('\n') + '\n', 'utf-8');
10626
+ renameSync(tmp, ledgerPath);
10627
+ }
10628
+
10629
+ if (json) {
10630
+ write(JSON.stringify({
10631
+ ok: true, applied: apply && plan.filledRows > 0, filledRows: plan.filledRows,
10632
+ totalRows: lines.length, disagreements: plan.disagreements,
10633
+ unfilled: unfilled.map((r) => ({ index: r.index, runId: r.runId, reason: r.skipped })),
10634
+ source: LEDGER_FILL_SOURCE, path: ledgerPath,
10635
+ }));
10636
+ return 0;
10637
+ }
10638
+ write('dz feature-adr-record --backfill: SCOPE — only tokens is derived; minutes and agents stay null (the run record exposes no duration, and the agents column means a different quantity than the spend-bearing agent ids). A row without a runId is matched by SLUG, and only when the slug names exactly one run.');
10639
+ write(`dz feature-adr-record --backfill: ${plan.filledRows} of ${lines.length} row(s) fillable from the host record${apply ? ' — WRITTEN' : ' (dry run; pass --yes to write)'}`);
10640
+ for (const d of plan.disagreements) {
10641
+ write(` DISAGREEMENT row ${d.index} ${d.field}: ledger says ${d.existing}, host record says ${d.derived} — the typed number stands, look at this`);
10642
+ }
10643
+ const byReason = new Map<string, number>();
10644
+ for (const r of unfilled) byReason.set(String(r.skipped), (byReason.get(String(r.skipped)) ?? 0) + 1);
10645
+ for (const [reason, n] of byReason) write(` not filled: ${n} row(s) — ${reason}`);
10646
+ return 0;
10647
+ }
10648
+
10550
10649
  function cmdFeatureAdrRecord(options: Map<string, string>, flags: Set<string>, cwd: string, write: Write): number {
10551
10650
  const json = flags.has('json');
10651
+ // `--backfill` is a different verb on the same store: it fills the ledger's null cost fields from
10652
+ // the host's own workflow record. DRY-RUN BY DEFAULT — this rewrites an append-only log, so the
10653
+ // writing form must be asked for (`--yes`), the same contract `dz publish` and `sync-canonical`
10654
+ // already use. See ledger-backfill.ts for the four rules the plan obeys.
10655
+ if (flags.has('backfill')) return cmdLedgerBackfill(options, flags, cwd, write);
10552
10656
  const kind = (options.get('kind') ?? '').trim() as RecordKind;
10553
10657
  const stage = (options.get('stage') ?? '').trim();
10554
10658
  const slug = (options.get('slug') ?? '').trim();
@@ -10592,6 +10696,11 @@ function cmdFeatureAdrRecord(options: Map<string, string>, flags: Set<string>, c
10592
10696
  });
10593
10697
  if (decision.line === null) return emit(decision);
10594
10698
 
10699
+ // Declared out here because the READ-BACK below must verify what was ACTUALLY written, not what
10700
+ // the decision proposed — the ledger path enriches the line with a resolved runId before the
10701
+ // append, and comparing against the pre-enrichment text made every ledger write report
10702
+ // "not-verified" (caught by running it, not by reading it).
10703
+ let lineToWrite = decision.line;
10595
10704
  // MEASURE, never assume: an unwritable target is a LOUD refusal, because a swallowed mkdir failure
10596
10705
  // is exactly how a write becomes a silent no-op (acid case A4).
10597
10706
  try {
@@ -10613,7 +10722,27 @@ function cmdFeatureAdrRecord(options: Map<string, string>, flags: Set<string>, c
10613
10722
  }
10614
10723
 
10615
10724
  try {
10616
- appendFileSync(target, `${decision.line}\n`, 'utf-8');
10725
+ // WRITE-TIME RUN-ID RESOLUTION. The sandboxed workflow cannot know its own run id — it is not in
10726
+ // `args` and not a sandbox global — so 16 of 20 automatic ledger rows carried no join key at all
10727
+ // and nothing could ever be joined to them (MEASURED 2026-08-25). This command runs on the host,
10728
+ // where the records live, so it answers the question the sandbox cannot. Resolving HERE beats
10729
+ // resolving later: right now the run is in flight and is simply the newest for this slug; an hour
10730
+ // from now the same slug may have several and the choice becomes a guess. Refuses on any tie.
10731
+ lineToWrite = decision.line;
10732
+ if (kind === 'ledger') {
10733
+ try {
10734
+ const parsed: unknown = JSON.parse(decision.line);
10735
+ if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
10736
+ const rowObj = parsed as Record<string, unknown>;
10737
+ const resolved = resolveLedgerRunId(rowObj, listCostLedgerRuns());
10738
+ if (resolved !== null) {
10739
+ // Marked, because a resolved run id is our inference, not something the pipeline knew.
10740
+ lineToWrite = JSON.stringify({ ...rowObj, runId: resolved, runIdSource: 'resolved-at-write' });
10741
+ }
10742
+ }
10743
+ } catch { /* resolution is an ENRICHMENT; a failure must never cost the row itself */ }
10744
+ }
10745
+ appendFileSync(target, `${lineToWrite}\n`, 'utf-8');
10617
10746
  } catch (err) {
10618
10747
  return emit({ verdict: 'refused', exit: 2, reason: `the append failed: ${(err as Error).message}`, blocking: false, line: null });
10619
10748
  }
@@ -10627,7 +10756,7 @@ function cmdFeatureAdrRecord(options: Map<string, string>, flags: Set<string>, c
10627
10756
  } catch {
10628
10757
  lastLine = null;
10629
10758
  }
10630
- return emit(decideReadBack(decision.line, lastLine), { target });
10759
+ return emit(decideReadBack(lineToWrite, lastLine), { target });
10631
10760
  }
10632
10761
 
10633
10762
  function cmdAmendmentCheck(options: Map<string, string>, flags: Set<string>, cwd: string, write: Write): number {
@@ -10784,7 +10913,9 @@ function cmdFeatureAdrCheckpoint(options: Map<string, string>, flags: Set<string
10784
10913
  const stateDir = join(featureDir, '.fa-state');
10785
10914
  try {
10786
10915
  mkdirSync(stateDir, { recursive: true });
10787
- appendFileSync(join(stateDir, 'checkpoints.jsonl'), verdict.line + '\n', 'utf-8');
10916
+ // The CLI is the only participant with a clock — the sandboxed workflow has no `Date` — so the
10917
+ // write instant is stamped here, at the append, and nowhere inside the blob-mirrored serializer.
10918
+ appendFileSync(join(stateDir, 'checkpoints.jsonl'), stampCheckpointLine(verdict.line, new Date().toISOString()) + '\n', 'utf-8');
10788
10919
  } catch (err) {
10789
10920
  const reason = err instanceof Error ? err.message : String(err);
10790
10921
  return emit({ ok: false, stage, reason }, 'dz feature-adr checkpoint: write failed — ' + reason, 1);
@@ -23,6 +23,7 @@ export const KNOWN_CLI_FLAGS: readonly string[] = [
23
23
  'audit-dev',
24
24
  'author',
25
25
  'auto',
26
+ 'backfill',
26
27
  'base',
27
28
  'baseline',
28
29
  'body',