@kontourai/flow-agents 2.2.0 → 2.4.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 (250) hide show
  1. package/.github/CODEOWNERS +8 -0
  2. package/.github/workflows/ci.yml +32 -0
  3. package/AGENTS.md +4 -4
  4. package/CHANGELOG.md +46 -0
  5. package/CONTEXT.md +14 -0
  6. package/README.md +9 -3
  7. package/agents/dev.json +1 -1
  8. package/agents/tool-code-reviewer.json +1 -1
  9. package/agents/tool-planner.json +3 -3
  10. package/agents/tool-verifier.json +3 -3
  11. package/build/src/cli/console-learning-projection.js +3 -2
  12. package/build/src/cli/init.js +104 -21
  13. package/build/src/cli/kit.js +37 -6
  14. package/build/src/cli/pull-work-provider.js +1 -1
  15. package/build/src/cli/usage-feedback.js +3 -3
  16. package/build/src/cli/validate-hook-influence.js +1 -0
  17. package/build/src/cli/validate-workflow-artifacts.js +22 -6
  18. package/build/src/cli/workflow-artifact-cleanup-audit.js +36 -2
  19. package/build/src/cli/workflow-sidecar.d.ts +14 -7
  20. package/build/src/cli/workflow-sidecar.js +792 -69
  21. package/build/src/flow-kit/validate.d.ts +23 -0
  22. package/build/src/flow-kit/validate.js +67 -1
  23. package/build/src/index.d.ts +1 -1
  24. package/build/src/index.js +1 -1
  25. package/build/src/lib/flow-resolver.d.ts +6 -4
  26. package/build/src/lib/flow-resolver.js +125 -58
  27. package/build/src/lib/local-artifact-root.d.ts +14 -2
  28. package/build/src/lib/local-artifact-root.js +22 -5
  29. package/build/src/lib/workflow-learning-projection.js +2 -2
  30. package/build/src/runtime-adapters.d.ts +12 -0
  31. package/build/src/runtime-adapters.js +56 -16
  32. package/build/src/tools/build-universal-bundles.js +49 -16
  33. package/build/src/tools/generate-context-map.js +10 -8
  34. package/build/src/tools/validate-source-tree.js +23 -1
  35. package/console.telemetry.json +22 -0
  36. package/context/contracts/artifact-contract.md +38 -7
  37. package/context/contracts/builder-kit-workflow-state-contract.md +3 -2
  38. package/context/contracts/decision-registry-contract.md +263 -0
  39. package/context/contracts/delivery-contract.md +1 -1
  40. package/context/contracts/planning-contract.md +2 -1
  41. package/context/contracts/review-contract.md +5 -3
  42. package/context/contracts/verification-contract.md +9 -1
  43. package/context/contracts/work-item-contract.md +1 -1
  44. package/context/deferred/parallelization.md +1 -1
  45. package/context/gate-awareness.md +4 -4
  46. package/context/scripts/git-status.sh +2 -2
  47. package/context/scripts/hooks/config-protection.js +502 -2
  48. package/context/scripts/hooks/lib/config-protection-remedies.js +69 -0
  49. package/context/scripts/hooks/run-hook.js +14 -1
  50. package/context/scripts/hooks/stop-goal-fit.js +1140 -99
  51. package/context/scripts/hooks/workflow-steering.js +212 -6
  52. package/docs/adr/0007-flow-skill-kit-tool-boundary.md +2 -0
  53. package/docs/adr/0015-flow-flow-agents-boundary-reconciliation.md +35 -0
  54. package/docs/adr/0019-kit-dependency-ownership.md +62 -0
  55. package/docs/adr/0020-trust-reconcile-manifest-and-claim-classification.md +290 -0
  56. package/docs/adr/0021-assignment-leases-and-stale-claim-takeover.md +221 -0
  57. package/docs/adr/0022-fail-closed-delivery-reconciliation-with-governed-exemptions.md +283 -0
  58. package/docs/agent-system-guidebook.md +3 -3
  59. package/docs/agent-usage-feedback-loop.md +6 -6
  60. package/docs/context-map.md +9 -7
  61. package/docs/decisions/decision-records.md +57 -0
  62. package/docs/decisions/index.md +14 -0
  63. package/docs/decisions/promotion-gate.md +52 -0
  64. package/docs/developer-architecture.md +1 -1
  65. package/docs/fixture-ownership.md +3 -0
  66. package/docs/flow-kit-repository-contract.md +5 -5
  67. package/docs/getting-started.md +12 -2
  68. package/docs/integrations/harness-install.md +2 -1
  69. package/docs/kit-authoring-guide.md +64 -5
  70. package/docs/knowledge-kit.md +5 -1
  71. package/docs/learnings/2026-07-improvement-program.md +203 -0
  72. package/docs/migrations.md +6 -5
  73. package/docs/north-star.md +2 -2
  74. package/docs/operating-layers.md +4 -3
  75. package/docs/repository-structure.md +5 -5
  76. package/docs/skills-map.md +8 -8
  77. package/docs/spec/runtime-hook-surface.md +19 -15
  78. package/docs/spikes/graph-provider-2026-07.md +155 -0
  79. package/docs/standards-register.md +7 -7
  80. package/docs/trust-anchor-adoption.md +105 -4
  81. package/docs/work-item-adapters.md +1 -1
  82. package/docs/workflow-artifact-lifecycle.md +72 -9
  83. package/docs/workflow-eval-strategy.md +3 -3
  84. package/docs/workflow-shared-contracts.md +1 -1
  85. package/docs/workflow-usage-guide.md +254 -21
  86. package/evals/acceptance/DEMO-false-completion.md +20 -11
  87. package/evals/acceptance/prove-capture-teeth-declared.sh +15 -15
  88. package/evals/acceptance/prove-capture-teeth.sh +11 -11
  89. package/evals/acceptance/test_kiro_harness.sh +1 -1
  90. package/evals/ci/antigaming-suite.sh +5 -0
  91. package/evals/ci/run-baseline.sh +65 -1
  92. package/evals/fixtures/flow-kit-repository/invalid-bad-dependency/flows/review.flow.json +26 -0
  93. package/evals/fixtures/flow-kit-repository/invalid-bad-dependency/kit.json +13 -0
  94. package/evals/fixtures/flow-kit-repository/valid-with-dependency/flows/review.flow.json +26 -0
  95. package/evals/fixtures/flow-kit-repository/valid-with-dependency/kit.json +13 -0
  96. package/evals/fixtures/hook-influence/cases.json +32 -0
  97. package/evals/fixtures/pull-work-provider/github-issues.json +6 -6
  98. package/evals/fixtures/trust-reconcile-exploits/fabricated-attestation.json +59 -0
  99. package/evals/fixtures/trust-reconcile-exploits/no-label-bypass.json +36 -0
  100. package/evals/fixtures/trust-reconcile-exploits/skip-assumed-bypass.json +59 -0
  101. package/evals/fixtures/trust-reconcile-exploits/status-misassertion.json +48 -0
  102. package/evals/fixtures/trust-reconcile-exploits/waived-command-check.json +44 -0
  103. package/evals/fixtures/trust-reconcile-mixed-bundle/mixed-bundle.json +159 -0
  104. package/evals/fixtures/trust-reconcile-ws3/ws3-bundle.json +920 -0
  105. package/evals/integration/test_actor_identity.sh +254 -0
  106. package/evals/integration/test_builder_step_producers.sh +33 -23
  107. package/evals/integration/test_bundle_install.sh +22 -16
  108. package/evals/integration/test_bundle_lifecycle.sh +11 -11
  109. package/evals/integration/test_captured_fail_reconciliation.sh +50 -50
  110. package/evals/integration/test_checkpoint_signing.sh +43 -0
  111. package/evals/integration/test_claim_lookup.sh +4 -4
  112. package/evals/integration/test_codex_hook_resolution.sh +114 -0
  113. package/evals/integration/test_command_log_concurrency.sh +8 -8
  114. package/evals/integration/test_command_log_fork_classification.sh +3 -3
  115. package/evals/integration/test_command_log_integrity.sh +16 -16
  116. package/evals/integration/test_console_learning_projection.sh +1 -1
  117. package/evals/integration/test_dual_emit_flow_step.sh +145 -0
  118. package/evals/integration/test_enforcer_expects_driven.sh +12 -12
  119. package/evals/integration/test_evidence_capture_hook.sh +14 -13
  120. package/evals/integration/test_fixture_retirement_audit.sh +2 -2
  121. package/evals/integration/test_flow_agents_statusline.sh +2 -2
  122. package/evals/integration/test_flow_kit_install_git.sh +52 -0
  123. package/evals/integration/test_flow_kit_repository.sh +2 -0
  124. package/evals/integration/test_flowdef_session_activation.sh +6 -6
  125. package/evals/integration/test_flowdef_union_floor_regression.sh +429 -0
  126. package/evals/integration/test_gate_bypass_chain.sh +52 -40
  127. package/evals/integration/test_gate_lockdown.sh +97 -73
  128. package/evals/integration/test_gate_review_inquiry_records.sh +3 -3
  129. package/evals/integration/test_goal_fit_escape_hatch.sh +10 -10
  130. package/evals/integration/test_goal_fit_ghost_session.sh +104 -0
  131. package/evals/integration/test_goal_fit_hook.sh +32 -32
  132. package/evals/integration/test_goal_fit_rederive.sh +9 -9
  133. package/evals/integration/test_hook_category_behaviors.sh +103 -0
  134. package/evals/integration/test_hook_influence_cases.sh +1 -0
  135. package/evals/integration/test_install_merge.sh +262 -1
  136. package/evals/integration/test_kit_identity_trust.sh +6 -6
  137. package/evals/integration/test_liveness_conflict_injection.sh +587 -0
  138. package/evals/integration/test_liveness_heartbeat.sh +677 -0
  139. package/evals/integration/test_liveness_verdict.sh +394 -0
  140. package/evals/integration/test_local_flow_kit_install.sh +15 -0
  141. package/evals/integration/test_phase_map_and_gate_claim.sh +63 -11
  142. package/evals/integration/test_promote_gate.sh +178 -0
  143. package/evals/integration/test_publish_delivery.sh +2 -2
  144. package/evals/integration/test_pull_work_liveness_preflight.sh +386 -0
  145. package/evals/integration/test_pull_work_provider.sh +2 -2
  146. package/evals/integration/test_reconcile_soundness.sh +4 -4
  147. package/evals/integration/test_resolvefirststep_security.sh +22 -0
  148. package/evals/integration/test_runtime_adapter_activation.sh +87 -18
  149. package/evals/integration/test_session_resume_roundtrip.sh +147 -0
  150. package/evals/integration/test_sidecar_field_preservation.sh +249 -0
  151. package/evals/integration/test_trust_reconcile.sh +2 -2
  152. package/evals/integration/test_trust_reconcile_manifest.sh +79 -0
  153. package/evals/integration/test_trust_reconcile_mixed_bundle.sh +96 -0
  154. package/evals/integration/test_trust_reconcile_negatives.sh +151 -0
  155. package/evals/integration/test_usage_feedback_global.sh +5 -5
  156. package/evals/integration/test_validate_artifacts_portability.sh +60 -0
  157. package/evals/integration/test_verify_cli.sh +2 -2
  158. package/evals/integration/test_veritas_governance_kit.sh +117 -0
  159. package/evals/integration/test_workflow_artifact_cleanup_audit.sh +26 -1
  160. package/evals/integration/test_workflow_artifacts.sh +33 -0
  161. package/evals/integration/test_workflow_sidecar_writer.sh +723 -74
  162. package/evals/integration/test_workflow_steering_hook.sh +175 -5
  163. package/evals/lib/codex-provider.sh +1 -1
  164. package/evals/lib/node.sh +19 -1
  165. package/evals/run.sh +22 -0
  166. package/evals/static/test_decisions.sh +150 -0
  167. package/evals/static/test_flowdef_codeowners_coverage.sh +56 -0
  168. package/evals/static/test_package.sh +19 -10
  169. package/evals/static/test_universal_bundles.sh +48 -3
  170. package/evals/static/test_workflow_skills.sh +41 -8
  171. package/kits/builder/flows/build.flow.json +3 -74
  172. package/kits/builder/flows/publish-learn.flow.json +90 -0
  173. package/kits/builder/kit.json +11 -0
  174. package/kits/builder/skills/builder-shape/SKILL.md +1 -1
  175. package/kits/builder/skills/deliver/SKILL.md +13 -11
  176. package/kits/builder/skills/design-probe/SKILL.md +4 -4
  177. package/kits/builder/skills/evidence-gate/SKILL.md +26 -1
  178. package/kits/builder/skills/execute-plan/SKILL.md +2 -2
  179. package/kits/builder/skills/fix-bug/SKILL.md +2 -0
  180. package/kits/builder/skills/gate-review/SKILL.md +6 -6
  181. package/kits/builder/skills/idea-to-backlog/SKILL.md +1 -1
  182. package/kits/builder/skills/learning-review/SKILL.md +7 -7
  183. package/kits/builder/skills/pickup-probe/SKILL.md +5 -4
  184. package/kits/builder/skills/plan-work/SKILL.md +27 -5
  185. package/kits/builder/skills/pull-work/SKILL.md +80 -5
  186. package/kits/builder/skills/release-readiness/SKILL.md +4 -4
  187. package/kits/builder/skills/review-work/SKILL.md +24 -1
  188. package/kits/builder/skills/tdd-workflow/SKILL.md +2 -0
  189. package/kits/builder/skills/verify-work/SKILL.md +8 -1
  190. package/kits/catalog.json +6 -0
  191. package/kits/veritas-governance/adapter/readiness-to-trust-bundle.mjs +178 -0
  192. package/kits/veritas-governance/docs/README.md +75 -0
  193. package/kits/veritas-governance/fixtures/readiness/not-ready.readiness-report.json +4645 -0
  194. package/kits/veritas-governance/fixtures/readiness/ready.readiness-report.json +4403 -0
  195. package/kits/veritas-governance/flows/readiness-check.flow.json +35 -0
  196. package/kits/veritas-governance/kit.json +16 -0
  197. package/package.json +8 -4
  198. package/packaging/README.md +2 -2
  199. package/packaging/conformance/fixtures/config-protection--allow-read-state-json.json +20 -0
  200. package/packaging/conformance/fixtures/config-protection--block-write-state-json.json +20 -0
  201. package/packaging/conformance/fixtures/stop-goal-fit--block-bundle-disputed-claim.json +1 -1
  202. package/packaging/conformance/fixtures/stop-goal-fit--block-capture-contradicts-claimed-pass.json +1 -1
  203. package/packaging/manifest.json +9 -10
  204. package/schemas/decision-record.schema.json +109 -0
  205. package/schemas/workflow-evidence.schema.json +1 -1
  206. package/schemas/workflow-state.schema.json +5 -0
  207. package/scripts/README.md +8 -3
  208. package/scripts/check-content-boundary.cjs +1 -1
  209. package/scripts/check-decisions.cjs +356 -0
  210. package/scripts/ci/derive-claim-status.mjs +74 -0
  211. package/scripts/ci/trust-reconcile.js +436 -79
  212. package/scripts/git-status.sh +2 -2
  213. package/scripts/hooks/claude-telemetry-hook.js +37 -3
  214. package/scripts/hooks/codex-telemetry-hook.js +36 -2
  215. package/scripts/hooks/config-protection.js +102 -30
  216. package/scripts/hooks/evidence-capture.js +3 -3
  217. package/scripts/hooks/lib/actor-identity.js +368 -0
  218. package/scripts/hooks/lib/config-protection-remedies.js +69 -0
  219. package/scripts/hooks/lib/liveness-heartbeat.js +361 -0
  220. package/scripts/hooks/lib/liveness-policy.js +127 -0
  221. package/scripts/hooks/lib/liveness-read.js +105 -12
  222. package/scripts/hooks/lib/liveness-write.js +46 -0
  223. package/scripts/hooks/lib/local-artifact-paths.js +12 -6
  224. package/scripts/hooks/opencode-telemetry-hook.js +27 -1
  225. package/scripts/hooks/pi-telemetry-hook.js +27 -1
  226. package/scripts/hooks/stop-goal-fit.js +157 -26
  227. package/scripts/hooks/workflow-steering.js +77 -3
  228. package/scripts/install-codex-home.sh +138 -38
  229. package/scripts/statusline/flow-agents-statusline.js +2 -2
  230. package/src/cli/console-learning-projection.ts +3 -2
  231. package/src/cli/init.ts +101 -21
  232. package/src/cli/kit.ts +37 -6
  233. package/src/cli/public-api.test.mjs +58 -6
  234. package/src/cli/pull-work-provider.ts +1 -1
  235. package/src/cli/trust-bundle-policy-order.test.mjs +87 -0
  236. package/src/cli/usage-feedback.ts +3 -3
  237. package/src/cli/validate-hook-influence.ts +1 -0
  238. package/src/cli/validate-workflow-artifacts.ts +22 -6
  239. package/src/cli/veritas-readiness-adapter.test.mjs +267 -0
  240. package/src/cli/workflow-artifact-cleanup-audit.ts +35 -2
  241. package/src/cli/workflow-sidecar.ts +795 -71
  242. package/src/flow-kit/validate.ts +74 -1
  243. package/src/index.ts +6 -2
  244. package/src/lib/flow-resolver.ts +123 -55
  245. package/src/lib/local-artifact-root.ts +24 -5
  246. package/src/lib/workflow-learning-projection.ts +2 -2
  247. package/src/runtime-adapters.ts +61 -15
  248. package/src/tools/build-universal-bundles.ts +49 -16
  249. package/src/tools/generate-context-map.ts +10 -8
  250. package/src/tools/validate-source-tree.ts +23 -1
