mason-context 0.17.4 → 0.19.0
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/CHANGELOG.md +19 -0
- package/README.md +2 -0
- package/dist/mason-audit.js +132 -68
- package/dist/mason-audit.js.map +1 -1
- package/dist/mason-auto.js +165 -45
- package/dist/mason-auto.js.map +1 -1
- package/dist/mason-drift.js +10 -4
- package/dist/mason-drift.js.map +1 -1
- package/dist/mason-hook.js +15 -9
- package/dist/mason-hook.js.map +1 -1
- package/dist/mason-mcp.js +1177 -348
- package/dist/mason-mcp.js.map +1 -1
- package/dist/mason-review.js +11 -5
- package/dist/mason-review.js.map +1 -1
- package/dist/mason-runtime.js +337 -0
- package/dist/mason-runtime.js.map +1 -0
- package/dist/mason.js +964 -238
- package/dist/mason.js.map +1 -1
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.19.0 — 2026-09-17
|
|
4
|
+
|
|
5
|
+
- Add automatic updates for new public standalone installations, with existing installations opting in through `mason updates enable`. Check at most daily for stable releases at least 24 hours old; leave npm, pinned versions, company mirrors, and CI under explicit control.
|
|
6
|
+
- Verify automatic downloads against Sigstore-signed release metadata from the exact tagged publishing workflow, stage and health-check each bundle, and activate at a future MCP launch only after other Mason MCP servers exit. Keep update work and diagnostics out of hook and MCP protocol streams.
|
|
7
|
+
- Add `mason updates` status, enable/disable and pin/unpin controls, `MASON_NO_AUTO_UPDATE=1`, persisted company mirror selection, and an offline `mason rollback` that restores and pins the previous installation. Retain `mason upgrade` for explicit immediate updates.
|
|
8
|
+
- Track standalone processes before startup and reclaim unused update bundles while preserving current, rollback, staged, and running versions. Retain legacy or uncertain bundles conservatively.
|
|
9
|
+
- Let linked Git worktrees inherit host activation from the primary checkout, with local setup and teardown taking precedence; keep audit evidence and observations local to each worktree.
|
|
10
|
+
- Limit dependency-change advisories to documents containing dependency guidance, preserve committed evidence through local edits, and distinguish current findings from historical advisories awaiting review.
|
|
11
|
+
|
|
12
|
+
## 0.18.0 — 2026-09-16
|
|
13
|
+
|
|
14
|
+
- Fix overlapping snapshot writes restoring old descriptions and deleted flows. Snapshot saves, verdicts, and partial writes share a per-checkout cross-process lock; reads, replacement, and consolidation cleanup stay within the protected operation.
|
|
15
|
+
- Bind map verification to entry kind, content, and bounded source evidence through review tokens. Changed or deleted entries return conflicts; older calls without tokens request a fresh review without recording a verdict. Existing snapshot files remain compatible; callers must include the returned entry kind and review token to record a verdict. Restart all Mason MCP processes for a checkout after upgrading; older binaries do not honor the new snapshot lock.
|
|
16
|
+
- Recheck saved map verification evidence on retrieval. Changed evidence reports stale verification; unavailable evidence and legacy verdicts without tokens report unknown. Preserve historical verdicts and failure reasons, including when reviewed local edits are reverted to a clean checkout.
|
|
17
|
+
- Diagnose incomplete lock owners and interrupted recovery guards, document safe manual cleanup after writers stop, and cover both interruption windows with controlled process-termination tests.
|
|
18
|
+
- Cover the reported overwrite with a controlled regression, exercise both call orders through one MCP session and separate processes, and test interrupted writers, contention, partial cleanup, and stale review evidence. Run the snapshot regressions in the existing macOS, Linux, and Windows distribution matrix before publishing.
|
|
19
|
+
|
|
20
|
+
Run `mason upgrade` for standalone installations, or `npm install -g mason-context@0.18.0` for npm installations. Restart **every Mason MCP process** sharing a checkout after upgrading: older processes do not participate in the new snapshot lock. Existing decisions and snapshot files are preserved. Older snapshot verdicts remain visible but report unknown verification until reviewed again with an evidence token. Existing integrations do not need setup again.
|
|
21
|
+
|
|
3
22
|
## 0.17.4 — 2026-09-15
|
|
4
23
|
|
|
5
24
|
- Share Git document/source inventories within each automation inspection, with a separate fresh inventory before publication. Batch module ignore checks by directory level and reuse only identical scoped history queries.
|
package/README.md
CHANGED
|
@@ -34,6 +34,8 @@ irm https://github.com/adrianczuczka/mason/releases/latest/download/install.ps1
|
|
|
34
34
|
|
|
35
35
|
No Node or npm required. Git is required. The installer configures PATH for Bash, Zsh, and Windows. [Platform details](docs/distribution.md) · [npm installation](docs/setup.md#unified-project-setup)
|
|
36
36
|
|
|
37
|
+
New standard standalone installs update automatically in the background. Updates activate on a future MCP launch after existing Mason MCP servers exit. Run `mason updates` to inspect or change this preference. Existing installations require one manual upgrade and `mason updates enable`. [Update controls](docs/distribution.md#automatic-updates)
|
|
38
|
+
|
|
37
39
|
Run this in your Git repository (open a new terminal if the installer asks):
|
|
38
40
|
|
|
39
41
|
```bash
|
package/dist/mason-audit.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
if (!import.meta.url.endsWith("/mason-runtime.js")) await import("./mason-runtime.js").then(m => m.registerRuntime());
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
5
|
var __esm = (fn, res) => function __init() {
|
|
@@ -234,6 +235,102 @@ var init_storage = __esm({
|
|
|
234
235
|
}
|
|
235
236
|
});
|
|
236
237
|
|
|
238
|
+
// src/utils/store-lock.ts
|
|
239
|
+
import fs3 from "fs/promises";
|
|
240
|
+
import os from "os";
|
|
241
|
+
async function lockDiagnostic(file) {
|
|
242
|
+
let reason;
|
|
243
|
+
try {
|
|
244
|
+
const owner = JSON.parse(await fs3.readFile(file, "utf8"));
|
|
245
|
+
if (!owner || typeof owner.host !== "string" || !Number.isInteger(owner.pid) || owner.pid <= 0) {
|
|
246
|
+
reason = "Lock owner data is incomplete or malformed.";
|
|
247
|
+
} else if (owner.host !== os.hostname()) {
|
|
248
|
+
reason = "The lock belongs to another host; its process cannot be checked locally.";
|
|
249
|
+
} else {
|
|
250
|
+
try {
|
|
251
|
+
process.kill(owner.pid, 0);
|
|
252
|
+
reason = `Local owner PID ${owner.pid} is still running.`;
|
|
253
|
+
} catch (error) {
|
|
254
|
+
reason = error.code === "ESRCH" ? `Local owner PID ${owner.pid} has exited, but lock recovery has not completed.` : `Local owner PID ${owner.pid} could not be checked.`;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
} catch (error) {
|
|
258
|
+
reason = error instanceof SyntaxError ? "Lock owner data is incomplete or malformed." : "Lock owner data could not be read; the lock may have changed.";
|
|
259
|
+
}
|
|
260
|
+
if (await fs3.lstat(file + ".reclaim").then(() => true, () => false)) {
|
|
261
|
+
reason += " A recovery guard also needs inspection: " + file + ".reclaim.";
|
|
262
|
+
}
|
|
263
|
+
return reason + " Retry if a writer is active. For manual recovery, stop all Mason writers for this checkout, confirm they have exited (including other hosts), then remove only confirmed abandoned lock and lock.reclaim files. See docs/reference.md#snapshot-repairs-and-verification.";
|
|
264
|
+
}
|
|
265
|
+
async function withStoreLock(root, directory, run, waitMs = 5e3, label = "Mason store") {
|
|
266
|
+
const file = await storePath(root, directory + "/lock", true);
|
|
267
|
+
const deadline = Date.now() + waitMs;
|
|
268
|
+
let handle;
|
|
269
|
+
while (!handle) {
|
|
270
|
+
try {
|
|
271
|
+
handle = await fs3.open(file, "wx", 384);
|
|
272
|
+
try {
|
|
273
|
+
await handle.writeFile(JSON.stringify({ pid: process.pid, host: os.hostname() }));
|
|
274
|
+
} catch (error) {
|
|
275
|
+
await handle.close().catch(() => {
|
|
276
|
+
});
|
|
277
|
+
handle = void 0;
|
|
278
|
+
await fs3.rm(file, { force: true }).catch(() => {
|
|
279
|
+
});
|
|
280
|
+
throw error;
|
|
281
|
+
}
|
|
282
|
+
} catch (error) {
|
|
283
|
+
if (error.code !== "EEXIST") throw error;
|
|
284
|
+
try {
|
|
285
|
+
const owner = JSON.parse(await fs3.readFile(file, "utf8"));
|
|
286
|
+
if (owner.host === os.hostname() && Number.isInteger(owner.pid) && owner.pid > 0) {
|
|
287
|
+
try {
|
|
288
|
+
process.kill(owner.pid, 0);
|
|
289
|
+
} catch (probe) {
|
|
290
|
+
if (probe.code === "ESRCH") {
|
|
291
|
+
const reclaim = file + ".reclaim";
|
|
292
|
+
let guard;
|
|
293
|
+
try {
|
|
294
|
+
guard = await fs3.open(reclaim, "wx", 384);
|
|
295
|
+
const current = JSON.parse(await fs3.readFile(file, "utf8"));
|
|
296
|
+
if (current.pid === owner.pid && current.host === owner.host) await fs3.unlink(file);
|
|
297
|
+
} finally {
|
|
298
|
+
if (guard) {
|
|
299
|
+
await guard.close();
|
|
300
|
+
await fs3.rm(reclaim, { force: true });
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
} catch {
|
|
307
|
+
}
|
|
308
|
+
if (Date.now() >= deadline) throw new Error(label + " is busy or its lock needs inspection: " + file + ". " + await lockDiagnostic(file));
|
|
309
|
+
await new Promise((resolve) => setTimeout(resolve, 40));
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
try {
|
|
313
|
+
return await run();
|
|
314
|
+
} finally {
|
|
315
|
+
await handle.close();
|
|
316
|
+
await fs3.unlink(file);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
var init_store_lock = __esm({
|
|
320
|
+
"src/utils/store-lock.ts"() {
|
|
321
|
+
"use strict";
|
|
322
|
+
init_storage();
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// src/snapshot/lock.ts
|
|
327
|
+
var init_lock = __esm({
|
|
328
|
+
"src/snapshot/lock.ts"() {
|
|
329
|
+
"use strict";
|
|
330
|
+
init_store_lock();
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
|
|
237
334
|
// src/test-map.ts
|
|
238
335
|
import path4 from "path";
|
|
239
336
|
var init_test_map = __esm({
|
|
@@ -264,6 +361,7 @@ var init_snapshot = __esm({
|
|
|
264
361
|
init_files();
|
|
265
362
|
init_files();
|
|
266
363
|
init_storage();
|
|
364
|
+
init_lock();
|
|
267
365
|
init_paths();
|
|
268
366
|
init_test_map();
|
|
269
367
|
repoPath = z.string().refine((value) => normalizeRepoPath(value) !== null, "Expected a relative repository path");
|
|
@@ -271,6 +369,7 @@ var init_snapshot = __esm({
|
|
|
271
369
|
refreshedHash: z.string().optional(),
|
|
272
370
|
verifiedAt: z.string().optional(),
|
|
273
371
|
verifiedHash: z.string().optional(),
|
|
372
|
+
verificationToken: z.string().optional(),
|
|
274
373
|
verificationFailed: z.boolean().optional(),
|
|
275
374
|
verificationNote: z.string().optional()
|
|
276
375
|
};
|
|
@@ -294,7 +393,7 @@ var init_snapshot = __esm({
|
|
|
294
393
|
});
|
|
295
394
|
|
|
296
395
|
// src/drift/drift.ts
|
|
297
|
-
import
|
|
396
|
+
import fs4 from "fs/promises";
|
|
298
397
|
import path6 from "path";
|
|
299
398
|
function parseChanges(output) {
|
|
300
399
|
const fields = output.split("\0");
|
|
@@ -855,12 +954,12 @@ var init_git = __esm({
|
|
|
855
954
|
});
|
|
856
955
|
|
|
857
956
|
// src/audit/inputs.ts
|
|
858
|
-
import
|
|
957
|
+
import fs5 from "fs/promises";
|
|
859
958
|
import * as nativeFs from "fs";
|
|
860
959
|
import path9 from "path";
|
|
861
960
|
import fg2 from "fast-glob";
|
|
862
961
|
async function auditInputPath(root, file) {
|
|
863
|
-
if (file === ".") return
|
|
962
|
+
if (file === ".") return fs5.realpath(root);
|
|
864
963
|
try {
|
|
865
964
|
return await storePath(root, file);
|
|
866
965
|
} catch (error) {
|
|
@@ -965,7 +1064,7 @@ var init_inputs = __esm({
|
|
|
965
1064
|
});
|
|
966
1065
|
|
|
967
1066
|
// src/audit/docs.ts
|
|
968
|
-
import
|
|
1067
|
+
import fs6 from "fs/promises";
|
|
969
1068
|
import path10 from "path";
|
|
970
1069
|
import fg3 from "fast-glob";
|
|
971
1070
|
async function dirtyDocs(resolvedRoot, files) {
|
|
@@ -1037,7 +1136,7 @@ async function localDocPaths(root) {
|
|
|
1037
1136
|
for (const dir of [".", ".claude"]) {
|
|
1038
1137
|
let entries;
|
|
1039
1138
|
try {
|
|
1040
|
-
entries = await
|
|
1139
|
+
entries = await fs6.readdir(await auditInputPath(root, dir), { withFileTypes: true });
|
|
1041
1140
|
} catch (error) {
|
|
1042
1141
|
if (error.code === "ENOENT") continue;
|
|
1043
1142
|
throw error;
|
|
@@ -1116,7 +1215,7 @@ var init_types = __esm({
|
|
|
1116
1215
|
});
|
|
1117
1216
|
|
|
1118
1217
|
// src/audit/scope.ts
|
|
1119
|
-
import
|
|
1218
|
+
import fs7 from "fs/promises";
|
|
1120
1219
|
import path11 from "path";
|
|
1121
1220
|
function scopedPath(directory, value) {
|
|
1122
1221
|
if (path11.posix.isAbsolute(value) || value.includes("\\") || /[\x00-\x1f]/.test(value)) return null;
|
|
@@ -1136,7 +1235,7 @@ function pathClaimScope(doc, claim) {
|
|
|
1136
1235
|
}
|
|
1137
1236
|
async function pathExists(root, file) {
|
|
1138
1237
|
try {
|
|
1139
|
-
await
|
|
1238
|
+
await fs7.access(await auditInputPath(root, file));
|
|
1140
1239
|
return true;
|
|
1141
1240
|
} catch (error) {
|
|
1142
1241
|
if (["ENOENT", "ENOTDIR"].includes(error.code ?? "")) return false;
|
|
@@ -1226,7 +1325,7 @@ var init_deleted_reference = __esm({
|
|
|
1226
1325
|
|
|
1227
1326
|
// src/audit/checks/new-module.ts
|
|
1228
1327
|
import fg4 from "fast-glob";
|
|
1229
|
-
import
|
|
1328
|
+
import fs8 from "fs/promises";
|
|
1230
1329
|
import path13 from "path";
|
|
1231
1330
|
function escapeRegExp(text2) {
|
|
1232
1331
|
return text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -1278,7 +1377,7 @@ async function collectModuleCandidates(root, combinedDocs) {
|
|
|
1278
1377
|
const byParent = /* @__PURE__ */ new Map();
|
|
1279
1378
|
for (let offset = 0; offset < parents.length; offset += 16) {
|
|
1280
1379
|
await Promise.all(parents.slice(offset, offset + 16).map(async (dir) => {
|
|
1281
|
-
const entries = await
|
|
1380
|
+
const entries = await fs8.readdir(await auditInputPath(root, dir), { withFileTypes: true });
|
|
1282
1381
|
const dirs = entries.filter((entry) => (entry.isDirectory() || entry.isSymbolicLink() && sources.some((file) => file.startsWith(path13.posix.join(dir, entry.name) + "/"))) && !entry.name.startsWith(".") && !DIR_DENYLIST.has(entry.name)).map((entry) => path13.posix.join(dir, entry.name));
|
|
1283
1382
|
byParent.set(dir, dirs);
|
|
1284
1383
|
}));
|
|
@@ -1691,6 +1790,10 @@ var init_release_metadata = __esm({
|
|
|
1691
1790
|
});
|
|
1692
1791
|
|
|
1693
1792
|
// src/audit/checks/deps-changed.ts
|
|
1793
|
+
function hasDependencyContent(content) {
|
|
1794
|
+
const text2 = content.replace(/<!-- mason:start -->[\s\S]*?<!-- mason:end -->/g, "").replace(/<!--[\s\S]*?-->/g, "");
|
|
1795
|
+
return /\b(?:dependenc(?:y|ies)|librar(?:y|ies)|frameworks?|tech(?:nology)? stack|prerequisites|requirements)\b/i.test(text2) || /\b(?:package\.json|build\.gradle(?:\.kts)?|settings\.gradle(?:\.kts)?|libs\.versions\.toml|Cargo\.toml|go\.mod|pyproject\.toml|requirements\.txt|Gemfile|composer\.json)\b/i.test(text2) || /\b(?:node(?:\.js)?|npm|pnpm|yarn|bun|deno|python|ruby|rust|go|java|jdk|kotlin|gradle|swift|php|react|vue|angular|compose|ktor|room)\s*(?:version\s*)?[`*:=>~^v\s-]*\d+(?:\.\d+)*\b/i.test(text2) || /\b(?:npm|pnpm|yarn|pip3?|cargo|gem|composer)\s+(?:install|add|require)\b/i.test(text2);
|
|
1796
|
+
}
|
|
1694
1797
|
function manifestPathspecs(doc) {
|
|
1695
1798
|
const scope = documentScope(doc);
|
|
1696
1799
|
if (scope === ".") return MANIFEST_PATHSPECS;
|
|
@@ -1717,6 +1820,19 @@ async function checkDepsChanged(ctx) {
|
|
|
1717
1820
|
reason: `${doc.path} has uncommitted edits \u2013 suppressed while in flight`
|
|
1718
1821
|
});
|
|
1719
1822
|
}
|
|
1823
|
+
let content = doc.content;
|
|
1824
|
+
if (doc.dirty) {
|
|
1825
|
+
try {
|
|
1826
|
+
content = (await inspectionGit(
|
|
1827
|
+
["show", `${doc.lastCommit.hash}:${doc.path}`],
|
|
1828
|
+
{ cwd: ctx.root, maxBuffer: 10 * 1024 * 1024, timeout: 1e4 }
|
|
1829
|
+
)).stdout;
|
|
1830
|
+
} catch {
|
|
1831
|
+
result.skipped.push({ check: "deps-changed", doc: doc.path, reason: `${doc.path}: committed dependency content is unavailable` });
|
|
1832
|
+
continue;
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
if (!hasDependencyContent(content)) continue;
|
|
1720
1836
|
const range = await commitsTouchingSince(
|
|
1721
1837
|
ctx.root,
|
|
1722
1838
|
doc.lastCommit.hash,
|
|
@@ -1762,6 +1878,7 @@ var init_deps_changed = __esm({
|
|
|
1762
1878
|
init_docs();
|
|
1763
1879
|
init_release_metadata();
|
|
1764
1880
|
init_git();
|
|
1881
|
+
init_inspection();
|
|
1765
1882
|
init_checks();
|
|
1766
1883
|
MANIFEST_COMMITS_CAP = 10;
|
|
1767
1884
|
MANIFEST_PATHSPECS = [
|
|
@@ -1942,7 +2059,7 @@ var init_provenance = __esm({
|
|
|
1942
2059
|
});
|
|
1943
2060
|
|
|
1944
2061
|
// src/decisions/decisions.ts
|
|
1945
|
-
import
|
|
2062
|
+
import fs9 from "fs/promises";
|
|
1946
2063
|
import path17 from "path";
|
|
1947
2064
|
import { createHash } from "crypto";
|
|
1948
2065
|
import { z as z3 } from "zod";
|
|
@@ -1951,7 +2068,7 @@ async function loadDecisionStore(rootDir) {
|
|
|
1951
2068
|
const diagnostics = [];
|
|
1952
2069
|
let entries;
|
|
1953
2070
|
try {
|
|
1954
|
-
entries = await
|
|
2071
|
+
entries = await fs9.readdir(await storePath(rootDir, ".mason/decisions"));
|
|
1955
2072
|
} catch (error) {
|
|
1956
2073
|
if (error.code !== "ENOENT") diagnostics.push({ path: ".mason/decisions", message: String(error) });
|
|
1957
2074
|
return { records, diagnostics };
|
|
@@ -2219,68 +2336,15 @@ var init_findings = __esm({
|
|
|
2219
2336
|
});
|
|
2220
2337
|
|
|
2221
2338
|
// src/automation/store.ts
|
|
2222
|
-
import fs9 from "fs/promises";
|
|
2223
|
-
import os from "os";
|
|
2224
2339
|
import { z as z5 } from "zod";
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
const deadline = Date.now() + waitMs;
|
|
2228
|
-
let handle;
|
|
2229
|
-
while (!handle) {
|
|
2230
|
-
try {
|
|
2231
|
-
handle = await fs9.open(file, "wx", 384);
|
|
2232
|
-
try {
|
|
2233
|
-
await handle.writeFile(JSON.stringify({ pid: process.pid, host: os.hostname() }));
|
|
2234
|
-
} catch (error) {
|
|
2235
|
-
await handle.close().catch(() => {
|
|
2236
|
-
});
|
|
2237
|
-
handle = void 0;
|
|
2238
|
-
await fs9.rm(file, { force: true }).catch(() => {
|
|
2239
|
-
});
|
|
2240
|
-
throw error;
|
|
2241
|
-
}
|
|
2242
|
-
} catch (error) {
|
|
2243
|
-
if (error.code !== "EEXIST") throw error;
|
|
2244
|
-
try {
|
|
2245
|
-
const owner = JSON.parse(await fs9.readFile(file, "utf8"));
|
|
2246
|
-
if (owner.host === os.hostname() && Number.isInteger(owner.pid) && owner.pid > 0) {
|
|
2247
|
-
try {
|
|
2248
|
-
process.kill(owner.pid, 0);
|
|
2249
|
-
} catch (probe) {
|
|
2250
|
-
if (probe.code === "ESRCH") {
|
|
2251
|
-
const reclaim = file + ".reclaim";
|
|
2252
|
-
let guard;
|
|
2253
|
-
try {
|
|
2254
|
-
guard = await fs9.open(reclaim, "wx", 384);
|
|
2255
|
-
const current = JSON.parse(await fs9.readFile(file, "utf8"));
|
|
2256
|
-
if (current.pid === owner.pid && current.host === owner.host) await fs9.unlink(file);
|
|
2257
|
-
} finally {
|
|
2258
|
-
if (guard) {
|
|
2259
|
-
await guard.close();
|
|
2260
|
-
await fs9.rm(reclaim, { force: true });
|
|
2261
|
-
}
|
|
2262
|
-
}
|
|
2263
|
-
}
|
|
2264
|
-
}
|
|
2265
|
-
}
|
|
2266
|
-
} catch {
|
|
2267
|
-
}
|
|
2268
|
-
if (Date.now() >= deadline) throw new Error("Automation is busy or its lock needs inspection: " + file);
|
|
2269
|
-
await new Promise((resolve) => setTimeout(resolve, 40));
|
|
2270
|
-
}
|
|
2271
|
-
}
|
|
2272
|
-
try {
|
|
2273
|
-
return await run();
|
|
2274
|
-
} finally {
|
|
2275
|
-
await handle.close();
|
|
2276
|
-
await fs9.unlink(file);
|
|
2277
|
-
}
|
|
2340
|
+
function withLock(root, directory, run, waitMs = 5e3) {
|
|
2341
|
+
return withStoreLock(root, directory, run, waitMs, "Automation");
|
|
2278
2342
|
}
|
|
2279
2343
|
var hostSchema, stateSchema;
|
|
2280
2344
|
var init_store = __esm({
|
|
2281
2345
|
"src/automation/store.ts"() {
|
|
2282
2346
|
"use strict";
|
|
2283
|
-
|
|
2347
|
+
init_store_lock();
|
|
2284
2348
|
hostSchema = z5.enum(["claude", "codex"]);
|
|
2285
2349
|
stateSchema = z5.object({
|
|
2286
2350
|
version: z5.literal(1),
|
|
@@ -2477,7 +2541,7 @@ async function verifyOriginal(rootDir, baselinePath, options, session) {
|
|
|
2477
2541
|
return {
|
|
2478
2542
|
...base,
|
|
2479
2543
|
status: "review-required",
|
|
2480
|
-
reason: "An audit cannot establish that this advisory was reviewed. Retain its original evidence and report a separate assessment; editing or committing the doc is not approval."
|
|
2544
|
+
reason: (now ? "The current check still reports this advisory. " : finding.type === "deps-changed" ? "The current check no longer reports this condition; the historical advisory remains awaiting assessment. " : "The current check no longer reports the original finding; retained evidence still requires assessment. ") + "An audit cannot establish that this advisory was reviewed. Retain its original evidence and report a separate assessment; editing or committing the doc is not approval."
|
|
2481
2545
|
};
|
|
2482
2546
|
}
|
|
2483
2547
|
return { ...base, status: "resolved", reason: "The original check ran and no longer reports this claim. Inspect the edit for semantic correctness." };
|