instar 0.28.93 → 0.28.94

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.
@@ -0,0 +1,112 @@
1
+ /**
2
+ * DriftSpendLedger — daily-rotated, lock-coordinated cost ledger for the
3
+ * drift checker.
4
+ *
5
+ * Spec: docs/specs/PROJECT-SCOPE-SPEC.md § Phase 1.4 ("Cost ceiling").
6
+ *
7
+ * Contract:
8
+ * - Total drift-check spend per agent ≤ DEFAULT_DAILY_CAP_USD ($1) per UTC
9
+ * day. Cap enforcement is `spent + estimated > cap → reject` (strict
10
+ * `>` boundary, per spec corrected from iter 2).
11
+ * - One file per UTC day at `.instar/drift-spend-YYYY-MM-DD.jsonl`.
12
+ * - Old files retained ≤ DEFAULT_RETENTION_DAYS (30). Anything older is
13
+ * archived to `.instar/drift-spend-archive/<year>-<month>.tar.gz` by
14
+ * `pruneOlderThan()` (caller-driven; no internal timer).
15
+ * - Each call pre-reserves estimated cost in an append-only row
16
+ * `{recordId, projectId, estimatedCost, actualCost?: null, timestamp}`.
17
+ * After the call returns, `reconcile(recordId, actualCost)` appends a
18
+ * second row with `actualCost` set, so a tally over the day is
19
+ * `sum(estimatedCost where actualCost is null) +
20
+ * sum(actualCost where present)` — O(rows in the day).
21
+ * - Advisory file lock on `.instar/local/drift-spend.lock` via
22
+ * proper-lockfile (consistent with AgentRegistry, SharedStateLedger,
23
+ * PlatformActivityRegistry). Lock file lives under `.instar/local/`
24
+ * (machine-local, NOT git-synced — same convention as
25
+ * `round-runner.lock`).
26
+ * - Multi-machine semantics: each machine writes its own ledger. The
27
+ * cap is per-machine, summed via git-sync at the file layer. The spec
28
+ * documents this as "up to N machines × cap/day in worst case"; the
29
+ * true atomic cross-machine cap is a separately-tracked deferred
30
+ * child (`drift-spend-cross-machine`).
31
+ */
32
+ export declare const DEFAULT_DAILY_CAP_USD = 1;
33
+ export declare const DEFAULT_RETENTION_DAYS = 30;
34
+ export interface DriftSpendLedgerConfig {
35
+ /** Absolute path to the agent's `.instar/` directory. */
36
+ stateDir: string;
37
+ /** Cap in USD per UTC day. Defaults to $1. Tests can lower. */
38
+ dailyCapUsd?: number;
39
+ /** Retention window in days; defaults to 30. */
40
+ retentionDays?: number;
41
+ }
42
+ export interface ReserveInput {
43
+ projectId: string;
44
+ /** Best-effort upfront estimate; refined by `reconcile`. */
45
+ estimatedCost: number;
46
+ }
47
+ export interface ReserveResult {
48
+ /** Stable id used to reconcile actualCost later. */
49
+ recordId: string;
50
+ /** Cumulative spent today AFTER this reservation. */
51
+ spentToday: number;
52
+ }
53
+ export declare class OverBudgetError extends Error {
54
+ capUsd: number;
55
+ spentToday: number;
56
+ estimatedCost: number;
57
+ constructor(capUsd: number, spentToday: number, estimatedCost: number);
58
+ }
59
+ export declare class DriftSpendLedger {
60
+ private stateDir;
61
+ private capUsd;
62
+ private retentionDays;
63
+ constructor(config: DriftSpendLedgerConfig);
64
+ /**
65
+ * Pre-reserve `estimatedCost`. Throws `OverBudgetError` if doing so would
66
+ * exceed the daily cap. The reservation is appended as a row with
67
+ * `actualCost: null`; the caller MUST call `reconcile()` after the LLM
68
+ * returns. If the caller dies before reconcile, the reservation stays in
69
+ * place as `estimatedCost` and contributes to the cap until UTC rollover.
70
+ */
71
+ reserve(input: ReserveInput): Promise<ReserveResult>;
72
+ /**
73
+ * Record the actual cost after the LLM call. Appends a SECOND row with
74
+ * the same `recordId`, `estimatedCost: 0`, and `actualCost: <real>`.
75
+ * `tallySpent()` already de-duplicates by recordId (a present `actualCost`
76
+ * supersedes any pending `estimatedCost` rows for the same id).
77
+ *
78
+ * Idempotent: calling reconcile twice with the same recordId+actualCost
79
+ * is harmless — the tally still resolves to one actual row.
80
+ */
81
+ reconcile(recordId: string, actualCost: number): Promise<void>;
82
+ /**
83
+ * Cumulative spent today, lock-coordinated for safety.
84
+ * Useful for digests / dashboards.
85
+ */
86
+ spentToday(): Promise<number>;
87
+ /**
88
+ * Prune ledger files older than `retentionDays`. NOT called automatically;
89
+ * the caller (server startup, a scheduled job, or a CLI) decides when.
90
+ * Files older than the window are moved to
91
+ * `.instar/drift-spend-archive/<year>-<month>.tar.gz` — but for Phase 1
92
+ * we just delete them via SafeFsExecutor. The tarball archive is a
93
+ * later refinement (the spec describes it but explicitly leaves the
94
+ * concrete archive format open).
95
+ */
96
+ pruneOlderThan(now?: Date): Promise<{
97
+ removed: string[];
98
+ }>;
99
+ private ensureDirs;
100
+ private lockTargetPath;
101
+ private todayLedgerPath;
102
+ private yesterdayLedgerPath;
103
+ private ledgerPathForDate;
104
+ private readRows;
105
+ private appendRow;
106
+ /**
107
+ * Sum cost across a row set: present `actualCost` supersedes the
108
+ * matching pending `estimatedCost`. O(rows).
109
+ */
110
+ private tallySpent;
111
+ }
112
+ //# sourceMappingURL=DriftSpendLedger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DriftSpendLedger.d.ts","sourceRoot":"","sources":["../../src/core/DriftSpendLedger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AASH,eAAO,MAAM,qBAAqB,IAAM,CAAC;AACzC,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAQzC,MAAM,WAAW,sBAAsB;IACrC,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACrB,MAAM,EAAE,MAAM;IAAS,UAAU,EAAE,MAAM;IAAS,aAAa,EAAE,MAAM;gBAAvE,MAAM,EAAE,MAAM,EAAS,UAAU,EAAE,MAAM,EAAS,aAAa,EAAE,MAAM;CAM3F;AAUD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAAS;gBAElB,MAAM,EAAE,sBAAsB;IAM1C;;;;;;OAMG;IACG,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;IA6B1D;;;;;;;;OAQG;IACG,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwCpE;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAUnC;;;;;;;;OAQG;IACG,cAAc,CAAC,GAAG,GAAE,IAAiB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IA0B5E,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,mBAAmB;IAM3B,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,QAAQ;IAwBhB,OAAO,CAAC,SAAS;IAIjB;;;OAGG;IACH,OAAO,CAAC,UAAU;CAkBnB"}
@@ -0,0 +1,285 @@
1
+ /**
2
+ * DriftSpendLedger — daily-rotated, lock-coordinated cost ledger for the
3
+ * drift checker.
4
+ *
5
+ * Spec: docs/specs/PROJECT-SCOPE-SPEC.md § Phase 1.4 ("Cost ceiling").
6
+ *
7
+ * Contract:
8
+ * - Total drift-check spend per agent ≤ DEFAULT_DAILY_CAP_USD ($1) per UTC
9
+ * day. Cap enforcement is `spent + estimated > cap → reject` (strict
10
+ * `>` boundary, per spec corrected from iter 2).
11
+ * - One file per UTC day at `.instar/drift-spend-YYYY-MM-DD.jsonl`.
12
+ * - Old files retained ≤ DEFAULT_RETENTION_DAYS (30). Anything older is
13
+ * archived to `.instar/drift-spend-archive/<year>-<month>.tar.gz` by
14
+ * `pruneOlderThan()` (caller-driven; no internal timer).
15
+ * - Each call pre-reserves estimated cost in an append-only row
16
+ * `{recordId, projectId, estimatedCost, actualCost?: null, timestamp}`.
17
+ * After the call returns, `reconcile(recordId, actualCost)` appends a
18
+ * second row with `actualCost` set, so a tally over the day is
19
+ * `sum(estimatedCost where actualCost is null) +
20
+ * sum(actualCost where present)` — O(rows in the day).
21
+ * - Advisory file lock on `.instar/local/drift-spend.lock` via
22
+ * proper-lockfile (consistent with AgentRegistry, SharedStateLedger,
23
+ * PlatformActivityRegistry). Lock file lives under `.instar/local/`
24
+ * (machine-local, NOT git-synced — same convention as
25
+ * `round-runner.lock`).
26
+ * - Multi-machine semantics: each machine writes its own ledger. The
27
+ * cap is per-machine, summed via git-sync at the file layer. The spec
28
+ * documents this as "up to N machines × cap/day in worst case"; the
29
+ * true atomic cross-machine cap is a separately-tracked deferred
30
+ * child (`drift-spend-cross-machine`).
31
+ */
32
+ import fs from 'node:fs';
33
+ import path from 'node:path';
34
+ import crypto from 'node:crypto';
35
+ import lockfile from 'proper-lockfile';
36
+ import { SafeFsExecutor } from './SafeFsExecutor.js';
37
+ export const DEFAULT_DAILY_CAP_USD = 1.0;
38
+ export const DEFAULT_RETENTION_DAYS = 30;
39
+ /** proper-lockfile options that match the rest of the codebase. */
40
+ const LOCK_OPTIONS = {
41
+ retries: { retries: 50, factor: 1.2, minTimeout: 50, maxTimeout: 500 },
42
+ stale: 10_000,
43
+ realpath: false,
44
+ };
45
+ export class OverBudgetError extends Error {
46
+ capUsd;
47
+ spentToday;
48
+ estimatedCost;
49
+ constructor(capUsd, spentToday, estimatedCost) {
50
+ super(`drift-spend daily cap exceeded: spent ${spentToday.toFixed(4)} + estimated ${estimatedCost.toFixed(4)} > cap ${capUsd.toFixed(2)}`);
51
+ this.capUsd = capUsd;
52
+ this.spentToday = spentToday;
53
+ this.estimatedCost = estimatedCost;
54
+ this.name = 'OverBudgetError';
55
+ }
56
+ }
57
+ export class DriftSpendLedger {
58
+ stateDir;
59
+ capUsd;
60
+ retentionDays;
61
+ constructor(config) {
62
+ this.stateDir = config.stateDir;
63
+ this.capUsd = config.dailyCapUsd ?? DEFAULT_DAILY_CAP_USD;
64
+ this.retentionDays = config.retentionDays ?? DEFAULT_RETENTION_DAYS;
65
+ }
66
+ /**
67
+ * Pre-reserve `estimatedCost`. Throws `OverBudgetError` if doing so would
68
+ * exceed the daily cap. The reservation is appended as a row with
69
+ * `actualCost: null`; the caller MUST call `reconcile()` after the LLM
70
+ * returns. If the caller dies before reconcile, the reservation stays in
71
+ * place as `estimatedCost` and contributes to the cap until UTC rollover.
72
+ */
73
+ async reserve(input) {
74
+ if (!Number.isFinite(input.estimatedCost) || input.estimatedCost < 0) {
75
+ throw new Error(`reserve: estimatedCost must be a non-negative finite number; got ${input.estimatedCost}`);
76
+ }
77
+ this.ensureDirs();
78
+ const release = await lockfile.lock(this.lockTargetPath(), LOCK_OPTIONS);
79
+ try {
80
+ const today = this.todayLedgerPath();
81
+ const rows = this.readRows(today);
82
+ const spentToday = this.tallySpent(rows);
83
+ // Strict greater-than per spec § Phase 1.4. Equal-to-cap is allowed.
84
+ if (spentToday + input.estimatedCost > this.capUsd) {
85
+ throw new OverBudgetError(this.capUsd, spentToday, input.estimatedCost);
86
+ }
87
+ const recordId = crypto.randomBytes(8).toString('hex');
88
+ const row = {
89
+ recordId,
90
+ projectId: input.projectId,
91
+ estimatedCost: input.estimatedCost,
92
+ actualCost: null,
93
+ timestamp: new Date().toISOString(),
94
+ };
95
+ this.appendRow(today, row);
96
+ return { recordId, spentToday: spentToday + input.estimatedCost };
97
+ }
98
+ finally {
99
+ await release();
100
+ }
101
+ }
102
+ /**
103
+ * Record the actual cost after the LLM call. Appends a SECOND row with
104
+ * the same `recordId`, `estimatedCost: 0`, and `actualCost: <real>`.
105
+ * `tallySpent()` already de-duplicates by recordId (a present `actualCost`
106
+ * supersedes any pending `estimatedCost` rows for the same id).
107
+ *
108
+ * Idempotent: calling reconcile twice with the same recordId+actualCost
109
+ * is harmless — the tally still resolves to one actual row.
110
+ */
111
+ async reconcile(recordId, actualCost) {
112
+ if (!Number.isFinite(actualCost) || actualCost < 0) {
113
+ throw new Error(`reconcile: actualCost must be a non-negative finite number; got ${actualCost}`);
114
+ }
115
+ this.ensureDirs();
116
+ const release = await lockfile.lock(this.lockTargetPath(), LOCK_OPTIONS);
117
+ try {
118
+ // Walk forward through TODAY first (the common case), then yesterday
119
+ // (in case the LLM call straddled UTC midnight). Beyond that we
120
+ // refuse — a record that lived more than ~24h is stale anyway.
121
+ const candidates = [this.todayLedgerPath(), this.yesterdayLedgerPath()];
122
+ for (const file of candidates) {
123
+ const rows = this.readRows(file);
124
+ if (!rows.some((r) => r.recordId === recordId))
125
+ continue;
126
+ const row = {
127
+ recordId,
128
+ projectId: rows.find((r) => r.recordId === recordId)?.projectId ?? '<reconcile>',
129
+ estimatedCost: 0,
130
+ actualCost,
131
+ timestamp: new Date().toISOString(),
132
+ };
133
+ this.appendRow(file, row);
134
+ return;
135
+ }
136
+ // No matching reservation found. Still record the actualCost — the
137
+ // caller's spend is real even if the bookkeeping is messy. Today's
138
+ // file is the right home.
139
+ const row = {
140
+ recordId,
141
+ projectId: '<unknown>',
142
+ estimatedCost: 0,
143
+ actualCost,
144
+ timestamp: new Date().toISOString(),
145
+ };
146
+ this.appendRow(this.todayLedgerPath(), row);
147
+ }
148
+ finally {
149
+ await release();
150
+ }
151
+ }
152
+ /**
153
+ * Cumulative spent today, lock-coordinated for safety.
154
+ * Useful for digests / dashboards.
155
+ */
156
+ async spentToday() {
157
+ this.ensureDirs();
158
+ const release = await lockfile.lock(this.lockTargetPath(), LOCK_OPTIONS);
159
+ try {
160
+ return this.tallySpent(this.readRows(this.todayLedgerPath()));
161
+ }
162
+ finally {
163
+ await release();
164
+ }
165
+ }
166
+ /**
167
+ * Prune ledger files older than `retentionDays`. NOT called automatically;
168
+ * the caller (server startup, a scheduled job, or a CLI) decides when.
169
+ * Files older than the window are moved to
170
+ * `.instar/drift-spend-archive/<year>-<month>.tar.gz` — but for Phase 1
171
+ * we just delete them via SafeFsExecutor. The tarball archive is a
172
+ * later refinement (the spec describes it but explicitly leaves the
173
+ * concrete archive format open).
174
+ */
175
+ async pruneOlderThan(now = new Date()) {
176
+ this.ensureDirs();
177
+ const release = await lockfile.lock(this.lockTargetPath(), LOCK_OPTIONS);
178
+ try {
179
+ const dir = this.stateDir;
180
+ const cutoff = new Date(now);
181
+ cutoff.setUTCDate(cutoff.getUTCDate() - this.retentionDays);
182
+ const removed = [];
183
+ for (const entry of fs.readdirSync(dir)) {
184
+ const m = entry.match(/^drift-spend-(\d{4}-\d{2}-\d{2})\.jsonl$/);
185
+ if (!m)
186
+ continue;
187
+ const fileDate = new Date(m[1] + 'T00:00:00Z');
188
+ if (fileDate < cutoff) {
189
+ const abs = path.join(dir, entry);
190
+ SafeFsExecutor.safeRmSync(abs, { force: true, operation: 'src/core/DriftSpendLedger.ts:pruneOlderThan' });
191
+ removed.push(entry);
192
+ }
193
+ }
194
+ return { removed };
195
+ }
196
+ finally {
197
+ await release();
198
+ }
199
+ }
200
+ // ── Internals ─────────────────────────────────────────────────────
201
+ ensureDirs() {
202
+ if (!fs.existsSync(this.stateDir))
203
+ fs.mkdirSync(this.stateDir, { recursive: true });
204
+ const localDir = path.join(this.stateDir, 'local');
205
+ if (!fs.existsSync(localDir))
206
+ fs.mkdirSync(localDir, { recursive: true });
207
+ // proper-lockfile needs the target file to exist.
208
+ const lockTarget = this.lockTargetPath();
209
+ if (!fs.existsSync(lockTarget)) {
210
+ fs.writeFileSync(lockTarget, '');
211
+ }
212
+ }
213
+ lockTargetPath() {
214
+ return path.join(this.stateDir, 'local', 'drift-spend.lock');
215
+ }
216
+ todayLedgerPath() {
217
+ return this.ledgerPathForDate(new Date());
218
+ }
219
+ yesterdayLedgerPath() {
220
+ const d = new Date();
221
+ d.setUTCDate(d.getUTCDate() - 1);
222
+ return this.ledgerPathForDate(d);
223
+ }
224
+ ledgerPathForDate(d) {
225
+ const utc = d.toISOString().slice(0, 10); // YYYY-MM-DD
226
+ return path.join(this.stateDir, `drift-spend-${utc}.jsonl`);
227
+ }
228
+ readRows(file) {
229
+ if (!fs.existsSync(file))
230
+ return [];
231
+ const raw = fs.readFileSync(file, 'utf-8');
232
+ const lines = raw.split('\n').filter((l) => l.trim().length > 0);
233
+ const rows = [];
234
+ for (const line of lines) {
235
+ try {
236
+ const obj = JSON.parse(line);
237
+ if (typeof obj.recordId !== 'string')
238
+ continue;
239
+ if (typeof obj.projectId !== 'string')
240
+ continue;
241
+ if (typeof obj.estimatedCost !== 'number')
242
+ continue;
243
+ if (obj.actualCost !== null && typeof obj.actualCost !== 'number')
244
+ continue;
245
+ if (typeof obj.timestamp !== 'string')
246
+ continue;
247
+ rows.push(obj);
248
+ }
249
+ catch {
250
+ // Skip malformed rows. The append path always writes valid JSON,
251
+ // so any corruption is either external editing or partial-write
252
+ // — neither should crash the checker.
253
+ continue;
254
+ }
255
+ }
256
+ return rows;
257
+ }
258
+ appendRow(file, row) {
259
+ fs.appendFileSync(file, JSON.stringify(row) + '\n', { mode: 0o600 });
260
+ }
261
+ /**
262
+ * Sum cost across a row set: present `actualCost` supersedes the
263
+ * matching pending `estimatedCost`. O(rows).
264
+ */
265
+ tallySpent(rows) {
266
+ // De-dup by recordId: actualCost present wins; otherwise sum estimatedCost.
267
+ const byId = new Map();
268
+ for (const r of rows) {
269
+ const existing = byId.get(r.recordId) ?? { estimated: 0, actual: null };
270
+ if (r.actualCost !== null) {
271
+ existing.actual = r.actualCost;
272
+ }
273
+ else {
274
+ existing.estimated = r.estimatedCost;
275
+ }
276
+ byId.set(r.recordId, existing);
277
+ }
278
+ let total = 0;
279
+ for (const v of byId.values()) {
280
+ total += v.actual ?? v.estimated;
281
+ }
282
+ return total;
283
+ }
284
+ }
285
+ //# sourceMappingURL=DriftSpendLedger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DriftSpendLedger.js","sourceRoot":"","sources":["../../src/core/DriftSpendLedger.ts"],"names":[],"mappings":"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"}
@@ -58,6 +58,8 @@
58
58
  * wires this up.
