agent-doc-system 0.0.0-stage → 1.1.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.
Files changed (124) hide show
  1. package/INSTALL_FOR_AGENTS.md +60 -0
  2. package/LICENSE +21 -0
  3. package/README.md +98 -2
  4. package/adapters/ansible.mjs +306 -0
  5. package/adapters/cache.mjs +27 -0
  6. package/adapters/contract-consumers.mjs +56 -0
  7. package/adapters/database.mjs +116 -0
  8. package/adapters/deploy-config.mjs +34 -0
  9. package/adapters/dockerfile.mjs +26 -0
  10. package/adapters/events.mjs +145 -0
  11. package/adapters/generic.mjs +86 -0
  12. package/adapters/github-actions.mjs +106 -0
  13. package/adapters/go.mjs +91 -0
  14. package/adapters/graphql.mjs +28 -0
  15. package/adapters/index.mjs +56 -0
  16. package/adapters/node.mjs +118 -0
  17. package/adapters/object-store.mjs +64 -0
  18. package/adapters/oidc.mjs +56 -0
  19. package/adapters/openapi.mjs +50 -0
  20. package/adapters/pnpm.mjs +42 -0
  21. package/adapters/protobuf.mjs +35 -0
  22. package/adapters/registry.mjs +27 -0
  23. package/adapters/resource-client.mjs +61 -0
  24. package/adapters/turborepo.mjs +135 -0
  25. package/adapters/typescript.mjs +85 -0
  26. package/adapters/wrangler.mjs +206 -0
  27. package/bin/agentdoc.mjs +494 -0
  28. package/bin/commands.mjs +169 -0
  29. package/bin/usage.txt +28 -0
  30. package/core/acceptances.mjs +69 -0
  31. package/core/audit.mjs +456 -0
  32. package/core/authority/classes.mjs +39 -0
  33. package/core/authority/engine.mjs +266 -0
  34. package/core/authority/facts.mjs +41 -0
  35. package/core/codes.mjs +126 -0
  36. package/core/compile.mjs +1018 -0
  37. package/core/contracts.mjs +121 -0
  38. package/core/derive.mjs +102 -0
  39. package/core/descriptors.mjs +489 -0
  40. package/core/determinism.mjs +95 -0
  41. package/core/discover.mjs +147 -0
  42. package/core/facts.mjs +53 -0
  43. package/core/fsx.mjs +348 -0
  44. package/core/graph.mjs +260 -0
  45. package/core/impact.mjs +175 -0
  46. package/core/indexes.mjs +165 -0
  47. package/core/jsonschema.mjs +298 -0
  48. package/core/normative.mjs +171 -0
  49. package/core/observations.mjs +129 -0
  50. package/core/provenance.mjs +83 -0
  51. package/core/query.mjs +384 -0
  52. package/core/relations.mjs +286 -0
  53. package/core/scaffold.mjs +322 -0
  54. package/core/secrets.mjs +92 -0
  55. package/core/sourcescan.mjs +88 -0
  56. package/core/toml.mjs +163 -0
  57. package/core/yaml.mjs +477 -0
  58. package/docs/ADAPTERS.md +207 -0
  59. package/docs/AUTHORITY.md +269 -0
  60. package/docs/CI.md +138 -0
  61. package/docs/COMPARISON-KODA.md +164 -0
  62. package/docs/EVALS.md +166 -0
  63. package/docs/MIGRATION.md +261 -0
  64. package/docs/PROVENANCE.md +133 -0
  65. package/docs/QUERY.md +180 -0
  66. package/docs/SPEC.md +339 -0
  67. package/docs/VERIFICATION.md +28 -0
  68. package/docs/assets/readme/agentdoc-hero.webp +0 -0
  69. package/docs/assets/readme/agentdoc-in-action.webp +0 -0
  70. package/docs/assets/readme/how-agentdoc-works.webp +0 -0
  71. package/docs/assets/readme/why-agentdoc.webp +0 -0
  72. package/docs/ci/generic-ci.sh +28 -0
  73. package/docs/ci/github-actions.yml +35 -0
  74. package/evals/harness.mjs +426 -0
  75. package/evals/scenarios/fixture-a-01-change-api.json +9 -0
  76. package/evals/scenarios/fixture-a-02-change-db.json +9 -0
  77. package/evals/scenarios/fixture-a-03-enforce-constraint.json +9 -0
  78. package/evals/scenarios/fixture-b-01-change-proto.json +9 -0
  79. package/evals/scenarios/fixture-b-02-change-write-path.json +9 -0
  80. package/evals/scenarios/fixture-b-03-change-shared-lib.json +9 -0
  81. package/evals/scenarios/fixture-b-04-journey.json +9 -0
  82. package/evals/scenarios/fixture-c-01-mixed-runtime-edge.json +19 -0
  83. package/evals/scenarios/fixture-c-02-stale-doc-contradiction.json +9 -0
  84. package/evals/scenarios/fixture-c-03-shared-vocabulary.json +9 -0
  85. package/evals/scenarios/fixture-c-04-contract-change.json +9 -0
  86. package/evals/scenarios/koda-01-modify-api-consumer.json +26 -0
  87. package/evals/scenarios/koda-02-change-db-schema.json +20 -0
  88. package/evals/scenarios/koda-03-change-auth.json +21 -0
  89. package/evals/scenarios/koda-04-add-event-producer.json +19 -0
  90. package/evals/scenarios/koda-05-modify-worker-binding.json +25 -0
  91. package/evals/scenarios/koda-06-change-cross-component-endpoint.json +22 -0
  92. package/evals/scenarios/koda-07-change-shared-package.json +16 -0
  93. package/evals/scenarios/koda-08-change-runtime-cron.json +23 -0
  94. package/evals/scenarios/koda-09-debug-production-mismatch.json +26 -0
  95. package/evals/thresholds.json +11 -0
  96. package/package.json +46 -4
  97. package/schemas/api.schema.json +84 -0
  98. package/schemas/common.schema.json +147 -0
  99. package/schemas/component.schema.json +44 -0
  100. package/schemas/config.schema.json +801 -0
  101. package/schemas/domain.schema.json +14 -0
  102. package/schemas/graph.schema.json +1084 -0
  103. package/schemas/journeys.schema.json +47 -0
  104. package/schemas/observation.schema.json +143 -0
  105. package/schemas/resource.schema.json +28 -0
  106. package/schemas/system.schema.json +26 -0
  107. package/skills/agent-doc-system/SKILL.md +117 -0
  108. package/skills/agent-doc-system/references/authority-and-conflicts.md +70 -0
  109. package/skills/agent-doc-system/references/cli.md +97 -0
  110. package/skills/agent-doc-system/references/create-migrate.md +113 -0
  111. package/skills/agent-doc-system/references/maintenance.md +52 -0
  112. package/templates/ADR.md +26 -0
  113. package/templates/ARCHITECTURE.md +19 -0
  114. package/templates/CONSTRAINTS.md +17 -0
  115. package/templates/PRODUCT.md +17 -0
  116. package/templates/api.yaml +10 -0
  117. package/templates/ci.yml +31 -0
  118. package/templates/component.yaml +14 -0
  119. package/templates/domain.yaml +6 -0
  120. package/templates/journey.yaml +7 -0
  121. package/templates/observation.yaml +19 -0
  122. package/templates/resource.yaml +8 -0
  123. package/templates/system.yaml +10 -0
  124. package/templates/warning-acceptance.yaml +14 -0
