agent-doc-system 0.0.0-stage → 1.0.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/INSTALL_FOR_AGENTS.md +60 -0
- package/LICENSE +21 -0
- package/README.md +151 -2
- package/adapters/cache.mjs +27 -0
- package/adapters/contract-consumers.mjs +56 -0
- package/adapters/database.mjs +116 -0
- package/adapters/deploy-config.mjs +34 -0
- package/adapters/dockerfile.mjs +26 -0
- package/adapters/events.mjs +145 -0
- package/adapters/generic.mjs +86 -0
- package/adapters/github-actions.mjs +106 -0
- package/adapters/go.mjs +91 -0
- package/adapters/graphql.mjs +28 -0
- package/adapters/index.mjs +55 -0
- package/adapters/node.mjs +118 -0
- package/adapters/object-store.mjs +64 -0
- package/adapters/oidc.mjs +56 -0
- package/adapters/openapi.mjs +50 -0
- package/adapters/pnpm.mjs +42 -0
- package/adapters/protobuf.mjs +35 -0
- package/adapters/registry.mjs +27 -0
- package/adapters/resource-client.mjs +61 -0
- package/adapters/turborepo.mjs +135 -0
- package/adapters/typescript.mjs +85 -0
- package/adapters/wrangler.mjs +206 -0
- package/bin/agentdoc.mjs +478 -0
- package/bin/usage.txt +26 -0
- package/core/acceptances.mjs +69 -0
- package/core/audit.mjs +407 -0
- package/core/authority/classes.mjs +39 -0
- package/core/authority/engine.mjs +243 -0
- package/core/authority/facts.mjs +41 -0
- package/core/codes.mjs +119 -0
- package/core/compile.mjs +881 -0
- package/core/contracts.mjs +121 -0
- package/core/derive.mjs +102 -0
- package/core/descriptors.mjs +486 -0
- package/core/determinism.mjs +95 -0
- package/core/discover.mjs +147 -0
- package/core/facts.mjs +53 -0
- package/core/fsx.mjs +342 -0
- package/core/graph.mjs +260 -0
- package/core/impact.mjs +175 -0
- package/core/indexes.mjs +165 -0
- package/core/jsonschema.mjs +298 -0
- package/core/observations.mjs +129 -0
- package/core/provenance.mjs +83 -0
- package/core/query.mjs +384 -0
- package/core/relations.mjs +286 -0
- package/core/scaffold.mjs +322 -0
- package/core/secrets.mjs +92 -0
- package/core/sourcescan.mjs +88 -0
- package/core/toml.mjs +163 -0
- package/core/yaml.mjs +475 -0
- package/docs/ADAPTERS.md +172 -0
- package/docs/AUTHORITY.md +260 -0
- package/docs/CI.md +136 -0
- package/docs/COMPARISON-KODA.md +164 -0
- package/docs/EVALS.md +166 -0
- package/docs/MIGRATION.md +193 -0
- package/docs/PROVENANCE.md +133 -0
- package/docs/QUERY.md +180 -0
- package/docs/SPEC.md +339 -0
- package/docs/VERIFICATION.md +28 -0
- package/docs/ci/generic-ci.sh +28 -0
- package/docs/ci/github-actions.yml +35 -0
- package/evals/harness.mjs +426 -0
- package/evals/scenarios/fixture-a-01-change-api.json +9 -0
- package/evals/scenarios/fixture-a-02-change-db.json +9 -0
- package/evals/scenarios/fixture-a-03-enforce-constraint.json +9 -0
- package/evals/scenarios/fixture-b-01-change-proto.json +9 -0
- package/evals/scenarios/fixture-b-02-change-write-path.json +9 -0
- package/evals/scenarios/fixture-b-03-change-shared-lib.json +9 -0
- package/evals/scenarios/fixture-b-04-journey.json +9 -0
- package/evals/scenarios/fixture-c-01-mixed-runtime-edge.json +19 -0
- package/evals/scenarios/fixture-c-02-stale-doc-contradiction.json +9 -0
- package/evals/scenarios/fixture-c-03-shared-vocabulary.json +9 -0
- package/evals/scenarios/fixture-c-04-contract-change.json +9 -0
- package/evals/scenarios/koda-01-modify-api-consumer.json +26 -0
- package/evals/scenarios/koda-02-change-db-schema.json +20 -0
- package/evals/scenarios/koda-03-change-auth.json +21 -0
- package/evals/scenarios/koda-04-add-event-producer.json +19 -0
- package/evals/scenarios/koda-05-modify-worker-binding.json +25 -0
- package/evals/scenarios/koda-06-change-cross-component-endpoint.json +22 -0
- package/evals/scenarios/koda-07-change-shared-package.json +16 -0
- package/evals/scenarios/koda-08-change-runtime-cron.json +23 -0
- package/evals/scenarios/koda-09-debug-production-mismatch.json +26 -0
- package/evals/thresholds.json +11 -0
- package/package.json +46 -4
- package/schemas/api.schema.json +84 -0
- package/schemas/common.schema.json +107 -0
- package/schemas/component.schema.json +40 -0
- package/schemas/config.schema.json +685 -0
- package/schemas/domain.schema.json +14 -0
- package/schemas/graph.schema.json +1084 -0
- package/schemas/journeys.schema.json +47 -0
- package/schemas/observation.schema.json +143 -0
- package/schemas/resource.schema.json +28 -0
- package/schemas/system.schema.json +26 -0
- package/skills/agent-doc-system/SKILL.md +113 -0
- package/skills/agent-doc-system/references/authority-and-conflicts.md +70 -0
- package/skills/agent-doc-system/references/cli.md +89 -0
- package/skills/agent-doc-system/references/create-migrate.md +101 -0
- package/skills/agent-doc-system/references/maintenance.md +52 -0
- package/templates/ADR.md +26 -0
- package/templates/ARCHITECTURE.md +19 -0
- package/templates/CONSTRAINTS.md +17 -0
- package/templates/PRODUCT.md +17 -0
- package/templates/api.yaml +10 -0
- package/templates/ci.yml +31 -0
- package/templates/component.yaml +14 -0
- package/templates/domain.yaml +6 -0
- package/templates/journey.yaml +7 -0
- package/templates/observation.yaml +19 -0
- package/templates/resource.yaml +8 -0
- package/templates/system.yaml +10 -0
- package/templates/warning-acceptance.yaml +14 -0
|
@@ -0,0 +1,243 @@
|
|
|
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
|
+
|
|
14
|
+
export class AuthorityEngine {
|
|
15
|
+
constructor(rules) {
|
|
16
|
+
this.rules = (rules || []).map((r) => {
|
|
17
|
+
let re;
|
|
18
|
+
try {
|
|
19
|
+
re = new RegExp(r.keyPattern, "u");
|
|
20
|
+
} catch {
|
|
21
|
+
throw new AgentDocError(CODES.CONFIG, "authority rule " + r.id + " has an invalid keyPattern: " + r.keyPattern);
|
|
22
|
+
}
|
|
23
|
+
for (const c of r.elect) {
|
|
24
|
+
if (!EVIDENCE_CLASSES.includes(c) || c === "UNRESOLVED") {
|
|
25
|
+
throw new AgentDocError(CODES.CONFIG, "authority rule " + r.id + " cannot elect " + c);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return { ...r, re };
|
|
29
|
+
});
|
|
30
|
+
for (let i = 0; i < this.rules.length; i++) {
|
|
31
|
+
for (let j = i + 1; j < this.rules.length; j++) {
|
|
32
|
+
if (this.rules[i].id === this.rules[j].id) {
|
|
33
|
+
throw new AgentDocError(CODES.CONFIG, "duplicate authority rule id " + this.rules[i].id);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Returns the most specific matching rule (longest pattern wins) or null.
|
|
39
|
+
ruleFor(subject, key) {
|
|
40
|
+
const target = subject + " " + key;
|
|
41
|
+
let best = null;
|
|
42
|
+
for (const r of this.rules) {
|
|
43
|
+
if (!r.re.test(target)) continue;
|
|
44
|
+
if (!best || r.keyPattern.length > best.keyPattern.length) best = r;
|
|
45
|
+
}
|
|
46
|
+
return best;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Group assertions by (subject, key) and elect at most one per group.
|
|
51
|
+
//
|
|
52
|
+
// Election basis, in the order it is attempted — and each basis is only
|
|
53
|
+
// reachable when a rule explicitly allows it:
|
|
54
|
+
// reviewed-override an explicit human interpretation exists
|
|
55
|
+
// authority-rule the rule's elect list contains exactly one class present
|
|
56
|
+
// superset the elected value strictly contains the others
|
|
57
|
+
// recency the newest runtime observation, and only if the rule says so
|
|
58
|
+
// none fail closed
|
|
59
|
+
export function resolveAssertions(assertions, authority) {
|
|
60
|
+
const groups = new Map();
|
|
61
|
+
for (const a of assertions) {
|
|
62
|
+
// An assertion a later observation supersedes stays in the graph as
|
|
63
|
+
// history but does not compete here: it cannot win an election and cannot
|
|
64
|
+
// anchor a conflict.
|
|
65
|
+
if (a.status === "superseded") continue;
|
|
66
|
+
const gk = a.subject + "\u0000" + a.key;
|
|
67
|
+
if (!groups.has(gk)) groups.set(gk, []);
|
|
68
|
+
groups.get(gk).push(a);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const conflicts = [];
|
|
72
|
+
const elections = [];
|
|
73
|
+
|
|
74
|
+
for (const [gk, list] of [...groups.entries()].sort((a, b) => (a[0] < b[0] ? -1 : 1))) {
|
|
75
|
+
const [subject, key] = gk.split("\u0000");
|
|
76
|
+
const distinct = distinctValues(list);
|
|
77
|
+
|
|
78
|
+
if (distinct.length === 1) {
|
|
79
|
+
// Agreement across evidence classes is corroboration, not a conflict —
|
|
80
|
+
// but a lone heuristic candidate is not agreement with anything. It stays
|
|
81
|
+
// UNRESOLVED and never becomes an elected fact.
|
|
82
|
+
const allCandidate = list.every((a) => a.confidence === "candidate");
|
|
83
|
+
for (const a of list) a.status = allCandidate ? "candidate" : "elected";
|
|
84
|
+
const rule = authority.ruleFor(subject, key);
|
|
85
|
+
elections.push({ subject, key, ruleId: rule ? rule.id : "agreement", basis: "authority-rule", elected: list[0] });
|
|
86
|
+
if (allCandidate) {
|
|
87
|
+
conflicts.push({
|
|
88
|
+
subject,
|
|
89
|
+
key,
|
|
90
|
+
kind: "ambiguity",
|
|
91
|
+
status: "unresolved",
|
|
92
|
+
assertionIds: list.map((a) => a.id).sort(),
|
|
93
|
+
election: {
|
|
94
|
+
elected: null,
|
|
95
|
+
basis: "none",
|
|
96
|
+
ruleId: rule ? rule.id : "no-rule",
|
|
97
|
+
rationale: "only heuristic candidates exist for '" + key + "'; deterministic discovery cannot resolve it",
|
|
98
|
+
reviewWhen: rule ? rule.reviewWhen : null,
|
|
99
|
+
contradicted: [],
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const rule = authority.ruleFor(subject, key);
|
|
107
|
+
const candidates = list.filter((a) => a.confidence !== "candidate");
|
|
108
|
+
const candidateOnly = list.length !== candidates.length;
|
|
109
|
+
|
|
110
|
+
// `reviewWhen` travels with the election so a consumer can report when this
|
|
111
|
+
// decision must be re-examined. A null here means "no rule governs it", which
|
|
112
|
+
// is a real state — and one that must never be rendered as if a condition
|
|
113
|
+
// existed.
|
|
114
|
+
const election = {
|
|
115
|
+
elected: null,
|
|
116
|
+
basis: "none",
|
|
117
|
+
ruleId: rule ? rule.id : "no-rule",
|
|
118
|
+
rationale: null,
|
|
119
|
+
reviewWhen: rule ? rule.reviewWhen : null,
|
|
120
|
+
contradicted: [],
|
|
121
|
+
};
|
|
122
|
+
let status = "unresolved";
|
|
123
|
+
|
|
124
|
+
// 1. reviewed override wins outright, but the divergence is still recorded.
|
|
125
|
+
const reviewed = candidates.filter((a) => a.evidenceClass === "REVIEWED_OVERRIDE");
|
|
126
|
+
if (rule && rule.elect.length === 1 && rule.elect[0] === "REVIEWED_OVERRIDE" && reviewed.length === 1) {
|
|
127
|
+
election.elected = reviewed[0];
|
|
128
|
+
election.basis = "reviewed-override";
|
|
129
|
+
election.rationale = rule.rationale;
|
|
130
|
+
status = "accepted";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 2. authority rule with a single elected class present among candidates.
|
|
134
|
+
if (!election.elected && rule) {
|
|
135
|
+
const allowed = rule.elect.filter((c) => candidates.some((a) => a.evidenceClass === c));
|
|
136
|
+
if (allowed.length === 1) {
|
|
137
|
+
const winners = candidates.filter((a) => a.evidenceClass === allowed[0]);
|
|
138
|
+
if (winners.length === 1) {
|
|
139
|
+
election.elected = winners[0];
|
|
140
|
+
election.basis = "authority-rule";
|
|
141
|
+
election.rationale = rule.rationale;
|
|
142
|
+
status = "resolved";
|
|
143
|
+
} else if (winners.length > 1) {
|
|
144
|
+
election.rationale = "multiple same-class assertions disagree; no election";
|
|
145
|
+
}
|
|
146
|
+
} else if (allowed.length > 1) {
|
|
147
|
+
election.rationale = "authority rule " + rule.id + " elects several classes that are all present; no deterministic election";
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 3. superset: one candidate's value contains every other candidate's value.
|
|
152
|
+
if (!election.elected && candidates.length > 1) {
|
|
153
|
+
const sup = candidates.find((c) => candidates.every((o) => o === c || containsValue(c.value, o.value)));
|
|
154
|
+
if (sup) {
|
|
155
|
+
election.elected = sup;
|
|
156
|
+
election.basis = "superset";
|
|
157
|
+
election.rationale = "one observed value strictly contains the others";
|
|
158
|
+
status = "resolved";
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// 4. recency: only when the rule names recency and the winner is a runtime fact.
|
|
163
|
+
if (!election.elected && rule && rule.elect.includes("OBSERVED_RUNTIME")) {
|
|
164
|
+
const runtime = candidates.filter((a) => a.evidenceClass === "OBSERVED_RUNTIME");
|
|
165
|
+
const dated = runtime.filter((a) => a.observed && a.observed.at);
|
|
166
|
+
if (dated.length > 1) {
|
|
167
|
+
const newest = dated.sort((a, b) => (a.observed.at < b.observed.at ? -1 : 1)).pop();
|
|
168
|
+
election.elected = newest;
|
|
169
|
+
election.basis = "recency";
|
|
170
|
+
election.rationale = "newest runtime observation, per authority rule " + rule.id;
|
|
171
|
+
status = "resolved";
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!election.elected) {
|
|
176
|
+
election.rationale = election.rationale || (
|
|
177
|
+
rule
|
|
178
|
+
? "authority rule " + rule.id + " does not resolve this disagreement"
|
|
179
|
+
: "no authority rule governs '" + key + "' for " + subject + "; fail closed"
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
for (const a of list) {
|
|
184
|
+
if (a === election.elected) a.status = "elected";
|
|
185
|
+
else if (a.confidence === "candidate") a.status = "candidate";
|
|
186
|
+
else a.status = "contradicted";
|
|
187
|
+
}
|
|
188
|
+
election.contradicted = list.filter((a) => a !== election.elected && a.confidence !== "candidate").map((a) => a.id);
|
|
189
|
+
|
|
190
|
+
const kind = classifyConflict(list, election, candidateOnly);
|
|
191
|
+
conflicts.push({
|
|
192
|
+
subject,
|
|
193
|
+
key,
|
|
194
|
+
kind,
|
|
195
|
+
status,
|
|
196
|
+
assertionIds: list.map((a) => a.id).sort(),
|
|
197
|
+
election,
|
|
198
|
+
});
|
|
199
|
+
elections.push({ subject, key, ruleId: election.ruleId, basis: election.basis, elected: election.elected });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return { conflicts, elections };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function classifyConflict(list, election, candidateOnly) {
|
|
206
|
+
const classes = new Set(list.map((a) => a.evidenceClass));
|
|
207
|
+
if (election.basis === "reviewed-override") return "reviewed-divergence";
|
|
208
|
+
// An observation that declares itself superseded no longer competes: a newer
|
|
209
|
+
// capture of the same authority system replaces it outright.
|
|
210
|
+
if (list.some((a) => a.observed && a.observed.supersedes)) return "supersession";
|
|
211
|
+
if (classes.has("OBSERVED_RUNTIME") && classes.has("AUTHORED")) return "contradiction";
|
|
212
|
+
if (classes.has("OBSERVED_RUNTIME") && classes.size === 1) return "staleness";
|
|
213
|
+
if (candidateOnly) return "ambiguity";
|
|
214
|
+
if (classes.size > 1) return "contradiction";
|
|
215
|
+
return "ambiguity";
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function distinctValues(list) {
|
|
219
|
+
const seen = new Map();
|
|
220
|
+
for (const a of list) {
|
|
221
|
+
const k = stable(a.value);
|
|
222
|
+
if (!seen.has(k)) seen.set(k, a.value);
|
|
223
|
+
}
|
|
224
|
+
return [...seen.values()];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function containsValue(big, small) {
|
|
228
|
+
if (Array.isArray(big) && Array.isArray(small)) {
|
|
229
|
+
return small.every((s) => big.some((b) => stable(b) === stable(s))) && big.length > small.length;
|
|
230
|
+
}
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function stable(v) {
|
|
235
|
+
if (v === null || typeof v !== "object") return JSON.stringify(v);
|
|
236
|
+
if (Array.isArray(v)) return "[" + v.map(stable).join(",") + "]";
|
|
237
|
+
return "{" + Object.keys(v).sort().map((k) => JSON.stringify(k) + ":" + stable(v[k])).join(",") + "}";
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function assertConfidence(c) {
|
|
241
|
+
if (!CONFIDENCE.includes(c)) throw new Error("unknown confidence " + c);
|
|
242
|
+
return c;
|
|
243
|
+
}
|
|
@@ -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,119 @@
|
|
|
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
|
+
// provenance & determinism
|
|
62
|
+
PROVENANCE: "AGENTDOC_PROVENANCE",
|
|
63
|
+
NONDETERMINISTIC: "AGENTDOC_NONDETERMINISTIC",
|
|
64
|
+
GRAPH_SCHEMA: "AGENTDOC_GRAPH_SCHEMA",
|
|
65
|
+
|
|
66
|
+
// artifacts & gates
|
|
67
|
+
STALE_GRAPH: "AGENTDOC_STALE_GRAPH",
|
|
68
|
+
GRAPH_FRESHNESS: "AGENTDOC_GRAPH_FRESHNESS",
|
|
69
|
+
GRAPH_MISSING: "AGENTDOC_GRAPH_MISSING",
|
|
70
|
+
COMPILER_MISMATCH: "AGENTDOC_COMPILER_MISMATCH",
|
|
71
|
+
DIRTY_SOURCE: "AGENTDOC_DIRTY_SOURCE",
|
|
72
|
+
WARNING_ACCEPTANCE: "AGENTDOC_WARNING_ACCEPTANCE",
|
|
73
|
+
OVERRIDE_STALE: "AGENTDOC_OVERRIDE_STALE",
|
|
74
|
+
JOURNEY: "AGENTDOC_JOURNEY",
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Warning codes that a reviewer may explicitly accept. Anything outside this
|
|
78
|
+
// list is unacceptable: acceptance must never downgrade a hard error.
|
|
79
|
+
export const ACCEPTABLE_WARNING_CODES = Object.freeze([
|
|
80
|
+
"AGENTDOC_EVENT_UNRESOLVED",
|
|
81
|
+
"AGENTDOC_RESOURCE_CANDIDATE",
|
|
82
|
+
"AGENTDOC_SERVICE_BINDING_UNRESOLVED",
|
|
83
|
+
"AGENTDOC_SCHEDULE_UNRESOLVED",
|
|
84
|
+
"AGENTDOC_BINDING_AMBIGUOUS",
|
|
85
|
+
"AGENTDOC_CONFLICT_REVIEWED",
|
|
86
|
+
"AGENTDOC_OBSERVATION_SUPERSEDED",
|
|
87
|
+
"AGENTDOC_EXTERNAL_DEPENDENCY_UNCLASSIFIED",
|
|
88
|
+
"AGENTDOC_DOC_ORPHANED",
|
|
89
|
+
"AGENTDOC_VERIFICATION_UNTIERED",
|
|
90
|
+
]);
|
|
91
|
+
|
|
92
|
+
export class AgentDocError extends Error {
|
|
93
|
+
constructor(code, message, opts = {}) {
|
|
94
|
+
super(message);
|
|
95
|
+
this.name = "AgentDocError";
|
|
96
|
+
this.code = code;
|
|
97
|
+
this.path = opts.path;
|
|
98
|
+
this.ref = opts.ref;
|
|
99
|
+
this.line = opts.line;
|
|
100
|
+
}
|
|
101
|
+
format() {
|
|
102
|
+
const where = [this.path, this.line ? "line " + this.line : null, this.ref].filter(Boolean).join(" ");
|
|
103
|
+
return this.code + (where ? " [" + where + "]" : "") + ": " + this.message;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Run fn, pushing any AgentDocError into errors and returning undefined.
|
|
108
|
+
// A non-AgentDocError is a compiler defect and is rethrown.
|
|
109
|
+
export function collect(fn, errors) {
|
|
110
|
+
try {
|
|
111
|
+
return fn();
|
|
112
|
+
} catch (e) {
|
|
113
|
+
if (e instanceof AgentDocError) {
|
|
114
|
+
errors.push(e);
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
throw e;
|
|
118
|
+
}
|
|
119
|
+
}
|