@kontourai/flow-agents 1.4.0 → 2.0.1

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 (184) 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/runtime-compat.yml +1 -1
  8. package/.github/workflows/trust-reconcile.yml +113 -0
  9. package/AGENTS.md +13 -0
  10. package/CHANGELOG.md +103 -0
  11. package/CONTRIBUTING.md +4 -4
  12. package/README.md +1 -0
  13. package/agents/tool-planner.json +1 -1
  14. package/build/src/cli/init.js +242 -20
  15. package/build/src/cli/validate-workflow-artifacts.js +19 -2
  16. package/build/src/cli/verify.d.ts +1 -0
  17. package/build/src/cli/verify.js +90 -0
  18. package/build/src/cli/workflow-sidecar.d.ts +316 -8
  19. package/build/src/cli/workflow-sidecar.js +1996 -91
  20. package/build/src/cli.js +2 -3
  21. package/build/src/lib/flow-resolver.d.ts +111 -0
  22. package/build/src/lib/flow-resolver.js +308 -0
  23. package/build/src/tools/build-universal-bundles.js +34 -22
  24. package/build/src/tools/generate-context-map.js +3 -16
  25. package/build/src/tools/validate-source-tree.d.ts +1 -1
  26. package/build/src/tools/validate-source-tree.js +42 -162
  27. package/context/contracts/artifact-contract.md +10 -0
  28. package/context/contracts/delivery-contract.md +1 -0
  29. package/context/contracts/review-contract.md +1 -0
  30. package/context/contracts/verification-contract.md +2 -0
  31. package/context/gate-awareness.md +39 -0
  32. package/context/scripts/hooks/stop-goal-fit.js +632 -70
  33. package/docs/adr/0001-flow-agents-consumes-flow.md +1 -1
  34. package/docs/adr/0002-flow-kits-as-extension-unit.md +1 -1
  35. package/docs/adr/0004-gates-expect-surface-claims.md +2 -0
  36. package/docs/adr/0005-kubernetes-inspired-resource-contracts.md +2 -0
  37. package/docs/adr/0007-skill-audit.md +1 -1
  38. package/docs/adr/0009-canonical-hook-core-kit-boundary.md +95 -0
  39. package/docs/adr/0010-workflow-trust-state-as-hachure-bundle.md +139 -0
  40. package/docs/adr/0011-mcp-posture.md +100 -0
  41. package/docs/adr/0012-agent-coordination-as-liveness-claims.md +119 -0
  42. package/docs/adr/0013-context-lifecycle.md +151 -0
  43. package/docs/adr/0014-core-vs-domain-kit-boundary.md +143 -0
  44. package/docs/adr/0015-flow-flow-agents-boundary-reconciliation.md +120 -0
  45. package/docs/adr/0016-three-hard-boundary-model.md +71 -0
  46. package/docs/adr/0017-anti-gaming-trust-security-model.md +155 -0
  47. package/docs/agent-system-guidebook.md +5 -12
  48. package/docs/context-map.md +4 -10
  49. package/docs/index.md +3 -2
  50. package/docs/integrations/framework-adapter.md +19 -6
  51. package/docs/integrations/index.md +2 -2
  52. package/docs/north-star.md +4 -4
  53. package/docs/operating-layers.md +3 -3
  54. package/docs/plans/adr-0010-phase2-gate-recompute.md +55 -0
  55. package/docs/repository-structure.md +2 -2
  56. package/docs/skills-map.md +1 -0
  57. package/docs/spec/runtime-hook-surface.md +62 -9
  58. package/docs/standards-register.md +3 -3
  59. package/docs/survey-utterance-check.md +1 -1
  60. package/docs/trust-anchor-adoption.md +197 -0
  61. package/docs/verifiable-trust.md +95 -0
  62. package/docs/veritas-integration.md +2 -2
  63. package/docs/workflow-usage-guide.md +69 -0
  64. package/evals/acceptance/DEMO-false-completion.md +144 -0
  65. package/evals/acceptance/demo-cast.sh +92 -0
  66. package/evals/acceptance/demo-false-completion.sh +72 -0
  67. package/evals/acceptance/demo-real-evidence.sh +104 -0
  68. package/evals/acceptance/demo.tape +29 -0
  69. package/evals/acceptance/prove-capture-teeth-declared.sh +335 -0
  70. package/evals/acceptance/prove-capture-teeth.sh +114 -0
  71. package/evals/acceptance/prove-teeth.sh +105 -0
  72. package/evals/ci/antigaming-suite.sh +55 -0
  73. package/evals/ci/run-baseline.sh +2 -0
  74. package/evals/fixtures/flow-kit-repository/invalid-missing-extension-asset/flows/review.flow.json +26 -0
  75. package/evals/fixtures/flow-kit-repository/invalid-missing-extension-asset/kit.json +20 -0
  76. package/evals/fixtures/flow-kit-repository/valid-unknown-extension/flows/review.flow.json +26 -0
  77. package/evals/fixtures/flow-kit-repository/valid-unknown-extension/kit.json +18 -0
  78. package/evals/integration/test_builder_step_producers.sh +379 -0
  79. package/evals/integration/test_bundle_install.sh +35 -71
  80. package/evals/integration/test_bundle_lifecycle.sh +39 -2
  81. package/evals/integration/test_captured_fail_reconciliation.sh +820 -0
  82. package/evals/integration/test_checkpoint_signing.sh +489 -0
  83. package/evals/integration/test_claim_lookup.sh +352 -0
  84. package/evals/integration/test_command_log_fork_classification.sh +134 -0
  85. package/evals/integration/test_command_log_integrity.sh +275 -0
  86. package/evals/integration/test_context_map.sh +0 -2
  87. package/evals/integration/test_dual_emit_flow_step.sh +278 -0
  88. package/evals/integration/test_enforcer_expects_driven.sh +281 -0
  89. package/evals/integration/test_evidence_capture_hook.sh +185 -0
  90. package/evals/integration/test_flow_kit_repository.sh +2 -0
  91. package/evals/integration/test_flowdef_session_activation.sh +273 -0
  92. package/evals/integration/test_flowdef_session_history_preservation.sh +250 -0
  93. package/evals/integration/test_gate_bypass_chain.sh +448 -0
  94. package/evals/integration/test_gate_lockdown.sh +1137 -0
  95. package/evals/integration/test_gate_review_inquiry_records.sh +399 -0
  96. package/evals/integration/test_goal_fit_escape_hatch.sh +73 -0
  97. package/evals/integration/test_goal_fit_hook.sh +69 -4
  98. package/evals/integration/test_goal_fit_rederive.sh +263 -0
  99. package/evals/integration/test_install_merge.sh +1176 -0
  100. package/evals/integration/test_kit_identity_trust.sh +393 -0
  101. package/evals/integration/test_mint_attestation.sh +373 -0
  102. package/evals/integration/test_phase_map_and_gate_claim.sh +365 -0
  103. package/evals/integration/test_publish_delivery.sh +269 -0
  104. package/evals/integration/test_reconcile_soundness.sh +528 -0
  105. package/evals/integration/test_resolvefirststep_security.sh +208 -0
  106. package/evals/integration/test_session_resume_roundtrip.sh +286 -0
  107. package/evals/integration/test_trust_checkpoint.sh +325 -0
  108. package/evals/integration/test_trust_reconcile.sh +293 -0
  109. package/evals/integration/test_verify_cli.sh +208 -0
  110. package/evals/integration/test_workflow_sidecar_writer.sh +549 -34
  111. package/evals/lib/node.sh +0 -6
  112. package/evals/run.sh +47 -0
  113. package/evals/static/test_workflow_skills.sh +6 -13
  114. package/install.sh +0 -7
  115. package/integrations/strands-ts/README.md +25 -15
  116. package/integrations/veritas/flow-agents.adapter.json +1 -2
  117. package/kits/builder/flows/build.flow.json +59 -12
  118. package/kits/builder/kit.json +85 -15
  119. package/kits/builder/skills/continue-work/SKILL.md +116 -0
  120. package/kits/builder/skills/deliver/SKILL.md +36 -6
  121. package/kits/builder/skills/design-probe/SKILL.md +28 -0
  122. package/kits/builder/skills/execute-plan/SKILL.md +9 -1
  123. package/kits/builder/skills/gate-review/SKILL.md +234 -0
  124. package/kits/builder/skills/learning-review/SKILL.md +30 -0
  125. package/kits/builder/skills/pickup-probe/SKILL.md +29 -0
  126. package/kits/builder/skills/plan-work/SKILL.md +13 -1
  127. package/kits/builder/skills/pull-work/SKILL.md +19 -0
  128. package/kits/knowledge/adapters/default-store/index.js +38 -0
  129. package/kits/knowledge/adapters/flow-runner/index.js +1620 -0
  130. package/kits/knowledge/adapters/obsidian-store/index.js +36 -6
  131. package/kits/knowledge/docs/store-contract.md +314 -0
  132. package/kits/knowledge/evals/audit-freshness/suite.test.js +368 -0
  133. package/kits/knowledge/evals/canonicalize-category/suite.test.js +383 -0
  134. package/kits/knowledge/evals/contract-suite/suite.test.js +111 -0
  135. package/kits/knowledge/evals/detect-contradictions/suite.test.js +324 -0
  136. package/kits/knowledge/evals/entities/suite.test.js +40 -0
  137. package/kits/knowledge/evals/glossary-sync/suite.test.js +416 -0
  138. package/kits/knowledge/evals/hygiene-review/suite.test.js +396 -0
  139. package/kits/knowledge/evals/retirement/suite.test.js +145 -0
  140. package/kits/knowledge/flows/audit-freshness.flow.json +44 -0
  141. package/kits/knowledge/flows/canonicalize-category.flow.json +44 -0
  142. package/kits/knowledge/flows/detect-contradictions.flow.json +44 -0
  143. package/kits/knowledge/flows/glossary-sync.flow.json +61 -0
  144. package/kits/knowledge/flows/hygiene-review.flow.json +43 -0
  145. package/kits/knowledge/kit.json +51 -1
  146. package/package.json +6 -6
  147. package/packaging/conformance/README.md +10 -2
  148. package/packaging/conformance/fixtures/evidence-capture--allow-records-command.json +29 -0
  149. package/packaging/conformance/fixtures/stop-goal-fit--block-bundle-disputed-claim.json +29 -0
  150. package/packaging/conformance/fixtures/stop-goal-fit--block-capture-contradicts-claimed-pass.json +30 -0
  151. package/packaging/conformance/fixtures/stop-goal-fit--block-mode.json +23 -0
  152. package/packaging/conformance/fixtures/stop-goal-fit--off-mode.json +24 -0
  153. package/packaging/conformance/fixtures/stop-goal-fit--warn-active-delivery.json +5 -2
  154. package/packaging/conformance/fixtures/stop-goal-fit--warn-no-bundle.json +23 -0
  155. package/packaging/conformance/fixtures/workflow-steering--reground-active-prompt.json +30 -0
  156. package/packaging/conformance/fixtures/workflow-steering--reground-session-start.json +30 -0
  157. package/packaging/conformance/run-conformance.js +1 -1
  158. package/scripts/README.md +2 -1
  159. package/scripts/build-universal-bundles.js +0 -1
  160. package/scripts/ci/mint-attestation.js +221 -0
  161. package/scripts/ci/trust-reconcile.js +545 -0
  162. package/scripts/hooks/config-protection.js +423 -1
  163. package/scripts/hooks/evidence-capture.js +348 -0
  164. package/scripts/hooks/lib/liveness-read.js +113 -0
  165. package/scripts/hooks/run-hook.js +6 -1
  166. package/scripts/hooks/stop-goal-fit.js +1524 -79
  167. package/scripts/hooks/workflow-steering.js +135 -5
  168. package/scripts/install-codex-home.sh +39 -0
  169. package/scripts/install-merge.js +330 -0
  170. package/scripts/repair-command-log.js +115 -0
  171. package/src/cli/init.ts +218 -20
  172. package/src/cli/validate-workflow-artifacts.ts +18 -2
  173. package/src/cli/verify.ts +100 -0
  174. package/src/cli/workflow-sidecar.ts +2127 -84
  175. package/src/cli.ts +2 -3
  176. package/src/lib/flow-resolver.ts +369 -0
  177. package/src/tools/build-universal-bundles.ts +34 -21
  178. package/src/tools/generate-context-map.ts +3 -17
  179. package/src/tools/validate-source-tree.ts +44 -104
  180. package/build/src/tools/filter-installed-packs.d.ts +0 -2
  181. package/build/src/tools/filter-installed-packs.js +0 -135
  182. package/packaging/packs.json +0 -49
  183. package/scripts/filter-installed-packs.js +0 -2
  184. package/src/tools/filter-installed-packs.ts +0 -132