package/docs/QUERY.md ADDED
@@ -0,0 +1,180 @@
1
+ # QUERY
2
+
3
+ The context router. Its job is **routing**, not documentation concatenation: for
4
+ a path or an entity, return the smallest set of facts an agent needs to work
5
+ safely on that subject, and refuse to answer from a stale graph.
6
+
7
+ ## Contract
8
+
9
+ ```bash
10
+ agentdoc query <repo-path|entity-ref> [--json] [--md]
11
+ ```
12
+
13
+ Resolution order:
14
+
15
+ 1. an exact entity ref;
16
+ 2. a documentation or constraint file a descriptor links, even outside the
17
+ component's source root;
18
+ 3. a directory that is exactly a unit root;
19
+ 4. the longest unit root that is a prefix of the path;
20
+ 5. a canonical contract file, which routes to the API entity that claims it.
21
+
22
+ If nothing resolves, the command fails with `AGENTDOC_REF_UNRESOLVED` and exits
23
+ non-zero. It never falls back to "the nearest component" or to a global dump.
24
+
25
+ ## Freshness refusal
26
+
27
+ `query` re-compiles in memory, reads the committed graph, and refuses it unless
28
+ `inputHash` and `dirty` agree. (The recorded commit is provenance, not a gate —
29
+ see `CI.md`.) Time-dependent gates run first: a
30
+ stale observation is reported as `AGENTDOC_OBSERVATION_STALE` before anything
31
+ else, because "your observation is 5 years old" and "rebuild the graph" send the
32
+ reader to completely different places.
33
+
34
+ There is no silent fallback. `--allow-stale-observations` exists as an explicit
35
+ override and is visible in the command line.
36
+
37
+ ## What comes back
38
+
39
+ ```json
40
+ {
41
+ "query": { "needle": "...", "resolved": "component:default/teacher" },
42
+ "entity": { "ref", "kind", "name", "description", "spec", "sourcePaths", "derived" },
43
+ "placement": { "systems": [], "domains": [] },
44
+ "relations": { "items": [ { "type", "direction", "other", "evidenceClass", "attributes" } ], "total", "truncated" },
45
+ "neighbours": [ { "ref", "kind", "name", "description" } ],
46
+ "apis": { "provided": [], "consumed": [], "provider", "consumers", "contract" },
47
+ "events": { "items": [], "total", "truncated" },
48
+ "resources": { "used": [], "usedBy": [] },
49
+ "externalDependencies": { "items": [], "total", "truncated" },
50
+ "capabilities": { "items": [], "total", "truncated" },
51
+ "constraints": { "docs": [], "constraints": [], "runbooks": [] },
52
+ "verification": { "items": [ { "id", "tier", "command", "configPaths" } ], "total", "truncated" },
53
+ "journeys": { "items": [], "total", "truncated" },
54
+ "journeyPeers": [ "component:default/..." ],
55
+ "contracts": { "items": [], "total", "truncated" },
56
+ "authority": {
57
+ "conflicts": { "items": [ { "key", "kind", "status", "election", "assertions" } ] },
58
+ "assertions": { "items": [] },
59
+ "observations":[ { "id", "environment", "sourceSystem", "collector", "capturedAt", "facts" } ]
60
+ },
61
+ "provenance": { "items": [] },
62
+ "globalGates": [ { "id", "tier", "command" } ],
63
+ "truncated": [ "relations", "events" ]
64
+ }
65
+ ```
66
+
67
+ ## Rules that matter
68
+
69
+ **Contracts are pointers, not copies.** `contracts[].ref` is the canonical
70
+ contract; `operations` is the inventory an agent needs to find a handler without
71
+ reading the whole specification.
72
+
73
+ **Critical context is separated from the rest.** `authority.conflicts`,
74
+ `authority.observations` and `constraints.constraints` are the things that cause
75
+ the most damage when missed, and the markdown rendering puts them under explicit
76
+ headings: `CONFLICTS (do not assume one is true)`, `RUNTIME OBSERVATIONS`,
77
+ `CONSTRAINTS`.
78
+
79
+ **Both sides of a conflict are printed.** A conflicted assertion shows its
80
+ evidence class, its confidence and its value. The contradicted value is never
81
+ withheld.
82
+
83
+ **A contract subject carries the provider's and consumers' checks.** Changing an
84
+ endpoint is proved by running their suites, so `verification` for an API subject
85
+ is the union over the provider and its consumers.
86
+
87
+ **Journey peers are named.** If a component appears in a journey, the other
88
+ components in that journey are in scope and are listed explicitly rather than
89
+ left for the agent to rediscover.
90
+
91
+ **Budgets are reported, never silent.** Every capped section returns
92
+ `{ items, total, truncated }`, and the top-level `truncated` lists every section
93
+ that was cut — including the nested `authority.conflicts` and
94
+ `authority.assertions`, reported as `authority.conflicts` and so on. A section is
95
+ never quietly shortened.
96
+
97
+ **Review commitments are surfaced.** `reviewCommitments` collects every conflict
98
+ election, accepted warning and reviewed override with the condition under which
99
+ it must be re-examined. A review condition nobody can see is a review condition
100
+ nobody performs. Where no condition was recorded — a conflict settled with no
101
+ governing authority rule — the entry says so explicitly rather than pointing at a
102
+ record that does not exist.
103
+
104
+ **Budgets are adjustable.** The defaults are deliberately generous, because a
105
+ one-hop route returns a handful of relations and a tight cap would truncate
106
+ everything. When an agent is working in a constrained context it can say so:
107
+
108
+ ```bash
109
+ agentdoc query component:default/api --budget relations=10,verification=4
110
+ ```
111
+
112
+ Limits are positive integers. Every capped section then reports
113
+ `{ items, total, truncated }` and the top-level `truncated` names each one, so
114
+ the slice is never mistaken for the whole.
115
+
116
+ ## Default budgets
117
+
118
+ | section | cap |
119
+ |---|---|
120
+ | relations | 40 |
121
+ | events | 20 |
122
+ | externalDependencies | 15 |
123
+ | capabilities | 10 |
124
+ | verification | 12 |
125
+ | journeys | 10 |
126
+ | conflicts | 20 |
127
+ | assertions | 25 |
128
+ | provenance | 20 |
129
+ | contracts | 10 |
130
+
131
+ ## Markdown rendering
132
+
133
+ `--md` (the default) produces a compact block suitable for a prompt or a human:
134
+
135
+ ```
136
+ # component:default/koda-cron (Component)
137
+ Triggers governed endpoints on a platform schedule and owns no business logic.
138
+ - source: workers/koda-cron
139
+ - system: system:default/scheduler
140
+ - CONSTRAINTS: workers/koda-cron/CONSTRAINTS.md
141
+ - verify:
142
+ - [unit] vitest run
143
+ - CONFLICTS (do not assume one is true):
144
+ - schedule.cron [contradiction/resolved]
145
+ - DERIVED/deterministic: "0 3 * * *"
146
+ - OBSERVED_RUNTIME/direct: "0 * * * *,3 0 * * *"
147
+ - RUNTIME OBSERVATIONS:
148
+ - production via platform-scheduler at 2026-09-27T18:40:00Z (evidence …)
149
+ ```
150
+
151
+ ## Change impact
152
+
153
+ ```bash
154
+ agentdoc impact <paths...>
155
+ agentdoc impact --diff <git-ref>
156
+ ```
157
+
158
+ Impact answers a question about the working tree, so it compiles in memory and
159
+ does not require a fresh committed graph. Using it before rebuilding is the
160
+ point.
161
+
162
+ It reports: affected components with the reason each was reached, affected
163
+ contracts, documentation, resources, verification and catalog sources; whether
164
+ the graph will be invalidated; the recommended verification commands; any open
165
+ conflicts on the affected subjects; and related journeys.
166
+
167
+ Reachability rules: a contract change reaches its provider and every consumer; a
168
+ component change reaches its transitive dependents two hops out, and everything
169
+ connected to it by a runtime call or a schedule. Two hops is deliberate — a
170
+ third hop is where the graph stops being a routing aid and becomes a dump.
171
+
172
+ ## Using it in a prompt
173
+
174
+ Ask for `--md` and give the output to the agent as-is. Two rules keep it
175
+ honest:
176
+
177
+ 1. Do not summarise a conflict away. If the context contains a `CONFLICTS`
178
+ block, the agent must be told about it.
179
+ 2. If the context contains `UNRESOLVED` or a conflict with `basis: none`, the
180
+ correct agent behaviour is to say so and stop, not to choose a side.
package/docs/SPEC.md ADDED
@@ -0,0 +1,339 @@
1
+ # SPEC
2
+
3
+ Normative specification of the agent documentation system. Where this document
4
+ and the code disagree, the code is the defect.
5
+
6
+ ## 1. Purpose
7
+
8
+ Given a repository, the system produces one deterministic graph that answers,
9
+ for any source path or entity: what this is, what it depends on, what depends
10
+ on it, what it must not violate, and which command proves a change correct.
11
+
12
+ ## 2. Namespace and versioning
13
+
14
+ | artefact | identity |
15
+ |---|---|
16
+ | entity descriptor | `apiVersion: agentdoc.dev/v1` |
17
+ | configuration | `apiVersion: agentdoc.dev/config/v1` |
18
+ | observation set | `apiVersion: agentdoc.dev/observation/v1` |
19
+ | journeys | `schemaVersion: agentdoc.dev/journeys/v1` |
20
+ | compiled graph | `schemaVersion: agentdoc.dev/graph/v1` |
21
+ | JSON Schema ids | `agentdoc.dev/schema/<name>.json` |
22
+ | compiler | `agentdoc-compiler@<semver>` |
23
+
24
+ **Evolution rules.**
25
+
26
+ - The major version in `apiVersion` changes only on a breaking change to the
27
+ descriptor shape. The compiler accepts exactly one major version and refuses
28
+ anything else with `AGENTDOC_API_VERSION`; a future format is never half-read.
29
+ - Every schema is `additionalProperties: false`. An unknown field is
30
+ `AGENTDOC_UNKNOWN_FIELD`, not a silently ignored key.
31
+ - Forward evolution happens through the per-entity `extensions` object, which is
32
+ typed as free-form and ignored by every core rule — including the prohibited
33
+ field check, so the escape hatch is genuinely usable for exactly the
34
+ slow-drifting metadata that has no home in an entity.
35
+ - The compiled graph records `schemaVersion`, `compiler.name`,
36
+ `compiler.version` and `compiler.adapters`. Freshness refuses a graph built by
37
+ a different compiler or schema.
38
+ - Schema files ship with the compiler, not with the target repository. A
39
+ project cannot loosen validation by editing a schema. Their content hashes are
40
+ folded into the graph input hash, so upgrading a schema invalidates every
41
+ graph compiled against the old one.
42
+
43
+ ## 3. Source dialect
44
+
45
+ Authored sources are a strict YAML 1.2 subset, parsed by `core/yaml.mjs`:
46
+
47
+ - supported: multi-document streams, block mappings, block sequences, plain and
48
+ quoted scalars, flow sequences and mappings of scalars, literal and folded
49
+ block scalars, comments, and YAML 1.2 core scalar resolution.
50
+ - rejected with a dedicated code: anchors, aliases, custom tags, merge keys,
51
+ environment interpolation, duplicate mapping keys, tab indentation,
52
+ non-UTF-8 input, complex keys, and any nesting the parser would have to guess
53
+ at.
54
+
55
+ The rationale is that authored descriptors are templates plus human review, and
56
+ ambiguity in them is a defect rather than a feature.
57
+
58
+ ## 4. Entity model
59
+
60
+ Five entity kinds, and five only. Journeys, events, external dependencies,
61
+ capabilities, verification entries, gates, contracts, assertions and
62
+ observations are **not** entities and may never appear as a relation endpoint.
63
+
64
+ ```
65
+ Domain a bounded area of the product with its own vocabulary
66
+ System a responsibility inside a domain, grouping components
67
+ Component a deployable or importable software unit
68
+ API a durable cross-component boundary with one canonical contract
69
+ Resource a logical thing shared or owned across components
70
+ ```
71
+
72
+ An entity carries: a stable ref, `metadata` (name, one-sentence description,
73
+ optional labels), an optional `spec`, and an `extensions` bag. Every entity is
74
+ authored in exactly one file, and the location is validated.
75
+
76
+ **Ref format:** `<kind>:<namespace>/<name>`, e.g. `component:default/teacher`.
77
+ The namespace comes from the configuration, so two repositories in one graph
78
+ never collide.
79
+
80
+ ### Why no further entities
81
+
82
+ A `Team`, `Milestone` or `Tag` entity was considered and rejected: none of them
83
+ can be derived, none of them changes a fact in the graph, and all three would
84
+ need a lifecycle and an owner — the two things this system deliberately refuses
85
+ to model, because they are the fields that drift fastest and carry the least
86
+ architectural meaning. Ownership is expressed by placement; lifecycle belongs
87
+ in version control.
88
+
89
+ ## 5. Facts, evidence and confidence
90
+
91
+ A **fact** is a claim about one `(subject, key)` pair. `key` is a dotted
92
+ name, optionally namespaced, e.g. `schedule.cron`, `binding.declared`,
93
+ `contract.supports`, `component.deployable`, `placement.system`,
94
+ `resource.ownership`. `core/authority/facts.mjs` documents the semantics of the
95
+ well-known keys.
96
+
97
+ Every fact carries an **evidence class** and a **confidence**:
98
+
99
+ | evidence class | meaning |
100
+ |---|---|
101
+ | `AUTHORED` | explicitly declared and intentionally maintained in this repository |
102
+ | `DERIVED` | deterministically extracted from committed inputs |
103
+ | `OBSERVED_RUNTIME` | read from an authoritative external or running system |
104
+ | `EXTERNAL_STANDARD` | the canonical authority is a published standard |
105
+ | `REVIEWED_OVERRIDE` | a reviewer approved an interpretation |
106
+ | `UNRESOLVED` | known ambiguity that must not become truth |
107
+
108
+ | confidence | meaning |
109
+ |---|---|
110
+ | `direct` | read straight from the named authority system |
111
+ | `deterministic` | a reproducible function of committed inputs |
112
+ | `declared` | asserted by a human-maintained document |
113
+ | `reviewed` | a reviewer signed off on an interpretation |
114
+ | `candidate` | heuristic; **never** allowed to become an elected fact |
115
+
116
+ See `AUTHORITY.md` for the full semantics and the conflict model.
117
+
118
+ ## 6. Relations
119
+
120
+ No relation is ever authored. Placement edges come from authored placement
121
+ fields, provider edges from the API descriptor, and everything else from
122
+ deterministic extractors or bounded reviewed overrides.
123
+
124
+ ```
125
+ partOf / hasPart Component->System|Domain, System->Domain, Resource->System
126
+ providesApi / apiProvidedBy Component<->API
127
+ consumesApi / apiConsumedBy Component<->API
128
+ buildDependsOn / buildDependencyOf
129
+ testDependsOn / testDependencyOf
130
+ runtimeCalls / runtimeCalledBy
131
+ usesResource / resourceUsedBy
132
+ schedules / scheduledBy
133
+ ```
134
+
135
+ Every relation is emitted with its inverse, its kind pair is validated, its
136
+ attribute keys are checked against a closed per-type whitelist, and duplicates
137
+ merge their provenance instead of forking the graph. Every relation carries at
138
+ least one provenance id and an evidence class.
139
+
140
+ ## 7. The compiled graph
141
+
142
+ ```
143
+ schemaVersion, source{commit, dirty, inputHash}, compiler{name, version, adapters}
144
+ entities[] ref, kind, name, entity, source, sourcePaths[], derived?
145
+ relations[] type, sourceRef, targetRef, evidenceClass, attributes, provenanceIds[]
146
+ interfaces.events[] pattern, producers, consumers, contract, resolution
147
+ externalDependencies[] componentRef, name, role, mechanism, evidenceClass
148
+ capabilities[] componentRef, name, evidenceClass
149
+ verification[] id, componentRefs, tier, command, configPaths
150
+ gates[] id, tier, command (repository-scoped checks)
151
+ contracts[] apiRef, ref, operations (pointer + inventory, never a copy)
152
+ journeys[] id, description, components
153
+ assertions[] id, subject, key, value, evidenceClass, confidence, status
154
+ conflicts[] id, subject, key, kind, status, assertionIds, election
155
+ observations[] id, environment, sourceSystem, collector, capturedAt, facts[]
156
+ provenance[] id, class, path, extractor, contentHash, subjects[]
157
+ diagnostics[] severity, code, message, acceptance?
158
+ ```
159
+
160
+ ### 7.1 Determinism
161
+
162
+ The graph is a pure function of the checkout.
163
+
164
+ - Objects are constructed in schema order; every array is sorted by a total
165
+ order over its identifying key.
166
+ - No wall-clock value, host path, username, process id or random id is
167
+ introduced at compile time.
168
+ - **Observation timestamps are the one permitted time value**, and only because
169
+ they are read from a committed `ObservationSet`. Before emission, the
170
+ serialized graph is scanned and every timestamp present must be one the
171
+ compiler actually read; anything else fails with `AGENTDOC_NONDETERMINISTIC`.
172
+ - The serialized output is scanned for absolute paths, host directories and
173
+ process identity. The scan covers the graph's *structure* — refs, ids, paths,
174
+ hashes, keys, provenance — and not opaque free-text fields, which are copied
175
+ verbatim from committed files and are therefore a pure function of the
176
+ checkout by construction. A repository is free to mention `/tmp` in a CI
177
+ command; a compiler-introduced host path is not.
178
+ - Provenance ids are positional over a canonical sort, so they are stable.
179
+
180
+ `compile` is idempotent: compiling the same checkout twice produces identical
181
+ bytes, and `check` proves the committed bytes equal a fresh compile.
182
+
183
+ ### 7.2 Freshness
184
+
185
+ `source.inputHash` is a length-prefixed hash over:
186
+
187
+ 1. the compiler name and version, the graph schema version, the entity api
188
+ version, the authority-rule count, every enabled adapter's name and version,
189
+ and the content hash of every shipped schema;
190
+ 2. every file the compiler read, as `path` + content;
191
+ 3. every existence probe, as path + result;
192
+ 4. every directory listing, as directory + sorted file list.
193
+
194
+ Every byte flows through the read tracker, so the record is complete by
195
+ construction. A consumer must refuse a graph whose `inputHash` or `dirty` flag
196
+ disagrees with the current checkout. There is no fallback.
197
+
198
+ The recorded `commit` is **provenance, not a gate**: committing the graph changes
199
+ `HEAD`, so a commit-equality check could never be satisfied by a committed graph.
200
+ What determines whether the graph still describes the repository is the input
201
+ hash and the dirty flag. A checkout with no VCS still compiles: the commit
202
+ becomes `0000000` and freshness rests on the hash alone.
203
+
204
+ `dirty` is true when a path that influenced the output differs from `HEAD`,
205
+ excluding the generated graph itself.
206
+
207
+ ### 7.3 Time-dependent gates
208
+
209
+ Observation staleness is time-dependent and is therefore evaluated at gate time
210
+ (`check`, `query`), never at compile time. A compile stays a pure function of the
211
+ checkout; a gate may legitimately fail because time passed.
212
+
213
+ ## 8. Contracts
214
+
215
+ Every durable cross-component boundary has exactly one canonical
216
+ machine-readable contract, and the graph **points at** it rather than copying
217
+ it. Supported dialects, all validated on load:
218
+
219
+ | type | validation |
220
+ |---|---|
221
+ | `openapi` | `openapi` 3.0/3.1, `info.title`, `paths`, `components` |
222
+ | `asyncapi` | `asyncapi` version, `channels` |
223
+ | `protobuf` | `syntax` or `edition`, at least one `service` |
224
+ | `graphql` / `graphql-sdl` | at least one type definition |
225
+ | `oidc` | a named standard plus an absolute discovery path; the provider must expose that path in its own source |
226
+
227
+ Two rules enforce the "exactly one" property: a contract file under a
228
+ configured `contractRoots` that no API claims is `AGENTDOC_CONTRACT_UNCLAIMED`,
229
+ and two APIs claiming the same file is `AGENTDOC_CONTRACT_SHARED`.
230
+
231
+ Consumers are discovered deterministically: a unit, other than the provider,
232
+ that references the contract file from a source or build-configuration file.
233
+
234
+ ## 9. Discovery
235
+
236
+ The core has no knowledge of folder names. A directory is a unit when it holds
237
+ a component descriptor, when an adapter recognises it from a workspace file, or
238
+ when it is listed in `discovery.supplementalRoots`.
239
+
240
+ - Every discovered unit must have exactly one descriptor at its root, or
241
+ `AGENTDOC_COVERAGE_ZERO`.
242
+ - Two descriptors at one root, or a descriptor no adapter can corroborate, is an
243
+ error. A descriptor is a human claim; an uncorroborated claim is a defect.
244
+ - The authored `type` is cross-checked against the artifact evidence:
245
+ a `library` with no importable artifact, or a `service` with no deployable
246
+ artifact, is `AGENTDOC_ARTIFACT_TYPE`.
247
+
248
+ ## 10. Provenance
249
+
250
+ See `PROVENANCE.md`. In summary: every important fact carries at least one
251
+ provenance record with a path, a content hash, an extractor identity and RFC
252
+ 6901 subject pointers into the graph. Classes are `declared`, `observed`,
253
+ `validated`, `runtime` and `standard`.
254
+
255
+ ## 11. Diagnostics and warning acceptance
256
+
257
+ Error-severity diagnostics block publication. Warnings may be **accepted** with
258
+ a reviewed disposition carrying `code`, `subject`, `classification`, `reason`,
259
+ `reviewWhen`, `observedRefs` and evidence content hashes.
260
+
261
+ An acceptance must match exactly one current warning, its `observedRefs` must
262
+ equal the diagnostic's current refs, and every evidence digest must still match.
263
+ If the evidence changes, the acceptance stops matching and compilation fails:
264
+ a review is forced, not inherited. Acceptance can never downgrade an error, and
265
+ only the codes in `ACCEPTABLE_WARNING_CODES` may be accepted.
266
+
267
+ ## 12. Commands
268
+
269
+ | command | contract |
270
+ |---|---|
271
+ | `init [--force]` | write a configuration and documentation skeleton; never clobber unless `--force` |
272
+ | `scaffold [--write]` | generate descriptors from provable evidence; dry run by default |
273
+ | `audit` | classify every fact; runs even when the repository cannot compile |
274
+ | `validate` | sources, schemas, semantic checks; no artifact written |
275
+ | `compile [--require-clean]` | deterministic build, atomic replace |
276
+ | `check [--require-clean] [--allow-stale-observations]` | freshness, determinism, time gates |
277
+ | `query <path\|ref> [--json] [--md]` | route context; refuses a stale graph |
278
+ | `impact <paths> \| --diff <ref>` | change impact; compiles in memory |
279
+ | `eval routing [--json]` | routing evaluation; `SKIP` when the repository has no scenarios |
280
+ | `pin-evidence [--json]` | check the digests pinned by reviewed overrides |
281
+ | `doctor` | environment self-check: node version, schema bundle, git, writability |
282
+
283
+ Exit codes: `0` ok, `1` failure, `2` usage.
284
+
285
+ ## 13. Error codes
286
+
287
+ Every failure carries a stable `AGENTDOC_*` code, a path or ref when relevant,
288
+ and an actionable message. Codes are listed in `core/codes.mjs` and are part of
289
+ the contract: tests and CI match on identity, never on message text.
290
+
291
+ ## 14. Source schema dialect
292
+
293
+ Schemas use the JSON Schema 2020-12 subset the shipped validator implements:
294
+ `$ref` (bundle ids and `#/$defs/...`), `$defs`, `type`, `const`, `enum`,
295
+ `required`, `properties`, `additionalProperties`, `patternProperties`, `items`,
296
+ `prefixItems`, `minItems`, `maxItems`, `uniqueItems`, `minLength`, `maxLength`,
297
+ `pattern`, `minimum`, `maximum`, `allOf`, `anyOf`, `oneOf`, `not`, `if`/`then`/
298
+ `else`, `propertyNames`, `minProperties`.
299
+
300
+ Two things follow, and both are enforced rather than asserted:
301
+
302
+ - **No unsupported keyword can be used.** A test walks every shipped schema at
303
+ every position where a schema may appear, against the validator's own
304
+ exported keyword set, and fails on anything else. `format` is deliberately
305
+ *not* in that set, so a schema author who reaches for `format: date-time`
306
+ gets a failure rather than a validator that quietly does nothing.
307
+ - **`entities[].entity` and `entities[].derived` are opaque payloads** in the
308
+ graph schema. The authored half is validated against its per-kind entity
309
+ schema at input time, which is where a malformed descriptor must be caught;
310
+ re-validating the same document against the graph schema would be a second
311
+ source of truth for the same constraint. Every other graph surface is closed
312
+ (`additionalProperties: false`).
313
+
314
+ ## 15. Dependencies
315
+
316
+ None. The YAML subset parser, the JSON Schema validator, the TOML reader, the
317
+ glob matcher, the hashing and the deterministic serializer are all in-tree.
318
+ Node 22 or newer. No network, no service, no hosted anything.
319
+
320
+ ## Reviewed overrides are re-verified, and so are their review conditions
321
+
322
+ A `REVIEWED_OVERRIDE` is the highest-authority input the model accepts, and the
323
+ only thing that can falsify it is the evidence it cites. So each override:
324
+
325
+ - must state `reviewWhen` — the condition under which it is re-examined. A
326
+ review with no re-examination condition is a review that is never revisited,
327
+ and a defaulted string would be the same sentence on every override: a review
328
+ condition that conditions nothing, reported as though it were real;
329
+ - pins its evidence by content hash (`{path, sha256}`), and those digests are
330
+ re-verified on every compile. If a cited file changes, compilation fails with
331
+ `AGENTDOC_OVERRIDE_STALE` and the interpretation must be reviewed before the
332
+ digest is updated. A digest that is recorded but never compared is decoration;
333
+ `agentdoc pin-evidence` reports which digests are current so the repair is
334
+ possible without computing SHA-256 by hand.
335
+
336
+ The same discipline applies to accepted warnings, for the same reason. An
337
+ authority rule's own `reviewWhen` travels onto the conflict it settles, so
338
+ `query`'s `reviewCommitments` surface reports a real condition or says plainly
339
+ that none was recorded — never a pointer to a record that does not exist.
@@ -0,0 +1,28 @@
1
+ # Verification record for this repository.
2
+ # Every claim below was executed; the commands are reproducible from the repo root.
3
+
4
+ node --test tests/*.test.mjs # unit, adversarial, mode and packaging tests
5
+ node evals/run-all.mjs # aggregate routing gate — verdict: PASS
6
+
7
+ Corpora (each compiles, checks, and passes its own routing scenarios):
8
+ fixtures/a-node-ts-postgres
9
+ fixtures/b-go-multi-service
10
+ fixtures/c-alt-monorepo
11
+ examples/koda/repo (golden corpus)
12
+
13
+ Distribution (covered by tests/packaging.test.mjs):
14
+ npm pack produces a tarball whose installed `agentdoc` binary runs
15
+ --version, --help, doctor, init, doctor --project, audit and
16
+ eval routing with no access to the source checkout.
17
+
18
+ Also verified:
19
+ - output is byte-identical across three consecutive compiles
20
+ - a checkout with no .git compiles and checks
21
+ - running the test suite does not modify the package
22
+ - a graph that violates its own schema is rejected
23
+ - a reviewed override stops applying when its cited evidence is rewritten
24
+ - `agentdoc init` creates project data only — never a copy of the CLI
25
+ - a catalog nested inside a larger checkout is not dirtied by outer-repo churn
26
+
27
+ Comparison regeneration (requires the reference graph, kept out of the repo):
28
+ python3 scripts/compare-koda.py <reference-graph> <portable-graph>
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env sh
2
+ # The drift gate, runner-agnostic. Any CI system that can run a shell can run
3
+ # this. The `agentdoc` CLI must be installed on the runner beforehand; exit
4
+ # status is the contract.
5
+ set -eu
6
+
7
+ if ! command -v agentdoc >/dev/null 2>&1; then
8
+ echo "agentdoc: CLI not on PATH — install it first:" >&2
9
+ echo " npm install -g github:lbframe/agent-doc-system" >&2
10
+ exit 1
11
+ fi
12
+
13
+ echo "==> validate"
14
+ agentdoc validate
15
+
16
+ echo "==> compile"
17
+ agentdoc compile
18
+
19
+ echo "==> check (freshness, determinism, time gates)"
20
+ agentdoc check
21
+
22
+ echo "==> check (clean source required)"
23
+ agentdoc check --require-clean
24
+
25
+ echo "==> routing evaluation (skips cleanly when no scenarios are registered)"
26
+ agentdoc eval routing
27
+
28
+ echo "agentdoc: all gates passed"
@@ -0,0 +1,35 @@
1
+ # Drift gate. The graph is a build artifact: compile it, then prove the
2
+ # committed one is byte-identical to a fresh compile. A stale or hand-edited
3
+ # graph fails here, not in someone's review three weeks later.
4
+ #
5
+ # This is the workflow `agentdoc init` installs as
6
+ # .github/workflows/agentdoc.yml (see templates/ci.yml). It assumes nothing
7
+ # about the repository beyond a supported Node toolchain.
8
+ name: agentdoc
9
+ on:
10
+ pull_request:
11
+ push:
12
+ branches: [main]
13
+ jobs:
14
+ agentdoc:
15
+ runs-on: ubuntu-latest
16
+ timeout-minutes: 5
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ with: { fetch-depth: 0 }
20
+ - uses: actions/setup-node@v4
21
+ with: { node-version: '24' }
22
+ - name: Install the agentdoc CLI
23
+ # Unpinned tracks main. For CI reproducibility pin a tag or commit:
24
+ # github:lbframe/agent-doc-system#v1.1.0
25
+ # A compiler upgrade intentionally invalidates committed graphs.
26
+ run: npm install -g github:lbframe/agent-doc-system
27
+ - name: Validate and compile
28
+ run: |
29
+ agentdoc validate
30
+ agentdoc compile
31
+ agentdoc check
32
+ - name: Reject dirty sources
33
+ run: agentdoc check --require-clean
34
+ - name: Routing evaluation
35
+ run: agentdoc eval routing