@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
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Governance sign-off as a COMMIT TRAILER.
|
|
3
|
+
*
|
|
4
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
* WHY A TRAILER, AND WHY THIS FORMAT
|
|
6
|
+
*
|
|
7
|
+
* A CI check runs from git refs. It cannot see a sign-off that only exists in a
|
|
8
|
+
* propose call or a PR body — which is how a signed change ended up failing the
|
|
9
|
+
* required status check, and how a sign-off silently no-opped before that.
|
|
10
|
+
*
|
|
11
|
+
* The document/event split applied to itself: **the sign-off EVENT belongs in
|
|
12
|
+
* Witness; a durable POINTER belongs in git.** Storing the authorising event in
|
|
13
|
+
* git would be the category error Witness exists to prevent. Storing a pointer
|
|
14
|
+
* is exactly the design — the commit hash tying documents to events.
|
|
15
|
+
*
|
|
16
|
+
* A trailer specifically (not a repo file):
|
|
17
|
+
* - DIFF-NEUTRAL BY CONSTRUCTION. Commit messages are not in the tree, so the
|
|
18
|
+
* governance diff — and therefore the finding id the sign-off references —
|
|
19
|
+
* cannot shift because a sign-off was added. A sign-off stored in a repo
|
|
20
|
+
* file risks perturbing the very diff it authorises.
|
|
21
|
+
* - DURABLE. It survives PR deletion, body edits and squash (with care), and
|
|
22
|
+
* it is visible in `git log` forever. A PR body is none of those things.
|
|
23
|
+
* - CONVENTIONAL. Same shape as DCO `Signed-off-by:`.
|
|
24
|
+
*
|
|
25
|
+
* A repo policy file was rejected: a stale `covers: "*"` would silently
|
|
26
|
+
* authorise every future weakening — a footgun with a long fuse — and it puts
|
|
27
|
+
* the event in the wrong store.
|
|
28
|
+
*
|
|
29
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
30
|
+
* FORMAT — designed so the Phase 4 swap is a VALUE change, not a format change
|
|
31
|
+
*
|
|
32
|
+
* Governance-Signoff: <finding-id> / <approver> / <proof>
|
|
33
|
+
*
|
|
34
|
+
* `proof` is the only field that changes when Phase 4 lands:
|
|
35
|
+
*
|
|
36
|
+
* today proof = `assertion` → rendered "claimed by X, UNVERIFIED"
|
|
37
|
+
* Phase 4 proof = `witness:<record-id>` → resolvable to a sealed hitl_decision
|
|
38
|
+
*
|
|
39
|
+
* The third field is optional on parse and defaults to `assertion`, so the
|
|
40
|
+
* short two-field form stays valid and nothing needs rewriting.
|
|
41
|
+
*
|
|
42
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
43
|
+
* WHAT THIS IS NOT
|
|
44
|
+
*
|
|
45
|
+
* A trailer is an ASSERTION. Anyone who can push can write any name into one.
|
|
46
|
+
* It is durable, not authenticated — strictly better than a PR body on
|
|
47
|
+
* persistence, no better on verifiability. The suite must therefore never
|
|
48
|
+
* render it as a verified check, and this module never claims it is one.
|
|
49
|
+
*
|
|
50
|
+
* Commit signing would authenticate the trailer's AUTHOR without Witness, and
|
|
51
|
+
* is worth having — but it proves who wrote the trailer, not that an authorised
|
|
52
|
+
* approver accepted a specific finding. Complementary to Witness, not a
|
|
53
|
+
* substitute. Tracked as its own decision.
|
|
54
|
+
*
|
|
55
|
+
* OFFLINE IS A HARD CONSTRAINT and this respects it: parsing needs no network
|
|
56
|
+
* and no tokens. When verification is unavailable the finding reports what
|
|
57
|
+
* could not be checked — it never fails closed on inability to verify, the same
|
|
58
|
+
* discipline as `did_unresolvable` in issuer verification.
|
|
59
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
60
|
+
*/
|
|
61
|
+
import type { SignOff } from '@getvda/compliance-guard';
|
|
62
|
+
export declare const TRAILER_KEY = "Governance-Signoff";
|
|
63
|
+
export type SignoffProof =
|
|
64
|
+
/** No verifiable backing. Durable claim only. */
|
|
65
|
+
{
|
|
66
|
+
readonly kind: 'assertion';
|
|
67
|
+
}
|
|
68
|
+
/** Phase 4: points at a sealed Witness hitl_decision. */
|
|
69
|
+
| {
|
|
70
|
+
readonly kind: 'witness';
|
|
71
|
+
readonly recordId: string;
|
|
72
|
+
};
|
|
73
|
+
export interface TrailerSignoff {
|
|
74
|
+
readonly findingId: string;
|
|
75
|
+
readonly approver: string;
|
|
76
|
+
readonly proof: SignoffProof;
|
|
77
|
+
/** The raw trailer line, for reporting exactly what was found. */
|
|
78
|
+
readonly raw: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Parse `Governance-Signoff:` trailers out of one or more commit messages.
|
|
82
|
+
*
|
|
83
|
+
* Tolerant on input, strict on status: a malformed or unrecognised trailer
|
|
84
|
+
* never produces a stronger claim than `assertion`.
|
|
85
|
+
*/
|
|
86
|
+
export declare function parseSignoffTrailers(commitMessages: readonly string[]): TrailerSignoff[];
|
|
87
|
+
/** Render a trailer line. Used when authoring a commit that carries a sign-off. */
|
|
88
|
+
export declare function formatSignoffTrailer(findingId: string, approver: string, proof?: SignoffProof): string;
|
|
89
|
+
/**
|
|
90
|
+
* Convert to the Guard's SignOff shape.
|
|
91
|
+
*
|
|
92
|
+
* NOTE the deliberate absence of a `'*'` blanket: a trailer covers exactly the
|
|
93
|
+
* finding it names. Blanket coverage is what made the policy-file route a
|
|
94
|
+
* footgun, and it is not reachable from here.
|
|
95
|
+
*/
|
|
96
|
+
export declare function toSignOffs(trailers: readonly TrailerSignoff[]): SignOff[];
|
|
97
|
+
/** How a sign-off should be described to a human, given what could be checked. */
|
|
98
|
+
export declare function describeProof(trailer: TrailerSignoff): string;
|
|
99
|
+
//# sourceMappingURL=signoff-trailer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signoff-trailer.d.ts","sourceRoot":"","sources":["../src/signoff-trailer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAExD,eAAO,MAAM,WAAW,uBAAuB,CAAC;AAEhD,MAAM,MAAM,YAAY;AACtB,iDAAiD;AAC/C;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;CAAE;AAChC,yDAAyD;GACvD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAcD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,GAAG,cAAc,EAAE,CAmBxF;AAED,mFAAmF;AACnF,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,YAAoC,GAC1C,MAAM,CAGR;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,GAAG,OAAO,EAAE,CASzE;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAI7D"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Governance sign-off as a COMMIT TRAILER.
|
|
3
|
+
*
|
|
4
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
* WHY A TRAILER, AND WHY THIS FORMAT
|
|
6
|
+
*
|
|
7
|
+
* A CI check runs from git refs. It cannot see a sign-off that only exists in a
|
|
8
|
+
* propose call or a PR body — which is how a signed change ended up failing the
|
|
9
|
+
* required status check, and how a sign-off silently no-opped before that.
|
|
10
|
+
*
|
|
11
|
+
* The document/event split applied to itself: **the sign-off EVENT belongs in
|
|
12
|
+
* Witness; a durable POINTER belongs in git.** Storing the authorising event in
|
|
13
|
+
* git would be the category error Witness exists to prevent. Storing a pointer
|
|
14
|
+
* is exactly the design — the commit hash tying documents to events.
|
|
15
|
+
*
|
|
16
|
+
* A trailer specifically (not a repo file):
|
|
17
|
+
* - DIFF-NEUTRAL BY CONSTRUCTION. Commit messages are not in the tree, so the
|
|
18
|
+
* governance diff — and therefore the finding id the sign-off references —
|
|
19
|
+
* cannot shift because a sign-off was added. A sign-off stored in a repo
|
|
20
|
+
* file risks perturbing the very diff it authorises.
|
|
21
|
+
* - DURABLE. It survives PR deletion, body edits and squash (with care), and
|
|
22
|
+
* it is visible in `git log` forever. A PR body is none of those things.
|
|
23
|
+
* - CONVENTIONAL. Same shape as DCO `Signed-off-by:`.
|
|
24
|
+
*
|
|
25
|
+
* A repo policy file was rejected: a stale `covers: "*"` would silently
|
|
26
|
+
* authorise every future weakening — a footgun with a long fuse — and it puts
|
|
27
|
+
* the event in the wrong store.
|
|
28
|
+
*
|
|
29
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
30
|
+
* FORMAT — designed so the Phase 4 swap is a VALUE change, not a format change
|
|
31
|
+
*
|
|
32
|
+
* Governance-Signoff: <finding-id> / <approver> / <proof>
|
|
33
|
+
*
|
|
34
|
+
* `proof` is the only field that changes when Phase 4 lands:
|
|
35
|
+
*
|
|
36
|
+
* today proof = `assertion` → rendered "claimed by X, UNVERIFIED"
|
|
37
|
+
* Phase 4 proof = `witness:<record-id>` → resolvable to a sealed hitl_decision
|
|
38
|
+
*
|
|
39
|
+
* The third field is optional on parse and defaults to `assertion`, so the
|
|
40
|
+
* short two-field form stays valid and nothing needs rewriting.
|
|
41
|
+
*
|
|
42
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
43
|
+
* WHAT THIS IS NOT
|
|
44
|
+
*
|
|
45
|
+
* A trailer is an ASSERTION. Anyone who can push can write any name into one.
|
|
46
|
+
* It is durable, not authenticated — strictly better than a PR body on
|
|
47
|
+
* persistence, no better on verifiability. The suite must therefore never
|
|
48
|
+
* render it as a verified check, and this module never claims it is one.
|
|
49
|
+
*
|
|
50
|
+
* Commit signing would authenticate the trailer's AUTHOR without Witness, and
|
|
51
|
+
* is worth having — but it proves who wrote the trailer, not that an authorised
|
|
52
|
+
* approver accepted a specific finding. Complementary to Witness, not a
|
|
53
|
+
* substitute. Tracked as its own decision.
|
|
54
|
+
*
|
|
55
|
+
* OFFLINE IS A HARD CONSTRAINT and this respects it: parsing needs no network
|
|
56
|
+
* and no tokens. When verification is unavailable the finding reports what
|
|
57
|
+
* could not be checked — it never fails closed on inability to verify, the same
|
|
58
|
+
* discipline as `did_unresolvable` in issuer verification.
|
|
59
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
60
|
+
*/
|
|
61
|
+
export const TRAILER_KEY = 'Governance-Signoff';
|
|
62
|
+
function parseProof(token) {
|
|
63
|
+
if (token === undefined || token === '' || token.toLowerCase() === 'assertion') {
|
|
64
|
+
return { kind: 'assertion' };
|
|
65
|
+
}
|
|
66
|
+
const witness = /^witness:(.+)$/i.exec(token);
|
|
67
|
+
if (witness?.[1] !== undefined)
|
|
68
|
+
return { kind: 'witness', recordId: witness[1].trim() };
|
|
69
|
+
// An unrecognised proof is treated as an assertion, NOT as a verification.
|
|
70
|
+
// Failing open on the claim while refusing to upgrade its status is the
|
|
71
|
+
// honest direction: we report what we could not check.
|
|
72
|
+
return { kind: 'assertion' };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Parse `Governance-Signoff:` trailers out of one or more commit messages.
|
|
76
|
+
*
|
|
77
|
+
* Tolerant on input, strict on status: a malformed or unrecognised trailer
|
|
78
|
+
* never produces a stronger claim than `assertion`.
|
|
79
|
+
*/
|
|
80
|
+
export function parseSignoffTrailers(commitMessages) {
|
|
81
|
+
const out = [];
|
|
82
|
+
const line = new RegExp(`^\\s*${TRAILER_KEY}\\s*:\\s*(.+)$`, 'i');
|
|
83
|
+
for (const message of commitMessages) {
|
|
84
|
+
for (const raw of message.split(/\r?\n/)) {
|
|
85
|
+
const m = line.exec(raw);
|
|
86
|
+
if (m?.[1] === undefined)
|
|
87
|
+
continue;
|
|
88
|
+
const parts = m[1].split('/').map((p) => p.trim());
|
|
89
|
+
const findingId = parts[0];
|
|
90
|
+
const approver = parts[1];
|
|
91
|
+
// A trailer without both a finding and a named approver authorises
|
|
92
|
+
// nothing — "someone signed off on something" is not a sign-off.
|
|
93
|
+
if (findingId === undefined || findingId === '')
|
|
94
|
+
continue;
|
|
95
|
+
if (approver === undefined || approver === '')
|
|
96
|
+
continue;
|
|
97
|
+
out.push({ findingId, approver, proof: parseProof(parts[2]), raw: raw.trim() });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
/** Render a trailer line. Used when authoring a commit that carries a sign-off. */
|
|
103
|
+
export function formatSignoffTrailer(findingId, approver, proof = { kind: 'assertion' }) {
|
|
104
|
+
const p = proof.kind === 'witness' ? `witness:${proof.recordId}` : 'assertion';
|
|
105
|
+
return `${TRAILER_KEY}: ${findingId} / ${approver} / ${p}`;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Convert to the Guard's SignOff shape.
|
|
109
|
+
*
|
|
110
|
+
* NOTE the deliberate absence of a `'*'` blanket: a trailer covers exactly the
|
|
111
|
+
* finding it names. Blanket coverage is what made the policy-file route a
|
|
112
|
+
* footgun, and it is not reachable from here.
|
|
113
|
+
*/
|
|
114
|
+
export function toSignOffs(trailers) {
|
|
115
|
+
return trailers.map((t) => ({
|
|
116
|
+
approver: t.approver,
|
|
117
|
+
covers: [t.findingId],
|
|
118
|
+
note: t.proof.kind === 'witness'
|
|
119
|
+
? `trailer, Witness record ${t.proof.recordId} (unresolved — no network verification in this run)`
|
|
120
|
+
: 'trailer assertion — durable in git history, NOT authenticated',
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
/** How a sign-off should be described to a human, given what could be checked. */
|
|
124
|
+
export function describeProof(trailer) {
|
|
125
|
+
return trailer.proof.kind === 'witness'
|
|
126
|
+
? `claimed by ${trailer.approver}, cites Witness record ${trailer.proof.recordId} — NOT RESOLVED in this run`
|
|
127
|
+
: `claimed by ${trailer.approver}, UNVERIFIED (durable assertion in git history, not authenticated)`;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=signoff-trailer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signoff-trailer.js","sourceRoot":"","sources":["../src/signoff-trailer.ts"],"names":[],"mappings":"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"}
|
package/dist/suite.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runTestSuite — composes the four gates into the ONE machine-readable result.
|
|
3
|
+
*
|
|
4
|
+
* 3a schema — the 1a contract as a gate (CONFIRMED strict, PROVISIONAL
|
|
5
|
+
* lenient). Blocking.
|
|
6
|
+
* 3b guard — the Compliance Guard (runComplianceGuard). Blocking.
|
|
7
|
+
* 3c scenarios — ACP governance-level adversarial scenarios. Blocking on the
|
|
8
|
+
* certain (structural) findings; heuristic ones warn.
|
|
9
|
+
* 3c sandbox — agent-behavioral eval, delegated to Onboarding Phase 5.
|
|
10
|
+
* Honest stub → skip/not_available. Non-blocking until live.
|
|
11
|
+
* 3d impact — deterministic impact analysis. Informational.
|
|
12
|
+
*
|
|
13
|
+
* Provider-free: it takes before/after governance SETS (file contents), so it
|
|
14
|
+
* runs as a lightweight step in the customer's own CI with no git, no network,
|
|
15
|
+
* no tokens. The CLI adapter sources the sets from plain git.
|
|
16
|
+
*/
|
|
17
|
+
import { type GovernanceSet } from '@getvda/governance-schema';
|
|
18
|
+
import { type GuardPolicy, type SignOff } from '@getvda/compliance-guard';
|
|
19
|
+
import { type TestSuiteMeta, type TestSuiteResult } from './types.js';
|
|
20
|
+
import { type ScenarioPolicy } from './scenarios.js';
|
|
21
|
+
import { type EnvironmentRegistry } from './impact.js';
|
|
22
|
+
import { type SandboxEvaluator } from './sandbox.js';
|
|
23
|
+
import { type TrailerSignoff } from './signoff-trailer.js';
|
|
24
|
+
export interface RunSuiteInput {
|
|
25
|
+
readonly before: GovernanceSet;
|
|
26
|
+
readonly after: GovernanceSet;
|
|
27
|
+
}
|
|
28
|
+
export interface RunSuiteOptions {
|
|
29
|
+
readonly policy?: GuardPolicy;
|
|
30
|
+
readonly signOffs?: readonly SignOff[];
|
|
31
|
+
/**
|
|
32
|
+
* Trailer-sourced sign-offs, carried so the report can state exactly WHAT
|
|
33
|
+
* KIND of sign-off was found. A trailer is a durable assertion, never a
|
|
34
|
+
* verified check, and the rendering must say so.
|
|
35
|
+
*/
|
|
36
|
+
readonly signoffTrailers?: readonly TrailerSignoff[];
|
|
37
|
+
readonly scenarioPolicy?: ScenarioPolicy;
|
|
38
|
+
readonly registry?: EnvironmentRegistry;
|
|
39
|
+
/** Agent-behavioral sandbox. Defaults to the honest Onboarding stub. */
|
|
40
|
+
readonly sandbox?: SandboxEvaluator;
|
|
41
|
+
readonly meta?: Partial<TestSuiteMeta>;
|
|
42
|
+
}
|
|
43
|
+
export declare function runTestSuite(input: RunSuiteInput, options?: RunSuiteOptions): Promise<TestSuiteResult>;
|
|
44
|
+
//# sourceMappingURL=suite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suite.d.ts","sourceRoot":"","sources":["../src/suite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,OAAO,EACb,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,eAAe,EACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAiB,KAAK,mBAAmB,EAAuB,MAAM,aAAa,CAAC;AAC3F,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC5E,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE1E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IACrD,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;IACzC,QAAQ,CAAC,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IACxC,wEAAwE;IACxE,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAC;IACpC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;CACxC;AAWD,wBAAsB,YAAY,CAChC,KAAK,EAAE,aAAa,EACpB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,eAAe,CAAC,CAwH1B"}
|
package/dist/suite.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runTestSuite — composes the four gates into the ONE machine-readable result.
|
|
3
|
+
*
|
|
4
|
+
* 3a schema — the 1a contract as a gate (CONFIRMED strict, PROVISIONAL
|
|
5
|
+
* lenient). Blocking.
|
|
6
|
+
* 3b guard — the Compliance Guard (runComplianceGuard). Blocking.
|
|
7
|
+
* 3c scenarios — ACP governance-level adversarial scenarios. Blocking on the
|
|
8
|
+
* certain (structural) findings; heuristic ones warn.
|
|
9
|
+
* 3c sandbox — agent-behavioral eval, delegated to Onboarding Phase 5.
|
|
10
|
+
* Honest stub → skip/not_available. Non-blocking until live.
|
|
11
|
+
* 3d impact — deterministic impact analysis. Informational.
|
|
12
|
+
*
|
|
13
|
+
* Provider-free: it takes before/after governance SETS (file contents), so it
|
|
14
|
+
* runs as a lightweight step in the customer's own CI with no git, no network,
|
|
15
|
+
* no tokens. The CLI adapter sources the sets from plain git.
|
|
16
|
+
*/
|
|
17
|
+
import { CONTRACT_VERSION, GOVERNANCE_FILE_NAMES, validateSet, } from '@getvda/governance-schema';
|
|
18
|
+
import { runComplianceGuard, } from '@getvda/compliance-guard';
|
|
19
|
+
import { statusOf, } from './types.js';
|
|
20
|
+
import { runScenarios } from './scenarios.js';
|
|
21
|
+
import { computeImpact } from './impact.js';
|
|
22
|
+
import { onboardingSandboxStub } from './sandbox.js';
|
|
23
|
+
import { describeProof } from './signoff-trailer.js';
|
|
24
|
+
function sourcesOf(set) {
|
|
25
|
+
const out = {};
|
|
26
|
+
for (const name of GOVERNANCE_FILE_NAMES) {
|
|
27
|
+
const entry = set.files[name];
|
|
28
|
+
if (entry)
|
|
29
|
+
out[name] = entry.source;
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
export async function runTestSuite(input, options = {}) {
|
|
34
|
+
const { before, after } = input;
|
|
35
|
+
const steps = [];
|
|
36
|
+
// ── 3a schema ──────────────────────────────────────────────────────────────
|
|
37
|
+
const validation = validateSet(sourcesOf(after));
|
|
38
|
+
const schemaFindings = validation.issues.map((i) => ({
|
|
39
|
+
severity: i.severity === 'error' ? 'error' : 'warning',
|
|
40
|
+
code: i.code,
|
|
41
|
+
message: i.message,
|
|
42
|
+
...(i.file ? { file: i.file } : {}),
|
|
43
|
+
}));
|
|
44
|
+
steps.push({
|
|
45
|
+
id: 'schema',
|
|
46
|
+
title: 'Schema validation (contract 1a)',
|
|
47
|
+
blocking: true,
|
|
48
|
+
status: statusOf(true, schemaFindings),
|
|
49
|
+
findings: schemaFindings,
|
|
50
|
+
});
|
|
51
|
+
// ── 3b guard ───────────────────────────────────────────────────────────────
|
|
52
|
+
const guard = runComplianceGuard(before, after, {
|
|
53
|
+
...(options.signOffs ? { signOffs: options.signOffs } : {}),
|
|
54
|
+
...(options.policy ? { policy: options.policy } : {}),
|
|
55
|
+
});
|
|
56
|
+
const trailersById = new Map((options.signoffTrailers ?? []).map((t) => [t.findingId, t]));
|
|
57
|
+
const guardFindings = [
|
|
58
|
+
...guard.unsignedBlocking.map((f) => ({
|
|
59
|
+
severity: 'error',
|
|
60
|
+
code: f.code,
|
|
61
|
+
message: `${f.headline} — requires a named sign-off`,
|
|
62
|
+
...(f.detail ? { detail: f.detail } : {}),
|
|
63
|
+
file: f.file,
|
|
64
|
+
})),
|
|
65
|
+
...guard.acknowledged.map((f) => {
|
|
66
|
+
// Never render an unverified claim as a verified check. If the sign-off
|
|
67
|
+
// came from a trailer we can say precisely what backs it — and what does
|
|
68
|
+
// not. Absent a trailer we still refuse to imply verification.
|
|
69
|
+
const trailer = trailersById.get(f.id);
|
|
70
|
+
return {
|
|
71
|
+
severity: 'info',
|
|
72
|
+
code: `${f.code}_signed_off`,
|
|
73
|
+
message: trailer
|
|
74
|
+
? `${f.headline} — sign-off ${describeProof(trailer)}`
|
|
75
|
+
: `${f.headline} — sign-off claimed, UNVERIFIED`,
|
|
76
|
+
...(trailer ? { detail: `Trailer: ${trailer.raw}` } : {}),
|
|
77
|
+
file: f.file,
|
|
78
|
+
};
|
|
79
|
+
}),
|
|
80
|
+
];
|
|
81
|
+
const diff = guard.diff;
|
|
82
|
+
steps.push({
|
|
83
|
+
id: 'guard',
|
|
84
|
+
title: 'Compliance Guard',
|
|
85
|
+
blocking: true,
|
|
86
|
+
status: statusOf(true, guardFindings),
|
|
87
|
+
findings: guardFindings,
|
|
88
|
+
});
|
|
89
|
+
// ── 3c scenarios (ACP governance-level) ──────────────────────────────────────
|
|
90
|
+
const scenarioFindings = runScenarios(before, after, options.scenarioPolicy);
|
|
91
|
+
steps.push({
|
|
92
|
+
id: 'scenarios',
|
|
93
|
+
title: 'Adversarial scenarios (governance-level)',
|
|
94
|
+
blocking: true,
|
|
95
|
+
status: statusOf(true, scenarioFindings),
|
|
96
|
+
findings: scenarioFindings,
|
|
97
|
+
});
|
|
98
|
+
// ── 3c sandbox (agent-behavioral, delegated) ─────────────────────────────────
|
|
99
|
+
const sandbox = options.sandbox ?? onboardingSandboxStub;
|
|
100
|
+
const sandboxResult = await sandbox.evaluate({
|
|
101
|
+
changedFiles: diff.files.map((f) => f.file),
|
|
102
|
+
...(after.files['AGENTS.md']?.parsed.frontmatter.environment
|
|
103
|
+
? { environment: String(after.files['AGENTS.md']?.parsed.frontmatter.environment) }
|
|
104
|
+
: {}),
|
|
105
|
+
});
|
|
106
|
+
const sandboxStatus = sandboxResult.status === 'not_available'
|
|
107
|
+
? 'skip'
|
|
108
|
+
: sandboxResult.status === 'passed'
|
|
109
|
+
? 'pass'
|
|
110
|
+
: 'fail';
|
|
111
|
+
steps.push({
|
|
112
|
+
id: 'sandbox',
|
|
113
|
+
title: 'Adversarial sandbox (agent-behavioral, via Onboarding)',
|
|
114
|
+
blocking: sandboxResult.status !== 'not_available',
|
|
115
|
+
status: sandboxStatus,
|
|
116
|
+
findings: [
|
|
117
|
+
{
|
|
118
|
+
severity: sandboxResult.status === 'failed' ? 'error' : 'info',
|
|
119
|
+
code: `sandbox_${sandboxResult.status}`,
|
|
120
|
+
message: sandboxResult.reason,
|
|
121
|
+
...(sandboxResult.passRate !== undefined
|
|
122
|
+
? { detail: `pass rate ${sandboxResult.passRate}` }
|
|
123
|
+
: {}),
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
});
|
|
127
|
+
// ── 3d impact (informational) ────────────────────────────────────────────────
|
|
128
|
+
const impact = computeImpact(before, after, diff, options.registry);
|
|
129
|
+
steps.push({
|
|
130
|
+
id: 'impact',
|
|
131
|
+
title: 'Impact analysis',
|
|
132
|
+
blocking: false,
|
|
133
|
+
status: 'info',
|
|
134
|
+
findings: impactFindings(impact),
|
|
135
|
+
});
|
|
136
|
+
const ok = steps.every((s) => s.status !== 'fail');
|
|
137
|
+
const meta = {
|
|
138
|
+
contractVersion: CONTRACT_VERSION,
|
|
139
|
+
source: 'in-memory',
|
|
140
|
+
...options.meta,
|
|
141
|
+
};
|
|
142
|
+
return { ok, exitCode: ok ? 0 : 1, steps, diff, impact, meta };
|
|
143
|
+
}
|
|
144
|
+
function impactFindings(impact) {
|
|
145
|
+
const out = [];
|
|
146
|
+
out.push({
|
|
147
|
+
severity: 'info',
|
|
148
|
+
code: 'impact_scope',
|
|
149
|
+
message: `Affects ${impact.changedFiles.length} file(s), ` +
|
|
150
|
+
`${impact.environments.length} environment(s), ` +
|
|
151
|
+
`${impact.agents.agents.length} known agent(s) (${impact.agents.source}${impact.agents.complete ? '' : ', incomplete'})`,
|
|
152
|
+
...(impact.capabilitiesAffected.length
|
|
153
|
+
? { detail: `capabilities affected: ${impact.capabilitiesAffected.join(', ')}` }
|
|
154
|
+
: {}),
|
|
155
|
+
});
|
|
156
|
+
for (const note of impact.notes) {
|
|
157
|
+
out.push({ severity: 'info', code: 'impact_note', message: note });
|
|
158
|
+
}
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=suite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suite.js","sourceRoot":"","sources":["../src/suite.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CI test-suite output contract.
|
|
3
|
+
*
|
|
4
|
+
* ONE machine-readable result (`TestSuiteResult`) and ONE renderer over it (see
|
|
5
|
+
* render.ts). The overall verdict and the human summary both read the SAME
|
|
6
|
+
* normalized `StepResult[]` — no second implementation of "did it pass", no
|
|
7
|
+
* second renderer. Every step normalizes its native findings into
|
|
8
|
+
* `SuiteFinding`, so adding a step never touches the renderer or the verdict.
|
|
9
|
+
*/
|
|
10
|
+
import type { GovernanceFileName } from '@getvda/governance-schema';
|
|
11
|
+
import type { GovernanceDiff } from '@getvda/compliance-guard';
|
|
12
|
+
import type { ImpactAnalysis } from './impact.js';
|
|
13
|
+
export type StepId = 'schema' | 'guard' | 'scenarios' | 'sandbox' | 'impact';
|
|
14
|
+
export type FindingSeverity = 'error' | 'warning' | 'info';
|
|
15
|
+
/** Normalized finding — the single shape the renderer and verdict consume. */
|
|
16
|
+
export interface SuiteFinding {
|
|
17
|
+
readonly severity: FindingSeverity;
|
|
18
|
+
/** Stable machine code, e.g. "sop_no_clauses", "unconditioned_capability". */
|
|
19
|
+
readonly code: string;
|
|
20
|
+
readonly message: string;
|
|
21
|
+
readonly detail?: string;
|
|
22
|
+
readonly file?: GovernanceFileName | string;
|
|
23
|
+
/** True when the finding is a heuristic best-effort (not a proof). */
|
|
24
|
+
readonly heuristic?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export type StepStatus = 'pass' | 'fail' | 'warn' | 'skip' | 'info';
|
|
27
|
+
export interface StepResult {
|
|
28
|
+
readonly id: StepId;
|
|
29
|
+
readonly title: string;
|
|
30
|
+
/** A fail here fails the suite. Informational steps are non-blocking. */
|
|
31
|
+
readonly blocking: boolean;
|
|
32
|
+
readonly status: StepStatus;
|
|
33
|
+
readonly findings: readonly SuiteFinding[];
|
|
34
|
+
}
|
|
35
|
+
export interface TestSuiteMeta {
|
|
36
|
+
readonly contractVersion: string;
|
|
37
|
+
readonly baseRef?: string;
|
|
38
|
+
readonly headRef?: string;
|
|
39
|
+
/** How before/after were sourced (git refs, in-memory, files). */
|
|
40
|
+
readonly source: string;
|
|
41
|
+
}
|
|
42
|
+
export interface TestSuiteResult {
|
|
43
|
+
/** true iff no blocking step failed. The CI gate. */
|
|
44
|
+
readonly ok: boolean;
|
|
45
|
+
/** 0 = pass, 1 = failed. For `process.exit`. */
|
|
46
|
+
readonly exitCode: number;
|
|
47
|
+
readonly steps: readonly StepResult[];
|
|
48
|
+
/** The honest governance diff (from the guard step) for the approver surface. */
|
|
49
|
+
readonly diff: GovernanceDiff;
|
|
50
|
+
readonly impact: ImpactAnalysis;
|
|
51
|
+
readonly meta: TestSuiteMeta;
|
|
52
|
+
}
|
|
53
|
+
/** Derive a step's status from its normalized findings. Only a blocking step
|
|
54
|
+
* with an error FAILS; errors on a non-blocking step and any warning surface as
|
|
55
|
+
* `warn` (which never fails the suite). */
|
|
56
|
+
export declare function statusOf(blocking: boolean, findings: readonly SuiteFinding[], informational?: boolean): StepStatus;
|
|
57
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,MAAM,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE7E,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAE3D,8EAA8E;AAC9E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAAC;IAC5C,sEAAsE;IACtE,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEpE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;CAC5C;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,qDAAqD;IACrD,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IACtC,iFAAiF;IACjF,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;CAC9B;AAED;;4CAE4C;AAC5C,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,OAAO,EACjB,QAAQ,EAAE,SAAS,YAAY,EAAE,EACjC,aAAa,UAAQ,GACpB,UAAU,CAOZ"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CI test-suite output contract.
|
|
3
|
+
*
|
|
4
|
+
* ONE machine-readable result (`TestSuiteResult`) and ONE renderer over it (see
|
|
5
|
+
* render.ts). The overall verdict and the human summary both read the SAME
|
|
6
|
+
* normalized `StepResult[]` — no second implementation of "did it pass", no
|
|
7
|
+
* second renderer. Every step normalizes its native findings into
|
|
8
|
+
* `SuiteFinding`, so adding a step never touches the renderer or the verdict.
|
|
9
|
+
*/
|
|
10
|
+
/** Derive a step's status from its normalized findings. Only a blocking step
|
|
11
|
+
* with an error FAILS; errors on a non-blocking step and any warning surface as
|
|
12
|
+
* `warn` (which never fails the suite). */
|
|
13
|
+
export function statusOf(blocking, findings, informational = false) {
|
|
14
|
+
if (informational)
|
|
15
|
+
return 'info';
|
|
16
|
+
const hasError = findings.some((f) => f.severity === 'error');
|
|
17
|
+
const hasWarning = findings.some((f) => f.severity === 'warning');
|
|
18
|
+
if (hasError)
|
|
19
|
+
return blocking ? 'fail' : 'warn';
|
|
20
|
+
if (hasWarning)
|
|
21
|
+
return 'warn';
|
|
22
|
+
return 'pass';
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAqDH;;4CAE4C;AAC5C,MAAM,UAAU,QAAQ,CACtB,QAAiB,EACjB,QAAiC,EACjC,aAAa,GAAG,KAAK;IAErB,IAAI,aAAa;QAAE,OAAO,MAAM,CAAC;IACjC,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;IAC9D,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC;IAClE,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IAChD,IAAI,UAAU;QAAE,OAAO,MAAM,CAAC;IAC9B,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@getvda/test-suite",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "The ACP governance CI test suite — schema validation, Compliance Guard, governance-level adversarial scenarios, and deterministic impact analysis. Provider-free; invocable as a step in the customer's own CI (GitHub Action / GitLab component / plain CLI).",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"bin": {
|
|
10
|
+
"acp-test": "dist/cli.js"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc --build",
|
|
14
|
+
"typecheck": "tsc --noEmit",
|
|
15
|
+
"test": "vitest run"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@getvda/governance-schema": "^0.2.0",
|
|
19
|
+
"@getvda/compliance-guard": "^0.1.0"
|
|
20
|
+
},
|
|
21
|
+
"license": "Apache-2.0",
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"README.md",
|
|
28
|
+
"LICENSE",
|
|
29
|
+
"NOTICE"
|
|
30
|
+
],
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/getvda-ai/acp-agent.git",
|
|
34
|
+
"directory": "packages/test-suite"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://getvda.ai"
|
|
37
|
+
}
|