@dzhechkov/harness-cli 0.3.259 → 0.3.260

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/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,36 @@ 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": 1 exact match(es); the order was already correct — nothing moved;
2363
+ # foreign-domain lessons kept (a boost, not a filter)
2364
+
2365
+ dz recall "signature" --limit 3 --domain health-research
2366
+ # domain "health-research": no lesson in this result carries it — order unchanged, nothing was hidden
2367
+ ```
2368
+
2369
+ **Why a boost and not a filter.** Cross-domain transfer is the reason one shared store beats two
2370
+ separate ones: *"a reviewer's evidence needs the same execute-don't-describe discipline as your own
2371
+ claims"* was learned reviewing code and applies verbatim to medical sources. A filter would cut
2372
+ exactly those transfers. So matching hits move UP — at most **2 places** for an exact domain, **1**
2373
+ for a hierarchical relative (`health` ↔ `health-research`) — and nothing is ever dropped: the hit
2374
+ count with `--domain` equals the hit count without it.
2375
+
2376
+ The bound matters: a domain tag is a hint about relevance, not evidence of it, so it breaks near-ties
2377
+ instead of overruling lexical ranking. And the note is honest in both directions — it says when
2378
+ nothing matched, and it distinguishes *matched* from *actually moved*.
2379
+
2350
2380
  ### Semantic recall (vector tier)
2351
2381
 
2352
2382
  `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;AAk3O9E,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,26 @@ 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
+ const oneLine = h.pattern.pattern.replace(/[\r\n]+/g, ' ⏎ ').slice(0, 80);
1901
+ write(` [${h.pattern.reward.toFixed(2)}] (${h.pattern.domain})${backendTag}${qTag} ${oneLine}`);
1874
1902
  }
1875
1903
  if (sawQuarantined) {
1876
1904
  // The loop stays VISIBLE (ADR D2): a quarantined hit is shown, marked, and explained.
1877
1905
  write(' ⚠q = quarantined (unproven hypothesis, rank damped) — confirm with dz teach --reinforce, or dz recall --promote <dzId> --apply');
1878
1906
  }
1907
+ if (boost !== null && wantedDomain !== undefined) {
1908
+ // Say what the boost did — INCLUDING when it did nothing. A silent reorder would
1909
+ // let the reader believe the ranking was domain-aware when it had no match to work with.
1910
+ write(renderDomainBoostNote(boost, wantedDomain));
1911
+ const cutNote = renderDomainCutNote(displaced, limit);
1912
+ if (cutNote !== '')
1913
+ write(cutNote);
1914
+ }
1879
1915
  if (result.vectorError !== undefined && mode !== 'lexical') {
1880
1916
  // Engine present but the semantic leg failed/timed out — one honest line, exit 0 (05 §2.3).
1881
1917
  write(` ℹ vector search degraded: ${result.vectorError} — showing lexical ranking`);