@dzhechkov/harness-cli 0.3.260 → 0.3.262

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": "af18869081f5141177b6427e812d724e15995d49b10cf67f24803eb5e1d79cb5"
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": "02a2ba1def8a57e1d4dcde048d5a62af19ab4cd825c98bcfbe8ac0df7de2062c"
45
45
  },
46
46
  {
47
47
  "path": "dist/cli.js",
48
- "sha256": "a75034dfe70f0b57811dfad831366bd5c7995583a57db0f218621b026ac8d854"
48
+ "sha256": "8d3674dc681dc2493886534c8a30e124664a158fd4f7f2da476f5ff2ec615edd"
49
49
  },
50
50
  {
51
51
  "path": "dist/cli.js.map",
52
- "sha256": "dbe8fa418aedcb77dead3a374bba7cbf0fb5e305dd9a06002946a19604279684"
52
+ "sha256": "3975feb3b9102aad34cdcc070315a7756ef9241aa2bb618759a1bbac25ec7951"
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": "3e9deb36cf53245aaae26a72de359a7f57e9691d52454266ffe32f61dc963a27"
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": "9549a9864fddc36eb99a172f3f0e7c7e72ae32d36aebadaf826b58af0bed16b4"
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": "3ghvnJARKOZq3JBJswFiPPiar56oO/qN6KDRN1kU2yunpTVyhNAqXdr49P/a5w/OHWPKD7hX5Vk72VLYNkTWDA=="
113
113
  }
package/README.md CHANGED
@@ -2359,13 +2359,20 @@ dz recall "signature" --limit 3 --domain security
2359
2359
  # [0.90] (security) ⟨both⟩ Crypto anti-hallucination anti-pattern: verifying a signature …
2360
2360
  # [0.85] (publishing) ⟨both⟩ ⚠q 'Re-sign LAST' applies to EVERY signed pack a sweep touches …
2361
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;
2362
+ # domain "security": among 6 candidate(s) — 1 exact match(es), 3 changed position;
2363
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
2364
2365
 
2365
2366
  dz recall "signature" --limit 3 --domain health-research
2366
2367
  # domain "health-research": no lesson in this result carries it — order unchanged, nothing was hidden
2367
2368
  ```
2368
2369
 
2370
+ **One line out per hit.** Every value that reaches a `dz recall` output line — the lesson, its
2371
+ domain, your query, the requested domain — is rendered on a single line, with embedded line breaks
2372
+ shown as `⏎`. That is not cosmetic: any tool parsing this output line-wise (the `health-advisor`
2373
+ research skill reads it to detect whether this `dz` supports `--domain`) would otherwise see lines
2374
+ forged by stored content or by a crafted query.
2375
+
2369
2376
  **Why a boost and not a filter.** Cross-domain transfer is the reason one shared store beats two
2370
2377
  separate ones: *"a reviewer's evidence needs the same execute-don't-describe discipline as your own
2371
2378
  claims"* was learned reviewing code and applies verbatim to medical sources. A filter would cut
@@ -2374,8 +2381,11 @@ for a hierarchical relative (`health` ↔ `health-research`) — and nothing is
2374
2381
  count with `--domain` equals the hit count without it.
2375
2382
 
2376
2383
  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*.
2384
+ instead of overruling lexical ranking. And the note is honest in three directions — it says when
2385
+ nothing matched, it distinguishes *matched* from *actually moved*, and it counts what the `--limit`
2386
+ cut hid. That last one is the subtle part: the boost never drops a hit, but promoting a match into
2387
+ the top N does push the last one out of the printed list, so "nothing was dropped" would be true of
2388
+ the ranked list and false of the one you are reading. The note names the difference instead.
2379
2389
 
2380
2390
  ### Semantic recall (vector tier)
2381
2391
 
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;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"}
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;AA83O9E,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA8J5E"}
package/dist/cli.js CHANGED
@@ -1848,6 +1848,14 @@ async function cmdRecall(options, flags, cwd, write) {
1848
1848
  // never receive its promised lift — the feature was weakest in exactly the case it
1849
1849
  // exists for (foreign-domain dilution pushing a relevant lesson just past the cut).
1850
1850
  // Fetch a bounded surplus, re-rank, then trim to the limit the caller asked for.
1851
+ // EVERY value that reaches an output line goes through this, not just stored fields.
1852
+ // Round 5: `pattern` and `domain` were sanitised but the QUERY and the requested
1853
+ // domain were not, so a caller-supplied newline could still forge a line that looks
1854
+ // like the tool's own — including the boost note a peer reads as a capability probe.
1855
+ // The rule is "one line out per line of output", and it has to hold for every field.
1856
+ const oneLine = (v) => String(v).replace(/[\r\n\u2028\u2029\u0085\v\f]+/g, ' ⏎ ');
1857
+ const shownQuery = oneLine(query);
1858
+ const shownDomain = wantedDomain === undefined ? undefined : oneLine(wantedDomain);
1851
1859
  const fetchLimit = wantedDomain !== undefined ? Math.min(limit * 3, limit + 20) : limit;
1852
1860
  const result = await recallHybrid(projectRoot, query, { limit: fetchLimit, mode });
1853
1861
  if (mode === 'semantic' && result.vectorEngine === 'none') {
@@ -1873,19 +1881,19 @@ async function cmdRecall(options, flags, cwd, write) {
1873
1881
  return 0;
1874
1882
  }
1875
1883
  if (hits.length === 0) {
1876
- write(`dz recall "${query}"`);
1884
+ write(`dz recall "${shownQuery}"`);
1877
1885
  write(` No matching patterns (teach some with \`dz teach\`, or consolidate sessions).`);
