@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.
Files changed (180) hide show
  1. package/.github/CODEOWNERS +29 -0
  2. package/.github/actions/trust-verify/action.yml +145 -0
  3. package/.github/workflows/ci.yml +11 -4
  4. package/.github/workflows/kit-gates-demo.yml +2 -2
  5. package/.github/workflows/publish-npm.yml +10 -2
  6. package/.github/workflows/release-please.yml +1 -1
  7. package/.github/workflows/trust-reconcile.yml +113 -0
  8. package/AGENTS.md +13 -0
  9. package/CHANGELOG.md +95 -0
  10. package/CONTRIBUTING.md +4 -4
  11. package/README.md +1 -0
  12. package/agents/tool-planner.json +1 -1
  13. package/build/src/cli/init.js +242 -20
  14. package/build/src/cli/validate-workflow-artifacts.js +19 -2
  15. package/build/src/cli/verify.d.ts +1 -0
  16. package/build/src/cli/verify.js +90 -0
  17. package/build/src/cli/workflow-sidecar.d.ts +300 -8
  18. package/build/src/cli/workflow-sidecar.js +1934 -83
  19. package/build/src/cli.js +2 -3
  20. package/build/src/lib/flow-resolver.d.ts +82 -0
  21. package/build/src/lib/flow-resolver.js +237 -0
  22. package/build/src/tools/build-universal-bundles.js +34 -22
  23. package/build/src/tools/generate-context-map.js +3 -16
  24. package/build/src/tools/validate-source-tree.d.ts +1 -1
  25. package/build/src/tools/validate-source-tree.js +42 -162
  26. package/context/contracts/artifact-contract.md +10 -0
  27. package/context/contracts/delivery-contract.md +1 -0
  28. package/context/contracts/review-contract.md +1 -0
  29. package/context/contracts/verification-contract.md +2 -0
  30. package/context/gate-awareness.md +39 -0
  31. package/context/scripts/hooks/stop-goal-fit.js +632 -70
  32. package/docs/adr/0001-flow-agents-consumes-flow.md +1 -1
  33. package/docs/adr/0002-flow-kits-as-extension-unit.md +1 -1
  34. package/docs/adr/0004-gates-expect-surface-claims.md +2 -0
  35. package/docs/adr/0005-kubernetes-inspired-resource-contracts.md +2 -0
  36. package/docs/adr/0007-skill-audit.md +1 -1
  37. package/docs/adr/0009-canonical-hook-core-kit-boundary.md +95 -0
  38. package/docs/adr/0010-workflow-trust-state-as-hachure-bundle.md +139 -0
  39. package/docs/adr/0011-mcp-posture.md +100 -0
  40. package/docs/adr/0012-agent-coordination-as-liveness-claims.md +119 -0
  41. package/docs/adr/0013-context-lifecycle.md +151 -0
  42. package/docs/adr/0014-core-vs-domain-kit-boundary.md +143 -0
  43. package/docs/adr/0015-flow-flow-agents-boundary-reconciliation.md +120 -0
  44. package/docs/adr/0016-three-hard-boundary-model.md +71 -0
  45. package/docs/adr/0017-anti-gaming-trust-security-model.md +155 -0
  46. package/docs/agent-system-guidebook.md +5 -12
  47. package/docs/context-map.md +4 -10
  48. package/docs/index.md +3 -2
  49. package/docs/integrations/framework-adapter.md +19 -6
  50. package/docs/integrations/index.md +2 -2
  51. package/docs/north-star.md +4 -4
  52. package/docs/operating-layers.md +3 -3
  53. package/docs/plans/adr-0010-phase2-gate-recompute.md +55 -0
  54. package/docs/repository-structure.md +2 -2
  55. package/docs/skills-map.md +1 -0
  56. package/docs/spec/runtime-hook-surface.md +62 -9
  57. package/docs/standards-register.md +3 -3
  58. package/docs/survey-utterance-check.md +1 -1
  59. package/docs/trust-anchor-adoption.md +197 -0
  60. package/docs/verifiable-trust.md +95 -0
  61. package/docs/veritas-integration.md +2 -2
  62. package/docs/workflow-usage-guide.md +69 -0
  63. package/evals/acceptance/DEMO-false-completion.md +144 -0
  64. package/evals/acceptance/demo-cast.sh +92 -0
  65. package/evals/acceptance/demo-false-completion.sh +72 -0
  66. package/evals/acceptance/demo-real-evidence.sh +104 -0
  67. package/evals/acceptance/demo.tape +29 -0
  68. package/evals/acceptance/prove-capture-teeth-declared.sh +335 -0
  69. package/evals/acceptance/prove-capture-teeth.sh +114 -0
  70. package/evals/acceptance/prove-teeth.sh +105 -0
  71. package/evals/ci/antigaming-suite.sh +54 -0
  72. package/evals/ci/run-baseline.sh +2 -0
  73. package/evals/fixtures/flow-kit-repository/invalid-missing-extension-asset/flows/review.flow.json +26 -0
  74. package/evals/fixtures/flow-kit-repository/invalid-missing-extension-asset/kit.json +20 -0
  75. package/evals/fixtures/flow-kit-repository/valid-unknown-extension/flows/review.flow.json +26 -0
  76. package/evals/fixtures/flow-kit-repository/valid-unknown-extension/kit.json +18 -0
  77. package/evals/integration/test_builder_step_producers.sh +379 -0
  78. package/evals/integration/test_bundle_install.sh +35 -71
  79. package/evals/integration/test_bundle_lifecycle.sh +39 -2
  80. package/evals/integration/test_captured_fail_reconciliation.sh +820 -0
  81. package/evals/integration/test_checkpoint_signing.sh +489 -0
  82. package/evals/integration/test_claim_lookup.sh +352 -0
  83. package/evals/integration/test_command_log_integrity.sh +275 -0
  84. package/evals/integration/test_context_map.sh +0 -2
  85. package/evals/integration/test_dual_emit_flow_step.sh +278 -0
  86. package/evals/integration/test_enforcer_expects_driven.sh +281 -0
  87. package/evals/integration/test_evidence_capture_hook.sh +185 -0
  88. package/evals/integration/test_flow_kit_repository.sh +2 -0
  89. package/evals/integration/test_flowdef_session_activation.sh +273 -0
  90. package/evals/integration/test_flowdef_session_history_preservation.sh +250 -0
  91. package/evals/integration/test_gate_bypass_chain.sh +448 -0
  92. package/evals/integration/test_gate_lockdown.sh +1137 -0
  93. package/evals/integration/test_gate_review_inquiry_records.sh +399 -0
  94. package/evals/integration/test_goal_fit_escape_hatch.sh +73 -0
  95. package/evals/integration/test_goal_fit_hook.sh +69 -4
  96. package/evals/integration/test_goal_fit_rederive.sh +263 -0
  97. package/evals/integration/test_install_merge.sh +1176 -0
  98. package/evals/integration/test_mint_attestation.sh +373 -0
  99. package/evals/integration/test_phase_map_and_gate_claim.sh +365 -0
  100. package/evals/integration/test_publish_delivery.sh +269 -0
  101. package/evals/integration/test_reconcile_soundness.sh +528 -0
  102. package/evals/integration/test_resolvefirststep_security.sh +208 -0
  103. package/evals/integration/test_session_resume_roundtrip.sh +286 -0
  104. package/evals/integration/test_trust_checkpoint.sh +325 -0
  105. package/evals/integration/test_trust_reconcile.sh +293 -0
  106. package/evals/integration/test_verify_cli.sh +208 -0
  107. package/evals/integration/test_workflow_sidecar_writer.sh +549 -34
  108. package/evals/lib/node.sh +0 -6
  109. package/evals/run.sh +45 -0
  110. package/evals/static/test_workflow_skills.sh +6 -13
  111. package/install.sh +0 -7
  112. package/integrations/strands-ts/README.md +25 -15
  113. package/integrations/veritas/flow-agents.adapter.json +1 -2
  114. package/kits/builder/flows/build.flow.json +59 -12
  115. package/kits/builder/kit.json +85 -15
  116. package/kits/builder/skills/continue-work/SKILL.md +116 -0
  117. package/kits/builder/skills/deliver/SKILL.md +36 -6
  118. package/kits/builder/skills/design-probe/SKILL.md +28 -0
  119. package/kits/builder/skills/execute-plan/SKILL.md +9 -1
  120. package/kits/builder/skills/gate-review/SKILL.md +234 -0
  121. package/kits/builder/skills/learning-review/SKILL.md +30 -0
  122. package/kits/builder/skills/pickup-probe/SKILL.md +29 -0
  123. package/kits/builder/skills/plan-work/SKILL.md +13 -1
  124. package/kits/builder/skills/pull-work/SKILL.md +19 -0
  125. package/kits/knowledge/adapters/default-store/index.js +38 -0
  126. package/kits/knowledge/adapters/flow-runner/index.js +1620 -0
  127. package/kits/knowledge/adapters/obsidian-store/index.js +36 -6
  128. package/kits/knowledge/docs/store-contract.md +314 -0
  129. package/kits/knowledge/evals/audit-freshness/suite.test.js +368 -0
  130. package/kits/knowledge/evals/canonicalize-category/suite.test.js +383 -0
  131. package/kits/knowledge/evals/contract-suite/suite.test.js +111 -0
  132. package/kits/knowledge/evals/detect-contradictions/suite.test.js +324 -0
  133. package/kits/knowledge/evals/entities/suite.test.js +40 -0
  134. package/kits/knowledge/evals/glossary-sync/suite.test.js +416 -0
  135. package/kits/knowledge/evals/hygiene-review/suite.test.js +396 -0
  136. package/kits/knowledge/evals/retirement/suite.test.js +145 -0
  137. package/kits/knowledge/flows/audit-freshness.flow.json +44 -0
  138. package/kits/knowledge/flows/canonicalize-category.flow.json +44 -0
  139. package/kits/knowledge/flows/detect-contradictions.flow.json +44 -0
  140. package/kits/knowledge/flows/glossary-sync.flow.json +61 -0
  141. package/kits/knowledge/flows/hygiene-review.flow.json +43 -0
  142. package/kits/knowledge/kit.json +51 -1
  143. package/package.json +4 -4
  144. package/packaging/conformance/README.md +10 -2
  145. package/packaging/conformance/fixtures/evidence-capture--allow-records-command.json +29 -0
  146. package/packaging/conformance/fixtures/stop-goal-fit--block-bundle-disputed-claim.json +29 -0
  147. package/packaging/conformance/fixtures/stop-goal-fit--block-capture-contradicts-claimed-pass.json +30 -0
  148. package/packaging/conformance/fixtures/stop-goal-fit--block-mode.json +23 -0
  149. package/packaging/conformance/fixtures/stop-goal-fit--off-mode.json +24 -0
  150. package/packaging/conformance/fixtures/stop-goal-fit--warn-active-delivery.json +5 -2
  151. package/packaging/conformance/fixtures/stop-goal-fit--warn-no-bundle.json +23 -0
  152. package/packaging/conformance/fixtures/workflow-steering--reground-active-prompt.json +30 -0
  153. package/packaging/conformance/fixtures/workflow-steering--reground-session-start.json +30 -0
  154. package/packaging/conformance/run-conformance.js +1 -1
  155. package/scripts/README.md +2 -1
  156. package/scripts/build-universal-bundles.js +0 -1
  157. package/scripts/ci/mint-attestation.js +221 -0
  158. package/scripts/ci/trust-reconcile.js +545 -0
  159. package/scripts/hooks/config-protection.js +423 -1
  160. package/scripts/hooks/evidence-capture.js +348 -0
  161. package/scripts/hooks/lib/liveness-read.js +113 -0
  162. package/scripts/hooks/run-hook.js +6 -1
  163. package/scripts/hooks/stop-goal-fit.js +1471 -79
  164. package/scripts/hooks/workflow-steering.js +135 -5
  165. package/scripts/install-codex-home.sh +39 -0
  166. package/scripts/install-merge.js +330 -0
  167. package/src/cli/init.ts +218 -20
  168. package/src/cli/validate-workflow-artifacts.ts +18 -2
  169. package/src/cli/verify.ts +100 -0
  170. package/src/cli/workflow-sidecar.ts +2064 -77
  171. package/src/cli.ts +2 -3
  172. package/src/lib/flow-resolver.ts +284 -0
  173. package/src/tools/build-universal-bundles.ts +34 -21
  174. package/src/tools/generate-context-map.ts +3 -17
  175. package/src/tools/validate-source-tree.ts +44 -104
  176. package/build/src/tools/filter-installed-packs.d.ts +0 -2
  177. package/build/src/tools/filter-installed-packs.js +0 -135
  178. package/packaging/packs.json +0 -49
  179. package/scripts/filter-installed-packs.js +0 -2
  180. package/src/tools/filter-installed-packs.ts +0 -132
