@am_shork/attest 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +114 -1
- package/README.md +4 -0
- package/dist/cli/index.js +21 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/core/apply.d.ts.map +1 -1
- package/dist/core/apply.js +7 -1
- package/dist/core/apply.js.map +1 -1
- package/dist/core/gate.d.ts +9 -0
- package/dist/core/gate.d.ts.map +1 -1
- package/dist/core/gate.js +25 -12
- package/dist/core/gate.js.map +1 -1
- package/dist/core/loader.d.ts.map +1 -1
- package/dist/core/loader.js +16 -1
- package/dist/core/loader.js.map +1 -1
- package/dist/core/locate.d.ts +29 -2
- package/dist/core/locate.d.ts.map +1 -1
- package/dist/core/locate.js +71 -28
- package/dist/core/locate.js.map +1 -1
- package/dist/core/order.d.ts +3 -0
- package/dist/core/order.d.ts.map +1 -0
- package/dist/core/order.js +13 -0
- package/dist/core/order.js.map +1 -0
- package/dist/core/pipeline.d.ts +9 -0
- package/dist/core/pipeline.d.ts.map +1 -1
- package/dist/core/pipeline.js +64 -24
- package/dist/core/pipeline.js.map +1 -1
- package/dist/core/render.d.ts.map +1 -1
- package/dist/core/render.js +8 -4
- package/dist/core/render.js.map +1 -1
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +16 -4
- package/dist/core/validator.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
### Under consideration (non-breaking, not implemented)
|
|
11
11
|
|
|
12
|
+
- **`attest check` is described as static validation, but it executes every
|
|
13
|
+
`*.reqs.ts` it finds.** Reading a registry means evaluating the module, via
|
|
14
|
+
Vite, anywhere under the project root. That is the same threat model as
|
|
15
|
+
`vitest run` — except `check` is advertised as "static structural validation
|
|
16
|
+
(fast CI pre-check)", which is exactly the wording that gets it scheduled
|
|
17
|
+
ahead of, or outside, whatever sandbox the test run is given. On a fork MR,
|
|
18
|
+
a `*.reqs.ts` with a top-level `fetch` is enough.
|
|
19
|
+
*The candidate fix, and why it is more than a patch:* read the registry from
|
|
20
|
+
the AST instead — the compiler API is already the parser layer's tool — for
|
|
21
|
+
`check`, `cover` and `render`, keeping the loader for `verify` and `archive`,
|
|
22
|
+
which run the suite anyway and so gain nothing from avoiding evaluation.
|
|
23
|
+
This makes "a registry is a literal" a design contract rather than a
|
|
24
|
+
limitation: `params: { maxMb: MAX_MB }` imported from application code
|
|
25
|
+
already violates the single-source rule the framework exists to enforce,
|
|
26
|
+
since the value's real owner is elsewhere. It would also turn `render`'s
|
|
27
|
+
purity from a convention into something structurally guaranteed — today a
|
|
28
|
+
`Date.now()` in a registry makes `--check` fail forever.
|
|
29
|
+
*What blocks it:* a non-literal registry must become an ERROR, never a silent
|
|
30
|
+
fallback to evaluation, or the guarantee is worth nothing — so this is a
|
|
31
|
+
breaking change for repos that compute their registry, and needs an explicit
|
|
32
|
+
opt-out (`--eval`) rather than a warning period, since a warning period
|
|
33
|
+
spends the whole transition still evaluating. The real risk is extractor
|
|
34
|
+
fidelity: `-1` is a unary expression, `1_000` is not `Number(text)`,
|
|
35
|
+
`as const` needs unwrapping, and any of those misread would break a repo the
|
|
36
|
+
change is supposed to leave untouched. Differential testing against the
|
|
37
|
+
current loader over every fixture is the prerequisite.
|
|
12
38
|
- **Two gaps in the intent layer: nothing resists a bloated requirement, and
|
|
13
39
|
nothing resists a duplicated one.** Recorded as a design note, with the
|
|
14
40
|
evidence that currently blocks each candidate fix.
|
|
@@ -92,6 +118,92 @@ relies on — diagnostic message text is not API.
|
|
|
92
118
|
land earlier in 0.1.x):* a pre-run report of how many spec files were located
|
|
93
119
|
and how many contain a `requirement()` call.
|
|
94
120
|
|
|
121
|
+
## [0.1.7] - 2026-07-26
|
|
122
|
+
|
|
123
|
+
A change name, a prototype key and a collation quirk each got to decide a
|
|
124
|
+
verdict; none of them do now.
|
|
125
|
+
|
|
126
|
+
### Security
|
|
127
|
+
|
|
128
|
+
- **`attest archive <change>` validated the change name only by using it.** The
|
|
129
|
+
name was interpolated straight into
|
|
130
|
+
`changes/<name>/requirements.delta.ts`, and that module is *executed* — while
|
|
131
|
+
path joining normalises `..`, so `attest archive '../../../tmp/evil'` loaded
|
|
132
|
+
and ran a module from outside `changes/` entirely. Harmless when you type it
|
|
133
|
+
yourself; not harmless the moment the name comes from a branch name or an MR
|
|
134
|
+
title, which is the natural way to wire the gate into CI. The name is now
|
|
135
|
+
checked **before a loader is even created** — a name already known to be
|
|
136
|
+
invalid must not cause a single resolution attempt — and rejected with a new
|
|
137
|
+
`invalid-change-name` ERROR.
|
|
138
|
+
The test is path safety, not a character whitelist: empty, `.`, `..`, or
|
|
139
|
+
anything containing a path separator or NUL is refused, and every other name a
|
|
140
|
+
directory may legitimately carry is admitted. A whitelist would have cost
|
|
141
|
+
adopters real change directories (`feat(auth)`, a name with a space) to buy no
|
|
142
|
+
additional safety, because the change name never reaches a glob.
|
|
143
|
+
- **A sibling proposal with a glob metacharacter in its name silently widened
|
|
144
|
+
the archive gate.** The globs that keep *other* proposals out of a gate run
|
|
145
|
+
were built by pasting directory names read from disk straight into
|
|
146
|
+
`**/changes/<name>/**`. A change called `feat(auth)` is a *pattern*, so it
|
|
147
|
+
matched nothing, and that proposal's specs joined the run — quietly enlarging
|
|
148
|
+
the scope of the single check that decides whether a change is done. Sibling
|
|
149
|
+
names are now escaped.
|
|
150
|
+
|
|
151
|
+
### Fixed
|
|
152
|
+
|
|
153
|
+
- **A statement placeholder could bind to a prototype method instead of a
|
|
154
|
+
param.** Both the validator and the renderer probed with `name in req.params`,
|
|
155
|
+
and `'toString' in {}` is `true`. So `{toString}` in a statement looked bound:
|
|
156
|
+
the `unbound-param` ERROR that exists to stop an undeclared placeholder never
|
|
157
|
+
fired, and `render` then interpolated
|
|
158
|
+
`function toString() { [native code] }` into the document that reviewers and
|
|
159
|
+
audit read as the system's promise. Both now use `Object.hasOwn`, so the
|
|
160
|
+
intent layer resolves only to what an author actually wrote. An own param
|
|
161
|
+
whose name happens to shadow a prototype key keeps working.
|
|
162
|
+
*Behaviour change:* a registry with such a statement newly fails `check`, and
|
|
163
|
+
its rendering changes (the placeholder is now left verbatim), which
|
|
164
|
+
`render --check` reports as stale. Both are the reports that should always
|
|
165
|
+
have been there.
|
|
166
|
+
- **The canonical form used for content comparison encoded the order params
|
|
167
|
+
happened to be written in.** `applyDelta` sorted param keys with
|
|
168
|
+
`localeCompare` before comparing, and `localeCompare` returns `0` for strings
|
|
169
|
+
that are *distinct* — a precomposed `ä` against its combining-mark spelling.
|
|
170
|
+
`Array#sort` is stable, so keys it called equal kept their insertion order,
|
|
171
|
+
and two requirements with identical content but differently ordered params
|
|
172
|
+
canonicalised differently: `add-conflict` reporting a requirement as
|
|
173
|
+
conflicting with an identical copy of itself. Which strings collate equal is
|
|
174
|
+
ICU-dependent on top of that, so the answer also varied with the Node build.
|
|
175
|
+
Ordering now goes through one shared code-unit comparator (`src/core/order.ts`),
|
|
176
|
+
which `render`'s id ordering — already code-unit for the same reason — now
|
|
177
|
+
shares.
|
|
178
|
+
- **The loader never removed its scratch directory.** Every `attest`
|
|
179
|
+
invocation created one under the OS temp dir to hold the vitest stub, and
|
|
180
|
+
`close()` shut the Vite server without deleting it: one directory per CI
|
|
181
|
+
build, accumulating forever. `close()` now removes it, and is idempotent.
|
|
182
|
+
|
|
183
|
+
### Changed
|
|
184
|
+
|
|
185
|
+
- **`check` walked the project tree twice and `archive` four times**, over a
|
|
186
|
+
tree that cannot change in between. One traversal now collects registry and
|
|
187
|
+
spec files together (`scanProject`). Directory recursion, spec reads, and
|
|
188
|
+
registry module evaluation all run concurrently; results are folded in sorted
|
|
189
|
+
file order, so reports stay byte-identical regardless of I/O timing. The
|
|
190
|
+
drift heuristic indexes scenarios once instead of re-filtering the whole plan
|
|
191
|
+
per requirement, which was the product that grows fastest in exactly the repos
|
|
192
|
+
where `check` needs to stay fast.
|
|
193
|
+
- **`verify` and `archive` now share one `declared-not-run` implementation.**
|
|
194
|
+
Each owned a verbatim copy, so the two verdicts agreed only by transcription —
|
|
195
|
+
in a tool whose entire argument is that agreement should be structural rather
|
|
196
|
+
than clerical.
|
|
197
|
+
- `attest <cmd> [dir]` now resolves `[dir]` to an absolute path. A relative one
|
|
198
|
+
previously worked only because Vite happens to default its own root to the
|
|
199
|
+
current directory.
|
|
200
|
+
- `render --out` writes through a temporary file and renames, so an interrupted
|
|
201
|
+
write can no longer leave a truncated document in a file that is meant to be
|
|
202
|
+
committed.
|
|
203
|
+
|
|
204
|
+
No `schemaVersion` change: the report envelope and the `Issue` shape are
|
|
205
|
+
untouched, and `invalid-change-name` is a new `code` value, which is additive.
|
|
206
|
+
|
|
95
207
|
## [0.1.6] - 2026-07-25
|
|
96
208
|
|
|
97
209
|
A readable spec for the people who cannot read the registry, plus the form
|
|
@@ -326,7 +438,8 @@ Initial release.
|
|
|
326
438
|
(MIT), whose four-stage engine and diff-first change model Attest's
|
|
327
439
|
architecture is adapted from (re-implemented from scratch, no source copied).
|
|
328
440
|
|
|
329
|
-
[Unreleased]: https://gitlab.com/Pseudorca/attest/-/compare/v0.1.
|
|
441
|
+
[Unreleased]: https://gitlab.com/Pseudorca/attest/-/compare/v0.1.7...main
|
|
442
|
+
[0.1.7]: https://gitlab.com/Pseudorca/attest/-/tags/v0.1.7
|
|
330
443
|
[0.1.6]: https://gitlab.com/Pseudorca/attest/-/tags/v0.1.6
|
|
331
444
|
[0.1.5]: https://gitlab.com/Pseudorca/attest/-/tags/v0.1.5
|
|
332
445
|
[0.1.4]: https://gitlab.com/Pseudorca/attest/-/tags/v0.1.4
|
package/README.md
CHANGED
|
@@ -73,6 +73,10 @@ attest render # the requirements as Markdown, for people who don't read TS
|
|
|
73
73
|
attest archive <change> # gate a proposed change: green + covered + no drift
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
`<change>` names one directory inside `changes/` — it cannot be empty, `.`,
|
|
77
|
+
`..`, or contain a path separator, since the gate loads and runs that change's
|
|
78
|
+
delta. Any other directory name is fine.
|
|
79
|
+
|
|
76
80
|
### A readable spec for humans (`attest render`)
|
|
77
81
|
|
|
78
82
|
The intent layer lives in TypeScript, which means a reviewer, a QA engineer, or
|
package/dist/cli/index.js
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
// where the report is an `internal-error` envelope instead of a bare stack.
|
|
10
10
|
import { Command } from 'commander';
|
|
11
11
|
import chalk from 'chalk';
|
|
12
|
-
import { writeFile } from 'node:fs/promises';
|
|
13
|
-
import { resolve } from 'node:path';
|
|
12
|
+
import { rename, writeFile } from 'node:fs/promises';
|
|
13
|
+
import { dirname, join, resolve } from 'node:path';
|
|
14
14
|
import { runCheck, runVerify, runCover, runArchive, runRender, runRenderCheck, } from '../core/pipeline.js';
|
|
15
15
|
import { hasError } from '../core/types.js';
|
|
16
16
|
import { formatIssues, summarize, formatCoverage } from './report.js';
|
|
@@ -22,8 +22,11 @@ program
|
|
|
22
22
|
.name('attest')
|
|
23
23
|
.description('TDD-native spec framework: tests attest that requirements hold.')
|
|
24
24
|
.version(VERSION);
|
|
25
|
+
// Always absolute: the project root is joined into module paths handed to the
|
|
26
|
+
// Vite loader and to Vitest's `root`, and a relative one only resolved
|
|
27
|
+
// correctly because Vite happens to default its own root to cwd.
|
|
25
28
|
function root(dir) {
|
|
26
|
-
return dir ? dir : process.cwd();
|
|
29
|
+
return dir ? resolve(process.cwd(), dir) : process.cwd();
|
|
27
30
|
}
|
|
28
31
|
const JSON_FLAG = '--json';
|
|
29
32
|
const JSON_HELP = 'emit one machine-readable JSON report on stdout';
|
|
@@ -33,6 +36,20 @@ const VITEST_CONFIG_HELP = 'load this Vitest config in the child run (transforms
|
|
|
33
36
|
function vitestConfig(opts) {
|
|
34
37
|
return opts.vitestConfig ? resolve(process.cwd(), opts.vitestConfig) : undefined;
|
|
35
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Write via a temporary file in the same directory, then rename.
|
|
41
|
+
*
|
|
42
|
+
* The rendering is a *committed* file guarded by `--check`. A plain write that
|
|
43
|
+
* is interrupted leaves a truncated document on disk — which `--check` would
|
|
44
|
+
* then correctly call stale, but only after someone commits the wreckage.
|
|
45
|
+
* `rename` within a directory is atomic, so the file is either the old one or
|
|
46
|
+
* the new one, never half of either.
|
|
47
|
+
*/
|
|
48
|
+
async function writeAtomic(dest, content) {
|
|
49
|
+
const tmp = join(dirname(dest), `.${VERSION}.${process.pid}.attest.tmp`);
|
|
50
|
+
await writeFile(tmp, content, 'utf8');
|
|
51
|
+
await rename(tmp, dest);
|
|
52
|
+
}
|
|
36
53
|
/**
|
|
37
54
|
* Run one command's work with a single output + exit-code contract:
|
|
38
55
|
* - success: print the JSON report (`--json`) or the human rendering, and set
|
|
@@ -146,7 +163,7 @@ program
|
|
|
146
163
|
const { markdown, issues } = await runRender(root(dir));
|
|
147
164
|
// Never overwrite a good document with the output of a broken registry.
|
|
148
165
|
if (!hasError(issues) && dest)
|
|
149
|
-
await
|
|
166
|
+
await writeAtomic(dest, markdown);
|
|
150
167
|
return {
|
|
151
168
|
report: renderReport(VERSION, issues, opts.out),
|
|
152
169
|
human: () => {
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,+EAA+E;AAC/E,sCAAsC;AACtC,EAAE;AACF,2EAA2E;AAC3E,4EAA4E;AAC5E,+EAA+E;AAC/E,2EAA2E;AAC3E,4EAA4E;AAE5E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,+EAA+E;AAC/E,sCAAsC;AACtC,EAAE;AACF,2EAA2E;AAC3E,4EAA4E;AAC5E,+EAA+E;AAC/E,2EAA2E;AAC3E,4EAA4E;AAE5E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EACL,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,UAAU,EACV,SAAS,EACT,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,EACL,aAAa,EACb,WAAW,EACX,WAAW,EACX,WAAW,EACX,UAAU,EACV,YAAY,EACZ,YAAY,GAGb,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;AAEjC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,iEAAiE,CAAC;KAC9E,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,8EAA8E;AAC9E,uEAAuE;AACvE,iEAAiE;AACjE,SAAS,IAAI,CAAC,GAAY;IACxB,OAAO,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;AAC3D,CAAC;AAiBD,MAAM,SAAS,GAAG,QAAQ,CAAC;AAC3B,MAAM,SAAS,GAAG,iDAAiD,CAAC;AAEpE,MAAM,kBAAkB,GAAG,wBAAwB,CAAC;AACpD,MAAM,kBAAkB,GACtB,gGAAgG,CAAC;AAEnG,uFAAuF;AACvF,SAAS,YAAY,CAAC,IAAuB;IAC3C,OAAO,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACnF,CAAC;AAED;;;;;;;;GAQG;AACH,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,OAAe;IACtD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,OAAO,IAAI,OAAO,CAAC,GAAG,aAAa,CAAC,CAAC;IACzE,MAAM,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACtC,MAAM,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC1B,CAAC;AAQD;;;;;;GAMG;AACH,KAAK,UAAU,SAAS,CACtB,OAAoB,EACpB,IAAoB,EACpB,MAA+B;IAE/B,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,EAAE,CAAC;QACzC,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;;YAC1C,KAAK,EAAE,CAAC;QACb,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;QAC9D,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,GAAG,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CACzE,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC;AAED,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,mDAAmD,CAAC;KAChE,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,CAAC;KAC5C,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC;KAC5B,MAAM,CAAC,CAAC,GAAuB,EAAE,IAAoB,EAAE,EAAE,CACxD,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE;IAClC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACzC,OAAO;QACL,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC;QACpC,KAAK,EAAE,GAAG,EAAE;YACV,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QACjC,CAAC;KACF,CAAC;AACJ,CAAC,CAAC,CACH,CAAC;AAEJ,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,qDAAqD,CAAC;KAClE,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,CAAC;KAC5C,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC;KAC5B,MAAM,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;KAC9C,MAAM,CAAC,CAAC,GAAuB,EAAE,IAAuB,EAAE,EAAE,CAC3D,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE;IACnC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,YAAY,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChG,OAAO;QACL,MAAM,EAAE,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;QACjD,KAAK,EAAE,GAAG,EAAE;YACV,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QACjC,CAAC;KACF,CAAC;AACJ,CAAC,CAAC,CACH,CAAC;AAEJ,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,sDAAsD,CAAC;KACnE,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,CAAC;KAC5C,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC;KAC5B,MAAM,CAAC,CAAC,GAAuB,EAAE,IAAoB,EAAE,EAAE,CACxD,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE;IAClC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IACxD,OAAO;QACL,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC;QAClC,KAAK,EAAE,GAAG,EAAE;YACV,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;YAClC,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,MAAM,oBAAoB,SAAS,YAAY,CAAC,CACrE,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC,CAAC,CACH,CAAC;AAEJ,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,mEAAmE,CAAC;KAChF,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,CAAC;KAC5C,MAAM,CAAC,cAAc,EAAE,mDAAmD,CAAC;KAC3E,MAAM,CAAC,SAAS,EAAE,4DAA4D,CAAC;KAC/E,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC;KAC5B,MAAM,CAAC,CAAC,GAAuB,EAAE,IAA0B,EAAE,GAAY,EAAE,EAAE;IAC5E,2EAA2E;IAC3E,wEAAwE;IACxE,uEAAuE;IACvE,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG;QAAE,GAAG,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC/E,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QAC1C,GAAG,CAAC,KAAK,CAAC,4FAA4F,CAAC,CAAC;IAC1G,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAErE,OAAO,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE;QAC1C,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,uEAAuE;YACvE,2DAA2D;YAC3D,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAK,EAAE;gBACpD,OAAO,EAAE,IAAI,CAAC,GAAI;gBAClB,OAAO,EAAE,gBAAgB,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,IAAI,CAAC,GAAI,EAAE;aACnE,CAAC,CAAC;YACH,OAAO;gBACL,MAAM,EAAE,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC;gBAC/C,KAAK,EAAE,GAAG,EAAE;oBACV,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;wBAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC;yBAC7E,CAAC;wBACJ,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;wBAClC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;oBACjC,CAAC;gBACH,CAAC;aACF,CAAC;QACJ,CAAC;QAED,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACxD,wEAAwE;QACxE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI;YAAE,MAAM,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACjE,OAAO;YACL,MAAM,EAAE,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC;YAC/C,KAAK,EAAE,GAAG,EAAE;gBACV,IAAI,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;oBACrB,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;oBAClC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;gBACjC,CAAC;qBAAM,IAAI,IAAI,EAAE,CAAC;oBAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;gBAClD,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBACjC,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,SAAS,CAAC;KAClB,QAAQ,CAAC,UAAU,EAAE,wBAAwB,CAAC;KAC9C,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,CAAC;KAC5C,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC;KAC5B,MAAM,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;KAC9C,WAAW,CAAC,gDAAgD,CAAC;KAC7D,MAAM,CAAC,CAAC,MAAc,EAAE,GAAuB,EAAE,IAAuB,EAAE,EAAE,CAC3E,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE;IACpC,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,EAAE,YAAY,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3F,OAAO;QACL,MAAM,EAAE,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC;QAChD,KAAK,EAAE,GAAG,EAAE;YACV,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1B,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,KAAK,CAAC,0BAA0B,MAAM,oBAAoB,CAAC,CAClE,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,0BAA0B,MAAM,GAAG,CAAC,CAAC,CAAC;gBAC5D,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACpC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC,CAAC,CACH,CAAC;AAEJ,OAAO,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC1C,8EAA8E;IAC9E,qDAAqD;IACrD,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACxF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
|
package/dist/core/apply.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/core/apply.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/core/apply.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAe,MAAM,YAAY,CAAC;AAE/D,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,aAAa,GAAG,WAAW,CAiExE"}
|
package/dist/core/apply.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// Map<id, Requirement>, with content-compare on ADDED and already-synced
|
|
4
4
|
// no-ops on RENAMED.
|
|
5
5
|
import { RequirementSchema } from './schema.js';
|
|
6
|
+
import { byCodeUnit } from './order.js';
|
|
6
7
|
/**
|
|
7
8
|
* Apply a change delta to a base registry, returning the merged registry plus
|
|
8
9
|
* any apply-level issues (conflicts, missing targets, invalid results). The
|
|
@@ -88,7 +89,12 @@ function canonical(req) {
|
|
|
88
89
|
outOfScope: [...req.outOfScope],
|
|
89
90
|
});
|
|
90
91
|
}
|
|
92
|
+
// Code-unit order, not localeCompare: this string is a *verdict input*.
|
|
93
|
+
// localeCompare calls some distinct keys equal, and a stable sort then leaves
|
|
94
|
+
// them in insertion order — so the canonical form would encode how the params
|
|
95
|
+
// happened to be written, and `add-conflict` would report a requirement as
|
|
96
|
+
// conflicting with an identical copy of itself.
|
|
91
97
|
function sortKeys(obj) {
|
|
92
|
-
return Object.fromEntries(Object.entries(obj).sort(([a], [b]) => a
|
|
98
|
+
return Object.fromEntries(Object.entries(obj).sort(([a], [b]) => byCodeUnit(a, b)));
|
|
93
99
|
}
|
|
94
100
|
//# sourceMappingURL=apply.js.map
|
package/dist/core/apply.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/core/apply.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,yEAAyE;AACzE,yEAAyE;AACzE,qBAAqB;AAErB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/core/apply.ts"],"names":[],"mappings":"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"}
|
package/dist/core/gate.d.ts
CHANGED
|
@@ -18,5 +18,14 @@ export interface GateInputs {
|
|
|
18
18
|
* 3. Static coverage vs runtime coverage: every declared scenario actually
|
|
19
19
|
* ran (catches skip/only false coverage).
|
|
20
20
|
*/
|
|
21
|
+
/**
|
|
22
|
+
* Declared-not-run: a scenario the static plan claims attests a requirement,
|
|
23
|
+
* which the runtime never executed (skipped, or excluded by an `.only`).
|
|
24
|
+
*
|
|
25
|
+
* Exported because `verify` and `archive` both make this call. While each owned
|
|
26
|
+
* a copy, the two verdicts agreed only by transcription — and the whole point of
|
|
27
|
+
* this framework is that agreement should be structural, not clerical.
|
|
28
|
+
*/
|
|
29
|
+
export declare function declaredNotRunIssues(plan: AttestPlan, run: RunResult): Issue[];
|
|
21
30
|
export declare function evaluateGate({ registry, plan, run }: GateInputs): Issue[];
|
|
22
31
|
//# sourceMappingURL=gate.d.ts.map
|
package/dist/core/gate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/core/gate.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEzE,MAAM,WAAW,UAAU;IACzB,yEAAyE;IACzE,QAAQ,EAAE,QAAQ,CAAC;IACnB,8CAA8C;IAC9C,IAAI,EAAE,UAAU,CAAC;IACjB,oDAAoD;IACpD,GAAG,EAAE,SAAS,CAAC;CAChB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,UAAU,GAAG,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/core/gate.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEzE,MAAM,WAAW,UAAU;IACzB,yEAAyE;IACzE,QAAQ,EAAE,QAAQ,CAAC;IACnB,8CAA8C;IAC9C,IAAI,EAAE,UAAU,CAAC;IACjB,oDAAoD;IACpD,GAAG,EAAE,SAAS,CAAC;CAChB;AAED;;;;;;;;;;GAUG;AACH;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,SAAS,GAAG,KAAK,EAAE,CAe9E;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,UAAU,GAAG,KAAK,EAAE,CAqBzE"}
|
package/dist/core/gate.js
CHANGED
|
@@ -15,6 +15,30 @@ import { validateStructure } from './validator.js';
|
|
|
15
15
|
* 3. Static coverage vs runtime coverage: every declared scenario actually
|
|
16
16
|
* ran (catches skip/only false coverage).
|
|
17
17
|
*/
|
|
18
|
+
/**
|
|
19
|
+
* Declared-not-run: a scenario the static plan claims attests a requirement,
|
|
20
|
+
* which the runtime never executed (skipped, or excluded by an `.only`).
|
|
21
|
+
*
|
|
22
|
+
* Exported because `verify` and `archive` both make this call. While each owned
|
|
23
|
+
* a copy, the two verdicts agreed only by transcription — and the whole point of
|
|
24
|
+
* this framework is that agreement should be structural, not clerical.
|
|
25
|
+
*/
|
|
26
|
+
export function declaredNotRunIssues(plan, run) {
|
|
27
|
+
const issues = [];
|
|
28
|
+
for (const s of plan.scenarios) {
|
|
29
|
+
if (!run.runtimeCoverage.get(s.reqId)?.has(s.name)) {
|
|
30
|
+
issues.push({
|
|
31
|
+
level: 'ERROR',
|
|
32
|
+
code: 'declared-not-run',
|
|
33
|
+
reqId: s.reqId,
|
|
34
|
+
file: s.file,
|
|
35
|
+
line: s.line,
|
|
36
|
+
message: `scenario "${s.name}" statically declares it attests ${s.reqId}, but never executed (skipped, or excluded by an .only?).`,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return issues;
|
|
41
|
+
}
|
|
18
42
|
export function evaluateGate({ registry, plan, run }) {
|
|
19
43
|
const blocking = [];
|
|
20
44
|
// 1) Structure (end-state re-validation).
|
|
@@ -28,18 +52,7 @@ export function evaluateGate({ registry, plan, run }) {
|
|
|
28
52
|
});
|
|
29
53
|
}
|
|
30
54
|
// 3) Declared-not-run: static coverage claimed, runtime never executed it.
|
|
31
|
-
|
|
32
|
-
if (!run.runtimeCoverage.get(s.reqId)?.has(s.name)) {
|
|
33
|
-
blocking.push({
|
|
34
|
-
level: 'ERROR',
|
|
35
|
-
code: 'declared-not-run',
|
|
36
|
-
reqId: s.reqId,
|
|
37
|
-
file: s.file,
|
|
38
|
-
line: s.line,
|
|
39
|
-
message: `scenario "${s.name}" statically declares it attests ${s.reqId}, but never executed (skipped, or excluded by an .only?).`,
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
}
|
|
55
|
+
blocking.push(...declaredNotRunIssues(plan, run));
|
|
43
56
|
return blocking;
|
|
44
57
|
}
|
|
45
58
|
//# sourceMappingURL=gate.js.map
|
package/dist/core/gate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/core/gate.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,iFAAiF;AACjF,4EAA4E;AAC5E,6EAA6E;AAC7E,wDAAwD;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAYnD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/core/gate.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,iFAAiF;AACjF,4EAA4E;AAC5E,6EAA6E;AAC7E,wDAAwD;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAYnD;;;;;;;;;;GAUG;AACH;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAgB,EAAE,GAAc;IACnE,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QAC/B,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACnD,MAAM,CAAC,IAAI,CAAC;gBACV,KAAK,EAAE,OAAO;gBACd,IAAI,EAAE,kBAAkB;gBACxB,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,OAAO,EAAE,aAAa,CAAC,CAAC,IAAI,oCAAoC,CAAC,CAAC,KAAK,2DAA2D;aACnI,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAc;IAC9D,MAAM,QAAQ,GAAY,EAAE,CAAC;IAE7B,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,CACX,GAAG,iBAAiB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,OAAO,CAAC,CACxE,CAAC;IAEF,4BAA4B;IAC5B,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;QAChB,QAAQ,CAAC,IAAI,CAAC;YACZ,KAAK,EAAE,OAAO;YACd,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,wDAAwD;SAClE,CAAC,CAAC;IACL,CAAC;IAED,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,CAAC,GAAG,oBAAoB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IAElD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/core/loader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/core/loader.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAmCpD"}
|
package/dist/core/loader.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// throws. The stub sidesteps that without affecting any read value.
|
|
9
9
|
import { createServer } from 'vite';
|
|
10
10
|
import { mkdtempSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import { rm } from 'node:fs/promises';
|
|
11
12
|
import { tmpdir } from 'node:os';
|
|
12
13
|
import { join } from 'node:path';
|
|
13
14
|
const VITEST_STUB = 'export const describe=()=>{};export const it=()=>{};export const test=()=>{};' +
|
|
@@ -27,12 +28,26 @@ export async function createLoader() {
|
|
|
27
28
|
ssr: { noExternal: ['vitest'] },
|
|
28
29
|
optimizeDeps: { noDiscovery: true },
|
|
29
30
|
});
|
|
31
|
+
let closed = false;
|
|
30
32
|
return {
|
|
31
33
|
async load(absPath) {
|
|
32
34
|
return (await server.ssrLoadModule(absPath));
|
|
33
35
|
},
|
|
36
|
+
// The scratch directory is the loader's, so the loader disposes of it.
|
|
37
|
+
// Leaving it behind meant one stray directory per `attest` invocation —
|
|
38
|
+
// i.e. per CI build — accumulating in the OS temp dir forever. `close` is
|
|
39
|
+
// idempotent because every pipeline calls it from a `finally`, which can
|
|
40
|
+
// run after an earlier explicit close on some paths.
|
|
34
41
|
async close() {
|
|
35
|
-
|
|
42
|
+
if (closed)
|
|
43
|
+
return;
|
|
44
|
+
closed = true;
|
|
45
|
+
try {
|
|
46
|
+
await server.close();
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
await rm(dir, { recursive: true, force: true });
|
|
50
|
+
}
|
|
36
51
|
},
|
|
37
52
|
};
|
|
38
53
|
}
|
package/dist/core/loader.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/core/loader.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,2EAA2E;AAC3E,6EAA6E;AAC7E,EAAE;AACF,sEAAsE;AACtE,yEAAyE;AACzE,6EAA6E;AAC7E,oEAAoE;AAEpE,OAAO,EAAE,YAAY,EAAsB,MAAM,MAAM,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,WAAW,GACf,+EAA+E;IAC/E,wFAAwF;IACxF,kFAAkF;IAClF,oBAAoB,CAAC;AAOvB,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;IAC1C,aAAa,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAEjC,MAAM,MAAM,GAAkB,MAAM,YAAY,CAAC;QAC/C,UAAU,EAAE,KAAK;QACjB,QAAQ,EAAE,OAAO;QACjB,OAAO,EAAE,QAAQ;QACjB,MAAM,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE;QAChC,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;QACpC,GAAG,EAAE,EAAE,UAAU,EAAE,CAAC,QAAQ,CAAC,EAAE;QAC/B,YAAY,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE;KACpC,CAAC,CAAC;IAEH,OAAO;QACL,KAAK,CAAC,IAAI,CAAI,OAAe;YAC3B,OAAO,CAAC,MAAM,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAM,CAAC;QACpD,CAAC;QACD,KAAK,CAAC,KAAK;YACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/core/loader.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,2EAA2E;AAC3E,6EAA6E;AAC7E,EAAE;AACF,sEAAsE;AACtE,yEAAyE;AACzE,6EAA6E;AAC7E,oEAAoE;AAEpE,OAAO,EAAE,YAAY,EAAsB,MAAM,MAAM,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,WAAW,GACf,+EAA+E;IAC/E,wFAAwF;IACxF,kFAAkF;IAClF,oBAAoB,CAAC;AAOvB,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;IAC1C,aAAa,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAEjC,MAAM,MAAM,GAAkB,MAAM,YAAY,CAAC;QAC/C,UAAU,EAAE,KAAK;QACjB,QAAQ,EAAE,OAAO;QACjB,OAAO,EAAE,QAAQ;QACjB,MAAM,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE;QAChC,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;QACpC,GAAG,EAAE,EAAE,UAAU,EAAE,CAAC,QAAQ,CAAC,EAAE;QAC/B,YAAY,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE;KACpC,CAAC,CAAC;IAEH,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,OAAO;QACL,KAAK,CAAC,IAAI,CAAI,OAAe;YAC3B,OAAO,CAAC,MAAM,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAM,CAAC;QACpD,CAAC;QACD,uEAAuE;QACvE,wEAAwE;QACxE,0EAA0E;QAC1E,yEAAyE;QACzE,qDAAqD;QACrD,KAAK,CAAC,KAAK;YACT,IAAI,MAAM;gBAAE,OAAO;YACnB,MAAM,GAAG,IAAI,CAAC;YACd,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACvB,CAAC;oBAAS,CAAC;gBACT,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/core/locate.d.ts
CHANGED
|
@@ -2,17 +2,44 @@ import type { Loader } from './loader.js';
|
|
|
2
2
|
import type { AttestPlan, Issue, Registry } from './types.js';
|
|
3
3
|
export declare const isReqsFile: (name: string) => boolean;
|
|
4
4
|
export declare const isSpecFile: (name: string) => boolean;
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Recursively find files under root whose basename matches `match`.
|
|
7
|
+
*
|
|
8
|
+
* Sibling directories are walked concurrently — the tree is I/O bound, and a
|
|
9
|
+
* serial `for await` spent one filesystem round-trip per directory, which is
|
|
10
|
+
* the dominant cost of `check` on a large repo. The result is sorted, so the
|
|
11
|
+
* order never depends on which `readdir` happened to resolve first.
|
|
12
|
+
*/
|
|
6
13
|
export declare function findFiles(root: string, match: (name: string) => boolean): Promise<string[]>;
|
|
14
|
+
/** The two file sets every command needs, collected in one pass. */
|
|
15
|
+
export interface ProjectScan {
|
|
16
|
+
reqsFiles: string[];
|
|
17
|
+
specFiles: string[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Find the registry and spec files under root in a **single** traversal.
|
|
21
|
+
*
|
|
22
|
+
* Calling findFiles once per pattern meant `check` walked the tree twice and
|
|
23
|
+
* `archive` four times, over a tree that cannot change in between.
|
|
24
|
+
*/
|
|
25
|
+
export declare function scanProject(root: string): Promise<ProjectScan>;
|
|
7
26
|
/**
|
|
8
27
|
* Load and merge every `*.reqs.ts` registry under root. A malformed registry
|
|
9
28
|
* (defineRequirements throws) becomes a registry-invalid ERROR; a duplicate id
|
|
10
29
|
* across files becomes a duplicate-requirement ERROR.
|
|
30
|
+
*
|
|
31
|
+
* Pass `files` to reuse a {@link scanProject} result instead of re-walking.
|
|
11
32
|
*/
|
|
12
|
-
export declare function loadRegistry(root: string, loader: Loader): Promise<{
|
|
33
|
+
export declare function loadRegistry(root: string, loader: Loader, files?: string[]): Promise<{
|
|
13
34
|
registry: Registry;
|
|
14
35
|
issues: Issue[];
|
|
15
36
|
}>;
|
|
37
|
+
/**
|
|
38
|
+
* Parse the given spec files into one merged plan (paths shown relative to
|
|
39
|
+
* `displayRoot`). Files are read concurrently; the merge follows the input
|
|
40
|
+
* order, so the plan never depends on I/O timing.
|
|
41
|
+
*/
|
|
42
|
+
export declare function parseSpecs(files: string[], displayRoot: string): Promise<AttestPlan>;
|
|
16
43
|
/** Parse every `*.spec.ts` under root into one merged plan (file paths shown relative to root). */
|
|
17
44
|
export declare function parseAllSpecFiles(root: string): Promise<AttestPlan>;
|
|
18
45
|
/** Parse the spec files belonging to a single proposed change (design §8). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locate.d.ts","sourceRoot":"","sources":["../../src/core/locate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"locate.d.ts","sourceRoot":"","sources":["../../src/core/locate.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAe,MAAM,YAAY,CAAC;AAc3E,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,OAAoC,CAAC;AAC/E,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,OAAoC,CAAC;AAE/E;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAC/B,OAAO,CAAC,MAAM,EAAE,CAAC,CAiBnB;AAED,oEAAoE;AACpE,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CASpE;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,MAAM,EAAE,GACf,OAAO,CAAC;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,KAAK,EAAE,CAAA;CAAE,CAAC,CAyDlD;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EAAE,EACf,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,UAAU,CAAC,CASrB;AAED,mGAAmG;AACnG,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAEzE;AAED,8EAA8E;AAC9E,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,CAAC,CAGrB;AAED,+DAA+D;AAC/D,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAOrE"}
|
package/dist/core/locate.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// Locate layer (design §4, L1): find registry + spec files, load the registry
|
|
2
2
|
// via the TS loader, and parse all spec files into one merged AttestPlan.
|
|
3
3
|
import { readdir, readFile } from 'node:fs/promises';
|
|
4
|
-
import { join, relative } from 'node:path';
|
|
4
|
+
import { basename, join, relative } from 'node:path';
|
|
5
5
|
import { parseSpecFile } from './parser.js';
|
|
6
|
+
import { byCodeUnit } from './order.js';
|
|
6
7
|
// Proposed changes and archived changes are excluded from normal scanning:
|
|
7
8
|
// a change's requirements/specs only count once its gate passes and it is
|
|
8
9
|
// merged (design §7, §8). `attest archive <name>` includes them explicitly.
|
|
@@ -16,41 +17,78 @@ const SKIP_DIRS = new Set([
|
|
|
16
17
|
]);
|
|
17
18
|
export const isReqsFile = (name) => name.endsWith('.reqs.ts');
|
|
18
19
|
export const isSpecFile = (name) => name.endsWith('.spec.ts');
|
|
19
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Recursively find files under root whose basename matches `match`.
|
|
22
|
+
*
|
|
23
|
+
* Sibling directories are walked concurrently — the tree is I/O bound, and a
|
|
24
|
+
* serial `for await` spent one filesystem round-trip per directory, which is
|
|
25
|
+
* the dominant cost of `check` on a large repo. The result is sorted, so the
|
|
26
|
+
* order never depends on which `readdir` happened to resolve first.
|
|
27
|
+
*/
|
|
20
28
|
export async function findFiles(root, match) {
|
|
21
29
|
const out = [];
|
|
22
30
|
const walk = async (dir) => {
|
|
23
31
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
32
|
+
const subdirs = [];
|
|
24
33
|
for (const e of entries) {
|
|
25
34
|
const full = join(dir, e.name);
|
|
26
35
|
if (e.isDirectory()) {
|
|
27
36
|
if (!SKIP_DIRS.has(e.name))
|
|
28
|
-
|
|
37
|
+
subdirs.push(full);
|
|
29
38
|
}
|
|
30
39
|
else if (e.isFile() && match(e.name)) {
|
|
31
40
|
out.push(full);
|
|
32
41
|
}
|
|
33
42
|
}
|
|
43
|
+
await Promise.all(subdirs.map(walk));
|
|
34
44
|
};
|
|
35
45
|
await walk(root);
|
|
36
|
-
return out.sort();
|
|
46
|
+
return out.sort(byCodeUnit);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Find the registry and spec files under root in a **single** traversal.
|
|
50
|
+
*
|
|
51
|
+
* Calling findFiles once per pattern meant `check` walked the tree twice and
|
|
52
|
+
* `archive` four times, over a tree that cannot change in between.
|
|
53
|
+
*/
|
|
54
|
+
export async function scanProject(root) {
|
|
55
|
+
const files = await findFiles(root, (n) => isReqsFile(n) || isSpecFile(n));
|
|
56
|
+
const reqsFiles = [];
|
|
57
|
+
const specFiles = [];
|
|
58
|
+
for (const f of files) {
|
|
59
|
+
if (isReqsFile(basename(f)))
|
|
60
|
+
reqsFiles.push(f);
|
|
61
|
+
else
|
|
62
|
+
specFiles.push(f);
|
|
63
|
+
}
|
|
64
|
+
return { reqsFiles, specFiles };
|
|
37
65
|
}
|
|
38
66
|
/**
|
|
39
67
|
* Load and merge every `*.reqs.ts` registry under root. A malformed registry
|
|
40
68
|
* (defineRequirements throws) becomes a registry-invalid ERROR; a duplicate id
|
|
41
69
|
* across files becomes a duplicate-requirement ERROR.
|
|
70
|
+
*
|
|
71
|
+
* Pass `files` to reuse a {@link scanProject} result instead of re-walking.
|
|
42
72
|
*/
|
|
43
|
-
export async function loadRegistry(root, loader) {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
let mod;
|
|
73
|
+
export async function loadRegistry(root, loader, files) {
|
|
74
|
+
const paths = files ?? (await scanProject(root)).reqsFiles;
|
|
75
|
+
// Evaluate the modules concurrently, then fold the results in sorted file
|
|
76
|
+
// order: the issue list stays deterministic regardless of which module
|
|
77
|
+
// finished first.
|
|
78
|
+
const loaded = await Promise.all(paths.map(async (file) => {
|
|
50
79
|
try {
|
|
51
|
-
mod
|
|
80
|
+
return { file, mod: await loader.load(file) };
|
|
52
81
|
}
|
|
53
82
|
catch (err) {
|
|
83
|
+
return { file, err };
|
|
84
|
+
}
|
|
85
|
+
}));
|
|
86
|
+
const registry = {};
|
|
87
|
+
const issues = [];
|
|
88
|
+
for (const entry of loaded) {
|
|
89
|
+
const display = relative(root, entry.file);
|
|
90
|
+
if ('err' in entry) {
|
|
91
|
+
const { err } = entry;
|
|
54
92
|
issues.push({
|
|
55
93
|
level: 'ERROR',
|
|
56
94
|
code: 'registry-invalid',
|
|
@@ -59,7 +97,7 @@ export async function loadRegistry(root, loader) {
|
|
|
59
97
|
});
|
|
60
98
|
continue;
|
|
61
99
|
}
|
|
62
|
-
const reg = mod.default;
|
|
100
|
+
const reg = entry.mod.default;
|
|
63
101
|
if (!reg || typeof reg !== 'object') {
|
|
64
102
|
issues.push({
|
|
65
103
|
level: 'ERROR',
|
|
@@ -70,7 +108,7 @@ export async function loadRegistry(root, loader) {
|
|
|
70
108
|
continue;
|
|
71
109
|
}
|
|
72
110
|
for (const [id, req] of Object.entries(reg)) {
|
|
73
|
-
if (id
|
|
111
|
+
if (Object.hasOwn(registry, id)) {
|
|
74
112
|
issues.push({
|
|
75
113
|
level: 'ERROR',
|
|
76
114
|
code: 'duplicate-requirement',
|
|
@@ -86,33 +124,38 @@ export async function loadRegistry(root, loader) {
|
|
|
86
124
|
}
|
|
87
125
|
return { registry, issues };
|
|
88
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Parse the given spec files into one merged plan (paths shown relative to
|
|
129
|
+
* `displayRoot`). Files are read concurrently; the merge follows the input
|
|
130
|
+
* order, so the plan never depends on I/O timing.
|
|
131
|
+
*/
|
|
132
|
+
export async function parseSpecs(files, displayRoot) {
|
|
133
|
+
const sources = await Promise.all(files.map((file) => readFile(file, 'utf8')));
|
|
134
|
+
const plan = { scenarios: [], paramRefs: [] };
|
|
135
|
+
for (const [i, file] of files.entries()) {
|
|
136
|
+
const parsed = parseSpecFile(relative(displayRoot, file), sources[i]);
|
|
137
|
+
plan.scenarios.push(...parsed.scenarios);
|
|
138
|
+
plan.paramRefs.push(...parsed.paramRefs);
|
|
139
|
+
}
|
|
140
|
+
return plan;
|
|
141
|
+
}
|
|
89
142
|
/** Parse every `*.spec.ts` under root into one merged plan (file paths shown relative to root). */
|
|
90
143
|
export async function parseAllSpecFiles(root) {
|
|
91
|
-
return
|
|
144
|
+
return parseSpecs(await findFiles(root, isSpecFile), root);
|
|
92
145
|
}
|
|
93
146
|
/** Parse the spec files belonging to a single proposed change (design §8). */
|
|
94
147
|
export async function parseChangeSpecs(root, changeName) {
|
|
95
|
-
|
|
148
|
+
const dir = join(root, 'changes', changeName);
|
|
149
|
+
return parseSpecs(await findFiles(dir, isSpecFile), root);
|
|
96
150
|
}
|
|
97
151
|
/** List the names of proposed changes under `root/changes`. */
|
|
98
152
|
export async function listChangeNames(root) {
|
|
99
153
|
try {
|
|
100
154
|
const entries = await readdir(join(root, 'changes'), { withFileTypes: true });
|
|
101
|
-
return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
155
|
+
return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort(byCodeUnit);
|
|
102
156
|
}
|
|
103
157
|
catch {
|
|
104
158
|
return [];
|
|
105
159
|
}
|
|
106
160
|
}
|
|
107
|
-
async function parseSpecsUnder(dir, displayRoot) {
|
|
108
|
-
const files = await findFiles(dir, isSpecFile);
|
|
109
|
-
const plan = { scenarios: [], paramRefs: [] };
|
|
110
|
-
for (const file of files) {
|
|
111
|
-
const source = await readFile(file, 'utf8');
|
|
112
|
-
const parsed = parseSpecFile(relative(displayRoot, file), source);
|
|
113
|
-
plan.scenarios.push(...parsed.scenarios);
|
|
114
|
-
plan.paramRefs.push(...parsed.paramRefs);
|
|
115
|
-
}
|
|
116
|
-
return plan;
|
|
117
|
-
}
|
|
118
161
|
//# sourceMappingURL=locate.js.map
|
package/dist/core/locate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locate.js","sourceRoot":"","sources":["../../src/core/locate.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,0EAA0E;AAE1E,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"locate.js","sourceRoot":"","sources":["../../src/core/locate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"order.d.ts","sourceRoot":"","sources":["../../src/core/order.ts"],"names":[],"mappings":"AASA,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAEvD"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Deterministic ordering primitives.
|
|
2
|
+
//
|
|
3
|
+
// Anything Attest writes to disk or compares for equality has to sort on the
|
|
4
|
+
// strings themselves and nothing else. `localeCompare` does not: it returns 0
|
|
5
|
+
// for *distinct* strings (a precomposed "ä" against its combining-mark
|
|
6
|
+
// spelling), and since `Array#sort` is stable, whatever it calls equal keeps
|
|
7
|
+
// its insertion order — so the result encodes how the input happened to be
|
|
8
|
+
// written. Which strings collate equal is ICU-dependent on top of that.
|
|
9
|
+
/** Compare by UTF-16 code unit — the same order as a bare `Array#sort()`. */
|
|
10
|
+
export function byCodeUnit(a, b) {
|
|
11
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=order.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"order.js","sourceRoot":"","sources":["../../src/core/order.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,uEAAuE;AACvE,6EAA6E;AAC7E,2EAA2E;AAC3E,wEAAwE;AAExE,6EAA6E;AAC7E,MAAM,UAAU,UAAU,CAAC,CAAS,EAAE,CAAS;IAC7C,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC"}
|
package/dist/core/pipeline.d.ts
CHANGED
|
@@ -44,6 +44,15 @@ export declare function runRender(root: string): Promise<RenderResult>;
|
|
|
44
44
|
* alongside a gate that fails when the file no longer matches its source.
|
|
45
45
|
*/
|
|
46
46
|
export declare function runRenderCheck(root: string, outFile: string, target: RenderTarget): Promise<Issue[]>;
|
|
47
|
+
/**
|
|
48
|
+
* The globs that keep *other* proposals out of a change's gate run.
|
|
49
|
+
*
|
|
50
|
+
* Sibling names come from `readdir`, not from the guard above, so they are
|
|
51
|
+
* escaped: a directory called `feat(auth)` pasted in raw is a *pattern*, it
|
|
52
|
+
* matches nothing, and that sibling's specs silently join the run — quietly
|
|
53
|
+
* widening the scope of the one check that decides "done".
|
|
54
|
+
*/
|
|
55
|
+
export declare function changeExcludeGlobs(others: string[]): string[];
|
|
47
56
|
/** Archive gate for a change (design §8, §9: `attest archive <change>`). */
|
|
48
57
|
export declare function runArchive(root: string, changeName: string, options?: RunOptions): Promise<Issue[]>;
|
|
49
58
|
/** True when a spec suite exists at all (used by the CLI to warn on empty roots). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/core/pipeline.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhD,OAAO,KAAK,EAAc,KAAK,EAAY,MAAM,YAAY,CAAC;AAE9D,2DAA2D;AAC3D,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/core/pipeline.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhD,OAAO,KAAK,EAAc,KAAK,EAAY,MAAM,YAAY,CAAC;AAE9D,2DAA2D;AAC3D,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAc7D;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,EAAE,EAAE,OAAO,CAAC;CACb;AAED,oEAAoE;AACpE,MAAM,WAAW,UAAU;IACzB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,4DAA4D;AAC5D,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,YAAY,CAAC,CAyB7F;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,wDAAwD;AACxD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAoBnE;AAED,MAAM,WAAW,YAAY;IAC3B,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAWnE;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,KAAK,EAAE,CAAC,CAOlB;AA2BD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAE7D;AAMD,4EAA4E;AAC5E,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,UAAe,GACvB,OAAO,CAAC,KAAK,EAAE,CAAC,CA2DlB;AAED,qFAAqF;AACrF,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE7D"}
|
package/dist/core/pipeline.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// High-level operations composing the core layers (design §9). These are
|
|
2
2
|
// tool-agnostic; the CLI is a thin shell that calls them and renders the result.
|
|
3
3
|
import { createLoader } from './loader.js';
|
|
4
|
-
import { loadRegistry,
|
|
4
|
+
import { loadRegistry, parseSpecs, parseChangeSpecs, listChangeNames, scanProject, } from './locate.js';
|
|
5
5
|
import { validateStructure, detectPotentialDrift } from './validator.js';
|
|
6
6
|
import { runAndCollect } from './runner.js';
|
|
7
7
|
import { applyDelta } from './apply.js';
|
|
8
|
-
import { evaluateGate } from './gate.js';
|
|
8
|
+
import { evaluateGate, declaredNotRunIssues } from './gate.js';
|
|
9
9
|
import { renderMarkdown, staleIssue } from './render.js';
|
|
10
10
|
import { readFile } from 'node:fs/promises';
|
|
11
11
|
import { join, relative } from 'node:path';
|
|
@@ -13,10 +13,11 @@ import { configDefaults } from 'vitest/config';
|
|
|
13
13
|
import { hasError } from './types.js';
|
|
14
14
|
/** Static structural check (design §9: `attest check`). */
|
|
15
15
|
export async function runCheck(root) {
|
|
16
|
+
const scan = await scanProject(root);
|
|
16
17
|
const loader = await createLoader();
|
|
17
18
|
try {
|
|
18
|
-
const { registry, issues } = await loadRegistry(root, loader);
|
|
19
|
-
const plan = await
|
|
19
|
+
const { registry, issues } = await loadRegistry(root, loader, scan.reqsFiles);
|
|
20
|
+
const plan = await parseSpecs(scan.specFiles, root);
|
|
20
21
|
return [
|
|
21
22
|
...issues,
|
|
22
23
|
...validateStructure(registry, plan),
|
|
@@ -29,15 +30,16 @@ export async function runCheck(root) {
|
|
|
29
30
|
}
|
|
30
31
|
/** Executable verification (design §9: `attest verify`). */
|
|
31
32
|
export async function runVerify(root, options = {}) {
|
|
33
|
+
const scan = await scanProject(root);
|
|
32
34
|
const loader = await createLoader();
|
|
33
35
|
let registry;
|
|
34
36
|
let plan;
|
|
35
37
|
const issues = [];
|
|
36
38
|
try {
|
|
37
|
-
const loaded = await loadRegistry(root, loader);
|
|
39
|
+
const loaded = await loadRegistry(root, loader, scan.reqsFiles);
|
|
38
40
|
registry = loaded.registry;
|
|
39
41
|
issues.push(...loaded.issues);
|
|
40
|
-
plan = await
|
|
42
|
+
plan = await parseSpecs(scan.specFiles, root);
|
|
41
43
|
}
|
|
42
44
|
finally {
|
|
43
45
|
await loader.close();
|
|
@@ -48,26 +50,16 @@ export async function runVerify(root, options = {}) {
|
|
|
48
50
|
if (!run.passed) {
|
|
49
51
|
issues.push({ level: 'ERROR', code: 'tests-red', message: 'Some tests are failing.' });
|
|
50
52
|
}
|
|
51
|
-
|
|
52
|
-
if (!run.runtimeCoverage.get(s.reqId)?.has(s.name)) {
|
|
53
|
-
issues.push({
|
|
54
|
-
level: 'ERROR',
|
|
55
|
-
code: 'declared-not-run',
|
|
56
|
-
reqId: s.reqId,
|
|
57
|
-
file: s.file,
|
|
58
|
-
line: s.line,
|
|
59
|
-
message: `scenario "${s.name}" statically declares it attests ${s.reqId}, but never executed (skipped, or excluded by an .only?).`,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
}
|
|
53
|
+
issues.push(...declaredNotRunIssues(plan, run));
|
|
63
54
|
return { issues, passed: run.passed, ok: run.passed && !hasError(issues) };
|
|
64
55
|
}
|
|
65
56
|
/** Coverage-only report (design §9: `attest cover`). */
|
|
66
57
|
export async function runCover(root) {
|
|
58
|
+
const scan = await scanProject(root);
|
|
67
59
|
const loader = await createLoader();
|
|
68
60
|
try {
|
|
69
|
-
const { registry } = await loadRegistry(root, loader);
|
|
70
|
-
const plan = await
|
|
61
|
+
const { registry } = await loadRegistry(root, loader, scan.reqsFiles);
|
|
62
|
+
const plan = await parseSpecs(scan.specFiles, root);
|
|
71
63
|
const counts = new Map();
|
|
72
64
|
for (const s of plan.scenarios) {
|
|
73
65
|
counts.set(s.reqId, (counts.get(s.reqId) ?? 0) + 1);
|
|
@@ -121,11 +113,59 @@ export async function runRenderCheck(root, outFile, target) {
|
|
|
121
113
|
const stale = staleIssue(target, current, markdown);
|
|
122
114
|
return stale ? [...issues, stale] : issues;
|
|
123
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* A change name must name one directory inside `changes/`, and nothing else.
|
|
118
|
+
*
|
|
119
|
+
* The name is interpolated into a path whose module is then *executed*, and
|
|
120
|
+
* `join` normalises `..` — so an unvalidated name loads code from anywhere on
|
|
121
|
+
* disk. That is reachable from CI the moment the name comes from a branch or
|
|
122
|
+
* MR title.
|
|
123
|
+
*
|
|
124
|
+
* The test is path safety, not a character whitelist: the name never reaches a
|
|
125
|
+
* glob (only *sibling* names do, and changeExcludeGlobs escapes those), so
|
|
126
|
+
* `feat(auth)` or a name with a space is a perfectly good directory and there
|
|
127
|
+
* is no reason for the gate to refuse it. Rejecting only what can escape the
|
|
128
|
+
* directory keeps the guard exactly as wide as the danger.
|
|
129
|
+
*/
|
|
130
|
+
function isSafeChangeName(name) {
|
|
131
|
+
return (name !== '' &&
|
|
132
|
+
name !== '.' &&
|
|
133
|
+
name !== '..' &&
|
|
134
|
+
!name.includes('/') &&
|
|
135
|
+
!name.includes('\\') &&
|
|
136
|
+
!name.includes('\0'));
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The globs that keep *other* proposals out of a change's gate run.
|
|
140
|
+
*
|
|
141
|
+
* Sibling names come from `readdir`, not from the guard above, so they are
|
|
142
|
+
* escaped: a directory called `feat(auth)` pasted in raw is a *pattern*, it
|
|
143
|
+
* matches nothing, and that sibling's specs silently join the run — quietly
|
|
144
|
+
* widening the scope of the one check that decides "done".
|
|
145
|
+
*/
|
|
146
|
+
export function changeExcludeGlobs(others) {
|
|
147
|
+
return others.map((n) => `**/changes/${escapeGlob(n)}/**`);
|
|
148
|
+
}
|
|
149
|
+
function escapeGlob(name) {
|
|
150
|
+
return name.replace(/[*?[\]{}()!+@|^$]/g, '\\$&');
|
|
151
|
+
}
|
|
124
152
|
/** Archive gate for a change (design §8, §9: `attest archive <change>`). */
|
|
125
153
|
export async function runArchive(root, changeName, options = {}) {
|
|
154
|
+
// Checked before any loader starts: nothing should be resolved, let alone
|
|
155
|
+
// evaluated, on behalf of a name we have already rejected.
|
|
156
|
+
if (!isSafeChangeName(changeName)) {
|
|
157
|
+
return [
|
|
158
|
+
{
|
|
159
|
+
level: 'ERROR',
|
|
160
|
+
code: 'invalid-change-name',
|
|
161
|
+
message: `Invalid change name ${JSON.stringify(changeName)}. A change name must be one directory inside changes/: it cannot be empty, "." or "..", or contain a path separator.`,
|
|
162
|
+
},
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
const scan = await scanProject(root);
|
|
126
166
|
const loader = await createLoader();
|
|
127
167
|
try {
|
|
128
|
-
const { registry: base, issues } = await loadRegistry(root, loader);
|
|
168
|
+
const { registry: base, issues } = await loadRegistry(root, loader, scan.reqsFiles);
|
|
129
169
|
if (issues.some((i) => i.level === 'ERROR'))
|
|
130
170
|
return issues;
|
|
131
171
|
const deltaPath = join(root, 'changes', changeName, 'requirements.delta.ts');
|
|
@@ -148,7 +188,7 @@ export async function runArchive(root, changeName, options = {}) {
|
|
|
148
188
|
if (applied.issues.length > 0)
|
|
149
189
|
return applied.issues;
|
|
150
190
|
// Static plan = merged base suite + this change's specs (design §8).
|
|
151
|
-
const basePlan = await
|
|
191
|
+
const basePlan = await parseSpecs(scan.specFiles, root);
|
|
152
192
|
const changePlan = await parseChangeSpecs(root, changeName);
|
|
153
193
|
const plan = {
|
|
154
194
|
scenarios: [...basePlan.scenarios, ...changePlan.scenarios],
|
|
@@ -159,7 +199,7 @@ export async function runArchive(root, changeName, options = {}) {
|
|
|
159
199
|
const exclude = [
|
|
160
200
|
...configDefaults.exclude,
|
|
161
201
|
'**/archive/**',
|
|
162
|
-
...others
|
|
202
|
+
...changeExcludeGlobs(others),
|
|
163
203
|
];
|
|
164
204
|
const run = await runAndCollect({ root, quiet: true, exclude, vitestConfig: options.vitestConfig });
|
|
165
205
|
return evaluateGate({ registry: applied.registry, plan, run });
|
|
@@ -170,6 +210,6 @@ export async function runArchive(root, changeName, options = {}) {
|
|
|
170
210
|
}
|
|
171
211
|
/** True when a spec suite exists at all (used by the CLI to warn on empty roots). */
|
|
172
212
|
export async function hasSpecs(root) {
|
|
173
|
-
return (await
|
|
213
|
+
return (await scanProject(root)).specFiles.length > 0;
|
|
174
214
|
}
|
|
175
215
|
//# sourceMappingURL=pipeline.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../src/core/pipeline.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,iFAAiF;AAEjF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EACL,YAAY,EACZ,
|
|
1
|
+
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../src/core/pipeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/core/render.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/core/render.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAe,MAAM,YAAY,CAAC;AAQ/D;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAezD;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,KAAK,EAAE,MAAM,GACZ,KAAK,GAAG,SAAS,CAYnB"}
|
package/dist/core/render.js
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
// - **Params interpolated into the statement.** The source says
|
|
21
21
|
// "{idleTimeoutMin} minutes"; a human reader wants "30 minutes". This is the
|
|
22
22
|
// one thing the projection gives that reading the source does not.
|
|
23
|
+
import { byCodeUnit } from './order.js';
|
|
23
24
|
const BANNER = '<!-- Generated by `attest render` — do not edit. Edit the `*.reqs.ts` registry and regenerate. -->';
|
|
24
25
|
/**
|
|
25
26
|
* Render the registry as a standalone Markdown document.
|
|
@@ -82,10 +83,10 @@ function compareIds(a, b) {
|
|
|
82
83
|
};
|
|
83
84
|
const [prefixA, numA] = split(a);
|
|
84
85
|
const [prefixB, numB] = split(b);
|
|
85
|
-
//
|
|
86
|
-
//
|
|
86
|
+
// byCodeUnit, not localeCompare: the ordering has to be identical on every
|
|
87
|
+
// machine, or `--check` would flap with the runner's locale.
|
|
87
88
|
if (prefixA !== prefixB)
|
|
88
|
-
return prefixA
|
|
89
|
+
return byCodeUnit(prefixA, prefixB);
|
|
89
90
|
return numA - numB;
|
|
90
91
|
}
|
|
91
92
|
/**
|
|
@@ -124,7 +125,10 @@ function section(id, req) {
|
|
|
124
125
|
* `unbound-param` error, and silently swallowing it here would hide that.
|
|
125
126
|
*/
|
|
126
127
|
function interpolate(statement, params) {
|
|
127
|
-
|
|
128
|
+
// `Object.hasOwn`, never `in`: `'toString' in {}` is true, and an `in` probe
|
|
129
|
+
// would splice `function toString() { [native code] }` into a document that
|
|
130
|
+
// reviewers and audit read as the system's promise.
|
|
131
|
+
return statement.replace(/\{(\w+)\}/g, (whole, name) => Object.hasOwn(params, name) ? `**${plain(params[name])}**` : whole);
|
|
128
132
|
}
|
|
129
133
|
/**
|
|
130
134
|
* A param value as prose, for inline use inside a statement. Escaped, unlike
|
package/dist/core/render.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/core/render.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,6BAA6B;AAC7B,EAAE;AACF,8EAA8E;AAC9E,+EAA+E;AAC/E,0EAA0E;AAC1E,mEAAmE;AACnE,EAAE;AACF,wCAAwC;AACxC,gFAAgF;AAChF,gFAAgF;AAChF,gFAAgF;AAChF,4EAA4E;AAC5E,+EAA+E;AAC/E,8EAA8E;AAC9E,yCAAyC;AACzC,4EAA4E;AAC5E,gFAAgF;AAChF,uDAAuD;AACvD,iEAAiE;AACjE,gFAAgF;AAChF,sEAAsE;
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/core/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/core/validator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/core/validator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAkB,QAAQ,EAAE,MAAM,YAAY,CAAC;AAExF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,GAAG,KAAK,EAAE,CAkD/E;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,QAAQ,EAAE,GACf,KAAK,EAAE,CA+BT"}
|
package/dist/core/validator.js
CHANGED
|
@@ -35,10 +35,13 @@ export function validateStructure(registry, plan) {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
// unbound-param: statement uses {param} but params does not define it.
|
|
38
|
+
// `Object.hasOwn`, never `in`: `'toString' in {}` is true, so an `in` probe
|
|
39
|
+
// silently accepts placeholders no author declared — and render.ts would then
|
|
40
|
+
// interpolate the prototype method into the document.
|
|
38
41
|
for (const [id, req] of Object.entries(registry)) {
|
|
39
42
|
for (const m of req.statement.matchAll(/\{(\w+)\}/g)) {
|
|
40
43
|
const name = m[1];
|
|
41
|
-
if (!(
|
|
44
|
+
if (!Object.hasOwn(req.params, name)) {
|
|
42
45
|
issues.push({
|
|
43
46
|
level: 'ERROR',
|
|
44
47
|
code: 'unbound-param',
|
|
@@ -61,15 +64,24 @@ export function validateStructure(registry, plan) {
|
|
|
61
64
|
export function detectPotentialDrift(registry, plan, refs) {
|
|
62
65
|
const readsParams = new Set(refs.map((r) => r.reqId));
|
|
63
66
|
const issues = [];
|
|
67
|
+
// Index the scenarios once. Filtering the whole plan per requirement made
|
|
68
|
+
// this O(requirements x scenarios), which is the product that grows fastest
|
|
69
|
+
// in exactly the repos where `check` needs to stay fast.
|
|
70
|
+
const firstCovering = new Map();
|
|
71
|
+
for (const s of plan.scenarios) {
|
|
72
|
+
if (!firstCovering.has(s.reqId))
|
|
73
|
+
firstCovering.set(s.reqId, s);
|
|
74
|
+
}
|
|
64
75
|
for (const [id, req] of Object.entries(registry)) {
|
|
65
76
|
if (Object.keys(req.params).length === 0)
|
|
66
77
|
continue;
|
|
67
|
-
|
|
68
|
-
if (covering.length === 0 || readsParams.has(id))
|
|
78
|
+
if (readsParams.has(id))
|
|
69
79
|
continue;
|
|
70
80
|
// Anchor the hint at the first covering scenario so it stays clickable,
|
|
71
81
|
// even though the verdict is now about the requirement as a whole.
|
|
72
|
-
const at =
|
|
82
|
+
const at = firstCovering.get(id);
|
|
83
|
+
if (!at)
|
|
84
|
+
continue;
|
|
73
85
|
issues.push({
|
|
74
86
|
level: 'INFO',
|
|
75
87
|
code: 'possible-drift',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/core/validator.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,8EAA8E;AAI9E;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAkB,EAAE,IAAgB;IACpE,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAChD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAE/D,yDAAyD;IACzD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QAC/B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC;gBACV,KAAK,EAAE,OAAO;gBACd,IAAI,EAAE,aAAa;gBACnB,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,OAAO,EAAE,aAAa,CAAC,CAAC,IAAI,kCAAkC,CAAC,CAAC,KAAK,2CAA2C;aACjH,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;QAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YACxB,MAAM,CAAC,IAAI,CAAC;gBACV,KAAK,EAAE,OAAO;gBACd,IAAI,EAAE,uBAAuB;gBAC7B,KAAK,EAAE,EAAE;gBACT,OAAO,EAAE,gBAAgB,EAAE,sEAAsE;aAClG,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,uEAAuE;IACvE,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjD,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;YACrD,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;YACnB,IAAI,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/core/validator.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED