@rasensio/aidlc 1.81.0 → 1.83.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/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/discover.d.ts +4 -2
- package/dist/commands/discover.d.ts.map +1 -1
- package/dist/commands/discover.js +12 -6
- package/dist/commands/discover.js.map +1 -1
- package/dist/commands/gate.d.ts.map +1 -1
- package/dist/commands/gate.js +39 -4
- package/dist/commands/gate.js.map +1 -1
- package/dist/commands/transition.d.ts.map +1 -1
- package/dist/commands/transition.js +15 -0
- package/dist/commands/transition.js.map +1 -1
- package/dist/commands/verify.d.ts +16 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +59 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/core/gate.d.ts +7 -0
- package/dist/core/gate.d.ts.map +1 -1
- package/dist/core/gate.js.map +1 -1
- package/dist/core/next-steps.d.ts.map +1 -1
- package/dist/core/next-steps.js +9 -0
- package/dist/core/next-steps.js.map +1 -1
- package/dist/core/types.d.ts +3 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/knowledge/seed.d.ts +7 -0
- package/dist/knowledge/seed.d.ts.map +1 -1
- package/dist/knowledge/seed.js +28 -0
- package/dist/knowledge/seed.js.map +1 -1
- package/dist/knowledge/writer.d.ts +23 -0
- package/dist/knowledge/writer.d.ts.map +1 -1
- package/dist/knowledge/writer.js +113 -0
- package/dist/knowledge/writer.js.map +1 -1
- package/dist/menu/roster.d.ts.map +1 -1
- package/dist/menu/roster.js +3 -0
- package/dist/menu/roster.js.map +1 -1
- package/dist/verify/config.d.ts +29 -0
- package/dist/verify/config.d.ts.map +1 -0
- package/dist/verify/config.js +61 -0
- package/dist/verify/config.js.map +1 -0
- package/dist/verify/gate-criterion.d.ts +29 -0
- package/dist/verify/gate-criterion.d.ts.map +1 -0
- package/dist/verify/gate-criterion.js +86 -0
- package/dist/verify/gate-criterion.js.map +1 -0
- package/dist/verify/git.d.ts +28 -0
- package/dist/verify/git.d.ts.map +1 -0
- package/dist/verify/git.js +76 -0
- package/dist/verify/git.js.map +1 -0
- package/dist/verify/receipt.d.ts +39 -0
- package/dist/verify/receipt.d.ts.map +1 -0
- package/dist/verify/receipt.js +60 -0
- package/dist/verify/receipt.js.map +1 -0
- package/dist/verify/run.d.ts +24 -0
- package/dist/verify/run.d.ts.map +1 -0
- package/dist/verify/run.js +59 -0
- package/dist/verify/run.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Receipt gate criterion, computed at the command layer after `evaluateGate`.
|
|
3
|
+
*
|
|
4
|
+
* Follows the `gate-criterion.ts` convention of `traceability/`, `tasks/` and
|
|
5
|
+
* `compliance/`: an evaluator plus a message helper, registered in `commands/gate.ts`
|
|
6
|
+
* and `commands/transition.ts`. It returns `null` outside the testing phase and in
|
|
7
|
+
* solo mode, so a project that never opted in gets no field, no warning and no git
|
|
8
|
+
* call (tool-written-test-receipts/AC-19, AC-23).
|
|
9
|
+
*
|
|
10
|
+
* `test-results.md` is never read here. That is what makes AC-12 hold: no prose can
|
|
11
|
+
* pass a receipt whose exit code is not 0.
|
|
12
|
+
*
|
|
13
|
+
* Requirements: tool-written-test-receipts/AC-11 to AC-19, AC-23
|
|
14
|
+
*/
|
|
15
|
+
export type ReceiptStatus = 'ok' | 'not-configured' | 'missing' | 'unreadable' | 'command-changed' | 'failed' | 'dirty' | 'stale';
|
|
16
|
+
export interface ReceiptResult {
|
|
17
|
+
mode: 'verified';
|
|
18
|
+
status: ReceiptStatus;
|
|
19
|
+
/** The receipt's commit, when one was read. */
|
|
20
|
+
commit: string | null;
|
|
21
|
+
exit_code: number | null;
|
|
22
|
+
/** The first paths that made the receipt stale, at most `MAX_CHANGED`. */
|
|
23
|
+
changed: string[];
|
|
24
|
+
warnings: string[];
|
|
25
|
+
}
|
|
26
|
+
export declare function evaluateReceiptCriterion(projectRoot: string, instance: string, phase: string): ReceiptResult | null;
|
|
27
|
+
/** One line naming why the receipt was refused (AC-18). */
|
|
28
|
+
export declare function receiptMessage(r: ReceiptResult, instance: string): string;
|
|
29
|
+
//# sourceMappingURL=gate-criterion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate-criterion.d.ts","sourceRoot":"","sources":["../../src/verify/gate-criterion.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,MAAM,MAAM,aAAa,GACrB,IAAI,GACJ,gBAAgB,GAChB,SAAS,GACT,YAAY,GACZ,iBAAiB,GACjB,QAAQ,GACR,OAAO,GACP,OAAO,CAAC;AAEZ,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;IACtB,+CAA+C;IAC/C,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAID,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,aAAa,GAAG,IAAI,CA4CtB;AAED,2DAA2D;AAC3D,wBAAgB,cAAc,CAAC,CAAC,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAqBzE"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Receipt gate criterion, computed at the command layer after `evaluateGate`.
|
|
3
|
+
*
|
|
4
|
+
* Follows the `gate-criterion.ts` convention of `traceability/`, `tasks/` and
|
|
5
|
+
* `compliance/`: an evaluator plus a message helper, registered in `commands/gate.ts`
|
|
6
|
+
* and `commands/transition.ts`. It returns `null` outside the testing phase and in
|
|
7
|
+
* solo mode, so a project that never opted in gets no field, no warning and no git
|
|
8
|
+
* call (tool-written-test-receipts/AC-19, AC-23).
|
|
9
|
+
*
|
|
10
|
+
* `test-results.md` is never read here. That is what makes AC-12 hold: no prose can
|
|
11
|
+
* pass a receipt whose exit code is not 0.
|
|
12
|
+
*
|
|
13
|
+
* Requirements: tool-written-test-receipts/AC-11 to AC-19, AC-23
|
|
14
|
+
*/
|
|
15
|
+
import { readAssurance, readVerifyCommand } from './config.js';
|
|
16
|
+
import { changedOutside } from './git.js';
|
|
17
|
+
import { hashCommand, readReceipt } from './receipt.js';
|
|
18
|
+
const MAX_CHANGED = 10;
|
|
19
|
+
export function evaluateReceiptCriterion(projectRoot, instance, phase) {
|
|
20
|
+
if (phase !== 'testing')
|
|
21
|
+
return null;
|
|
22
|
+
const assurance = readAssurance(projectRoot);
|
|
23
|
+
if (assurance.mode !== 'verified')
|
|
24
|
+
return null;
|
|
25
|
+
const warnings = assurance.unrecognised === null
|
|
26
|
+
? []
|
|
27
|
+
: [
|
|
28
|
+
`assurance.default '${assurance.unrecognised}' is not 'solo' or 'verified' — ` +
|
|
29
|
+
'treated as verified, so the receipt check stays on.',
|
|
30
|
+
];
|
|
31
|
+
const result = (status, extra = {}) => ({
|
|
32
|
+
mode: 'verified',
|
|
33
|
+
status,
|
|
34
|
+
commit: extra.commit ?? null,
|
|
35
|
+
exit_code: extra.exit_code ?? null,
|
|
36
|
+
changed: extra.changed ?? [],
|
|
37
|
+
warnings,
|
|
38
|
+
});
|
|
39
|
+
const command = readVerifyCommand(projectRoot);
|
|
40
|
+
if (command === null)
|
|
41
|
+
return result('not-configured');
|
|
42
|
+
const read = readReceipt(projectRoot, instance);
|
|
43
|
+
if (!read.ok)
|
|
44
|
+
return result(read.reason);
|
|
45
|
+
const { receipt } = read;
|
|
46
|
+
const seen = { commit: receipt.commit, exit_code: receipt.exit_code };
|
|
47
|
+
if (receipt.command_sha256 !== hashCommand(command))
|
|
48
|
+
return result('command-changed', seen);
|
|
49
|
+
if (receipt.exit_code !== 0)
|
|
50
|
+
return result('failed', seen);
|
|
51
|
+
if (receipt.dirty)
|
|
52
|
+
return result('dirty', seen);
|
|
53
|
+
// No shortcut on HEAD === commit: the working tree can differ from HEAD, and an
|
|
54
|
+
// uncommitted edit made after the run is as untested as a committed one (AC-13).
|
|
55
|
+
const changed = changedOutside(projectRoot, instance, receipt.commit);
|
|
56
|
+
if (!changed.ok)
|
|
57
|
+
return result('stale', { ...seen, changed: [`(${changed.reason})`] });
|
|
58
|
+
if (changed.paths.length > 0) {
|
|
59
|
+
return result('stale', { ...seen, changed: changed.paths.slice(0, MAX_CHANGED) });
|
|
60
|
+
}
|
|
61
|
+
return result('ok', seen);
|
|
62
|
+
}
|
|
63
|
+
/** One line naming why the receipt was refused (AC-18). */
|
|
64
|
+
export function receiptMessage(r, instance) {
|
|
65
|
+
const file = `.aidlc/state/${instance}/receipt.json`;
|
|
66
|
+
const short = r.commit?.slice(0, 7) ?? '';
|
|
67
|
+
switch (r.status) {
|
|
68
|
+
case 'ok':
|
|
69
|
+
return `receipt ${short} matches`;
|
|
70
|
+
case 'not-configured':
|
|
71
|
+
return 'verified mode needs a test command: set verify.command in .aidlc/config.yaml';
|
|
72
|
+
case 'missing':
|
|
73
|
+
return `no test receipt: ${file} does not exist`;
|
|
74
|
+
case 'unreadable':
|
|
75
|
+
return `test receipt unreadable: ${file} is not a receipt aidlc verify wrote`;
|
|
76
|
+
case 'command-changed':
|
|
77
|
+
return `test receipt ${short} ran a different command than verify.command now names`;
|
|
78
|
+
case 'failed':
|
|
79
|
+
return `test receipt ${short} records exit code ${r.exit_code} — the tests failed`;
|
|
80
|
+
case 'dirty':
|
|
81
|
+
return `test receipt ${short} was taken with uncommitted changes outside the instance state directory`;
|
|
82
|
+
case 'stale':
|
|
83
|
+
return `test receipt ${short} is stale — changed since: ${r.changed.join(', ')}`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=gate-criterion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate-criterion.js","sourceRoot":"","sources":["../../src/verify/gate-criterion.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAuBxD,MAAM,WAAW,GAAG,EAAE,CAAC;AAEvB,MAAM,UAAU,wBAAwB,CACtC,WAAmB,EACnB,QAAgB,EAChB,KAAa;IAEb,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,SAAS,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IAC7C,IAAI,SAAS,CAAC,IAAI,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IAE/C,MAAM,QAAQ,GACZ,SAAS,CAAC,YAAY,KAAK,IAAI;QAC7B,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC;YACE,sBAAsB,SAAS,CAAC,YAAY,kCAAkC;gBAC5E,qDAAqD;SACxD,CAAC;IACR,MAAM,MAAM,GAAG,CACb,MAAqB,EACrB,QAA0E,EAAE,EAC7D,EAAE,CAAC,CAAC;QACnB,IAAI,EAAE,UAAU;QAChB,MAAM;QACN,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI;QAC5B,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI;QAClC,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,EAAE;QAC5B,QAAQ;KACT,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAC/C,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAEtD,MAAM,IAAI,GAAG,WAAW,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAChD,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACzB,MAAM,IAAI,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC;IAEtE,IAAI,OAAO,CAAC,cAAc,KAAK,WAAW,CAAC,OAAO,CAAC;QAAE,OAAO,MAAM,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC;IAC5F,IAAI,OAAO,CAAC,SAAS,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC3D,IAAI,OAAO,CAAC,KAAK;QAAE,OAAO,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAEhD,gFAAgF;IAChF,iFAAiF;IACjF,MAAM,OAAO,GAAG,cAAc,CAAC,WAAW,EAAE,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACtE,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,OAAO,MAAM,CAAC,OAAO,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC;IACvF,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,OAAO,MAAM,CAAC,OAAO,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IACpF,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,cAAc,CAAC,CAAgB,EAAE,QAAgB;IAC/D,MAAM,IAAI,GAAG,gBAAgB,QAAQ,eAAe,CAAC;IACrD,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1C,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC;QACjB,KAAK,IAAI;YACP,OAAO,WAAW,KAAK,UAAU,CAAC;QACpC,KAAK,gBAAgB;YACnB,OAAO,8EAA8E,CAAC;QACxF,KAAK,SAAS;YACZ,OAAO,oBAAoB,IAAI,iBAAiB,CAAC;QACnD,KAAK,YAAY;YACf,OAAO,4BAA4B,IAAI,sCAAsC,CAAC;QAChF,KAAK,iBAAiB;YACpB,OAAO,gBAAgB,KAAK,wDAAwD,CAAC;QACvF,KAAK,QAAQ;YACX,OAAO,gBAAgB,KAAK,sBAAsB,CAAC,CAAC,SAAS,qBAAqB,CAAC;QACrF,KAAK,OAAO;YACV,OAAO,gBAAgB,KAAK,0EAA0E,CAAC;QACzG,KAAK,OAAO;YACV,OAAO,gBAAgB,KAAK,8BAA8B,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IACrF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The git reads behind a receipt: which commit was tested, and what has changed since.
|
|
3
|
+
*
|
|
4
|
+
* Paths are compared relative to the repository's top level, and the instance's state
|
|
5
|
+
* directory is expressed the same way, so a project nested inside a larger repository
|
|
6
|
+
* still recognises its own state files.
|
|
7
|
+
*
|
|
8
|
+
* Requirements: tool-written-test-receipts/AC-6, AC-13, AC-14
|
|
9
|
+
*/
|
|
10
|
+
export type ChangedResult = {
|
|
11
|
+
ok: true;
|
|
12
|
+
paths: string[];
|
|
13
|
+
} | {
|
|
14
|
+
ok: false;
|
|
15
|
+
reason: string;
|
|
16
|
+
};
|
|
17
|
+
/** HEAD's full sha; null outside a repository or before the first commit (AC-9). */
|
|
18
|
+
export declare function headCommit(projectRoot: string): string | null;
|
|
19
|
+
/**
|
|
20
|
+
* Paths outside `.aidlc/state/<instance>/` that differ.
|
|
21
|
+
*
|
|
22
|
+
* With `since` null: from the working tree's status — the dirty check before a run
|
|
23
|
+
* (AC-6). With a commit: that commit against the working tree, so committed, staged
|
|
24
|
+
* and unstaged changes all count, plus untracked files (AC-13, AC-14). A commit git
|
|
25
|
+
* cannot resolve — a shallow CI clone — is a failure, which reads as stale.
|
|
26
|
+
*/
|
|
27
|
+
export declare function changedOutside(projectRoot: string, instance: string, since: string | null): ChangedResult;
|
|
28
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/verify/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,MAAM,MAAM,aAAa,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAW1F,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM7D;AAyBD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GAAG,IAAI,GACnB,aAAa,CAgBf"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The git reads behind a receipt: which commit was tested, and what has changed since.
|
|
3
|
+
*
|
|
4
|
+
* Paths are compared relative to the repository's top level, and the instance's state
|
|
5
|
+
* directory is expressed the same way, so a project nested inside a larger repository
|
|
6
|
+
* still recognises its own state files.
|
|
7
|
+
*
|
|
8
|
+
* Requirements: tool-written-test-receipts/AC-6, AC-13, AC-14
|
|
9
|
+
*/
|
|
10
|
+
import { execFileSync } from 'node:child_process';
|
|
11
|
+
import { realpathSync } from 'node:fs';
|
|
12
|
+
import { join, relative, sep } from 'node:path';
|
|
13
|
+
function git(projectRoot, args) {
|
|
14
|
+
return execFileSync('git', args, {
|
|
15
|
+
cwd: projectRoot,
|
|
16
|
+
encoding: 'utf8',
|
|
17
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
18
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/** HEAD's full sha; null outside a repository or before the first commit (AC-9). */
|
|
22
|
+
export function headCommit(projectRoot) {
|
|
23
|
+
try {
|
|
24
|
+
return git(projectRoot, ['rev-parse', '--verify', '--quiet', 'HEAD']).trim() || null;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** The instance state directory as a top-level-relative prefix ending in `/`. */
|
|
31
|
+
function instancePrefix(projectRoot, instance) {
|
|
32
|
+
const top = realpathSync(git(projectRoot, ['rev-parse', '--show-toplevel']).trim());
|
|
33
|
+
const dir = join(realpathSync(projectRoot), '.aidlc', 'state', instance);
|
|
34
|
+
return relative(top, dir).split(sep).join('/') + '/';
|
|
35
|
+
}
|
|
36
|
+
function splitZ(out) {
|
|
37
|
+
return out.split('\0').filter((p) => p !== '');
|
|
38
|
+
}
|
|
39
|
+
/** Porcelain v1 -z: `XY path\0`, with a rename's source as one extra entry after it. */
|
|
40
|
+
function statusPaths(out) {
|
|
41
|
+
const entries = splitZ(out);
|
|
42
|
+
const paths = [];
|
|
43
|
+
for (let i = 0; i < entries.length; i++) {
|
|
44
|
+
const entry = entries[i];
|
|
45
|
+
paths.push(entry.slice(3));
|
|
46
|
+
if (entry[0] === 'R' || entry[0] === 'C')
|
|
47
|
+
paths.push(entries[++i] ?? '');
|
|
48
|
+
}
|
|
49
|
+
return paths.filter((p) => p !== '');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Paths outside `.aidlc/state/<instance>/` that differ.
|
|
53
|
+
*
|
|
54
|
+
* With `since` null: from the working tree's status — the dirty check before a run
|
|
55
|
+
* (AC-6). With a commit: that commit against the working tree, so committed, staged
|
|
56
|
+
* and unstaged changes all count, plus untracked files (AC-13, AC-14). A commit git
|
|
57
|
+
* cannot resolve — a shallow CI clone — is a failure, which reads as stale.
|
|
58
|
+
*/
|
|
59
|
+
export function changedOutside(projectRoot, instance, since) {
|
|
60
|
+
try {
|
|
61
|
+
const prefix = instancePrefix(projectRoot, instance);
|
|
62
|
+
const paths = since === null
|
|
63
|
+
? statusPaths(git(projectRoot, ['status', '--porcelain=v1', '-z', '--untracked-files=all']))
|
|
64
|
+
: [
|
|
65
|
+
...splitZ(git(projectRoot, ['diff', '--name-only', '-z', '--no-renames', since, '--'])),
|
|
66
|
+
...splitZ(git(projectRoot, ['ls-files', '--others', '--exclude-standard', '-z', '--full-name'])),
|
|
67
|
+
];
|
|
68
|
+
// `git diff` and `status` print top-level-relative paths; `ls-files` does with --full-name.
|
|
69
|
+
return { ok: true, paths: [...new Set(paths)].filter((p) => !p.startsWith(prefix)) };
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
const stderr = err.stderr?.trim();
|
|
73
|
+
return { ok: false, reason: stderr || err.message };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/verify/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `receipt.json` — the record `aidlc verify` writes after running the test command.
|
|
3
|
+
*
|
|
4
|
+
* It holds hashes and metadata only. Test output can print tokens and the receipt is
|
|
5
|
+
* committed, so the output itself is never stored (tool-written-test-receipts/AC-7).
|
|
6
|
+
* A read that finds any field of the wrong type is `unreadable`, never a pass (AC-16).
|
|
7
|
+
*
|
|
8
|
+
* Requirements: tool-written-test-receipts/AC-5, AC-7, AC-16
|
|
9
|
+
*/
|
|
10
|
+
export declare const RECEIPT_FILE = "receipt.json";
|
|
11
|
+
export interface Receipt {
|
|
12
|
+
version: 1;
|
|
13
|
+
/** HEAD when the run started. */
|
|
14
|
+
commit: string;
|
|
15
|
+
/** Whether any path outside the instance's state directory had changed (AC-6). */
|
|
16
|
+
dirty: boolean;
|
|
17
|
+
command_sha256: string;
|
|
18
|
+
/** A signal is recorded as 128 + its number (AC-10). */
|
|
19
|
+
exit_code: number;
|
|
20
|
+
/** sha256 of stdout and stderr bytes in arrival order. */
|
|
21
|
+
output_sha256: string;
|
|
22
|
+
duration_ms: number;
|
|
23
|
+
cli_version: string;
|
|
24
|
+
/** ISO 8601 UTC. */
|
|
25
|
+
finished_at: string;
|
|
26
|
+
}
|
|
27
|
+
export type ReadReceiptResult = {
|
|
28
|
+
ok: true;
|
|
29
|
+
receipt: Receipt;
|
|
30
|
+
} | {
|
|
31
|
+
ok: false;
|
|
32
|
+
reason: 'missing' | 'unreadable';
|
|
33
|
+
};
|
|
34
|
+
export declare function sha256(data: string | Buffer): string;
|
|
35
|
+
export declare function hashCommand(command: string): string;
|
|
36
|
+
export declare function receiptPath(projectRoot: string, instance: string): string;
|
|
37
|
+
export declare function readReceipt(projectRoot: string, instance: string): ReadReceiptResult;
|
|
38
|
+
export declare function writeReceipt(projectRoot: string, instance: string, receipt: Receipt): string;
|
|
39
|
+
//# sourceMappingURL=receipt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipt.d.ts","sourceRoot":"","sources":["../../src/verify/receipt.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAE3C,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,CAAC,CAAC;IACX,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC9B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,SAAS,GAAG,YAAY,CAAA;CAAE,CAAC;AASpD,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEzE;AAkBD,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CASpF;AAED,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAI5F"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `receipt.json` — the record `aidlc verify` writes after running the test command.
|
|
3
|
+
*
|
|
4
|
+
* It holds hashes and metadata only. Test output can print tokens and the receipt is
|
|
5
|
+
* committed, so the output itself is never stored (tool-written-test-receipts/AC-7).
|
|
6
|
+
* A read that finds any field of the wrong type is `unreadable`, never a pass (AC-16).
|
|
7
|
+
*
|
|
8
|
+
* Requirements: tool-written-test-receipts/AC-5, AC-7, AC-16
|
|
9
|
+
*/
|
|
10
|
+
import { createHash } from 'node:crypto';
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
export const RECEIPT_FILE = 'receipt.json';
|
|
14
|
+
/**
|
|
15
|
+
* A full git object id. Checked before the commit reaches `git diff`, so a crafted
|
|
16
|
+
* receipt cannot pass an option where a revision belongs.
|
|
17
|
+
*/
|
|
18
|
+
const COMMIT_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
19
|
+
const SHA256_RE = /^[0-9a-f]{64}$/;
|
|
20
|
+
export function sha256(data) {
|
|
21
|
+
return createHash('sha256').update(data).digest('hex');
|
|
22
|
+
}
|
|
23
|
+
export function hashCommand(command) {
|
|
24
|
+
return sha256(Buffer.from(command, 'utf8'));
|
|
25
|
+
}
|
|
26
|
+
export function receiptPath(projectRoot, instance) {
|
|
27
|
+
return join(projectRoot, '.aidlc', 'state', instance, RECEIPT_FILE);
|
|
28
|
+
}
|
|
29
|
+
function isReceipt(raw) {
|
|
30
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
|
|
31
|
+
return false;
|
|
32
|
+
const r = raw;
|
|
33
|
+
return (r.version === 1 &&
|
|
34
|
+
typeof r.commit === 'string' && COMMIT_RE.test(r.commit) &&
|
|
35
|
+
typeof r.dirty === 'boolean' &&
|
|
36
|
+
typeof r.command_sha256 === 'string' && SHA256_RE.test(r.command_sha256) &&
|
|
37
|
+
typeof r.exit_code === 'number' && Number.isInteger(r.exit_code) &&
|
|
38
|
+
typeof r.output_sha256 === 'string' && SHA256_RE.test(r.output_sha256) &&
|
|
39
|
+
typeof r.duration_ms === 'number' && r.duration_ms >= 0 &&
|
|
40
|
+
typeof r.cli_version === 'string' &&
|
|
41
|
+
typeof r.finished_at === 'string');
|
|
42
|
+
}
|
|
43
|
+
export function readReceipt(projectRoot, instance) {
|
|
44
|
+
const path = receiptPath(projectRoot, instance);
|
|
45
|
+
if (!existsSync(path))
|
|
46
|
+
return { ok: false, reason: 'missing' };
|
|
47
|
+
try {
|
|
48
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
49
|
+
return isReceipt(parsed) ? { ok: true, receipt: parsed } : { ok: false, reason: 'unreadable' };
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return { ok: false, reason: 'unreadable' };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export function writeReceipt(projectRoot, instance, receipt) {
|
|
56
|
+
const path = receiptPath(projectRoot, instance);
|
|
57
|
+
writeFileSync(path, JSON.stringify(receipt, null, 2) + '\n', 'utf8');
|
|
58
|
+
return path;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=receipt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipt.js","sourceRoot":"","sources":["../../src/verify/receipt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run `verify.command` and write the receipt.
|
|
3
|
+
*
|
|
4
|
+
* The command runs through the platform shell because it is a string a person wrote
|
|
5
|
+
* for one (`pnpm test && pnpm lint` must work). Output is shown live and hashed as it
|
|
6
|
+
* arrives; it is never stored (AC-7). The receipt is written whether the command
|
|
7
|
+
* passed or failed, so a failing run replaces an older passing one (AC-10).
|
|
8
|
+
*
|
|
9
|
+
* Requirements: tool-written-test-receipts/AC-4, AC-5, AC-6, AC-10
|
|
10
|
+
*/
|
|
11
|
+
import type { Writable } from 'node:stream';
|
|
12
|
+
import type { Receipt } from './receipt.js';
|
|
13
|
+
export interface VerifyOutput {
|
|
14
|
+
stdout: Writable;
|
|
15
|
+
stderr: Writable;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* @param commit HEAD, already resolved by the caller (which refuses when there is none).
|
|
19
|
+
*/
|
|
20
|
+
export declare function runVerify(projectRoot: string, instance: string, command: string, commit: string, out?: VerifyOutput): Promise<{
|
|
21
|
+
receipt: Receipt;
|
|
22
|
+
path: string;
|
|
23
|
+
}>;
|
|
24
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/verify/run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAK5C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,QAAQ,CAAC;IACjB,MAAM,EAAE,QAAQ,CAAC;CAClB;AAQD;;GAEG;AACH,wBAAsB,SAAS,CAC7B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,GAAG,GAAE,YAAiE,GACrE,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAmC7C"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run `verify.command` and write the receipt.
|
|
3
|
+
*
|
|
4
|
+
* The command runs through the platform shell because it is a string a person wrote
|
|
5
|
+
* for one (`pnpm test && pnpm lint` must work). Output is shown live and hashed as it
|
|
6
|
+
* arrives; it is never stored (AC-7). The receipt is written whether the command
|
|
7
|
+
* passed or failed, so a failing run replaces an older passing one (AC-10).
|
|
8
|
+
*
|
|
9
|
+
* Requirements: tool-written-test-receipts/AC-4, AC-5, AC-6, AC-10
|
|
10
|
+
*/
|
|
11
|
+
import { spawn } from 'node:child_process';
|
|
12
|
+
import { createHash } from 'node:crypto';
|
|
13
|
+
import { constants } from 'node:os';
|
|
14
|
+
import { VERSION } from '../core/version.js';
|
|
15
|
+
import { changedOutside } from './git.js';
|
|
16
|
+
import { hashCommand, writeReceipt } from './receipt.js';
|
|
17
|
+
/** Exit code for a run killed by a signal: the shell convention, 128 + its number. */
|
|
18
|
+
function signalExitCode(signal) {
|
|
19
|
+
const n = signal ? constants.signals[signal] : undefined;
|
|
20
|
+
return n === undefined ? 1 : 128 + n;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* @param commit HEAD, already resolved by the caller (which refuses when there is none).
|
|
24
|
+
*/
|
|
25
|
+
export async function runVerify(projectRoot, instance, command, commit, out = { stdout: process.stdout, stderr: process.stderr }) {
|
|
26
|
+
// Measured before the run, so what the run itself writes cannot make it dirty. An
|
|
27
|
+
// unanswerable status counts as dirty: unknown is not clean (AC-6).
|
|
28
|
+
const changed = changedOutside(projectRoot, instance, null);
|
|
29
|
+
const dirty = !changed.ok || changed.paths.length > 0;
|
|
30
|
+
const hash = createHash('sha256');
|
|
31
|
+
const started = Date.now();
|
|
32
|
+
const exitCode = await new Promise((resolve) => {
|
|
33
|
+
const child = spawn(command, { shell: true, cwd: projectRoot, stdio: ['inherit', 'pipe', 'pipe'] });
|
|
34
|
+
child.stdout.on('data', (chunk) => {
|
|
35
|
+
hash.update(chunk);
|
|
36
|
+
out.stdout.write(chunk);
|
|
37
|
+
});
|
|
38
|
+
child.stderr.on('data', (chunk) => {
|
|
39
|
+
hash.update(chunk);
|
|
40
|
+
out.stderr.write(chunk);
|
|
41
|
+
});
|
|
42
|
+
// A shell that cannot start is the shell's "command not found".
|
|
43
|
+
child.on('error', () => resolve(127));
|
|
44
|
+
child.on('close', (code, signal) => resolve(code ?? signalExitCode(signal)));
|
|
45
|
+
});
|
|
46
|
+
const receipt = {
|
|
47
|
+
version: 1,
|
|
48
|
+
commit,
|
|
49
|
+
dirty,
|
|
50
|
+
command_sha256: hashCommand(command),
|
|
51
|
+
exit_code: exitCode,
|
|
52
|
+
output_sha256: hash.digest('hex'),
|
|
53
|
+
duration_ms: Date.now() - started,
|
|
54
|
+
cli_version: VERSION,
|
|
55
|
+
finished_at: new Date().toISOString(),
|
|
56
|
+
};
|
|
57
|
+
return { receipt, path: writeReceipt(projectRoot, instance, receipt) };
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/verify/run.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rasensio/aidlc",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.83.0",
|
|
4
4
|
"description": "AI Development Lifecycle Framework — structured lifecycle guidance for AI coding agents across platforms",
|
|
5
5
|
"homepage": "https://aidlc.rodrigoasensio.com",
|
|
6
6
|
"repository": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"commander": "15.0.0",
|
|
32
32
|
"picomatch": "4.0.5",
|
|
33
33
|
"yaml": "2.9.0",
|
|
34
|
-
"@rasensio/aidlc-content": "1.
|
|
34
|
+
"@rasensio/aidlc-content": "1.83.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "22.15.32",
|