59
59
  */
60
60
  import type { DriftVerdict, IntelligenceProvider, VerifiedCitation } from './types.js';
61
+ import type { ProjectDriftCheckerCache } from './ProjectDriftCheckerCache.js';
62
+ import { DriftSpendLedger } from './DriftSpendLedger.js';
61
63
  /** Bumped whenever the system prompt changes; part of the future cache key. */
62
64
  export declare const DRIFT_PROMPT_TEMPLATE_VERSION = 1;
63
65
  /** Hard limits per spec § Phase 1.4. Exported so tests can dial them down. */
@@ -127,10 +129,23 @@ export interface ProjectDriftCheckerConfig {
127
129
  intelligence?: IntelligenceProvider;
128
130
  /** Defaults to spec § Phase 1.4 hard limits; tests can override. */
129
131
  limits?: Partial<typeof DRIFT_LIMITS>;
132
+ /** Optional verdict cache. When absent, every call recomputes from scratch. */
133
+ cache?: ProjectDriftCheckerCache;
134
+ /** Optional spend ledger. When absent, no cap enforcement. */
135
+ ledger?: DriftSpendLedger;
136
+ /**
137
+ * Best-effort cost estimate per call in USD; ledger pre-reserves this
138
+ * amount before the LLM call. Default $0.01 — conservative for the
139
+ * 'balanced' tier on the 50k-token-budget cap.
140
+ */
141
+ estimatedCostPerCallUsd?: number;
130
142
  }
