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
@@ -0,0 +1,266 @@
1
+ // Authority rules and conflict resolution.
2
+ //
3
+ // There is deliberately NO global precedence order. Authority depends on the
4
+ // fact: an OpenAPI file may be authoritative for the desired wire contract
5
+ // while a runtime observation is authoritative for what is deployed right now,
6
+ // and source code is authoritative for the behaviour shipped by a commit.
7
+ //
8
+ // A fact with an authority rule gets an election. A fact with NO matching rule
9
+ // and disagreeing evidence stays UNRESOLVED and raises a conflict. That is the
10
+ // fail-closed default: silence is not a resolution.
11
+ import { AgentDocError, CODES } from "../codes.mjs";
12
+ import { EVIDENCE_CLASSES, CONFIDENCE } from "./classes.mjs";
13
+ import { isNormative } from "../normative.mjs";
14
+
15
+ export class AuthorityEngine {
16
+ constructor(rules) {
17
+ this.rules = (rules || []).map((r) => {
18
+ let re;
19
+ try {
20
+ re = new RegExp(r.keyPattern, "u");
21
+ } catch {
22
+ throw new AgentDocError(CODES.CONFIG, "authority rule " + r.id + " has an invalid keyPattern: " + r.keyPattern);
23
+ }
24
+ for (const c of r.elect) {
25
+ if (!EVIDENCE_CLASSES.includes(c) || c === "UNRESOLVED") {
26
+ throw new AgentDocError(CODES.CONFIG, "authority rule " + r.id + " cannot elect " + c);
27
+ }
28
+ }
29
+ return { ...r, re };
30
+ });
31
+ for (let i = 0; i < this.rules.length; i++) {
32
+ for (let j = i + 1; j < this.rules.length; j++) {
33
+ if (this.rules[i].id === this.rules[j].id) {
34
+ throw new AgentDocError(CODES.CONFIG, "duplicate authority rule id " + this.rules[i].id);
35
+ }
36
+ }
37
+ }
38
+ }
39
+ // Returns the most specific matching rule (longest pattern wins) or null.
40
+ ruleFor(subject, key) {
41
+ const target = subject + " " + key;
42
+ let best = null;
43
+ for (const r of this.rules) {
44
+ if (!r.re.test(target)) continue;
45
+ if (!best || r.keyPattern.length > best.keyPattern.length) best = r;
46
+ }
47
+ return best;
48
+ }
49
+ }
50
+
51
+ // Group assertions by (subject, key) and elect at most one per group.
52
+ //
53
+ // Election basis, in the order it is attempted — and each basis is only
54
+ // reachable when a rule explicitly allows it:
55
+ // reviewed-override an explicit human interpretation exists
56
+ // authority-rule the rule's elect list contains exactly one class present
57
+ // superset the elected value strictly contains the others
58
+ // recency the newest runtime observation, and only if the rule says so
59
+ // none fail closed
60
+ export function resolveAssertions(assertions, authority) {
61
+ const groups = new Map();
62
+ for (const a of assertions) {
63
+ // An assertion a later observation supersedes stays in the graph as
64
+ // history but does not compete here: it cannot win an election and cannot
65
+ // anchor a conflict.
66
+ if (a.status === "superseded") continue;
67
+ const gk = a.subject + "\u0000" + a.key;
68
+ if (!groups.has(gk)) groups.set(gk, []);
69
+ groups.get(gk).push(a);
70
+ }
71
+
72
+ const conflicts = [];
73
+ const elections = [];
74
+
75
+ for (const [gk, list] of [...groups.entries()].sort((a, b) => (a[0] < b[0] ? -1 : 1))) {
76
+ const [subject, key] = gk.split("\u0000");
77
+ const distinct = distinctValues(list);
78
+
79
+ // A normative requirement is a claim about what the implementation must
80
+ // do, so it can only be corroborated, never elected on its own word. With
81
+ // no implementation evidence beside it, it stays unresolved: the gap the
82
+ // migration must surface.
83
+ const normative = list.some(isNormative);
84
+ if (normative && list.every((a) => isNormative(a) || a.confidence === "candidate")) {
85
+ // A heuristic candidate is not evidence either way: it stays a candidate,
86
+ // and the requirement stays unverified.
87
+ for (const a of list) a.status = isNormative(a) ? "unresolved" : "candidate";
88
+ continue;
89
+ }
90
+
91
+ if (distinct.length === 1) {
92
+ // Agreement across evidence classes is corroboration, not a conflict —
93
+ // but a lone heuristic candidate is not agreement with anything. It stays
94
+ // UNRESOLVED and never becomes an elected fact.
95
+ const allCandidate = list.every((a) => a.confidence === "candidate");
96
+ for (const a of list) a.status = allCandidate ? "candidate" : "elected";
97
+ const rule = authority.ruleFor(subject, key);
98
+ elections.push({ subject, key, ruleId: rule ? rule.id : "agreement", basis: "authority-rule", elected: list[0] });
99
+ if (allCandidate) {
100
+ conflicts.push({
101
+ subject,
102
+ key,
103
+ kind: "ambiguity",
104
+ status: "unresolved",
105
+ assertionIds: list.map((a) => a.id).sort(),
106
+ election: {
107
+ elected: null,
108
+ basis: "none",
109
+ ruleId: rule ? rule.id : "no-rule",
110
+ rationale: "only heuristic candidates exist for '" + key + "'; deterministic discovery cannot resolve it",
111
+ reviewWhen: rule ? rule.reviewWhen : null,
112
+ contradicted: [],
113
+ },
114
+ });
115
+ }
116
+ continue;
117
+ }
118
+
119
+ const rule = authority.ruleFor(subject, key);
120
+ const candidates = list.filter((a) => a.confidence !== "candidate");
121
+ const candidateOnly = list.length !== candidates.length;
122
+
123
+ // `reviewWhen` travels with the election so a consumer can report when this
124
+ // decision must be re-examined. A null here means "no rule governs it", which
125
+ // is a real state — and one that must never be rendered as if a condition
126
+ // existed.
127
+ const election = {
128
+ elected: null,
129
+ basis: "none",
130
+ ruleId: rule ? rule.id : "no-rule",
131
+ rationale: null,
132
+ reviewWhen: rule ? rule.reviewWhen : null,
133
+ contradicted: [],
134
+ };
135
+ let status = "unresolved";
136
+
137
+ // 0. A normative requirement and the implementation disagree. No rule may
138
+ // settle that: electing the implementation waives the specification,
139
+ // electing the requirement hides the missing implementation. Only a
140
+ // change to one of them resolves it.
141
+ if (normative) {
142
+ election.rationale =
143
+ "a normative requirement disagrees with the implementation evidence; no authority rule may settle it — " +
144
+ "change the implementation, or change the specification and re-review its section";
145
+ }
146
+
147
+ // 1. reviewed override wins outright, but the divergence is still recorded.
148
+ const reviewed = candidates.filter((a) => a.evidenceClass === "REVIEWED_OVERRIDE");
149
+ if (!normative && rule && rule.elect.length === 1 && rule.elect[0] === "REVIEWED_OVERRIDE" && reviewed.length === 1) {
150
+ election.elected = reviewed[0];
151
+ election.basis = "reviewed-override";
152
+ election.rationale = rule.rationale;
153
+ status = "accepted";
154
+ }
155
+
156
+ // 2. authority rule with a single elected class present among candidates.
157
+ if (!normative && !election.elected && rule) {
158
+ const allowed = rule.elect.filter((c) => candidates.some((a) => a.evidenceClass === c));
159
+ if (allowed.length === 1) {
160
+ const winners = candidates.filter((a) => a.evidenceClass === allowed[0]);
161
+ if (winners.length === 1) {
162
+ election.elected = winners[0];
163
+ election.basis = "authority-rule";
164
+ election.rationale = rule.rationale;
165
+ status = "resolved";
166
+ } else if (winners.length > 1) {
167
+ election.rationale = "multiple same-class assertions disagree; no election";
168
+ }
169
+ } else if (allowed.length > 1) {
170
+ election.rationale = "authority rule " + rule.id + " elects several classes that are all present; no deterministic election";
171
+ }
172
+ }
173
+
174
+ // 3. superset: one candidate's value contains every other candidate's value.
175
+ if (!normative && !election.elected && candidates.length > 1) {
176
+ const sup = candidates.find((c) => candidates.every((o) => o === c || containsValue(c.value, o.value)));
177
+ if (sup) {
178
+ election.elected = sup;
179
+ election.basis = "superset";
180
+ election.rationale = "one observed value strictly contains the others";
181
+ status = "resolved";
182
+ }
183
+ }
184
+
185
+ // 4. recency: only when the rule names recency and the winner is a runtime fact.
186
+ if (!normative && !election.elected && rule && rule.elect.includes("OBSERVED_RUNTIME")) {
187
+ const runtime = candidates.filter((a) => a.evidenceClass === "OBSERVED_RUNTIME");
188
+ const dated = runtime.filter((a) => a.observed && a.observed.at);
189
+ if (dated.length > 1) {
190
+ const newest = dated.sort((a, b) => (a.observed.at < b.observed.at ? -1 : 1)).pop();
191
+ election.elected = newest;
192
+ election.basis = "recency";
193
+ election.rationale = "newest runtime observation, per authority rule " + rule.id;
194
+ status = "resolved";
195
+ }
196
+ }
197
+
198
+ if (!election.elected) {
199
+ election.rationale = election.rationale || (
200
+ rule
201
+ ? "authority rule " + rule.id + " does not resolve this disagreement"
202
+ : "no authority rule governs '" + key + "' for " + subject + "; fail closed"
203
+ );
204
+ }
205
+
206
+ for (const a of list) {
207
+ if (a === election.elected) a.status = "elected";
208
+ else if (a.confidence === "candidate") a.status = "candidate";
209
+ else a.status = "contradicted";
210
+ }
211
+ election.contradicted = list.filter((a) => a !== election.elected && a.confidence !== "candidate").map((a) => a.id);
212
+
213
+ const kind = classifyConflict(list, election, candidateOnly);
214
+ conflicts.push({
215
+ subject,
216
+ key,
217
+ kind,
218
+ status,
219
+ assertionIds: list.map((a) => a.id).sort(),
220
+ election,
221
+ });
222
+ elections.push({ subject, key, ruleId: election.ruleId, basis: election.basis, elected: election.elected });
223
+ }
224
+
225
+ return { conflicts, elections };
226
+ }
227
+
228
+ function classifyConflict(list, election, candidateOnly) {
229
+ const classes = new Set(list.map((a) => a.evidenceClass));
230
+ if (election.basis === "reviewed-override") return "reviewed-divergence";
231
+ // An observation that declares itself superseded no longer competes: a newer
232
+ // capture of the same authority system replaces it outright.
233
+ if (list.some((a) => a.observed && a.observed.supersedes)) return "supersession";
234
+ if (classes.has("OBSERVED_RUNTIME") && classes.has("AUTHORED")) return "contradiction";
235
+ if (classes.has("OBSERVED_RUNTIME") && classes.size === 1) return "staleness";
236
+ if (candidateOnly) return "ambiguity";
237
+ if (classes.size > 1) return "contradiction";
238
+ return "ambiguity";
239
+ }
240
+
241
+ export function distinctValues(list) {
242
+ const seen = new Map();
243
+ for (const a of list) {
244
+ const k = stable(a.value);
245
+ if (!seen.has(k)) seen.set(k, a.value);
246
+ }
247
+ return [...seen.values()];
248
+ }
249
+
250
+ function containsValue(big, small) {
251
+ if (Array.isArray(big) && Array.isArray(small)) {
252
+ return small.every((s) => big.some((b) => stable(b) === stable(s))) && big.length > small.length;
253
+ }
254
+ return false;
255
+ }
256
+
257
+ export function stable(v) {
258
+ if (v === null || typeof v !== "object") return JSON.stringify(v);
259
+ if (Array.isArray(v)) return "[" + v.map(stable).join(",") + "]";
260
+ return "{" + Object.keys(v).sort().map((k) => JSON.stringify(k) + ":" + stable(v[k])).join(",") + "}";
261
+ }
262
+
263
+ export function assertConfidence(c) {
264
+ if (!CONFIDENCE.includes(c)) throw new Error("unknown confidence " + c);
265
+ return c;
266
+ }
@@ -0,0 +1,41 @@
1
+ // Fact model.
2
+ //
3
+ // A "fact" is a claim about one (subject, key) pair. Multiple facts may exist
4
+ // for the same pair when different evidence classes disagree — that is a
5
+ // conflict, and it is surfaced, never reconciled silently.
6
+ //
7
+ // Well-known keys are namespaced by domain so an authority rule can be written
8
+ // per fact-kind rather than per project. See SPEC.md "Fact keys".
9
+ export const FACT_KEYS = Object.freeze({
10
+ // what a scheduler actually runs
11
+ "schedule.cron": "the cron expression a platform scheduler evaluates for this component's job",
12
+ // what a binding actually points at
13
+ "binding.kind": "the kind of platform resource a declared binding resolves to",
14
+ "binding.target": "the concrete instance a declared binding resolves to",
15
+ // what an external API actually accepts
16
+ "contract.supports": "capabilities an external contract actually accepts",
17
+ "contract.version": "the deployed version of a contract",
18
+ // component shape
19
+ "component.deployable": "whether the component ships as an independently deployable artifact",
20
+ "component.importable": "whether the component ships as an independently importable library",
21
+ "component.runtime": "the runtime the deployed artifact executes on",
22
+ "component.language": "the implementation language of the component",
23
+ // governance
24
+ "placement.system": "the system a component is placed in",
25
+ "placement.domain": "the domain a component or system is placed in",
26
+ "resource.ownership": "which component owns a logical resource",
27
+ "schedule.enabled": "whether a scheduled job is enabled in the scheduler",
28
+ "schedule.target": "the endpoint a scheduled job invokes",
29
+ });
30
+
31
+ export function isKnownFactKey(key) {
32
+ return Object.prototype.hasOwnProperty.call(FACT_KEYS, key);
33
+ }
34
+
35
+ export function knownFactKeys() {
36
+ return Object.keys(FACT_KEYS);
37
+ }
38
+
39
+ export function assertFactKeyShape(key) {
40
+ return typeof key === "string" && /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/.test(key) && key.length <= 80;
41
+ }
package/core/codes.mjs ADDED
@@ -0,0 +1,126 @@
1
+ // Stable diagnostic/error codes. Every failure the compiler or a gate can
2
+ // produce is named here so tests, CI and the skill can match on identity
3
+ // rather than on human-readable message text.
4
+ export const CODES = {
5
+ // source & schema
6
+ UTF8: "AGENTDOC_UTF8",
7
+ YAML_PARSE: "AGENTDOC_YAML_PARSE",
8
+ YAML_DUP_KEY: "AGENTDOC_YAML_DUP_KEY",
9
+ YAML_ANCHOR: "AGENTDOC_YAML_ANCHOR",
10
+ YAML_ALIAS: "AGENTDOC_YAML_ALIAS",
11
+ YAML_TAG: "AGENTDOC_YAML_TAG",
12
+ YAML_MERGE_KEY: "AGENTDOC_YAML_MERGE_KEY",
13
+ YAML_ENV: "AGENTDOC_YAML_ENV",
14
+ YAML_UNSUPPORTED: "AGENTDOC_YAML_UNSUPPORTED",
15
+ YAML_NON_MAP_ROOT: "AGENTDOC_YAML_NON_MAP_ROOT",
16
+ SCHEMA: "AGENTDOC_SCHEMA",
17
+ UNKNOWN_FIELD: "AGENTDOC_UNKNOWN_FIELD",
18
+ PROHIBITED_FIELD: "AGENTDOC_PROHIBITED_FIELD",
19
+ API_VERSION: "AGENTDOC_API_VERSION",
20
+ KIND: "AGENTDOC_KIND",
21
+ LOCATION: "AGENTDOC_LOCATION",
22
+ CONFIG: "AGENTDOC_CONFIG",
23
+ GLOB: "AGENTDOC_GLOB",
24
+ PATH_ESCAPE: "AGENTDOC_PATH_ESCAPE",
25
+ PATH_UNRESOLVED: "AGENTDOC_PATH_UNRESOLVED",
26
+ SECRET: "AGENTDOC_SECRET",
27
+
28
+ // identity & references
29
+ NAME_INVALID: "AGENTDOC_NAME_INVALID",
30
+ REF_INVALID: "AGENTDOC_REF_INVALID",
31
+ REF_UNRESOLVED: "AGENTDOC_REF_UNRESOLVED",
32
+ DUPLICATE_IDENTITY: "AGENTDOC_DUPLICATE_IDENTITY",
33
+
34
+ // discovery coverage
35
+ COVERAGE_ZERO: "AGENTDOC_COVERAGE_ZERO",
36
+ COVERAGE_MULTI: "AGENTDOC_COVERAGE_MULTI",
37
+ COMPONENT_UNCORROBORATED: "AGENTDOC_COMPONENT_UNCORROBORATED",
38
+ EVENT_CONTRACT: "AGENTDOC_EVENT_CONTRACT",
39
+ ARTIFACT_TYPE: "AGENTDOC_ARTIFACT_TYPE",
40
+
41
+ // contracts
42
+ CONTRACT_FORM: "AGENTDOC_CONTRACT_FORM",
43
+ CONTRACT_SYNTAX: "AGENTDOC_CONTRACT_SYNTAX",
44
+ CONTRACT_UNCLAIMED: "AGENTDOC_CONTRACT_UNCLAIMED",
45
+ CONTRACT_SHARED: "AGENTDOC_CONTRACT_SHARED",
46
+ OIDC_DISCOVERY: "AGENTDOC_OIDC_DISCOVERY",
47
+
48
+ // relations
49
+ RELATION_KIND: "AGENTDOC_RELATION_KIND",
50
+ RELATION_UNRESOLVED: "AGENTDOC_RELATION_UNRESOLVED",
51
+ RELATION_AUTHORED: "AGENTDOC_RELATION_AUTHORED",
52
+ RELATION_MISSING_INVERSE: "AGENTDOC_RELATION_MISSING_INVERSE",
53
+ RELATION_CONTRACT_DUP: "AGENTDOC_RELATION_CONTRACT_DUP",
54
+
55
+ // authority / conflicts
56
+ CONFLICT_UNRESOLVED: "AGENTDOC_CONFLICT_UNRESOLVED",
57
+ OBSERVATION_STALE: "AGENTDOC_OBSERVATION_STALE",
58
+ ELECTION_STALE: "AGENTDOC_ELECTION_STALE",
59
+ UNRESOLVED_FACT: "AGENTDOC_UNRESOLVED_FACT",
60
+
61
+ // normative requirements
62
+ NORMATIVE_UNCOVERED: "AGENTDOC_NORMATIVE_UNCOVERED",
63
+ NORMATIVE_SOURCE_STALE: "AGENTDOC_NORMATIVE_SOURCE_STALE",
64
+ REQUIREMENT_UNVERIFIED: "AGENTDOC_REQUIREMENT_UNVERIFIED",
65
+
66
+ // provenance & determinism
67
+ PROVENANCE: "AGENTDOC_PROVENANCE",
68
+ NONDETERMINISTIC: "AGENTDOC_NONDETERMINISTIC",
69
+ GRAPH_SCHEMA: "AGENTDOC_GRAPH_SCHEMA",
70
+
71
+ // artifacts & gates
72
+ STALE_GRAPH: "AGENTDOC_STALE_GRAPH",
73
+ GRAPH_FRESHNESS: "AGENTDOC_GRAPH_FRESHNESS",
74
+ GRAPH_MISSING: "AGENTDOC_GRAPH_MISSING",
75
+ COMPILER_MISMATCH: "AGENTDOC_COMPILER_MISMATCH",
76
+ DIRTY_SOURCE: "AGENTDOC_DIRTY_SOURCE",
77
+ WARNING_ACCEPTANCE: "AGENTDOC_WARNING_ACCEPTANCE",
78
+ OVERRIDE_STALE: "AGENTDOC_OVERRIDE_STALE",
79
+ JOURNEY: "AGENTDOC_JOURNEY",
80
+ };
81
+
82
+ // Warning codes that a reviewer may explicitly accept. Anything outside this
83
+ // list is unacceptable: acceptance must never downgrade a hard error.
84
+ export const ACCEPTABLE_WARNING_CODES = Object.freeze([
85
+ "AGENTDOC_EVENT_UNRESOLVED",
86
+ "AGENTDOC_RESOURCE_CANDIDATE",
87
+ "AGENTDOC_SERVICE_BINDING_UNRESOLVED",
88
+ "AGENTDOC_SCHEDULE_UNRESOLVED",
89
+ "AGENTDOC_BINDING_AMBIGUOUS",
90
+ "AGENTDOC_CONFLICT_REVIEWED",
91
+ "AGENTDOC_OBSERVATION_SUPERSEDED",
92
+ "AGENTDOC_EXTERNAL_DEPENDENCY_UNCLASSIFIED",
93
+ "AGENTDOC_DOC_ORPHANED",
94
+ "AGENTDOC_VERIFICATION_UNTIERED",
95
+ "AGENTDOC_NORMATIVE_UNCOVERED",
96
+ "AGENTDOC_REQUIREMENT_UNVERIFIED",
97
+ ]);
98
+
99
+ export class AgentDocError extends Error {
100
+ constructor(code, message, opts = {}) {
101
+ super(message);
102
+ this.name = "AgentDocError";
103
+ this.code = code;
104
+ this.path = opts.path;
105
+ this.ref = opts.ref;
106
+ this.line = opts.line;
107
+ }
108
+ format() {
109
+ const where = [this.path, this.line ? "line " + this.line : null, this.ref].filter(Boolean).join(" ");
110
+ return this.code + (where ? " [" + where + "]" : "") + ": " + this.message;
111
+ }
112
+ }
113
+
114
+ // Run fn, pushing any AgentDocError into errors and returning undefined.
115
+ // A non-AgentDocError is a compiler defect and is rethrown.
116
+ export function collect(fn, errors) {
117
+ try {
118
+ return fn();
119
+ } catch (e) {
120
+ if (e instanceof AgentDocError) {
121
+ errors.push(e);
122
+ return undefined;
123
+ }
124
+ throw e;
125
+ }
126
+ }