agent-doc-system 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +402 -0
- package/README.md +38 -0
- package/adapters/ansible.mjs +1 -1
- package/adapters/cache.mjs +9 -2
- package/adapters/contract-consumers.mjs +1 -1
- package/adapters/database.mjs +19 -4
- package/adapters/deploy-config.mjs +3 -2
- package/adapters/dockerfile.mjs +3 -2
- package/adapters/events.mjs +1 -1
- package/adapters/generic.mjs +2 -1
- package/adapters/github-actions.mjs +245 -85
- package/adapters/go.mjs +5 -4
- package/adapters/index.mjs +3 -1
- package/adapters/node.mjs +3 -2
- package/adapters/object-store.mjs +2 -2
- package/adapters/python.mjs +1142 -0
- package/adapters/resource-client.mjs +1 -1
- package/adapters/rust.mjs +990 -0
- package/adapters/turborepo.mjs +3 -2
- package/adapters/typescript.mjs +7 -5
- package/adapters/wrangler.mjs +3 -2
- package/bin/agentdoc.mjs +11 -4
- package/bin/usage.txt +2 -0
- package/core/audit.mjs +12 -8
- package/core/codes.mjs +4 -0
- package/core/compile.mjs +131 -16
- package/core/dependents.mjs +42 -0
- package/core/derive.mjs +4 -3
- package/core/descriptors.mjs +3 -2
- package/core/discover.mjs +61 -9
- package/core/fsx.mjs +185 -36
- package/core/graph.mjs +1 -1
- package/core/impact.mjs +66 -48
- package/core/paths.mjs +71 -0
- package/core/query.mjs +76 -31
- package/core/scaffold.mjs +216 -44
- package/core/sourcescan.mjs +14 -2
- package/core/toml.mjs +463 -140
- package/docs/ADAPTERS.md +196 -6
- package/docs/BENCHMARK.md +130 -0
- package/docs/CI.md +7 -1
- package/docs/EVALS.md +239 -16
- package/docs/MIGRATION.md +26 -6
- package/docs/PROVENANCE.md +6 -0
- package/docs/QUERY.md +78 -7
- package/docs/SPEC.md +69 -2
- package/docs/ci/github-actions.yml +1 -1
- package/evals/baseline-search.mjs +272 -0
- package/evals/facts.mjs +28 -0
- package/evals/harness.mjs +149 -119
- package/evals/scenarios/fixture-b-03-change-shared-lib.json +8 -1
- package/evals/scenarios/fixture-c-03-shared-vocabulary.json +7 -1
- package/evals/scenarios/fixture-e-01-change-shared-lib.json +14 -0
- package/evals/scenarios/fixture-e-02-change-manifest.json +9 -0
- package/evals/scenarios/fixture-e-03-local-module.json +9 -0
- package/evals/scenarios/fixture-e-04-ambiguous-crate.json +14 -0
- package/evals/scenarios/fixture-f-01-change-shared-lib.json +9 -0
- package/evals/scenarios/fixture-f-02-change-manifest.json +9 -0
- package/evals/scenarios/fixture-f-03-shadowed-import.json +9 -0
- package/evals/scenarios/fixture-f-04-nested-workspace.json +9 -0
- package/evals/scenarios/fixture-f-05-alembic-database.json +9 -0
- package/evals/scenarios/fixture-f-06-unittest-job.json +9 -0
- package/package.json +5 -2
- package/skills/agent-doc-system/SKILL.md +2 -1
- package/skills/agent-doc-system/references/cli.md +6 -1
- package/skills/agent-doc-system/references/create-migrate.md +21 -5
- package/templates/ci.yml +1 -1
package/docs/EVALS.md
CHANGED
|
@@ -7,34 +7,182 @@ noise than exploring the repository unaided?
|
|
|
7
7
|
|
|
8
8
|
The question is not "does it compile". A catalog can compile perfectly and still
|
|
9
9
|
route an agent into the wrong corner of the repository. The measurement is
|
|
10
|
-
retrieval quality
|
|
10
|
+
retrieval quality on the same scenarios for three retrieval systems.
|
|
11
11
|
|
|
12
|
-
**baseline-targeted** —
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
**baseline-targeted** — the historical, graph-assisted reference: the touched
|
|
13
|
+
entity, its subtree, directly connected entities, documents, events and checks,
|
|
14
|
+
plus the original repository-wide documentation sweep. It reads compiled
|
|
15
|
+
relations and authority facts. It is not an agent without AgentDoc.
|
|
15
16
|
|
|
16
|
-
**baseline-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
**baseline-search** — a deterministic model of an agent with file reads and
|
|
18
|
+
text search over the working tree, starting at the touched paths. It searches
|
|
19
|
+
for importers and importees to a fixed point, then reads the reached components'
|
|
20
|
+
docs, manifests and checks. It has no compiled graph or compiler-derived
|
|
21
|
+
relations. Its complete heuristics are below.
|
|
20
22
|
|
|
21
23
|
**catalog** — what `agentdoc query` returns for the touched paths, plus the
|
|
22
24
|
context of the components `impact` says are affected.
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
**baseline-full** is retained only as a size yardstick: all graph entities,
|
|
27
|
+
relations, event subjects, checks and documents. It is a graph inventory, not
|
|
28
|
+
unaided repository exploration.
|
|
29
|
+
|
|
30
|
+
No model is involved. Retrieval sets are compared as sets. All systems and the
|
|
31
|
+
noise closure use `evals/facts.mjs`: resource links use `uses:` / `usedBy:`, API
|
|
32
|
+
links use `provides:` / `consumes:` / `provider:` / `consumer:`, and component
|
|
33
|
+
and placement links use bare entity refs. A relation also identifies its other
|
|
34
|
+
endpoint, as query's neighbour summaries do. Inverse edges respect direction.
|
|
35
|
+
An API subject supplies `provides:<its-ref>`, as query does. Documents use
|
|
36
|
+
`doc:` throughout, including the old targeted sweep (formerly `repodoc:`).
|
|
37
|
+
Compiler relation names are not separate facts. Tests pin this vocabulary.
|
|
26
38
|
|
|
27
39
|
## Metrics
|
|
28
40
|
|
|
29
41
|
| metric | definition |
|
|
30
42
|
|---|---|
|
|
31
|
-
| recall | fraction of the scenario's ground-truth facts
|
|
43
|
+
| recall | fraction of the scenario's ground-truth facts retrieved by each system |
|
|
32
44
|
| critical recall | the same, restricted to critical context: constraints, conflicts, observations, resources, API boundaries, event subjects |
|
|
33
|
-
| verification recall | fraction of the
|
|
45
|
+
| verification recall | fraction of the scenario's verification expectations returned; 1 when there are no expectations, for every system |
|
|
46
|
+
| check selectivity | distinct recommended check IDs / distinct check IDs in the repository graph (`verification` + `gates`); reported only, not gated; lower means fewer checks recommended |
|
|
34
47
|
| precision | `1 - irrelevance` |
|
|
35
48
|
| irrelevance (noise) | fraction of retrieved facts outside the graph's one-hop closure of the touched entities |
|
|
36
49
|
| retrieval reduction | `1 - catalog facts / baseline-full facts` |
|
|
37
50
|
|
|
51
|
+
Scenario verification expectations accept `verify:<command>` and
|
|
52
|
+
`verify-id:<check-id>`. Use check IDs when multiple components have the same
|
|
53
|
+
command (for example `go test ./...`), so the library's own check cannot stand in
|
|
54
|
+
for a dependent's. Retrieved context includes the entity refs already returned
|
|
55
|
+
in query's neighbour summaries, even when those neighbours are dependencies and
|
|
56
|
+
therefore absent from impact's affected components.
|
|
57
|
+
|
|
58
|
+
Check selectivity counts the union of checks returned by queries for touched
|
|
59
|
+
and impact-affected entities (`verification.items` and `globalGates`), plus
|
|
60
|
+
impact's `recommendedVerification`. It measures the returned lists, respecting
|
|
61
|
+
their output caps. IDs deduplicate repeated recommendations while preserving
|
|
62
|
+
separate scoped checks that happen to share a command. With no checks in the
|
|
63
|
+
graph, selectivity is 1 (there is nothing to narrow). Corpus values are scenario
|
|
64
|
+
means; the aggregate is scenario-weighted across corpora. Both CLI reports
|
|
65
|
+
include per-scenario values in their `--json` output.
|
|
66
|
+
|
|
67
|
+
Targeted baseline verification recall is 1 by construction: its verification strategy
|
|
68
|
+
runs every graph verification entry and repository gate, so it cannot miss a
|
|
69
|
+
registered expectation. Empty expectations also score 1 on both sides. Its
|
|
70
|
+
check selectivity is therefore 1. Search discovers its own checks; it does not
|
|
71
|
+
inherit this union. Selectivity is the meaningful comparison of
|
|
72
|
+
how much routing narrows the check set, read alongside catalog verification
|
|
73
|
+
recall so that omitting a required check is not mistaken for an improvement.
|
|
74
|
+
|
|
75
|
+
`recall` is a scenario mean. `microRecall` instead divides total fact hits by
|
|
76
|
+
total truth facts; per-scenario hit/total counts make this independently
|
|
77
|
+
recomputable. Aggregate micro-recall uses these raw counts, not corpus means.
|
|
78
|
+
The legacy JSON keys remain: catalog metrics are at the top level, targeted
|
|
79
|
+
metrics are under `baseline`, and search metrics are under `searchBaseline`.
|
|
80
|
+
|
|
81
|
+
Search returns scoped `(componentRef, command)` pairs without graph check IDs.
|
|
82
|
+
Only the scorer matches these pairs to graph verification entries and gates,
|
|
83
|
+
then counts their IDs for selectivity and `verify-id:` expectations. A root
|
|
84
|
+
command matches only a repository-scoped check. Identical commands in different
|
|
85
|
+
components remain distinct. Unregistered commands remain retrieved facts and
|
|
86
|
+
can contribute noise, but do not select a registered ID. Read selectivity with
|
|
87
|
+
verification recall; a low value can mean a missed check.
|
|
88
|
+
|
|
89
|
+
### Search baseline heuristics
|
|
90
|
+
|
|
91
|
+
`evals/baseline-search.mjs` imports only filesystem/path utilities, the existing
|
|
92
|
+
YAML/TOML syntax readers and the shared fact helper. It never invokes adapters,
|
|
93
|
+
routing, a package manager, a model or a shell search. The following rules are
|
|
94
|
+
fixed independently of scenario truth:
|
|
95
|
+
|
|
96
|
+
1. **Working tree.** Walk regular files in sorted name order, including
|
|
97
|
+
uncommitted files. Do not follow symlinks. Skip `.git`, `.agentdoc`,
|
|
98
|
+
`node_modules`, `.venv`, `venv`, `__pycache__`, `target`, `dist`, `build` and
|
|
99
|
+
`.next`, and any `graph.json`. Read JS/TS (including CJS/MJS/JSX/TSX), Go,
|
|
100
|
+
Rust, Python, proto, SQL, Markdown, RST, TXT, JSON, YAML, TOML, INI, CFG,
|
|
101
|
+
MOD, WORK, LOCK and Dockerfile text; skip NUL-containing files. This is a
|
|
102
|
+
literal exclusion list, not `.gitignore` or a tracked-file snapshot.
|
|
103
|
+
2. **Ownership only.** YAML documents with `apiVersion: agentdoc.dev/...`
|
|
104
|
+
are excluded from search. For `Component` documents only, take the metadata
|
|
105
|
+
name/namespace as identity, the descriptor directory as source root, and
|
|
106
|
+
string arrays under `spec.context` as owned document paths. Explicit document
|
|
107
|
+
ownership wins, otherwise the longest component root wins. No placement,
|
|
108
|
+
relations, API/resource declarations, journeys, observations or authority
|
|
109
|
+
rules are used. Other files under `agentdoc/` are excluded too.
|
|
110
|
+
3. **Import names.** Read each component's root `package.json` name, Cargo
|
|
111
|
+
package name and its underscore spelling, `go.mod` module path, Python
|
|
112
|
+
distribution names, and top-level Python packages with `__init__.py` or
|
|
113
|
+
`__main__.py` (under the root or `src/`). These are search terms, not
|
|
114
|
+
compiler-resolved units. A component without an import name can still own
|
|
115
|
+
touched paths and participate through relative imports or endpoint strings.
|
|
116
|
+
4. **Importers and importees.** Search source for JS/TS `from`, `import` and
|
|
117
|
+
`require` quoted specifiers; Go quoted strings; Rust `use`, `extern crate`
|
|
118
|
+
and `name::`; Python line-leading `import` / `from` names. Match exact names
|
|
119
|
+
or slash/dot subpaths. Search manifests for names at word/path boundaries,
|
|
120
|
+
and for local `path =`, `file:` and `link:` dependencies relative to that
|
|
121
|
+
manifest. Relative source specifiers are resolved against their file's
|
|
122
|
+
directory and mapped to an owner. Rust names declared with `mod` in the
|
|
123
|
+
same file are suppressed; Python imports with a matching local `.py` file
|
|
124
|
+
anywhere in the importing component are suppressed. Ambiguous sibling names
|
|
125
|
+
may reach multiple components. There is no symbol resolver, alias resolver
|
|
126
|
+
or full import grammar (for example Python comma imports are incomplete).
|
|
127
|
+
5. **Fixed point.** Import evidence connects components in both directions.
|
|
128
|
+
Start with owners of touched files (or explicitly touched component refs),
|
|
129
|
+
add all importers and importees, and repeat until no component is added.
|
|
130
|
+
API/endpoint text links described next participate in the same closure.
|
|
131
|
+
There is no hop limit or output cap.
|
|
132
|
+
6. **APIs and endpoints.** Read OpenAPI YAML `info.title` and `paths`, and
|
|
133
|
+
protobuf `service` names. A lowercase hyphenated title plus `-api` (without
|
|
134
|
+
duplicating an existing `-api` suffix) names an OpenAPI fact; a hyphenated
|
|
135
|
+
service name names a protobuf fact. CamelCase boundaries are split. These
|
|
136
|
+
labels are guesses, not lookups in the API catalog; namespaces default to
|
|
137
|
+
`default`. Find readers by literal contract file paths in component files.
|
|
138
|
+
Find providers by exact endpoint strings in source, Next-style
|
|
139
|
+
`/app/<path>/route.ts` files, or `Register<Service>Server` text. Contract
|
|
140
|
+
readers/providers are connected. Touching a contract seeds them and emits
|
|
141
|
+
the API ref and `provides:` fact; reached readers/providers emit API roles,
|
|
142
|
+
with a provider excluded from the consumer set. Other API kinds, dynamic
|
|
143
|
+
routes, semantic serving roles and arbitrary catalog names are not inferred.
|
|
144
|
+
Also connect source/TOML files sharing an exact slash-prefixed endpoint or
|
|
145
|
+
HTTP(S) URL pathname; Next `route.js`/`route.ts` paths count. Ignore `/`.
|
|
146
|
+
Hostnames are ignored. Common paths such as `/api/health` can consequently
|
|
147
|
+
connect unrelated services; the measured noise includes this overreach.
|
|
148
|
+
7. **Docs and events.** Read all text belonging to reached components. Markdown,
|
|
149
|
+
RST and TXT files count as `doc:`, except a basename containing `constraint`
|
|
150
|
+
gives `constraint:` and a path containing `runbook` gives `runbook:`.
|
|
151
|
+
In source lines mentioning subject, event, topic, publish or subscribe
|
|
152
|
+
(case insensitive), quoted lowercase-leading dotted strings with at least
|
|
153
|
+
three segments count as events. There are no configured event prefixes or
|
|
154
|
+
compiler event contracts. Imports reach a shared subject's defining file.
|
|
155
|
+
8. **Resources and externals.** PostgreSQL URL schemes, `sql.Open("postgres"`,
|
|
156
|
+
`pg`, `psycopg`, `asyncpg`, `sqlx`, `diesel`, or SQL files under `migrations/`
|
|
157
|
+
suggest `resource:default/<component-name>-postgres`. The word `redis`
|
|
158
|
+
suggests `<component-name>-redis`. TOML-style `bucket =` / `queue_name =`
|
|
159
|
+
string literals supply their slugged names. These guesses emit resource refs
|
|
160
|
+
and `uses:`; no catalog resource names are read. Plain environment variable
|
|
161
|
+
names without one of these signals are insufficient to identify a resource.
|
|
162
|
+
External names come from Node dependency maps, Cargo dependency tables,
|
|
163
|
+
Go requirements, Python project dependencies/extras/dev groups and
|
|
164
|
+
requirements text. Names already identifying local units are excluded.
|
|
165
|
+
Conditional dependencies and comments are not semantically evaluated.
|
|
166
|
+
9. **Checks.** Node scripts whose names mention test/lint/typecheck/check,
|
|
167
|
+
clippy/vet/pytest/ruff/mypy/unittest return their literal commands; root Turbo
|
|
168
|
+
tasks with those names generate `turbo run <task> --filter=<package>` for
|
|
169
|
+
reached packages. A Go manifest suggests `go test ./...` and `go vet ./...`;
|
|
170
|
+
a Cargo package suggests `cargo test -p <name>` and `cargo clippy -p <name>`.
|
|
171
|
+
Python component text mentioning pytest, ruff or mypy suggests that command
|
|
172
|
+
(`ruff check` for ruff); the nearest ancestor uv/Poetry lockfile selects
|
|
173
|
+
`uv run` / `poetry run`. Unittest without pytest suggests the standard-library
|
|
174
|
+
command `python -m unittest discover`. These are conventional suggestions,
|
|
175
|
+
not proof the checks pass. GitHub Actions YAML run steps are split by lines:
|
|
176
|
+
check-like lines select reached components through explicit package names
|
|
177
|
+
or step/job working directories; unscoped root lines are repository checks.
|
|
178
|
+
Filtered commands, `cd` and matrix expressions without a resolved scope are
|
|
179
|
+
skipped. Expressions, shell semantics and workflow matrices are not expanded.
|
|
180
|
+
Checks deduplicate by component and command.
|
|
181
|
+
10. **No hidden knowledge.** There is no scenario-specific branch, truth-set
|
|
182
|
+
lookup or graph membership filter in retrieval. Unsupported constructs may
|
|
183
|
+
miss facts; naming guesses may add facts absent from the graph. Search does
|
|
184
|
+
not infer placement, runtime observations or authority conflicts from code.
|
|
185
|
+
|
|
38
186
|
### Why noise is measured against reachability, not against the truth set
|
|
39
187
|
|
|
40
188
|
The first definition of noise was the complement of truth recall. It is
|
|
@@ -58,6 +206,65 @@ repository; a router that returned *every fact in the repository* then scored as
|
|
|
58
206
|
precise, because everything was technically adjacent to something. The metric
|
|
59
207
|
was measuring its own closure, not the router.
|
|
60
208
|
|
|
209
|
+
## Measured comparison (2026-10-27)
|
|
210
|
+
|
|
211
|
+
Values are scenario means; the aggregate weights corpora by scenario count.
|
|
212
|
+
Recall is higher-is-better; noise and check selectivity are lower-is-better
|
|
213
|
+
when read alongside recall. All three systems have verification recall 1.0 on
|
|
214
|
+
these scenarios.
|
|
215
|
+
|
|
216
|
+
| Corpus (scenarios) | System | Mean recall | Noise | Check selectivity |
|
|
217
|
+
|---|---|---:|---:|---:|
|
|
218
|
+
| Koda (9) | baseline-targeted (corrected) | 100.00% | 41.23% | 100.00% |
|
|
219
|
+
| Koda (9) | baseline-search | 71.90% | 71.55% | 100.00% |
|
|
220
|
+
| Koda (9) | catalog | 100.00% | 4.02% | 28.79% |
|
|
221
|
+
| Node/TS (3) | baseline-targeted (corrected) | 100.00% | 17.86% | 100.00% |
|
|
222
|
+
| Node/TS (3) | baseline-search | 91.67% | 30.64% | 83.33% |
|
|
223
|
+
| Node/TS (3) | catalog | 100.00% | 0.00% | 100.00% |
|
|
224
|
+
| Go (4) | baseline-targeted (corrected) | 93.75% | 28.94% | 100.00% |
|
|
225
|
+
| Go (4) | baseline-search | 83.33% | 26.01% | 100.00% |
|
|
226
|
+
| Go (4) | catalog | 100.00% | 5.88% | 66.67% |
|
|
227
|
+
| Mixed monorepo (4) | baseline-targeted (corrected) | 100.00% | 37.82% | 100.00% |
|
|
228
|
+
| Mixed monorepo (4) | baseline-search | 100.00% | 41.25% | 100.00% |
|
|
229
|
+
| Mixed monorepo (4) | catalog | 100.00% | 5.88% | 54.17% |
|
|
230
|
+
| Rust (4) | baseline-targeted (corrected) | 100.00% | 48.25% | 100.00% |
|
|
231
|
+
| Rust (4) | baseline-search | 100.00% | 32.81% | 83.33% |
|
|
232
|
+
| Rust (4) | catalog | 100.00% | 0.00% | 25.00% |
|
|
233
|
+
| Python (6) | baseline-targeted (corrected) | 100.00% | 46.70% | 100.00% |
|
|
234
|
+
| Python (6) | baseline-search | 100.00% | 23.68% | 41.67% |
|
|
235
|
+
| Python (6) | catalog | 100.00% | 0.00% | 41.67% |
|
|
236
|
+
| Aggregate (30) | baseline-targeted (corrected) | 99.17% | 38.83% | 100.00% |
|
|
237
|
+
| Aggregate (30) | baseline-search | 88.51% | 42.61% | 84.44% |
|
|
238
|
+
| Aggregate (30) | catalog | 100.00% | 2.77% | 46.42% |
|
|
239
|
+
|
|
240
|
+
Corrected targeted micro-recall is **100/101 = 0.9901** (mean recall 0.9917).
|
|
241
|
+
Its only missing truth fact is `component:default/ingest` in b-04. Search
|
|
242
|
+
micro-recall is **85/101 = 0.8416** (mean recall 0.8851); catalog micro- and
|
|
243
|
+
mean recall are both 1.0. These averages answer different questions and should
|
|
244
|
+
not be interchanged.
|
|
245
|
+
|
|
246
|
+
Search matches catalog recall in 21 scenarios: koda-04, koda-07, a-02, a-03,
|
|
247
|
+
b-01, b-03, b-04, c-01, c-02, c-03, c-04, e-01, e-02, e-03, e-04, f-01,
|
|
248
|
+
f-02, f-03, f-04, f-05 and f-06. It exceeds catalog recall in none.
|
|
249
|
+
|
|
250
|
+
The label correction removes the old recall gap against targeted retrieval.
|
|
251
|
+
Search recovers all expected facts in many import-related changes, but misses
|
|
252
|
+
some catalog identity/placement and authority facts. Its broad fixed point
|
|
253
|
+
also reads unrelated context, especially through common endpoint strings in
|
|
254
|
+
Koda. These numbers compare deterministic retrieval rules on small authored
|
|
255
|
+
corpora; they do not establish an agent productivity or token-saving benefit,
|
|
256
|
+
or an upper bound on search. Noise is judged by the catalog's graph closure,
|
|
257
|
+
so it is not independent evidence that every extra source fact is irrelevant.
|
|
258
|
+
|
|
259
|
+
`recallNotWorseThanTargetedBaseline` still **passes**: catalog and corrected
|
|
260
|
+
targeted recall tie in five corpora; catalog is higher in Go. Every other
|
|
261
|
+
applicable pre-registered gate passes. No threshold or scenario changed and
|
|
262
|
+
search has no new gate. Normalizing the vocabulary changes noise and deduplicates
|
|
263
|
+
fact counts, so old noise/size figures are not directly comparable. The largest
|
|
264
|
+
full inventory now has 86 facts (Koda), below the unchanged 100-fact floor.
|
|
265
|
+
Aggregate retrieval reduction is therefore **not measured** (`null`), with no
|
|
266
|
+
corpus included in that aggregate. Per-corpus raw ratios remain reported.
|
|
267
|
+
|
|
61
268
|
## Thresholds
|
|
62
269
|
|
|
63
270
|
Pre-registered in `evals/thresholds.json`, dated, and not moved since:
|
|
@@ -101,6 +308,8 @@ Two additional gates use the same numbers:
|
|
|
101
308
|
| `fixtures/a-node-ts-postgres` | 3 | single Node/TypeScript service, REST, Postgres, GitHub Actions |
|
|
102
309
|
| `fixtures/b-go-multi-service` | 4 | multi-service Go, protobuf contracts, shared library, resources |
|
|
103
310
|
| `fixtures/c-alt-monorepo` | 4 | a layout that is deliberately not `apps`+`packages`+`workers`, mixed runtimes, inconsistent pre-existing documentation |
|
|
311
|
+
| `fixtures/e-rust-workspace` | 4 | Cargo workspace, shared crate and its dependent, member manifest, local module, ambiguous crate names |
|
|
312
|
+
| `fixtures/f-python-workspace` | 6 | uv workspace, shared library and its dependent, member manifest, shadowed import, nested workspace, Alembic database, unittest |
|
|
104
313
|
|
|
105
314
|
Scenarios are repository-scoped: a scenario only runs against the corpus it was
|
|
106
315
|
written for, declared by a `repository` tag in a `.agentdoc-fixture.json` at the
|
|
@@ -117,7 +326,12 @@ cron; debug a production mismatch.
|
|
|
117
326
|
The synthetic fixtures add: change an API contract; change a write path; change
|
|
118
327
|
the only-writer rule; break an end-to-end journey; change an edge worker's flush
|
|
119
328
|
schedule; follow stale documentation while debugging; change a subject vocabulary
|
|
120
|
-
shared across runtimes; add an operation to a contract.
|
|
329
|
+
shared across runtimes; add an operation to a contract. The Rust workspace adds
|
|
330
|
+
a shared crate, the manifest that declares its dependents, a local module that
|
|
331
|
+
shares a name with another crate, and a hyphenated crate beside its underscored
|
|
332
|
+
twin. The Python workspace adds a shared library, a service manifest, an import
|
|
333
|
+
shadowed by a local module, a nested workspace member, an Alembic migration
|
|
334
|
+
bound to its database, and a job whose check is `python -m unittest discover`.
|
|
121
335
|
|
|
122
336
|
## Running it
|
|
123
337
|
|
|
@@ -157,9 +371,18 @@ Each was fixed in the engine, not by relaxing a threshold.
|
|
|
157
371
|
- Ground truth is hand-authored per scenario. It is written from the
|
|
158
372
|
repository's own manifests and contracts, not from the system's output, but it
|
|
159
373
|
is still a human judgement about what matters.
|
|
160
|
-
-
|
|
161
|
-
|
|
162
|
-
|
|
374
|
+
- Search is one explicit model of exploration, not a record of an agent
|
|
375
|
+
session or an upper bound on what search can find. Its import and endpoint
|
|
376
|
+
closure deliberately errs toward reading more; different heuristics can
|
|
377
|
+
produce different recall and noise.
|
|
378
|
+
- The targeted baseline's old label mismatch is corrected. It still reads the
|
|
379
|
+
compiled graph, so its near-perfect recall does not measure unaided search.
|
|
380
|
+
The former 100%-versus-58% comparison remains withdrawn; see
|
|
381
|
+
[BENCHMARK.md](BENCHMARK.md#correction-the-fact-recall-comparison-is-withdrawn).
|
|
382
|
+
- Noise is judged by the catalog's one-hop graph closure, including for search.
|
|
383
|
+
Useful source facts absent from that graph can count as noise, and legitimate
|
|
384
|
+
transitive importer context can fall outside one hop. This is not an
|
|
385
|
+
independent ground truth for irrelevant work or an agent time/token metric.
|
|
163
386
|
- `retrievalReduction` is a fact-count proxy, not a token count. It does not
|
|
164
387
|
weight a long constraint file against a short ref.
|
|
165
388
|
- Scenarios touch one or two files. A change spanning twenty files is not
|
package/docs/MIGRATION.md
CHANGED
|
@@ -164,9 +164,27 @@ agentdoc check
|
|
|
164
164
|
```
|
|
165
165
|
|
|
166
166
|
`init` writes `agentdoc/agentdoc.config.yaml`, the four central descriptor
|
|
167
|
-
files, `agentdoc/journeys.yaml`, the documentation hierarchy
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
files, `agentdoc/journeys.yaml`, the documentation hierarchy, the observations
|
|
168
|
+
README, and a CI workflow. It never overwrites an existing file without
|
|
169
|
+
`--force`, and reports every path it wrote or skipped.
|
|
170
|
+
|
|
171
|
+
The documentation hierarchy goes where the configuration says. When
|
|
172
|
+
`agentdoc/agentdoc.config.yaml` already exists, `init` and `scaffold --write`
|
|
173
|
+
write `PRODUCT.md`, `ARCHITECTURE.md`, `CONSTRAINTS.md` and the ADR README to
|
|
174
|
+
the configured `docs.product`, `docs.architecture`, `docs.constraints` and
|
|
175
|
+
`docs.adrDir`. A role the configuration leaves out is not written anywhere and
|
|
176
|
+
is listed under `notConfigured`. Without a configuration, `init` uses
|
|
177
|
+
`agentdoc/docs/PRODUCT.md`, `agentdoc/docs/ARCHITECTURE.md`,
|
|
178
|
+
`agentdoc/docs/CONSTRAINTS.md` and `agentdoc/docs/adr/`, and records those
|
|
179
|
+
paths in the configuration it writes.
|
|
180
|
+
|
|
181
|
+
Authored AgentDoc material lives in `agentdoc/`; generated output lives in
|
|
182
|
+
`.agentdoc/`. `docs/` stays the product's own user documentation, which is
|
|
183
|
+
often published as-is (MkDocs, Docusaurus, GitHub Pages). To keep the skeleton
|
|
184
|
+
in `docs/`, or anywhere else, declare `docs.product`, `docs.architecture`,
|
|
185
|
+
`docs.constraints` and `docs.adrDir` in `agentdoc/agentdoc.config.yaml` before
|
|
186
|
+
running `init` or `scaffold --write`. A configuration that already declares
|
|
187
|
+
`docs.*` keeps its paths, `docs/...` included, and nothing ever moves an
|
|
170
188
|
existing file.
|
|
171
189
|
|
|
172
190
|
`scaffold` is a dry run unless `--write` is passed.
|
|
@@ -204,8 +222,10 @@ Then, in order:
|
|
|
204
222
|
documentation, fix the code, or record an authority rule saying which class
|
|
205
223
|
wins for that key.
|
|
206
224
|
3. **Orphan documentation.** `documentation.orphanDocs`. For each: link it to a
|
|
207
|
-
|
|
208
|
-
|
|
225
|
+
Component, Resource or System via `context.docs`, `context.constraints` or
|
|
226
|
+
`context.runbooks`, or delete it. Conventional component documents and
|
|
227
|
+
configured repository-wide roles also count as routed. An orphan is a
|
|
228
|
+
document nothing will ever route to.
|
|
209
229
|
4. **Missing documentation.** `documentation.undocumentedComponents` and
|
|
210
230
|
`documentation.unverifiedComponents`. These are the components an agent will
|
|
211
231
|
get wrong.
|
|
@@ -237,7 +257,7 @@ worth discovering before documenting it.
|
|
|
237
257
|
| `PRODUCT.md` | what it is, for whom, what it does not do | describe components |
|
|
238
258
|
| `ARCHITECTURE.md` | why the shape is what it is | enumerate components or contracts |
|
|
239
259
|
| `CONSTRAINTS.md` | repository-wide rules, one rule one reason one check | restate a component's constraints |
|
|
240
|
-
|
|
|
260
|
+
| `<docs.adrDir>/NNNN-*.md` | one decision, its consequences, superseded not edited | be a design document |
|
|
241
261
|
| `<component>/CONSTRAINTS.md` | rules for that component only | duplicate a repository-wide rule |
|
|
242
262
|
| `<component>/README.md` | how to run it, what it is for | restate the catalog |
|
|
243
263
|
| runbooks | operational procedure | contain architectural decisions |
|
package/docs/PROVENANCE.md
CHANGED
|
@@ -89,6 +89,12 @@ record is complete by construction rather than by remembering to register files.
|
|
|
89
89
|
A change to any input changes the hash; `check` refuses a graph whose hash
|
|
90
90
|
disagrees.
|
|
91
91
|
|
|
92
|
+
Paths git ignores are not inputs (SPEC §7.2): the tracked read path, probes and
|
|
93
|
+
listings all treat them as absent, so local build output in an ignored
|
|
94
|
+
directory cannot move the hash. Tracked files always count, even when they
|
|
95
|
+
match an ignore pattern. Outside git the input set is the filesystem minus
|
|
96
|
+
`discovery.generatedDirs`.
|
|
97
|
+
|
|
92
98
|
## Secrets
|
|
93
99
|
|
|
94
100
|
Two independent layers, because derivation can synthesise a secret-shaped string
|
package/docs/QUERY.md
CHANGED
|
@@ -15,12 +15,29 @@ Resolution order:
|
|
|
15
15
|
1. an exact entity ref;
|
|
16
16
|
2. a documentation or constraint file a descriptor links, even outside the
|
|
17
17
|
component's source root;
|
|
18
|
-
3.
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
3. the component whose source root most specifically owns the path: a
|
|
19
|
+
directory that is exactly a unit root, or else the longest unit root
|
|
20
|
+
containing it;
|
|
21
|
+
4. a canonical contract file, which routes to the API entity that claims it;
|
|
22
|
+
5. a root component (a descriptor at the repository root, `sourcePath: "."`),
|
|
23
|
+
which owns every file no step above claims.
|
|
24
|
+
|
|
25
|
+
Paths are compared in one canonical, repository-relative form, the one git uses
|
|
26
|
+
for pathspecs and `ls-files`: `./vite.config.ts`, `vite.config.ts` and
|
|
27
|
+
`tooling//config.ts` are the same paths as `vite.config.ts` and
|
|
28
|
+
`tooling/config.ts`. Ownership is decided on whole path segments, never on a
|
|
29
|
+
raw string prefix, so a unit at `src` owns `src/x.ts` and never `src2/x.ts`.
|
|
30
|
+
When several unit roots contain a path, the longest one wins, as in a routing
|
|
31
|
+
table or a CODEOWNERS file. The repository root is the shortest possible root,
|
|
32
|
+
so a nested component always takes precedence over a root component. A path
|
|
33
|
+
that is absolute or climbs out of the repository with `..` resolves to nothing.
|
|
34
|
+
A needle shaped like an entity ref (`kind:namespace/name`) that names no entity
|
|
35
|
+
is unresolved; it is never treated as a file a root component owns.
|
|
21
36
|
|
|
22
37
|
If nothing resolves, the command fails with `AGENTDOC_REF_UNRESOLVED` and exits
|
|
23
38
|
non-zero. It never falls back to "the nearest component" or to a global dump.
|
|
39
|
+
Without a root descriptor, a file outside every unit root stays unresolved: the
|
|
40
|
+
repository root is owned only when a descriptor claims it.
|
|
24
41
|
|
|
25
42
|
## Freshness refusal
|
|
26
43
|
|
|
@@ -84,6 +101,17 @@ withheld.
|
|
|
84
101
|
endpoint is proved by running their suites, so `verification` for an API subject
|
|
85
102
|
is the union over the provider and its consumers.
|
|
86
103
|
|
|
104
|
+
**A component change carries its dependents' checks.** Verification follows the
|
|
105
|
+
reverse `buildDependsOn` / `testDependsOn` closure (including their inverse
|
|
106
|
+
`buildDependencyOf` / `testDependencyOf` spellings). Build dependents contribute
|
|
107
|
+
all tiers; a route containing a test dependency contributes only `unit` checks.
|
|
108
|
+
If an all-build route also reaches that dependent, all tiers apply. Cycles are
|
|
109
|
+
visited safely. The subject's own checks come first, then the dependents', with
|
|
110
|
+
each group sorted by check ID. A check shared by multiple components appears
|
|
111
|
+
once by ID; identical command text in different component checks remains scoped
|
|
112
|
+
by its ID and `configPaths`. The usual verification budget and truncation fields
|
|
113
|
+
apply to the combined list.
|
|
114
|
+
|
|
87
115
|
**Journey peers are named.** If a component appears in a journey, the other
|
|
88
116
|
components in that journey are in scope and are listed explicitly rather than
|
|
89
117
|
left for the agent to rediscover.
|
|
@@ -127,9 +155,25 @@ the slice is never mistaken for the whole.
|
|
|
127
155
|
| assertions | 25 |
|
|
128
156
|
| provenance | 20 |
|
|
129
157
|
| contracts | 10 |
|
|
158
|
+
| docs (markdown) | 12 |
|
|
159
|
+
| constraints (markdown) | uncapped |
|
|
160
|
+
| runbooks (markdown) | 12 |
|
|
130
161
|
|
|
131
162
|
## Markdown rendering
|
|
132
163
|
|
|
164
|
+
Component, Resource and System descriptors may carry `spec.context`. Their
|
|
165
|
+
documentation, constraints and runbooks appear under `docs`, `CONSTRAINTS` and
|
|
166
|
+
`runbooks`, respectively. Components also include conventional files discovered
|
|
167
|
+
in their source root. API and Domain schemas do not allow `context`.
|
|
168
|
+
|
|
169
|
+
Markdown docs and runbooks default to 12 paths each. Constraints are uncapped by
|
|
170
|
+
default so critical routing context is preserved. Set an explicit cap with, for
|
|
171
|
+
example, `--budget docs=1,constraints=5,runbooks=1`. A shortened line ends with
|
|
172
|
+
`[truncated: 1 of 2]`, reporting the number shown and the total. JSON retains the
|
|
173
|
+
complete arrays under `constraints.docs`, `constraints.constraints` and
|
|
174
|
+
`constraints.runbooks`; these markdown-only caps do not shorten the JSON arrays
|
|
175
|
+
or `entity.spec.context`.
|
|
176
|
+
|
|
133
177
|
`--md` (the default) produces a compact block suitable for a prompt or a human:
|
|
134
178
|
|
|
135
179
|
```
|
|
@@ -164,10 +208,37 @@ contracts, documentation, resources, verification and catalog sources; whether
|
|
|
164
208
|
the graph will be invalidated; the recommended verification commands; any open
|
|
165
209
|
conflicts on the affected subjects; and related journeys.
|
|
166
210
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
211
|
+
Changed paths resolve to components with the same rules as `query`: a `./`
|
|
212
|
+
prefix is ignored, the longest owning unit root wins, and a root component
|
|
213
|
+
receives the files no other component owns. A file nothing owns is listed in
|
|
214
|
+
`unmatchedFiles`.
|
|
215
|
+
|
|
216
|
+
Reachability rules: a contract change reaches its provider and every consumer;
|
|
217
|
+
a Resource change reaches components bound to that exact Resource. A migration
|
|
218
|
+
change (including Alembic `alembic/versions`) reports its changed path and the
|
|
219
|
+
logical databases bound to its enclosing component through existing
|
|
220
|
+
`usesResource` / `resourceUsedBy` relations. Migration authority alone never
|
|
221
|
+
establishes ownership or invents a Resource binding.
|
|
222
|
+
|
|
223
|
+
A component change reaches its reverse build/test dependency closure, plus
|
|
224
|
+
direct runtime-call and schedule peers. Dependencies are not affected just
|
|
225
|
+
because the changed component uses them; they remain explicitly typed context
|
|
226
|
+
in query's `relations`. The dependent closure has no hop cutoff, so a long chain
|
|
227
|
+
cannot silently omit an affected consumer.
|
|
228
|
+
|
|
229
|
+
Changing a workflow file follows recorded verification `configPaths` to the
|
|
230
|
+
components its checks cover. Those components seed the reverse build/test
|
|
231
|
+
dependency closure just like directly changed components. Matching repository
|
|
232
|
+
gates appear in `affectedGates`; a workflow matched by any check or gate is
|
|
233
|
+
removed from `unmatchedFiles`.
|
|
234
|
+
|
|
235
|
+
Impact uses the same verification selection and ordering as query, including
|
|
236
|
+
the test-only tier restriction. Directly affected components' checks precede
|
|
237
|
+
their dependents' checks, followed by the matching repository gates. The
|
|
238
|
+
combined `recommendedVerification` array is capped at 25;
|
|
239
|
+
`recommendedVerificationTotal` gives the uncapped count including gates and
|
|
240
|
+
`recommendedVerificationTruncated` reports overflow. Query's default cap is 12
|
|
241
|
+
and can be adjusted with `--budget verification=...`.
|
|
171
242
|
|
|
172
243
|
## Using it in a prompt
|
|
173
244
|
|
package/docs/SPEC.md
CHANGED
|
@@ -195,6 +195,34 @@ Every byte flows through the read tracker, so the record is complete by
|
|
|
195
195
|
construction. A consumer must refuse a graph whose `inputHash` or `dirty` flag
|
|
196
196
|
disagrees with the current checkout. There is no fallback.
|
|
197
197
|
|
|
198
|
+
**The input set is the checkout as git sees it.** Reads, probes, listings and
|
|
199
|
+
walks — and therefore discovery as well as the hash — never see a path git
|
|
200
|
+
ignores: a local build writing into an ignored directory (`.astro/`, `build/`,
|
|
201
|
+
`*.log`) changes nothing, and a graph compiled after that build is
|
|
202
|
+
byte-identical to the one a clean checkout or CI produces. An ignored path is
|
|
203
|
+
treated as absent: probed as `false`, left out of listings and walks, and a
|
|
204
|
+
tracked read of it fails like a read of a missing file. Git is the oracle
|
|
205
|
+
(`git status --ignored=matching`, one call per compile, shared with the dirty
|
|
206
|
+
check), so its documented semantics apply exactly:
|
|
207
|
+
|
|
208
|
+
- a tracked file stays an input even when it matches an ignore pattern;
|
|
209
|
+
- nested `.gitignore` files, `$GIT_DIR/info/exclude` and the user's
|
|
210
|
+
`core.excludesFile` apply with gitignore(5) precedence, negations included,
|
|
211
|
+
and a file whose parent directory is excluded cannot be re-included;
|
|
212
|
+
- `core.excludesFile` counts on purpose. An ignored untracked file is absent
|
|
213
|
+
from every clean checkout whatever rule ignored it, so excluding it only ever
|
|
214
|
+
brings the local input set closer to CI's — and it keeps the hash in step
|
|
215
|
+
with `git status`, which drives `dirty`;
|
|
216
|
+
- an ignored *ancestor* of the project root (a project nested in a larger
|
|
217
|
+
checkout) is not honoured: the directory the compiler is pointed at is
|
|
218
|
+
compiled whole.
|
|
219
|
+
|
|
220
|
+
`discovery.generatedDirs` (or its built-in list) is excluded in addition, in a
|
|
221
|
+
git checkout or not. Without git — no repository, no git binary — there is no
|
|
222
|
+
oracle: the input set is the filesystem minus those directories, and
|
|
223
|
+
`.gitignore` files are not interpreted. `check` recomputes the hash by running
|
|
224
|
+
the same compile, so compile and check cannot disagree on the input set.
|
|
225
|
+
|
|
198
226
|
The recorded `commit` is **provenance, not a gate**: committing the graph changes
|
|
199
227
|
`HEAD`, so a commit-equality check could never be satisfied by a committed graph.
|
|
200
228
|
What determines whether the graph still describes the repository is the input
|
|
@@ -235,7 +263,31 @@ that references the contract file from a source or build-configuration file.
|
|
|
235
263
|
|
|
236
264
|
The core has no knowledge of folder names. A directory is a unit when it holds
|
|
237
265
|
a component descriptor, when an adapter recognises it from a workspace file, or
|
|
238
|
-
when it is listed in `discovery.supplementalRoots`.
|
|
266
|
+
when it is listed in `discovery.supplementalRoots`. Discovery sees only the
|
|
267
|
+
input set of §7.2: a descriptor or manifest under a git-ignored path is not
|
|
268
|
+
discovered.
|
|
269
|
+
|
|
270
|
+
`init` configures the recursive component descriptor glob `*/**/agentdoc.yaml`,
|
|
271
|
+
so descriptors for adapter-discovered units and units added later are loaded
|
|
272
|
+
without regenerating a path list. The scaffold layout scan supplements its
|
|
273
|
+
shallow marker heuristics with adapter discovery: declared workspace members
|
|
274
|
+
under `examples`, `example`, `test`, or `tests`, or beyond the scan depth, remain
|
|
275
|
+
units. Those names alone do not create units. Scaffold selects artifact types
|
|
276
|
+
from adapter shape: deployable (including both shapes) → `service`, importable
|
|
277
|
+
only → `library`, neither → `other`. Placement remains a human choice.
|
|
278
|
+
|
|
279
|
+
`discovery.notUnitRoots` excludes each listed root and its whole subtree from
|
|
280
|
+
inferred units (adapter markers, adapter `roots()`, and supplemental roots) and
|
|
281
|
+
from scaffold layout scans and descriptor proposals, in both dry run and
|
|
282
|
+
`--write`. The defaults are `agentdoc`, `docs`, `scripts`, `node_modules`, and
|
|
283
|
+
`.github`; a configured list replaces them, and `[]` disables these exclusions.
|
|
284
|
+
An existing authored component descriptor strictly below an excluded root still
|
|
285
|
+
claims that directory as a unit. Its own root must supply the usual adapter
|
|
286
|
+
evidence for its artifact type; the exclusion does not waive corroboration.
|
|
287
|
+
Scaffold preserves that descriptor and never proposes or writes one there.
|
|
288
|
+
A descriptor exactly at an excluded root is refused with
|
|
289
|
+
`AGENTDOC_COMPONENT_UNCORROBORATED` naming the exclusion: remove that root from
|
|
290
|
+
`notUnitRoots` or move the descriptor.
|
|
239
291
|
|
|
240
292
|
- Every discovered unit must have exactly one descriptor at its root, or
|
|
241
293
|
`AGENTDOC_COVERAGE_ZERO`.
|
|
@@ -269,7 +321,7 @@ only the codes in `ACCEPTABLE_WARNING_CODES` may be accepted.
|
|
|
269
321
|
| command | contract |
|
|
270
322
|
|---|---|
|
|
271
323
|
| `init [--force]` | write a configuration and documentation skeleton; never clobber unless `--force` |
|
|
272
|
-
| `scaffold [--write]` | generate descriptors from provable evidence; dry run by default |
|
|
324
|
+
| `scaffold [--write]` | generate descriptors from provable evidence; dry run by default; never clobbers |
|
|
273
325
|
| `audit` | classify every fact; runs even when the repository cannot compile |
|
|
274
326
|
| `validate` | sources, schemas, semantic checks; no artifact written |
|
|
275
327
|
| `compile [--require-clean]` | deterministic build, atomic replace |
|
|
@@ -280,6 +332,21 @@ only the codes in `ACCEPTABLE_WARNING_CODES` may be accepted.
|
|
|
280
332
|
| `pin-evidence [--json]` | check the digests pinned by reviewed overrides |
|
|
281
333
|
| `doctor` | environment self-check: node version, schema bundle, git, writability |
|
|
282
334
|
|
|
335
|
+
`init` and `scaffold` write the documentation skeleton to the paths an
|
|
336
|
+
existing configuration declares in `docs.product`, `docs.architecture`,
|
|
337
|
+
`docs.constraints` and `docs.adrDir`, read through the same loader as
|
|
338
|
+
`compile` and `audit`. A role the configuration omits is not written and is
|
|
339
|
+
reported in `notConfigured`; nothing falls back to a default. Only when no
|
|
340
|
+
configuration exists does `init` use the defaults `agentdoc/docs/PRODUCT.md`,
|
|
341
|
+
`agentdoc/docs/ARCHITECTURE.md`, `agentdoc/docs/CONSTRAINTS.md` and
|
|
342
|
+
`agentdoc/docs/adr/`, and record them in the `docs:` block of the configuration
|
|
343
|
+
it writes. Authored AgentDoc material lives in `agentdoc/`; generated output
|
|
344
|
+
lives in `.agentdoc/`. `docs/` is left to the product's own user documentation:
|
|
345
|
+
`init` and `scaffold` write there only when a configuration declares `docs.*`
|
|
346
|
+
paths under it, and they never move an existing file. A target outside the
|
|
347
|
+
repository, including through a symlink, fails with `AGENTDOC_PATH_ESCAPE`
|
|
348
|
+
before anything is written.
|
|
349
|
+
|
|
283
350
|
Exit codes: `0` ok, `1` failure, `2` usage.
|
|
284
351
|
|
|
285
352
|
## 13. Error codes
|
|
@@ -21,7 +21,7 @@ jobs:
|
|
|
21
21
|
with: { node-version: '24' }
|
|
22
22
|
- name: Install the agentdoc CLI
|
|
23
23
|
# Unpinned tracks main. For CI reproducibility pin a tag or commit:
|
|
24
|
-
# github:lbframe/agent-doc-system#v1.
|
|
24
|
+
# github:lbframe/agent-doc-system#v1.3.0
|
|
25
25
|
# A compiler upgrade intentionally invalidates committed graphs.
|
|
26
26
|
run: npm install -g github:lbframe/agent-doc-system
|
|
27
27
|
- name: Validate and compile
|