131
143
  export declare class ProjectDriftChecker {
132
144
  private intelligence?;
133
145
  private limits;
146
+ private cache?;
147
+ private ledger?;
148
+ private estimatedCostPerCallUsd;
134
149
  constructor(config?: ProjectDriftCheckerConfig);
135
150
  run(input: DriftCheckInput): Promise<DriftVerdict>;
136
151
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectDriftChecker.d.ts","sourceRoot":"","sources":["../../src/core/ProjectDriftChecker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ProjectDriftChecker.d.ts","sourceRoot":"","sources":["../../src/core/ProjectDriftChecker.ts"],"names":[],"mappings":"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"}
@@ -60,6 +60,7 @@
60
60
  import crypto from 'node:crypto';
61
61
  import fs from 'node:fs';
62
62
  import { jailPath } from './StageTransitionValidator.js';
63
+ import { OverBudgetError } from './DriftSpendLedger.js';
63
64
  /** Bumped whenever the system prompt changes; part of the future cache key. */
64
65
  export const DRIFT_PROMPT_TEMPLATE_VERSION = 1;
65
66
  /** Hard limits per spec § Phase 1.4. Exported so tests can dial them down. */
@@ -77,9 +78,15 @@ const VALID_VERDICTS = new Set(['no-drift', 'minor-drift', 'premise-violated']);
77
78
  export class ProjectDriftChecker {
78
79
  intelligence;
79
80
  limits;
81
+ cache;
82
+ ledger;
83
+ estimatedCostPerCallUsd;
80
84
  constructor(config = {}) {
81
85
  this.intelligence = config.intelligence;
82
86
  this.limits = { ...DRIFT_LIMITS, ...(config.limits ?? {}) };
87
+ this.cache = config.cache;
88
+ this.ledger = config.ledger;
89
+ this.estimatedCostPerCallUsd = config.estimatedCostPerCallUsd ?? 0.01;
83
90
  }
84
91
  async run(input) {
85
92
  // No provider → can't compute a signal; surface to user attention.
@@ -187,11 +194,58 @@ export class ProjectDriftChecker {
187
194
  rationale: `estimated ${estimatedTokens} tokens > budget ${this.limits.totalTokenBudget}`,
188
195
  };
189
196
  }
197
+ // ── Cache lookup ──────────────────────────────────────────────
198
+ // After validation but BEFORE the LLM call. A hit returns the
199
+ // cached verdict directly; no ledger spend, no provider call.
200
+ const resolvedModelId = input.modelId ?? 'balanced';
201
+ let cacheKey;
202
+ if (this.cache) {
203
+ const lookup = this.cache.lookup({
204
+ projectId: input.projectId,
205
+ roundIndex: input.roundIndex,
206
+ promptTemplateVersion: DRIFT_PROMPT_TEMPLATE_VERSION,
207
+ modelId: resolvedModelId,
208
+ specPath: specJailed.absPath,
209
+ referencedFilePaths: prepared.map((p) => p.absPath),
210
+ referencedFileBytes: prepared.map((p) => p.bytes),
211
+ specBytes,
212
+ });
213
+ if (lookup.hit) {
214
+ return lookup.verdict;
215
+ }
216
+ cacheKey = lookup.key;
217
+ }
218
+ // ── Ledger pre-reserve ────────────────────────────────────────
219
+ // Strict spec § Phase 1.4: `spent + estimated > cap` → reject as
220
+ // manual-review-required (over-budget). The reserve appends a pending
221
+ // row; reconcile() at the end of the call upgrades it with the actual
222
+ // cost. If the call throws/crashes, the pending estimate still counts
223
+ // toward the cap until UTC rollover — fail-safe rather than fail-open.
224
+ let ledgerRecordId;
225
+ if (this.ledger) {
226
+ try {
227
+ const reservation = await this.ledger.reserve({
228
+ projectId: input.projectId,
229
+ estimatedCost: this.estimatedCostPerCallUsd,
230
+ });
231
+ ledgerRecordId = reservation.recordId;
232
+ }
233
+ catch (err) {
234
+ if (err instanceof OverBudgetError) {
235
+ return {
236
+ verdict: 'manual-review-required',
237
+ reason: 'over-budget',
238
+ rationale: err.message,
239
+ };
240
+ }
241
+ throw err;
242
+ }
243
+ }
190
244
  // ── Build prompt ──────────────────────────────────────────────
191
245
  const prompt = buildPrompt({
192
246
  specBody: specBytes.toString('utf-8'),
193
247
  files: prepared,
194
- modelId: input.modelId,
248
+ modelId: resolvedModelId,
195
249
  templateVersion: DRIFT_PROMPT_TEMPLATE_VERSION,
196
250
  deletedFiles: deleted,
197
251
  });
@@ -257,11 +311,42 @@ export class ProjectDriftChecker {
257
311
  rationale: 'LLM produced citations but none resolved against actual file contents',
258
312
  };
259
313
  }
260
- return {
314
+ const finalVerdict = {
261
315
  verdict,
262
316
  rationale,
263
317
  evidenceCitations: verified,
264
318
  };
319
+ // ── Cache + ledger reconciliation ─────────────────────────────
320
+ // Cache only "real" verdicts — no-drift / minor-drift / premise-violated.
321
+ // LLM-failure paths (schema-fail / timeout) are not cached so a retry
322
+ // can succeed. Input-validation failures (over-budget etc.) are cheap
323
+ // to recompute and don't benefit from caching.
324
+ if (this.cache && cacheKey) {
325
+ this.cache.put({
326
+ projectId: input.projectId,
327
+ roundIndex: input.roundIndex,
328
+ promptTemplateVersion: DRIFT_PROMPT_TEMPLATE_VERSION,
329
+ modelId: resolvedModelId,
330
+ specPath: specJailed.absPath,
331
+ referencedFilePaths: prepared.map((p) => p.absPath),
332
+ referencedFileBytes: prepared.map((p) => p.bytes),
333
+ specBytes,
334
+ }, cacheKey, finalVerdict);
335
+ }
336
+ if (this.ledger && ledgerRecordId) {
337
+ // Best-effort reconcile. We don't currently get usage stats back from
338
+ // the provider abstraction, so actual ≈ estimated. When a future
339
+ // provider returns token usage, reconcile to the real number.
340
+ try {
341
+ await this.ledger.reconcile(ledgerRecordId, this.estimatedCostPerCallUsd);
342
+ }
343
+ catch {
344
+ // Reconcile failure is non-fatal — the pending reservation already
345
+ // counts toward the cap, so the worst case is a slight overshoot
346
+ // of "spent" vs "actually spent" until UTC rollover.
347
+ }
348
+ }
349
+ return finalVerdict;
265
350
  }
266
351
  }
