@dzhechkov/harness-cli 0.3.259 → 0.3.261

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": "6b3bcc252253732b9703f1eb43ac1f0e9f0599979b09d0fea3208932fd0f33a8"
12
+ "sha256": "61c71e355d633a88401a19c247e517d69f7a8d1483ea7549b3505eccb2a8740b"
13
13
  },
14
14
  {
15
15
  "path": "coverage/coverage-final.json",
@@ -41,15 +41,15 @@
41
41
  },
42
42
  {
43
43
  "path": "dist/cli.d.ts.map",
44
- "sha256": "4c5fa8c51cc350874413efceb0a2e4dce2f0c71f39914ddd8be425336a37ae2d"
44
+ "sha256": "942d09c054994014e50139b807e1984586e172809b21e309114a5bf2ea92667c"
45
45
  },
46
46
  {
47
47
  "path": "dist/cli.js",
48
- "sha256": "a75034dfe70f0b57811dfad831366bd5c7995583a57db0f218621b026ac8d854"
48
+ "sha256": "43b1280af2070d30296162cc33a045a23452201b2da2ce7aec12eea1900451f3"
49
49
  },
50
50
  {
51
51
  "path": "dist/cli.js.map",
52
- "sha256": "dbe8fa418aedcb77dead3a374bba7cbf0fb5e305dd9a06002946a19604279684"
52
+ "sha256": "d0847603ca911ccca9e0838d54dadd1924ac0d9008e103c4edaef16dc4be0678"
53
53
  },
54
54
  {
55
55
  "path": "dist/index.d.ts",
@@ -73,7 +73,7 @@
73
73
  },
74
74
  {
75
75
  "path": "package.json",
76
- "sha256": "72858a1f0199832c7ad781f892b7b117ec980af33ff9598587e1ed1544046a35"
76
+ "sha256": "582e9b8e1b919650b444b6be90cc3a1ae54e2fd190524fdb3ffa46c8f2058990"
77
77
  },
78
78
  {
79
79
  "path": "src/bin.ts",
@@ -81,7 +81,7 @@
81
81
  },
82
82
  {
83
83
  "path": "src/cli.ts",
84
- "sha256": "1481238a2fc3a60051f678dcf6b415f8052ce954a9e55ee7577d8c3b2261e062"
84
+ "sha256": "e6b54081432afb9d3f1ee8a851c0e6b5ec8012dad4b9ae02a0d9036772226044"
85
85
  },
86
86
  {
87
87
  "path": "src/index.ts",
@@ -109,5 +109,5 @@
109
109
  }
110
110
  ]
111
111
  },
112
- "signature": "uYnxiEzDxjwdXlp1SGaC0Zv2x/MW39skbUKw3+xpvnLcoNsEV4pp8kFD5q7urBvC87fC+lRKsIpatoXCNGzwBA=="
112
+ "signature": "y8gV9ZSni/bgEg5MSCmyhQ+Nr9Q22yNs4Ac2MNR5JFmL6hsn8VsOzxFdWlog2Fw2oyZN4ZuprbK4lUVU4GLSDw=="
113
113
  }
