@dzhechkov/harness-cli 0.8.5 → 0.8.6

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": "e74440eef6812dbeaa550bdbbd80483e4cf4f38c22857260c534726f14ebb680"
12
+ "sha256": "4a54061280f2c8a3173a07e43492a73a927fdb98dccf6c1acd933735e5b9aca5"
13
13
  },
14
14
  {
15
15
  "path": "dist/bin.d.ts",
@@ -49,15 +49,15 @@
49
49
  },
50
50
  {
51
51
  "path": "dist/cli.d.ts.map",
52
- "sha256": "a5d8d7579320ddbb30016d48c6d74a8ecf5628d90efe3925ed8ecb06cde7477c"
52
+ "sha256": "d02abab0b734f19ac7e75d7a28b25ae43cbfc025911128c5848f8e81ba2bf01f"
53
53
  },
54
54
  {
55
55
  "path": "dist/cli.js",
56
- "sha256": "bee6b00ec45da2d49c60ff85a675f989100bf703ce05cbaa5e4f730417161ccc"
56
+ "sha256": "3ac75ba951cac7ae985b2b7407ffb2645ef36d98f44897d9094b00f4db20517f"
57
57
  },
58
58
  {
59
59
  "path": "dist/cli.js.map",
60
- "sha256": "e68711d595224a75faf9527aa6ec186b0c37ef551470c9df3690b780efe9cfd5"
60
+ "sha256": "c7c308fbc3d596905c25760ba8fa337656f42de06d0565f73ad7c230979a514d"
61
61
  },
62
62
  {
63
63
  "path": "dist/core-compat.d.ts",
@@ -133,7 +133,7 @@
133
133
  },
134
134
  {
135
135
  "path": "package.json",
136
- "sha256": "11df4bc14008b0e123b91bf5dc395589a147c24de3afd1f7beb58093ec753325"
136
+ "sha256": "2549a7e988b7a04071319dbbad8917d4231cd648720e70485a594ee9e3bd4aa1"
137
137
  },
138
138
  {
139
139
  "path": "src/bin.ts",
@@ -145,7 +145,7 @@
145
145
  },
146
146
  {
147
147
  "path": "src/cli.ts",
148
- "sha256": "6d402ac972df1c859da5996a65dbd94699f5df5080217cf6fce9af354fe92699"
148
+ "sha256": "5ff3270b1b82fe28e15d09f37d8099468102abc926512303c7ef76e6c75ecf0d"
149
149
  },
150
150
  {
151
151
  "path": "src/core-compat.ts",
@@ -165,5 +165,5 @@
165
165
  }
166
166
  ]
167
167
  },
168
- "signature": "Of546A8oarnbK/BQr+Vl9yXG6+PcIKe7Ymi8e1KFVk9GkZIw8SMy+JWG3RFiYyzuR1Ya3+XbJAbAJwIkCZ74Dg=="
168
+ "signature": "Wy4woPmqLQEeqD66Hc6eyZZDUc9jyxDeSA9pKbsIZyMLu+d0VJELsfj4hOiC5bQXKRFZqyOACrHd/UBB2ugVDA=="
169
169
  }
