mason-context 0.10.0 → 0.10.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 +9 -0
- package/README.md +8 -4
- package/dist/mason-audit.js +36 -13
- package/dist/mason-audit.js.map +1 -1
- package/dist/mason-drift.js +33 -8
- package/dist/mason-drift.js.map +1 -1
- package/dist/mason-hook.js +74 -22
- package/dist/mason-hook.js.map +1 -1
- package/dist/mason-mcp.js +98 -65
- package/dist/mason-mcp.js.map +1 -1
- package/dist/mason-review.js +70 -18
- package/dist/mason-review.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.1 — 2026-09-05
|
|
4
|
+
|
|
5
|
+
Editing an accepted decision previously hid its accepted content from ordinary retrieval until the draft was reviewed. Mason now keeps the accepted constraint visible alongside the proposed replacement.
|
|
6
|
+
|
|
7
|
+
- Preserve the last accepted decision while a replacement revision is proposed. Retrieval, hooks, map indexes, reviews, and audits distinguish the accepted revision from its pending proposal, including separate anchors, ownership, and freshness. CI evidence remains associated with accepted anchors. Existing version 2 history supplies both revisions without a storage migration.
|
|
8
|
+
- Keep review evidence for both revisions, block superseding a draft that still has an operative accepted constraint, and update existing hook sessions when acceptance or retirement moves the anchors.
|
|
9
|
+
|
|
10
|
+
Upgrade every client using the decision store to `mason-context@0.10.1` and restart it. No data migration is required; older clients still have the old retrieval behavior. Accepting a draft replaces the operative revision, and retiring a decision withdraws both the accepted revision and its draft.
|
|
11
|
+
|
|
3
12
|
## 0.10.0 — 2026-09-05
|
|
4
13
|
|
|
5
14
|
Mason now provides useful project checks and decision capture without building an architecture map. This release strengthens the trust evidence around stored knowledge and brings existing test and analysis results into the same review.
|
package/README.md
CHANGED
|
@@ -29,7 +29,9 @@ After resolving a real incident or settling a constraint, ask your assistant to
|
|
|
29
29
|
|
|
30
30
|
For architecture navigation, ask: *"Build a Mason concept map."* The assistant calls `mason_init` with `mode: "map"` for the full Map-Reduce workflow.
|
|
31
31
|
|
|
32
|
-
> **
|
|
32
|
+
> **0.10.1 decision fix:** Accepted constraints remain visible while their replacement revisions are proposed. Update every client sharing the decision store to `mason-context@0.10.1` and restart it; no data migration is needed. See the [release notes](CHANGELOG.md#0101--2026-09-05).
|
|
33
|
+
|
|
34
|
+
> **Upgrading from 0.9.x:** Update Mason in every client that shares decision records, then re-run `mason_init` and refresh the marker-delimited assistant instructions. New decisions are version 2 proposals; legacy records stay explicitly unreviewed until reviewed. Use `review_decision` for acceptance or reaffirmation; saving unchanged content no longer refreshes evidence. Map builds now require `mode: "map"`. See the [migration notes](CHANGELOG.md#upgrading-from-090).
|
|
33
35
|
|
|
34
36
|
> **0.4.0 note:** The previous `mason <command>` CLI was removed in v0.4.0. Setup and map editing use MCP; dedicated drift, audit, hook, and review binaries support automation. See [0.4.0 migration](#040-migration) below if you used the old CLI.
|
|
35
37
|
|
|
@@ -115,7 +117,9 @@ After the user or cited team review authorizes a verdict, call `review_decision`
|
|
|
115
117
|
|
|
116
118
|
When anchored code changes later, use the same preparation flow and `action: "reaffirm"` to record that the accepted decision still holds, or `action: "retire"` to withdraw it. Retirement preserves history and can be recorded when Git history is unavailable. A reviewer may establish a new acceptance baseline when old history is unreachable; that gap stays recorded in the review event. Anchorless knowledge retains unknown code freshness even after acceptance.
|
|
117
119
|
|
|
118
|
-
Tools preserve earlier content and review events in each `.mason/decisions/<id>.json` file. Reviews record the reviewer, reason, timestamp, revision, and code baseline. Editing content, anchors, owner, or sources creates a new proposed revision
|
|
120
|
+
Tools preserve earlier content and review events in each `.mason/decisions/<id>.json` file. Reviews record the reviewer, reason, timestamp, revision, and code baseline. Editing content, anchors, owner, or sources creates a new proposed revision. The last accepted revision remains the operative constraint while that draft is reviewed. Context, hooks, map indexes, and diff reviews show the accepted content and a separate `pendingProposal`, each with its own anchors, attribution, and freshness. CI findings continue to associate with the accepted revision's anchors. This is derived from existing version 2 history without rewriting stored records.
|
|
121
|
+
|
|
122
|
+
Preparing a review shows the draft and its `operativeDecision`, with evidence covering both sets of anchors. Accepting the draft replaces the operative revision; retiring the record withdraws the accepted constraint and its draft together. Saving identical content is a no-op: it does not silently reaffirm or refresh the decision. A proposal cannot supersede a record that has an operative accepted revision. When creating a replacement under a different id, review it and explicitly retire the original separately.
|
|
119
123
|
|
|
120
124
|
**Existing records:** Version 1 records remain readable and explicitly unreviewed, without automatic file rewrites or invented attribution. Their first revision or review upgrades them to version 2 with an import event marking the missing earlier history. Old clients that only understand version 1 must be upgraded before consuming new records. Use the tools to revise records; inconsistent content/history is reported as invalid.
|
|
121
125
|
|
|
@@ -135,8 +139,8 @@ Mason records assertions of review; it does not authenticate reviewer identity,
|
|
|
135
139
|
| `export_to_confluence` | Sync the concept map to Confluence as PM-readable wiki pages. |
|
|
136
140
|
| `get_snapshot` | Architecture navigation when a map is available. Loads the concept map — feature → file lookup — in one LLM-free call. |
|
|
137
141
|
| `get_context` | Decisions with approval, provenance, file impact, tests, and trust for a task; adds features/flows when a map exists. No setup required. |
|
|
138
|
-
| `save_decision` | Capture or revise proposals with rationale, anchors, owner, sources, and
|
|
139
|
-
| `review_decision` | Prepare
|
|
142
|
+
| `save_decision` | Capture or revise proposals with rationale, anchors, owner, sources, and history. Prior accepted revisions remain operative while drafts are reviewed. |
|
|
143
|
+
| `review_decision` | Prepare draft and operative decision evidence, then record authorized acceptance, reaffirmation, or retirement against that revision. |
|
|
140
144
|
| `mason_check_drift` | Feature-level staleness report — what changed since the snapshot, and whether to refresh incrementally or rebuild. |
|
|
141
145
|
| `verify_snapshot` | Spot-check map correctness — sampled entries + file skeletons for the assistant to judge, least-recently-verified first. |
|
|
142
146
|
| `save_verification` | Record verification verdicts — failures flag entries for re-mapping until fixed. |
|
package/dist/mason-audit.js
CHANGED
|
@@ -1200,6 +1200,23 @@ function decisionContent(record) {
|
|
|
1200
1200
|
function decisionApproval(record) {
|
|
1201
1201
|
return record.version === 1 ? "unreviewed" : record.approval;
|
|
1202
1202
|
}
|
|
1203
|
+
function effectiveDecision(record) {
|
|
1204
|
+
if (record.version !== 2 || record.status !== "active" || record.approval !== "proposed") return record;
|
|
1205
|
+
let index = record.history.length - 1;
|
|
1206
|
+
while (index >= 0 && !["accepted", "reaffirmed"].includes(record.history[index].kind)) index--;
|
|
1207
|
+
if (index < 0) return record;
|
|
1208
|
+
const event = record.history[index];
|
|
1209
|
+
return {
|
|
1210
|
+
...record,
|
|
1211
|
+
...event.content,
|
|
1212
|
+
owner: event.content.owner,
|
|
1213
|
+
approval: "accepted",
|
|
1214
|
+
revision: event.revision,
|
|
1215
|
+
refreshedHash: event.refreshedHash,
|
|
1216
|
+
updatedAt: event.at,
|
|
1217
|
+
history: record.history.slice(0, index + 1)
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1203
1220
|
function decisionProvenance(record, freshness = "unknown") {
|
|
1204
1221
|
const approval = decisionApproval(record);
|
|
1205
1222
|
const review = record.version === 2 ? [...record.history].reverse().find((e) => ["accepted", "reaffirmed"].includes(e.kind) && e.revision === record.revision) : void 0;
|
|
@@ -1246,12 +1263,8 @@ async function computeDecisionDrift(rootDir, decisions) {
|
|
|
1246
1263
|
const report = { historyAvailable: true, totalDecisions: store.records.length, staleDecisions: {}, freshness: {}, diagnostics: store.diagnostics };
|
|
1247
1264
|
const [head, workingTree] = await Promise.all([getCurrentGitHash(resolvedRoot), getWorkingTree(resolvedRoot)]);
|
|
1248
1265
|
const changesByHash = /* @__PURE__ */ new Map();
|
|
1249
|
-
|
|
1250
|
-
if (record.
|
|
1251
|
-
if (record.files.length === 0) {
|
|
1252
|
-
report.freshness[record.id] = "unknown";
|
|
1253
|
-
continue;
|
|
1254
|
-
}
|
|
1266
|
+
const inspect = async (record) => {
|
|
1267
|
+
if (record.files.length === 0) return { freshness: "unknown", changedFiles: [] };
|
|
1255
1268
|
let touched = changesByHash.get(record.refreshedHash);
|
|
1256
1269
|
if (touched === void 0) {
|
|
1257
1270
|
const changes = record.refreshedHash === head && head !== "unknown" ? [] : await getChangesWithStatus(resolvedRoot, record.refreshedHash);
|
|
@@ -1260,9 +1273,16 @@ async function computeDecisionDrift(rootDir, decisions) {
|
|
|
1260
1273
|
}
|
|
1261
1274
|
if (touched === null) report.historyAvailable = false;
|
|
1262
1275
|
const hits = touched ? matchingPaths(record.files, touched) : [];
|
|
1263
|
-
if (hits.length) report.staleDecisions[record.id] = hits;
|
|
1264
1276
|
const localHits = matchingPaths(record.files, workingTree.changedFiles);
|
|
1265
|
-
|
|
1277
|
+
return { freshness: touched === null || !workingTree.available ? "unknown" : hits.length || localHits.length ? "changed" : "current", changedFiles: hits };
|
|
1278
|
+
};
|
|
1279
|
+
for (const record of store.records) {
|
|
1280
|
+
if (record.status !== "active") continue;
|
|
1281
|
+
const effective = effectiveDecision(record);
|
|
1282
|
+
const state = await inspect(effective);
|
|
1283
|
+
report.freshness[record.id] = state.freshness;
|
|
1284
|
+
if (state.changedFiles.length) report.staleDecisions[record.id] = state.changedFiles;
|
|
1285
|
+
if (effective !== record) (report.pendingProposals ??= {})[record.id] = await inspect(record);
|
|
1266
1286
|
}
|
|
1267
1287
|
return report;
|
|
1268
1288
|
}
|
|
@@ -1281,11 +1301,14 @@ async function checkDecisionAnchors(ctx) {
|
|
|
1281
1301
|
reason: "some decision base commits are unreachable (shallow clone?)"
|
|
1282
1302
|
});
|
|
1283
1303
|
}
|
|
1284
|
-
const
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1304
|
+
const changed = records.flatMap((record) => [
|
|
1305
|
+
{ record: effectiveDecision(record), changedFiles: drift.staleDecisions[record.id] ?? [], freshness: drift.freshness?.[record.id] ?? "unknown" },
|
|
1306
|
+
{ record, changedFiles: drift.pendingProposals?.[record.id]?.changedFiles ?? [], freshness: drift.pendingProposals?.[record.id]?.freshness ?? "unknown" }
|
|
1307
|
+
]);
|
|
1308
|
+
for (const { record, changedFiles, freshness } of changed) {
|
|
1309
|
+
if (!changedFiles.length) continue;
|
|
1310
|
+
const id = record.id;
|
|
1311
|
+
const provenance = decisionProvenance(record, freshness);
|
|
1289
1312
|
result.advisories.push({
|
|
1290
1313
|
type: "decision-anchor-drift",
|
|
1291
1314
|
message: `decision "${record.title}" (${provenance.approval}) has anchor files that changed since its evidence baseline \u2013 needs human review`,
|