package/README.md CHANGED
@@ -999,7 +999,7 @@ dz teach "<pattern>" [--reward <0-1>] [--domain <name>] [--type rule
999
999
  dz teach --reinforce "<dzId-or-exact-text>" [--project <dir>] # bump an existing learned pattern instead of writing a near-duplicate
1000
1000
  dz teach --from-json <file> [--project <dir>] [--no-mirror] [--harmonize] # bulk-import a `dz recall --all --json` export; prints a harmonize dry-run advisory
1001
1001
  dz consolidate [--sessions-dir <dir>] [--project <dir>] [--no-mirror]
1002
- dz recall "<query>" [--limit <N>] [--semantic | --no-semantic] [--project <dir>] # hybrid lexical+vector when the vector tier is enabled
1002
+ dz recall "<query>" [--limit <N>] [--domain <name>] [--semantic | --no-semantic] [--project <dir>] # hybrid lexical+vector when the vector tier is enabled; --domain BOOSTS same-domain lessons (never filters)
1003
1003
  dz recall --all [--json] [--stats] # export the WHOLE learned store, or inspect learning stats/top uses
1004
1004
  dz recall --books "<query>" [--book <slug>] # digitized-book KUs; --book narrows to one book
1005
1005
  dz vector status [--project <dir>] [--json] # semantic tier: engine availability, mirrored vs lexical counts, pending queue
@@ -2347,6 +2347,40 @@ guard, `--loc-cap <n>` tunes, default 700), **secret-scan** (the same high-signa
2347
2347
  edit caps/paths/waivers in `guards.config.json`; the project-critic role should then *not* re-flag these
2348
2348
  rules, only waivers without a reason.
2349
2349
 
2350
+ ### Domain-scoped recall — `--domain <name>` (a boost, never a filter)
2351
+
2352
+ **When to use it.** One brain store, more than one *kind* of work. `dz teach --domain` has always
2353
+ recorded a domain, but until now the ranking ignored it — so medical-research lessons and coding
2354
+ lessons came back interleaved, and the dilution grew with the store.
2355
+
2356
+ ```bash
2357
+ dz recall "signature" --limit 3 --domain security
2358
+ # dz recall "signature" — 3 hit(s), SQLite FTS5 + vector (agentdb) ranking
2359
+ # [0.90] (security) ⟨both⟩ Crypto anti-hallucination anti-pattern: verifying a signature …
2360
+ # [0.85] (publishing) ⟨both⟩ ⚠q 'Re-sign LAST' applies to EVERY signed pack a sweep touches …
2361
+ # [0.80] (testing) ⟨vector⟩ A structural registry-coverage test that only checks key-SET …
2362
+ # domain "security": among 6 candidate(s) — 1 exact match(es), 3 changed position;
2363
+ # foreign-domain lessons kept (a boost, not a filter)
2364
+ # 1 lower-ranked lesson fell past the --limit 3 cut to make room — raise --limit to see them
2365
+
2366
+ dz recall "signature" --limit 3 --domain health-research
2367
+ # domain "health-research": no lesson in this result carries it — order unchanged, nothing was hidden
2368
+ ```
2369
+
2370
+ **Why a boost and not a filter.** Cross-domain transfer is the reason one shared store beats two
2371
+ separate ones: *"a reviewer's evidence needs the same execute-don't-describe discipline as your own
2372
+ claims"* was learned reviewing code and applies verbatim to medical sources. A filter would cut
2373
+ exactly those transfers. So matching hits move UP — at most **2 places** for an exact domain, **1**
2374
+ for a hierarchical relative (`health` ↔ `health-research`) — and nothing is ever dropped: the hit
2375
+ count with `--domain` equals the hit count without it.
2376
+
2377
+ The bound matters: a domain tag is a hint about relevance, not evidence of it, so it breaks near-ties
2378
+ instead of overruling lexical ranking. And the note is honest in three directions — it says when
2379
+ nothing matched, it distinguishes *matched* from *actually moved*, and it counts what the `--limit`
2380
+ cut hid. That last one is the subtle part: the boost never drops a hit, but promoting a match into
2381
+ the top N does push the last one out of the printed list, so "nothing was dropped" would be true of
2382
+ the ranked list and false of the one you are reading. The note names the difference instead.
2383
+
2350
2384
  ### Semantic recall (vector tier)
2351
2385
 
2352
2386
  `dz recall` is **hybrid** when the vector tier is available and **exactly the old lexical command** when it is not — enabling it never changes behavior for projects that skip it.
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;AA0XH,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;CAC5C;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;AA+0O9E,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA8J5E"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA8XH,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;CAC5C;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;AAu3O9E,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA8J5E"}
package/dist/cli.js CHANGED
@@ -15,7 +15,7 @@ assembleCandidates, renderPromotionReport, renderPromotionAdr, normalizePromotio
15
15
  // Cold-vs-warm EPOCH RUNNER (feature epoch-replay) — orchestrates + scores, never calls a model.
16
16
  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,
17
17
  // Smart Backlog (feature smart-backlog) — goal-directed idea pipeline over the Brain vector engine.
18
- readBacklogConfig, readIdeas, writeIdeas, ideaId, dedupIdea, readGoalMap, readGoalMapDetailed, parseEffort, ensureBacklogGitignored, isSafeId, alignIdea, mirrorIdeaVector, snapshotIdeas, spinRoulette, rankRoulette, seededRng, eligibleIdeas, stageEnrichment, buildJiraDraft, resolveJiraAdapter, makeBacklogIO, harmonizeBacklog, BACKLOG_BACKENDS, parseReqeDebt, buildReqeBrief, settleReqeDebt, renderReqeList, REQE_SCOPE, } from '@dzhechkov/harness-core';
18
+ readBacklogConfig, readIdeas, writeIdeas, ideaId, dedupIdea, readGoalMap, readGoalMapDetailed, parseEffort, ensureBacklogGitignored, isSafeId, alignIdea, mirrorIdeaVector, snapshotIdeas, spinRoulette, rankRoulette, seededRng, eligibleIdeas, stageEnrichment, buildJiraDraft, resolveJiraAdapter, makeBacklogIO, harmonizeBacklog, BACKLOG_BACKENDS, applyDomainBoost, countDisplacedByCut, renderDomainBoostNote, renderDomainCutNote, parseReqeDebt, buildReqeBrief, settleReqeDebt, renderReqeList, REQE_SCOPE, } from '@dzhechkov/harness-core';
19
19
  import { getPreset, PRESET_NAMES } from '@dzhechkov/harness-presets';
