mason-context 0.19.1 → 0.20.1

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 CHANGED
@@ -1,5 +1,30 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 0.20.1 — 2026-09-30
6
+
7
+ - Retry temporary Windows lock access failures during concurrent snapshot writes, preserving the acquisition timeout and persistent permission errors.
8
+ - Includes the stats and CLI changes below; 0.20.0 was tagged but not published after its Windows release check failed.
9
+
10
+ ## 0.20.0 — not published
11
+
12
+ - Keep optional stats failures from blocking setup, allow disabling with damaged stats storage, and preserve newer audit outcomes when delivery observations arrive late.
13
+ - Remove the `mason-auto` executable. Use `mason setup`, `mason status`, `mason check`, and `mason stats`; hook configuration and execution use `mason auto`.
14
+ - Enable local stats during setup, preserving explicit opt-outs, with summaries for hook finding delivery, observed resolution, returned decision IDs, automation timing, and supplied feedback ratings. Keep uncertain outcomes explicit; no prompts or source contents are stored or uploaded.
15
+
16
+ - Add a controlled agent-behavior benchmark with matched conditions, independent grading, and retained hook evidence. Pilot results establish delivery, not improved outcomes.
17
+
18
+ ## 0.19.2 — 2026-09-29
19
+
20
+ - Use operating-system trusted certificates in standalone Mason, preserving bundled and extra certificates, so company TLS certificates work for upgrades and other requests. Report GitHub release lookup failures with their underlying error code and certificate or network guidance.
21
+
22
+ - Capture supported lessons immediately as proposals, keeping unimplemented remedies tentative and acceptance subject to explicit review.
23
+ - Give assistants new or worsened audit findings during the task and show remaining findings at response completion. Keep the existing backlog quiet; explicit checks still report it in full.
24
+ - Preserve notification history across restarts, ignore report-only changes, and retain overflow for later notices. Notices never force another agent turn.
25
+ - Recover notification tracking from unreadable upgrade reports while preserving repair evidence. Deduplicate verification warnings per session.
26
+ - Show uncommitted decision records and pending proposals in explicit status, with paths, ages, and oldest proposals first.
27
+
3
28
  ## 0.19.1 — 2026-09-25
4
29
 
5
30
  - Match supported dependency changes to named document passages; retain unmatched manifest recency as information without a review obligation. Preserve specific findings, original baselines and prior assessments, and label historical evidence explicitly.
