musubix3 0.1.15 → 0.1.17
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/.github/plugin/marketplace.json +2 -2
- package/CHANGELOG.md +35 -0
- package/README-ja.md +1 -1
- package/README.md +4 -2
- package/dist/packages/analysis/src/approval-domains.d.ts +22 -0
- package/dist/packages/analysis/src/approval-domains.js +75 -0
- package/dist/packages/analysis/src/approval-domains.js.map +1 -0
- package/dist/packages/analysis/src/approval-record.d.ts +7 -1
- package/dist/packages/analysis/src/approval-record.js +23 -9
- package/dist/packages/analysis/src/approval-record.js.map +1 -1
- package/dist/packages/analysis/src/approval.d.ts +67 -5
- package/dist/packages/analysis/src/approval.js +226 -27
- package/dist/packages/analysis/src/approval.js.map +1 -1
- package/dist/packages/analysis/src/change-evidence.d.ts +91 -0
- package/dist/packages/analysis/src/change-evidence.js +172 -0
- package/dist/packages/analysis/src/change-evidence.js.map +1 -0
- package/dist/packages/analysis/src/change-waiver.d.ts +186 -0
- package/dist/packages/analysis/src/change-waiver.js +688 -0
- package/dist/packages/analysis/src/change-waiver.js.map +1 -0
- package/dist/packages/analysis/src/change.d.ts +11 -53
- package/dist/packages/analysis/src/change.js +148 -77
- package/dist/packages/analysis/src/change.js.map +1 -1
- package/dist/packages/analysis/src/config.d.ts +5 -0
- package/dist/packages/analysis/src/config.js +36 -3
- package/dist/packages/analysis/src/config.js.map +1 -1
- package/dist/packages/analysis/src/gate.d.ts +21 -1
- package/dist/packages/analysis/src/gate.js +42 -8
- package/dist/packages/analysis/src/gate.js.map +1 -1
- package/dist/packages/analysis/src/index.d.ts +2 -0
- package/dist/packages/analysis/src/index.js +2 -0
- package/dist/packages/analysis/src/index.js.map +1 -1
- package/dist/packages/analysis/src/order.d.ts +50 -2
- package/dist/packages/analysis/src/order.js +43 -6
- package/dist/packages/analysis/src/order.js.map +1 -1
- package/dist/packages/analysis/src/scaffold-artifact.d.ts +4 -0
- package/dist/packages/analysis/src/scaffold-artifact.js +79 -0
- package/dist/packages/analysis/src/scaffold-artifact.js.map +1 -0
- package/dist/packages/analysis/src/tdd.d.ts +29 -1
- package/dist/packages/analysis/src/tdd.js +196 -11
- package/dist/packages/analysis/src/tdd.js.map +1 -1
- package/dist/packages/cli/src/main.js +79 -13
- package/dist/packages/cli/src/main.js.map +1 -1
- package/dist/packages/domain/src/types.d.ts +8 -0
- package/dist/packages/domain/src/types.js.map +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
"owner": { "name": "nahisaho" },
|
|
4
4
|
"metadata": {
|
|
5
5
|
"description": "GitHub Copilot CLI specification-driven development skills",
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.17"
|
|
7
7
|
},
|
|
8
8
|
"plugins": [
|
|
9
9
|
{
|
|
10
10
|
"name": "musubix3",
|
|
11
11
|
"source": ".",
|
|
12
|
-
"version": "0.1.
|
|
12
|
+
"version": "0.1.17",
|
|
13
13
|
"description": "Evidence-driven SDD without duplicating native Copilot capabilities."
|
|
14
14
|
}
|
|
15
15
|
]
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.17 - 2026-09-13
|
|
4
|
+
|
|
5
|
+
`gate --changed` remediation for the change-evidence-waiver feature
|
|
6
|
+
(CHANGE-0012, GitHub Issue #1 item):
|
|
7
|
+
|
|
8
|
+
- Fixed a `TRACE_DUPLICATE` diagnostic caused by a duplicate trace `@id` on
|
|
9
|
+
two distinct annotations in `order.ts`.
|
|
10
|
+
- Added a missing `@implements REQ-TDD-CYCLE-VOID-013` trace annotation on
|
|
11
|
+
the voided-cycle handling in `tdd.ts`, closing a `CHANGE_COMPLETENESS_CODE`
|
|
12
|
+
gap for CHANGE-0011.
|
|
13
|
+
- Retroactively waived 166 historical `change-history`/`change-completeness`
|
|
14
|
+
diagnostics predating the CHANGE-0012 waiver mechanism as reviewed legacy
|
|
15
|
+
debt (CHANGE-0005, 0006, 0009, 0010, 0011).
|
|
16
|
+
- Performed a full retroactive TDD Red-Green ceremony (18 test/requirement
|
|
17
|
+
pairs across 15 tests in `tests/change-evidence-waiver.test.ts`), resolving
|
|
18
|
+
all `TDD_TEST_STALE`/`TDD_REQUIREMENT_UNCOVERED` diagnostics.
|
|
19
|
+
|
|
20
|
+
## 0.1.16 - 2026-09-12
|
|
21
|
+
|
|
22
|
+
New feature: scaffold starter `requirements.md`/`design.md` files for a new
|
|
23
|
+
feature slug (GitHub Issue #18).
|
|
24
|
+
|
|
25
|
+
- `musubix3 requirements scaffold <slug> [--title <text>]` creates
|
|
26
|
+
`.musubix/features/<slug>/requirements.md` from a fixed placeholder
|
|
27
|
+
template.
|
|
28
|
+
- `musubix3 design scaffold <slug>` creates
|
|
29
|
+
`.musubix/features/<slug>/design.md` from a fixed placeholder template; it
|
|
30
|
+
does not require a matching `requirements.md` to already exist.
|
|
31
|
+
- Both commands validate the slug (`^[a-z0-9]+(-[a-z0-9]+)*$`, matching
|
|
32
|
+
`install()`'s existing convention) and refuse to overwrite an existing
|
|
33
|
+
target file, using an OS-level exclusive create (`wx`) so a concurrent
|
|
34
|
+
create is never silently clobbered (see `ADR-0020`).
|
|
35
|
+
- Spec: `REQ-REQUIREMENTS-DESIGN-SCAFFOLD-001..006`,
|
|
36
|
+
`DES-REQUIREMENTS-DESIGN-SCAFFOLD-001`, tracked end-to-end in `CHANGE-0008`.
|
|
37
|
+
|
|
3
38
|
## 0.1.15 - 2026-09-11
|
|
4
39
|
|
|
5
40
|
Documentation/workflow update: AI-generated documentation deliverables now go
|
package/README-ja.md
CHANGED
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# musubix3
|
|
2
2
|
|
|
3
|
-
**Latest release v0.1.
|
|
3
|
+
**Latest release v0.1.17 · GitHub Copilot CLI only · Node.js ≥20 · TypeScript · MIT**
|
|
4
4
|
|
|
5
5
|
[日本語](README-ja.md)
|
|
6
6
|
|
|
@@ -384,8 +384,10 @@ validation/gate or requested solver failure, **2** usage, I/O or malformed confi
|
|
|
384
384
|
| `upgrade [--dry-run]` | Refresh bundled skill files only; never touches config, constitution, or feature artifacts |
|
|
385
385
|
| `plugin-install` | Invoke native Copilot installer (no internal config edits) |
|
|
386
386
|
| `requirements validate <file>` | IDs, priorities, declared/detected EARS pattern |
|
|
387
|
+
| `requirements scaffold <slug> [--title <text>]` | Create `.musubix/features/<slug>/requirements.md` from a fixed placeholder template; never overwrites an existing file |
|
|
387
388
|
| `constitution validate [file]` | Versioned principles and measurable rule definitions |
|
|
388
389
|
| `design validate <file>` | Fields, global requirement IDs, existing ADR references |
|
|
390
|
+
| `design scaffold <slug>` | Create `.musubix/features/<slug>/design.md` from a fixed placeholder template; never overwrites an existing file, and does not require a pre-existing `requirements.md` |
|
|
389
391
|
| `design c4 <file>` | Mermaid component/dependency diagram from explicit fields |
|
|
390
392
|
| `approval prepare <requirements\|design\|release>` | Display the exact deterministic manifest and hash for human review |
|
|
391
393
|
| `approval record <stage> --approver <name> --artifact-sha256 <hash> --confirm` | Record approval only if the reviewed hash is still current |
|
|
@@ -415,7 +417,7 @@ validation/gate or requested solver failure, **2** usage, I/O or malformed confi
|
|
|
415
417
|
| `attestation oidc-audience --key-id <id> [--public-key-file <pem>]` | Derive the GitHub custom audience that authorizes a signing key |
|
|
416
418
|
| `attestation payload --provider <name> --run-id <id> --key-id <id> [--public-key-file <pem>] [--github-oidc-token-file <jwt>]` | Emit canonical unsigned CI payload for external signing |
|
|
417
419
|
| `attestation verify` | Verify static-key or GitHub OIDC-authorized Ed25519 provenance |
|
|
418
|
-
| `change-record <CHANGE-ID> <phase> --requirement <REQ-ID
|
|
420
|
+
| `change-record <CHANGE-ID> <phase> --requirement <REQ-ID...> [--allow-unchanged] [--dry-run]` | Record ordered artifact/TDD fingerprints for a staged change. `impact`/`requirements`/`design`/`quality` require the change's full requirement ID set; `red`/`implementation`/`green` also accept a proper non-empty subset, recorded as an independent per-requirement batch, so a multi-requirement change can be completed with an interleaved per-requirement Red-Implementation-Green loop instead of one global batch. Rejects with a non-zero exit and a stable `*_UNCHANGED_AT_RECORD` diagnostic (leaving `changes.json`/`order.json` untouched) when a phase's fingerprint is byte-identical to the immediately preceding phase's, since that mistake is otherwise only caught much later by `trace check --strict`/`gate`, at which point the append-only evidence store makes it unfixable. `--allow-unchanged` bypasses this check for the `requirements` phase only (the one case `sdd-change` documents as legitimate: a defect fix that intentionally leaves its requirement unchanged) and persists a durable marker so later validation does not re-flag it. `--dry-run` previews the exact success/rejection outcome, including every existing check, without persisting anything |
|
|
419
421
|
| `config lint` | Report configured commands whose `args` reference repository-relative paths that do not exist |
|
|
420
422
|
| `config scaffold` | Propose native test-command entries for detected Go/Rust/Maven/Python/Node toolchains without writing `.musubix/config.json` |
|
|
421
423
|
| `gate [--changed] [--feature <name>]` | Fresh full checks plus actual configured commands; persist evidence. `--feature` scopes requirements/design/trace/tdd/change-history/change-completeness checks to one feature as a diagnostic view; never a substitute for the repository-wide gate |
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ApprovalConfig } from './config.js';
|
|
2
|
+
export interface ResolvedDomain {
|
|
3
|
+
name: string;
|
|
4
|
+
features: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare function domainsConfigured(config: ApprovalConfig): boolean;
|
|
7
|
+
/** @id CODE-APPROVAL-DOMAIN-SCOPING-002
|
|
8
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-002 REQ-APPROVAL-DOMAIN-SCOPING-003 REQ-APPROVAL-DOMAIN-SCOPING-004
|
|
9
|
+
* @design DES-APPROVAL-DOMAIN-SCOPING-002
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveDomains(root: string, config: ApprovalConfig): Promise<ResolvedDomain[]>;
|
|
12
|
+
export declare function domainOwning(resolved: ResolvedDomain[], slug: string): string | null;
|
|
13
|
+
/** @id CODE-APPROVAL-DOMAIN-SCOPING-014
|
|
14
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-016
|
|
15
|
+
* @design DES-APPROVAL-DOMAIN-SCOPING-005
|
|
16
|
+
*/
|
|
17
|
+
export declare function featureOwningRequirement(root: string, requirementId: string): Promise<string>;
|
|
18
|
+
/** @id CODE-APPROVAL-DOMAIN-SCOPING-015
|
|
19
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-017
|
|
20
|
+
* @design DES-APPROVAL-DOMAIN-SCOPING-005
|
|
21
|
+
*/
|
|
22
|
+
export declare function featureOwningDesignFile(file: string): string | null;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { validateRequirements } from '../../domain/src/index.js';
|
|
2
|
+
import { matchGlob } from './graph.js';
|
|
3
|
+
import { files, readText } from './files.js';
|
|
4
|
+
const featureRequirementsPattern = /^\.musubix\/features\/([^/]+)\/requirements\.md$/;
|
|
5
|
+
const featureDesignPattern = /^\.musubix\/features\/([^/]+)\/design\.md$/;
|
|
6
|
+
export function domainsConfigured(config) {
|
|
7
|
+
return config.domains.length > 0;
|
|
8
|
+
}
|
|
9
|
+
/** @id CODE-APPROVAL-DOMAIN-SCOPING-002
|
|
10
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-002 REQ-APPROVAL-DOMAIN-SCOPING-003 REQ-APPROVAL-DOMAIN-SCOPING-004
|
|
11
|
+
* @design DES-APPROVAL-DOMAIN-SCOPING-002
|
|
12
|
+
*/
|
|
13
|
+
export async function resolveDomains(root, config) {
|
|
14
|
+
const domains = config.domains;
|
|
15
|
+
if (domains.length === 0)
|
|
16
|
+
return [];
|
|
17
|
+
const slugs = [...new Set((await files(root))
|
|
18
|
+
.map((path) => featureRequirementsPattern.exec(path)?.[1])
|
|
19
|
+
.filter((slug) => !!slug))].sort();
|
|
20
|
+
const owners = new Map();
|
|
21
|
+
for (const slug of slugs) {
|
|
22
|
+
const matching = domains.filter((domain) => domain.featureGlobs.some((glob) => matchGlob(slug, glob)));
|
|
23
|
+
owners.set(slug, matching.map((domain) => domain.name).sort());
|
|
24
|
+
}
|
|
25
|
+
for (const slug of slugs) {
|
|
26
|
+
const matching = owners.get(slug);
|
|
27
|
+
if (matching.length === 0) {
|
|
28
|
+
throw new Error(`Feature "${slug}" is not owned by any configured approval domain; every feature directory must match exactly one domain's featureGlobs.`);
|
|
29
|
+
}
|
|
30
|
+
if (matching.length > 1) {
|
|
31
|
+
throw new Error(`Feature "${slug}" matches more than one configured approval domain: ${matching.join(', ')}; every feature directory must match exactly one domain.`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const resolved = domains.map((domain) => ({
|
|
35
|
+
name: domain.name,
|
|
36
|
+
features: slugs.filter((slug) => owners.get(slug)[0] === domain.name).sort(),
|
|
37
|
+
}));
|
|
38
|
+
for (const domain of resolved) {
|
|
39
|
+
if (domain.features.length === 0) {
|
|
40
|
+
throw new Error(`Approval domain "${domain.name}" matches zero existing feature directories; remove it or add a matching feature.`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return resolved;
|
|
44
|
+
}
|
|
45
|
+
export function domainOwning(resolved, slug) {
|
|
46
|
+
return resolved.find((domain) => domain.features.includes(slug))?.name ?? null;
|
|
47
|
+
}
|
|
48
|
+
/** @id CODE-APPROVAL-DOMAIN-SCOPING-014
|
|
49
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-016
|
|
50
|
+
* @design DES-APPROVAL-DOMAIN-SCOPING-005
|
|
51
|
+
*/
|
|
52
|
+
export async function featureOwningRequirement(root, requirementId) {
|
|
53
|
+
const paths = (await files(root)).filter((path) => featureRequirementsPattern.test(path));
|
|
54
|
+
const owners = [];
|
|
55
|
+
for (const path of paths) {
|
|
56
|
+
const slug = featureRequirementsPattern.exec(path)[1];
|
|
57
|
+
const result = validateRequirements(await readText(root, path), path);
|
|
58
|
+
if (result.value.some((requirement) => requirement.id === requirementId))
|
|
59
|
+
owners.push(slug);
|
|
60
|
+
}
|
|
61
|
+
if (owners.length === 0)
|
|
62
|
+
throw new Error(`Requirement ${requirementId} was not found in any feature's requirements.md.`);
|
|
63
|
+
if (owners.length > 1)
|
|
64
|
+
throw new Error(`Requirement ${requirementId} is defined in more than one feature: ${owners.join(', ')}.`);
|
|
65
|
+
return owners[0];
|
|
66
|
+
}
|
|
67
|
+
/** @id CODE-APPROVAL-DOMAIN-SCOPING-015
|
|
68
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-017
|
|
69
|
+
* @design DES-APPROVAL-DOMAIN-SCOPING-005
|
|
70
|
+
*/
|
|
71
|
+
export function featureOwningDesignFile(file) {
|
|
72
|
+
const match = featureDesignPattern.exec(file);
|
|
73
|
+
return match ? match[1] : null;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=approval-domains.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval-domains.js","sourceRoot":"","sources":["../../../../packages/analysis/src/approval-domains.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
import type { ApprovalConfig } from './config.js';
|
|
1
2
|
import { type ApprovalEvidence, type ApprovalStage } from './approval.js';
|
|
2
3
|
/** @id CODE-HUMAN-APPROVAL-GATES-002
|
|
3
4
|
* @implements REQ-HUMAN-APPROVAL-GATES-001 REQ-HUMAN-APPROVAL-GATES-006
|
|
4
5
|
* @design DES-APPROVAL-002 DES-APPROVAL-003
|
|
5
6
|
*/
|
|
6
|
-
|
|
7
|
+
/** @id CODE-APPROVAL-DOMAIN-SCOPING-016
|
|
8
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-005 REQ-APPROVAL-DOMAIN-SCOPING-006 REQ-APPROVAL-DOMAIN-SCOPING-007
|
|
9
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-013 REQ-APPROVAL-DOMAIN-SCOPING-014 REQ-APPROVAL-DOMAIN-SCOPING-015
|
|
10
|
+
* @design DES-APPROVAL-DOMAIN-SCOPING-004
|
|
11
|
+
*/
|
|
12
|
+
export declare function recordApproval(root: string, stage: ApprovalStage, approver: string, expectedArtifactSha256: string, config: ApprovalConfig, domainName?: string): Promise<ApprovalEvidence>;
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import { approvalManifest, requireApproval, validateStageArtifacts, } from './approval.js';
|
|
1
|
+
import { approvalManifest, approvalPath, domainsConfigured, requireApproval, requireDomainOption, resolveDomains, validateStageArtifacts, } from './approval.js';
|
|
2
2
|
import { writeJson } from './files.js';
|
|
3
3
|
import { runGate } from './gate.js';
|
|
4
4
|
/** @id CODE-HUMAN-APPROVAL-GATES-002
|
|
5
5
|
* @implements REQ-HUMAN-APPROVAL-GATES-001 REQ-HUMAN-APPROVAL-GATES-006
|
|
6
6
|
* @design DES-APPROVAL-002 DES-APPROVAL-003
|
|
7
7
|
*/
|
|
8
|
-
|
|
8
|
+
/** @id CODE-APPROVAL-DOMAIN-SCOPING-016
|
|
9
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-005 REQ-APPROVAL-DOMAIN-SCOPING-006 REQ-APPROVAL-DOMAIN-SCOPING-007
|
|
10
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-013 REQ-APPROVAL-DOMAIN-SCOPING-014 REQ-APPROVAL-DOMAIN-SCOPING-015
|
|
11
|
+
* @design DES-APPROVAL-DOMAIN-SCOPING-004
|
|
12
|
+
*/
|
|
13
|
+
export async function recordApproval(root, stage, approver, expectedArtifactSha256, config, domainName) {
|
|
9
14
|
const normalizedApprover = approver.trim();
|
|
10
15
|
if (!normalizedApprover || normalizedApprover.includes('\0')) {
|
|
11
16
|
throw new Error('Approver must be a nonempty name without NUL bytes.');
|
|
@@ -13,19 +18,28 @@ export async function recordApproval(root, stage, approver, expectedArtifactSha2
|
|
|
13
18
|
if (!/^[a-f0-9]{64}$/.test(expectedArtifactSha256)) {
|
|
14
19
|
throw new Error('Expected approval artifact SHA-256 must be 64 lowercase hexadecimal characters.');
|
|
15
20
|
}
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
requireDomainOption(config, stage, domainName);
|
|
22
|
+
const resolved = domainsConfigured(config) ? await resolveDomains(root, config) : [];
|
|
23
|
+
const domain = domainName ? resolved.find((d) => d.name === domainName) : undefined;
|
|
24
|
+
await validateStageArtifacts(root, stage, domain);
|
|
25
|
+
const requiredApproval = { mode: 'required', domains: [] };
|
|
26
|
+
if (stage === 'design') {
|
|
27
|
+
await requireApproval(root, 'requirements', requiredApproval, domain);
|
|
28
|
+
}
|
|
20
29
|
if (stage === 'release') {
|
|
21
|
-
|
|
30
|
+
for (const d of resolved) {
|
|
31
|
+
await requireApproval(root, 'requirements', requiredApproval, d);
|
|
32
|
+
await requireApproval(root, 'design', requiredApproval, d);
|
|
33
|
+
}
|
|
34
|
+
if (!resolved.length)
|
|
35
|
+
await requireApproval(root, 'design', requiredApproval);
|
|
22
36
|
const quality = await runGate(root);
|
|
23
37
|
const blockers = quality.checks.filter((check) => check.name !== 'approval' && check.required && check.status !== 'pass');
|
|
24
38
|
if (blockers.length) {
|
|
25
39
|
throw new Error(`Release approval requires passing non-approval quality checks: ${blockers.map((check) => check.name).join(', ')}.`);
|
|
26
40
|
}
|
|
27
41
|
}
|
|
28
|
-
const manifest = await approvalManifest(root, stage);
|
|
42
|
+
const manifest = await approvalManifest(root, stage, domain);
|
|
29
43
|
if (!Object.keys(manifest.artifacts).length)
|
|
30
44
|
throw new Error(`No artifacts are available for ${stage} approval.`);
|
|
31
45
|
if (manifest.artifactSha256 !== expectedArtifactSha256) {
|
|
@@ -37,7 +51,7 @@ export async function recordApproval(root, stage, approver, expectedArtifactSha2
|
|
|
37
51
|
approver: normalizedApprover,
|
|
38
52
|
approvedAt: new Date().toISOString(),
|
|
39
53
|
};
|
|
40
|
-
await writeJson(root,
|
|
54
|
+
await writeJson(root, approvalPath(stage, domainName), evidence);
|
|
41
55
|
return evidence;
|
|
42
56
|
}
|
|
43
57
|
//# sourceMappingURL=approval-record.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval-record.js","sourceRoot":"","sources":["../../../../packages/analysis/src/approval-record.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAAE,eAAe,EAAE,sBAAsB,
|
|
1
|
+
{"version":3,"file":"approval-record.js","sourceRoot":"","sources":["../../../../packages/analysis/src/approval-record.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { type Diagnostic } from '../../domain/src/index.js';
|
|
2
2
|
import type { ApprovalConfig } from './config.js';
|
|
3
|
+
import { type ResolvedDomain } from './approval-domains.js';
|
|
4
|
+
export { domainsConfigured, domainOwning, featureOwningDesignFile, featureOwningRequirement, resolveDomains, type ResolvedDomain, } from './approval-domains.js';
|
|
3
5
|
export declare const approvalStages: readonly ["requirements", "design", "release"];
|
|
4
6
|
export type ApprovalStage = typeof approvalStages[number];
|
|
5
7
|
export type ApprovalStatus = 'approved' | 'missing' | 'stale';
|
|
6
8
|
export interface ApprovalManifest {
|
|
7
9
|
stage: ApprovalStage;
|
|
10
|
+
domain?: string;
|
|
11
|
+
features?: string[];
|
|
8
12
|
artifacts: Record<string, string>;
|
|
9
13
|
artifactSha256: string;
|
|
10
14
|
}
|
|
@@ -15,6 +19,7 @@ export interface ApprovalEvidence extends ApprovalManifest {
|
|
|
15
19
|
}
|
|
16
20
|
export interface ApprovalStageValidation {
|
|
17
21
|
stage: ApprovalStage;
|
|
22
|
+
domain?: string;
|
|
18
23
|
required: boolean;
|
|
19
24
|
present: boolean;
|
|
20
25
|
status: ApprovalStatus;
|
|
@@ -22,27 +27,84 @@ export interface ApprovalStageValidation {
|
|
|
22
27
|
currentArtifactSha256: string;
|
|
23
28
|
diagnostics: Diagnostic[];
|
|
24
29
|
}
|
|
30
|
+
export interface DomainApprovalValidation {
|
|
31
|
+
name: string;
|
|
32
|
+
stages: ApprovalStageValidation[];
|
|
33
|
+
}
|
|
25
34
|
export interface ApprovalValidation {
|
|
26
35
|
schemaVersion: 1;
|
|
27
36
|
mode: ApprovalConfig['mode'];
|
|
28
37
|
present: boolean;
|
|
29
38
|
valid: boolean;
|
|
30
39
|
stages: ApprovalStageValidation[];
|
|
40
|
+
domain?: string;
|
|
41
|
+
domains?: DomainApprovalValidation[];
|
|
42
|
+
release?: ApprovalStageValidation;
|
|
31
43
|
diagnostics: Diagnostic[];
|
|
32
44
|
}
|
|
45
|
+
export declare function approvalPath(stage: ApprovalStage, domain?: string): string;
|
|
33
46
|
/** @id CODE-HUMAN-APPROVAL-GATES-001
|
|
34
47
|
* @implements REQ-HUMAN-APPROVAL-GATES-001 REQ-HUMAN-APPROVAL-GATES-002 REQ-HUMAN-APPROVAL-GATES-003
|
|
35
48
|
* @implements REQ-HUMAN-APPROVAL-GATES-004 REQ-HUMAN-APPROVAL-GATES-005 REQ-HUMAN-APPROVAL-GATES-006
|
|
36
49
|
* @implements REQ-HUMAN-APPROVAL-GATES-007 REQ-HUMAN-APPROVAL-GATES-008
|
|
37
50
|
* @design DES-APPROVAL-001 DES-APPROVAL-002 DES-APPROVAL-003
|
|
38
51
|
*/
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
52
|
+
/** @id CODE-APPROVAL-DOMAIN-SCOPING-007
|
|
53
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-011 REQ-APPROVAL-DOMAIN-SCOPING-012 REQ-APPROVAL-DOMAIN-SCOPING-013
|
|
54
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-014 REQ-APPROVAL-DOMAIN-SCOPING-023 REQ-APPROVAL-DOMAIN-SCOPING-024
|
|
55
|
+
* @design DES-APPROVAL-DOMAIN-SCOPING-003
|
|
56
|
+
*/
|
|
57
|
+
export declare function approvalManifest(root: string, stage: ApprovalStage, domain?: ResolvedDomain): Promise<ApprovalManifest>;
|
|
58
|
+
export declare function loadApproval(root: string, stage: ApprovalStage, domain?: string): Promise<ApprovalEvidence | null>;
|
|
59
|
+
export declare function validateApprovalStage(root: string, stage: ApprovalStage, config: ApprovalConfig, domain?: ResolvedDomain): Promise<ApprovalStageValidation>;
|
|
60
|
+
/** @id CODE-APPROVAL-DOMAIN-SCOPING-004
|
|
61
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-008 REQ-APPROVAL-DOMAIN-SCOPING-021 REQ-APPROVAL-DOMAIN-SCOPING-022
|
|
62
|
+
* @design DES-APPROVAL-DOMAIN-SCOPING-004
|
|
63
|
+
*/
|
|
42
64
|
export declare function validateApprovals(root: string, config: ApprovalConfig): Promise<ApprovalValidation>;
|
|
65
|
+
/** @id CODE-APPROVAL-DOMAIN-SCOPING-008
|
|
66
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-009 REQ-APPROVAL-DOMAIN-SCOPING-010
|
|
67
|
+
* @design DES-APPROVAL-DOMAIN-SCOPING-004
|
|
68
|
+
*/
|
|
69
|
+
export declare function validateApprovalsForDomain(root: string, config: ApprovalConfig, domainName: string): Promise<ApprovalValidation>;
|
|
70
|
+
/** @id CODE-APPROVAL-DOMAIN-SCOPING-006
|
|
71
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-019 REQ-APPROVAL-DOMAIN-SCOPING-020
|
|
72
|
+
* @design DES-APPROVAL-DOMAIN-SCOPING-006
|
|
73
|
+
*/
|
|
74
|
+
export declare function validateApprovalsForFeatureGate(root: string, config: ApprovalConfig, domainName: string): Promise<ApprovalValidation>;
|
|
75
|
+
/** @id CODE-APPROVAL-DOMAIN-SCOPING-009
|
|
76
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-005 REQ-APPROVAL-DOMAIN-SCOPING-006 REQ-APPROVAL-DOMAIN-SCOPING-007
|
|
77
|
+
* @design DES-APPROVAL-DOMAIN-SCOPING-004
|
|
78
|
+
*/
|
|
79
|
+
export declare function requireDomainOption(config: ApprovalConfig, stage: ApprovalStage, domain: string | undefined): void;
|
|
80
|
+
export declare function requireValidateDomainOption(config: ApprovalConfig, domain: string | undefined): void;
|
|
43
81
|
/** @id CODE-CLI-WORKFLOW-UX-003
|
|
44
82
|
* @implements REQ-CLI-WORKFLOW-UX-003
|
|
45
83
|
* @design DES-CLI-WORKFLOW-UX-003
|
|
46
84
|
*/
|
|
47
|
-
|
|
48
|
-
|
|
85
|
+
/** @id CODE-APPROVAL-DOMAIN-SCOPING-005
|
|
86
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-016 REQ-APPROVAL-DOMAIN-SCOPING-017 REQ-APPROVAL-DOMAIN-SCOPING-018
|
|
87
|
+
* @design DES-APPROVAL-DOMAIN-SCOPING-005
|
|
88
|
+
*/
|
|
89
|
+
export declare function requireApproval(root: string, stage: ApprovalStage, config: ApprovalConfig, domain?: ResolvedDomain): Promise<void>;
|
|
90
|
+
/** @id CODE-APPROVAL-DOMAIN-SCOPING-010
|
|
91
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-016 REQ-APPROVAL-DOMAIN-SCOPING-017 REQ-APPROVAL-DOMAIN-SCOPING-018
|
|
92
|
+
* @design DES-APPROVAL-DOMAIN-SCOPING-005
|
|
93
|
+
*/
|
|
94
|
+
export declare function resolveOwningDomainOrThrow(root: string, config: ApprovalConfig, slug: string): Promise<ResolvedDomain | undefined>;
|
|
95
|
+
/** @id CODE-APPROVAL-DOMAIN-SCOPING-011
|
|
96
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-005 REQ-APPROVAL-DOMAIN-SCOPING-013
|
|
97
|
+
* @design DES-APPROVAL-DOMAIN-SCOPING-004
|
|
98
|
+
*/
|
|
99
|
+
export declare function resolveNamedDomain(root: string, config: ApprovalConfig, domainName: string): Promise<ResolvedDomain>;
|
|
100
|
+
/** @id CODE-APPROVAL-DOMAIN-SCOPING-012
|
|
101
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-017
|
|
102
|
+
* @design DES-APPROVAL-DOMAIN-SCOPING-005
|
|
103
|
+
*/
|
|
104
|
+
export declare function resolveDesignFileDomain(root: string, config: ApprovalConfig, file: string): Promise<ResolvedDomain | undefined>;
|
|
105
|
+
/** @id CODE-APPROVAL-DOMAIN-SCOPING-013
|
|
106
|
+
* @implements REQ-APPROVAL-DOMAIN-SCOPING-016
|
|
107
|
+
* @design DES-APPROVAL-DOMAIN-SCOPING-005
|
|
108
|
+
*/
|
|
109
|
+
export declare function resolveRequirementDomain(root: string, config: ApprovalConfig, requirementId: string): Promise<ResolvedDomain | undefined>;
|
|
110
|
+
export declare function validateStageArtifacts(root: string, stage: ApprovalStage, domain?: ResolvedDomain): Promise<void>;
|