@agentskit/doc-bridge 1.6.2 → 1.6.3
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 +6 -0
- package/action.yml +1 -1
- package/bin/ak-verify.js +9 -0
- package/dist/cli/program.js +303 -55
- package/dist/cli/program.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +6 -0
- package/dist/config/index.js.map +1 -1
- package/dist/{index-Di7PkJuf.d.ts → index-DudNuwI5.d.ts} +24 -1
- package/dist/index.d.ts +24 -5
- package/dist/index.js +249 -43
- package/dist/index.js.map +1 -1
- package/docs/knowledge-engine-runbook.md +14 -0
- package/docs/verification-harness.md +36 -0
- package/mcpb/manifest.json +1 -1
- package/package.json +52 -44
- package/scripts/report-visual-check.mjs +239 -0
- package/scripts/verification-harness.mjs +280 -0
- package/skills/doc-bridge-handoff/scripts/resolve-handoff.mjs +1 -1
- package/src/cli/program.ts +64 -7
- package/src/config/index.ts +2 -0
- package/src/config/schema.ts +9 -0
- package/src/index.ts +8 -1
- package/src/lib/markdown.ts +5 -0
- package/src/reconciliation/reconcile.ts +9 -1
- package/src/report/html.ts +292 -40
- package/src/schemas/knowledge.ts +1 -0
- package/src/version.ts +1 -1
package/dist/cli/program.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/cli/program.ts
|
|
2
|
-
import { existsSync as existsSync21, mkdirSync as mkdirSync8, readFileSync as readFileSync21, writeFileSync as writeFileSync8 } from "fs";
|
|
3
|
-
import { dirname as dirname10, resolve as resolve18 } from "path";
|
|
2
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync8, readFileSync as readFileSync21, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
3
|
+
import { dirname as dirname10, relative as relative10, resolve as resolve18 } from "path";
|
|
4
4
|
import { createInterface } from "readline/promises";
|
|
5
5
|
|
|
6
6
|
// src/config/load-config.ts
|
|
@@ -473,6 +473,10 @@ var RulesConfigSchema = z.object({
|
|
|
473
473
|
criticalPaths: z.array(z.string().min(1).max(512)).max(128).optional(),
|
|
474
474
|
warningThresholds: z.record(RuleIdSchema, z.number().int().min(1).max(1e5)).optional()
|
|
475
475
|
}).strict();
|
|
476
|
+
var ReconciliationConfigSchema = z.object({
|
|
477
|
+
/** Relation kinds that must have documentation declarations. Omit to require all observed kinds; [] disables this signal. */
|
|
478
|
+
requiredRelationKinds: z.array(z.string().min(1).max(128)).max(128).optional()
|
|
479
|
+
}).strict();
|
|
476
480
|
var WorkflowConfigSchema = z.object({
|
|
477
481
|
stateDir: z.string().min(1).max(512).optional()
|
|
478
482
|
}).strict();
|
|
@@ -646,6 +650,7 @@ var DocBridgeConfigV1Schema = z.object({
|
|
|
646
650
|
index: IndexConfigSchema.optional(),
|
|
647
651
|
routing: RoutingConfigSchema.optional(),
|
|
648
652
|
gates: GatesConfigSchema.optional(),
|
|
653
|
+
reconciliation: ReconciliationConfigSchema.optional(),
|
|
649
654
|
rules: RulesConfigSchema.optional(),
|
|
650
655
|
workflow: WorkflowConfigSchema.optional(),
|
|
651
656
|
safety: RepositorySafetyConfigSchema.optional(),
|
|
@@ -1087,6 +1092,10 @@ var firstParagraph = (markdown, maxLen = 400) => {
|
|
|
1087
1092
|
if (buf.length) break;
|
|
1088
1093
|
continue;
|
|
1089
1094
|
}
|
|
1095
|
+
if (/^(?:!?)\[[^\]]*\]\([^)]*\)$/.test(t)) {
|
|
1096
|
+
if (buf.length) break;
|
|
1097
|
+
continue;
|
|
1098
|
+
}
|
|
1090
1099
|
buf.push(t);
|
|
1091
1100
|
if (buf.join(" ").length >= maxLen) break;
|
|
1092
1101
|
}
|
|
@@ -1439,7 +1448,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
|
|
|
1439
1448
|
};
|
|
1440
1449
|
|
|
1441
1450
|
// src/version.ts
|
|
1442
|
-
var PACKAGE_VERSION = "1.6.
|
|
1451
|
+
var PACKAGE_VERSION = "1.6.3";
|
|
1443
1452
|
|
|
1444
1453
|
// src/index-builder/capabilities.ts
|
|
1445
1454
|
var renderCapabilitiesJson = (config, index, paths) => {
|
|
@@ -2631,7 +2640,8 @@ var ReconciliationReportV1Schema = z3.object({
|
|
|
2631
2640
|
summary: z3.object({
|
|
2632
2641
|
entityCount: z3.number().int().nonnegative(),
|
|
2633
2642
|
relationCount: z3.number().int().nonnegative(),
|
|
2634
|
-
diagnosticCount: z3.number().int().nonnegative()
|
|
2643
|
+
diagnosticCount: z3.number().int().nonnegative(),
|
|
2644
|
+
requiredRelationKinds: z3.array(boundedString(128)).max(128).optional()
|
|
2635
2645
|
}).strict()
|
|
2636
2646
|
}).strict();
|
|
2637
2647
|
var WorkflowStateSchema = z3.enum([
|
|
@@ -6302,11 +6312,12 @@ var reportDiagnostic = (code, status, severity, message, evidence2, value, entit
|
|
|
6302
6312
|
...relationIds?.length ? { relationIds: [...relationIds] } : {},
|
|
6303
6313
|
...remediation ? { remediation } : {}
|
|
6304
6314
|
});
|
|
6305
|
-
var reconcileKnowledge = (observed, declared) => {
|
|
6315
|
+
var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
6306
6316
|
const resolveEntity2 = entityResolver([observed, declared]);
|
|
6307
6317
|
const entities = entityById([observed, declared]);
|
|
6308
6318
|
const observedRelations = observed.relations.filter((relation) => relation.provenance === "observed" && !ignoredDocumentationRelations.has(relation.kind));
|
|
6309
6319
|
const declaredRelations = declared.relations.filter((relation) => relation.provenance === "declared" && !ignoredDocumentationRelations.has(relation.kind));
|
|
6320
|
+
const requiredRelationKinds = options.requiredRelationKinds === void 0 ? void 0 : new Set(options.requiredRelationKinds);
|
|
6310
6321
|
const diagnostics = [];
|
|
6311
6322
|
for (const entity of declared.entities.filter((item) => isUnresolved(item.id, entities)).sort((a, b) => a.id.localeCompare(b.id))) {
|
|
6312
6323
|
diagnostics.push(reportDiagnostic(
|
|
@@ -6357,7 +6368,7 @@ var reconcileKnowledge = (observed, declared) => {
|
|
|
6357
6368
|
void 0,
|
|
6358
6369
|
[relation.id, match.id]
|
|
6359
6370
|
));
|
|
6360
|
-
} else if (coverageAvailable(observed, relation)) {
|
|
6371
|
+
} else if (coverageAvailable(observed, relation) && (requiredRelationKinds === void 0 || requiredRelationKinds.has(relation.kind))) {
|
|
6361
6372
|
diagnostics.push(reportDiagnostic(
|
|
6362
6373
|
"RELATION_UNDOCUMENTED",
|
|
6363
6374
|
"undocumented",
|
|
@@ -6419,17 +6430,107 @@ var reconcileKnowledge = (observed, declared) => {
|
|
|
6419
6430
|
summary: {
|
|
6420
6431
|
entityCount: observed.entities.length,
|
|
6421
6432
|
relationCount: observedRelations.length,
|
|
6422
|
-
diagnosticCount: sortedDiagnostics.length
|
|
6433
|
+
diagnosticCount: sortedDiagnostics.length,
|
|
6434
|
+
...options.requiredRelationKinds === void 0 ? {} : { requiredRelationKinds: [...new Set(options.requiredRelationKinds)].sort() }
|
|
6423
6435
|
}
|
|
6424
6436
|
};
|
|
6425
6437
|
return ReconciliationReportV1Schema.parse({ ...base, contentHash: contentHashForArtifactV1(base) });
|
|
6426
6438
|
};
|
|
6427
6439
|
|
|
6428
6440
|
// src/report/html.ts
|
|
6441
|
+
var DEFAULT_LARGE_REPORT_THRESHOLD_BYTES = 5e6;
|
|
6442
|
+
var pathParts = (value) => (value ?? "").split("/").filter(Boolean);
|
|
6443
|
+
var packageFamily = (entity) => {
|
|
6444
|
+
const packageName2 = entity.name.replace(/^@[^/]+\//, "");
|
|
6445
|
+
const nameFamily = packageName2.split(/[-_./]/)[0];
|
|
6446
|
+
if (nameFamily) return nameFamily.toLowerCase();
|
|
6447
|
+
return pathParts(entity.path)[1] ?? pathParts(entity.path)[0] ?? "other";
|
|
6448
|
+
};
|
|
6449
|
+
var reportGroupKey = (entity, appLayout) => {
|
|
6450
|
+
const parts = pathParts(entity.path);
|
|
6451
|
+
if (appLayout && parts[0] === "apps" && parts[1]) return `app:${parts[1]}`;
|
|
6452
|
+
if (appLayout && parts[0] === "packages") return "shared-packages";
|
|
6453
|
+
if (appLayout && (parts.length === 0 || parts[0] === ".")) return "workspace";
|
|
6454
|
+
return packageFamily(entity);
|
|
6455
|
+
};
|
|
6456
|
+
var titleCase = (value) => value.split(/[-_]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
6457
|
+
var buildReportViewModel = (snapshot) => {
|
|
6458
|
+
const entities = new Map(snapshot.entities.map((entity) => [entity.id, entity]));
|
|
6459
|
+
const packages = snapshot.entities.filter((entity) => entity.kind === "package");
|
|
6460
|
+
const packageForEntity = /* @__PURE__ */ new Map();
|
|
6461
|
+
const externalIds = /* @__PURE__ */ new Set();
|
|
6462
|
+
for (const relation of snapshot.relations) {
|
|
6463
|
+
if (relation.kind === "contains" && entities.get(relation.from)?.kind === "package") packageForEntity.set(relation.to, relation.from);
|
|
6464
|
+
if (entities.get(relation.from)?.kind === "external") externalIds.add(relation.from);
|
|
6465
|
+
if (entities.get(relation.to)?.kind === "external") externalIds.add(relation.to);
|
|
6466
|
+
}
|
|
6467
|
+
const appLayout = packages.some((entity) => pathParts(entity.path)[0] === "apps");
|
|
6468
|
+
const groups = /* @__PURE__ */ new Map();
|
|
6469
|
+
const entityGroup = {};
|
|
6470
|
+
for (const entity of packages) {
|
|
6471
|
+
const family = reportGroupKey(entity, appLayout);
|
|
6472
|
+
const id = `group:${family}`;
|
|
6473
|
+
const current = groups.get(id) ?? {
|
|
6474
|
+
kind: family.startsWith("app:") ? "app" : family === "shared-packages" ? "shared" : family === "workspace" ? "workspace" : "domain",
|
|
6475
|
+
name: family.startsWith("app:") ? `${titleCase(family.slice(4))} app` : family === "shared-packages" ? "Shared packages" : family === "workspace" ? "Workspace root" : family,
|
|
6476
|
+
path: family.startsWith("app:") ? `apps/${family.slice(4)}` : family === "shared-packages" ? "packages" : family === "workspace" ? "." : void 0,
|
|
6477
|
+
members: []
|
|
6478
|
+
};
|
|
6479
|
+
current.members.push(entity.id);
|
|
6480
|
+
groups.set(id, current);
|
|
6481
|
+
entityGroup[entity.id] = id;
|
|
6482
|
+
}
|
|
6483
|
+
for (const [entityId2, packageId2] of packageForEntity) {
|
|
6484
|
+
const groupId = entityGroup[packageId2];
|
|
6485
|
+
if (groupId) entityGroup[entityId2] = groupId;
|
|
6486
|
+
}
|
|
6487
|
+
if (externalIds.size) {
|
|
6488
|
+
const externalGroupId = "group:external-dependencies";
|
|
6489
|
+
groups.set(externalGroupId, {
|
|
6490
|
+
kind: "external",
|
|
6491
|
+
name: "External dependencies",
|
|
6492
|
+
path: void 0,
|
|
6493
|
+
members: [...externalIds].sort()
|
|
6494
|
+
});
|
|
6495
|
+
for (const entityId2 of externalIds) entityGroup[entityId2] = externalGroupId;
|
|
6496
|
+
}
|
|
6497
|
+
const allOverviewNodes = [...groups.entries()].map(([id, group]) => ({
|
|
6498
|
+
id,
|
|
6499
|
+
kind: group.kind,
|
|
6500
|
+
name: group.name,
|
|
6501
|
+
path: group.path,
|
|
6502
|
+
memberCount: group.members.length
|
|
6503
|
+
}));
|
|
6504
|
+
const hasApplications = allOverviewNodes.some((node) => node.kind === "app");
|
|
6505
|
+
const overviewNodes = allOverviewNodes.filter(
|
|
6506
|
+
(node) => node.kind !== "external" && (!hasApplications || node.kind !== "workspace")
|
|
6507
|
+
);
|
|
6508
|
+
const overviewNodeIds = new Set(overviewNodes.map((node) => node.id));
|
|
6509
|
+
const edgeMap = /* @__PURE__ */ new Map();
|
|
6510
|
+
for (const relation of snapshot.relations) {
|
|
6511
|
+
if (relation.kind === "contains") continue;
|
|
6512
|
+
const from = entityGroup[relation.from];
|
|
6513
|
+
const to = entityGroup[relation.to];
|
|
6514
|
+
if (!from || !to || from === to) continue;
|
|
6515
|
+
const key = `${from}\u2192${to}`;
|
|
6516
|
+
const edge = edgeMap.get(key) ?? { from, to, count: 0, kinds: /* @__PURE__ */ new Set(), relationIds: /* @__PURE__ */ new Set() };
|
|
6517
|
+
edge.count += 1;
|
|
6518
|
+
edge.kinds.add(relation.kind);
|
|
6519
|
+
edge.relationIds.add(relation.id);
|
|
6520
|
+
edgeMap.set(key, edge);
|
|
6521
|
+
}
|
|
6522
|
+
return {
|
|
6523
|
+
overview: {
|
|
6524
|
+
nodes: overviewNodes.sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id)),
|
|
6525
|
+
edges: [...edgeMap.values()].filter((edge) => overviewNodeIds.has(edge.from) && overviewNodeIds.has(edge.to)).map((edge) => ({ ...edge, kinds: [...edge.kinds].sort(), relationIds: [...edge.relationIds].sort() })).sort((left, right) => right.count - left.count || left.from.localeCompare(right.from) || left.to.localeCompare(right.to))
|
|
6526
|
+
},
|
|
6527
|
+
groups: Object.fromEntries([...groups.entries()].map(([id, group]) => [id, { ...group, members: [...group.members].sort() }])),
|
|
6528
|
+
entityGroup
|
|
6529
|
+
};
|
|
6530
|
+
};
|
|
6429
6531
|
var escapeHtml = (value) => String(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
6430
6532
|
var anchor = (prefix, value) => `${prefix}-${value.replace(/[^A-Za-z0-9_-]+/g, "-")}`;
|
|
6431
6533
|
var embeddedJson = (value) => JSON.stringify(value).replaceAll("<", "\\u003c");
|
|
6432
|
-
var errorPage = (message) => `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Doc Bridge report error</title><style>body{font:16px system-ui;margin:3rem;color:#311}main{max-width:60rem;margin:auto;border:1px solid #d99;padding:2rem;border-radius:8px;background:#fff8f8}code{white-space:pre-wrap}</style></head><body><main><h1>Doc Bridge report unavailable</h1><p>The saved snapshot/report could not be rendered.</p><code>${escapeHtml(message)}</code><p>Run <code>ak-docs check</code> to regenerate valid artifacts.</p></main></body></html>`;
|
|
6433
6534
|
var reportData = (snapshot, report, includeSnippets) => ({
|
|
6434
6535
|
project: snapshot.project,
|
|
6435
6536
|
revision: snapshot.sourceRevision,
|
|
@@ -6439,6 +6540,14 @@ var reportData = (snapshot, report, includeSnippets) => ({
|
|
|
6439
6540
|
configurationHash: snapshot.configurationHash,
|
|
6440
6541
|
pipelineVersion: snapshot.pipelineVersion,
|
|
6441
6542
|
analyzerVersions: snapshot.analyzerVersions,
|
|
6543
|
+
diagnosticCount: report.diagnostics.length,
|
|
6544
|
+
requiredRelationKinds: report.summary.requiredRelationKinds,
|
|
6545
|
+
diagnosticSummary: {
|
|
6546
|
+
errors: report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "error").length,
|
|
6547
|
+
warnings: report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "warn").length,
|
|
6548
|
+
undocumented: report.diagnostics.filter((diagnostic2) => diagnostic2.status === "undocumented").length,
|
|
6549
|
+
drift: report.diagnostics.filter((diagnostic2) => diagnostic2.status === "stale-or-unverified" || diagnostic2.status === "conflict").length
|
|
6550
|
+
},
|
|
6442
6551
|
entities: snapshot.entities.map((entity, index) => ({
|
|
6443
6552
|
id: entity.id,
|
|
6444
6553
|
anchor: entity.id.length > 64 ? `entity-n${index}` : anchor("entity", entity.id),
|
|
@@ -6467,66 +6576,153 @@ var reportData = (snapshot, report, includeSnippets) => ({
|
|
|
6467
6576
|
remediation: diagnostic2.remediation,
|
|
6468
6577
|
evidence: diagnostic2.evidence.map(({ context, ...item }) => includeSnippets && context ? { ...item, context: redactSecrets(context) } : item)
|
|
6469
6578
|
})),
|
|
6470
|
-
coverage: snapshot.coverage
|
|
6579
|
+
coverage: snapshot.coverage,
|
|
6580
|
+
view: buildReportViewModel(snapshot)
|
|
6471
6581
|
});
|
|
6472
6582
|
var styles = String.raw`
|
|
6473
6583
|
:root{color-scheme:light;--ink:#17251f;--muted:#60706a;--paper:#f5f7f3;--panel:#fff;--line:#d9e0da;--green:#18794e;--blue:#236b83;--amber:#a9640b;--red:#b33c35;--shadow:0 12px 32px rgba(23,37,31,.08)}
|
|
6474
|
-
*{box-sizing:border-box}body{margin:0;background:var(--paper);color:var(--ink);font:14px/1.5 Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}button,input,select{font:inherit}button{cursor:pointer}a{color:var(--blue)}.shell{max-width:1500px;margin:auto;padding:24px}.masthead{display:flex;justify-content:space-between;gap:24px;align-items:flex-end;border-bottom:1px solid var(--line);padding:8px 0 24px}.eyebrow{color:var(--green);font-size:11px;font-weight:800;letter-spacing:.12em;text-transform:uppercase}.masthead h1{font:700 clamp(28px,4vw,52px)/1.03 Georgia,serif;letter-spacing:-.04em;margin:8px 0}.lede{color:var(--muted);font-size:16px;max-width:720px;margin:0}.run-meta{text-align:right;color:var(--muted);font-size:12px}.run-meta strong{display:block;color:var(--green);font-size:14px}.read-only{border:1px solid #b9d7c6;background:#edf8f0;color:#185b3b;border-radius:999px;display:inline-flex;padding:4px 10px;font-weight:700;font-size:12px}.summary{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px;margin:20px 0}.metric,.insight,.panel{background:var(--panel);border:1px solid var(--line);box-shadow:var(--shadow);border-radius:14px}.metric{padding:16px}.metric b{font-size:27px;display:block;line-height:1}.metric span{color:var(--muted);display:block;margin-top:7px}.metric.warn b{color:var(--amber)}.metric.bad b{color:var(--red)}.lens-bar{display:flex;align-items:center;justify-content:space-between;gap:14px;margin:22px 0 12px}.tabs{display:flex;gap:4px;flex-wrap:wrap}.tab,.level{border:1px solid transparent;background:transparent;border-radius:8px;padding:8px 11px;color:var(--muted)}.tab:hover,.level:hover{background:#e8eee9;color:var(--ink)}.tab[aria-selected=true],.level[aria-pressed=true]{background:var(--ink);color:#fff}.filters{display:flex;gap:8px;flex-wrap:wrap;margin:12px 0}.filters label{color:var(--muted);font-size:12px;display:flex;align-items:center;gap:6px}.filters input,.filters select,.filters button{border:1px solid var(--line);background:#fff;border-radius:8px;padding:8px 10px;color:var(--ink)}.filters input{min-width:220px}.filters button{color:var(--blue)}.insights{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin:14px 0 20px}.insight{padding:14px}.insight h3{font-size:13px;margin:0 0 5px}.insight p{color:var(--muted);margin:0;font-size:12px}.tag{display:inline-block;border-radius:999px;padding:2px 7px;font-size:10px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;background:#edf0ee;color:var(--muted)}.tag.heuristic{background:#fff3dc;color:#8a570a}.tag.error{background:#fde8e6;color:var(--red)}.tag.warn{background:#fff2d9;color:var(--amber)}.tag.info{background:#e6f1f5;color:var(--blue)}.workspace{display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:14px}.panel{padding:18px}.panel h2{font:700 23px Georgia,serif;margin:0}.panel h3{font-size:14px;margin:0}.panel-head{display:flex;justify-content:space-between;gap:12px;align-items:flex-start;margin-bottom:14px}.subtle{color:var(--muted);font-size:12px}.map-wrap{border:1px solid var(--line);border-radius:10px;overflow:auto;background:linear-gradient(135deg,#fbfcfa,#f0f5f0)}#graph{display:block;width:100%;min-width:760px;height:560px}.edge{stroke:#b8c6bd;stroke-width:1.3;opacity:.65}.edge.alert{stroke:#d08a35;stroke-width:2}.edge-label{fill:var(--muted);font-size:10px}.graph-node{cursor:pointer}.graph-node rect{fill:#fff;stroke:#9eb0a4;stroke-width:1.2}.graph-node:hover rect,.graph-node:focus rect{stroke:var(--blue);stroke-width:2}.graph-node.selected rect{stroke:var(--green);stroke-width:3}.graph-node.issue rect{fill:#fffaf0;stroke:#c98a2c}.graph-node text{pointer-events:none}.node-label{font-size:12px;font-weight:750}.node-kind{font-size:10px;fill:var(--muted)}.node-count{font-size:10px;fill:var(--green);font-weight:800}.empty{padding:32px;text-align:center;color:var(--muted)}.side{position:sticky;top:14px;align-self:start}.detail-title{font:700 22px Georgia,serif;margin:4px 0}.detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;border-top:1px solid var(--line);border-bottom:1px solid var(--line);padding:12px 0;margin:14px 0}.detail-grid b{display:block;font-size:19px}.detail-grid span{color:var(--muted);font-size:11px}.path{overflow-wrap:anywhere;color:var(--muted);font-size:12px}.list{list-style:none;margin:10px 0 0;padding:0}.list li{border-top:1px solid var(--line);padding:8px 0;font-size:12px}.run{margin-top:14px}.run-summary{display:flex;gap:8px;flex-wrap:wrap;margin:12px 0}.chip{border:1px solid var(--line);border-radius:999px;padding:5px 9px;color:var(--muted);font-size:12px}.chip b{color:var(--ink)}.finding{border-top:1px solid var(--line);padding:14px 0}.finding-head{display:flex;justify-content:space-between;gap:10px}.finding h3{margin:0;font-size:14px}.finding p{margin:5px 0;color:var(--muted)}.finding ul{margin:8px 0 0;padding-left:18px;color:var(--muted);font-size:12px}.coverage-row{display:grid;grid-template-columns:170px 1fr 90px;gap:10px;align-items:center;border-top:1px solid var(--line);padding:10px 0;font-size:12px}.bar{height:7px;background:#e7ece8;border-radius:99px;overflow:hidden}.bar i{display:block;height:100%;background:var(--green)}.bar i.partial{background:var(--amber)}.bar i.none{background:var(--red)}.metadata{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin-top:14px}.metadata div{border-top:1px solid var(--line);padding-top:8px;color:var(--muted);font-size:12px;overflow-wrap:anywhere}.metadata b{display:block;color:var(--ink);font-size:11px;text-transform:uppercase;letter-spacing:.08em;margin-bottom:3px}.hidden{display:none!important}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media(max-width:900px){.summary{grid-template-columns:repeat(3,minmax(0,1fr))}.insights{grid-template-columns:repeat(2,minmax(0,1fr))}.workspace{grid-template-columns:1fr}.side{position:static}.run-meta{display:none}}@media(max-width:560px){.shell{padding:14px}.summary{grid-template-columns:repeat(2,minmax(0,1fr))}.insights{grid-template-columns:1fr}.masthead{display:block}.filters input{min-width:0;width:100%}.filters label:first-child{width:100%}.metadata{grid-template-columns:1fr}}@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important}}
|
|
6584
|
+
*{box-sizing:border-box}body{margin:0;background:var(--paper);color:var(--ink);font:14px/1.5 Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}button,input,select{font:inherit}button{cursor:pointer}a{color:var(--blue)}.shell{max-width:1500px;margin:auto;padding:24px}.masthead{display:flex;justify-content:space-between;gap:24px;align-items:flex-end;border-bottom:1px solid var(--line);padding:8px 0 24px}.eyebrow{color:var(--green);font-size:11px;font-weight:800;letter-spacing:.12em;text-transform:uppercase}.masthead h1{font:700 clamp(28px,4vw,52px)/1.03 Georgia,serif;letter-spacing:-.04em;margin:8px 0}.lede{color:var(--muted);font-size:16px;max-width:720px;margin:0}.run-meta{text-align:right;color:var(--muted);font-size:12px}.run-meta strong{display:block;color:var(--green);font-size:14px}.read-only{border:1px solid #b9d7c6;background:#edf8f0;color:#185b3b;border-radius:999px;display:inline-flex;padding:4px 10px;font-weight:700;font-size:12px}.summary{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px;margin:20px 0}.metric,.insight,.panel{background:var(--panel);border:1px solid var(--line);box-shadow:var(--shadow);border-radius:14px}.metric{padding:16px}.metric b{font-size:27px;display:block;line-height:1}.metric span{color:var(--muted);display:block;margin-top:7px}.metric.warn b{color:var(--amber)}.metric.bad b{color:var(--red)}.lens-bar{display:flex;align-items:center;justify-content:space-between;gap:14px;margin:22px 0 12px}.tabs{display:flex;gap:4px;flex-wrap:wrap}.tab,.level{border:1px solid transparent;background:transparent;border-radius:8px;padding:8px 11px;color:var(--muted)}.tab:hover,.level:hover{background:#e8eee9;color:var(--ink)}.tab[aria-selected=true],.level[aria-pressed=true]{background:var(--ink);color:#fff}.filters{display:flex;gap:8px;flex-wrap:wrap;margin:12px 0}.filters label{color:var(--muted);font-size:12px;display:flex;align-items:center;gap:6px}.filters input,.filters select,.filters button{border:1px solid var(--line);background:#fff;border-radius:8px;padding:8px 10px;color:var(--ink)}.filters input{min-width:220px}.filters button{color:var(--blue)}.insights{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin:14px 0 20px}.insight{padding:14px}.insight h3{font-size:13px;margin:0 0 5px}.insight p{color:var(--muted);margin:0;font-size:12px}.tag{display:inline-block;border-radius:999px;padding:2px 7px;font-size:10px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;background:#edf0ee;color:var(--muted)}.tag.heuristic{background:#fff3dc;color:#8a570a}.tag.error{background:#fde8e6;color:var(--red)}.tag.warn{background:#fff2d9;color:var(--amber)}.tag.info{background:#e6f1f5;color:var(--blue)}.workspace{display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:14px}.panel{padding:18px}.panel h2{font:700 23px Georgia,serif;margin:0}.panel h3{font-size:14px;margin:0}.panel-head{display:flex;justify-content:space-between;gap:12px;align-items:flex-start;margin-bottom:14px}.subtle{color:var(--muted);font-size:12px}.map-wrap{border:1px solid var(--line);border-radius:10px;overflow:auto;background:linear-gradient(135deg,#fbfcfa,#f0f5f0)}#graph{display:block;width:100%;min-width:760px;height:560px}.edge{stroke:#b8c6bd;stroke-width:1.3;opacity:.65}.edge.alert{stroke:#d08a35;stroke-width:2}.edge-label{fill:var(--muted);font-size:10px}.graph-node{cursor:pointer}.graph-node rect{fill:#fff;stroke:#9eb0a4;stroke-width:1.2}.graph-node:hover rect,.graph-node:focus rect{stroke:var(--blue);stroke-width:2}.graph-node.selected rect{stroke:var(--green);stroke-width:3}.graph-node.issue rect{fill:#fffaf0;stroke:#c98a2c}.graph-node text{pointer-events:none}.node-label{font-size:12px;font-weight:750}.node-kind{font-size:10px;fill:var(--muted)}.node-count{font-size:10px;fill:var(--green);font-weight:800}.empty{padding:32px;text-align:center;color:var(--muted)}.side{position:sticky;top:14px;align-self:start}.detail-title{font:700 22px Georgia,serif;margin:4px 0}.detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;border-top:1px solid var(--line);border-bottom:1px solid var(--line);padding:12px 0;margin:14px 0}.detail-grid b{display:block;font-size:19px}.detail-grid span{color:var(--muted);font-size:11px}.path{overflow-wrap:anywhere;color:var(--muted);font-size:12px}.list{list-style:none;margin:10px 0 0;padding:0}.list li{border-top:1px solid var(--line);padding:8px 0;font-size:12px}.run{margin-top:14px}.run-summary{display:flex;gap:8px;flex-wrap:wrap;margin:12px 0}.chip{border:1px solid var(--line);border-radius:999px;padding:5px 9px;color:var(--muted);font-size:12px}.chip b{color:var(--ink)}.finding{border-top:1px solid var(--line);padding:14px 0}.finding-head{display:flex;justify-content:space-between;gap:10px}.finding h3{margin:0;font-size:14px}.finding p{margin:5px 0;color:var(--muted)}.finding ul{margin:8px 0 0;padding-left:18px;color:var(--muted);font-size:12px}.coverage-row{display:grid;grid-template-columns:170px 1fr 90px;gap:10px;align-items:center;border-top:1px solid var(--line);padding:10px 0;font-size:12px}.bar{height:7px;background:#e7ece8;border-radius:99px;overflow:hidden}.bar i{display:block;height:100%;background:var(--green)}.bar i.partial{background:var(--amber)}.bar i.none{background:var(--red)}.metadata{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin-top:14px}.metadata div{border-top:1px solid var(--line);padding-top:8px;color:var(--muted);font-size:12px;overflow-wrap:anywhere}.metadata b{display:block;color:var(--ink);font-size:11px;text-transform:uppercase;letter-spacing:.08em;margin-bottom:3px}.hidden{display:none!important}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media(max-width:900px){.summary{grid-template-columns:repeat(3,minmax(0,1fr))}.insights{grid-template-columns:repeat(2,minmax(0,1fr))}.workspace{grid-template-columns:1fr}.side{position:static}.run-meta{display:none}}@media(max-width:560px){.shell{padding:14px}.summary{grid-template-columns:repeat(2,minmax(0,1fr))}.insights{grid-template-columns:1fr}.masthead{display:block}.filters input{min-width:0;width:100%}.filters label:first-child{width:100%}.metadata{grid-template-columns:1fr}}@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important}}@media(prefers-color-scheme:dark){:root{color-scheme:dark;--ink:#edf5ef;--muted:#a8b8ae;--paper:#111815;--panel:#18221d;--line:#304037;--green:#6dd19c;--blue:#7dc4dc;--amber:#efb866;--red:#f08a80;--shadow:0 12px 32px rgba(0,0,0,.24)}.map-wrap{background:linear-gradient(135deg,#19231d,#101611)}.filters input,.filters select,.filters button{background:#1d2922;color:var(--ink)}.graph-node rect{fill:#1b2820;stroke:#719080}.graph-node.issue rect{fill:#332c1d}}
|
|
6475
6585
|
`;
|
|
6586
|
+
styles += String.raw`
|
|
6587
|
+
:root{--ink:#182b2b;--muted:#536664;--paper:#efe9df;--panel:#f8f5ee;--line:#c8c8b9;--green:#176b51;--blue:#155a68;--amber:#93530b;--red:#9b302b;--shadow:none}
|
|
6588
|
+
*{box-sizing:border-box}body{background:var(--paper);font-family:"Avenir Next","Segoe UI",ui-sans-serif,system-ui,sans-serif;font-size:14px;line-height:1.5}.shell{max-width:1720px;padding:30px clamp(20px,4vw,64px) 56px}.masthead{align-items:flex-start;border-bottom:2px solid var(--ink);padding:4px 0 28px}.masthead>div:first-child{max-width:840px}.eyebrow{font-size:10px;letter-spacing:.16em}.masthead h1{font-family:"Iowan Old Style",Georgia,serif;font-size:clamp(3rem,7vw,7.2rem);letter-spacing:-.075em;line-height:.9;margin:16px 0 20px}.lede{font-size:17px;line-height:1.45;max-width:650px}.run-meta{padding-top:3px;min-width:210px}.read-only{border:0;border-radius:0;padding:0;background:none;color:var(--green);font-size:10px;letter-spacing:.1em;text-transform:uppercase}.run-meta strong{color:var(--ink);font-size:12px;overflow-wrap:anywhere;margin-top:16px}.summary{grid-template-columns:repeat(5,minmax(0,1fr));gap:0;margin:0;padding:18px 0 22px;border-bottom:1px solid var(--line)}.metric{border:0;border-left:1px solid var(--line);border-radius:0;background:none;box-shadow:none;padding:0 18px}.metric:first-child{border-left:0;padding-left:0}.metric b{font-family:"Iowan Old Style",Georgia,serif;font-size:38px;letter-spacing:-.05em}.metric span{font-size:11px;letter-spacing:.07em;text-transform:uppercase}.lens-bar{align-items:flex-end;margin:42px 0 6px}.tabs{gap:0}.tab,.level{border:0;border-bottom:2px solid transparent;border-radius:0;background:transparent;color:var(--muted);padding:8px 14px;min-height:38px;font-weight:700}.tab:first-child,.level:first-child{padding-left:0}.tab:hover,.level:hover{background:none;color:var(--ink);border-color:var(--line)}.tab[aria-selected=true],.level[aria-pressed=true]{background:none;color:var(--ink);border-color:var(--green)}.lens-bar>.subtle{max-width:430px;text-align:right}.filters{border-block:1px solid var(--line);padding:14px 0;margin:12px 0 24px}.filters input,.filters select,.filters button{border:1px solid var(--line);border-radius:3px;background:var(--panel);min-height:38px}.filters button{font-weight:700}.insights{grid-template-columns:repeat(4,minmax(0,1fr));gap:0;border-top:1px solid var(--line);border-bottom:1px solid var(--line);margin:18px 0 30px}.insight{border:0;border-left:1px solid var(--line);border-radius:0;background:none;box-shadow:none;padding:14px 16px 16px}.insight:first-child{border-left:0;padding-left:0}.insight h3{font-size:12px;letter-spacing:.02em}.insight p{font-size:13px}.tag{border-radius:2px;padding:2px 5px;font-size:9px}.workspace{grid-template-columns:minmax(0,1fr) 350px;gap:24px}.panel{border:1px solid var(--line);border-radius:3px;background:var(--panel);box-shadow:none;padding:24px}.panel h2{font-family:"Iowan Old Style",Georgia,serif;font-size:30px;letter-spacing:-.04em}.map-wrap{border:1px solid #aeb9ae;border-radius:2px;background-color:#e8eee7;background-image:linear-gradient(rgba(24,43,43,.07) 1px,transparent 1px),linear-gradient(90deg,rgba(24,43,43,.07) 1px,transparent 1px);background-size:24px 24px}.map-wrap::-webkit-scrollbar{height:10px}.map-wrap::-webkit-scrollbar-thumb{background:var(--line)}#graph{height:620px}.graph-node rect{fill:#f8f5ee;stroke:#274744;stroke-width:1.4;rx:2}.graph-node:hover rect,.graph-node:focus rect{stroke:var(--blue);stroke-width:2.5}.graph-node.selected rect{fill:#dceee1;stroke:var(--green);stroke-width:3}.graph-node.issue rect{fill:#fff0d4;stroke:var(--amber)}.edge{stroke:#69857d;stroke-width:1.5;opacity:.8}.edge.alert{stroke:var(--amber);stroke-width:2.5}.edge-label{fill:var(--ink);font-size:10px;font-weight:700}.node-label{font-size:12px;font-weight:800}.node-kind{font-size:10px;fill:var(--muted)}.node-count{font-size:10px;fill:var(--green);font-weight:800}.side{top:20px}.side .panel-head{border-bottom:1px solid var(--line);padding-bottom:14px}.detail-title{font-family:"Iowan Old Style",Georgia,serif;font-size:28px}.detail-grid{gap:0}.detail-grid>div{border-left:1px solid var(--line);padding-left:10px}.detail-grid>div:nth-child(odd){border-left:0;padding-left:0}.detail-grid b{font-family:"Iowan Old Style",Georgia,serif;font-size:26px}.run{margin-top:24px}.run-summary{border-bottom:1px solid var(--line);padding-bottom:12px}.chip{border:0;border-left:1px solid var(--line);border-radius:0;padding:2px 10px}.chip:first-child{border-left:0;padding-left:0}.finding-groups{grid-template-columns:repeat(2,minmax(0,1fr));gap:0}.finding-group{border:0;border-top:1px solid var(--line);border-radius:0;background:none;padding:16px 14px 16px 0}.finding-group:nth-child(even){padding-left:14px;border-left:1px solid var(--line)}.finding-group h3{font-size:13px;letter-spacing:.02em}.finding-detail{border-top:2px solid var(--ink)}.finding{padding:16px 0}.metadata{gap:24px;margin-top:26px}.metadata div{border-top:1px solid var(--line);padding-top:9px}.map-wrap,.panel,.finding-group,.insight{transition:background-color .16s ease,border-color .16s ease}@media(max-width:900px){.masthead{display:block}.run-meta{padding-top:24px;text-align:left}.summary{grid-template-columns:repeat(3,minmax(0,1fr));row-gap:18px}.metric:nth-child(4){border-left:0;padding-left:0}.workspace{grid-template-columns:1fr}.side{position:static}.lens-bar{display:block}.lens-bar>.subtle{text-align:left;margin-top:10px}.finding-groups{grid-template-columns:1fr}.finding-group:nth-child(even){padding-left:0;border-left:0}}@media(max-width:560px){.shell{padding:20px 16px 40px}.masthead h1{font-size:clamp(3rem,16vw,5rem)}.summary{grid-template-columns:repeat(2,minmax(0,1fr))}.metric:nth-child(odd){border-left:0;padding-left:0}.metric:nth-child(even){border-left:1px solid var(--line);padding-left:12px}.insights{grid-template-columns:1fr}.insight,.insight:first-child{border-left:0;border-top:1px solid var(--line);padding-left:0}.insight:first-child{border-top:0}.panel{padding:16px}.panel-head{display:block}.panel-head>.tabs{margin-top:12px}.filters label{width:100%}.filters input,.filters select{width:100%}.metadata{grid-template-columns:1fr}}@media(prefers-reduced-motion:reduce){.map-wrap,.panel,.finding-group,.insight{transition:none}}@media(prefers-color-scheme:dark){:root{--ink:#e8eee7;--muted:#b0beb3;--paper:#131c1a;--panel:#1b2824;--line:#42534b;--green:#72d09d;--blue:#86cfe1;--amber:#f0bc6b;--red:#ff9186}.map-wrap{background-color:#17231e;background-image:linear-gradient(rgba(232,238,231,.07) 1px,transparent 1px),linear-gradient(90deg,rgba(232,238,231,.07) 1px,transparent 1px)}.graph-node rect{fill:#1c2b25;stroke:#90aa99}.graph-node.selected rect{fill:#294936}.graph-node.issue rect{fill:#40321e}.filters input,.filters select,.filters button{background:#202f29;color:var(--ink)}}
|
|
6589
|
+
`;
|
|
6590
|
+
styles += String.raw`.graph-node .node-label,.graph-node text.node-label{fill:var(--ink)}.graph-node .node-kind{fill:var(--muted)}.edge-label{fill:var(--ink)}@media(max-width:1100px){.workspace{grid-template-columns:1fr}.side{position:static}.map-wrap #graph{min-width:0!important;width:100%!important}.panel-head>.tabs{margin-top:12px}}@media(max-width:700px){.summary{grid-template-columns:repeat(2,minmax(0,1fr))}.lens-bar{display:block}.lens-bar>.subtle{text-align:left;margin-top:10px}.filters label{width:100%}.filters input,.filters select{width:100%}.finding-groups{grid-template-columns:1fr}.finding-group:nth-child(even){padding-left:0;border-left:0}}`;
|
|
6591
|
+
styles += String.raw`.level{display:none!important}.report-tabs{display:flex;gap:0;align-items:flex-end;border-bottom:1px solid var(--line);margin:24px 0 0}.report-tabs .tab{font-size:13px}.breadcrumbs{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin:0 0 14px;color:var(--muted);font-size:12px}.breadcrumbs button{border:0;background:none;color:var(--blue);padding:3px 0;font-weight:700}.breadcrumbs button:hover{text-decoration:underline}.breadcrumbs .current{color:var(--ink);font-weight:700}.scope-note{margin:0 0 12px}.report-dashboard{display:grid;grid-template-columns:minmax(0,1.35fr) minmax(280px,1fr);gap:24px}.report-table{width:100%;border-collapse:collapse;font-size:12px}.report-table th,.report-table td{border-top:1px solid var(--line);padding:10px 8px;text-align:left;vertical-align:top}.report-table th{color:var(--muted);font-size:10px;letter-spacing:.08em;text-transform:uppercase}.report-table td:first-child{font-weight:750}.bar-list{display:grid;gap:10px}.bar-item{display:grid;grid-template-columns:minmax(120px,1fr) 2fr auto;gap:10px;align-items:center;font-size:12px}.bar-item i{height:8px;background:var(--line);display:block;overflow:hidden}.bar-item i b{display:block;height:100%;background:var(--green)}.map-wrap{touch-action:none;cursor:grab}.map-wrap.dragging{cursor:grabbing}@media(max-width:700px){.report-tabs{overflow:auto}.report-tabs .tab{white-space:nowrap}.report-dashboard{grid-template-columns:1fr}.bar-item{grid-template-columns:minmax(100px,1fr) 1fr auto}}`;
|
|
6592
|
+
styles += String.raw`body[data-report-view="architecture"] #insights,body[data-report-view="architecture"] .filters,body[data-report-view="architecture"] .run,body[data-report-view="architecture"] .metadata{display:none}body[data-report-view="insights"] .workspace,body[data-report-view="insights"] .filters,body[data-report-view="insights"] .run,body[data-report-view="insights"] .metadata{display:none}body[data-report-view="findings"] .workspace,body[data-report-view="findings"] #insights,body[data-report-view="findings"] #report-dashboard,body[data-report-view="findings"] #coverage-title,body[data-report-view="findings"] #coverage-title~*{display:none}body[data-report-view="findings"] .run[aria-labelledby="coverage-title"]{display:none}body[data-report-view="coverage"] .workspace,body[data-report-view="coverage"] #insights,body[data-report-view="coverage"] #report-dashboard,body[data-report-view="coverage"] .filters,body[data-report-view="coverage"] .run[aria-labelledby="run-title"],body[data-report-view="coverage"] .metadata{display:none}body[data-report-view="insights"] #report-dashboard{display:block}body[data-report-view="map"] #report-dashboard{display:none}`;
|
|
6593
|
+
styles += String.raw`body[data-report-view="architecture"] #report-dashboard{display:none!important}.edge{fill:none}`;
|
|
6594
|
+
styles += String.raw`:root{--ink:#17251f;--muted:#586b63;--paper:#f2f5f1;--panel:#fbfcf9;--line:#c9d5cc;--green:#13734a;--blue:#155f78;--amber:#995d08;--red:#a52e2b}html,body{max-width:100%;overflow-x:hidden}body{font-size:15px;line-height:1.55}.shell{width:100%;max-width:1600px;margin:0 auto;padding:28px clamp(16px,3vw,48px) 56px}.masthead{gap:32px;padding:0 0 24px;align-items:flex-start}.masthead h1{font-size:clamp(2.8rem,5.5vw,5.4rem);line-height:.94;margin:12px 0 16px;max-width:100%;overflow-wrap:anywhere}.lede{max-width:60ch;font-size:16px}.run-meta{min-width:0;max-width:300px}.summary{margin:0;padding:18px 0 20px;grid-template-columns:repeat(5,minmax(0,1fr));gap:0}.metric{min-width:0;padding:0 16px;border-inline-start:1px solid var(--line)}.metric:first-child{padding-inline-start:0;border-inline-start:0}.metric b{font-size:clamp(1.8rem,3vw,3rem);max-width:100%;overflow-wrap:anywhere}.metric span{font-size:10px;letter-spacing:.06em;line-height:1.25}.lens-bar{margin:24px 0 4px;align-items:flex-end}.lens-bar>.subtle{max-width:52ch}.report-tabs{max-width:100%;overflow-x:auto}.tab,.level{min-height:40px;padding:8px 12px}.filters{max-width:100%;align-items:center}.filters label{min-width:0}.filters input,.filters select{max-width:100%;min-width:0}.workspace{min-width:0;grid-template-columns:minmax(0,1fr) minmax(280px,340px);gap:20px}.panel{min-width:0}.map-wrap{min-width:0;max-width:100%;width:100%;overflow:hidden;contain:layout paint}#graph{display:block;width:100%!important;min-width:0!important;max-width:100%;height:clamp(480px,60vh,680px)!important}.side{min-width:0}.detail-title,.path,.list,.finding,.finding-group,.report-table{min-width:0;overflow-wrap:anywhere}.report-dashboard{min-width:0}.report-table{table-layout:fixed}.report-table th,.report-table td{overflow-wrap:anywhere}.bar-item{min-width:0}.bar-item span{min-width:0;overflow-wrap:anywhere}.tab:focus-visible,.level:focus-visible,input:focus-visible,select:focus-visible{outline:3px solid var(--blue);outline-offset:2px}@media(max-width:1100px){.workspace{grid-template-columns:1fr}.side{position:static}.summary{grid-template-columns:repeat(3,minmax(0,1fr));row-gap:16px}.summary .metric:nth-child(4){border-inline-start:0;padding-inline-start:0}}@media(max-width:700px){.shell{padding:12px 16px 32px}.masthead{padding-bottom:14px}.masthead h1{font-size:clamp(2.2rem,11vw,3.4rem);margin:6px 0 10px}.lede{font-size:14px;line-height:1.35}.run-meta{display:none}.summary{padding:10px 0 12px;grid-template-columns:repeat(2,minmax(0,1fr));row-gap:8px}.summary .metric:nth-child(even){border-inline-start:1px solid var(--line);padding-inline-start:12px}.summary .metric:nth-child(odd){border-inline-start:0;padding-inline-start:0}.metric{padding-inline:10px}.metric b{font-size:1.65rem}.lens-bar{display:block;margin-top:14px}.lens-bar>.subtle{display:none}.filters{display:grid;grid-template-columns:1fr;gap:10px}.filters label{display:grid;grid-template-columns:1fr;gap:5px}.filters input,.filters select,.filters button{width:100%;min-height:44px}.panel{padding:16px}.panel-head{display:block}.panel-head>.tabs{margin-top:12px}.finding-groups{grid-template-columns:1fr}.report-dashboard{grid-template-columns:1fr}#graph{height:520px!important}.lens-bar + #lens-caption{margin:8px 0}.lens-bar + #lens-caption + #insights{margin-top:8px}}@media(prefers-color-scheme:dark){:root{--ink:#edf5ef;--muted:#b7c6ba;--paper:#111815;--panel:#1a2821;--line:#405248;--green:#73d19d;--blue:#8bd1e6;--amber:#f0bd70;--red:#ff9389}}`;
|
|
6595
|
+
styles += String.raw`@media(max-width:700px){.map-wrap{overflow:auto;contain:layout paint}#graph{width:720px!important;min-width:720px!important;max-width:none!important}}`;
|
|
6596
|
+
styles += String.raw`#lens-caption + p.subtle{display:none}`;
|
|
6476
6597
|
var script = String.raw`
|
|
6477
6598
|
const data=${"${DATA}"};
|
|
6478
|
-
|
|
6479
|
-
const
|
|
6480
|
-
const
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
const
|
|
6484
|
-
const
|
|
6485
|
-
|
|
6486
|
-
const
|
|
6599
|
+
data.entities=data.entities||[];data.relations=data.relations||[];data.diagnostics=data.diagnostics||[];
|
|
6600
|
+
const policySuffix=data.requiredRelationKinds===undefined?"Policy: all observed relation kinds require declarations.":data.requiredRelationKinds.length?"Policy: declarations required for "+data.requiredRelationKinds.join(", ")+".":"Policy: missing relation declarations disabled; stale, conflicting, and unresolved declarations remain checked.";
|
|
6601
|
+
const ensurePolicyNote=()=>{const note=document.querySelector("#map-note");if(note&&!note.textContent.includes(policySuffix))note.textContent=(note.textContent+" · "+policySuffix).trim()};
|
|
6602
|
+
new MutationObserver(ensurePolicyNote).observe(document.querySelector("#map-note"),{childList:true,characterData:true});
|
|
6603
|
+
const lazyChunks=globalThis.__DOC_BRIDGE_LAZY_CHUNKS__||[],loadedChunks=new Set(),loadingChunks=new Map();
|
|
6604
|
+
const esc=(value)=>String(value??"").replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll("\"",""").replaceAll("'","'");
|
|
6605
|
+
const byId=new Map(),parent=new Map(),relationById=new Map(),packageMembers=new Map(),findingIndex=new Map(),relationFindingIndex=new Map(),relationsByEntity=new Map(),groupEntityIds=new Map(),groupFindingCounts=new Map(),packageCache=new Map(),nodeIssueCache=new Map();
|
|
6606
|
+
const groupById=new Map(Object.entries(data.view?.groups||{}));
|
|
6607
|
+
const addFinding=(index,id,finding)=>{const values=index.get(id)||[];values.push(finding);index.set(id,values)};
|
|
6608
|
+
const addRelation=(id,relation)=>{const values=relationsByEntity.get(id)||[];values.push(relation);relationsByEntity.set(id,values)};
|
|
6609
|
+
const hydrate=()=>{byId.clear();parent.clear();relationById.clear();packageMembers.clear();findingIndex.clear();relationFindingIndex.clear();relationsByEntity.clear();groupEntityIds.clear();groupFindingCounts.clear();packageCache.clear();nodeIssueCache.clear();data.entities.forEach((entity)=>{byId.set(entity.id,entity);const groupId=data.view?.entityGroup?.[entity.id];if(groupId){const ids=groupEntityIds.get(groupId)||new Set();ids.add(entity.id);groupEntityIds.set(groupId,ids)}});data.relations.forEach((relation)=>{relationById.set(relation.id,relation);addRelation(relation.from,relation);addRelation(relation.to,relation);if(relation.kind==="contains"){if(!parent.has(relation.to))parent.set(relation.to,relation.from);const members=packageMembers.get(relation.from)||new Set();members.add(relation.to);packageMembers.set(relation.from,members)}});data.diagnostics.forEach((finding)=>{(finding.entityIds||[]).forEach((id)=>addFinding(findingIndex,id,finding));(finding.relationIds||[]).forEach((id)=>addFinding(relationFindingIndex,id,finding));const scopes=new Set();(finding.entityIds||[]).forEach((id)=>{const scope=data.view?.entityGroup?.[id];if(scope)scopes.add(scope)});(finding.relationIds||[]).forEach((id)=>{const relation=relationById.get(id);if(!relation)return;[relation.from,relation.to].forEach((endpoint)=>{const scope=data.view?.entityGroup?.[endpoint];if(scope)scopes.add(scope)})});scopes.forEach((scope)=>groupFindingCounts.set(scope,(groupFindingCounts.get(scope)||0)+1))})};
|
|
6610
|
+
const hasChunk=(name)=>!lazyChunks.includes(name)||loadedChunks.has(name);
|
|
6611
|
+
const loadChunk=(name)=>{if(hasChunk(name))return Promise.resolve();if(loadingChunks.has(name))return loadingChunks.get(name);const promise=new Promise((resolve,reject)=>{const element=document.createElement("script");element.src=name;element.onload=()=>{loadedChunks.add(name);hydrate();resolve()};element.onerror=()=>reject(new Error("Unable to load offline report chunk: "+name));document.head.append(element)});loadingChunks.set(name,promise);return promise};
|
|
6612
|
+
const readHash=()=>new URLSearchParams(location.hash.slice(1));
|
|
6613
|
+
const hash=readHash();
|
|
6614
|
+
const state={lens:hash.get("lens")||"architecture",level:hash.get("level")||"overview",selected:hash.get("selected"),query:hash.get("query")||"",status:hash.get("status")||"",severity:hash.get("severity")||"",findingGroup:hash.get("findingGroup"),findingPage:Number(hash.get("findingPage")||0)};
|
|
6615
|
+
const syncHash=()=>{const params=new URLSearchParams();[["lens",state.lens,"architecture"],["level",state.level,"overview"],["selected",state.selected,""],["query",state.query,""],["status",state.status,""],["severity",state.severity,""],["findingGroup",state.findingGroup,""],["findingPage",state.findingPage,"0"]].forEach(([key,value,defaultValue])=>{if(value&&value!==defaultValue)params.set(key,String(value))});const next=params.toString();try{if(location.hash.slice(1)!==next)history.replaceState(null,"",next?"#"+next:"")}catch{}};
|
|
6487
6616
|
const rootOf=(id)=>{let current=id;const seen=new Set();while(parent.has(current)&&!seen.has(current)){seen.add(current);current=parent.get(current)}return current};
|
|
6488
|
-
const
|
|
6489
|
-
const groupFor=(id)=>
|
|
6490
|
-
const
|
|
6491
|
-
const
|
|
6492
|
-
const
|
|
6493
|
-
const
|
|
6617
|
+
const packageOf=(id)=>{if(packageCache.has(id))return packageCache.get(id);let current=id;const seen=new Set();while(current&&!seen.has(current)){seen.add(current);if(byId.get(current)?.kind==="package"){packageCache.set(id,current);return current}current=parent.get(current)}packageCache.set(id,id);return id};
|
|
6618
|
+
const groupFor=(id)=>data.view?.entityGroup?.[id]||id;
|
|
6619
|
+
const packageNodes=()=>data.entities.filter((entity)=>entity.kind==="package");
|
|
6620
|
+
const viewLabels={architecture:"Architecture",drift:"Insights",risks:"Findings",evidence:"Coverage"};
|
|
6621
|
+
const ensureReportChrome=()=>{document.querySelectorAll(".tab[data-lens]").forEach((tab)=>{tab.textContent=viewLabels[tab.dataset.lens]||tab.dataset.lens;tab.closest(".tabs")?.classList.add("report-tabs")});let breadcrumbs=document.querySelector("#breadcrumbs");if(!breadcrumbs){breadcrumbs=document.createElement("nav");breadcrumbs.id="breadcrumbs";breadcrumbs.className="breadcrumbs";breadcrumbs.setAttribute("aria-label","Report location");document.querySelector(".workspace")?.before(breadcrumbs)}return breadcrumbs};
|
|
6622
|
+
const label=(entity)=>entity?.name||entity?.id||"Unknown";
|
|
6623
|
+
const short=(value,max)=>{const text=String(value??""),limit=text==="External dependencies"?16:max;return text.length>limit?text.slice(0,limit-1)+"…":text};
|
|
6624
|
+
const renderBreadcrumbs=()=>{const breadcrumbs=ensureReportChrome(),items=[{label:"Repository",selected:"",level:"overview"}];if(state.selected){const group=groupById.get(state.selected),entity=byId.get(state.selected);if(group)items.push({label:group.name,selected:group.id,level:"package"});else if(entity){const packageId=packageOf(entity.id),packageEntity=byId.get(packageId),groupId=groupFor(packageId);if(groupById.has(groupId))items.push({label:groupById.get(groupId).name,selected:groupId,level:"package"});if(packageEntity&&packageEntity.id!==entity.id)items.push({label:label(packageEntity),selected:packageEntity.id,level:"module"});items.push({label:label(entity),selected:entity.id,level:state.level})}}breadcrumbs.innerHTML=items.map((item,index)=>{const separator="<span aria-hidden=\"true\">"+(index?" / ":"")+"</span>";if(index===items.length-1)return separator+"<span class=\"current\">"+esc(item.label)+"</span>";return separator+"<button type=\"button\" data-breadcrumb-selected=\""+esc(item.selected)+"\" data-breadcrumb-level=\""+esc(item.level)+"\">"+esc(item.label)+"</button>"}).join("")};
|
|
6494
6625
|
const degreeMap=(relations)=>{const degrees=new Map();relations.forEach((relation)=>{degrees.set(relation.from,(degrees.get(relation.from)||0)+1);degrees.set(relation.to,(degrees.get(relation.to)||0)+1)});return degrees};
|
|
6495
|
-
const
|
|
6496
|
-
const
|
|
6497
|
-
const
|
|
6498
|
-
const
|
|
6499
|
-
const
|
|
6500
|
-
const
|
|
6501
|
-
const
|
|
6502
|
-
const
|
|
6503
|
-
const
|
|
6504
|
-
const
|
|
6505
|
-
|
|
6506
|
-
|
|
6626
|
+
const relationHealth=(relation)=>{if(!relation)return "";const findings=relationFindingIndex.get(relation.id)||[];return findings.some((finding)=>finding.severity==="error")?"error":findings.length?"warn":""};
|
|
6627
|
+
const nodeIssues=(nodeId)=>{if(nodeIssueCache.has(nodeId))return nodeIssueCache.get(nodeId);const ids=groupEntityIds.get(nodeId)||new Set([nodeId]),findings=new Set(),relations=new Set();for(const id of ids){(findingIndex.get(id)||[]).forEach((finding)=>findings.add(finding));(relationsByEntity.get(id)||[]).forEach((relation)=>relations.add(relation))}relations.forEach((relation)=>(relationFindingIndex.get(relation.id)||[]).forEach((finding)=>findings.add(finding)));const result=[...findings];nodeIssueCache.set(nodeId,result);return result};
|
|
6628
|
+
const findingInLens=(finding)=>state.lens==="architecture"||(state.lens==="drift"&&finding.status!=="confirmed")||(state.lens==="risks"&&(finding.severity==="error"||finding.severity==="warn"))||(state.lens==="evidence"&&finding.evidence.length>0);
|
|
6629
|
+
const relationInLens=(id)=>{const findings=relationFindingIndex.get(id)||[];return findings.some(findingInLens)};
|
|
6630
|
+
const externalFor=(ids)=>{const externalIds=new Set();for(const id of ids)for(const relation of relationsByEntity.get(id)||[]){const other=relation.from===id?relation.to:relation.from;if(byId.get(other)?.kind==="external")externalIds.add(other)}return [...externalIds].map((id)=>byId.get(id)).filter(Boolean)};
|
|
6631
|
+
const nodesFor=()=>{if(state.level==="overview")return data.view?.overview?.nodes||[];const packages=packageNodes(),selectedEntity=state.selected?byId.get(state.selected):null,groupScope=state.selected&&groupById.has(state.selected)?state.selected:null,packageScope=selectedEntity?.kind==="package"?selectedEntity.id:state.selected&&!groupScope?packageOf(state.selected):null;if(state.level==="package"&&!state.selected){const packageIds=new Set(packages.map((node)=>node.id));return packages.concat(state.lens==="architecture"?[]:externalFor(packageIds))}if((state.level==="module"||state.level==="file")&&!state.selected)return[];let nodes=data.entities.filter((entity)=>{if(state.level==="package")return entity.kind==="package"&&(!groupScope||groupFor(entity.id)===groupScope)&&(!packageScope||entity.id===packageScope);if(state.level==="module")return entity.kind==="module"&&(groupScope?groupFor(entity.id)===groupScope:packageOf(entity.id)===packageScope);return Boolean(entity.path)&&(groupScope?groupFor(entity.id)===groupScope:packageOf(entity.id)===packageScope)});if(state.level==="module"&&selectedEntity?.kind==="module"){const neighborhood=new Set([selectedEntity.id]);(relationsByEntity.get(selectedEntity.id)||[]).forEach((relation)=>{if(!["imports","re-exports"].includes(relation.kind))return;if(relation.from===selectedEntity.id)neighborhood.add(relation.to);if(relation.to===selectedEntity.id)neighborhood.add(relation.from)});nodes=data.entities.filter((entity)=>entity.kind==="module"&&neighborhood.has(entity.id))}const scopedIds=new Set(nodes.map((node)=>node.id)),external=state.lens==="architecture"?[]:externalFor(scopedIds),degrees=degreeMap(data.relations);return nodes.concat(external).sort((a,b)=>(degrees.get(b.id)||0)-(degrees.get(a.id)||0)||a.id.localeCompare(b.id)).slice(0,state.level==="file"?80:60)};
|
|
6632
|
+
const graphModel=()=>{if(state.level==="overview"){const overviewNodes=data.view?.overview?.nodes||[],overviewNodeIds=new Set(overviewNodes.map((node)=>node.id)),base=(data.view?.overview?.edges||[]).filter((edge)=>overviewNodeIds.has(edge.from)&&overviewNodeIds.has(edge.to)),edges=state.lens==="architecture"?base:base.filter((edge)=>edge.relationIds.some(relationInLens)),visible=new Set(edges.flatMap((edge)=>[edge.from,edge.to]));return{nodes:state.lens==="architecture"?overviewNodes:overviewNodes.filter((node)=>visible.has(node.id)||nodeIssues(node.id).some((finding)=>findingInLens(finding))),edges:edges.map((edge)=>({...edge,health:edge.relationIds.some((id)=>relationHealth(relationById.get(id)))?"error":""}))}}const nodes=nodesFor(),ids=new Set(nodes.map((node)=>node.id)),aggregate=state.level==="package"&&(!state.selected||state.selected.startsWith("group:")),edges=new Map();data.relations.forEach((relation)=>{const essential=state.level==="package"?relation.kind==="depends-on":relation.kind==="imports"||relation.kind==="re-exports";if(relation.kind==="contains"|| (state.lens==="architecture"&&!essential)||(state.lens!=="architecture"&&!relationInLens(relation.id)))return;const from=aggregate?packageOf(relation.from):relation.from,to=aggregate?packageOf(relation.to):relation.to;if(from===to||!ids.has(from)||!ids.has(to))return;const key=from+"→"+to,current=edges.get(key)||{from,to,count:0,kinds:new Set(),relationIds:new Set(),health:""};current.count++;current.kinds.add(relation.kind);current.relationIds.add(relation.id);current.health=current.health==="error"||relationHealth(relation)==="error"?"error":current.health||relationHealth(relation);edges.set(key,current)});const visibleEdges=[...edges.values()].sort((a,b)=>b.count-a.count||a.from.localeCompare(b.from)||a.to.localeCompare(b.to));const visible=new Set(visibleEdges.flatMap((edge)=>[edge.from,edge.to]));return{nodes:state.lens==="architecture"?nodes:nodes.filter((node)=>visible.has(node.id)||nodeIssues(node.id).some((finding)=>findingInLens(finding))),edges:visibleEdges}};
|
|
6633
|
+
const renderInsights=()=>{const summary=data.diagnosticSummary||{},undocumented=summary.undocumented??data.diagnostics.filter((finding)=>finding.status==="undocumented").length,drift=summary.drift??data.diagnostics.filter((finding)=>finding.status==="stale-or-unverified"||finding.status==="conflict").length,unsupported=(data.coverage||[]).filter((entry)=>entry.status==="not-analyzed"||entry.status==="partial").length,model=graphModel(),degrees=degreeMap(model.edges),values=[...degrees.values()].sort((a,b)=>a-b),median=values.length?values[Math.floor(values.length/2)]:0,hot=[...degrees.values()].filter((value)=>value>=Math.max(4,median*2)).length,isolated=model.nodes.filter((node)=>!model.edges.some((edge)=>edge.from===node.id||edge.to===node.id)).length;document.querySelector("#insights").innerHTML=[["Documentation drift",undocumented+drift,"Relations or docs needing comparison.",""],["Unanalyzed scope",unsupported,"Coverage gaps are explicit.",""],["Connectivity hotspots",hot,"Heuristic: unusually connected nodes.","heuristic"],["Disconnected nodes",isolated,"Heuristic: no visible edge at this level.","heuristic"]].map(([title,count,copy,tag])=>"<article class=\"insight\"><h3>"+esc(title)+" <span class=\"tag "+tag+"\">"+(tag?"heuristic":"signal")+"</span></h3><p><strong>"+count+"</strong> · "+esc(copy)+"</p></article>").join("")};
|
|
6634
|
+
const renderInsightsDashboard=()=>{let dashboard=document.querySelector("#report-dashboard");if(!dashboard){dashboard=document.createElement("section");dashboard.id="report-dashboard";dashboard.className="report-dashboard";document.querySelector(".filters")?.before(dashboard)}if(!data.diagnostics.length){dashboard.innerHTML="<p class=\"empty\">Open Findings or load the findings data to calculate insights.</p>";return}const degrees=degreeMap(data.relations),groups=[...groupById.entries()].filter(([,group])=>group.kind!=="external"),rows=groups.map(([id,group])=>{const members=groupEntityIds.get(id)||new Set(),modules=[...members].filter((entityId)=>byId.get(entityId)?.kind==="module").length;return{name:group.name,packages:group.members.length,modules,findings:groupFindingCounts.get(id)||0}}).sort((a,b)=>b.findings-a.findings||a.name.localeCompare(b.name)),topPackages=[...data.entities].filter((entity)=>entity.kind==="package").map((entity)=>({name:label(entity),degree:degrees.get(entity.id)||0})).sort((a,b)=>b.degree-a.degree||a.name.localeCompare(b.name)).slice(0,10),max=Math.max(1,...topPackages.map((row)=>row.degree));dashboard.innerHTML="<section><div class=\"eyebrow\">Attention by scope</div><h2>Where the map needs a closer look</h2><table class=\"report-table\"><thead><tr><th>Scope</th><th>Packages</th><th>Modules</th><th>Findings</th></tr></thead><tbody>"+rows.map((row)=>"<tr><td>"+esc(row.name)+"</td><td>"+row.packages+"</td><td>"+row.modules+"</td><td><strong>"+row.findings+"</strong></td></tr>").join("")+"</tbody></table></section><section><div class=\"eyebrow\">Connectivity</div><h2>Most connected packages</h2><div class=\"bar-list\">"+topPackages.map((row)=>"<div class=\"bar-item\"><span>"+esc(row.name)+"</span><i><b style=\"width:"+Math.round(row.degree/max*100)+"%\"></b></i><strong>"+row.degree+"</strong></div>").join("")+"</div></section>"};
|
|
6635
|
+
const mapState={scale:1,dragging:false,x:0,y:0};
|
|
6636
|
+
const applyMapTransform=()=>{const svg=document.querySelector("#graph");if(!svg)return;svg.style.transform="translate("+mapState.x+"px,"+mapState.y+"px) scale("+mapState.scale+")";svg.style.transformOrigin="center center"};
|
|
6637
|
+
const routeGraphEdges=()=>{const svg=document.querySelector("#graph");if(!svg)return;svg.querySelectorAll("line.edge").forEach((line,index)=>{const x1=Number(line.getAttribute("x1")),y1=Number(line.getAttribute("y1")),x2=Number(line.getAttribute("x2")),y2=Number(line.getAttribute("y2")),midX=(x1+x2)/2+((index%5)-2)*14,path=document.createElementNS("http://www.w3.org/2000/svg","path");path.setAttribute("class",line.getAttribute("class")||"edge");path.setAttribute("fill","none");path.setAttribute("marker-end",line.getAttribute("marker-end")||"url(#arrow)");path.setAttribute("d","M "+x1+" "+y1+" H "+midX+" V "+y2+" H "+x2);const label=line.parentElement?.querySelector("text.edge-label");if(label){label.setAttribute("x",String(midX+3));label.setAttribute("y",String((y1+y2)/2))}line.replaceWith(path)})};
|
|
6638
|
+
const enterNode=(id)=>{const group=groupById.get(id),entity=byId.get(id);if(group){state.selected=id;state.level="package"}else if(entity?.kind==="package"){state.selected=id;state.level="module"}else if(entity?.kind==="module"){state.selected=id;state.level="file"}else return;mapState.scale=1;mapState.x=0;mapState.y=0;render()};
|
|
6639
|
+
let nodeClickTimer;
|
|
6640
|
+
document.addEventListener("dblclick",(event)=>{const node=event.target.closest?.("[data-node]");if(!node)return;event.preventDefault();event.stopImmediatePropagation();clearTimeout(nodeClickTimer);enterNode(node.dataset.node)},true);
|
|
6641
|
+
document.addEventListener("click",(event)=>{const node=event.target.closest?.("[data-node]");if(!node)return;event.preventDefault();event.stopImmediatePropagation();clearTimeout(nodeClickTimer);if(event.detail>=2){enterNode(node.dataset.node);return}nodeClickTimer=setTimeout(()=>{state.selected=node.dataset.node;render()},380)},true);
|
|
6642
|
+
document.addEventListener("click",(event)=>{const crumb=event.target.closest?.("[data-breadcrumb-level]");if(!crumb)return;event.preventDefault();event.stopImmediatePropagation();state.level=crumb.dataset.breadcrumbLevel;state.selected=crumb.dataset.breadcrumbSelected||null;mapState.scale=1;mapState.x=0;mapState.y=0;render()},true);
|
|
6643
|
+
const diagnosticsFor=(id)=>findingIndex.get(id)||[];
|
|
6644
|
+
const renderDetails=()=>{const panel=document.querySelector("#details"),entity=state.selected?byId.get(state.selected):null,group=state.selected?groupById.get(state.selected):null;if(group){const members=group.members.map((id)=>byId.get(id)).filter(Boolean),relations=new Set(),ids=groupEntityIds.get(state.selected)||new Set();ids.forEach((id)=>(relationsByEntity.get(id)||[]).forEach((relation)=>relations.add(relation)));const findings=nodeIssues(state.selected),evidence=members.flatMap((member)=>member.evidence||[]).slice(0,8),unit=group.kind==="external"?"dependencies":"packages";panel.innerHTML="<div class=\"eyebrow\">Selected group</div><h2 class=\"detail-title\">"+esc(group.name)+"</h2><span class=\"tag\">"+esc(group.kind)+"</span><p class=\"path\">"+esc(group.path||"Derived from stable package identity and repository structure")+"</p><div class=\"detail-grid\"><div><b>"+members.length+"</b><span>"+unit+"</span></div><div><b>"+relations.size+"</b><span>relations</span></div><div><b>"+findings.length+"</b><span>findings</span></div><div><b>"+evidence.length+"</b><span>evidence items</span></div></div><h3>Members</h3><ul class=\"list\">"+(members.length?members.slice(0,8).map((member)=>"<li>"+esc(label(member))+"</li>").join(""):"<li>No members recorded.</li>")+(members.length>8?"<li class=\"subtle\">+"+(members.length-8)+" more — choose Package level to inspect all.</li>":"")+"</ul>"+(findings.length?"<h3 style=\"margin-top:16px\">Attention</h3><ul class=\"list\">"+findings.slice(0,5).map((finding)=>"<li><span class=\"tag "+esc(finding.severity)+"\">"+esc(finding.severity)+"</span> "+esc(finding.code)+"</li>").join("")+"</ul>":"");return}if(!entity){panel.innerHTML="<p class=\"subtle\">Select a node in the map to inspect its evidence, connectivity, and findings.</p>";return}const relations=relationsByEntity.get(entity.id)||[],incoming=relations.filter((relation)=>relation.to===entity.id),outgoing=relations.filter((relation)=>relation.from===entity.id),findings=diagnosticsFor(entity.id),evidence=[...(entity.evidence||[]),...incoming.flatMap((relation)=>relation.evidence||[]),...outgoing.flatMap((relation)=>relation.evidence||[])].slice(0,8);panel.innerHTML="<div class=\"eyebrow\">Selected entity</div><h2 class=\"detail-title\">"+esc(label(entity))+"</h2><span class=\"tag\">"+esc(entity.kind)+"</span><p class=\"path\">"+esc(entity.path||entity.id)+"</p><div class=\"detail-grid\"><div><b>"+incoming.length+"</b><span>incoming</span></div><div><b>"+outgoing.length+"</b><span>outgoing</span></div><div><b>"+findings.length+"</b><span>findings</span></div><div><b>"+evidence.length+"</b><span>evidence items</span></div></div><h3>Evidence</h3><ul class=\"list\">"+(evidence.length?evidence.map((item)=>"<li>"+esc(item.path+(item.lineStart?":"+item.lineStart:"")+(item.context?" — "+item.context:""))+"</li>").join(""):"<li>No evidence recorded.</li>")+"</ul>"+(findings.length?"<h3 style=\"margin-top:16px\">Attention</h3><ul class=\"list\">"+findings.slice(0,5).map((finding)=>"<li><span class=\"tag "+esc(finding.severity)+"\">"+esc(finding.severity)+"</span> "+esc(finding.code)+"</li>").join("")+"</ul>":"")};
|
|
6645
|
+
const renderGraph=()=>{const model=graphModel(),svg=document.querySelector("#graph");if(!model.nodes.length){svg.innerHTML="<text x=\"500\" y=\"270\" text-anchor=\"middle\" class=\"subtle\">Select an app or package to expand this view.</text>";return}const ids=new Set(model.nodes.map((node)=>node.id)),incoming=new Map(model.nodes.map((node)=>[node.id,0])),outgoing=new Map(model.nodes.map((node)=>[node.id,[]]));model.edges.forEach((edge)=>{if(!ids.has(edge.from)||!ids.has(edge.to))return;incoming.set(edge.to,(incoming.get(edge.to)||0)+1);outgoing.get(edge.from).push(edge.to)});const rank=new Map(),work=[];model.nodes.filter((node)=>(incoming.get(node.id)||0)===0).sort((a,b)=>label(a).localeCompare(label(b))||a.id.localeCompare(b.id)).forEach((node)=>{rank.set(node.id,0);work.push(node)});for(let index=0;index<work.length;index++){const node=work[index];for(const next of outgoing.get(node.id)||[]){const nextRank=Math.max(rank.get(next)||0,(rank.get(node.id)||0)+1);rank.set(next,nextRank);if(!work.some((item)=>item.id===next))work.push(byId.get(next)||{id:next,name:next})}}model.nodes.forEach((node)=>{if(!rank.has(node.id))rank.set(node.id,0)});const columns=new Map();model.nodes.forEach((node)=>{const items=columns.get(rank.get(node.id))||[];items.push(node);columns.set(rank.get(node.id),items)});for(const items of columns.values())items.sort((a,b)=>label(a).localeCompare(label(b))||a.id.localeCompare(b.id));const maxRows=Math.max(1,...[...columns.values()].map((items)=>items.length)),cellWidth=190,cellHeight=82,pad=28,columnCount=Math.max(1,...columns.keys())+1,width=Math.max(620,columnCount*cellWidth+pad*2,Math.min(1000,svg.clientWidth*1.05)),height=Math.max(520,maxRows*cellHeight+pad*2);svg.setAttribute("viewBox","0 0 "+width+" "+height);svg.style.width="100%";svg.style.minWidth="0";svg.style.height="100%";const positions=new Map();for(const [column,items] of columns)items.forEach((node,row)=>positions.set(node.id,{x:pad+column*cellWidth+78,y:pad+row*cellHeight+27}));const defs="<defs><marker id=\"arrow\" markerWidth=\"8\" markerHeight=\"8\" refX=\"7\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L0,6 L7,3 z\" fill=\"#789087\"/></marker></defs>",edges=model.edges.map((edge)=>{const from=positions.get(edge.from),to=positions.get(edge.to);if(!from||!to)return "";const midX=(from.x+to.x)/2,midY=(from.y+to.y)/2;return "<g><line class=\"edge "+(edge.health?"alert":"")+"\" x1=\""+from.x+"\" y1=\""+from.y+"\" x2=\""+to.x+"\" y2=\""+to.y+"\" marker-end=\"url(#arrow)\"/><text class=\"edge-label\" x=\""+midX+"\" y=\""+midY+"\">"+esc(edge.count>1?edge.count+"×":"")+"</text></g>"}).join(""),nodes=model.nodes.map((node)=>{const pos=positions.get(node.id),issues=nodeIssues(node.id),degree=degreeMap(model.edges).get(node.id)||0,selected=state.selected===node.id?" selected":"",issue=issues.length?" issue":"",nodeLabel=label(node),nodeKind=node.kind==="domain"?(node.memberCount||0)+" packages":node.kind;return "<g class=\"graph-node"+selected+issue+"\" transform=\"translate("+(pos.x-78)+","+(pos.y-27)+")\"><title>"+esc(nodeLabel)+" · "+esc(nodeKind)+"</title><rect width=\"156\" height=\"54\" rx=\"8\" role=\"button\" tabindex=\"0\" aria-label=\""+esc(nodeLabel+" "+nodeKind)+"\" data-node=\""+esc(node.id)+"\"></rect><text class=\"node-label\" x=\"10\" y=\"19\">"+esc(short(nodeLabel,22))+"</text><text class=\"node-kind\" x=\"10\" y=\"34\">"+esc(short(nodeKind,22))+"</text><text class=\"node-count\" x=\"144\" y=\"19\" text-anchor=\"end\">"+(degree||"")+"</text></g>"}).join("");svg.innerHTML=defs+edges+nodes;routeGraphEdges();applyMapTransform();document.querySelector("#map-note").textContent=(state.level==="overview"?"Bounded domain view. ":state.level+" view. ")+"Directional edges are aggregated from canonical relations; "+model.edges.length+" visible connection groups."};
|
|
6646
|
+
const findingMatches=(finding)=>{const query=state.query.toLowerCase();return(!query||[finding.id,finding.code,finding.message,...finding.entityIds,...finding.relationIds].join(" ").toLowerCase().includes(query))&&(!state.status||finding.status===state.status)&&(!state.severity||finding.severity===state.severity)};
|
|
6647
|
+
const findingScope=(finding)=>{const ids=[...(finding.entityIds||[])];(finding.relationIds||[]).forEach((id)=>{const relation=relationById.get(id);if(relation)ids.push(relation.from,relation.to)});return ids.map(groupFor).sort()[0]||"repository"};
|
|
6648
|
+
const findingGroups=(findings)=>{const groups=new Map();findings.forEach((finding)=>{const scope=findingScope(finding),key=[scope,finding.code,finding.status,finding.severity].join("|"),group=groups.get(key)||{key,scope,code:finding.code,status:finding.status,severity:finding.severity,findings:[]};group.findings.push(finding);groups.set(key,group)});return[...groups.values()].sort((left,right)=>right.findings.length-left.findings.length||left.code.localeCompare(right.code)||left.key.localeCompare(right.key))};
|
|
6649
|
+
const renderFinding=(finding)=>"<article class=\"finding\" id=\""+esc("diagnostic-"+finding.id.replace(/[^A-Za-z0-9_-]+/g,"-"))+"\"><div class=\"finding-head\"><h3>"+esc(finding.code)+"</h3><span><span class=\"tag "+esc(finding.severity)+"\">"+esc(finding.severity)+"</span> <span class=\"tag\">"+esc(finding.status)+"</span></span></div><p>"+esc(finding.message)+"</p>"+(finding.evidence.length?"<ul>"+finding.evidence.slice(0,4).map((item)=>"<li>"+esc(item.path+(item.lineStart?":"+item.lineStart:"")+(item.context?" — "+item.context:""))+"</li>").join("")+"</ul>":"")+(finding.remediation?"<p><strong>Next check:</strong> "+esc(finding.remediation)+"</p>":"")+"</article>";
|
|
6650
|
+
const renderFindings=()=>{let findings=data.diagnostics.filter(findingMatches);if(state.lens==="risks")findings=findings.filter((finding)=>finding.severity==="error"||finding.severity==="warn");if(state.lens==="evidence")findings=findings.filter((finding)=>finding.evidence.length);if(state.lens==="drift")findings=findings.filter((finding)=>finding.status!=="confirmed");const groups=findingGroups(findings),active=groups.find((group)=>group.key===state.findingGroup);if(!active){state.findingGroup=null;state.findingPage=0}syncHash();const selected=active||null,pageSize=40,pageCount=selected?Math.ceil(selected.findings.length/pageSize):0,page=Math.max(0,Math.min(state.findingPage,Math.max(0,pageCount-1))),start=page*pageSize;document.querySelector("#finding-count").textContent=findings.length+" findings · "+groups.length+" groups";document.querySelector("#findings").innerHTML=findings.length?"<div class=\"finding-groups\">"+groups.map((group)=>"<article class=\"finding-group\"><div class=\"finding-head\"><h3>"+esc(group.code)+"</h3><span><span class=\"tag "+esc(group.severity)+"\">"+esc(group.severity)+"</span> <span class=\"tag\">"+esc(group.status)+"</span></span></div><p><strong>"+group.findings.length+"</strong> finding"+(group.findings.length===1?"":"s")+" · "+esc(groupById.get(group.scope)?.name||group.scope)+"</p><p>"+esc(group.findings[0].message)+"</p><button class=\"tab\" type=\"button\" data-finding-group=\""+esc(group.key)+"\" aria-expanded=\""+String(selected?.key===group.key)+"\">Inspect group</button></article>").join("")+"</div>"+(selected?"<div class=\"finding-detail\"><div class=\"finding-head\"><h3>"+esc(selected.code)+" · "+esc(groupById.get(selected.scope)?.name||selected.scope)+"</h3><span class=\"subtle\">"+selected.findings.length+" total</span></div>"+selected.findings.slice(start,start+pageSize).map(renderFinding).join("")+"<div class=\"finding-actions\"><button class=\"tab\" type=\"button\" data-finding-group=\""+esc(selected.key)+"\" data-finding-page=\""+(page-1)+"\" "+(page===0?"disabled":"")+">Previous</button><span class=\"subtle\">Showing "+(start+1)+"–"+Math.min(start+pageSize,selected.findings.length)+" of "+selected.findings.length+" · page "+(page+1)+"/"+pageCount+"</span><button class=\"tab\" type=\"button\" data-finding-group=\""+esc(selected.key)+"\" data-finding-page=\""+(page+1)+"\" "+(page+1>=pageCount?"disabled":"")+">Next</button></div></div>":""):"<p class=\"empty\">No findings match the current lens and filters.</p>"};
|
|
6651
|
+
const renderCoverage=()=>{document.querySelector("#coverage-list").innerHTML=(data.coverage||[]).map((entry)=>{const width=entry.status==="complete"?100:entry.status==="partial"?55:12;return"<div class=\"coverage-row\"><div><b>"+esc(entry.analyzer)+"</b><br><span class=\"subtle\">"+esc(entry.scope)+"</span></div><div class=\"bar\"><i class=\""+(entry.status==="complete"?"":entry.status==="partial"?"partial":"none")+"\" style=\"width:"+width+"%\"></i></div><div>"+esc(entry.status)+"</div></div>"}).join("")||"<p class=\"empty\">No coverage metadata.</p>"};
|
|
6652
|
+
const deferFindings=()=>{if(findingsLoaded())return;const count=data.diagnosticCount||0;document.querySelector("#finding-count").textContent=count+" findings · available on demand";document.querySelector("#findings").innerHTML="<div class=\"empty\"><p>Findings stay out of the first paint so large repositories remain responsive.</p><button id=\"load-findings\" class=\"tab\" type=\"button\">Load findings</button></div>"};
|
|
6653
|
+
const scopePrompt=()=>{if((state.level==="module"||state.level==="file")&&!state.selected){document.querySelector("#map-note").textContent="Select an app or package to inspect this level.";document.querySelector("#graph").innerHTML="<text x=\"500\" y=\"270\" text-anchor=\"middle\" class=\"subtle\">Select an app or package to expand this view.</text>"}};
|
|
6654
|
+
const findingsLoaded=()=>!lazyChunks.includes("chunks/findings.js")||loadedChunks.has("chunks/findings.js");
|
|
6655
|
+
const levelsLoaded=()=>!lazyChunks.includes("chunks/levels.js")||loadedChunks.has("chunks/levels.js");
|
|
6656
|
+
const setReportView=()=>{document.body.dataset.reportView={architecture:"architecture",drift:"insights",risks:"findings",evidence:"coverage"}[state.lens]||"architecture";ensureReportChrome()};
|
|
6657
|
+
const render=()=>{if(state.level!=="overview"&&!levelsLoaded()){document.querySelector("#map-note").textContent="Loading canonical entities…";loadChunk("chunks/levels.js").then(render).catch((error)=>{document.querySelector("#map-note").textContent=error.message});return}if(state.lens!=="architecture"&&!findingsLoaded()){document.querySelector("#lens-caption").textContent="Loading findings for this lens…";loadChunk("chunks/findings.js").then(render).catch((error)=>{document.querySelector("#lens-caption").textContent=error.message});return}syncHash();setReportView();renderBreadcrumbs();document.querySelectorAll(".tab[data-lens]").forEach((tab)=>tab.setAttribute("aria-selected",String(tab.dataset.lens===state.lens)));document.querySelectorAll(".level").forEach((button)=>button.setAttribute("aria-pressed",String(button.dataset.level===state.level)));document.querySelector("#lens-caption").textContent=state.lens==="architecture"?"The repository topology at the selected level.":state.lens==="drift"?"Where canonical code relations and documentation declarations need attention.":state.lens==="risks"?"Signals that deserve human review; connectivity warnings are heuristics, not architectural proof.":"What the analyzers observed, declared, or could not analyze.";renderInsights();renderInsightsDashboard();renderGraph();renderDetails();renderFindings();deferFindings();renderCoverage()};
|
|
6658
|
+
document.addEventListener("click",async(event)=>{const target=event.target.closest?.("[data-level],[data-lens],#load-findings");if(!target)return;if(target.id==="load-findings"){event.preventDefault();event.stopImmediatePropagation();await loadChunk("chunks/findings.js");render();return}if(target.dataset.level&&!levelsLoaded()){event.preventDefault();event.stopImmediatePropagation();state.level=target.dataset.level;await loadChunk("chunks/levels.js");render();return}if(target.dataset.lens&&target.dataset.lens!=="architecture"&&!findingsLoaded()){event.preventDefault();event.stopImmediatePropagation();state.lens=target.dataset.lens;state.findingGroup=null;await loadChunk("chunks/findings.js");render()}},true);
|
|
6659
|
+
document.addEventListener("click",(event)=>{const node=event.target.closest?.("[data-node]");if(!node)return;event.preventDefault();event.stopImmediatePropagation();clearTimeout(nodeClickTimer);if(event.detail>=2){enterNode(node.dataset.node);return}nodeClickTimer=setTimeout(()=>{state.selected=node.dataset.node;render()},380)},true);
|
|
6660
|
+
document.addEventListener("click",(event)=>{const crumb=event.target.closest?.("[data-breadcrumb-level]");if(!crumb)return;event.preventDefault();event.stopImmediatePropagation();state.level=crumb.dataset.breadcrumbLevel;state.selected=crumb.dataset.breadcrumbSelected||null;mapState.scale=1;mapState.x=0;mapState.y=0;render()},true);
|
|
6661
|
+
document.addEventListener("input",async(event)=>{const target=event.target;if(target?.id!=="search"||findingsLoaded())return;event.stopImmediatePropagation();state.query=target.value;state.findingGroup=null;await loadChunk("chunks/findings.js");render()},true);
|
|
6662
|
+
document.addEventListener("change",async(event)=>{const target=event.target;if(!["status","severity"].includes(target?.id)||findingsLoaded())return;event.stopImmediatePropagation();state[target.id]=target.value;state.findingGroup=null;await loadChunk("chunks/findings.js");render()},true);
|
|
6663
|
+
document.addEventListener("wheel",(event)=>{const wrap=event.target.closest?.(".map-wrap");if(!wrap)return;event.preventDefault();mapState.scale=Math.max(.65,Math.min(2.5,mapState.scale*(event.deltaY<0?1.12:.89)));applyMapTransform()},{passive:false});
|
|
6664
|
+
document.addEventListener("pointerdown",(event)=>{const wrap=event.target.closest?.(".map-wrap");if(!wrap)return;mapState.dragging=true;mapState.lastX=event.clientX;mapState.lastY=event.clientY;wrap.classList.add("dragging")},true);
|
|
6665
|
+
document.addEventListener("pointermove",(event)=>{if(!mapState.dragging)return;mapState.x+=event.clientX-mapState.lastX;mapState.y+=event.clientY-mapState.lastY;mapState.lastX=event.clientX;mapState.lastY=event.clientY;applyMapTransform()});
|
|
6666
|
+
document.addEventListener("pointerup",()=>{mapState.dragging=false;document.querySelector(".map-wrap")?.classList.remove("dragging")},true);
|
|
6667
|
+
document.addEventListener("keydown",(event)=>{if(event.target.matches?.("input,select,textarea"))return;if(event.key==="+"||event.key==="="){mapState.scale=Math.min(2.5,mapState.scale*1.15);applyMapTransform()}if(event.key==="-"){mapState.scale=Math.max(.65,mapState.scale*.87);applyMapTransform()}if(event.key==="0"){mapState.scale=1;mapState.x=0;mapState.y=0;applyMapTransform()}if(event.key==="Escape"){if(state.selected){state.selected=null}else if(state.level==="file"){state.level="module"}else if(state.level==="module"){state.level="package"}else if(state.level==="package"){state.level="overview"}render()}},true);
|
|
6668
|
+
document.querySelectorAll(".tab[data-lens]").forEach((tab)=>tab.addEventListener("click",()=>{state.lens=tab.dataset.lens;state.findingGroup=null;render()}));
|
|
6669
|
+
document.querySelectorAll(".level").forEach((button)=>button.addEventListener("click",()=>{state.level=button.dataset.level;render()}));
|
|
6670
|
+
document.querySelector("#search").addEventListener("input",(event)=>{state.query=event.target.value;state.findingGroup=null;renderFindings()});
|
|
6671
|
+
document.querySelector("#status").addEventListener("change",(event)=>{state.status=event.target.value;state.findingGroup=null;renderFindings()});
|
|
6672
|
+
document.querySelector("#severity").addEventListener("change",(event)=>{state.severity=event.target.value;state.findingGroup=null;renderFindings()});
|
|
6673
|
+
document.querySelector("#findings").addEventListener("click",(event)=>{const button=event.target.closest("[data-finding-group]");if(!button)return;state.findingGroup=button.dataset.findingGroup;state.findingPage=Number(button.dataset.findingPage||0);renderFindings()});
|
|
6674
|
+
document.querySelector("#reset").addEventListener("click",()=>{state.query="";state.status="";state.severity="";state.findingGroup=null;state.findingPage=0;renderFindings()});
|
|
6675
|
+
document.querySelector("#clear-selection").addEventListener("click",()=>{state.selected=null;render()});
|
|
6676
|
+
const keepMobileGraphReadable=()=>{const svg=document.querySelector("#graph");if(!svg)return;if(window.innerWidth<=700){svg.style.setProperty("width","720px","important");svg.style.setProperty("min-width","720px","important");svg.style.setProperty("max-width","none","important")}else{svg.style.removeProperty("width");svg.style.removeProperty("min-width");svg.style.removeProperty("max-width")}};
|
|
6677
|
+
new MutationObserver(keepMobileGraphReadable).observe(document.querySelector("#graph"),{attributes:true,attributeFilter:["style"]});
|
|
6678
|
+
window.addEventListener("resize",keepMobileGraphReadable);
|
|
6679
|
+
window.addEventListener("hashchange",()=>{const next=readHash();state.lens=next.get("lens")||"architecture";state.level=next.get("level")||"overview";state.selected=next.get("selected");state.query=next.get("query")||"";state.status=next.get("status")||"";state.severity=next.get("severity")||"";state.findingGroup=next.get("findingGroup");state.findingPage=Number(next.get("findingPage")||0);render()});
|
|
6680
|
+
hydrate();render();keepMobileGraphReadable();ensurePolicyNote();
|
|
6507
6681
|
`;
|
|
6508
6682
|
var render = (input, options) => {
|
|
6509
6683
|
const { snapshot, report } = input;
|
|
6510
6684
|
const includeSnippets = options.includeSnippets === true;
|
|
6511
6685
|
const data = reportData(snapshot, report, includeSnippets);
|
|
6512
|
-
const scriptBody = script.replace("${DATA}", embeddedJson(data));
|
|
6686
|
+
const scriptBody = script.replace("${DATA}", options.dataExpression ?? embeddedJson(data));
|
|
6513
6687
|
const largeNote = snapshot.entities.length > 500 || report.diagnostics.length > 1e3 ? "Large snapshots are rendered from compact canonical data with progressive graph levels." : "The viewer starts with a grouped topology and expands into canonical entities on demand.";
|
|
6514
6688
|
const issueCount = report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "error" || diagnostic2.severity === "warn").length;
|
|
6515
6689
|
const unsupportedCount = snapshot.coverage.filter((entry) => entry.status !== "complete").length;
|
|
6690
|
+
const policyNote = report.summary.requiredRelationKinds === void 0 ? "Missing relation declarations are checked for every observed relation kind." : report.summary.requiredRelationKinds.length ? `Missing relation declarations are checked for: ${report.summary.requiredRelationKinds.join(", ")}.` : "Missing relation declarations are disabled by configuration; stale, conflicting, and unresolved declarations remain checked.";
|
|
6516
6691
|
const statusOptions = ["confirmed", "undocumented", "stale-or-unverified", "conflict", "unresolved", "not-analyzed"];
|
|
6517
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Doc Bridge \u2014 ${escapeHtml(snapshot.project.name)}</title><style>${styles}</style></head><body
|
|
6518
|
-
};
|
|
6519
|
-
var
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6692
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Doc Bridge \u2014 ${escapeHtml(snapshot.project.name)}</title><style>${styles}</style></head><body>${options.dataScripts ?? ""}<main class="shell"><header class="masthead"><div><div class="eyebrow">Doc Bridge / Knowledge report</div><h1>${escapeHtml(snapshot.project.name)}</h1><p class="lede">A read-only architecture and documentation map. Start broad, then follow evidence to the exact entity, relation, or finding.</p></div><div class="run-meta"><span class="read-only">Read-only snapshot</span><strong>${escapeHtml(snapshot.sourceRevision)}</strong><span>${escapeHtml(snapshot.sourceRevisionKind)} \xB7 pipeline ${escapeHtml(snapshot.pipelineVersion)}</span></div></header><section class="summary" aria-label="Snapshot summary"><div class="metric"><b>${snapshot.entities.length}</b><span>entities</span></div><div class="metric"><b>${snapshot.relations.length}</b><span>canonical relations</span></div><div class="metric ${issueCount ? "warn" : ""}"><b>${issueCount}</b><span>warn/error findings</span></div><div class="metric ${report.diagnostics.length ? "warn" : ""}"><b>${report.diagnostics.length}</b><span>total findings</span></div><div class="metric ${unsupportedCount ? "bad" : ""}"><b>${unsupportedCount}</b><span>partial / unanalyzed scopes</span></div></section><div class="lens-bar"><nav class="tabs" aria-label="Report lenses"><button class="tab" data-lens="architecture" aria-selected="true">Architecture</button><button class="tab" data-lens="drift" aria-selected="false">Documentation drift</button><button class="tab" data-lens="risks" aria-selected="false">Risks & hotspots</button><button class="tab" data-lens="evidence" aria-selected="false">Evidence</button></nav><span class="subtle">${escapeHtml(largeNote)}</span></div><p id="lens-caption" class="subtle">The repository topology at the selected level.</p><p class="subtle">${escapeHtml(policyNote)}</p><section id="insights" class="insights" aria-label="Attention signals"></section><section class="filters" aria-label="Finding filters"><label>Search <input id="search" type="search" placeholder="Press / to search findings"></label><label>Status <select id="status"><option value="">Any status</option>${statusOptions.map((value) => `<option>${escapeHtml(value)}</option>`).join("")}</select></label><label>Severity <select id="severity"><option value="">Any severity</option><option>error</option><option>warn</option><option>info</option></select></label><button id="reset" type="button">Reset filters</button></section><div class="workspace"><section class="panel" aria-labelledby="architecture-title"><div class="panel-head"><div><h2 id="architecture-title">Architecture map</h2><p id="map-note" class="subtle">Grouped package/external view.</p></div><div class="tabs" aria-label="Graph level"><button class="level" data-level="overview" aria-pressed="true">Overview</button><button class="level" data-level="package" aria-pressed="false">Package</button><button class="level" data-level="module" aria-pressed="false">Module</button><button class="level" data-level="file" aria-pressed="false">File</button></div></div><div class="map-wrap"><svg id="graph" viewBox="0 0 1000 560" role="img" aria-label="Interactive architecture graph"></svg></div><p class="subtle">Node number = visible connection degree. Amber nodes/edges have findings. Click or focus a node to inspect its evidence, connectivity, and findings. Grouped edges preserve canonical relation direction and count.</p></section><aside class="panel side" aria-labelledby="details-title"><div class="panel-head"><div><div class="eyebrow">Evidence trail</div><h2 id="details-title">Details</h2></div><button id="clear-selection" class="tab" type="button">Clear</button></div><div id="details"><p class="subtle">Select a node in the map to inspect its evidence, connectivity, and findings.</p></div></aside></div><section class="panel run" aria-labelledby="run-title"><div class="panel-head"><div><div class="eyebrow">Jest-like diagnostics</div><h2 id="run-title">Run report</h2></div><span id="finding-count" class="subtle">${report.diagnostics.length} shown</span></div><div class="run-summary"><span class="chip"><b>${report.diagnostics.filter((item) => item.severity === "error").length}</b> errors</span><span class="chip"><b>${report.diagnostics.filter((item) => item.severity === "warn").length}</b> warnings</span><span class="chip"><b>${report.diagnostics.filter((item) => item.status === "undocumented").length}</b> undocumented</span><span class="chip"><b>${report.diagnostics.filter((item) => item.status === "stale-or-unverified" || item.status === "conflict").length}</b> drift/conflict</span></div><div id="findings"></div></section><section class="panel run" aria-labelledby="coverage-title"><div class="panel-head"><div><div class="eyebrow">Analyzer boundaries</div><h2 id="coverage-title">Coverage & unsupported areas</h2></div><span class="subtle">Explicit limits are part of the evidence</span></div><div id="coverage-list"></div></section><section class="metadata" aria-label="Run metadata"><div><b>Snapshot</b>${escapeHtml(snapshot.contentHash)}</div><div><b>Report</b>${escapeHtml(report.contentHash)}</div><div><b>Configuration</b>${escapeHtml(snapshot.configurationHash)}</div><div><b>Analyzers</b>${escapeHtml(Object.entries(snapshot.analyzerVersions).map(([name, version]) => `${name} ${version}`).join(", "))}</div><div><b>Source revision</b>${escapeHtml(snapshot.sourceRevision)} (${escapeHtml(snapshot.sourceRevisionKind)})</div><div><b>Mode</b>Read-only browser viewer; approvals and fixes remain outside this artifact.</div></section></main><script>${scriptBody}</script></body></html>`;
|
|
6693
|
+
};
|
|
6694
|
+
var parseReportInput = (input) => {
|
|
6695
|
+
if (!input || typeof input !== "object") throw new Error("Input must contain snapshot and report artifacts.");
|
|
6696
|
+
const value = input;
|
|
6697
|
+
const snapshot = DiscoverySnapshotV1Schema.parse(value.snapshot);
|
|
6698
|
+
const report = ReconciliationReportV1Schema.parse(value.report);
|
|
6699
|
+
if (report.snapshotHash !== snapshot.contentHash) throw new Error("Report snapshotHash does not match snapshot contentHash.");
|
|
6700
|
+
return { snapshot, report };
|
|
6701
|
+
};
|
|
6702
|
+
var chunkScript = (payload) => `globalThis.__DOC_BRIDGE_DATA__=Object.assign(globalThis.__DOC_BRIDGE_DATA__||{},${embeddedJson(payload)});`;
|
|
6703
|
+
var byteLength = (value) => new TextEncoder().encode(value).byteLength;
|
|
6704
|
+
var renderOfflineReportArtifact = (input, options = {}) => {
|
|
6705
|
+
const parsed = parseReportInput(input);
|
|
6706
|
+
const thresholdBytes = options.thresholdBytes ?? DEFAULT_LARGE_REPORT_THRESHOLD_BYTES;
|
|
6707
|
+
const renderOptions = { includeSnippets: options.includeSnippets === true, thresholdBytes };
|
|
6708
|
+
const singleFile = render(parsed, options);
|
|
6709
|
+
if (byteLength(singleFile) <= thresholdBytes) {
|
|
6710
|
+
const files2 = { "index.html": sha256NormalizedV1(singleFile) };
|
|
6711
|
+
const manifestPayload2 = { schemaVersion: 1, mode: "single-file", generatedBy: "@agentskit/doc-bridge", snapshotHash: parsed.snapshot.contentHash, reportHash: parsed.report.contentHash, configurationHash: parsed.snapshot.configurationHash, sourceRevision: parsed.snapshot.sourceRevision, renderOptions, files: files2 };
|
|
6712
|
+
const manifest2 = JSON.stringify({ ...manifestPayload2, artifactHash: sha256NormalizedV1(manifestPayload2) }, null, 2);
|
|
6713
|
+
return { mode: "single-file", indexHtml: singleFile, files: { "index.html": singleFile }, manifest: manifest2 };
|
|
6714
|
+
}
|
|
6715
|
+
const data = reportData(parsed.snapshot, parsed.report, options.includeSnippets === true);
|
|
6716
|
+
const files = {
|
|
6717
|
+
"chunks/overview.js": chunkScript({ project: data.project, revision: data.revision, revisionKind: data.revisionKind, snapshotHash: data.snapshotHash, reportHash: data.reportHash, configurationHash: data.configurationHash, pipelineVersion: data.pipelineVersion, analyzerVersions: data.analyzerVersions, coverage: data.coverage, diagnosticCount: data.diagnosticCount, diagnosticSummary: data.diagnosticSummary, requiredRelationKinds: data.requiredRelationKinds, view: data.view }),
|
|
6718
|
+
"chunks/levels.js": chunkScript({ entities: data.entities, relations: data.relations }),
|
|
6719
|
+
"chunks/findings.js": chunkScript({ diagnostics: data.diagnostics })
|
|
6720
|
+
};
|
|
6721
|
+
const indexHtml = render(parsed, { ...options, dataExpression: "globalThis.__DOC_BRIDGE_DATA__", dataScripts: `<script>globalThis.__DOC_BRIDGE_LAZY_CHUNKS__=${embeddedJson(["chunks/levels.js", "chunks/findings.js"])}</script><script src="chunks/overview.js"></script>` });
|
|
6722
|
+
const fileHashes = Object.fromEntries(Object.entries({ ...files, "index.html": indexHtml }).map(([file, content]) => [file, sha256NormalizedV1(content)]));
|
|
6723
|
+
const manifestPayload = { schemaVersion: 1, mode: "directory", generatedBy: "@agentskit/doc-bridge", snapshotHash: parsed.snapshot.contentHash, reportHash: parsed.report.contentHash, configurationHash: parsed.snapshot.configurationHash, sourceRevision: parsed.snapshot.sourceRevision, renderOptions, files: fileHashes };
|
|
6724
|
+
const manifest = JSON.stringify({ ...manifestPayload, artifactHash: sha256NormalizedV1(manifestPayload) }, null, 2);
|
|
6725
|
+
return { mode: "directory", indexHtml, files: { ...files, "index.html": indexHtml }, manifest };
|
|
6530
6726
|
};
|
|
6531
6727
|
|
|
6532
6728
|
// src/cli/program.ts
|
|
@@ -6538,7 +6734,7 @@ Core (no API key):
|
|
|
6538
6734
|
ak-docs doctor [--text] [--badge] [--write-badge]
|
|
6539
6735
|
ak-docs index [--watch]
|
|
6540
6736
|
ak-docs discover [--text|--json]
|
|
6541
|
-
ak-docs scan | reconcile | check | map [--text|--json] [--html]
|
|
6737
|
+
ak-docs scan | reconcile | check | map [--text|--json] [--html] [--report-threshold <bytes>]
|
|
6542
6738
|
ak-docs fix propose links|normalize <artifact> [--output <file>]
|
|
6543
6739
|
ak-docs fix approve|apply <proposal.json> [--by <name>]
|
|
6544
6740
|
ak-docs suggest [--json|--text] run the configured local Registry agent
|
|
@@ -6828,6 +7024,7 @@ var workflowOptions = (root, config, sourceRevision2, stage, handlers) => ({
|
|
|
6828
7024
|
...config.workflow?.stateDir ? { stateDir: config.workflow.stateDir } : {},
|
|
6829
7025
|
sourceRevision: sourceRevision2,
|
|
6830
7026
|
configurationHash: sha256NormalizedV1(config),
|
|
7027
|
+
toolVersion: "1.0.1",
|
|
6831
7028
|
stage,
|
|
6832
7029
|
handlers
|
|
6833
7030
|
});
|
|
@@ -6841,7 +7038,7 @@ var reconcileWorkflow = (root, config) => {
|
|
|
6841
7038
|
const scanned = scanWorkflow(root, config);
|
|
6842
7039
|
const snapshot = parseDiscoverySnapshot(loadWorkflowStepOutput(scanned.stateDir, "normalize"));
|
|
6843
7040
|
const declared = applyDocumentationDeclarations(snapshot, documentationInputs(root, snapshot)).snapshot;
|
|
6844
|
-
const report = reconcileKnowledge(snapshot, declared);
|
|
7041
|
+
const report = reconcileKnowledge(snapshot, declared, config.reconciliation?.requiredRelationKinds === void 0 ? {} : { requiredRelationKinds: config.reconciliation.requiredRelationKinds });
|
|
6845
7042
|
return runWorkflow(workflowOptions(root, config, snapshot.sourceRevision, "reconcile", { reconcile: () => report }));
|
|
6846
7043
|
};
|
|
6847
7044
|
var checkWorkflow = (root, config) => {
|
|
@@ -6886,6 +7083,53 @@ var workflowOutput = (result) => {
|
|
|
6886
7083
|
...rules ? { rules } : {}
|
|
6887
7084
|
};
|
|
6888
7085
|
};
|
|
7086
|
+
var writeAtomicFile = (path, content) => {
|
|
7087
|
+
const temporaryPath = `${path}.tmp-${process.pid}`;
|
|
7088
|
+
writeFileSync8(temporaryPath, content, "utf8");
|
|
7089
|
+
renameSync3(temporaryPath, path);
|
|
7090
|
+
};
|
|
7091
|
+
var writeReportArtifact = (htmlPath, artifact2) => {
|
|
7092
|
+
mkdirSync8(dirname10(htmlPath), { recursive: true });
|
|
7093
|
+
if (artifact2.mode === "single-file") {
|
|
7094
|
+
writeAtomicFile(htmlPath, artifact2.indexHtml);
|
|
7095
|
+
const artifactDir2 = htmlPath.replace(/\.html?$/i, "");
|
|
7096
|
+
if (existsSync21(artifactDir2)) rmSync3(artifactDir2, { recursive: true, force: true });
|
|
7097
|
+
return htmlPath;
|
|
7098
|
+
}
|
|
7099
|
+
const artifactDir = htmlPath.replace(/\.html?$/i, "");
|
|
7100
|
+
const temporaryDir = `${artifactDir}.tmp-${process.pid}`;
|
|
7101
|
+
const backupDir = `${artifactDir}.previous-${process.pid}`;
|
|
7102
|
+
const launcherTemp = `${htmlPath}.tmp-${process.pid}`;
|
|
7103
|
+
rmSync3(temporaryDir, { recursive: true, force: true });
|
|
7104
|
+
rmSync3(backupDir, { recursive: true, force: true });
|
|
7105
|
+
mkdirSync8(temporaryDir, { recursive: true });
|
|
7106
|
+
for (const [file, content] of Object.entries(artifact2.files)) {
|
|
7107
|
+
const filePath = resolve18(temporaryDir, file);
|
|
7108
|
+
mkdirSync8(dirname10(filePath), { recursive: true });
|
|
7109
|
+
writeFileSync8(filePath, content, "utf8");
|
|
7110
|
+
}
|
|
7111
|
+
writeFileSync8(resolve18(temporaryDir, "manifest.json"), artifact2.manifest, "utf8");
|
|
7112
|
+
const frameSource = `${relative10(dirname10(htmlPath), artifactDir)}/index.html`;
|
|
7113
|
+
const launcher = `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Doc Bridge report</title></head><body style="margin:0"><iframe title="Doc Bridge report" src="${frameSource}" style="border:0;width:100vw;height:100vh"></iframe></body></html>`;
|
|
7114
|
+
writeFileSync8(launcherTemp, launcher, "utf8");
|
|
7115
|
+
try {
|
|
7116
|
+
if (existsSync21(artifactDir)) renameSync3(artifactDir, backupDir);
|
|
7117
|
+
renameSync3(temporaryDir, artifactDir);
|
|
7118
|
+
renameSync3(launcherTemp, htmlPath);
|
|
7119
|
+
if (existsSync21(backupDir)) rmSync3(backupDir, { recursive: true, force: true });
|
|
7120
|
+
} catch (error) {
|
|
7121
|
+
if (existsSync21(backupDir)) {
|
|
7122
|
+
const failedDir = `${artifactDir}.failed-${process.pid}`;
|
|
7123
|
+
if (existsSync21(artifactDir)) renameSync3(artifactDir, failedDir);
|
|
7124
|
+
renameSync3(backupDir, artifactDir);
|
|
7125
|
+
if (existsSync21(failedDir)) rmSync3(failedDir, { recursive: true, force: true });
|
|
7126
|
+
} else if (existsSync21(artifactDir)) rmSync3(artifactDir, { recursive: true, force: true });
|
|
7127
|
+
if (existsSync21(temporaryDir)) rmSync3(temporaryDir, { recursive: true, force: true });
|
|
7128
|
+
if (existsSync21(launcherTemp)) rmSync3(launcherTemp, { force: true });
|
|
7129
|
+
throw error;
|
|
7130
|
+
}
|
|
7131
|
+
return artifactDir;
|
|
7132
|
+
};
|
|
6889
7133
|
var runWorkflowCommand = (command, flags, configPath, argv) => {
|
|
6890
7134
|
try {
|
|
6891
7135
|
const { config, root } = loadProject(configPath);
|
|
@@ -6898,8 +7142,12 @@ var runWorkflowCommand = (command, flags, configPath, argv) => {
|
|
|
6898
7142
|
const outputPath = optionValues(argv, "--output")[0] ?? ".doc-bridge/report.html";
|
|
6899
7143
|
const htmlPath = resolve18(root, outputPath);
|
|
6900
7144
|
mkdirSync8(dirname10(htmlPath), { recursive: true });
|
|
6901
|
-
|
|
6902
|
-
|
|
7145
|
+
const thresholdValue = optionValues(argv, "--report-threshold")[0];
|
|
7146
|
+
const thresholdBytes = thresholdValue === void 0 ? void 0 : Number(thresholdValue);
|
|
7147
|
+
if (thresholdBytes !== void 0 && (!Number.isSafeInteger(thresholdBytes) || thresholdBytes < 1)) throw new Error("--report-threshold must be a positive integer.");
|
|
7148
|
+
const artifact2 = renderOfflineReportArtifact({ snapshot, report }, { ...thresholdBytes === void 0 ? {} : { thresholdBytes } });
|
|
7149
|
+
output.htmlPath = writeReportArtifact(htmlPath, artifact2);
|
|
7150
|
+
output.htmlMode = artifact2.mode;
|
|
6903
7151
|
}
|
|
6904
7152
|
if (wantsTextOutput(flags, config)) {
|
|
6905
7153
|
writeLines([`Run: ${String(output.runId)}`, `State: ${String(output.state)}`, ...output.snapshotHash ? [`Snapshot: ${String(output.snapshotHash)}`] : [], ...output.reportHash ? [`Report: ${String(output.reportHash)}`] : [], `Artifacts: ${String(output.artifactRefs.length)}`]);
|