@@ -0,0 +1,263 @@
1
+ #!/usr/bin/env bash
2
+ # test_goal_fit_rederive.sh — Killer test for ADR 0010 Phase 2b re-derive-at-gate hardening.
3
+ #
4
+ # Proves that:
5
+ # 1. A TAMPERED trust.bundle (stored status "verified" but evidence re-derives to
6
+ # "disputed" because evidence[].passing === false) still BLOCKS (exit 2) and emits
7
+ # the "stored status does not match recompute (possible tampered bundle)" warning.
8
+ # 2. A LEGITIMATE bundle (stored "verified" AND evidence re-derives to "verified") is
9
+ # ALLOWED (no false-block).
10
+ # 3. The existing stored-status path still fires for a stored "disputed" claim (no
11
+ # regression from #133).
12
+ #
13
+ # Design: self-cleaning, deterministic (no model spend, no live commands).
14
+ # Usage: bash evals/integration/test_goal_fit_rederive.sh
15
+
16
+ set -uo pipefail
17
+
18
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
19
+ GATE="$ROOT/scripts/hooks/stop-goal-fit.js"
20
+
21
+ export FLOW_AGENTS_GOAL_FIT_MAX_BLOCKS=100000
22
+
23
+ TMP="$(mktemp -d)"
24
+ errors=0
25
+ _pass() { echo " ✓ $1"; }
26
+ _fail() { echo " ✗ $1"; errors=$((errors + 1)); }
27
+
28
+ cleanup() { rm -rf "$TMP"; }
29
+ trap cleanup EXIT
30
+
31
+ # ─── helper: seed a minimal delivered workflow artifact ───────────────────────
32
+ seed_repo() { # $1=dir $2=slug
33
+ local p="$1" slug="$2"
34
+ mkdir -p "$p/.flow-agents/$slug"
35
+ printf '# Repo\n' > "$p/AGENTS.md"
36
+ printf '%s' "{\"schema_version\":\"1.0\",\"task_slug\":\"$slug\",\"status\":\"delivered\",\"phase\":\"done\",\"updated_at\":\"2026-06-23T00:00:00Z\",\"next_action\":{\"status\":\"done\",\"summary\":\"done\"}}" \
37
+ > "$p/.flow-agents/$slug/state.json"
38
+ cat > "$p/.flow-agents/$slug/$slug--deliver.md" << MD
39
+ # $slug
40
+
41
+ branch: main
42
+ status: delivered
43
+ type: deliver
44
+
45
+ ## Definition Of Done
46
+ - [x] tests pass
47
+
48
+ ## Goal Fit Gate
49
+ - [x] acceptance verified
50
+
51
+ ### Verdict: PASS
52
+ MD
53
+ }
54
+
55
+ # ─── Test 1: TAMPERED bundle — stored "verified" but evidence re-derives to disputed ─
56
+ # A trust.bundle where the agent wrote claim.status="verified" to bypass the gate,
57
+ # but the evidence array has passing:false (a failing command result folded in by
58
+ # buildTrustBundle). Surface's deriveClaimStatus must re-derive "disputed" from
59
+ # that evidence, and the gate must block with a tamper warning.
60
+ echo "Test 1: tampered bundle (stored verified, evidence→disputed) must BLOCK"
61
+
62
+ TAMPER_DIR="$TMP/tamper"
63
+ seed_repo "$TAMPER_DIR" "tampered"
64
+
65
+ # Build a trust.bundle:
66
+ # - claim.status = "verified" (stored, tampered to look safe)
67
+ # - evidence[passing=false] (real command failed, fold in by sidecar)
68
+ # Surface.deriveClaimStatus will see passing:false evidence and return "disputed".
69
+ python3 - "$TAMPER_DIR/.flow-agents/tampered/trust.bundle" << 'PY'
70
+ import json, sys
71
+ bundle = {
72
+ "schemaVersion": 3,
73
+ "source": "flow-agents/workflow-sidecar",
74
+ "claims": [{
75
+ "id": "c1",
76
+ "subjectId": "tampered/unit-tests",
77
+ "subjectType": "workflow-check",
78
+ "claimType": "workflow.check.command",
79
+ "fieldOrBehavior": "unit tests",
80
+ "value": "pass",
81
+ "impactLevel": "high",
82
+ "status": "verified", # tampered: agent edited this from "disputed" → "verified"
83
+ "createdAt": "2026-06-23T00:00:00Z",
84
+ "updatedAt": "2026-06-23T00:00:00Z"
85
+ }],
86
+ "evidence": [{
87
+ "id": "ev1",
88
+ "claimId": "c1",
89
+ "evidenceType": "test_output",
90
+ "method": "validation",
91
+ "sourceRef": "command-log.jsonl",
92
+ "excerptOrSummary": "npm test failed with exit 1",
93
+ "observedAt": "2026-06-23T00:00:00Z",
94
+ "collectedBy": "harness",
95
+ "passing": False, # the actual command FAILED — surface sees this
96
+ "blocking": True
97
+ }],
98
+ "policies": [],
99
+ "events": [{
100
+ "id": "evt1",
101
+ "claimId": "c1",
102
+ "status": "verified", # the event says verified (tampered)
103
+ "actor": "agent",
104
+ "method": "workflow-check",
105
+ "evidenceIds": ["ev1"],
106
+ "createdAt": "2026-06-23T00:00:00Z"
107
+ }]
108
+ }
109
+ json.dump(bundle, open(sys.argv[1], 'w'))
110
+ PY
111
+
112
+ # Run the gate in block mode.
113
+ set +e
114
+ result_out="$(FLOW_AGENTS_GOAL_FIT_MODE=block FLOW_AGENTS_GOAL_FIT_BACKSTOP=skip \
115
+ node "$GATE" 2>&1 <<< "{\"hook_event_name\":\"Stop\",\"cwd\":\"$TAMPER_DIR\"}")"
116
+ result_exit="$?"
117
+ set -e
118
+
119
+ if [ "$result_exit" -eq 2 ]; then
120
+ _pass "tampered bundle blocks (exit 2)"
121
+ else
122
+ _fail "tampered bundle did NOT block: exit=$result_exit output=$result_out"
123
+ fi
124
+
125
+ if echo "$result_out" | grep -qE "stored status.*does not match recompute|possible tampered bundle"; then
126
+ _pass "tampered bundle emits tamper warning"
127
+ else
128
+ _fail "tampered bundle missing tamper warning: $result_out"
129
+ fi
130
+
131
+ if echo "$result_out" | grep -q "caught false-completion"; then
132
+ _pass "tampered bundle emits caught false-completion"
133
+ else
134
+ _fail "tampered bundle missing caught false-completion: $result_out"
135
+ fi
136
+
137
+ # ─── Test 2: LEGITIMATE bundle — stored "verified" AND evidence re-derives to "verified" ─
138
+ # A bundle where both the stored status and the re-derived status agree on "verified"
139
+ # (a passing:true evidence + a "verified" event). Must ALLOW (exit 0 in warn mode).
140
+ echo ""
141
+ echo "Test 2: legitimate bundle (stored verified, evidence→verified) must ALLOW"
142
+
143
+ LEGIT_DIR="$TMP/legit"
144
+ seed_repo "$LEGIT_DIR" "legit"
145
+
146
+ python3 - "$LEGIT_DIR/.flow-agents/legit/trust.bundle" << 'PY'
147
+ import json, sys
148
+ bundle = {
149
+ "schemaVersion": 3,
150
+ "source": "flow-agents/workflow-sidecar",
151
+ "claims": [{
152
+ "id": "c2",
153
+ "subjectId": "legit/unit-tests",
154
+ "subjectType": "workflow-check",
155
+ "claimType": "workflow.check.command",
156
+ "fieldOrBehavior": "unit tests",
157
+ "value": "pass",
158
+ "impactLevel": "high",
159
+ "status": "verified",
160
+ "createdAt": "2026-06-23T00:00:00Z",
161
+ "updatedAt": "2026-06-23T00:00:00Z"
162
+ }],
163
+ "evidence": [{
164
+ "id": "ev2",
165
+ "claimId": "c2",
166
+ "evidenceType": "test_output",
167
+ "method": "validation",
168
+ "sourceRef": "command-log.jsonl",
169
+ "excerptOrSummary": "npm test passed",
170
+ "observedAt": "2026-06-23T00:00:00Z",
171
+ "collectedBy": "harness",
172
+ "passing": True, # command genuinely passed
173
+ "blocking": False
174
+ }],
175
+ "policies": [],
176
+ "events": [{
177
+ "id": "evt2",
178
+ "claimId": "c2",
179
+ "status": "verified",
180
+ "actor": "agent",
181
+ "method": "workflow-check",
182
+ "evidenceIds": ["ev2"],
183
+ "createdAt": "2026-06-23T00:00:00Z"
184
+ }]
185
+ }
186
+ json.dump(bundle, open(sys.argv[1], 'w'))
187
+ PY
188
+
189
+ set +e
190
+ legit_out="$(FLOW_AGENTS_GOAL_FIT_MODE=block FLOW_AGENTS_GOAL_FIT_BACKSTOP=skip \
191
+ node "$GATE" 2>&1 <<< "{\"hook_event_name\":\"Stop\",\"cwd\":\"$LEGIT_DIR\"}")"
192
+ legit_exit="$?"
193
+ set -e
194
+
195
+ if [ "$legit_exit" -ne 2 ]; then
196
+ _pass "legitimate bundle not blocked (exit $legit_exit)"
197
+ else
198
+ _fail "legitimate bundle false-blocked (exit 2): $legit_out"
199
+ fi
200
+
201
+ if echo "$legit_out" | grep -q "caught false-completion"; then
202
+ _fail "legitimate bundle incorrectly emits false-completion: $legit_out"
203
+ else
204
+ _pass "legitimate bundle does not emit false-completion"
205
+ fi
206
+
207
+ # ─── Test 3: existing stored-disputed path still fires (no regression from #133) ──
208
+ echo ""
209
+ echo "Test 3: stored-disputed bundle must still BLOCK (no regression from #133)"
210
+
211
+ STORED_DIR="$TMP/stored"
212
+ seed_repo "$STORED_DIR" "stored"
213
+
214
+ python3 - "$STORED_DIR/.flow-agents/stored/trust.bundle" << 'PY'
215
+ import json, sys
216
+ bundle = {
217
+ "schemaVersion": 3,
218
+ "source": "flow-agents/workflow-sidecar",
219
+ "claims": [{
220
+ "id": "c3",
221
+ "subjectId": "stored/unit-tests",
222
+ "subjectType": "workflow-check",
223
+ "claimType": "workflow.check.command",
224
+ "fieldOrBehavior": "unit tests",
225
+ "value": "fail",
226
+ "impactLevel": "high",
227
+ "status": "disputed", # stored as disputed (not tampered — correctly flagged)
228
+ "createdAt": "2026-06-23T00:00:00Z",
229
+ "updatedAt": "2026-06-23T00:00:00Z"
230
+ }],
231
+ "evidence": [],
232
+ "policies": [],
233
+ "events": []
234
+ }
235
+ json.dump(bundle, open(sys.argv[1], 'w'))
236
+ PY
237
+
238
+ set +e
239
+ stored_out="$(FLOW_AGENTS_GOAL_FIT_MODE=block FLOW_AGENTS_GOAL_FIT_BACKSTOP=skip \
240
+ node "$GATE" 2>&1 <<< "{\"hook_event_name\":\"Stop\",\"cwd\":\"$STORED_DIR\"}")"
241
+ stored_exit="$?"
242
+ set -e
243
+
244
+ if [ "$stored_exit" -eq 2 ]; then
245
+ _pass "stored-disputed bundle blocks (exit 2)"
246
+ else
247
+ _fail "stored-disputed bundle did NOT block (exit $stored_exit): $stored_out"
248
+ fi
249
+
250
+ if echo "$stored_out" | grep -q "caught false-completion"; then
251
+ _pass "stored-disputed bundle emits caught false-completion"
252
+ else
253
+ _fail "stored-disputed bundle missing caught false-completion: $stored_out"
254
+ fi
255
+
256
+ # ─── Summary ─────────────────────────────────────────────────────────────────
257
+ echo ""
258
+ if [ "$errors" -eq 0 ]; then
259
+ echo "Re-derive-at-gate hardening tests passed."
260
+ exit 0
261
+ fi
262
+ echo "Re-derive-at-gate hardening tests FAILED: $errors issue(s)."
263
+ exit 1