@am_shork/attest 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/LICENSE +21 -0
  3. package/README.md +101 -0
  4. package/bin/attest.js +3 -0
  5. package/dist/cli/index.d.ts +2 -0
  6. package/dist/cli/index.d.ts.map +1 -0
  7. package/dist/cli/index.js +69 -0
  8. package/dist/cli/index.js.map +1 -0
  9. package/dist/cli/report.d.ts +7 -0
  10. package/dist/cli/report.d.ts.map +1 -0
  11. package/dist/cli/report.js +45 -0
  12. package/dist/cli/report.js.map +1 -0
  13. package/dist/core/apply.d.ts +13 -0
  14. package/dist/core/apply.d.ts.map +1 -0
  15. package/dist/core/apply.js +94 -0
  16. package/dist/core/apply.js.map +1 -0
  17. package/dist/core/gate.d.ts +22 -0
  18. package/dist/core/gate.d.ts.map +1 -0
  19. package/dist/core/gate.js +44 -0
  20. package/dist/core/gate.js.map +1 -0
  21. package/dist/core/loader.d.ts +6 -0
  22. package/dist/core/loader.d.ts.map +1 -0
  23. package/dist/core/loader.js +39 -0
  24. package/dist/core/loader.js.map +1 -0
  25. package/dist/core/locate.d.ts +22 -0
  26. package/dist/core/locate.d.ts.map +1 -0
  27. package/dist/core/locate.js +117 -0
  28. package/dist/core/locate.js.map +1 -0
  29. package/dist/core/parser.d.ts +3 -0
  30. package/dist/core/parser.d.ts.map +1 -0
  31. package/dist/core/parser.js +0 -0
  32. package/dist/core/parser.js.map +1 -0
  33. package/dist/core/pipeline.d.ts +22 -0
  34. package/dist/core/pipeline.d.ts.map +1 -0
  35. package/dist/core/pipeline.js +135 -0
  36. package/dist/core/pipeline.js.map +1 -0
  37. package/dist/core/registry.d.ts +35 -0
  38. package/dist/core/registry.d.ts.map +1 -0
  39. package/dist/core/registry.js +41 -0
  40. package/dist/core/registry.js.map +1 -0
  41. package/dist/core/runner.d.ts +15 -0
  42. package/dist/core/runner.d.ts.map +1 -0
  43. package/dist/core/runner.js +59 -0
  44. package/dist/core/runner.js.map +1 -0
  45. package/dist/core/schema.d.ts +42 -0
  46. package/dist/core/schema.d.ts.map +1 -0
  47. package/dist/core/schema.js +20 -0
  48. package/dist/core/schema.js.map +1 -0
  49. package/dist/core/types.d.ts +39 -0
  50. package/dist/core/types.d.ts.map +1 -0
  51. package/dist/core/types.js +5 -0
  52. package/dist/core/types.js.map +1 -0
  53. package/dist/core/validator.d.ts +15 -0
  54. package/dist/core/validator.d.ts.map +1 -0
  55. package/dist/core/validator.js +73 -0
  56. package/dist/core/validator.js.map +1 -0
  57. package/dist/index.d.ts +5 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +5 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/runtime.d.ts +11 -0
  62. package/dist/runtime.d.ts.map +1 -0
  63. package/dist/runtime.js +32 -0
  64. package/dist/runtime.js.map +1 -0
  65. package/package.json +76 -0