@@ -0,0 +1,254 @@
1
+ #!/usr/bin/env bash
2
+ # test_actor_identity.sh - unit-style eval for the shared actor-identity resolver
3
+ # (scripts/hooks/lib/actor-identity.js). Written against the frozen interface documented
4
+ # in .kontourai/flow-agents/kontourai-flow-agents-287/kontourai-flow-agents-287--plan-work.md
5
+ # Wave 1 Task 1.1 (detectRuntime/runtimeSessionId/ancestorActorSeed/sanitizeSegment/
6
+ # serializeActor/resolveActor).
7
+ set -uo pipefail
8
+
9
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
10
+ MODULE="$ROOT/scripts/hooks/lib/actor-identity.js"
11
+
12
+ errors=0
13
+
14
+ _pass() { echo " ✓ $1"; }
15
+ _fail() { echo " ✗ $1"; errors=$((errors + 1)); }
16
+
17
+ if [[ ! -f "$MODULE" ]]; then
18
+ echo "actor identity eval skipped: $MODULE does not exist yet." >&2
19
+ exit 1
20
+ fi
21
+
22
+ # 1. serializeActor never contains "::" or a raw ":" inside any single segment, even given
23
+ # adversarial input (session_id containing ":", "/", spaces, unicode).
24
+ if node - "$MODULE" <<'NODE'
25
+ const path = process.argv[2];
26
+ const { serializeActor } = require(path);
27
+ const out = serializeActor({
28
+ runtime: "claude-code",
29
+ session_id: "sess:with/slash space:moreéé",
30
+ host: "ho:st/name",
31
+ human: "hu man:name",
32
+ });
33
+ if (typeof out !== "string" || out.length === 0) {
34
+ throw new Error(`serializeActor did not return a non-empty string: ${JSON.stringify(out)}`);
35
+ }
36
+ if (out.includes("::")) {
37
+ throw new Error(`serializeActor output contains "::": ${JSON.stringify(out)}`);
38
+ }
39
+ if (!/^[A-Za-z0-9_.:-]+$/.test(out)) {
40
+ throw new Error(`serializeActor output contains disallowed characters: ${JSON.stringify(out)}`);
41
+ }
42
+ NODE
43
+ then
44
+ _pass "serializeActor sanitizes adversarial segments (no :: and no raw : inside a segment)"
45
+ else
46
+ _fail "serializeActor did not sanitize adversarial input safely"
47
+ fi
48
+
49
+ # 2. resolveActor({FLOW_AGENTS_ACTOR: "my-actor"}) returns {actor: "my-actor", ...} — explicit
50
+ # override always wins, even if a runtime var is also present in the injected env.
51
+ if node - "$MODULE" <<'NODE'
52
+ const path = process.argv[2];
53
+ const { resolveActor } = require(path);
54
+ const out = resolveActor({ FLOW_AGENTS_ACTOR: "my-actor", CLAUDE_CODE_SESSION_ID: "sess-should-be-ignored" });
55
+ if (!out || out.actor !== "my-actor") {
56
+ throw new Error(`resolveActor did not honor explicit FLOW_AGENTS_ACTOR override: ${JSON.stringify(out)}`);
57
+ }
58
+ NODE
59
+ then
60
+ _pass "resolveActor honors explicit FLOW_AGENTS_ACTOR override even with a runtime var present"
61
+ else
62
+ _fail "resolveActor did not honor explicit FLOW_AGENTS_ACTOR override"
63
+ fi
64
+
65
+ # 3. resolveActor({FLOW_AGENTS_ACTOR: "local"}) does NOT honor the literal "local" override
66
+ # (falls through to runtime/ancestry resolution) — proves "local" can never round-trip back
67
+ # in via the override seam.
68
+ if node - "$MODULE" <<'NODE'
69
+ const path = process.argv[2];
70
+ const { resolveActor } = require(path);
71
+ const out = resolveActor({ FLOW_AGENTS_ACTOR: "local" });
72
+ if (!out || typeof out.actor !== "string" || out.actor.trim().toLowerCase() === "local") {
73
+ throw new Error(`resolveActor honored the literal "local" override: ${JSON.stringify(out)}`);
74
+ }
75
+ NODE
76
+ then
77
+ _pass "resolveActor never honors the literal \"local\" override"
78
+ else
79
+ _fail "resolveActor incorrectly honored the literal \"local\" override"
80
+ fi
81
+
82
+ # 4. resolveActor({CLAUDE_CODE_SESSION_ID: "sess-a"}) vs resolveActor({CLAUDE_CODE_SESSION_ID:
83
+ # "sess-b"}) (host held constant) produce two different serialized actors.
84
+ if node - "$MODULE" <<'NODE'
85
+ const path = process.argv[2];
86
+ const { resolveActor } = require(path);
87
+ const a = resolveActor({ CLAUDE_CODE_SESSION_ID: "sess-a" });
88
+ const b = resolveActor({ CLAUDE_CODE_SESSION_ID: "sess-b" });
89
+ if (!a || !b || !a.actor || !b.actor) {
90
+ throw new Error(`resolveActor returned an empty actor for a runtime-native session id: a=${JSON.stringify(a)} b=${JSON.stringify(b)}`);
91
+ }
92
+ if (a.actor === b.actor) {
93
+ throw new Error(`resolveActor produced the same actor for two distinct session ids: ${JSON.stringify(a)} vs ${JSON.stringify(b)}`);
94
+ }
95
+ NODE
96
+ then
97
+ _pass "resolveActor produces distinct actors for distinct runtime session ids"
98
+ else
99
+ _fail "resolveActor did not distinguish between distinct runtime session ids"
100
+ fi
101
+
102
+ # 5. Stability test: call resolveActor({}) (real ambient env, no runtime var, forcing the
103
+ # ancestry path) twice in the same process with a real setTimeout gap of several seconds
104
+ # between calls; assert byte-identical output both times.
105
+ if node - "$MODULE" <<'NODE'
106
+ const path = process.argv[2];
107
+ const { resolveActor } = require(path);
108
+ const first = resolveActor({});
109
+ setTimeout(() => {
110
+ const second = resolveActor({});
111
+ const a = JSON.stringify(first);
112
+ const b = JSON.stringify(second);
113
+ if (a !== b) {
114
+ console.error(`resolveActor({}) was not stable across a delay: first=${a} second=${b}`);
115
+ process.exit(1);
116
+ }
117
+ process.exit(0);
118
+ }, 3000);
119
+ NODE
120
+ then
121
+ _pass "resolveActor({}) is byte-identical across a several-second real-time gap (ancestry stability)"
122
+ else
123
+ _fail "resolveActor({}) was not stable across a several-second real-time gap"
124
+ fi
125
+
126
+ # 6. resolveActor({FLOW_AGENTS_ACTOR_TEST_FORCE_UNRESOLVED: "1", NODE_ENV: "test"}) returns
127
+ # {actor: "", source: "test-forced-unresolved"} — the hatch requires BOTH the force-unresolved
128
+ # var AND NODE_ENV=test (F4, #287 fix iteration 1); the var alone (no NODE_ENV=test) must NOT
129
+ # trip the hatch, proving it cannot be tripped by an accidental/malicious env var outside a
130
+ # real test harness.
131
+ if node - "$MODULE" <<'NODE'
132
+ const path = process.argv[2];
133
+ const { resolveActor } = require(path);
134
+ const out = resolveActor({ FLOW_AGENTS_ACTOR_TEST_FORCE_UNRESOLVED: "1", NODE_ENV: "test" });
135
+ if (!out || out.actor !== "" || out.source !== "test-forced-unresolved") {
136
+ throw new Error(`resolveActor did not honor the test-forced-unresolved escape hatch: ${JSON.stringify(out)}`);
137
+ }
138
+ NODE
139
+ then
140
+ _pass "resolveActor honors FLOW_AGENTS_ACTOR_TEST_FORCE_UNRESOLVED escape hatch when NODE_ENV=test is also set"
141
+ else
142
+ _fail "resolveActor did not honor FLOW_AGENTS_ACTOR_TEST_FORCE_UNRESOLVED escape hatch with NODE_ENV=test"
143
+ fi
144
+
145
+ # 6b (F4 negative). The hatch var alone, WITHOUT NODE_ENV=test, must NOT force an unresolved actor
146
+ # — proves the gate is a real AND, not an OR.
147
+ if node - "$MODULE" <<'NODE'
148
+ const path = process.argv[2];
149
+ const { resolveActor } = require(path);
150
+ const out = resolveActor({ FLOW_AGENTS_ACTOR_TEST_FORCE_UNRESOLVED: "1" });
151
+ if (out && out.source === "test-forced-unresolved") {
152
+ throw new Error(`resolveActor honored the test-forced-unresolved escape hatch without NODE_ENV=test: ${JSON.stringify(out)}`);
153
+ }
154
+ NODE
155
+ then
156
+ _pass "resolveActor does NOT honor the escape hatch when NODE_ENV=test is missing (F4 gate is AND, not OR)"
157
+ else
158
+ _fail "resolveActor incorrectly honored the escape hatch without NODE_ENV=test"
159
+ fi
160
+
161
+ # 7 (T1, F2). Adversarial FLOW_AGENTS_ACTOR override — containing "::", a raw newline, and
162
+ # exceeding 64 chars — resolves to a sanitized, capped actor: no ":" anywhere, length <= 64.
163
+ if node - "$MODULE" <<'NODE'
164
+ const path = process.argv[2];
165
+ const { resolveActor } = require(path);
166
+ const adversarial = "a::b\nc" + "x".repeat(100);
167
+ const out = resolveActor({ FLOW_AGENTS_ACTOR: adversarial });
168
+ if (!out || typeof out.actor !== "string" || !out.actor) {
169
+ throw new Error(`resolveActor returned no actor for adversarial override: ${JSON.stringify(out)}`);
170
+ }
171
+ if (out.actor.includes(":")) {
172
+ throw new Error(`resolveActor did not strip ":" from adversarial override: ${JSON.stringify(out)}`);
173
+ }
174
+ if (out.actor.includes("\n")) {
175
+ throw new Error(`resolveActor did not strip newline from adversarial override: ${JSON.stringify(out)}`);
176
+ }
177
+ if (out.actor.length > 64) {
178
+ throw new Error(`resolveActor did not cap adversarial override at 64 chars: ${JSON.stringify(out)}`);
179
+ }
180
+ NODE
181
+ then
182
+ _pass "resolveActor sanitizes and caps an adversarial FLOW_AGENTS_ACTOR override (:: / newline / >64 chars)"
183
+ else
184
+ _fail "resolveActor did not sanitize/cap an adversarial FLOW_AGENTS_ACTOR override"
185
+ fi
186
+
187
+ # 8 (T1, F3). Rejected FLOW_AGENTS_ACTOR=local (case-insensitive) emits exactly one stderr warning
188
+ # line naming FLOW_AGENTS_ACTOR — never on stdout, never a thrown exception.
189
+ TMPDIR_EVAL_STDERR_CAPTURE="$(mktemp)"
190
+ if node - "$MODULE" >"$TMPDIR_EVAL_STDERR_CAPTURE.out" 2>"$TMPDIR_EVAL_STDERR_CAPTURE.err" <<'NODE'
191
+ const path = process.argv[2];
192
+ const { resolveActor } = require(path);
193
+ const out = resolveActor({ FLOW_AGENTS_ACTOR: "LOCAL" });
194
+ process.stdout.write(JSON.stringify(out));
195
+ NODE
196
+ then
197
+ if grep -qF "FLOW_AGENTS_ACTOR=local" "$TMPDIR_EVAL_STDERR_CAPTURE.err" \
198
+ && ! grep -qF "FLOW_AGENTS_ACTOR=local" "$TMPDIR_EVAL_STDERR_CAPTURE.out"; then
199
+ _pass "rejected FLOW_AGENTS_ACTOR=local (case-insensitive) emits one stderr warning, never on stdout"
200
+ else
201
+ _fail "rejected FLOW_AGENTS_ACTOR=local warning missing from stderr or leaked onto stdout: out=$(cat "$TMPDIR_EVAL_STDERR_CAPTURE.out") err=$(cat "$TMPDIR_EVAL_STDERR_CAPTURE.err")"
202
+ fi
203
+ else
204
+ _fail "resolveActor threw for a rejected FLOW_AGENTS_ACTOR=local override (must never throw)"
205
+ fi
206
+ rm -f "$TMPDIR_EVAL_STDERR_CAPTURE.out" "$TMPDIR_EVAL_STDERR_CAPTURE.err"
207
+
208
+ # 9 (T4, F7). resolveActor({FLOW_AGENTS_ACTOR: ":::"}) — a value that strips to empty under the
209
+ # allowed [A-Za-z0-9_.-] charset — must NOT adopt the shared "unknown" sentinel: it falls through
210
+ # to derivation (source != "explicit-override", actor != "unknown") and emits one stderr warning,
211
+ # never stdout, never a thrown exception.
212
+ TMPDIR_EVAL_STRIPEMPTY_CAPTURE="$(mktemp)"
213
+ if node - "$MODULE" >"$TMPDIR_EVAL_STRIPEMPTY_CAPTURE.out" 2>"$TMPDIR_EVAL_STRIPEMPTY_CAPTURE.err" <<'NODE'
214
+ const path = process.argv[2];
215
+ const { resolveActor } = require(path);
216
+ const out = resolveActor({ FLOW_AGENTS_ACTOR: ":::" });
217
+ process.stdout.write(JSON.stringify(out));
218
+ NODE
219
+ then
220
+ STRIPEMPTY_OUT="$(cat "$TMPDIR_EVAL_STRIPEMPTY_CAPTURE.out")"
221
+ if echo "$STRIPEMPTY_OUT" | node -e 'const out = JSON.parse(require("fs").readFileSync(0, "utf8")); if (!out || out.actor === "unknown" || out.source === "explicit-override") process.exit(1);' && grep -qF "FLOW_AGENTS_ACTOR" "$TMPDIR_EVAL_STRIPEMPTY_CAPTURE.err" && ! grep -qF "FLOW_AGENTS_ACTOR" "$TMPDIR_EVAL_STRIPEMPTY_CAPTURE.out"; then
222
+ _pass "resolveActor({FLOW_AGENTS_ACTOR: ':::'}) falls through to a derived actor (not 'unknown', not explicit-override) with a stderr warning"
223
+ else
224
+ _fail "resolveActor did not fall through correctly for a strip-to-empty override: out=$STRIPEMPTY_OUT err=$(cat "$TMPDIR_EVAL_STRIPEMPTY_CAPTURE.err")"
225
+ fi
226
+ else
227
+ _fail "resolveActor threw for a strip-to-empty FLOW_AGENTS_ACTOR override (must never throw)"
228
+ fi
229
+ rm -f "$TMPDIR_EVAL_STRIPEMPTY_CAPTURE.out" "$TMPDIR_EVAL_STRIPEMPTY_CAPTURE.err"
230
+
231
+ # 10 (T4, F7). resolveActor({FLOW_AGENTS_ACTOR: "unknown"}) — a deliberate literal "unknown" — is
232
+ # honored as-is via the explicit-override path (proves the sentinel itself is not blacklisted;
233
+ # only a strip-to-empty value is rejected).
234
+ if node - "$MODULE" <<'NODE'
235
+ const path = process.argv[2];
236
+ const { resolveActor } = require(path);
237
+ const out = resolveActor({ FLOW_AGENTS_ACTOR: "unknown" });
238
+ if (!out || out.actor !== "unknown" || out.source !== "explicit-override") {
239
+ throw new Error(`resolveActor did not honor a deliberate literal "unknown" override: ${JSON.stringify(out)}`);
240
+ }
241
+ NODE
242
+ then
243
+ _pass "resolveActor honors a deliberate literal FLOW_AGENTS_ACTOR=unknown override (sentinel itself is not blacklisted)"
244
+ else
245
+ _fail "resolveActor did not honor a deliberate literal FLOW_AGENTS_ACTOR=unknown override"
246
+ fi
247
+
248
+ if [[ "$errors" -eq 0 ]]; then
249
+ echo "Actor identity resolver integration passed."
250
+ exit 0
251
+ fi
252
+
253
+ echo "Actor identity resolver integration failed: $errors issue(s)."
254
+ exit 1
@@ -15,8 +15,8 @@
15
15
  # 5. builder.learn.decisions (step: learn, expectation: decision-evidence)