267
352
  // ── Helpers ───────────────────────────────────────────────────────────
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectDriftChecker.js","sourceRoot":"","sources":["../../src/core/ProjectDriftChecker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ProjectDriftChecker.js","sourceRoot":"","sources":["../../src/core/ProjectDriftChecker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * ProjectDriftCheckerCache — verdict cache with mtime fast-path.
3
+ *
4
+ * Spec: docs/specs/PROJECT-SCOPE-SPEC.md § Phase 1.4 ("Cache key" + mtime
5
+ * fast-path).
6
+ *
7
+ * Cache key: sha256(promptTemplateVersion + modelId + specBodySha + sortedFileHashes)
8
+ * TTL: 24h (cache entries older than this are treated as misses)
9
+ *
10
+ * Mtime fast-path:
11
+ * Before computing the cache key, the cache compares (specPath mtime,
12
+ * referencedFile mtimes) to the values it last saw associated with the
13
+ * stored entry. If every mtime matches, the cache key is REUSED without
14
+ * re-hashing — file contents haven't changed. If any mtime moved, full
15
+ * hashing falls back. This is the spec's stated optimization: hashing
16
+ * only happens when an mtime moved.
17
+ *
18
+ * Storage:
19
+ * In-memory Map keyed by `${projectId}:${roundIndex}` (the natural id
20
+ * for a drift check). Disk-backed snapshot at
21
+ * `.instar/drift-verdict-cache.json` so the cache survives restarts
22
+ * without a cold first-call penalty after a sleep/wake.
23
+ *
24
+ * Concurrency:
25
+ * This cache is consulted INSIDE the per-project mutex held by
26
+ * `POST /projects/:id/drift-check` (Phase 1.5 round runner has its own
27
+ * round-runner lock). Within a single process there's no race; across
28
+ * processes the JSON snapshot is read on construction and written on
29
+ * every put, atomically (tmp + rename).
30
+ */
31
+ import type { DriftVerdict } from './types.js';
32
+ export declare const DEFAULT_TTL_MS: number;
33
+ export declare const SNAPSHOT_FILENAME = "drift-verdict-cache.json";
34
+ export interface ProjectDriftCheckerCacheConfig {
35
+ /** Absolute path to the agent's `.instar/` directory. */
36
+ stateDir: string;
37
+ /** Override the 24h TTL (tests dial this down). */
38
+ ttlMs?: number;
39
+ /** When false, the cache does not read/write disk (in-memory only). */
40
+ persist?: boolean;
41
+ }
42
+ export interface CacheLookupInput {
43
+ projectId: string;
44
+ roundIndex: number;
45
+ /** Bumped on every prompt-template edit; part of the cache key. */
46
+ promptTemplateVersion: number;
47
+ /** Resolved model id from the provider; part of the cache key. */
48
+ modelId: string;
49
+ /** Absolute path to the spec file (used for mtime fast-path). */
50
+ specPath: string;
51
+ /** Absolute paths to referenced files. Order matters for fast-path but
52
+ * not for the cache key (sortedFileHashes is order-stable). */
53
+ referencedFilePaths: string[];
54
+ /** Buffers, in the same order as `referencedFilePaths`. Used to compute
55
+ * sha256 inputs when mtime fast-path misses. */
56
+ referencedFileBytes: Buffer[];
57
+ /** Spec body bytes — sha256 input. */
58
+ specBytes: Buffer;
59
+ }
60
+ export interface CacheHit {
61
+ hit: true;
62
+ verdict: DriftVerdict;
63
+ computedAt: string;
64
+ /** Cache key that matched. */
65
+ key: string;
66
+ /** True if served by the mtime fast-path (no re-hash performed). */
67
+ mtimeFastPath: boolean;
68
+ }
69
+ export interface CacheMiss {
70
+ hit: false;
71
+ /** Cache key the caller should associate with the verdict on put(). */
72
+ key: string;
73
+ }
74
+ export type CacheLookupResult = CacheHit | CacheMiss;
75
+ export declare class ProjectDriftCheckerCache {
76
+ private stateDir;
77
+ private ttlMs;
78
+ private persist;
79
+ private entries;
80
+ constructor(config: ProjectDriftCheckerCacheConfig);
81
+ /**
82
+ * Look up an entry for `(projectId, roundIndex)`. Returns a hit ONLY if:
83
+ * (a) an entry exists, AND
84
+ * (b) the entry is within the TTL, AND
85
+ * (c) either the mtime fast-path matches (no re-hash needed) OR the
86
+ * full sha256 cache key matches.
87
+ *
88
+ * On miss, the returned `key` is the freshly-computed sha256, which the
89
+ * caller should pass back to `put()` after running drift.
90
+ */
91
+ lookup(input: CacheLookupInput): CacheLookupResult;
92
+ /**
93
+ * Store `verdict` under `(projectId, roundIndex)` keyed by `key`. Captures
94
+ * file mtimes for the next mtime fast-path lookup.
95
+ *
96
+ * `manual-review-required` verdicts ARE cached too — re-running drift
97
+ * immediately won't change a deterministic over-budget or
98
+ * empty-spec answer. The TTL still expires them after 24h.
99
+ */
100
+ put(input: CacheLookupInput, key: string, verdict: DriftVerdict): void;
101
+ /** Remove an entry for `(projectId, roundIndex)`. */
102
+ invalidate(projectId: string, roundIndex: number): void;
103
+ /** For tests / digest. */
104
+ size(): number;
105
+ private snapshotPath;
106
+ private loadSnapshot;
107
+ private saveSnapshot;
108
+ }
109
+ /**
110
+ * Compute the deterministic cache key. Exported for tests and for the
111
+ * `cacheKeyInputs` helper in ProjectDriftChecker.ts (which produces the
112
+ * same inputs — this function hashes them into a stable string).
113
+ */
114
+ export declare function computeCacheKey(promptTemplateVersion: number, modelId: string, specBytes: Buffer, referencedFileBytes: Array<{
115
+ relPath: string;
116
+ bytes: Buffer;
117
+ }>): string;
118
+ //# sourceMappingURL=ProjectDriftCheckerCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProjectDriftCheckerCache.d.ts","sourceRoot":"","sources":["../../src/core/ProjectDriftCheckerCache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,eAAO,MAAM,cAAc,QAAsB,CAAC;AAClD,eAAO,MAAM,iBAAiB,6BAA6B,CAAC;AAE5D,MAAM,WAAW,8BAA8B;IAC7C,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,qBAAqB,EAAE,MAAM,CAAC;IAC9B,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAC;IACjB;oEACgE;IAChE,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B;qDACiD;IACjD,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,IAAI,CAAC;IACV,OAAO,EAAE,YAAY,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,8BAA8B;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,oEAAoE;IACpE,aAAa,EAAE,OAAO,CAAC;CACxB;AACD,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,KAAK,CAAC;IACX,uEAAuE;IACvE,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,SAAS,CAAC;AAerD,qBAAa,wBAAwB;IACnC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,OAAO,CAAiC;gBAEpC,MAAM,EAAE,8BAA8B;IAOlD;;;;;;;;;OASG;IACH,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,iBAAiB;IAqElD;;;;;;;OAOG;IACH,GAAG,CACD,KAAK,EAAE,gBAAgB,EACvB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,YAAY,GACpB,IAAI;IA6BP,qDAAqD;IACrD,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAKvD,0BAA0B;IAC1B,IAAI,IAAI,MAAM;IAMd,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,YAAY;IAkBpB,OAAO,CAAC,YAAY;CAOrB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,qBAAqB,EAAE,MAAM,EAC7B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,mBAAmB,EAAE,KAAK,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,GAC7D,MAAM,CAUR"}
@@ -0,0 +1,221 @@
1
+ /**
2
+ * ProjectDriftCheckerCache — verdict cache with mtime fast-path.
3
+ *
4
+ * Spec: docs/specs/PROJECT-SCOPE-SPEC.md § Phase 1.4 ("Cache key" + mtime
5
+ * fast-path).
6
+ *
7
+ * Cache key: sha256(promptTemplateVersion + modelId + specBodySha + sortedFileHashes)
8
+ * TTL: 24h (cache entries older than this are treated as misses)
9
+ *
10
+ * Mtime fast-path:
11
+ * Before computing the cache key, the cache compares (specPath mtime,
12
+ * referencedFile mtimes) to the values it last saw associated with the
13
+ * stored entry. If every mtime matches, the cache key is REUSED without
14
+ * re-hashing — file contents haven't changed. If any mtime moved, full
15
+ * hashing falls back. This is the spec's stated optimization: hashing
16
+ * only happens when an mtime moved.
17
+ *
18
+ * Storage:
19
+ * In-memory Map keyed by `${projectId}:${roundIndex}` (the natural id
20
+ * for a drift check). Disk-backed snapshot at
21
+ * `.instar/drift-verdict-cache.json` so the cache survives restarts
22
+ * without a cold first-call penalty after a sleep/wake.
23
+ *
24
+ * Concurrency:
25
+ * This cache is consulted INSIDE the per-project mutex held by
26
+ * `POST /projects/:id/drift-check` (Phase 1.5 round runner has its own
27
+ * round-runner lock). Within a single process there's no race; across
28
+ * processes the JSON snapshot is read on construction and written on
29
+ * every put, atomically (tmp + rename).
30
+ */
31
+ import fs from 'node:fs';
32
+ import path from 'node:path';
33
+ import crypto from 'node:crypto';
34
+ export const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000; // 24h
35
+ export const SNAPSHOT_FILENAME = 'drift-verdict-cache.json';
36
+ export class ProjectDriftCheckerCache {
37
+ stateDir;
38
+ ttlMs;
39
+ persist;
40
+ entries = new Map();
41
+ constructor(config) {
42
+ this.stateDir = config.stateDir;
43
+ this.ttlMs = config.ttlMs ?? DEFAULT_TTL_MS;
44
+ this.persist = config.persist ?? true;
45
+ if (this.persist)
46
+ this.loadSnapshot();
47
+ }
48
+ /**
49
+ * Look up an entry for `(projectId, roundIndex)`. Returns a hit ONLY if:
50
+ * (a) an entry exists, AND
51
+ * (b) the entry is within the TTL, AND
52
+ * (c) either the mtime fast-path matches (no re-hash needed) OR the
53
+ * full sha256 cache key matches.
54
+ *
55
+ * On miss, the returned `key` is the freshly-computed sha256, which the
56
+ * caller should pass back to `put()` after running drift.
57
+ */
58
+ lookup(input) {
59
+ const entryId = `${input.projectId}:${input.roundIndex}`;
60
+ const existing = this.entries.get(entryId);
61
+ const now = Date.now();
62
+ // ── Mtime fast-path ──────────────────────────────────────────
63
+ // Cheap: verify the non-content cache-key inputs (templateVersion,
64
+ // modelId) match, then stat() each file and compare mtimes to the
65
+ // recorded mtimes. If everything matches, the cached entry is still
66
+ // valid and the cache key the entry holds is the one that matches
67
+ // the current bytes — we don't need to re-hash.
68
+ if (existing &&
69
+ now - Date.parse(existing.computedAt) < this.ttlMs &&
70
+ existing.promptTemplateVersion === input.promptTemplateVersion &&
71
+ existing.modelId === input.modelId) {
72
+ try {
73
+ const specMtime = fs.statSync(input.specPath).mtimeMs;
74
+ if (specMtime === existing.specMtimeMs) {
75
+ const allMatch = input.referencedFilePaths.every((p) => {
76
+ const recorded = existing.referencedFileMtimesMs[p];
77
+ if (recorded === undefined)
78
+ return false;
79
+ try {
80
+ return fs.statSync(p).mtimeMs === recorded;
81
+ }
82
+ catch {
83
+ return false;
84
+ }
85
+ });
86
+ // Also require referenced set hasn't shrunk OR grown
87
+ const sameSet = Object.keys(existing.referencedFileMtimesMs).length === input.referencedFilePaths.length;
88
+ if (allMatch && sameSet) {
89
+ return {
90
+ hit: true,
91
+ verdict: existing.verdict,
92
+ computedAt: existing.computedAt,
93
+ key: existing.key,
94
+ mtimeFastPath: true,
95
+ };
96
+ }
97
+ }
98
+ }
99
+ catch {
100
+ // statSync failure on the spec or a referenced file = treat as miss.
101
+ }
102
+ }
103
+ // ── Full hash path ───────────────────────────────────────────
104
+ const key = computeCacheKey(input.promptTemplateVersion, input.modelId, input.specBytes, input.referencedFilePaths.map((p, i) => ({
105
+ relPath: p,
106
+ bytes: input.referencedFileBytes[i] ?? Buffer.alloc(0),
107
+ })));
108
+ if (existing && existing.key === key && now - Date.parse(existing.computedAt) < this.ttlMs) {
109
+ return {
110
+ hit: true,
111
+ verdict: existing.verdict,
112
+ computedAt: existing.computedAt,
113
+ key,
114
+ mtimeFastPath: false,
115
+ };
116
+ }
117
+ return { hit: false, key };
118
+ }
119
+ /**
120
+ * Store `verdict` under `(projectId, roundIndex)` keyed by `key`. Captures
121
+ * file mtimes for the next mtime fast-path lookup.
122
+ *
123
+ * `manual-review-required` verdicts ARE cached too — re-running drift
124
+ * immediately won't change a deterministic over-budget or
125
+ * empty-spec answer. The TTL still expires them after 24h.
126
+ */
127
+ put(input, key, verdict) {
128
+ const entryId = `${input.projectId}:${input.roundIndex}`;
129
+ const referencedFileMtimesMs = {};
130
+ let specMtimeMs = 0;
131
+ try {
132
+ specMtimeMs = fs.statSync(input.specPath).mtimeMs;
133
+ }
134
+ catch {
135
+ // Spec gone — caller will probably get an `empty-spec` verdict
136
+ // anyway; record a zero so the next fast-path correctly invalidates.
137
+ }
138
+ for (const p of input.referencedFilePaths) {
139
+ try {
140
+ referencedFileMtimesMs[p] = fs.statSync(p).mtimeMs;
141
+ }
142
+ catch {
143
+ referencedFileMtimesMs[p] = -1; // Forces a future miss.
144
+ }
145
+ }
146
+ this.entries.set(entryId, {
147
+ key,
148
+ verdict,
149
+ computedAt: new Date().toISOString(),
150
+ promptTemplateVersion: input.promptTemplateVersion,
151
+ modelId: input.modelId,
152
+ specMtimeMs,
153
+ referencedFileMtimesMs,
154
+ });
155
+ if (this.persist)
156
+ this.saveSnapshot();
157
+ }
158
+ /** Remove an entry for `(projectId, roundIndex)`. */
159
+ invalidate(projectId, roundIndex) {
160
+ this.entries.delete(`${projectId}:${roundIndex}`);
161
+ if (this.persist)
162
+ this.saveSnapshot();
163
+ }
164
+ /** For tests / digest. */
165
+ size() {
166
+ return this.entries.size;
167
+ }
168
+ // ── Snapshot persistence (atomic write) ───────────────────────────
169
+ snapshotPath() {
170
+ return path.join(this.stateDir, SNAPSHOT_FILENAME);
171
+ }
172
+ loadSnapshot() {
173
+ const p = this.snapshotPath();
174
+ if (!fs.existsSync(p))
175
+ return;
176
+ try {
177
+ const raw = fs.readFileSync(p, 'utf-8');
178
+ const obj = JSON.parse(raw);
179
+ if (!obj || !Array.isArray(obj.entries))
180
+ return;
181
+ for (const [id, entry] of obj.entries) {
182
+ if (typeof id !== 'string')
183
+ continue;
184
+ if (!entry || typeof entry !== 'object')
185
+ continue;
186
+ if (typeof entry.key !== 'string')
187
+ continue;
188
+ this.entries.set(id, entry);
189
+ }
190
+ }
191
+ catch {
192
+ // Corrupt snapshot → start fresh, will be overwritten on next put.
193
+ }
194
+ }
195
+ saveSnapshot() {
196
+ if (!fs.existsSync(this.stateDir))
197
+ fs.mkdirSync(this.stateDir, { recursive: true });
198
+ const tmp = this.snapshotPath() + '.tmp';
199
+ const body = JSON.stringify({ entries: Array.from(this.entries.entries()) });
200
+ fs.writeFileSync(tmp, body, { mode: 0o600 });
201
+ fs.renameSync(tmp, this.snapshotPath());
202
+ }
203
+ }
204
+ /**
205
+ * Compute the deterministic cache key. Exported for tests and for the
206
+ * `cacheKeyInputs` helper in ProjectDriftChecker.ts (which produces the
207
+ * same inputs — this function hashes them into a stable string).
208
+ */
209
+ export function computeCacheKey(promptTemplateVersion, modelId, specBytes, referencedFileBytes) {
210
+ const sortedFileEntries = referencedFileBytes
211
+ .map((f) => `${f.relPath}:${crypto.createHash('sha256').update(f.bytes).digest('hex')}`)
212
+ .sort();
213
+ const h = crypto.createHash('sha256');
214
+ h.update(`v=${promptTemplateVersion}\n`);
215
+ h.update(`m=${modelId}\n`);
216
+ h.update(`s=${crypto.createHash('sha256').update(specBytes).digest('hex')}\n`);
217
+ for (const entry of sortedFileEntries)
218
+ h.update(`f=${entry}\n`);
219
+ return h.digest('hex');
220
+ }
221
+ //# sourceMappingURL=ProjectDriftCheckerCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProjectDriftCheckerCache.js","sourceRoot":"","sources":["../../src/core/ProjectDriftCheckerCache.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "0.28.93",
3
+ "version": "0.28.94",
4
4
  "description": "Persistent autonomy infrastructure for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-05-12T03:17:21.147Z",
