@agentskit/doc-bridge 1.6.4 → 1.7.45

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.
Files changed (64) hide show
  1. package/CHANGELOG.md +249 -0
  2. package/CONTRIBUTING.md +6 -4
  3. package/action.yml +1 -1
  4. package/dist/cli/program.js +1139 -294
  5. package/dist/cli/program.js.map +1 -1
  6. package/dist/config/index.d.ts +1 -1
  7. package/dist/config/index.js +43 -5
  8. package/dist/config/index.js.map +1 -1
  9. package/dist/index-BUL0q7s8.d.ts +660 -0
  10. package/dist/index.d.ts +817 -2134
  11. package/dist/index.js +1154 -244
  12. package/dist/index.js.map +1 -1
  13. package/docs/PRD-enterprise-hardening.md +288 -0
  14. package/docs/RELEASE.md +22 -8
  15. package/docs/adr/0001-enterprise-verification-contract.md +35 -0
  16. package/docs/agent-corpus/INDEX.md +2 -2
  17. package/docs/agent-corpus/chat.md +2 -2
  18. package/docs/agent-corpus/cli.md +2 -2
  19. package/docs/agent-corpus/conformance.md +2 -2
  20. package/docs/agent-corpus/doc-bridge.md +1 -1
  21. package/docs/agent-corpus/doctor.md +2 -2
  22. package/docs/agent-corpus/gates.md +2 -2
  23. package/docs/agent-corpus/mcp.md +2 -2
  24. package/docs/agent-corpus/memory.md +2 -2
  25. package/docs/agent-corpus/query.md +2 -2
  26. package/docs/knowledge-engine-runbook.md +30 -2
  27. package/docs/spec/analyzer-plugin-v1.md +24 -0
  28. package/docs/spec/benchmark-v1.md +36 -0
  29. package/docs/spec/config-v1.md +156 -0
  30. package/docs/validation-cycle-plan.md +255 -0
  31. package/docs/verification-harness.md +37 -4
  32. package/mcpb/manifest.json +1 -1
  33. package/package.json +68 -70
  34. package/scripts/check-ecosystem-upstream.mjs +3 -2
  35. package/scripts/report-visual-check.mjs +64 -12
  36. package/scripts/verification-harness.mjs +216 -14
  37. package/skills/doc-bridge-handoff/scripts/resolve-handoff.mjs +1 -1
  38. package/src/agents/registry-adapter.ts +31 -7
  39. package/src/cli/demo.ts +2 -2
  40. package/src/cli/program.ts +59 -16
  41. package/src/config/index.ts +2 -0
  42. package/src/config/load-config.ts +7 -1
  43. package/src/config/schema.ts +60 -2
  44. package/src/conformance/documentation-standard-v1.ts +14 -8
  45. package/src/discovery/documentation.ts +90 -23
  46. package/src/discovery/repository.ts +147 -19
  47. package/src/doctor/run-doctor.ts +2 -15
  48. package/src/federation/llms.ts +72 -20
  49. package/src/fixes/proposals.ts +4 -3
  50. package/src/index-builder/human-adapters/fumadocs.ts +1 -1
  51. package/src/index-builder/watch-index.ts +1 -1
  52. package/src/index.ts +29 -0
  53. package/src/lib/bounded-text.ts +15 -10
  54. package/src/metrics/benchmark.ts +176 -0
  55. package/src/plugins/contract.ts +89 -0
  56. package/src/reconciliation/reconcile.ts +181 -5
  57. package/src/report/html.ts +318 -88
  58. package/src/rules/engine.ts +15 -2
  59. package/src/safety/repository.ts +1 -1
  60. package/src/schemas/knowledge.ts +21 -3
  61. package/src/validate.ts +7 -1
  62. package/src/version.ts +1 -1
  63. package/src/workflow/engine.ts +65 -9
  64. package/dist/index-DudNuwI5.d.ts +0 -2060
