Add public CLI-first acceptance gate

This commit is contained in:
Michel Paulissen 2026-07-03 23:12:37 +02:00
parent afaf918973
commit 4b323224b2
6 changed files with 143 additions and 7 deletions

73
scripts/acceptance-cli-first.sh Executable file
View file

@ -0,0 +1,73 @@
#!/usr/bin/env bash
set -euo pipefail
repo="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$repo"
if [[ "${DISASMER_PUBLIC_RELEASE_DRYRUN_E2E:-}" == "1" ]]; then
echo "CLI-first acceptance does not run final public-release e2e; unset DISASMER_PUBLIC_RELEASE_DRYRUN_E2E" >&2
exit 1
fi
if [[ "${DISASMER_PUBLIC_RELEASE_DRYRUN_FINAL:-}" == "1" ]]; then
echo "CLI-first acceptance does not run final public-release evidence; unset DISASMER_PUBLIC_RELEASE_DRYRUN_FINAL" >&2
exit 1
fi
node scripts/acceptance-report.js cli-first
node scripts/cli-first-contract-smoke.js
node scripts/acceptance-report-smoke.js
node scripts/acceptance-doc-contract-smoke.js
node scripts/acceptance-environment-contract-smoke.js
node scripts/acceptance-evidence-contract-smoke.js
node scripts/public-private-boundary-smoke.js
node scripts/release-blocker-smoke.js
node scripts/resource-metering-contract-smoke.js
node scripts/hostile-input-contract-smoke.js
node scripts/tenant-isolation-contract-smoke.js
node scripts/public-story-contract-smoke.js
node scripts/public-release-dryrun-contract-smoke.js
node scripts/public-browser-login-contract-smoke.js
node scripts/self-hosted-coordinator-smoke.js
node scripts/public-local-demo-matrix-smoke.js
scripts/release-source-scan.sh
if [[ "${DISASMER_CLI_FIRST_PREPARE_PUBLIC_RELEASE:-1}" == "1" ]]; then
node scripts/prepare-public-release-dryrun.js
fi
cargo fmt --all --check
cargo test --workspace
cargo build --workspace --bins
node scripts/docs-smoke.js
node scripts/cli-output-mode-smoke.js
node scripts/cli-login-smoke.js
node scripts/cli-error-exit-smoke.js
node scripts/cli-browser-login-flow-smoke.js
node scripts/cli-install-smoke.js
node scripts/user-session-token-boundary-smoke.js
node scripts/sdk-spawn-runtime-smoke.js
node scripts/node-lifecycle-contract-smoke.js
node scripts/wasmtime-node-smoke.js
node scripts/podman-backend-smoke.js
node scripts/vscode-extension-smoke.js
node scripts/vscode-f5-smoke.js
node scripts/node-attach-smoke.js
node scripts/local-services-smoke.js
node scripts/cancellation-smoke.js
node scripts/cli-local-run-smoke.js
node scripts/artifact-download-smoke.js
node scripts/artifact-export-smoke.js
node scripts/operator-panel-smoke.js
node scripts/source-preparation-smoke.js
node scripts/scheduler-placement-smoke.js
node scripts/windows-best-effort-smoke.js
node scripts/windows-validation-contract-smoke.js
node scripts/quic-smoke.js
node scripts/dap-smoke.js
node scripts/flagship-demo-smoke.js
if [[ "${DISASMER_CLI_FIRST_INCLUDE_PRIVATE:-0}" == "1" ]]; then
scripts/acceptance-private.sh
fi

View file

