@dzhechkov/harness-core 0.7.3 → 0.7.5

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.
Files changed (72) hide show
  1. package/.dz-manifest.json +131 -51
  2. package/README.md +1 -1
  3. package/dist/agentdb-index.d.ts.map +1 -1
  4. package/dist/agentdb-index.js +26 -2
  5. package/dist/agentdb-index.js.map +1 -1
  6. package/dist/amendment-trace.d.ts.map +1 -1
  7. package/dist/amendment-trace.js +6 -1
  8. package/dist/amendment-trace.js.map +1 -1
  9. package/dist/cadence.d.ts +66 -0
  10. package/dist/cadence.d.ts.map +1 -0
  11. package/dist/cadence.js +222 -0
  12. package/dist/cadence.js.map +1 -0
  13. package/dist/cli-flag-notice.d.ts +50 -0
  14. package/dist/cli-flag-notice.d.ts.map +1 -0
  15. package/dist/cli-flag-notice.js +106 -0
  16. package/dist/cli-flag-notice.js.map +1 -0
  17. package/dist/feature-adr-routing.d.ts.map +1 -1
  18. package/dist/feature-adr-routing.js +1 -1
  19. package/dist/feature-adr-routing.js.map +1 -1
  20. package/dist/index.d.ts +8 -2
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +6 -2
  23. package/dist/index.js.map +1 -1
  24. package/dist/loop-blobs.generated.js +2 -2
  25. package/dist/loop-blobs.generated.js.map +1 -1
  26. package/dist/operations.d.ts.map +1 -1
  27. package/dist/operations.js +61 -19
  28. package/dist/operations.js.map +1 -1
  29. package/dist/publish.d.ts +31 -0
  30. package/dist/publish.d.ts.map +1 -1
  31. package/dist/publish.js +78 -0
  32. package/dist/publish.js.map +1 -1
  33. package/dist/recall-hook-policy.d.ts +3 -1
  34. package/dist/recall-hook-policy.d.ts.map +1 -1
  35. package/dist/recall-hook-policy.js +15 -2
  36. package/dist/recall-hook-policy.js.map +1 -1
  37. package/dist/recall-usage.d.ts +15 -0
  38. package/dist/recall-usage.d.ts.map +1 -1
  39. package/dist/recall-usage.js +51 -1
  40. package/dist/recall-usage.js.map +1 -1
  41. package/dist/score.d.ts.map +1 -1
  42. package/dist/score.js +38 -4
  43. package/dist/score.js.map +1 -1
  44. package/dist/tg-post.d.ts +54 -0
  45. package/dist/tg-post.d.ts.map +1 -0
  46. package/dist/tg-post.js +117 -0
  47. package/dist/tg-post.js.map +1 -0
  48. package/dist/usage.d.ts +31 -0
  49. package/dist/usage.d.ts.map +1 -1
  50. package/dist/usage.js +108 -21
  51. package/dist/usage.js.map +1 -1
  52. package/dist/writer-quiescence.d.ts +42 -0
  53. package/dist/writer-quiescence.d.ts.map +1 -0
  54. package/dist/writer-quiescence.js +82 -0
  55. package/dist/writer-quiescence.js.map +1 -0
  56. package/package.json +13 -13
  57. package/sbom.json +250 -50
  58. package/src/agentdb-index.ts +26 -2
  59. package/src/amendment-trace.ts +6 -1
  60. package/src/cadence.ts +227 -0
  61. package/src/cli-flag-notice.ts +114 -0
  62. package/src/feature-adr-routing.ts +1 -1
  63. package/src/index.ts +8 -1
  64. package/src/loop-blobs.generated.ts +2 -2
  65. package/src/operations.ts +60 -20
  66. package/src/publish.ts +98 -0
  67. package/src/recall-hook-policy.ts +15 -2
  68. package/src/recall-usage.ts +44 -1
  69. package/src/score.ts +30 -4
  70. package/src/tg-post.ts +137 -0
  71. package/src/usage.ts +132 -17
  72. package/src/writer-quiescence.ts +95 -0
package/dist/score.js CHANGED
@@ -50,7 +50,12 @@ const NEGATION_RE = /\b(no|not|never|without|wasn'?t|isn'?t)\b/i;
50
50
  * `\bno\b` does NOT match "Nothing". Hedges like "skipped" stay out of both lists — they routinely
51
51
  * appear inside genuine evidence lines.
52
52
  */
