@agentskit/doc-bridge 1.9.0 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,54 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.10.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 0926c57: Keep a committed index fresh across commits.
8
+
9
+ The retrieval projection sealed its content hash over the discovery snapshot's hash, and a
10
+ snapshot's hash covers its `sourceRevision` — the commit SHA when the working tree is clean, a
11
+ digest of the scanned files when it is not. That is right for an artifact whose job is to say what
12
+ one revision looked like, and wrong as a projection input: the projection is a function of what the
13
+ snapshot observed, not of where it observed it.
14
+
15
+ The consequence only appears in a repository that commits `.doc-bridge/index.json`, which is the
16
+ recommended setup: committing the index changes the revision that the next run hashes, so the
17
+ artifact was stale the moment it landed — landing it is a commit. `ak-docs gate run` reported
18
+ `index-freshness` failing on an index that nothing had invalidated, and no regenerate could fix it,
19
+ because the fix was itself a commit. Dogfooding on a 25-package monorepo, the gate could not be
20
+ made to pass twice in a row.
21
+
22
+ The seal is now over what the snapshot observed: the entities, the relations and the analyzer
23
+ identity that produced them, alongside the overlay and configuration hashes it already covered. The
24
+ artifact still carries `snapshotHash`, now documented as provenance rather than a seal input, so a
25
+ reader can still say which snapshot a projection came from. `RETRIEVAL_PROJECTION_VERSION` goes to
26
+ 2, so no reader compares a hash across the change, and every index's content hash changes once on
27
+ the next `ak-docs index`.
28
+
29
+ ## 1.10.0
30
+
31
+ ### Minor Changes
32
+
33
+ - 97851d0: Let a repository say which directories are not areas.
34
+
35
+ An area is the unit of architecture between a package and a file, derived as the first directory
36
+ level under a package's source roots. In a monorepo where every package keeps `tests/` and
37
+ `fixtures/` beside `src/`, that derives one area per directory — and the doctor's connectivity
38
+ dimension then asks for a document about a folder of test data. Dogfooding on a 26-package
39
+ monorepo, 43 of its 81 undocumented areas were `tests/` or `fixtures/`: the metric was mostly
40
+ measuring directories no documentation should describe.
41
+
42
+ `analysis.areas.exclude` takes glob patterns for directories that hold code without being a unit
43
+ of architecture. A matching candidate is not derived, and its modules fall to the most specific
44
+ area that still encloses them — or to none, which is the honest answer for a folder of fixtures.
45
+ An ownership record naming an excluded path still makes it an area: a person saying a directory is
46
+ a unit outranks a pattern saying it is not.
47
+
48
+ On that monorepo, excluding `**/tests`, `**/fixtures`, `**/__tests__` and `**/__fixtures__` took
49
+ areas from 103 to 53 and the documented share from 21% to 34%, before a single document was
50
+ written.
51
+
3
52
  ## 1.9.0
4
53
 
5
54
  ### Minor Changes
package/action.yml CHANGED
@@ -20,7 +20,7 @@ inputs:
20
20
  package-version:
21
21
  description: Exact @agentskit/doc-bridge npm version (kept in sync with this Action release)
22
22
  required: false
23
- default: '1.9.0'
23
+ default: '1.10.1'
24
24
 
25
25
  runs:
26
26
  using: composite
@@ -525,11 +525,20 @@ var AnalysisConfigSchema = z.object({
525
525
  /**
526
526
  * How code areas are derived — the unit of architecture between a package and a file.
527
527
  * `roots` names directories that contain areas rather than being one (`src` holds
528
- * `src/query`); `depth` is how many levels below such a root an area sits.
528
+ * `src/query`); `depth` is how many levels below such a root an area sits; `exclude`
529
+ * names directories that hold code without being a unit of architecture.
529
530
  */
530
531
  areas: z.object({
531
532
  depth: z.number().int().min(1).max(8).optional(),
532
- roots: z.array(z.string().min(1).max(128)).max(32).optional()
533
+ roots: z.array(z.string().min(1).max(128)).max(32).optional(),
534
+ /**
535
+ * Glob patterns for directories that are not areas. A monorepo where every package
536
+ * keeps `tests/` and `fixtures/` beside `src/` derives one area per directory, and
537
+ * then connectivity asks for a document about a folder of test data. An ownership
538
+ * record naming an excluded path still makes it an area: a person saying a directory
539
+ * is a unit outranks a pattern saying it is not.
540
+ */
541
+ exclude: z.array(z.string().min(1).max(256)).max(64).optional()
533
542
  }).strict().optional()
534
543
  }).strict();
