@dzhechkov/harness-cli 0.4.2 → 0.4.3
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 +12 -8
- package/README.md +82 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +366 -5
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/sbom.json +17 -7
- package/src/cli.ts +324 -1
package/.dz-manifest.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
11
|
"path": "README.md",
|
|
12
|
-
"sha256": "
|
|
12
|
+
"sha256": "532051106b153c0d9bb7ad2abbeb71d418f88f1bc88e60a35e29cf42162af243"
|
|
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": "
|
|
40
|
+
"sha256": "5bdc153e789ac180543e9b4955ea36666dc27b7238637b05875e048fc49ae30f"
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
"path": "dist/cli.js",
|
|
44
|
-
"sha256": "
|
|
44
|
+
"sha256": "0183c01a98e232554a7460be48ee5fdf59dd3a7d09c719a31b08acc9c6b3528c"
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
"path": "dist/cli.js.map",
|
|
48
|
-
"sha256": "
|
|
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": "
|
|
72
|
+
"sha256": "4a164e5ac53ceec178463c1bdab9f80c1755311d5e4202c1f6234ca22c473b37"
|
|
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": "
|
|
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": "
|
|
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": "
|
|
224
|
+
"signature": "wIo2tChkUqPT1qEHFeLlgbuzL29rRhhpVer4yaDmeuq7CsvdMeYN2QcttC496DwlepKeZ1EE3h2sSoY5w16qAA=="
|
|
221
225
|
}
|
package/README.md
CHANGED
|
@@ -1141,6 +1141,57 @@ 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
|
+
### Move a run's telemetry to another machine (`workflow-trace export` / `import`)
|
|
1145
|
+
|
|
1146
|
+
A run leaves traces on the machine that produced it. `export` puts one run's telemetry into a single
|
|
1147
|
+
movable file; `import` reconstructs that run under a root you name.
|
|
1148
|
+
|
|
1149
|
+
```bash
|
|
1150
|
+
dz workflow-trace export --slug my-feature --o my-feature.bundle.json
|
|
1151
|
+
dz workflow-trace import my-feature.bundle.json --into /other/project
|
|
1152
|
+
```
|
|
1153
|
+
|
|
1154
|
+
**What a bundle carries** — the raw event lines (`trace.jsonl`, `.fa-state/checkpoints.jsonl`), the
|
|
1155
|
+
ledger rows selected for that run, optionally the training pairs, and `runMeta`: WHO ran each stage,
|
|
1156
|
+
read from the harness's own workflow records. **Events, never aggregates.** The one derived value —
|
|
1157
|
+
`attribution`, "which model ran which stage" — travels ALONGSIDE the records it was folded from,
|
|
1158
|
+
marked `derived`, naming its rule and the record ids, so a consumer that disagrees can recompute it.
|
|
1159
|
+
The rule is stated rather than implied: last-writer-wins by timestamp is a CHOICE — a run whose
|
|
1160
|
+
phases used different models has no single honest answer, and the map reports who ran it *last*.
|
|
1161
|
+
|
|
1162
|
+
**The ledger selector** matches a row by `runId`, or by `slug` when the row has no `runId` — because
|
|
1163
|
+
only `loop-run` rows carry a `runId`, so a `runId`-only filter would select nothing for a feature-adr
|
|
1164
|
+
run. The bundle reports rows scanned vs matched, so an empty slice is visibly empty rather than
|
|
1165
|
+
indistinguishable from an absent ledger.
|
|
1166
|
+
|
|
1167
|
+
**Consent does not travel inside the bundle.** Training pairs may contain target-repo code, so
|
|
1168
|
+
including them needs `--include-pairs --yes` at export AND `--with-pairs` at import; a pairs-bearing
|
|
1169
|
+
bundle imported without the flag writes no pair content.
|
|
1170
|
+
|
|
1171
|
+
**Import is fail-closed.** It reconstructs the run's native layout under `--into`, and REFUSES to
|
|
1172
|
+
write into a run directory that already has content unless it is the bundle's own run and `--force`
|
|
1173
|
+
is given; an identity mismatch refuses even under `--force`. A refused import writes nothing — not
|
|
1174
|
+
one file.
|
|
1175
|
+
|
|
1176
|
+
**Degradation is loud and typed**, and exactly one reason asks for action:
|
|
1177
|
+
|
|
1178
|
+
| reason | meaning | action |
|
|
1179
|
+
|---|---|---|
|
|
1180
|
+
| `records-absent` / `no-match` | no harness records, or none for this run | none |
|
|
1181
|
+
| `predates-model-routing` | a genuine older run, from before per-stage model routing | none — this is history |
|
|
1182
|
+
| `unreadable` | a record could not be parsed | look at that record |
|
|
1183
|
+
| `layout-unrecognised` | records exist and parse, but the fields we read are gone | **the harness record layout CHANGED — update the reader** |
|
|
1184
|
+
|
|
1185
|
+
By default a degraded export still succeeds and prints one named line per degraded member; `--strict`
|
|
1186
|
+
makes it exit non-zero so automation fails closed. The split exists because the actionable reason
|
|
1187
|
+
used to fire on normal data — three of thirty-two runs in a real store were simply older than
|
|
1188
|
+
per-stage routing — and an alarm that sounds on normal operation stops being an alarm.
|
|
1189
|
+
|
|
1190
|
+
**Honest scope:** `runMeta` is read from a store this project does not own, so its shape can change
|
|
1191
|
+
without notice. That is precisely what `layout-unrecognised` exists to announce, and why the reader
|
|
1192
|
+
refuses rather than half-parsing: a partially-read record would report a model-blind run as
|
|
1193
|
+
model-known.
|
|
1194
|
+
|
|
1144
1195
|
**The v1 plan surface is deliberately NARROW and fully enacted** — `dz workflow validate` REJECTS
|
|
1145
1196
|
(named diagnostics, never a silent no-op) anything the generated loop would not perform: retry
|
|
1146
1197
|
timing (`initialDelayMs`/`backoffMultiplier`/`maxDelayMs`/`jitter` — v1 retries are immediate;
|
|
@@ -1245,6 +1296,8 @@ dz scout [--topics <list>] [--since <date>] [--deep] [--output <file
|
|
|
1245
1296
|
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
1297
|
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
1298
|
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
|
|
1299
|
+
dz workflow-trace export <run> --o <file> [--include-pairs --yes] [--strict] # one run's telemetry as ONE movable file
|
|
1300
|
+
dz workflow-trace import <bundle> --into <root> [--force] [--with-pairs] # reconstruct that run; fail-closed against clobbering
|
|
1248
1301
|
dz plugin [--version <ver>]
|
|
1249
1302
|
dz downloads
|
|
1250
1303
|
dz migrate [--project <dir>]
|
|
@@ -1336,6 +1389,13 @@ exits 1 rather than silently weakening panel arbitration. It is load-bearing in
|
|
|
1336
1389
|
24 h. Use `--project <dir>` to pin the panel to a specific project root.
|
|
1337
1390
|
|
|
1338
1391
|
### Usage estimate (`dz usage`)
|
|
1392
|
+
> **Pin the weekly reset to an ABSOLUTE instant.** `weeklyResetAnchor: "Wed 08:59"` is
|
|
1393
|
+
> server-timezone-relative — measured: the same moment lands a week apart under UTC vs `+03:00`, so
|
|
1394
|
+
> after a real account reset the counter can keep showing the OLD week for hours while printing the
|
|
1395
|
+
> "correct" clock time. Add your offset: `"Wed 08:59 +03:00"` in `.dz/config.json` — the boundary
|
|
1396
|
+
> then never moves with the machine's timezone, and `dz usage` prints the full anchor
|
|
1397
|
+
> (`resets Wed 08:59 +03:00`). Without an offset it warns on every run.
|
|
1398
|
+
|
|
1339
1399
|
|
|
1340
1400
|
`dz usage` prints a READONLY, never-throw ESTIMATE of Claude SESSION and WEEKLY token usage,
|
|
1341
1401
|
aggregated from your local `~/.claude/projects/**/*.jsonl` transcripts. Weekly counts start at the
|
|
@@ -1713,6 +1773,27 @@ dz backlog ship 268d3cb1 --reason "shipped in harness-core 0.3.151"
|
|
|
1713
1773
|
# → dz backlog ship: 268d3cb1… new → shipped add a compounding metric …
|
|
1714
1774
|
# (shipping an already-shipped idea is a SAID no-op, exit 0 — safe in cleanup batches)
|
|
1715
1775
|
dz backlog drop 9286f5eb --reason "superseded by 268d3cb1" # retire without shipping (→ dropped)
|
|
1776
|
+
dz backlog edit 9286f5eb --text "corrected wording" # rewrite ONE idea's text; every other field
|
|
1777
|
+
|
|
1778
|
+
# feed the learned auto-cost routing from REAL run telemetry, then read its advice
|
|
1779
|
+
dz routing recommend # per-stage args.models suggestion, printed WITH its basis:
|
|
1780
|
+
# n runs, the time window, the grade-floor rule (success ⇔ QE grade ≥ B,
|
|
1781
|
+
# attributed run-level — an inference, and it says so), and every skipped
|
|
1782
|
+
# record with WHY. qe is FORCED to the cross-family of the code pick —
|
|
1783
|
+
# a same-family qe recommendation is unrepresentable, not filtered.
|
|
1784
|
+
dz routing recommend --apply # feed the samples into .dz/routing-outcomes.json (the store the
|
|
1785
|
+
# `auto-cost` plan spec reads) — idempotent by runId: a second apply
|
|
1786
|
+
# feeds 0 and names the skipped runs. Insufficient data is SAID
|
|
1787
|
+
# (cold-start pick + escalation chain), never dressed as a bar-met pick.
|
|
1788
|
+
# (status, effort, goal, uses…) is preserved byte-for-byte,
|
|
1789
|
+
# the previous text lands in .dz/backlog/edits.jsonl, and the
|
|
1790
|
+
# dedup vector is re-embedded in the same bounded form.
|
|
1791
|
+
# If the re-embed FAILS the edit still lands, exits 1, and the
|
|
1792
|
+
# record is MARKED embedStale — dedup then refuses to trust its
|
|
1793
|
+
# similarity (exact-text identity still applies) until
|
|
1794
|
+
# `dz vector reindex` repairs it. The guard sits where the harm
|
|
1795
|
+
# would be (the future duplicate verdict), not in a warning
|
|
1796
|
+
# nobody re-reads. --append adds instead of replacing; --dry-run previews.
|
|
1716
1797
|
dz backlog reopen 268d3cb1 # changed your mind → back to the pool (new)
|
|
1717
1798
|
# reopen on an already-new idea is REFUSED (exit 1) — that is almost always the wrong id.
|
|
1718
1799
|
# Terminal→terminal never happens silently: ship on a dropped idea (or drop on shipped) is refused;
|
|
@@ -3222,7 +3303,7 @@ npx @dzhechkov/p-replicator init
|
|
|
3222
3303
|
|
|
3223
3304
|
## Status
|
|
3224
3305
|
|
|
3225
|
-
`v0.4.
|
|
3306
|
+
`v0.4.3` — published on npm. Also available as [Claude Plugin](#claude-plugin). Part of [DZ Harness Hub](https://github.com/djd1m/dz-harness-hub).
|
|
3226
3307
|
|
|
3227
3308
|
## Claude Plugin
|
|
3228
3309
|
|
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;
|
|
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"}
|
package/dist/cli.js
CHANGED
|
@@ -10,15 +10,15 @@ import { execFileSync, execSync, spawn } from 'node:child_process';
|
|
|
10
10
|
import { createHash } from 'node:crypto';
|
|
11
11
|
import { homedir, tmpdir } from 'node:os';
|
|
12
12
|
import { createRequire } from 'node:module';
|
|
13
|
-
import { createSkill, getSkillInfo, isTargetName, listSkills, runDoctor, runInit, resolvePackageSkillRoots, PACKAGE_SKILL_LAYOUTS, benchmarkSkill, benchmarkSkills, scanMcp, reconcileCapabilities, RECONCILE_BANNER, buildRegistry, discoverSkillPackDirs, checkUpstream, compareSkills, checkAllUpstream, sweepSkillDrift, syncCanonicalSkill, checkUpgrades, discoverPackages, discoverSourcePackages, fetchAllDownloads, filterByCategory, pretrain, recommend, generatePlugin, publishPackages, runSetup, runMigrate, searchRegistry, runSync, runVerify, runInitAgentsMd, runInitGeminiMd, TARGET_NAMES, buildParityMatrix, TARGET_CAPABILITIES, TARGET_SHORT_LABELS, WORKFLOW_TEMPLATES_RETIRED_MESSAGE, parsePlan, isParseErrors, validatePlan, normalizePlan, planDigest, toTraceProjection, renderPlan, mergeRender, lint, lintExitCode, LOOP_BLOBS, parseTrace, assembleTimeline, runInvariants, renderTimelineHtml, importEcc, recordPattern, resolveLearningBackend, storeStats, consolidateSessions, pruneNoisePatterns, lessonDeltaReport, removePatternsByIds, snapshotStore, recallHybrid, teachGuard, mirrorPatternsToVector, mirrorEntriesToVector, patternVectorEntry, readMemoryLearningConfig, promotePatterns, quarantineExpiryCandidates, pruneQuarantinePatterns, clearAgentdbQuarantine, vectorMirrorEnabled, vectorTierStatus, resolveVectorEngine, reindexVectorStore, harmonizeVectorStore, importRvfCheckpoint, statuslineData, writeFeatureAdrState, computeUsage, deriveCostLedger, renderCostLedger, verifyCostLedgerReport, writeCostLedgerJsonl, COST_LEDGER_SCOPE, deriveUsageCalibration, normalizeClaudeUsageModelKey, readUsageLimits, claimCheck, summarize, queryBookKnowledge, loadStorePatternsSync, patternRecordId, loadStoreRecords, recordToPattern, bundleSkills, brainHome, listBrain, promoteProjectToBrain, updateBrainSource, queryBrain, groundPrompt, expandKu, reindexBrainVectors, buildPrimer, exportBrainSlice, importBrainSlice, registerKusToBrain, RECALL_USAGE_LOG_RELATIVE, RECALL_USAGE_LOG_MAX_BYTES, parseRecallUsageLog, buildRecallUsageReport, EVENT_CHAIN_TAIL_BYTES, EMPTY_LOG_TAIL, readTailInfo, appendChainedLines, verifyEventChainText, buildManifest, buildSbom, resolveTrustRoot, decideVerifyPolicy, generateSigningKeypair, evaluateGuard, resolveRules, auditRecord, guardExitCode, DEFAULT_RULES, parsePnpmLockImporters, scannableStubPath,
|
|
13
|
+
import { createSkill, getSkillInfo, isTargetName, listSkills, runDoctor, runInit, resolvePackageSkillRoots, PACKAGE_SKILL_LAYOUTS, benchmarkSkill, benchmarkSkills, scanMcp, reconcileCapabilities, RECONCILE_BANNER, buildRegistry, discoverSkillPackDirs, checkUpstream, compareSkills, checkAllUpstream, sweepSkillDrift, syncCanonicalSkill, checkUpgrades, discoverPackages, discoverSourcePackages, fetchAllDownloads, filterByCategory, pretrain, recommend, generatePlugin, publishPackages, runSetup, runMigrate, searchRegistry, runSync, runVerify, runInitAgentsMd, runInitGeminiMd, TARGET_NAMES, buildParityMatrix, TARGET_CAPABILITIES, TARGET_SHORT_LABELS, WORKFLOW_TEMPLATES_RETIRED_MESSAGE, parsePlan, isParseErrors, validatePlan, normalizePlan, planDigest, toTraceProjection, renderPlan, mergeRender, lint, lintExitCode, LOOP_BLOBS, parseTrace, assembleTimeline, runInvariants, renderTimelineHtml, importEcc, recordPattern, resolveLearningBackend, storeStats, consolidateSessions, pruneNoisePatterns, lessonDeltaReport, removePatternsByIds, snapshotStore, recallHybrid, teachGuard, mirrorPatternsToVector, mirrorEntriesToVector, patternVectorEntry, readMemoryLearningConfig, promotePatterns, quarantineExpiryCandidates, pruneQuarantinePatterns, clearAgentdbQuarantine, vectorMirrorEnabled, vectorTierStatus, resolveVectorEngine, reindexVectorStore, harmonizeVectorStore, importRvfCheckpoint, statuslineData, writeFeatureAdrState, computeUsage, deriveCostLedger, renderCostLedger, verifyCostLedgerReport, writeCostLedgerJsonl, COST_LEDGER_SCOPE, deriveUsageCalibration, normalizeClaudeUsageModelKey, readUsageLimits, parseWeeklyResetAnchor, claimCheck, summarize, queryBookKnowledge, loadStorePatternsSync, patternRecordId, loadStoreRecords, recordToPattern, bundleSkills, brainHome, listBrain, promoteProjectToBrain, updateBrainSource, queryBrain, groundPrompt, expandKu, reindexBrainVectors, buildPrimer, exportBrainSlice, importBrainSlice, registerKusToBrain, RECALL_USAGE_LOG_RELATIVE, RECALL_USAGE_LOG_MAX_BYTES, parseRecallUsageLog, buildRecallUsageReport, EVENT_CHAIN_TAIL_BYTES, EMPTY_LOG_TAIL, readTailInfo, appendChainedLines, verifyEventChainText, buildManifest, buildSbom, resolveTrustRoot, decideVerifyPolicy, generateSigningKeypair, evaluateGuard, resolveRules, auditRecord, guardExitCode, DEFAULT_RULES, parsePnpmLockImporters, scannableStubPath,
|
|
14
14
|
// guard-promotion (feature guard-promotion, scout idea #1)
|
|
15
|
-
assembleCandidates, renderPromotionReport, renderPromotionAdr, normalizePromotionState, nextPromotionState, globMatch, promotionAdrRelPath, DEFAULT_WINDOW_DAYS, DEFAULT_PERIODS, MAX_CONTENT_FETCHES, BUILTIN_COVERAGE, decideProvenance, isInsideTree, signManifest, verifyManifest, listSignablePackFiles, assertKeyOutsideTree, decidePublishGate, collectPackageFacts, planReleaseGates, selectAffectedPackages, classifyGateExecutions, buildFailureIssue, buildReleaseNotes, releaseTagName, firstOutputLine, formatPublishError, MANIFEST_NAME, SBOM_NAME, buildArchitectureMap, renderMapHuman, findArchitectureDrift, renderDriftReport, scanWorkspacePackages, loadSubsystemManifest, loadProductVision, checkFeatureAgainstArchitecture, renderArchCheck, planProjectSkills, guidanceForStage, renderInjectionReport, analyzeCorpus, renderRakeReport, renderCriticSection, rakeAsLesson, rakeReward, DEFAULT_RAKE_THRESHOLDS, streamSessionEvents, findLatestTranscript, detectProcessRakes, buildRetro, renderRetro, retroLessonText, PROCESS_SIGNATURES, RETRO_DOMAIN, scanForSetup, buildSetupPlan, scaffoldFromSpec, renderScaffoldPreview, readExistingForScaffold, assembleChallengeContext, buildChallengeBrief, planDiscriminationCheck, classifyDiscrimination, pickAdversaryModel, CHALLENGE_QUESTIONS, loadOutcomes, renderOutcomes, statsForKey, selectAutoCost, recordProvisional, finalizeOutcome, COST_LADDER, splitScenarios, budgetPlan, selectWinner, proseScopeOk, renderProseDiff, readScenarioIds, DEFAULT_MAX_JUDGE_RUNS, collectDeliveryFacts, planDeliveryCheck, renderDeliveryBrief, classifyDelivery, isUsablePlaneResult, renderDeliveryReview, scanSkillsLayout, parseInitFacts, verifyRegistration, buildContentProbePrompt, classifyContentProbe, renderContentProbe, findNonRegistrableSkillDirs, assembleCompoundingReport,
|
|
15
|
+
assembleCandidates, renderPromotionReport, renderPromotionAdr, normalizePromotionState, nextPromotionState, globMatch, promotionAdrRelPath, DEFAULT_WINDOW_DAYS, DEFAULT_PERIODS, MAX_CONTENT_FETCHES, BUILTIN_COVERAGE, decideProvenance, isInsideTree, signManifest, verifyManifest, listSignablePackFiles, assertKeyOutsideTree, decidePublishGate, collectPackageFacts, planReleaseGates, selectAffectedPackages, classifyGateExecutions, buildFailureIssue, buildReleaseNotes, releaseTagName, firstOutputLine, formatPublishError, MANIFEST_NAME, SBOM_NAME, buildArchitectureMap, renderMapHuman, findArchitectureDrift, renderDriftReport, scanWorkspacePackages, loadSubsystemManifest, loadProductVision, checkFeatureAgainstArchitecture, renderArchCheck, planProjectSkills, guidanceForStage, renderInjectionReport, analyzeCorpus, renderRakeReport, renderCriticSection, rakeAsLesson, rakeReward, DEFAULT_RAKE_THRESHOLDS, streamSessionEvents, findLatestTranscript, detectProcessRakes, buildRetro, renderRetro, retroLessonText, PROCESS_SIGNATURES, RETRO_DOMAIN, scanForSetup, buildSetupPlan, scaffoldFromSpec, renderScaffoldPreview, readExistingForScaffold, assembleChallengeContext, buildChallengeBrief, planDiscriminationCheck, classifyDiscrimination, pickAdversaryModel, CHALLENGE_QUESTIONS, loadOutcomes, renderOutcomes, statsForKey, selectAutoCost, recordProvisional, finalizeOutcome, harvestStageOutcomes, recommendModels, planFeed, GRADE_SUCCESS_FLOOR, COST_LADDER, splitScenarios, budgetPlan, selectWinner, proseScopeOk, renderProseDiff, readScenarioIds, DEFAULT_MAX_JUDGE_RUNS, collectDeliveryFacts, planDeliveryCheck, renderDeliveryBrief, classifyDelivery, isUsablePlaneResult, renderDeliveryReview, scanSkillsLayout, parseInitFacts, verifyRegistration, buildContentProbePrompt, classifyContentProbe, renderContentProbe, findNonRegistrableSkillDirs, assembleCompoundingReport,
|
|
16
16
|
// Cold-vs-warm EPOCH RUNNER (feature epoch-replay) — orchestrates + scores, never calls a model.
|
|
17
17
|
replayableInstances, buildWorkOrder, buildJudgePrompts, unblindJudgments, verifyWorkOrder, isValidMargin, DIGEST_HONEST_SCOPE, scoreEpochReplay, generateMockOutcomes, renderEpochReplayResult, renderWorkOrderSummary, renderJudgePromptsSummary, WORK_ORDER_KIND, DEFAULT_MOCK_N, DEFAULT_MOCK_SEED, scoreRun, renderScorecard, renderCompoundingReport, readReinforcementState, readQuarantineState, registrationExitCode, renderRegistrationReport,
|
|
18
18
|
// Smart Backlog (feature smart-backlog) — goal-directed idea pipeline over the Brain vector engine.
|
|
19
|
-
readBacklogConfig, readIdeas, writeIdeas, ideaId, dedupIdea, readGoalMap, readGoalMapDetailed, parseEffort, ensureBacklogGitignored, isSafeId, alignIdea, mirrorIdeaVector, ensureBacklogEmbedForm, readBacklogEmbedFormVersion, recordAbsorption, DEDUP_EMBED_FORM_VERSION, snapshotIdeas, spinRoulette, rankRoulette, seededRng, eligibleIdeas, stageEnrichment, buildJiraDraft, resolveJiraAdapter, makeBacklogIO, harmonizeBacklog, transitionIdeas, BACKLOG_BACKENDS, applyDomainBoost, DZ_OWNED_TASK_TYPES, applyExportHoldout, DEFAULT_HELD_OUT_DOMAINS, canonicalDomainKey, readAgentdbRowsByTaskType, heldOutAfterOptIn, renderHoldoutNote, renderSharedStoreAdvice, decideVectorExport, countDisplacedByCut, renderDomainBoostNote, renderDomainCutNote, parseReqeDebt, buildReqeBrief, settleReqeDebt, renderReqeList, REQE_SCOPE,
|
|
19
|
+
readBacklogConfig, readIdeas, writeIdeas, ideaId, dedupIdea, readGoalMap, readGoalMapDetailed, parseEffort, ensureBacklogGitignored, isSafeId, alignIdea, mirrorIdeaVector, ensureBacklogEmbedForm, readBacklogEmbedFormVersion, recordAbsorption, DEDUP_EMBED_FORM_VERSION, snapshotIdeas, spinRoulette, rankRoulette, seededRng, eligibleIdeas, stageEnrichment, buildJiraDraft, resolveJiraAdapter, makeBacklogIO, harmonizeBacklog, transitionIdeas, editIdea, clearEmbedStale, BACKLOG_BACKENDS, applyDomainBoost, DZ_OWNED_TASK_TYPES, applyExportHoldout, DEFAULT_HELD_OUT_DOMAINS, canonicalDomainKey, readAgentdbRowsByTaskType, heldOutAfterOptIn, renderHoldoutNote, renderSharedStoreAdvice, decideVectorExport, countDisplacedByCut, renderDomainBoostNote, renderDomainCutNote, parseReqeDebt, buildReqeBrief, settleReqeDebt, renderReqeList, REQE_SCOPE,
|
|
20
20
|
// Mutation gate (feature ha-mutation-gate) — break each named protection, run the suite, require red.
|
|
21
|
-
parseMutationRegistry, applyMutationToText, countFailingTests, classifyBaseline, classifyRunFailure, classifyMutationOutcome, mutationGateExitCode, summarizeMutationResults, renderMutationReport, } from '@dzhechkov/harness-core';
|
|
21
|
+
parseMutationRegistry, applyMutationToText, countFailingTests, classifyBaseline, classifyRunFailure, classifyMutationOutcome, mutationGateExitCode, summarizeMutationResults, renderMutationReport, TRACE_BUNDLE_LEDGER_PATH, TRACE_BUNDLE_SCHEMA, TRACE_BUNDLE_RUN_META_FILE, buildBundle, serializeBundle, parseBundle, planImport, } from '@dzhechkov/harness-core';
|
|
22
22
|
import { getPreset, PRESET_NAMES } from '@dzhechkov/harness-presets';
|
|
23
23
|
import { scanGitHub, analyzeRepo, generateReport, deepAnalyze, scanAllSources, ScoutMemory } from '@dzhechkov/scout';
|
|
24
24
|
const USAGE = `dz - DZ cross-platform harness CLI
|
|
@@ -39,6 +39,8 @@ Usage:
|
|
|
39
39
|
dz workflow blobs [--check] (list/self-check the subsystem blob registry)
|
|
40
40
|
dz workflow-lint <script.js> [--plan <plan.json>] [--require-plan|--legacy] [--json] (layer-1 gate; exit 0/1/3 — inconclusive is never a pass)
|
|
41
41
|
dz workflow-trace <runDir|--slug <s>|--run <id>> [--invariants <plan.json>] [--html <out.html>] [--json] (timeline + SEQ invariant runner)
|
|
42
|
+
dz workflow-trace export <run> --o <file> [--include-pairs --yes] [--strict] (one run's telemetry as ONE movable file: events, not aggregates; degradation is typed and LOUD, --strict fails closed)
|
|
43
|
+
dz workflow-trace import <bundle> --into <root> [--force] [--with-pairs] (reconstruct that run under an explicit root; FAIL-CLOSED — never writes over a run that already has content)
|
|
42
44
|
dz install <npm-pkg> [--target <name>] [--project <dir>] [--force]
|
|
43
45
|
dz bundle [--preset <name> | --select id,id,...] [--out <dir>] [--skills-dir <dir>] [--force] (portable self-contained skill bundles for a generic/LangGraph consumer)
|
|
44
46
|
dz doctor [--project <dir>] [--pubkey <path>] [--require-signing] (health + signature check of installed packs)
|
|
@@ -65,6 +67,8 @@ Usage:
|
|
|
65
67
|
dz backlog roulette [--pick <N>] [--seed <n>] [--commit] [--project <dir>] [--json] (WEIGHTED draw over eligible ideas: alignment^alpha * recencyDecay * 1/effort, seeded; --pick N = ranked shortlist; --commit flips the pick to in-progress)
|
|
66
68
|
dz backlog ship <id> [<id>…] [--reason <t>] [--dry-run] [--project <dir>] [--json] (mark work DONE: new|enriched|in-progress → shipped, removing it from the roulette pool — run it after finishing a task; short id prefixes ok, ambiguous = loud error)
|
|
67
69
|
dz backlog drop <id> [<id>…] [--reason <t>] [--dry-run] [--project <dir>] [--json] (retire an idea: new|enriched|in-progress → dropped)
|
|
70
|
+
dz backlog edit <id> --text "<new>" | --append "<more>" [--dry-run] [--project <dir>] [--json] (rewrite ONE idea's text, preserving every other field; re-embeds the dedup vector, and on a failed re-embed MARKS the record embedStale so dedup refuses to trust it — previous text preserved in .dz/backlog/edits.jsonl)
|
|
71
|
+
dz routing recommend [--tier <t>] [--apply] [--json] (per-stage args.models suggestion from REAL telemetry — harness records + imported run-meta sidecars — printed WITH its basis: n runs, window, the grade-floor rule, skip reasons; qe is FORCED cross-family of code; --apply feeds .dz/routing-outcomes.json idempotently by runId)
|
|
68
72
|
dz backlog reopen <id> [<id>…] [--reason <t>] [--dry-run] [--project <dir>] [--json] (back to the pool: shipped|dropped|in-progress → new)
|
|
69
73
|
dz backlog enrich <id> [--project <dir>] [--json] (stage the idea2prd input scaffold in features/<slug>/ and hand off to the idea2prd-manual skill — the CLI never fabricates a PRD)
|
|
70
74
|
dz backlog jira <id> [--project <dir>] [--json] (draft a Jira issue via the configurable adapter (backlog.jira.adapter: jira-mcp|copilot-mcp|none); none writes an auditable jira-outbox/<id>.json stub)
|
|
@@ -758,7 +762,244 @@ function cmdWorkflowLint(options, flags, cwd, write) {
|
|
|
758
762
|
/** `dz workflow-trace` — timeline + invariant runner over a run's trace.jsonl. Scope is CAPPED
|
|
759
763
|
* (AM-8): <runDir|--slug|--run>, --invariants, --html, --json. NO watch/filter/compare/search/
|
|
760
764
|
* retention/access-control — adding one needs an ADR amendment (the surface test pins this). */
|
|
765
|
+
/** The tool version stamped into a bundle's provenance. Unknown is honest; a throw is not. */
|
|
766
|
+
function traceBundleToolVersion() {
|
|
767
|
+
try {
|
|
768
|
+
const req = createRequire(import.meta.url);
|
|
769
|
+
const pkg = req('../package.json');
|
|
770
|
+
return typeof pkg.version === 'string' ? pkg.version : 'unknown';
|
|
771
|
+
}
|
|
772
|
+
catch {
|
|
773
|
+
return 'unknown';
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
/** Resolve a run the SAME three ways the timeline reader does — positional, --slug, --run — so a
|
|
777
|
+
* bundle can never address a run by a scheme the rest of the command does not understand. */
|
|
778
|
+
function resolveTraceRun(options, cwd, positional) {
|
|
779
|
+
const slug = options.get('slug');
|
|
780
|
+
const runId = options.get('run');
|
|
781
|
+
if (positional !== undefined && positional !== '') {
|
|
782
|
+
return { runDir: resolve(cwd, positional), slug: slug ?? null, runId: runId ?? null };
|
|
783
|
+
}
|
|
784
|
+
if (slug !== undefined)
|
|
785
|
+
return { runDir: resolve(cwd, 'features', slug), slug, runId: runId ?? null };
|
|
786
|
+
if (runId !== undefined)
|
|
787
|
+
return { runDir: resolve(cwd, '.dz', 'loop-trace', runId), slug: slug ?? null, runId };
|
|
788
|
+
return null;
|
|
789
|
+
}
|
|
790
|
+
/** Every harness workflow record under the project, already parsed. An unparseable file is SKIPPED
|
|
791
|
+
* here rather than guessed at — the pure half answers `unreadable` from what it is handed. */
|
|
792
|
+
function readHarnessRecords(cwd) {
|
|
793
|
+
const out = [];
|
|
794
|
+
const base = join(cwd, 'roam', 'claude-state');
|
|
795
|
+
if (!existsSync(base))
|
|
796
|
+
return out;
|
|
797
|
+
let sessions = [];
|
|
798
|
+
try {
|
|
799
|
+
sessions = readdirSync(base);
|
|
800
|
+
}
|
|
801
|
+
catch {
|
|
802
|
+
return out;
|
|
803
|
+
}
|
|
804
|
+
for (const session of sessions) {
|
|
805
|
+
const dir = join(base, session, 'workflows');
|
|
806
|
+
if (!existsSync(dir))
|
|
807
|
+
continue;
|
|
808
|
+
let names = [];
|
|
809
|
+
try {
|
|
810
|
+
names = readdirSync(dir);
|
|
811
|
+
}
|
|
812
|
+
catch {
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
for (const name of names) {
|
|
816
|
+
if (!name.endsWith('.json'))
|
|
817
|
+
continue;
|
|
818
|
+
try {
|
|
819
|
+
out.push(JSON.parse(readFileSync(join(dir, name), 'utf-8')));
|
|
820
|
+
}
|
|
821
|
+
catch { /* skipped, never guessed */ }
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
return out;
|
|
825
|
+
}
|
|
826
|
+
/** `dz workflow-trace export` — one run's telemetry as ONE movable file. */
|
|
827
|
+
function cmdWorkflowTraceExport(options, flags, cwd, write) {
|
|
828
|
+
const out = options.get('o') ?? options.get('out');
|
|
829
|
+
const run = resolveTraceRun(options, cwd, options.get('_positional_1'));
|
|
830
|
+
if (run === null || out === undefined || out === '') {
|
|
831
|
+
write('dz workflow-trace export: usage — dz workflow-trace export <runDir|--slug <s>|--run <id>> --o <file> [--include-pairs --yes] [--strict]');
|
|
832
|
+
return 1;
|
|
833
|
+
}
|
|
834
|
+
const wantPairs = flags.has('include-pairs');
|
|
835
|
+
if (wantPairs && !flags.has('yes')) {
|
|
836
|
+
write('dz workflow-trace export: --include-pairs needs --yes — training pairs may carry TARGET-REPO CODE and full prompts, so shipping them off this machine is a second, explicit decision');
|
|
837
|
+
return 1;
|
|
838
|
+
}
|
|
839
|
+
const readSlot = (abs, origin) => existsSync(abs)
|
|
840
|
+
? { present: true, member: { origin, content: readFileSync(abs, 'utf-8') } }
|
|
841
|
+
: { present: false, reason: `absent on disk at ${origin}` };
|
|
842
|
+
const trace = readSlot(join(run.runDir, 'trace.jsonl'), 'trace.jsonl');
|
|
843
|
+
const checkpoints = readSlot(join(run.runDir, '.fa-state', 'checkpoints.jsonl'), '.fa-state/checkpoints.jsonl');
|
|
844
|
+
const ledgerAbs = join(cwd, TRACE_BUNDLE_LEDGER_PATH);
|
|
845
|
+
// An empty trailing line is not a malformed row — hand the pure half only real lines, or the
|
|
846
|
+
// bundle reports a phantom parse failure on every well-formed ledger (MEASURED on first run).
|
|
847
|
+
const ledgerLines = existsSync(ledgerAbs)
|
|
848
|
+
? readFileSync(ledgerAbs, 'utf-8').split('\n').filter((line) => line.trim() !== '')
|
|
849
|
+
: null;
|
|
850
|
+
const pairFiles = [];
|
|
851
|
+
if (wantPairs && run.slug !== null) {
|
|
852
|
+
const pairDir = join(cwd, '.dz', 'fa-training', run.slug);
|
|
853
|
+
if (existsSync(pairDir)) {
|
|
854
|
+
for (const name of readdirSync(pairDir)) {
|
|
855
|
+
if (!name.endsWith('.jsonl'))
|
|
856
|
+
continue;
|
|
857
|
+
pairFiles.push({ origin: `.dz/fa-training/${run.slug}/${name}`, content: readFileSync(join(pairDir, name), 'utf-8') });
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
const bundle = buildBundle({
|
|
862
|
+
sourceRoot: cwd,
|
|
863
|
+
runAddress: options.get('_positional_1') ?? (run.slug !== null ? `--slug ${run.slug}` : `--run ${String(run.runId)}`),
|
|
864
|
+
slug: run.slug,
|
|
865
|
+
runId: run.runId,
|
|
866
|
+
toolVersion: traceBundleToolVersion(),
|
|
867
|
+
createdAt: null,
|
|
868
|
+
trace,
|
|
869
|
+
checkpoints,
|
|
870
|
+
ledgerLines,
|
|
871
|
+
...(ledgerLines === null ? { ledgerReason: `no ledger at ${TRACE_BUNDLE_LEDGER_PATH}` } : {}),
|
|
872
|
+
includePairs: wantPairs,
|
|
873
|
+
pairFiles: wantPairs ? pairFiles : null,
|
|
874
|
+
records: readHarnessRecords(cwd),
|
|
875
|
+
});
|
|
876
|
+
try {
|
|
877
|
+
mkdirSync(dirname(resolve(cwd, out)), { recursive: true });
|
|
878
|
+
writeFileSync(resolve(cwd, out), serializeBundle(bundle));
|
|
879
|
+
}
|
|
880
|
+
catch (err) {
|
|
881
|
+
write(`dz workflow-trace export: could not write ${out} — ${err instanceof Error ? err.message : String(err)}`);
|
|
882
|
+
return 1;
|
|
883
|
+
}
|
|
884
|
+
// Degradation is NAMED, never silent — and exactly one reason is actionable.
|
|
885
|
+
let degraded = 0;
|
|
886
|
+
if (!bundle.trace.present) {
|
|
887
|
+
degraded++;
|
|
888
|
+
write(`dz workflow-trace export: trace absent — ${bundle.trace.reason}`);
|
|
889
|
+
}
|
|
890
|
+
if (!bundle.checkpoints.present) {
|
|
891
|
+
degraded++;
|
|
892
|
+
write(`dz workflow-trace export: checkpoints absent — ${bundle.checkpoints.reason}`);
|
|
893
|
+
}
|
|
894
|
+
if (!bundle.ledger.present) {
|
|
895
|
+
degraded++;
|
|
896
|
+
write(`dz workflow-trace export: ledger absent — ${String(bundle.ledger.reason)}`);
|
|
897
|
+
}
|
|
898
|
+
if (!bundle.runMeta.resolved) {
|
|
899
|
+
degraded++;
|
|
900
|
+
const reason = bundle.runMeta.reason;
|
|
901
|
+
write(reason === 'layout-unrecognised'
|
|
902
|
+
? 'dz workflow-trace export: runMeta UNRESOLVED (layout-unrecognised) — the harness workflow-record layout CHANGED; update the reader in harness-core/src/trace-bundle.ts. This is the one reason that needs action'
|
|
903
|
+
: `dz workflow-trace export: runMeta unresolved (${reason}) — no action needed, this is history or absence, not a layout change`);
|
|
904
|
+
}
|
|
905
|
+
write(`dz workflow-trace export: wrote ${out} (${bundle.schema}; ledger rows scanned ${bundle.ledger.scanned}, matched ${bundle.ledger.matched}, malformed ${bundle.ledger.malformed})`);
|
|
906
|
+
if (degraded > 0 && flags.has('strict')) {
|
|
907
|
+
write(`dz workflow-trace export: --strict — ${degraded} member(s) degraded, failing closed`);
|
|
908
|
+
return 1;
|
|
909
|
+
}
|
|
910
|
+
return 0;
|
|
911
|
+
}
|
|
912
|
+
/** `dz workflow-trace import` — reconstruct a run under an explicit root. FAIL-CLOSED. */
|
|
913
|
+
function cmdWorkflowTraceImport(options, flags, cwd, write) {
|
|
914
|
+
const file = options.get('_positional_1');
|
|
915
|
+
const into = options.get('into');
|
|
916
|
+
if (file === undefined || file === '' || into === undefined || into === '') {
|
|
917
|
+
write('dz workflow-trace import: usage — dz workflow-trace import <bundle.json> --into <root> [--force] [--with-pairs]');
|
|
918
|
+
return 1;
|
|
919
|
+
}
|
|
920
|
+
const abs = resolve(cwd, file);
|
|
921
|
+
if (!existsSync(abs)) {
|
|
922
|
+
write(`dz workflow-trace import: no such bundle: ${file}`);
|
|
923
|
+
return 1;
|
|
924
|
+
}
|
|
925
|
+
const parsed = parseBundle(readFileSync(abs, 'utf-8'));
|
|
926
|
+
if (!parsed.ok) {
|
|
927
|
+
write(parsed.reason === 'unknown-schema'
|
|
928
|
+
? `dz workflow-trace import: REFUSED — unknown bundle schema "${parsed.found}"; this dz reads ${TRACE_BUNDLE_SCHEMA}. A future bundle is never best-effort parsed`
|
|
929
|
+
: parsed.reason === 'member-shape'
|
|
930
|
+
? `dz workflow-trace import: REFUSED — member "${parsed.member}" fails its shape check; a partial import is never left behind`
|
|
931
|
+
: 'dz workflow-trace import: REFUSED — the bundle is not parseable JSON');
|
|
932
|
+
return 1;
|
|
933
|
+
}
|
|
934
|
+
const bundle = parsed.bundle;
|
|
935
|
+
const root = resolve(cwd, into);
|
|
936
|
+
const runDirRel = bundle.provenance.slug !== null ? join('features', bundle.provenance.slug) : join('.dz', 'loop-trace', String(bundle.provenance.runId ?? 'unknown-run'));
|
|
937
|
+
const runDirAbs = join(root, runDirRel);
|
|
938
|
+
let runDirHasContent = false;
|
|
939
|
+
try {
|
|
940
|
+
runDirHasContent = existsSync(runDirAbs) && readdirSync(runDirAbs).length > 0;
|
|
941
|
+
}
|
|
942
|
+
catch {
|
|
943
|
+
runDirHasContent = false;
|
|
944
|
+
}
|
|
945
|
+
let runIdentity = null;
|
|
946
|
+
const metaAbs = join(runDirAbs, TRACE_BUNDLE_RUN_META_FILE);
|
|
947
|
+
if (existsSync(metaAbs)) {
|
|
948
|
+
try {
|
|
949
|
+
const meta = JSON.parse(readFileSync(metaAbs, 'utf-8'));
|
|
950
|
+
runIdentity = {
|
|
951
|
+
slug: typeof meta.slug === 'string' ? meta.slug : null,
|
|
952
|
+
runId: typeof meta.runId === 'string' ? meta.runId : null,
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
catch {
|
|
956
|
+
runIdentity = null;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
const plan = planImport(bundle, {
|
|
960
|
+
runDir: runDirRel,
|
|
961
|
+
existingPaths: [],
|
|
962
|
+
runDirHasContent,
|
|
963
|
+
runIdentity,
|
|
964
|
+
force: flags.has('force'),
|
|
965
|
+
withPairs: flags.has('with-pairs'),
|
|
966
|
+
bundleName: basename(abs),
|
|
967
|
+
});
|
|
968
|
+
if (!plan.ok) {
|
|
969
|
+
for (const refusal of plan.refusals)
|
|
970
|
+
write(`dz workflow-trace import: REFUSED ${refusal.path} — ${refusal.reason}`);
|
|
971
|
+
write('dz workflow-trace import: nothing was written');
|
|
972
|
+
return 1;
|
|
973
|
+
}
|
|
974
|
+
let written = 0;
|
|
975
|
+
const failures = [];
|
|
976
|
+
for (const entry of plan.writes) {
|
|
977
|
+
const target = join(root, entry.path);
|
|
978
|
+
try {
|
|
979
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
980
|
+
writeFileSync(target, entry.content);
|
|
981
|
+
written++;
|
|
982
|
+
}
|
|
983
|
+
catch (err) {
|
|
984
|
+
failures.push(`${entry.path} — ${err instanceof Error ? err.message : String(err)}`);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
// Report what ACTUALLY landed, computed from the writes, not from the plan.
|
|
988
|
+
for (const skipped of plan.refusals)
|
|
989
|
+
write(`dz workflow-trace import: skipped ${skipped.path} — ${skipped.reason}`);
|
|
990
|
+
for (const failure of failures)
|
|
991
|
+
write(`dz workflow-trace import: FAILED ${failure}`);
|
|
992
|
+
write(`dz workflow-trace import: wrote ${written} of ${plan.writes.length} member(s) under ${into}`);
|
|
993
|
+
return failures.length > 0 ? 1 : 0;
|
|
994
|
+
}
|
|
761
995
|
function cmdWorkflowTrace(options, flags, cwd, write) {
|
|
996
|
+
// Two subcommands ride the SAME run addressing as the timeline reader (AM-8 keeps the surface
|
|
997
|
+
// capped; these are bundle transport, not new query verbs).
|
|
998
|
+
const sub = options.get('_positional_0');
|
|
999
|
+
if (sub === 'export')
|
|
1000
|
+
return cmdWorkflowTraceExport(options, flags, cwd, write);
|
|
1001
|
+
if (sub === 'import')
|
|
1002
|
+
return cmdWorkflowTraceImport(options, flags, cwd, write);
|
|
762
1003
|
const runDirArg = options.get('_positional_0');
|
|
763
1004
|
const slug = options.get('slug');
|
|
764
1005
|
const runId = options.get('run');
|
|
@@ -1599,7 +1840,16 @@ function cmdUsage(options, optionLists, flags, cwd, write) {
|
|
|
1599
1840
|
const s = u.sessionPct === null ? 'n/a' : '~' + u.sessionPct + '%';
|
|
1600
1841
|
const binding = hasModelLimits && u.weeklyBindingModel !== undefined ? ' ' + u.weeklyBindingModel + '-bound' : '';
|
|
1601
1842
|
const w = u.weeklyPct === null ? 'n/a' : '~' + u.weeklyPct + '%' + binding;
|
|
1602
|
-
|
|
1843
|
+
// The weekly reset is WEEKLY: print the anchor verbatim (weekday + offset), not a bare clock
|
|
1844
|
+
// time — 'resets 08:59' reads as daily and hides the weekday (idea c8513be9: the bare form
|
|
1845
|
+
// misread a Monday reading as '41 minutes after the boundary' when the boundary was Wednesday's).
|
|
1846
|
+
const weeklyAnchorLabel = typeof lim.weeklyResetAnchor === 'string' && lim.weeklyResetAnchor !== ''
|
|
1847
|
+
? lim.weeklyResetAnchor
|
|
1848
|
+
: clock(u.weeklyResetsAt);
|
|
1849
|
+
write('usage: session ' + s + ' (resets ' + clock(u.sessionResetsAt) + ') · week ' + w + ' (resets ' + weeklyAnchorLabel + ') · estimated');
|
|
1850
|
+
if (typeof lim.weeklyResetAnchor === 'string' && parseWeeklyResetAnchor(lim.weeklyResetAnchor)?.offsetMinutes === undefined) {
|
|
1851
|
+
write(' ⚠ weeklyResetAnchor has NO utc offset — the boundary follows the SERVER timezone, not your account\'s true reset instant (measured: the same moment lands a week apart under UTC vs +03:00). Pin it: "' + lim.weeklyResetAnchor + ' +03:00" (your offset) in .dz/config.json');
|
|
1852
|
+
}
|
|
1603
1853
|
// re-QE debt surfacing (backlog 6b40e667): the moment someone checks usage is the moment a
|
|
1604
1854
|
// usage-switched self-review debt should be visible. Best-effort — never breaks the contract.
|
|
1605
1855
|
try {
|
|
@@ -8385,6 +8635,55 @@ async function cmdBacklog(options, flags, cwd, write) {
|
|
|
8385
8635
|
write(' (dry-run — nothing written; re-run without --dry-run to apply)');
|
|
8386
8636
|
return report.ok ? 0 : 1;
|
|
8387
8637
|
}
|
|
8638
|
+
// ── edit — replace/extend ONE idea's text (idea 1fde7bf6). The verb exists because a hand-edit
|
|
8639
|
+
// does not re-embed: the dedup vector keeps describing the OLD text. editIdea owns the text change
|
|
8640
|
+
// and MARKS the record embedStale; this layer owns the async re-embed and clears the mark ONLY
|
|
8641
|
+
// after the vector tier confirms. A failed re-embed is loud here AND enforced at the harm point:
|
|
8642
|
+
// classifyDedup refuses vector candidacy for a marked record (ADR-001).
|
|
8643
|
+
if (sub === 'edit') {
|
|
8644
|
+
const id = options.get('_positional_1');
|
|
8645
|
+
if (id === undefined)
|
|
8646
|
+
return emitErr('an idea id is required: dz backlog edit <id> --text "<new>" | --append "<more>" [--dry-run]');
|
|
8647
|
+
const report = editIdea(projectRoot, id, {
|
|
8648
|
+
...(options.get('text') !== undefined ? { text: options.get('text') } : {}),
|
|
8649
|
+
...(options.get('append') !== undefined ? { append: options.get('append') } : {}),
|
|
8650
|
+
dryRun: flags.has('dry-run'),
|
|
8651
|
+
});
|
|
8652
|
+
let embed = 'skipped';
|
|
8653
|
+
if (report.ok && report.written && report.id !== undefined) {
|
|
8654
|
+
const updated = readIdeas(projectRoot).find((i) => i.id === report.id);
|
|
8655
|
+
if (updated !== undefined) {
|
|
8656
|
+
const mirror = await mirrorIdeaVector(projectRoot, updated);
|
|
8657
|
+
if (mirror.mirrored > 0 && mirror.error === undefined && clearEmbedStale(projectRoot, report.id))
|
|
8658
|
+
embed = 'ok';
|
|
8659
|
+
else
|
|
8660
|
+
embed = 'stale';
|
|
8661
|
+
}
|
|
8662
|
+
else
|
|
8663
|
+
embed = 'stale';
|
|
8664
|
+
}
|
|
8665
|
+
if (json) {
|
|
8666
|
+
write(JSON.stringify({ verb: 'edit', ...report, embed, exitCode: report.ok ? (embed === 'stale' ? 1 : 0) : 1 }, null, 2));
|
|
8667
|
+
return report.ok ? (embed === 'stale' ? 1 : 0) : 1;
|
|
8668
|
+
}
|
|
8669
|
+
for (const e of report.errors)
|
|
8670
|
+
write(`dz backlog edit: ${e}`);
|
|
8671
|
+
if (report.ok && report.id !== undefined) {
|
|
8672
|
+
if (!report.written && !report.dryRun)
|
|
8673
|
+
write(`dz backlog edit: ${report.id} — text unchanged, nothing to do`);
|
|
8674
|
+
else
|
|
8675
|
+
write(`dz backlog edit${report.dryRun ? ' (dry-run)' : ''}: ${report.id}\n was: ${report.previousText?.slice(0, 100)}\n now: ${report.newText?.slice(0, 100)}`);
|
|
8676
|
+
if (report.written) {
|
|
8677
|
+
if (embed === 'ok')
|
|
8678
|
+
write(' vector re-embedded (dedup form) — the record is fully consistent');
|
|
8679
|
+
else
|
|
8680
|
+
write(' ⚠ vector NOT re-embedded — the record is MARKED embedStale: dedup will refuse to trust its similarity until `dz vector reindex` repairs it (the text edit itself landed)');
|
|
8681
|
+
}
|
|
8682
|
+
if (report.dryRun)
|
|
8683
|
+
write(' (dry-run — nothing written; re-run without --dry-run to apply)');
|
|
8684
|
+
}
|
|
8685
|
+
return report.ok ? (embed === 'stale' ? 1 : 0) : 1;
|
|
8686
|
+
}
|
|
8388
8687
|
if (sub === 'enrich') {
|
|
8389
8688
|
const id = options.get('_positional_1');
|
|
8390
8689
|
if (id === undefined)
|
|
@@ -8486,6 +8785,68 @@ function cmdRouting(options, flags, cwd, write) {
|
|
|
8486
8785
|
repoRoot = execSync('git rev-parse --show-toplevel', { cwd, encoding: 'utf-8' }).trim() || cwd;
|
|
8487
8786
|
}
|
|
8488
8787
|
catch { /* not git */ }
|
|
8788
|
+
// ── recommend — harvest real telemetry, print per-stage picks WITH THE BASIS, optionally feed the
|
|
8789
|
+
// store (a9c3dd5c fn 3, ADR-001). Sources: live harness records + imported run-meta.json sidecars.
|
|
8790
|
+
if (options.get('_positional_0') === 'recommend') {
|
|
8791
|
+
const records = readHarnessRecords(repoRoot);
|
|
8792
|
+
// imported runs (trace-bundle ADR-001 D4): run-meta sidecars carry runMeta.records of the SAME shape
|
|
8793
|
+
for (const base of [join(repoRoot, 'features'), join(repoRoot, '.dz', 'loop-trace')]) {
|
|
8794
|
+
if (!existsSync(base))
|
|
8795
|
+
continue;
|
|
8796
|
+
let names = [];
|
|
8797
|
+
try {
|
|
8798
|
+
names = readdirSync(base);
|
|
8799
|
+
}
|
|
8800
|
+
catch {
|
|
8801
|
+
continue;
|
|
8802
|
+
}
|
|
8803
|
+
for (const name of names) {
|
|
8804
|
+
const sidecar = join(base, name, 'run-meta.json');
|
|
8805
|
+
if (!existsSync(sidecar))
|
|
8806
|
+
continue;
|
|
8807
|
+
try {
|
|
8808
|
+
const meta = JSON.parse(readFileSync(sidecar, 'utf-8'));
|
|
8809
|
+
if (meta.runMeta?.resolved === true && Array.isArray(meta.runMeta.records))
|
|
8810
|
+
records.push(...meta.runMeta.records);
|
|
8811
|
+
}
|
|
8812
|
+
catch { /* an unreadable sidecar contributes nothing — counted below as noResult */ }
|
|
8813
|
+
}
|
|
8814
|
+
}
|
|
8815
|
+
const harvest = harvestStageOutcomes(records);
|
|
8816
|
+
const rec = recommendModels(harvest, { ...(options.get('tier') !== undefined ? { tier: options.get('tier') } : {}) });
|
|
8817
|
+
if (flags.has('apply')) {
|
|
8818
|
+
const fedPath = join(repoRoot, '.dz', 'routing-fed.json');
|
|
8819
|
+
let alreadyFed = [];
|
|
8820
|
+
try {
|
|
8821
|
+
alreadyFed = JSON.parse(readFileSync(fedPath, 'utf-8'));
|
|
8822
|
+
}
|
|
8823
|
+
catch { /* first apply */ }
|
|
8824
|
+
const plan = planFeed(harvest.samples, alreadyFed);
|
|
8825
|
+
for (const sample of plan.toFeed)
|
|
8826
|
+
finalizeOutcome(repoRoot, sample.stage, sample.tier, sample.model, sample.success);
|
|
8827
|
+
try {
|
|
8828
|
+
mkdirSync(join(repoRoot, '.dz'), { recursive: true });
|
|
8829
|
+
writeFileSync(fedPath, JSON.stringify(plan.fedAfter));
|
|
8830
|
+
}
|
|
8831
|
+
catch (e) {
|
|
8832
|
+
write(`dz routing recommend: fed ${plan.toFeed.length} sample(s) but could NOT persist the fed-set — a re-apply WILL double-count: ${e.message}`);
|
|
8833
|
+
return 1;
|
|
8834
|
+
}
|
|
8835
|
+
write(`dz routing recommend --apply: fed ${plan.toFeed.length} sample(s) into .dz/routing-outcomes.json${plan.skippedRuns.length > 0 ? `; skipped ${plan.skippedRuns.length} already-fed run(s) (idempotent by runId)` : ''}`);
|
|
8836
|
+
}
|
|
8837
|
+
if (flags.has('json')) {
|
|
8838
|
+
write(JSON.stringify({ recommendation: rec, exitCode: 0 }, null, 2));
|
|
8839
|
+
return 0;
|
|
8840
|
+
}
|
|
8841
|
+
write(`dz routing recommend — args.models suggestion from ${rec.basis.runsUsed} run(s)${rec.basis.window !== null ? ` (${rec.basis.window.min.slice(0, 10)}..${rec.basis.window.max.slice(0, 10)})` : ''}:`);
|
|
8842
|
+
for (const s of rec.perStage) {
|
|
8843
|
+
write(` ${s.stage.padEnd(14)} ${s.spec.padEnd(20)} ${s.insufficientData ? '[insufficient data — cold-start pick]' : '[quality bar met]'} ${s.pick.evidence}`);
|
|
8844
|
+
}
|
|
8845
|
+
write(` basis: ${rec.basis.rule}`);
|
|
8846
|
+
write(` basis: skipped records — no result ${rec.basis.skipped.noResult}, no modelsUsed ${rec.basis.skipped.noModels} (predates model routing — history, not an error), no grade ${rec.basis.skipped.noGrade}, unknown model ${rec.basis.skipped.unknownModel}`);
|
|
8847
|
+
write(` basis: ${rec.basis.crossFamilyNote}`);
|
|
8848
|
+
return 0;
|
|
8849
|
+
}
|
|
8489
8850
|
const stage = options.get('stage');
|
|
8490
8851
|
const tier = options.get('tier');
|
|
8491
8852
|
const model = options.get('model');
|