package/README.md CHANGED
@@ -1374,7 +1374,7 @@ Get the whole set with `dz init --target claude-code --preset meta`, or pick one
1374
1374
  | [@dzhechkov/p-replicator](https://www.npmjs.com/package/@dzhechkov/p-replicator) | `npx @dzhechkov/p-replicator init` | AI product development (/replicate pipeline) |
1375
1375
  | [@dzhechkov/health-advisor](https://www.npmjs.com/package/@dzhechkov/health-advisor) | `npx @dzhechkov/health-advisor init` | Medical AI (25 skills) |
1376
1376
  | [@dzhechkov/skills-bto](https://www.npmjs.com/package/@dzhechkov/skills-bto) | `npx @dzhechkov/skills-bto init` | BTO benchmarking (Build-Test-Optimize) |
1377
- | [@dzhechkov/skills-feature-adr](https://www.npmjs.com/package/@dzhechkov/skills-feature-adr) | `npx @dzhechkov/skills-feature-adr init` | 11-step feature pipeline |
1377
+ | [@dzhechkov/skills-feature-adr](https://www.npmjs.com/package/@dzhechkov/skills-feature-adr) | `npx @dzhechkov/skills-feature-adr init` | 11-step feature pipeline with two-axis `primary` + per-family `budget` routing (`normal`/`eco`/`hybrid`); explicit stage models keep precedence |
1378
1378
  | [@dzhechkov/skills-edu-site](https://www.npmjs.com/package/@dzhechkov/skills-edu-site) | `npx @dzhechkov/skills-edu-site init` | Gamified edu site generator |
1379
1379
  | [@dzhechkov/skills-transcript-site](https://www.npmjs.com/package/@dzhechkov/skills-transcript-site) | `npx @dzhechkov/skills-transcript-site init` | Transcript → interactive site |
1380
1380
  | [@dzhechkov/skills-analyst-manual](https://www.npmjs.com/package/@dzhechkov/skills-analyst-manual) | `npx @dzhechkov/skills-analyst-manual init` | 3-phase analyst composite |
@@ -1619,13 +1619,14 @@ run pkg-audit-1; sources: trace, ledger
1619
1619
  The gate passed, the inventory (52 directories = 49 canonical + 2 private + 1 non-package) was
1620
1620
  cross-checked deterministically against the filesystem, and the stale test was re-pinned to it.
1621
1621
 
1622
- **And the run surfaced a real defect kept here on purpose.** Read the trace again: the registry
1623
- names six chunks, but only `chunk-1..3` ever dispatched — `maxFanout: 3` **silently truncated** the
1624
- other three lanes, and the run stayed green only because the synthesize agent noticed the gap and
1625
- audited the missing chunks with its own tools. That is exactly what the trace is for: it shows what
1626
- your loop *did*, not what the plan promised. (Filed in this repo's backlog: over-registry items must
1627
- become a named failure or a loud trace event, never a silent drop. Until then: keep
1628
- `fanouts[].registry.length maxFanout`, and read the trace after the first run of any new plan.)
1622
+ **And this recorded run is now the regression proof for a fixed defect.** Read the trace again: the
1623
+ registry names six chunks, but only `chunk-1..3` dispatched — the old renderer treated
1624
+ `maxFanout: 3` as a prefix cap, and the run survived only because synthesize audited the missing
1625
+ chunks itself. Current plans treat `maxFanout` as concurrency: all six dispatch through a three-wide
1626
+ window. Deliberate sampling is explicit (`overflow: "truncate"` plus a non-blank
1627
+ `truncateReason`) and emits a banner, a `[fanout-truncated]` stderr line, and a trace receipt. The
1628
+ reader now grades this historical trace `region-dispatch-completeness:fan = FAIL` and names
1629
+ `chunk-4..6`; a legacy projection without registry evidence is `INCONCLUSIVE`, never a guessed pass.
1629
1630
 
1630
1631
  #### How to phrase the ASK — Claude Code vs Codex
1631
1632
 
@@ -2525,10 +2526,14 @@ dz routing recommend # per-stage args.models suggestion, printed
2525
2526
  # attributed run-level — an inference, and it says so), and every skipped
2526
2527
  # record with WHY. qe is FORCED to the cross-family of the code pick —
2527
2528
  # a same-family qe recommendation is unrepresentable, not filtered.
2529
+ # A `store:` line always says current / STALE / UNFED and names
2530
+ # the remedy; JSON carries basis.freshness + basis.unfed.
2528
2531
  dz routing recommend --apply # feed the samples into .dz/routing-outcomes.json (the store the
2529
2532
  # `auto-cost` plan spec reads) — idempotent by runId: a second apply
2530
2533
  # feeds 0 and names the skipped runs. Insufficient data is SAID
2531
2534
  # (cold-start pick + escalation chain), never dressed as a bar-met pick.
2535
+ # `dz guard --op publish` warns SOFT as routing-store-stale when
2536
+ # harvested runIds are still missing from that store.
2532
2537
  # (status, effort, goal, uses…) is preserved byte-for-byte,
2533
2538
  # the previous text lands in .dz/backlog/edits.jsonl, and the
2534
2539
  # dedup vector is re-embedded in the same bounded form.
@@ -3498,7 +3503,7 @@ invariants: HARD rules **block** the operation, SOFT rules warn. Zero config nee
3498
3503
  cover the known rakes; `.dz/guard.json` (via `dz guard --init`) exists only if you want to tune a severity
3499
3504
  or disable a rule.
3500
3505
  ```bash
3501
- dz guard check --op publish # no-workspace-star · no-skill-drift · no-secrets · readme-consistency · readme-first · skills-registrable · lockfile-in-sync · no-stubs · review-round · licence-hold
3506
+ dz guard check --op publish # no-workspace-star · no-skill-drift · no-secrets · readme-consistency · readme-first · skills-registrable · lockfile-in-sync · marketplace-parity · no-stubs · review-round · licence-hold
3502
3507
  dz guard check --op teach --text "the fix: export sk-abc..." # → BLOCK (exit 1): looks like a credential
3503
3508
  dz guard log # append-only audit: every verdict + every forced override
3504
3509
  ```
@@ -3533,6 +3538,13 @@ zero specifiers, and then report every real dependency as "not recorded". The ru
3533
3538
  `.dz/guard.json` cannot promote it to HARD, since a parser designed to admit "I may not understand this
3534
3539
  file" must never be able to block a publish.
3535
3540
 
3541
+ The SOFT, publish-only `marketplace-parity` rule regenerates `.claude-plugin/plugin.json` and
3542
+ `.claude-plugin/marketplace.json` from the live registry in an isolated temp directory and compares
3543
+ their composition with the published showcase. It deliberately excludes `version`, which remains an
3544
+ operator field, and never blocks publication; composition drift warns with the exact repair command
3545
+ `dz plugin --version <published>`. A repository with no complete `.claude-plugin/` showcase is out of
3546
+ scope: the rule records an informational skip reason and emits no violation.
3547
+
3536
3548
  **`review-round` (HARD, publish)** — a package that bumps its version AND changes source must bring a
3537
3549
  GRADED `features/*/08_qe_report.md` in the same change. The gate had eleven rules and not one asked
3538
3550
  whether anyone but the author had read the code; the cost is on the record (health-advisor slice H:
@@ -4051,7 +4063,7 @@ The self-learning loop is only compounding if every run reads and writes the **s
4051
4063
  dz vector harmonize --apply --project <brain> # optional: collapse near-dup paraphrases
4052
4064
  ```
4053
4065
 
4054
- - **Route each stage to an optimal model — `args.models`.** The `feature-adr` workflow takes an optional per-stage map (`{router, requirements, research, adr, ideation, ddd, architecture, plan, code, qe, fleet}`); each value a **spec** — Claude `fable|opus|sonnet|haiku` or Codex `codex[:id[:reasoning]]` (ids incl. `gpt-5.5`, `gpt-5.6`) — backed by a proven default table. The **load-bearing default**: when `models.qe` is unset, QE auto-routes to the *other family than the coder* (a model that writes code must not also self-QE — cross-model review catches what self-review misses), falling back to a Claude reviewer if Codex is absent (never blocks). `args.models[stage]` wins over the legacy `planner`/`coder`/`qeReviewer`/`codexModel` knobs; omitting it is byte-identical to today; the result reports `modelsUsed`. Adding a new Codex id is a data-only allowlist edit (gpt-5.6-ready).
4066
+ - **Route by primary family, budget, or stage.** The `feature-adr` workflow accepts `primary=claude|codex`, per-family `budget=normal|eco|hybrid`, and an optional per-stage `models` map over `{router, requirements, research, adr, ideation, ddd, architecture, plan, code, qe, fleet}`. Each stage value is a **spec** — Claude `fable|opus|sonnet|haiku` or Codex `codex[:id[:reasoning]]` (ids incl. `gpt-5.5`, `gpt-5.6`). The **load-bearing default** routes unset QE to the other family than the actual coder; Codex QE uses `sol:high` under normal/hybrid and `terra:medium` under eco, with a loud Claude fallback if the selected id is unavailable. Precedence is `models[stage]` > legacy `planner`/`coder`/`qeReviewer`/`codexModel` knobs > primary/budget defaults > session-inherited. Omitting every routing knob keeps the inherited session path; the result reports `modelsUsed`.
4055
4067
  ```js
4056
4068
  // Claude codes, Codex independently QEs (cross-model by construction):
4057
4069
  Workflow({ scriptPath: '.claude/workflows/feature-adr.js',
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;AASH,OAAO,EAAoC,KAAK,EAAa,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAK3G,OAAO,EA4CL,iBAAiB,EAmBjB,KAAK,oBAAoB,EAmWzB,KAAK,YAAY,EAwElB,MAAM,yBAAyB,CAAC;AAkBjC,4FAA4F;AAC5F,eAAO,MAAM,WAAW,EAAE,SAAS,MAAM,EAexC,CAAC;AA8IF,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;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C;;;;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;AAo1F9E;;;;;;;GAOG;AACH,wBAAsB,yBAAyB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CA0BnF;AAiuHD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,mBAAmB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAQzG;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,oBAAoB,EAAE,KAAK,SAAkB,GAAG,iBAAiB,CAoB1G;AAwGD,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,mBAAmB,EAC1B,IAAI,GAAE,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC,CAAC,KAAK,oBAA+C,EAC3G,KAAK,SAAa,GACjB,iBAAiB,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAA;CAAE,CAK/D;AA2mED,wFAAwF;AACxF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAElG;AA4BD,gFAAgF;AAChF,wBAAgB,qBAAqB,IAAI;IAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,MAAM,CAAA;CAAE,CAM7I;AAusED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EAAE,EACd,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,GAAG,GAAE,MAAsB,EAC3B,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,eAAe,CAAC,CAI1B;AA8CD,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAEvG;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,EAAE;IACJ,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IACxC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,WAAW,CAAC;IAChC,kGAAkG;IAClG,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9B,GACA,OAAO,CAAC,eAAe,CAAC,CA8D1B;AA+kFD,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAoP5E"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -14,11 +14,12 @@ import { execFile, execFileSync, execSync, spawn, spawnSync } from 'node:child_p
14
14
  import { createHash, randomBytes } from 'node:crypto';
15
15
  import { homedir, tmpdir } from 'node:os';
16
16
  import { createRequire } from 'node:module';
17
+ import { isDeepStrictEqual } from 'node:util';
17
18
  import { createSkill, getSkillInfo, listSkillsDetailed, formatSkillLoadFailures, formatSkillApplyFailures, resolveTargetName, formatTargetProblem, formatTargetAliasNote, TARGET_NAMES_SORTED, runDoctor, runInit, resolvePackageSkillRoots, PACKAGE_SKILL_LAYOUTS, benchmarkSkill, benchmarkSkills, scanMcp, reconcileCapabilities, RECONCILE_BANNER, buildRegistry, discoverSkillPackDirs, discoverVerifiablePackDirs, checkUpstream, compareSkills, checkAllUpstream, sweepSkillDrift, syncCanonicalSkill, checkUpgrades, discoverPackages, discoverSourcePackages, fetchAllDownloads, filterByCategory, pretrain, recommend, generatePlugin, publishPackages, runSetup, runMigrate, searchRegistry, runSync, runVerify, runInitAgentsMd, runInitGeminiMd, runSyncAgentsPolicy, runSyncCodexHooks, resolveCodexHome, withNamedLockSync,
18
19
  // dz workflow run (feature dz-workflow-run): the pure scheduler + the dispatch adapters.
19
20
  TRACE_RUNID_RE, WF_RUN_OWNER_HOST, preflight, runWorkflow, makeClaudePDispatcher, makeCodexExecDispatcher, NamedLockTimeoutError, NamedLockCompromisedError, POLICY_SOURCES, detectPolicyDrift, hasPolicyFence, TARGET_NAMES, buildParityMatrix, downgradeForStaleEvidence, findStaleTranscriptEvidence, TARGET_CAPABILITIES, TARGET_SHORT_LABELS, WORKFLOW_TEMPLATES_RETIRED_MESSAGE, parsePlan, isParseErrors, validatePlan, normalizePlan, planDigest, toTraceProjection, renderPlan, mergeRender, lint, lintExitCode, LOOP_BLOBS, parseTrace, assembleTimeline, runInvariants, deriveAttestation, stampAttestation, corroborate, NOT_WITNESSED, 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, planLedgerBackfill, listCostLedgerRuns, resolveLedgerRunId, AMBIGUOUS, stampCheckpointLine, LEDGER_FILL_SOURCE, renderCostLedger, verifyCostLedgerReport, writeCostLedgerJsonl, COST_LEDGER_SCOPE, deriveUsageCalibration, normalizeClaudeUsageModelKey, readUsageLimits, parseWeeklyResetAnchor, claimCheck, summarize, BUNDLED_SLOP_REGISTRY_URL, DEFAULT_SLOP_CONFIG, parseSlopRegistry, slopLint, validateSlopLintConfig, queryBookKnowledge, loadStorePatternsSync, patternRecordId, loadStoreRecords, recordToPattern, bundleSkills, brainHome, listBrain, bookKbPath, 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,
20
21
  // guard-promotion (feature guard-promotion, scout idea #1)
21
- 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, classifyExecutionEvidence, 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, declaredPluginSurface, parseInitFacts, verifyRegistration, buildContentProbePrompt, classifyContentProbe, renderContentProbe, findNonRegistrableSkillDirs, assembleCompoundingReport, buildDeadwoodReport, compactCmdUsageIfNeeded, measureCmdUsageDepthDays, recordCommandInvocation, resolveCmdUsageRoot, renderDeadwoodReport, CMD_USAGE_LOG_RELATIVE, banditStats, narrowBanditReport, renderBanditHealth,
22
+ 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, classifyExecutionEvidence, pickAdversaryModel, CHALLENGE_QUESTIONS, loadOutcomes, renderOutcomes, statsForKey, selectAutoCost, recordProvisional, finalizeOutcome, harvestStageOutcomes, recommendModels, planFeed, unfedRuns, GRADE_SUCCESS_FLOOR, COST_LADDER, splitScenarios, budgetPlan, selectWinner, proseScopeOk, renderProseDiff, readScenarioIds, DEFAULT_MAX_JUDGE_RUNS, collectDeliveryFacts, planDeliveryCheck, renderDeliveryBrief, classifyDelivery, isUsablePlaneResult, renderDeliveryReview, scanSkillsLayout, declaredPluginSurface, parseInitFacts, verifyRegistration, buildContentProbePrompt, classifyContentProbe, renderContentProbe, findNonRegistrableSkillDirs, assembleCompoundingReport, buildDeadwoodReport, compactCmdUsageIfNeeded, measureCmdUsageDepthDays, recordCommandInvocation, resolveCmdUsageRoot, renderDeadwoodReport, CMD_USAGE_LOG_RELATIVE, banditStats, narrowBanditReport, renderBanditHealth,
22
23
  // Cold-vs-warm EPOCH RUNNER (feature epoch-replay) — orchestrates + scores, never calls a model.
23
24
  replayableInstances, buildWorkOrder, buildJudgePrompts, unblindJudgments, verifyWorkOrder, isValidMargin, DIGEST_HONEST_SCOPE, scoreEpochReplay, generateMockOutcomes, renderEpochReplayResult, renderWorkOrderSummary, renderJudgePromptsSummary, WORK_ORDER_KIND, DEFAULT_MOCK_N, DEFAULT_MOCK_SEED, scoreRun, readQeGrade, recapWindow, decideHorizon, withinWindow, buildRecap, renderRecap, parseSourceManifest, tgPostHtmlIssues, tgVisibleLength, decideTgSend, TG_TEXT_LIMIT, countRecallEventsForRun, unknownFlagNotice, mirrorWriterExplanation, appendRecallUsage, closenessLine, anyAboveFloor, decideNameCheck, renderNameCheck, exportedNamesIn, dispatchedCommandsIn, decideSourceProvenance, renderSourceProvenance, REFUSED_HORIZONS, renderScorecard, renderCompoundingReport, readReinforcementState, readQuarantineState, registrationExitCode, renderRegistrationReport,
24
25
  // Smart Backlog (feature smart-backlog) — goal-directed idea pipeline over the Brain vector engine.
@@ -115,7 +116,7 @@ Usage:
115
116
  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)
116
117
  dz backlog drop <id> [<id>…] [--reason <t>] [--dry-run] [--project <dir>] [--json] (retire an idea: new|enriched|in-progress → dropped)
117
118
  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)
118
- 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)
119
+ 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 + current/STALE/UNFED store receipt; qe is FORCED cross-family of code; --apply feeds .dz/routing-outcomes.json idempotently by runId)
119
120
  dz backlog reopen <id> [<id>…] [--reason <t>] [--dry-run] [--project <dir>] [--json] (back to the pool: shipped|dropped|in-progress → new)
120
121
  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)
121
122
  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)
@@ -7224,6 +7225,101 @@ function gatherReadmeCounts(root) {
7224
7225
  function gatherGuardFacts(op, root, text, storeCap) {
7225
7226
  const facts = { op };
7226
7227
  if (op === 'publish') {
7228
+ // Advisory I/O: unreadable telemetry or fed state is absence of evidence, never a fabricated
7229
+ // stale finding and never a publish blocker.
7230
+ try {
7231
+ const routing = readRoutingTelemetry(root);
7232
+ facts['routingFreshness'] = { unfedRunIds: unfedRuns(routing.harvest.samples, routing.alreadyFed) };
7233
+ }
7234
+ catch { /* fail-open: routing-store-stale is SOFT and needs gathered evidence */ }
7235
+ // marketplace-parity: regenerate the two published showcase manifests from the LIVE registry,
7236
+ // but redirect every write to a unique temp root. Version is operator-owned, so pinning the
7237
+ // published value into generatePlugin makes the comparison composition-only by construction.
7238
+ try {
7239
+ const showcaseDir = join(root, '.claude-plugin');
7240
+ if (!existsSync(showcaseDir)) {
7241
+ facts['marketplaceParity'] = { applicable: false };
7242
+ }
7243
+ else {
7244
+ const pluginPath = join(showcaseDir, 'plugin.json');
7245
+ const marketplacePath = join(showcaseDir, 'marketplace.json');
7246
+ const hasPlugin = existsSync(pluginPath);
7247
+ const hasMarketplace = existsSync(marketplacePath);
7248
+ const manifestFailures = [];
7249
+ const readManifest = (path, file) => {
7250
+ try {
7251
+ return JSON.parse(readFileSync(path, 'utf8'));
7252
+ }
7253
+ catch (error) {
7254
+ const message = error instanceof Error ? error.message : String(error);
7255
+ manifestFailures.push({ file, error: message.replace(/\s+/g, ' ').slice(0, 240) });
7256
+ return undefined;
7257
+ }
7258
+ };
7259
+ const versionOf = (manifest) => {
7260
+ if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest))
7261
+ return undefined;
7262
+ const version = manifest['version'];
7263
+ return typeof version === 'string' && version !== '' ? version : undefined;
7264
+ };
7265
+ const publishedPlugin = hasPlugin ? readManifest(pluginPath, 'plugin.json') : undefined;
7266
+ const publishedMarketplace = hasMarketplace ? readManifest(marketplacePath, 'marketplace.json') : undefined;
7267
+ const marketplaceRecord = publishedMarketplace && typeof publishedMarketplace === 'object' && !Array.isArray(publishedMarketplace)
7268
+ ? publishedMarketplace
7269
+ : undefined;
7270
+ const marketplacePlugins = marketplaceRecord !== undefined && Array.isArray(marketplaceRecord['plugins'])
7271
+ ? marketplaceRecord['plugins']
7272
+ : [];
7273
+ const publishedVersion = versionOf(publishedPlugin)
7274
+ ?? versionOf(publishedMarketplace)
7275
+ ?? versionOf(marketplacePlugins[0]);
7276
+ if (manifestFailures.length > 0) {
7277
+ facts['marketplaceParity'] = {
7278
+ applicable: true,
7279
+ manifestFailures,
7280
+ ...(publishedVersion !== undefined ? { publishedVersion } : {}),
7281
+ };
7282
+ }
7283
+ else if (hasPlugin !== hasMarketplace) {
7284
+ facts['marketplaceParity'] = {
7285
+ applicable: true,
7286
+ onlyOnePresent: true,
7287
+ ...(publishedVersion !== undefined ? { publishedVersion } : {}),
7288
+ };
7289
+ }
7290
+ else if (!hasPlugin) {
7291
+ facts['marketplaceParity'] = { applicable: false };
7292
+ }
7293
+ else {
7294
+ let scratch;
7295
+ try {
7296
+ const registry = buildRegistry(root);
7297
+ scratch = mkdtempSync(join(tmpdir(), 'dz-guard-marketplace-'));
7298
+ const generated = generatePlugin(scratch, registry, { version: publishedVersion });
7299
+ const freshPlugin = JSON.parse(readFileSync(generated.pluginJsonPath, 'utf8'));
7300
+ const freshMarketplace = JSON.parse(readFileSync(generated.marketplaceJsonPath, 'utf8'));
7301
+ facts['marketplaceParity'] = {
7302
+ applicable: true,
7303
+ diverged: !isDeepStrictEqual(publishedPlugin, freshPlugin)
7304
+ || !isDeepStrictEqual(publishedMarketplace, freshMarketplace),
7305
+ ...(publishedVersion !== undefined ? { publishedVersion } : {}),
7306
+ };
7307
+ }
7308
+ catch {
7309
+ facts['marketplaceParity'] = {
7310
+ applicable: true,
7311
+ regenerateFailed: true,
7312
+ ...(publishedVersion !== undefined ? { publishedVersion } : {}),
7313
+ };
7314
+ }
7315
+ finally {
7316
+ if (scratch !== undefined)
7317
+ rmSync(scratch, { recursive: true, force: true });
7318
+ }
7319
+ }
7320
+ }
7321
+ }
7322
+ catch { /* fail-open only on unexpected showcase-discovery failure; manifest read/parse errors are structured violations above */ }
7227
7323
  // agents-md-policy-sync: fixed registry, no tree walk. The pure detector
7228
7324
  // recomputes every expected hash from current source text; this gatherer
7229
7325
  // only supplies bytes. Any unexpected gather failure omits the fact, and
@@ -14016,6 +14112,44 @@ async function cmdBacklog(options, flags, cwd, write) {
14016
14112
  write(' harmonize [--apply][--threshold 0-1] batch semantic dedup of the backlog');
14017
14113
  return sub === undefined ? 0 : 1;
14018
14114
  }
14115
+ /** Read the recommender's existing telemetry planes plus its idempotency receipt. One adapter is
14116
+ * shared by `recommend` and the publish advisory so their freshness definitions cannot drift. */
14117
+ function readRoutingTelemetry(repoRoot) {
14118
+ const records = readHarnessRecords(repoRoot);
14119
+ for (const base of [join(repoRoot, 'features'), join(repoRoot, '.dz', 'loop-trace')]) {
14120
+ if (!existsSync(base))
14121
+ continue;
14122
+ let names = [];
14123
+ try {
14124
+ names = readdirSync(base);
14125
+ }
14126
+ catch {
14127
+ continue;
14128
+ }
14129
+ for (const name of names) {
14130
+ const sidecar = join(base, name, 'run-meta.json');
14131
+ if (!existsSync(sidecar))
14132
+ continue;
14133
+ try {
14134
+ const meta = JSON.parse(readFileSync(sidecar, 'utf-8'));
14135
+ if (meta.runMeta?.resolved === true && Array.isArray(meta.runMeta.records))
14136
+ records.push(...meta.runMeta.records);
14137
+ }
14138
+ catch { /* an unreadable sidecar contributes no asserted record */ }
14139
+ }
14140
+ }
14141
+ let alreadyFed = [];
14142
+ const fedPath = join(repoRoot, '.dz', 'routing-fed.json');
14143
+ if (existsSync(fedPath)) {
14144
+ const parsed = JSON.parse(readFileSync(join(repoRoot, '.dz', 'routing-fed.json'), 'utf-8'));
14145
+ if (!Array.isArray(parsed))
14146
+ throw new Error('.dz/routing-fed.json must be a JSON array of runIds');
14147
+ if (parsed.some((v) => typeof v !== 'string'))
14148
+ throw new Error('.dz/routing-fed.json contains a non-string runId');
14149
+ alreadyFed = parsed;
14150
+ }
14151
+ return { harvest: harvestStageOutcomes(records), alreadyFed };
14152
+ }
14019
14153
  /**
14020
14154
  * `dz routing` — inspect the learned cost-optimal routing outcome store (feature learned-cost-routing). Shows
14021
14155
  * what `args.models[stage]='auto-cost'` currently believes per (stage, complexity-tier, model): gated
@@ -14032,40 +14166,22 @@ function cmdRouting(options, flags, cwd, write) {
14032
14166
  // ── recommend — harvest real telemetry, print per-stage picks WITH THE BASIS, optionally feed the
14033
14167
  // store (a9c3dd5c fn 3, ADR-001). Sources: live harness records + imported run-meta.json sidecars.
14034
14168
  if (options.get('_positional_0') === 'recommend') {
14035
- const records = readHarnessRecords(repoRoot);
14036
- // imported runs (trace-bundle ADR-001 D4): run-meta sidecars carry runMeta.records of the SAME shape
14037
- for (const base of [join(repoRoot, 'features'), join(repoRoot, '.dz', 'loop-trace')]) {
14038
- if (!existsSync(base))
14039
- continue;
14040
- let names = [];
14041
- try {
14042
- names = readdirSync(base);
14043
- }
14044
- catch {
14045
- continue;
14046
- }
14047
- for (const name of names) {
14048
- const sidecar = join(base, name, 'run-meta.json');
14049
- if (!existsSync(sidecar))
14050
- continue;
14051
- try {
14052
- const meta = JSON.parse(readFileSync(sidecar, 'utf-8'));
14053
- if (meta.runMeta?.resolved === true && Array.isArray(meta.runMeta.records))
14054
- records.push(...meta.runMeta.records);
14055
- }
14056
- catch { /* an unreadable sidecar contributes nothing — counted below as noResult */ }
14057
- }
14169
+ let routing;
14170
+ try {
14171
+ routing = readRoutingTelemetry(repoRoot);
14172
+ }
14173
+ catch (e) {
14174
+ write(`dz routing recommend: cannot read routing freshness state — ${e.message}`);
14175
+ return 1;
14058
14176
  }
14059
- const harvest = harvestStageOutcomes(records);
14060
- const rec = recommendModels(harvest, { ...(options.get('tier') !== undefined ? { tier: options.get('tier') } : {}) });
14177
+ const harvest = routing.harvest;
14178
+ let rec = recommendModels(harvest, {
14179
+ ...(options.get('tier') !== undefined ? { tier: options.get('tier') } : {}),
14180
+ alreadyFed: routing.alreadyFed,
14181
+ });
14061
14182
  if (flags.has('apply')) {
14062
14183
  const fedPath = join(repoRoot, '.dz', 'routing-fed.json');
14063
- let alreadyFed = [];
14064
- try {
14065
- alreadyFed = JSON.parse(readFileSync(fedPath, 'utf-8'));
14066
- }
14067
- catch { /* first apply */ }
14068
- const plan = planFeed(harvest.samples, alreadyFed);
14184
+ const plan = planFeed(harvest.samples, routing.alreadyFed);
14069
14185
  for (const sample of plan.toFeed)
14070
14186
  finalizeOutcome(repoRoot, sample.stage, sample.tier, sample.model, sample.success);
14071
14187
  try {
@@ -14076,6 +14192,10 @@ function cmdRouting(options, flags, cwd, write) {
14076
14192
  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}`);
14077
14193
  return 1;
14078
14194
  }
14195
+ rec = recommendModels(harvest, {
14196
+ ...(options.get('tier') !== undefined ? { tier: options.get('tier') } : {}),
14197
+ alreadyFed: plan.fedAfter,
14198
+ });
14079
14199
  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)` : ''}`);
14080
14200
  }
14081
14201
  if (flags.has('json')) {
@@ -14087,6 +14207,12 @@ function cmdRouting(options, flags, cwd, write) {
14087
14207
  write(` ${s.stage.padEnd(14)} ${s.spec.padEnd(20)} ${s.insufficientData ? '[insufficient data — cold-start pick]' : '[quality bar met]'} ${s.pick.evidence}`);
14088
14208
  }
14089
14209
  write(` basis: ${rec.basis.rule}`);
14210
+ if (rec.basis.freshness === 'current')
14211
+ write(' store: current (0 unharvested runs)');
14212
+ else if (rec.basis.freshness === 'stale')
14213
+ write(` store: STALE — ${rec.basis.unfed.count} run(s) harvested but never fed; auto-cost is routing from an older snapshot — run \`dz routing recommend --apply\``);
14214
+ else
14215
+ write(' store: UNFED — auto-cost has no learned rows; picks below are cold-start');
14090
14216
  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}`);
14091
14217
  write(` basis: ${rec.basis.crossFamilyNote}`);
14092
14218
  return 0;