@rasensio/aidlc 1.19.0 → 1.20.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/change/cross-check.d.ts +28 -0
- package/dist/change/cross-check.d.ts.map +1 -0
- package/dist/change/cross-check.js +53 -0
- package/dist/change/cross-check.js.map +1 -0
- package/dist/change/impact.d.ts +57 -0
- package/dist/change/impact.d.ts.map +1 -0
- package/dist/change/impact.js +288 -0
- package/dist/change/impact.js.map +1 -0
- package/dist/change/index.d.ts +12 -0
- package/dist/change/index.d.ts.map +1 -0
- package/dist/change/index.js +11 -0
- package/dist/change/index.js.map +1 -0
- package/dist/change/parser.d.ts +52 -0
- package/dist/change/parser.d.ts.map +1 -0
- package/dist/change/parser.js +339 -0
- package/dist/change/parser.js.map +1 -0
- package/dist/change/reopen.d.ts +80 -0
- package/dist/change/reopen.d.ts.map +1 -0
- package/dist/change/reopen.js +227 -0
- package/dist/change/reopen.js.map +1 -0
- package/dist/change/types.d.ts +144 -0
- package/dist/change/types.d.ts.map +1 -0
- package/dist/change/types.js +8 -0
- package/dist/change/types.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/amend.d.ts +22 -0
- package/dist/commands/amend.d.ts.map +1 -0
- package/dist/commands/amend.js +355 -0
- package/dist/commands/amend.js.map +1 -0
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +6 -0
- package/dist/commands/docs.js.map +1 -1
- package/dist/core/lifecycle.d.ts +3 -0
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +13 -2
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core/types.d.ts +37 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/menu/roster.d.ts.map +1 -1
- package/dist/menu/roster.js +4 -0
- package/dist/menu/roster.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Amendment cross-check (FR2): do the ids an amendment claims match what the
|
|
3
|
+
* requirements file actually defines?
|
|
4
|
+
*
|
|
5
|
+
* Two checks only, and both are data (AC-13) — they never fail a gate and never
|
|
6
|
+
* change an exit code. The obvious third check, "a retired criterion no
|
|
7
|
+
* amendment claims", was specified and then dropped: measured on this repo it
|
|
8
|
+
* fires 44 times across 12 instances that have never been amended, which is
|
|
9
|
+
* noise arriving before any signal (retired AC-12).
|
|
10
|
+
*
|
|
11
|
+
* Warnings from here surface in `aidlc amend` output alone (AC-74). Putting
|
|
12
|
+
* them where the traceability warnings surface — `aidlc gate --json` and the
|
|
13
|
+
* `criterionWarnings` block at `commands/transition.ts:408-419` — would have
|
|
14
|
+
* changed output for every never-amended instance on the first run.
|
|
15
|
+
*
|
|
16
|
+
* Requirements: change-management/AC-10, AC-11, AC-13
|
|
17
|
+
*/
|
|
18
|
+
import type { ParsedRequirements } from '../traceability/types.js';
|
|
19
|
+
import type { Amendment } from './types.js';
|
|
20
|
+
/**
|
|
21
|
+
* Cross-check one amendment's claimed ids against the AC definitions.
|
|
22
|
+
*
|
|
23
|
+
* @param amendment - The amendment under consideration.
|
|
24
|
+
* @param parsed - Parsed requirements for the same file.
|
|
25
|
+
* @returns Warnings, ordered `Retires:` then `Issues:`, ascending by id.
|
|
26
|
+
*/
|
|
27
|
+
export declare function crossCheck(amendment: Amendment, parsed: ParsedRequirements): string[];
|
|
28
|
+
//# sourceMappingURL=cross-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-check.d.ts","sourceRoot":"","sources":["../../src/change/cross-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,kBAAkB,GAAG,MAAM,EAAE,CAsCrF"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Amendment cross-check (FR2): do the ids an amendment claims match what the
|
|
3
|
+
* requirements file actually defines?
|
|
4
|
+
*
|
|
5
|
+
* Two checks only, and both are data (AC-13) — they never fail a gate and never
|
|
6
|
+
* change an exit code. The obvious third check, "a retired criterion no
|
|
7
|
+
* amendment claims", was specified and then dropped: measured on this repo it
|
|
8
|
+
* fires 44 times across 12 instances that have never been amended, which is
|
|
9
|
+
* noise arriving before any signal (retired AC-12).
|
|
10
|
+
*
|
|
11
|
+
* Warnings from here surface in `aidlc amend` output alone (AC-74). Putting
|
|
12
|
+
* them where the traceability warnings surface — `aidlc gate --json` and the
|
|
13
|
+
* `criterionWarnings` block at `commands/transition.ts:408-419` — would have
|
|
14
|
+
* changed output for every never-amended instance on the first run.
|
|
15
|
+
*
|
|
16
|
+
* Requirements: change-management/AC-10, AC-11, AC-13
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Cross-check one amendment's claimed ids against the AC definitions.
|
|
20
|
+
*
|
|
21
|
+
* @param amendment - The amendment under consideration.
|
|
22
|
+
* @param parsed - Parsed requirements for the same file.
|
|
23
|
+
* @returns Warnings, ordered `Retires:` then `Issues:`, ascending by id.
|
|
24
|
+
*/
|
|
25
|
+
export function crossCheck(amendment, parsed) {
|
|
26
|
+
const byId = new Map(parsed.defs.map((def) => [def.id, def]));
|
|
27
|
+
const warnings = [];
|
|
28
|
+
// AC-10: an id claimed as retired that is not defined as retired.
|
|
29
|
+
for (const id of [...amendment.retires].sort((a, b) => a - b)) {
|
|
30
|
+
const def = byId.get(id);
|
|
31
|
+
if (!def) {
|
|
32
|
+
warnings.push(`Amendment ${amendment.n} retires AC-${id}, which this file does not define`);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (def.status !== 'retired') {
|
|
36
|
+
warnings.push(`Amendment ${amendment.n} retires AC-${id}, but line ${def.line} defines it as ` +
|
|
37
|
+
`${def.status} — mark the definition bullet retired, or drop it from Retires:`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// AC-11: an id claimed as issued that is undefined, or defined as retired.
|
|
41
|
+
for (const id of [...amendment.issues].sort((a, b) => a - b)) {
|
|
42
|
+
const def = byId.get(id);
|
|
43
|
+
if (!def) {
|
|
44
|
+
warnings.push(`Amendment ${amendment.n} issues AC-${id}, which this file does not define`);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (def.status === 'retired') {
|
|
48
|
+
warnings.push(`Amendment ${amendment.n} issues AC-${id}, but line ${def.line} defines it as retired`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return warnings;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=cross-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-check.js","sourceRoot":"","sources":["../../src/change/cross-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,SAAoB,EAAE,MAA0B;IACzE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,kEAAkE;IAClE,KAAK,MAAM,EAAE,IAAI,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC9D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzB,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,QAAQ,CAAC,IAAI,CACX,aAAa,SAAS,CAAC,CAAC,eAAe,EAAE,mCAAmC,CAC7E,CAAC;YACF,SAAS;QACX,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC7B,QAAQ,CAAC,IAAI,CACX,aAAa,SAAS,CAAC,CAAC,eAAe,EAAE,cAAc,GAAG,CAAC,IAAI,iBAAiB;gBAC9E,GAAG,GAAG,CAAC,MAAM,iEAAiE,CACjF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,KAAK,MAAM,EAAE,IAAI,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC7D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzB,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,QAAQ,CAAC,IAAI,CACX,aAAa,SAAS,CAAC,CAAC,cAAc,EAAE,mCAAmC,CAC5E,CAAC;YACF,SAAS;QACX,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC7B,QAAQ,CAAC,IAAI,CACX,aAAa,SAAS,CAAC,CAAC,cAAc,EAAE,cAAc,GAAG,CAAC,IAAI,wBAAwB,CACvF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Impact walk (FR3): which citations of a retired criterion are still out there?
|
|
3
|
+
*
|
|
4
|
+
* Read-only and advisory. It performs no writes (AC-22) and its `suggestedReopens`
|
|
5
|
+
* is a suggestion the author may paste into `Reopens:` — `aidlc amend` never reads
|
|
6
|
+
* it (AC-58). The first draft derived the reopen set from this walk, which was
|
|
7
|
+
* degenerate: `requirements.md` is the requirements phase's declared artifact and
|
|
8
|
+
* always cites every retired id (the retirement bullet, the `superseded by AC-m`
|
|
9
|
+
* clause, and the amendment's own `Retires:` field), so every amendment would have
|
|
10
|
+
* reopened requirements. A destructive state write must not depend on a
|
|
11
|
+
* prose-scanning heuristic.
|
|
12
|
+
*
|
|
13
|
+
* Three halves, each with a stated resolution rule, because the two shipped rules
|
|
14
|
+
* genuinely differ and picking one for both would be wrong:
|
|
15
|
+
*
|
|
16
|
+
* - **Test files** use the uniqueness rule at `traceability/matrix.ts:77-87` — a
|
|
17
|
+
* bare `AC-n` counts only when exactly one AC-bearing instance defines the id.
|
|
18
|
+
* - **Tasks** use the owning-instance rule (`tasks/types.ts:15-19`), which that file
|
|
19
|
+
* documents as "never traceability's uniqueness rule".
|
|
20
|
+
* - **Artifacts** use the same tokens as the scanner, resolved by the test rule.
|
|
21
|
+
*
|
|
22
|
+
* `requirements.md` is the one artifact never read: it is the authority being
|
|
23
|
+
* amended, not a consumer of it (AC-81). AC-definition-shaped bullets *elsewhere*
|
|
24
|
+
* are reported rather than excluded — measured, 20 such bullets live in
|
|
25
|
+
* `tasks.md`, `code-complete.md` and `reproduction.md` files and every one is
|
|
26
|
+
* verification evidence citing a criterion, which is signal.
|
|
27
|
+
*
|
|
28
|
+
* Requirements: change-management/AC-15, AC-17, AC-21, AC-22, AC-48, AC-49,
|
|
29
|
+
* AC-58, AC-71, AC-80, AC-81, AC-88
|
|
30
|
+
*/
|
|
31
|
+
import type { WorkflowTemplate } from '../core/types.js';
|
|
32
|
+
import type { ParsedRequirements } from '../traceability/types.js';
|
|
33
|
+
import type { Amendment, ImpactReport } from './types.js';
|
|
34
|
+
export interface WalkInput {
|
|
35
|
+
projectRoot: string;
|
|
36
|
+
instance: string;
|
|
37
|
+
/** The amendment whose retirements drive the walk. */
|
|
38
|
+
amendment: Amendment;
|
|
39
|
+
/** All instances' parsed requirements — the uniqueness-rule registry. */
|
|
40
|
+
registry: Map<string, ParsedRequirements>;
|
|
41
|
+
resolvedTemplate: WorkflowTemplate;
|
|
42
|
+
scope: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Walk every citation of the amendment's retired ids.
|
|
46
|
+
*
|
|
47
|
+
* @returns A report whose `outcome` distinguishes four states (AC-88): a
|
|
48
|
+
* truncated `Retires:` and a genuinely empty one must never render alike.
|
|
49
|
+
*/
|
|
50
|
+
export declare function walkImpact(input: WalkInput): ImpactReport;
|
|
51
|
+
/**
|
|
52
|
+
* One-line human summary. Every outcome says something — an empty result is
|
|
53
|
+
* never rendered as silence (AC-21), because a silent no-op is
|
|
54
|
+
* indistinguishable from success.
|
|
55
|
+
*/
|
|
56
|
+
export declare function describeOutcome(report: ImpactReport): string;
|
|
57
|
+
//# sourceMappingURL=impact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"impact.d.ts","sourceRoot":"","sources":["../../src/change/impact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAKH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AASnE,OAAO,KAAK,EAAE,SAAS,EAAa,YAAY,EAAiB,MAAM,YAAY,CAAC;AA2BpF,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,SAAS,EAAE,SAAS,CAAC;IACrB,yEAAyE;IACzE,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC1C,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,YAAY,CAuNzD;AAMD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAa5D"}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Impact walk (FR3): which citations of a retired criterion are still out there?
|
|
3
|
+
*
|
|
4
|
+
* Read-only and advisory. It performs no writes (AC-22) and its `suggestedReopens`
|
|
5
|
+
* is a suggestion the author may paste into `Reopens:` — `aidlc amend` never reads
|
|
6
|
+
* it (AC-58). The first draft derived the reopen set from this walk, which was
|
|
7
|
+
* degenerate: `requirements.md` is the requirements phase's declared artifact and
|
|
8
|
+
* always cites every retired id (the retirement bullet, the `superseded by AC-m`
|
|
9
|
+
* clause, and the amendment's own `Retires:` field), so every amendment would have
|
|
10
|
+
* reopened requirements. A destructive state write must not depend on a
|
|
11
|
+
* prose-scanning heuristic.
|
|
12
|
+
*
|
|
13
|
+
* Three halves, each with a stated resolution rule, because the two shipped rules
|
|
14
|
+
* genuinely differ and picking one for both would be wrong:
|
|
15
|
+
*
|
|
16
|
+
* - **Test files** use the uniqueness rule at `traceability/matrix.ts:77-87` — a
|
|
17
|
+
* bare `AC-n` counts only when exactly one AC-bearing instance defines the id.
|
|
18
|
+
* - **Tasks** use the owning-instance rule (`tasks/types.ts:15-19`), which that file
|
|
19
|
+
* documents as "never traceability's uniqueness rule".
|
|
20
|
+
* - **Artifacts** use the same tokens as the scanner, resolved by the test rule.
|
|
21
|
+
*
|
|
22
|
+
* `requirements.md` is the one artifact never read: it is the authority being
|
|
23
|
+
* amended, not a consumer of it (AC-81). AC-definition-shaped bullets *elsewhere*
|
|
24
|
+
* are reported rather than excluded — measured, 20 such bullets live in
|
|
25
|
+
* `tasks.md`, `code-complete.md` and `reproduction.md` files and every one is
|
|
26
|
+
* verification evidence citing a criterion, which is signal.
|
|
27
|
+
*
|
|
28
|
+
* Requirements: change-management/AC-15, AC-17, AC-21, AC-22, AC-48, AC-49,
|
|
29
|
+
* AC-58, AC-71, AC-80, AC-81, AC-88
|
|
30
|
+
*/
|
|
31
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
32
|
+
import { join } from 'node:path';
|
|
33
|
+
import { phasesForScope } from '../core/template-resolver.js';
|
|
34
|
+
import { DEFAULT_TEST_GLOBS, discoverTestFiles, scanContent, scanFiles, } from '../traceability/scanner.js';
|
|
35
|
+
import { loadTraceabilityConfig } from '../traceability/gate-criterion.js';
|
|
36
|
+
import { parseTasks } from '../tasks/parser.js';
|
|
37
|
+
/** The one artifact the walk never reads (AC-81). */
|
|
38
|
+
const AUTHORITY_ARTIFACT = 'requirements.md';
|
|
39
|
+
/** Same bounds the scanner applies (`scanner.ts:31-33`), copied onto the artifact half. */
|
|
40
|
+
const MAX_FILE_BYTES = 1024 * 1024;
|
|
41
|
+
const BINARY_SNIFF_BYTES = 8 * 1024;
|
|
42
|
+
/**
|
|
43
|
+
* A template artifact name that could escape the instance directory is skipped
|
|
44
|
+
* (AC-17), mirroring `traceability/gate-criterion.ts:64-71`. Template files come
|
|
45
|
+
* from `.aidlc/templates/`, which is tracked and therefore untrusted.
|
|
46
|
+
*/
|
|
47
|
+
function isSafeArtifactName(name) {
|
|
48
|
+
return !name.includes('/') && !name.includes('\\') && !name.includes('..');
|
|
49
|
+
}
|
|
50
|
+
/** Instances defining a given AC id, for the uniqueness rule. */
|
|
51
|
+
function definersOf(registry, acId) {
|
|
52
|
+
const definers = [];
|
|
53
|
+
for (const [name, parsed] of registry) {
|
|
54
|
+
if (parsed.defs.some((def) => def.id === acId))
|
|
55
|
+
definers.push(name);
|
|
56
|
+
}
|
|
57
|
+
return definers.sort();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Walk every citation of the amendment's retired ids.
|
|
61
|
+
*
|
|
62
|
+
* @returns A report whose `outcome` distinguishes four states (AC-88): a
|
|
63
|
+
* truncated `Retires:` and a genuinely empty one must never render alike.
|
|
64
|
+
*/
|
|
65
|
+
export function walkImpact(input) {
|
|
66
|
+
const { projectRoot, instance, amendment, registry, resolvedTemplate, scope } = input;
|
|
67
|
+
if (!amendment.retiresPresent) {
|
|
68
|
+
return emptyReport('retires-absent', []);
|
|
69
|
+
}
|
|
70
|
+
const retiredIds = [...new Set(amendment.retires)].sort((a, b) => a - b);
|
|
71
|
+
if (retiredIds.length === 0) {
|
|
72
|
+
return emptyReport('no-retirements', []);
|
|
73
|
+
}
|
|
74
|
+
const wanted = new Set(retiredIds);
|
|
75
|
+
const refs = [];
|
|
76
|
+
const warnings = [];
|
|
77
|
+
// Ambiguity is counted per id and reported once (AC-80), never per occurrence.
|
|
78
|
+
// Measured on this instance: 289 of 289 bare occurrences were ambiguous and
|
|
79
|
+
// none resolved here, so per-occurrence reporting is 289 lines of noise before
|
|
80
|
+
// any signal — the shape that retired AC-12 at 44.
|
|
81
|
+
const ambiguous = new Map();
|
|
82
|
+
const noteAmbiguous = (acId, path) => {
|
|
83
|
+
let paths = ambiguous.get(acId);
|
|
84
|
+
if (!paths)
|
|
85
|
+
ambiguous.set(acId, (paths = new Set()));
|
|
86
|
+
paths.add(path);
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Resolve one scanned reference to this instance, or explain why not.
|
|
90
|
+
* Returns true when the reference belongs to this instance.
|
|
91
|
+
*/
|
|
92
|
+
const resolvesHere = (acId, refInstance, path) => {
|
|
93
|
+
if (refInstance !== null) {
|
|
94
|
+
// Qualified: counts only for the named instance, and an unknown
|
|
95
|
+
// instance/id warns and drops (`matrix.ts:66-75`).
|
|
96
|
+
const target = registry.get(refInstance);
|
|
97
|
+
if (!target || !target.defs.some((def) => def.id === acId))
|
|
98
|
+
return false;
|
|
99
|
+
return refInstance === instance;
|
|
100
|
+
}
|
|
101
|
+
const definers = definersOf(registry, acId);
|
|
102
|
+
if (definers.length === 0)
|
|
103
|
+
return false;
|
|
104
|
+
if (definers.length > 1) {
|
|
105
|
+
// Only worth mentioning when this instance is among the definers. When it
|
|
106
|
+
// is not, the reference is provably not ours and saying so is pure noise.
|
|
107
|
+
if (definers.includes(instance))
|
|
108
|
+
noteAmbiguous(acId, path);
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
return definers[0] === instance;
|
|
112
|
+
};
|
|
113
|
+
// ---- Test files (AC-80) -------------------------------------------------
|
|
114
|
+
const config = loadTraceabilityConfig(projectRoot);
|
|
115
|
+
const globs = config?.test_globs ?? DEFAULT_TEST_GLOBS;
|
|
116
|
+
const discovery = discoverTestFiles(projectRoot, globs);
|
|
117
|
+
warnings.push(...discovery.warnings);
|
|
118
|
+
const scan = scanFiles(projectRoot, discovery.files);
|
|
119
|
+
warnings.push(...scan.warnings);
|
|
120
|
+
for (const ref of scan.refs) {
|
|
121
|
+
if (!wanted.has(ref.acId))
|
|
122
|
+
continue;
|
|
123
|
+
if (!resolvesHere(ref.acId, ref.instance, ref.path))
|
|
124
|
+
continue;
|
|
125
|
+
refs.push({ path: ref.path, line: ref.line, acId: ref.acId, source: 'test' });
|
|
126
|
+
}
|
|
127
|
+
// ---- tasks.md (AC-15) ---------------------------------------------------
|
|
128
|
+
// The owning-instance rule, deliberately different from the rule above.
|
|
129
|
+
const instanceDir = join(projectRoot, '.aidlc', 'state', instance);
|
|
130
|
+
const tasksPath = join(instanceDir, 'tasks.md');
|
|
131
|
+
if (existsSync(tasksPath)) {
|
|
132
|
+
let tasksContent = null;
|
|
133
|
+
try {
|
|
134
|
+
tasksContent = readFileSync(tasksPath, 'utf8');
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
warnings.push(`skipped tasks.md: unreadable`);
|
|
138
|
+
}
|
|
139
|
+
if (tasksContent !== null) {
|
|
140
|
+
const relTasks = `.aidlc/state/${instance}/tasks.md`;
|
|
141
|
+
for (const task of parseTasks(tasksContent).tasks) {
|
|
142
|
+
for (const acRef of task.acRefs) {
|
|
143
|
+
if (!wanted.has(acRef.id))
|
|
144
|
+
continue;
|
|
145
|
+
// Bare refs resolve to the owning instance; a qualified ref naming
|
|
146
|
+
// someone else is that instance's business.
|
|
147
|
+
if (acRef.instance !== null && acRef.instance !== instance)
|
|
148
|
+
continue;
|
|
149
|
+
refs.push({
|
|
150
|
+
path: relTasks,
|
|
151
|
+
line: task.line,
|
|
152
|
+
acId: acRef.id,
|
|
153
|
+
source: 'task',
|
|
154
|
+
taskId: task.id,
|
|
155
|
+
taskState: task.state,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// ---- Phase artifacts (AC-81) -------------------------------------------
|
|
162
|
+
// Every artifact the template declares, required and optional, except the
|
|
163
|
+
// authority. Phase ownership is what turns a hit into a suggested reopen.
|
|
164
|
+
const applicable = phasesForScope(resolvedTemplate, scope);
|
|
165
|
+
const phaseByArtifact = new Map();
|
|
166
|
+
const impactedPhases = new Set();
|
|
167
|
+
for (const phase of applicable) {
|
|
168
|
+
const declared = [
|
|
169
|
+
...(phase.required_artifacts ?? []),
|
|
170
|
+
...(phase.optional_artifacts ?? []),
|
|
171
|
+
];
|
|
172
|
+
for (const decl of declared) {
|
|
173
|
+
if (decl.name === AUTHORITY_ARTIFACT)
|
|
174
|
+
continue;
|
|
175
|
+
if (!isSafeArtifactName(decl.name)) {
|
|
176
|
+
warnings.push(`skipped artifact "${decl.name}" declared by phase "${phase.name}": ` +
|
|
177
|
+
`name could escape the instance directory`);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
// First declaring phase wins, so an artifact named by two phases is
|
|
181
|
+
// attributed deterministically rather than by iteration accident.
|
|
182
|
+
if (!phaseByArtifact.has(decl.name))
|
|
183
|
+
phaseByArtifact.set(decl.name, String(phase.name));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
for (const [artifact, phaseName] of phaseByArtifact) {
|
|
187
|
+
const abs = join(instanceDir, artifact);
|
|
188
|
+
if (!existsSync(abs))
|
|
189
|
+
continue;
|
|
190
|
+
let size;
|
|
191
|
+
try {
|
|
192
|
+
size = statSync(abs).size;
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
warnings.push(`skipped ${artifact}: unreadable`);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (size > MAX_FILE_BYTES) {
|
|
199
|
+
warnings.push(`skipped ${artifact}: too large`);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
let buf;
|
|
203
|
+
try {
|
|
204
|
+
buf = readFileSync(abs);
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
warnings.push(`skipped ${artifact}: unreadable`);
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (buf.subarray(0, BINARY_SNIFF_BYTES).includes(0)) {
|
|
211
|
+
warnings.push(`skipped ${artifact}: binary`);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const rel = `.aidlc/state/${instance}/${artifact}`;
|
|
215
|
+
for (const ref of scanContent(buf.toString('utf8'), rel)) {
|
|
216
|
+
if (!wanted.has(ref.acId))
|
|
217
|
+
continue;
|
|
218
|
+
if (!resolvesHere(ref.acId, ref.instance, ref.path))
|
|
219
|
+
continue;
|
|
220
|
+
refs.push({ path: rel, line: ref.line, acId: ref.acId, source: 'artifact' });
|
|
221
|
+
impactedPhases.add(phaseName);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// ---- Assembly (AC-71, AC-48) -------------------------------------------
|
|
225
|
+
// Dedup on path:line:acId with `task` winning, because a task record carries
|
|
226
|
+
// strictly more information and tasks.md is walked twice by construction —
|
|
227
|
+
// once as tasks, once as the design phase's declared artifact.
|
|
228
|
+
const byKey = new Map();
|
|
229
|
+
for (const ref of refs) {
|
|
230
|
+
const key = `${ref.path}:${ref.line}:${ref.acId}`;
|
|
231
|
+
const existing = byKey.get(key);
|
|
232
|
+
if (!existing || (existing.source === 'artifact' && ref.source === 'task')) {
|
|
233
|
+
byKey.set(key, ref);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const deduped = [...byKey.values()].sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0) ||
|
|
237
|
+
a.line - b.line ||
|
|
238
|
+
a.acId - b.acId);
|
|
239
|
+
// A task citing a retired id means the design phase's tasks.md is stale;
|
|
240
|
+
// a test citing one means the testing phase is.
|
|
241
|
+
for (const ref of deduped) {
|
|
242
|
+
if (ref.source === 'task') {
|
|
243
|
+
const design = applicable.find((p) => (p.required_artifacts ?? []).some((a) => a.name === 'tasks.md'));
|
|
244
|
+
if (design)
|
|
245
|
+
impactedPhases.add(String(design.name));
|
|
246
|
+
}
|
|
247
|
+
else if (ref.source === 'test') {
|
|
248
|
+
if (applicable.some((p) => p.name === 'testing'))
|
|
249
|
+
impactedPhases.add('testing');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
for (const [acId, paths] of [...ambiguous.entries()].sort((a, b) => a[0] - b[0])) {
|
|
253
|
+
warnings.push(`bare AC-${acId} is ambiguous in ${paths.size} file(s) and was not counted; ` +
|
|
254
|
+
`qualify as ${instance}/AC-${acId}`);
|
|
255
|
+
}
|
|
256
|
+
const order = new Map(applicable.map((p, i) => [String(p.name), i]));
|
|
257
|
+
const suggestedReopens = [...impactedPhases].sort((a, b) => (order.get(a) ?? 0) - (order.get(b) ?? 0));
|
|
258
|
+
return {
|
|
259
|
+
outcome: deduped.length > 0 ? 'impacts' : 'none',
|
|
260
|
+
retiredIds,
|
|
261
|
+
refs: deduped,
|
|
262
|
+
suggestedReopens,
|
|
263
|
+
warnings,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
function emptyReport(outcome, retiredIds) {
|
|
267
|
+
return { outcome, retiredIds, refs: [], suggestedReopens: [], warnings: [] };
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* One-line human summary. Every outcome says something — an empty result is
|
|
271
|
+
* never rendered as silence (AC-21), because a silent no-op is
|
|
272
|
+
* indistinguishable from success.
|
|
273
|
+
*/
|
|
274
|
+
export function describeOutcome(report) {
|
|
275
|
+
switch (report.outcome) {
|
|
276
|
+
case 'retires-absent':
|
|
277
|
+
return 'this amendment declares no Retires: field, so there is nothing to walk';
|
|
278
|
+
case 'no-retirements':
|
|
279
|
+
return 'this amendment retires no criteria, so there is nothing to walk';
|
|
280
|
+
case 'none':
|
|
281
|
+
return `no citations of AC-${report.retiredIds.join(', AC-')} found outside requirements.md`;
|
|
282
|
+
case 'impacts': {
|
|
283
|
+
const files = new Set(report.refs.map((r) => r.path)).size;
|
|
284
|
+
return `${report.refs.length} citation(s) across ${files} file(s) still name a retired criterion`;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=impact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"impact.js","sourceRoot":"","sources":["../../src/change/impact.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mid-instance change management: amendments, the read-only impact walk, and
|
|
3
|
+
* the `reopen` transition.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
export type { Amendment, AmendEnvelope, AmendErrorCode, BlockedCode, ImpactOutcome, ImpactRef, ImpactReport, ParsedAmendments, ReopenPlan, } from './types.js';
|
|
8
|
+
export { parseAmendments, scanOutsideFences } from './parser.js';
|
|
9
|
+
export { crossCheck } from './cross-check.js';
|
|
10
|
+
export { describeOutcome, walkImpact } from './impact.js';
|
|
11
|
+
export { applyReopen, mergeApplied, planReopen, recordApplied } from './reopen.js';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/change/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EACV,SAAS,EACT,aAAa,EACb,cAAc,EACd,WAAW,EACX,aAAa,EACb,SAAS,EACT,YAAY,EACZ,gBAAgB,EAChB,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mid-instance change management: amendments, the read-only impact walk, and
|
|
3
|
+
* the `reopen` transition.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
export { parseAmendments, scanOutsideFences } from './parser.js';
|
|
8
|
+
export { crossCheck } from './cross-check.js';
|
|
9
|
+
export { describeOutcome, walkImpact } from './impact.js';
|
|
10
|
+
export { applyReopen, mergeApplied, planReopen, recordApplied } from './reopen.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/change/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Amendment parser: closed grammar over the `## Amendment <n>` sections of
|
|
3
|
+
* requirements.md (FR1).
|
|
4
|
+
*
|
|
5
|
+
* The grammar was derived from the corpus, not invented. Measured over 39
|
|
6
|
+
* `requirements.md` and 29 `tasks.md` on 2026-09-07, two conventions exist:
|
|
7
|
+
*
|
|
8
|
+
* ## Amendment 1 — 2026-09-05, after the design review (5 real, accepted)
|
|
9
|
+
* ### Amendments from the design review (2026-09-05) (4 real, warned)
|
|
10
|
+
*
|
|
11
|
+
* The second is unnumbered and undated and cannot be applied, so it warns
|
|
12
|
+
* rather than vanishing — a near-miss that reads as absence is this project's
|
|
13
|
+
* single most repeated defect class (AC-53).
|
|
14
|
+
*
|
|
15
|
+
* Two rules exist because a prototype got them wrong on real input:
|
|
16
|
+
*
|
|
17
|
+
* - **Three emphasis variants, copied from `ACTIVE_RE`** (`traceability/parser.ts:23-24`).
|
|
18
|
+
* The first draft accepted `Retires:` and `**Retires**:` but not
|
|
19
|
+
* `**Retires:**` — which is the form this instance's own Amendment 1 was
|
|
20
|
+
* written with, so its 23 retirements parsed as zero (AC-78).
|
|
21
|
+
* - **A wrapped bullet is one bullet.** Ending the field block at "the first
|
|
22
|
+
* non-bullet non-blank line" truncates any field that wraps: measured, a
|
|
23
|
+
* wrapped `Retires:` yielded 12 of 23 ids and dropped `Issues`, `Reason`
|
|
24
|
+
* and `Reopens` outright, and the impact report would then have said "no
|
|
25
|
+
* retired ids" for an amendment that retired 23 criteria (AC-78, AC-94).
|
|
26
|
+
*
|
|
27
|
+
* Requirements: change-management/AC-3, AC-5, AC-7, AC-8, AC-51, AC-53,
|
|
28
|
+
* AC-54, AC-78, AC-79
|
|
29
|
+
*/
|
|
30
|
+
import type { ParsedAmendments } from './types.js';
|
|
31
|
+
/**
|
|
32
|
+
* Walk lines outside fenced code blocks (AC-79).
|
|
33
|
+
*
|
|
34
|
+
* CommonMark rules: a fence opens with 3+ backticks or tildes and closes only
|
|
35
|
+
* on the same character at the same length or longer. Written correctly rather
|
|
36
|
+
* than as a naive toggle because it is cheap to do so — the corpus currently
|
|
37
|
+
* cannot tell the two apart (1047 AC-shaped lines, zero inside fences), which
|
|
38
|
+
* is a reason to write the correct one, not evidence that it does not matter.
|
|
39
|
+
*
|
|
40
|
+
* Indentation is deliberately permissive (`\s*`, not ` {0,3}`): AIDLC markdown
|
|
41
|
+
* nests fenced examples inside list items routinely — see the `ac-coverage`
|
|
42
|
+
* example at `packages/content/skills/50-testing.md:43-45`, indented five
|
|
43
|
+
* spaces — and an absolute three-space rule is blind to every one of them.
|
|
44
|
+
*/
|
|
45
|
+
export declare function scanOutsideFences(lines: string[], visit: (line: string, index: number) => void): void;
|
|
46
|
+
/**
|
|
47
|
+
* Parse every amendment section in one requirements.md.
|
|
48
|
+
*
|
|
49
|
+
* @param content - File content (UTF-8). Never throws.
|
|
50
|
+
*/
|
|
51
|
+
export declare function parseAmendments(content: string): ParsedAmendments;
|
|
52
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/change/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAa,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA+D9D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EAAE,EACf,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAC3C,IAAI,CAgBN;AA4FD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAwJjE"}
|