535
544
  var WorkflowConfigSchema = z.object({
@@ -2081,6 +2090,9 @@ var areaSuggestionCoverage = (snapshot, options = {}) => {
2081
2090
  }));
2082
2091
  };
2083
2092
 
2093
+ // src/discovery/areas.ts
2094
+ import { minimatch as minimatch2 } from "minimatch";
2095
+
2084
2096
  // src/discovery/identity.ts
2085
2097
  var MAX_ID_LENGTH = 256;
2086
2098
  var ID_HASH_LENGTH = 32;
@@ -2129,10 +2141,12 @@ var deriveAreas = (options) => {
2129
2141
  const depth = options.depth ?? DEFAULT_AREA_DEPTH;
2130
2142
  const roots = options.roots ?? [...DEFAULT_AREA_ROOTS];
2131
2143
  const ownership = (options.ownership ?? []).map((record) => ({ ...record, path: normalize(record.path) }));
2144
+ const excluded = options.exclude ?? [];
2145
+ const isExcluded2 = (path) => excluded.some((pattern) => minimatch2(path, pattern, { dot: true }));
2132
2146
  const candidates = /* @__PURE__ */ new Map();
2133
2147
  for (const module of options.modules) {
2134
2148
  const path = conventionalAreaPath(module, depth, roots);
2135
- if (path && !candidates.has(path)) candidates.set(path, module.packageId);
2149
+ if (path && !candidates.has(path) && !isExcluded2(path)) candidates.set(path, module.packageId);
2136
2150
  }
2137
2151
  for (const record of ownership) {
2138
2152
  if (!record.path || candidates.has(record.path)) continue;
@@ -3230,7 +3244,8 @@ var discoverRepository = (opts = {}) => {
3230
3244
  modules: areaModules,
3231
3245
  ownership: Object.entries(opts.config?.routing?.options?.ownership ?? {}).map(([id, record]) => ({ id, path: record.path })),
3232
3246
  ...opts.config?.analysis?.areas?.depth !== void 0 ? { depth: opts.config.analysis.areas.depth } : {},
3233
- ...opts.config?.analysis?.areas?.roots !== void 0 ? { roots: opts.config.analysis.areas.roots } : {}
3247
+ ...opts.config?.analysis?.areas?.roots !== void 0 ? { roots: opts.config.analysis.areas.roots } : {},
3248
+ ...opts.config?.analysis?.areas?.exclude !== void 0 ? { exclude: opts.config.analysis.areas.exclude } : {}
3234
3249
  });
3235
3250
  const areasById = new Map(areas.map((area) => [area.id, area]));
3236
3251
  const areasByPath = new Map(areas.map((area) => [area.path, area.id]));
@@ -3601,8 +3616,13 @@ var RetrievalIndexV1Schema = z5.object({
3601
3616
  schemaVersion: z5.literal(RETRIEVAL_INDEX_SCHEMA_VERSION),
3602
3617
  contentHash: hash2,
3603
3618
  contentHashAlgo: z5.literal("sha256-normalized-v1"),
3604
- /** The three inputs the projection is a function of. Same three hashes, same projection. */
3619
+ /**
3620
+ * Which snapshot this was projected from. Provenance, not a seal input: it carries the
3621
+ * snapshot's `sourceRevision`, and the projection is a function of what the snapshot observed
3622
+ * rather than of the revision it was observed at.
3623
+ */
3605
3624
  snapshotHash: hash2,
3625
+ /** The inputs the projection is a function of. Same hashes, same projection. */
3606
3626
  overlayHash: hash2,
3607
3627
  configurationHash: hash2,
3608
3628
  lexiconVersion: z5.number().int().nonnegative().max(1e3),
@@ -3614,7 +3634,7 @@ var RetrievalIndexV1Schema = z5.object({
3614
3634
  }).strict();
3615
3635
 
3616
3636
  // src/index-builder/scan-corpus.ts
3617
- import { minimatch as minimatch2 } from "minimatch";
3637
+ import { minimatch as minimatch3 } from "minimatch";
3618
3638
 
3619
3639
  // src/lib/markdown.ts
3620
3640
  var parseFrontmatter = (markdown) => {
@@ -3765,8 +3785,8 @@ var configuredPathMatches = (relPath, include, exclude) => {
3765
3785
  const normalize3 = (pattern) => toPosix(pattern).replace(/^\.\//, "");
3766
3786
  const included = include?.filter(Boolean).map(normalize3) ?? [];
3767
3787
  const excluded = exclude?.filter(Boolean).map(normalize3) ?? [];
3768
- if (excluded.some((pattern) => minimatch2(relPath, pattern, { dot: true }))) return false;
3769
- return included.length === 0 || included.some((pattern) => minimatch2(relPath, pattern, { dot: true }));
3788
+ if (excluded.some((pattern) => minimatch3(relPath, pattern, { dot: true }))) return false;
3789
+ return included.length === 0 || included.some((pattern) => minimatch3(relPath, pattern, { dot: true }));
3770
3790
  };
3771
3791
  var scanAgentCorpus = (root, config) => {
3772
3792
  const agentRoot = containedProjectPath(root, config.corpus.agent.root);
@@ -4040,7 +4060,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
4040
4060
  };
4041
4061
 
4042
4062
  // src/version.ts
4043
- var PACKAGE_VERSION = "1.9.0";
4063
+ var PACKAGE_VERSION = "1.10.1";
4044
4064
 
4045
4065
  // src/index-builder/capabilities.ts
4046
4066
  var renderCapabilitiesJson = (config, index, paths) => {
@@ -4945,14 +4965,14 @@ var nextraAdapter = {
4945
4965
  };
4946
4966
 
4947
4967
  // src/index-builder/human-adapters/plain-markdown.ts
4948
- import { minimatch as minimatch3 } from "minimatch";
4968
+ import { minimatch as minimatch4 } from "minimatch";
4949
4969
  var stringPatterns = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.length > 0) : [];
4950
4970
  var matchesConfiguredPath = (relPath, options) => {
4951
4971
  const normalize3 = (pattern) => pattern.replaceAll("\\", "/").replace(/^\.\//, "");
4952
4972
  const include = stringPatterns(options?.include).map(normalize3);
4953
4973
  const exclude = stringPatterns(options?.exclude).map(normalize3);
4954
- if (exclude.some((pattern) => minimatch3(relPath, pattern, { dot: true }))) return false;
4955
- return include.length === 0 || include.some((pattern) => minimatch3(relPath, pattern, { dot: true }));
4974
+ if (exclude.some((pattern) => minimatch4(relPath, pattern, { dot: true }))) return false;
4975
+ return include.length === 0 || include.some((pattern) => minimatch4(relPath, pattern, { dot: true }));
4956
4976
  };
4957
4977
  var plainMarkdownAdapter = {
4958
4978
  plugin: "plain-markdown",
@@ -4990,7 +5010,7 @@ var starlightAdapter = {
4990
5010
  };
4991
5011
 
4992
5012
  // src/index-builder/human-adapters/vitepress.ts
4993
- import { minimatch as minimatch4 } from "minimatch";
5013
+ import { minimatch as minimatch5 } from "minimatch";
4994
5014
  var isVitePressPage = (relPath) => !relPath.split("/").some((part) => part === ".vitepress" || part.startsWith("."));
4995
5015
  var srcExcludePatterns = (value) => {
4996
5016
  if (value === void 0) return [];
@@ -5004,7 +5024,7 @@ var srcExcludePatterns = (value) => {
5004
5024
  return pattern;
5005
5025
  });
5006
5026
  };
5007
- var isExcluded = (relPath, patterns) => patterns.some((pattern) => minimatch4(relPath, pattern, { dot: true }));
5027
+ var isExcluded = (relPath, patterns) => patterns.some((pattern) => minimatch5(relPath, pattern, { dot: true }));
5008
5028
  var vitepressSlug = (relPath, cleanUrls) => {
5009
5029
  const slug2 = routeSlug(relPath);
5010
5030
  if (cleanUrls || /(?:^|\/)index\.mdx?$/.test(relPath)) return slug2;
@@ -5618,7 +5638,7 @@ var resolveSearchParams = (configured) => ({
5618
5638
  });
5619
5639
 
5620
5640
  // src/retrieval/project.ts
5621
- var RETRIEVAL_PROJECTION_VERSION = 1;
5641
+ var RETRIEVAL_PROJECTION_VERSION = 2;
5622
5642
  var DOCUMENT_BODY_LIMIT = 4e3;
5623
5643
  var MAX_EDGES = 64;
5624
5644
  var MAX_ALIASES = 32;
@@ -5626,6 +5646,12 @@ var MAX_TAGS = 32;
5626
5646
  var MAX_SYMBOLS = 256;
5627
5647
  var MAX_SUMMARY = 400;
5628
5648
  var EMPTY_OVERLAY_HASH = sha256NormalizedV1({ accepted: [] });
5649
+ var snapshotObservationHash = (snapshot) => sha256NormalizedV1({
5650
+ pipelineVersion: snapshot.pipelineVersion,
5651
+ analyzerVersions: snapshot.analyzerVersions,
5652
+ entities: snapshot.entities,
5653
+ relations: snapshot.relations
5654
+ });
5629
5655
  var CONFIDENCE_RANK = { observed: 0, declared: 1, fuzzy: 2, proposed: 3 };
5630
5656
  var weakerConfidence = (a, b) => CONFIDENCE_RANK[a] >= CONFIDENCE_RANK[b] ? a : b;
5631
5657
  var relationConfidence = (relation) => relation.metadata?.confidence === "fuzzy" ? "fuzzy" : relation.provenance;
@@ -5886,7 +5912,7 @@ var projectRetrievalIndex = (options) => {
5886
5912
  };
5887
5913
  const contentHash = sha256NormalizedV1({
5888
5914
  projectionVersion: RETRIEVAL_PROJECTION_VERSION,
5889
- snapshotHash: base.snapshotHash,
5915
+ observationHash: snapshotObservationHash(snapshot),
5890
5916
  overlayHash: base.overlayHash,
5891
5917
  configurationHash: base.configurationHash,
5892
5918
  lexiconVersion: base.lexiconVersion,
@@ -8189,7 +8215,7 @@ var resolveGateIds = (config) => {
8189
8215
  };
8190
8216
 
8191
8217
  // src/rules/engine.ts
8192
- import { minimatch as minimatch5 } from "minimatch";
8218
+ import { minimatch as minimatch6 } from "minimatch";
8193
8219
  var diagnosticRules = {
8194
8220
  DOCUMENTATION_QUALITY: "documentation-quality",
8195
8221
  RELATION_UNDOCUMENTED: "graph-undocumented-relation",
@@ -8263,7 +8289,7 @@ var evaluateRules = (report, options = {}) => {
8263
8289
  findings.push(criticalFinding(finding, criticalSeverity, matchingEntity));
8264
8290
  }
8265
8291
  for (const path of resolved.criticalPaths) {
8266
- if (finding.evidence.some((item) => minimatch5(item.path, path, { dot: true })) && !resolved.ignore.has("critical-path-risk") && criticalSeverity !== "off") {
8292
+ if (finding.evidence.some((item) => minimatch6(item.path, path, { dot: true })) && !resolved.ignore.has("critical-path-risk") && criticalSeverity !== "off") {
8267
8293
  findings.push(criticalFinding(finding, criticalSeverity, path));
8268
8294
  }
8269
8295
  }
@@ -13982,7 +14008,7 @@ var formatBenchmarkText = (result) => [
13982
14008
  // src/audit/documentation.ts
13983
14009
  import { readFileSync as readFileSync28 } from "fs";
13984
14010
  import { resolve as resolve25 } from "path";
13985
- import { minimatch as minimatch6 } from "minimatch";
14011
+ import { minimatch as minimatch7 } from "minimatch";
13986
14012
 
13987
14013
  // src/render/data.ts
13988
14014
  var MAX_SYMBOLS2 = 12;
@@ -14313,7 +14339,7 @@ var bodyForDuplicate = (content) => {
14313
14339
  const start = lines[0] === "---" ? lines.findIndex((line, index) => index > 0 && line === "---") + 1 : 0;
14314
14340
  return lines.slice(start).join("\n").replace(/\s+/g, " ").trim().toLocaleLowerCase();
14315
14341
  };
14316
- var matches = (path, patterns) => patterns.some((pattern) => minimatch6(path, pattern, { dot: true }));
14342
+ var matches = (path, patterns) => patterns.some((pattern) => minimatch7(path, pattern, { dot: true }));
14317
14343
  var rate2 = (count4, total) => total ? count4 / total : null;
14318
14344
  var metadataPresent = (content, key) => {
14319
14345
  const value = parseFrontmatter(content).data[key];