16
16
  # 6. builder.learn.evidence (step: learn, expectation: learning-evidence)
17
17
  #
18
- # build.flow.json confirmation:
19
- # - All 6 claims above are required:true.
18
+ # Flow Definition confirmation:
19
+ # - All 6 claims above are required:true across builder.build and builder.publish-learn.
20
20
  # - policy-compliance remains required:false (advisory — no skill producer).
21
21
  #
22
22
  # Deterministic, no model spend, self-cleaning.
@@ -106,21 +106,21 @@ setup_tamper_session() {
106
106
  local t_dir="$1" slug="$2" step="$3"
107
107
  mkdir -p "$t_dir"
108
108
  printf '# Repo\n' > "$t_dir/AGENTS.md"
109
- mkdir -p "$t_dir/.flow-agents/$slug"
109
+ mkdir -p "$t_dir/.kontourai/flow-agents/$slug"
110
110
 
111
111
  flow_agents_node "workflow-sidecar" ensure-session \
112
- --artifact-root "$t_dir/.flow-agents" \
112
+ --artifact-root "$t_dir/.kontourai/flow-agents" \
113
113
  --task-slug "$slug" \
114
114
  --title "Tamper test: $step" \
115
115
  --summary "Testing tamper detection." \
116
116
  --flow-id builder.build \
117
117
  --timestamp "2026-06-26T00:00:00Z" >/dev/null 2>&1
118
118
 
119
- flow_agents_node "workflow-sidecar" init-plan "$t_dir/.flow-agents/$slug/$slug--deliver.md" \
119
+ flow_agents_node "workflow-sidecar" init-plan "$t_dir/.kontourai/flow-agents/$slug/$slug--deliver.md" \
120
120
  --source-request "Test" --summary "Testing" \
121
121
  --timestamp "2026-06-26T00:00:00Z" >/dev/null 2>&1
122
122
 
123
- set_active_step "$t_dir/.flow-agents" "$slug" "$step"
123
+ set_active_step "$t_dir/.kontourai/flow-agents" "$slug" "$step"
124
124
  }
