@dzhechkov/harness-core 0.3.106 → 0.3.107
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/dist/claim-check-hook-policy.d.ts +8 -4
- package/dist/claim-check-hook-policy.d.ts.map +1 -1
- package/dist/claim-check-hook-policy.js +8 -24
- package/dist/claim-check-hook-policy.js.map +1 -1
- package/dist/claim-check.d.ts +11 -0
- package/dist/claim-check.d.ts.map +1 -1
- package/dist/claim-check.js +95 -6
- package/dist/claim-check.js.map +1 -1
- package/package.json +3 -3
- package/src/claim-check-hook-policy.ts +8 -20
- package/src/claim-check.ts +94 -6
|
@@ -21,11 +21,15 @@ export interface HookDecision {
|
|
|
21
21
|
*/
|
|
22
22
|
export declare const ESCAPE_TEACHING: string;
|
|
23
23
|
/**
|
|
24
|
-
* Is the 1-based `line` inside a fenced
|
|
25
|
-
*
|
|
26
|
-
*
|
|
24
|
+
* Is the 1-based `line` inside a fenced code block within `text`?
|
|
25
|
+
*
|
|
26
|
+
* MOVED to `claim-check.ts` and re-exported here. The engine skips fenced lines and this hook exempts
|
|
27
|
+
* them from its deny path — if the two ever computed "inside a fence" differently, the hook would deny
|
|
28
|
+
* a line the engine had already dismissed, or vice versa. Two implementations WILL drift; one cannot.
|
|
29
|
+
* The engine owns it because the engine is the pure module with no dependents.
|
|
27
30
|
*/
|
|
28
|
-
|
|
31
|
+
import { isFenced } from './claim-check.js';
|
|
32
|
+
export { isFenced };
|
|
29
33
|
/**
|
|
30
34
|
* Is `excerpt` a NEW claim line (guard d)? True when there is no pre-image (`oldString` absent/empty —
|
|
31
35
|
* e.g. a `Write`) or the pre-image does not already contain the excerpt. A trailing clip ellipsis
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claim-check-hook-policy.d.ts","sourceRoot":"","sources":["../src/claim-check-hook-policy.ts"],"names":[],"mappings":"AA2BA,wCAAwC;AACxC,MAAM,WAAW,gBAAgB;IAC/B,qGAAqG;IACrG,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,mGAAmG;IACnG,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,8DAA8D;AAC9D,MAAM,WAAW,YAAY;IAC3B,oGAAoG;IACpG,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;IAClC,sGAAsG;IACtG,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,4GAA4G;IAC5G,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,QAIiF,CAAC;AAuB9G
|
|
1
|
+
{"version":3,"file":"claim-check-hook-policy.d.ts","sourceRoot":"","sources":["../src/claim-check-hook-policy.ts"],"names":[],"mappings":"AA2BA,wCAAwC;AACxC,MAAM,WAAW,gBAAgB;IAC/B,qGAAqG;IACrG,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,mGAAmG;IACnG,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,8DAA8D;AAC9D,MAAM,WAAW,YAAY;IAC3B,oGAAoG;IACpG,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;IAClC,sGAAsG;IACtG,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,4GAA4G;IAC5G,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,QAIiF,CAAC;AAuB9G;;;;;;;GAOG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,CAAC;AAEpB;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAOtE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,YAAY,CAoDhF"}
|
|
@@ -53,31 +53,15 @@ function formatFinding(f) {
|
|
|
53
53
|
return ' - L' + f.line + ': "' + f.excerpt + '" — ' + f.reason + ' -> ' + f.suggestion;
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
|
-
* Is the 1-based `line` inside a fenced
|
|
57
|
-
*
|
|
58
|
-
*
|
|
56
|
+
* Is the 1-based `line` inside a fenced code block within `text`?
|
|
57
|
+
*
|
|
58
|
+
* MOVED to `claim-check.ts` and re-exported here. The engine skips fenced lines and this hook exempts
|
|
59
|
+
* them from its deny path — if the two ever computed "inside a fence" differently, the hook would deny
|
|
60
|
+
* a line the engine had already dismissed, or vice versa. Two implementations WILL drift; one cannot.
|
|
61
|
+
* The engine owns it because the engine is the pure module with no dependents.
|
|
59
62
|
*/
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return false;
|
|
63
|
-
const lines = text.split(/\r?\n/);
|
|
64
|
-
const upTo = Math.min(line - 1, lines.length);
|
|
65
|
-
// CommonMark allows BOTH ``` and ~~~ fences, and a fence is closed only by its OWN marker — a
|
|
66
|
-
// ``` inside a ~~~ block is literal content, not a toggle. A naive toggle counter that accepts
|
|
67
|
-
// either marker therefore mis-tracks nesting; track the open marker instead.
|
|
68
|
-
let open = null;
|
|
69
|
-
for (let i = 0; i < upTo; i++) {
|
|
70
|
-
const m = /^\s*(`{3,}|~{3,})/.exec(lines[i] || '');
|
|
71
|
-
if (!m)
|
|
72
|
-
continue;
|
|
73
|
-
const marker = m[1][0];
|
|
74
|
-
if (open === null)
|
|
75
|
-
open = marker;
|
|
76
|
-
else if (open === marker)
|
|
77
|
-
open = null;
|
|
78
|
-
}
|
|
79
|
-
return open !== null;
|
|
80
|
-
}
|
|
63
|
+
import { isFenced } from './claim-check.js';
|
|
64
|
+
export { isFenced };
|
|
81
65
|
/**
|
|
82
66
|
* Is `excerpt` a NEW claim line (guard d)? True when there is no pre-image (`oldString` absent/empty —
|
|
83
67
|
* e.g. a `Write`) or the pre-image does not already contain the excerpt. A trailing clip ellipsis
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claim-check-hook-policy.js","sourceRoot":"","sources":["../src/claim-check-hook-policy.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,6EAA6E;AAC7E,gGAAgG;AAChG,EAAE;AACF,mGAAmG;AACnG,+FAA+F;AAC/F,+FAA+F;AAC/F,+FAA+F;AAC/F,qGAAqG;AACrG,qDAAqD;AACrD,EAAE;AACF,8BAA8B;AAC9B,uGAAuG;AACvG,oGAAoG;AACpG,mGAAmG;AACnG,8DAA8D;AAC9D,uGAAuG;AACvG,qGAAqG;AACrG,+FAA+F;AAC/F,qBAAqB;AACrB,EAAE;AACF,sGAAsG;AACtG,yFAAyF;AAEzF,OAAO,EAAE,UAAU,EAA4C,MAAM,kBAAkB,CAAC;AAoBxF;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAC1B,iGAAiG;IACjG,oGAAoG;IACpG,+FAA+F;IAC/F,2GAA2G,CAAC;AAE9G,wGAAwG;AACxG,SAAS,cAAc,CAAC,IAAY;IAClC,IAAI,CAAC;QACH,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IACpC,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,EAAiB,EAAE,QAAiB;IACpD,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,CAAe;IACpC,OAAO,OAAO,GAAG,CAAC,CAAC,IAAI,GAAG,KAAK,GAAG,CAAC,CAAC,OAAO,GAAG,MAAM,GAAG,CAAC,CAAC,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,UAAU,CAAC;AAC1F,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"claim-check-hook-policy.js","sourceRoot":"","sources":["../src/claim-check-hook-policy.ts"],"names":[],"mappings":"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"}
|
package/dist/claim-check.d.ts
CHANGED
|
@@ -11,6 +11,17 @@ export interface ClaimCheckResult {
|
|
|
11
11
|
readonly ok: boolean;
|
|
12
12
|
readonly findings: ClaimFinding[];
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Is the 1-based `line` inside a fenced code block within `text`?
|
|
16
|
+
*
|
|
17
|
+
* OWNED HERE, re-exported by `claim-check-hook-policy.ts`. The engine skips fenced lines; the hook
|
|
18
|
+
* exempts them from its deny path. Two implementations of "inside a fence" would drift; one cannot.
|
|
19
|
+
*
|
|
20
|
+
* CommonMark allows BOTH ``` and ~~~ fences, and a fence closes only on its OWN marker — a ``` inside
|
|
21
|
+
* a ~~~ block is literal content, not a toggle. A naive toggle counter that accepts either marker
|
|
22
|
+
* mis-tracks nesting, so track the open marker instead. Never throws.
|
|
23
|
+
*/
|
|
24
|
+
export declare function isFenced(text: string, line: number): boolean;
|
|
14
25
|
/**
|
|
15
26
|
* Lint a block of text for untagged or overstated accuracy claims.
|
|
16
27
|
* Pure and never-throws: a non-string or empty input returns `{ ok: true, findings: [] }`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claim-check.d.ts","sourceRoot":"","sources":["../src/claim-check.ts"],"names":[],"mappings":"AAkBA,oGAAoG;AACpG,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,2DAA2D;AAC3D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAC;CACnC;
|
|
1
|
+
{"version":3,"file":"claim-check.d.ts","sourceRoot":"","sources":["../src/claim-check.ts"],"names":[],"mappings":"AAkBA,oGAAoG;AACpG,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,2DAA2D;AAC3D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAC;CACnC;AA4FD;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAa5D;AA4CD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CA6FzD;AAMD,4DAA4D;AAC5D,wBAAgB,SAAS,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAI1D"}
|
package/dist/claim-check.js
CHANGED
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
/** Phrases that signal a quantitative accuracy claim (safe as substrings). */
|
|
19
19
|
const METRIC_TERMS = [
|
|
20
20
|
// Generic accuracy vocabulary kept from ruview.
|
|
21
|
-
'
|
|
21
|
+
// `recall` is NOT here — it is this repo's command name. It re-enters via RECALL_METRIC_RE, which
|
|
22
|
+
// demands a scoring context. `precision` stays: it has no command collision.
|
|
23
|
+
'accuracy', 'precision',
|
|
22
24
|
'error rate', 'detection rate', 'true positive',
|
|
23
25
|
// dz claim vocabulary — a harness monorepo claims coverage, test counts, catalogue sizes,
|
|
24
26
|
// and performance numbers, not WiFi-sensing PCK/MPJPE.
|
|
@@ -68,18 +70,89 @@ function scrubLine(lower) {
|
|
|
68
70
|
function mentionsMetricTerm(lower, scrubbed) {
|
|
69
71
|
if (METRIC_TERMS.some((t) => lower.includes(t)))
|
|
70
72
|
return true;
|
|
73
|
+
// `recall` only in a scoring context (see RECALL_METRIC_RE). `precision` on the line is enough:
|
|
74
|
+
// "precision 0.9 / recall 0.8" is the canonical ML pair.
|
|
75
|
+
if (RECALL_METRIC_RE.test(scrubbed))
|
|
76
|
+
return true;
|
|
71
77
|
if (!HAS_NUMBER_RE.test(scrubbed))
|
|
72
78
|
return false;
|
|
73
79
|
return METRIC_TERMS_SHORT.some((re) => re.test(scrubbed));
|
|
74
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* The PARAGRAPH containing 1-based `line`: the maximal run of contiguous non-blank lines, bounded by
|
|
83
|
+
* a blank line, a heading, or a fence marker. The claim is DETECTED per line; its honesty tag and its
|
|
84
|
+
* reproducer may live anywhere in this run — prose wrapped at 100 columns routinely splits a number
|
|
85
|
+
* from its `MEASURED`, and flagging the author who tagged correctly is how a gate teaches its users to
|
|
86
|
+
* ignore it.
|
|
87
|
+
*
|
|
88
|
+
* LAUNDERING EXPOSURE, stated: one tag covers every claim in its paragraph. It cannot cross a blank
|
|
89
|
+
* line, a heading, or a fence.
|
|
90
|
+
*/
|
|
91
|
+
function paragraphAround(lines, i) {
|
|
92
|
+
const bounds = (k) => k.trim() === '' || HEADING_RE.test(k) || FENCE_RE.test(k);
|
|
93
|
+
let a = i;
|
|
94
|
+
let b = i;
|
|
95
|
+
while (a > 0 && !bounds(lines[a - 1] ?? ''))
|
|
96
|
+
a--;
|
|
97
|
+
while (b < lines.length - 1 && !bounds(lines[b + 1] ?? ''))
|
|
98
|
+
b++;
|
|
99
|
+
return lines.slice(a, b + 1).join('\n');
|
|
100
|
+
}
|
|
75
101
|
/** Tags that make a claim honest (case-insensitive). */
|
|
76
102
|
// `estimated` agrees with the `estimated: true` honest-uncertainty marker `dz usage` already
|
|
77
103
|
// emits — the two honesty systems must not contradict each other.
|
|
78
104
|
const HONEST_TAGS = ['measured', 'claimed', 'synthetic', 'unvalidated', 'baseline', 'estimated'];
|
|
105
|
+
/**
|
|
106
|
+
* Is the 1-based `line` inside a fenced code block within `text`?
|
|
107
|
+
*
|
|
108
|
+
* OWNED HERE, re-exported by `claim-check-hook-policy.ts`. The engine skips fenced lines; the hook
|
|
109
|
+
* exempts them from its deny path. Two implementations of "inside a fence" would drift; one cannot.
|
|
110
|
+
*
|
|
111
|
+
* CommonMark allows BOTH ``` and ~~~ fences, and a fence closes only on its OWN marker — a ``` inside
|
|
112
|
+
* a ~~~ block is literal content, not a toggle. A naive toggle counter that accepts either marker
|
|
113
|
+
* mis-tracks nesting, so track the open marker instead. Never throws.
|
|
114
|
+
*/
|
|
115
|
+
export function isFenced(text, line) {
|
|
116
|
+
if (typeof text !== 'string' || typeof line !== 'number' || !isFinite(line) || line < 1)
|
|
117
|
+
return false;
|
|
118
|
+
const lines = text.split(/\r?\n/);
|
|
119
|
+
const upTo = Math.min(line - 1, lines.length);
|
|
120
|
+
let open = null;
|
|
121
|
+
for (let i = 0; i < upTo; i++) {
|
|
122
|
+
const m = FENCE_RE.exec(lines[i] || '');
|
|
123
|
+
if (!m)
|
|
124
|
+
continue;
|
|
125
|
+
const marker = m[1][0];
|
|
126
|
+
if (open === null)
|
|
127
|
+
open = marker;
|
|
128
|
+
else if (open === marker)
|
|
129
|
+
open = null;
|
|
130
|
+
}
|
|
131
|
+
return open !== null;
|
|
132
|
+
}
|
|
133
|
+
const FENCE_RE = /^\s*(`{3,}|~{3,})/;
|
|
134
|
+
const HEADING_RE = /^\s{0,3}#{1,6}\s/;
|
|
135
|
+
/**
|
|
136
|
+
* `recall` is ruview's completeness metric AND this repo's command name (`dz recall`). Matching it as
|
|
137
|
+
* a bare term produced 31 false positives (MEASURED — reproducer `dz claim-check --json`), every one
|
|
138
|
+
* of them prose about the command. It counts as a metric only in a scoring context: paired with
|
|
139
|
+
* `precision`, or immediately followed by a score (`recall@5`, `recall rate`, `recall of 0.9`,
|
|
140
|
+
* `recall = 0.9`, `recall score`).
|
|
141
|
+
*/
|
|
142
|
+
const RECALL_METRIC_RE = /\brecall\s*(?:@\s*\d|rate\b|score\b|of\s+[\d.]|[:=]\s*[\d.])/i;
|
|
143
|
+
/**
|
|
144
|
+
* A shell reproducer is STRUCTURAL, never a word. `(MEASURED — reproducer)` is self-certifying and
|
|
145
|
+
* must not pass; a backticked span whose first token is a command this repo actually measures with is
|
|
146
|
+
* evidence. The allowlist boundary is exactly that: an unknown binary is a claim ABOUT evidence.
|
|
147
|
+
*/
|
|
148
|
+
const SHELL_REPRO_RE = /`\s*\$?\s*(?:ps|stat|lsof|time|git|npm|npx|node|pnpm|yarn|dz|curl|wc|grep|find|cargo|make|docker|kubectl|awk|sed|du|df|vitest|pytest)\b[^`]*`/i;
|
|
79
149
|
/** Reproducer references that count as evidence backing a MEASURED claim. */
|
|
80
150
|
const REPRODUCER_HINTS = [
|
|
81
151
|
// Generic evidence hints kept from ruview.
|
|
82
|
-
|
|
152
|
+
// 'reproduce' was inherited from ruview and is SELF-CERTIFYING: it makes the bare word "reproducer"
|
|
153
|
+
// count as its own evidence, so `(MEASURED — reproducer)` passed. Removed (D5). Evidence must be a
|
|
154
|
+
// named command or artifact — see SHELL_REPRO_RE for the structural form.
|
|
155
|
+
'baseline', 'sha256', 'tarball', 'cargo test',
|
|
83
156
|
// Packaging-claim reproducers (npm reviews): the tarball itself.
|
|
84
157
|
'npm pack', 'npm view', 'npm i ', 'npm install',
|
|
85
158
|
// dz reproducers — the actual commands/artifacts that back a dz claim.
|
|
@@ -107,14 +180,26 @@ export function claimCheck(text) {
|
|
|
107
180
|
const original = raw.trim();
|
|
108
181
|
if (!original)
|
|
109
182
|
return;
|
|
183
|
+
// A fenced block is CODE, not prose. The hook already exempts fenced lines from its deny path
|
|
184
|
+
// (it re-exports `isFenced` from here); the engine flagging them was not a policy, it was a bug —
|
|
185
|
+
// 127 of this repo's 488 findings lived inside fences, and NONE of them was `high`
|
|
186
|
+
// (MEASURED — reproducer `dz claim-check --json`).
|
|
187
|
+
// EXPOSURE, accepted: a claim written inside a fence is now unreachable. That is exactly the right
|
|
188
|
+
// the inline backtick escape already grants; a fence is its block-level form.
|
|
189
|
+
if (isFenced(text, i + 1))
|
|
190
|
+
return;
|
|
110
191
|
// Analyse the URL-stripped line, but report the ORIGINAL so the excerpt stays recognisable.
|
|
111
192
|
const line = stripUrls(original);
|
|
112
193
|
if (!line.trim())
|
|
113
194
|
return;
|
|
114
195
|
const lower = line.toLowerCase();
|
|
115
|
-
const hasPercent = PERCENT_RE.test(line);
|
|
116
|
-
PERCENT_RE.lastIndex = 0; // reset stateful global regex
|
|
117
196
|
const scrubbed = scrubLine(lower);
|
|
197
|
+
// The percent TRIGGER reads the code-span-scrubbed line. Before this, `hasPercent` was computed on
|
|
198
|
+
// the UN-scrubbed line while metric terms were matched on the scrubbed one, so a backticked `99%`
|
|
199
|
+
// still opened the scan: the backtick escape downgraded a quoted claim from `high` to `medium`
|
|
200
|
+
// instead of silencing it. Documentation that quotes a forbidden claim was punished for doing so.
|
|
201
|
+
const hasPercent = PERCENT_RE.test(scrubbed);
|
|
202
|
+
PERCENT_RE.lastIndex = 0; // reset stateful global regex
|
|
118
203
|
// DELIBERATE DIVERGENCE from ruview, which matched METRIC_TERMS against the UNSCRUBBED line.
|
|
119
204
|
// dz's vocabulary contains path-like words ('packages', 'commands', 'skills'), so a code span
|
|
120
205
|
// such as `packages/x/usage.test.ts:42` registered as a metric mention and produced a false
|
|
@@ -124,8 +209,12 @@ export function claimCheck(text) {
|
|
|
124
209
|
const mentionsMetric = mentionsMetricTerm(scrubbed, scrubbed);
|
|
125
210
|
if (!hasPercent && !mentionsMetric)
|
|
126
211
|
return;
|
|
127
|
-
|
|
128
|
-
|
|
212
|
+
// The claim is detected on its own line; its honesty tag and reproducer may live anywhere in the
|
|
213
|
+
// same paragraph. Wrapped prose routinely puts `MEASURED` on the next line.
|
|
214
|
+
const para = stripUrls(paragraphAround(lines, i));
|
|
215
|
+
const paraLower = para.toLowerCase();
|
|
216
|
+
const tagged = HONEST_TAGS.some((t) => paraLower.includes(t));
|
|
217
|
+
const hasReproducer = REPRODUCER_HINTS.some((h) => paraLower.includes(h)) || SHELL_REPRO_RE.test(para);
|
|
129
218
|
const perfect = PERFECT_WORD_RE.test(line) || (mentionsMetric && PERFECT_PCT_RE.test(line));
|
|
130
219
|
if (perfect && !lower.includes('retract')) {
|
|
131
220
|
findings.push({
|
package/dist/claim-check.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claim-check.js","sourceRoot":"","sources":["../src/claim-check.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,4FAA4F;AAC5F,8FAA8F;AAC9F,8FAA8F;AAC9F,6FAA6F;AAC7F,iGAAiG;AACjG,8FAA8F;AAC9F,8CAA8C;AAC9C,EAAE;AACF,+FAA+F;AAC/F,mGAAmG;AACnG,0FAA0F;AAC1F,8FAA8F;AAC9F,oGAAoG;AACpG,+FAA+F;AAC/F,yDAAyD;AAiBzD,8EAA8E;AAC9E,MAAM,YAAY,GAAG;IACnB,gDAAgD;IAChD,UAAU,EAAE,WAAW
|
|
1
|
+
{"version":3,"file":"claim-check.js","sourceRoot":"","sources":["../src/claim-check.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dzhechkov/harness-core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.107",
|
|
4
4
|
"description": "Shared harness logic - skill loading, additive apply, and the init/sync/verify/doctor operations.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
"@dzhechkov/adapter-opencode": "^0.2.0",
|
|
32
32
|
"yaml": "^2.0.0",
|
|
33
33
|
"@dzhechkov/adapter-agents-md": "0.1.1",
|
|
34
|
-
"@dzhechkov/adapter-cursor": "0.1.1",
|
|
35
34
|
"@dzhechkov/adapter-copilot": "0.1.1",
|
|
35
|
+
"@dzhechkov/adapter-cursor": "0.1.1",
|
|
36
36
|
"@dzhechkov/adapter-gemini": "0.1.1",
|
|
37
|
-
"@dzhechkov/core": "0.2.14",
|
|
38
37
|
"@dzhechkov/adapter-windsurf": "0.1.1",
|
|
38
|
+
"@dzhechkov/core": "0.2.14",
|
|
39
39
|
"@dzhechkov/memory": "0.2.9"
|
|
40
40
|
},
|
|
41
41
|
"peerDependenciesMeta": {
|
|
@@ -76,27 +76,15 @@ function formatFinding(f: ClaimFinding): string {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
|
-
* Is the 1-based `line` inside a fenced
|
|
80
|
-
*
|
|
81
|
-
*
|
|
79
|
+
* Is the 1-based `line` inside a fenced code block within `text`?
|
|
80
|
+
*
|
|
81
|
+
* MOVED to `claim-check.ts` and re-exported here. The engine skips fenced lines and this hook exempts
|
|
82
|
+
* them from its deny path — if the two ever computed "inside a fence" differently, the hook would deny
|
|
83
|
+
* a line the engine had already dismissed, or vice versa. Two implementations WILL drift; one cannot.
|
|
84
|
+
* The engine owns it because the engine is the pure module with no dependents.
|
|
82
85
|
*/
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const lines = text.split(/\r?\n/);
|
|
86
|
-
const upTo = Math.min(line - 1, lines.length);
|
|
87
|
-
// CommonMark allows BOTH ``` and ~~~ fences, and a fence is closed only by its OWN marker — a
|
|
88
|
-
// ``` inside a ~~~ block is literal content, not a toggle. A naive toggle counter that accepts
|
|
89
|
-
// either marker therefore mis-tracks nesting; track the open marker instead.
|
|
90
|
-
let open: '`' | '~' | null = null;
|
|
91
|
-
for (let i = 0; i < upTo; i++) {
|
|
92
|
-
const m = /^\s*(`{3,}|~{3,})/.exec(lines[i] || '');
|
|
93
|
-
if (!m) continue;
|
|
94
|
-
const marker = m[1]![0] as '`' | '~';
|
|
95
|
-
if (open === null) open = marker;
|
|
96
|
-
else if (open === marker) open = null;
|
|
97
|
-
}
|
|
98
|
-
return open !== null;
|
|
99
|
-
}
|
|
86
|
+
import { isFenced } from './claim-check.js';
|
|
87
|
+
export { isFenced };
|
|
100
88
|
|
|
101
89
|
/**
|
|
102
90
|
* Is `excerpt` a NEW claim line (guard d)? True when there is no pre-image (`oldString` absent/empty —
|
package/src/claim-check.ts
CHANGED
|
@@ -34,7 +34,9 @@ export interface ClaimCheckResult {
|
|
|
34
34
|
/** Phrases that signal a quantitative accuracy claim (safe as substrings). */
|
|
35
35
|
const METRIC_TERMS = [
|
|
36
36
|
// Generic accuracy vocabulary kept from ruview.
|
|
37
|
-
'
|
|
37
|
+
// `recall` is NOT here — it is this repo's command name. It re-enters via RECALL_METRIC_RE, which
|
|
38
|
+
// demands a scoring context. `precision` stays: it has no command collision.
|
|
39
|
+
'accuracy', 'precision',
|
|
38
40
|
'error rate', 'detection rate', 'true positive',
|
|
39
41
|
// dz claim vocabulary — a harness monorepo claims coverage, test counts, catalogue sizes,
|
|
40
42
|
// and performance numbers, not WiFi-sensing PCK/MPJPE.
|
|
@@ -88,19 +90,89 @@ function scrubLine(lower: string): string {
|
|
|
88
90
|
|
|
89
91
|
function mentionsMetricTerm(lower: string, scrubbed: string): boolean {
|
|
90
92
|
if (METRIC_TERMS.some((t) => lower.includes(t))) return true;
|
|
93
|
+
// `recall` only in a scoring context (see RECALL_METRIC_RE). `precision` on the line is enough:
|
|
94
|
+
// "precision 0.9 / recall 0.8" is the canonical ML pair.
|
|
95
|
+
if (RECALL_METRIC_RE.test(scrubbed)) return true;
|
|
91
96
|
if (!HAS_NUMBER_RE.test(scrubbed)) return false;
|
|
92
97
|
return METRIC_TERMS_SHORT.some((re) => re.test(scrubbed));
|
|
93
98
|
}
|
|
94
99
|
|
|
100
|
+
/**
|
|
101
|
+
* The PARAGRAPH containing 1-based `line`: the maximal run of contiguous non-blank lines, bounded by
|
|
102
|
+
* a blank line, a heading, or a fence marker. The claim is DETECTED per line; its honesty tag and its
|
|
103
|
+
* reproducer may live anywhere in this run — prose wrapped at 100 columns routinely splits a number
|
|
104
|
+
* from its `MEASURED`, and flagging the author who tagged correctly is how a gate teaches its users to
|
|
105
|
+
* ignore it.
|
|
106
|
+
*
|
|
107
|
+
* LAUNDERING EXPOSURE, stated: one tag covers every claim in its paragraph. It cannot cross a blank
|
|
108
|
+
* line, a heading, or a fence.
|
|
109
|
+
*/
|
|
110
|
+
function paragraphAround(lines: readonly string[], i: number): string {
|
|
111
|
+
const bounds = (k: string): boolean => k.trim() === '' || HEADING_RE.test(k) || FENCE_RE.test(k);
|
|
112
|
+
let a = i;
|
|
113
|
+
let b = i;
|
|
114
|
+
while (a > 0 && !bounds(lines[a - 1] ?? '')) a--;
|
|
115
|
+
while (b < lines.length - 1 && !bounds(lines[b + 1] ?? '')) b++;
|
|
116
|
+
return lines.slice(a, b + 1).join('\n');
|
|
117
|
+
}
|
|
118
|
+
|
|
95
119
|
/** Tags that make a claim honest (case-insensitive). */
|
|
96
120
|
// `estimated` agrees with the `estimated: true` honest-uncertainty marker `dz usage` already
|
|
97
121
|
// emits — the two honesty systems must not contradict each other.
|
|
98
122
|
const HONEST_TAGS = ['measured', 'claimed', 'synthetic', 'unvalidated', 'baseline', 'estimated'];
|
|
99
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Is the 1-based `line` inside a fenced code block within `text`?
|
|
126
|
+
*
|
|
127
|
+
* OWNED HERE, re-exported by `claim-check-hook-policy.ts`. The engine skips fenced lines; the hook
|
|
128
|
+
* exempts them from its deny path. Two implementations of "inside a fence" would drift; one cannot.
|
|
129
|
+
*
|
|
130
|
+
* CommonMark allows BOTH ``` and ~~~ fences, and a fence closes only on its OWN marker — a ``` inside
|
|
131
|
+
* a ~~~ block is literal content, not a toggle. A naive toggle counter that accepts either marker
|
|
132
|
+
* mis-tracks nesting, so track the open marker instead. Never throws.
|
|
133
|
+
*/
|
|
134
|
+
export function isFenced(text: string, line: number): boolean {
|
|
135
|
+
if (typeof text !== 'string' || typeof line !== 'number' || !isFinite(line) || line < 1) return false;
|
|
136
|
+
const lines = text.split(/\r?\n/);
|
|
137
|
+
const upTo = Math.min(line - 1, lines.length);
|
|
138
|
+
let open: '`' | '~' | null = null;
|
|
139
|
+
for (let i = 0; i < upTo; i++) {
|
|
140
|
+
const m = FENCE_RE.exec(lines[i] || '');
|
|
141
|
+
if (!m) continue;
|
|
142
|
+
const marker = m[1]![0] as '`' | '~';
|
|
143
|
+
if (open === null) open = marker;
|
|
144
|
+
else if (open === marker) open = null;
|
|
145
|
+
}
|
|
146
|
+
return open !== null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const FENCE_RE = /^\s*(`{3,}|~{3,})/;
|
|
150
|
+
const HEADING_RE = /^\s{0,3}#{1,6}\s/;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* `recall` is ruview's completeness metric AND this repo's command name (`dz recall`). Matching it as
|
|
154
|
+
* a bare term produced 31 false positives (MEASURED — reproducer `dz claim-check --json`), every one
|
|
155
|
+
* of them prose about the command. It counts as a metric only in a scoring context: paired with
|
|
156
|
+
* `precision`, or immediately followed by a score (`recall@5`, `recall rate`, `recall of 0.9`,
|
|
157
|
+
* `recall = 0.9`, `recall score`).
|
|
158
|
+
*/
|
|
159
|
+
const RECALL_METRIC_RE = /\brecall\s*(?:@\s*\d|rate\b|score\b|of\s+[\d.]|[:=]\s*[\d.])/i;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* A shell reproducer is STRUCTURAL, never a word. `(MEASURED — reproducer)` is self-certifying and
|
|
163
|
+
* must not pass; a backticked span whose first token is a command this repo actually measures with is
|
|
164
|
+
* evidence. The allowlist boundary is exactly that: an unknown binary is a claim ABOUT evidence.
|
|
165
|
+
*/
|
|
166
|
+
const SHELL_REPRO_RE =
|
|
167
|
+
/`\s*\$?\s*(?:ps|stat|lsof|time|git|npm|npx|node|pnpm|yarn|dz|curl|wc|grep|find|cargo|make|docker|kubectl|awk|sed|du|df|vitest|pytest)\b[^`]*`/i;
|
|
168
|
+
|
|
100
169
|
/** Reproducer references that count as evidence backing a MEASURED claim. */
|
|
101
170
|
const REPRODUCER_HINTS = [
|
|
102
171
|
// Generic evidence hints kept from ruview.
|
|
103
|
-
|
|
172
|
+
// 'reproduce' was inherited from ruview and is SELF-CERTIFYING: it makes the bare word "reproducer"
|
|
173
|
+
// count as its own evidence, so `(MEASURED — reproducer)` passed. Removed (D5). Evidence must be a
|
|
174
|
+
// named command or artifact — see SHELL_REPRO_RE for the structural form.
|
|
175
|
+
'baseline', 'sha256', 'tarball', 'cargo test',
|
|
104
176
|
// Packaging-claim reproducers (npm reviews): the tarball itself.
|
|
105
177
|
'npm pack', 'npm view', 'npm i ', 'npm install',
|
|
106
178
|
// dz reproducers — the actual commands/artifacts that back a dz claim.
|
|
@@ -130,14 +202,25 @@ export function claimCheck(text: string): ClaimCheckResult {
|
|
|
130
202
|
lines.forEach((raw, i) => {
|
|
131
203
|
const original = raw.trim();
|
|
132
204
|
if (!original) return;
|
|
205
|
+
// A fenced block is CODE, not prose. The hook already exempts fenced lines from its deny path
|
|
206
|
+
// (it re-exports `isFenced` from here); the engine flagging them was not a policy, it was a bug —
|
|
207
|
+
// 127 of this repo's 488 findings lived inside fences, and NONE of them was `high`
|
|
208
|
+
// (MEASURED — reproducer `dz claim-check --json`).
|
|
209
|
+
// EXPOSURE, accepted: a claim written inside a fence is now unreachable. That is exactly the right
|
|
210
|
+
// the inline backtick escape already grants; a fence is its block-level form.
|
|
211
|
+
if (isFenced(text, i + 1)) return;
|
|
133
212
|
// Analyse the URL-stripped line, but report the ORIGINAL so the excerpt stays recognisable.
|
|
134
213
|
const line = stripUrls(original);
|
|
135
214
|
if (!line.trim()) return;
|
|
136
215
|
const lower = line.toLowerCase();
|
|
137
216
|
|
|
138
|
-
const hasPercent = PERCENT_RE.test(line);
|
|
139
|
-
PERCENT_RE.lastIndex = 0; // reset stateful global regex
|
|
140
217
|
const scrubbed = scrubLine(lower);
|
|
218
|
+
// The percent TRIGGER reads the code-span-scrubbed line. Before this, `hasPercent` was computed on
|
|
219
|
+
// the UN-scrubbed line while metric terms were matched on the scrubbed one, so a backticked `99%`
|
|
220
|
+
// still opened the scan: the backtick escape downgraded a quoted claim from `high` to `medium`
|
|
221
|
+
// instead of silencing it. Documentation that quotes a forbidden claim was punished for doing so.
|
|
222
|
+
const hasPercent = PERCENT_RE.test(scrubbed);
|
|
223
|
+
PERCENT_RE.lastIndex = 0; // reset stateful global regex
|
|
141
224
|
// DELIBERATE DIVERGENCE from ruview, which matched METRIC_TERMS against the UNSCRUBBED line.
|
|
142
225
|
// dz's vocabulary contains path-like words ('packages', 'commands', 'skills'), so a code span
|
|
143
226
|
// such as `packages/x/usage.test.ts:42` registered as a metric mention and produced a false
|
|
@@ -147,8 +230,13 @@ export function claimCheck(text: string): ClaimCheckResult {
|
|
|
147
230
|
const mentionsMetric = mentionsMetricTerm(scrubbed, scrubbed);
|
|
148
231
|
if (!hasPercent && !mentionsMetric) return;
|
|
149
232
|
|
|
150
|
-
|
|
151
|
-
|
|
233
|
+
// The claim is detected on its own line; its honesty tag and reproducer may live anywhere in the
|
|
234
|
+
// same paragraph. Wrapped prose routinely puts `MEASURED` on the next line.
|
|
235
|
+
const para = stripUrls(paragraphAround(lines, i));
|
|
236
|
+
const paraLower = para.toLowerCase();
|
|
237
|
+
const tagged = HONEST_TAGS.some((t) => paraLower.includes(t));
|
|
238
|
+
const hasReproducer =
|
|
239
|
+
REPRODUCER_HINTS.some((h) => paraLower.includes(h)) || SHELL_REPRO_RE.test(para);
|
|
152
240
|
const perfect = PERFECT_WORD_RE.test(line) || (mentionsMetric && PERFECT_PCT_RE.test(line));
|
|
153
241
|
|
|
154
242
|
if (perfect && !lower.includes('retract')) {
|