@kontourai/flow-agents 1.4.0 → 2.0.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/.github/CODEOWNERS +29 -0
- package/.github/actions/trust-verify/action.yml +145 -0
- package/.github/workflows/ci.yml +11 -4
- package/.github/workflows/kit-gates-demo.yml +2 -2
- package/.github/workflows/publish-npm.yml +10 -2
- package/.github/workflows/release-please.yml +1 -1
- package/.github/workflows/trust-reconcile.yml +113 -0
- package/AGENTS.md +13 -0
- package/CHANGELOG.md +95 -0
- package/CONTRIBUTING.md +4 -4
- package/README.md +1 -0
- package/agents/tool-planner.json +1 -1
- package/build/src/cli/init.js +242 -20
- package/build/src/cli/validate-workflow-artifacts.js +19 -2
- package/build/src/cli/verify.d.ts +1 -0
- package/build/src/cli/verify.js +90 -0
- package/build/src/cli/workflow-sidecar.d.ts +300 -8
- package/build/src/cli/workflow-sidecar.js +1934 -83
- package/build/src/cli.js +2 -3
- package/build/src/lib/flow-resolver.d.ts +82 -0
- package/build/src/lib/flow-resolver.js +237 -0
- package/build/src/tools/build-universal-bundles.js +34 -22
- package/build/src/tools/generate-context-map.js +3 -16
- package/build/src/tools/validate-source-tree.d.ts +1 -1
- package/build/src/tools/validate-source-tree.js +42 -162
- package/context/contracts/artifact-contract.md +10 -0
- package/context/contracts/delivery-contract.md +1 -0
- package/context/contracts/review-contract.md +1 -0
- package/context/contracts/verification-contract.md +2 -0
- package/context/gate-awareness.md +39 -0
- package/context/scripts/hooks/stop-goal-fit.js +632 -70
- package/docs/adr/0001-flow-agents-consumes-flow.md +1 -1
- package/docs/adr/0002-flow-kits-as-extension-unit.md +1 -1
- package/docs/adr/0004-gates-expect-surface-claims.md +2 -0
- package/docs/adr/0005-kubernetes-inspired-resource-contracts.md +2 -0
- package/docs/adr/0007-skill-audit.md +1 -1
- package/docs/adr/0009-canonical-hook-core-kit-boundary.md +95 -0
- package/docs/adr/0010-workflow-trust-state-as-hachure-bundle.md +139 -0
- package/docs/adr/0011-mcp-posture.md +100 -0
- package/docs/adr/0012-agent-coordination-as-liveness-claims.md +119 -0
- package/docs/adr/0013-context-lifecycle.md +151 -0
- package/docs/adr/0014-core-vs-domain-kit-boundary.md +143 -0
- package/docs/adr/0015-flow-flow-agents-boundary-reconciliation.md +120 -0
- package/docs/adr/0016-three-hard-boundary-model.md +71 -0
- package/docs/adr/0017-anti-gaming-trust-security-model.md +155 -0
- package/docs/agent-system-guidebook.md +5 -12
- package/docs/context-map.md +4 -10
- package/docs/index.md +3 -2
- package/docs/integrations/framework-adapter.md +19 -6
- package/docs/integrations/index.md +2 -2
- package/docs/north-star.md +4 -4
- package/docs/operating-layers.md +3 -3
- package/docs/plans/adr-0010-phase2-gate-recompute.md +55 -0
- package/docs/repository-structure.md +2 -2
- package/docs/skills-map.md +1 -0
- package/docs/spec/runtime-hook-surface.md +62 -9
- package/docs/standards-register.md +3 -3
- package/docs/survey-utterance-check.md +1 -1
- package/docs/trust-anchor-adoption.md +197 -0
- package/docs/verifiable-trust.md +95 -0
- package/docs/veritas-integration.md +2 -2
- package/docs/workflow-usage-guide.md +69 -0
- package/evals/acceptance/DEMO-false-completion.md +144 -0
- package/evals/acceptance/demo-cast.sh +92 -0
- package/evals/acceptance/demo-false-completion.sh +72 -0
- package/evals/acceptance/demo-real-evidence.sh +104 -0
- package/evals/acceptance/demo.tape +29 -0
- package/evals/acceptance/prove-capture-teeth-declared.sh +335 -0
- package/evals/acceptance/prove-capture-teeth.sh +114 -0
- package/evals/acceptance/prove-teeth.sh +105 -0
- package/evals/ci/antigaming-suite.sh +54 -0
- package/evals/ci/run-baseline.sh +2 -0
- package/evals/fixtures/flow-kit-repository/invalid-missing-extension-asset/flows/review.flow.json +26 -0
- package/evals/fixtures/flow-kit-repository/invalid-missing-extension-asset/kit.json +20 -0
- package/evals/fixtures/flow-kit-repository/valid-unknown-extension/flows/review.flow.json +26 -0
- package/evals/fixtures/flow-kit-repository/valid-unknown-extension/kit.json +18 -0
- package/evals/integration/test_builder_step_producers.sh +379 -0
- package/evals/integration/test_bundle_install.sh +35 -71
- package/evals/integration/test_bundle_lifecycle.sh +39 -2
- package/evals/integration/test_captured_fail_reconciliation.sh +820 -0
- package/evals/integration/test_checkpoint_signing.sh +489 -0
- package/evals/integration/test_claim_lookup.sh +352 -0
- package/evals/integration/test_command_log_integrity.sh +275 -0
- package/evals/integration/test_context_map.sh +0 -2
- package/evals/integration/test_dual_emit_flow_step.sh +278 -0
- package/evals/integration/test_enforcer_expects_driven.sh +281 -0
- package/evals/integration/test_evidence_capture_hook.sh +185 -0
- package/evals/integration/test_flow_kit_repository.sh +2 -0
- package/evals/integration/test_flowdef_session_activation.sh +273 -0
- package/evals/integration/test_flowdef_session_history_preservation.sh +250 -0
- package/evals/integration/test_gate_bypass_chain.sh +448 -0
- package/evals/integration/test_gate_lockdown.sh +1137 -0
- package/evals/integration/test_gate_review_inquiry_records.sh +399 -0
- package/evals/integration/test_goal_fit_escape_hatch.sh +73 -0
- package/evals/integration/test_goal_fit_hook.sh +69 -4
- package/evals/integration/test_goal_fit_rederive.sh +263 -0
- package/evals/integration/test_install_merge.sh +1176 -0
- package/evals/integration/test_mint_attestation.sh +373 -0
- package/evals/integration/test_phase_map_and_gate_claim.sh +365 -0
- package/evals/integration/test_publish_delivery.sh +269 -0
- package/evals/integration/test_reconcile_soundness.sh +528 -0
- package/evals/integration/test_resolvefirststep_security.sh +208 -0
- package/evals/integration/test_session_resume_roundtrip.sh +286 -0
- package/evals/integration/test_trust_checkpoint.sh +325 -0
- package/evals/integration/test_trust_reconcile.sh +293 -0
- package/evals/integration/test_verify_cli.sh +208 -0
- package/evals/integration/test_workflow_sidecar_writer.sh +549 -34
- package/evals/lib/node.sh +0 -6
- package/evals/run.sh +45 -0
- package/evals/static/test_workflow_skills.sh +6 -13
- package/install.sh +0 -7
- package/integrations/strands-ts/README.md +25 -15
- package/integrations/veritas/flow-agents.adapter.json +1 -2
- package/kits/builder/flows/build.flow.json +59 -12
- package/kits/builder/kit.json +85 -15
- package/kits/builder/skills/continue-work/SKILL.md +116 -0
- package/kits/builder/skills/deliver/SKILL.md +36 -6
- package/kits/builder/skills/design-probe/SKILL.md +28 -0
- package/kits/builder/skills/execute-plan/SKILL.md +9 -1
- package/kits/builder/skills/gate-review/SKILL.md +234 -0
- package/kits/builder/skills/learning-review/SKILL.md +30 -0
- package/kits/builder/skills/pickup-probe/SKILL.md +29 -0
- package/kits/builder/skills/plan-work/SKILL.md +13 -1
- package/kits/builder/skills/pull-work/SKILL.md +19 -0
- package/kits/knowledge/adapters/default-store/index.js +38 -0
- package/kits/knowledge/adapters/flow-runner/index.js +1620 -0
- package/kits/knowledge/adapters/obsidian-store/index.js +36 -6
- package/kits/knowledge/docs/store-contract.md +314 -0
- package/kits/knowledge/evals/audit-freshness/suite.test.js +368 -0
- package/kits/knowledge/evals/canonicalize-category/suite.test.js +383 -0
- package/kits/knowledge/evals/contract-suite/suite.test.js +111 -0
- package/kits/knowledge/evals/detect-contradictions/suite.test.js +324 -0
- package/kits/knowledge/evals/entities/suite.test.js +40 -0
- package/kits/knowledge/evals/glossary-sync/suite.test.js +416 -0
- package/kits/knowledge/evals/hygiene-review/suite.test.js +396 -0
- package/kits/knowledge/evals/retirement/suite.test.js +145 -0
- package/kits/knowledge/flows/audit-freshness.flow.json +44 -0
- package/kits/knowledge/flows/canonicalize-category.flow.json +44 -0
- package/kits/knowledge/flows/detect-contradictions.flow.json +44 -0
- package/kits/knowledge/flows/glossary-sync.flow.json +61 -0
- package/kits/knowledge/flows/hygiene-review.flow.json +43 -0
- package/kits/knowledge/kit.json +51 -1
- package/package.json +4 -4
- package/packaging/conformance/README.md +10 -2
- package/packaging/conformance/fixtures/evidence-capture--allow-records-command.json +29 -0
- package/packaging/conformance/fixtures/stop-goal-fit--block-bundle-disputed-claim.json +29 -0
- package/packaging/conformance/fixtures/stop-goal-fit--block-capture-contradicts-claimed-pass.json +30 -0
- package/packaging/conformance/fixtures/stop-goal-fit--block-mode.json +23 -0
- package/packaging/conformance/fixtures/stop-goal-fit--off-mode.json +24 -0
- package/packaging/conformance/fixtures/stop-goal-fit--warn-active-delivery.json +5 -2
- package/packaging/conformance/fixtures/stop-goal-fit--warn-no-bundle.json +23 -0
- package/packaging/conformance/fixtures/workflow-steering--reground-active-prompt.json +30 -0
- package/packaging/conformance/fixtures/workflow-steering--reground-session-start.json +30 -0
- package/packaging/conformance/run-conformance.js +1 -1
- package/scripts/README.md +2 -1
- package/scripts/build-universal-bundles.js +0 -1
- package/scripts/ci/mint-attestation.js +221 -0
- package/scripts/ci/trust-reconcile.js +545 -0
- package/scripts/hooks/config-protection.js +423 -1
- package/scripts/hooks/evidence-capture.js +348 -0
- package/scripts/hooks/lib/liveness-read.js +113 -0
- package/scripts/hooks/run-hook.js +6 -1
- package/scripts/hooks/stop-goal-fit.js +1471 -79
- package/scripts/hooks/workflow-steering.js +135 -5
- package/scripts/install-codex-home.sh +39 -0
- package/scripts/install-merge.js +330 -0
- package/src/cli/init.ts +218 -20
- package/src/cli/validate-workflow-artifacts.ts +18 -2
- package/src/cli/verify.ts +100 -0
- package/src/cli/workflow-sidecar.ts +2064 -77
- package/src/cli.ts +2 -3
- package/src/lib/flow-resolver.ts +284 -0
- package/src/tools/build-universal-bundles.ts +34 -21
- package/src/tools/generate-context-map.ts +3 -17
- package/src/tools/validate-source-tree.ts +44 -104
- package/build/src/tools/filter-installed-packs.d.ts +0 -2
- package/build/src/tools/filter-installed-packs.js +0 -135
- package/packaging/packs.json +0 -49
- package/scripts/filter-installed-packs.js +0 -2
- package/src/tools/filter-installed-packs.ts +0 -132
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_reconcile_soundness.sh — Soundness regression evals for trust-reconcile.js.
|
|
3
|
+
#
|
|
4
|
+
# Proves all Round-5 adversarial gaps are closed:
|
|
5
|
+
#
|
|
6
|
+
# A. COMPILE-ONLY-CLOSED: no trust-reconcile-verify configured (only build fallback)
|
|
7
|
+
# → exits 1 with "refusing to attest a compile-only check" message.
|
|
8
|
+
#
|
|
9
|
+
# B. REAL-VERIFY-CLOSED: build passes but a fake "real verify" (eval:static substitute)
|
|
10
|
+
# fails → trust-reconcile exits 1 (PRE-FIX this would exit 0 because only build ran).
|
|
11
|
+
#
|
|
12
|
+
# C. REAL-VERIFY-PASSES: the comprehensive verify resolves green → exits 0 (legit
|
|
13
|
+
# work is not false-blocked when package.json trust-reconcile-verify is present).
|
|
14
|
+
#
|
|
15
|
+
# D. CHECKPOINT-BYPASS-CLOSED: a checkpoint-only bundle (no evidence/claims, statusByClaimId
|
|
16
|
+
# all-passed) → exits 1 with checkpoint-bypass divergence (not a silent skip).
|
|
17
|
+
#
|
|
18
|
+
# E. EV-PASSING-NORMALIZED: bundle with passing:"pass" and passing:1 (non-boolean truthy)
|
|
19
|
+
# evidence items → both are treated as claimed-pass and reconciled.
|
|
20
|
+
#
|
|
21
|
+
# F. CLAIM-NO-EVIDENCE: workflow.check.command claim with no evidence item →
|
|
22
|
+
# not-run divergence, exits 1.
|
|
23
|
+
#
|
|
24
|
+
# G. LAUNDERING-OR: claimed pass for "npm test || exit 0" → laundering, exits 1.
|
|
25
|
+
#
|
|
26
|
+
# H. LAUNDERING-ECHO-OK: claimed pass for "npm test || echo ok" → laundering, exits 1.
|
|
27
|
+
#
|
|
28
|
+
# I. LAUNDERING-BIN-TRUE: claimed pass for "npm test || /bin/true" → laundering, exits 1.
|
|
29
|
+
#
|
|
30
|
+
# J. LAUNDERING-SEMI-TRUE: claimed pass for "npm test; true" → laundering, exits 1.
|
|
31
|
+
#
|
|
32
|
+
# Deterministic, no model spend, self-cleaning.
|
|
33
|
+
# Usage: bash evals/integration/test_reconcile_soundness.sh
|
|
34
|
+
|
|
35
|
+
set -uo pipefail
|
|
36
|
+
|
|
37
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
38
|
+
RECONCILE="$ROOT/scripts/ci/trust-reconcile.js"
|
|
39
|
+
|
|
40
|
+
TMP="$(mktemp -d)"
|
|
41
|
+
errors=0
|
|
42
|
+
|
|
43
|
+
_pass() { echo " PASS: $1"; }
|
|
44
|
+
_fail() { echo " FAIL: $1"; errors=$((errors + 1)); }
|
|
45
|
+
|
|
46
|
+
cleanup() { rm -rf "$TMP"; }
|
|
47
|
+
trap cleanup EXIT
|
|
48
|
+
|
|
49
|
+
# ─── Minimal package.json without trust-reconcile-verify ──────────────────────
|
|
50
|
+
PKG_NO_VERIFY="$TMP/pkg_no_verify"
|
|
51
|
+
mkdir -p "$PKG_NO_VERIFY"
|
|
52
|
+
node -e "
|
|
53
|
+
const fs = require('fs');
|
|
54
|
+
const pkg = { name: 'test-pkg', scripts: { build: 'echo BUILD_ONLY' } };
|
|
55
|
+
fs.writeFileSync('$PKG_NO_VERIFY/package.json', JSON.stringify(pkg, null, 2));
|
|
56
|
+
"
|
|
57
|
+
|
|
58
|
+
# ─── Minimal package.json WITH trust-reconcile-verify ─────────────────────────
|
|
59
|
+
PKG_WITH_VERIFY="$TMP/pkg_with_verify"
|
|
60
|
+
mkdir -p "$PKG_WITH_VERIFY"
|
|
61
|
+
node -e "
|
|
62
|
+
const fs = require('fs');
|
|
63
|
+
const pkg = {
|
|
64
|
+
name: 'test-pkg',
|
|
65
|
+
scripts: {
|
|
66
|
+
build: 'node -e \"process.exit(0)\"',
|
|
67
|
+
'trust-reconcile-verify': 'node -e \"process.exit(0)\"'
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
fs.writeFileSync('$PKG_WITH_VERIFY/package.json', JSON.stringify(pkg, null, 2));
|
|
71
|
+
"
|
|
72
|
+
|
|
73
|
+
# ─── Minimal package.json with failing verify ─────────────────────────────────
|
|
74
|
+
PKG_FAIL_VERIFY="$TMP/pkg_fail_verify"
|
|
75
|
+
mkdir -p "$PKG_FAIL_VERIFY"
|
|
76
|
+
node -e "
|
|
77
|
+
const fs = require('fs');
|
|
78
|
+
const pkg = {
|
|
79
|
+
name: 'test-pkg',
|
|
80
|
+
scripts: {
|
|
81
|
+
build: 'node -e \"process.exit(0)\"',
|
|
82
|
+
'trust-reconcile-verify': 'node -e \"process.exit(1)\"'
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
fs.writeFileSync('$PKG_FAIL_VERIFY/package.json', JSON.stringify(pkg, null, 2));
|
|
86
|
+
"
|
|
87
|
+
|
|
88
|
+
# ─── Bundle builder helpers ────────────────────────────────────────────────────
|
|
89
|
+
# write_bundle_evidence: bundle with evidence[].passing = <passing_val> (any JS value)
|
|
90
|
+
write_bundle_evidence() {
|
|
91
|
+
local bundle_path="$1"
|
|
92
|
+
local label="$2"
|
|
93
|
+
local passing_val="$3" # raw JS value: true, false, "\"pass\"", 1, etc.
|
|
94
|
+
|
|
95
|
+
node - "$bundle_path" "$label" "$passing_val" << 'NODE'
|
|
96
|
+
const fs = require('fs');
|
|
97
|
+
const [,, bundlePath, label, passingVal] = process.argv;
|
|
98
|
+
// Evaluate passing value as JS literal
|
|
99
|
+
const passing = JSON.parse(passingVal);
|
|
100
|
+
const bundle = {
|
|
101
|
+
schemaVersion: 3,
|
|
102
|
+
source: "test-fixture",
|
|
103
|
+
claims: [
|
|
104
|
+
{
|
|
105
|
+
id: "c1",
|
|
106
|
+
claimType: "workflow.check.build",
|
|
107
|
+
value: "pass",
|
|
108
|
+
status: "verified",
|
|
109
|
+
subjectId: "test/build",
|
|
110
|
+
surface: "flow-agents.workflow",
|
|
111
|
+
subjectType: "workflow-check",
|
|
112
|
+
fieldOrBehavior: "build",
|
|
113
|
+
createdAt: "2026-06-27T00:00:00Z",
|
|
114
|
+
updatedAt: "2026-06-27T00:00:00Z",
|
|
115
|
+
impactLevel: "high",
|
|
116
|
+
verificationPolicyId: "policy:workflow.check.build"
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
evidence: [
|
|
120
|
+
{
|
|
121
|
+
id: "ev1",
|
|
122
|
+
claimId: "c1",
|
|
123
|
+
evidenceType: "test_output",
|
|
124
|
+
method: "validation",
|
|
125
|
+
sourceRef: "test/command-log.jsonl",
|
|
126
|
+
excerptOrSummary: "build",
|
|
127
|
+
observedAt: "2026-06-27T00:00:00Z",
|
|
128
|
+
collectedBy: "flow-agents/evidence-capture",
|
|
129
|
+
passing: passing,
|
|
130
|
+
execution: {
|
|
131
|
+
runner: "bash",
|
|
132
|
+
label: label,
|
|
133
|
+
isError: !passing,
|
|
134
|
+
exitCode: passing ? 0 : 1
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
],
|
|
138
|
+
policies: [],
|
|
139
|
+
events: []
|
|
140
|
+
};
|
|
141
|
+
fs.writeFileSync(bundlePath, JSON.stringify(bundle, null, 2));
|
|
142
|
+
NODE
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
# write_checkpoint: checkpoint-only bundle (no evidence[], no claims[])
|
|
146
|
+
write_checkpoint() {
|
|
147
|
+
local bundle_path="$1"
|
|
148
|
+
node - "$bundle_path" << 'NODE'
|
|
149
|
+
const fs = require('fs');
|
|
150
|
+
const [,, bundlePath] = process.argv;
|
|
151
|
+
const bundle = {
|
|
152
|
+
schemaVersion: 1,
|
|
153
|
+
source: "checkpoint",
|
|
154
|
+
checkpoint: {
|
|
155
|
+
statusByClaimId: {
|
|
156
|
+
"c1": "passed",
|
|
157
|
+
"c2": "passed",
|
|
158
|
+
"c3": "passed"
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
fs.writeFileSync(bundlePath, JSON.stringify(bundle, null, 2));
|
|
163
|
+
NODE
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
# write_bundle_claim_no_evidence: bundle with a workflow.check.command claim but no evidence
|
|
167
|
+
write_bundle_claim_no_evidence() {
|
|
168
|
+
local bundle_path="$1"
|
|
169
|
+
node - "$bundle_path" << 'NODE'
|
|
170
|
+
const fs = require('fs');
|
|
171
|
+
const [,, bundlePath] = process.argv;
|
|
172
|
+
const bundle = {
|
|
173
|
+
schemaVersion: 3,
|
|
174
|
+
source: "test-fixture",
|
|
175
|
+
claims: [
|
|
176
|
+
{
|
|
177
|
+
id: "c-no-ev",
|
|
178
|
+
claimType: "workflow.check.command",
|
|
179
|
+
value: "pass",
|
|
180
|
+
status: "verified",
|
|
181
|
+
subjectId: "test/command-never-run",
|
|
182
|
+
surface: "flow-agents.workflow",
|
|
183
|
+
subjectType: "workflow-check",
|
|
184
|
+
fieldOrBehavior: "npm run test-never-ran",
|
|
185
|
+
createdAt: "2026-06-27T00:00:00Z",
|
|
186
|
+
updatedAt: "2026-06-27T00:00:00Z",
|
|
187
|
+
impactLevel: "high",
|
|
188
|
+
verificationPolicyId: "policy:workflow.check.command"
|
|
189
|
+
}
|
|
190
|
+
],
|
|
191
|
+
evidence: [],
|
|
192
|
+
policies: [],
|
|
193
|
+
events: []
|
|
194
|
+
};
|
|
195
|
+
fs.writeFileSync(bundlePath, JSON.stringify(bundle, null, 2));
|
|
196
|
+
NODE
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
200
|
+
# TEST A: COMPILE-ONLY-CLOSED
|
|
201
|
+
# No trust-reconcile-verify in package.json, no TRUST_RECONCILE_COMMANDS → fail-closed
|
|
202
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
203
|
+
echo ""
|
|
204
|
+
echo "=== TEST A: COMPILE-ONLY-CLOSED — no verify script configured → fail-closed ==="
|
|
205
|
+
|
|
206
|
+
outA=$(node "$RECONCILE" \
|
|
207
|
+
--repo-root "$PKG_NO_VERIFY" 2>&1)
|
|
208
|
+
exitA=$?
|
|
209
|
+
|
|
210
|
+
if [[ $exitA -ne 0 ]]; then
|
|
211
|
+
_pass "COMPILE-ONLY-CLOSED: exits 1 (got $exitA)"
|
|
212
|
+
else
|
|
213
|
+
_fail "COMPILE-ONLY-CLOSED: expected exit 1 (compile-only refused), got 0"
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
if echo "$outA" | grep -qi "refusing to attest a compile-only check\|no comprehensive trust-reconcile-verify"; then
|
|
217
|
+
_pass "COMPILE-ONLY-CLOSED: message explains compile-only refusal"
|
|
218
|
+
else
|
|
219
|
+
_fail "COMPILE-ONLY-CLOSED: expected compile-only refusal message, got: $outA"
|
|
220
|
+
fi
|
|
221
|
+
|
|
222
|
+
# Also verify: the message recommends how to fix it
|
|
223
|
+
if echo "$outA" | grep -q "trust-reconcile-verify\|TRUST_RECONCILE_COMMANDS"; then
|
|
224
|
+
_pass "COMPILE-ONLY-CLOSED: message references how to fix (declare scripts or env)"
|
|
225
|
+
else
|
|
226
|
+
_fail "COMPILE-ONLY-CLOSED: expected fix hint in message, got: $outA"
|
|
227
|
+
fi
|
|
228
|
+
|
|
229
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
230
|
+
# TEST B: REAL-VERIFY-CLOSED
|
|
231
|
+
# package.json has trust-reconcile-verify but it FAILS (simulates real tests failing)
|
|
232
|
+
# PRE-FIX: would have exited 0 (only build ran). POST-FIX: exits 1.
|
|
233
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
234
|
+
echo ""
|
|
235
|
+
echo "=== TEST B: REAL-VERIFY-CLOSED — trust-reconcile-verify fails → exits 1 ==="
|
|
236
|
+
|
|
237
|
+
outB=$(node "$RECONCILE" \
|
|
238
|
+
--repo-root "$PKG_FAIL_VERIFY" 2>&1)
|
|
239
|
+
exitB=$?
|
|
240
|
+
|
|
241
|
+
if [[ $exitB -ne 0 ]]; then
|
|
242
|
+
_pass "REAL-VERIFY-CLOSED: exits 1 when trust-reconcile-verify fails (got $exitB)"
|
|
243
|
+
else
|
|
244
|
+
_fail "REAL-VERIFY-CLOSED: expected exit 1 when real verify fails, got 0 — output: $outB"
|
|
245
|
+
fi
|
|
246
|
+
|
|
247
|
+
if echo "$outB" | grep -q "verification failed in CI"; then
|
|
248
|
+
_pass "REAL-VERIFY-CLOSED: 'verification failed in CI' message present"
|
|
249
|
+
else
|
|
250
|
+
_fail "REAL-VERIFY-CLOSED: expected 'verification failed in CI' message, got: $outB"
|
|
251
|
+
fi
|
|
252
|
+
|
|
253
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
254
|
+
# TEST C: REAL-VERIFY-PASSES
|
|
255
|
+
# package.json has passing trust-reconcile-verify → exits 0 (not false-blocked)
|
|
256
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
257
|
+
echo ""
|
|
258
|
+
echo "=== TEST C: REAL-VERIFY-PASSES — passing verify + no bundle → exits 0 ==="
|
|
259
|
+
|
|
260
|
+
outC=$(node "$RECONCILE" \
|
|
261
|
+
--repo-root "$PKG_WITH_VERIFY" 2>&1)
|
|
262
|
+
exitC=$?
|
|
263
|
+
|
|
264
|
+
if [[ $exitC -eq 0 ]]; then
|
|
265
|
+
_pass "REAL-VERIFY-PASSES: exits 0 when real verify passes and no bundle (got $exitC)"
|
|
266
|
+
else
|
|
267
|
+
_fail "REAL-VERIFY-PASSES: expected exit 0, got $exitC — output: $outC"
|
|
268
|
+
fi
|
|
269
|
+
|
|
270
|
+
if echo "$outC" | grep -q "fresh verify passed"; then
|
|
271
|
+
_pass "REAL-VERIFY-PASSES: 'fresh verify passed' message present"
|
|
272
|
+
else
|
|
273
|
+
_fail "REAL-VERIFY-PASSES: expected 'fresh verify passed' in output, got: $outC"
|
|
274
|
+
fi
|
|
275
|
+
|
|
276
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
277
|
+
# TEST D: CHECKPOINT-BYPASS-CLOSED
|
|
278
|
+
# Checkpoint-only bundle (no evidence/claims, all statusByClaimId=passed) → divergence
|
|
279
|
+
# PRE-FIX: exited 0 (silently skipped per-command reconcile). POST-FIX: exits 1.
|
|
280
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
281
|
+
echo ""
|
|
282
|
+
echo "=== TEST D: CHECKPOINT-BYPASS-CLOSED — checkpoint-only bundle → divergence ==="
|
|
283
|
+
|
|
284
|
+
CHECKPOINT_BUNDLE="$TMP/checkpoint.json"
|
|
285
|
+
write_checkpoint "$CHECKPOINT_BUNDLE"
|
|
286
|
+
|
|
287
|
+
outD=$(TRUST_RECONCILE_COMMANDS="node -e 'process.exit(0)'" \
|
|
288
|
+
node "$RECONCILE" \
|
|
289
|
+
--bundle "$CHECKPOINT_BUNDLE" \
|
|
290
|
+
--repo-root "$TMP" 2>&1)
|
|
291
|
+
exitD=$?
|
|
292
|
+
|
|
293
|
+
if [[ $exitD -ne 0 ]]; then
|
|
294
|
+
_pass "CHECKPOINT-BYPASS-CLOSED: exits 1 (got $exitD)"
|
|
295
|
+
else
|
|
296
|
+
_fail "CHECKPOINT-BYPASS-CLOSED: expected exit 1 (checkpoint bypass closed), got 0 — output: $outD"
|
|
297
|
+
fi
|
|
298
|
+
|
|
299
|
+
if echo "$outD" | grep -q "checkpoint-only bundle"; then
|
|
300
|
+
_pass "CHECKPOINT-BYPASS-CLOSED: 'checkpoint-only bundle' message present"
|
|
301
|
+
else
|
|
302
|
+
_fail "CHECKPOINT-BYPASS-CLOSED: expected 'checkpoint-only bundle' in output, got: $outD"
|
|
303
|
+
fi
|
|
304
|
+
|
|
305
|
+
if echo "$outD" | grep -q "trust divergence"; then
|
|
306
|
+
_pass "CHECKPOINT-BYPASS-CLOSED: 'trust divergence' emitted for checkpoint bundle"
|
|
307
|
+
else
|
|
308
|
+
_fail "CHECKPOINT-BYPASS-CLOSED: expected 'trust divergence' in output, got: $outD"
|
|
309
|
+
fi
|
|
310
|
+
|
|
311
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
312
|
+
# TEST E: EV-PASSING-NORMALIZED
|
|
313
|
+
# evidence with passing:"pass" is treated as claimed-pass (not dropped)
|
|
314
|
+
# evidence with passing:1 is also treated as claimed-pass
|
|
315
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
316
|
+
echo ""
|
|
317
|
+
echo "=== TEST E: EV-PASSING-NORMALIZED — passing:\"pass\" and passing:1 both reconciled ==="
|
|
318
|
+
|
|
319
|
+
# E1: passing:"pass" — claimed pass, CI re-run also PASSES → reconciled (exit 0)
|
|
320
|
+
BUNDLE_E1="$TMP/bundle-pass-string.json"
|
|
321
|
+
write_bundle_evidence "$BUNDLE_E1" "node -e 'process.exit(0)'" '"pass"'
|
|
322
|
+
|
|
323
|
+
outE1=$(TRUST_RECONCILE_COMMANDS="node -e 'process.exit(0)'" \
|
|
324
|
+
node "$RECONCILE" \
|
|
325
|
+
--bundle "$BUNDLE_E1" \
|
|
326
|
+
--repo-root "$TMP" 2>&1)
|
|
327
|
+
exitE1=$?
|
|
328
|
+
|
|
329
|
+
if [[ $exitE1 -eq 0 ]]; then
|
|
330
|
+
_pass "EV-PASSING-NORMALIZED: passing:\"pass\" evidence is reconciled (exit 0)"
|
|
331
|
+
else
|
|
332
|
+
_fail "EV-PASSING-NORMALIZED: passing:\"pass\" should exit 0 (reconciled), got $exitE1 — output: $outE1"
|
|
333
|
+
fi
|
|
334
|
+
|
|
335
|
+
if echo "$outE1" | grep -q "RECONCILED"; then
|
|
336
|
+
_pass "EV-PASSING-NORMALIZED: RECONCILED shown for passing:\"pass\" evidence"
|
|
337
|
+
else
|
|
338
|
+
_fail "EV-PASSING-NORMALIZED: expected RECONCILED for passing:\"pass\", got: $outE1"
|
|
339
|
+
fi
|
|
340
|
+
|
|
341
|
+
# E2: passing:"pass" — claimed pass, CI re-run FAILS → divergence (exit 1)
|
|
342
|
+
BUNDLE_E2="$TMP/bundle-pass-string-fail.json"
|
|
343
|
+
write_bundle_evidence "$BUNDLE_E2" "node -e 'process.exit(1)'" '"pass"'
|
|
344
|
+
|
|
345
|
+
outE2=$(TRUST_RECONCILE_COMMANDS="node -e 'process.exit(1)'" \
|
|
346
|
+
node "$RECONCILE" \
|
|
347
|
+
--bundle "$BUNDLE_E2" \
|
|
348
|
+
--repo-root "$TMP" 2>&1)
|
|
349
|
+
exitE2=$?
|
|
350
|
+
|
|
351
|
+
if [[ $exitE2 -ne 0 ]]; then
|
|
352
|
+
_pass "EV-PASSING-NORMALIZED: passing:\"pass\" evidence triggers divergence when CI fails (exit 1)"
|
|
353
|
+
else
|
|
354
|
+
_fail "EV-PASSING-NORMALIZED: passing:\"pass\" should exit 1 (divergence), got 0 — output: $outE2"
|
|
355
|
+
fi
|
|
356
|
+
|
|
357
|
+
# E3: passing:1 — treated as claimed-pass, CI PASSES → reconciled (exit 0)
|
|
358
|
+
BUNDLE_E3="$TMP/bundle-pass-int.json"
|
|
359
|
+
write_bundle_evidence "$BUNDLE_E3" "node -e 'process.exit(0)'" '1'
|
|
360
|
+
|
|
361
|
+
outE3=$(TRUST_RECONCILE_COMMANDS="node -e 'process.exit(0)'" \
|
|
362
|
+
node "$RECONCILE" \
|
|
363
|
+
--bundle "$BUNDLE_E3" \
|
|
364
|
+
--repo-root "$TMP" 2>&1)
|
|
365
|
+
exitE3=$?
|
|
366
|
+
|
|
367
|
+
if [[ $exitE3 -eq 0 ]]; then
|
|
368
|
+
_pass "EV-PASSING-NORMALIZED: passing:1 evidence is reconciled (exit 0)"
|
|
369
|
+
else
|
|
370
|
+
_fail "EV-PASSING-NORMALIZED: passing:1 should exit 0 (reconciled), got $exitE3 — output: $outE3"
|
|
371
|
+
fi
|
|
372
|
+
|
|
373
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
374
|
+
# TEST F: CLAIM-NO-EVIDENCE
|
|
375
|
+
# workflow.check.command claim with no evidence item → not-run divergence, exits 1
|
|
376
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
377
|
+
echo ""
|
|
378
|
+
echo "=== TEST F: CLAIM-NO-EVIDENCE — workflow.check.command claim, no evidence → not-run ==="
|
|
379
|
+
|
|
380
|
+
BUNDLE_F="$TMP/bundle-claim-no-ev.json"
|
|
381
|
+
write_bundle_claim_no_evidence "$BUNDLE_F"
|
|
382
|
+
|
|
383
|
+
outF=$(TRUST_RECONCILE_COMMANDS="node -e 'process.exit(0)'" \
|
|
384
|
+
node "$RECONCILE" \
|
|
385
|
+
--bundle "$BUNDLE_F" \
|
|
386
|
+
--repo-root "$TMP" 2>&1)
|
|
387
|
+
exitF=$?
|
|
388
|
+
|
|
389
|
+
if [[ $exitF -ne 0 ]]; then
|
|
390
|
+
_pass "CLAIM-NO-EVIDENCE: exits 1 (not-run divergence) (got $exitF)"
|
|
391
|
+
else
|
|
392
|
+
_fail "CLAIM-NO-EVIDENCE: expected exit 1 (no-evidence divergence), got 0 — output: $outF"
|
|
393
|
+
fi
|
|
394
|
+
|
|
395
|
+
if echo "$outF" | grep -q "trust divergence"; then
|
|
396
|
+
_pass "CLAIM-NO-EVIDENCE: 'trust divergence' emitted"
|
|
397
|
+
else
|
|
398
|
+
_fail "CLAIM-NO-EVIDENCE: expected 'trust divergence' in output, got: $outF"
|
|
399
|
+
fi
|
|
400
|
+
|
|
401
|
+
if echo "$outF" | grep -q "no supporting evidence\|never captured"; then
|
|
402
|
+
_pass "CLAIM-NO-EVIDENCE: message describes missing evidence"
|
|
403
|
+
else
|
|
404
|
+
_fail "CLAIM-NO-EVIDENCE: expected 'no supporting evidence' or 'never captured' message, got: $outF"
|
|
405
|
+
fi
|
|
406
|
+
|
|
407
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
408
|
+
# TEST G: LAUNDERING-OR-EXIT0
|
|
409
|
+
# claimed pass for "npm test || exit 0" → laundering, exits 1
|
|
410
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
411
|
+
echo ""
|
|
412
|
+
echo "=== TEST G: LAUNDERING-OR-EXIT0 — \"npm test || exit 0\" → laundering ==="
|
|
413
|
+
|
|
414
|
+
BUNDLE_G="$TMP/bundle-launder-or-exit0.json"
|
|
415
|
+
write_bundle_evidence "$BUNDLE_G" "npm test || exit 0" 'true'
|
|
416
|
+
|
|
417
|
+
outG=$(TRUST_RECONCILE_COMMANDS="node -e 'process.exit(0)'" \
|
|
418
|
+
node "$RECONCILE" \
|
|
419
|
+
--bundle "$BUNDLE_G" \
|
|
420
|
+
--repo-root "$TMP" 2>&1)
|
|
421
|
+
exitG=$?
|
|
422
|
+
|
|
423
|
+
if [[ $exitG -ne 0 ]]; then
|
|
424
|
+
_pass "LAUNDERING-OR-EXIT0: exits 1"
|
|
425
|
+
else
|
|
426
|
+
_fail "LAUNDERING-OR-EXIT0: expected exit 1, got 0 — output: $outG"
|
|
427
|
+
fi
|
|
428
|
+
|
|
429
|
+
if echo "$outG" | grep -q "laundering"; then
|
|
430
|
+
_pass "LAUNDERING-OR-EXIT0: 'laundering' message present"
|
|
431
|
+
else
|
|
432
|
+
_fail "LAUNDERING-OR-EXIT0: expected 'laundering' message, got: $outG"
|
|
433
|
+
fi
|
|
434
|
+
|
|
435
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
436
|
+
# TEST H: LAUNDERING-OR-ECHO-OK
|
|
437
|
+
# claimed pass for "npm test || echo ok" → laundering (any ||), exits 1
|
|
438
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
439
|
+
echo ""
|
|
440
|
+
echo "=== TEST H: LAUNDERING-OR-ECHO-OK — \"npm test || echo ok\" → laundering ==="
|
|
441
|
+
|
|
442
|
+
BUNDLE_H="$TMP/bundle-launder-or-echo.json"
|
|
443
|
+
write_bundle_evidence "$BUNDLE_H" "npm test || echo ok" 'true'
|
|
444
|
+
|
|
445
|
+
outH=$(TRUST_RECONCILE_COMMANDS="node -e 'process.exit(0)'" \
|
|
446
|
+
node "$RECONCILE" \
|
|
447
|
+
--bundle "$BUNDLE_H" \
|
|
448
|
+
--repo-root "$TMP" 2>&1)
|
|
449
|
+
exitH=$?
|
|
450
|
+
|
|
451
|
+
if [[ $exitH -ne 0 ]]; then
|
|
452
|
+
_pass "LAUNDERING-OR-ECHO-OK: exits 1"
|
|
453
|
+
else
|
|
454
|
+
_fail "LAUNDERING-OR-ECHO-OK: expected exit 1, got 0 — output: $outH"
|
|
455
|
+
fi
|
|
456
|
+
|
|
457
|
+
if echo "$outH" | grep -q "laundering"; then
|
|
458
|
+
_pass "LAUNDERING-OR-ECHO-OK: 'laundering' message present"
|
|
459
|
+
else
|
|
460
|
+
_fail "LAUNDERING-OR-ECHO-OK: expected 'laundering' message, got: $outH"
|
|
461
|
+
fi
|
|
462
|
+
|
|
463
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
464
|
+
# TEST I: LAUNDERING-OR-BIN-TRUE
|
|
465
|
+
# claimed pass for "npm test || /bin/true" → laundering (any ||), exits 1
|
|
466
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
467
|
+
echo ""
|
|
468
|
+
echo "=== TEST I: LAUNDERING-OR-BIN-TRUE — \"npm test || /bin/true\" → laundering ==="
|
|
469
|
+
|
|
470
|
+
BUNDLE_I="$TMP/bundle-launder-or-bintrue.json"
|
|
471
|
+
write_bundle_evidence "$BUNDLE_I" "npm test || /bin/true" 'true'
|
|
472
|
+
|
|
473
|
+
outI=$(TRUST_RECONCILE_COMMANDS="node -e 'process.exit(0)'" \
|
|
474
|
+
node "$RECONCILE" \
|
|
475
|
+
--bundle "$BUNDLE_I" \
|
|
476
|
+
--repo-root "$TMP" 2>&1)
|
|
477
|
+
exitI=$?
|
|
478
|
+
|
|
479
|
+
if [[ $exitI -ne 0 ]]; then
|
|
480
|
+
_pass "LAUNDERING-OR-BIN-TRUE: exits 1"
|
|
481
|
+
else
|
|
482
|
+
_fail "LAUNDERING-OR-BIN-TRUE: expected exit 1, got 0 — output: $outI"
|
|
483
|
+
fi
|
|
484
|
+
|
|
485
|
+
if echo "$outI" | grep -q "laundering"; then
|
|
486
|
+
_pass "LAUNDERING-OR-BIN-TRUE: 'laundering' message present"
|
|
487
|
+
else
|
|
488
|
+
_fail "LAUNDERING-OR-BIN-TRUE: expected 'laundering' message, got: $outI"
|
|
489
|
+
fi
|
|
490
|
+
|
|
491
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
492
|
+
# TEST J: LAUNDERING-SEMI-TRUE
|
|
493
|
+
# claimed pass for "npm test; true" → laundering (; true form), exits 1
|
|
494
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
495
|
+
echo ""
|
|
496
|
+
echo "=== TEST J: LAUNDERING-SEMI-TRUE — \"npm test; true\" → laundering ==="
|
|
497
|
+
|
|
498
|
+
BUNDLE_J="$TMP/bundle-launder-semi-true.json"
|
|
499
|
+
write_bundle_evidence "$BUNDLE_J" "npm test; true" 'true'
|
|
500
|
+
|
|
501
|
+
outJ=$(TRUST_RECONCILE_COMMANDS="node -e 'process.exit(0)'" \
|
|
502
|
+
node "$RECONCILE" \
|
|
503
|
+
--bundle "$BUNDLE_J" \
|
|
504
|
+
--repo-root "$TMP" 2>&1)
|
|
505
|
+
exitJ=$?
|
|
506
|
+
|
|
507
|
+
if [[ $exitJ -ne 0 ]]; then
|
|
508
|
+
_pass "LAUNDERING-SEMI-TRUE: exits 1"
|
|
509
|
+
else
|
|
510
|
+
_fail "LAUNDERING-SEMI-TRUE: expected exit 1, got 0 — output: $outJ"
|
|
511
|
+
fi
|
|
512
|
+
|
|
513
|
+
if echo "$outJ" | grep -q "laundering"; then
|
|
514
|
+
_pass "LAUNDERING-SEMI-TRUE: 'laundering' message present"
|
|
515
|
+
else
|
|
516
|
+
_fail "LAUNDERING-SEMI-TRUE: expected 'laundering' message, got: $outJ"
|
|
517
|
+
fi
|
|
518
|
+
|
|
519
|
+
# ─── Summary ──────────────────────────────────────────────────────────────────
|
|
520
|
+
echo ""
|
|
521
|
+
echo "────────────────────────────────────────────"
|
|
522
|
+
if [[ $errors -eq 0 ]]; then
|
|
523
|
+
echo "test_reconcile_soundness: all checks passed."
|
|
524
|
+
exit 0
|
|
525
|
+
else
|
|
526
|
+
echo "test_reconcile_soundness: $errors check(s) failed."
|
|
527
|
+
exit 1
|
|
528
|
+
fi
|