@@ -0,0 +1,393 @@
1
+ #!/usr/bin/env bash
2
+ # test_kit_identity_trust.sh — Regression eval for kit identity end-to-end in the trust chain.
3
+ #
4
+ # Proves Fix 1 and Fix 2 from the kit-identity task:
5
+ #
6
+ # Fix 1 (surfaceCheckFromArtifact reads kit from bundle, never hardcodes "builder"):
7
+ # 1a. KNOWLEDGE-TYPED bundle → kitIdentityFromBundle derives kitId="knowledge", subject="knowledge-kit"
8
+ # 1b. BUILDER-TYPED bundle → kitIdentityFromBundle derives kitId="builder", subject="builder-kit"
9
+ # 1c. WORKFLOW-ONLY bundle (no kit-typed claim, no current.json) → kitId="unknown", subject="unknown-kit"
10
+ # 1d. record-evidence --surface-trust-json <knowledge-fixture> completes without crash
11
+ #
12
+ # Fix 2 (route-back guard is FlowDefinition-driven, not hardcoded to builder.build):
13
+ # 2a. builder.build: verification→execution still enforced (identical behavior preserved)
14
+ # 2b. Custom non-builder flow WITH route_back_policy: verification→execution ENFORCED
15
+ # 2c. Custom flow WITHOUT route_back_policy: verification→execution NOT ENFORCED
16
+ #
17
+ # Deterministic, no model spend, self-cleaning.
18
+ # Usage: bash evals/integration/test_kit_identity_trust.sh
19
+
20
+ set -uo pipefail
21
+
22
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
23
+ source "$ROOT/evals/lib/node.sh"
24
+
25
+ TMP="$(mktemp -d)"
26
+ errors=0
27
+
28
+ _pass() { echo " ✓ $1"; }
29
+ _fail() { echo " ✗ $1"; errors=$((errors + 1)); }
30
+
31
+ cleanup() { rm -rf "$TMP"; }
32
+ trap cleanup EXIT
33
+
34
+ SIDECAR_JS="${ROOT}/build/src/cli/workflow-sidecar.js"
35
+ SIDECAR_BUNDLE_WRITER="workflow-sidecar"
36
+
37
+ echo ""
38
+ echo "=== Fix 1: kitIdentityFromBundle reads kit from bundle claims (not hardcoded 'builder') ==="
39
+
40
+ # ─── Write fixture bundle files (note: argv[2] = file path since argv[1] = "-" for stdin) ─────────
41
+
42
+ node - "$TMP/knowledge.bundle" << 'NODE'
43
+ const fs = require('fs');
44
+ // argv[0]=node, argv[1]="-", argv[2]=file path
45
+ const bundlePath = process.argv[2];
46
+ const bundle = {
47
+ schemaVersion: 3, source: "test-fixture",
48
+ claims: [{
49
+ id: "c-knowledge-1", claimType: "knowledge.verify.tests",
50
+ subjectType: "flow-step", subjectId: "test-slug/knowledge-ev",
51
+ surface: "flow-agents.workflow", fieldOrBehavior: "knowledge verification",
52
+ value: "pass", status: "verified",
53
+ createdAt: "2026-06-27T00:00:00Z", updatedAt: "2026-06-27T00:00:00Z",
54
+ impactLevel: "high", verificationPolicyId: "policy:knowledge.verify.tests"
55
+ }],
56
+ evidence: [], policies: [], events: []
57
+ };
58
+ fs.writeFileSync(bundlePath, JSON.stringify(bundle, null, 2));
59
+ NODE
60
+
61
+ node - "$TMP/builder.bundle" << 'NODE'
62
+ const fs = require('fs');
63
+ const bundlePath = process.argv[2];
64
+ const bundle = {
65
+ schemaVersion: 3, source: "test-fixture",
66
+ claims: [{
67
+ id: "c-builder-1", claimType: "builder.verify.tests",
68
+ subjectType: "flow-step", subjectId: "test-slug/builder-ev",
69
+ surface: "flow-agents.workflow", fieldOrBehavior: "builder verification",
70
+ value: "pass", status: "verified",
71
+ createdAt: "2026-06-27T00:00:00Z", updatedAt: "2026-06-27T00:00:00Z",
72
+ impactLevel: "high", verificationPolicyId: "policy:builder.verify.tests"
73
+ }],
74
+ evidence: [], policies: [], events: []
75
+ };
76
+ fs.writeFileSync(bundlePath, JSON.stringify(bundle, null, 2));
77
+ NODE
78
+
79
+ node - "$TMP/workflow-only.bundle" << 'NODE'
80
+ const fs = require('fs');
81
+ const bundlePath = process.argv[2];
82
+ const bundle = {
83
+ schemaVersion: 3, source: "test-fixture",
84
+ claims: [{
85
+ id: "c-wf-1", claimType: "workflow.check.build",
86
+ subjectType: "workflow-check", subjectId: "test-slug/build",
87
+ surface: "flow-agents.workflow", fieldOrBehavior: "build check",
88
+ value: "pass", status: "verified",
89
+ createdAt: "2026-06-27T00:00:00Z", updatedAt: "2026-06-27T00:00:00Z",
90
+ impactLevel: "high", verificationPolicyId: "policy:workflow.check.build"
91
+ }],
92
+ evidence: [], policies: [], events: []
93
+ };
94
+ fs.writeFileSync(bundlePath, JSON.stringify(bundle, null, 2));
95
+ NODE
96
+
97
+ echo ""
98
+ echo "=== 1a. KNOWLEDGE-TYPED bundle → kitIdentityFromBundle derives knowledge kit ==="
99
+ KNOWLEDGE_BUNDLE="$TMP/knowledge.bundle"
100
+ SIDECAR_JS_PATH="$SIDECAR_JS"
101
+ node --input-type=module << JSEOF
102
+ import { kitIdentityFromBundle } from '${SIDECAR_JS_PATH}';
103
+ import { readFileSync } from 'node:fs';
104
+ const raw = JSON.parse(readFileSync('${KNOWLEDGE_BUNDLE}', 'utf8'));
105
+ const result = kitIdentityFromBundle(raw, '${KNOWLEDGE_BUNDLE}');
106
+ if (result.kitId !== 'knowledge') throw new Error('Expected kitId=knowledge, got: ' + result.kitId);
107
+ if (result.subject !== 'knowledge-kit') throw new Error('Expected subject=knowledge-kit, got: ' + result.subject);
108
+ if (!result.claimType.startsWith('knowledge.')) throw new Error('Expected claimType to start with knowledge., got: ' + result.claimType);
109
+ if (result.claimType === 'knowledge.trust.bundle') throw new Error('Should use the specific claim type, not the generic fallback, got: ' + result.claimType);
110
+ JSEOF
111
+ if [ $? -eq 0 ]; then
112
+ _pass "KNOWLEDGE bundle: kitId=knowledge, subject=knowledge-kit, claimType=knowledge.verify.tests (not builder)"
113
+ else
114
+ _fail "KNOWLEDGE bundle: expected kitId=knowledge and subject=knowledge-kit, not builder hardcode"
115
+ fi
116
+
117
+ echo ""
118
+ echo "=== 1b. BUILDER-TYPED bundle → kitIdentityFromBundle derives builder kit ==="
119
+ BUILDER_BUNDLE="$TMP/builder.bundle"
120
+ node --input-type=module << JSEOF
121
+ import { kitIdentityFromBundle } from '${SIDECAR_JS_PATH}';
122
+ import { readFileSync } from 'node:fs';
123
+ const raw = JSON.parse(readFileSync('${BUILDER_BUNDLE}', 'utf8'));
124
+ const result = kitIdentityFromBundle(raw, '${BUILDER_BUNDLE}');
125
+ if (result.kitId !== 'builder') throw new Error('Expected kitId=builder, got: ' + result.kitId);
126
+ if (result.subject !== 'builder-kit') throw new Error('Expected subject=builder-kit, got: ' + result.subject);
127
+ if (!result.claimType.startsWith('builder.')) throw new Error('Expected claimType to start with builder., got: ' + result.claimType);
128
+ JSEOF
129
+ if [ $? -eq 0 ]; then
130
+ _pass "BUILDER bundle: kitId=builder, subject=builder-kit (correctly derived from claims, not hardcoded)"
131
+ else
132
+ _fail "BUILDER bundle: expected kitId=builder and subject=builder-kit"
133
+ fi
134
+
135
+ echo ""
136
+ echo "=== 1c. WORKFLOW-ONLY bundle (no kit-typed claim, no current.json) → unknown identity ==="
137
+ ISOLATED_DIR="$TMP/isolated-session"
138
+ mkdir -p "$ISOLATED_DIR"
139
+ cp "$TMP/workflow-only.bundle" "$ISOLATED_DIR/workflow-only.bundle"
140
+ WORKFLOW_BUNDLE="$ISOLATED_DIR/workflow-only.bundle"
141
+ node --input-type=module << JSEOF
142
+ import { kitIdentityFromBundle } from '${SIDECAR_JS_PATH}';
143
+ import { readFileSync } from 'node:fs';
144
+ const raw = JSON.parse(readFileSync('${WORKFLOW_BUNDLE}', 'utf8'));
145
+ const result = kitIdentityFromBundle(raw, '${WORKFLOW_BUNDLE}');
146
+ if (result.kitId !== 'unknown') throw new Error('Expected kitId=unknown (no kit-typed claim, no active flow), got: ' + result.kitId);
147
+ if (result.subject !== 'unknown-kit') throw new Error('Expected subject=unknown-kit, got: ' + result.subject);
148
+ if (result.claimType !== 'unknown.trust.bundle') throw new Error('Expected claimType=unknown.trust.bundle, got: ' + result.claimType);
149
+ JSEOF
150
+ if [ $? -eq 0 ]; then
151
+ _pass "WORKFLOW-ONLY bundle: kitId=unknown, subject=unknown-kit (never falls back to builder)"
152
+ else
153
+ _fail "WORKFLOW-ONLY bundle: expected kitId=unknown (no hardcoded builder fallback)"
154
+ fi
155
+
156
+ echo ""
157
+ echo "=== 1d. Full pipeline: record-evidence --surface-trust-json with knowledge fixture ==="
158
+ PIPELINE_AROOT="$TMP/pipeline-test/.flow-agents"
159
+ PIPELINE_SLUG="pipeline-kit-identity"
160
+ PIPELINE_DIR="$PIPELINE_AROOT/$PIPELINE_SLUG"
161
+ mkdir -p "$PIPELINE_AROOT"
162
+
163
+ flow_agents_node "$SIDECAR_BUNDLE_WRITER" ensure-session \
164
+ --artifact-root "$PIPELINE_AROOT" \
165
+ --task-slug "$PIPELINE_SLUG" \
166
+ --title "Pipeline kit identity test" \
167
+ --summary "Proves record-evidence processes knowledge bundle without crashing." \
168
+ --criterion "Kit identity preserved" \
169
+ --timestamp "2026-06-27T10:00:00Z" > "$TMP/pipeline-ensure.out" 2>&1
170
+
171
+ KNOWLEDGE_BUNDLE_PATH="$TMP/knowledge.bundle"
172
+ if flow_agents_node "$SIDECAR_BUNDLE_WRITER" record-evidence "$PIPELINE_DIR" \
173
+ --verdict not_verified \
174
+ --surface-trust-json "$KNOWLEDGE_BUNDLE_PATH" \
175
+ --timestamp "2026-06-27T10:01:00Z" > "$TMP/pipeline-evidence.out" 2>&1; then
176
+ if [[ -f "$PIPELINE_DIR/trust.bundle" ]]; then
177
+ _pass "record-evidence --surface-trust-json with knowledge bundle completes (pipeline proof: fix is in production code path)"
178
+ else
179
+ _fail "record-evidence --surface-trust-json with knowledge bundle did not write trust.bundle"
180
+ fi
181
+ else
182
+ _fail "record-evidence --surface-trust-json with knowledge bundle failed: $(cat "$TMP/pipeline-evidence.out")"
183
+ fi
184
+
185
+ echo ""
186
+ echo "=== Fix 2: FlowDefinition-driven route-back guard ==="
187
+
188
+ # ─── 2a. builder.build: verification→execution still enforced ─────────────────
189
+ echo ""
190
+ echo "=== 2a. builder.build route-back guard: still enforces verification→execution ==="
191
+ BUILDER_DIR="$TMP/fix2-builder/.flow-agents/builder-fix2"
192
+ mkdir -p "$TMP/fix2-builder/.flow-agents"
193
+
194
+ flow_agents_node "$SIDECAR_BUNDLE_WRITER" ensure-session \
195
+ --artifact-root "$TMP/fix2-builder/.flow-agents" \
196
+ --task-slug "builder-fix2" \
197
+ --title "Fix2 builder route-back test" \
198
+ --summary "Verify builder.build route-back still enforced." \
199
+ --timestamp "2026-06-27T10:00:00Z" > "$TMP/fix2-builder-ensure.out" 2>&1
200
+
201
+ flow_agents_node "$SIDECAR_BUNDLE_WRITER" advance-state "$BUILDER_DIR" \
202
+ --status verifying --phase verification \
203
+ --summary "Moving to verification." \
204
+ --flow-definition builder.build \
205
+ --timestamp "2026-06-27T10:01:00Z" > "$TMP/fix2-builder-verify.out" 2>&1
206
+
207
+ if flow_agents_node "$SIDECAR_BUNDLE_WRITER" advance-state "$BUILDER_DIR" \
208
+ --status in_progress --phase execution \
209
+ --summary "Route back without reason." \
210
+ --flow-definition builder.build \
211
+ --timestamp "2026-06-27T10:02:00Z" > "$TMP/fix2-builder-noReason.out" 2>&1; then
212
+ _fail "builder.build route-back should require --route-back-reason"
213
+ elif grep -q 'route_back_reason_required' "$TMP/fix2-builder-noReason.out"; then
214
+ _pass "builder.build: verification→execution requires --route-back-reason (identical behavior preserved)"
215
+ else
216
+ _fail "builder.build route-back lacked expected diagnostic (got: $(cat "$TMP/fix2-builder-noReason.out"))"
217
+ fi
218
+
219
+ if flow_agents_node "$SIDECAR_BUNDLE_WRITER" advance-state "$BUILDER_DIR" \
220
+ --status in_progress --phase execution \
221
+ --summary "Route back with reason." \
222
+ --flow-definition builder.build \
223
+ --route-back-reason implementation_defect \
224
+ --timestamp "2026-06-27T10:03:00Z" > "$TMP/fix2-builder-withReason.out" 2>&1; then
225
+ _pass "builder.build: verification→execution with reason succeeds (identical behavior preserved)"
226
+ else
227
+ _fail "builder.build route-back with reason should succeed (got: $(cat "$TMP/fix2-builder-withReason.out"))"
228
+ fi
229
+
230
+ # ─── 2b. Custom non-builder flow WITH route_back_policy: enforced ─────────────
231
+ echo ""
232
+ echo "=== 2b. Custom non-builder flow WITH route_back_policy: enforced ==="
233
+
234
+ CUSTOM_FLOWS_DIR="$TMP/custom-flows"
235
+ mkdir -p "$CUSTOM_FLOWS_DIR"
236
+
237
+ # Write acme.deliver flow with route_back_policy (using argv[2] correctly)
238
+ node - "$CUSTOM_FLOWS_DIR/acme.deliver.flow.json" << 'NODE'
239
+ const fs = require('fs');
240
+ const flowPath = process.argv[2];
241
+ const flow = {
242
+ id: "acme.deliver", version: "1.0",
243
+ phase_map: { execution: "execute", verification: "verify" },
244
+ steps: [{ id: "execute", next: "verify" }, { id: "verify", next: "done" }, { id: "done", next: null }],
245
+ gates: {
246
+ "execute-gate": {
247
+ step: "execute",
248
+ expects: [{ id: "execution-scope", kind: "trust.bundle", required: true,
249
+ bundle_claim: { claimType: "acme.execute.scope", subjectType: "change", accepted_statuses: ["trusted","accepted"] } }]
250
+ },
251
+ "verify-gate": {
252
+ step: "verify",
253
+ on_route_back: { implementation_defect: "execute", missing_evidence: "verify", default: "verify" },
254
+ route_back_policy: { max_attempts: 2, on_exceeded: "block" },
255
+ expects: [{ id: "verify-evidence", kind: "trust.bundle", required: true,
256
+ bundle_claim: { claimType: "acme.verify.tests", subjectType: "flow-step", accepted_statuses: ["trusted","accepted"] } }]
257
+ }
258
+ }
259
+ };
260
+ fs.writeFileSync(flowPath, JSON.stringify(flow, null, 2));
261
+ NODE
262
+
263
+ ACME_DIR="$TMP/fix2-acme/.flow-agents/acme-fix2"
264
+ mkdir -p "$TMP/fix2-acme/.flow-agents"
265
+
266
+ flow_agents_node "$SIDECAR_BUNDLE_WRITER" ensure-session \
267
+ --artifact-root "$TMP/fix2-acme/.flow-agents" \
268
+ --task-slug "acme-fix2" \
269
+ --title "Fix2 acme route-back test" \
270
+ --summary "Verify non-builder flow with route_back_policy is enforced." \
271
+ --timestamp "2026-06-27T10:00:00Z" > "$TMP/fix2-acme-ensure.out" 2>&1
272
+
273
+ # Set FLOW_AGENTS_FLOW_DEFS_DIR and export it for the duration of this block
274
+ export FLOW_AGENTS_FLOW_DEFS_DIR="$CUSTOM_FLOWS_DIR"
275
+
276
+ flow_agents_node "$SIDECAR_BUNDLE_WRITER" advance-state "$ACME_DIR" \
277
+ --status verifying --phase verification \
278
+ --summary "Moving acme to verification." \
279
+ --flow-definition acme.deliver \
280
+ --timestamp "2026-06-27T10:01:00Z" > "$TMP/fix2-acme-verify.out" 2>&1
281
+
282
+ if flow_agents_node "$SIDECAR_BUNDLE_WRITER" advance-state "$ACME_DIR" \
283
+ --status in_progress --phase execution \
284
+ --summary "Acme route back without reason." \
285
+ --flow-definition acme.deliver \
286
+ --timestamp "2026-06-27T10:02:00Z" > "$TMP/fix2-acme-noReason.out" 2>&1; then
287
+ _fail "acme.deliver route-back should require --route-back-reason when route_back_policy is declared"
288
+ elif grep -q 'route_back_reason_required' "$TMP/fix2-acme-noReason.out"; then
289
+ _pass "acme.deliver (non-builder): verification→execution requires reason when route_back_policy declared"
290
+ else
291
+ _fail "acme.deliver route-back lacked expected diagnostic (got: $(cat "$TMP/fix2-acme-noReason.out"))"
292
+ fi
293
+
294
+ # Do 2 successful route-backs
295
+ flow_agents_node "$SIDECAR_BUNDLE_WRITER" advance-state "$ACME_DIR" \
296
+ --status in_progress --phase execution \
297
+ --summary "Acme route back 1." --flow-definition acme.deliver \
298
+ --route-back-reason implementation_defect \
299
+ --timestamp "2026-06-27T10:03:00Z" > "$TMP/fix2-acme-rb1.out" 2>&1
300
+ flow_agents_node "$SIDECAR_BUNDLE_WRITER" advance-state "$ACME_DIR" \
301
+ --status verifying --phase verification \
302
+ --summary "Back to verify." --flow-definition acme.deliver \
303
+ --timestamp "2026-06-27T10:04:00Z" > "$TMP/fix2-acme-fwd1.out" 2>&1
304
+ flow_agents_node "$SIDECAR_BUNDLE_WRITER" advance-state "$ACME_DIR" \
305
+ --status in_progress --phase execution \
306
+ --summary "Acme route back 2." --flow-definition acme.deliver \
307
+ --route-back-reason implementation_defect \
308
+ --timestamp "2026-06-27T10:05:00Z" > "$TMP/fix2-acme-rb2.out" 2>&1
309
+ flow_agents_node "$SIDECAR_BUNDLE_WRITER" advance-state "$ACME_DIR" \
310
+ --status verifying --phase verification \
311
+ --summary "Back to verify again." --flow-definition acme.deliver \
312
+ --timestamp "2026-06-27T10:06:00Z" > "$TMP/fix2-acme-fwd2.out" 2>&1
313
+
314
+ # Third attempt should exceed max_attempts=2 (flow declares max 2, not hardcoded 3)
315
+ if flow_agents_node "$SIDECAR_BUNDLE_WRITER" advance-state "$ACME_DIR" \
316
+ --status in_progress --phase execution \
317
+ --summary "Acme exceeds route-back limit." --flow-definition acme.deliver \
318
+ --route-back-reason implementation_defect \
319
+ --timestamp "2026-06-27T10:07:00Z" > "$TMP/fix2-acme-exceeded.out" 2>&1; then
320
+ _fail "acme.deliver should block after flow-declared max_attempts=2 route-backs"
321
+ elif grep -q 'route_back_attempts_exceeded' "$TMP/fix2-acme-exceeded.out"; then
322
+ _pass "acme.deliver: blocks after flow-declared max_attempts=2 (not the hardcoded 3 from old builder code)"
323
+ else
324
+ _fail "acme.deliver exceeded max_attempts but wrong diagnostic (got: $(cat "$TMP/fix2-acme-exceeded.out"))"
325
+ fi
326
+
327
+ unset FLOW_AGENTS_FLOW_DEFS_DIR
328
+
329
+ # ─── 2c. Custom flow WITHOUT route_back_policy: NOT enforced ──────────────────
330
+ echo ""
331
+ echo "=== 2c. Custom flow WITHOUT route_back_policy: verification→execution NOT enforced ==="
332
+
333
+ CUSTOM_FLOWS_DIR_2="$TMP/custom-flows-2"
334
+ mkdir -p "$CUSTOM_FLOWS_DIR_2"
335
+
336
+ node - "$CUSTOM_FLOWS_DIR_2/acme.nodecl.flow.json" << 'NODE'
337
+ const fs = require('fs');
338
+ const flowPath = process.argv[2];
339
+ const flow = {
340
+ id: "acme.nodecl", version: "1.0",
341
+ phase_map: { execution: "execute", verification: "verify" },
342
+ steps: [{ id: "execute", next: "verify" }, { id: "verify", next: "done" }, { id: "done", next: null }],
343
+ gates: {
344
+ "verify-gate": {
345
+ step: "verify",
346
+ expects: [{ id: "verify-evidence", kind: "trust.bundle", required: true,
347
+ bundle_claim: { claimType: "acme.verify.tests", subjectType: "flow-step", accepted_statuses: ["trusted","accepted"] } }]
348
+ }
349
+ }
350
+ };
351
+ fs.writeFileSync(flowPath, JSON.stringify(flow, null, 2));
352
+ NODE
353
+
354
+ NODECL_DIR="$TMP/fix2-nodecl/.flow-agents/nodecl-fix2"
355
+ mkdir -p "$TMP/fix2-nodecl/.flow-agents"
356
+
357
+ flow_agents_node "$SIDECAR_BUNDLE_WRITER" ensure-session \
358
+ --artifact-root "$TMP/fix2-nodecl/.flow-agents" \
359
+ --task-slug "nodecl-fix2" \
360
+ --title "Fix2 nodecl route-back test" \
361
+ --summary "Verify flow without route_back_policy is not guarded." \
362
+ --timestamp "2026-06-27T10:00:00Z" > "$TMP/fix2-nodecl-ensure.out" 2>&1
363
+
364
+ export FLOW_AGENTS_FLOW_DEFS_DIR="$CUSTOM_FLOWS_DIR_2"
365
+
366
+ flow_agents_node "$SIDECAR_BUNDLE_WRITER" advance-state "$NODECL_DIR" \
367
+ --status verifying --phase verification \
368
+ --summary "Moving nodecl to verification." \
369
+ --flow-definition acme.nodecl \
370
+ --timestamp "2026-06-27T10:01:00Z" > "$TMP/fix2-nodecl-verify.out" 2>&1
371
+
372
+ if flow_agents_node "$SIDECAR_BUNDLE_WRITER" advance-state "$NODECL_DIR" \
373
+ --status in_progress --phase execution \
374
+ --summary "Nodecl route back — should be free without reason." \
375
+ --flow-definition acme.nodecl \
376
+ --timestamp "2026-06-27T10:02:00Z" > "$TMP/fix2-nodecl-rb.out" 2>&1 \
377
+ && [[ ! -f "$NODECL_DIR/transition-attempts.json" ]]; then
378
+ _pass "acme.nodecl (no route_back_policy): verification→execution freely allowed, no attempts file"
379
+ else
380
+ _fail "acme.nodecl without route_back_policy should allow route-back freely (got: $(cat "$TMP/fix2-nodecl-rb.out"))"
381
+ fi
382
+
383
+ unset FLOW_AGENTS_FLOW_DEFS_DIR
384
+
385
+ echo ""
386
+ echo "────────────────────────────────────────────"
387
+ if [[ "$errors" -eq 0 ]]; then
388
+ echo "test_kit_identity_trust: all checks passed."
389
+ exit 0
390
+ else
391
+ echo "test_kit_identity_trust: $errors check(s) FAILED."
392
+ exit 1
393
+ fi