20
20
  import { scanGitHub, analyzeRepo, generateReport, deepAnalyze, scanAllSources, ScoutMemory } from '@dzhechkov/scout';
21
21
  const USAGE = `dz - DZ cross-platform harness CLI
@@ -61,7 +61,7 @@ Usage:
61
61
  dz teach "<pattern>" [--reward <0-1>] [--domain <name>] [--type rule|success-pattern|lesson-learned] [--project <dir>] [--no-mirror] (--project pins the learned store to <dir>/.dz, not the cwd — pin to a canonical brain)
62
62
  dz teach --from-json <file> [--project <dir>] [--no-mirror] (bulk-import a 'dz recall --all --json' export — share a learned store across machines)
63
63
  dz consolidate [--sessions-dir <dir>] [--project <dir>] [--no-mirror] [--prune-noise [--apply]] [--prune-quarantine [--apply]] (both prunes: DRY-RUN by default; --apply snapshots then deletes; prune-quarantine = expired unproven lessons ONLY, never coupled to noise)
64
- dz recall "<query>" [--limit <N>] [--semantic | --no-semantic] [--books [--book <slug>]] [--project <dir>] | dz recall --all [--json] | dz recall --usage [--json] | dz recall --forget <dzId>[,<dzId>] [--apply] | dz recall --promote <dzId>[,<dzId>] [--apply] (forget/promote: dry-run default; forget snapshots before removing; promote lifts lesson-quarantine)
64
+ dz recall "<query>" [--limit <N>] [--domain <name>] [--semantic | --no-semantic] [--books [--book <slug>]] [--project <dir>] | dz recall --all [--json] | dz recall --usage [--json] | dz recall --forget <dzId>[,<dzId>] [--apply] | dz recall --promote <dzId>[,<dzId>] [--apply] (--domain <name> BOOSTS lessons of that domain without dropping foreign ones — a shared store keeps its cross-domain transfers; forget/promote: dry-run default; forget snapshots before removing; promote lifts lesson-quarantine)
65
65
  dz vector status [--project <dir>] [--json] (semantic tier: engine, mirrored vs lexical counts, pending queue)
66
66
  dz vector reindex [--project <dir>] [--json] (snapshot, re-embed learned-pattern vectors, stamp current model)
67
67
  dz vector export <path> [--project <dir>] (portable VECTOR form (.rvf, opt-in RVF engine); patterns ship via recall --all --json)
@@ -1842,13 +1842,30 @@ async function cmdRecall(options, flags, cwd, write) {
1842
1842
  : flags.has('no-semantic') || flags.has('lexical')
1843
1843
  ? 'lexical'
1844
1844
  : 'hybrid';
1845
- const result = await recallHybrid(projectRoot, query, { limit, mode });
1845
+ const wantedDomain = options.get('domain');
1846
+ // OVER-FETCH before boosting (Codex QE #5): the boost used to run on hits ALREADY
1847
+ // truncated to `limit`, so an exact-domain lesson sitting at rank limit+1 could
1848
+ // never receive its promised lift — the feature was weakest in exactly the case it
1849
+ // exists for (foreign-domain dilution pushing a relevant lesson just past the cut).
1850
+ // Fetch a bounded surplus, re-rank, then trim to the limit the caller asked for.
1851
+ const fetchLimit = wantedDomain !== undefined ? Math.min(limit * 3, limit + 20) : limit;
1852
+ const result = await recallHybrid(projectRoot, query, { limit: fetchLimit, mode });
1846
1853
  if (mode === 'semantic' && result.vectorEngine === 'none') {
1847
1854
  // --semantic is an explicit ask — degrading it silently would be dishonest (FR-3).
1848
1855
  write(`dz recall --semantic: ${result.vectorReason ?? 'no vector engine available — run: dz setup --memory agentdb'}`);
1849
1856
  return 1;
1850
1857
  }
1851
- const hits = result.hits;
1858
+ // Domain-aware re-ranking (health-advisor slice H): `--domain <name>` lifts lessons
1859
+ // tagged with that domain WITHOUT dropping foreign ones — a boost, not a filter, so a
1860
+ // shared store keeps the cross-domain transfers that make it worth more than two stores.
1861
+ const boost = wantedDomain !== undefined ? applyDomainBoost(result.hits, wantedDomain) : null;
1862
+ const hits = (boost ? boost.hits : result.hits).slice(0, limit);
1863
+ // The boost never drops a hit, but the CUT still can: promoting a match into the top
1864
+ // `limit` pushes the last one out, so a lesson visible WITHOUT --domain can vanish
1865
+ // WITH it. Cross-model review called this out as a lie by omission — the note said
1866
+ // "foreign-domain lessons kept" about the pre-cut list while the printed list was
1867
+ // missing one. Count it and say so; the reader can act on it (raise --limit).
1868
+ const displaced = boost !== null ? countDisplacedByCut(result.hits, boost.hits, limit) : 0;
1852
1869
  if (asJson) {
1853
1870
  // Portable contract UNCHANGED (I-7/AC-6): a plain PatternRecord[] — round-trips through
1854
1871
  // `dz teach --from-json` regardless of which backend ranked each hit.
@@ -1858,6 +1875,11 @@ async function cmdRecall(options, flags, cwd, write) {
1858
1875
  if (hits.length === 0) {
1859
1876
  write(`dz recall "${query}"`);
1860
1877
  write(` No matching patterns (teach some with \`dz teach\`, or consolidate sessions).`);