125
125
 
126
126
  # ─── Test: produce a gate claim at a given step ───────────────────────────────
@@ -178,12 +178,12 @@ test_tamper_blocks() {
178
178
  setup_tamper_session "$t_dir" "$slug" "$step"
179
179
 
180
180
  # Write a TAMPERED trust.bundle: stored verified, evidence passing=false
181
- python3 - "$t_dir/.flow-agents/$slug/trust.bundle" "$claim_type" "$subject_type" << 'PY'
181
+ python3 - "$t_dir/.kontourai/flow-agents/$slug/trust.bundle" "$claim_type" "$subject_type" << 'PY'
182
182
  import json, sys
183
183
  claim_type = sys.argv[2]
184
184
  subject_type = sys.argv[3]
185
185
  bundle = {
186
- "schemaVersion": 3,
186
+ "schemaVersion": 5,
187
187
  "source": "flow-agents/workflow-sidecar",
188
188
  "claims": [{
189
189
  "id": "c1",
@@ -254,13 +254,16 @@ PY
254
254
  fi
255
255
  }
256
256
 
257
- # ─── Test 0: build.flow.json required:true confirmation ──────────────────────
257
+ # ─── Test 0: Flow Definition required:true confirmation ──────────────────────
258
258
  echo ""
259
- echo "=== 0. build.flow.json: confirm required:true for produced gates ==="
259
+ echo "=== 0. Flow Definitions: confirm required:true for produced gates ==="
260
260
 
261
261
  node -e "
262
262
  const fs = require('fs');
263
- const flow = JSON.parse(fs.readFileSync('$ROOT/kits/builder/flows/build.flow.json', 'utf8'));
263
+ const flows = [
264
+ JSON.parse(fs.readFileSync('$ROOT/kits/builder/flows/build.flow.json', 'utf8')),
265
+ JSON.parse(fs.readFileSync('$ROOT/kits/builder/flows/publish-learn.flow.json', 'utf8')),
266
+ ];
264
267
  const requiredTrue = [
265
268
  'selected-work',
266
269
  'pickup-probe-readiness',
@@ -271,26 +274,31 @@ node -e "
271
274
  ];
272
275
  const requiredFalse = ['policy-compliance'];
273
276
  let ok = true;
274
- for (const [gateName, gate] of Object.entries(flow.gates)) {
275
- for (const exp of gate.expects || []) {
277
+ for (const flow of flows) {
278
+ for (const [gateName, gate] of Object.entries(flow.gates || {})) {
279
+ for (const exp of gate.expects || []) {
276
280
  if (requiredTrue.includes(exp.id) && exp.required !== true) {
277
- console.error('FAIL: ' + exp.id + ' in ' + gateName + ' should be required:true, got ' + exp.required);
281
+ console.error('FAIL: ' + exp.id + ' in ' + flow.id + '/' + gateName + ' should be required:true, got ' + exp.required);
278
282
  ok = false;
279
283
  }
280
284
  if (requiredFalse.includes(exp.id) && exp.required !== false) {
281
- console.error('FAIL: ' + exp.id + ' in ' + gateName + ' should remain required:false (advisory), got ' + exp.required);
285
+ console.error('FAIL: ' + exp.id + ' in ' + flow.id + '/' + gateName + ' should remain required:false (advisory), got ' + exp.required);
282
286
  ok = false;
283
287
  }
288
+ }
284
289
  }
285
290
  }
286
291
  if (!ok) process.exit(1);
287
292
  " 2>/dev/null \
288
- && _pass "build.flow.json: 6 produced gates are required:true, policy-compliance is required:false" \
289
- || _fail "build.flow.json: required flag mismatch"
293
+ && _pass "Flow Definitions: 6 produced gates are required:true, policy-compliance is required:false" \
294
+ || _fail "Flow Definitions: required flag mismatch"
290
295
 
291
296
  node -e "
292
297
  const fs = require('fs');
293
- const flow = JSON.parse(fs.readFileSync('$ROOT/kits/builder/flows/build.flow.json', 'utf8'));
298
+ const flows = [
299
+ JSON.parse(fs.readFileSync('$ROOT/kits/builder/flows/build.flow.json', 'utf8')),
300
+ JSON.parse(fs.readFileSync('$ROOT/kits/builder/flows/publish-learn.flow.json', 'utf8')),
301
+ ];
294
302
  const producedIds = [
295
303
  'selected-work',
296
304
  'pickup-probe-readiness',
@@ -300,18 +308,20 @@ node -e "
300
308
  'learning-evidence',
301
309
  ];
302
310
  let ok = true;
303
- for (const [gateName, gate] of Object.entries(flow.gates)) {
304
- for (const exp of gate.expects || []) {
311
+ for (const flow of flows) {
312
+ for (const [gateName, gate] of Object.entries(flow.gates || {})) {
313
+ for (const exp of gate.expects || []) {
305
314
  if (producedIds.includes(exp.id) && exp.explore_hint) {
306
- console.error('FAIL: ' + exp.id + ' in ' + gateName + ' still has explore_hint (remove when producer exists)');
315
+ console.error('FAIL: ' + exp.id + ' in ' + flow.id + '/' + gateName + ' still has explore_hint (remove when producer exists)');
307
316
  ok = false;
308
317
  }
318
+ }
309
319
  }
310
320
  }
311
321
  if (!ok) process.exit(1);
312
322
  " 2>/dev/null \
313
- && _pass "build.flow.json: no explore_hint on produced gate entries" \
314
- || _fail "build.flow.json: produced gate entries still have explore_hint"
323
+ && _pass "Flow Definitions: no explore_hint on produced gate entries" \
324
+ || _fail "Flow Definitions: produced gate entries still have explore_hint"
315
325
 
316
326
  # ─── Tests 1–6: produce + tamper-block for each of the 6 claims ──────────────
317
327
 
@@ -158,13 +158,17 @@ echo "--- Installed Layout ---"
158
158
  for dir in \
159
159
  "$KIRO_DEST/agents" \
160
160
  "$BASE_DEST/.flow-agents" \
161
+ "$BASE_DEST/.kontourai/flow-agents" \
161
162
  "$CLAUDE_DEST/.claude/agents" \
162
163
  "$CLAUDE_DEST/.claude/skills" \
163
164
  "$CLAUDE_DEST/.flow-agents" \
165
+ "$CLAUDE_DEST/.kontourai/flow-agents" \
164
166
  "$CODEX_DEST/.codex/agents" \
165
167
  "$CODEX_DEST/.codex/skills" \
166
168
  "$CODEX_DEST/.flow-agents" \
167
- "$CODEX_FULL_DEST/.flow-agents"; do
169
+ "$CODEX_DEST/.kontourai/flow-agents" \
170
+ "$CODEX_FULL_DEST/.flow-agents" \
171
+ "$CODEX_FULL_DEST/.kontourai/flow-agents"; do
168
172
  if [[ -d "$dir" ]]; then
169
173
  _pass "$dir exists"
170
174
  else
@@ -218,7 +222,7 @@ fi
218
222
 
219
223
  if rg -F -q "console_telemetry_url=$LOCAL_KONTOUR_CONSOLE_URL" "$CODEX_INIT_DEST/scripts/telemetry/telemetry.conf" \
220
224
  && rg -q '^console_tenant_id=tenant-a$' "$CODEX_INIT_DEST/scripts/telemetry/telemetry.conf" \
221
- && [[ -f "$CODEX_INIT_DEST/.flow-agents/runtime/codex/activation.json" ]]; then
225
+ && [[ -f "$CODEX_INIT_DEST/.kontourai/flow-agents/projections/codex/activation.json" ]]; then
222
226
  _pass "flow-agents init persists Console config and activates Codex kits"
223
227
  else
224
228
  _fail "flow-agents init did not persist Console config or activate Codex kits"
@@ -226,6 +230,7 @@ fi
226
230
 
227
231
  if [[ -f "$BASE_INIT_DEST/AGENTS.md" ]] \
228
232
  && [[ -d "$BASE_INIT_DEST/.flow-agents" ]] \
233
+ && [[ -d "$BASE_INIT_DEST/.kontourai/flow-agents" ]] \
229
234
  && rg -F -q "console_telemetry_url=$LOCAL_KONTOUR_CONSOLE_URL" "$BASE_INIT_DEST/scripts/telemetry/telemetry.conf"; then
230
235
  _pass "flow-agents init default installs base AGENTS.md workspace contract"
231
236
  else
@@ -287,7 +292,7 @@ NODE
287
292
  else
288
293
  _fail "$dir is missing Kit Catalog or Builder Kit manifest"
289
294
  fi
290
- if [[ -f "$dir/kits/builder/flows/shape.flow.json" && -f "$dir/kits/builder/flows/build.flow.json" ]]; then
295
+ if [[ -f "$dir/kits/builder/flows/shape.flow.json" && -f "$dir/kits/builder/flows/build.flow.json" && -f "$dir/kits/builder/flows/publish-learn.flow.json" ]]; then
291
296
  _pass "$dir includes Builder Kit Flow Definitions"
292
297
  else
293
298
  _fail "$dir is missing Builder Kit Flow Definitions"
@@ -316,13 +321,13 @@ const dest = process.argv[2];
316
321
  const data = JSON.parse(fs.readFileSync(process.argv[3], "utf8"));
317
322
  if (data.selected_adapter !== "codex-local") throw new Error("codex-local was not selected");
318
323
  const ids = new Set((data.generated_runtime_files || []).map((item) => item.asset_id));
319
- for (const expected of ["builder.shape", "builder.build", "codex-local.activation"]) {
324
+ for (const expected of ["builder.shape", "builder.build", "builder.publish-learn", "codex-local.activation"]) {
320
325
  if (!ids.has(expected)) throw new Error(`missing generated runtime asset: ${expected}`);
321
326
  }
322
327
  for (const item of data.generated_runtime_files || []) {
323
328
  if (!fs.existsSync(path.join(dest, item.path))) throw new Error(`generated runtime file missing: ${item.path}`);
324
329
  }
325
- if (!fs.existsSync(path.join(dest, ".flow-agents/runtime/codex/activation.json"))) throw new Error("runtime activation manifest missing");
330
+ if (!fs.existsSync(path.join(dest, ".kontourai/flow-agents/projections/codex/activation.json"))) throw new Error("runtime activation manifest missing");
326
331
  console.log("ok");
327
332
  NODE
328
333
  then
@@ -335,7 +340,7 @@ if node - "$KIRO_DEST" "$BASE_DEST" "$CLAUDE_DEST" "$CODEX_DEST" <<'NODE'
335
340
  const fs = require("node:fs");
336
341
  const path = require("node:path");
337
342
  for (const root of process.argv.slice(2)) {
338
- for (const rel of ["kits/catalog.json", "kits/builder/kit.json", "kits/builder/flows/shape.flow.json", "kits/builder/flows/build.flow.json"]) {
343
+ for (const rel of ["kits/catalog.json", "kits/builder/kit.json", "kits/builder/flows/shape.flow.json", "kits/builder/flows/build.flow.json", "kits/builder/flows/publish-learn.flow.json"]) {
339
344
  JSON.parse(fs.readFileSync(path.join(root, rel), "utf8"));
340
345
  }
341
346
  }
@@ -367,25 +372,25 @@ else
367
372
  _pass "installed bundles are free of machine-local absolute paths"
368
373
  fi
369
374
 
370
- if [[ -f "$CLAUDE_DEST/.flow-agents/.gitkeep" ]]; then
375
+ if [[ -f "$CLAUDE_DEST/.kontourai/flow-agents/.gitkeep" ]]; then
371
376
  _pass "Claude Code task dir scaffold installed"
372
377
  else
373
378
  _fail "Claude Code task dir scaffold missing"
374
379
  fi
375
380
 
376
- if [[ -f "$CODEX_DEST/.flow-agents/.gitkeep" ]]; then
381
+ if [[ -f "$CODEX_DEST/.kontourai/flow-agents/.gitkeep" ]]; then
377
382
  _pass "Codex task dir scaffold installed"
378
383
  else
379
384
  _fail "Codex task dir scaffold missing"
380
385
  fi
381
386
 
382
- if [[ -f "$OPENCODE_DEST/.flow-agents/.gitkeep" ]]; then
387
+ if [[ -f "$OPENCODE_DEST/.kontourai/flow-agents/.gitkeep" ]]; then
383
388
  _pass "opencode task dir scaffold installed"
384
389
  else
385
390
  _fail "opencode task dir scaffold missing"
386
391
  fi
387
392
 
388
- if [[ -f "$PI_DEST/.flow-agents/.gitkeep" ]]; then
393
+ if [[ -f "$PI_DEST/.kontourai/flow-agents/.gitkeep" ]]; then
389
394
  _pass "pi task dir scaffold installed"
390
395
  else
391
396
  _fail "pi task dir scaffold missing"
@@ -417,9 +422,10 @@ else
417
422
  _fail "installed Claude permissions/statusline or Codex profile-v2 progress config is missing"
418
423
  fi
419
424
 
420
- if [[ -f "$CODEX_DEST/.codex/kdev.config.toml" && -f "$CODEX_DEST/.codex/kdev-br.config.toml" ]] \
421
- && rg -q 'approvals_reviewer = "auto_review"' "$CODEX_DEST/.codex/kdev.config.toml" \
422
- && rg -q 'model_provider = "amazon-bedrock"' "$CODEX_DEST/.codex/kdev-br.config.toml"; then
425
+ if [[ -f "$CODEX_DEST/.codex/builder.config.toml" && -f "$CODEX_DEST/.codex/personal.config.toml" ]] \
426
+ && [[ "$(find "$CODEX_DEST/.codex" -maxdepth 1 -name '*.config.toml' | wc -l | tr -d ' ')" == "2" ]] \
427
+ && rg -q 'Flow Agents Builder mode' "$CODEX_DEST/.codex/builder.config.toml" \
428
+ && rg -q 'knowledge-capture' "$CODEX_DEST/.codex/personal.config.toml"; then
423
429
  _pass "Codex install includes profile-v2 config files"
424
430
  else
425
431
  _fail "Codex install is missing profile-v2 config files"
@@ -517,7 +523,7 @@ const critique = {
517
523
  }],
518
524
  };
519
525
  function writeFixture(root) {
520
- const taskDir = path.join(root, ".flow-agents/installed-hook-demo");
526
+ const taskDir = path.join(root, ".kontourai/flow-agents/installed-hook-demo");
521
527
  fs.mkdirSync(taskDir, { recursive: true });
522
528
  fs.writeFileSync(path.join(taskDir, "state.json"), JSON.stringify(state), "utf8");
523
529
  fs.writeFileSync(path.join(taskDir, "critique.json"), JSON.stringify(critique), "utf8");
@@ -593,7 +599,7 @@ const critique = {
593
599
  critiques: [{ id: "oc-review", reviewer: "tool-code-reviewer", reviewed_at: "2026-06-01T00:01:00Z", verdict: "fail", summary: "Blocking.", findings: [{ id: "oc-open", severity: "high", status: "open", description: "Open finding." }] }],
594
600
  };
595
601
  function writeFixture(root) {
596
- const taskDir = path.join(root, ".flow-agents/opencode-hook-demo");
602
+ const taskDir = path.join(root, ".kontourai/flow-agents/opencode-hook-demo");
597
603
  fs.mkdirSync(taskDir, { recursive: true });
598
604
  fs.writeFileSync(path.join(taskDir, "state.json"), JSON.stringify(state), "utf8");
599
605
  fs.writeFileSync(path.join(taskDir, "critique.json"), JSON.stringify(critique), "utf8");
@@ -652,7 +658,7 @@ const critique = {
652
658
  critiques: [{ id: "pi-review", reviewer: "tool-code-reviewer", reviewed_at: "2026-06-01T00:01:00Z", verdict: "fail", summary: "Blocking.", findings: [{ id: "pi-open", severity: "high", status: "open", description: "Open finding." }] }],
653
659
  };
654
660
  function writeFixture(root) {
655
- const taskDir = path.join(root, ".flow-agents/pi-hook-demo");
661
+ const taskDir = path.join(root, ".kontourai/flow-agents/pi-hook-demo");
656
662
  fs.mkdirSync(taskDir, { recursive: true });
657
663
  fs.writeFileSync(path.join(taskDir, "state.json"), JSON.stringify(state), "utf8");
658
664
  fs.writeFileSync(path.join(taskDir, "critique.json"), JSON.stringify(critique), "utf8");
@@ -231,18 +231,18 @@ OPENCODE_USER="$TMPDIR_EVAL/user-opencode"
231
231
  # Create user-owned files (unknown to the bundle)
232
232
  mkdir -p "$CLAUDE_USER/.claude/custom"
233
233
  echo "# user custom agent" > "$CLAUDE_USER/.claude/custom/my-custom-agent.md"
234
- mkdir -p "$CLAUDE_USER/.flow-agents/my-session"
235
- echo '{"custom":"data"}' > "$CLAUDE_USER/.flow-agents/my-session/state.json"
234
+ mkdir -p "$CLAUDE_USER/.kontourai/flow-agents/my-session"
235
+ echo '{"custom":"data"}' > "$CLAUDE_USER/.kontourai/flow-agents/my-session/state.json"
236
236
 
237
237
  mkdir -p "$CODEX_USER/.codex/custom"
238
238
  printf 'name = "my-custom-agent"\n' > "$CODEX_USER/.codex/custom/my-custom-agent.toml"
239
- mkdir -p "$CODEX_USER/.flow-agents/my-session"
240
- echo '{"custom":"data"}' > "$CODEX_USER/.flow-agents/my-session/state.json"
239
+ mkdir -p "$CODEX_USER/.kontourai/flow-agents/my-session"
240
+ echo '{"custom":"data"}' > "$CODEX_USER/.kontourai/flow-agents/my-session/state.json"
241
241
 
242
242
  mkdir -p "$OPENCODE_USER/.opencode/custom"
243
243
  echo "# user custom agent" > "$OPENCODE_USER/.opencode/custom/my-custom-agent.md"
244
- mkdir -p "$OPENCODE_USER/.flow-agents/my-session"
245
- echo '{"custom":"data"}' > "$OPENCODE_USER/.flow-agents/my-session/state.json"
244
+ mkdir -p "$OPENCODE_USER/.kontourai/flow-agents/my-session"
245
+ echo '{"custom":"data"}' > "$OPENCODE_USER/.kontourai/flow-agents/my-session/state.json"
246
246
 
247
247
  # Modify an installed skill file to simulate user edits
248
248
  CLAUDE_INSTALLED_SKILL="$CLAUDE_USER/.claude/skills/plan-work/SKILL.md"
@@ -272,19 +272,19 @@ fi
272
272
  (cd "$ROOT_DIR/dist/opencode" && bash install.sh "$OPENCODE_USER" >/dev/null 2>&1) || true
273
273
 
274
274
  # Assert: user-owned unknown files survive
275
- if [[ -f "$CLAUDE_USER/.claude/custom/my-custom-agent.md" && -f "$CLAUDE_USER/.flow-agents/my-session/state.json" ]]; then
275
+ if [[ -f "$CLAUDE_USER/.claude/custom/my-custom-agent.md" && -f "$CLAUDE_USER/.kontourai/flow-agents/my-session/state.json" ]]; then
276
276
  _pass "claude-code re-install: user-owned files not removed by rsync"
277
277
  else
278
278
  _fail "claude-code re-install: user-owned files were removed"
279
279
  fi
280
280
 
281
- if [[ -f "$CODEX_USER/.codex/custom/my-custom-agent.toml" && -f "$CODEX_USER/.flow-agents/my-session/state.json" ]]; then
281
+ if [[ -f "$CODEX_USER/.codex/custom/my-custom-agent.toml" && -f "$CODEX_USER/.kontourai/flow-agents/my-session/state.json" ]]; then
282
282
  _pass "codex re-install: user-owned files not removed by rsync"
283
283
  else
284
284
  _fail "codex re-install: user-owned files were removed"
285
285
  fi
286
286
 
287
- if [[ -f "$OPENCODE_USER/.opencode/custom/my-custom-agent.md" && -f "$OPENCODE_USER/.flow-agents/my-session/state.json" ]]; then
287
+ if [[ -f "$OPENCODE_USER/.opencode/custom/my-custom-agent.md" && -f "$OPENCODE_USER/.kontourai/flow-agents/my-session/state.json" ]]; then
288
288
  _pass "opencode re-install: user-owned files not removed by rsync"
289
289
  else
290
290
  _fail "opencode re-install: user-owned files were removed"
@@ -528,7 +528,7 @@ fi
528
528
  # and point CLAUDE_PROJECT_DIR there so the hook can resolve its scripts.
529
529
  # This mirrors the real dogfood use case where the repo root has scripts/ from the bundle.
530
530
  DOGFOOD_WORKSPACE="$CLAUDE_IDEM" # reuse the installed workspace from the idempotent section
531
- mkdir -p "$DOGFOOD_WORKSPACE/.flow-agents"
531
+ mkdir -p "$DOGFOOD_WORKSPACE/.kontourai/flow-agents"
532
532
 
533
533
  if node - "$DOGFOOD_DEST2/.claude/settings.json" "$DOGFOOD_WORKSPACE" << 'NODE'
534
534
  const fs = require("node:fs");
@@ -537,7 +537,7 @@ const { spawnSync } = require("node:child_process");
537
537
  const [settingsPath, workspace] = process.argv.slice(2);
538
538
 
539
539
  // Write minimal fixtures for workflow-steering into the workspace
540
- const taskDir = path.join(workspace, ".flow-agents", "dogfood-hook-demo");
540
+ const taskDir = path.join(workspace, ".kontourai", "flow-agents", "dogfood-hook-demo");
541
541
  fs.mkdirSync(taskDir, { recursive: true });
542
542
  const state = {
543
543
  schema_version: "1.0",