@ecoma-io/archkeep 0.24.1 → 0.26.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/cli.mjs +166 -506
- package/commands.mjs +7 -1
- package/package.json +1 -1
- package/src/architecture-intent/judge.mjs +1 -1
- package/src/architecture-intent/model.mjs +34 -0
- package/src/canonical.mjs +2 -1
- package/src/commands/adr-for-workspace.mjs +63 -0
- package/src/commands/analyze-capability.mjs +13 -0
- package/src/commands/change-intent.mjs +11 -0
- package/src/commands/change.mjs +315 -55
- package/src/commands/check-capability.mjs +13 -0
- package/src/commands/check.mjs +13 -3
- package/src/commands/compare-capability.mjs +20 -0
- package/src/commands/completeness.mjs +12 -0
- package/src/commands/context-command.mjs +31 -0
- package/src/commands/coverage-verdict.mjs +15 -2
- package/src/commands/debt.mjs +30 -0
- package/src/commands/decisions.mjs +59 -10
- package/src/commands/delta-snapshot.mjs +25 -4
- package/src/commands/delta.mjs +301 -67
- package/src/commands/diff.mjs +48 -9
- package/src/commands/discover.mjs +37 -0
- package/src/commands/drift.mjs +45 -0
- package/src/commands/evolution.mjs +22 -2
- package/src/commands/explain-capability.mjs +11 -0
- package/src/commands/explain.mjs +24 -0
- package/src/commands/fitness.mjs +119 -10
- package/src/commands/govern-capability.mjs +16 -0
- package/src/commands/graph.mjs +39 -0
- package/src/commands/health.mjs +34 -0
- package/src/commands/history.mjs +119 -2
- package/src/commands/impact.mjs +23 -0
- package/src/commands/inspect-capability.mjs +16 -0
- package/src/commands/plan-context-command.mjs +35 -0
- package/src/commands/policy.mjs +42 -4
- package/src/commands/provenance-command.mjs +15 -24
- package/src/commands/report.mjs +38 -0
- package/src/commands/rules-capability.mjs +16 -0
- package/src/commands/rules.mjs +3 -1
- package/src/commands/scenario.mjs +23 -0
- package/src/commands/snapshot-meta.mjs +33 -0
- package/src/commands/trajectory.mjs +35 -0
- package/src/commands/waivers.mjs +30 -0
- package/src/corpus/goldens/.gitkeep +0 -0
- package/src/corpus/goldens/adr.json +62 -0
- package/src/corpus/goldens/adr.text +6 -0
- package/src/corpus/goldens/change.json +98 -0
- package/src/corpus/goldens/change.text +6 -0
- package/src/corpus/goldens/check.json +90 -0
- package/src/corpus/goldens/check.sarif +479 -0
- package/src/corpus/goldens/check.text +9 -0
- package/src/corpus/goldens/context.json +47 -0
- package/src/corpus/goldens/context.text +6 -0
- package/src/corpus/goldens/debt.json +59 -0
- package/src/corpus/goldens/debt.text +7 -0
- package/src/corpus/goldens/decisions.json +74 -0
- package/src/corpus/goldens/decisions.text +7 -0
- package/src/corpus/goldens/delta.json +94 -0
- package/src/corpus/goldens/delta.sarif +472 -0
- package/src/corpus/goldens/delta.text +5 -0
- package/src/corpus/goldens/diff.json +58 -0
- package/src/corpus/goldens/diff.text +4 -0
- package/src/corpus/goldens/discover.json +77 -0
- package/src/corpus/goldens/discover.text +10 -0
- package/src/corpus/goldens/drift.json +43 -0
- package/src/corpus/goldens/drift.text +3 -0
- package/src/corpus/goldens/evolution.json +328 -0
- package/src/corpus/goldens/evolution.text +32 -0
- package/src/corpus/goldens/explain.json +60 -0
- package/src/corpus/goldens/explain.text +7 -0
- package/src/corpus/goldens/fitness.json +45 -0
- package/src/corpus/goldens/fitness.text +3 -0
- package/src/corpus/goldens/graph.json +78 -0
- package/src/corpus/goldens/graph.text +10 -0
- package/src/corpus/goldens/health.json +72 -0
- package/src/corpus/goldens/health.text +10 -0
- package/src/corpus/goldens/help.text +104 -0
- package/src/corpus/goldens/history.json +43 -0
- package/src/corpus/goldens/history.text +4 -0
- package/src/corpus/goldens/impact.json +327 -0
- package/src/corpus/goldens/impact.text +10 -0
- package/src/corpus/goldens/provenance.json +453 -0
- package/src/corpus/goldens/provenance.text +14 -0
- package/src/corpus/goldens/reconcile.json +168 -0
- package/src/corpus/goldens/reconcile.text +3 -0
- package/src/corpus/goldens/report.json +168 -0
- package/src/corpus/goldens/report.text +42 -0
- package/src/corpus/goldens/rules verify.json +0 -0
- package/src/corpus/goldens/rules verify.text +0 -0
- package/src/corpus/goldens/scenario.json +441 -0
- package/src/corpus/goldens/scenario.text +28 -0
- package/src/corpus/goldens/trajectory.json +83 -0
- package/src/corpus/goldens/trajectory.text +10 -0
- package/src/corpus/goldens/usage-error.text +105 -0
- package/src/corpus/goldens/waivers.json +40 -0
- package/src/corpus/goldens/waivers.text +1 -0
- package/src/corpus/goldens-lsp/initialize.result.json +15 -0
- package/src/corpus/goldens-lsp/publish-cleared-empty.json +4 -0
- package/src/corpus/goldens-lsp/publish-fixed-empty.json +5 -0
- package/src/corpus/goldens-lsp/publish-unparseable.json +54 -0
- package/src/corpus/goldens-lsp/publish-violation.json +22 -0
- package/src/corpus/goldens-lsp/register-watched-files.json +72 -0
- package/src/custom-rules/values.mjs +1 -4
- package/src/go-work.mjs +8 -23
- package/src/governance/evolution-event.mjs +15 -0
- package/src/governance/evolution-store.mjs +59 -31
- package/src/governance/fitness-registry.mjs +14 -1
- package/src/governance/provenance-record.mjs +0 -152
- package/src/governance/verdict.mjs +62 -7
- package/src/intent/intent-manifest.json +22 -16
- package/src/lsp/server.mjs +5 -8
- package/src/lsp/workspace-index.mjs +55 -220
- package/src/options.mjs +10 -0
- package/src/providers/native/discover.mjs +13 -12
- package/src/providers/native/model.mjs +8 -3
- package/src/providers/nx-static.mjs +231 -0
- package/src/report/json.mjs +7 -4
- package/src/report/sarif.mjs +8 -3
- package/src/rules/edge-constraints.mjs +9 -0
- package/src/rules/index.mjs +26 -10
- package/src/rules/messages.mjs +64 -14
- package/src/rules/specifiers.mjs +21 -20
- package/src/tsconfig-paths.mjs +8 -14
- package/src/values.mjs +11 -0
- package/src/verdict.mjs +127 -16
|
@@ -37,17 +37,8 @@
|
|
|
37
37
|
* or a polluted prototype cannot smuggle keys into a validated origin. This
|
|
38
38
|
* module builds nothing from untrusted keys; it validates and, at write time,
|
|
39
39
|
* builds a fresh object with only the three permitted keys.
|
|
40
|
-
* ## The decision-lifecycle record
|
|
41
|
-
*
|
|
42
|
-
* The same discipline extends from a row to a DECISION (an ADR id — the
|
|
43
|
-
* stable handle later waves reference). `recordDecisionLifecycle` records one
|
|
44
|
-
* lifecycle event — a status transition, a supersession, or a bindings
|
|
45
|
-
* change — attributed by the same `origin` shape and the same clock door:
|
|
46
|
-
* `by`/`tool` are required, and `on` comes from `recordOrigin` and nowhere
|
|
47
|
-
* else. A record that records nothing (a no-op transition) is refused loudly.
|
|
48
40
|
*/
|
|
49
41
|
|
|
50
|
-
import { ADR_STATUSES } from "./adr-registry.mjs";
|
|
51
42
|
import { clockViolations } from "./clock.mjs";
|
|
52
43
|
import { describe, isPlainObject } from "../values.mjs";
|
|
53
44
|
|
|
@@ -175,146 +166,3 @@ export function recordOrigin({ by, tool, clock }) {
|
|
|
175
166
|
// record, so two calls with the same clock are byte-identical.
|
|
176
167
|
return { by, tool, on: clock.now() };
|
|
177
168
|
}
|
|
178
|
-
/**
|
|
179
|
-
* The decision-lifecycle events one record can hold, each a single fact about
|
|
180
|
-
* ONE decision (an ADR id — `docs/adr/NNN-slug.md` — the stable handle the
|
|
181
|
-
* registry's `byId` map keys on). A decision's creation and every status
|
|
182
|
-
* change, supersession, and bindings change is recorded as one of these.
|
|
183
|
-
*/
|
|
184
|
-
export const DECISION_LIFECYCLE_KINDS = Object.freeze([
|
|
185
|
-
// used by its own test
|
|
186
|
-
"status-transition",
|
|
187
|
-
"supersession",
|
|
188
|
-
"bindings-change",
|
|
189
|
-
]);
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* @typedef {object} DecisionLifecycleRecord
|
|
193
|
-
* @property {"status-transition"|"supersession"|"bindings-change"} kind
|
|
194
|
-
* One recorded lifecycle event on one decision.
|
|
195
|
-
* @property {string} decisionId The ADR id the event happened to — the stable
|
|
196
|
-
* handle the ADR registry keys on (`docs/adr/NNN-slug.md`).
|
|
197
|
-
* @property {string|null} [from] status-transition: the status the decision
|
|
198
|
-
* left, or null when the event is the decision's creation (its proposed
|
|
199
|
-
* entry).
|
|
200
|
-
* @property {string} [to] status-transition: the status the decision entered.
|
|
201
|
-
* @property {string[]} [superseded] supersession: the ADR id(s) this decision
|
|
202
|
-
* replaced — `decisionId` is the RECORDING record, the successor.
|
|
203
|
-
* @property {string[]} [added] bindings-change: constraint ids made
|
|
204
|
-
* enforceable.
|
|
205
|
-
* @property {string[]} [removed] bindings-change: constraint ids unbound.
|
|
206
|
-
* @property {OriginRecord} origin WHO recorded the event and with what tool —
|
|
207
|
-
* `on` produced by `recordOrigin`, the only door.
|
|
208
|
-
*/
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Records one decision-lifecycle event, attributed by the same `origin`
|
|
212
|
-
* discipline as a row: `by` and `tool` are required, and `on` comes from the
|
|
213
|
-
* injected clock through `recordOrigin` — the only producer of an `on`, and
|
|
214
|
-
* the refusal to run without one is its own, inherited here.
|
|
215
|
-
*
|
|
216
|
-
* The record carries ONLY the kind's own keys and the origin, built fresh, so
|
|
217
|
-
* nothing from untrusted input rides along. A no-op event — a status
|
|
218
|
-
* transition that changes nothing, a supersession naming no target, a
|
|
219
|
-
* bindings change that adds and removes nothing — is refused loudly: a record
|
|
220
|
-
* that records nothing would read as a transition that happened, the silent
|
|
221
|
-
* direction this module exists to exclude.
|
|
222
|
-
*
|
|
223
|
-
* Statuses are validated against `ADR_STATUSES` (`./adr-registry.mjs`), the
|
|
224
|
-
* single status vocabulary — a record can never attest a status the registry
|
|
225
|
-
* could not hold.
|
|
226
|
-
*
|
|
227
|
-
* @param {{kind: "status-transition"|"supersession"|"bindings-change",
|
|
228
|
-
* decisionId: string,
|
|
229
|
-
* from?: string|null, to?: string,
|
|
230
|
-
* superseded?: string[],
|
|
231
|
-
* added?: string[], removed?: string[],
|
|
232
|
-
* origin: {by: string, tool: string},
|
|
233
|
-
* clock: import("./clock.mjs").Clock}} event
|
|
234
|
-
* @returns {DecisionLifecycleRecord}
|
|
235
|
-
* @throws {Error} on an unknown kind, a missing decisionId, a status outside
|
|
236
|
-
* the registry's `ADR_STATUSES`, a no-op event, or an invalid origin/clock.
|
|
237
|
-
*/
|
|
238
|
-
export function recordDecisionLifecycle({
|
|
239
|
-
// used by its own test
|
|
240
|
-
kind,
|
|
241
|
-
decisionId,
|
|
242
|
-
from = null,
|
|
243
|
-
to,
|
|
244
|
-
superseded,
|
|
245
|
-
added,
|
|
246
|
-
removed,
|
|
247
|
-
origin,
|
|
248
|
-
clock,
|
|
249
|
-
}) {
|
|
250
|
-
const violations = [];
|
|
251
|
-
if (!DECISION_LIFECYCLE_KINDS.includes(kind)) {
|
|
252
|
-
violations.push(
|
|
253
|
-
`kind: must be one of ${DECISION_LIFECYCLE_KINDS.join(", ")}, got ${describe(kind)}`,
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
if (typeof decisionId !== "string" || decisionId.trim() === "") {
|
|
257
|
-
violations.push(
|
|
258
|
-
`decisionId: must be a non-empty string naming the ADR, got ${describe(decisionId)}`,
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
if (kind === "status-transition") {
|
|
262
|
-
if (from !== null && !ADR_STATUSES.includes(from)) {
|
|
263
|
-
violations.push(
|
|
264
|
-
`from: must be null or one of ${ADR_STATUSES.join(", ")}, got ${describe(from)}`,
|
|
265
|
-
);
|
|
266
|
-
}
|
|
267
|
-
if (!ADR_STATUSES.includes(to)) {
|
|
268
|
-
violations.push(`to: must be one of ${ADR_STATUSES.join(", ")}, got ${describe(to)}`);
|
|
269
|
-
}
|
|
270
|
-
if (from !== null && from === to) {
|
|
271
|
-
violations.push(
|
|
272
|
-
`to: equals from (${JSON.stringify(from)}) — a status transition that changes nothing is not a recordable event`,
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
} else if (kind === "supersession") {
|
|
276
|
-
if (
|
|
277
|
-
!Array.isArray(superseded) ||
|
|
278
|
-
superseded.length === 0 ||
|
|
279
|
-
superseded.some((ref) => typeof ref !== "string" || ref.trim() === "")
|
|
280
|
-
) {
|
|
281
|
-
violations.push("superseded: must be a non-empty array of ADR ids this decision replaced");
|
|
282
|
-
}
|
|
283
|
-
} else if (kind === "bindings-change") {
|
|
284
|
-
for (const [name, value] of [
|
|
285
|
-
["added", added],
|
|
286
|
-
["removed", removed],
|
|
287
|
-
]) {
|
|
288
|
-
if (value !== undefined && !Array.isArray(value)) {
|
|
289
|
-
violations.push(`${name}: must be an array of constraint ids, got ${describe(value)}`);
|
|
290
|
-
} else if (
|
|
291
|
-
Array.isArray(value) &&
|
|
292
|
-
value.some((id) => typeof id !== "string" || id.trim() === "")
|
|
293
|
-
) {
|
|
294
|
-
violations.push(`${name}: every entry must be a non-empty constraint id`);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
const addedList = Array.isArray(added) ? added : [];
|
|
298
|
-
const removedList = Array.isArray(removed) ? removed : [];
|
|
299
|
-
if (addedList.length === 0 && removedList.length === 0) {
|
|
300
|
-
violations.push(
|
|
301
|
-
"added/removed: a bindings change that adds nothing and removes nothing is not a recordable event",
|
|
302
|
-
);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
if (violations.length > 0) {
|
|
306
|
-
throw new Error(`decisionLifecycle: ${violations.join("; ")}`);
|
|
307
|
-
}
|
|
308
|
-
const eventFields =
|
|
309
|
-
kind === "status-transition"
|
|
310
|
-
? { from, to }
|
|
311
|
-
: kind === "supersession"
|
|
312
|
-
? { superseded }
|
|
313
|
-
: { added: added ?? [], removed: removed ?? [] };
|
|
314
|
-
return {
|
|
315
|
-
kind,
|
|
316
|
-
decisionId,
|
|
317
|
-
...eventFields,
|
|
318
|
-
origin: recordOrigin({ by: origin?.by, tool: origin?.tool, clock }),
|
|
319
|
-
};
|
|
320
|
-
}
|
|
@@ -13,9 +13,17 @@
|
|
|
13
13
|
* `"findings"` → `"fail"`
|
|
14
14
|
* `"no-verdict"` → `"unknown"`
|
|
15
15
|
*
|
|
16
|
-
* and `"not_applicable"` has no source status in this release —
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* and `"not_applicable"` has no source status in this release — no envelope
|
|
17
|
+
* status maps to it, so it never rides an envelope. Engine behavior does emit
|
|
18
|
+
* it today, on the paths that cannot judge: the fitness registry answers it
|
|
19
|
+
* for a declared function whose `match` selects no observed project, and
|
|
20
|
+
* again for `coverage-minimum` on a path-scoped run, which no scoped run can
|
|
21
|
+
* judge; decision fitness derives it for a decision without authority; the
|
|
22
|
+
* boundary metrics answer it when there is no boundary config to judge
|
|
23
|
+
* against (`./metrics.mjs`). A
|
|
24
|
+
* custom rule may return it as its own verdict — the host requires a
|
|
25
|
+
* `notApplicableReason` (`../custom-rules/host.mjs`) — but no engine path
|
|
26
|
+
* answers it on a rule's behalf.
|
|
19
27
|
*
|
|
20
28
|
* ## The invariants, I1–I5
|
|
21
29
|
*
|
|
@@ -41,8 +49,25 @@
|
|
|
41
49
|
* (`../verdict.mjs`) depend on the presentation layer. `../report/evidence.mjs`
|
|
42
50
|
* re-exports `buildDecision` so the render-side callers keep their import
|
|
43
51
|
* path — a path, never a second implementation.
|
|
52
|
+
*
|
|
53
|
+
* Layer: the verdict vocabulary and decision owner — `VERDICTS`, `isVerdict`,
|
|
54
|
+
* `verdictForStatus`, `fitnessVerdict`, and `buildDecision`. Its counterpart,
|
|
55
|
+
* `../verdict.mjs`, is the check lane built on top of `buildDecision` — the
|
|
56
|
+
* process's exit codes, the coverage clauses, and `verdictFor`, the per-lint
|
|
57
|
+
* verdict the `check` command words its envelope and its exit from. The two
|
|
58
|
+
* near-collision pairs cross that boundary, and the name decides which file
|
|
59
|
+
* a reader wants: `verdictForStatus` here looks one status up in a frozen
|
|
60
|
+
* map, while `../verdict.mjs`'s `verdictFor` folds a run's counts into
|
|
61
|
+
* status + exitCode; `VERDICT_FOR_STATUS` here is an independently frozen
|
|
62
|
+
* map, while `../verdict.mjs`'s `EXIT_FOR_STATUS` is derived from its own
|
|
63
|
+
* `EXIT`. Verdict vocabulary and decision construction live here; exit-code,
|
|
64
|
+
* coverage and check-lane concerns live there — and the layering stays
|
|
65
|
+
* one-directional (the check lane imports this module, never the reverse),
|
|
66
|
+
* so this module must not grow exit-code or coverage concerns.
|
|
44
67
|
*/
|
|
45
68
|
|
|
69
|
+
import { describe, isNonEmptyString } from "../values.mjs";
|
|
70
|
+
|
|
46
71
|
/** The four canonical verdict values. */
|
|
47
72
|
export const VERDICTS = Object.freeze(["pass", "fail", "unknown", "not_applicable"]);
|
|
48
73
|
|
|
@@ -113,7 +138,7 @@ export function fitnessVerdict({ verdict, name, evidence, message, rows, notAppl
|
|
|
113
138
|
`expected one of ${VERDICTS.join(", ")}.`,
|
|
114
139
|
);
|
|
115
140
|
}
|
|
116
|
-
if (verdict === "not_applicable" && notApplicableReason
|
|
141
|
+
if (verdict === "not_applicable" && !isNonEmptyString(notApplicableReason)) {
|
|
117
142
|
throw new Error(
|
|
118
143
|
`archkeep: fitness function "${name}" returned "not_applicable" without ` +
|
|
119
144
|
`notApplicableReason — invariant I4: the reader must be told why the ` +
|
|
@@ -206,6 +231,21 @@ export function buildDecision(run) {
|
|
|
206
231
|
throw new Error("archkeep: buildDecision needs either a status or an explicit verdict");
|
|
207
232
|
}
|
|
208
233
|
const verdict = run.verdict ?? verdictForStatus(run.status);
|
|
234
|
+
// The vocabulary latch. `verdictForStatus` cannot produce a stranger, so
|
|
235
|
+
// this refuses exactly the explicitly-passed verdicts — and it must run
|
|
236
|
+
// BEFORE the status-conflict check below, so a malformed verdict reports
|
|
237
|
+
// the vocabulary it violated rather than a conflict it never had. Without
|
|
238
|
+
// this latch, "perhaps", "PASS", `42` and `{}` fell through the
|
|
239
|
+
// pass/fail/unknown arms into the not_applicable one and SHIPPED whenever
|
|
240
|
+
// `notApplicableReason` happened to be truthy — a byte-legal decision
|
|
241
|
+
// about a workspace nobody judged.
|
|
242
|
+
if (!isVerdict(verdict)) {
|
|
243
|
+
throw new Error(
|
|
244
|
+
`archkeep: refusing to build a decision with verdict ${describe(run.verdict)} — ` +
|
|
245
|
+
`expected one of ${VERDICTS.join(", ")}. This is a bug in the command that ` +
|
|
246
|
+
`built the decision.`,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
209
249
|
if (
|
|
210
250
|
run.verdict !== undefined &&
|
|
211
251
|
run.status !== undefined &&
|
|
@@ -260,17 +300,32 @@ export function buildDecision(run) {
|
|
|
260
300
|
}
|
|
261
301
|
|
|
262
302
|
if (verdict === "unknown") {
|
|
303
|
+
// An absent reason (undefined/null) still defaults to the generic one —
|
|
304
|
+
// absence is not emptiness. A SUPPLIED reason must actually say
|
|
305
|
+
// something: "" and " " are byte-present but semantically absent, and
|
|
306
|
+
// a non-string reason would ship a `typeof` artifact where the reader
|
|
307
|
+
// was promised a sentence (I3).
|
|
263
308
|
const reason =
|
|
264
309
|
run.reason ??
|
|
265
310
|
(run.coverageComplete === true ? "no verdict was reached" : "coverage was incomplete");
|
|
311
|
+
if (!isNonEmptyString(reason)) {
|
|
312
|
+
throw new Error(
|
|
313
|
+
`archkeep: refusing to emit an "unknown" decision with a ${describe(run.reason)} reason — ` +
|
|
314
|
+
`I3 requires a non-empty reason naming why no verdict was reached. ` +
|
|
315
|
+
`This is a bug in the command that built the decision.`,
|
|
316
|
+
);
|
|
317
|
+
}
|
|
266
318
|
return withSampleTime({ verdict, reason }, run.sampleTime);
|
|
267
319
|
}
|
|
268
320
|
|
|
269
|
-
// verdict === "not_applicable" (I4).
|
|
270
|
-
|
|
321
|
+
// verdict === "not_applicable" (I4). The reason must be a string a reader
|
|
322
|
+
// could act on — the falsy check this replaces refused "" but SHIPPED
|
|
323
|
+
// " " and non-strings, the same byte-present-semantically-absent hole.
|
|
324
|
+
if (!isNonEmptyString(run.notApplicableReason)) {
|
|
271
325
|
throw new Error(
|
|
272
326
|
`archkeep: refusing to emit a "not_applicable" decision without notApplicableReason — ` +
|
|
273
|
-
`"did not apply" and "did not run" must never be indistinguishable
|
|
327
|
+
`"did not apply" and "did not run" must never be indistinguishable, and a reason of ` +
|
|
328
|
+
`${describe(run.notApplicableReason)} tells the reader nothing. ` +
|
|
274
329
|
`This is a bug in the command that built the decision.`,
|
|
275
330
|
);
|
|
276
331
|
}
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
{
|
|
26
26
|
"type": "source-evidence",
|
|
27
27
|
"path": "src/commands/context.mjs",
|
|
28
|
-
"assertion": "
|
|
28
|
+
"assertion": "commands/context.mjs imports providers — the designated orchestration path",
|
|
29
29
|
"sha256": "b505e3a7476c64af4b2daeb106e2f9cb05be82e314604229d508dda04b629a98"
|
|
30
30
|
}
|
|
31
31
|
],
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"type": "behavioral-test",
|
|
41
41
|
"path": "src/report/json.test.mjs",
|
|
42
42
|
"assertion": "JSON envelope throws on status=ok + incomplete coverage, status/exitCode disagreement, coverage.complete/notAnalyzed disagreement",
|
|
43
|
-
"sha256": "
|
|
43
|
+
"sha256": "8f587d3bd3f06519b90f760e7eafc091ca4e4cf26b67929ab180abe35c36c8f0"
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
"type": "source-evidence",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"type": "source-evidence",
|
|
53
53
|
"path": "cli.mjs",
|
|
54
54
|
"assertion": "Exit codes 0/1/3 distinguish clean/findings/cannot-look",
|
|
55
|
-
"sha256": "
|
|
55
|
+
"sha256": "2b6590f4039464d3569727d0a73db61dd86c18648a519ee034ebcc2d845ecfc7"
|
|
56
56
|
}
|
|
57
57
|
],
|
|
58
58
|
"status": "proven"
|
|
@@ -66,13 +66,13 @@
|
|
|
66
66
|
"type": "behavioral-test",
|
|
67
67
|
"path": "src/intent/intent.test.mjs",
|
|
68
68
|
"assertion": "analysis output conforms to the frozen contract schema (no extra verdict/policy fields); analysis output is invariant under project tag changes (three workspaces differing only in project tag fields compared byte-for-byte)",
|
|
69
|
-
"sha256": "
|
|
69
|
+
"sha256": "749414363ef9991ae3697fc00ab5969751666867792d4bb4dbe528ec47ccd740"
|
|
70
70
|
},
|
|
71
71
|
{
|
|
72
72
|
"type": "architecture-test",
|
|
73
73
|
"path": "src/intent/intent.test.mjs",
|
|
74
74
|
"assertion": "the gate walks every production analysis module and fails when judging vocabulary (judge/forbid/permit/allow/ban) appears in code",
|
|
75
|
-
"sha256": "
|
|
75
|
+
"sha256": "749414363ef9991ae3697fc00ab5969751666867792d4bb4dbe528ec47ccd740"
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
78
|
"type": "source-evidence",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"type": "source-evidence",
|
|
85
85
|
"path": "AGENTS.md",
|
|
86
86
|
"assertion": "src/graph/ is a lossy view of analysis, on purpose; src/analysis/ never judges",
|
|
87
|
-
"sha256": "
|
|
87
|
+
"sha256": "552b54c1956370f024ffeecec95c0e0941a0c66d4a14094045b444a25a27f913"
|
|
88
88
|
}
|
|
89
89
|
],
|
|
90
90
|
"status": "proven"
|
|
@@ -103,14 +103,20 @@
|
|
|
103
103
|
{
|
|
104
104
|
"type": "source-evidence",
|
|
105
105
|
"path": "src/commands/graph.mjs",
|
|
106
|
-
"assertion": "Plain string comparison, never localeCompare; INTERNAL_DATA_FIELDS stripped
|
|
107
|
-
"sha256": "
|
|
106
|
+
"assertion": "Plain string comparison, never localeCompare; INTERNAL_DATA_FIELDS stripped",
|
|
107
|
+
"sha256": "4f493421e11b012ce7b9c1b111155c8ebdbca6ab4ad3fa37221fbe1a465bef46"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"type": "source-evidence",
|
|
111
|
+
"path": "src/report/json.mjs",
|
|
112
|
+
"assertion": "SCHEMA_VERSION = 2 — the envelope's schemaVersion constant",
|
|
113
|
+
"sha256": "ac6db6e74b4ef7e54db12cc14ef57a7c5080ad081d7bf1273beb5ba543dcac24"
|
|
108
114
|
},
|
|
109
115
|
{
|
|
110
116
|
"type": "source-evidence",
|
|
111
117
|
"path": "src/commands/graph.mjs",
|
|
112
118
|
"assertion": "computePolicyFingerprint produces SHA-256 of canonicalized policy",
|
|
113
|
-
"sha256": "
|
|
119
|
+
"sha256": "4f493421e11b012ce7b9c1b111155c8ebdbca6ab4ad3fa37221fbe1a465bef46"
|
|
114
120
|
}
|
|
115
121
|
],
|
|
116
122
|
"status": "proven"
|
|
@@ -130,7 +136,7 @@
|
|
|
130
136
|
"type": "source-evidence",
|
|
131
137
|
"path": "src/commands/diff.mjs",
|
|
132
138
|
"assertion": "parseBaseline validates schemaVersion; refuses unknown versions",
|
|
133
|
-
"sha256": "
|
|
139
|
+
"sha256": "4d44eea475cd4b06122a405718203d2e0018a163c1d42b17324400e7e4de5eea"
|
|
134
140
|
}
|
|
135
141
|
],
|
|
136
142
|
"status": "proven"
|
|
@@ -150,7 +156,7 @@
|
|
|
150
156
|
"type": "source-evidence",
|
|
151
157
|
"path": "src/commands/diff.mjs",
|
|
152
158
|
"assertion": "computeDiff returns structural diff; policyMismatch detected via fingerprint; computeRuleImpact for depConstraints-only context",
|
|
153
|
-
"sha256": "
|
|
159
|
+
"sha256": "4d44eea475cd4b06122a405718203d2e0018a163c1d42b17324400e7e4de5eea"
|
|
154
160
|
}
|
|
155
161
|
],
|
|
156
162
|
"status": "proven"
|
|
@@ -230,7 +236,7 @@
|
|
|
230
236
|
"type": "behavioral-test",
|
|
231
237
|
"path": "src/intent/intent.test.mjs",
|
|
232
238
|
"assertion": "depConstraints verdicts from judgeEdge agree with evaluate in both directions (violating edge found by both, legal edge reported by neither); explain includes the same violations as evaluate at a given site",
|
|
233
|
-
"sha256": "
|
|
239
|
+
"sha256": "749414363ef9991ae3697fc00ab5969751666867792d4bb4dbe528ec47ccd740"
|
|
234
240
|
},
|
|
235
241
|
{
|
|
236
242
|
"type": "behavioral-test",
|
|
@@ -248,19 +254,19 @@
|
|
|
248
254
|
"type": "source-evidence",
|
|
249
255
|
"path": "src/commands/context-command.mjs",
|
|
250
256
|
"assertion": "coverage.notes warns that per-edge violations cover only depConstraints (3 of 15 violation types)",
|
|
251
|
-
"sha256": "
|
|
257
|
+
"sha256": "2d8a17dce1a5f4075ee02a7a8c1bdff94e70afe5cd609f63fe3cf6324f78aaf3"
|
|
252
258
|
},
|
|
253
259
|
{
|
|
254
260
|
"type": "source-evidence",
|
|
255
261
|
"path": "src/commands/impact.mjs",
|
|
256
262
|
"assertion": "coverage.notes warns that per-edge violations cover only depConstraints (3 of 15 violation types)",
|
|
257
|
-
"sha256": "
|
|
263
|
+
"sha256": "440f20017cceae4b13c6bc5c653c7a8b99511e0c062dcc7abfb18859f3f0520c"
|
|
258
264
|
},
|
|
259
265
|
{
|
|
260
266
|
"type": "source-evidence",
|
|
261
267
|
"path": "src/commands/diff.mjs",
|
|
262
268
|
"assertion": "coverage.notes warns when ruleImpact is computed (depConstraints only, 3 of 15)",
|
|
263
|
-
"sha256": "
|
|
269
|
+
"sha256": "4d44eea475cd4b06122a405718203d2e0018a163c1d42b17324400e7e4de5eea"
|
|
264
270
|
},
|
|
265
271
|
{
|
|
266
272
|
"type": "documentation",
|
|
@@ -286,7 +292,7 @@
|
|
|
286
292
|
"type": "source-evidence",
|
|
287
293
|
"path": "src/commands/graph.mjs",
|
|
288
294
|
"assertion": "Plain string comparison throughout; never localeCompare",
|
|
289
|
-
"sha256": "
|
|
295
|
+
"sha256": "4f493421e11b012ce7b9c1b111155c8ebdbca6ab4ad3fa37221fbe1a465bef46"
|
|
290
296
|
}
|
|
291
297
|
],
|
|
292
298
|
"status": "proven"
|
package/src/lsp/server.mjs
CHANGED
|
@@ -60,12 +60,8 @@ import {
|
|
|
60
60
|
TEXT_DOCUMENT_SYNC_KIND,
|
|
61
61
|
uriToPath,
|
|
62
62
|
} from "./protocol.mjs";
|
|
63
|
-
import {
|
|
64
|
-
|
|
65
|
-
listWorkspaceFiles,
|
|
66
|
-
PROJECT_CONFIG_FILE,
|
|
67
|
-
readWorkspaceFile,
|
|
68
|
-
} from "./workspace-index.mjs";
|
|
63
|
+
import { PROJECT_CONFIG_FILE } from "../providers/native/discover.mjs";
|
|
64
|
+
import { buildWorkspaceIndex, listWorkspaceFiles, readWorkspaceFile } from "./workspace-index.mjs";
|
|
69
65
|
|
|
70
66
|
/**
|
|
71
67
|
* A project's own `package.json`, and the two spellings of its Module
|
|
@@ -232,8 +228,9 @@ const POLYGLOT_GRAPH_MANIFESTS = Object.freeze([
|
|
|
232
228
|
* the annotations cached against the old one. The stale waiver then publishes
|
|
233
229
|
* `[]` for a real violation for the rest of the session, which is the silent
|
|
234
230
|
* direction exactly (`../../../../AGENTS.md`). `package.json` earns the entry a
|
|
235
|
-
* second way as well: it is where
|
|
236
|
-
* takes a project's NAME
|
|
231
|
+
* second way as well: it is where the static acquisition
|
|
232
|
+
* (`../providers/nx-static.mjs`'s `discoverProjects`) takes a project's NAME
|
|
233
|
+
* when its `project.json` states none, so an edit to it
|
|
237
234
|
* can move a project in the graph and not only waive something in it.
|
|
238
235
|
*
|
|
239
236
|
* The polyglot graph manifests are here for that same argument one level out,
|