1878
1886
  // The domain note must print here too (Codex QE #10): a --domain run with zero hits
1879
1887
  // silently said nothing about the domain, so the reader could not tell whether the
1880
1888
  // boost had been applied and found nothing, or had not run at all.
1881
- if (boost !== null && wantedDomain !== undefined)
1882
- write(renderDomainBoostNote(boost, wantedDomain));
1889
+ if (boost !== null && shownDomain !== undefined)
1890
+ write(renderDomainBoostNote(boost, shownDomain));
1883
1891
  return 0;
1884
1892
  }
1885
1893
  const vectorOn = result.vectorEngine !== 'none' && result.vectorError === undefined && mode !== 'lexical';
1886
1894
  const lexLabel = result.lexicalBackend === 'sqlite' ? 'SQLite FTS5' : 'keyword (JSON)';
1887
1895
  const ranking = vectorOn ? `${lexLabel} + vector (${result.vectorEngine}) ranking` : `${lexLabel} ranking (lexical)`;
1888
- write(`dz recall "${query}" — ${hits.length} hit(s), ${ranking}`);
1896
+ write(`dz recall "${shownQuery}" — ${hits.length} hit(s), ${ranking}`);
1889
1897
  let sawQuarantined = false;
1890
1898
  for (const h of hits) {
1891
1899
  const backendTag = vectorOn ? ` ⟨${h.backend}⟩` : '';
@@ -1897,17 +1905,21 @@ async function cmdRecall(options, flags, cwd, write) {
1897
1905
  // the domain-boost note that learning_bridge.py reads as a capability probe. Any
1898
1906
  // consumer that parses this output line-wise has the same exposure, so the fix
1899
1907
  // 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}`);
1908
+ // Sanitise EVERY stored field that reaches the line, and every character a reader
1909
+ // might treat as a break — not just CR/LF. Round 4 forged the domain-boost note
1910
+ // twice over: once through `domain`, which was rendered raw, and once through a
1911
+ // U+2028 that Python's splitlines() honours and this replace did not.
1912
+ const shown = oneLine(h.pattern.pattern).slice(0, 80);
1913
+ write(` [${h.pattern.reward.toFixed(2)}] (${oneLine(h.pattern.domain)})${backendTag}${qTag} ${shown}`);
1902
1914
  }
1903
1915
  if (sawQuarantined) {
1904
1916
  // The loop stays VISIBLE (ADR D2): a quarantined hit is shown, marked, and explained.
1905
1917
  write(' ⚠q = quarantined (unproven hypothesis, rank damped) — confirm with dz teach --reinforce, or dz recall --promote <dzId> --apply');
1906
1918
  }
1907
- if (boost !== null && wantedDomain !== undefined) {
1919
+ if (boost !== null && shownDomain !== undefined) {
1908
1920
  // Say what the boost did — INCLUDING when it did nothing. A silent reorder would
1909
1921
  // let the reader believe the ranking was domain-aware when it had no match to work with.
1910
- write(renderDomainBoostNote(boost, wantedDomain));
1922
+ write(renderDomainBoostNote(boost, shownDomain));
1911
1923
  const cutNote = renderDomainCutNote(displaced, limit);
1912
1924
  if (cutNote !== '')
1913
1925
  write(cutNote);