@am_shork/attest 0.7.3 → 0.8.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/CHANGELOG.md +1053 -172
- package/README.md +1 -1
- package/dist/cli/index.js +11 -8
- package/dist/cli/json.d.ts +26 -1
- package/dist/cli/json.js +28 -2
- package/dist/core/compiler.d.ts +32 -0
- package/dist/core/compiler.js +78 -0
- package/dist/core/docs.d.ts +1 -1
- package/dist/core/docs.js +1 -0
- package/dist/core/gate.js +3 -3
- package/dist/core/locate.d.ts +22 -2
- package/dist/core/locate.js +33 -8
- package/dist/core/parser.d.ts +12 -0
- package/dist/core/parser.js +18 -2
- package/dist/core/pipeline.d.ts +17 -1
- package/dist/core/pipeline.js +109 -17
- package/dist/core/red-record.d.ts +14 -5
- package/dist/core/red-record.js +82 -24
- package/dist/core/registry-issues.d.ts +30 -0
- package/dist/core/registry-issues.js +26 -0
- package/dist/core/registry.d.ts +12 -5
- package/dist/core/registry.js +10 -8
- package/dist/core/runner.js +21 -9
- package/dist/core/schema.d.ts +32 -27
- package/dist/core/schema.js +33 -5
- package/dist/core/skill.js +13 -0
- package/dist/core/splice.js +45 -14
- package/dist/core/static-registry.d.ts +34 -2
- package/dist/core/static-registry.js +138 -12
- package/dist/core/status.js +2 -2
- package/dist/core/terminal.js +5 -2
- package/dist/core/types.d.ts +39 -10
- package/dist/core/validator.d.ts +9 -4
- package/dist/core/validator.js +74 -21
- package/package.json +2 -2
package/dist/core/validator.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Structural validation + anti-drift heuristic (design §5.3, §6).
|
|
2
2
|
// Graded reporting: ERROR / WARNING / INFO, each with file + line + fix hint.
|
|
3
|
+
import { byCodeUnit } from './order.js';
|
|
3
4
|
/**
|
|
4
5
|
* Uncovered requirements: intent exists but no scenario attests it (design §5.3).
|
|
5
6
|
*
|
|
@@ -35,11 +36,15 @@ export function uncoveredIssues(registry, plan) {
|
|
|
35
36
|
* and one WARNING:
|
|
36
37
|
* - rationale-placeholder: a `{name}` in a rationale, which is never interpolated
|
|
37
38
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
39
|
+
* and one more WARNING, which exists only when a registry file failed to load:
|
|
40
|
+
* - orphan-from-failed-registry: the scenarios attesting ids that file declares
|
|
41
|
+
*
|
|
42
|
+
* `unreadable` is the `*.reqs.ts` files that failed to load, each with the ids
|
|
43
|
+
* its source still names (see `loadRegistry`). It changes no verdict — only
|
|
44
|
+
* which findings are stated per scenario and which are stated once, for the
|
|
45
|
+
* reason below.
|
|
41
46
|
*/
|
|
42
|
-
export function validateStructure(registry, plan,
|
|
47
|
+
export function validateStructure(registry, plan, unreadable = []) {
|
|
43
48
|
const issues = [];
|
|
44
49
|
const knownIds = new Set(Object.keys(registry));
|
|
45
50
|
// orphan-test: covers a requirement that does not exist.
|
|
@@ -50,27 +55,75 @@ export function validateStructure(registry, plan, registryIncomplete = false) {
|
|
|
50
55
|
// following the hint would add a duplicate. That case is not rare when it
|
|
51
56
|
// happens — one unreadable `*.reqs.ts` orphans every scenario of every
|
|
52
57
|
// requirement it declared, so the wrong advice is also the loudest thing in
|
|
53
|
-
// the report.
|
|
58
|
+
// the report. Measured on one broken registry of three requirements: six
|
|
59
|
+
// ERRORs, of which one was the cause and five were its shadow.
|
|
60
|
+
//
|
|
61
|
+
// Which of the two a given orphan is *can* be decided here, because
|
|
62
|
+
// `loadRegistry` brings back the ids the broken file's source still names. So
|
|
63
|
+
// the scenarios attesting those ids are not reported one by one: they are
|
|
64
|
+
// fallout from a finding already in the report, and the fallout is stated once
|
|
65
|
+
// per file that caused it. That is the same move `duplicate-prefix` makes —
|
|
66
|
+
// the fact is about the file, not about each requirement that reveals it — and
|
|
67
|
+
// the same order ATX-62 records for `spec-load-failed`: the replacement comes
|
|
68
|
+
// first, the withdrawal follows it, because suppressing with nothing in its
|
|
69
|
+
// place trades a wrong message for silence.
|
|
54
70
|
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
const
|
|
61
|
-
|
|
71
|
+
// Nothing is lost by collapsing them. The replacement names the file, the
|
|
72
|
+
// count and the ids, so a reader can still see which scenarios are affected;
|
|
73
|
+
// the load failure is already an ERROR, so the verdict cannot move; and the
|
|
74
|
+
// scenarios themselves are not defective — they will be verified normally the
|
|
75
|
+
// moment the registry loads.
|
|
76
|
+
const swallowed = new Map(unreadable.map((u) => [u.file, []]));
|
|
77
|
+
const ownerOf = new Map();
|
|
78
|
+
for (const u of unreadable)
|
|
79
|
+
for (const id of u.ids)
|
|
80
|
+
ownerOf.set(id, u.file);
|
|
81
|
+
// An unreadable file whose ids could not be recovered leaves the question open
|
|
82
|
+
// for every orphan that is not claimed by another one, and the hedged advice
|
|
83
|
+
// is what an open question sounds like. When every unreadable file gave up its
|
|
84
|
+
// ids, an orphan none of them claims is genuinely unknown — and gets the plain
|
|
85
|
+
// advice it deserves, which the hedge had been withdrawing from correct
|
|
86
|
+
// findings too.
|
|
87
|
+
const someFileOpaque = unreadable.some((u) => u.ids.length === 0);
|
|
88
|
+
const orphanFix = (id) => someFileOpaque
|
|
89
|
+
? `A registry file failed to load and its ids could not be read, so "${id}" may be one of them — fix that first, and add it only if it is still unknown afterwards.`
|
|
62
90
|
: `Add it to the registry, or fix the id.`;
|
|
63
91
|
for (const s of plan.scenarios) {
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
line: s.line,
|
|
71
|
-
message: `scenario "${s.name}" attests unknown requirement "${s.reqId}". ${orphanFix(s.reqId)}`,
|
|
72
|
-
});
|
|
92
|
+
if (knownIds.has(s.reqId))
|
|
93
|
+
continue;
|
|
94
|
+
const owner = ownerOf.get(s.reqId);
|
|
95
|
+
if (owner !== undefined) {
|
|
96
|
+
swallowed.get(owner).push(s.reqId);
|
|
97
|
+
continue;
|
|
73
98
|
}
|
|
99
|
+
issues.push({
|
|
100
|
+
level: 'ERROR',
|
|
101
|
+
code: 'orphan-test',
|
|
102
|
+
reqId: s.reqId,
|
|
103
|
+
file: s.file,
|
|
104
|
+
line: s.line,
|
|
105
|
+
message: `scenario "${s.name}" attests unknown requirement "${s.reqId}". ${orphanFix(s.reqId)}`,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
// WARNING, not ERROR: the defect is the load failure, which is an ERROR of its
|
|
109
|
+
// own and always present when this is. Reporting it a second time at the same
|
|
110
|
+
// level would say a broken registry is two problems.
|
|
111
|
+
//
|
|
112
|
+
// No `reqId`: the finding is about a file and the set of ids it took down with
|
|
113
|
+
// it, and no single requirement is implicated — the same reason
|
|
114
|
+
// `duplicate-prefix` and `spec-load-failed` carry none.
|
|
115
|
+
for (const u of unreadable) {
|
|
116
|
+
const ids = swallowed.get(u.file);
|
|
117
|
+
if (ids.length === 0)
|
|
118
|
+
continue;
|
|
119
|
+
const unique = [...new Set(ids)].sort(byCodeUnit);
|
|
120
|
+
issues.push({
|
|
121
|
+
level: 'WARNING',
|
|
122
|
+
code: 'orphan-from-failed-registry',
|
|
123
|
+
file: u.file,
|
|
124
|
+
message: `${ids.length} ${ids.length === 1 ? 'scenario attests' : 'scenarios attest'} ids ${u.file} declares (${unique.join(', ')}), and it failed to load — so those ids are missing from this run. ` +
|
|
125
|
+
`They are not orphans: fix the load failure reported above and they will be verified as usual.`,
|
|
126
|
+
});
|
|
74
127
|
}
|
|
75
128
|
// uncovered-requirement: intent exists but no scenario attests it. Shared
|
|
76
129
|
// with `cover`, so the two commands cannot drift apart.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@am_shork/attest",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "TDD-native spec framework: tests are the source of truth for verification, ID-bound requirements the source of truth for intent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"chalk": "^5.3.0",
|
|
49
49
|
"commander": "^12.1.0",
|
|
50
50
|
"typescript": "^5.5.0 || ^6.0.0",
|
|
51
|
-
"zod": "^
|
|
51
|
+
"zod": "^4.4.3"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
54
|
"vite": "^8.0.0",
|