@agentskit/doc-bridge 1.7.44 → 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 (55) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/CONTRIBUTING.md +6 -4
  3. package/action.yml +1 -1
  4. package/dist/cli/program.js +378 -189
  5. package/dist/cli/program.js.map +1 -1
  6. package/dist/config/index.d.ts +1 -1
  7. package/dist/config/index.js +5 -3
  8. package/dist/config/index.js.map +1 -1
  9. package/dist/index-BUL0q7s8.d.ts +660 -0
  10. package/dist/index.d.ts +637 -2724
  11. package/dist/index.js +328 -149
  12. package/dist/index.js.map +1 -1
  13. package/docs/RELEASE.md +22 -8
  14. package/docs/agent-corpus/INDEX.md +2 -2
  15. package/docs/agent-corpus/chat.md +2 -2
  16. package/docs/agent-corpus/cli.md +2 -2
  17. package/docs/agent-corpus/conformance.md +2 -2
  18. package/docs/agent-corpus/doc-bridge.md +1 -1
  19. package/docs/agent-corpus/doctor.md +2 -2
  20. package/docs/agent-corpus/gates.md +2 -2
  21. package/docs/agent-corpus/mcp.md +2 -2
  22. package/docs/agent-corpus/memory.md +2 -2
  23. package/docs/agent-corpus/query.md +2 -2
  24. package/docs/knowledge-engine-runbook.md +13 -1
  25. package/docs/spec/benchmark-v1.md +6 -0
  26. package/docs/spec/config-v1.md +45 -0
  27. package/docs/validation-cycle-plan.md +19 -0
  28. package/docs/verification-harness.md +4 -0
  29. package/mcpb/manifest.json +1 -1
  30. package/package.json +68 -70
  31. package/scripts/check-ecosystem-upstream.mjs +3 -2
  32. package/scripts/report-visual-check.mjs +20 -3
  33. package/scripts/verification-harness.mjs +0 -1
  34. package/skills/doc-bridge-handoff/scripts/resolve-handoff.mjs +1 -1
  35. package/src/cli/demo.ts +2 -2
  36. package/src/cli/program.ts +15 -5
  37. package/src/config/load-config.ts +7 -1
  38. package/src/config/schema.ts +4 -2
  39. package/src/conformance/documentation-standard-v1.ts +14 -8
  40. package/src/discovery/documentation.ts +44 -18
  41. package/src/discovery/repository.ts +77 -28
  42. package/src/doctor/run-doctor.ts +2 -15
  43. package/src/federation/llms.ts +72 -20
  44. package/src/fixes/proposals.ts +4 -3
  45. package/src/index-builder/human-adapters/fumadocs.ts +1 -1
  46. package/src/index-builder/watch-index.ts +1 -1
  47. package/src/lib/bounded-text.ts +15 -10
  48. package/src/reconciliation/reconcile.ts +47 -5
  49. package/src/report/html.ts +21 -15
  50. package/src/rules/engine.ts +15 -2
  51. package/src/safety/repository.ts +1 -1
  52. package/src/schemas/knowledge.ts +5 -2
  53. package/src/validate.ts +7 -1
  54. package/src/version.ts +1 -1
  55. package/dist/index-C2PCQSrB.d.ts +0 -2251
package/dist/index.js CHANGED
@@ -198,17 +198,19 @@ 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
208
  /** Semantic comparison level. Raw discovery always keeps file-level relations. */
209
209
  scope: z.enum(["file", "module", "package"]).optional(),
210
210
  /** Relation kinds that must have documentation declarations. Omit to require all observed kinds; [] disables this signal. */
211
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(),
212
214
  includeOrphanedDocuments: z.boolean().optional()
213
215
  }).strict();
214
216
  var AnalysisConfigSchema = z.object({
@@ -754,7 +756,7 @@ var parseConfig = (input) => {
754
756
  throw new Error(
755
757
  `Invalid doc-bridge config:
756
758
  ${result.error.issues.map(
757
- (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}`
758
760
  ).join("\n")}`
759
761
  );
760
762
  };
@@ -1177,7 +1179,7 @@ var EntitySchema = z5.object({
1177
1179
  aliases: z5.array(boundedString(256)).max(32).optional(),
1178
1180
  provenance: ProvenanceSchema,
1179
1181
  evidence: z5.array(EvidenceSchema).max(64),
1180
- metadata: z5.record(z5.unknown()).optional()
1182
+ metadata: z5.record(z5.string(), z5.unknown()).optional()
1181
1183
  }).strict();
1182
1184
  var RelationSchema = z5.object({
1183
1185
  id: boundedString(256),
@@ -1187,7 +1189,7 @@ var RelationSchema = z5.object({
1187
1189
  discriminator: boundedString(256).optional(),
1188
1190
  provenance: ProvenanceSchema,
1189
1191
  evidence: z5.array(EvidenceSchema).max(64),
1190
- metadata: z5.record(z5.unknown()).optional()
1192
+ metadata: z5.record(z5.string(), z5.unknown()).optional()
1191
1193
  }).strict();
1192
1194
  var DiscoverySnapshotV1Schema = z5.object({
1193
1195
  type: z5.literal("discovery-snapshot"),
@@ -1233,11 +1235,14 @@ var ReconciliationReportV1Schema = z5.object({
1233
1235
  diagnosticCount: z5.number().int().nonnegative(),
1234
1236
  scope: z5.enum(["file", "module", "package"]).optional(),
1235
1237
  requiredRelationKinds: z5.array(boundedString(128)).max(128).optional(),
1238
+ requiredRelationTargets: z5.enum(["all", "internal"]).optional(),
1236
1239
  diagnosticsByCode: z5.record(z5.string().max(128), z5.number().int().nonnegative()).optional(),
1237
1240
  diagnosticsByStatus: z5.record(z5.string().max(128), z5.number().int().nonnegative()).optional(),
1238
1241
  documentation: z5.object({
1239
1242
  documentCount: z5.number().int().nonnegative(),
1240
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()),
1241
1246
  packageCount: z5.number().int().nonnegative(),
1242
1247
  packageStatus: z5.object({
1243
1248
  fresh: z5.number().int().nonnegative(),
@@ -1348,9 +1353,14 @@ var FixProposalV1Schema = z5.object({
1348
1353
  });
1349
1354
 
1350
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
+ };
1351
1361
  var zodIssues = (error) => error.issues.map((issue) => ({
1352
1362
  path: issue.path.join(".") || "(root)",
1353
- message: issue.message
1363
+ message: zodMessage(issue)
1354
1364
  }));
1355
1365
  var safeParseAgentHandoff = (input) => {
1356
1366
  const legacy = AgentHandoffLegacySchema.safeParse(input);
@@ -1390,7 +1400,7 @@ ${zodIssues(result.error).map((i) => ` - ${i.path}: ${i.message}`).join("\n")}`
1390
1400
 
1391
1401
  // src/index-builder/build-index.ts
1392
1402
  import { mkdirSync, readFileSync as readFileSync7, writeFileSync } from "fs";
1393
- import { dirname as dirname4, join as join7 } from "path";
1403
+ import { dirname as dirname3, join as join7 } from "path";
1394
1404
 
1395
1405
  // src/lib/paths.ts
1396
1406
  import { existsSync as existsSync2, realpathSync } from "fs";
@@ -1474,22 +1484,27 @@ var defaultChecksForTarget = (root, opts) => {
1474
1484
  import { minimatch } from "minimatch";
1475
1485
 
1476
1486
  // src/lib/bounded-text.ts
1477
- import { readFileSync as readFileSync3, statSync } from "fs";
1487
+ import { closeSync, fstatSync, openSync, readFileSync as readFileSync3 } from "fs";
1478
1488
  var MAX_DOCUMENT_BYTES = 4 * 1024 * 1024;
1479
1489
  var MAX_CORPUS_BYTES = 64 * 1024 * 1024;
1480
1490
  var readBoundedText = (path, budget, limits) => {
1481
1491
  const maxFileBytes = limits?.maxFileBytes ?? MAX_DOCUMENT_BYTES;
1482
1492
  const maxCorpusBytes = limits?.maxCorpusBytes ?? MAX_CORPUS_BYTES;
1483
- const stat = statSync(path);
1484
- if (!stat.isFile()) throw new Error(`Documentation path is not a regular file: ${path}`);
1485
- if (stat.size > maxFileBytes) {
1486
- throw new Error(`Documentation file exceeds the ${maxFileBytes} byte limit: ${path}`);
1487
- }
1488
- if (budget.used + stat.size > maxCorpusBytes) {
1489
- 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);
1490
1507
  }
1491
- budget.used += stat.size;
1492
- return readFileSync3(path, "utf8");
1493
1508
  };
1494
1509
 
1495
1510
  // src/lib/markdown.ts
@@ -1913,7 +1928,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
1913
1928
  };
1914
1929
 
1915
1930
  // src/version.ts
1916
- var PACKAGE_VERSION = "1.7.44";
1931
+ var PACKAGE_VERSION = "1.7.45";
1917
1932
 
1918
1933
  // src/index-builder/capabilities.ts
1919
1934
  var renderCapabilitiesJson = (config, index, paths) => {
@@ -2344,7 +2359,7 @@ var scanHumanDocs = (root, config) => Object.fromEntries(scanHumanDocRecords(roo
2344
2359
 
2345
2360
  // src/index-builder/plugins/nx.ts
2346
2361
  import { existsSync as existsSync6, lstatSync as lstatSync2, readFileSync as readFileSync5, realpathSync as realpathSync5 } from "fs";
2347
- 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";
2348
2363
  var NX_SCAN_SKIP = /* @__PURE__ */ new Set([
2349
2364
  "node_modules",
2350
2365
  ".git",
@@ -2408,7 +2423,7 @@ var discoverNxProjects = (root, config) => {
2408
2423
  for (const manifest of manifests) {
2409
2424
  const json = readJsonRecord(manifest);
2410
2425
  if (!json) continue;
2411
- const manifestDir = dirname3(manifest);
2426
+ const manifestDir = dirname2(manifest);
2412
2427
  const manifestName = basename2(manifest);
2413
2428
  const isProjectJson = manifestName === "project.json";
2414
2429
  if (!isProjectJson && manifestName !== "package.json") continue;
@@ -2456,7 +2471,7 @@ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
2456
2471
  import { basename as basename3, join as join6 } from "path";
2457
2472
 
2458
2473
  // src/lib/glob-expand.ts
2459
- import { existsSync as existsSync7, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
2474
+ import { existsSync as existsSync7, readdirSync as readdirSync2, statSync } from "fs";
2460
2475
  import { join as join5 } from "path";
2461
2476
  var expandWorkspaceGlobs = (root, patterns) => {
2462
2477
  const dirs = /* @__PURE__ */ new Set();
@@ -2473,7 +2488,7 @@ var expandWorkspaceGlobs = (root, patterns) => {
2473
2488
  for (const name of readdirSync2(base)) {
2474
2489
  const abs = join5(base, name);
2475
2490
  try {
2476
- if (statSync2(abs).isDirectory()) dirs.add(toPosix(abs));
2491
+ if (statSync(abs).isDirectory()) dirs.add(toPosix(abs));
2477
2492
  } catch {
2478
2493
  }
2479
2494
  }
@@ -2583,7 +2598,7 @@ var buildDocBridgeIndex = (opts) => {
2583
2598
  lookup
2584
2599
  };
2585
2600
  if (write) {
2586
- mkdirSync(dirname4(indexPath), { recursive: true });
2601
+ mkdirSync(dirname3(indexPath), { recursive: true });
2587
2602
  writeFileSync(indexPath, `${JSON.stringify(index, null, 2)}
2588
2603
  `, "utf8");
2589
2604
  }
@@ -2606,7 +2621,7 @@ var buildDocBridgeIndex = (opts) => {
2606
2621
  const capabilitiesOut = config.index?.capabilities?.outFile ?? ".doc-bridge/capabilities.json";
2607
2622
  capabilitiesPath = join7(root, capabilitiesOut);
2608
2623
  if (write) {
2609
- mkdirSync(dirname4(capabilitiesPath), { recursive: true });
2624
+ mkdirSync(dirname3(capabilitiesPath), { recursive: true });
2610
2625
  writeFileSync(
2611
2626
  capabilitiesPath,
2612
2627
  renderCapabilitiesJson(config, index, {
@@ -2628,14 +2643,14 @@ var buildDocBridgeIndex = (opts) => {
2628
2643
  // src/discovery/repository.ts
2629
2644
  import { execFileSync } from "child_process";
2630
2645
  import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
2631
- 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";
2632
2647
  import * as ts from "typescript";
2633
2648
 
2634
2649
  // src/safety/repository.ts
2635
- 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";
2636
2651
  import { isAbsolute as isAbsolute4, relative as relative4, resolve as resolve6, sep as sep5 } from "path";
2637
2652
  import { minimatch as minimatch4 } from "minimatch";
2638
- 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"];
2639
2654
  var containedPath = (root, candidate) => {
2640
2655
  const projectRoot = realpathSync6.native(resolve6(root));
2641
2656
  const unresolved = resolve6(projectRoot, candidate);
@@ -2695,7 +2710,7 @@ var safeWalkFiles = (root, options = {}) => {
2695
2710
  reason = `Repository scan exceeded the ${options.maxFiles ?? 1e4} file limit.`;
2696
2711
  return;
2697
2712
  }
2698
- bytes += statSync3(absolute).size;
2713
+ bytes += statSync2(absolute).size;
2699
2714
  if (options.maxBytes !== void 0 && bytes > options.maxBytes) {
2700
2715
  reason = `Repository scan exceeded the ${options.maxBytes} byte limit.`;
2701
2716
  return;
@@ -2752,6 +2767,7 @@ var firstLineContaining = (text, pattern) => {
2752
2767
  };
2753
2768
  var documentClassification = (path) => {
2754
2769
  if (/(^|\/)docs\/for-agents(?:\/|$)/.test(path)) return "agent";
2770
+ if (/(^|\/)docs-archive(?:\/|$)/.test(path)) return "archive";
2755
2771
  if (/(^|\/)docs(?:\/|$)/.test(path)) return "human";
2756
2772
  if (/(^|\/)(README|CONTRIBUTING|SECURITY|CHANGELOG)(?:\.|$)/i.test(path)) return "project";
2757
2773
  return "unclassified";
@@ -2830,7 +2846,7 @@ var readCompilerOptions = (root) => {
2830
2846
  if (!configPath) return { options: {} };
2831
2847
  const parsed = ts.readConfigFile(configPath, ts.sys.readFile);
2832
2848
  if (parsed.error) return { options: {}, error: ts.flattenDiagnosticMessageText(parsed.error.messageText, "\n") };
2833
- const config = ts.parseJsonConfigFileContent(parsed.config, ts.sys, dirname5(configPath));
2849
+ const config = ts.parseJsonConfigFileContent(parsed.config, ts.sys, dirname4(configPath));
2834
2850
  if (config.errors.length) {
2835
2851
  return {
2836
2852
  options: config.options,
@@ -2899,20 +2915,56 @@ var exportedNames = (sourceFile) => {
2899
2915
  };
2900
2916
  var moduleReferences = (root, path, sourceFile, runtimeWiringMethods) => {
2901
2917
  const references = [];
2918
+ const dynamicEvidence = [];
2902
2919
  let hasDynamic = false;
2903
2920
  let hasLiteralDynamic = false;
2904
2921
  let hasRuntimeWiring = false;
2905
2922
  let hasUnresolvedRuntimeWiring = false;
2906
2923
  const importedBindings = /* @__PURE__ */ new Map();
2907
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
+ };
2908
2937
  const collectStaticStringBindings = (node) => {
2909
- if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && ts.isStringLiteralLike(node.initializer) && ts.isVariableDeclarationList(node.parent) && (node.parent.flags & ts.NodeFlags.Const) !== 0) {
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);
2910
2940
  const previous = staticStringBindings.get(node.name.text);
2911
- staticStringBindings.set(node.name.text, !staticStringBindings.has(node.name.text) || previous === node.initializer.text ? node.initializer.text : void 0);
2941
+ staticStringBindings.set(node.name.text, !staticStringBindings.has(node.name.text) || previous === value ? value : void 0);
2912
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);
2913
2945
  ts.forEachChild(node, collectStaticStringBindings);
2914
2946
  };
2915
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
+ };
2916
2968
  const addReference = (specifier, kind, node, detection) => {
2917
2969
  references.push({ specifier: specifier.text, kind, evidence: nodeEvidence(root, path, sourceFile, node), ...detection ? { detection } : {} });
2918
2970
  };
@@ -2932,32 +2984,35 @@ var moduleReferences = (root, path, sourceFile, runtimeWiringMethods) => {
2932
2984
  importedBindings.set(node.name.text, node.moduleReference.expression.text);
2933
2985
  } else if (ts.isCallExpression(node)) {
2934
2986
  if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
2935
- if (node.arguments[0] && ts.isStringLiteralLike(node.arguments[0])) {
2987
+ const specifier = node.arguments[0] ? resolveStaticString(node.arguments[0]) : void 0;
2988
+ if (specifier !== void 0) {
2936
2989
  hasLiteralDynamic = true;
2937
- addReference(node.arguments[0], "imports", node, "dynamic-literal");
2938
- } else if (node.arguments[0] && ts.isIdentifier(node.arguments[0]) && staticStringBindings.get(node.arguments[0].text)) {
2939
- hasLiteralDynamic = true;
2940
- addReference({ text: staticStringBindings.get(node.arguments[0].text) }, "imports", node, "dynamic-literal");
2941
- } else hasDynamic = 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
+ }
2942
2996
  } else if (ts.isIdentifier(node.expression) && node.expression.text === "require") {
2943
2997
  const argument = node.arguments[0];
2944
- if (argument && ts.isStringLiteralLike(argument)) addReference(argument, "imports", node);
2945
- else if (argument && ts.isIdentifier(argument) && staticStringBindings.get(argument.text)) addReference({ text: staticStringBindings.get(argument.text) }, "imports", node);
2946
- else hasDynamic = true;
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
+ }
2947
3006
  } else if (ts.isPropertyAccessExpression(node.expression) && runtimeWiringMethods.has(node.expression.name.text)) {
2948
3007
  hasRuntimeWiring = true;
2949
- const before = references.length;
2950
- let hasPotentialTargetArgument = false;
3008
+ let hasUnresolvedTarget = false;
2951
3009
  for (const argument of node.arguments) {
2952
- if (ts.isIdentifier(argument)) {
2953
- hasPotentialTargetArgument = true;
2954
- const specifier = importedBindings.get(argument.text);
2955
- if (specifier) addReference({ text: specifier }, "runtime-wiring", node, "runtime-wiring-static");
2956
- } else if (ts.isPropertyAccessExpression(argument) || ts.isCallExpression(argument)) {
2957
- hasPotentialTargetArgument = true;
2958
- }
3010
+ if (ts.isStringLiteralLike(argument)) continue;
3011
+ if (addImportedBindingReference(argument, node)) continue;
3012
+ if (!isKnownLocal(argument)) hasUnresolvedTarget = true;
2959
3013
  }
2960
- if (hasPotentialTargetArgument && references.length === before) hasUnresolvedRuntimeWiring = true;
3014
+ const receiver = node.expression.expression;
3015
+ if (hasUnresolvedTarget && !isKnownLocal(receiver)) hasUnresolvedRuntimeWiring = true;
2961
3016
  }
2962
3017
  }
2963
3018
  ts.forEachChild(node, visit);
@@ -2966,6 +3021,7 @@ var moduleReferences = (root, path, sourceFile, runtimeWiringMethods) => {
2966
3021
  return {
2967
3022
  references,
2968
3023
  exports: exportedNames(sourceFile),
3024
+ dynamicEvidence,
2969
3025
  hasDynamic,
2970
3026
  hasLiteralDynamic,
2971
3027
  hasRuntimeWiring,
@@ -2973,7 +3029,7 @@ var moduleReferences = (root, path, sourceFile, runtimeWiringMethods) => {
2973
3029
  };
2974
3030
  };
2975
3031
  var resolveRelativeModule = (specifier, containingFile, modulePaths) => {
2976
- const base = resolve7(dirname5(containingFile), specifier);
3032
+ const base = resolve7(dirname4(containingFile), specifier);
2977
3033
  const extension = extname(base);
2978
3034
  const extensionlessBase = extension ? base.slice(0, -extension.length) : base;
2979
3035
  const candidates = [
@@ -3042,11 +3098,11 @@ var artifact = (root, config, files, entities, relations, coverage) => {
3042
3098
  sourceRevision: revision.value,
3043
3099
  sourceRevisionKind: revision.kind,
3044
3100
  configurationHash: sha256NormalizedV1(config ?? {}),
3045
- pipelineVersion: "1.0.0",
3046
- analyzerVersions: { repository: "1.0.0", "js-ts": "1.3.0" },
3101
+ pipelineVersion: "1.1.8",
3102
+ analyzerVersions: { repository: "1.1.1", "js-ts": "1.3.4" },
3047
3103
  entities: [...entities].sort((a, b) => a.id.localeCompare(b.id)),
3048
3104
  relations: [...relations].sort((a, b) => a.id.localeCompare(b.id)),
3049
- coverage: coverage.map((entry) => ({ ...entry, analyzerVersion: entry.analyzerVersion ?? ({ repository: "1.0.0", "js-ts": "1.3.0" }[entry.analyzer] ?? "1.0.0") }))
3105
+ coverage: coverage.map((entry) => ({ ...entry, analyzerVersion: entry.analyzerVersion ?? ({ repository: "1.1.1", "js-ts": "1.3.4" }[entry.analyzer] ?? "1.0.0") }))
3050
3106
  };
3051
3107
  return DiscoverySnapshotV1Schema.parse({ ...base, contentHash: contentHashForArtifactV1(base) });
3052
3108
  };
@@ -3140,6 +3196,7 @@ var discoverRepository = (opts = {}) => {
3140
3196
  const includeTestRuntimeWiring = opts.config?.analysis?.jsTs?.includeTestRuntimeWiring ?? false;
3141
3197
  let observedLiteralDynamic = false;
3142
3198
  let observedUnresolvedDynamic = false;
3199
+ const observedDynamicEvidence = [];
3143
3200
  let observedRuntimeWiring = false;
3144
3201
  let observedUnresolvedRuntimeWiring = false;
3145
3202
  for (const module of modules.values()) {
@@ -3149,6 +3206,7 @@ var discoverRepository = (opts = {}) => {
3149
3206
  const references = moduleReferences(root, module.absPath, sourceFile, runtimeWiringMethods);
3150
3207
  observedLiteralDynamic ||= references.hasLiteralDynamic;
3151
3208
  observedUnresolvedDynamic ||= references.hasDynamic;
3209
+ observedDynamicEvidence.push(...references.dynamicEvidence);
3152
3210
  observedRuntimeWiring ||= references.hasRuntimeWiring;
3153
3211
  observedUnresolvedRuntimeWiring ||= references.hasUnresolvedRuntimeWiring;
3154
3212
  for (const reference of references.references) {
@@ -3160,10 +3218,10 @@ var discoverRepository = (opts = {}) => {
3160
3218
  }
3161
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 } } : {} });
3162
3220
  }
3163
- 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: [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)] });
3164
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)] });
3165
3223
  }
3166
- 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." } : observedLiteralDynamic ? { analyzer: "js-ts", scope: "dynamic-imports", status: "complete", reason: "All observed dynamic imports used literal targets and were resolved." } : { analyzer: "js-ts", scope: "dynamic-imports", status: "not-applicable", reason: "No dynamic loading expression was observed." };
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." };
3167
3225
  const runtimeCoverageIndex = coverage.findIndex((entry) => entry.scope === "runtime-wiring");
3168
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." };
3169
3227
  return artifact(root, opts.config, allFiles, [...entities.values()], [...relations.values()], coverage);
@@ -3279,6 +3337,29 @@ var scalar = (value) => {
3279
3337
  }
3280
3338
  return trimmed;
3281
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
+ };
3282
3363
  var conventionalPackageReference = (path, agentRoot) => {
3283
3364
  const prefix = `${agentRoot.replace(/\/$/, "")}/`;
3284
3365
  if (!path.startsWith(prefix)) return void 0;
@@ -3388,11 +3469,10 @@ var parseBlock = (input, options = {}) => {
3388
3469
  section = void 0;
3389
3470
  continue;
3390
3471
  }
3391
- if (/^ {2}[A-Za-z][A-Za-z0-9_-]*\s*:/.test(raw)) {
3472
+ const sectionField = parseIndentedField(raw, 2);
3473
+ if (sectionField) {
3392
3474
  finishRelation();
3393
- const match = /^ {2}([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(raw);
3394
- const key = match?.[1];
3395
- const value = match?.[2] ?? "";
3475
+ const { key, value } = sectionField;
3396
3476
  if (key !== "covers" && key !== "relations") {
3397
3477
  addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_UNKNOWN", `Unknown docbridge field: ${key ?? "(missing)"}.`, line);
3398
3478
  section = void 0;
@@ -3409,35 +3489,36 @@ var parseBlock = (input, options = {}) => {
3409
3489
  }
3410
3490
  continue;
3411
3491
  }
3412
- if (section === "covers" && /^ {4}-\s*/.test(raw)) {
3413
- 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);
3414
3495
  if (!value) addDiagnostic(diagnostics, input, "DOCBRIDGE_REFERENCE_MISSING", "covers entries must not be empty.", line);
3415
3496
  else covers.push({ value, line });
3416
3497
  continue;
3417
3498
  }
3418
- if (section === "relations" && /^ {4}-\s*/.test(raw)) {
3499
+ if (section === "relations" && listValue !== void 0) {
3419
3500
  finishRelation();
3420
- const firstField = /^ {4}-\s*([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(raw);
3501
+ const firstField = parseIndentedField(` ${listValue}`, 4);
3421
3502
  current = { startLine: line, endLine: line, fields: /* @__PURE__ */ new Set() };
3422
- if (firstField?.[1]) {
3423
- current.fields.add(firstField[1]);
3424
- current[firstField[1]] = scalar(firstField[2] ?? "");
3425
- } 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) {
3426
3507
  addDiagnostic(diagnostics, input, "DOCBRIDGE_RELATION_INVALID", "Relation entries must be field mappings.", line);
3427
3508
  }
3428
3509
  continue;
3429
3510
  }
3430
- if (section === "relations" && current && /^ {6}[A-Za-z][A-Za-z0-9_-]*\s*:/.test(raw)) {
3431
- const field = /^ {6}([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(raw);
3432
- const key = field?.[1];
3511
+ const field = parseIndentedField(raw, 6);
3512
+ if (section === "relations" && current && field) {
3513
+ const { key, value } = field;
3433
3514
  current.endLine = line;
3434
- if (!key || !["from", "to", "kind", "detection"].includes(key)) {
3435
- 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);
3436
3517
  } else if (current.fields.has(key)) {
3437
3518
  addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_DUPLICATE", `Duplicate relation field: ${key}.`, line);
3438
3519
  } else {
3439
3520
  current.fields.add(key);
3440
- current[key] = scalar(field?.[2] ?? "");
3521
+ current[key] = scalar(value);
3441
3522
  }
3442
3523
  continue;
3443
3524
  }
@@ -3527,11 +3608,13 @@ var applyDocumentationDeclarations = (snapshot, documents, options = {}) => {
3527
3608
 
3528
3609
  // src/reconciliation/reconcile.ts
3529
3610
  var ignoredDocumentationRelations = /* @__PURE__ */ new Set(["covers"]);
3611
+ var isInternalEntity = (id) => !id.startsWith("external:") && !id.startsWith("unresolved:");
3530
3612
  var metadataDetection = (relation) => {
3531
3613
  const detection = relation.metadata?.detection;
3532
3614
  return typeof detection === "string" ? detection : void 0;
3533
3615
  };
3534
- 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");
3535
3618
  var entityResolver = (snapshots) => {
3536
3619
  const references = /* @__PURE__ */ new Map();
3537
3620
  const entities = snapshots.flatMap((snapshot) => snapshot.entities).sort((a, b) => a.id.localeCompare(b.id));
@@ -3558,6 +3641,19 @@ var diagnosticCounts = (values) => {
3558
3641
  for (const value of values) counts.set(value, (counts.get(value) ?? 0) + 1);
3559
3642
  return Object.fromEntries([...counts.entries()].sort(([a], [b]) => a.localeCompare(b)));
3560
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
+ };
3561
3657
  var diagnosticId = (code, value) => `reconciliation:${code}:${sha256NormalizedV1(value).slice(0, 32)}`;
3562
3658
  var coverageAvailable = (snapshot, relation) => {
3563
3659
  const status = snapshot.coverage.find(
@@ -3610,7 +3706,6 @@ var aggregatedRelations = (relations, scope, entities, packageByModule) => {
3610
3706
  const parts = key.split("\0");
3611
3707
  const from = parts[0] ?? first.from;
3612
3708
  const to = parts[1] ?? first.to;
3613
- const kind = parts[2] ?? first.kind;
3614
3709
  const detection = parts[3] ?? relationDetection(first);
3615
3710
  const mergedEvidence = mergeEvidence(...group);
3616
3711
  return {
@@ -3692,9 +3787,18 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
3692
3787
  group.push(relation);
3693
3788
  declaredByBase.set(base2, group);
3694
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
+ }
3695
3797
  for (const group of declaredByBase.values()) {
3696
3798
  const detections = new Set(group.map(relationDetection));
3697
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;
3698
3802
  diagnostics.push(reportDiagnostic(
3699
3803
  "CONFLICTING_DECLARATIONS",
3700
3804
  "conflict",
@@ -3721,7 +3825,7 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
3721
3825
  void 0,
3722
3826
  [relation.id, match.id]
3723
3827
  ));
3724
- } 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))) {
3725
3829
  diagnostics.push(reportDiagnostic(
3726
3830
  "RELATION_UNDOCUMENTED",
3727
3831
  "undocumented",
@@ -3784,9 +3888,13 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
3784
3888
  else if (statuses.has("undocumented") || statuses.has("unresolved") || statuses.has("not-analyzed")) packageStatus.unverified += 1;
3785
3889
  else packageStatus.fresh += 1;
3786
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));
3787
3893
  const documentation = {
3788
- documentCount: observed.entities.filter((entity) => entity.kind === "document").length,
3789
- documentedDocumentCount: new Set(allDeclaredRelations.filter((relation) => relation.from.startsWith("document:")).map((relation) => relation.from)).size,
3894
+ documentCount: documents.length,
3895
+ documentedDocumentCount: documentedDocumentIds.size,
3896
+ documentClassificationCounts: countDocumentClasses(documents),
3897
+ documentedDocumentClassificationCounts: countDocumentClasses(documents, documentedDocumentIds),
3790
3898
  packageCount: packageEntities.length,
3791
3899
  packageStatus
3792
3900
  };
@@ -3809,6 +3917,7 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
3809
3917
  diagnosticCount: sortedDiagnostics.length,
3810
3918
  ...options.scope === void 0 ? {} : { scope: options.scope },
3811
3919
  ...options.requiredRelationKinds === void 0 ? {} : { requiredRelationKinds: [...new Set(options.requiredRelationKinds)].sort() },
3920
+ ...options.requiredRelationTargets === void 0 ? {} : { requiredRelationTargets: options.requiredRelationTargets },
3812
3921
  diagnosticsByCode: diagnosticCounts(sortedDiagnostics.map((diagnostic2) => diagnostic2.code)),
3813
3922
  diagnosticsByStatus: diagnosticCounts(sortedDiagnostics.map((diagnostic2) => diagnostic2.status)),
3814
3923
  documentation
@@ -3932,7 +4041,11 @@ var reportData = (snapshot, report, includeSnippets, privacy = "private") => {
3932
4041
  pipelineVersion: snapshot.pipelineVersion,
3933
4042
  analyzerVersions: snapshot.analyzerVersions,
3934
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,
3935
4046
  requiredRelationKinds: report.summary.requiredRelationKinds,
4047
+ requiredRelationTargets: report.summary.requiredRelationTargets,
4048
+ documentation: report.summary.documentation,
3936
4049
  diagnosticSummary: {
3937
4050
  errors: report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "error").length,
3938
4051
  warnings: report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "warn").length,
@@ -4090,8 +4203,8 @@ styles += String.raw`
4090
4203
  :root{--ink:#182b2b;--muted:#536664;--paper:#efe9df;--panel:#f8f5ee;--line:#c8c8b9;--green:#176b51;--blue:#155a68;--amber:#93530b;--red:#9b302b;--shadow:none}
4091
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)}}
4092
4205
  `;
4093
- 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}}`;
4094
- 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}}`;
4095
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}`;
4096
4209
  styles += String.raw`body[data-report-view="architecture"] #report-dashboard{display:none!important}.edge{fill:none}`;
4097
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}}`;
@@ -4101,7 +4214,8 @@ styles += String.raw`#lens-caption + p.subtle{display:none}`;
4101
4214
  var script = String.raw`
4102
4215
  const data=${"${DATA}"};
4103
4216
  data.entities=data.entities||[];data.relations=data.relations||[];data.diagnostics=data.diagnostics||[];
4104
- 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.";
4105
4219
  const ensurePolicyNote=()=>{const note=document.querySelector("#map-note");if(note&&!note.textContent.includes(policySuffix))note.textContent=(note.textContent+" · "+policySuffix).trim()};
4106
4220
  new MutationObserver(ensurePolicyNote).observe(document.querySelector("#map-note"),{childList:true,characterData:true});
4107
4221
  const lazyChunks=globalThis.__DOC_BRIDGE_LAZY_CHUNKS__||[],hasLevelChunks=globalThis.__DOC_BRIDGE_HAS_LEVEL_CHUNKS__===true,loadedChunks=new Set(),loadingChunks=new Map();
@@ -4112,7 +4226,7 @@ const groupById=new Map(Object.entries(data.view?.groups||{}));
4112
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]]));
4113
4227
  const addFinding=(index,id,finding)=>{const values=index.get(id)||[];values.push(finding);index.set(id,values)};
4114
4228
  const addRelation=(id,relation)=>{const values=relationsByEntity.get(id)||[];values.push(relation);relationsByEntity.set(id,values)};
4115
- 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]||[];scopes.forEach((scope)=>{addFinding(groupFindingIndex,scope,finding);groupFindingCounts.set(scope,(groupFindingCounts.get(scope)||0)+1)})})};
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)})})};
4116
4230
  const isLevelChunk=(name)=>name.startsWith("chunks/levels-");
4117
4231
  const isDetailChunk=(name)=>name.startsWith("chunks/details-");
4118
4232
  const hasChunk=(name)=>isLevelChunk(name)?activeLevelChunk===name:isDetailChunk(name)?loadedChunks.has(name):!lazyChunks.includes(name)||loadedChunks.has(name);
@@ -4129,10 +4243,10 @@ const viewLabels={architecture:"Architecture",drift:"Insights",risks:"Findings",
4129
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};
4130
4244
  const label=(entity)=>entity?.name||entity?.id||"Unknown";
4131
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};
4132
- 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("")};
4133
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};
4134
4248
  const relationHealth=(relation)=>{if(!relation)return "";const findings=relationFindings(relation.id);return findings.some((finding)=>finding.severity==="error")?"error":findings.length?"warn":""};
4135
- 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)||[]).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};
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};
4136
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);
4137
4251
  const relationFindings=(id)=>relationFindingIndex.get(id)||data.view?.diagnosticRelationFindings?.[id]||[];
4138
4252
  const relationInLens=(id)=>relationFindings(id).some(findingInLens);
@@ -4142,8 +4256,8 @@ const detailChunkName=()=>{const entity=state.selected?byId.get(state.selected):
4142
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)};
4143
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}};
4144
4258
  const graphModel=()=>{const key=state.lens+"|"+state.level+"|"+(state.selected||"");if(graphCacheKey===key&&graphCache)return graphCache;graphCacheKey=key;return graphCache=graphModelUncached()};
4145
- 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("")};
4146
- 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=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>"};
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>"};
4147
4261
  const mapState={scale:1,dragging:false,x:0,y:0};
4148
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"};
4149
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)})};
@@ -4152,16 +4266,16 @@ let nodeClickTimer;
4152
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);
4153
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);
4154
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);
4155
- const diagnosticsFor=(id)=>findingIndex.get(id)||[];
4269
+ const diagnosticsFor=(id)=>(findingIndex.get(id)||[]).filter((finding)=>finding.status!=="confirmed");
4156
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>":"")};
4157
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+").":".");};
4158
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)};
4159
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"})();
4160
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))};
4161
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>";
4162
- 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>"};
4163
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>"};
4164
- 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>"};
4165
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>"}};
4166
4280
  const findingsLoaded=()=>!lazyChunks.includes("chunks/findings.js")||loadedChunks.has("chunks/findings.js");
4167
4281
  const levelsLoaded=()=>!hasLevelChunks||state.level==="overview"||activeLevelChunk===levelChunkName();
@@ -4197,11 +4311,12 @@ var render = (input, options) => {
4197
4311
  const data = reportData(snapshot, report, includeSnippets, options.privacy);
4198
4312
  const scriptBody = script.replace("${DATA}", options.dataExpression ?? embeddedJson(data));
4199
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.";
4200
- 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;
4201
4316
  const unsupportedCount = snapshot.coverage.filter((entry) => entry.status !== "complete").length;
4202
- 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.";
4203
4318
  const statusOptions = ["confirmed", "undocumented", "stale-or-unverified", "conflict", "unresolved", "not-analyzed"];
4204
- 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 ${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(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>`;
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>`;
4205
4320
  };
4206
4321
  var parseReportInput = (input) => {
4207
4322
  if (!input || typeof input !== "object") throw new Error("Input must contain snapshot and report artifacts.");
@@ -4314,7 +4429,7 @@ var renderOfflineReportArtifact = (input, options = {}) => {
4314
4429
  diagnosticRelationFindings: Object.fromEntries(diagnosticRelationFindings)
4315
4430
  };
4316
4431
  const files = {
4317
- "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, entities: packageEntities, relations: packageRelations, view: overviewView }),
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 }),
4318
4433
  ...levelFiles,
4319
4434
  ...detailFiles,
4320
4435
  "chunks/findings.js": chunkScript({ diagnostics: data.diagnostics })
@@ -4327,8 +4442,8 @@ var renderOfflineReportArtifact = (input, options = {}) => {
4327
4442
  };
4328
4443
 
4329
4444
  // src/fixes/proposals.ts
4330
- import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as readFileSync10, realpathSync as realpathSync7, renameSync, statSync as statSync4, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
4331
- 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";
4332
4447
  var hash2 = (value) => sha256NormalizedV1(value);
4333
4448
  var artifactMetadata = (root, options) => ({
4334
4449
  schemaVersion: 1,
@@ -4384,12 +4499,12 @@ var createMarkdownLinkFixProposal = (root, options) => {
4384
4499
  const target = match[3];
4385
4500
  if (!target || match[1] === "!" || /^(?:[a-z]+:|\/|#)/i.test(target)) continue;
4386
4501
  const targetPath = target.split("#")[0]?.split("?")[0];
4387
- if (!targetPath || existsSync11(resolve9(projectRoot, dirname6(path), targetPath))) continue;
4502
+ if (!targetPath || existsSync11(resolve9(projectRoot, dirname5(path), targetPath))) continue;
4388
4503
  const targetStem = basename5(targetPath, extname2(targetPath)).toLowerCase();
4389
4504
  const labelStem = (match[2] ?? "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
4390
4505
  const candidates = paths.filter((candidate) => basename5(candidate, extname2(candidate)).toLowerCase() === targetStem || basename5(candidate, extname2(candidate)).toLowerCase().replace(/[^a-z0-9]+/g, "") === labelStem);
4391
4506
  if (candidates.length !== 1) continue;
4392
- let replacement = relative6(dirname6(path), candidates[0]).split(sep7).join("/");
4507
+ let replacement = relative6(dirname5(path), candidates[0]).split(sep7).join("/");
4393
4508
  if (target.startsWith("./") && !replacement.startsWith(".")) replacement = `./${replacement}`;
4394
4509
  next = next.replace(match[0], match[0].replace(target, replacement));
4395
4510
  }
@@ -4401,8 +4516,13 @@ var createArtifactNormalizationProposal = (root, artifactPath, options) => {
4401
4516
  const projectRoot = realpathSync7.native(resolve9(root));
4402
4517
  const path = artifactPath.split(sep7).join("/");
4403
4518
  const absolute = containedPath(projectRoot, path);
4404
- if (!absolute || !existsSync11(absolute) || !statSync4(absolute).isFile()) return void 0;
4405
- 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
+ }
4406
4526
  let after;
4407
4527
  try {
4408
4528
  after = `${JSON.stringify(sortJson(JSON.parse(before)), null, 2)}
@@ -4792,8 +4912,20 @@ var evaluateRules = (report, options = {}) => {
4792
4912
  const sortedFindings = [...findings].sort((a, b) => a.id.localeCompare(b.id));
4793
4913
  return { mode: resolved.mode, findings: sortedFindings, exitCode: sortedFindings.some((finding) => finding.severity === "error") ? 1 : 0 };
4794
4914
  };
4795
- var parseRuleId = (value) => RuleIdSchema.parse(value);
4796
- 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
+ };
4797
4929
 
4798
4930
  // src/federation/ecosystem-llms.ts
4799
4931
  function formatEcosystemLlmsBlock(options) {
@@ -4825,7 +4957,7 @@ function formatEcosystemLlmsSection(options) {
4825
4957
  import { readFileSync as readFileSync14 } from "fs";
4826
4958
 
4827
4959
  // src/conformance/documentation-standard-v1.ts
4828
- 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";
4829
4961
  import { isAbsolute as isAbsolute5, relative as relative8, resolve as resolve11, sep as sep8 } from "path";
4830
4962
 
4831
4963
  // src/conformance/ecosystem-contract.ts
@@ -5030,11 +5162,10 @@ var fileEvidence = (root, path, options) => {
5030
5162
  evidence: { path, detail: "Path escapes the project root." }
5031
5163
  };
5032
5164
  }
5033
- if (!existsSync13(abs)) {
5034
- return { exists: false, content: "", evidence: { path, detail: "File does not exist." } };
5035
- }
5165
+ let fd;
5036
5166
  try {
5037
- const stat = statSync5(abs);
5167
+ fd = openSync2(abs, "r");
5168
+ const stat = fstatSync2(fd);
5038
5169
  if (!stat.isFile()) {
5039
5170
  return { exists: false, content: "", evidence: { path, detail: "Path is not a regular file." } };
5040
5171
  }
@@ -5055,7 +5186,7 @@ var fileEvidence = (root, path, options) => {
5055
5186
  evidence: { path, detail: `Text evidence exceeds ${MAX_TEXT_EVIDENCE_BYTES} bytes.` }
5056
5187
  };
5057
5188
  }
5058
- const content = readFileSync12(abs, "utf8");
5189
+ const content = readFileSync12(fd, "utf8");
5059
5190
  return {
5060
5191
  exists: content.trim().length > 0,
5061
5192
  content,
@@ -5064,8 +5195,15 @@ var fileEvidence = (root, path, options) => {
5064
5195
  detail: content.trim().length > 0 ? "File exists and is non-empty." : "File is empty."
5065
5196
  }
5066
5197
  };
5067
- } catch {
5068
- 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);
5069
5207
  }
5070
5208
  };
5071
5209
  var resultWithException = (draft, options) => {
@@ -6330,7 +6468,7 @@ var startMcpStdioServer = (ctx) => {
6330
6468
  // src/mcp/install.ts
6331
6469
  import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync17, writeFileSync as writeFileSync6 } from "fs";
6332
6470
  import { homedir } from "os";
6333
- 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";
6334
6472
  var SERVER_NAME = "ak-docs";
6335
6473
  var mcpServerEntry = (root) => ({
6336
6474
  command: "npx",
@@ -6347,7 +6485,7 @@ var readJson2 = (path) => {
6347
6485
  }
6348
6486
  };
6349
6487
  var writeJson = (path, value) => {
6350
- mkdirSync5(dirname7(path), { recursive: true });
6488
+ mkdirSync5(dirname6(path), { recursive: true });
6351
6489
  writeFileSync6(path, `${JSON.stringify(value, null, 2)}
6352
6490
  `, "utf8");
6353
6491
  };
@@ -6427,16 +6565,6 @@ var gradeForScore = (score) => {
6427
6565
  if (score >= 40) return "D";
6428
6566
  return "F";
6429
6567
  };
6430
- var agentDocPaths = (index) => {
6431
- const paths = /* @__PURE__ */ new Set();
6432
- for (const owner of Object.values(index.lookup?.ownership ?? {})) {
6433
- if (owner.agentDoc) paths.add(owner.agentDoc);
6434
- }
6435
- for (const handoff of Object.values(index.handoffs ?? {})) {
6436
- if (handoff.startHere) paths.add(handoff.startHere);
6437
- }
6438
- return paths;
6439
- };
6440
6568
  var computeScore = (coverage) => {
6441
6569
  let score = 0;
6442
6570
  if (coverage.freshness.hasIndex) score += 15;
@@ -6524,7 +6652,7 @@ var runDoctor = (root, config) => {
6524
6652
  let index;
6525
6653
  let hasIndex = true;
6526
6654
  let freshnessOk = false;
6527
- let freshnessMessage = "Index is fresh";
6655
+ let freshnessMessage;
6528
6656
  try {
6529
6657
  index = loadDocBridgeIndex(root, config);
6530
6658
  const next = buildDocBridgeIndex({ root, config, write: false }).index.contentHash;
@@ -6544,8 +6672,6 @@ var runDoctor = (root, config) => {
6544
6672
  const missingAgentDoc = ownership.filter(([, owner]) => !owner.agentDoc || owner.agentDoc === config.corpus.agent.index).map(([id]) => id);
6545
6673
  const missingHumanDoc = ownership.filter(([, owner]) => !owner.humanDoc).map(([id]) => id);
6546
6674
  const indexedPaths = new Set(index.knowledge.map((entry) => entry.path));
6547
- const expectedAgentDocs = agentDocPaths(index);
6548
- const unindexed = [...expectedAgentDocs].filter((path) => !indexedPaths.has(path));
6549
6675
  const corpusDocs = scanAgentCorpus(root, config).filter(
6550
6676
  (doc) => doc.path !== config.corpus.agent.index
6551
6677
  );
@@ -6624,7 +6750,7 @@ var formatDoctorText = (report) => {
6624
6750
 
6625
6751
  // src/index-builder/watch-index.ts
6626
6752
  import { existsSync as existsSync17, watch } from "fs";
6627
- import { dirname as dirname8, resolve as resolve15 } from "path";
6753
+ import { dirname as dirname7, resolve as resolve15 } from "path";
6628
6754
  var WATCH_PATTERN = /\.(md|mdx|json|ya?ml|mdc)$/i;
6629
6755
  var NX_MANIFEST_PATTERN = /(^|[/\\])(project|package)\.json$/i;
6630
6756
  var collectWatchRoots = (root, config, configPath) => {
@@ -6638,7 +6764,7 @@ var collectWatchRoots = (root, config, configPath) => {
6638
6764
  if (typeof value === "string" && value.length) roots.add(resolve15(root, value));
6639
6765
  }
6640
6766
  }
6641
- if (configPath) roots.add(dirname8(resolve15(configPath)));
6767
+ if (configPath) roots.add(dirname7(resolve15(configPath)));
6642
6768
  return [...roots].filter((dir) => existsSync17(dir));
6643
6769
  };
6644
6770
  var watchDocBridgeIndex = (opts) => {
@@ -6708,7 +6834,7 @@ var watchDocBridgeIndex = (opts) => {
6708
6834
  // src/memory/github-pr.ts
6709
6835
  import { execFileSync as execFileSync2, spawnSync } from "child_process";
6710
6836
  import { existsSync as existsSync18, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
6711
- import { join as join17 } from "path";
6837
+ import { join as join16 } from "path";
6712
6838
  var run = (cmd, args, cwd) => {
6713
6839
  const result = spawnSync(cmd, [...args], { cwd, encoding: "utf8" });
6714
6840
  const out = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
@@ -6716,10 +6842,10 @@ var run = (cmd, args, cwd) => {
6716
6842
  };
6717
6843
  var hasGh = () => run("gh", ["--version"], process.cwd()).ok;
6718
6844
  var slug = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
6719
- 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`);
6720
6846
  var writePromotionDraft = (root, draft, path) => {
6721
6847
  const draftPath = path ?? defaultPromotionDraftPath(root);
6722
- mkdirSync6(join17(root, ".doc-bridge", "drafts"), { recursive: true });
6848
+ mkdirSync6(join16(root, ".doc-bridge", "drafts"), { recursive: true });
6723
6849
  writeFileSync7(draftPath, `${draft.body}
6724
6850
  `, "utf8");
6725
6851
  return draftPath;
@@ -6755,7 +6881,7 @@ var promoteMemoryToGithubPr = (root, draft, options = {}) => {
6755
6881
  message: `Wrote draft to ${relDraft}. Run the printed git/gh commands to open a draft PR.`
6756
6882
  };
6757
6883
  }
6758
- if (!existsSync18(join17(root, ".git"))) {
6884
+ if (!existsSync18(join16(root, ".git"))) {
6759
6885
  return {
6760
6886
  ok: false,
6761
6887
  dryRun: false,
@@ -7039,9 +7165,20 @@ var defaultFetchText = async (url) => {
7039
7165
  if (!res.ok) throw new Error(`Failed to fetch ${url}: ${res.status}`);
7040
7166
  return res.text();
7041
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
+ };
7042
7178
  var sourceText = async (root, source, fetchText) => {
7043
7179
  try {
7044
- if (/^https?:\/\//.test(source)) return await fetchText(source);
7180
+ const remote = httpUrl(source);
7181
+ if (remote) return await fetchText(remote);
7045
7182
  const path = resolve16(root, source);
7046
7183
  if (!existsSync19(path)) return null;
7047
7184
  return readFileSync18(path, "utf8");
@@ -7054,22 +7191,57 @@ var sameOrigin = (base, target) => {
7054
7191
  return new URL(base).origin === new URL(target).origin;
7055
7192
  };
7056
7193
  var parseLlmsTxtLinks = (raw) => {
7057
- const links = [...raw.matchAll(/\[([^\]]+)\]\(([^)]+)\)(?::\s*([^\n]+))?/g)].map((match) => ({
7058
- title: match[1] ?? match[2] ?? "link",
7059
- url: match[2] ?? "",
7060
- ...match[3]?.trim() ? { description: match[3].trim() } : {}
7061
- })).filter((link) => link.url);
7062
- for (const match of raw.matchAll(/(?:^|\s)(?:Raw|llms\.txt|Full bundle|ZIP bundle)?:?\s*(https?:\/\/\S+)/gi)) {
7063
- const url = match[1]?.replace(/[),.;]+$/, "");
7064
- 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
+ }
7065
7220
  }
7066
7221
  return links;
7067
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
+ };
7068
7239
  var chunksFromMarkdown = (property, raw, sourceUrl) => {
7069
- 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;
7070
7242
  const sections = searchable.includes("\n==== ") ? searchable.split(/\n====\s+/).filter((section) => section.trim()) : searchable.split(/\n(?=##?\s+)/);
7071
7243
  return sections.map((section, index) => {
7072
- 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);
7073
7245
  const id = slugFromPath(title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")) || `${index}`;
7074
7246
  return {
7075
7247
  chunkKey: `${property}:federated:${id}`,
@@ -7097,8 +7269,15 @@ var loadFederatedChunks = async (root, config, options = {}) => {
7097
7269
  chunks.push(...chunksFromMarkdown(source.id, llms, source.llmsTxt));
7098
7270
  const links = parseLlmsTxtLinks(llms);
7099
7271
  for (const link of links) {
7100
- const url = !/^https?:\/\//.test(link.url) && source.rawBaseUrl ? `${source.rawBaseUrl.replace(/\/$/, "")}/${link.url.replace(/^\//, "")}` : link.url;
7101
- 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;
7102
7281
  if (!sameOrigin(source.llmsTxt, url)) continue;
7103
7282
  const raw = await sourceText(root, url, fetchText);
7104
7283
  if (raw) chunks.push(...chunksFromMarkdown(source.id, raw, url));
@@ -7130,10 +7309,10 @@ var retrieveHybridChunks = async (root, config, index, query, options = {}) => {
7130
7309
 
7131
7310
  // src/intelligence/rag.ts
7132
7311
  import { readFileSync as readFileSync19 } from "fs";
7133
- import { join as join19 } from "path";
7312
+ import { join as join18 } from "path";
7134
7313
 
7135
7314
  // src/intelligence/adapter.ts
7136
- import { join as join18 } from "path";
7315
+ import { join as join17 } from "path";
7137
7316
 
7138
7317
  // src/intelligence/peers.ts
7139
7318
  var PeerMissingError = class extends Error {
@@ -7260,7 +7439,7 @@ var resolveIntelligenceRuntime = async (config) => {
7260
7439
  }
7261
7440
  return { adapter, embed, provider, ...model ? { model } : {} };
7262
7441
  };
7263
- var defaultVectorStorePath = (root) => join18(root, ".doc-bridge", "vectors");
7442
+ var defaultVectorStorePath = (root) => join17(root, ".doc-bridge", "vectors");
7264
7443
 
7265
7444
  // src/intelligence/rag.ts
7266
7445
  var loadDocuments = (root, index, sources) => {
@@ -7268,7 +7447,7 @@ var loadDocuments = (root, index, sources) => {
7268
7447
  const docs = [];
7269
7448
  if (includeAgent) {
7270
7449
  for (const entry of index.knowledge) {
7271
- const abs = join19(root, entry.path);
7450
+ const abs = join18(root, entry.path);
7272
7451
  let content = "";
7273
7452
  try {
7274
7453
  content = readFileSync19(abs, "utf8");
@@ -7289,7 +7468,7 @@ var createDocBridgeRag = async (root, config, index) => {
7289
7468
  const { embed } = await resolveIntelligenceRuntime(config);
7290
7469
  const ragMod = await importPeer("@agentskit/rag");
7291
7470
  const memoryMod = await importPeer("@agentskit/memory");
7292
- 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);
7293
7472
  const store = memoryMod.fileVectorMemory({ path: storePath });
7294
7473
  const rag = ragMod.createRAG({
7295
7474
  embed,