@@ -489,17 +489,12 @@ async function withStoreLock(root, directory, run, waitMs = 5e3, label = "Mason
489
489
  while (!handle) {
490
490
  try {
491
491
  handle = await fs4.open(file, "wx", 384);
492
- try {
493
- await handle.writeFile(JSON.stringify({ pid: process.pid, host: os.hostname() }));
494
- } catch (error) {
495
- await handle.close().catch(() => {
496
- });
497
- handle = void 0;
498
- await fs4.rm(file, { force: true }).catch(() => {
499
- });
500
- throw error;
501
- }
502
492
  } catch (error) {
493
+ if (process.platform === "win32" && error.code === "EPERM") {
494
+ if (Date.now() >= deadline) throw error;
495
+ await new Promise((resolve) => setTimeout(resolve, 40));
496
+ continue;
497
+ }
503
498
  if (error.code !== "EEXIST") throw error;
504
499
  try {
505
500
  const owner = JSON.parse(await fs4.readFile(file, "utf8"));
@@ -528,6 +523,17 @@ async function withStoreLock(root, directory, run, waitMs = 5e3, label = "Mason
528
523
  if (Date.now() >= deadline) throw new Error(label + " is busy or its lock needs inspection: " + file + ". " + await lockDiagnostic(file));
529
524
  await new Promise((resolve) => setTimeout(resolve, 40));
530
525
  }
526
+ if (handle) {
527
+ try {
528
+ await handle.writeFile(JSON.stringify({ pid: process.pid, host: os.hostname() }));
529
+ } catch (error) {
530
+ await handle.close().catch(() => {
531
+ });
532
+ await fs4.rm(file, { force: true }).catch(() => {
533
+ });
534
+ throw error;
535
+ }
536
+ }
531
537
  }
532
538
  try {
533
539
  return await run();
@@ -2479,8 +2485,30 @@ var init_findings = __esm({
2479
2485
  }
2480
2486
  });
2481
2487
 
2482
- // src/automation/store.ts
2488
+ // src/automation/notifications.ts
2483
2489
  import { z as z5 } from "zod";
2490
+ var conditionSchema, notificationStateSchema;
2491
+ var init_notifications = __esm({
2492
+ "src/automation/notifications.ts"() {
2493
+ "use strict";
2494
+ init_findings();
2495
+ conditionSchema = z5.object({
2496
+ level: z5.number(),
2497
+ facts: z5.array(z5.string()),
2498
+ magnitude: z5.number().optional()
2499
+ });
2500
+ notificationStateSchema = z5.object({
2501
+ version: z5.literal(1),
2502
+ baselineHead: z5.string(),
2503
+ baseline: z5.record(conditionSchema),
2504
+ delivered: z5.record(conditionSchema),
2505
+ cleared: z5.array(z5.string())
2506
+ });
2507
+ }
2508
+ });
2509
+
2510
+ // src/automation/store.ts
2511
+ import { z as z6 } from "zod";
2484
2512
  function withLock(root, directory, run, waitMs = 5e3) {
2485
2513
  return withStoreLock(root, directory, run, waitMs, "Automation");
2486
2514
  }
@@ -2489,29 +2517,28 @@ var init_store = __esm({
2489
2517
  "src/automation/store.ts"() {
2490
2518
  "use strict";
2491
2519
  init_store_lock();
2492
- hostSchema = z5.enum(["claude", "codex"]);
2493
- stateSchema = z5.object({
2494
- version: z5.literal(1),
2495
- root: z5.string(),
2496
- gitDir: z5.string(),
2497
- branch: z5.string(),
2498
- baselines: z5.array(z5.object({ path: z5.string(), at: z5.string(), event: z5.string(), fingerprint: z5.string() })).max(128),
2499
- sessions: z5.record(z5.object({
2520
+ init_notifications();
2521
+ hostSchema = z6.enum(["claude", "codex"]);
2522
+ stateSchema = z6.object({
2523
+ version: z6.literal(1),
2524
+ root: z6.string(),
2525
+ gitDir: z6.string(),
2526
+ branch: z6.string(),
2527
+ baselines: z6.array(z6.object({ path: z6.string(), at: z6.string(), event: z6.string(), fingerprint: z6.string() })).max(128),
2528
+ sessions: z6.record(z6.object({
2500
2529
  host: hostSchema,
2501
- seen: z5.string().nullable(),
2502
- continued: z5.boolean(),
2503
- initialIssues: z5.array(z5.string()),
2504
- initialDocs: z5.record(z5.string().nullable()),
2505
- lastUsed: z5.string(),
2506
- mutationObserved: z5.boolean(),
2507
- pending: z5.record(z5.string()),
2508
- coverageGaps: z5.array(z5.string()),
2509
- events: z5.record(z5.object({ at: z5.string(), count: z5.number().int().positive() }))
2530
+ lastUsed: z6.string(),
2531
+ notifiedDiagnostics: z6.array(z6.string()).default([]),
2532
+ pending: z6.record(z6.string()),
2533
+ coverageGaps: z6.array(z6.string()),
2534
+ events: z6.record(z6.object({ at: z6.string(), count: z6.number().int().positive() }))
2510
2535
  })),
2511
- updatedAt: z5.string(),
2512
- fingerprint: z5.string().nullable(),
2513
- latest: z5.string().nullable(),
2514
- analysis: z5.object({ id: z5.string(), fingerprint: z5.string(), completedAt: z5.number(), path: z5.string() }).optional()
2536
+ updatedAt: z6.string(),
2537
+ fingerprint: z6.string().nullable(),
2538
+ latest: z6.string().nullable(),
2539
+ notifications: notificationStateSchema.optional(),
2540
+ agentNotifications: notificationStateSchema.optional(),
2541
+ analysis: z6.object({ id: z6.string(), fingerprint: z6.string(), completedAt: z6.number(), path: z6.string() }).optional()
2515
2542
  });
2516
2543
  }
2517
2544
  });
@@ -2533,7 +2560,7 @@ __export(repair_exports, {
2533
2560
  import fs10 from "fs/promises";
2534
2561
  import path20 from "path";
2535
2562
  import { randomUUID as randomUUID2 } from "crypto";
2536
- import { z as z6 } from "zod";
2563
+ import { z as z7 } from "zod";
2537
2564
  function recheckable(finding) {
2538
2565
  return !("confidence" in finding) && finding.resolution === "recheck" && ["deleted-reference", "dead-command", "new-module"].includes(finding.type) && ["missing-path", "missing-script", "unmentioned-dir"].includes(finding.evidence.kind);
2539
2566
  }
@@ -2604,7 +2631,7 @@ async function withRepairSession(root, options, run, validate, cachedAudit) {
2604
2631
  }
2605
2632
  async function prepareOriginal(rootDir, checks, options, session) {
2606
2633
  const root = await fs10.realpath(rootDir);
2607
- const selected = z6.array(checkSchema).nonempty().parse(checks);
2634
+ const selected = z7.array(checkSchema).nonempty().parse(checks);
2608
2635
  const report = await (session ? session.audit(selected) : stableAudit(root, selected, options));
2609
2636
  if (!report) throw new Error("No context files found to prepare a repair.");
2610
2637
  if (!report.gitAvailable) throw new Error("Readable Git history is required to prepare a repair.");
@@ -2761,42 +2788,42 @@ var init_repair = __esm({
2761
2788
  init_findings();
2762
2789
  init_advisory_review();
2763
2790
  init_findings();
2764
- count2 = z6.number().int().nonnegative();
2765
- reportSchema = z6.object({
2766
- version: z6.literal(1),
2767
- root: z6.string(),
2768
- gitAvailable: z6.literal(true),
2791
+ count2 = z7.number().int().nonnegative();
2792
+ reportSchema = z7.object({
2793
+ version: z7.literal(1),
2794
+ root: z7.string(),
2795
+ gitAvailable: z7.literal(true),
2769
2796
  headHash: commitSchema.shape.hash,
2770
- checksRun: z6.array(checkSchema).nonempty(),
2771
- docs: z6.array(z6.object({
2772
- path: z6.string().refine((value) => normalizeRepoPath(value) === value),
2773
- scope: z6.string().optional(),
2774
- kind: z6.enum(["instructions", "readme", "guide"]).optional(),
2797
+ checksRun: z7.array(checkSchema).nonempty(),
2798
+ docs: z7.array(z7.object({
2799
+ path: z7.string().refine((value) => normalizeRepoPath(value) === value),
2800
+ scope: z7.string().optional(),
2801
+ kind: z7.enum(["instructions", "readme", "guide"]).optional(),
2775
2802
  lastCommit: commitSchema.nullable(),
2776
- dirty: z6.boolean(),
2803
+ dirty: z7.boolean(),
2777
2804
  lineCount: count2
2778
2805
  })).nonempty(),
2779
- decisionsChecked: z6.boolean(),
2780
- clean: z6.boolean(),
2781
- excludedChecks: z6.array(z6.object({ check: checkSchema, doc: z6.string() })).optional(),
2782
- issues: z6.array(issueSchema),
2783
- advisories: z6.array(advisorySchema),
2784
- suppressedAdvisories: z6.array(advisorySchema).optional(),
2785
- skippedChecks: z6.array(z6.object({ check: z6.string(), reason: z6.string(), doc: z6.string().optional() }))
2806
+ decisionsChecked: z7.boolean(),
2807
+ clean: z7.boolean(),
2808
+ excludedChecks: z7.array(z7.object({ check: checkSchema, doc: z7.string() })).optional(),
2809
+ issues: z7.array(issueSchema),
2810
+ advisories: z7.array(advisorySchema),
2811
+ suppressedAdvisories: z7.array(advisorySchema).optional(),
2812
+ skippedChecks: z7.array(z7.object({ check: z7.string(), reason: z7.string(), doc: z7.string().optional() }))
2786
2813
  });
2787
- baselineSchema = z6.object({
2788
- kind: z6.literal("mason-audit-repair"),
2789
- version: z6.literal(1),
2790
- createdAt: z6.string().datetime(),
2814
+ baselineSchema = z7.object({
2815
+ kind: z7.literal("mason-audit-repair"),
2816
+ version: z7.literal(1),
2817
+ createdAt: z7.string().datetime(),
2791
2818
  report: reportSchema,
2792
- digest: z6.string().regex(/^[a-f0-9]{64}$/)
2819
+ digest: z7.string().regex(/^[a-f0-9]{64}$/)
2793
2820
  });
2794
- repairFindingSchema = z6.object({
2795
- id: z6.string(),
2796
- original: z6.union([issueSchema, advisorySchema]),
2797
- status: z6.enum(["resolved", "unresolved", "review-required", "unverified"]),
2798
- reason: z6.string(),
2799
- current: z6.union([issueSchema, advisorySchema]).optional(),
2821
+ repairFindingSchema = z7.object({
2822
+ id: z7.string(),
2823
+ original: z7.union([issueSchema, advisorySchema]),
2824
+ status: z7.enum(["resolved", "unresolved", "review-required", "unverified"]),
2825
+ reason: z7.string(),
2826
+ current: z7.union([issueSchema, advisorySchema]).optional(),
2800
2827
  review: reviewSummarySchema.optional()
2801
2828
  });
2802
2829
  }
@@ -2804,7 +2831,7 @@ var init_repair = __esm({
2804
2831
 
2805
2832
  // src/audit/advisory-review.ts
2806
2833
  import fs11 from "fs/promises";
2807
- import { z as z7 } from "zod";
2834
+ import { z as z8 } from "zod";
2808
2835
  async function readRecord(root, id) {
2809
2836
  const raw = await readStoreJson(root, recordPath(id));
2810
2837
  if (raw === null) return null;
@@ -3023,41 +3050,41 @@ var init_advisory_review = __esm({
3023
3050
  init_decisions();
3024
3051
  init_drift2();
3025
3052
  git2 = async (root, ...args) => (await (["log", "ls-tree", "ls-files", "merge-base"].includes(args[0]) ? inspectionGit : execGit)(args, { cwd: root, timeout: 1e4, maxBuffer: 8 * 1024 * 1024 })).stdout;
3026
- hashSchema = z7.string().regex(/^[a-f0-9]{64}$/);
3027
- commitSchema2 = z7.string().regex(/^[a-f0-9]{40,64}$/);
3028
- evidenceSchema2 = z7.object({
3053
+ hashSchema = z8.string().regex(/^[a-f0-9]{64}$/);
3054
+ commitSchema2 = z8.string().regex(/^[a-f0-9]{40,64}$/);
3055
+ evidenceSchema2 = z8.object({
3029
3056
  head: commitSchema2,
3030
3057
  fingerprint: hashSchema,
3031
- scope: z7.array(z7.string()).min(1).max(1e3),
3032
- tree: z7.string().max(2 * 1024 * 1024),
3058
+ scope: z8.array(z8.string()).min(1).max(1e3),
3059
+ tree: z8.string().max(2 * 1024 * 1024),
3033
3060
  lastScopeCommit: commitSchema2,
3034
- paths: z7.array(z7.object({ path: z7.string(), exists: z7.boolean() })).max(1e3)
3061
+ paths: z8.array(z8.object({ path: z8.string(), exists: z8.boolean() })).max(1e3)
3035
3062
  });
3036
- dismissalReasonSchema = z7.enum(["no-dependency-claims", "unrelated-manifest-change"]);
3037
- eventSchema2 = z7.object({
3038
- at: z7.string().datetime(),
3039
- reviewer: z7.string().trim().min(1).max(200).optional(),
3040
- note: z7.string().trim().min(1).max(2500),
3041
- outcome: z7.enum(["addressed", "inapplicable", "deferred", "dismissed"]),
3063
+ dismissalReasonSchema = z8.enum(["no-dependency-claims", "unrelated-manifest-change"]);
3064
+ eventSchema2 = z8.object({
3065
+ at: z8.string().datetime(),
3066
+ reviewer: z8.string().trim().min(1).max(200).optional(),
3067
+ note: z8.string().trim().min(1).max(2500),
3068
+ outcome: z8.enum(["addressed", "inapplicable", "deferred", "dismissed"]),
3042
3069
  reasonCode: dismissalReasonSchema.optional(),
3043
3070
  finding: advisorySchema.refine((finding) => finding.evidence.kind !== "decision-anchor", "Decision findings use decision reviews"),
3044
3071
  evidence: evidenceSchema2
3045
3072
  });
3046
- recordSchema = z7.object({ version: z7.literal(1), id: hashSchema, events: z7.array(eventSchema2).min(1).max(200), digest: hashSchema });
3047
- reviewSummarySchema = z7.object({
3073
+ recordSchema = z8.object({ version: z8.literal(1), id: hashSchema, events: z8.array(eventSchema2).min(1).max(200), digest: hashSchema });
3074
+ reviewSummarySchema = z8.object({
3048
3075
  id: hashSchema,
3049
- status: z7.enum(["current", "reopened", "deferred", "unverified"]),
3050
- reason: z7.string(),
3051
- recordPath: z7.string().optional(),
3052
- reviewer: z7.string().optional(),
3053
- note: z7.string().optional(),
3054
- outcome: z7.enum(["addressed", "inapplicable", "deferred", "accepted", "reaffirmed", "retired", "dismissed"]).optional(),
3055
- reviewedHead: z7.string().optional()
3076
+ status: z8.enum(["current", "reopened", "deferred", "unverified"]),
3077
+ reason: z8.string(),
3078
+ recordPath: z8.string().optional(),
3079
+ reviewer: z8.string().optional(),
3080
+ note: z8.string().optional(),
3081
+ outcome: z8.enum(["addressed", "inapplicable", "deferred", "accepted", "reaffirmed", "retired", "dismissed"]).optional(),
3082
+ reviewedHead: z8.string().optional()
3056
3083
  });
3057
- advisoryReviewRequest = z7.object({
3058
- baselinePath: z7.string().min(1),
3084
+ advisoryReviewRequest = z8.object({
3085
+ baselinePath: z8.string().min(1),
3059
3086
  findingId: hashSchema,
3060
- action: z7.enum(["prepare", "addressed", "inapplicable", "deferred", "dismiss"]).default("prepare"),
3087
+ action: z8.enum(["prepare", "addressed", "inapplicable", "deferred", "dismiss"]).default("prepare"),
3061
3088
  reasonCode: dismissalReasonSchema.optional(),
3062
3089
  reviewer: eventSchema2.shape.reviewer.optional(),
3063
3090
  note: eventSchema2.shape.note.optional(),