53
- const NEGATION_QUANTIFIED_RE = /\b(no|not|never|without|nothing|none|neither|nor|nobody|wasn'?t|isn'?t)\b/i;
53
+ // RU negation quantifiers joined 2026-08-24 with the RU live-markers (773185ca): a corpus where
54
+ // 63% of traffic is Russian was screened by an English-only list — «ничего не измерено» would have
55
+ // read as a live marker the moment ИЗМЕРЕНО joined the positives.
56
+ // \b is ASCII-only in JS even under /u — «не» never matched through it (measured by the pin the
57
+ // moment it was written). Unicode lookarounds carry the boundary instead.
58
+ const NEGATION_QUANTIFIED_RE = /\b(no|not|never|without|nothing|none|neither|nor|nobody|wasn'?t|isn'?t)\b|(?<![\p{L}\p{N}])(не|нет|ни одного|ничего|никогда|без)(?![\p{L}\p{N}])/iu;
54
59
  function evidenceLinePositive(text, re, negationRe = NEGATION_RE) {
55
60
  for (const line of text.split('\n')) {
56
61
  if (!re.test(line))
@@ -99,7 +104,33 @@ function normaliseGradeSign(grade) {
99
104
  */
100
105
  export function readQeGrade(qeText) {
101
106
  const found = [];
107
+ const lines = qeText.split('\n');
108
+ // Line offsets once, so each match maps to ITS line for the negation screen (67d7883d: the
109
+ // parser was negation-blind — «No Grade: B was assigned» contributed B and a cross-model PASS;
110
+ // the display-locator fix could not reach this because the GRADE rests here). The same
111
+ // deliberate trade as evidenceLinePositive: a genuine grade on a line that happens to carry a
112
+ // negation is SKIPPED (visible in `found`'s absence) rather than a negated line being COUNTED.
113
+ const lineStarts = [0];
114
+ for (let i = 0; i < lines.length - 1; i++)
115
+ lineStarts.push(lineStarts[i] + lines[i].length + 1);
116
+ // The screen covers the line PREFIX up to the match, not the whole line: «No Grade: B was
117
+ // assigned» negates BEFORE the grade; «**Grade: B** — no blockers remain» carries its negation
118
+ // AFTER, about something else entirely, and whole-line screening dropped that real, common
119
+ // phrase (caught by the standing display-locator pin the moment the sweep was «completed»).
120
+ const linePrefixOf = (idx) => {
121
+ let lo = 0, hi = lineStarts.length - 1;
122
+ while (lo < hi) {
123
+ const mid = (lo + hi + 1) >> 1;
124
+ if (lineStarts[mid] <= idx)
125
+ lo = mid;
126
+ else
127
+ hi = mid - 1;
128
+ }
129
+ return qeText.slice(lineStarts[lo], idx);
130
+ };
102
131
  for (const m of qeText.matchAll(new RegExp(GRADE_RE.source, 'g'))) {
132
+ if (NEGATION_QUANTIFIED_RE.test(linePrefixOf(m.index ?? 0)))
133
+ continue;
103
134
  const g = normaliseGradeSign(m[1]);
104
135
  if (!found.includes(g))
105
136
  found.push(g);
@@ -170,9 +201,12 @@ export function scoreRun(slug, artifacts) {
170
201
  // report is the cautionary case: "✅ (mechanism)" with no live evidence shipped a dead feature.
171
202
  // POSITIVE (wave1-scorer-negation): "nothing was MEASURED" / "no reproducer was run" is the
172
203
  // claim's exact opposite and used to score as proof of it (the crossrt-1 6/7 shape).
173
- const live = evidenceLinePositive(qeText, /MEASURED|verified live|VERIFIED LIVE|reproducer/i, NEGATION_QUANTIFIED_RE);
174
- const liveAnywhere = live ?? evidenceLinePositive(allText, /MEASURED|verified live|VERIFIED LIVE|reproducer/i, NEGATION_QUANTIFIED_RE);
175
- add('live-verification', 'claims verified by running, not by reasoning', live !== null ? 'pass' : liveAnywhere !== null ? 'partial' : 'absent', live ?? liveAnywhere ?? 'no MEASURED / verified-live / reproducer marker anywhere — every claim is inferred');
204
+ // 773185ca: MEASURED-class markers in BOTH working languages. dz-recap carried 10× ИЗМЕРЕНО and
205
+ // MEASURED and scored «всё выведено рассуждением» the cyrillic-tokenizer class again.
206
+ const LIVE_MARKER_RE = /MEASURED|verified live|VERIFIED LIVE|reproducer|ИЗМЕРЕНО|МЕРЕНО|измерено|проверено живьём|живой прогон|репродьюсер/iu;
207
+ const live = evidenceLinePositive(qeText, LIVE_MARKER_RE, NEGATION_QUANTIFIED_RE);
208
+ const liveAnywhere = live ?? evidenceLinePositive(allText, LIVE_MARKER_RE, NEGATION_QUANTIFIED_RE);
209
+ add('live-verification', 'claims verified by running, not by reasoning', live !== null ? 'pass' : liveAnywhere !== null ? 'partial' : 'absent', live ?? liveAnywhere ?? 'no MEASURED/ИЗМЕРЕНО/verified-live/reproducer marker anywhere — every claim is inferred');
176
210
  // 5. README-first — the docs travelled in the same change.
177
211
  // POSITIVE (wave1-scorer-negation): THE acid-A5 defect. crossrt-1-agents-md scored ✓ here over
178
212
  // its own finding "README-first not satisfied — no README was touched": a negation rendered as
package/dist/score.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"score.js","sourceRoot":"","sources":["../src/score.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"score.js","sourceRoot":"","sources":["../src/score.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * `dz tg-post` — the pure half: validate an approved draft against what Telegram will accept and
3
+ * what the channel's own accepted design demands.
4
+ *
5
+ * The design is NOT this module's to invent. features/genai-tweets-channel/ carries four ACCEPTED
6
+ * ADRs (2026-08-04): HTML mode, never MarkdownV2 (18 escapes against 3, one miss is a 400); the
7
+ * cadence rule "no posts 00:00-06:00 MSK"; link previews off by default because x.com previews in
8
+ * Telegram have been broken since 2022; and ADR-004's standing order — publishing stays MANUAL, and
9
+ * autonomous publishing means REVISING the ADR, not flipping a quiet flag. This module only makes
10
+ * those decisions checkable.
11
+ *
12
+ * PURE: no filesystem, no network, no clock — the timestamp arrives as a parameter, or the MSK
13
+ * night-window rule could not be tested at all.
14
+ */
15
+ /** Tags Bot API accepts in HTML mode (verified against the live docs, Bot API 10.2). */
16
+ export declare const TG_ALLOWED_TAGS: readonly string[];
17
+ /** Hard ceiling for `sendMessage.text`, characters after entity parsing. */
18
+ export declare const TG_TEXT_LIMIT = 4096;
19
+ export interface TgHtmlIssue {
20
+ readonly kind: 'unknown-tag' | 'unclosed-tag' | 'stray-close' | 'bare-ampersand' | 'bare-angle' | 'over-limit' | 'empty';
21
+ readonly detail: string;
22
+ }
23
+ /**
24
+ * The character count Telegram limits: text WITHOUT the markup. An approximation is stated as one —
25
+ * entities like tg-emoji count differently — but a draft within this bound by a margin is safe, and
26
+ * the render prints the number so the author sees the headroom, not a verdict.
27
+ */
28
+ export declare function tgVisibleLength(html: string): number;
29
+ /**
30
+ * Everything wrong with a draft, or an empty list. One pass, every finding named — a validator that
31
+ * stops at the first fault sends the author around the loop once per mistake.
32
+ */
33
+ export declare function tgPostHtmlIssues(html: string): TgHtmlIssue[];
34
+ export interface TgSendDecision {
35
+ readonly action: 'send' | 'refuse';
36
+ readonly reason: string;
37
+ }
38
+ /**
39
+ * May this draft go out NOW?
40
+ *
41
+ * The night window is ADR-003's cadence rule, encoded as a refusal with an explicit override rather
42
+ * than as advice: 00:00-06:00 MSK is when the channel's audience is asleep and its author is too —
43
+ * a send landing then is far more often a timezone mistake than an intention. `--night` states the
44
+ * intention; without it the refusal names the local MSK time it computed, so the operator can check
45
+ * the arithmetic instead of trusting it.
46
+ */
47
+ export declare function decideTgSend(input: {
48
+ readonly issues: readonly TgHtmlIssue[];
49
+ readonly provenanceOutcome: 'allowed' | 'blocked' | 'not-established' | 'skipped';
50
+ readonly confirmed: boolean;
51
+ readonly nowUtcIso: string;
52
+ readonly nightOverride: boolean;
53
+ }): TgSendDecision;
54
+ //# sourceMappingURL=tg-post.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tg-post.d.ts","sourceRoot":"","sources":["../src/tg-post.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,wFAAwF;AACxF,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAG5C,CAAC;AAEF,4EAA4E;AAC5E,eAAO,MAAM,aAAa,OAAO,CAAC;AAElC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,aAAa,GAAG,cAAc,GAAG,aAAa,GAAG,gBAAgB,GAAG,YAAY,GAAG,YAAY,GAAG,OAAO,CAAC;IACzH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CA+C5D;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;IACxC,QAAQ,CAAC,iBAAiB,EAAE,SAAS,GAAG,SAAS,GAAG,iBAAiB,GAAG,SAAS,CAAC;IAClF,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC,GAAG,cAAc,CAyBjB"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * `dz tg-post` — the pure half: validate an approved draft against what Telegram will accept and
3
+ * what the channel's own accepted design demands.
4
+ *
5
+ * The design is NOT this module's to invent. features/genai-tweets-channel/ carries four ACCEPTED
6
+ * ADRs (2026-08-04): HTML mode, never MarkdownV2 (18 escapes against 3, one miss is a 400); the
7
+ * cadence rule "no posts 00:00-06:00 MSK"; link previews off by default because x.com previews in
8
+ * Telegram have been broken since 2022; and ADR-004's standing order — publishing stays MANUAL, and
9
+ * autonomous publishing means REVISING the ADR, not flipping a quiet flag. This module only makes
10
+ * those decisions checkable.
11
+ *
12
+ * PURE: no filesystem, no network, no clock — the timestamp arrives as a parameter, or the MSK
13
+ * night-window rule could not be tested at all.
14
+ */
15
+ /** Tags Bot API accepts in HTML mode (verified against the live docs, Bot API 10.2). */
16
+ export const TG_ALLOWED_TAGS = [
17
+ 'b', 'strong', 'i', 'em', 'u', 'ins', 's', 'strike', 'del', 'tg-spoiler',
18
+ 'a', 'tg-emoji', 'code', 'pre', 'blockquote',
19
+ ];
20
+ /** Hard ceiling for `sendMessage.text`, characters after entity parsing. */
21
+ export const TG_TEXT_LIMIT = 4096;
22
+ /**
23
+ * The character count Telegram limits: text WITHOUT the markup. An approximation is stated as one —
24
+ * entities like tg-emoji count differently — but a draft within this bound by a margin is safe, and
25
+ * the render prints the number so the author sees the headroom, not a verdict.
26
+ */
27
+ export function tgVisibleLength(html) {
28
+ return html.replace(/<[^>]*>/g, '').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&').length;
29
+ }
30
+ /**
31
+ * Everything wrong with a draft, or an empty list. One pass, every finding named — a validator that
32
+ * stops at the first fault sends the author around the loop once per mistake.
33
+ */
34
+ export function tgPostHtmlIssues(html) {
35
+ const issues = [];
36
+ const text = html.trim();
37
+ if (text === '')
38
+ return [{ kind: 'empty', detail: 'the draft is empty — nothing to send' }];
39
+ // Tag balance over the allowed set. Telegram closes nothing for you: an unclosed <b> is a 400.
40
+ const stack = [];
41
+ const tagRe = /<(\/?)([a-zA-Z-]+)((?:\s+[a-zA-Z-]+(?:="[^"]*")?)*)\s*(\/?)>/g;
42
+ let covered = 0;
43
+ for (let m = tagRe.exec(text); m !== null; m = tagRe.exec(text)) {
44
+ covered += 1;
45
+ const closing = m[1] === '/';
46
+ const name = m[2].toLowerCase();
47
+ const expandable = name === 'blockquote'; // `<blockquote expandable>` is the ADR-003 body form
48
+ if (!TG_ALLOWED_TAGS.includes(name) && !expandable) {
49
+ issues.push({ kind: 'unknown-tag', detail: `<${name}> is not a Bot API HTML tag — Telegram answers 400 to tags it does not know` });
50
+ continue;
51
+ }
52
+ if (closing) {
53
+ if (stack.length === 0 || stack[stack.length - 1] !== name) {
54
+ issues.push({ kind: 'stray-close', detail: `</${name}> closes nothing that is open — tags must nest, not interleave` });
55
+ }
56
+ else {
57
+ stack.pop();
58
+ }
59
+ }
60
+ else if (m[4] !== '/') {
61
+ stack.push(name);
62
+ }
63
+ }
64
+ for (const open of stack) {
65
+ issues.push({ kind: 'unclosed-tag', detail: `<${open}> is never closed — Telegram closes nothing for you, this is a 400` });
66
+ }
67
+ // Bare & and < outside tags: HTML mode requires entity-escaping exactly these.
68
+ const outside = text.replace(/<[^>]*>/g, '');
69
+ if (/&(?!(lt|gt|amp|quot|#\d+|#x[0-9a-fA-F]+);)/.test(outside)) {
70
+ issues.push({ kind: 'bare-ampersand', detail: 'a bare & outside an entity — HTML mode needs &amp;' });
71
+ }
72
+ if (/</.test(outside.replace(/&lt;/g, ''))) {
73
+ issues.push({ kind: 'bare-angle', detail: 'a bare < that is not a known tag — Telegram reads it as markup and answers 400' });
74
+ }
75
+ const visible = tgVisibleLength(text);
76
+ if (visible > TG_TEXT_LIMIT) {
77
+ issues.push({ kind: 'over-limit', detail: `${visible} visible characters against the ${TG_TEXT_LIMIT} hard limit — cut ${visible - TG_TEXT_LIMIT}` });
78
+ }
79
+ void covered;
80
+ return issues;
81
+ }
82
+ /**
83
+ * May this draft go out NOW?
84
+ *
85
+ * The night window is ADR-003's cadence rule, encoded as a refusal with an explicit override rather
86
+ * than as advice: 00:00-06:00 MSK is when the channel's audience is asleep and its author is too —
87
+ * a send landing then is far more often a timezone mistake than an intention. `--night` states the
88
+ * intention; without it the refusal names the local MSK time it computed, so the operator can check
89
+ * the arithmetic instead of trusting it.
90
+ */
91
+ export function decideTgSend(input) {
92
+ if (input.issues.length > 0) {
93
+ return { action: 'refuse', reason: `${input.issues.length} formatting issue(s) — Telegram would refuse or mangle this draft` };
94
+ }
95
+ // The provenance gate is not optional and "skipped" is not a pass: ADR-002 of the provenance
96
+ // feature — nothing leaves this machine citing a source that may not.
97
+ if (input.provenanceOutcome !== 'allowed') {
98
+ return {
99
+ action: 'refuse',
100
+ reason: input.provenanceOutcome === 'skipped'
101
+ ? 'no provenance manifest was checked — an unchecked draft is not an approved draft'
102
+ : `the provenance gate said ${input.provenanceOutcome} — nothing goes out citing a source that may not leave this machine`,
103
+ };
104
+ }
105
+ if (!input.confirmed) {
106
+ return { action: 'refuse', reason: 'publishing is MANUAL by the channel\'s own ADR-004 — pass --send --yes to state the decision out loud' };
107
+ }
108
+ const utc = Date.parse(input.nowUtcIso);
109
+ if (Number.isFinite(utc)) {
110
+ const mskHour = new Date(utc + 3 * 3600_000).getUTCHours();
111
+ if (mskHour < 6 && !input.nightOverride) {
112
+ return { action: 'refuse', reason: `it is ${String(mskHour).padStart(2, '0')}:xx MSK — the channel posts nothing between 00:00 and 06:00 MSK (ADR-003). Pass --night if this is deliberate` };
113
+ }
114
+ }
115
+ return { action: 'send', reason: 'formatted, provenance-cleared, confirmed, and inside posting hours' };
116
+ }
117
+ //# sourceMappingURL=tg-post.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tg-post.js","sourceRoot":"","sources":["../src/tg-post.ts"],"names":[],"mappings":"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"}
package/dist/usage.d.ts CHANGED
@@ -93,6 +93,11 @@ export interface UsageLimits {
93
93
  readonly sessionBlockHours?: number;
94
94
  readonly calibratedAt?: string;
95
95
  readonly source?: string;
96
+ /** Routing must not read the estimated pcts (ADR-001 usage-honesty, FR-3). Legacy
97
+ * `_disabledReason` free-text also reads as true — the note WAS the switch, now it is data. */
98
+ readonly routingDisabled?: boolean;
99
+ /** Account identity captured at calibration time (FR-4); a login change stales the calibration. */
100
+ readonly calibrationAccount?: string | null;
96
101
  }
97
102
  export interface UsageModelEstimate {
98
103
  readonly tokens: number;
@@ -123,6 +128,24 @@ export interface UsageEstimate {
123
128
  /** Traceability for tests and calibration diagnostics. */
124
129
  readonly sessionStartedAt: string | null;
125
130
  readonly weeklyStartedAt: string | null;
131
+ /**
132
+ * Why the routed pcts are null (ADR-001 usage-honesty): empty ⇔ the numbers are established.
133
+ * `scan-empty` — recent transcripts exist yet the scan extracted nothing (instrument failure);
134
+ * `window-miss:*` — samples exist but the configured window filtered them all (misaligned
135
+ * anchor); `routing-disabled` — config says the estimates must not steer; `calibration-stale` —
136
+ * the account changed since calibration. A zero pct is legitimate ONLY on a machine with no
137
+ * recent transcripts at all.
138
+ */
139
+ readonly notEstablished: readonly UsageNotEstablishedReason[];
140
+ /**
141
+ * The raw estimates when the routed pcts are nulled by `routing-disabled`/`calibration-stale` —
142
+ * for HUMAN eyes (recalled lesson: an estimated pct must never drive routing until its window is
143
+ * verified against the provider). Absent when the top-level pcts already carry the numbers.
144
+ */
145
+ readonly estimatesNotForRouting?: {
146
+ readonly sessionPct: number | null;
147
+ readonly weeklyPct: number | null;
148
+ };
126
149
  }
127
150
  export interface UsageCalibrationInput {
128
151
  readonly sessionPct?: unknown;
@@ -158,6 +181,14 @@ export declare function normalizeClaudeUsageModelKey(raw: unknown): ClaudeUsageM
158
181
  * The `~/.claude/projects` root (the account-wide transcript store). Overridable via
159
182
  * `DZ_CLAUDE_PROJECTS_ROOT` — used by tests to point at a temp tree. Never throws.
160
183
  */
184
+ /**
185
+ * The logged-in account identity, from `~/.claude.json` (oauthAccount email or uuid). Honest null
186
+ * when unreadable/absent — and null==null is NOT an account change (machines that never expose it
187
+ * keep the pre-FR-4 behavior). Never throws.
188
+ */
189
+ export declare function readClaudeAccountId(): string | null;
190
+ /** Closed set of not-established reasons (ADR-001). */
191
+ export type UsageNotEstablishedReason = 'scan-empty' | 'window-miss:weekly' | 'routing-disabled' | 'calibration-stale:account-changed' | 'calibration-stale:account-unverifiable';
161
192
  export declare function claudeProjectsRoot(): string;
162
193
  /**
163
194
  * Read `memory.usage.*` from `<projectRoot>/.dz/config.json`. NEVER throws —
@@ -1 +1 @@
1
- {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAYH;;;;GAIG;AACH,eAAO,MAAM,aAAa;;;;;;CAAsF,CAAC;AAEjH,gFAAgF;AAChF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,+FAA+F;IAC/F,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAMD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAiBzD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAoBvD;AAED,eAAO,MAAM,mBAAmB,+CAAgD,CAAC;AACjF,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;8DAK0D;IAC1D,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,kGAAkG;AAClG,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7E,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,8FAA8F;AAC9F,MAAM,WAAW,aAAa;IAC5B,oEAAoE;IACpE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,4DAA4D;IAC5D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,2EAA2E;IAC3E,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,wFAAwF;IACxF,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,iGAAiG;IACjG,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,gDAAgD;IAChD,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,uFAAuF;IACvF,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,gEAAgE;IAChE,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAC/E,2FAA2F;IAC3F,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAAC;IACxE,0EAA0E;IAC1E,QAAQ,CAAC,kBAAkB,CAAC,EAAE,gBAAgB,CAAC;IAC/C,0DAA0D;IAC1D,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CACzC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAC;CAC7C;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,SAAS,sBAAsB,EAAE,CAAC;IACpD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AA+CD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAgB/E;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAuCjF;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAOzF;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CAQ/E;AAED,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CAIlF;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAI3C;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,CAwChE;AA0MD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,aAAa,CA4E7E;AAyCD,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,qBAAqB,GAC3B,oBAAoB,CAkEtB"}
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"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"}
package/dist/usage.js CHANGED
@@ -211,10 +211,55 @@ export function normalizeClaudeUsageModelKey(raw) {
211
211
  const key = raw.trim().toLowerCase();
212
212
  return CLAUDE_USAGE_MODELS.includes(key) ? key : null;
213
213
  }
214
+ /** Recursive .jsonl collector under a subagents tree — bounded depth, lstat-guarded. */
215
+ function walkTranscriptTree(dir, depthLeft, out) {
216
+ if (depthLeft <= 0)
217
+ return;
218
+ let entries;
219
+ try {
220
+ if (!lstatSync(dir).isDirectory())
221
+ return; // symlinked dir ⇒ not walked
222
+ entries = readdirSync(dir);
223
+ }
224
+ catch {
225
+ return;
226
+ }
227
+ for (const e of entries) {
228
+ const p = join(dir, e);
229
+ if (e.endsWith('.jsonl')) {
230
+ const m = regularFileMtime(p);
231
+ if (m !== null)
232
+ out.push({ path: p, mtimeMs: m });
233
+ }
234
+ else {
235
+ walkTranscriptTree(p, depthLeft - 1, out);
236
+ }
237
+ }
238
+ }
214
239
  /**
215
240
  * The `~/.claude/projects` root (the account-wide transcript store). Overridable via
216
241
  * `DZ_CLAUDE_PROJECTS_ROOT` — used by tests to point at a temp tree. Never throws.
217
242
  */
243
+ /**
244
+ * The logged-in account identity, from `~/.claude.json` (oauthAccount email or uuid). Honest null
245
+ * when unreadable/absent — and null==null is NOT an account change (machines that never expose it
246
+ * keep the pre-FR-4 behavior). Never throws.
247
+ */
248
+ export function readClaudeAccountId() {
249
+ try {
250
+ const raw = JSON.parse(readFileSync(join(homedir(), '.claude.json'), 'utf-8'));
251
+ const email = raw.oauthAccount?.emailAddress;
252
+ if (typeof email === 'string' && email !== '')
253
+ return email;
254
+ const uuid = raw.oauthAccount?.accountUuid;
255
+ if (typeof uuid === 'string' && uuid !== '')
256
+ return uuid;
257
+ return null;
258
+ }
259
+ catch {
260
+ return null;
261
+ }
262
+ }
218
263
  export function claudeProjectsRoot() {
219
264
  const override = process.env['DZ_CLAUDE_PROJECTS_ROOT'];
220
265
  if (typeof override === 'string' && override.length > 0)
@@ -268,6 +313,13 @@ export function readUsageLimits(projectRoot) {
268
313
  out.calibratedAt = u['calibratedAt'];
269
314
  if (typeof u['source'] === 'string')
270
315
  out.source = u['source'];
316
+ // routingDisabled: the boolean is authoritative; the legacy free-text note counts as true so
317
+ // the fleet's existing config disables TODAY, without an edit.
318
+ if (u['routingDisabled'] === true || typeof u['_disabledReason'] === 'string')
319
+ out.routingDisabled = true;
320
+ if (typeof u['calibrationAccount'] === 'string' || u['calibrationAccount'] === null) {
321
+ out.calibrationAccount = u['calibrationAccount'];
322
+ }
271
323
  return out;
272
324
  }
273
325
  catch {
@@ -316,26 +368,15 @@ function listTranscriptFiles(root) {
316
368
  continue;
317
369
  }
318
370
  for (const f of files) {
319
- // A session's SUBAGENT transcripts live one level deeper — `<session>/subagents/*.jsonl` and
320
- // carry real, non-duplicated usage that was silently excluded (MEASURED: 27 such files on this
321
- // machine). Their tokens are spent exactly like the main loop's.
371
+ // A session's SUBAGENT transcripts live under `<session>/subagents/` and carry real,
372
+ // non-duplicated usage that was silently excluded (MEASURED: 27 such files in the first
373
+ // round). The walk is RECURSIVE with a depth cap: workflow agents write to
374
+ // `subagents/workflows/wf_*/agent-*.jsonl` — one level deeper than the first fix reached —
375
+ // and that blind spot alone hid 283.62M weighted tokens across 551 files (MEASURED
376
+ // 2026-08-24, 7-day window, this machine). Depth 4 covers today's deepest layout plus one
377
+ // future level; lstat at EVERY step keeps symlinked directories unwalked.
322
378
  if (!f.endsWith('.jsonl')) {
323
- const nested = join(projDir, f, 'subagents');
324
- try {
325
- if (!lstatSync(nested).isDirectory())
326
- continue; // no symlinked session/subagent dirs
327
- for (const sf of readdirSync(nested)) {
328
- if (!sf.endsWith('.jsonl'))
329
- continue;
330
- const sp = join(nested, sf);
331
- const m = regularFileMtime(sp);
332
- if (m !== null)
333
- out.push({ path: sp, mtimeMs: m });
334
- }
335
- }
336
- catch {
337
- /* not a session dir — skip */
338
- }
379
+ walkTranscriptTree(join(projDir, f, 'subagents'), 4, out);
339
380
  continue;
340
381
  }
341
382
  const p = join(projDir, f);
@@ -480,12 +521,14 @@ export function computeUsage(projectRoot, now) {
480
521
  const scanCutoff = Math.min(sessionScanCutoff, weeklyScanCutoff) - MTIME_SLACK_MS;
481
522
  const samples = [];
482
523
  const seen = new Set();
524
+ let scanFileCount = 0;
483
525
  try {
484
526
  const files = listTranscriptFiles(claudeProjectsRoot());
485
527
  for (const f of files) {
486
528
  // mtime prefilter: a file last written before every relevant cutoff cannot contribute.
487
529
  if (f.mtimeMs < scanCutoff)
488
530
  continue;
531
+ scanFileCount += 1;
489
532
  extractSamples(f.path, scanCutoff, samples, seen);
490
533
  }
491
534
  }
@@ -522,11 +565,47 @@ export function computeUsage(projectRoot, now) {
522
565
  }
523
566
  }
524
567
  }
568
+ // ── Establishment gate (ADR-001): a number may only flow to the routed pct fields when the
569
+ // scan actually established it. Fail-closed in exactly four named ways; the raw estimates stay
570
+ // visible to humans under estimatesNotForRouting when policy (not measurement) nulls them.
571
+ const reasons = [];
572
+ const recentFiles = scanFileCount > 0;
573
+ if (recentFiles && samples.length === 0)
574
+ reasons.push('scan-empty');
575
+ // An IDLE session inside a busy week (block 0, weekly > 0) is a MEASURED zero, not a miss — the
576
+ // first cut of this gate flagged it and four standing tests rightly reddened. Second narrowing
577
+ // (cross-family review): a week that JUST reset over an idle machine still scans pre-reset
578
+ // samples (the session cutoff reaches 10h back), and weekly 0 is then a healthy fresh week. The
579
+ // true miss signature needs a sample AT or PAST the window start that the window still refuses —
580
+ // future-stamped (clock skew) or beyond-reset (stale anchor) — exactly d3639bf0's shape.
581
+ if (weeklyWindow !== null &&
582
+ weeklyTokens <= 0 &&
583
+ samples.some((smp) => smp.ts >= weeklyWindow.startedAtMs)) {
584
+ reasons.push('window-miss:weekly');
585
+ }
586
+ if (limits.routingDisabled === true)
587
+ reasons.push('routing-disabled');
588
+ const account = readClaudeAccountId();
589
+ if (limits.calibrationAccount !== undefined && limits.calibrationAccount !== null) {
590
+ // A stored identity DEMANDS verification (cross-family review: null-current was fail-open —
591
+ // an unreadable ~/.claude.json silently reused another account's calibration). A stored null
592
+ // stays exempt: those machines never claimed an identity to verify.
593
+ if (account === null)
594
+ reasons.push('calibration-stale:account-unverifiable');
595
+ else if (account !== limits.calibrationAccount)
596
+ reasons.push('calibration-stale:account-changed');
597
+ }
598
+ const rawSessionPct = pct(block.tokens, limits.sessionTokenLimit);
599
+ const rawWeeklyPct = weeklyPct;
600
+ const measurementBroken = reasons.some((r) => r === 'scan-empty' || r.startsWith('window-miss'));
601
+ const policyNulled = reasons.some((r) => r === 'routing-disabled' || r.startsWith('calibration-stale'));
602
+ const gatedSessionPct = measurementBroken || policyNulled ? null : rawSessionPct;
603
+ const gatedWeeklyPct = measurementBroken || policyNulled ? null : rawWeeklyPct;
525
604
  return {
526
605
  sessionTokens: block.tokens,
527
606
  weeklyTokens,
528
- sessionPct: pct(block.tokens, limits.sessionTokenLimit),
529
- weeklyPct,
607
+ sessionPct: gatedSessionPct,
608
+ weeklyPct: gatedWeeklyPct,
530
609
  sessionResetsAt: block.resetsAtMs === null ? null : new Date(block.resetsAtMs).toISOString(),
531
610
  weeklyResetsAt: weeklyWindow === null ? null : new Date(weeklyWindow.resetsAtMs).toISOString(),
532
611
  estimated: true,
@@ -535,6 +614,10 @@ export function computeUsage(projectRoot, now) {
535
614
  ...(weeklyBindingModel !== undefined ? { weeklyBindingModel } : {}),
536
615
  sessionStartedAt: block.startedAtMs === null ? null : new Date(block.startedAtMs).toISOString(),
537
616
  weeklyStartedAt: weeklyWindow === null ? null : new Date(weeklyWindow.startedAtMs).toISOString(),
617
+ notEstablished: reasons,
618
+ ...(policyNulled && !measurementBroken
619
+ ? { estimatesNotForRouting: { sessionPct: rawSessionPct, weeklyPct: rawWeeklyPct } }
620
+ : {}),
538
621
  };
539
622
  }
540
623
  function validPct(label, value, skipped) {
@@ -627,6 +710,10 @@ export function deriveUsageCalibration(current, before, input) {
627
710
  if (changes.length > 0) {
628
711
  after.calibratedAt = input.calibratedAt;
629
712
  after.source = input.source;
713
+ // FR-4: a calibration is a claim about ONE account's limits. Stamp whose — a later login under
714
+ // a different identity then stales it by itself (the 2026-08-24 re-login is the reproducer:
715
+ // the old anchor kept printing 55% on the new account).
716
+ after.calibrationAccount = readClaudeAccountId();
630
717
  }
631
718
  return { before, after, changes, skipped };
632
719
  }