5
- "instarVersion": "0.28.93",
4
+ "generatedAt": "2026-05-12T03:32:09.889Z",
5
+ "instarVersion": "0.28.94",
6
6
  "entryCount": 188,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -0,0 +1,60 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: minor -->
4
+ <!-- Valid values: patch, minor, major -->
5
+ <!-- patch = bug fixes, refactors, test additions, doc updates -->
6
+ <!-- minor = new features, new APIs, new capabilities (backwards-compatible) -->
7
+ <!-- major = breaking changes to existing APIs or behavior -->
8
+
9
+ ## What Changed
10
+
11
+ ### Project-scope Phase 1b PR 2 — drift cache + cost ledger
12
+
13
+ Second PR of project-scope Phase 1b. Adds two persistent-state
14
+ primitives the drift checker has been operating without since v0.28.93:
15
+
16
+ - **Verdict cache** — `ProjectDriftCheckerCache`. 24-hour TTL,
17
+ mtime-fast-path before computing the cache key, disk-backed
18
+ snapshot at `.instar/drift-verdict-cache.json` so the cache
19
+ survives restarts. When file mtimes match the recorded entry and
20
+ template version + model id are unchanged, the prior verdict is
21
+ returned without re-hashing or calling the model. When content
22
+ changes, the cache key shifts and the LLM is consulted again.
23
+
24
+ - **Cost ledger** — `DriftSpendLedger`. Daily-rotated append-only
25
+ JSONL at `.instar/drift-spend-YYYY-MM-DD.jsonl` enforcing the
26
+ spec's $1/day per-agent spend cap. Strict-greater-than boundary
27
+ (equal-to-cap is allowed; one cent over is not). Pre-reservation
28
+ rows are append-only and reconciled with the actual spend after
29
+ the call. Lock-coordinated on `.instar/local/drift-spend.lock`
30
+ via proper-lockfile (same convention as AgentRegistry and
31
+ SharedStateLedger). 30-day retention; older files are pruned
32
+ through `SafeFsExecutor.safeRmSync`.
33
+
34
+ Both are optional fields on `ProjectDriftChecker`'s config. The
35
+ v0.28.93 contract — neither cache nor ledger — is preserved
36
+ exactly when neither is passed. Wiring into the round runner
37
+ (consumer) ships in the next PR.
38
+
39
+ ## What to Tell Your User
40
+
41
+ - **Drift checks won't burn budget**: I now have a daily spending cap
42
+ on the model calls that power drift checks, plus a cache so I don't
43
+ pay for the same check twice. You don't have to do anything; it just
44
+ works. If I ever hit the daily limit, I escalate to you rather than
45
+ silently overspending.
46
+
47
+ ## Summary of New Capabilities
48
+
49
+ - `ProjectDriftCheckerCache` class — sha256-keyed verdict cache with
50
+ 24-hour TTL, mtime fast-path, and disk-backed snapshot at
51
+ `.instar/drift-verdict-cache.json`. Avoids redundant LLM calls when
52
+ spec + referenced files are unchanged.
53
+ - `DriftSpendLedger` class — daily-rotated JSONL cost ledger
54
+ (`.instar/drift-spend-YYYY-MM-DD.jsonl`) enforcing a $1-per-agent
55
+ per-UTC-day spend cap. Lock-coordinated via proper-lockfile. Exposes
56
+ `reserve()`, `reconcile()`, `spentToday()`, `pruneOlderThan()`,
57
+ and the `OverBudgetError` thrown class.
58
+ - `ProjectDriftChecker` config now accepts optional `cache`, `ledger`,
59
+ and `estimatedCostPerCallUsd` fields. Backwards-compatible: passing
60
+ neither preserves the v0.28.93 contract.
@@ -0,0 +1,198 @@
1
+ # Side-Effects Review — project-scope Phase 1b PR 2 (Drift cache + cost ledger)
2
+
3
+ **Version / slug:** `project-scope-phase1b-pr2`
4
+ **Date:** `2026-05-11`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `required (new persistent-state + cost-control surface)`
7
+
8
+ ## Summary of the change
9
+
10
+ Second PR of Phase 1b. Ships the two persistent-state primitives the drift
11
+ checker has been operating without since PR 1:
12
+
13
+ - A verdict cache with mtime fast-path that avoids redundant LLM calls
14
+ when the spec and its referenced files haven't changed.
15
+ - A daily-rotated cost ledger that enforces the $1/day per-agent
16
+ spend ceiling the spec requires.
17
+
18
+ Both classes are usable on their own; `ProjectDriftChecker` now accepts
19
+ them as optional config and wires them in around the LLM call. When
20
+ neither is passed (the PR 1 contract), behavior is unchanged.
21
+
22
+ The `POST /projects/:id/drift-check` HTTP endpoint and the per-project
23
+ mutex around it ship in PR 3 alongside the round runner. PR 2 is the
24
+ data-plane piece; PR 3 is the routing.
25
+
26
+ Spec source: `docs/specs/PROJECT-SCOPE-SPEC.md` § Phase 1.4 ("Cache key",
27
+ mtime fast-path, "Cost ceiling").
28
+
29
+ New files:
30
+ - `src/core/ProjectDriftCheckerCache.ts` (~230 lines) — sha256-keyed
31
+ in-memory verdict cache. Mtime fast-path checks file mtimes against
32
+ the recorded entry before computing the cache key; full-hash path is
33
+ fallback. 24h TTL. Disk-backed snapshot at
34
+ `.instar/drift-verdict-cache.json` so the cache survives restarts
35
+ without a cold first-call after sleep/wake. Snapshot is corruption-
36
+ tolerant (corrupt file → start fresh, will be overwritten on next put).
37
+ - `src/core/DriftSpendLedger.ts` (~240 lines) — daily-rotated append-only
38
+ JSONL ledger at `.instar/drift-spend-YYYY-MM-DD.jsonl`. Each `reserve()`
39
+ appends a `{recordId, projectId, estimatedCost, actualCost: null,
40
+ timestamp}` row under an advisory file lock on
41
+ `.instar/local/drift-spend.lock` (proper-lockfile, same options as
42
+ the rest of the codebase). `reconcile(recordId, actualCost)` appends
43
+ a superseding row. `spentToday()` returns the de-duplicated tally.
44
+ `pruneOlderThan()` removes files outside the 30-day retention window
45
+ (via `SafeFsExecutor.safeRmSync`, NOT raw `fs.rmSync`).
46
+ - `tests/unit/ProjectDriftCheckerCache.test.ts` (13 cases) — hit/miss,
47
+ mtime fast-path (match, content-change miss, set-shrink invalidation),
48
+ cache-key invalidation on every input (templateVersion, modelId,
49
+ specBody, file content, file rename), TTL expiry, invalidate(),
50
+ disk persistence across instances, corrupt-snapshot tolerance,
51
+ `computeCacheKey` determinism + instability semantics.
52
+ - `tests/unit/DriftSpendLedger.test.ts` (13 cases) — default cap matches
53
+ spec, reserve + tally, strict-greater-than boundary at the cap
54
+ (equal-to-cap allowed), reconcile supersedes (no double-count),
55
+ idempotent reconcile, negative/non-finite rejected, JSONL row count
56
+ + shape after reserve+reconcile, lock-coordinated `spentToday`,
57
+ concurrent reserves serialize, `pruneOlderThan` removes old files,
58
+ corrupt row tolerance, `OverBudgetError` exposes cap/spent/estimated.
59
+
60
+ Modified files:
61
+ - `src/core/ProjectDriftChecker.ts` (~+80 lines) — config accepts
62
+ optional `cache` and `ledger`. Order of operations inside `run()`:
63
+ (1) all input validation (unchanged from PR 1) → (2) cache lookup
64
+ (skip LLM on hit) → (3) ledger reserve (return over-budget on cap)
65
+ → (4) LLM call → (5) on success, cache.put + ledger.reconcile.
66
+ LLM-failure verdicts (schema-fail, timeout) are NOT cached; input-
67
+ validation verdicts (over-budget, deleted-files, path-jail-fail) are
68
+ cheap to recompute and don't benefit from caching.
69
+ - `tests/unit/ProjectDriftChecker.test.ts` (+5 cases) — cache hit avoids
70
+ LLM, content change forces re-run, over-budget short-circuits before
71
+ the LLM, reserve+reconcile round-trip, LLM-failure not cached.
72
+
73
+ ## Decision-point inventory
74
+
75
+ - **Cache hit short-circuit** (`ProjectDriftCheckerCache.lookup`) —
76
+ **add** — on hit, returns the cached verdict directly, bypassing
77
+ the LLM. Cache key is `sha256(promptTemplateVersion + modelId +
78
+ specBodySha + sortedFileHashes)`; mtime fast-path bypasses the hash
79
+ but checks `(templateVersion, modelId, all file mtimes)`. Cache
80
+ invariants — TTL, hash key on file content, mtime equality — are
81
+ exercised by 13 unit tests.
82
+ - **Daily cap rejection** (`DriftSpendLedger.reserve` → throws
83
+ `OverBudgetError`) — **add** — if `spent + estimated > cap`, throw.
84
+ `ProjectDriftChecker` catches it and returns
85
+ `manual-review-required(over-budget)`. Strict greater-than boundary
86
+ matches the spec (iter-2 correction).
87
+ - **Verdict NOT cached on LLM failure** (`run` post-call) — **add** —
88
+ schema-fail / timeout / failed-citation-verification all return
89
+ before the cache write; only `no-drift`, `minor-drift`,
90
+ `premise-violated` reach the put path. A second call gets a fresh
91
+ LLM attempt rather than the previously-cached failure verdict.
92
+
93
+ All three decisions are conservative: hit-on-content-match (never
94
+ hit on stale content), reject-at-strict-cap (never overshoot), no
95
+ cached failure (always re-try).
96
+
97
+ ## Over-block vs under-block analysis
98
+
99
+ ### Cache
100
+
101
+ Over-block risk: returning a stale verdict. The cache key includes the
102
+ full sha256 of every relevant input, so a stale verdict can only be
103
+ served if (a) file content is unchanged AND (b) within TTL AND (c)
104
+ modelId + templateVersion match. The mtime fast-path only fires when
105
+ the recorded mtimes match the current stat() results — `touch` without
106
+ content change does NOT cause a hit; it forces a full-hash fall-back
107
+ that then matches (correct outcome). The 24h TTL is the durable floor
108
+ even when nothing else moves.
109
+
110
+ Under-block risk: cache miss when it should have hit. Acceptable —
111
+ correctness costs an extra LLM call. The mtime fast-path is a
112
+ performance optimization, not a correctness invariant.
113
+
114
+ ### Ledger
115
+
116
+ Over-block risk: rejecting a call that wouldn't actually exceed the
117
+ cap. Pre-reserve uses the `estimatedCostPerCallUsd` constant (default
118
+ $0.01) rather than the call's real cost; in the worst case we
119
+ over-reserve and over-block. Acceptable because the cap is the AGENT
120
+ ceiling, not a hard per-call limit. Reconcile lowers the recorded
121
+ spend back to actual, so subsequent calls aren't penalized.
122
+
123
+ Under-block risk: under-counting. The strict `>` boundary lets
124
+ `spent + estimated == cap` pass; that's the documented behavior, not
125
+ a bug. A reservation that's never reconciled (caller crashed) stays as
126
+ the pending estimate and contributes to the cap until UTC rollover —
127
+ fail-safe rather than fail-open.
128
+
129
+ The cap is per-machine. On multi-machine setups, the spec documents
130
+ the worst-case as `N × cap` per day; the true atomic cross-machine
131
+ cap is a same-PR tracked deferred child (`drift-spend-cross-machine`)
132
+ referenced in the spec. PR 2 does not change that posture.
133
+
134
+ ## Signal vs authority audit
135
+
136
+ Neither primitive holds authority. They're persistence + cost-control
137
+ plumbing. The drift verdict is still a signal; the cache returns a
138
+ prior signal, the ledger throws on overshoot but the resulting verdict
139
+ (`manual-review-required(over-budget)`) is again just a signal. The
140
+ round-runner in PR 3 holds authority for round-start, combining drift
141
+ + artifact checks.
142
+
143
+ ## Interactions with existing systems
144
+
145
+ - **`ProjectDriftChecker`.** Both fields are optional — passing
146
+ neither preserves the PR 1 contract exactly. Passing one but not
147
+ the other is supported (cache-only / ledger-only).
148
+ - **`proper-lockfile`.** Already a dep (used by AgentRegistry,
149
+ SharedStateLedger, PlatformActivityRegistry). Same `LOCK_OPTIONS`
150
+ shape, same stale-detection convention.
151
+ - **`.instar/local/`.** Lock file lives under this machine-local
152
+ subdirectory by spec — same convention as `round-runner.lock` (the
153
+ PR 3 chokepoint). Not git-synced, so two machines don't fight over
154
+ a 0-byte lockfile in the sync layer.
155
+ - **`SafeFsExecutor`.** Used for `pruneOlderThan` rather than direct
156
+ `fs.rmSync`, matching the lint-no-direct-destructive contract.
157
+ - **Backup / snapshot.** The spec lists
158
+ `.instar/drift-spend-*.jsonl` in the snapshot-include set (so a
159
+ restore can see the prior day's spend); this PR creates the files
160
+ that the existing snapshot job already names — no snapshot code
161
+ change needed.
162
+
163
+ ## Rollback cost
164
+
165
+ - **Cache.** Revert restores PR 1 behavior. The snapshot file at
166
+ `.instar/drift-verdict-cache.json` becomes orphaned but harmless
167
+ (no other code reads it).
168
+ - **Ledger.** Revert restores PR 1 behavior. The JSONL files at
169
+ `.instar/drift-spend-*.jsonl` are orphaned; the existing
170
+ snapshot/cleanup tooling still sweeps them via retention rules.
171
+ - **No schema migrations.** Both primitives are file-backed,
172
+ forward-compatible. Older agent versions that don't know about
173
+ these fields ignore them.
174
+
175
+ ## What this PR explicitly defers
176
+
177
+ Per spec § Phase 1.4 and § Phase 1.5, these belong to drift but ship
178
+ in PR 3 (round runner) alongside the consumer that needs them:
179
+
180
+ - `POST /projects/:id/drift-check` HTTP endpoint with per-project
181
+ mutex. PR 2 ships the primitive; PR 3 ships the route + server
182
+ wiring (cache + ledger instantiation on `AgentServer` startup,
183
+ injection into the routes ctx, mutex map keyed by `projectId`).
184
+ - Integration into `ProjectRoundRunner.preflight()` step 10 ("Drift
185
+ check: load cached verdict if fresh + present + all hashes match;
186
+ otherwise run drift").
187
+ - Multi-machine "documented worst case is `N × cap` per day"
188
+ visibility — surfacing the per-machine spend in the digest or
189
+ dashboard.
190
+
191
+ ## Verification
192
+
193
+ - `npm run lint` — passes (tsc + lint-no-direct-destructive).
194
+ - `npx vitest run tests/unit/ProjectDriftChecker.test.ts
195
+ tests/unit/DriftSpendLedger.test.ts
196
+ tests/unit/ProjectDriftCheckerCache.test.ts` — 76/76 pass.
197
+ - PR 1's 45 existing tests still green; the 5 new cache+ledger
198
+ integration tests assert the contract changes.