@ -35,10 +35,12 @@ function assertNoOpenCriteria(source, name) {
const phase2 = read("acceptance_criteria_phase2.md");
const base = read("acceptance_criteria.md");
const cliFirst = read("cli_acceptance_criteria.md");
const docsSmoke = read("scripts/docs-smoke.js");
const releaseBlockerSmoke = read("scripts/release-blocker-smoke.js");
const publicAcceptance = read("scripts/acceptance-public.sh");
const privateAcceptance = read("scripts/acceptance-private.sh");
const cliFirstAcceptance = read("scripts/acceptance-cli-first.sh");
const publicSplit = read("scripts/verify-public-split.sh");
assert.match(
@ -60,12 +62,13 @@ assert.match(
for (const [source, name] of [
[base, "acceptance_criteria.md"],
[phase2, "acceptance_criteria_phase2.md"],
[cliFirst, "cli_acceptance_criteria.md"],
]) {
assertEveryCriterionHasStatus(source, name);
assertNoOpenCriteria(source, name);
}
for (const file of ["MVP.md", "acceptance_criteria.md", "acceptance_criteria_phase2.md"]) {
for (const file of ["MVP.md", "acceptance_criteria.md", "acceptance_criteria_phase2.md", "cli_acceptance_criteria.md"]) {
assert(
docsSmoke.includes(`"${file}"`),
`docs smoke must include ${file} as user-facing acceptance context`
@ -100,6 +103,7 @@ for (const [source, name] of [
for (const [scriptName, script] of [
["public acceptance", publicAcceptance],
["private acceptance", privateAcceptance],
["CLI-first acceptance", cliFirstAcceptance],
["public split", publicSplit],
]) {
assert(

View file

@ -33,6 +33,7 @@ function expect(source, name, pattern) {
const criteria = read("cli_acceptance_criteria.md");
const cli = read("crates/disasmer-cli/src/main.rs");
const coordinator = read("crates/disasmer-coordinator/src/service.rs");
const cliFirstAcceptance = read("scripts/acceptance-cli-first.sh");
const outputModeSmoke = read("scripts/cli-output-mode-smoke.js");
const errorExitSmoke = read("scripts/cli-error-exit-smoke.js");
@ -71,10 +72,64 @@ for (const line of lines) {
const openCriteria = lines.filter((line) => /\*\*Open(?::| \()/.test(line));
assert.deepStrictEqual(
openCriteria,
[
"- [ ] **Open:** Every criterion in this document is validated end-to-end or with integration-style coverage, preferably against a realistic NixOS deployment that can pretend to be live hosted infrastructure.",
],
"only the full CLI-first verification gate should remain Open while command/API facts are Partial"
[],
"CLI-first criteria should not leave Open items once the non-e2e gate exists; remaining unfinished facts stay Partial"
);
expect(
criteria,
"CLI-first non-e2e gate wording",
/scripts\/acceptance-cli-first\.sh[\s\S]*final public-release e2e remains intentionally excluded/
);
expect(
cliFirstAcceptance,
"CLI-first acceptance report",
/node scripts\/acceptance-report\.js cli-first/
);
expect(
cliFirstAcceptance,
"CLI-first acceptance refuses final e2e",
/DISASMER_PUBLIC_RELEASE_DRYRUN_E2E[\s\S]*does not run final public-release e2e/
);
expect(
cliFirstAcceptance,
"CLI-first acceptance refuses final evidence",
/DISASMER_PUBLIC_RELEASE_DRYRUN_FINAL[\s\S]*does not run final public-release evidence/
);
expect(
cliFirstAcceptance,
"CLI-first acceptance composes public API contracts",
/node scripts\/cli-output-mode-smoke\.js[\s\S]*node scripts\/cli-login-smoke\.js[\s\S]*node scripts\/cli-error-exit-smoke\.js[\s\S]*node scripts\/cli-browser-login-flow-smoke\.js/
);
expect(
cliFirstAcceptance,
"CLI-first acceptance composes service boundary checks",
/node scripts\/local-services-smoke\.js[\s\S]*node scripts\/cli-local-run-smoke\.js/
);
expect(
cliFirstAcceptance,
"CLI-first acceptance composes self-hosted checks",
/node scripts\/self-hosted-coordinator-smoke\.js/
);
expect(
cliFirstAcceptance,
"CLI-first acceptance composes debug and artifact checks",
/node scripts\/vscode-f5-smoke\.js[\s\S]*node scripts\/artifact-download-smoke\.js[\s\S]*node scripts\/artifact-export-smoke\.js/
);
expect(
cliFirstAcceptance,
"CLI-first acceptance composes DAP checks",
/node scripts\/dap-smoke\.js/
);
expect(
cliFirstAcceptance,
"CLI-first acceptance can include private hosted gate",
/DISASMER_CLI_FIRST_INCLUDE_PRIVATE[\s\S]*scripts\/acceptance-private\.sh/
);
assert.doesNotMatch(
cliFirstAcceptance,
/node scripts\/public-release-dryrun-e2e\.js|node scripts\/public-release-dryrun-final-evidence\.js/,
"CLI-first non-e2e gate must not invoke final public release e2e or final evidence verifier"
);
for (const [name, pattern] of [

View file

@ -123,6 +123,7 @@ function runCli(args, env = {}) {
assert.strictEqual(report.plan.entry, "build");
assert.deepStrictEqual(report.plan.session, {
AgentPublicKey: {
agent: "agent",
public_key_fingerprint: sha256(agentPublicKey),
browser_interaction_required: false
}

View file

@ -11,6 +11,7 @@ const userFacingDocs = [
"MVP.md",
"acceptance_criteria.md",
"acceptance_criteria_phase2.md",
"cli_acceptance_criteria.md",
].map((file) => [file, fs.readFileSync(path.join(repo, file), "utf8")]);
const publicAcceptance = fs.readFileSync(
path.join(repo, "scripts/acceptance-public.sh"),
@ -48,6 +49,8 @@ const requiredReadmePatterns = [
["artifact download smoke", /node scripts\/artifact-download-smoke\.js/],
["artifact export smoke", /node scripts\/artifact-export-smoke\.js/],
["acceptance report smoke", /node scripts\/acceptance-report-smoke\.js/],
["CLI-first acceptance gate", /scripts\/acceptance-cli-first\.sh/],
["CLI-first acceptance before e2e", /CLI-first non-e2e gate before any final public-release e2e attempt/],
["public private boundary smoke", /node scripts\/public-private-boundary-smoke\.js/],
["release blocker smoke", /node scripts\/release-blocker-smoke\.js/],
["explicit export", /attached receiver node or user-provided\s+storage integration/],