1878
+ // The domain note must print here too (Codex QE #10): a --domain run with zero hits
1879
+ // silently said nothing about the domain, so the reader could not tell whether the
1880
+ // boost had been applied and found nothing, or had not run at all.
1881
+ if (boost !== null && wantedDomain !== undefined)
1882
+ write(renderDomainBoostNote(boost, wantedDomain));
1861
1883
  return 0;
1862
1884
  }
1863
1885
  const vectorOn = result.vectorEngine !== 'none' && result.vectorError === undefined && mode !== 'lexical';
@@ -1870,12 +1892,31 @@ async function cmdRecall(options, flags, cwd, write) {
1870
1892
  const qTag = h.quarantined === true ? ' ⚠q' : '';
1871
1893
  if (h.quarantined === true)
1872
1894
  sawQuarantined = true;
1873
- write(` [${h.pattern.reward.toFixed(2)}] (${h.pattern.domain})${backendTag}${qTag} ${h.pattern.pattern.slice(0, 80)}`);
1895
+ // ONE line per hit, always. A lesson may contain newlines, and printing them raw
1896
+ // let stored CONTENT forge lines that look like the tool's own output — including
1897
+ // the domain-boost note that learning_bridge.py reads as a capability probe. Any
1898
+ // consumer that parses this output line-wise has the same exposure, so the fix
1899
+ // belongs at the point of rendering rather than in each reader.
1900
+ // Sanitise EVERY stored field that reaches the line, and every character a reader
1901
+ // might treat as a break — not just CR/LF. Round 4 forged the domain-boost note
1902
+ // twice over: once through `domain`, which was rendered raw, and once through a
1903
+ // U+2028 that Python's splitlines() honours and this replace did not.
1904
+ const oneLine = (v) => v.replace(/[\r\n\u2028\u2029\u0085\v\f]+/g, ' ⏎ ');
1905
+ const shown = oneLine(h.pattern.pattern).slice(0, 80);
1906
+ write(` [${h.pattern.reward.toFixed(2)}] (${oneLine(h.pattern.domain)})${backendTag}${qTag} ${shown}`);
1874
1907
  }
1875
1908
  if (sawQuarantined) {
1876
1909
  // The loop stays VISIBLE (ADR D2): a quarantined hit is shown, marked, and explained.
1877
1910
  write(' ⚠q = quarantined (unproven hypothesis, rank damped) — confirm with dz teach --reinforce, or dz recall --promote <dzId> --apply');
1878
1911
  }
1912
+ if (boost !== null && wantedDomain !== undefined) {
1913
+ // Say what the boost did — INCLUDING when it did nothing. A silent reorder would
1914
+ // let the reader believe the ranking was domain-aware when it had no match to work with.
1915
+ write(renderDomainBoostNote(boost, wantedDomain));
1916
+ const cutNote = renderDomainCutNote(displaced, limit);
1917
+ if (cutNote !== '')
1918
+ write(cutNote);
1919
+ }
1879
1920
  if (result.vectorError !== undefined && mode !== 'lexical') {
1880
1921
  // Engine present but the semantic leg failed/timed out — one honest line, exit 0 (05 §2.3).
1881
1922
  write(` ℹ vector search degraded: ${result.vectorError} — showing lexical ranking`);