mason-context 0.17.4 → 0.18.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.
@@ -305,18 +305,34 @@ var init_storage = __esm({
305
305
  }
306
306
  });
307
307
 
308
- // src/automation/store.ts
308
+ // src/utils/store-lock.ts
309
309
  import fs3 from "fs/promises";
310
310
  import os from "os";
311
- import { z } from "zod";
312
- function parseState(raw) {
311
+ async function lockDiagnostic(file) {
312
+ let reason2;
313
313
  try {
314
- return stateSchema.parse(raw);
314
+ const owner = JSON.parse(await fs3.readFile(file, "utf8"));
315
+ if (!owner || typeof owner.host !== "string" || !Number.isInteger(owner.pid) || owner.pid <= 0) {
316
+ reason2 = "Lock owner data is incomplete or malformed.";
317
+ } else if (owner.host !== os.hostname()) {
318
+ reason2 = "The lock belongs to another host; its process cannot be checked locally.";
319
+ } else {
320
+ try {
321
+ process.kill(owner.pid, 0);
322
+ reason2 = `Local owner PID ${owner.pid} is still running.`;
323
+ } catch (error) {
324
+ reason2 = 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.`;
325
+ }
326
+ }
315
327
  } catch (error) {
316
- throw new Error("Invalid automation state; original evidence was retained.", { cause: error });
328
+ reason2 = error instanceof SyntaxError ? "Lock owner data is incomplete or malformed." : "Lock owner data could not be read; the lock may have changed.";
317
329
  }
330
+ if (await fs3.lstat(file + ".reclaim").then(() => true, () => false)) {
331
+ reason2 += " A recovery guard also needs inspection: " + file + ".reclaim.";
332
+ }
333
+ return reason2 + " 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.";
318
334
  }
319
- async function withLock(root, directory, run, waitMs = 5e3) {
335
+ async function withStoreLock(root, directory, run, waitMs = 5e3, label = "Mason store") {
320
336
  const file = await storePath(root, directory + "/lock", true);
321
337
  const deadline = Date.now() + waitMs;
322
338
  let handle;
@@ -359,7 +375,7 @@ async function withLock(root, directory, run, waitMs = 5e3) {
359
375
  }
360
376
  } catch {
361
377
  }
362
- if (Date.now() >= deadline) throw new Error("Automation is busy or its lock needs inspection: " + file);
378
+ if (Date.now() >= deadline) throw new Error(label + " is busy or its lock needs inspection: " + file + ". " + await lockDiagnostic(file));
363
379
  await new Promise((resolve) => setTimeout(resolve, 40));
364
380
  }
365
381
  }
@@ -370,11 +386,30 @@ async function withLock(root, directory, run, waitMs = 5e3) {
370
386
  await fs3.unlink(file);
371
387
  }
372
388
  }
389
+ var init_store_lock = __esm({
390
+ "src/utils/store-lock.ts"() {
391
+ "use strict";
392
+ init_storage();
393
+ }
394
+ });
395
+
396
+ // src/automation/store.ts
397
+ import { z } from "zod";
398
+ function parseState(raw) {
399
+ try {
400
+ return stateSchema.parse(raw);
401
+ } catch (error) {
402
+ throw new Error("Invalid automation state; original evidence was retained.", { cause: error });
403
+ }
404
+ }
405
+ function withLock(root, directory, run, waitMs = 5e3) {
406
+ return withStoreLock(root, directory, run, waitMs, "Automation");
407
+ }
373
408
  var hostSchema, events, stateSchema;
374
409
  var init_store = __esm({
375
410
  "src/automation/store.ts"() {
376
411
  "use strict";
377
- init_storage();
412
+ init_store_lock();
378
413
  hostSchema = z.enum(["claude", "codex"]);
379
414
  events = ["session_start", "turn_start", "before_tool", "after_tool", "task_end"];
380
415
  stateSchema = z.object({
@@ -1204,6 +1239,14 @@ var init_scope = __esm({
1204
1239
  }
1205
1240
  });
1206
1241
 
1242
+ // src/snapshot/lock.ts
1243
+ var init_lock = __esm({
1244
+ "src/snapshot/lock.ts"() {
1245
+ "use strict";
1246
+ init_store_lock();
1247
+ }
1248
+ });
1249
+
1207
1250
  // src/test-map.ts
1208
1251
  import path9 from "path";
1209
1252
  async function buildTestMap(dir) {
@@ -1313,6 +1356,7 @@ var init_snapshot = __esm({
1313
1356
  init_files();
1314
1357
  init_files();
1315
1358
  init_storage();
1359
+ init_lock();
1316
1360
  init_paths();
1317
1361
  init_test_map();
1318
1362
  repoPath = z2.string().refine((value) => normalizeRepoPath(value) !== null, "Expected a relative repository path");
@@ -1320,6 +1364,7 @@ var init_snapshot = __esm({
1320
1364
  refreshedHash: z2.string().optional(),
1321
1365
  verifiedAt: z2.string().optional(),
1322
1366
  verifiedHash: z2.string().optional(),
1367
+ verificationToken: z2.string().optional(),
1323
1368
  verificationFailed: z2.boolean().optional(),
1324
1369
  verificationNote: z2.string().optional()
1325
1370
  };
@@ -3146,7 +3191,7 @@ var init_evidence = __esm({
3146
3191
  init_dead_command();
3147
3192
  init_storage();
3148
3193
  init_advisory_review();
3149
- engineVersion = true ? "0.17.4" : "development";
3194
+ engineVersion = true ? "0.18.0" : "development";
3150
3195
  hash = (value) => createHash3("sha256").update(JSON.stringify(value)).digest("hex");
3151
3196
  content = readAuditInput;
3152
3197
  cacheSchema = z8.object({ version: z8.literal(1), entries: z8.record(z8.object({ key: z8.string(), result: checkResultSchema })), digest: z8.string() });
@@ -3831,7 +3876,7 @@ var init_launcher = __esm({
3831
3876
  "use strict";
3832
3877
  init_evidence();
3833
3878
  init_model();
3834
- executingVersion = () => true ? "0.17.4" : "development";
3879
+ executingVersion = () => true ? "0.18.0" : "development";
3835
3880
  mcpCommand = (host) => globalCommand(["--setup-host", host, "mcp"]);
3836
3881
  hookCommand = (host) => `mason --setup-host ${host} auto`;
3837
3882
  }
@@ -4200,7 +4245,7 @@ Mason provides recorded decisions and file impact over MCP. A concept map is opt
4200
4245
  - Before editing a file \u2192 \`get_impact\` to check references, tests, and historical change partners.
4201
4246
  - Investigation or correction reveals a reusable project lesson \u2192 \`save_decision\` before finishing. Capture only the lasting constraint and reason, with anchors and known sources, owner, and recorder. This creates a proposal; never infer approval. Preserve uncertain causes and unknown attribution. Route project lessons here; keep personal preferences in agent memory. Skip code summaries, current checkout status, temporary task permissions, session trivia, and secrets. No qualifying lesson means no record.
4202
4247
  - Before saving, compare the lesson with matching \`get_context\` records and any pending proposal. If new evidence changes the same lesson's assumptions, scope, or recommended action, revise it with \`save_decision(id: existingId)\`, including when it is accepted. Preserve supported rationale and sources, replace obsolete instructions, and add the new evidence. Create a separate record for a genuinely distinct lesson; skip unchanged restatements. Revision preserves history and leaves the accepted version operative until authorized review.
4203
- - Consult trust metadata before relying on entries: unknown or changed freshness requires inspection, and failed verification means the description must be corrected. Check approval too: proposals are suggestions, legacy records are unreviewed, and accepted decisions are recorded constraints subject to freshness checks. An accepted revision remains operative while a pending proposal is reviewed; keep both versions and their freshness distinct.
4248
+ - Consult trust metadata before relying on entries: unknown or changed freshness requires inspection, stale or unknown map verification requires a fresh evidence review, and a recorded failed verdict means the description must be corrected. Check approval too: proposals are suggestions, legacy records are unreviewed, and accepted decisions are recorded constraints subject to freshness checks. An accepted revision remains operative while a pending proposal is reviewed; keep both versions and their freshness distinct.
4204
4249
  - Asked to review or re-verify a decision \u2192 \`review_decision\` first to inspect content, sources, history, and code changes. Record acceptance, reaffirmation, or retirement only when authorized by the user or a cited team review, with the actual reviewer and reason. Never infer approval from unchanged code. Review and commit the local record through the normal project workflow.
4205
4250
  - For an architectural overview, use \`get_snapshot\` if a map is available. If \`map.status\` is missing or invalid, use available decisions and source evidence; do not start building a map unless requested.
4206
4251
  - \`mason_init\` returns documentation audit and committed-diff review results, plus a short setup guide. Pass \`evidence\` with local CI manifest paths to include test and analysis results; the CLI equivalent is \`mason-review --evidence <manifest>\`. State skipped, unavailable, stale, or unknown checks explicitly. Related accepted decisions identify review context, not proven violations.