@@ -0,0 +1,117 @@
1
+ // Locate layer (design §4, L1): find registry + spec files, load the registry
2
+ // via the TS loader, and parse all spec files into one merged AttestPlan.
3
+ import { readdir, readFile } from 'node:fs/promises';
4
+ import { join, relative } from 'node:path';
5
+ import { parseSpecFile } from './parser.js';
6
+ // Proposed changes and archived changes are excluded from normal scanning:
7
+ // a change's requirements/specs only count once its gate passes and it is
8
+ // merged (design §7, §8). `attest archive <name>` includes them explicitly.
9
+ const SKIP_DIRS = new Set([
10
+ 'node_modules',
11
+ 'dist',
12
+ '.git',
13
+ 'coverage',
14
+ 'changes',
15
+ 'archive',
16
+ ]);
17
+ export const isReqsFile = (name) => name.endsWith('.reqs.ts');
18
+ export const isSpecFile = (name) => name.endsWith('.spec.ts');
19
+ /** Recursively find files under root whose basename matches `match`. */
20
+ export async function findFiles(root, match) {
21
+ const out = [];
22
+ const walk = async (dir) => {
23
+ const entries = await readdir(dir, { withFileTypes: true });
24
+ for (const e of entries) {
25
+ const full = join(dir, e.name);
26
+ if (e.isDirectory()) {
27
+ if (!SKIP_DIRS.has(e.name))
28
+ await walk(full);
29
+ }
30
+ else if (e.isFile() && match(e.name)) {
31
+ out.push(full);
32
+ }
33
+ }
34
+ };
35
+ await walk(root);
36
+ return out.sort();
37
+ }
38
+ /**
39
+ * Load and merge every `*.reqs.ts` registry under root. A malformed registry
40
+ * (defineRequirements throws) becomes a registry-invalid ERROR; a duplicate id
41
+ * across files becomes a duplicate-requirement ERROR.
42
+ */
43
+ export async function loadRegistry(root, loader) {
44
+ const files = await findFiles(root, isReqsFile);
45
+ const registry = {};
46
+ const issues = [];
47
+ for (const file of files) {
48
+ const display = relative(root, file);
49
+ let mod;
50
+ try {
51
+ mod = await loader.load(file);
52
+ }
53
+ catch (err) {
54
+ issues.push({
55
+ level: 'ERROR',
56
+ code: 'registry-invalid',
57
+ file: display,
58
+ message: `無法載入 registry:${err instanceof Error ? err.message : String(err)}`,
59
+ });
60
+ continue;
61
+ }
62
+ const reg = mod.default;
63
+ if (!reg || typeof reg !== 'object') {
64
+ issues.push({
65
+ level: 'ERROR',
66
+ code: 'registry-no-default',
67
+ file: display,
68
+ message: 'registry 檔必須 default export defineRequirements(...) 的結果。',
69
+ });
70
+ continue;
71
+ }
72
+ for (const [id, req] of Object.entries(reg)) {
73
+ if (id in registry) {
74
+ issues.push({
75
+ level: 'ERROR',
76
+ code: 'duplicate-requirement',
77
+ file: display,
78
+ message: `需求 "${id}" 在多個 registry 檔重複定義。`,
79
+ });
80
+ }
81
+ else {
82
+ registry[id] = req;
83
+ }
84
+ }
85
+ }
86
+ return { registry, issues };
87
+ }
88
+ /** Parse every `*.spec.ts` under root into one merged plan (file paths shown relative to root). */
89
+ export async function parseAllSpecFiles(root) {
90
+ return parseSpecsUnder(root, root);
91
+ }
92
+ /** Parse the spec files belonging to a single proposed change (design §8). */
93
+ export async function parseChangeSpecs(root, changeName) {
94
+ return parseSpecsUnder(join(root, 'changes', changeName), root);
95
+ }
96
+ /** List the names of proposed changes under `root/changes`. */
97
+ export async function listChangeNames(root) {
98
+ try {
99
+ const entries = await readdir(join(root, 'changes'), { withFileTypes: true });
100
+ return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
101
+ }
102
+ catch {
103
+ return [];
104
+ }
105
+ }
106
+ async function parseSpecsUnder(dir, displayRoot) {
107
+ const files = await findFiles(dir, isSpecFile);
108
+ const plan = { scenarios: [], paramRefs: [] };
109
+ for (const file of files) {
110
+ const source = await readFile(file, 'utf8');
111
+ const parsed = parseSpecFile(relative(displayRoot, file), source);
112
+ plan.scenarios.push(...parsed.scenarios);
113
+ plan.paramRefs.push(...parsed.paramRefs);
114
+ }
115
+ return plan;
116
+ }
117
+ //# sourceMappingURL=locate.js.map
@@ -0,0 +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;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAI5C,2EAA2E;AAC3E,0EAA0E;AAC1E,4EAA4E;AAC5E,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC;IACxB,cAAc;IACd,MAAM;IACN,MAAM;IACN,UAAU;IACV,SAAS;IACT,SAAS;CACV,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAY,EAAW,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AAC/E,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAY,EAAW,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AAE/E,wEAAwE;AACxE,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,IAAY,EACZ,KAAgC;IAEhC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,KAAK,EAAE,GAAW,EAAiB,EAAE;QAChD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5D,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;YAC/B,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;gBACpB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;oBAAE,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;YAC/C,CAAC;iBAAM,IAAI,CAAC,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;gBACvC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,IAAY,EACZ,MAAc;IAEd,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,MAAM,GAAY,EAAE,CAAC;IAE3B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACrC,IAAI,GAA2B,CAAC;QAChC,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAyB,IAAI,CAAC,CAAC;QACxD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CAAC;gBACV,KAAK,EAAE,OAAO;gBACd,IAAI,EAAE,kBAAkB;gBACxB,IAAI,EAAE,OAAO;gBACb,OAAO,EAAE,iBAAiB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;aAC7E,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC;QACxB,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YACpC,MAAM,CAAC,IAAI,CAAC;gBACV,KAAK,EAAE,OAAO;gBACd,IAAI,EAAE,qBAAqB;gBAC3B,IAAI,EAAE,OAAO;gBACb,OAAO,EAAE,0DAA0D;aACpE,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAA4B,EAAE,CAAC;YACvE,IAAI,EAAE,IAAI,QAAQ,EAAE,CAAC;gBACnB,MAAM,CAAC,IAAI,CAAC;oBACV,KAAK,EAAE,OAAO;oBACd,IAAI,EAAE,uBAAuB;oBAC7B,IAAI,EAAE,OAAO;oBACb,OAAO,EAAE,OAAO,EAAE,uBAAuB;iBAC1C,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC;YACrB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AAC9B,CAAC;AAED,mGAAmG;AACnG,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY;IAClD,OAAO,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,IAAY,EACZ,UAAkB;IAElB,OAAO,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE,IAAI,CAAC,CAAC;AAClE,CAAC;AAED,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY;IAChD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9E,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,GAAW,EAAE,WAAmB;IAC7D,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAe,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC1D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QAClE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { AttestPlan } from './types.js';
2
+ export declare function parseSpecFile(file: string, source: string): AttestPlan;
3
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/core/parser.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAA4B,MAAM,YAAY,CAAC;AAEvE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CA0DtE"}
Binary file
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/core/parser.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,0EAA0E;AAC1E,6EAA6E;AAC7E,8EAA8E;AAE9E,OAAO,EAAE,MAAM,YAAY,CAAC;AAG5B,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,MAAc;IACxD,MAAM,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAC5B,IAAI,EACJ,MAAM,EACN,EAAE,CAAC,YAAY,CAAC,MAAM;IACtB,oBAAoB,CAAC,IAAI,CAC1B,CAAC;IACF,MAAM,SAAS,GAAqB,EAAE,CAAC;IACvC,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC,CAAC,wCAAwC;IAC5E,IAAI,YAAY,GAAkB,IAAI,CAAC;IACvC,IAAI,eAAe,GAAkB,IAAI,CAAC;IAE1C,MAAM,KAAK,GAAG,CAAC,IAAa,EAAQ,EAAE;QACpC,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAClE,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YACnC,MAAM,OAAO,GACX,QAAQ,IAAI,EAAE,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;YAE3E,IAAI,EAAE,KAAK,aAAa,IAAI,OAAO,EAAE,CAAC;gBACpC,MAAM,IAAI,GAAG,YAAY,CAAC;gBAC1B,YAAY,GAAG,OAAO,CAAC;gBACvB,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,sCAAsC;gBACpE,YAAY,GAAG,IAAI,CAAC;gBACpB,OAAO;YACT,CAAC;YACD,IAAI,EAAE,KAAK,UAAU,IAAI,OAAO,IAAI,YAAY,EAAE,CAAC;gBACjD,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;gBACrE,SAAS,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC7E,MAAM,IAAI,GAAG,eAAe,CAAC;gBAC7B,eAAe,GAAG,OAAO,CAAC;gBAC1B,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,qCAAqC;gBACnE,eAAe,GAAG,IAAI,CAAC;gBACvB,OAAO;YACT,CAAC;QACH,CAAC;QAED,4EAA4E;QAC5E,8EAA8E;QAC9E,IACE,YAAY;YACZ,eAAe;YACf,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC;YACnC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,EAC3B,CAAC;YACD,MAAM,GAAG,GAAG,GAAG,YAAY,IAAI,eAAe,EAAE,CAAC;YACjD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvB,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAClB,SAAS,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC,CAAC;YACrE,CAAC;QACH,CAAC;QAED,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC,CAAC;IAEF,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AAClC,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { Issue } from './types.js';
2
+ /** Static structural check (design §9: `attest check`). */
3
+ export declare function runCheck(root: string): Promise<Issue[]>;
4
+ export interface VerifyResult {
5
+ issues: Issue[];
6
+ passed: boolean;
7
+ ok: boolean;
8
+ }
9
+ /** Executable verification (design §9: `attest verify`). */
10
+ export declare function runVerify(root: string): Promise<VerifyResult>;
11
+ export interface CoverageRow {
12
+ reqId: string;
13
+ covered: boolean;
14
+ scenarioCount: number;
15
+ }
16
+ /** Coverage-only report (design §9: `attest cover`). */
17
+ export declare function runCover(root: string): Promise<CoverageRow[]>;
18
+ /** Archive gate for a change (design §8, §9: `attest archive <change>`). */
19
+ export declare function runArchive(root: string, changeName: string): Promise<Issue[]>;
20
+ /** True when a spec suite exists at all (used by the CLI to warn on empty roots). */
21
+ export declare function hasSpecs(root: string): Promise<boolean>;
22
+ //# sourceMappingURL=pipeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/core/pipeline.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAc,KAAK,EAAY,MAAM,YAAY,CAAC;AAE9D,2DAA2D;AAC3D,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAa7D;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,EAAE,EAAE,OAAO,CAAC;CACb;AAED,4DAA4D;AAC5D,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAmCnE;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,CAmBnE;AAED,4EAA4E;AAC5E,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CA8CnF;AAED,qFAAqF;AACrF,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE7D"}
@@ -0,0 +1,135 @@
1
+ // High-level operations composing the core layers (design §9). These are
2
+ // tool-agnostic; the CLI is a thin shell that calls them and renders the result.
3
+ import { createLoader } from './loader.js';
4
+ import { loadRegistry, parseAllSpecFiles, parseChangeSpecs, listChangeNames, findFiles, } from './locate.js';
5
+ import { validateStructure, detectPotentialDrift } from './validator.js';
6
+ import { runAndCollect } from './runner.js';
7
+ import { applyDelta } from './apply.js';
8
+ import { evaluateGate } from './gate.js';
9
+ import { join, relative } from 'node:path';
10
+ import { configDefaults } from 'vitest/config';
11
+ /** Static structural check (design §9: `attest check`). */
12
+ export async function runCheck(root) {
13
+ const loader = await createLoader();
14
+ try {
15
+ const { registry, issues } = await loadRegistry(root, loader);
16
+ const plan = await parseAllSpecFiles(root);
17
+ return [
18
+ ...issues,
19
+ ...validateStructure(registry, plan),
20
+ ...detectPotentialDrift(registry, plan, plan.paramRefs),
21
+ ];
22
+ }
23
+ finally {
24
+ await loader.close();
25
+ }
26
+ }
27
+ /** Executable verification (design §9: `attest verify`). */
28
+ export async function runVerify(root) {
29
+ const loader = await createLoader();
30
+ let registry;
31
+ let plan;
32
+ const issues = [];
33
+ try {
34
+ const loaded = await loadRegistry(root, loader);
35
+ registry = loaded.registry;
36
+ issues.push(...loaded.issues);
37
+ plan = await parseAllSpecFiles(root);
38
+ }
39
+ finally {
40
+ await loader.close();
41
+ }
42
+ issues.push(...validateStructure(registry, plan));
43
+ issues.push(...detectPotentialDrift(registry, plan, plan.paramRefs));
44
+ const run = await runAndCollect({ root, quiet: true });
45
+ if (!run.passed) {
46
+ issues.push({ level: 'ERROR', code: 'tests-red', message: '尚有測試未通過。' });
47
+ }
48
+ for (const s of plan.scenarios) {
49
+ if (!run.runtimeCoverage.get(s.reqId)?.has(s.name)) {
50
+ issues.push({
51
+ level: 'ERROR',
52
+ code: 'declared-not-run',
53
+ file: s.file,
54
+ line: s.line,
55
+ message: `scenario "${s.name}" 靜態宣告覆蓋 ${s.reqId},執行期卻沒跑到(被 skip/only?)。`,
56
+ });
57
+ }
58
+ }
59
+ const hasError = issues.some((i) => i.level === 'ERROR');
60
+ return { issues, passed: run.passed, ok: run.passed && !hasError };
61
+ }
62
+ /** Coverage-only report (design §9: `attest cover`). */
63
+ export async function runCover(root) {
64
+ const loader = await createLoader();
65
+ try {
66
+ const { registry } = await loadRegistry(root, loader);
67
+ const plan = await parseAllSpecFiles(root);
68
+ const counts = new Map();
69
+ for (const s of plan.scenarios) {
70
+ counts.set(s.reqId, (counts.get(s.reqId) ?? 0) + 1);
71
+ }
72
+ return Object.keys(registry)
73
+ .sort()
74
+ .map((reqId) => ({
75
+ reqId,
76
+ covered: (counts.get(reqId) ?? 0) > 0,
77
+ scenarioCount: counts.get(reqId) ?? 0,
78
+ }));
79
+ }
80
+ finally {
81
+ await loader.close();
82
+ }
83
+ }
84
+ /** Archive gate for a change (design §8, §9: `attest archive <change>`). */
85
+ export async function runArchive(root, changeName) {
86
+ const loader = await createLoader();
87
+ try {
88
+ const { registry: base, issues } = await loadRegistry(root, loader);
89
+ if (issues.some((i) => i.level === 'ERROR'))
90
+ return issues;
91
+ const deltaPath = join(root, 'changes', changeName, 'requirements.delta.ts');
92
+ let delta;
93
+ try {
94
+ const mod = await loader.load(deltaPath);
95
+ delta = mod.default;
96
+ }
97
+ catch (err) {
98
+ return [
99
+ {
100
+ level: 'ERROR',
101
+ code: 'change-not-found',
102
+ file: relative(root, deltaPath),
103
+ message: `找不到或無法載入 change "${changeName}" 的 delta:${err instanceof Error ? err.message : String(err)}`,
104
+ },
105
+ ];
106
+ }
107
+ const applied = applyDelta(base, delta);
108
+ if (applied.issues.length > 0)
109
+ return applied.issues;
110
+ // Static plan = merged base suite + this change's specs (design §8).
111
+ const basePlan = await parseAllSpecFiles(root);
112
+ const changePlan = await parseChangeSpecs(root, changeName);
113
+ const plan = {
114
+ scenarios: [...basePlan.scenarios, ...changePlan.scenarios],
115
+ paramRefs: [...basePlan.paramRefs, ...changePlan.paramRefs],
116
+ };
117
+ // Run base specs + this change's specs; exclude other proposals and archive.
118
+ const others = (await listChangeNames(root)).filter((n) => n !== changeName);
119
+ const exclude = [
120
+ ...configDefaults.exclude,
121
+ '**/archive/**',
122
+ ...others.map((n) => `**/changes/${n}/**`),
123
+ ];
124
+ const run = await runAndCollect({ root, quiet: true, exclude });
125
+ return evaluateGate({ registry: applied.registry, plan, run });
126
+ }
127
+ finally {
128
+ await loader.close();
129
+ }
130
+ }
131
+ /** True when a spec suite exists at all (used by the CLI to warn on empty roots). */
132
+ export async function hasSpecs(root) {
133
+ return (await findFiles(root, (n) => n.endsWith('.spec.ts'))).length > 0;
134
+ }
135
+ //# sourceMappingURL=pipeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../src/core/pipeline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { z } from 'zod';
2
+ import { type RegistryInput, type RequirementInput } from './schema.js';
3
+ import type { Registry } from './types.js';
4
+ /** Thrown when a registry fails structural validation (design §5.1). */
5
+ export declare class RegistryValidationError extends Error {
6
+ readonly issues: z.ZodIssue[];
7
+ constructor(issues: z.ZodIssue[]);
8
+ }
9
+ /**
10
+ * Declare a requirement registry. Validates structure against RegistrySchema
11
+ * (SHALL/MUST keyword, non-empty rationale, well-formed IDs, scalar params) and
12
+ * returns the parsed registry with defaults applied (design §2, §5.1).
13
+ * Throws {@link RegistryValidationError} on invalid input.
14
+ */
15
+ export declare function defineRequirements(input: RegistryInput): Registry;
16
+ /**
17
+ * Shape of a change delta over the registry (design §7). Authoring (input)
18
+ * shapes are used so added / modified entries may omit defaulted fields.
19
+ */
20
+ export interface RegistryDelta {
21
+ added?: RegistryInput;
22
+ modified?: Record<string, Partial<RequirementInput>>;
23
+ removed?: string[];
24
+ renamed?: {
25
+ from: string;
26
+ to: string;
27
+ }[];
28
+ }
29
+ /**
30
+ * Declare a registry delta for a change. This is an identity pass-through that
31
+ * fixes the authoring surface; ordered idempotent application
32
+ * (RENAMED -> REMOVED -> MODIFIED -> ADDED) lives in applyDelta (design §7).
33
+ */
34
+ export declare function delta(d: RegistryDelta): RegistryDelta;
35
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/core/registry.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACtB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,wEAAwE;AACxE,qBAAa,uBAAwB,SAAQ,KAAK;aACpB,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;gBAApB,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;CAIjD;AAQD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,GAAG,QAAQ,CAMjE;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACrD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1C;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,aAAa,GAAG,aAAa,CAErD"}
@@ -0,0 +1,41 @@
1
+ // Requirement registry authoring API (design §2, §5.1, §7).
2
+ // defineRequirements validates against RegistrySchema and applies defaults;
3
+ // delta() declares a change delta, applied by applyDelta (see apply.ts).
4
+ import { z } from 'zod';
5
+ import { RegistrySchema, } from './schema.js';
6
+ /** Thrown when a registry fails structural validation (design §5.1). */
7
+ export class RegistryValidationError extends Error {
8
+ issues;
9
+ constructor(issues) {
10
+ super(`Invalid requirement registry:\n${formatIssues(issues)}`);
11
+ this.issues = issues;
12
+ this.name = 'RegistryValidationError';
13
+ }
14
+ }
15
+ function formatIssues(issues) {
16
+ return issues
17
+ .map((i) => ` - ${i.path.join('.') || '(root)'}: ${i.message}`)
18
+ .join('\n');
19
+ }
20
+ /**
21
+ * Declare a requirement registry. Validates structure against RegistrySchema
22
+ * (SHALL/MUST keyword, non-empty rationale, well-formed IDs, scalar params) and
23
+ * returns the parsed registry with defaults applied (design §2, §5.1).
24
+ * Throws {@link RegistryValidationError} on invalid input.
25
+ */
26
+ export function defineRequirements(input) {
27
+ const result = RegistrySchema.safeParse(input);
28
+ if (!result.success) {
29
+ throw new RegistryValidationError(result.error.issues);
30
+ }
31
+ return result.data;
32
+ }
33
+ /**
34
+ * Declare a registry delta for a change. This is an identity pass-through that
35
+ * fixes the authoring surface; ordered idempotent application
36
+ * (RENAMED -> REMOVED -> MODIFIED -> ADDED) lives in applyDelta (design §7).
37
+ */
38
+ export function delta(d) {
39
+ return d;
40
+ }
41
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/core/registry.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,4EAA4E;AAC5E,yEAAyE;AAEzE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,cAAc,GAGf,MAAM,aAAa,CAAC;AAGrB,wEAAwE;AACxE,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IACpB;IAA5B,YAA4B,MAAoB;QAC9C,KAAK,CAAC,kCAAkC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QADtC,WAAM,GAAN,MAAM,CAAc;QAE9C,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF;AAED,SAAS,YAAY,CAAC,MAAoB;IACxC,OAAO,MAAM;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;SAC/D,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAoB;IACrD,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC/C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAaD;;;;GAIG;AACH,MAAM,UAAU,KAAK,CAAC,CAAgB;IACpC,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { RunResult } from './types.js';
2
+ export interface RunAndCollectOptions {
3
+ /** Project root to run. Defaults to the current working directory. */
4
+ root?: string;
5
+ /** Optional Vitest CLI test-name filters. */
6
+ filters?: string[];
7
+ /** Suppress the child Vitest reporter output (the CLI renders its own report). */
8
+ quiet?: boolean;
9
+ /** Spec include globs (default: all spec files). */
10
+ include?: string[];
11
+ /** Spec exclude globs (default: node_modules/dist + changes/archive). */
12
+ exclude?: string[];
13
+ }
14
+ export declare function runAndCollect(options?: RunAndCollectOptions): Promise<RunResult>;
15
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/core/runner.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAQ5C,MAAM,WAAW,oBAAoB;IACnC,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,kFAAkF;IAClF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,wBAAsB,aAAa,CACjC,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,SAAS,CAAC,CA0CpB"}
@@ -0,0 +1,59 @@
1
+ // Executable verification: run Vitest programmatically and reconstruct runtime
2
+ // coverage from the task tree (design §5.4). Coverage is NOT collected via an
3
+ // in-process singleton — tests run in worker subprocesses, so the singleton
4
+ // never crosses the process boundary; the serialized task tree does.
5
+ import { startVitest } from 'vitest/node';
6
+ import { configDefaults } from 'vitest/config';
7
+ /** requirement() names each describe block `[reqId]`; recover the id from that. */
8
+ const REQ_SUITE = /^\[(.+)\]$/;
9
+ /** Proposed / archived changes are excluded from a normal run (design §7, §8). */
10
+ const DEFAULT_EXCLUDE = [...configDefaults.exclude, '**/changes/**', '**/archive/**'];
11
+ export async function runAndCollect(options = {}) {
12
+ const vitest = await startVitest('test', options.filters ?? [], {
13
+ watch: false,
14
+ root: options.root,
15
+ include: options.include ?? ['**/*.spec.ts'],
16
+ exclude: options.exclude ?? DEFAULT_EXCLUDE,
17
+ // Isolate from any ambient vitest.config so the child run is deterministic.
18
+ config: false,
19
+ ...(options.quiet ? { silent: true, reporters: [{}] } : {}),
20
+ });
21
+ if (!vitest) {
22
+ throw new Error('runAndCollect: failed to start Vitest.');
23
+ }
24
+ try {
25
+ const runtimeCoverage = new Map();
26
+ // Reconstruct coverage from the task tree (not an in-process singleton).
27
+ const walk = (task) => {
28
+ if (task.type === 'suite') {
29
+ const m = REQ_SUITE.exec(task.name);
30
+ if (m) {
31
+ const id = m[1];
32
+ const set = runtimeCoverage.get(id) ?? new Set();
33
+ for (const c of task.tasks ?? []) {
34
+ // A scenario counts as covered only if it actually executed —
35
+ // skipped/todo scenarios have no run result (enables §8's
36
+ // declared-not-run check).
37
+ if (c.type === 'test' && isExecuted(c))
38
+ set.add(c.name);
39
+ }
40
+ runtimeCoverage.set(id, set);
41
+ }
42
+ }
43
+ for (const c of task.tasks ?? [])
44
+ walk(c);
45
+ };
46
+ for (const file of vitest.state.getFiles())
47
+ walk(file);
48
+ const passed = (vitest.state.getCountOfFailedTests?.() ?? 0) === 0;
49
+ return { passed, runtimeCoverage };
50
+ }
51
+ finally {
52
+ await vitest.close();
53
+ }
54
+ }
55
+ function isExecuted(task) {
56
+ const state = task.result?.state;
57
+ return state === 'pass' || state === 'fail';
58
+ }
59
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/core/runner.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,8EAA8E;AAC9E,4EAA4E;AAC5E,qEAAqE;AAErE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,mFAAmF;AACnF,MAAM,SAAS,GAAG,YAAY,CAAC;AAE/B,kFAAkF;AAClF,MAAM,eAAe,GAAG,CAAC,GAAG,cAAc,CAAC,OAAO,EAAE,eAAe,EAAE,eAAe,CAAC,CAAC;AAetF,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,UAAgC,EAAE;IAElC,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE,EAAE;QAC9D,KAAK,EAAE,KAAK;QACZ,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,CAAC,cAAc,CAAC;QAC5C,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,eAAe;QAC3C,4EAA4E;QAC5E,MAAM,EAAE,KAAK;QACb,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5D,CAAC,CAAC;IACH,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IAED,IAAI,CAAC;QACH,MAAM,eAAe,GAAG,IAAI,GAAG,EAAuB,CAAC;QAEvD,yEAAyE;QACzE,MAAM,IAAI,GAAG,CAAC,IAAc,EAAQ,EAAE;YACpC,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAC1B,MAAM,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACpC,IAAI,CAAC,EAAE,CAAC;oBACN,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;oBACjB,MAAM,GAAG,GAAG,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;oBACzD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;wBACjC,8DAA8D;wBAC9D,0DAA0D;wBAC1D,2BAA2B;wBAC3B,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,UAAU,CAAC,CAAC,CAAC;4BAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;oBAC1D,CAAC;oBACD,eAAe,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;gBAC/B,CAAC;YACH,CAAC;YACD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE;gBAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5C,CAAC,CAAC;QACF,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE;YAAE,IAAI,CAAC,IAA2B,CAAC,CAAC;QAE9E,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC;QACnE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IACrC,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;AACH,CAAC;AASD,SAAS,UAAU,CAAC,IAAc;IAChC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;IACjC,OAAO,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,MAAM,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,42 @@
1
+ import { z } from 'zod';
2
+ /** A single behavioural contract (design §2). */
3
+ export declare const RequirementSchema: z.ZodObject<{
4
+ statement: z.ZodEffects<z.ZodString, string, string>;
5
+ rationale: z.ZodString;
6
+ params: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodNumber, z.ZodString, z.ZodBoolean]>>>;
7
+ outOfScope: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
8
+ }, "strip", z.ZodTypeAny, {
9
+ params: Record<string, string | number | boolean>;
10
+ statement: string;
11
+ rationale: string;
12
+ outOfScope: string[];
13
+ }, {
14
+ statement: string;
15
+ rationale: string;
16
+ params?: Record<string, string | number | boolean> | undefined;
17
+ outOfScope?: string[] | undefined;
18
+ }>;
19
+ /** The requirement registry: stable ID -> requirement (design §2, §5.1). */
20
+ export declare const RegistrySchema: z.ZodRecord<z.ZodString, z.ZodObject<{
21
+ statement: z.ZodEffects<z.ZodString, string, string>;
22
+ rationale: z.ZodString;
23
+ params: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodNumber, z.ZodString, z.ZodBoolean]>>>;
24
+ outOfScope: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
25
+ }, "strip", z.ZodTypeAny, {
26
+ params: Record<string, string | number | boolean>;
27
+ statement: string;
28
+ rationale: string;
29
+ outOfScope: string[];
30
+ }, {
31
+ statement: string;
32
+ rationale: string;
33
+ params?: Record<string, string | number | boolean> | undefined;
34
+ outOfScope?: string[] | undefined;
35
+ }>>;
36
+ /** Parsed (output) shapes — defaults applied. */
37
+ export type Requirement = z.infer<typeof RequirementSchema>;
38
+ export type Registry = z.infer<typeof RegistrySchema>;
39
+ /** Authoring (input) shapes — params / outOfScope optional. */
40
+ export type RequirementInput = z.input<typeof RequirementSchema>;
41
+ export type RegistryInput = z.input<typeof RegistrySchema>;
42
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/core/schema.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,iDAAiD;AACjD,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;EAW5B,CAAC;AAEH,4EAA4E;AAC5E,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;GAG1B,CAAC;AAEF,iDAAiD;AACjD,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC5D,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD,+DAA+D;AAC/D,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AACjE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC"}
@@ -0,0 +1,20 @@
1
+ // Zod structural schema for the requirement registry (design §2, §5.1).
2
+ // This is the single source of truth for the shape of the intent layer; the
3
+ // Requirement / Registry TypeScript types are inferred from it.
4
+ import { z } from 'zod';
5
+ /** A single behavioural contract (design §2). */
6
+ export const RequirementSchema = z.object({
7
+ statement: z
8
+ .string()
9
+ .refine((s) => /\b(SHALL|MUST)\b/.test(s), {
10
+ message: 'statement 必須含 RFC-2119 關鍵字 SHALL/MUST',
11
+ }),
12
+ rationale: z.string().min(10, 'rationale 不可為空(意圖層必須說明為什麼)'),
13
+ params: z
14
+ .record(z.string(), z.union([z.number(), z.string(), z.boolean()]))
15
+ .default({}),
16
+ outOfScope: z.array(z.string()).default([]),
17
+ });
18
+ /** The requirement registry: stable ID -> requirement (design §2, §5.1). */
19
+ export const RegistrySchema = z.record(z.string().regex(/^[A-Z]+-\d+$/, 'ID 需形如 AUTH-3'), RequirementSchema);
20
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/core/schema.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,4EAA4E;AAC5E,gEAAgE;AAEhE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,iDAAiD;AACjD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;QACzC,OAAO,EAAE,uCAAuC;KACjD,CAAC;IACJ,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,4BAA4B,CAAC;IAC3D,MAAM,EAAE,CAAC;SACN,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;SAClE,OAAO,CAAC,EAAE,CAAC;IACd,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CAC5C,CAAC,CAAC;AAEH,4EAA4E;AAC5E,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CACpC,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,eAAe,CAAC,EACjD,iBAAiB,CAClB,CAAC"}
@@ -0,0 +1,39 @@
1
+ export type { Requirement, Registry } from './schema.js';
2
+ /** A scenario extracted statically from a spec file (design §5.2). */
3
+ export interface ParsedScenario {
4
+ reqId: string;
5
+ name: string;
6
+ file: string;
7
+ line: number;
8
+ }
9
+ /**
10
+ * A static signal that a scenario referenced a `.params` value — the producer
11
+ * for the anti-drift heuristic (design §6, mechanism 3).
12
+ */
13
+ export interface ParamRef {
14
+ reqId: string;
15
+ scenario: string;
16
+ }
17
+ /** The strongly-typed static plan produced by the AST parser (design §5.2). */
18
+ export interface AttestPlan {
19
+ scenarios: ParsedScenario[];
20
+ paramRefs: ParamRef[];
21
+ }
22
+ /** Result of an executable verification run (design §5.4). */
23
+ export interface RunResult {
24
+ /** True when no test failed. */
25
+ passed: boolean;
26
+ /** reqId -> set of scenario names that actually executed (from the task tree). */
27
+ runtimeCoverage: Map<string, Set<string>>;
28
+ }
29
+ /** Severity levels for graded reporting (design §5.3). */
30
+ export type Level = 'ERROR' | 'WARNING' | 'INFO';
31
+ /** A single graded diagnostic with location + fix hint (design §5.3). */
32
+ export interface Issue {
33
+ level: Level;
34
+ code: string;
35
+ file?: string;
36
+ line?: number;
37
+ message: string;
38
+ }
39
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAIA,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEzD,sEAAsE;AACtE,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,+EAA+E;AAC/E,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,SAAS,EAAE,QAAQ,EAAE,CAAC;CACvB;AAED,8DAA8D;AAC9D,MAAM,WAAW,SAAS;IACxB,gCAAgC;IAChC,MAAM,EAAE,OAAO,CAAC;IAChB,kFAAkF;IAClF,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;CAC3C;AAED,0DAA0D;AAC1D,MAAM,MAAM,KAAK,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAEjD,yEAAyE;AACzE,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB"}
@@ -0,0 +1,5 @@
1
+ // Central strongly-typed model for Attest.
2
+ // Requirement / Registry are inferred from the Zod schema (design §5.1, the
3
+ // single source of truth for the intent layer); the remaining shapes live here.
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,4EAA4E;AAC5E,gFAAgF"}
@@ -0,0 +1,15 @@
1
+ import type { AttestPlan, Issue, ParamRef, Registry } from './types.js';
2
+ /**
3
+ * Static structural validation (design §5.3). Emits ERROR-level issues for:
4
+ * - orphan-test: a scenario covers an unknown requirement id
5
+ * - uncovered-requirement: a requirement has no scenario
6
+ * - unbound-param: a statement placeholder has no matching param
7
+ */
8
+ export declare function validateStructure(registry: Registry, plan: AttestPlan): Issue[];
9
+ /**
10
+ * Weak anti-drift heuristic (design §6, mechanism 3). Emits an INFO when a
11
+ * scenario covers a requirement that has params but the scenario does not
12
+ * reference them — a place a drifting hardcoded literal could hide.
13
+ */
14
+ export declare function detectPotentialDrift(registry: Registry, plan: AttestPlan, refs: ParamRef[]): Issue[];
15
+ //# sourceMappingURL=validator.d.ts.map
@@ -0,0 +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,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAExE;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,GAAG,KAAK,EAAE,CA4C/E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,QAAQ,EAAE,GACf,KAAK,EAAE,CAmBT"}