@voybio/ace-swarm 2.4.3 → 2.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/assets/agent-state/INTERFACE_REGISTRY.md +2 -2
- package/assets/agent-state/MODULES/gates/gate-autonomy.json +4 -2
- package/assets/agent-state/MODULES/gates/gate-completeness.json +4 -2
- package/assets/agent-state/MODULES/gates/gate-operability.json +4 -2
- package/assets/agent-state/MODULES/gates/gate-security.json +3 -1
- package/assets/agent-state/MODULES/registry.json +1 -4
- package/assets/agent-state/MODULES/roles/capability-build.json +1 -2
- package/assets/agent-state/MODULES/roles/capability-eval.json +1 -1
- package/assets/agent-state/MODULES/roles/capability-qa.json +1 -2
- package/assets/agent-state/QUALITY_GATES.md +25 -9
- package/assets/agent-state/TEAL_CONFIG.md +2 -11
- package/dist/astgrep-index.d.ts +7 -1
- package/dist/astgrep-index.js +66 -39
- package/dist/gate-contract.d.ts +63 -0
- package/dist/gate-contract.js +178 -0
- package/dist/runtime-executor.js +45 -2
- package/dist/runtime-tool-specs.d.ts +8 -1
- package/dist/runtime-tool-specs.js +19 -1
- package/dist/schemas.js +16 -15
- package/dist/store/bootstrap-store.js +30 -6
- package/dist/store/gate-contract-migration.d.ts +10 -0
- package/dist/store/gate-contract-migration.js +413 -0
- package/dist/tools-files.js +68 -5
- package/dist/tools-framework.js +115 -37
- package/package.json +3 -1
- package/assets/agent-state/MODULES/gates/gate-correctness.json +0 -7
- package/assets/agent-state/MODULES/gates/gate-evaluation.json +0 -7
- package/assets/agent-state/MODULES/gates/gate-typescript-public-surface.json +0 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.4.3] - 2026-05-23
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Reduced default packaged workspace gates to portable ACE lifecycle gates only (`gate-autonomy`, `gate-completeness`, `gate-security`, `gate-operability`).
|
|
8
|
+
- Made `execute_gates({})` resolve the active ACE stage from `STATUS.md` and `TEAL_CONFIG.md` and only run stage-activated workspace gates by default.
|
|
9
|
+
- Added automatic exact-match migration for legacy package-seeded gate assets during store bootstrap and `ace repair`, while preserving customized project gates.
|
|
10
|
+
- Kept TypeScript public-surface verification in the package maintenance path instead of default workspace gate execution.
|
|
11
|
+
|
|
3
12
|
## [2.4.1] - 2026-05-08
|
|
4
13
|
|
|
5
14
|
### Changed
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
|
|
55
55
|
## Public Surface Gate
|
|
56
56
|
|
|
57
|
-
- `audit_public_surface` is the canonical TypeScript public-surface
|
|
57
|
+
- `audit_public_surface` is the canonical TypeScript public-surface audit for MCP tools, resources, prompts, and registered event names.
|
|
58
58
|
- `PUBLIC_SURFACE_REPORT.md` is the durable audit artifact written by that gate.
|
|
59
|
-
-
|
|
59
|
+
- The package-level public-surface check remains an explicit package maintenance tool or CI gate; it is not seeded into initialized ACE workspaces as a default stage gate.
|
|
60
60
|
|
|
61
61
|
## Provenance Contract
|
|
62
62
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "gate-autonomy",
|
|
3
3
|
"type": "artifact_scan",
|
|
4
|
-
"
|
|
4
|
+
"scope": "workspace",
|
|
5
|
+
"activation": "stage",
|
|
6
|
+
"invariant": "ACE lifecycle state records objective, evidence, and handoff continuity for the active stage",
|
|
5
7
|
"command": "",
|
|
6
|
-
"evidence_requirement": "STATUS + EVIDENCE_LOG + HANDOFF
|
|
8
|
+
"evidence_requirement": "STATUS.md + EVIDENCE_LOG.md + HANDOFF.json"
|
|
7
9
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "gate-completeness",
|
|
3
3
|
"type": "artifact_scan",
|
|
4
|
-
"
|
|
4
|
+
"scope": "workspace",
|
|
5
|
+
"activation": "stage",
|
|
6
|
+
"invariant": "Required ACE lifecycle state artifacts exist and are non-empty",
|
|
5
7
|
"command": "",
|
|
6
|
-
"evidence_requirement": "
|
|
8
|
+
"evidence_requirement": "TASK.md + SCOPE.md + QUALITY_GATES.md + STATUS.md + HANDOFF.json + EVIDENCE_LOG.md + DECISIONS.md + RISKS.md + TEAL_CONFIG.md + PROVENANCE_LOG.md + ARTIFACT_MANIFEST.json"
|
|
7
9
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "gate-operability",
|
|
3
3
|
"type": "artifact_scan",
|
|
4
|
-
"
|
|
4
|
+
"scope": "workspace",
|
|
5
|
+
"activation": "stage",
|
|
6
|
+
"invariant": "Release-stage operability evidence records observability coverage and a release decision",
|
|
5
7
|
"command": "",
|
|
6
|
-
"evidence_requirement": "
|
|
8
|
+
"evidence_requirement": "OBSERVABILITY_REPORT.md + RELEASE_DECISION.md"
|
|
7
9
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "gate-security",
|
|
3
3
|
"type": "manual_review",
|
|
4
|
+
"scope": "workspace",
|
|
5
|
+
"activation": "stage",
|
|
4
6
|
"invariant": "No unmitigated high-severity security or policy risks",
|
|
5
7
|
"command": "",
|
|
6
|
-
"evidence_requirement": "
|
|
8
|
+
"evidence_requirement": "RISKS.md + SECURITY_REPORT.md"
|
|
7
9
|
}
|
|
@@ -19,12 +19,9 @@
|
|
|
19
19
|
],
|
|
20
20
|
"gates": [
|
|
21
21
|
"gate-completeness",
|
|
22
|
-
"gate-correctness",
|
|
23
22
|
"gate-autonomy",
|
|
24
23
|
"gate-security",
|
|
25
|
-
"gate-operability"
|
|
26
|
-
"gate-evaluation",
|
|
27
|
-
"gate-typescript-public-surface"
|
|
24
|
+
"gate-operability"
|
|
28
25
|
],
|
|
29
26
|
"schemas": [
|
|
30
27
|
"STATUS_EVENT.schema.json",
|
|
@@ -1,15 +1,31 @@
|
|
|
1
1
|
# QUALITY GATES
|
|
2
2
|
|
|
3
|
+
Portable default workspace gates cover ACE lifecycle verification only. Product correctness, evaluation, and package public-surface checks must be added explicitly when a project needs them.
|
|
4
|
+
|
|
3
5
|
## gate-completeness
|
|
4
6
|
|
|
5
|
-
- Invariant: required artifacts exist and are non-empty.
|
|
6
|
-
- Pass condition:
|
|
7
|
-
- Fail condition: any
|
|
8
|
-
- Evidence:
|
|
7
|
+
- Invariant: required ACE lifecycle state artifacts exist and are non-empty.
|
|
8
|
+
- Pass condition: the seeded ACE state surface is present and readable.
|
|
9
|
+
- Fail condition: any required ACE state artifact is missing or empty.
|
|
10
|
+
- Evidence: `TASK.md`, `SCOPE.md`, `QUALITY_GATES.md`, `STATUS.md`, `HANDOFF.json`, `EVIDENCE_LOG.md`, `DECISIONS.md`, `RISKS.md`, `TEAL_CONFIG.md`, `PROVENANCE_LOG.md`, and `ARTIFACT_MANIFEST.json`.
|
|
11
|
+
|
|
12
|
+
## gate-autonomy
|
|
13
|
+
|
|
14
|
+
- Invariant: the active ACE stage records status, evidence, and handoff continuity.
|
|
15
|
+
- Pass condition: `STATUS.md`, `EVIDENCE_LOG.md`, and `HANDOFF.json` exist for the active stage.
|
|
16
|
+
- Fail condition: any of those lifecycle artifacts is missing.
|
|
17
|
+
- Evidence: `STATUS.md`, `EVIDENCE_LOG.md`, and `HANDOFF.json`.
|
|
18
|
+
|
|
19
|
+
## gate-security
|
|
20
|
+
|
|
21
|
+
- Invariant: security review has explicit risk context and a review artifact.
|
|
22
|
+
- Pass condition: the active security review records `RISKS.md` and `SECURITY_REPORT.md`, then the manual review is completed.
|
|
23
|
+
- Fail condition: required review artifacts are missing or manual review is still pending.
|
|
24
|
+
- Evidence: `RISKS.md` and `SECURITY_REPORT.md`.
|
|
9
25
|
|
|
10
|
-
## gate-
|
|
26
|
+
## gate-operability
|
|
11
27
|
|
|
12
|
-
- Invariant:
|
|
13
|
-
- Pass condition:
|
|
14
|
-
- Fail condition:
|
|
15
|
-
- Evidence:
|
|
28
|
+
- Invariant: release readiness includes observability evidence and a release decision.
|
|
29
|
+
- Pass condition: `OBSERVABILITY_REPORT.md` and `RELEASE_DECISION.md` exist for the release stage.
|
|
30
|
+
- Fail condition: either release-stage artifact is missing.
|
|
31
|
+
- Evidence: `OBSERVABILITY_REPORT.md` and `RELEASE_DECISION.md`.
|
|
@@ -37,7 +37,7 @@ modules:
|
|
|
37
37
|
qa:
|
|
38
38
|
version: "1.0.0"
|
|
39
39
|
depends_on: ["build"]
|
|
40
|
-
gates: ["gate-completeness"
|
|
40
|
+
gates: ["gate-completeness"]
|
|
41
41
|
|
|
42
42
|
docs:
|
|
43
43
|
version: "1.0.0"
|
|
@@ -64,7 +64,6 @@ modules:
|
|
|
64
64
|
eval:
|
|
65
65
|
version: "1.0.0"
|
|
66
66
|
depends_on: ["qa"]
|
|
67
|
-
gates: ["gate-evaluation"]
|
|
68
67
|
|
|
69
68
|
release:
|
|
70
69
|
version: "1.0.0"
|
|
@@ -93,11 +92,7 @@ pipelines:
|
|
|
93
92
|
gates:
|
|
94
93
|
gate-completeness:
|
|
95
94
|
module: qa
|
|
96
|
-
inputs: ["
|
|
97
|
-
|
|
98
|
-
gate-correctness:
|
|
99
|
-
module: qa
|
|
100
|
-
inputs: ["test_results", "EVIDENCE_LOG.md"]
|
|
95
|
+
inputs: ["TASK.md", "SCOPE.md", "QUALITY_GATES.md", "STATUS.md", "HANDOFF.json", "EVIDENCE_LOG.md", "DECISIONS.md", "RISKS.md", "TEAL_CONFIG.md", "PROVENANCE_LOG.md", "ARTIFACT_MANIFEST.json"]
|
|
101
96
|
|
|
102
97
|
gate-autonomy:
|
|
103
98
|
module: skeptic
|
|
@@ -107,10 +102,6 @@ gates:
|
|
|
107
102
|
module: security
|
|
108
103
|
inputs: ["RISKS.md", "SECURITY_REPORT.md"]
|
|
109
104
|
|
|
110
|
-
gate-evaluation:
|
|
111
|
-
module: eval
|
|
112
|
-
inputs: ["EVAL_REPORT.md"]
|
|
113
|
-
|
|
114
105
|
gate-operability:
|
|
115
106
|
module: release
|
|
116
107
|
inputs: ["OBSERVABILITY_REPORT.md", "RELEASE_DECISION.md"]
|
package/dist/astgrep-index.d.ts
CHANGED
|
@@ -10,11 +10,17 @@ interface AstGrepMatch {
|
|
|
10
10
|
line?: number;
|
|
11
11
|
column?: number;
|
|
12
12
|
};
|
|
13
|
+
byteOffset?: {
|
|
14
|
+
start?: number;
|
|
15
|
+
end?: number;
|
|
16
|
+
};
|
|
13
17
|
};
|
|
14
|
-
metaVariables?:
|
|
18
|
+
metaVariables?: unknown;
|
|
15
19
|
kind?: string;
|
|
16
20
|
nodeKind?: string;
|
|
21
|
+
replacement?: string;
|
|
17
22
|
}
|
|
23
|
+
export declare function flattenMetaVariables(raw: unknown): Record<string, string>;
|
|
18
24
|
export interface AstgrepMatch {
|
|
19
25
|
file: string;
|
|
20
26
|
line: number;
|
package/dist/astgrep-index.js
CHANGED
|
@@ -4,6 +4,57 @@ import { isAbsolute, relative, resolve } from "node:path";
|
|
|
4
4
|
import { spawnSync } from "node:child_process";
|
|
5
5
|
import { appendStatusEventSafe } from "./status-events.js";
|
|
6
6
|
import { safeRead, safeWrite, WORKSPACE_ROOT, wsPath } from "./helpers.js";
|
|
7
|
+
function textFromMetaValue(value) {
|
|
8
|
+
if (typeof value === "string")
|
|
9
|
+
return value;
|
|
10
|
+
if (value && typeof value === "object") {
|
|
11
|
+
const text = value.text;
|
|
12
|
+
if (typeof text === "string")
|
|
13
|
+
return text;
|
|
14
|
+
}
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
const META_SINGLE_BUCKETS = ["single", "transformed"];
|
|
18
|
+
const META_MULTI_BUCKETS = ["multi"];
|
|
19
|
+
export function flattenMetaVariables(raw) {
|
|
20
|
+
const out = {};
|
|
21
|
+
if (!raw || typeof raw !== "object")
|
|
22
|
+
return out;
|
|
23
|
+
const root = raw;
|
|
24
|
+
for (const bucketKey of META_SINGLE_BUCKETS) {
|
|
25
|
+
const bucket = root[bucketKey];
|
|
26
|
+
if (bucket && typeof bucket === "object") {
|
|
27
|
+
for (const [name, value] of Object.entries(bucket)) {
|
|
28
|
+
const text = textFromMetaValue(value);
|
|
29
|
+
if (typeof text === "string" && text.length > 0)
|
|
30
|
+
out[name] = text;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
for (const bucketKey of META_MULTI_BUCKETS) {
|
|
35
|
+
const bucket = root[bucketKey];
|
|
36
|
+
if (bucket && typeof bucket === "object") {
|
|
37
|
+
for (const [name, value] of Object.entries(bucket)) {
|
|
38
|
+
if (!Array.isArray(value))
|
|
39
|
+
continue;
|
|
40
|
+
const texts = value
|
|
41
|
+
.map(textFromMetaValue)
|
|
42
|
+
.filter((text) => typeof text === "string" && text.length > 0);
|
|
43
|
+
if (texts.length > 0)
|
|
44
|
+
out[name] = texts.join("");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const reserved = new Set([...META_SINGLE_BUCKETS, ...META_MULTI_BUCKETS]);
|
|
49
|
+
for (const [name, value] of Object.entries(root)) {
|
|
50
|
+
if (reserved.has(name) || out[name] !== undefined)
|
|
51
|
+
continue;
|
|
52
|
+
const text = textFromMetaValue(value);
|
|
53
|
+
if (typeof text === "string" && text.length > 0)
|
|
54
|
+
out[name] = text;
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
7
58
|
export function runAstgrepQuery(pattern, lang, roots, _contextLines) {
|
|
8
59
|
const cmd = detectAstgrepCommand();
|
|
9
60
|
const raw = runAstgrep(cmd, pattern, lang, roots);
|
|
@@ -32,15 +83,8 @@ function nodeKindForMatch(match) {
|
|
|
32
83
|
return undefined;
|
|
33
84
|
}
|
|
34
85
|
function captureMapForMatch(match) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const captures = Object.entries(match.metaVariables).reduce((acc, [key]) => {
|
|
38
|
-
const text = extractMetaVarText(match, key);
|
|
39
|
-
if (typeof text === "string" && text.length > 0)
|
|
40
|
-
acc[key] = text;
|
|
41
|
-
return acc;
|
|
42
|
-
}, {});
|
|
43
|
-
return Object.keys(captures).length > 0 ? captures : undefined;
|
|
86
|
+
const flat = flattenMetaVariables(match.metaVariables);
|
|
87
|
+
return Object.keys(flat).length > 0 ? flat : undefined;
|
|
44
88
|
}
|
|
45
89
|
function computeMatchId(input) {
|
|
46
90
|
const encoded = [
|
|
@@ -59,12 +103,8 @@ function computeMatchId(input) {
|
|
|
59
103
|
function matchUsesSymbolHint(match, symbolHint) {
|
|
60
104
|
if ((match.text ?? "").includes(symbolHint))
|
|
61
105
|
return true;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return Object.keys(match.metaVariables).some((key) => {
|
|
65
|
-
const text = extractMetaVarText(match, key);
|
|
66
|
-
return typeof text === "string" && text.includes(symbolHint);
|
|
67
|
-
});
|
|
106
|
+
const flat = flattenMetaVariables(match.metaVariables);
|
|
107
|
+
return Object.values(flat).some((text) => text.includes(symbolHint));
|
|
68
108
|
}
|
|
69
109
|
export function locateAstgrepMatches(input) {
|
|
70
110
|
const scope = resolveScope(input.scope ?? "src");
|
|
@@ -109,6 +149,14 @@ export function locateAstgrepMatches(input) {
|
|
|
109
149
|
line: match.range.end?.line ?? 0,
|
|
110
150
|
column: match.range.end?.column ?? 0,
|
|
111
151
|
},
|
|
152
|
+
...(match.range.byteOffset
|
|
153
|
+
? {
|
|
154
|
+
byteOffset: {
|
|
155
|
+
start: match.range.byteOffset.start ?? 0,
|
|
156
|
+
end: match.range.byteOffset.end ?? 0,
|
|
157
|
+
},
|
|
158
|
+
}
|
|
159
|
+
: {}),
|
|
112
160
|
};
|
|
113
161
|
return [{
|
|
114
162
|
match_id: computeMatchId({
|
|
@@ -270,27 +318,6 @@ function runAstgrep(astgrepCmd, pattern, lang, paths) {
|
|
|
270
318
|
return [];
|
|
271
319
|
return parseAstgrepOutput(res.stdout);
|
|
272
320
|
}
|
|
273
|
-
function extractMetaVarText(match, key) {
|
|
274
|
-
const raw = match.metaVariables?.[key];
|
|
275
|
-
if (!raw)
|
|
276
|
-
return undefined;
|
|
277
|
-
if (typeof raw === "string")
|
|
278
|
-
return raw;
|
|
279
|
-
if (raw && typeof raw === "object" && "text" in raw && typeof raw.text === "string") {
|
|
280
|
-
return raw.text;
|
|
281
|
-
}
|
|
282
|
-
if (Array.isArray(raw)) {
|
|
283
|
-
const first = raw[0];
|
|
284
|
-
if (typeof first === "string")
|
|
285
|
-
return first;
|
|
286
|
-
if (first && typeof first === "object" && "text" in first) {
|
|
287
|
-
const text = first.text;
|
|
288
|
-
if (typeof text === "string")
|
|
289
|
-
return text;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
return undefined;
|
|
293
|
-
}
|
|
294
321
|
function uniqueSorted(values, limit) {
|
|
295
322
|
return [...new Set(values.filter(Boolean))].sort((a, b) => a.localeCompare(b)).slice(0, limit);
|
|
296
323
|
}
|
|
@@ -357,13 +384,13 @@ export async function refreshAstgrepIndex(input = {}) {
|
|
|
357
384
|
const pyTestMatches = runAstgrep(astgrepCmd, "def test_$NAME($$$ARGS):", "python", pyRoots);
|
|
358
385
|
const handoffRefMatches = runAstgrep(astgrepCmd, '$X(\"SWARM_HANDOFF.$Y\")', "ts", tsRoots);
|
|
359
386
|
const exportedFunctions = uniqueSorted(exportedFnMatches
|
|
360
|
-
.map((m) =>
|
|
387
|
+
.map((m) => flattenMetaVariables(m.metaVariables).NAME)
|
|
361
388
|
.filter((v) => Boolean(v)), 40);
|
|
362
389
|
const exportedClasses = uniqueSorted(exportedClassMatches
|
|
363
|
-
.map((m) =>
|
|
390
|
+
.map((m) => flattenMetaVariables(m.metaVariables).NAME)
|
|
364
391
|
.filter((v) => Boolean(v)), 30);
|
|
365
392
|
const asyncFunctions = uniqueSorted(asyncFnMatches
|
|
366
|
-
.map((m) =>
|
|
393
|
+
.map((m) => flattenMetaVariables(m.metaVariables).NAME)
|
|
367
394
|
.filter((v) => Boolean(v)), 40);
|
|
368
395
|
// Fallback symbol extraction when AST match shapes differ across language versions.
|
|
369
396
|
const tsFiles = codeFiles.filter((file) => /\.(ts|tsx|js)$/i.test(file));
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export type GateScope = "workspace" | "package";
|
|
2
|
+
export type GateActivation = "stage" | "explicit_only";
|
|
3
|
+
export type GateManifestType = "executable" | "artifact_scan" | "manual_review";
|
|
4
|
+
export type GateManifestSource = "workspace" | "store" | "none";
|
|
5
|
+
export type GateSelectionMode = "explicit" | "active_stage";
|
|
6
|
+
export interface GateManifest {
|
|
7
|
+
id: string;
|
|
8
|
+
type: GateManifestType;
|
|
9
|
+
invariant: string;
|
|
10
|
+
command: string;
|
|
11
|
+
evidence_requirement: string;
|
|
12
|
+
scope?: GateScope;
|
|
13
|
+
activation?: GateActivation;
|
|
14
|
+
source_ref?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface GateSelectionContext {
|
|
17
|
+
active_role?: string;
|
|
18
|
+
active_module?: string;
|
|
19
|
+
active_pipeline?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface TealModuleConfig {
|
|
22
|
+
version?: string;
|
|
23
|
+
interface?: string;
|
|
24
|
+
sidecar?: boolean;
|
|
25
|
+
depends_on?: string[];
|
|
26
|
+
outputs?: string[];
|
|
27
|
+
gates?: string[];
|
|
28
|
+
}
|
|
29
|
+
export interface TealGateConfig {
|
|
30
|
+
module?: string;
|
|
31
|
+
inputs?: string[];
|
|
32
|
+
}
|
|
33
|
+
export interface TealConfig {
|
|
34
|
+
modules?: Record<string, TealModuleConfig>;
|
|
35
|
+
pipelines?: Record<string, unknown>;
|
|
36
|
+
gates?: Record<string, TealGateConfig>;
|
|
37
|
+
}
|
|
38
|
+
export interface GateSelectionResult extends GateSelectionContext {
|
|
39
|
+
selection_mode: GateSelectionMode;
|
|
40
|
+
manifest_source: GateManifestSource;
|
|
41
|
+
selected: GateManifest[];
|
|
42
|
+
selected_gate_ids: string[];
|
|
43
|
+
skipped_gate_ids: string[];
|
|
44
|
+
all_gate_ids: string[];
|
|
45
|
+
missing_explicit_gate_ids: string[];
|
|
46
|
+
note?: string;
|
|
47
|
+
}
|
|
48
|
+
export declare function normalizeActiveModuleName(value?: string): string | undefined;
|
|
49
|
+
export declare function parseGateManifest(raw: string, sourceRef: string): GateManifest | undefined;
|
|
50
|
+
export declare function extractFencedYamlBlock(content: string): {
|
|
51
|
+
language: string;
|
|
52
|
+
yaml: string;
|
|
53
|
+
} | undefined;
|
|
54
|
+
export declare function parseTealConfigMarkdown(content: string): TealConfig | undefined;
|
|
55
|
+
export declare function parseStatusStageContext(content: string): GateSelectionContext;
|
|
56
|
+
export declare function resolveGateSelection(params: {
|
|
57
|
+
gates: GateManifest[];
|
|
58
|
+
manifestSource: GateManifestSource;
|
|
59
|
+
statusRaw: string;
|
|
60
|
+
tealRaw: string;
|
|
61
|
+
gateIds?: string[];
|
|
62
|
+
}): GateSelectionResult;
|
|
63
|
+
//# sourceMappingURL=gate-contract.d.ts.map
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { parseDocument } from "yaml";
|
|
2
|
+
function isRecord(value) {
|
|
3
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
function normalizeLineEndings(value) {
|
|
6
|
+
return value.replace(/\r\n/g, "\n");
|
|
7
|
+
}
|
|
8
|
+
function uniquePush(target, value) {
|
|
9
|
+
if (!target.includes(value))
|
|
10
|
+
target.push(value);
|
|
11
|
+
}
|
|
12
|
+
export function normalizeActiveModuleName(value) {
|
|
13
|
+
if (typeof value !== "string")
|
|
14
|
+
return undefined;
|
|
15
|
+
const trimmed = value.trim().replace(/^`|`$/g, "");
|
|
16
|
+
if (!trimmed)
|
|
17
|
+
return undefined;
|
|
18
|
+
return trimmed.replace(/^(capability|agent)-/, "");
|
|
19
|
+
}
|
|
20
|
+
export function parseGateManifest(raw, sourceRef) {
|
|
21
|
+
try {
|
|
22
|
+
const gate = JSON.parse(raw);
|
|
23
|
+
if (!isRecord(gate))
|
|
24
|
+
return undefined;
|
|
25
|
+
const id = typeof gate.id === "string" ? gate.id.trim() : "";
|
|
26
|
+
if (!id)
|
|
27
|
+
return undefined;
|
|
28
|
+
const type = gate.type === "executable" || gate.type === "artifact_scan" || gate.type === "manual_review"
|
|
29
|
+
? gate.type
|
|
30
|
+
: "manual_review";
|
|
31
|
+
const scope = gate.scope === "workspace" || gate.scope === "package" ? gate.scope : undefined;
|
|
32
|
+
const activation = gate.activation === "stage" || gate.activation === "explicit_only"
|
|
33
|
+
? gate.activation
|
|
34
|
+
: undefined;
|
|
35
|
+
return {
|
|
36
|
+
id,
|
|
37
|
+
type,
|
|
38
|
+
invariant: typeof gate.invariant === "string" ? gate.invariant : "",
|
|
39
|
+
command: typeof gate.command === "string" ? gate.command : "",
|
|
40
|
+
evidence_requirement: typeof gate.evidence_requirement === "string" ? gate.evidence_requirement : "",
|
|
41
|
+
...(scope ? { scope } : {}),
|
|
42
|
+
...(activation ? { activation } : {}),
|
|
43
|
+
source_ref: sourceRef,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function extractFencedYamlBlock(content) {
|
|
51
|
+
const normalized = normalizeLineEndings(content);
|
|
52
|
+
const fenceCount = (normalized.match(/```/g) ?? []).length;
|
|
53
|
+
const fencedBlocks = [...normalized.matchAll(/```([^\n]*)\n([\s\S]*?)```/g)];
|
|
54
|
+
if (fenceCount !== 2 || fencedBlocks.length !== 1) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
const block = fencedBlocks[0];
|
|
58
|
+
return {
|
|
59
|
+
language: block[1].trim().toLowerCase(),
|
|
60
|
+
yaml: block[2].trim(),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export function parseTealConfigMarkdown(content) {
|
|
64
|
+
const block = extractFencedYamlBlock(content);
|
|
65
|
+
if (!block || block.language !== "yaml" || !block.yaml) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
const document = parseDocument(block.yaml);
|
|
69
|
+
if (document.errors.length > 0) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
const parsed = document.toJS();
|
|
73
|
+
return isRecord(parsed) ? parsed : undefined;
|
|
74
|
+
}
|
|
75
|
+
function extractStatusField(content, label) {
|
|
76
|
+
const normalized = normalizeLineEndings(content);
|
|
77
|
+
const escapedLabel = label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
78
|
+
const match = normalized.match(new RegExp(`^-\\s*${escapedLabel}:\\s*(.+)$`, "im"));
|
|
79
|
+
if (!match)
|
|
80
|
+
return undefined;
|
|
81
|
+
const value = match[1].trim().replace(/^`|`$/g, "");
|
|
82
|
+
return value || undefined;
|
|
83
|
+
}
|
|
84
|
+
export function parseStatusStageContext(content) {
|
|
85
|
+
const activeRole = extractStatusField(content, "Current role");
|
|
86
|
+
const explicitModule = extractStatusField(content, "Current module");
|
|
87
|
+
return {
|
|
88
|
+
active_role: activeRole,
|
|
89
|
+
active_module: normalizeActiveModuleName(explicitModule ?? activeRole),
|
|
90
|
+
active_pipeline: extractStatusField(content, "Active pipeline"),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function readStageAssignedGateIds(tealConfig, activeModule) {
|
|
94
|
+
if (!tealConfig || !activeModule)
|
|
95
|
+
return [];
|
|
96
|
+
const gateIds = [];
|
|
97
|
+
const moduleConfig = tealConfig.modules?.[activeModule];
|
|
98
|
+
if (moduleConfig && Array.isArray(moduleConfig.gates)) {
|
|
99
|
+
for (const gateId of moduleConfig.gates) {
|
|
100
|
+
if (typeof gateId === "string" && gateId.trim()) {
|
|
101
|
+
uniquePush(gateIds, gateId.trim());
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (tealConfig.gates) {
|
|
106
|
+
for (const [gateId, gateConfig] of Object.entries(tealConfig.gates)) {
|
|
107
|
+
if (gateConfig &&
|
|
108
|
+
typeof gateConfig.module === "string" &&
|
|
109
|
+
normalizeActiveModuleName(gateConfig.module) === activeModule) {
|
|
110
|
+
uniquePush(gateIds, gateId);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return gateIds;
|
|
115
|
+
}
|
|
116
|
+
function effectiveGateScope(gate) {
|
|
117
|
+
return gate.scope ?? "workspace";
|
|
118
|
+
}
|
|
119
|
+
function effectiveGateActivation(gate, assignedStageGateIds) {
|
|
120
|
+
return gate.activation ?? (assignedStageGateIds.has(gate.id) ? "stage" : "explicit_only");
|
|
121
|
+
}
|
|
122
|
+
export function resolveGateSelection(params) {
|
|
123
|
+
const { gates, manifestSource, statusRaw, tealRaw, gateIds } = params;
|
|
124
|
+
const allGateIds = gates.map((gate) => gate.id);
|
|
125
|
+
const statusContext = parseStatusStageContext(statusRaw);
|
|
126
|
+
if (Array.isArray(gateIds) && gateIds.length > 0) {
|
|
127
|
+
const gateMap = new Map(gates.map((gate) => [gate.id, gate]));
|
|
128
|
+
const selected = [];
|
|
129
|
+
const missingExplicitGateIds = [];
|
|
130
|
+
for (const gateId of gateIds) {
|
|
131
|
+
const gate = gateMap.get(gateId);
|
|
132
|
+
if (gate) {
|
|
133
|
+
selected.push(gate);
|
|
134
|
+
}
|
|
135
|
+
else if (!missingExplicitGateIds.includes(gateId)) {
|
|
136
|
+
missingExplicitGateIds.push(gateId);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
selection_mode: "explicit",
|
|
141
|
+
manifest_source: manifestSource,
|
|
142
|
+
selected,
|
|
143
|
+
selected_gate_ids: selected.map((gate) => gate.id),
|
|
144
|
+
skipped_gate_ids: allGateIds.filter((gateId) => !selected.some((gate) => gate.id === gateId)),
|
|
145
|
+
all_gate_ids: allGateIds,
|
|
146
|
+
missing_explicit_gate_ids: missingExplicitGateIds,
|
|
147
|
+
...statusContext,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const tealConfig = parseTealConfigMarkdown(tealRaw);
|
|
151
|
+
const mappedGateIds = readStageAssignedGateIds(tealConfig, statusContext.active_module);
|
|
152
|
+
const mappedGateIdSet = new Set(mappedGateIds);
|
|
153
|
+
const selected = gates.filter((gate) => mappedGateIdSet.has(gate.id) &&
|
|
154
|
+
effectiveGateScope(gate) === "workspace" &&
|
|
155
|
+
effectiveGateActivation(gate, mappedGateIdSet) === "stage");
|
|
156
|
+
let note;
|
|
157
|
+
if (!statusContext.active_module) {
|
|
158
|
+
note = "No active ACE role/module could be resolved from STATUS.md. Use explicit gate_ids when needed.";
|
|
159
|
+
}
|
|
160
|
+
else if (mappedGateIds.length === 0) {
|
|
161
|
+
note = `No stage-activated workspace gates are configured for active module "${statusContext.active_module}". Use explicit gate_ids when needed.`;
|
|
162
|
+
}
|
|
163
|
+
else if (selected.length === 0) {
|
|
164
|
+
note = `No implicitly runnable workspace gates matched active module "${statusContext.active_module}". Package-scope and explicit-only gates require explicit gate_ids.`;
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
selection_mode: "active_stage",
|
|
168
|
+
manifest_source: manifestSource,
|
|
169
|
+
selected,
|
|
170
|
+
selected_gate_ids: selected.map((gate) => gate.id),
|
|
171
|
+
skipped_gate_ids: allGateIds.filter((gateId) => !selected.some((gate) => gate.id === gateId)),
|
|
172
|
+
all_gate_ids: allGateIds,
|
|
173
|
+
missing_explicit_gate_ids: [],
|
|
174
|
+
note,
|
|
175
|
+
...statusContext,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=gate-contract.js.map
|