@getvda/test-suite 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +30 -0
- package/README.md +74 -0
- package/dist/cli.d.ts +26 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +0 -0
- package/dist/cli.js.map +1 -0
- package/dist/condition.d.ts +46 -0
- package/dist/condition.d.ts.map +1 -0
- package/dist/condition.js +117 -0
- package/dist/condition.js.map +1 -0
- package/dist/impact.d.ts +50 -0
- package/dist/impact.d.ts.map +1 -0
- package/dist/impact.js +125 -0
- package/dist/impact.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/render.d.ts +17 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +65 -0
- package/dist/render.js.map +1 -0
- package/dist/sandbox.d.ts +32 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +28 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/scenarios.d.ts +34 -0
- package/dist/scenarios.d.ts.map +1 -0
- package/dist/scenarios.js +209 -0
- package/dist/scenarios.js.map +1 -0
- package/dist/signoff-trailer.d.ts +99 -0
- package/dist/signoff-trailer.d.ts.map +1 -0
- package/dist/signoff-trailer.js +129 -0
- package/dist/signoff-trailer.js.map +1 -0
- package/dist/suite.d.ts +44 -0
- package/dist/suite.d.ts.map +1 -0
- package/dist/suite.js +161 -0
- package/dist/suite.js.map +1 -0
- package/dist/types.d.ts +57 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +24 -0
- package/dist/types.js.map +1 -0
- package/package.json +37 -0
package/dist/impact.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic impact analysis (3d).
|
|
3
|
+
*
|
|
4
|
+
* Given a governance change, answer — from the data ACP actually has — which
|
|
5
|
+
* files, environments, agents, capabilities, and journeys it affects. Same input
|
|
6
|
+
* → same output; no heuristics in the counts.
|
|
7
|
+
*
|
|
8
|
+
* HONESTY about data sources: ACP can read environments/agents from governance
|
|
9
|
+
* frontmatter and from an ACP-maintained environment registry. It CANNOT yet
|
|
10
|
+
* read live admitted-agent facts from Onboarding — that skill does not exist on
|
|
11
|
+
* Onboarding's live card (verified 2026-07-18). So when the agent list is derived
|
|
12
|
+
* only from frontmatter, impact says so and marks the result incomplete, rather
|
|
13
|
+
* than implying it enumerated the real fleet.
|
|
14
|
+
*
|
|
15
|
+
* Shaped for the Phase-4 human approver, same as the honest diff.
|
|
16
|
+
*/
|
|
17
|
+
import { GOVERNANCE_FILE_NAMES, presentFiles, } from '@getvda/governance-schema';
|
|
18
|
+
function environmentsOf(set) {
|
|
19
|
+
const envs = new Set();
|
|
20
|
+
for (const name of GOVERNANCE_FILE_NAMES) {
|
|
21
|
+
const env = set.files[name]?.parsed.frontmatter.environment;
|
|
22
|
+
if (typeof env === 'string' && env)
|
|
23
|
+
envs.add(env);
|
|
24
|
+
}
|
|
25
|
+
return envs;
|
|
26
|
+
}
|
|
27
|
+
function agentsFromFrontmatter(set) {
|
|
28
|
+
const agents = new Set();
|
|
29
|
+
for (const name of GOVERNANCE_FILE_NAMES) {
|
|
30
|
+
const agent = set.files[name]?.parsed.frontmatter.agent;
|
|
31
|
+
if (typeof agent === 'string' && agent)
|
|
32
|
+
agents.add(agent);
|
|
33
|
+
}
|
|
34
|
+
return agents;
|
|
35
|
+
}
|
|
36
|
+
function capabilityNames(set) {
|
|
37
|
+
const skill = set.files['SKILL.md'];
|
|
38
|
+
return new Map((skill?.parsed.capabilities ?? []).map((c) => [c.capability, c.conditions]));
|
|
39
|
+
}
|
|
40
|
+
function sameConditions(a, b) {
|
|
41
|
+
if (a.length !== b.length)
|
|
42
|
+
return false;
|
|
43
|
+
const bs = new Set(b);
|
|
44
|
+
return a.every((x) => bs.has(x));
|
|
45
|
+
}
|
|
46
|
+
export function computeImpact(before, after, diff, registry) {
|
|
47
|
+
const notes = [];
|
|
48
|
+
const changedFiles = diff.files.map((f) => f.file);
|
|
49
|
+
const environments = [...new Set([...environmentsOf(before), ...environmentsOf(after)])].sort();
|
|
50
|
+
// Capabilities affected: added, removed, or whose conditions changed.
|
|
51
|
+
const beforeCaps = capabilityNames(before);
|
|
52
|
+
const afterCaps = capabilityNames(after);
|
|
53
|
+
const affectedCaps = new Set();
|
|
54
|
+
for (const [name, conds] of afterCaps) {
|
|
55
|
+
const prev = beforeCaps.get(name);
|
|
56
|
+
if (prev === undefined || !sameConditions(prev, conds))
|
|
57
|
+
affectedCaps.add(name);
|
|
58
|
+
}
|
|
59
|
+
for (const name of beforeCaps.keys()) {
|
|
60
|
+
if (!afterCaps.has(name))
|
|
61
|
+
affectedCaps.add(name);
|
|
62
|
+
}
|
|
63
|
+
// Clause impact — deterministic deltas across the SOP fingerprint.
|
|
64
|
+
const bSop = before.files['SOP.md']?.fingerprint;
|
|
65
|
+
const aSop = after.files['SOP.md']?.fingerprint;
|
|
66
|
+
const clauseImpact = {
|
|
67
|
+
mustDelta: (aSop?.mustCount ?? 0) - (bSop?.mustCount ?? 0),
|
|
68
|
+
mustNotDelta: (aSop?.mustNotCount ?? 0) - (bSop?.mustNotCount ?? 0),
|
|
69
|
+
citationsChanged: [...diff.summary.citationsDropped, ...diff.summary.citationsAdded].sort(),
|
|
70
|
+
};
|
|
71
|
+
// Agents — registry first, else frontmatter (incomplete), else none.
|
|
72
|
+
const agents = resolveAgents(environments, after, before, registry, notes);
|
|
73
|
+
// Journeys — only from the registry; otherwise honestly undetermined.
|
|
74
|
+
let journeys = 'undetermined';
|
|
75
|
+
if (registry) {
|
|
76
|
+
const js = new Set();
|
|
77
|
+
for (const env of environments) {
|
|
78
|
+
const entry = registry.environments.find((e) => e.id === env);
|
|
79
|
+
for (const j of entry?.journeys ?? [])
|
|
80
|
+
js.add(j);
|
|
81
|
+
}
|
|
82
|
+
journeys = js.size ? [...js].sort() : 'undetermined';
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
notes.push('Journeys undetermined: no environment registry provided.');
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
changedFiles,
|
|
89
|
+
environments,
|
|
90
|
+
agents,
|
|
91
|
+
capabilitiesAffected: [...affectedCaps].sort(),
|
|
92
|
+
clauseImpact,
|
|
93
|
+
journeys,
|
|
94
|
+
notes,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function resolveAgents(environments, after, before, registry, notes) {
|
|
98
|
+
if (registry) {
|
|
99
|
+
const agents = new Set();
|
|
100
|
+
let coveredAll = true;
|
|
101
|
+
for (const env of environments) {
|
|
102
|
+
const entry = registry.environments.find((e) => e.id === env);
|
|
103
|
+
if (!entry) {
|
|
104
|
+
coveredAll = false;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
for (const a of entry.agents)
|
|
108
|
+
agents.add(a);
|
|
109
|
+
}
|
|
110
|
+
if (!coveredAll) {
|
|
111
|
+
notes.push('Some affected environments are not in the registry; agent list may be incomplete.');
|
|
112
|
+
}
|
|
113
|
+
return { agents: [...agents].sort(), source: 'registry', complete: coveredAll };
|
|
114
|
+
}
|
|
115
|
+
const fromFm = new Set([...agentsFromFrontmatter(after), ...agentsFromFrontmatter(before)]);
|
|
116
|
+
if (fromFm.size > 0) {
|
|
117
|
+
notes.push('Agent list derived from governance frontmatter and may be incomplete: Onboarding admitted-agent facts are not yet available (Phase 2–6 staged, verified 2026-07-18).');
|
|
118
|
+
return { agents: [...fromFm].sort(), source: 'frontmatter', complete: false };
|
|
119
|
+
}
|
|
120
|
+
notes.push('Affected agents unknown: no environment registry, and no agent named in governance frontmatter. ' +
|
|
121
|
+
'Onboarding admitted-agent facts not yet available.');
|
|
122
|
+
void presentFiles; // reserved for future per-file agent attribution
|
|
123
|
+
return { agents: [], source: 'none', complete: false };
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=impact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"impact.js","sourceRoot":"","sources":["../src/impact.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @getvda/test-suite — the ACP governance CI test suite.
|
|
3
|
+
*
|
|
4
|
+
* Provider-free (schema + guard only). One machine-readable TestSuiteResult, one
|
|
5
|
+
* human renderer over it. Invocable as a step in the customer's own CI via the
|
|
6
|
+
* `acp-test` CLI (see cli.ts) or embedded via `runTestSuite`.
|
|
7
|
+
*/
|
|
8
|
+
export { runTestSuite, type RunSuiteInput, type RunSuiteOptions } from './suite.js';
|
|
9
|
+
export { statusOf, type StepId, type StepResult, type StepStatus, type SuiteFinding, type FindingSeverity, type TestSuiteMeta, type TestSuiteResult, } from './types.js';
|
|
10
|
+
export { runScenarios, type ScenarioPolicy } from './scenarios.js';
|
|
11
|
+
export { computeImpact, type ImpactAnalysis, type AffectedAgents, type EnvironmentRegistry, type EnvironmentRegistryEntry, } from './impact.js';
|
|
12
|
+
export { onboardingSandboxStub, type SandboxEvaluator, type SandboxContext, type SandboxResult, type SandboxStatus, } from './sandbox.js';
|
|
13
|
+
export { renderSuiteForHuman, toJson } from './render.js';
|
|
14
|
+
export { judgeCondition, judgeConditions, type ConditionVerdict } from './condition.js';
|
|
15
|
+
export { TRAILER_KEY, describeProof, formatSignoffTrailer, parseSignoffTrailers, toSignOffs, type SignoffProof, type TrailerSignoff, } from './signoff-trailer.js';
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAEpF,OAAO,EACL,QAAQ,EACR,KAAK,MAAM,EACX,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,eAAe,GACrB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EACL,aAAa,EACb,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,GAC9B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,qBAAqB,EACrB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACxF,OAAO,EACL,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,UAAU,EACV,KAAK,YAAY,EACjB,KAAK,cAAc,GACpB,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @getvda/test-suite — the ACP governance CI test suite.
|
|
3
|
+
*
|
|
4
|
+
* Provider-free (schema + guard only). One machine-readable TestSuiteResult, one
|
|
5
|
+
* human renderer over it. Invocable as a step in the customer's own CI via the
|
|
6
|
+
* `acp-test` CLI (see cli.ts) or embedded via `runTestSuite`.
|
|
7
|
+
*/
|
|
8
|
+
export { runTestSuite } from './suite.js';
|
|
9
|
+
export { statusOf, } from './types.js';
|
|
10
|
+
export { runScenarios } from './scenarios.js';
|
|
11
|
+
export { computeImpact, } from './impact.js';
|
|
12
|
+
export { onboardingSandboxStub, } from './sandbox.js';
|
|
13
|
+
export { renderSuiteForHuman, toJson } from './render.js';
|
|
14
|
+
export { judgeCondition, judgeConditions } from './condition.js';
|
|
15
|
+
export { TRAILER_KEY, describeProof, formatSignoffTrailer, parseSignoffTrailers, toSignOffs, } from './signoff-trailer.js';
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAA4C,MAAM,YAAY,CAAC;AAEpF,OAAO,EACL,QAAQ,GAQT,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAuB,MAAM,gBAAgB,CAAC;AACnE,OAAO,EACL,aAAa,GAKd,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,qBAAqB,GAKtB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,EAAE,cAAc,EAAE,eAAe,EAAyB,MAAM,gBAAgB,CAAC;AACxF,OAAO,EACL,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,UAAU,GAGX,MAAM,sBAAsB,CAAC"}
|
package/dist/render.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE renderer over TestSuiteResult.
|
|
3
|
+
*
|
|
4
|
+
* The machine-readable output is the TestSuiteResult itself (JSON). This is the
|
|
5
|
+
* single human renderer over that same object — the verdict a human reads and the
|
|
6
|
+
* verdict a machine reads come from ONE result, and this is the ONLY code that
|
|
7
|
+
* turns it into prose. No second renderer, no divergent "did it pass" logic.
|
|
8
|
+
*
|
|
9
|
+
* Reuses the Compliance Guard's human diff renderer for the diff section, so even
|
|
10
|
+
* the change description has a single implementation.
|
|
11
|
+
*/
|
|
12
|
+
import type { TestSuiteResult } from './types.js';
|
|
13
|
+
/** Machine-readable output: the result as canonical JSON. */
|
|
14
|
+
export declare function toJson(result: TestSuiteResult): string;
|
|
15
|
+
/** Human-readable summary for the CI log and the Phase-4 approver surface. */
|
|
16
|
+
export declare function renderSuiteForHuman(result: TestSuiteResult): string;
|
|
17
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAc,eAAe,EAAE,MAAM,YAAY,CAAC;AAW9D,6DAA6D;AAC7D,wBAAgB,MAAM,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAEtD;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CA4BnE"}
|
package/dist/render.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE renderer over TestSuiteResult.
|
|
3
|
+
*
|
|
4
|
+
* The machine-readable output is the TestSuiteResult itself (JSON). This is the
|
|
5
|
+
* single human renderer over that same object — the verdict a human reads and the
|
|
6
|
+
* verdict a machine reads come from ONE result, and this is the ONLY code that
|
|
7
|
+
* turns it into prose. No second renderer, no divergent "did it pass" logic.
|
|
8
|
+
*
|
|
9
|
+
* Reuses the Compliance Guard's human diff renderer for the diff section, so even
|
|
10
|
+
* the change description has a single implementation.
|
|
11
|
+
*/
|
|
12
|
+
import { renderDiffForHuman } from '@getvda/compliance-guard';
|
|
13
|
+
const STATUS_MARK = {
|
|
14
|
+
pass: '✓ PASS',
|
|
15
|
+
fail: '✗ FAIL',
|
|
16
|
+
warn: '! WARN',
|
|
17
|
+
skip: '– SKIP',
|
|
18
|
+
info: 'ℹ INFO',
|
|
19
|
+
};
|
|
20
|
+
/** Machine-readable output: the result as canonical JSON. */
|
|
21
|
+
export function toJson(result) {
|
|
22
|
+
return JSON.stringify(result, null, 2);
|
|
23
|
+
}
|
|
24
|
+
/** Human-readable summary for the CI log and the Phase-4 approver surface. */
|
|
25
|
+
export function renderSuiteForHuman(result) {
|
|
26
|
+
const lines = [];
|
|
27
|
+
lines.push(result.ok ? '✓ ACP governance test suite: PASSED' : '✗ ACP governance test suite: FAILED');
|
|
28
|
+
lines.push(`contract ${result.meta.contractVersion} · source ${result.meta.source}`);
|
|
29
|
+
lines.push('');
|
|
30
|
+
for (const step of result.steps) {
|
|
31
|
+
lines.push(`${STATUS_MARK[step.status]} ${step.title}`);
|
|
32
|
+
for (const f of step.findings) {
|
|
33
|
+
const tag = f.severity === 'error' ? '✗' : f.severity === 'warning' ? '!' : '·';
|
|
34
|
+
const heur = f.heuristic ? ' (heuristic)' : '';
|
|
35
|
+
const where = f.file ? ` [${f.file}]` : '';
|
|
36
|
+
lines.push(` ${tag}${where} ${f.message}${heur}`);
|
|
37
|
+
if (f.detail)
|
|
38
|
+
lines.push(` ${f.detail}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
lines.push('');
|
|
42
|
+
lines.push('── What changed (meaning) ──');
|
|
43
|
+
lines.push(renderDiffForHuman(result.diff));
|
|
44
|
+
lines.push('');
|
|
45
|
+
lines.push('── Impact ──');
|
|
46
|
+
lines.push(renderImpact(result.impact));
|
|
47
|
+
return lines.join('\n');
|
|
48
|
+
}
|
|
49
|
+
function renderImpact(impact) {
|
|
50
|
+
const lines = [];
|
|
51
|
+
lines.push(`Files: ${impact.changedFiles.join(', ') || '(none)'}`);
|
|
52
|
+
lines.push(`Environments: ${impact.environments.join(', ') || '(none named)'}`);
|
|
53
|
+
const agentNote = impact.agents.complete ? '' : ' (may be incomplete)';
|
|
54
|
+
lines.push(`Agents: ${impact.agents.agents.join(', ') || 'unknown'} — from ${impact.agents.source}${agentNote}`);
|
|
55
|
+
if (impact.capabilitiesAffected.length) {
|
|
56
|
+
lines.push(`Capabilities: ${impact.capabilitiesAffected.join(', ')}`);
|
|
57
|
+
}
|
|
58
|
+
lines.push(`Clauses: MUST ${signed(impact.clauseImpact.mustDelta)}, MUST NOT ${signed(impact.clauseImpact.mustNotDelta)}`);
|
|
59
|
+
lines.push(`Journeys: ${impact.journeys === 'undetermined' ? 'undetermined' : impact.journeys.join(', ')}`);
|
|
60
|
+
return lines.join('\n');
|
|
61
|
+
}
|
|
62
|
+
function signed(n) {
|
|
63
|
+
return n > 0 ? `+${n}` : String(n);
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent-behavioral sandbox seam (3c, delegated).
|
|
3
|
+
*
|
|
4
|
+
* A DIFFERENT check from ACP's own governance-level scenarios: "does this change
|
|
5
|
+
* break an ADMITTED AGENT's runtime decisions?" That needs an agent sandbox —
|
|
6
|
+
* Onboarding's Phase 5. Which, verified on Onboarding's live card on 2026-07-18,
|
|
7
|
+
* is STAGED / pass-through: `admit_agent` documents phases 2–6 as not executing
|
|
8
|
+
* live evaluation ("Do not rely on it as current capability"). It seals a
|
|
9
|
+
* not_implemented agent_action; it does not run scenarios.
|
|
10
|
+
*
|
|
11
|
+
* So ACP ships the SEAM and an HONEST stub that reports `not_available` — never a
|
|
12
|
+
* fake pass. When Onboarding's sandbox goes live, drop in a real evaluator here;
|
|
13
|
+
* nothing else in the suite changes.
|
|
14
|
+
*/
|
|
15
|
+
export type SandboxStatus = 'not_available' | 'passed' | 'failed';
|
|
16
|
+
export interface SandboxResult {
|
|
17
|
+
readonly status: SandboxStatus;
|
|
18
|
+
/** Present only when status is passed/failed. */
|
|
19
|
+
readonly passRate?: number;
|
|
20
|
+
readonly reason: string;
|
|
21
|
+
readonly provider: string;
|
|
22
|
+
}
|
|
23
|
+
export interface SandboxContext {
|
|
24
|
+
readonly environment?: string;
|
|
25
|
+
readonly changedFiles: readonly string[];
|
|
26
|
+
}
|
|
27
|
+
export interface SandboxEvaluator {
|
|
28
|
+
evaluate(ctx: SandboxContext): Promise<SandboxResult>;
|
|
29
|
+
}
|
|
30
|
+
/** The honest stub — reports the real state of Onboarding Phase 5. */
|
|
31
|
+
export declare const onboardingSandboxStub: SandboxEvaluator;
|
|
32
|
+
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,aAAa,GAAG,eAAe,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAElE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,iDAAiD;IACjD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CACvD;AAED,sEAAsE;AACtE,eAAO,MAAM,qBAAqB,EAAE,gBAYnC,CAAC"}
|
package/dist/sandbox.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent-behavioral sandbox seam (3c, delegated).
|
|
3
|
+
*
|
|
4
|
+
* A DIFFERENT check from ACP's own governance-level scenarios: "does this change
|
|
5
|
+
* break an ADMITTED AGENT's runtime decisions?" That needs an agent sandbox —
|
|
6
|
+
* Onboarding's Phase 5. Which, verified on Onboarding's live card on 2026-07-18,
|
|
7
|
+
* is STAGED / pass-through: `admit_agent` documents phases 2–6 as not executing
|
|
8
|
+
* live evaluation ("Do not rely on it as current capability"). It seals a
|
|
9
|
+
* not_implemented agent_action; it does not run scenarios.
|
|
10
|
+
*
|
|
11
|
+
* So ACP ships the SEAM and an HONEST stub that reports `not_available` — never a
|
|
12
|
+
* fake pass. When Onboarding's sandbox goes live, drop in a real evaluator here;
|
|
13
|
+
* nothing else in the suite changes.
|
|
14
|
+
*/
|
|
15
|
+
/** The honest stub — reports the real state of Onboarding Phase 5. */
|
|
16
|
+
export const onboardingSandboxStub = {
|
|
17
|
+
async evaluate() {
|
|
18
|
+
return {
|
|
19
|
+
status: 'not_available',
|
|
20
|
+
reason: 'Onboarding Phase 5 adversarial sandbox is staged/pass-through (verified 2026-07-18): ' +
|
|
21
|
+
'it seals a not_implemented agent_action and does not run scenarios. Agent-behavioral ' +
|
|
22
|
+
'eval is therefore not available; ACP governance-level scenarios ran instead (see the ' +
|
|
23
|
+
'scenarios step).',
|
|
24
|
+
provider: 'onboarding:did:web:onboard.getvda.ai',
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAqBH,sEAAsE;AACtE,MAAM,CAAC,MAAM,qBAAqB,GAAqB;IACrD,KAAK,CAAC,QAAQ;QACZ,OAAO;YACL,MAAM,EAAE,eAAe;YACvB,MAAM,EACJ,uFAAuF;gBACvF,uFAAuF;gBACvF,uFAAuF;gBACvF,kBAAkB;YACpB,QAAQ,EAAE,sCAAsC;SACjD,CAAC;IACJ,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Governance-level adversarial scenario runner (3c) — ACP's own crown jewel.
|
|
3
|
+
*
|
|
4
|
+
* Static analysis over the governance documents (NOT an agent sandbox). It asks
|
|
5
|
+
* the adversarial questions a reviewer would:
|
|
6
|
+
* SC-1a unconditioned capability — a permission with NO conditions (structural,
|
|
7
|
+
* CERTAIN → blocking).
|
|
8
|
+
* SC-1b conditioned in name only — conditions exist but cannot be false, e.g.
|
|
9
|
+
* "always permitted" (structural where provable → blocking; otherwise a
|
|
10
|
+
* heuristic warning). Counting conditions is defeatable by writing a
|
|
11
|
+
* word; SC-1b asks whether the condition can ever fail. See condition.ts
|
|
12
|
+
* for the rule, and for why prose conditions are a bridge rather than
|
|
13
|
+
* the destination.
|
|
14
|
+
* SC-2 prohibition ↔ permission conflict — an SOP "MUST NOT <x>" whose subject
|
|
15
|
+
* overlaps a granted capability (heuristic → warning; flagged when the
|
|
16
|
+
* change NEWLY introduces the conflict — "permit something previously
|
|
17
|
+
* denied").
|
|
18
|
+
* SC-3 contradictory obligations — a "MUST <x>" and a "MUST NOT <x>" on the
|
|
19
|
+
* same subject (heuristic → warning).
|
|
20
|
+
*
|
|
21
|
+
* Certainty is labelled honestly: SC-1a and a proven-vacuous SC-1b are structural
|
|
22
|
+
* (a proof); an undecidable SC-1b, SC-2 and SC-3 are keyword heuristics
|
|
23
|
+
* (best-effort flags for a human), never presented as proofs.
|
|
24
|
+
*/
|
|
25
|
+
import { type GovernanceSet } from '@getvda/governance-schema';
|
|
26
|
+
import type { SuiteFinding } from './types.js';
|
|
27
|
+
export interface ScenarioPolicy {
|
|
28
|
+
/** Capability names allowed to be unconditioned (e.g. "escalate_to_human"). */
|
|
29
|
+
readonly allowUnconditioned?: readonly string[];
|
|
30
|
+
/** Shared-token threshold for the heuristic conflict detectors. Default 2. */
|
|
31
|
+
readonly conflictTokenThreshold?: number;
|
|
32
|
+
}
|
|
33
|
+
export declare function runScenarios(before: GovernanceSet, after: GovernanceSet, policy?: ScenarioPolicy): SuiteFinding[];
|
|
34
|
+
//# sourceMappingURL=scenarios.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenarios.d.ts","sourceRoot":"","sources":["../src/scenarios.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACtF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,MAAM,WAAW,cAAc;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChD,8EAA8E;IAC9E,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;CAC1C;AA8FD,wBAAgB,YAAY,CAC1B,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,aAAa,EACpB,MAAM,CAAC,EAAE,cAAc,GACtB,YAAY,EAAE,CAuGhB"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Governance-level adversarial scenario runner (3c) — ACP's own crown jewel.
|
|
3
|
+
*
|
|
4
|
+
* Static analysis over the governance documents (NOT an agent sandbox). It asks
|
|
5
|
+
* the adversarial questions a reviewer would:
|
|
6
|
+
* SC-1a unconditioned capability — a permission with NO conditions (structural,
|
|
7
|
+
* CERTAIN → blocking).
|
|
8
|
+
* SC-1b conditioned in name only — conditions exist but cannot be false, e.g.
|
|
9
|
+
* "always permitted" (structural where provable → blocking; otherwise a
|
|
10
|
+
* heuristic warning). Counting conditions is defeatable by writing a
|
|
11
|
+
* word; SC-1b asks whether the condition can ever fail. See condition.ts
|
|
12
|
+
* for the rule, and for why prose conditions are a bridge rather than
|
|
13
|
+
* the destination.
|
|
14
|
+
* SC-2 prohibition ↔ permission conflict — an SOP "MUST NOT <x>" whose subject
|
|
15
|
+
* overlaps a granted capability (heuristic → warning; flagged when the
|
|
16
|
+
* change NEWLY introduces the conflict — "permit something previously
|
|
17
|
+
* denied").
|
|
18
|
+
* SC-3 contradictory obligations — a "MUST <x>" and a "MUST NOT <x>" on the
|
|
19
|
+
* same subject (heuristic → warning).
|
|
20
|
+
*
|
|
21
|
+
* Certainty is labelled honestly: SC-1a and a proven-vacuous SC-1b are structural
|
|
22
|
+
* (a proof); an undecidable SC-1b, SC-2 and SC-3 are keyword heuristics
|
|
23
|
+
* (best-effort flags for a human), never presented as proofs.
|
|
24
|
+
*/
|
|
25
|
+
import { GOVERNANCE_FILE_NAMES } from '@getvda/governance-schema';
|
|
26
|
+
import { judgeConditions } from './condition.js';
|
|
27
|
+
const STOPWORDS = new Set([
|
|
28
|
+
'the',
|
|
29
|
+
'agent',
|
|
30
|
+
'must',
|
|
31
|
+
'not',
|
|
32
|
+
'may',
|
|
33
|
+
'and',
|
|
34
|
+
'for',
|
|
35
|
+
'with',
|
|
36
|
+
'that',
|
|
37
|
+
'this',
|
|
38
|
+
'any',
|
|
39
|
+
'all',
|
|
40
|
+
'its',
|
|
41
|
+
'are',
|
|
42
|
+
'was',
|
|
43
|
+
'will',
|
|
44
|
+
'shall',
|
|
45
|
+
'from',
|
|
46
|
+
'into',
|
|
47
|
+
'when',
|
|
48
|
+
'than',
|
|
49
|
+
'then',
|
|
50
|
+
'every',
|
|
51
|
+
'each',
|
|
52
|
+
'without',
|
|
53
|
+
'within',
|
|
54
|
+
'have',
|
|
55
|
+
'has',
|
|
56
|
+
'been',
|
|
57
|
+
'a',
|
|
58
|
+
'an',
|
|
59
|
+
'to',
|
|
60
|
+
'of',
|
|
61
|
+
'in',
|
|
62
|
+
'on',
|
|
63
|
+
'is',
|
|
64
|
+
'be',
|
|
65
|
+
'or',
|
|
66
|
+
'no',
|
|
67
|
+
'it',
|
|
68
|
+
'as',
|
|
69
|
+
'by',
|
|
70
|
+
'at',
|
|
71
|
+
'if',
|
|
72
|
+
'do',
|
|
73
|
+
'does',
|
|
74
|
+
]);
|
|
75
|
+
/** Significant lowercased tokens (len ≥ 4, non-stopword) for overlap heuristics. */
|
|
76
|
+
function tokens(text) {
|
|
77
|
+
const out = new Set();
|
|
78
|
+
for (const raw of text.toLowerCase().split(/[^a-z0-9]+/)) {
|
|
79
|
+
if (raw.length >= 4 && !STOPWORDS.has(raw))
|
|
80
|
+
out.add(raw);
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
function overlap(a, b) {
|
|
85
|
+
return [...a].filter((x) => b.has(x));
|
|
86
|
+
}
|
|
87
|
+
/** Detect prohibition↔permission conflicts in a single set (heuristic). */
|
|
88
|
+
function detectConflicts(set, threshold) {
|
|
89
|
+
const sop = set.files['SOP.md'];
|
|
90
|
+
const skill = set.files['SKILL.md'];
|
|
91
|
+
if (!sop || !skill)
|
|
92
|
+
return [];
|
|
93
|
+
const conflicts = [];
|
|
94
|
+
const prohibitions = sop.parsed.clauses.filter((c) => c.modality === 'MUST NOT');
|
|
95
|
+
for (const p of prohibitions) {
|
|
96
|
+
const pTokens = tokens(p.text);
|
|
97
|
+
for (const cap of skill.parsed.capabilities) {
|
|
98
|
+
const capText = `${cap.capability} ${cap.conditions.join(' ')}`;
|
|
99
|
+
const shared = overlap(pTokens, tokens(capText));
|
|
100
|
+
if (shared.length >= threshold) {
|
|
101
|
+
conflicts.push({ prohibition: p.text, capability: cap.capability, shared });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return conflicts;
|
|
106
|
+
}
|
|
107
|
+
function conflictKey(c) {
|
|
108
|
+
return `${c.capability}::${c.prohibition}`;
|
|
109
|
+
}
|
|
110
|
+
export function runScenarios(before, after, policy) {
|
|
111
|
+
const findings = [];
|
|
112
|
+
const threshold = policy?.conflictTokenThreshold ?? 2;
|
|
113
|
+
const allow = new Set(policy?.allowUnconditioned ?? []);
|
|
114
|
+
// SC-1a — unconditioned capability (structural, certain, blocking error).
|
|
115
|
+
// SC-1b — conditioned in name only: the conditions exist but do not constrain.
|
|
116
|
+
//
|
|
117
|
+
// SC-1a alone was defeatable by writing a word: a capability conditioned on
|
|
118
|
+
// "always permitted" passes a length check while meaning the opposite. SC-1b
|
|
119
|
+
// asks the real question — can this condition ever be false?
|
|
120
|
+
const skill = after.files['SKILL.md'];
|
|
121
|
+
for (const cap of skill?.parsed.capabilities ?? []) {
|
|
122
|
+
if (allow.has(cap.capability))
|
|
123
|
+
continue;
|
|
124
|
+
if (cap.conditions.length === 0) {
|
|
125
|
+
findings.push({
|
|
126
|
+
severity: 'error',
|
|
127
|
+
code: 'unconditioned_capability',
|
|
128
|
+
message: `Capability "${cap.capability}" is granted with NO conditions.`,
|
|
129
|
+
detail: 'An unconditioned capability is an unbounded permission. Add a condition or allowlist it.',
|
|
130
|
+
file: 'SKILL.md',
|
|
131
|
+
});
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const judged = judgeConditions(cap.conditions);
|
|
135
|
+
if (judged.verdict === 'vacuous') {
|
|
136
|
+
// Still structural and certain: every condition is a tautology, so the
|
|
137
|
+
// capability is unbounded in substance even though the array is not empty.
|
|
138
|
+
findings.push({
|
|
139
|
+
severity: 'error',
|
|
140
|
+
code: 'vacuously_conditioned_capability',
|
|
141
|
+
message: `Capability "${cap.capability}" is conditioned in name only.`,
|
|
142
|
+
detail: `Condition(s) ${judged.vacuous.map((c) => `"${c}"`).join(', ')} cannot be false, ` +
|
|
143
|
+
'so they do not constrain anything. A condition that cannot fail is not a condition — ' +
|
|
144
|
+
'state what must hold for the capability to apply.',
|
|
145
|
+
file: 'SKILL.md',
|
|
146
|
+
});
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (judged.verdict === 'indeterminate') {
|
|
150
|
+
// Deliberately a WARNING, and deliberately labelled heuristic. Whether
|
|
151
|
+
// prose constrains is a semantic judgement; asserting a verdict we do not
|
|
152
|
+
// have would be the same failure this check exists to catch.
|
|
153
|
+
findings.push({
|
|
154
|
+
severity: 'warning',
|
|
155
|
+
code: 'condition_may_not_constrain',
|
|
156
|
+
message: `Capability "${cap.capability}" may be conditioned in name only.`,
|
|
157
|
+
detail: `Could not tell whether ${judged.indeterminate.map((c) => `"${c}"`).join(', ')} ` +
|
|
158
|
+
'actually constrains the capability. Heuristic over prose (English-only vocabulary) — ' +
|
|
159
|
+
'a human should confirm. See condition.ts for the rule and its known limits.',
|
|
160
|
+
file: 'SKILL.md',
|
|
161
|
+
heuristic: true,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// SC-2 — prohibition ↔ permission conflict (heuristic, warning).
|
|
166
|
+
const beforeConflicts = new Set(detectConflicts(before, threshold).map(conflictKey));
|
|
167
|
+
for (const c of detectConflicts(after, threshold)) {
|
|
168
|
+
const isNew = !beforeConflicts.has(conflictKey(c));
|
|
169
|
+
findings.push({
|
|
170
|
+
severity: 'warning',
|
|
171
|
+
code: isNew ? 'newly_permitted_conflict' : 'prohibition_permission_conflict',
|
|
172
|
+
message: isNew
|
|
173
|
+
? `This change may PERMIT something previously denied: capability "${c.capability}" overlaps prohibition "${trim(c.prohibition)}".`
|
|
174
|
+
: `Capability "${c.capability}" may conflict with prohibition "${trim(c.prohibition)}".`,
|
|
175
|
+
detail: `Shared terms: ${c.shared.join(', ')}. Heuristic — confirm manually.`,
|
|
176
|
+
file: 'SKILL.md',
|
|
177
|
+
heuristic: true,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
// SC-3 — contradictory obligations (heuristic, warning).
|
|
181
|
+
const sop = after.files['SOP.md'];
|
|
182
|
+
if (sop) {
|
|
183
|
+
const musts = sop.parsed.clauses.filter((c) => c.modality === 'MUST');
|
|
184
|
+
const mustNots = sop.parsed.clauses.filter((c) => c.modality === 'MUST NOT');
|
|
185
|
+
for (const mn of mustNots) {
|
|
186
|
+
const mnTokens = tokens(mn.text);
|
|
187
|
+
for (const m of musts) {
|
|
188
|
+
const shared = overlap(mnTokens, tokens(m.text));
|
|
189
|
+
if (shared.length >= threshold + 1) {
|
|
190
|
+
findings.push({
|
|
191
|
+
severity: 'warning',
|
|
192
|
+
code: 'contradictory_obligations',
|
|
193
|
+
message: `Possible contradiction: "${trim(m.text)}" vs "${trim(mn.text)}".`,
|
|
194
|
+
detail: `Shared terms: ${shared.join(', ')}. Heuristic — confirm manually.`,
|
|
195
|
+
file: 'SOP.md',
|
|
196
|
+
heuristic: true,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return findings;
|
|
203
|
+
}
|
|
204
|
+
function trim(s) {
|
|
205
|
+
return s.length > 70 ? `${s.slice(0, 67)}…` : s;
|
|
206
|
+
}
|
|
207
|
+
// keep the taxonomy import meaningful for future per-file scenarios
|
|
208
|
+
void GOVERNANCE_FILE_NAMES;
|
|
209
|
+
//# sourceMappingURL=scenarios.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenarios.js","sourceRoot":"","sources":["../src/scenarios.ts"],"names":[],"mappings":"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"}
|