daftari 1.14.0 → 1.15.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 +45 -0
- package/dist/curation/lint.d.ts +16 -1
- package/dist/curation/lint.d.ts.map +1 -1
- package/dist/curation/lint.js +91 -78
- package/dist/curation/lint.js.map +1 -1
- package/dist/curation/tension-blast.d.ts +37 -0
- package/dist/curation/tension-blast.d.ts.map +1 -0
- package/dist/curation/tension-blast.js +223 -0
- package/dist/curation/tension-blast.js.map +1 -0
- package/dist/curation/tension-clusters.d.ts +18 -0
- package/dist/curation/tension-clusters.d.ts.map +1 -0
- package/dist/curation/tension-clusters.js +157 -0
- package/dist/curation/tension-clusters.js.map +1 -0
- package/dist/curation/tension.d.ts +4 -0
- package/dist/curation/tension.d.ts.map +1 -1
- package/dist/curation/tension.js +54 -0
- package/dist/curation/tension.js.map +1 -1
- package/dist/curation/vault-docs.d.ts +14 -0
- package/dist/curation/vault-docs.d.ts.map +1 -0
- package/dist/curation/vault-docs.js +90 -0
- package/dist/curation/vault-docs.js.map +1 -0
- package/dist/tools/curation.d.ts +4 -0
- package/dist/tools/curation.d.ts.map +1 -1
- package/dist/tools/curation.js +101 -0
- package/dist/tools/curation.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,51 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.15.0] - 2026-05-31
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Blast radius of stale tensions** (Step 5 of the Tension Graph plan,
|
|
15
|
+
cross-feature integration). `vault_lint`'s tension health surface now
|
|
16
|
+
reports `blastRadiusOfStaleTensions`: the cardinality of the
|
|
17
|
+
deduplicated `primary_blast` set (sources channel only — the same
|
|
18
|
+
primary set `vault_tension_blast` returns) over the union of contested
|
|
19
|
+
docs from every entry where `resolved: false` AND
|
|
20
|
+
`agingTier === "stale"`. Renders as "Blast radius of stale tensions: N
|
|
21
|
+
downstream documents". When there are no stale unresolved tensions the
|
|
22
|
+
metric is 0; the line always renders for consistency with the rest of
|
|
23
|
+
the tension health section. Reuses `computeBlast` from
|
|
24
|
+
`tension-blast.ts` — advisory link edges still participate in BFS
|
|
25
|
+
traversal, but the published metric stays disciplined to the primary
|
|
26
|
+
channel.
|
|
27
|
+
|
|
28
|
+
- **Tension blast radius** (Phase 3 of the Tension Graph plan). New
|
|
29
|
+
`vault_tension_blast` tool computes the transitive closure of
|
|
30
|
+
downstream documents that cite or link a contested document — or the
|
|
31
|
+
union over a contested cluster. Accepts exactly one of `document` or
|
|
32
|
+
`cluster_id`. Two confidence channels: `primary_blast` (via `sources`
|
|
33
|
+
frontmatter) is authoritative; `advisory_blast` (via in-vault markdown
|
|
34
|
+
links) is suggestive. `superseded_by` is not a blast edge — the doc
|
|
35
|
+
that supersedes a contested doc is the replacement, not an inheritor.
|
|
36
|
+
|
|
37
|
+
- **Tension clusters** (Phase 2 of the Tension Graph plan). New
|
|
38
|
+
`vault_tension_clusters` tool computes connected components of the
|
|
39
|
+
tension graph over unresolved, non-accepted tensions. Cluster IDs are
|
|
40
|
+
content-addressed (`cluster:` + first 8 hex chars of sha256 of
|
|
41
|
+
canonical-sorted member paths) — stable across runs for unchanged
|
|
42
|
+
membership; a different ID encodes a different membership. `vault_lint`
|
|
43
|
+
reports cluster count, max size, and flags clusters that are large
|
|
44
|
+
(>5 docs, smell) or aged (oldest tension >90 days, tech debt).
|
|
45
|
+
|
|
46
|
+
- **Tension aging tiers** (Phase 4 of the Tension Graph plan). Tensions
|
|
47
|
+
in the tension log now report aging tiers (Fresh 0–30d / Aging 31–90d
|
|
48
|
+
/ Stale 90+d) in `vault_lint`, with kind-specific lint copy at the
|
|
49
|
+
stale tier. Unspecified tensions and tensions resolved with kind
|
|
50
|
+
`accepted` are excluded from the aging pipeline — the former because
|
|
51
|
+
they predate classification, the latter because explicitly accepted
|
|
52
|
+
persistent disagreements are stable epistemic features rather than
|
|
53
|
+
debt.
|
|
54
|
+
|
|
10
55
|
## [1.14.0] - 2026-05-31
|
|
11
56
|
|
|
12
57
|
### Added
|
package/dist/curation/lint.d.ts
CHANGED
|
@@ -6,6 +6,19 @@ export interface LintFinding {
|
|
|
6
6
|
path: string;
|
|
7
7
|
detail: string;
|
|
8
8
|
}
|
|
9
|
+
export interface TensionAging {
|
|
10
|
+
fresh: number;
|
|
11
|
+
aging: number;
|
|
12
|
+
stale: number;
|
|
13
|
+
staleByKind: Record<TensionKind, number>;
|
|
14
|
+
staleMessages: Partial<Record<Exclude<TensionKind, "unspecified">, string>>;
|
|
15
|
+
}
|
|
16
|
+
export interface TensionClustersHealth {
|
|
17
|
+
count: number;
|
|
18
|
+
maxSize: number;
|
|
19
|
+
large: number;
|
|
20
|
+
aged: number;
|
|
21
|
+
}
|
|
9
22
|
export interface TensionHealth {
|
|
10
23
|
total: number;
|
|
11
24
|
byKind: Record<TensionKind, number>;
|
|
@@ -13,6 +26,9 @@ export interface TensionHealth {
|
|
|
13
26
|
byResolutionKind: Record<ResolutionKind, number>;
|
|
14
27
|
stableAcknowledged: number;
|
|
15
28
|
unspecifiedLegacy: number;
|
|
29
|
+
aging: TensionAging;
|
|
30
|
+
clusters: TensionClustersHealth;
|
|
31
|
+
blastRadiusOfStaleTensions: number;
|
|
16
32
|
}
|
|
17
33
|
export interface LintReport {
|
|
18
34
|
generatedAt: string;
|
|
@@ -25,6 +41,5 @@ export interface LintOptions {
|
|
|
25
41
|
draftMaxDays?: number;
|
|
26
42
|
lowConfidenceMaxDays?: number;
|
|
27
43
|
}
|
|
28
|
-
export declare function extractLinks(content: string): string[];
|
|
29
44
|
export declare function runLint(vaultRoot: string, opts?: LintOptions): Promise<Result<LintReport, Error>>;
|
|
30
45
|
//# sourceMappingURL=lint.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../src/curation/lint.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../src/curation/lint.ts"],"names":[],"mappings":"AAOA,OAAO,EAAM,KAAK,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAG1D,OAAO,EAIL,KAAK,cAAc,EAGnB,KAAK,WAAW,EACjB,MAAM,cAAc,CAAC;AAWtB,eAAO,MAAM,WAAW,8HAOd,CAAC;AACX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAyBD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IACzC,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,aAAa,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;CAC7E;AAOD,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IACpC,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IACjD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE,YAAY,CAAC;IACpB,QAAQ,EAAE,qBAAqB,CAAC;IAMhC,0BAA0B,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,aAAa,EAAE,WAAW,EAAE,CAAC,CAAC;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,aAAa,CAAC;CAC9B;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AA8BD,wBAAsB,OAAO,CAC3B,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,CAqHpC"}
|
package/dist/curation/lint.js
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
// runs six checks. It only ever *reports*: no file is edited, no status is
|
|
5
5
|
// changed, nothing is auto-fixed. The output is a structured report grouped by
|
|
6
6
|
// check, for a human (or an agent acting on a human's behalf) to triage.
|
|
7
|
-
import { posix } from "node:path";
|
|
8
|
-
import { parseDocument } from "../frontmatter/parser.js";
|
|
9
7
|
import { ok } from "../frontmatter/types.js";
|
|
10
|
-
import { listFiles, readFile, resolveVaultPath } from "../storage/local.js";
|
|
11
8
|
import { DRAFT_MAX_DAYS, LOW_CONFIDENCE_MAX_DAYS } from "./decay.js";
|
|
12
9
|
import { ageInDays, computeStaleness } from "./staleness.js";
|
|
13
|
-
import { listTensions, RESOLUTION_KINDS, TENSION_KINDS, } from "./tension.js";
|
|
10
|
+
import { agingTier, listTensions, RESOLUTION_KINDS, STALE_TIER_LINT_COPY, TENSION_KINDS, } from "./tension.js";
|
|
11
|
+
import { buildReverseLinkMap, buildReverseSourceMap, computeBlast } from "./tension-blast.js";
|
|
12
|
+
import { computeTensionClusters } from "./tension-clusters.js";
|
|
13
|
+
import { buildPathIndexes, extractLinks, loadDocuments, resolveLink, } from "./vault-docs.js";
|
|
14
14
|
export const LINT_CHECKS = [
|
|
15
15
|
"staleFiles",
|
|
16
16
|
"orphanFiles",
|
|
@@ -19,45 +19,6 @@ export const LINT_CHECKS = [
|
|
|
19
19
|
"deprecatedStillLinked",
|
|
20
20
|
"unansweredQuestions",
|
|
21
21
|
];
|
|
22
|
-
// --- link extraction ------------------------------------------------------
|
|
23
|
-
// Pulls every internal link target out of a markdown body: both [[wikilinks]]
|
|
24
|
-
// and [text](target) markdown links. External URLs and anchors are dropped.
|
|
25
|
-
export function extractLinks(content) {
|
|
26
|
-
const targets = [];
|
|
27
|
-
for (const m of content.matchAll(/\[\[([^\]]+)\]\]/g)) {
|
|
28
|
-
// A wikilink may carry a |display alias and/or a #heading anchor.
|
|
29
|
-
const raw = m[1].split("|")[0]?.split("#")[0]?.trim();
|
|
30
|
-
if (raw)
|
|
31
|
-
targets.push(raw);
|
|
32
|
-
}
|
|
33
|
-
for (const m of content.matchAll(/\[[^\]]*\]\(([^)\s]+)\)/g)) {
|
|
34
|
-
const raw = m[1].split("#")[0]?.trim();
|
|
35
|
-
if (!raw)
|
|
36
|
-
continue;
|
|
37
|
-
if (/^(https?:|mailto:|#)/i.test(raw))
|
|
38
|
-
continue;
|
|
39
|
-
targets.push(raw);
|
|
40
|
-
}
|
|
41
|
-
return targets;
|
|
42
|
-
}
|
|
43
|
-
// Resolves a raw link target to a vault-relative path, or null if it points
|
|
44
|
-
// nowhere. Tries, in order: the target as-is, with a .md suffix, resolved
|
|
45
|
-
// relative to the linking file's directory, then a bare basename match (the
|
|
46
|
-
// common [[note-name]] wikilink form).
|
|
47
|
-
function resolveLink(rawTarget, fromPath, byPath, byBasename) {
|
|
48
|
-
const withMd = (p) => (p.endsWith(".md") ? p : `${p}.md`);
|
|
49
|
-
if (byPath.has(rawTarget))
|
|
50
|
-
return rawTarget;
|
|
51
|
-
if (byPath.has(withMd(rawTarget)))
|
|
52
|
-
return withMd(rawTarget);
|
|
53
|
-
const relual = posix.normalize(posix.join(posix.dirname(fromPath), rawTarget));
|
|
54
|
-
if (byPath.has(relual))
|
|
55
|
-
return relual;
|
|
56
|
-
if (byPath.has(withMd(relual)))
|
|
57
|
-
return withMd(relual);
|
|
58
|
-
const base = posix.basename(rawTarget).replace(/\.md$/, "");
|
|
59
|
-
return byBasename.get(base) ?? null;
|
|
60
|
-
}
|
|
61
22
|
// --- question matching ----------------------------------------------------
|
|
62
23
|
// Normalizes a question for cross-document matching: trimmed, lower-cased,
|
|
63
24
|
// internal whitespace collapsed. Exact (normalized) equality is the matching
|
|
@@ -66,39 +27,9 @@ function normalizeQuestion(q) {
|
|
|
66
27
|
return q.trim().toLowerCase().replace(/\s+/g, " ");
|
|
67
28
|
}
|
|
68
29
|
// --- check orchestration --------------------------------------------------
|
|
69
|
-
async function loadDocuments(vaultRoot) {
|
|
70
|
-
const list = await listFiles(vaultRoot);
|
|
71
|
-
if (!list.ok)
|
|
72
|
-
return list;
|
|
73
|
-
const docs = [];
|
|
74
|
-
for (const relPath of list.value) {
|
|
75
|
-
const resolved = resolveVaultPath(vaultRoot, relPath);
|
|
76
|
-
if (!resolved.ok)
|
|
77
|
-
continue;
|
|
78
|
-
const file = await readFile(resolved.value);
|
|
79
|
-
if (!file.ok)
|
|
80
|
-
continue;
|
|
81
|
-
const parsed = parseDocument(file.value);
|
|
82
|
-
if (!parsed.ok)
|
|
83
|
-
continue;
|
|
84
|
-
docs.push({
|
|
85
|
-
path: relPath,
|
|
86
|
-
frontmatter: parsed.value.frontmatter,
|
|
87
|
-
content: parsed.value.content,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
return ok(docs);
|
|
91
|
-
}
|
|
92
30
|
// Maps each document to the set of documents that link to it.
|
|
93
31
|
function buildInboundMap(docs) {
|
|
94
|
-
const byPath =
|
|
95
|
-
const byBasename = new Map();
|
|
96
|
-
for (const d of docs) {
|
|
97
|
-
const base = posix.basename(d.path).replace(/\.md$/, "");
|
|
98
|
-
// First write wins, so a basename collision resolves deterministically.
|
|
99
|
-
if (!byBasename.has(base))
|
|
100
|
-
byBasename.set(base, d.path);
|
|
101
|
-
}
|
|
32
|
+
const { byPath, byBasename } = buildPathIndexes(docs);
|
|
102
33
|
const inbound = new Map();
|
|
103
34
|
for (const d of docs) {
|
|
104
35
|
for (const raw of extractLinks(d.content)) {
|
|
@@ -206,7 +137,7 @@ export async function runLint(vaultRoot, opts = {}) {
|
|
|
206
137
|
}
|
|
207
138
|
}
|
|
208
139
|
const totalFindings = LINT_CHECKS.reduce((n, name) => n + checks[name].length, 0);
|
|
209
|
-
const tensionHealth = await computeTensionHealth(vaultRoot);
|
|
140
|
+
const tensionHealth = await computeTensionHealth(vaultRoot, docs, now);
|
|
210
141
|
if (!tensionHealth.ok)
|
|
211
142
|
return tensionHealth;
|
|
212
143
|
return ok({
|
|
@@ -216,18 +147,29 @@ export async function runLint(vaultRoot, opts = {}) {
|
|
|
216
147
|
tensionHealth: tensionHealth.value,
|
|
217
148
|
});
|
|
218
149
|
}
|
|
219
|
-
// Aggregates the tension log into the Phase 1 health summary
|
|
220
|
-
// is not an error — every counter is
|
|
221
|
-
|
|
150
|
+
// Aggregates the tension log into the Phase 1 health summary plus the Phase 4
|
|
151
|
+
// aging breakdown. A missing log is not an error — every counter is zero.
|
|
152
|
+
//
|
|
153
|
+
// Aging scope: tiers are counted over the active surface only. An entry
|
|
154
|
+
// contributes to fresh / aging / stale iff it is unresolved AND `agingTier`
|
|
155
|
+
// returns a non-null tier (which already excludes `unspecified`). Resolved
|
|
156
|
+
// entries — including `resolution.kind: accepted` — do not appear in any
|
|
157
|
+
// aging tier; they show up in the Phase 1 stable-acknowledged and resolved
|
|
158
|
+
// totals instead.
|
|
159
|
+
async function computeTensionHealth(vaultRoot, docs, now) {
|
|
222
160
|
const tensions = await listTensions(vaultRoot);
|
|
223
161
|
if (!tensions.ok)
|
|
224
162
|
return tensions;
|
|
225
163
|
const byKind = Object.fromEntries(TENSION_KINDS.map((k) => [k, 0]));
|
|
226
164
|
const byResolutionKind = Object.fromEntries(RESOLUTION_KINDS.map((k) => [k, 0]));
|
|
165
|
+
const staleByKind = Object.fromEntries(TENSION_KINDS.map((k) => [k, 0]));
|
|
227
166
|
let total = 0;
|
|
228
167
|
let resolvedLifetime = 0;
|
|
229
168
|
let stableAcknowledged = 0;
|
|
230
169
|
let unspecifiedLegacy = 0;
|
|
170
|
+
let fresh = 0;
|
|
171
|
+
let aging = 0;
|
|
172
|
+
let stale = 0;
|
|
231
173
|
for (const t of tensions.value) {
|
|
232
174
|
total += 1;
|
|
233
175
|
byKind[t.kind] += 1;
|
|
@@ -239,7 +181,75 @@ async function computeTensionHealth(vaultRoot) {
|
|
|
239
181
|
if (t.resolution.kind === "accepted")
|
|
240
182
|
stableAcknowledged += 1;
|
|
241
183
|
}
|
|
184
|
+
if (t.resolved)
|
|
185
|
+
continue;
|
|
186
|
+
const tier = agingTier(t, now);
|
|
187
|
+
if (tier === "fresh")
|
|
188
|
+
fresh += 1;
|
|
189
|
+
else if (tier === "aging")
|
|
190
|
+
aging += 1;
|
|
191
|
+
else if (tier === "stale") {
|
|
192
|
+
stale += 1;
|
|
193
|
+
staleByKind[t.kind] += 1;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// Render kind-specific stale-tier copy only for kinds with a nonzero count.
|
|
197
|
+
// `unspecified` never produces a message even if the count somehow appears
|
|
198
|
+
// (it can't, since unspecified entries get tier null — defense in depth).
|
|
199
|
+
const staleMessages = {};
|
|
200
|
+
for (const kind of ["temporal", "factual", "interpretive"]) {
|
|
201
|
+
if (staleByKind[kind] > 0) {
|
|
202
|
+
staleMessages[kind] = STALE_TIER_LINT_COPY[kind];
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Cluster surface (Phase 2). computeTensionClusters applies the same scope
|
|
206
|
+
// filter the cluster tool does — unresolved AND non-accepted — so the lint
|
|
207
|
+
// metrics line up exactly with what `vault_tension_clusters` reports.
|
|
208
|
+
const clusterResult = computeTensionClusters(tensions.value, now);
|
|
209
|
+
let maxSize = 0;
|
|
210
|
+
let large = 0;
|
|
211
|
+
let aged = 0;
|
|
212
|
+
for (const c of clusterResult.clusters) {
|
|
213
|
+
if (c.size > maxSize)
|
|
214
|
+
maxSize = c.size;
|
|
215
|
+
if (c.size > 5)
|
|
216
|
+
large += 1;
|
|
217
|
+
if (c.oldest_tension_age_days > 90)
|
|
218
|
+
aged += 1;
|
|
242
219
|
}
|
|
220
|
+
const clusters = {
|
|
221
|
+
count: clusterResult.cluster_count,
|
|
222
|
+
maxSize,
|
|
223
|
+
large,
|
|
224
|
+
aged,
|
|
225
|
+
};
|
|
226
|
+
// Step 5 of the tension graph plan (2026-05-31). Collect every contested
|
|
227
|
+
// doc from every entry where `resolved: false` AND `agingTier === "stale"`,
|
|
228
|
+
// then reuse computeBlast to walk the dependency graph from that union as
|
|
229
|
+
// seeds. We report the count of the `primary_blast` channel — the same
|
|
230
|
+
// sources-only primary set that vault_tension_blast returns. Advisory link
|
|
231
|
+
// edges still participate in BFS traversal (so a doc reached via a link
|
|
232
|
+
// edge can still gain `source` classification from a separate incoming
|
|
233
|
+
// source edge), but the published metric is the primary count only — the
|
|
234
|
+
// top-level lint metric stays disciplined against advisory inflation.
|
|
235
|
+
const staleSeeds = new Set();
|
|
236
|
+
for (const t of tensions.value) {
|
|
237
|
+
if (t.resolved)
|
|
238
|
+
continue;
|
|
239
|
+
if (agingTier(t, now) !== "stale")
|
|
240
|
+
continue;
|
|
241
|
+
if (t.sourceA)
|
|
242
|
+
staleSeeds.add(t.sourceA);
|
|
243
|
+
if (t.sourceB)
|
|
244
|
+
staleSeeds.add(t.sourceB);
|
|
245
|
+
}
|
|
246
|
+
const reverseSource = buildReverseSourceMap(docs);
|
|
247
|
+
const reverseLink = buildReverseLinkMap(docs);
|
|
248
|
+
const blast = computeBlast({
|
|
249
|
+
seeds: [...staleSeeds],
|
|
250
|
+
reverseSource,
|
|
251
|
+
reverseLink,
|
|
252
|
+
});
|
|
243
253
|
return ok({
|
|
244
254
|
total,
|
|
245
255
|
byKind,
|
|
@@ -247,6 +257,9 @@ async function computeTensionHealth(vaultRoot) {
|
|
|
247
257
|
byResolutionKind,
|
|
248
258
|
stableAcknowledged,
|
|
249
259
|
unspecifiedLegacy,
|
|
260
|
+
aging: { fresh, aging, stale, staleByKind, staleMessages },
|
|
261
|
+
clusters,
|
|
262
|
+
blastRadiusOfStaleTensions: blast.primary_blast,
|
|
250
263
|
});
|
|
251
264
|
}
|
|
252
265
|
//# sourceMappingURL=lint.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lint.js","sourceRoot":"","sources":["../../src/curation/lint.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,+EAA+E;AAC/E,yEAAyE;AAEzE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"lint.js","sourceRoot":"","sources":["../../src/curation/lint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type Result } from "../frontmatter/types.js";
|
|
2
|
+
import { type LoadedDoc } from "./vault-docs.js";
|
|
3
|
+
export type BlastDependencyType = "source" | "link";
|
|
4
|
+
export interface BlastDownstreamEntry {
|
|
5
|
+
path: string;
|
|
6
|
+
dependency_type: BlastDependencyType;
|
|
7
|
+
distance: number;
|
|
8
|
+
}
|
|
9
|
+
export interface TensionBlastResult {
|
|
10
|
+
contested_document: string | null;
|
|
11
|
+
cluster_id: string | null;
|
|
12
|
+
cluster_documents: string[];
|
|
13
|
+
downstream: BlastDownstreamEntry[];
|
|
14
|
+
primary_blast: number;
|
|
15
|
+
advisory_blast: number;
|
|
16
|
+
max_depth: number;
|
|
17
|
+
}
|
|
18
|
+
export interface TensionBlastInput {
|
|
19
|
+
document?: string;
|
|
20
|
+
cluster_id?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function buildReverseSourceMap(docs: LoadedDoc[]): Map<string, Set<string>>;
|
|
23
|
+
export declare function buildReverseLinkMap(docs: LoadedDoc[]): Map<string, Set<string>>;
|
|
24
|
+
export interface ComputeBlastArgs {
|
|
25
|
+
seeds: string[];
|
|
26
|
+
reverseSource: Map<string, Set<string>>;
|
|
27
|
+
reverseLink: Map<string, Set<string>>;
|
|
28
|
+
}
|
|
29
|
+
export interface ComputeBlastOutput {
|
|
30
|
+
downstream: BlastDownstreamEntry[];
|
|
31
|
+
primary_blast: number;
|
|
32
|
+
advisory_blast: number;
|
|
33
|
+
max_depth: number;
|
|
34
|
+
}
|
|
35
|
+
export declare function computeBlast(args: ComputeBlastArgs): ComputeBlastOutput;
|
|
36
|
+
export declare function computeTensionBlast(vaultRoot: string, input: TensionBlastInput): Promise<Result<TensionBlastResult, Error>>;
|
|
37
|
+
//# sourceMappingURL=tension-blast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tension-blast.d.ts","sourceRoot":"","sources":["../../src/curation/tension-blast.ts"],"names":[],"mappings":"AAuCA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,EAGL,KAAK,SAAS,EAGf,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEpD,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,mBAAmB,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAQD,MAAM,WAAW,kBAAkB;IACjC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,UAAU,EAAE,oBAAoB,EAAE,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAQD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAYjF;AAKD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAY/E;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACxC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,oBAAoB,EAAE,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AAaD,wBAAgB,YAAY,CAAC,IAAI,EAAE,gBAAgB,GAAG,kBAAkB,CA8DvE;AAKD,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC,CAoE5C"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
// Tension blast radius — Phase 3 of the tension graph plan (2026-05-31).
|
|
2
|
+
//
|
|
3
|
+
// "Blast" is the transitive closure of downstream documents that cite or
|
|
4
|
+
// depend on a contested node. Given either a single document or a tension
|
|
5
|
+
// cluster, this surface walks the dependency graph and reports the docs
|
|
6
|
+
// exposed to that contested state.
|
|
7
|
+
//
|
|
8
|
+
// Two edge types (spec, Gap 5):
|
|
9
|
+
// - frontmatter `sources` array → "source" edges (primary, high-confidence).
|
|
10
|
+
// - in-vault markdown links → "link" edges (advisory).
|
|
11
|
+
//
|
|
12
|
+
// `superseded_by` is NOT a blast edge. A doc that supersedes a contested doc
|
|
13
|
+
// is the *replacement*, not an inheritor — walking the blast through that
|
|
14
|
+
// relationship would falsely contaminate the resolution path. The existing
|
|
15
|
+
// `deprecated-still-linked` lint check covers the related-but-different
|
|
16
|
+
// question of deprecated docs still being cited.
|
|
17
|
+
//
|
|
18
|
+
// Two confidence channels (spec, Gap 6):
|
|
19
|
+
// - `primary_blast`: count of downstream docs that have at least one
|
|
20
|
+
// incoming source edge from a visited node (a contested member or a doc
|
|
21
|
+
// already in the downstream set).
|
|
22
|
+
// - `advisory_blast`: count of downstream docs reached only via link edges.
|
|
23
|
+
//
|
|
24
|
+
// A doc reachable via both edge types counts as primary — higher-confidence
|
|
25
|
+
// wins. The per-immediate-edge rule matches the spec's "reached via both edge
|
|
26
|
+
// types" wording: from the downstream doc's perspective, does any visited
|
|
27
|
+
// predecessor cite it via the `sources` frontmatter array?
|
|
28
|
+
//
|
|
29
|
+
// Two input modes (spec, Gap 7):
|
|
30
|
+
// - document mode: blast for a single doc. Response also identifies the
|
|
31
|
+
// containing cluster (if any) so the agent sees the broader region.
|
|
32
|
+
// - cluster mode: blast for the union of all cluster members. Seeds are
|
|
33
|
+
// never reported as downstream of themselves.
|
|
34
|
+
//
|
|
35
|
+
// Computation is on-demand — we build the reverse-source and reverse-link
|
|
36
|
+
// maps at tool-call time from the loaded docs. There is no maintained graph.
|
|
37
|
+
// Cycle protection is implicit: BFS marks nodes visited; a node is never
|
|
38
|
+
// re-queued, so a sources b sources a terminates after one round trip.
|
|
39
|
+
import { err, ok } from "../frontmatter/types.js";
|
|
40
|
+
import { loadTensionClusters } from "./tension-clusters.js";
|
|
41
|
+
import { buildPathIndexes, extractLinks, loadDocuments, resolveLink, } from "./vault-docs.js";
|
|
42
|
+
// Reverse-source map: target doc → docs whose frontmatter `sources` cites
|
|
43
|
+
// the target. Source paths are resolved through the same path-resolution
|
|
44
|
+
// rules as in-vault links (exact match, .md suffix, relative-to-from,
|
|
45
|
+
// basename), so author-written shorthand paths like `pricing/foo` line up
|
|
46
|
+
// with the canonical `pricing/foo.md`. Unresolved sources (typos, links to
|
|
47
|
+
// non-vault material) and self-citations are dropped.
|
|
48
|
+
export function buildReverseSourceMap(docs) {
|
|
49
|
+
const reverse = new Map();
|
|
50
|
+
const { byPath, byBasename } = buildPathIndexes(docs);
|
|
51
|
+
for (const d of docs) {
|
|
52
|
+
for (const raw of d.frontmatter.sources ?? []) {
|
|
53
|
+
const target = resolveLink(raw, d.path, byPath, byBasename);
|
|
54
|
+
if (!target || target === d.path)
|
|
55
|
+
continue;
|
|
56
|
+
if (!reverse.has(target))
|
|
57
|
+
reverse.set(target, new Set());
|
|
58
|
+
reverse.get(target).add(d.path);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return reverse;
|
|
62
|
+
}
|
|
63
|
+
// Reverse-link map: target doc → docs whose body contains an in-vault
|
|
64
|
+
// markdown or wikilink to the target. Mirrors lint's inbound-link map but
|
|
65
|
+
// exposed as its own helper so blast and lint can't drift.
|
|
66
|
+
export function buildReverseLinkMap(docs) {
|
|
67
|
+
const reverse = new Map();
|
|
68
|
+
const { byPath, byBasename } = buildPathIndexes(docs);
|
|
69
|
+
for (const d of docs) {
|
|
70
|
+
for (const raw of extractLinks(d.content)) {
|
|
71
|
+
const target = resolveLink(raw, d.path, byPath, byBasename);
|
|
72
|
+
if (!target || target === d.path)
|
|
73
|
+
continue;
|
|
74
|
+
if (!reverse.has(target))
|
|
75
|
+
reverse.set(target, new Set());
|
|
76
|
+
reverse.get(target).add(d.path);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return reverse;
|
|
80
|
+
}
|
|
81
|
+
// Pure BFS over the combined edge set. Returns the sorted downstream list,
|
|
82
|
+
// the two channel counts, and the max depth observed.
|
|
83
|
+
//
|
|
84
|
+
// Layered BFS guarantees `distance` is the minimum hop count from any seed;
|
|
85
|
+
// the `visited` set is the cycle guard, so a doc is never re-queued.
|
|
86
|
+
//
|
|
87
|
+
// Channel assignment is per-immediate-edge: a downstream doc D is "source"
|
|
88
|
+
// iff some visited node V (a seed or another downstream doc) has D in its
|
|
89
|
+
// reverse-source successor list, i.e. D's frontmatter cites V. Otherwise D
|
|
90
|
+
// is "link". This matches the spec's "reached via both edge types counts as
|
|
91
|
+
// primary" wording while staying decidable from the dependency graph alone.
|
|
92
|
+
export function computeBlast(args) {
|
|
93
|
+
const { seeds, reverseSource, reverseLink } = args;
|
|
94
|
+
const seedSet = new Set(seeds);
|
|
95
|
+
const visited = new Set(seeds);
|
|
96
|
+
const distance = new Map();
|
|
97
|
+
let frontier = new Set(seeds);
|
|
98
|
+
let depth = 0;
|
|
99
|
+
while (frontier.size > 0) {
|
|
100
|
+
const next = new Set();
|
|
101
|
+
for (const node of frontier) {
|
|
102
|
+
const successors = new Set();
|
|
103
|
+
for (const s of reverseSource.get(node) ?? [])
|
|
104
|
+
successors.add(s);
|
|
105
|
+
for (const s of reverseLink.get(node) ?? [])
|
|
106
|
+
successors.add(s);
|
|
107
|
+
for (const succ of successors) {
|
|
108
|
+
if (visited.has(succ))
|
|
109
|
+
continue;
|
|
110
|
+
visited.add(succ);
|
|
111
|
+
distance.set(succ, depth + 1);
|
|
112
|
+
next.add(succ);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
frontier = next;
|
|
116
|
+
depth += 1;
|
|
117
|
+
}
|
|
118
|
+
// primaryDownstream: every doc that has at least one incoming source edge
|
|
119
|
+
// from a visited node. Built by iterating each visited node's
|
|
120
|
+
// reverse-source successors; we exclude seeds because the response only
|
|
121
|
+
// surfaces downstream-of-the-contested-set, never the contested set
|
|
122
|
+
// itself.
|
|
123
|
+
const primaryDownstream = new Set();
|
|
124
|
+
for (const v of visited) {
|
|
125
|
+
for (const child of reverseSource.get(v) ?? []) {
|
|
126
|
+
if (seedSet.has(child))
|
|
127
|
+
continue;
|
|
128
|
+
primaryDownstream.add(child);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const entries = [];
|
|
132
|
+
for (const [path, dist] of distance) {
|
|
133
|
+
const dependency_type = primaryDownstream.has(path) ? "source" : "link";
|
|
134
|
+
entries.push({ path, dependency_type, distance: dist });
|
|
135
|
+
}
|
|
136
|
+
entries.sort((a, b) => {
|
|
137
|
+
if (a.distance !== b.distance)
|
|
138
|
+
return a.distance - b.distance;
|
|
139
|
+
if (a.dependency_type !== b.dependency_type) {
|
|
140
|
+
return a.dependency_type === "source" ? -1 : 1;
|
|
141
|
+
}
|
|
142
|
+
return a.path < b.path ? -1 : a.path > b.path ? 1 : 0;
|
|
143
|
+
});
|
|
144
|
+
let primary_blast = 0;
|
|
145
|
+
let advisory_blast = 0;
|
|
146
|
+
let max_depth = 0;
|
|
147
|
+
for (const e of entries) {
|
|
148
|
+
if (e.dependency_type === "source")
|
|
149
|
+
primary_blast += 1;
|
|
150
|
+
else
|
|
151
|
+
advisory_blast += 1;
|
|
152
|
+
if (e.distance > max_depth)
|
|
153
|
+
max_depth = e.distance;
|
|
154
|
+
}
|
|
155
|
+
return { downstream: entries, primary_blast, advisory_blast, max_depth };
|
|
156
|
+
}
|
|
157
|
+
// Orchestration: validate the exactly-one-of input, load the docs and
|
|
158
|
+
// clusters, resolve seeds, build the reverse maps, run the BFS, and assemble
|
|
159
|
+
// the response. Returns a Result — tool handlers never throw.
|
|
160
|
+
export async function computeTensionBlast(vaultRoot, input) {
|
|
161
|
+
const hasDoc = typeof input.document === "string" && input.document.length > 0;
|
|
162
|
+
const hasCluster = typeof input.cluster_id === "string" && input.cluster_id.length > 0;
|
|
163
|
+
if (hasDoc && hasCluster) {
|
|
164
|
+
return err(new Error("vault_tension_blast accepts exactly one of 'document' or 'cluster_id', not both"));
|
|
165
|
+
}
|
|
166
|
+
if (!hasDoc && !hasCluster) {
|
|
167
|
+
return err(new Error("vault_tension_blast requires exactly one of 'document' or 'cluster_id'"));
|
|
168
|
+
}
|
|
169
|
+
const docsResult = await loadDocuments(vaultRoot);
|
|
170
|
+
if (!docsResult.ok)
|
|
171
|
+
return docsResult;
|
|
172
|
+
const docs = docsResult.value;
|
|
173
|
+
const knownPaths = new Set(docs.map((d) => d.path));
|
|
174
|
+
const clustersResult = await loadTensionClusters(vaultRoot);
|
|
175
|
+
if (!clustersResult.ok)
|
|
176
|
+
return clustersResult;
|
|
177
|
+
const clusters = clustersResult.value.clusters;
|
|
178
|
+
let seeds;
|
|
179
|
+
let contested_document;
|
|
180
|
+
let cluster_id = null;
|
|
181
|
+
let cluster_documents = [];
|
|
182
|
+
if (hasDoc) {
|
|
183
|
+
const doc = input.document;
|
|
184
|
+
if (!knownPaths.has(doc)) {
|
|
185
|
+
return err(new Error(`vault_tension_blast: document not found in vault: ${doc}`));
|
|
186
|
+
}
|
|
187
|
+
contested_document = doc;
|
|
188
|
+
const containing = clusters.find((c) => c.documents.includes(doc));
|
|
189
|
+
if (containing) {
|
|
190
|
+
cluster_id = containing.id;
|
|
191
|
+
cluster_documents = [...containing.documents];
|
|
192
|
+
}
|
|
193
|
+
seeds = [doc];
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
const id = input.cluster_id;
|
|
197
|
+
const found = clusters.find((c) => c.id === id);
|
|
198
|
+
if (!found) {
|
|
199
|
+
return err(new Error(`vault_tension_blast: cluster_id not found: ${id}`));
|
|
200
|
+
}
|
|
201
|
+
contested_document = null;
|
|
202
|
+
cluster_id = found.id;
|
|
203
|
+
cluster_documents = [...found.documents];
|
|
204
|
+
seeds = [...found.documents];
|
|
205
|
+
}
|
|
206
|
+
const reverseSource = buildReverseSourceMap(docs);
|
|
207
|
+
const reverseLink = buildReverseLinkMap(docs);
|
|
208
|
+
const { downstream, primary_blast, advisory_blast, max_depth } = computeBlast({
|
|
209
|
+
seeds,
|
|
210
|
+
reverseSource,
|
|
211
|
+
reverseLink,
|
|
212
|
+
});
|
|
213
|
+
return ok({
|
|
214
|
+
contested_document,
|
|
215
|
+
cluster_id,
|
|
216
|
+
cluster_documents,
|
|
217
|
+
downstream,
|
|
218
|
+
primary_blast,
|
|
219
|
+
advisory_blast,
|
|
220
|
+
max_depth,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=tension-blast.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tension-blast.js","sourceRoot":"","sources":["../../src/curation/tension-blast.ts"],"names":[],"mappings":"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"}
|