package/dist/index.js CHANGED
@@ -198,15 +198,41 @@ var RuleIdSchema = z.enum([
198
198
  var RuleSeveritySchema = z.enum(["off", "info", "warn", "error"]);
199
199
  var RulesConfigSchema = z.object({
200
200
  mode: z.enum(["default", "recommended", "strict"]).optional(),
201
- severity: z.record(RuleIdSchema, RuleSeveritySchema).optional(),
201
+ severity: z.partialRecord(RuleIdSchema, RuleSeveritySchema).optional(),
202
202
  ignore: z.array(RuleIdSchema).max(128).optional(),
203
203
  criticalEntities: z.array(z.string().min(1).max(256)).max(128).optional(),
204
204
  criticalPaths: z.array(z.string().min(1).max(512)).max(128).optional(),
205
- warningThresholds: z.record(RuleIdSchema, z.number().int().min(1).max(1e5)).optional()
205
+ warningThresholds: z.partialRecord(RuleIdSchema, z.number().int().min(1).max(1e5)).optional()
206
206
  }).strict();
207
207
  var ReconciliationConfigSchema = z.object({
208
+ /** Semantic comparison level. Raw discovery always keeps file-level relations. */
209
+ scope: z.enum(["file", "module", "package"]).optional(),
208
210
  /** Relation kinds that must have documentation declarations. Omit to require all observed kinds; [] disables this signal. */
209
- requiredRelationKinds: z.array(z.string().min(1).max(128)).max(128).optional()
211
+ requiredRelationKinds: z.array(z.string().min(1).max(128)).max(128).optional(),
212
+ /** Limit missing-declaration findings to relations whose endpoints are internal project entities. */
213
+ requiredRelationTargets: z.enum(["all", "internal"]).optional(),
214
+ includeOrphanedDocuments: z.boolean().optional()
215
+ }).strict();
216
+ var AnalysisConfigSchema = z.object({
217
+ plugins: z.array(
218
+ z.object({
219
+ id: z.string().regex(/^[a-z][a-z0-9-]*$/).max(128),
220
+ enabled: z.boolean().optional(),
221
+ order: z.number().int().nonnegative().optional(),
222
+ options: z.record(z.string(), z.unknown()).optional(),
223
+ reason: z.string().min(1).max(1024).optional()
224
+ }).strict()
225
+ ).max(128).optional(),
226
+ jsTs: z.object({
227
+ runtimeWiringMethods: z.array(z.string().regex(/^[A-Za-z_$][A-Za-z0-9_$]*$/).max(64)).max(64).optional(),
228
+ runtimeWiringAdapters: z.array(
229
+ z.object({
230
+ id: z.string().min(1).max(128),
231
+ methods: z.array(z.string().regex(/^[A-Za-z_$][A-Za-z0-9_$]*$/).max(64)).min(1).max(64)
232
+ }).strict()
233
+ ).max(32).optional(),
234
+ includeTestRuntimeWiring: z.boolean().optional()
235
+ }).strict().optional()
210
236
  }).strict();
211
237
  var WorkflowConfigSchema = z.object({
212
238
  stateDir: z.string().min(1).max(512).optional()
@@ -219,6 +245,10 @@ var RepositorySafetyConfigSchema = z.object({
219
245
  maxMemoryMb: z.number().int().positive().max(1048576).optional(),
220
246
  redactSecrets: z.boolean().optional()
221
247
  }).strict();
248
+ var ReportConfigSchema = z.object({
249
+ /** Public report privacy mode. Private is the default; anonymized preserves topology without project identity. */
250
+ privacy: z.enum(["private", "anonymized"]).optional()
251
+ }).strict();
222
252
  var SurfacesConfigSchema = z.object({
223
253
  cli: z.object({
224
254
  bin: z.string().min(1).max(64).optional(),
@@ -290,7 +320,12 @@ var IntelligenceConfigSchema = z.object({
290
320
  enabled: z.boolean().optional(),
291
321
  agentId: z.string().min(1).max(256).optional(),
292
322
  agentRoot: z.string().min(1).max(512).optional(),
293
- runnerModule: z.string().min(1).max(512).optional()
323
+ runnerModule: z.string().min(1).max(512).optional(),
324
+ deterministic: z.boolean().optional(),
325
+ timeoutMs: z.number().int().positive().max(6e5).optional(),
326
+ maxTokens: z.number().int().positive().max(1e6).optional(),
327
+ maxResponseBytes: z.number().int().positive().max(1e7).optional(),
328
+ maxConcurrency: z.number().int().positive().max(64).optional()
294
329
  }).strict().optional()
295
330
  }).strict();
296
331
  var FederationConfigSchema = z.object({
@@ -382,9 +417,11 @@ var DocBridgeConfigV1Schema = z.object({
382
417
  routing: RoutingConfigSchema.optional(),
383
418
  gates: GatesConfigSchema.optional(),
384
419
  reconciliation: ReconciliationConfigSchema.optional(),
420
+ analysis: AnalysisConfigSchema.optional(),
385
421
  rules: RulesConfigSchema.optional(),
386
422
  workflow: WorkflowConfigSchema.optional(),
387
423
  safety: RepositorySafetyConfigSchema.optional(),
424
+ report: ReportConfigSchema.optional(),
388
425
  surfaces: SurfacesConfigSchema.optional(),
389
426
  intelligence: IntelligenceConfigSchema.optional(),
390
427
  federation: FederationConfigSchema.optional(),
@@ -719,7 +756,7 @@ var parseConfig = (input) => {
719
756
  throw new Error(
720
757
  `Invalid doc-bridge config:
721
758
  ${result.error.issues.map(
722
- (issue) => ` - ${issue.path.join(".") || "(root)"}: ${issue.message}`
759
+ (issue) => ` - ${issue.path.join(".") || "(root)"}: ${issue.code === "invalid_type" && issue.message.endsWith("received undefined") ? "Required" : issue.code === "invalid_value" && "values" in issue ? "Invalid enum value" : issue.message}`
723
760
  ).join("\n")}`
724
761
  );
725
762
  };
@@ -1110,9 +1147,10 @@ var EvidenceSchema = z5.object({
1110
1147
  });
1111
1148
  }
1112
1149
  });
1113
- var CoverageStatusSchema = z5.enum(["complete", "partial", "not-analyzed"]);
1150
+ var CoverageStatusSchema = z5.enum(["complete", "partial", "not-analyzed", "not-applicable"]);
1114
1151
  var CoverageSchema = z5.object({
1115
1152
  analyzer: boundedString(128),
1153
+ analyzerVersion: boundedString(64).optional(),
1116
1154
  scope: boundedString(512),
1117
1155
  status: CoverageStatusSchema,
1118
1156
  reason: z5.string().max(1024).optional(),
@@ -1141,7 +1179,7 @@ var EntitySchema = z5.object({
1141
1179
  aliases: z5.array(boundedString(256)).max(32).optional(),
1142
1180
  provenance: ProvenanceSchema,
1143
1181
  evidence: z5.array(EvidenceSchema).max(64),
1144
- metadata: z5.record(z5.unknown()).optional()
1182
+ metadata: z5.record(z5.string(), z5.unknown()).optional()
1145
1183
  }).strict();
1146
1184
  var RelationSchema = z5.object({
1147
1185
  id: boundedString(256),
@@ -1151,7 +1189,7 @@ var RelationSchema = z5.object({
1151
1189
  discriminator: boundedString(256).optional(),
1152
1190
  provenance: ProvenanceSchema,
1153
1191
  evidence: z5.array(EvidenceSchema).max(64),
1154
- metadata: z5.record(z5.unknown()).optional()
1192
+ metadata: z5.record(z5.string(), z5.unknown()).optional()
1155
1193
  }).strict();
1156
1194
  var DiscoverySnapshotV1Schema = z5.object({
1157
1195
  type: z5.literal("discovery-snapshot"),
@@ -1195,7 +1233,24 @@ var ReconciliationReportV1Schema = z5.object({
1195
1233
  entityCount: z5.number().int().nonnegative(),
1196
1234
  relationCount: z5.number().int().nonnegative(),
1197
1235
  diagnosticCount: z5.number().int().nonnegative(),
1198
- requiredRelationKinds: z5.array(boundedString(128)).max(128).optional()
1236
+ scope: z5.enum(["file", "module", "package"]).optional(),
1237
+ requiredRelationKinds: z5.array(boundedString(128)).max(128).optional(),
1238
+ requiredRelationTargets: z5.enum(["all", "internal"]).optional(),
1239
+ diagnosticsByCode: z5.record(z5.string().max(128), z5.number().int().nonnegative()).optional(),
1240
+ diagnosticsByStatus: z5.record(z5.string().max(128), z5.number().int().nonnegative()).optional(),
1241
+ documentation: z5.object({
1242
+ documentCount: z5.number().int().nonnegative(),
1243
+ documentedDocumentCount: z5.number().int().nonnegative(),
1244
+ documentClassificationCounts: z5.record(z5.string().max(128), z5.number().int().nonnegative()),
1245
+ documentedDocumentClassificationCounts: z5.record(z5.string().max(128), z5.number().int().nonnegative()),
1246
+ packageCount: z5.number().int().nonnegative(),
1247
+ packageStatus: z5.object({
1248
+ fresh: z5.number().int().nonnegative(),
1249
+ stale: z5.number().int().nonnegative(),
1250
+ missing: z5.number().int().nonnegative(),
1251
+ unverified: z5.number().int().nonnegative()
1252
+ }).strict()
1253
+ }).strict().optional()
1199
1254
  }).strict()
1200
1255
  }).strict();
1201
1256
  var WorkflowStateSchema = z5.enum([
@@ -1298,9 +1353,14 @@ var FixProposalV1Schema = z5.object({
1298
1353
  });
1299
1354
 
1300
1355
  // src/validate.ts
1356
+ var zodMessage = (issue) => {
1357
+ if (issue.code === "invalid_type" && issue.message.endsWith("received undefined")) return "Required";
1358
+ if (issue.code === "invalid_value" && "values" in issue) return "Invalid enum value";
1359
+ return issue.message;
1360
+ };
1301
1361
  var zodIssues = (error) => error.issues.map((issue) => ({
1302
1362
  path: issue.path.join(".") || "(root)",
1303
- message: issue.message
1363
+ message: zodMessage(issue)
1304
1364
  }));
1305
1365
  var safeParseAgentHandoff = (input) => {
1306
1366
  const legacy = AgentHandoffLegacySchema.safeParse(input);
@@ -1340,7 +1400,7 @@ ${zodIssues(result.error).map((i) => ` - ${i.path}: ${i.message}`).join("\n")}`
1340
1400
 
1341
1401
  // src/index-builder/build-index.ts
1342
1402
  import { mkdirSync, readFileSync as readFileSync7, writeFileSync } from "fs";
1343
- import { dirname as dirname4, join as join7 } from "path";
1403
+ import { dirname as dirname3, join as join7 } from "path";
1344
1404
 
1345
1405
  // src/lib/paths.ts
1346
1406
  import { existsSync as existsSync2, realpathSync } from "fs";
@@ -1424,22 +1484,27 @@ var defaultChecksForTarget = (root, opts) => {
1424
1484
  import { minimatch } from "minimatch";
1425
1485
 
1426
1486
  // src/lib/bounded-text.ts
1427
- import { readFileSync as readFileSync3, statSync } from "fs";
1487
+ import { closeSync, fstatSync, openSync, readFileSync as readFileSync3 } from "fs";
1428
1488
  var MAX_DOCUMENT_BYTES = 4 * 1024 * 1024;
1429
1489
  var MAX_CORPUS_BYTES = 64 * 1024 * 1024;
1430
1490
  var readBoundedText = (path, budget, limits) => {
1431
1491
  const maxFileBytes = limits?.maxFileBytes ?? MAX_DOCUMENT_BYTES;
1432
1492
  const maxCorpusBytes = limits?.maxCorpusBytes ?? MAX_CORPUS_BYTES;
1433
- const stat = statSync(path);
1434
- if (!stat.isFile()) throw new Error(`Documentation path is not a regular file: ${path}`);
1435
- if (stat.size > maxFileBytes) {
1436
- throw new Error(`Documentation file exceeds the ${maxFileBytes} byte limit: ${path}`);
1437
- }
1438
- if (budget.used + stat.size > maxCorpusBytes) {
1439
- throw new Error(`Documentation corpus exceeds the ${maxCorpusBytes} byte read budget.`);
1493
+ const fd = openSync(path, "r");
1494
+ try {
1495
+ const stat = fstatSync(fd);
1496
+ if (!stat.isFile()) throw new Error(`Documentation path is not a regular file: ${path}`);
1497
+ if (stat.size > maxFileBytes) {
1498
+ throw new Error(`Documentation file exceeds the ${maxFileBytes} byte limit: ${path}`);
1499
+ }
1500
+ if (budget.used + stat.size > maxCorpusBytes) {
1501
+ throw new Error(`Documentation corpus exceeds the ${maxCorpusBytes} byte read budget.`);
1502
+ }
1503
+ budget.used += stat.size;
1504
+ return readFileSync3(fd, "utf8");
1505
+ } finally {
1506
+ closeSync(fd);
1440
1507
  }
1441
- budget.used += stat.size;
1442
- return readFileSync3(path, "utf8");
1443
1508
  };
1444
1509
 
1445
1510
  // src/lib/markdown.ts
@@ -1863,7 +1928,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
1863
1928
  };
1864
1929
 
1865
1930
  // src/version.ts
1866
- var PACKAGE_VERSION = "1.6.4";
1931
+ var PACKAGE_VERSION = "1.7.45";
1867
1932
 
1868
1933
  // src/index-builder/capabilities.ts
1869
1934
  var renderCapabilitiesJson = (config, index, paths) => {
@@ -1920,9 +1985,9 @@ var contentHashForArtifactV1 = (artifact2) => {
1920
1985
 
1921
1986
  // src/index-builder/llms-txt.ts
1922
1987
  var routePath = (path, pathPrefix) => {
1923
- const normalizedPath = path.replaceAll("\\", "/").replace(/\.(?:md|mdx)$/, "");
1988
+ const normalizedPath2 = path.replaceAll("\\", "/").replace(/\.(?:md|mdx)$/, "");
1924
1989
  const normalizedPrefix = pathPrefix?.replaceAll("\\", "/").replace(/\/$/, "");
1925
- const relativePath3 = normalizedPrefix && normalizedPath.startsWith(`${normalizedPrefix}/`) ? normalizedPath.slice(normalizedPrefix.length + 1) : normalizedPath;
1990
+ const relativePath3 = normalizedPrefix && normalizedPath2.startsWith(`${normalizedPrefix}/`) ? normalizedPath2.slice(normalizedPrefix.length + 1) : normalizedPath2;
1926
1991
  return relativePath3.replace(/^\/+/, "");
1927
1992
  };
1928
1993
  var knowledgeUrl = (path, options = {}) => {
@@ -2294,7 +2359,7 @@ var scanHumanDocs = (root, config) => Object.fromEntries(scanHumanDocRecords(roo
2294
2359
 
2295
2360
  // src/index-builder/plugins/nx.ts
2296
2361
  import { existsSync as existsSync6, lstatSync as lstatSync2, readFileSync as readFileSync5, realpathSync as realpathSync5 } from "fs";
2297
- import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute3, relative as relative3, resolve as resolve5, sep as sep4 } from "path";
2362
+ import { basename as basename2, dirname as dirname2, isAbsolute as isAbsolute3, relative as relative3, resolve as resolve5, sep as sep4 } from "path";
2298
2363
  var NX_SCAN_SKIP = /* @__PURE__ */ new Set([
2299
2364
  "node_modules",
2300
2365
  ".git",
@@ -2358,7 +2423,7 @@ var discoverNxProjects = (root, config) => {
2358
2423
  for (const manifest of manifests) {
2359
2424
  const json = readJsonRecord(manifest);
2360
2425
  if (!json) continue;
2361
- const manifestDir = dirname3(manifest);
2426
+ const manifestDir = dirname2(manifest);
2362
2427
  const manifestName = basename2(manifest);
2363
2428
  const isProjectJson = manifestName === "project.json";
2364
2429
  if (!isProjectJson && manifestName !== "package.json") continue;
@@ -2406,7 +2471,7 @@ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
2406
2471
  import { basename as basename3, join as join6 } from "path";
2407
2472
 
2408
2473
  // src/lib/glob-expand.ts
2409
- import { existsSync as existsSync7, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
2474
+ import { existsSync as existsSync7, readdirSync as readdirSync2, statSync } from "fs";
2410
2475
  import { join as join5 } from "path";
2411
2476
  var expandWorkspaceGlobs = (root, patterns) => {
2412
2477
  const dirs = /* @__PURE__ */ new Set();
@@ -2423,7 +2488,7 @@ var expandWorkspaceGlobs = (root, patterns) => {
2423
2488
  for (const name of readdirSync2(base)) {
2424
2489
  const abs = join5(base, name);
2425
2490
  try {
2426
- if (statSync2(abs).isDirectory()) dirs.add(toPosix(abs));
2491
+ if (statSync(abs).isDirectory()) dirs.add(toPosix(abs));
2427
2492
  } catch {
2428
2493
  }
2429
2494
  }
@@ -2533,7 +2598,7 @@ var buildDocBridgeIndex = (opts) => {
2533
2598
  lookup
2534
2599
  };
2535
2600
  if (write) {
2536
- mkdirSync(dirname4(indexPath), { recursive: true });
2601
+ mkdirSync(dirname3(indexPath), { recursive: true });
2537
2602
  writeFileSync(indexPath, `${JSON.stringify(index, null, 2)}
2538
2603
  `, "utf8");
2539
2604
  }
@@ -2556,7 +2621,7 @@ var buildDocBridgeIndex = (opts) => {
2556
2621
  const capabilitiesOut = config.index?.capabilities?.outFile ?? ".doc-bridge/capabilities.json";
2557
2622
  capabilitiesPath = join7(root, capabilitiesOut);
2558
2623
  if (write) {
2559
- mkdirSync(dirname4(capabilitiesPath), { recursive: true });
2624
+ mkdirSync(dirname3(capabilitiesPath), { recursive: true });
2560
2625
  writeFileSync(
2561
2626
  capabilitiesPath,
2562
2627
  renderCapabilitiesJson(config, index, {
@@ -2578,14 +2643,14 @@ var buildDocBridgeIndex = (opts) => {
2578
2643
  // src/discovery/repository.ts
2579
2644
  import { execFileSync } from "child_process";
2580
2645
  import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
2581
- import { basename as basename4, dirname as dirname5, extname, join as join8, relative as relative5, resolve as resolve7, sep as sep6 } from "path";
2646
+ import { basename as basename4, dirname as dirname4, extname, join as join8, relative as relative5, resolve as resolve7, sep as sep6 } from "path";
2582
2647
  import * as ts from "typescript";
2583
2648
 
2584
2649
  // src/safety/repository.ts
2585
- import { lstatSync as lstatSync3, readdirSync as readdirSync3, realpathSync as realpathSync6, statSync as statSync3 } from "fs";
2650
+ import { lstatSync as lstatSync3, readdirSync as readdirSync3, realpathSync as realpathSync6, statSync as statSync2 } from "fs";
2586
2651
  import { isAbsolute as isAbsolute4, relative as relative4, resolve as resolve6, sep as sep5 } from "path";
2587
2652
  import { minimatch as minimatch4 } from "minimatch";
2588
- var DEFAULT_SAFETY_EXCLUDES = ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/build/**", "**/coverage/**", "**/.doc-bridge/**", "**/.turbo/**", "**/.env", "**/.env.*", "**/*secret*", "**/*credential*", "**/*.pem", "**/*.key"];
2653
+ var DEFAULT_SAFETY_EXCLUDES = ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/build/**", "**/coverage/**", "**/.doc-bridge/**", "**/.next/**", "**/out/**", "**/.turbo/**", "**/.svelte-kit/**", "**/.mcpb-build/**", "**/.mcpb-output/**", "**/.env", "**/.env.*", "**/*secret*", "**/*credential*", "**/*.pem", "**/*.key"];
2589
2654
  var containedPath = (root, candidate) => {
2590
2655
  const projectRoot = realpathSync6.native(resolve6(root));
2591
2656
  const unresolved = resolve6(projectRoot, candidate);
@@ -2645,7 +2710,7 @@ var safeWalkFiles = (root, options = {}) => {
2645
2710
  reason = `Repository scan exceeded the ${options.maxFiles ?? 1e4} file limit.`;
2646
2711
  return;
2647
2712
  }
2648
- bytes += statSync3(absolute).size;
2713
+ bytes += statSync2(absolute).size;
2649
2714
  if (options.maxBytes !== void 0 && bytes > options.maxBytes) {
2650
2715
  reason = `Repository scan exceeded the ${options.maxBytes} byte limit.`;
2651
2716
  return;
@@ -2670,6 +2735,8 @@ var SOURCE_EXTENSIONS = [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mts", "
2670
2735
  var DOCUMENT_EXTENSIONS = [".md", ".mdx"];
2671
2736
  var DEFAULT_MAX_FILES2 = 1e4;
2672
2737
  var EMPTY_HASH = "0".repeat(64);
2738
+ var DEFAULT_RUNTIME_WIRING_METHODS = ["register", "use", "mount", "attach"];
2739
+ var TEST_MODULE_PATTERN = /(?:\.test|\.spec|__tests__)/;
2673
2740
  var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2674
2741
  var readJson = (path) => {
2675
2742
  try {
@@ -2698,6 +2765,13 @@ var firstLineContaining = (text, pattern) => {
2698
2765
  const line = text.split(/\r?\n/).findIndex((value) => value.includes(pattern));
2699
2766
  return line >= 0 ? line + 1 : void 0;
2700
2767
  };
2768
+ var documentClassification = (path) => {
2769
+ if (/(^|\/)docs\/for-agents(?:\/|$)/.test(path)) return "agent";
2770
+ if (/(^|\/)docs-archive(?:\/|$)/.test(path)) return "archive";
2771
+ if (/(^|\/)docs(?:\/|$)/.test(path)) return "human";
2772
+ if (/(^|\/)(README|CONTRIBUTING|SECURITY|CHANGELOG)(?:\.|$)/i.test(path)) return "project";
2773
+ return "unclassified";
2774
+ };
2701
2775
  var packageName = (manifest, fallback) => typeof manifest.name === "string" && manifest.name.length > 0 ? manifest.name : fallback || void 0;
2702
2776
  var workspacePatterns = (root, rootManifest) => {
2703
2777
  const fromPackageJson = rootManifest?.workspaces;
@@ -2772,7 +2846,7 @@ var readCompilerOptions = (root) => {
2772
2846
  if (!configPath) return { options: {} };
2773
2847
  const parsed = ts.readConfigFile(configPath, ts.sys.readFile);
2774
2848
  if (parsed.error) return { options: {}, error: ts.flattenDiagnosticMessageText(parsed.error.messageText, "\n") };
2775
- const config = ts.parseJsonConfigFileContent(parsed.config, ts.sys, dirname5(configPath));
2849
+ const config = ts.parseJsonConfigFileContent(parsed.config, ts.sys, dirname4(configPath));
2776
2850
  if (config.errors.length) {
2777
2851
  return {
2778
2852
  options: config.options,
@@ -2839,29 +2913,106 @@ var exportedNames = (sourceFile) => {
2839
2913
  visit(sourceFile);
2840
2914
  return [...names].sort();
2841
2915
  };
2842
- var moduleReferences = (root, path, sourceFile) => {
2916
+ var moduleReferences = (root, path, sourceFile, runtimeWiringMethods) => {
2843
2917
  const references = [];
2918
+ const dynamicEvidence = [];
2844
2919
  let hasDynamic = false;
2920
+ let hasLiteralDynamic = false;
2845
2921
  let hasRuntimeWiring = false;
2846
- const addReference = (specifier, kind, node) => {
2847
- references.push({ specifier: specifier.text, kind, evidence: nodeEvidence(root, path, sourceFile, node) });
2922
+ let hasUnresolvedRuntimeWiring = false;
2923
+ const importedBindings = /* @__PURE__ */ new Map();
2924
+ const staticStringBindings = /* @__PURE__ */ new Map();
2925
+ const localBindings = /* @__PURE__ */ new Set();
2926
+ const resolveStaticString = (expression) => {
2927
+ if (ts.isStringLiteralLike(expression)) return expression.text;
2928
+ if (ts.isIdentifier(expression)) return staticStringBindings.get(expression.text);
2929
+ if (ts.isParenthesizedExpression(expression)) return resolveStaticString(expression.expression);
2930
+ if (ts.isBinaryExpression(expression) && expression.operatorToken.kind === ts.SyntaxKind.PlusToken) {
2931
+ const left = resolveStaticString(expression.left);
2932
+ const right = resolveStaticString(expression.right);
2933
+ return left !== void 0 && right !== void 0 ? left + right : void 0;
2934
+ }
2935
+ return void 0;
2936
+ };
2937
+ const collectStaticStringBindings = (node) => {
2938
+ if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && ts.isVariableDeclarationList(node.parent) && (node.parent.flags & ts.NodeFlags.Const) !== 0) {
2939
+ const value = resolveStaticString(node.initializer);
2940
+ const previous = staticStringBindings.get(node.name.text);
2941
+ staticStringBindings.set(node.name.text, !staticStringBindings.has(node.name.text) || previous === value ? value : void 0);
2942
+ }
2943
+ if ((ts.isVariableDeclaration(node) || ts.isParameter(node) || ts.isBindingElement(node)) && ts.isIdentifier(node.name)) localBindings.add(node.name.text);
2944
+ if ((ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || ts.isEnumDeclaration(node)) && node.name) localBindings.add(node.name.text);
2945
+ ts.forEachChild(node, collectStaticStringBindings);
2946
+ };
2947
+ collectStaticStringBindings(sourceFile);
2948
+ const addImportedBindingReference = (expression, node) => {
2949
+ if (ts.isIdentifier(expression)) {
2950
+ const specifier = importedBindings.get(expression.text);
2951
+ if (specifier) {
2952
+ addReference({ text: specifier }, "runtime-wiring", node, "runtime-wiring-static");
2953
+ return true;
2954
+ }
2955
+ return false;
2956
+ }
2957
+ if (ts.isPropertyAccessExpression(expression)) return addImportedBindingReference(expression.expression, node);
2958
+ if (ts.isCallExpression(expression)) return addImportedBindingReference(expression.expression, node);
2959
+ return false;
2960
+ };
2961
+ const isKnownLocal = (expression) => {
2962
+ if (ts.isIdentifier(expression)) return localBindings.has(expression.text) || importedBindings.has(expression.text);
2963
+ if (expression.kind === ts.SyntaxKind.ThisKeyword) return true;
2964
+ if (ts.isPropertyAccessExpression(expression)) return isKnownLocal(expression.expression);
2965
+ if (ts.isCallExpression(expression)) return isKnownLocal(expression.expression);
2966
+ return ts.isStringLiteralLike(expression);
2967
+ };
2968
+ const addReference = (specifier, kind, node, detection) => {
2969
+ references.push({ specifier: specifier.text, kind, evidence: nodeEvidence(root, path, sourceFile, node), ...detection ? { detection } : {} });
2848
2970
  };
2849
2971
  const visit = (node) => {
2850
2972
  if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
2851
2973
  addReference(node.moduleSpecifier, "imports", node);
2974
+ const clause = node.importClause;
2975
+ if (clause?.name) importedBindings.set(clause.name.text, node.moduleSpecifier.text);
2976
+ if (clause?.namedBindings && ts.isNamespaceImport(clause.namedBindings)) importedBindings.set(clause.namedBindings.name.text, node.moduleSpecifier.text);
2977
+ if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
2978
+ for (const element of clause.namedBindings.elements) importedBindings.set((element.name ?? element.propertyName)?.text ?? "", node.moduleSpecifier.text);
2979
+ }
2852
2980
  } else if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
2853
2981
  addReference(node.moduleSpecifier, "re-exports", node);
2854
2982
  } else if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference) && ts.isStringLiteral(node.moduleReference.expression)) {
2855
2983
  addReference(node.moduleReference.expression, "imports", node);
2984
+ importedBindings.set(node.name.text, node.moduleReference.expression.text);
2856
2985
  } else if (ts.isCallExpression(node)) {
2857
2986
  if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
2858
- if (!node.arguments[0] || !ts.isStringLiteralLike(node.arguments[0])) hasDynamic = true;
2987
+ const specifier = node.arguments[0] ? resolveStaticString(node.arguments[0]) : void 0;
2988
+ if (specifier !== void 0) {
2989
+ hasLiteralDynamic = true;
2990
+ dynamicEvidence.push(nodeEvidence(root, path, sourceFile, node));
2991
+ addReference({ text: specifier }, "imports", node, "dynamic-literal");
2992
+ } else {
2993
+ hasDynamic = true;
2994
+ dynamicEvidence.push(nodeEvidence(root, path, sourceFile, node));
2995
+ }
2859
2996
  } else if (ts.isIdentifier(node.expression) && node.expression.text === "require") {
2860
2997
  const argument = node.arguments[0];
2861
- if (argument && ts.isStringLiteralLike(argument)) addReference(argument, "imports", node);
2862
- else hasDynamic = true;
2863
- } else if (ts.isPropertyAccessExpression(node.expression) && node.expression.name.text === "register") {
2998
+ const specifier = argument ? resolveStaticString(argument) : void 0;
2999
+ if (specifier !== void 0) {
3000
+ dynamicEvidence.push(nodeEvidence(root, path, sourceFile, node));
3001
+ addReference({ text: specifier }, "imports", node);
3002
+ } else {
3003
+ hasDynamic = true;
3004
+ dynamicEvidence.push(nodeEvidence(root, path, sourceFile, node));
3005
+ }
3006
+ } else if (ts.isPropertyAccessExpression(node.expression) && runtimeWiringMethods.has(node.expression.name.text)) {
2864
3007
  hasRuntimeWiring = true;
3008
+ let hasUnresolvedTarget = false;
3009
+ for (const argument of node.arguments) {
3010
+ if (ts.isStringLiteralLike(argument)) continue;
3011
+ if (addImportedBindingReference(argument, node)) continue;
3012
+ if (!isKnownLocal(argument)) hasUnresolvedTarget = true;
3013
+ }
3014
+ const receiver = node.expression.expression;
3015
+ if (hasUnresolvedTarget && !isKnownLocal(receiver)) hasUnresolvedRuntimeWiring = true;
2865
3016
  }
2866
3017
  }
2867
3018
  ts.forEachChild(node, visit);
@@ -2870,12 +3021,15 @@ var moduleReferences = (root, path, sourceFile) => {
2870
3021
  return {
2871
3022
  references,
2872
3023
  exports: exportedNames(sourceFile),
3024
+ dynamicEvidence,
2873
3025
  hasDynamic,
2874
- hasRuntimeWiring
3026
+ hasLiteralDynamic,
3027
+ hasRuntimeWiring,
3028
+ hasUnresolvedRuntimeWiring
2875
3029
  };
2876
3030
  };
2877
3031
  var resolveRelativeModule = (specifier, containingFile, modulePaths) => {
2878
- const base = resolve7(dirname5(containingFile), specifier);
3032
+ const base = resolve7(dirname4(containingFile), specifier);
2879
3033
  const extension = extname(base);
2880
3034
  const extensionlessBase = extension ? base.slice(0, -extension.length) : base;
2881
3035
  const candidates = [
@@ -2944,11 +3098,11 @@ var artifact = (root, config, files, entities, relations, coverage) => {
2944
3098
  sourceRevision: revision.value,
2945
3099
  sourceRevisionKind: revision.kind,
2946
3100
  configurationHash: sha256NormalizedV1(config ?? {}),
2947
- pipelineVersion: "1.0.0",
2948
- analyzerVersions: { repository: "1.0.0", "js-ts": "1.0.0" },
3101
+ pipelineVersion: "1.1.8",
3102
+ analyzerVersions: { repository: "1.1.1", "js-ts": "1.3.4" },
2949
3103
  entities: [...entities].sort((a, b) => a.id.localeCompare(b.id)),
2950
3104
  relations: [...relations].sort((a, b) => a.id.localeCompare(b.id)),
2951
- coverage: [...coverage]
3105
+ coverage: coverage.map((entry) => ({ ...entry, analyzerVersion: entry.analyzerVersion ?? ({ repository: "1.1.1", "js-ts": "1.3.4" }[entry.analyzer] ?? "1.0.0") }))
2952
3106
  };
2953
3107
  return DiscoverySnapshotV1Schema.parse({ ...base, contentHash: contentHashForArtifactV1(base) });
2954
3108
  };
@@ -3008,12 +3162,12 @@ var discoverRepository = (opts = {}) => {
3008
3162
  const sourceFile = ts.createSourceFile(absPath, text, ts.ScriptTarget.Latest, true, scriptKind(absPath));
3009
3163
  const exports = exportedNames(sourceFile);
3010
3164
  modules.set(resolve7(absPath), { absPath, path, entityId: id, ...pkg ? { packageId: pkg.id } : {} });
3011
- addEntity({ id, kind: "module", name: basename4(absPath), path, provenance: "observed", evidence: [lineEvidence("code", root, absPath, 1, sourceFile.getLineAndCharacterOfPosition(sourceFile.getEnd()).line + 1)], ...exports.length ? { metadata: { exports, test: /(?:\.test|\.spec|__tests__)/.test(path) } } : {} });
3165
+ addEntity({ id, kind: "module", name: basename4(absPath), path, provenance: "observed", evidence: [lineEvidence("code", root, absPath, 1, sourceFile.getLineAndCharacterOfPosition(sourceFile.getEnd()).line + 1)], ...exports.length ? { metadata: { exports, test: TEST_MODULE_PATTERN.test(path) } } : {} });
3012
3166
  if (pkg) addRelation({ id: entityId("relation", `${pkg.id}:contains:${id}`), kind: "contains", from: pkg.id, to: id, provenance: "observed", evidence: [lineEvidence("code", root, absPath, 1)] });
3013
3167
  }
3014
3168
  for (const absPath of documentPaths) {
3015
3169
  const path = relativePath(root, absPath);
3016
- addEntity({ id: entityId("document", path), kind: "document", name: basename4(absPath), path, provenance: "observed", evidence: [lineEvidence("documentation", root, absPath, 1)] });
3170
+ addEntity({ id: entityId("document", path), kind: "document", name: basename4(absPath), path, provenance: "observed", evidence: [lineEvidence("documentation", root, absPath, 1)], metadata: { classification: documentClassification(path) } });
3017
3171
  }
3018
3172
  const compiler = readCompilerOptions(root);
3019
3173
  const coverage = [
@@ -3021,8 +3175,8 @@ var discoverRepository = (opts = {}) => {
3021
3175
  { analyzer: "repository", scope: "package-manager", status: hasPackageManagerMetadata(root, rootManifest) ? "complete" : "partial", ...!hasPackageManagerMetadata(root, rootManifest) ? { reason: `No package manager metadata found; default helper would fall back to ${detectPackageManager(root)}.` } : {} },
3022
3176
  { analyzer: "repository", scope: "workspace-packages", status: packageResult.coverage.some((item) => item.status === "partial") ? "partial" : "complete", ...packageResult.coverage.find((item) => item.reason)?.reason ? { reason: packageResult.coverage.find((item) => item.reason)?.reason } : {} },
3023
3177
  { analyzer: "js-ts", scope: "static-imports-and-exports", status: compiler.error ? "partial" : "complete", ...compiler.error ? { reason: compiler.error } : {} },
3024
- { analyzer: "js-ts", scope: "dynamic-imports", status: "not-analyzed", reason: "Dynamic import expressions and non-literal require calls are not resolved." },
3025
- { analyzer: "js-ts", scope: "runtime-wiring", status: "not-analyzed", reason: "Reflection, dependency injection and runtime wiring are not inferred." },
3178
+ { analyzer: "js-ts", scope: "dynamic-imports", status: "not-applicable", reason: "No dynamic loading expression was observed." },
3179
+ { analyzer: "js-ts", scope: "runtime-wiring", status: "not-applicable", reason: "No configured runtime-wiring call was observed." },
3026
3180
  { analyzer: "js-ts", scope: "generated-code", status: "not-analyzed", reason: "Generated code is not interpreted as source architecture." }
3027
3181
  ];
3028
3182
  for (const pkg of packageResult.packages) {
@@ -3033,10 +3187,28 @@ var discoverRepository = (opts = {}) => {
3033
3187
  addRelation({ id: entityId("relation", `${pkg.id}:depends-on:${target}:${dependency.type}`), kind: "depends-on", from: pkg.id, to: target, provenance: "observed", evidence: [lineEvidence("configuration", root, pkg.manifestPath, firstLineContaining(text, `"${dependency.name}"`))], metadata: { dependencyType: dependency.type } });
3034
3188
  }
3035
3189
  }
3190
+ const dynamicCoverageIndex = coverage.findIndex((entry) => entry.scope === "dynamic-imports");
3191
+ const configuredRuntimeWiringMethods = /* @__PURE__ */ new Set([
3192
+ ...opts.config?.analysis?.jsTs?.runtimeWiringMethods ?? [],
3193
+ ...opts.config?.analysis?.jsTs?.runtimeWiringAdapters?.flatMap((adapter) => adapter.methods) ?? []
3194
+ ]);
3195
+ if (!configuredRuntimeWiringMethods.size) for (const method of DEFAULT_RUNTIME_WIRING_METHODS) configuredRuntimeWiringMethods.add(method);
3196
+ const includeTestRuntimeWiring = opts.config?.analysis?.jsTs?.includeTestRuntimeWiring ?? false;
3197
+ let observedLiteralDynamic = false;
3198
+ let observedUnresolvedDynamic = false;
3199
+ const observedDynamicEvidence = [];
3200
+ let observedRuntimeWiring = false;
3201
+ let observedUnresolvedRuntimeWiring = false;
3036
3202
  for (const module of modules.values()) {
3037
3203
  const text = readFileSync8(module.absPath, "utf8");
3038
3204
  const sourceFile = ts.createSourceFile(module.absPath, text, ts.ScriptTarget.Latest, true, scriptKind(module.absPath));
3039
- const references = moduleReferences(root, module.absPath, sourceFile);
3205
+ const runtimeWiringMethods = includeTestRuntimeWiring || !TEST_MODULE_PATTERN.test(module.path) ? configuredRuntimeWiringMethods : /* @__PURE__ */ new Set();
3206
+ const references = moduleReferences(root, module.absPath, sourceFile, runtimeWiringMethods);
3207
+ observedLiteralDynamic ||= references.hasLiteralDynamic;
3208
+ observedUnresolvedDynamic ||= references.hasDynamic;
3209
+ observedDynamicEvidence.push(...references.dynamicEvidence);
3210
+ observedRuntimeWiring ||= references.hasRuntimeWiring;
3211
+ observedUnresolvedRuntimeWiring ||= references.hasUnresolvedRuntimeWiring;
3040
3212
  for (const reference of references.references) {
3041
3213
  const target = resolveReference(reference, module.absPath, modules, packageResult.packages, compiler.options);
3042
3214
  if (!target) continue;
@@ -3044,11 +3216,14 @@ var discoverRepository = (opts = {}) => {
3044
3216
  const externalName = target.targetId.replace(/^external:/, "");
3045
3217
  addEntity({ id: target.targetId, kind: "external", name: externalName, provenance: "observed", evidence: [reference.evidence] });
3046
3218
  }
3047
- addRelation({ id: entityId("relation", `${module.entityId}:${reference.kind}:${target.targetId}`), kind: reference.kind, from: module.entityId, to: target.targetId, provenance: "observed", evidence: [reference.evidence] });
3219
+ addRelation({ id: entityId("relation", `${module.entityId}:${reference.kind}:${target.targetId}`), kind: reference.kind, from: module.entityId, to: target.targetId, provenance: "observed", evidence: [reference.evidence], ...reference.detection ? { metadata: { detection: reference.detection } } : {} });
3048
3220
  }
3049
- if (references.hasDynamic) coverage.push({ analyzer: "js-ts", scope: `dynamic-imports:${module.path}`, status: "not-analyzed", reason: "A dynamic import or non-literal require was found.", evidence: [lineEvidence("code", root, module.absPath)] });
3050
- if (references.hasRuntimeWiring) coverage.push({ analyzer: "js-ts", scope: `runtime-wiring:${module.path}`, status: "not-analyzed", reason: "A possible runtime registration/wiring call was found.", evidence: [lineEvidence("code", root, module.absPath)] });
3221
+ if (references.hasLiteralDynamic || references.hasDynamic) coverage.push({ analyzer: "js-ts", scope: `dynamic-imports:${module.path}`, status: references.hasDynamic ? "not-analyzed" : "complete", reason: references.hasDynamic ? "A non-literal dynamic import was found; the target is unresolved." : "Literal dynamic imports were resolved.", evidence: [...references.dynamicEvidence.slice(0, 32)] });
3222
+ if (references.hasUnresolvedRuntimeWiring) coverage.push({ analyzer: "js-ts", scope: `runtime-wiring:${module.path}`, status: "not-analyzed", reason: "A runtime registration/wiring call was found without a statically imported target.", evidence: [lineEvidence("code", root, module.absPath)] });
3051
3223
  }
3224
+ if (dynamicCoverageIndex >= 0) coverage[dynamicCoverageIndex] = observedUnresolvedDynamic ? { analyzer: "js-ts", scope: "dynamic-imports", status: "partial", reason: "Literal dynamic imports are resolved; non-literal import expressions and require calls remain unresolved. Evidence lists representative dynamic loading sites.", evidence: [...observedDynamicEvidence.slice(0, 32)] } : observedLiteralDynamic ? { analyzer: "js-ts", scope: "dynamic-imports", status: "complete", reason: "All observed dynamic imports used literal targets and were resolved.", evidence: [...observedDynamicEvidence.slice(0, 32)] } : { analyzer: "js-ts", scope: "dynamic-imports", status: "not-applicable", reason: "No dynamic loading expression was observed." };
3225
+ const runtimeCoverageIndex = coverage.findIndex((entry) => entry.scope === "runtime-wiring");
3226
+ if (runtimeCoverageIndex >= 0) coverage[runtimeCoverageIndex] = observedUnresolvedRuntimeWiring ? { analyzer: "js-ts", scope: "runtime-wiring", status: "partial", reason: "Some configured runtime-wiring calls remain unresolved after static binding analysis." } : observedRuntimeWiring ? { analyzer: "js-ts", scope: "runtime-wiring", status: "complete", reason: "All observed configured runtime-wiring calls resolved to static bindings." } : { analyzer: "js-ts", scope: "runtime-wiring", status: "not-applicable", reason: "No configured runtime-wiring call was observed." };
3052
3227
  return artifact(root, opts.config, allFiles, [...entities.values()], [...relations.values()], coverage);
3053
3228
  };
3054
3229
 
@@ -3098,15 +3273,40 @@ var loadRegistryAgentMetadata = (root, config) => {
3098
3273
  var createRegistryAgentAdapter = (root, config, runner) => {
3099
3274
  if (!registryConfig(config)?.enabled) throw new Error("Registry agents are disabled. Set intelligence.registry.enabled: true to run an assisted workflow.");
3100
3275
  const metadata = loadRegistryAgentMetadata(resolve8(root), config);
3276
+ const settings = registryConfig(config) ?? {};
3277
+ const timeoutMs = settings.timeoutMs ?? 12e4;
3278
+ const maxResponseBytes = settings.maxResponseBytes ?? 256e3;
3279
+ const maxTokens = settings.maxTokens ?? Math.ceil(maxResponseBytes / 4);
3280
+ const maxConcurrency = settings.maxConcurrency ?? 1;
3281
+ let active = 0;
3282
+ const deterministicCache = /* @__PURE__ */ new Map();
3101
3283
  return {
3102
3284
  metadata,
3103
3285
  run: async (snapshot, report, evidence2 = report.diagnostics.flatMap((diagnostic2) => diagnostic2.evidence).slice(0, 64)) => {
3104
- const context = deepFreeze({ snapshot: redactValue(snapshot), report: redactValue(report), evidence: redactValue(evidence2), capabilities: ["snapshot.read", "evidence.read", "proposal.write"], network: false, shell: false });
3105
- const proposal = AgentProposalV1Schema.parse(await runner(context));
3106
- if (proposal.contentHash !== contentHashForArtifactV1(proposal)) throw new Error("Registry agent proposal contentHash does not match its canonical contents.");
3107
- if (proposal.baseSnapshotHash !== snapshot.contentHash || proposal.baseReportHash !== report.contentHash) throw new Error("Registry agent proposal is not based on the supplied snapshot/report hashes.");
3108
- if (proposal.origin.kind !== "registry-agent" || proposal.origin.id !== metadata.id) throw new Error(`Registry agent proposal origin must be ${metadata.id}.`);
3109
- return proposal;
3286
+ if (active >= maxConcurrency) throw new Error(`Registry agent concurrency limit ${maxConcurrency} exceeded.`);
3287
+ const cacheKey = sha256NormalizedV1({ snapshotHash: snapshot.contentHash, reportHash: report.contentHash, agentId: metadata.id, agentVersion: metadata.version, evidence: evidence2 });
3288
+ if (settings.deterministic && deterministicCache.has(cacheKey)) return deterministicCache.get(cacheKey);
3289
+ active += 1;
3290
+ let timer;
3291
+ try {
3292
+ const context = deepFreeze({ snapshot: redactValue(snapshot), report: redactValue(report), evidence: redactValue(evidence2), capabilities: ["snapshot.read", "evidence.read", "proposal.write"], network: false, shell: false, deterministic: settings.deterministic ?? true });
3293
+ const timeout = new Promise((_, reject) => {
3294
+ timer = setTimeout(() => reject(new Error(`Registry agent timed out after ${timeoutMs}ms.`)), timeoutMs);
3295
+ });
3296
+ const raw = await Promise.race([Promise.resolve(runner(context)), timeout]);
3297
+ const responseBytes = Buffer.byteLength(JSON.stringify(raw));
3298
+ if (responseBytes > maxResponseBytes) throw new Error(`Registry agent response limit ${maxResponseBytes} bytes exceeded.`);
3299
+ if (Math.ceil(responseBytes / 4) > maxTokens) throw new Error(`Registry agent token budget ${maxTokens} exceeded.`);
3300
+ const proposal = AgentProposalV1Schema.parse(raw);
3301
+ if (proposal.contentHash !== contentHashForArtifactV1(proposal)) throw new Error("Registry agent proposal contentHash does not match its canonical contents.");
3302
+ if (proposal.baseSnapshotHash !== snapshot.contentHash || proposal.baseReportHash !== report.contentHash) throw new Error("Registry agent proposal is not based on the supplied snapshot/report hashes.");
3303
+ if (proposal.origin.kind !== "registry-agent" || proposal.origin.id !== metadata.id) throw new Error(`Registry agent proposal origin must be ${metadata.id}.`);
3304
+ if (settings.deterministic) deterministicCache.set(cacheKey, proposal);
3305
+ return proposal;
3306
+ } finally {
3307
+ if (timer) clearTimeout(timer);
3308
+ active -= 1;
3309
+ }
3110
3310
  }
3111
3311
  };
3112
3312
  };
@@ -3137,6 +3337,37 @@ var scalar = (value) => {
3137
3337
  }
3138
3338
  return trimmed;
3139
3339
  };
3340
+ var isFieldName = (value) => {
3341
+ if (!/^[A-Za-z]/.test(value)) return false;
3342
+ for (const character of value.slice(1)) {
3343
+ if (!/[A-Za-z0-9_-]/.test(character)) return false;
3344
+ }
3345
+ return true;
3346
+ };
3347
+ var parseIndentedField = (raw, indentation) => {
3348
+ const prefix = " ".repeat(indentation);
3349
+ if (!raw.startsWith(prefix) || raw[indentation] === " ") return void 0;
3350
+ const body = raw.slice(indentation);
3351
+ const separator = body.indexOf(":");
3352
+ if (separator <= 0) return void 0;
3353
+ const key = body.slice(0, separator).trim();
3354
+ return isFieldName(key) ? { key, value: body.slice(separator + 1).trim() } : void 0;
3355
+ };
3356
+ var parseListItem = (raw, indentation) => {
3357
+ const prefix = `${" ".repeat(indentation)}-`;
3358
+ if (!raw.startsWith(prefix)) return void 0;
3359
+ const rest = raw.slice(prefix.length);
3360
+ if (rest && !/\s/.test(rest[0] ?? "")) return void 0;
3361
+ return rest.trim();
3362
+ };
3363
+ var conventionalPackageReference = (path, agentRoot) => {
3364
+ const prefix = `${agentRoot.replace(/\/$/, "")}/`;
3365
+ if (!path.startsWith(prefix)) return void 0;
3366
+ const relative10 = path.slice(prefix.length);
3367
+ const [scope, file] = relative10.split("/");
3368
+ if (scope !== "packages" && scope !== "apps" || !file) return void 0;
3369
+ return file.replace(/\.mdx?$/, "");
3370
+ };
3140
3371
  var list = (value) => {
3141
3372
  const trimmed = value.trim();
3142
3373
  if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return void 0;
@@ -3153,7 +3384,15 @@ var addDiagnostic = (diagnostics, input, code, message, line, endLine = line) =>
3153
3384
  diagnostics.push(diagnostic(input.path, code, message, line, endLine));
3154
3385
  };
3155
3386
  var resolveEntity = (reference, entities, input, lineStart, unresolved) => {
3156
- const resolved = entities.find((entity2) => entity2.id === reference || entity2.aliases?.includes(reference));
3387
+ const direct = entities.find((entity2) => entity2.id === reference || entity2.aliases?.includes(reference));
3388
+ if (direct) return direct;
3389
+ const packageReference = reference.replace(/^package:/, "");
3390
+ const packageCandidates = entities.filter((entity2) => {
3391
+ if (entity2.kind !== "package") return false;
3392
+ const pathName = entity2.path?.split("/").pop();
3393
+ return entity2.name === reference || entity2.path === reference || pathName === packageReference || entity2.name.endsWith(`/${packageReference}`);
3394
+ });
3395
+ const resolved = packageCandidates.length === 1 ? packageCandidates[0] : void 0;
3157
3396
  if (resolved) return resolved;
3158
3397
  const id = `unresolved:${reference}`;
3159
3398
  const existing = unresolved.get(id);
@@ -3169,9 +3408,13 @@ var resolveEntity = (reference, entities, input, lineStart, unresolved) => {
3169
3408
  return entity;
3170
3409
  };
3171
3410
  var relationKey = (from, to, kind) => `${from}\0${to}\0${kind}`;
3172
- var parseBlock = (input) => {
3411
+ var parseBlock = (input, options = {}) => {
3412
+ const conventionalPath = conventionalPackageReference(input.path, options.agentRoot ?? "docs/for-agents");
3173
3413
  const frontmatter = findFrontmatter(input.content);
3174
3414
  if (!frontmatter) {
3415
+ if (conventionalPath && !input.content.replace(/^\uFEFF/, "").startsWith("---")) {
3416
+ return { covers: [{ value: conventionalPath, line: 1 }], relations: [], diagnostics: [], hasDocbridge: true };
3417
+ }
3175
3418
  if (input.content.replace(/^\uFEFF/, "").startsWith("---")) {
3176
3419
  return {
3177
3420
  covers: [],
@@ -3183,8 +3426,24 @@ var parseBlock = (input) => {
3183
3426
  return { covers: [], relations: [], diagnostics: [], hasDocbridge: false };
3184
3427
  }
3185
3428
  const { lines, end } = frontmatter;
3186
- const docbridgeLine = lines.findIndex((line, index) => index > 0 && index < end && /^docbridge\s*:/.test(line));
3187
- if (docbridgeLine < 0) return { covers: [], relations: [], diagnostics: [], hasDocbridge: false };
3429
+ let docbridgeLine = -1;
3430
+ let typeLine = -1;
3431
+ let packageLine = -1;
3432
+ let humanDocLine = -1;
3433
+ for (let index = 1; index < end; index += 1) {
3434
+ const line = lines[index] ?? "";
3435
+ if (docbridgeLine < 0 && /^docbridge\s*:/.test(line)) docbridgeLine = index;
3436
+ if (typeLine < 0 && /^type\s*:/.test(line)) typeLine = index;
3437
+ if (packageLine < 0 && /^package\s*:/.test(line)) packageLine = index;
3438
+ if (humanDocLine < 0 && /^humanDoc\s*:/.test(line)) humanDocLine = index;
3439
+ }
3440
+ if (docbridgeLine < 0) {
3441
+ const type = typeLine >= 0 ? scalar(lines[typeLine]?.slice("type:".length) ?? "") : "";
3442
+ const packageReference = packageLine >= 0 ? scalar(lines[packageLine]?.slice("package:".length) ?? "") : conventionalPath ?? "";
3443
+ if (type === "package" && packageReference) return { covers: [{ value: packageReference, line: packageLine >= 0 ? packageLine + 1 : typeLine + 1 }], relations: [], diagnostics: [], hasDocbridge: true };
3444
+ if (conventionalPath) return { covers: [{ value: packageReference, line: humanDocLine >= 0 ? humanDocLine + 1 : 1 }], relations: [], diagnostics: [], hasDocbridge: true };
3445
+ return { covers: [], relations: [], diagnostics: [], hasDocbridge: false };
3446
+ }
3188
3447
  const diagnostics = [];
3189
3448
  const covers = [];
3190
3449
  const relations = [];
@@ -3210,11 +3469,10 @@ var parseBlock = (input) => {
3210
3469
  section = void 0;
3211
3470
  continue;
3212
3471
  }
3213
- if (/^ {2}[A-Za-z][A-Za-z0-9_-]*\s*:/.test(raw)) {
3472
+ const sectionField = parseIndentedField(raw, 2);
3473
+ if (sectionField) {
3214
3474
  finishRelation();
3215
- const match = /^ {2}([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(raw);
3216
- const key = match?.[1];
3217
- const value = match?.[2] ?? "";
3475
+ const { key, value } = sectionField;
3218
3476
  if (key !== "covers" && key !== "relations") {
3219
3477
  addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_UNKNOWN", `Unknown docbridge field: ${key ?? "(missing)"}.`, line);
3220
3478
  section = void 0;
@@ -3231,35 +3489,36 @@ var parseBlock = (input) => {
3231
3489
  }
3232
3490
  continue;
3233
3491
  }
3234
- if (section === "covers" && /^ {4}-\s*/.test(raw)) {
3235
- const value = scalar(raw.replace(/^ {4}-\s*/, ""));
3492
+ const listValue = parseListItem(raw, 4);
3493
+ if (section === "covers" && listValue !== void 0) {
3494
+ const value = scalar(listValue);
3236
3495
  if (!value) addDiagnostic(diagnostics, input, "DOCBRIDGE_REFERENCE_MISSING", "covers entries must not be empty.", line);
3237
3496
  else covers.push({ value, line });
3238
3497
  continue;
3239
3498
  }
3240
- if (section === "relations" && /^ {4}-\s*/.test(raw)) {
3499
+ if (section === "relations" && listValue !== void 0) {
3241
3500
  finishRelation();
3242
- const firstField = /^ {4}-\s*([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(raw);
3501
+ const firstField = parseIndentedField(` ${listValue}`, 4);
3243
3502
  current = { startLine: line, endLine: line, fields: /* @__PURE__ */ new Set() };
3244
- if (firstField?.[1]) {
3245
- current.fields.add(firstField[1]);
3246
- current[firstField[1]] = scalar(firstField[2] ?? "");
3247
- } else if (raw.replace(/^ {4}-\s*/, "").trim()) {
3503
+ if (firstField?.key) {
3504
+ current.fields.add(firstField.key);
3505
+ current[firstField.key] = scalar(firstField.value);
3506
+ } else if (listValue) {
3248
3507
  addDiagnostic(diagnostics, input, "DOCBRIDGE_RELATION_INVALID", "Relation entries must be field mappings.", line);
3249
3508
  }
3250
3509
  continue;
3251
3510
  }
3252
- if (section === "relations" && current && /^ {6}[A-Za-z][A-Za-z0-9_-]*\s*:/.test(raw)) {
3253
- const field = /^ {6}([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(raw);
3254
- const key = field?.[1];
3511
+ const field = parseIndentedField(raw, 6);
3512
+ if (section === "relations" && current && field) {
3513
+ const { key, value } = field;
3255
3514
  current.endLine = line;
3256
- if (!key || !["from", "to", "kind", "detection"].includes(key)) {
3257
- addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_UNKNOWN", `Unknown relation field: ${key ?? "(missing)"}.`, line);
3515
+ if (!["from", "to", "kind", "detection"].includes(key)) {
3516
+ addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_UNKNOWN", `Unknown relation field: ${key}.`, line);
3258
3517
  } else if (current.fields.has(key)) {
3259
3518
  addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_DUPLICATE", `Duplicate relation field: ${key}.`, line);
3260
3519
  } else {
3261
3520
  current.fields.add(key);
3262
- current[key] = scalar(field?.[2] ?? "");
3521
+ current[key] = scalar(value);
3263
3522
  }
3264
3523
  continue;
3265
3524
  }
@@ -3269,7 +3528,7 @@ var parseBlock = (input) => {
3269
3528
  return { covers, relations, diagnostics, hasDocbridge: true };
3270
3529
  };
3271
3530
  var parseDocumentationDeclarations = (input, options) => {
3272
- const parsed = parseBlock(input);
3531
+ const parsed = parseBlock(input, options);
3273
3532
  if (!parsed.hasDocbridge) return { hasDocbridge: false, entities: [], relations: [], diagnostics: [] };
3274
3533
  const diagnostics = [...parsed.diagnostics];
3275
3534
  const unresolved = /* @__PURE__ */ new Map();
@@ -3330,12 +3589,12 @@ var parseDocumentationDeclarations = (input, options) => {
3330
3589
  diagnostics
3331
3590
  };
3332
3591
  };
3333
- var applyDocumentationDeclarations = (snapshot, documents) => {
3592
+ var applyDocumentationDeclarations = (snapshot, documents, options = {}) => {
3334
3593
  const entities = new Map(snapshot.entities.map((entity) => [entity.id, entity]));
3335
3594
  const relations = new Map(snapshot.relations.map((relation) => [relation.id, relation]));
3336
3595
  const diagnostics = [];
3337
3596
  for (const document of documents) {
3338
- const result = parseDocumentationDeclarations(document, { snapshot });
3597
+ const result = parseDocumentationDeclarations(document, { snapshot, ...options });
3339
3598
  diagnostics.push(...result.diagnostics);
3340
3599
  for (const entity of result.entities) entities.set(entity.id, entity);
3341
3600
  for (const relation of result.relations) relations.set(relation.id, relation);
@@ -3349,11 +3608,13 @@ var applyDocumentationDeclarations = (snapshot, documents) => {
3349
3608
 
3350
3609
  // src/reconciliation/reconcile.ts
3351
3610
  var ignoredDocumentationRelations = /* @__PURE__ */ new Set(["covers"]);
3611
+ var isInternalEntity = (id) => !id.startsWith("external:") && !id.startsWith("unresolved:");
3352
3612
  var metadataDetection = (relation) => {
3353
3613
  const detection = relation.metadata?.detection;
3354
3614
  return typeof detection === "string" ? detection : void 0;
3355
3615
  };
3356
- var relationDetection = (relation) => relation.discriminator ?? metadataDetection(relation) ?? "static";
3616
+ var normalizeDetection = (value) => value === "dynamic-literal" ? "dynamic" : value;
3617
+ var relationDetection = (relation) => normalizeDetection(relation.discriminator ?? metadataDetection(relation) ?? "static");
3357
3618
  var entityResolver = (snapshots) => {
3358
3619
  const references = /* @__PURE__ */ new Map();
3359
3620
  const entities = snapshots.flatMap((snapshot) => snapshot.entities).sort((a, b) => a.id.localeCompare(b.id));
@@ -3374,6 +3635,25 @@ var mergeEvidence = (...relations) => {
3374
3635
  }
3375
3636
  return [...merged.values()].sort((a, b) => evidenceKey(a).localeCompare(evidenceKey(b)));
3376
3637
  };
3638
+ var boundedEvidence = (evidence2) => [...evidence2].slice(0, 64);
3639
+ var diagnosticCounts = (values) => {
3640
+ const counts = /* @__PURE__ */ new Map();
3641
+ for (const value of values) counts.set(value, (counts.get(value) ?? 0) + 1);
3642
+ return Object.fromEntries([...counts.entries()].sort(([a], [b]) => a.localeCompare(b)));
3643
+ };
3644
+ var documentClass = (entity) => {
3645
+ const classification = entity.metadata?.classification;
3646
+ return typeof classification === "string" && classification.length > 0 ? classification : "unclassified";
3647
+ };
3648
+ var countDocumentClasses = (documents, selectedIds) => {
3649
+ const counts = /* @__PURE__ */ new Map();
3650
+ for (const document of documents) {
3651
+ if (selectedIds && !selectedIds.has(document.id)) continue;
3652
+ const classification = documentClass(document);
3653
+ counts.set(classification, (counts.get(classification) ?? 0) + 1);
3654
+ }
3655
+ return Object.fromEntries([...counts.entries()].sort(([a], [b]) => a.localeCompare(b)));
3656
+ };
3377
3657
  var diagnosticId = (code, value) => `reconciliation:${code}:${sha256NormalizedV1(value).slice(0, 32)}`;
3378
3658
  var coverageAvailable = (snapshot, relation) => {
3379
3659
  const status = snapshot.coverage.find(
@@ -3389,6 +3669,62 @@ var entityById = (snapshots) => {
3389
3669
  return entities;
3390
3670
  };
3391
3671
  var isUnresolved = (id, entities) => id.startsWith("unresolved:") || entities.get(id)?.kind === "unresolved-reference";
3672
+ var normalizedPath = (path) => path.replaceAll("\\", "/").replace(/^\.\//, "");
3673
+ var semanticEntityId = (id, scope, entities, packageByModule) => {
3674
+ if (scope === "file" || id.startsWith("external:") || id.startsWith("unresolved:")) return id;
3675
+ const entity = entities.get(id);
3676
+ if (!entity || entity.kind !== "module" || !entity.path) return id;
3677
+ if (scope === "module") return id;
3678
+ return packageByModule.get(id) ?? id;
3679
+ };
3680
+ var packageLookup = (scope, entities) => {
3681
+ if (scope !== "package") return /* @__PURE__ */ new Map();
3682
+ const packages = [...entities.values()].filter((entity) => entity.kind === "package" && entity.path).map((entity) => ({ id: entity.id, path: normalizedPath(entity.path) })).sort((a, b) => b.path.length - a.path.length || a.id.localeCompare(b.id));
3683
+ const result = /* @__PURE__ */ new Map();
3684
+ for (const entity of entities.values()) {
3685
+ if (entity.kind !== "module" || !entity.path) continue;
3686
+ const modulePath = normalizedPath(entity.path);
3687
+ const packageEntity = packages.find(({ path }) => path === "." || modulePath === path || modulePath.startsWith(`${path}/`));
3688
+ if (packageEntity) result.set(entity.id, packageEntity.id);
3689
+ }
3690
+ return result;
3691
+ };
3692
+ var aggregatedRelations = (relations, scope, entities, packageByModule) => {
3693
+ if (scope === "file") return [...relations];
3694
+ const groups = /* @__PURE__ */ new Map();
3695
+ for (const relation of relations) {
3696
+ const from = semanticEntityId(relation.from, scope, entities, packageByModule);
3697
+ const to = semanticEntityId(relation.to, scope, entities, packageByModule);
3698
+ const detection = relationDetection(relation);
3699
+ const key = `${from}\0${to}\0${relation.kind}\0${detection}`;
3700
+ const group = groups.get(key) ?? [];
3701
+ group.push(relation);
3702
+ groups.set(key, group);
3703
+ }
3704
+ return [...groups.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([key, group]) => {
3705
+ const first = group[0];
3706
+ const parts = key.split("\0");
3707
+ const from = parts[0] ?? first.from;
3708
+ const to = parts[1] ?? first.to;
3709
+ const detection = parts[3] ?? relationDetection(first);
3710
+ const mergedEvidence = mergeEvidence(...group);
3711
+ return {
3712
+ ...first,
3713
+ id: `relation:aggregated:${scope}:${sha256NormalizedV1(key).slice(0, 32)}`,
3714
+ from,
3715
+ to,
3716
+ ...detection === "static" && !first.discriminator ? {} : { discriminator: detection },
3717
+ evidence: boundedEvidence(mergedEvidence),
3718
+ metadata: {
3719
+ ...first.metadata ?? {},
3720
+ aggregationScope: scope,
3721
+ aggregatedRelationCount: group.length,
3722
+ aggregatedEvidenceCount: mergedEvidence.length,
3723
+ ...mergedEvidence.length > 64 ? { evidenceTruncated: true } : {}
3724
+ }
3725
+ };
3726
+ });
3727
+ };
3392
3728
  var relationMatches = (observed, declared, resolveEntity2) => {
3393
3729
  if (relationBase(observed, resolveEntity2) !== relationBase(declared, resolveEntity2)) return false;
3394
3730
  return relationDetection(declared) === relationDetection(observed);
@@ -3399,7 +3735,7 @@ var reportDiagnostic = (code, status, severity, message, evidence2, value, entit
3399
3735
  status,
3400
3736
  severity,
3401
3737
  message,
3402
- evidence: [...evidence2],
3738
+ evidence: boundedEvidence(evidence2),
3403
3739
  ...entityIds?.length ? { entityIds: [...entityIds] } : {},
3404
3740
  ...relationIds?.length ? { relationIds: [...relationIds] } : {},
3405
3741
  ...remediation ? { remediation } : {}
@@ -3407,10 +3743,30 @@ var reportDiagnostic = (code, status, severity, message, evidence2, value, entit
3407
3743
  var reconcileKnowledge = (observed, declared, options = {}) => {
3408
3744
  const resolveEntity2 = entityResolver([observed, declared]);
3409
3745
  const entities = entityById([observed, declared]);
3410
- const observedRelations = observed.relations.filter((relation) => relation.provenance === "observed" && !ignoredDocumentationRelations.has(relation.kind));
3411
- const declaredRelations = declared.relations.filter((relation) => relation.provenance === "declared" && !ignoredDocumentationRelations.has(relation.kind));
3746
+ const scope = options.scope ?? "file";
3747
+ const packageByModule = packageLookup(scope, entities);
3748
+ const allDeclaredRelations = declared.relations.filter((relation) => relation.provenance === "declared");
3749
+ const observedRelations = aggregatedRelations(observed.relations.filter((relation) => relation.provenance === "observed" && !ignoredDocumentationRelations.has(relation.kind)), scope, entities, packageByModule);
3750
+ const declaredRelations = aggregatedRelations(declared.relations.filter((relation) => relation.provenance === "declared" && !ignoredDocumentationRelations.has(relation.kind)), scope, entities, packageByModule);
3412
3751
  const requiredRelationKinds = options.requiredRelationKinds === void 0 ? void 0 : new Set(options.requiredRelationKinds);
3413
3752
  const diagnostics = [];
3753
+ if (options.includeOrphanedDocuments) {
3754
+ const declaredDocumentIds = new Set(allDeclaredRelations.map((relation) => relation.from).filter((id) => id.startsWith("document:")));
3755
+ for (const document of observed.entities.filter((entity) => entity.kind === "document").sort((a, b) => a.id.localeCompare(b.id))) {
3756
+ if (declaredDocumentIds.has(document.id)) continue;
3757
+ diagnostics.push(reportDiagnostic(
3758
+ "DOCUMENTATION_ORPHANED",
3759
+ "undocumented",
3760
+ "info",
3761
+ "Markdown document has no Doc Bridge declaration linking it to observed knowledge.",
3762
+ document.evidence,
3763
+ document.id,
3764
+ [document.id],
3765
+ void 0,
3766
+ "Add a docbridge declaration or exclude this document from the documentation comparison scope."
3767
+ ));
3768
+ }
3769
+ }
3414
3770
  for (const entity of declared.entities.filter((item) => isUnresolved(item.id, entities)).sort((a, b) => a.id.localeCompare(b.id))) {
3415
3771
  diagnostics.push(reportDiagnostic(
3416
3772
  "UNRESOLVED_ENTITY_REFERENCE",
@@ -3431,9 +3787,18 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
3431
3787
  group.push(relation);
3432
3788
  declaredByBase.set(base2, group);
3433
3789
  }
3790
+ const observedDetectionsByBase = /* @__PURE__ */ new Map();
3791
+ for (const relation of observedRelations) {
3792
+ const base2 = relationBase(relation, resolveEntity2);
3793
+ const detections = observedDetectionsByBase.get(base2) ?? /* @__PURE__ */ new Set();
3794
+ detections.add(relationDetection(relation));
3795
+ observedDetectionsByBase.set(base2, detections);
3796
+ }
3434
3797
  for (const group of declaredByBase.values()) {
3435
3798
  const detections = new Set(group.map(relationDetection));
3436
3799
  if (detections.size < 2) continue;
3800
+ const observedDetections = observedDetectionsByBase.get(relationBase(group[0], resolveEntity2));
3801
+ if (observedDetections && [...detections].every((detection) => observedDetections.has(detection))) continue;
3437
3802
  diagnostics.push(reportDiagnostic(
3438
3803
  "CONFLICTING_DECLARATIONS",
3439
3804
  "conflict",
@@ -3460,7 +3825,7 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
3460
3825
  void 0,
3461
3826
  [relation.id, match.id]
3462
3827
  ));
3463
- } else if (coverageAvailable(observed, relation) && (requiredRelationKinds === void 0 || requiredRelationKinds.has(relation.kind))) {
3828
+ } else if (coverageAvailable(observed, relation) && (requiredRelationKinds === void 0 || requiredRelationKinds.has(relation.kind)) && (options.requiredRelationTargets !== "internal" || relation.from !== relation.to && isInternalEntity(relation.from) && isInternalEntity(relation.to))) {
3464
3829
  diagnostics.push(reportDiagnostic(
3465
3830
  "RELATION_UNDOCUMENTED",
3466
3831
  "undocumented",
@@ -3506,6 +3871,33 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
3506
3871
  }
3507
3872
  }
3508
3873
  const sortedDiagnostics = [...diagnostics].sort((a, b) => a.id.localeCompare(b.id));
3874
+ const packageEntities = observed.entities.filter((entity) => entity.kind === "package");
3875
+ const packageStatus = { fresh: 0, stale: 0, missing: 0, unverified: 0 };
3876
+ const diagnosticStatusByEntity = /* @__PURE__ */ new Map();
3877
+ const relationEndpoints = new Map([...observedRelations, ...declaredRelations].map((relation) => [relation.id, [relation.from, relation.to]]));
3878
+ for (const diagnostic2 of sortedDiagnostics) for (const id of [...diagnostic2.entityIds ?? [], ...(diagnostic2.relationIds ?? []).flatMap((relationId) => relationEndpoints.get(relationId) ?? [])]) {
3879
+ const statuses = diagnosticStatusByEntity.get(id) ?? /* @__PURE__ */ new Set();
3880
+ statuses.add(diagnostic2.status);
3881
+ diagnosticStatusByEntity.set(id, statuses);
3882
+ }
3883
+ for (const packageEntity of packageEntities) {
3884
+ const docs = allDeclaredRelations.filter((relation) => relation.from.startsWith("document:") && relation.to === packageEntity.id);
3885
+ const statuses = diagnosticStatusByEntity.get(packageEntity.id) ?? /* @__PURE__ */ new Set();
3886
+ if (!docs.length) packageStatus.missing += 1;
3887
+ else if (statuses.has("conflict") || statuses.has("stale-or-unverified")) packageStatus.stale += 1;
3888
+ else if (statuses.has("undocumented") || statuses.has("unresolved") || statuses.has("not-analyzed")) packageStatus.unverified += 1;
3889
+ else packageStatus.fresh += 1;
3890
+ }
3891
+ const documents = observed.entities.filter((entity) => entity.kind === "document");
3892
+ const documentedDocumentIds = new Set(allDeclaredRelations.filter((relation) => relation.from.startsWith("document:")).map((relation) => relation.from));
3893
+ const documentation = {
3894
+ documentCount: documents.length,
3895
+ documentedDocumentCount: documentedDocumentIds.size,
3896
+ documentClassificationCounts: countDocumentClasses(documents),
3897
+ documentedDocumentClassificationCounts: countDocumentClasses(documents, documentedDocumentIds),
3898
+ packageCount: packageEntities.length,
3899
+ packageStatus
3900
+ };
3509
3901
  const base = {
3510
3902
  type: "reconciliation-report",
3511
3903
  schemaVersion: 1,
@@ -3523,7 +3915,12 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
3523
3915
  entityCount: observed.entities.length,
3524
3916
  relationCount: observedRelations.length,
3525
3917
  diagnosticCount: sortedDiagnostics.length,
3526
- ...options.requiredRelationKinds === void 0 ? {} : { requiredRelationKinds: [...new Set(options.requiredRelationKinds)].sort() }
3918
+ ...options.scope === void 0 ? {} : { scope: options.scope },
3919
+ ...options.requiredRelationKinds === void 0 ? {} : { requiredRelationKinds: [...new Set(options.requiredRelationKinds)].sort() },
3920
+ ...options.requiredRelationTargets === void 0 ? {} : { requiredRelationTargets: options.requiredRelationTargets },
3921
+ diagnosticsByCode: diagnosticCounts(sortedDiagnostics.map((diagnostic2) => diagnostic2.code)),
3922
+ diagnosticsByStatus: diagnosticCounts(sortedDiagnostics.map((diagnostic2) => diagnostic2.status)),
3923
+ documentation
3527
3924
  }
3528
3925
  };
3529
3926
  return ReconciliationReportV1Schema.parse({ ...base, contentHash: contentHashForArtifactV1(base) });
@@ -3546,6 +3943,7 @@ var reportGroupKey = (entity, appLayout) => {
3546
3943
  return packageFamily(entity);
3547
3944
  };
3548
3945
  var titleCase = (value) => value.split(/[-_]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
3946
+ var levelChunkName = (scope, id) => `chunks/levels-${scope}-${sha256NormalizedV1(id).slice(0, 12)}.js`;
3549
3947
  var buildReportViewModel = (snapshot) => {
3550
3948
  const entities = new Map(snapshot.entities.map((entity) => [entity.id, entity]));
3551
3949
  const packages = snapshot.entities.filter((entity) => entity.kind === "package");
@@ -3559,6 +3957,7 @@ var buildReportViewModel = (snapshot) => {
3559
3957
  const appLayout = packages.some((entity) => pathParts(entity.path)[0] === "apps");
3560
3958
  const groups = /* @__PURE__ */ new Map();
3561
3959
  const entityGroup = {};
3960
+ const groupModuleCounts = /* @__PURE__ */ new Map();
3562
3961
  for (const entity of packages) {
3563
3962
  const family = reportGroupKey(entity, appLayout);
3564
3963
  const id = `group:${family}`;
@@ -3575,6 +3974,7 @@ var buildReportViewModel = (snapshot) => {
3575
3974
  for (const [entityId2, packageId2] of packageForEntity) {
3576
3975
  const groupId = entityGroup[packageId2];
3577
3976
  if (groupId) entityGroup[entityId2] = groupId;
3977
+ if (groupId && entities.get(entityId2)?.kind === "module") groupModuleCounts.set(groupId, (groupModuleCounts.get(groupId) ?? 0) + 1);
3578
3978
  }
3579
3979
  if (externalIds.size) {
3580
3980
  const externalGroupId = "group:external-dependencies";
@@ -3616,62 +4016,185 @@ var buildReportViewModel = (snapshot) => {
3616
4016
  nodes: overviewNodes.sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id)),
3617
4017
  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))
3618
4018
  },
3619
- groups: Object.fromEntries([...groups.entries()].map(([id, group]) => [id, { ...group, members: [...group.members].sort() }])),
3620
- entityGroup
4019
+ groups: Object.fromEntries([...groups.entries()].map(([id, group]) => [id, { ...group, members: [...group.members].sort(), moduleCount: groupModuleCounts.get(id) ?? 0 }])),
4020
+ entityGroup,
4021
+ levelChunks: {
4022
+ default: "chunks/levels-packages.js",
4023
+ groups: Object.fromEntries([...groups.keys()].map((id) => [id, levelChunkName("group", id)])),
4024
+ packages: [...packages].sort((left, right) => left.id.localeCompare(right.id)).map((entity) => levelChunkName("package", entity.id))
4025
+ }
3621
4026
  };
3622
4027
  };
3623
4028
  var escapeHtml = (value) => String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
3624
4029
  var anchor = (prefix, value) => `${prefix}-${value.replace(/[^A-Za-z0-9_-]+/g, "-")}`;
3625
4030
  var embeddedJson = (value) => JSON.stringify(value).replaceAll("<", "\\u003c");
3626
4031
  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>`;
3627
- var reportData = (snapshot, report, includeSnippets) => ({
3628
- project: snapshot.project,
3629
- revision: snapshot.sourceRevision,
3630
- revisionKind: snapshot.sourceRevisionKind,
3631
- snapshotHash: snapshot.contentHash,
3632
- reportHash: report.contentHash,
3633
- configurationHash: snapshot.configurationHash,
3634
- pipelineVersion: snapshot.pipelineVersion,
3635
- analyzerVersions: snapshot.analyzerVersions,
3636
- diagnosticCount: report.diagnostics.length,
3637
- requiredRelationKinds: report.summary.requiredRelationKinds,
3638
- diagnosticSummary: {
3639
- errors: report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "error").length,
3640
- warnings: report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "warn").length,
3641
- undocumented: report.diagnostics.filter((diagnostic2) => diagnostic2.status === "undocumented").length,
3642
- drift: report.diagnostics.filter((diagnostic2) => diagnostic2.status === "stale-or-unverified" || diagnostic2.status === "conflict").length
3643
- },
3644
- entities: snapshot.entities.map((entity, index) => ({
3645
- id: entity.id,
3646
- anchor: entity.id.length > 64 ? `entity-n${index}` : anchor("entity", entity.id),
3647
- kind: entity.kind,
3648
- name: entity.name,
3649
- path: entity.path,
3650
- provenance: entity.provenance,
3651
- evidence: entity.evidence.map(({ context, ...item }) => includeSnippets && context ? { ...item, context: redactSecrets(context) } : item)
3652
- })),
3653
- relations: snapshot.relations.map((relation) => ({
3654
- id: relation.id,
3655
- kind: relation.kind,
3656
- from: relation.from,
3657
- to: relation.to,
3658
- provenance: relation.provenance,
3659
- evidence: relation.evidence.map(({ context, ...item }) => includeSnippets && context ? { ...item, context: redactSecrets(context) } : item)
3660
- })),
3661
- diagnostics: report.diagnostics.map((diagnostic2) => ({
3662
- id: diagnostic2.id,
3663
- code: diagnostic2.code,
3664
- status: diagnostic2.status,
3665
- severity: diagnostic2.severity,
3666
- message: diagnostic2.message,
3667
- entityIds: diagnostic2.entityIds ?? [],
3668
- relationIds: diagnostic2.relationIds ?? [],
3669
- remediation: diagnostic2.remediation,
3670
- evidence: diagnostic2.evidence.map(({ context, ...item }) => includeSnippets && context ? { ...item, context: redactSecrets(context) } : item)
3671
- })),
3672
- coverage: snapshot.coverage,
3673
- view: buildReportViewModel(snapshot)
3674
- });
4032
+ var reportData = (snapshot, report, includeSnippets, privacy = "private") => {
4033
+ const evidence2 = (items) => items.map(({ context, ...item }) => includeSnippets && context ? { ...item, context: redactSecrets(context) } : item);
4034
+ const base = {
4035
+ project: snapshot.project,
4036
+ revision: snapshot.sourceRevision,
4037
+ revisionKind: snapshot.sourceRevisionKind,
4038
+ snapshotHash: snapshot.contentHash,
4039
+ reportHash: report.contentHash,
4040
+ configurationHash: snapshot.configurationHash,
4041
+ pipelineVersion: snapshot.pipelineVersion,
4042
+ analyzerVersions: snapshot.analyzerVersions,
4043
+ diagnosticCount: report.diagnostics.length,
4044
+ actionableCount: report.diagnostics.filter((diagnostic2) => diagnostic2.status !== "confirmed").length,
4045
+ confirmedCount: report.diagnostics.filter((diagnostic2) => diagnostic2.status === "confirmed").length,
4046
+ requiredRelationKinds: report.summary.requiredRelationKinds,
4047
+ requiredRelationTargets: report.summary.requiredRelationTargets,
4048
+ documentation: report.summary.documentation,
4049
+ diagnosticSummary: {
4050
+ errors: report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "error").length,
4051
+ warnings: report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "warn").length,
4052
+ undocumented: report.diagnostics.filter((diagnostic2) => diagnostic2.status === "undocumented").length,
4053
+ drift: report.diagnostics.filter((diagnostic2) => diagnostic2.status === "stale-or-unverified" || diagnostic2.status === "conflict").length
4054
+ },
4055
+ entities: snapshot.entities.map((entity, index) => ({
4056
+ id: entity.id,
4057
+ anchor: entity.id.length > 64 ? `entity-n${index}` : anchor("entity", entity.id),
4058
+ kind: entity.kind,
4059
+ name: entity.name,
4060
+ path: entity.path,
4061
+ provenance: entity.provenance,
4062
+ evidence: evidence2(entity.evidence)
4063
+ })),
4064
+ relations: snapshot.relations.map((relation) => ({
4065
+ id: relation.id,
4066
+ kind: relation.kind,
4067
+ from: relation.from,
4068
+ to: relation.to,
4069
+ provenance: relation.provenance,
4070
+ evidence: evidence2(relation.evidence)
4071
+ })),
4072
+ diagnostics: report.diagnostics.map((diagnostic2) => ({
4073
+ id: diagnostic2.id,
4074
+ code: diagnostic2.code,
4075
+ status: diagnostic2.status,
4076
+ severity: diagnostic2.severity,
4077
+ message: diagnostic2.message,
4078
+ entityIds: diagnostic2.entityIds ?? [],
4079
+ relationIds: diagnostic2.relationIds ?? [],
4080
+ remediation: diagnostic2.remediation,
4081
+ evidence: evidence2(diagnostic2.evidence)
4082
+ })),
4083
+ coverage: snapshot.coverage,
4084
+ view: buildReportViewModel(snapshot)
4085
+ };
4086
+ if (privacy !== "anonymized") return base;
4087
+ const pad = (value) => String(value).padStart(3, "0");
4088
+ const sortedEntities = [...base.entities].sort((left, right) => left.id.localeCompare(right.id));
4089
+ const entityOrdinals = /* @__PURE__ */ new Map();
4090
+ const entityIds = new Map(sortedEntities.map((entity) => {
4091
+ const next = (entityOrdinals.get(entity.kind) ?? 0) + 1;
4092
+ entityOrdinals.set(entity.kind, next);
4093
+ return [entity.id, `entity:${entity.kind}-${pad(next)}`];
4094
+ }));
4095
+ const entityNames = /* @__PURE__ */ new Map();
4096
+ const nameFor = (kind) => {
4097
+ const prefix = kind === "external" ? "dependency" : kind;
4098
+ const next = (entityNames.get(prefix) ?? 0) + 1;
4099
+ entityNames.set(prefix, next);
4100
+ return `${prefix}-${pad(next)}`;
4101
+ };
4102
+ const sortedRelations = [...base.relations].sort((left, right) => left.id.localeCompare(right.id));
4103
+ const relationIds = new Map(sortedRelations.map((relation, index) => [relation.id, `relation:${pad(index + 1)}`]));
4104
+ const sortedDiagnostics = [...base.diagnostics].sort((left, right) => left.id.localeCompare(right.id));
4105
+ const diagnosticIds = new Map(sortedDiagnostics.map((diagnostic2, index) => [diagnostic2.id, `finding:${pad(index + 1)}`]));
4106
+ const genericEvidence = (items) => items.map((item, index) => ({
4107
+ source: item.source,
4108
+ path: `evidence-${pad(index + 1)}`,
4109
+ ...item.lineStart === void 0 ? {} : { lineStart: item.lineStart },
4110
+ ...item.lineEnd === void 0 ? {} : { lineEnd: item.lineEnd }
4111
+ }));
4112
+ const groups = Object.entries(base.view.groups).sort(([left], [right]) => left.localeCompare(right));
4113
+ const groupIds = new Map(groups.map(([id], index) => [id, `group:${pad(index + 1)}`]));
4114
+ const groupNames = /* @__PURE__ */ new Map();
4115
+ const groupName = (kind) => {
4116
+ if (kind === "external") return "External dependencies";
4117
+ if (kind === "shared") return "Shared packages";
4118
+ if (kind === "workspace") return "Workspace";
4119
+ const next = (groupNames.get(kind) ?? 0) + 1;
4120
+ groupNames.set(kind, next);
4121
+ return `${kind.charAt(0).toUpperCase()}${kind.slice(1)} ${next}`;
4122
+ };
4123
+ const sanitizedGroups = Object.fromEntries(groups.map(([id, group]) => [groupIds.get(id), {
4124
+ kind: group.kind,
4125
+ name: groupName(group.kind),
4126
+ path: void 0,
4127
+ members: group.members.map((member) => entityIds.get(member)),
4128
+ moduleCount: group.moduleCount
4129
+ }]));
4130
+ const sanitizedOverviewNodes = base.view.overview.nodes.map((node) => ({
4131
+ id: groupIds.get(node.id),
4132
+ kind: node.kind,
4133
+ name: sanitizedGroups[groupIds.get(node.id)]?.name ?? "Scope",
4134
+ path: void 0,
4135
+ memberCount: node.memberCount
4136
+ }));
4137
+ const sanitizedView = {
4138
+ overview: {
4139
+ nodes: sanitizedOverviewNodes,
4140
+ edges: base.view.overview.edges.map((edge) => ({
4141
+ from: groupIds.get(edge.from),
4142
+ to: groupIds.get(edge.to),
4143
+ count: edge.count,
4144
+ kinds: edge.kinds,
4145
+ relationIds: edge.relationIds.map((id) => relationIds.get(id))
4146
+ }))
4147
+ },
4148
+ groups: sanitizedGroups,
4149
+ entityGroup: Object.fromEntries(Object.entries(base.view.entityGroup).map(([id, group]) => [entityIds.get(id), groupIds.get(group)])),
4150
+ levelChunks: {
4151
+ default: base.view.levelChunks.default,
4152
+ groups: Object.fromEntries([...groupIds.values()].map((id) => [id, levelChunkName("group", id)])),
4153
+ packages: [...entityIds.values()].filter((id) => id.startsWith("entity:package-")).sort().map((id) => levelChunkName("package", id))
4154
+ }
4155
+ };
4156
+ return {
4157
+ ...base,
4158
+ project: { name: "Anonymized repository" },
4159
+ revision: "redacted",
4160
+ entities: base.entities.map((entity) => ({
4161
+ id: entityIds.get(entity.id),
4162
+ anchor: anchor("entity", entityIds.get(entity.id)),
4163
+ kind: entity.kind,
4164
+ name: nameFor(entity.kind),
4165
+ path: void 0,
4166
+ provenance: entity.provenance,
4167
+ evidence: genericEvidence(entity.evidence)
4168
+ })),
4169
+ relations: base.relations.map((relation) => ({
4170
+ id: relationIds.get(relation.id),
4171
+ kind: relation.kind,
4172
+ from: entityIds.get(relation.from),
4173
+ to: entityIds.get(relation.to),
4174
+ provenance: relation.provenance,
4175
+ evidence: genericEvidence(relation.evidence)
4176
+ })),
4177
+ diagnostics: base.diagnostics.map((diagnostic2) => ({
4178
+ id: diagnosticIds.get(diagnostic2.id),
4179
+ code: diagnostic2.code,
4180
+ status: diagnostic2.status,
4181
+ severity: diagnostic2.severity,
4182
+ message: `Finding ${diagnostic2.code}`,
4183
+ entityIds: diagnostic2.entityIds?.map((id) => entityIds.get(id)) ?? [],
4184
+ relationIds: diagnostic2.relationIds?.map((id) => relationIds.get(id)) ?? [],
4185
+ evidence: genericEvidence(diagnostic2.evidence)
4186
+ })),
4187
+ coverage: base.coverage.map((entry, index) => ({
4188
+ analyzer: entry.analyzer,
4189
+ analyzerVersion: entry.analyzerVersion,
4190
+ scope: `scope-${pad(index + 1)}`,
4191
+ status: entry.status,
4192
+ ...entry.reason ? { reason: "Coverage boundary reported by analyzer." } : {},
4193
+ ...entry.evidence ? { evidence: genericEvidence(entry.evidence) } : {}
4194
+ })),
4195
+ view: sanitizedView
4196
+ };
4197
+ };
3675
4198
  var styles = String.raw`
3676
4199
  :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)}
3677
4200
  *{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}}
@@ -3680,76 +4203,86 @@ styles += String.raw`
3680
4203
  :root{--ink:#182b2b;--muted:#536664;--paper:#efe9df;--panel:#f8f5ee;--line:#c8c8b9;--green:#176b51;--blue:#155a68;--amber:#93530b;--red:#9b302b;--shadow:none}
3681
4204
  *{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)}}
3682
4205
  `;
3683
- 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}}`;
3684
- 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}}`;
4206
+ 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)}.panel-head>div:first-child{min-width:0;flex:1 1 0}.panel-head>.tabs:not(.report-tabs){flex-wrap:nowrap;max-width:100%;overflow-x:auto}.panel-head>.tabs:not(.report-tabs) .level{flex:0 0 auto}@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}}`;
4207
+ styles += String.raw`.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}}`;
3685
4208
  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}`;
3686
4209
  styles += String.raw`body[data-report-view="architecture"] #report-dashboard{display:none!important}.edge{fill:none}`;
3687
4210
  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}}`;
3688
4211
  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}}`;
4212
+ styles += String.raw`.map-wrap.dense{overflow:auto}.map-wrap.dense #graph{width:2000px!important;max-width:none!important}`;
3689
4213
  styles += String.raw`#lens-caption + p.subtle{display:none}`;
3690
4214
  var script = String.raw`
3691
4215
  const data=${"${DATA}"};
3692
4216
  data.entities=data.entities||[];data.relations=data.relations||[];data.diagnostics=data.diagnostics||[];
3693
- 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.";
4217
+ globalThis.__DOC_BRIDGE_DATA__=data;
4218
+ const policySuffix=data.requiredRelationKinds===undefined?"Policy: all observed relation kinds require declarations.":data.requiredRelationKinds.length?"Policy: declarations required for "+data.requiredRelationKinds.join(", ")+(data.requiredRelationTargets==="internal"?" between internal entities":"")+".":"Policy: missing relation declarations disabled; stale, conflicting, and unresolved declarations remain checked.";
3694
4219
  const ensurePolicyNote=()=>{const note=document.querySelector("#map-note");if(note&&!note.textContent.includes(policySuffix))note.textContent=(note.textContent+" · "+policySuffix).trim()};
3695
4220
  new MutationObserver(ensurePolicyNote).observe(document.querySelector("#map-note"),{childList:true,characterData:true});
3696
- const lazyChunks=globalThis.__DOC_BRIDGE_LAZY_CHUNKS__||[],loadedChunks=new Set(),loadingChunks=new Map();
4221
+ const lazyChunks=globalThis.__DOC_BRIDGE_LAZY_CHUNKS__||[],hasLevelChunks=globalThis.__DOC_BRIDGE_HAS_LEVEL_CHUNKS__===true,loadedChunks=new Set(),loadingChunks=new Map();
3697
4222
  const esc=(value)=>String(value??"").replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll("\"","&quot;").replaceAll("'","&#39;");
3698
- 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();
4223
+ 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(),groupFindingIndex=new Map(),groupFindingCounts=new Map(),packageCache=new Map(),nodeIssueCache=new Map();let graphCacheKey="",graphCache;
4224
+ let activeLevelChunk="";
3699
4225
  const groupById=new Map(Object.entries(data.view?.groups||{}));
4226
+ const packageChunkNameById=new Map([...groupById.values()].flatMap((group)=>group.members).filter((id)=>data.entities.find((entity)=>entity.id===id)?.kind==="package").sort().map((id,index)=>[id,data.view?.levelChunks?.packages?.[index]]));
3700
4227
  const addFinding=(index,id,finding)=>{const values=index.get(id)||[];values.push(finding);index.set(id,values)};
3701
4228
  const addRelation=(id,relation)=>{const values=relationsByEntity.get(id)||[];values.push(relation);relationsByEntity.set(id,values)};
3702
- 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))})};
3703
- const hasChunk=(name)=>!lazyChunks.includes(name)||loadedChunks.has(name);
3704
- 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};
4229
+ const hydrate=()=>{byId.clear();parent.clear();relationById.clear();packageMembers.clear();findingIndex.clear();relationFindingIndex.clear();relationsByEntity.clear();groupEntityIds.clear();groupFindingIndex.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=data.view?.diagnosticGroup?.[finding.id]||[];if(finding.status!=="confirmed")scopes.forEach((scope)=>{addFinding(groupFindingIndex,scope,finding);groupFindingCounts.set(scope,(groupFindingCounts.get(scope)||0)+1)})})};
4230
+ const isLevelChunk=(name)=>name.startsWith("chunks/levels-");
4231
+ const isDetailChunk=(name)=>name.startsWith("chunks/details-");
4232
+ const hasChunk=(name)=>isLevelChunk(name)?activeLevelChunk===name:isDetailChunk(name)?loadedChunks.has(name):!lazyChunks.includes(name)||loadedChunks.has(name);
4233
+ 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=()=>{try{if(isLevelChunk(name)){const payload=globalThis.__DOC_BRIDGE_LEVEL_PAYLOAD__;if(!payload)throw new Error("Offline report level payload is missing: "+name);data.entities=payload.entities||[];data.relations=payload.relations||[];activeLevelChunk=name;delete globalThis.__DOC_BRIDGE_LEVEL_PAYLOAD__}else if(isDetailChunk(name)){const payload=globalThis.__DOC_BRIDGE_DETAIL_PAYLOAD__;if(!payload)throw new Error("Offline report detail payload is missing: "+name);const entities=new Map((payload.entities||[]).map((entity)=>[entity.id,entity]));const relations=new Map((payload.relations||[]).map((relation)=>[relation.id,relation]));data.entities=data.entities.map((entity)=>entities.get(entity.id)||entity);data.relations=data.relations.map((relation)=>relations.get(relation.id)||relation);delete globalThis.__DOC_BRIDGE_DETAIL_PAYLOAD__}loadedChunks.add(name);hydrate();loadingChunks.delete(name);resolve()}catch(error){loadingChunks.delete(name);reject(error)}};element.onerror=()=>{loadingChunks.delete(name);reject(new Error("Unable to load offline report chunk: "+name))};document.head.append(element)});loadingChunks.set(name,promise);return promise};
3705
4234
  const readHash=()=>new URLSearchParams(location.hash.slice(1));
3706
4235
  const hash=readHash();
3707
4236
  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)};
3708
4237
  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{}};
3709
4238
  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};
3710
4239
  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};
3711
- const groupFor=(id)=>data.view?.entityGroup?.[id]||id;
4240
+ const groupFor=(id)=>{const direct=data.view?.entityGroup?.[id];if(direct)return direct;const entity=byId.get(id);return entity?.kind==="module"||entity?.kind==="file"?groupFor(packageOf(id)):id};
3712
4241
  const packageNodes=()=>data.entities.filter((entity)=>entity.kind==="package");
3713
4242
  const viewLabels={architecture:"Architecture",drift:"Insights",risks:"Findings",evidence:"Coverage"};
3714
4243
  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};
3715
4244
  const label=(entity)=>entity?.name||entity?.id||"Unknown";
3716
4245
  const short=(value,max)=>{const text=String(value??""),limit=text==="External dependencies"?16:max;return text.length>limit?text.slice(0,limit-1)+"…":text};
3717
- 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("")};
4246
+ 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),group=groupById.get(groupId);if(group&&groupId!==packageId&&group.name!==label(packageEntity))items.push({label:group.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("")};
3718
4247
  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};
3719
- const relationHealth=(relation)=>{if(!relation)return "";const findings=relationFindingIndex.get(relation.id)||[];return findings.some((finding)=>finding.severity==="error")?"error":findings.length?"warn":""};
3720
- 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};
4248
+ const relationHealth=(relation)=>{if(!relation)return "";const findings=relationFindings(relation.id);return findings.some((finding)=>finding.severity==="error")?"error":findings.length?"warn":""};
4249
+ const nodeIssues=(nodeId)=>{if(nodeIssueCache.has(nodeId))return nodeIssueCache.get(nodeId);const ids=groupEntityIds.get(nodeId)||new Set([nodeId]),findings=new Set(groupFindingIndex.get(nodeId)||[]),relations=new Set();for(const id of ids){(findingIndex.get(id)||[]).filter((finding)=>finding.status!=="confirmed").forEach((finding)=>findings.add(finding));(relationsByEntity.get(id)||[]).forEach((relation)=>relations.add(relation))}relations.forEach((relation)=>(relationFindingIndex.get(relation.id)||[]).filter((finding)=>finding.status!=="confirmed").forEach((finding)=>findings.add(finding)));const result=[...findings];nodeIssueCache.set(nodeId,result);return result};
3721
4250
  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);
3722
- const relationInLens=(id)=>{const findings=relationFindingIndex.get(id)||[];return findings.some(findingInLens)};
4251
+ const relationFindings=(id)=>relationFindingIndex.get(id)||data.view?.diagnosticRelationFindings?.[id]||[];
4252
+ const relationInLens=(id)=>relationFindings(id).some(findingInLens);
3723
4253
  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)};
4254
+ const levelChunkName=()=>{if(state.selected&&groupById.has(state.selected))return data.view?.levelChunks?.groups?.[state.selected]||data.view?.levelChunks?.default;const selected=state.selected?byId.get(state.selected):null;const packageId=selected?.kind==="package"?selected.id:selected?packageOf(selected.id):state.selected?.startsWith("package:")?state.selected:null;const packageChunk=packageId&&packageChunkNameById.get(packageId);if(packageChunk)return packageChunk;const selectedGroup=state.selected?data.view?.entityGroup?.[state.selected]:null;if(selectedGroup&&data.view?.levelChunks?.groups?.[selectedGroup])return data.view.levelChunks.groups[selectedGroup];return data.view?.levelChunks?.default};
4255
+ const detailChunkName=()=>{const entity=state.selected?byId.get(state.selected):null;const packageId=entity?.kind==="package"?entity.id:entity?packageOf(entity.id):null;const levelName=packageId&&packageChunkNameById.get(packageId);return levelName?.replace("chunks/levels-package-","chunks/details-")||null};
3724
4256
  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)};
3725
- 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}};
3726
- 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("")};
3727
- 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>"};
4257
+ const graphModelUncached=()=>{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}};
4258
+ const graphModel=()=>{const key=state.lens+"|"+state.level+"|"+(state.selected||"");if(graphCacheKey===key&&graphCache)return graphCache;graphCacheKey=key;return graphCache=graphModelUncached()};
4259
+ const renderInsights=()=>{const summary=data.diagnosticSummary||{},documentation=data.documentation||{},classificationCounts=documentation.documentClassificationCounts||{},documentedCounts=documentation.documentedDocumentClassificationCounts||{},agentDocs=classificationCounts.agent??0,documentedAgentDocs=documentedCounts.agent??0,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.",""],["Agent documentation",documentedAgentDocs+"/"+agentDocs,"Configured agent-corpus documents linked to knowledge.",""],["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("")};
4260
+ 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)}const actionable=data.diagnostics.filter((finding)=>finding.status!=="confirmed");if(!actionable.length){dashboard.innerHTML="<p class=\"empty\">No actionable findings. Confirmed checks are available in Evidence.</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=group.moduleCount||[...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>"};
3728
4261
  const mapState={scale:1,dragging:false,x:0,y:0};
3729
- 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"};
4262
+ const applyMapTransform=()=>{const svg=document.querySelector("#graph");if(!svg)return;const dense=svg.closest(".map-wrap")?.classList.contains("dense");svg.setAttribute("preserveAspectRatio",dense?"xMinYMin meet":"xMidYMid meet");svg.style.transform="translate("+mapState.x+"px,"+mapState.y+"px) scale("+mapState.scale+")";svg.style.transformOrigin="center center"};
3730
4263
  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)})};
3731
4264
  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()};
3732
4265
  let nodeClickTimer;
3733
4266
  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);
3734
- 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);
4267
+ document.addEventListener("click",(event)=>{const node=event.target.closest?.("[data-node]");if(!node)return;event.preventDefault();event.stopImmediatePropagation();clearTimeout(nodeClickTimer);nodeClickTimer=setTimeout(async()=>{state.selected=node.dataset.node;render();const chunk=detailChunkName();if(chunk&&!hasChunk(chunk)){await loadChunk(chunk);render()}},380)},true);
3735
4268
  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);
3736
- const diagnosticsFor=(id)=>findingIndex.get(id)||[];
4269
+ const diagnosticsFor=(id)=>(findingIndex.get(id)||[]).filter((finding)=>finding.status!=="confirmed");
3737
4270
  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>":"")};
3738
- 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."};
4271
+ const renderGraph=()=>{const model=graphModel(),svg=document.querySelector("#graph"),dense=model.edges.length>64,edges=dense?[...model.edges].sort((a,b)=>(b.health?1:0)-(a.health?1:0)||b.count-a.count||a.from.localeCompare(b.from)||a.to.localeCompare(b.to)).slice(0,64):model.edges;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,[]]));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));if(dense){columns.clear();model.nodes.forEach((node,index)=>{const column=Math.floor(index/8),items=columns.get(column)||[];items.push(node);columns.set(column,items)})}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=dense?Math.max(1000,width)+"px":"100%";svg.style.minWidth="0";svg.style.height="100%";document.querySelector(".map-wrap")?.classList.toggle("dense",dense);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>",edgeMarkup=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(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+edgeMarkup+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"+(dense?" (showing "+edges.length+" prioritized of "+model.edges.length+").":".");};
3739
4272
  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)};
3740
- 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"};
4273
+ const findingScope=(finding)=>data.view?.diagnosticGroup?.[finding.id]?.[0]||(()=>{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"})();
3741
4274
  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))};
3742
4275
  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>";
3743
- 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>"};
4276
+ 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 itemLabel=state.lens==="evidence"?"evidence checks":"findings",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+" "+itemLabel+" · "+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> "+itemLabel+" · "+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+" "+itemLabel+"</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 "+itemLabel+" match the current lens and filters.</p>"};
3744
4277
  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>"};
3745
- 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>"};
4278
+ const deferFindings=()=>{if(findingsLoaded())return;const actionable=data.actionableCount??data.diagnosticCount??0,confirmed=data.confirmedCount??0;document.querySelector("#finding-count").textContent=actionable+" actionable findings · "+confirmed+" confirmed checks available on demand";document.querySelector("#findings").innerHTML="<div class=\"empty\"><p>Findings stay out of the first paint so large repositories remain responsive. Confirmed checks are evidence, not issues.</p><button id=\"load-findings\" class=\"tab\" type=\"button\">Load findings</button></div>"};
3746
4279
  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>"}};
3747
4280
  const findingsLoaded=()=>!lazyChunks.includes("chunks/findings.js")||loadedChunks.has("chunks/findings.js");
3748
- const levelsLoaded=()=>!lazyChunks.includes("chunks/levels.js")||loadedChunks.has("chunks/levels.js");
4281
+ const levelsLoaded=()=>!hasLevelChunks||state.level==="overview"||activeLevelChunk===levelChunkName();
3749
4282
  const setReportView=()=>{document.body.dataset.reportView={architecture:"architecture",drift:"insights",risks:"findings",evidence:"coverage"}[state.lens]||"architecture";ensureReportChrome()};
3750
- 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()};
3751
- 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);
3752
- 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);
4283
+ const measureRender=(name,renderFn)=>{const started=performance.now(),value=renderFn(),timings=JSON.parse(document.documentElement.dataset.docBridgeRenderTimings||"{}");timings[name]=Math.round(performance.now()-started);document.documentElement.dataset.docBridgeRenderTimings=JSON.stringify(timings);return value};
4284
+ const render=()=>{if(state.level!=="overview"&&!levelsLoaded()){document.querySelector("#map-note").textContent="Loading canonical entities…";loadChunk(levelChunkName()).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.";document.documentElement.dataset.docBridgeRenderTimings="{}";measureRender("insights",renderInsights);if(state.lens!=="architecture")measureRender("insightsDashboard",renderInsightsDashboard);measureRender("graph",renderGraph);measureRender("details",renderDetails);if(state.lens!=="architecture")measureRender("findings",renderFindings);else document.querySelector("#findings")?.replaceChildren();deferFindings();measureRender("coverage",renderCoverage)};
4285
+ 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(levelChunkName());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);
3753
4286
  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);
3754
4287
  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);
3755
4288
  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);
@@ -3775,14 +4308,15 @@ hydrate();render();keepMobileGraphReadable();ensurePolicyNote();
3775
4308
  var render = (input, options) => {
3776
4309
  const { snapshot, report } = input;
3777
4310
  const includeSnippets = options.includeSnippets === true;
3778
- const data = reportData(snapshot, report, includeSnippets);
4311
+ const data = reportData(snapshot, report, includeSnippets, options.privacy);
3779
4312
  const scriptBody = script.replace("${DATA}", options.dataExpression ?? embeddedJson(data));
3780
4313
  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.";
3781
- const issueCount = report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "error" || diagnostic2.severity === "warn").length;
4314
+ const actionableCount = report.diagnostics.filter((diagnostic2) => diagnostic2.status !== "confirmed").length;
4315
+ const confirmedCount = report.diagnostics.filter((diagnostic2) => diagnostic2.status === "confirmed").length;
3782
4316
  const unsupportedCount = snapshot.coverage.filter((entry) => entry.status !== "complete").length;
3783
- 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.";
4317
+ 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(", ")}${report.summary.requiredRelationTargets === "internal" ? " between internal entities" : ""}.` : "Missing relation declarations are disabled by configuration; stale, conflicting, and unresolved declarations remain checked.";
3784
4318
  const statusOptions = ["confirmed", "undocumented", "stale-or-unverified", "conflict", "unresolved", "not-analyzed"];
3785
- 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 &amp; 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 &amp; 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>`;
4319
+ 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(data.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(data.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(data.revision)}</strong><span>${escapeHtml(data.revisionKind)} \xB7 pipeline ${escapeHtml(data.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 ${actionableCount ? "warn" : ""}"><b>${actionableCount}</b><span>actionable findings</span></div><div class="metric"><b>${confirmedCount}</b><span>confirmed checks</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 &amp; 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 actionable 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">${actionableCount} actionable findings \xB7 ${confirmedCount} confirmed checks</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><span class="chip"><b>${confirmedCount}</b> confirmed checks</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 &amp; 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(data.snapshotHash)}</div><div><b>Report</b>${escapeHtml(data.reportHash)}</div><div><b>Configuration</b>${escapeHtml(data.configurationHash)}</div><div><b>Analyzers</b>${escapeHtml(Object.entries(data.analyzerVersions).map(([name, version]) => `${name} ${version}`).join(", "))}</div><div><b>Source revision</b>${escapeHtml(data.revision)} (${escapeHtml(data.revisionKind)})</div><div><b>Mode</b>${options.privacy === "anonymized" ? "Anonymized read-only browser viewer; evidence paths and project identity are redacted." : "Read-only browser viewer; approvals and fixes remain outside this artifact."}</div></section></main><script>${scriptBody}</script></body></html>`;
3786
4320
  };
3787
4321
  var parseReportInput = (input) => {
3788
4322
  if (!input || typeof input !== "object") throw new Error("Input must contain snapshot and report artifacts.");
@@ -3793,6 +4327,8 @@ var parseReportInput = (input) => {
3793
4327
  return { snapshot, report };
3794
4328
  };
3795
4329
  var chunkScript = (payload) => `globalThis.__DOC_BRIDGE_DATA__=Object.assign(globalThis.__DOC_BRIDGE_DATA__||{},${embeddedJson(payload)});`;
4330
+ var levelChunkScript = (payload) => `globalThis.__DOC_BRIDGE_LEVEL_PAYLOAD__=${embeddedJson(payload)};`;
4331
+ var detailChunkScript = (payload) => `globalThis.__DOC_BRIDGE_DETAIL_PAYLOAD__=${embeddedJson(payload)};`;
3796
4332
  var byteLength = (value) => new TextEncoder().encode(value).byteLength;
3797
4333
  var renderOfflineReport = (input, options = {}) => {
3798
4334
  try {
@@ -3804,30 +4340,110 @@ var renderOfflineReport = (input, options = {}) => {
3804
4340
  var renderOfflineReportArtifact = (input, options = {}) => {
3805
4341
  const parsed = parseReportInput(input);
3806
4342
  const thresholdBytes = options.thresholdBytes ?? DEFAULT_LARGE_REPORT_THRESHOLD_BYTES;
3807
- const renderOptions = { includeSnippets: options.includeSnippets === true, thresholdBytes };
4343
+ const renderOptions = { includeSnippets: options.includeSnippets === true, privacy: options.privacy ?? "private", thresholdBytes };
3808
4344
  const singleFile = render(parsed, options);
3809
4345
  if (byteLength(singleFile) <= thresholdBytes) {
3810
4346
  const files2 = { "index.html": sha256NormalizedV1(singleFile) };
3811
- 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 };
4347
+ const manifestPayload2 = { schemaVersion: 1, mode: "single-file", generatedBy: "@agentskit/doc-bridge", snapshotHash: parsed.snapshot.contentHash, reportHash: parsed.report.contentHash, configurationHash: parsed.snapshot.configurationHash, sourceRevision: options.privacy === "anonymized" ? "redacted" : parsed.snapshot.sourceRevision, renderOptions, files: files2 };
3812
4348
  const manifest2 = JSON.stringify({ ...manifestPayload2, artifactHash: sha256NormalizedV1(manifestPayload2) }, null, 2);
3813
4349
  return { mode: "single-file", indexHtml: singleFile, files: { "index.html": singleFile }, manifest: manifest2 };
3814
4350
  }
3815
- const data = reportData(parsed.snapshot, parsed.report, options.includeSnippets === true);
4351
+ const data = reportData(parsed.snapshot, parsed.report, options.includeSnippets === true, options.privacy);
4352
+ const entitiesById = new Map(data.entities.map((entity) => [entity.id, entity]));
4353
+ const packageEntities = data.entities.filter((entity) => entity.kind === "package");
4354
+ const packageIds = new Set(packageEntities.map((entity) => entity.id));
4355
+ const packageRelations = data.relations.filter((relation) => packageIds.has(relation.from) && packageIds.has(relation.to));
4356
+ const diagnosticGroup = Object.fromEntries(data.diagnostics.map((diagnostic2) => {
4357
+ const groups = /* @__PURE__ */ new Set();
4358
+ for (const entityId2 of diagnostic2.entityIds ?? []) {
4359
+ const groupId = data.view.entityGroup[entityId2];
4360
+ if (groupId) groups.add(groupId);
4361
+ }
4362
+ for (const relationId of diagnostic2.relationIds ?? []) {
4363
+ const relation = data.relations.find((candidate) => candidate.id === relationId);
4364
+ if (!relation) continue;
4365
+ for (const endpoint of [relation.from, relation.to]) {
4366
+ const groupId = data.view.entityGroup[endpoint];
4367
+ if (groupId) groups.add(groupId);
4368
+ }
4369
+ }
4370
+ return [diagnostic2.id, [...groups].sort()];
4371
+ }));
4372
+ const diagnosticRelationFindings = /* @__PURE__ */ new Map();
4373
+ for (const diagnostic2 of data.diagnostics) {
4374
+ for (const relationId of diagnostic2.relationIds ?? []) {
4375
+ const findings = diagnosticRelationFindings.get(relationId) ?? [];
4376
+ findings.push({ status: diagnostic2.status, severity: diagnostic2.severity });
4377
+ diagnosticRelationFindings.set(relationId, findings);
4378
+ }
4379
+ }
4380
+ const containsRelations = data.relations.filter((relation) => relation.kind === "contains");
4381
+ const collectScope = (roots) => {
4382
+ const ids = new Set(roots);
4383
+ let changed = true;
4384
+ while (changed) {
4385
+ changed = false;
4386
+ for (const relation of containsRelations) {
4387
+ if (ids.has(relation.from) && !ids.has(relation.to)) {
4388
+ ids.add(relation.to);
4389
+ changed = true;
4390
+ }
4391
+ }
4392
+ }
4393
+ return ids;
4394
+ };
4395
+ const scopedPayload = (ids) => ({
4396
+ entities: data.entities.filter((entity) => ids.has(entity.id)),
4397
+ relations: data.relations.filter((relation) => ids.has(relation.from) && ids.has(relation.to))
4398
+ });
4399
+ const packagePayload = (ids) => ({
4400
+ entities: data.entities.filter((entity) => ids.has(entity.id) && entity.kind === "package"),
4401
+ relations: data.relations.filter((relation) => ids.has(relation.from) && ids.has(relation.to) && entitiesById.get(relation.from)?.kind === "package" && entitiesById.get(relation.to)?.kind === "package")
4402
+ });
4403
+ const compactPayload = (payload) => ({
4404
+ entities: payload.entities.map(({ evidence: _evidence, anchor: _anchor, provenance: _provenance, ...entity }) => entity),
4405
+ relations: payload.relations.map(({ evidence: _evidence, provenance: _provenance, ...relation }) => relation)
4406
+ });
4407
+ const levelFiles = {};
4408
+ const detailFiles = {};
4409
+ for (const [groupId, group] of Object.entries(data.view.groups)) {
4410
+ const ids = new Set(group.members.filter((id) => entitiesById.get(id)?.kind === "package"));
4411
+ const chunkName = data.view.levelChunks.groups[groupId];
4412
+ if (chunkName) levelFiles[chunkName] = levelChunkScript(packagePayload(ids));
4413
+ }
4414
+ const packageIdList = packageEntities.map((entity) => entity.id);
4415
+ const packageIdSet = new Set(packageIdList);
4416
+ levelFiles[data.view.levelChunks.default] = levelChunkScript(scopedPayload(packageIdSet));
4417
+ const sortedPackageIds = [...packageIds].sort();
4418
+ for (const [index, packageId2] of sortedPackageIds.entries()) {
4419
+ const chunkName = data.view.levelChunks.packages[index];
4420
+ const packageScope = scopedPayload(collectScope([packageId2]));
4421
+ if (chunkName) levelFiles[chunkName] = levelChunkScript(compactPayload(packageScope));
4422
+ const detailName = chunkName?.replace("chunks/levels-package-", "chunks/details-");
4423
+ if (detailName) detailFiles[detailName] = detailChunkScript(packageScope);
4424
+ }
4425
+ const overviewView = {
4426
+ ...data.view,
4427
+ entityGroup: Object.fromEntries(Object.entries(data.view.entityGroup).filter(([id]) => data.entities.find((entity) => entity.id === id)?.kind === "package")),
4428
+ diagnosticGroup,
4429
+ diagnosticRelationFindings: Object.fromEntries(diagnosticRelationFindings)
4430
+ };
3816
4431
  const files = {
3817
- "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 }),
3818
- "chunks/levels.js": chunkScript({ entities: data.entities, relations: data.relations }),
4432
+ "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, actionableCount: data.actionableCount, confirmedCount: data.confirmedCount, diagnosticSummary: data.diagnosticSummary, documentation: data.documentation, requiredRelationKinds: data.requiredRelationKinds, entities: packageEntities, relations: packageRelations, view: overviewView }),
4433
+ ...levelFiles,
4434
+ ...detailFiles,
3819
4435
  "chunks/findings.js": chunkScript({ diagnostics: data.diagnostics })
3820
4436
  };
3821
- 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>` });
4437
+ const indexHtml = render(parsed, { ...options, dataExpression: "globalThis.__DOC_BRIDGE_DATA__", dataScripts: `<script>globalThis.__DOC_BRIDGE_HAS_LEVEL_CHUNKS__=true;globalThis.__DOC_BRIDGE_LAZY_CHUNKS__=["chunks/findings.js"]</script><script src="chunks/overview.js"></script>` });
3822
4438
  const fileHashes = Object.fromEntries(Object.entries({ ...files, "index.html": indexHtml }).map(([file, content]) => [file, sha256NormalizedV1(content)]));
3823
- 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 };
4439
+ const manifestPayload = { schemaVersion: 1, mode: "directory", generatedBy: "@agentskit/doc-bridge", snapshotHash: parsed.snapshot.contentHash, reportHash: parsed.report.contentHash, configurationHash: parsed.snapshot.configurationHash, sourceRevision: options.privacy === "anonymized" ? "redacted" : parsed.snapshot.sourceRevision, renderOptions, files: fileHashes };
3824
4440
  const manifest = JSON.stringify({ ...manifestPayload, artifactHash: sha256NormalizedV1(manifestPayload) }, null, 2);
3825
4441
  return { mode: "directory", indexHtml, files: { ...files, "index.html": indexHtml }, manifest };
3826
4442
  };
3827
4443
 
3828
4444
  // src/fixes/proposals.ts
3829
- import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as readFileSync10, realpathSync as realpathSync7, renameSync, statSync as statSync4, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
3830
- import { basename as basename5, dirname as dirname6, extname as extname2, join as join10, relative as relative6, resolve as resolve9, sep as sep7 } from "path";
4445
+ import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as readFileSync10, realpathSync as realpathSync7, renameSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
4446
+ import { basename as basename5, dirname as dirname5, extname as extname2, join as join10, relative as relative6, resolve as resolve9, sep as sep7 } from "path";
3831
4447
  var hash2 = (value) => sha256NormalizedV1(value);
3832
4448
  var artifactMetadata = (root, options) => ({
3833
4449
  schemaVersion: 1,
@@ -3883,12 +4499,12 @@ var createMarkdownLinkFixProposal = (root, options) => {
3883
4499
  const target = match[3];
3884
4500
  if (!target || match[1] === "!" || /^(?:[a-z]+:|\/|#)/i.test(target)) continue;
3885
4501
  const targetPath = target.split("#")[0]?.split("?")[0];
3886
- if (!targetPath || existsSync11(resolve9(projectRoot, dirname6(path), targetPath))) continue;
4502
+ if (!targetPath || existsSync11(resolve9(projectRoot, dirname5(path), targetPath))) continue;
3887
4503
  const targetStem = basename5(targetPath, extname2(targetPath)).toLowerCase();
3888
4504
  const labelStem = (match[2] ?? "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
3889
4505
  const candidates = paths.filter((candidate) => basename5(candidate, extname2(candidate)).toLowerCase() === targetStem || basename5(candidate, extname2(candidate)).toLowerCase().replace(/[^a-z0-9]+/g, "") === labelStem);
3890
4506
  if (candidates.length !== 1) continue;
3891
- let replacement = relative6(dirname6(path), candidates[0]).split(sep7).join("/");
4507
+ let replacement = relative6(dirname5(path), candidates[0]).split(sep7).join("/");
3892
4508
  if (target.startsWith("./") && !replacement.startsWith(".")) replacement = `./${replacement}`;
3893
4509
  next = next.replace(match[0], match[0].replace(target, replacement));
3894
4510
  }
@@ -3900,8 +4516,13 @@ var createArtifactNormalizationProposal = (root, artifactPath, options) => {
3900
4516
  const projectRoot = realpathSync7.native(resolve9(root));
3901
4517
  const path = artifactPath.split(sep7).join("/");
3902
4518
  const absolute = containedPath(projectRoot, path);
3903
- if (!absolute || !existsSync11(absolute) || !statSync4(absolute).isFile()) return void 0;
3904
- const before = readFileSync10(absolute, "utf8");
4519
+ if (!absolute) return void 0;
4520
+ let before;
4521
+ try {
4522
+ before = readFileSync10(absolute, "utf8");
4523
+ } catch {
4524
+ return void 0;
4525
+ }
3905
4526
  let after;
3906
4527
  try {
3907
4528
  after = `${JSON.stringify(sortJson(JSON.parse(before)), null, 2)}
@@ -3991,6 +4612,22 @@ var appendTransition = (stateDir, transition2) => {
3991
4612
  `, "utf8");
3992
4613
  };
3993
4614
  var transition = (run2, to, reason) => {
4615
+ const allowed = {
4616
+ created: ["created", "discovering", "failed", "cancelled"],
4617
+ discovering: ["discovering", "analyzed", "failed", "cancelled", "stale"],
4618
+ analyzed: ["analyzed", "compared", "failed", "cancelled", "stale"],
4619
+ compared: ["compared", "proposed", "failed", "cancelled", "stale"],
4620
+ "awaiting-agent": ["awaiting-agent", "proposed", "failed", "cancelled", "stale"],
4621
+ proposed: ["proposed", "validating", "delivered", "failed", "cancelled", "stale"],
4622
+ "awaiting-approval": ["awaiting-approval", "validating", "failed", "cancelled", "stale"],
4623
+ validating: ["validating", "delivered", "failed", "cancelled", "stale"],
4624
+ delivered: ["delivered", "stale", "failed", "cancelled"],
4625
+ failed: ["failed", "discovering", "analyzed", "compared", "proposed", "validating", "delivered", "cancelled"],
4626
+ cancelled: ["cancelled", "discovering", "analyzed", "compared", "proposed", "validating", "delivered"],
4627
+ stale: [],
4628
+ superseded: []
4629
+ };
4630
+ if (!allowed[run2.state].includes(to)) throw new Error(`Illegal workflow transition ${run2.state} -> ${to}.`);
3994
4631
  const item = {
3995
4632
  from: run2.state,
3996
4633
  to,
@@ -4000,14 +4637,25 @@ var transition = (run2, to, reason) => {
4000
4637
  return { ...run2, state: to, transitions: [...run2.transitions, item] };
4001
4638
  };
4002
4639
  var runId = () => `${Date.now()}-${process.pid}`;
4003
- var stageInputHash = (options, stage, input) => sha256NormalizedV1({ stage, input, sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, toolVersion: options.toolVersion ?? "1.0.0" });
4640
+ var stageInputHash = (options, stage, input) => sha256NormalizedV1({ stage, input, sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, pipelineVersion: options.pipelineVersion ?? "1.0.0", analyzerVersions: options.analyzerVersions ?? {}, toolVersion: options.toolVersion ?? "1.0.0" });
4004
4641
  var stageArtifactPath = (stateDir, stage, inputHash) => join11(stateDir, "artifacts", `${stage}-${inputHash}.json`);
4005
4642
  var readArtifact = (path) => JSON.parse(readFileSync11(path, "utf8"));
4006
- var stepArtifactPath = (stateDir, step) => resolve10(stateDir, step.artifactRefs?.[0] ?? "");
4643
+ var readVerifiedArtifact = (path, stage, step) => {
4644
+ const artifact2 = readArtifact(path);
4645
+ if (artifact2.type !== "workflow-step-artifact" || artifact2.stage !== stage || artifact2.inputHash !== step.inputHash) throw new Error(`Invalid workflow artifact for stage "${stage}".`);
4646
+ if (sha256NormalizedV1(artifact2.value) !== artifact2.outputHash || artifact2.outputHash !== step.outputHash) throw new Error(`Workflow artifact hash mismatch for stage "${stage}".`);
4647
+ return artifact2;
4648
+ };
4649
+ var stepArtifactPath = (stateDir, step) => {
4650
+ const path = resolve10(stateDir, step.artifactRefs?.[0] ?? "");
4651
+ const pathRelativeToState = relative7(stateDir, path);
4652
+ if (pathRelativeToState.startsWith("..") || pathRelativeToState.startsWith("/")) throw new Error(`Workflow artifact escapes state directory for stage "${step.name}".`);
4653
+ return path;
4654
+ };
4007
4655
  var stepOutput = (stateDir, run2, stage) => {
4008
4656
  const step = run2.steps.find((item) => item.name === stage);
4009
4657
  if (!step || step.status !== "completed" || !step.artifactRefs?.[0]) return null;
4010
- return readArtifact(stepArtifactPath(stateDir, step)).value;
4658
+ return readVerifiedArtifact(stepArtifactPath(stateDir, step), stage, step).value;
4011
4659
  };
4012
4660
  var acquireLock = (stateDir) => {
4013
4661
  const lock = join11(stateDir, ".lock");
@@ -4034,7 +4682,7 @@ var loadManifest = (stateDir) => {
4034
4682
  return WorkflowRunV1Schema.parse(JSON.parse(readFileSync11(path, "utf8")));
4035
4683
  };
4036
4684
  var baseRun = (options, stateDir, supersedes) => {
4037
- const inputHash = sha256NormalizedV1({ sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, toolVersion: options.toolVersion ?? "1.0.0" });
4685
+ const inputHash = sha256NormalizedV1({ sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, pipelineVersion: options.pipelineVersion ?? "1.0.0", analyzerVersions: options.analyzerVersions ?? {}, toolVersion: options.toolVersion ?? "1.0.0" });
4038
4686
  return WorkflowRunV1Schema.parse({
4039
4687
  type: "workflow-run",
4040
4688
  schemaVersion: 1,
@@ -4044,8 +4692,8 @@ var baseRun = (options, stateDir, supersedes) => {
4044
4692
  sourceRevision: options.sourceRevision,
4045
4693
  sourceRevisionKind: "content",
4046
4694
  configurationHash: options.configurationHash,
4047
- pipelineVersion: "1.0.0",
4048
- analyzerVersions: { workflow: options.toolVersion ?? "1.0.0" },
4695
+ pipelineVersion: options.pipelineVersion ?? "1.0.0",
4696
+ analyzerVersions: { ...options.analyzerVersions ?? {}, workflow: options.toolVersion ?? "1.0.0" },
4049
4697
  runId: options.runId ?? runId(),
4050
4698
  state: "created",
4051
4699
  steps: WORKFLOW_STAGES.map((name) => ({ name, status: "pending", inputHash })),
@@ -4054,7 +4702,7 @@ var baseRun = (options, stateDir, supersedes) => {
4054
4702
  });
4055
4703
  };
4056
4704
  var withHash = (run2) => WorkflowRunV1Schema.parse({ ...run2, contentHash: contentHashForArtifactV1(run2) });
4057
- var sameInputs = (run2, options) => run2.sourceRevision === options.sourceRevision && run2.configurationHash === options.configurationHash && run2.analyzerVersions.workflow === (options.toolVersion ?? "1.0.0");
4705
+ var sameInputs = (run2, options) => run2.sourceRevision === options.sourceRevision && run2.configurationHash === options.configurationHash && run2.pipelineVersion === (options.pipelineVersion ?? "1.0.0") && sha256NormalizedV1(run2.analyzerVersions) === sha256NormalizedV1({ ...options.analyzerVersions ?? {}, workflow: options.toolVersion ?? "1.0.0" });
4058
4706
  var selectedStages = (stage) => stage && stage !== "all" ? [stage] : WORKFLOW_STAGES;
4059
4707
  var runWorkflow = (options) => {
4060
4708
  const root = resolve10(options.root);
@@ -4079,7 +4727,17 @@ var runWorkflow = (options) => {
4079
4727
  if (!run2) throw new Error("Workflow manifest was not initialized.");
4080
4728
  const firstSelectedStage = selectedStages(options.stage)[0];
4081
4729
  const previousStageIndex = firstSelectedStage ? WORKFLOW_STAGES.indexOf(firstSelectedStage) - 1 : -1;
4082
- let previousOutput = previousStageIndex >= 0 ? stepOutput(stateDir, run2, WORKFLOW_STAGES[previousStageIndex]) : null;
4730
+ let previousOutput = null;
4731
+ if (previousStageIndex >= 0) {
4732
+ try {
4733
+ previousOutput = stepOutput(stateDir, run2, WORKFLOW_STAGES[previousStageIndex]);
4734
+ } catch (error) {
4735
+ run2 = withHash(transition(run2, "failed", error instanceof Error ? error.message : String(error)));
4736
+ appendTransition(stateDir, run2.transitions[run2.transitions.length - 1]);
4737
+ writeManifest(stateDir, run2);
4738
+ throw error;
4739
+ }
4740
+ }
4083
4741
  const reusedStages = [];
4084
4742
  for (const stage of selectedStages(options.stage)) {
4085
4743
  const input = options.inputs?.[stage] ?? previousOutput;
@@ -4087,10 +4745,23 @@ var runWorkflow = (options) => {
4087
4745
  const existing = run2.steps.find((step) => step.name === stage);
4088
4746
  const artifactPath = existing?.artifactRefs?.[0] ? resolve10(stateDir, existing.artifactRefs[0]) : stageArtifactPath(stateDir, stage, inputHash);
4089
4747
  if (existing?.status === "completed" && existing.inputHash === inputHash && existing.outputHash && existsSync12(artifactPath)) {
4090
- previousOutput = readArtifact(artifactPath).value;
4748
+ try {
4749
+ previousOutput = readVerifiedArtifact(artifactPath, stage, existing).value;
4750
+ } catch (error) {
4751
+ run2 = withHash(transition(run2, "failed", error instanceof Error ? error.message : String(error)));
4752
+ appendTransition(stateDir, run2.transitions[run2.transitions.length - 1]);
4753
+ writeManifest(stateDir, run2);
4754
+ throw error;
4755
+ }
4091
4756
  reusedStages.push(stage);
4092
4757
  continue;
4093
4758
  }
4759
+ if (options.shouldCancel?.()) {
4760
+ run2 = withHash(transition(run2, "cancelled", `Workflow cancellation requested before stage "${stage}".`));
4761
+ appendTransition(stateDir, run2.transitions[run2.transitions.length - 1]);
4762
+ writeManifest(stateDir, run2);
4763
+ return { run: run2, stateDir, reusedStages };
4764
+ }
4094
4765
  const handler = options.handlers[stage];
4095
4766
  if (!handler) throw new Error(`No handler configured for workflow stage "${stage}".`);
4096
4767
  run2 = withHash(transition(run2, stageState[stage]));
@@ -4241,8 +4912,20 @@ var evaluateRules = (report, options = {}) => {
4241
4912
  const sortedFindings = [...findings].sort((a, b) => a.id.localeCompare(b.id));
4242
4913
  return { mode: resolved.mode, findings: sortedFindings, exitCode: sortedFindings.some((finding) => finding.severity === "error") ? 1 : 0 };
4243
4914
  };
4244
- var parseRuleId = (value) => RuleIdSchema.parse(value);
4245
- var parseRuleSeverity = (value) => RuleSeveritySchema.parse(value);
4915
+ var parseRuleId = (value) => {
4916
+ try {
4917
+ return RuleIdSchema.parse(value);
4918
+ } catch {
4919
+ throw new Error(`Invalid enum value: ${value}`);
4920
+ }
4921
+ };
4922
+ var parseRuleSeverity = (value) => {
4923
+ try {
4924
+ return RuleSeveritySchema.parse(value);
4925
+ } catch {
4926
+ throw new Error(`Invalid enum value: ${value}`);
4927
+ }
4928
+ };
4246
4929
 
4247
4930
  // src/federation/ecosystem-llms.ts
4248
4931
  function formatEcosystemLlmsBlock(options) {
@@ -4274,7 +4957,7 @@ function formatEcosystemLlmsSection(options) {
4274
4957
  import { readFileSync as readFileSync14 } from "fs";
4275
4958
 
4276
4959
  // src/conformance/documentation-standard-v1.ts
4277
- import { existsSync as existsSync13, readFileSync as readFileSync12, realpathSync as realpathSync8, statSync as statSync5 } from "fs";
4960
+ import { closeSync as closeSync2, existsSync as existsSync13, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync12, realpathSync as realpathSync8 } from "fs";
4278
4961
  import { isAbsolute as isAbsolute5, relative as relative8, resolve as resolve11, sep as sep8 } from "path";
4279
4962
 
4280
4963
  // src/conformance/ecosystem-contract.ts
@@ -4479,11 +5162,10 @@ var fileEvidence = (root, path, options) => {
4479
5162
  evidence: { path, detail: "Path escapes the project root." }
4480
5163
  };
4481
5164
  }
4482
- if (!existsSync13(abs)) {
4483
- return { exists: false, content: "", evidence: { path, detail: "File does not exist." } };
4484
- }
5165
+ let fd;
4485
5166
  try {
4486
- const stat = statSync5(abs);
5167
+ fd = openSync2(abs, "r");
5168
+ const stat = fstatSync2(fd);
4487
5169
  if (!stat.isFile()) {
4488
5170
  return { exists: false, content: "", evidence: { path, detail: "Path is not a regular file." } };
4489
5171
  }
@@ -4504,7 +5186,7 @@ var fileEvidence = (root, path, options) => {
4504
5186
  evidence: { path, detail: `Text evidence exceeds ${MAX_TEXT_EVIDENCE_BYTES} bytes.` }
4505
5187
  };
4506
5188
  }
4507
- const content = readFileSync12(abs, "utf8");
5189
+ const content = readFileSync12(fd, "utf8");
4508
5190
  return {
4509
5191
  exists: content.trim().length > 0,
4510
5192
  content,
@@ -4513,8 +5195,15 @@ var fileEvidence = (root, path, options) => {
4513
5195
  detail: content.trim().length > 0 ? "File exists and is non-empty." : "File is empty."
4514
5196
  }
4515
5197
  };
4516
- } catch {
4517
- return { exists: false, content: "", evidence: { path, detail: "File is not readable text." } };
5198
+ } catch (error) {
5199
+ const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
5200
+ return {
5201
+ exists: false,
5202
+ content: "",
5203
+ evidence: { path, detail: code === "ENOENT" ? "File does not exist." : "File is not readable text." }
5204
+ };
5205
+ } finally {
5206
+ if (fd !== void 0) closeSync2(fd);
4518
5207
  }
4519
5208
  };
4520
5209
  var resultWithException = (draft, options) => {
@@ -5779,7 +6468,7 @@ var startMcpStdioServer = (ctx) => {
5779
6468
  // src/mcp/install.ts
5780
6469
  import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync17, writeFileSync as writeFileSync6 } from "fs";
5781
6470
  import { homedir } from "os";
5782
- import { dirname as dirname7, join as join15, resolve as resolve14 } from "path";
6471
+ import { dirname as dirname6, join as join15, resolve as resolve14 } from "path";
5783
6472
  var SERVER_NAME = "ak-docs";
5784
6473
  var mcpServerEntry = (root) => ({
5785
6474
  command: "npx",
@@ -5796,7 +6485,7 @@ var readJson2 = (path) => {
5796
6485
  }
5797
6486
  };
5798
6487
  var writeJson = (path, value) => {
5799
- mkdirSync5(dirname7(path), { recursive: true });
6488
+ mkdirSync5(dirname6(path), { recursive: true });
5800
6489
  writeFileSync6(path, `${JSON.stringify(value, null, 2)}
5801
6490
  `, "utf8");
5802
6491
  };
@@ -5876,16 +6565,6 @@ var gradeForScore = (score) => {
5876
6565
  if (score >= 40) return "D";
5877
6566
  return "F";
5878
6567
  };
5879
- var agentDocPaths = (index) => {
5880
- const paths = /* @__PURE__ */ new Set();
5881
- for (const owner of Object.values(index.lookup?.ownership ?? {})) {
5882
- if (owner.agentDoc) paths.add(owner.agentDoc);
5883
- }
5884
- for (const handoff of Object.values(index.handoffs ?? {})) {
5885
- if (handoff.startHere) paths.add(handoff.startHere);
5886
- }
5887
- return paths;
5888
- };
5889
6568
  var computeScore = (coverage) => {
5890
6569
  let score = 0;
5891
6570
  if (coverage.freshness.hasIndex) score += 15;
@@ -5973,7 +6652,7 @@ var runDoctor = (root, config) => {
5973
6652
  let index;
5974
6653
  let hasIndex = true;
5975
6654
  let freshnessOk = false;
5976
- let freshnessMessage = "Index is fresh";
6655
+ let freshnessMessage;
5977
6656
  try {
5978
6657
  index = loadDocBridgeIndex(root, config);
5979
6658
  const next = buildDocBridgeIndex({ root, config, write: false }).index.contentHash;
@@ -5993,8 +6672,6 @@ var runDoctor = (root, config) => {
5993
6672
  const missingAgentDoc = ownership.filter(([, owner]) => !owner.agentDoc || owner.agentDoc === config.corpus.agent.index).map(([id]) => id);
5994
6673
  const missingHumanDoc = ownership.filter(([, owner]) => !owner.humanDoc).map(([id]) => id);
5995
6674
  const indexedPaths = new Set(index.knowledge.map((entry) => entry.path));
5996
- const expectedAgentDocs = agentDocPaths(index);
5997
- const unindexed = [...expectedAgentDocs].filter((path) => !indexedPaths.has(path));
5998
6675
  const corpusDocs = scanAgentCorpus(root, config).filter(
5999
6676
  (doc) => doc.path !== config.corpus.agent.index
6000
6677
  );
@@ -6073,7 +6750,7 @@ var formatDoctorText = (report) => {
6073
6750
 
6074
6751
  // src/index-builder/watch-index.ts
6075
6752
  import { existsSync as existsSync17, watch } from "fs";
6076
- import { dirname as dirname8, resolve as resolve15 } from "path";
6753
+ import { dirname as dirname7, resolve as resolve15 } from "path";
6077
6754
  var WATCH_PATTERN = /\.(md|mdx|json|ya?ml|mdc)$/i;
6078
6755
  var NX_MANIFEST_PATTERN = /(^|[/\\])(project|package)\.json$/i;
6079
6756
  var collectWatchRoots = (root, config, configPath) => {
@@ -6087,7 +6764,7 @@ var collectWatchRoots = (root, config, configPath) => {
6087
6764
  if (typeof value === "string" && value.length) roots.add(resolve15(root, value));
6088
6765
  }
6089
6766
  }
6090
- if (configPath) roots.add(dirname8(resolve15(configPath)));
6767
+ if (configPath) roots.add(dirname7(resolve15(configPath)));
6091
6768
  return [...roots].filter((dir) => existsSync17(dir));
6092
6769
  };
6093
6770
  var watchDocBridgeIndex = (opts) => {
@@ -6157,7 +6834,7 @@ var watchDocBridgeIndex = (opts) => {
6157
6834
  // src/memory/github-pr.ts
6158
6835
  import { execFileSync as execFileSync2, spawnSync } from "child_process";
6159
6836
  import { existsSync as existsSync18, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
6160
- import { join as join17 } from "path";
6837
+ import { join as join16 } from "path";
6161
6838
  var run = (cmd, args, cwd) => {
6162
6839
  const result = spawnSync(cmd, [...args], { cwd, encoding: "utf8" });
6163
6840
  const out = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
@@ -6165,10 +6842,10 @@ var run = (cmd, args, cwd) => {
6165
6842
  };
6166
6843
  var hasGh = () => run("gh", ["--version"], process.cwd()).ok;
6167
6844
  var slug = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
6168
- var defaultPromotionDraftPath = (root) => join17(root, ".doc-bridge", "drafts", `memory-promotion-${slug()}.md`);
6845
+ var defaultPromotionDraftPath = (root) => join16(root, ".doc-bridge", "drafts", `memory-promotion-${slug()}.md`);
6169
6846
  var writePromotionDraft = (root, draft, path) => {
6170
6847
  const draftPath = path ?? defaultPromotionDraftPath(root);
6171
- mkdirSync6(join17(root, ".doc-bridge", "drafts"), { recursive: true });
6848
+ mkdirSync6(join16(root, ".doc-bridge", "drafts"), { recursive: true });
6172
6849
  writeFileSync7(draftPath, `${draft.body}
6173
6850
  `, "utf8");
6174
6851
  return draftPath;
@@ -6204,7 +6881,7 @@ var promoteMemoryToGithubPr = (root, draft, options = {}) => {
6204
6881
  message: `Wrote draft to ${relDraft}. Run the printed git/gh commands to open a draft PR.`
6205
6882
  };
6206
6883
  }
6207
- if (!existsSync18(join17(root, ".git"))) {
6884
+ if (!existsSync18(join16(root, ".git"))) {
6208
6885
  return {
6209
6886
  ok: false,
6210
6887
  dryRun: false,
@@ -6306,6 +6983,174 @@ ${auth.out}`
6306
6983
  };
6307
6984
  };
6308
6985
 
6986
+ // src/plugins/contract.ts
6987
+ import { z as z9 } from "zod";
6988
+ var ANALYZER_PLUGIN_CONTRACT_VERSION = 1;
6989
+ var AnalyzerPluginManifestSchema = z9.object({
6990
+ id: z9.string().regex(/^[a-z][a-z0-9-]*$/).max(128),
6991
+ version: z9.string().min(1).max(64),
6992
+ languages: z9.array(z9.string().min(1).max(64)).min(1).max(32),
6993
+ frameworks: z9.array(z9.string().min(1).max(128)).max(32).default([]),
6994
+ capabilities: z9.array(z9.string().min(1).max(128)).min(1).max(32),
6995
+ knowledgeSchemaVersion: z9.literal(1),
6996
+ compatibility: z9.object({ pipelineMajor: z9.number().int().nonnegative() }).strict(),
6997
+ unsupportedConstructs: z9.array(z9.string().min(1).max(256)).max(128).default([]),
6998
+ resourceLimits: z9.object({ maxFiles: z9.number().int().positive().optional(), maxBytes: z9.number().int().positive().optional() }).strict().default({})
6999
+ }).strict();
7000
+ var AnalyzerPluginOutputSchema = z9.object({
7001
+ entities: z9.array(EntitySchema).max(5e4).default([]),
7002
+ relations: z9.array(RelationSchema).max(1e5).default([]),
7003
+ coverage: z9.array(CoverageSchema).max(1e3).default([]),
7004
+ diagnostics: z9.array(DiagnosticSchema).max(1e5).default([])
7005
+ }).strict();
7006
+ var pipelineMajor = (version) => Number.parseInt(version.split(".")[0] ?? "", 10);
7007
+ var createAnalyzerRegistry = (options = {}) => {
7008
+ const plugins = /* @__PURE__ */ new Map();
7009
+ const pipeline = pipelineMajor(options.pipelineVersion ?? "1.0.0");
7010
+ return {
7011
+ register(plugin) {
7012
+ const manifest = AnalyzerPluginManifestSchema.parse(plugin.manifest);
7013
+ if (manifest.compatibility.pipelineMajor !== pipeline) throw new Error(`Analyzer plugin "${manifest.id}" requires pipeline major ${manifest.compatibility.pipelineMajor}; current pipeline is ${pipeline}.`);
7014
+ if (plugins.has(manifest.id)) throw new Error(`Analyzer plugin "${manifest.id}" is already registered.`);
7015
+ if (options.maxPlugins !== void 0 && plugins.size >= options.maxPlugins) throw new Error(`Analyzer plugin limit ${options.maxPlugins} exceeded.`);
7016
+ plugins.set(manifest.id, { ...plugin, manifest });
7017
+ },
7018
+ list() {
7019
+ return [...plugins.values()].map((plugin) => plugin.manifest).sort((a, b) => a.id.localeCompare(b.id));
7020
+ },
7021
+ async analyze(id, input) {
7022
+ const plugin = plugins.get(id);
7023
+ if (!plugin) throw new Error(`Analyzer plugin "${id}" is not registered.`);
7024
+ const { maxFiles, maxBytes } = plugin.manifest.resourceLimits;
7025
+ const bytes = input.files.reduce((total, file) => total + file.bytes, 0);
7026
+ if (maxFiles !== void 0 && input.files.length > maxFiles) throw new Error(`Analyzer plugin "${id}" file limit ${maxFiles} exceeded.`);
7027
+ if (maxBytes !== void 0 && bytes > maxBytes) throw new Error(`Analyzer plugin "${id}" byte limit ${maxBytes} exceeded.`);
7028
+ try {
7029
+ const output = AnalyzerPluginOutputSchema.parse(await plugin.analyze(input));
7030
+ return {
7031
+ ...output,
7032
+ coverage: output.coverage.map((entry) => ({ ...entry, analyzer: plugin.manifest.id, analyzerVersion: plugin.manifest.version }))
7033
+ };
7034
+ } catch (error) {
7035
+ return {
7036
+ entities: [],
7037
+ relations: [],
7038
+ diagnostics: [],
7039
+ coverage: [{ analyzer: plugin.manifest.id, analyzerVersion: plugin.manifest.version, scope: "plugin", status: "not-analyzed", reason: `Plugin failed safely: ${error instanceof Error ? error.message : String(error)}` }]
7040
+ };
7041
+ }
7042
+ }
7043
+ };
7044
+ };
7045
+
7046
+ // src/metrics/benchmark.ts
7047
+ import { z as z10 } from "zod";
7048
+ var BENCHMARK_SCHEMA_VERSION = 1;
7049
+ var stringList = z10.array(z10.string().min(1).max(512)).max(1e5);
7050
+ var benchmarkSets = z10.object({
7051
+ entities: stringList.default([]),
7052
+ relations: stringList.default([]),
7053
+ findings: stringList.default([])
7054
+ }).strict();
7055
+ var BenchmarkFixtureV1Schema = z10.object({
7056
+ schemaVersion: z10.literal(BENCHMARK_SCHEMA_VERSION),
7057
+ supported: benchmarkSets,
7058
+ excluded: benchmarkSets.optional()
7059
+ }).strict();
7060
+ var unique = (values) => new Set(values);
7061
+ var ratio = (numerator, denominator) => denominator === 0 ? 1 : numerator / denominator;
7062
+ var setMetrics = (actualValues, expectedValues, excludedValues) => {
7063
+ const excluded = unique(excludedValues);
7064
+ const activeActualValues = actualValues.filter((value) => !excluded.has(value));
7065
+ const actual = unique(activeActualValues);
7066
+ const expected = unique(expectedValues.filter((value) => !excluded.has(value)));
7067
+ const truePositives = [...actual].filter((value) => expected.has(value)).length;
7068
+ const falsePositives = actual.size - truePositives;
7069
+ const falseNegatives = expected.size - truePositives;
7070
+ return {
7071
+ truePositives,
7072
+ falsePositives,
7073
+ falseNegatives,
7074
+ precision: ratio(truePositives, actual.size),
7075
+ recall: ratio(truePositives, expected.size),
7076
+ duplicateCount: activeActualValues.length - actual.size
7077
+ };
7078
+ };
7079
+ var benchmarkFixture = (fixture) => BenchmarkFixtureV1Schema.parse(fixture);
7080
+ var measureBenchmark = (observation, fixture, thresholds = {}) => {
7081
+ const excluded = fixture.excluded ?? { entities: [], relations: [], findings: [] };
7082
+ const quality = {
7083
+ entities: setMetrics(observation.entities, fixture.supported.entities, excluded.entities),
7084
+ relations: setMetrics(observation.relations, fixture.supported.relations, excluded.relations),
7085
+ findings: setMetrics(observation.findings, fixture.supported.findings, excluded.findings)
7086
+ };
7087
+ const actualEntities = unique(observation.entities);
7088
+ const evidenced = unique(observation.evidenced ?? []);
7089
+ const evidenceRatio = ratio([...actualEntities].filter((id) => evidenced.has(id)).length, actualEntities.size);
7090
+ const findingDensity = ratio(unique(observation.findings).size, actualEntities.size);
7091
+ const precisionThreshold = thresholds.precision ?? 0.95;
7092
+ const recallThreshold = thresholds.recall ?? 1;
7093
+ const regressions = [];
7094
+ for (const [name, metrics] of Object.entries(quality)) {
7095
+ if (metrics.precision < precisionThreshold) regressions.push(`${name}.precision below ${precisionThreshold}`);
7096
+ if (metrics.recall < recallThreshold) regressions.push(`${name}.recall below ${recallThreshold}`);
7097
+ }
7098
+ return {
7099
+ schemaVersion: BENCHMARK_SCHEMA_VERSION,
7100
+ quality,
7101
+ evidenceRatio,
7102
+ findingDensity,
7103
+ findingCategoryDistribution: Object.fromEntries(Object.entries(observation.findingCategories ?? {}).sort(([a], [b]) => a.localeCompare(b))),
7104
+ excludedCaseCount: excluded.entities.length + excluded.relations.length + excluded.findings.length,
7105
+ excludedCaseIds: { entities: excluded.entities.length, relations: excluded.relations.length, findings: excluded.findings.length },
7106
+ thresholds: { precision: precisionThreshold, recall: recallThreshold },
7107
+ regressions
7108
+ };
7109
+ };
7110
+ var compareBenchmarkSnapshots = (previous, current) => {
7111
+ const ids = /* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(current)]);
7112
+ let added = 0;
7113
+ let removed = 0;
7114
+ let unchanged = 0;
7115
+ let reclassified = 0;
7116
+ let unchangedEvidence = 0;
7117
+ for (const id of ids) {
7118
+ if (!(id in previous)) {
7119
+ added += 1;
7120
+ continue;
7121
+ }
7122
+ if (!(id in current)) {
7123
+ removed += 1;
7124
+ continue;
7125
+ }
7126
+ if (previous[id] === current[id]) unchanged += 1;
7127
+ else reclassified += 1;
7128
+ if (previous[id] === current[id]) unchangedEvidence += 1;
7129
+ }
7130
+ return { added, removed, unchanged, reclassified, unchangedEvidence };
7131
+ };
7132
+ var percentile95 = (values) => {
7133
+ if (!values.length) return 0;
7134
+ const sorted = [...values].sort((a, b) => a - b);
7135
+ return sorted[Math.min(sorted.length - 1, Math.ceil(sorted.length * 0.95) - 1)] ?? 0;
7136
+ };
7137
+ var measureAgentEfficiency = (observation) => ({
7138
+ hitRate: ratio(observation.hits, observation.queries),
7139
+ latencyP95Ms: percentile95(observation.latencyMs),
7140
+ responseBytesP95: percentile95(observation.responseBytes),
7141
+ estimatedTokensP95: percentile95(observation.estimatedTokens),
7142
+ corpusBytes: observation.corpusBytes,
7143
+ contextReduction: observation.corpusBytes > 0 ? 1 - percentile95(observation.responseBytes) / observation.corpusBytes : 0
7144
+ });
7145
+ var formatBenchmarkText = (result) => [
7146
+ `Precision: entities ${result.quality.entities.precision.toFixed(3)}, relations ${result.quality.relations.precision.toFixed(3)}, findings ${result.quality.findings.precision.toFixed(3)}`,
7147
+ `Recall: entities ${result.quality.entities.recall.toFixed(3)}, relations ${result.quality.relations.recall.toFixed(3)}, findings ${result.quality.findings.recall.toFixed(3)}`,
7148
+ `Evidence ratio: ${result.evidenceRatio.toFixed(3)}`,
7149
+ `Finding density: ${result.findingDensity.toFixed(3)}`,
7150
+ `Excluded cases: ${result.excludedCaseCount}`,
7151
+ `Regressions: ${result.regressions.length ? result.regressions.join("; ") : "none"}`
7152
+ ].join("\n");
7153
+
6309
7154
  // src/federation/llms.ts
6310
7155
  import { existsSync as existsSync19, readFileSync as readFileSync18 } from "fs";
6311
7156
  import { resolve as resolve16 } from "path";
@@ -6320,9 +7165,20 @@ var defaultFetchText = async (url) => {
6320
7165
  if (!res.ok) throw new Error(`Failed to fetch ${url}: ${res.status}`);
6321
7166
  return res.text();
6322
7167
  };
7168
+ var httpUrl = (value) => {
7169
+ try {
7170
+ const parsed = new URL(value);
7171
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return void 0;
7172
+ if (parsed.username || parsed.password) return void 0;
7173
+ return parsed.href;
7174
+ } catch {
7175
+ return void 0;
7176
+ }
7177
+ };
6323
7178
  var sourceText = async (root, source, fetchText) => {
6324
7179
  try {
6325
- if (/^https?:\/\//.test(source)) return await fetchText(source);
7180
+ const remote = httpUrl(source);
7181
+ if (remote) return await fetchText(remote);
6326
7182
  const path = resolve16(root, source);
6327
7183
  if (!existsSync19(path)) return null;
6328
7184
  return readFileSync18(path, "utf8");
@@ -6335,22 +7191,57 @@ var sameOrigin = (base, target) => {
6335
7191
  return new URL(base).origin === new URL(target).origin;
6336
7192
  };
6337
7193
  var parseLlmsTxtLinks = (raw) => {
6338
- const links = [...raw.matchAll(/\[([^\]]+)\]\(([^)]+)\)(?::\s*([^\n]+))?/g)].map((match) => ({
6339
- title: match[1] ?? match[2] ?? "link",
6340
- url: match[2] ?? "",
6341
- ...match[3]?.trim() ? { description: match[3].trim() } : {}
6342
- })).filter((link) => link.url);
6343
- for (const match of raw.matchAll(/(?:^|\s)(?:Raw|llms\.txt|Full bundle|ZIP bundle)?:?\s*(https?:\/\/\S+)/gi)) {
6344
- const url = match[1]?.replace(/[),.;]+$/, "");
6345
- if (url && !links.some((link) => link.url === url)) links.push({ title: slugFromPath(url), url });
7194
+ const links = [];
7195
+ for (const line of raw.split(/\r?\n/)) {
7196
+ let cursor = 0;
7197
+ while (cursor < line.length) {
7198
+ const open = line.indexOf("[", cursor);
7199
+ if (open < 0) break;
7200
+ const titleEnd = line.indexOf("]", open + 1);
7201
+ const urlStart = titleEnd < 0 ? -1 : line.indexOf("(", titleEnd + 1);
7202
+ const urlEnd = urlStart < 0 ? -1 : line.indexOf(")", urlStart + 1);
7203
+ if (titleEnd < 0 || urlStart !== titleEnd + 1 || urlEnd < 0) {
7204
+ cursor = open + 1;
7205
+ continue;
7206
+ }
7207
+ const title = line.slice(open + 1, titleEnd).trim();
7208
+ const url = line.slice(urlStart + 1, urlEnd).trim();
7209
+ const description = line.slice(urlEnd + 1).trim().replace(/^:\s*/, "");
7210
+ if (url) links.push({ title: title || url, url, ...description ? { description } : {} });
7211
+ cursor = urlEnd + 1;
7212
+ }
7213
+ for (const token of line.split(/\s+/)) {
7214
+ let end = token.length;
7215
+ while (end > 0 && "),.;:".includes(token[end - 1] ?? "")) end -= 1;
7216
+ const url = token.slice(0, end);
7217
+ if (!/^https?:\/\//i.test(url) || links.some((link) => link.url === url)) continue;
7218
+ links.push({ title: slugFromPath(url), url });
7219
+ }
6346
7220
  }
6347
7221
  return links;
6348
7222
  };
7223
+ var firstMatchingLine = (section, predicate) => section.split(/\r?\n/).find((line) => predicate(line.trim()))?.trim();
7224
+ var sectionTitle = (section, sourceUrl) => {
7225
+ const titleLine = firstMatchingLine(section, (line) => line.startsWith("title:"));
7226
+ if (titleLine) return titleLine.slice("title:".length).trim();
7227
+ const urlLine = firstMatchingLine(section, (line) => /^https?:\/\//i.test(line));
7228
+ if (urlLine) {
7229
+ try {
7230
+ return new URL(urlLine).pathname.split("/").filter(Boolean).at(-1) ?? slugFromPath(sourceUrl);
7231
+ } catch {
7232
+ return slugFromPath(sourceUrl);
7233
+ }
7234
+ }
7235
+ const heading = firstMatchingLine(section, (line) => line.startsWith("#"));
7236
+ if (heading) return heading.replace(/^#+\s*/, "").trim();
7237
+ return slugFromPath(sourceUrl);
7238
+ };
6349
7239
  var chunksFromMarkdown = (property, raw, sourceUrl) => {
6350
- const searchable = raw.includes("\n==== ") ? raw : raw.replace(/^---\n[\s\S]*?\n---\n?/, "");
7240
+ const frontmatterEnd = raw.startsWith("---\n") ? raw.indexOf("\n---", 4) : -1;
7241
+ const searchable = raw.includes("\n==== ") ? raw : frontmatterEnd >= 0 ? raw.slice(frontmatterEnd + "\n---".length).replace(/^\n/, "") : raw;
6351
7242
  const sections = searchable.includes("\n==== ") ? searchable.split(/\n====\s+/).filter((section) => section.trim()) : searchable.split(/\n(?=##?\s+)/);
6352
7243
  return sections.map((section, index) => {
6353
- const title = /^title:\s*(.+)$/m.exec(section)?.[1]?.trim() ?? /^https?:\/\/\S+\/([^/\s]+)$/m.exec(section)?.[1]?.trim() ?? /^#+\s+(.+)$/m.exec(section)?.[1]?.trim() ?? slugFromPath(sourceUrl);
7244
+ const title = sectionTitle(section, sourceUrl);
6354
7245
  const id = slugFromPath(title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")) || `${index}`;
6355
7246
  return {
6356
7247
  chunkKey: `${property}:federated:${id}`,
@@ -6378,8 +7269,15 @@ var loadFederatedChunks = async (root, config, options = {}) => {
6378
7269
  chunks.push(...chunksFromMarkdown(source.id, llms, source.llmsTxt));
6379
7270
  const links = parseLlmsTxtLinks(llms);
6380
7271
  for (const link of links) {
6381
- const url = !/^https?:\/\//.test(link.url) && source.rawBaseUrl ? `${source.rawBaseUrl.replace(/\/$/, "")}/${link.url.replace(/^\//, "")}` : link.url;
6382
- if (!/\.(md|txt)(?:$|\?)/.test(url)) continue;
7272
+ const linkUrl = httpUrl(link.url);
7273
+ const baseUrl = source.rawBaseUrl ? httpUrl(source.rawBaseUrl) : void 0;
7274
+ const url = linkUrl ?? (baseUrl ? new URL(link.url, baseUrl).href : link.url);
7275
+ let pathname = url;
7276
+ try {
7277
+ pathname = new URL(url).pathname;
7278
+ } catch {
7279
+ }
7280
+ if (!/\.(md|txt)$/i.test(pathname)) continue;
6383
7281
  if (!sameOrigin(source.llmsTxt, url)) continue;
6384
7282
  const raw = await sourceText(root, url, fetchText);
6385
7283
  if (raw) chunks.push(...chunksFromMarkdown(source.id, raw, url));
@@ -6411,10 +7309,10 @@ var retrieveHybridChunks = async (root, config, index, query, options = {}) => {
6411
7309
 
6412
7310
  // src/intelligence/rag.ts
6413
7311
  import { readFileSync as readFileSync19 } from "fs";
6414
- import { join as join19 } from "path";
7312
+ import { join as join18 } from "path";
6415
7313
 
6416
7314
  // src/intelligence/adapter.ts
6417
- import { join as join18 } from "path";
7315
+ import { join as join17 } from "path";
6418
7316
 
6419
7317
  // src/intelligence/peers.ts
6420
7318
  var PeerMissingError = class extends Error {
@@ -6541,7 +7439,7 @@ var resolveIntelligenceRuntime = async (config) => {
6541
7439
  }
6542
7440
  return { adapter, embed, provider, ...model ? { model } : {} };
6543
7441
  };
6544
- var defaultVectorStorePath = (root) => join18(root, ".doc-bridge", "vectors");
7442
+ var defaultVectorStorePath = (root) => join17(root, ".doc-bridge", "vectors");
6545
7443
 
6546
7444
  // src/intelligence/rag.ts
6547
7445
  var loadDocuments = (root, index, sources) => {
@@ -6549,7 +7447,7 @@ var loadDocuments = (root, index, sources) => {
6549
7447
  const docs = [];
6550
7448
  if (includeAgent) {
6551
7449
  for (const entry of index.knowledge) {
6552
- const abs = join19(root, entry.path);
7450
+ const abs = join18(root, entry.path);
6553
7451
  let content = "";
6554
7452
  try {
6555
7453
  content = readFileSync19(abs, "utf8");
@@ -6570,7 +7468,7 @@ var createDocBridgeRag = async (root, config, index) => {
6570
7468
  const { embed } = await resolveIntelligenceRuntime(config);
6571
7469
  const ragMod = await importPeer("@agentskit/rag");
6572
7470
  const memoryMod = await importPeer("@agentskit/memory");
6573
- const storePath = typeof config.intelligence?.retriever?.options?.storePath === "string" ? join19(root, config.intelligence.retriever.options.storePath) : defaultVectorStorePath(root);
7471
+ const storePath = typeof config.intelligence?.retriever?.options?.storePath === "string" ? join18(root, config.intelligence.retriever.options.storePath) : defaultVectorStorePath(root);
6574
7472
  const store = memoryMod.fileVectorMemory({ path: storePath });
6575
7473
  const rag = ragMod.createRAG({
6576
7474
  embed,
@@ -6824,12 +7722,17 @@ var docBridgePatternPayload = () => ({
6824
7722
  body: docBridgePatternMarkdown()
6825
7723
  });
6826
7724
  export {
7725
+ ANALYZER_PLUGIN_CONTRACT_VERSION,
6827
7726
  AffectedFileSchema,
6828
7727
  AgentHandoffLegacySchema,
6829
7728
  AgentHandoffV1JsonSchema,
6830
7729
  AgentHandoffV1Schema,
6831
7730
  AgentProposalV1Schema,
6832
7731
  AgentSearchV1Schema,
7732
+ AnalyzerPluginManifestSchema,
7733
+ AnalyzerPluginOutputSchema,
7734
+ BENCHMARK_SCHEMA_VERSION,
7735
+ BenchmarkFixtureV1Schema,
6833
7736
  ConfigNotFoundError,
6834
7737
  CoverageSchema,
6835
7738
  DEFAULT_LARGE_REPORT_THRESHOLD_BYTES,
@@ -6874,6 +7777,7 @@ export {
6874
7777
  ProvenanceSchema,
6875
7778
  ReconciliationReportV1Schema,
6876
7779
  RelationSchema,
7780
+ ReportConfigSchema,
6877
7781
  RepositorySafetyConfigSchema,
6878
7782
  RuleIdSchema,
6879
7783
  RuleSeveritySchema,
@@ -6888,14 +7792,17 @@ export {
6888
7792
  applyDocumentationDeclarations,
6889
7793
  applyFixProposal,
6890
7794
  approveFixProposal,
7795
+ benchmarkFixture,
6891
7796
  buildDocBridgeIndex,
6892
7797
  buildLookup,
6893
7798
  canonicalJsonV1,
6894
7799
  chunksFromMarkdown,
6895
7800
  classifyMemoryCandidates,
6896
7801
  collectPackages,
7802
+ compareBenchmarkSnapshots,
6897
7803
  containedPath,
6898
7804
  contentHashForArtifactV1,
7805
+ createAnalyzerRegistry,
6899
7806
  createArtifactNormalizationProposal,
6900
7807
  createDocBridgeRag,
6901
7808
  createDocBridgeRetriever,
@@ -6910,6 +7817,7 @@ export {
6910
7817
  doctorBadgeMetrics,
6911
7818
  draftMemoryPromotion,
6912
7819
  evaluateRules,
7820
+ formatBenchmarkText,
6913
7821
  formatDoctorBadgeJson,
6914
7822
  formatDoctorBadgeMarkdown,
6915
7823
  formatDoctorText,
@@ -6931,6 +7839,8 @@ export {
6931
7839
  loadWorkflowManifest,
6932
7840
  loadWorkflowStepOutput,
6933
7841
  mcpSnippet,
7842
+ measureAgentEfficiency,
7843
+ measureBenchmark,
6934
7844
  normalizeAgentHandoff,
6935
7845
  parseAgentHandoff,
6936
7846
  parseAgentProposal,