@kontourai/flow-agents 2.3.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 +39 -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
@@ -16,7 +16,7 @@ ideas -> Builder Kit shape / idea-to-backlog -> work items -> pull-work -> picku
16
16
 
17
17
  You can do this in one conversation, but the gates should stay explicit. Do not let shaping, planning, implementation, and release confidence blur into one continuous task.
18
18
 
19
- Workflow artifacts follow a closeout lifecycle. Local runtime artifacts live under `.flow-agents/<slug>/` and stay uncommitted. When a branch needs reviewable in-progress planning, promote durable behavior, decisions, evidence, and usage notes into long-lived docs, source, schemas, or provider records before merge.
19
+ Workflow artifacts follow a closeout lifecycle. Local runtime artifacts live under `.kontourai/flow-agents/<slug>/` and stay uncommitted. When a branch needs reviewable in-progress planning, promote durable behavior, decisions, evidence, and usage notes into long-lived docs, source, schemas, or provider records before merge.
20
20
 
21
21
  For local artifact queue hygiene, run the read-only cleanup audit:
22
22
 
@@ -56,7 +56,7 @@ Expected behavior:
56
56
  Expected artifact:
57
57
 
58
58
  ```text
59
- .flow-agents/<slug>/<slug>--idea-to-backlog.md
59
+ .kontourai/flow-agents/<slug>/<slug>--idea-to-backlog.md
60
60
  ```
61
61
 
62
62
  The artifact should include `source_ideas`, `idea_inventory`, `slice_candidates`, `bundle_justification`, `dependency_map`, `phase`, `decisions`, `opportunity_briefs`, `shaped_work`, `risk_release_notes`, `backlog_links`, `parked_or_rejected`, `open_questions`, `next_gate`, and the Builder Kit Flow Definition link. Use this phase to decide what deserves backlog space. Provider-backed work items should be executable or near-executable work, not a dumping ground for every idea. GitHub issues are the first adapter example.
@@ -109,7 +109,7 @@ Expected behavior:
109
109
  Expected artifact:
110
110
 
111
111
  ```text
112
- .flow-agents/<slug>/<slug>--pull-work.md
112
+ .kontourai/flow-agents/<slug>/<slug>--pull-work.md
113
113
  ```
114
114
 
115
115
  When a repository has backlog provider settings, `pull-work` should use those settings without requiring the user to name the board. In Flow Agents, `npm run effective-backlog-settings -- --repo-path . --json` resolves `kontourai/flow-agents` to GitHub Project `kontourai/1`, so a prompt like `use pull-work` is enough for the configured provider path.
@@ -136,7 +136,7 @@ Your WIP can block starting new work. Other people's WIP should block only when
136
136
 
137
137
  After `pull-work` passes the pickup gate in the Builder Kit build flow, use the Builder Kit pickup Probe before planning. The Flow Definition step is named `design-probe`, and the build path is `pull-work -> design-probe -> plan`. The generic `design-probe` skill owns one-question-at-a-time design alignment; the `pickup-probe` skill is the Builder Kit work-item/docs/provider-grounded specialization used at that step.
138
138
 
139
- The pickup Probe must record goal fit and scope, blockers and dependencies, dependency freshness, acceptance criteria quality, provider state, risk, stop-short risks, planning readiness, decisions, unresolved questions, accepted gaps, sandbox/worktree mode, expected modified files, and conflict risks. Record those in `.flow-agents/<slug>/<slug>--pull-work.md` or the plan handoff artifact before `plan-work` begins.
139
+ The pickup Probe must record goal fit and scope, blockers and dependencies, dependency freshness, acceptance criteria quality, provider state, risk, stop-short risks, planning readiness, decisions, unresolved questions, accepted gaps, sandbox/worktree mode, expected modified files, and conflict risks. Record those in `.kontourai/flow-agents/<slug>/<slug>--pull-work.md` or the plan handoff artifact before `plan-work` begins.
140
140
 
141
141
  When the selected work item includes `planned_base_ref` and `planned_base_sha`, compare that base with current `main` before planning. If relevant files, contracts, docs, schemas, or dependency states changed since the work was shaped, classify the drift as `no_material_drift`, `scope_drift`, `dependency_drift`, `contract_drift`, or `conflict_risk`. Ask for alignment before planning when drift changes scope, acceptance criteria, dependency assumptions, or execution risk.
142
142
 
@@ -184,7 +184,7 @@ Then use `plan-work`.
184
184
  Example prompt:
185
185
 
186
186
  ```text
187
- Use plan-work for the selected work item in .flow-agents/<slug>/<slug>--pull-work.md. Produce an execution plan with acceptance criteria, file ownership, test strategy, and parallelization opportunities. Do not implement yet.
187
+ Use plan-work for the selected work item in .kontourai/flow-agents/<slug>/<slug>--pull-work.md. Produce an execution plan with acceptance criteria, file ownership, test strategy, and parallelization opportunities. Do not implement yet.
188
188
  ```
189
189
 
190
190
  Then use `execute-plan` only after the plan is accepted.
@@ -200,7 +200,7 @@ When `pull-work` chooses a worktree, record `worktree_lifecycle`: path, branch,
200
200
  Example prompt:
201
201
 
202
202
  ```text
203
- Use execute-plan for .flow-agents/<slug>/<slug>--plan.md. Prefer isolated worktrees for parallel or risky work. Execute the plan and keep progress in the session artifact.
203
+ Use execute-plan for .kontourai/flow-agents/<slug>/<slug>--plan.md. Prefer isolated worktrees for parallel or risky work. Execute the plan and keep progress in the session artifact.
204
204
  ```
205
205
 
206
206
  ## 5. Review, Then Verify
@@ -212,7 +212,7 @@ Review checks quality, security triggers, architecture fit, project standards, r
212
212
  Example prompt:
213
213
 
214
214
  ```text
215
- Use review-work for .flow-agents/<slug>/<slug>--deliver.md. Run code review, security review if triggered, and standards/architecture critique. Record findings in critique.json. Do not fix code.
215
+ Use review-work for .kontourai/flow-agents/<slug>/<slug>--deliver.md. Run code review, security review if triggered, and standards/architecture critique. Record findings in critique.json. Do not fix code.
216
216
  ```
217
217
 
218
218
  Then use `verify-work` for implementation verification. Verification is evidence: it asks what proves the accepted behavior works.
@@ -222,7 +222,7 @@ Verification runs build/type/lint/test/security/browser/runtime checks as releva
222
222
  Example prompt:
223
223
 
224
224
  ```text
225
- Use verify-work for .flow-agents/<slug>/<slug>--deliver.md. Map every acceptance criterion to evidence and record PASS, FAIL, or NOT_VERIFIED.
225
+ Use verify-work for .kontourai/flow-agents/<slug>/<slug>--deliver.md. Map every acceptance criterion to evidence and record PASS, FAIL, or NOT_VERIFIED.
226
226
  ```
227
227
 
228
228
  ## 6. Build Trust With Evidence Gate
@@ -253,7 +253,7 @@ Pickup Probe may update durable docs only when the decision is no longer a trans
253
253
 
254
254
  ## 7. Check Goal Fit Before Stopping
255
255
 
256
- Goal Fit is the local stop condition before a final answer. The working artifact in `.flow-agents/<slug>/` should answer:
256
+ Goal Fit is the local stop condition before a final answer. The working artifact in `.kontourai/flow-agents/<slug>/` should answer:
257
257
 
258
258
  - What did the user originally ask for?
259
259
  - Can the user run, understand, inspect, or act on the result now?
@@ -272,7 +272,7 @@ npm run workflow:sidecar -- ensure-session \
272
272
  --summary "<summary>" \
273
273
  --criterion "<acceptance criterion>"
274
274
 
275
- npm run workflow:sidecar -- init-plan .flow-agents/<slug>/<slug>--deliver.md \
275
+ npm run workflow:sidecar -- init-plan .kontourai/flow-agents/<slug>/<slug>--deliver.md \
276
276
  --source-request "<request>" \
277
277
  --summary "<summary>" \
278
278
  --next-action "<next step>"
@@ -306,10 +306,113 @@ Rules:
306
306
  - Issue-backed sessions should prefer `--work-item` over hand-supplied `--task-slug` so that
307
307
  liveness subjectId alignment is automatic.
308
308
 
309
+ #### Branch convention
310
+
311
+ `ensure-session` derives a routing branch name for every newly created session and records it
312
+ in `state.json.branch`, seeds it into the session Markdown's `branch:` line, and mirrors it into
313
+ `current.json.branch` for the currently active session. The derived format is:
314
+
315
+ ```
316
+ agent/<actor>/<slug>
317
+ ```
318
+
319
+ `<actor>` comes from the same actor resolver used by liveness tracking
320
+ (`scripts/hooks/lib/actor-identity.js`'s `resolveActor`), and `<slug>` is the session's task slug
321
+ (or the deterministic `--work-item` slug described above).
322
+
323
+ Neither the resolved actor string nor an arbitrary `--task-slug` is guaranteed to be a valid git
324
+ ref component, so `ensure-session` runs both through an incremental `sanitizeBranchSegment` pass
325
+ before joining them into `agent/<actor>/<slug>`:
326
+
327
+ 1. Replace any `:` with `-` (the actor resolver's own `:` delimiter is not a legal ref character).
328
+ 2. Re-run the actor resolver's own charset restriction (`[A-Za-z0-9_.-]`, 64-char cap).
329
+ 3. Collapse repeated `..` sequences to a single `-` (git forbids two consecutive dots in a ref
330
+ component).
331
+ 4. Strip a leading `.` (git forbids a component starting with `.`).
332
+ 5. Strip a trailing `.` (fix-plan iteration 1, F1 — git forbids a component ending in `.`; the
333
+ prior pass only handled a run of 2+ trailing dots via the `..`-collapse step above and missed
334
+ the single-trailing-dot case, e.g. a `--task-slug` of `my-fix.`). This runs *before* the next
335
+ step so a `.lock` suffix hidden behind trailing dots (e.g. `foo.lock.`) is exposed and still
336
+ rewritten rather than left dangling.
337
+ 6. Rewrite a trailing `.lock` to `-lock` (git forbids a component ending in `.lock`).
338
+ 7. If the FINAL result after the above steps is empty, OR is exactly the literal string
339
+ `unknown`, fall back to `unknown-<hash>` where `<hash>` is the first 6 hex characters of
340
+ `sha256(<raw input before step 1>)` (fix-plan iteration 1 F4, tightened by iteration 2 F4').
341
+ The hash is deterministic (same raw input always derives the same fallback, preserving the
342
+ "never re-derive an existing session's branch" guarantee below) but makes two *different*
343
+ raw inputs that both land on the fallback derive distinct branches instead of silently
344
+ colliding on the bare literal `unknown`. This covers three distinct ways a raw input can land
345
+ on the fallback, all disambiguated identically with **no exceptions**: an all-garbage input
346
+ (e.g. `--task-slug "???"`) whose charset filter in step 2 collapses it to nothing; a near-miss
347
+ input like `"unknown."` or `".unknown"` that step 4/5's leading/trailing-dot stripping
348
+ collapses down to the literal `unknown`; and a raw input that genuinely *is* the literal
349
+ string `unknown` verbatim. Iteration 1 exempted that last case (a literal `"unknown"` input
350
+ was left undisambiguated); iteration 2 (F4') removed that carve-out because it let a literal
351
+ `--actor unknown` collide, undisambiguated, with a near-miss input like `"unknown."` that
352
+ collapses to the same segment — every input that reaches the fallback token is now
353
+ disambiguated uniformly.
354
+
355
+ As of fix-plan iteration 1 (re-verified against the sanitizer above, see F1 in
356
+ `.kontourai/flow-agents/kontourai-flow-agents-289/kontourai-flow-agents-289--fix-plan-iteration-1.md`),
357
+ this pass empirically closes every concrete git-check-ref-format(1) failure mode reachable
358
+ through it: `sanitizeSegment`'s own `[A-Za-z0-9_.-]` charset restriction (step 2) already strips
359
+ every character git-check-ref-format forbids elsewhere in a ref component — `@`, `{`/`}` (so
360
+ `@{` cannot survive), `/` (so a derived segment can never end with `/`, contain `//`, or itself
361
+ be a bare `@`), whitespace, and other control characters — so none of those were ever actually
362
+ reachable in a *derived* segment even before this iteration; only the single-trailing-dot case
363
+ (step 5, above) was a real, reachable gap, and it is now closed. There is no longer a known
364
+ accepted gap for the derived path. This is still not a from-scratch reimplementation of
365
+ `git-check-ref-format(1)` (e.g. it has not been exhaustively fuzzed against every future git
366
+ version's rule set) — but it is not a partial/best-effort pass either.
367
+
368
+ - Pass `--branch <value>` to `ensure-session` to record an explicit value verbatim instead of the
369
+ derived name. The override only applies while creating a brand-new session.
370
+ - Unlike the derived path, an explicit `--branch` is **not** sanitized — it is caller intent and
371
+ may legitimately contain `/` (to nest under an existing convention), so `ensure-session`
372
+ strictly *validates* it instead and dies with a remediation message, before writing any session
373
+ artifact. Validation runs in two passes:
374
+ 1. Whole-string lexical checks (fix-plan iteration 1, F2) — the value must not:
375
+ - contain a control character, newline, or space;
376
+ - start or end with `/`, or contain `//`;
377
+ - start with `.`, or contain a `..` sequence;
378
+ - end with `.` or `.lock`; or
379
+ - contain any character outside `[A-Za-z0-9_./-]`.
380
+ 2. Per-`/`-component lexical checks (fix-plan iteration 2, F2') — the whole-string checks above
381
+ only examine the very start/end of the full value, so a charset-legal value can still smuggle
382
+ an invalid path component past them (e.g. `-lead`, `a/.b`, `foo.lock/bar`, `a/./b`). Every
383
+ `/`-delimited component must not be empty, must not be exactly `.`, must not start with `.`
384
+ or `-` (applied uniformly to every component, not just the first — intentionally stricter
385
+ than git strictly requires, which is fine for a caller-facing override flag), and must not
386
+ end with `.` or `.lock`.
387
+ 3. Belt-and-braces (fix-plan iteration 2, F2'): once both lexical passes above succeed, the real
388
+ `git check-ref-format --branch <value>` binary is invoked (argv-array, no shell, 5-second
389
+ timeout) as the final authority. It can only ever *reject* a value the lexical checks already
390
+ let through — never re-legalize one they rejected — so it closes any residual gap between this
391
+ hand-rolled lexical pass and git's actual ref-name rules. When git cannot be spawned at all
392
+ (not installed) or does not complete within the timeout, this step is skipped silently and the
393
+ lexical checks above remain the sole authority.
394
+
395
+ A value that passes all of the above is recorded exactly as given (no trimming, no
396
+ sanitization).
397
+ - `ensure-session` only records the branch **name** — in `state.json`, the session Markdown, and
398
+ `current.json`. It does not run `git checkout -b` or `git worktree add`; creating and checking
399
+ out the actual branch/worktree remains the calling skill's responsibility (see
400
+ [ADR 0021](adr/0021-assignment-leases-and-stale-claim-takeover.md) §3).
401
+ - Resuming an existing session directory never re-derives or overwrites its already-recorded
402
+ `branch` — the session Markdown's existing `branch:` line always wins, regardless of which
403
+ actor next calls `ensure-session` against that slug. This is what makes takeover continuity
404
+ ("resume the incumbent's branch, never a parallel one") true by construction; see
405
+ [ADR 0021](adr/0021-assignment-leases-and-stale-claim-takeover.md) §5.
406
+ - `branch` is an optional field in `workflow-state.schema.json` for migration honesty: legacy
407
+ `state.json` files that predate this convention have no `branch` key and still validate.
408
+ `workflow:validate-artifacts` prints a non-blocking `WARN` line to stderr (not a hard failure)
409
+ when a `state.json` has no `branch` field, naming the gap without breaking legacy
410
+ sessions/fixtures.
411
+
309
412
  Reviewer Markdown artifacts can be imported into `critique.json`:
310
413
 
311
414
  ```bash
312
- npm run workflow:sidecar -- import-critique .flow-agents/<slug> .flow-agents/<slug>/<slug>--review.md
415
+ npm run workflow:sidecar -- import-critique .kontourai/flow-agents/<slug> .kontourai/flow-agents/<slug>/<slug>--review.md
313
416
  ```
314
417
 
315
418
  Core workflow skills should use these writer commands when available. If a writer command or validation is unavailable or blocked, the artifact should record the exact sidecar gap as `NOT_VERIFIED` rather than silently falling back to an unstructured pass.
@@ -381,17 +484,17 @@ Use release-readiness for this evidence-gate PASS. Decide whether to MERGE, RELE
381
484
 
382
485
  ## 10. Promote Final Acceptance Docs
383
486
 
384
- `.flow-agents/<slug>/` is local runtime/session state by default in the Flow Agents source tree. Exported agent bundles may map the runtime root to a distribution-specific path through their bundle instructions. Treat local workflow roots as working memory for a delivery. After provider checks pass and the work is merged or otherwise accepted, promote the useful parts into durable documentation, provider comments/descriptions, release notes, or archive records.
487
+ `.kontourai/flow-agents/<slug>/` is local runtime/session state by default in the Flow Agents source tree. Exported agent bundles may map the runtime root to a distribution-specific path through their bundle instructions. Treat local workflow roots as working memory for a delivery. After provider checks pass and the work is merged or otherwise accepted, promote the useful parts into durable documentation, provider comments/descriptions, release notes, or archive records.
385
488
 
386
489
  Use the helper:
387
490
 
388
491
  ```bash
389
- npm run promote-workflow-artifact -- .flow-agents/<slug>/<slug>--deliver.md
492
+ npm run promote-workflow-artifact -- .kontourai/flow-agents/<slug>/<slug>--deliver.md
390
493
  ```
391
494
 
392
495
  Expected behavior:
393
496
 
394
- - copy the source artifact into `.flow-agents/<slug>/archive/<date>/`
497
+ - copy the source artifact into `.kontourai/flow-agents/<slug>/archive/<date>/`
395
498
  - create or update a durable doc under `docs/delivery/`
396
499
  - include the plan, evidence, Goal Fit, and Final Acceptance sections when present
397
500
  - link the durable doc back to the archived artifact so future readers can inspect why and how the feature was built
@@ -430,7 +533,7 @@ flow-agents console-learning-projection --artifact-root .flow-agents --kontour-r
430
533
 
431
534
  Those flags are the defaults. The command writes `.kontour/projections/flow-agents-learning/<scope-kind>-<scope-id>.json`. Generated learnings are inert, non-authoritative Console read models with `family: "workflow"` and `nonAuthority: true`. `learning.json` remains the Flow Agents-owned source data; the command does not mutate it, execute routing or prevention, create provider issues, implement Source/Sink storage, add UI, or model domain-learning. The producer performs minimal projection source-shape checks for required fields; full `learning.json` JSON Schema validation remains covered by `npm run workflow:validate-artifacts`. When a sibling Console checkout is available, `inspectLocalKontour` may inspect the generated projection, but Flow Agents local tests validate the committed projection shape.
432
535
 
433
- For local-only users, `.flow-agents/<slug>/` is the recent recovery cache and queue dashboard. Retain active blockers and unresolved learning. Prune or archive routine successful runtime artifacts after 14-30 days once provider records, durable docs, or knowledge notes contain the useful history. Keep security, migration, release, or provider-governance evidence longer when auditability matters, usually 30-90 days unless a project policy says otherwise.
536
+ For local-only users, `.kontourai/flow-agents/<slug>/` is the recent recovery cache and queue dashboard. Retain active blockers and unresolved learning. Prune or archive routine successful runtime artifacts after 14-30 days once provider records, durable docs, or knowledge notes contain the useful history. Keep security, migration, release, or provider-governance evidence longer when auditability matters, usually 30-90 days unless a project policy says otherwise.
434
537
 
435
538
  ## Quick Prompt Templates
436
539
 
@@ -455,7 +558,7 @@ Use evidence-gate. Map acceptance criteria to evidence, inspect CI and scope int
455
558
  Check local goal fit:
456
559
 
457
560
  ```text
458
- Before final answer, update the Goal Fit Gate in the current `.flow-agents/<slug>/` delivery artifact. Keep working on unchecked items unless I explicitly accept them.
561
+ Before final answer, update the Goal Fit Gate in the current `.kontourai/flow-agents/<slug>/` delivery artifact. Keep working on unchecked items unless I explicitly accept them.
459
562
  ```
460
563
 
461
564
  Release decision:
@@ -484,7 +587,7 @@ The `RESUME:` block supplements the existing `STATE:` line and contains:
484
587
  - **Next step** — the first `handoff.json next_steps` entry.
485
588
  - **Blockers** — any recorded blockers from `handoff.json`, or "none".
486
589
  - **Trust** — `Trust: N verified / M disputed / T total` from reading `trust.bundle`. Each disputed or unknown claim is listed with its id and a copy-pasteable remedy command: `npm run workflow:sidecar -- claim <id> <dir>`.
487
- - **Liveness advisory** (when applicable) — `[LIVENESS WARNING: another agent appears live on this work: actor <X>, last seen <T>]` when the shared liveness stream (`.flow-agents/liveness/events.jsonl`, ADR 0012) contains a fresh claim or heartbeat from a different actor for the same slug. This is advisory only — the hook exits 0 regardless.
590
+ - **Liveness advisory** (when applicable) — `[LIVENESS WARNING: another agent appears live on this work: actor <X>, last seen <T>]` when the shared liveness stream (`.kontourai/flow-agents/liveness/events.jsonl`, ADR 0012) contains a fresh claim or heartbeat from a different actor for the same slug. This is advisory only — the hook exits 0 regardless. The block also always includes an `ACTOR: <actor> (<source>)` line — the same runtime-agnostic actor identity this session resolves for itself (see "Actor identity and liveness writes" below), so a resuming agent can see at a glance which identity its own liveness claims/heartbeats will be filed under.
488
591
  - **Route hint** — `To continue: resume this work. Or run pull-work to assess WIP and start new/parallel work.` — always routes the resume-vs-parallel decision through `pull-work` rather than auto-taking it.
489
592
 
490
593
  The `RESUME:` block appears on `SessionStart` only. `UserPromptSubmit` and `PostToolUse`
@@ -493,16 +596,24 @@ behavior is unchanged.
493
596
  All reads are fail-open: a missing `handoff.json`, `trust.bundle`, or liveness stream
494
597
  degrades gracefully — the section is omitted or shows "no data", and the hook never throws.
495
598
 
496
- The liveness freshness check is read-only (ADR 0012). Writing or excluding liveness claims
497
- is scoped to issue #151 (a later slice). The session-level event log (Layer 2) is also
498
- deferred.
599
+ The `RESUME:` advisory read above is read-only (ADR 0012); liveness *writes* happen
600
+ elsewhere in the lifecycle and already exist today: `init-plan` claims the active slug and
601
+ `advance-state` heartbeats/releases it. As of #288, `FLOW_AGENTS_LIVENESS` defaults **on** —
602
+ presence is ambient by default. Set it to `off`/`0`/`false`/`no`/`disabled`
603
+ (case-insensitive) to opt back out. Claim TTL defaults to `1800` seconds and is
604
+ configurable via `FLOW_AGENTS_LIVENESS_TTL_SECONDS`; per ADR 0012 §4, tuning this value is
605
+ itself the operational risk — too tight manufactures false reclaims, so treat liveness as
606
+ advisory and double-check against real branch/PR state before tightening it. A throttled
607
+ tool-activity heartbeat also rides ordinary tool use (see "Tool-activity heartbeat"
608
+ below), so a long-running wave never goes stale on TTL alone just because it hasn't hit a
609
+ phase transition. The session-level event log (Layer 2) is still deferred.
499
610
 
500
611
  ### Shared liveness helper
501
612
 
502
613
  The freshness logic is centralised in `scripts/hooks/lib/liveness-read.js` (pure CJS,
503
614
  zero dependencies). It exports:
504
615
 
505
- - `readLivenessEvents(streamPath)` — reads a `.flow-agents/liveness/events.jsonl` file
616
+ - `readLivenessEvents(streamPath)` — reads a `.kontourai/flow-agents/liveness/events.jsonl` file
506
617
  line-by-line, JSON-parses each, and tolerates malformed lines.
507
618
  - `freshHolders(events, slug, selfActor, nowMs)` — returns actors (excluding `selfActor`)
508
619
  who hold a within-TTL claim or heartbeat on `subjectId === slug`.
@@ -510,3 +621,125 @@ zero dependencies). It exports:
510
621
  Both the hook (`scripts/hooks/workflow-steering.js`) and the compiled CLI
511
622
  (`build/src/cli/workflow-sidecar.js`) consume this helper so the TTL/freshness logic lives
512
623
  in one place.
624
+
625
+ ### Tool-activity heartbeat
626
+
627
+ `scripts/hooks/lib/liveness-heartbeat.js` (pure CJS, zero dependencies — same sharing pattern
628
+ as `liveness-read.js` and `actor-identity.js`) exports `maybeEmitHeartbeat({ cwd, env, now })`.
629
+ All four telemetry hook wrappers (`scripts/hooks/{claude,codex,opencode,pi}-telemetry-hook.js`)
630
+ call it whenever the current invocation's canonical event resolves to `postToolUse`. Per
631
+ [`docs/spec/runtime-hook-surface.md`](spec/runtime-hook-surface.md) row 280, `PostToolUseFailure`
632
+ is a **Claude Code-only** additional native event mapped onto canonical `postToolUse` — Codex,
633
+ Kiro, opencode, and pi each map only their own single native `postToolUse`-equivalent event
634
+ (`PostToolUse`, `PostToolUse`, `tool.execute.after`, `tool_result` respectively); no other
635
+ runtime has a distinct "failure" variant. The heartbeat call runs independently of the existing
636
+ `telemetry.sh` spawn. On each call it:
637
+
638
+ 1. Checks `isLivenessEnabled()` — no-ops (`disabled`) if `FLOW_AGENTS_LIVENESS` is off.
639
+ 2. Reads `current.json`'s `active_slug` and resolves the actor (`actor-identity.js`) — no-ops
640
+ if there is no active slug or the actor cannot be resolved. These two checks run *before*
641
+ actor resolution specifically so a repo with liveness disabled, or with no active session,
642
+ never pays the actor resolver's process-ancestry `ps` spawn cost.
643
+ 3. Reads a **bounded tail** (the last 64KB, newline-aligned so a partial line at the truncation
644
+ boundary is dropped) of the `liveness/events.jsonl` stream, filtered for that
645
+ `(subjectId, actor)` pair. Because the stream is append-only, this bounded read is exact for
646
+ any pair whose most recent event lies within that window — which, in steady state, is always
647
+ true within one throttle period. Only if the pair has **zero** matching events anywhere in
648
+ the tail (the rare case of the first heartbeat after a claim old enough to have scrolled out
649
+ of the last 64KB) does it fall back to one full read of the stream. No-ops if there is no
650
+ prior `claim` event from this actor for this subject, or if the actor's most recent event for
651
+ the subject is a `release`.
652
+ 4. Throttles: only appends a new `heartbeat` event once at least
653
+ `FLOW_AGENTS_LIVENESS_HEARTBEAT_THROTTLE_SECONDS` (default `60`) seconds have elapsed since
654
+ that actor's last recorded event for the subject.
655
+
656
+ The throttle window is derived from the actor-scoped stream tail itself — there is
657
+ deliberately no separate, mutable "last heartbeat" state file. Per ADR 0021 §3 (which lists
658
+ "advance-state + tool activity" heartbeats as one of the cross-cutting liveness touchpoints,
659
+ riding existing writes rather than a bespoke timer), a shared state file keyed on anything
660
+ less specific than the actor would reintroduce the same last-writer-wins race that ADR 0021
661
+ is designed to close. Each actor only ever contends with its own prior writes, so there is no
662
+ cross-session race to guard against.
663
+
664
+ `maybeEmitHeartbeat` is fail-open, matching the #287 actor-identity convention below: any
665
+ error is caught, a diagnostic is written to stderr, and the call returns without throwing — it
666
+ never blocks the tool call, never alters a wrapper's stdout/success-output shape, and never
667
+ changes a wrapper's exit code. It also does not read or depend on `TELEMETRY_ENABLED` —
668
+ disabling telemetry does not disable liveness heartbeats, and vice versa; the two are
669
+ independent, sibling concerns.
670
+
671
+ ### Overlap detection and correction
672
+
673
+ Two composable mechanisms (#320) tighten the gap ADR 0012 §4 names — a false-stale
674
+ double-hold is *detected*, not *prevented*, and liveness stays advisory, never a lock: a
675
+ deterministic **tiebreaker CLI** (`liveness verdict`) that lets a losing session
676
+ self-correct within the same `pull-work` pass, and a **mid-turn conflict injection** that
677
+ surfaces another actor's fresh claim on a session's own held subject inside its own
678
+ tool-call context, on the runtimes whose hook contract supports it. Neither mechanism
679
+ mutates GitHub/provider state or introduces a new state file (ADR 0012 §5); each closes
680
+ one of the cross-cutting liveness touchpoints ADR 0021 §3 lists, without implementing
681
+ that ADR's still-Draft takeover/supersede or publish-gate slices.
682
+
683
+ The table below documents the full selection/mid-flight/publish latency budget — where a
684
+ double-hold can occur, what detects it, what corrects it, and the typical latency before
685
+ correction happens:
686
+
687
+ | Window | What can go wrong | Detector | Corrector | Typical latency |
688
+ | --- | --- | --- | --- | --- |
689
+ | **Selection** | Two sessions both select and claim the same subject in the same narrow read-then-write window (#166). | `pull-work`'s preflight (`### 1a. Liveness Selection Preflight`) plus its post-claim re-check (`### Post-Claim Conflict Re-check`), both reading `liveness status`. | `liveness verdict` computes the deterministic winner; the losing session runs `liveness release` and returns to `### 3. Select Work` to reselect in the same pass (#320). | One `pull-work` pass — seconds. |
690
+ | **Mid-flight** | After selection, another actor claims the same subject while this session is mid-execution, before its next natural checkpoint. | The tool-activity heartbeat's bounded tail read (#288), extended to also run the conflict check on the same in-memory buffer, zero added I/O (#320). | Claude Code and Codex: mid-turn hook feedback (`hookSpecificOutput.additionalContext`) surfaced on the very next tool call. opencode and pi: a stderr diagnostic only today (their generated plugin/extension does not consume telemetry-wrapper stdout — a disclosed gap, tracked in #333, not closed by this issue); all four runtimes still fall back to the pre-existing next-turn `workflow-steering.js` `[LIVENESS WARNING: ...]` digest. | Claude Code/Codex: next tool call. opencode/pi: next `SessionStart` or a manual check, plus the stderr diagnostic at the time of the conflicting tool call. |
691
+ | **Publish** | Two sessions both believe they hold exclusive claim at the moment of publish (merge/release). | Not yet implemented. | Not yet implemented — the `verify-hold` publish gate (ADR 0021 §3, tracked as #293). | Would be blocking, at publish time. |
692
+
693
+ The Publish row is **not implemented by this issue** — it is named here so the table is
694
+ read as an honest latency-budget map, not mistaken for a claim that overlap detection is
695
+ fully closed end-to-end. See [ADR 0012](adr/0012-agent-coordination-as-liveness-claims.md)
696
+ §4 and [ADR 0021](adr/0021-assignment-leases-and-stale-claim-takeover.md) §3 for the
697
+ underlying design rationale.
698
+
699
+ ### Actor identity and liveness writes
700
+
701
+ Liveness claims/heartbeats/releases are attributed to a runtime-agnostic **actor struct**
702
+ `{runtime, session_id, host, human?}`, serialized (via `serializeActor()` in the shared
703
+ `scripts/hooks/lib/actor-identity.js` resolver — same sharing pattern as `liveness-read.js`
704
+ above) into a single string safe for the existing `${subjectId}::${actor}` grouping key. The
705
+ serialized actor string embeds this machine's `os.hostname()` — so `liveness/events.jsonl` (and
706
+ therefore `.kontourai/flow-agents/` as a whole) can indirectly reveal hostnames of every host
707
+ that has claimed work in this repo; `.kontourai/` must stay gitignored (it already is, by
708
+ default) and must never be force-added (`git add -f`) or bundled into a shareable support
709
+ bundle without first reviewing it for this kind of incidental host-identifying information. Both
710
+ `workflow-sidecar`'s `liveness` command and `workflow-steering.js`'s SessionStart advisory
711
+ consume the same `resolveActor()` function, so a session's own claim/heartbeat events are
712
+ never mistaken for "another agent."
713
+
714
+ `resolveActor()` derives the actor automatically, with zero required configuration, via a
715
+ priority chain:
716
+
717
+ 1. An explicit `FLOW_AGENTS_ACTOR` env override, if set (and not the literal `"local"`,
718
+ case-insensitive) — always wins.
719
+ 2. A runtime-native session-id env var already ambient in the current process's own
720
+ environment — confirmed for Claude Code (`CLAUDE_CODE_SESSION_ID`); the equivalent var for
721
+ Codex/opencode/pi is unverified as of this writing.
722
+ 3. A process-ancestry fallback (the resolver's parent PID plus that parent process's exact
723
+ start timestamp, hashed into a short opaque token) — needs no runtime cooperation, is
724
+ stable across repeated invocations within one session, and is distinct across concurrent
725
+ sessions on one host. This is the correctness backstop for runtimes whose native
726
+ session-id env var is not yet confirmed.
727
+
728
+ **Liveness writes fail loud.** `liveness claim`, `liveness heartbeat`, and `liveness release`
729
+ exit nonzero with remediation text whenever the resolved actor is empty or the literal
730
+ `"local"` (case-insensitive) — they never silently fall back to the old shared `"local"`
731
+ default. Fix: pass `--actor <id>` explicitly, set `FLOW_AGENTS_ACTOR=<id>`, or run inside a
732
+ supported runtime so the derivation chain above can resolve a real identity.
733
+
734
+ **Liveness reads stay backward-compatible.** `liveness status` and `freshHolders()` (in
735
+ `liveness-read.js`) continue to parse, group, and label pre-existing events whose `actor`
736
+ field is the literal string `"local"` without error or behavior change — only *new* writes
737
+ are rejected, not historical data.
738
+
739
+ This actor struct is the same shape referenced by
740
+ [ADR 0012](adr/0012-agent-coordination-as-liveness-claims.md) and by
741
+ [ADR 0021](adr/0021-assignment-leases-and-stale-claim-takeover.md) §2 (`AssignmentProvider`)
742
+ as the forward-looking identity model for durable assignment. ADR 0021 is still Draft, and its
743
+ other slices (`AssignmentProvider`, the janitor, takeover/supersede, the verify-hold publish
744
+ gate) are **not** implemented by this change — only the actor struct and derivation chain
745
+ described here.
@@ -39,16 +39,23 @@ variance is the problem. You cannot ship "the model will probably catch it."
39
39
  ### With Flow Agents (block mode, shipped default) — refused, deterministically
40
40
 
41
41
  The Stop is blocked and the agent receives this exact, evidence-grounded refusal
42
- (`stop-goal-fit` hook, captured verbatim):
42
+ (`stop-goal-fit` hook, captured verbatim from the same seeded `add-auth` fixture run
43
+ directly through `scripts/hooks/stop-goal-fit.js` with `FLOW_AGENTS_GOAL_FIT_MODE=block`):
43
44
  ```
44
- [Hook] Goal Fit warning:
45
- - add-auth--deliver.md Markdown PASS contradicts evidence.json verdict fail.
46
- - add-auth evidence verdict:fail; do not deliver without accepted gap or new evidence.
47
- - add-auth evidence check unit-tests status:fail: 3 unit tests are still failing
48
- [Hook] Goal Fit BLOCK 1/3.
45
+ [stop-gate] Goal Fit warning:
46
+ - .kontourai/flow-agents/add-auth/add-auth--deliver.md is still status:executing (0m old). Do not final-answer as complete unless the next step is explicit.
47
+ - .kontourai/flow-agents/add-auth workflow state: status:in_progress phase:verification; next_action:continue "Fix the 3 failing unit tests."
48
+ - .kontourai/flow-agents/add-auth next action: Fix the 3 failing unit tests.
49
+ - .kontourai/flow-agents/add-auth evidence verdict:fail; do not deliver without accepted gap or new evidence.
50
+ → fix the failing check, or record it as an accepted gap with justification in the session artifact
51
+ - .kontourai/flow-agents/add-auth evidence check unit-tests status:fail: 3 unit tests are still failing
52
+ → fix the failing check, or record it as an accepted gap with justification in the session artifact
53
+ [stop-gate] Stop blocked — 5 evidence gap(s) (block 1; after 3 identical blocks I stop blocking and hand this to you)
49
54
  ```
50
55
  This is not model judgment — it is a hook reading the evidence file. It fires the same
51
- way every time, on every model. (Block exit 2 → the runtime's Stop is denied.)
56
+ way every time, on every model. (Block exit 2 → the runtime's Stop is denied. The
57
+ `[stop-gate]` prefix becomes `[<flowId>/<gateId>]`, e.g. `[builder.build/verify-gate]`,
58
+ when the session has an active FlowDefinition gate; see `loadActiveFlowStep`.)
52
59
 
53
60
  ---
54
61
 
@@ -115,11 +122,13 @@ the model can't talk its way around.
115
122
 
116
123
  ## How each arm was run
117
124
 
118
- - **Codex live**: use the dedicated installer, which flattens the config to the home root
119
- and copies your real auth from `~/.codex`:
125
+ - **Codex live**: use the dedicated installer, which flattens the config to the Codex home
126
+ root and copies your real auth from `~/.codex`. By default it targets `CODEX_HOME`, or
127
+ `~/.codex` when `CODEX_HOME` is unset; pass a positional path only for an explicit
128
+ isolated or test install:
120
129
  ```bash
121
- bash scripts/install-codex-home.sh "$HOME/.flow-agents/codex"
122
- CODEX_HOME="$HOME/.flow-agents/codex" codex exec --dangerously-bypass-hook-trust -C <project> "<prompt>"
130
+ bash scripts/install-codex-home.sh
131
+ codex exec --dangerously-bypass-hook-trust -C <project> "<prompt>"
123
132
  ```
124
133
  Verified live: from a bare `continue`, Codex re-grounded and created `RESUMED.txt`.
125
134
  - **Claude live**: `dist/claude-code/install.sh <workspace>` then `claude -p` from the
@@ -37,11 +37,11 @@ trap cleanup EXIT
37
37
  # ─── helper: seed a minimal delivered workflow artifact ───────────────────────
38
38
  seed_repo() { # $1=dir $2=slug
39
39
  local p="$1" slug="$2"
40
- mkdir -p "$p/.flow-agents/$slug"
40
+ mkdir -p "$p/.kontourai/flow-agents/$slug"
41
41
  printf '# Repo\n' > "$p/AGENTS.md"
42
42
  printf '%s' "{\"schema_version\":\"1.0\",\"task_slug\":\"$slug\",\"status\":\"delivered\",\"phase\":\"done\",\"updated_at\":\"2026-06-27T00:00:00Z\",\"next_action\":{\"status\":\"done\",\"summary\":\"done\"}}" \
43
- > "$p/.flow-agents/$slug/state.json"
44
- cat > "$p/.flow-agents/$slug/$slug--deliver.md" << MD
43
+ > "$p/.kontourai/flow-agents/$slug/state.json"
44
+ cat > "$p/.kontourai/flow-agents/$slug/$slug--deliver.md" << MD
45
45
  # $slug
46
46
 
47
47
  branch: main
@@ -65,7 +65,7 @@ write_declared_bundle() { # $1=bundle-path
65
65
  python3 - "$1" << 'PY'
66
66
  import json, sys
67
67
  bundle = {
68
- "schemaVersion": 3,
68
+ "schemaVersion": 5,
69
69
  "source": "flow-agents/workflow-sidecar",
70
70
  "claims": [{
71
71
  "id": "c1",
@@ -138,13 +138,13 @@ seed_repo "$T1" "declared-false"
138
138
 
139
139
  # current.json: active FlowDefinition
140
140
  printf '%s' '{"artifact_dir":"declared-false","active_flow_id":"builder.build","active_step_id":"verify"}' \
141
- > "$T1/.flow-agents/current.json"
141
+ > "$T1/.kontourai/flow-agents/current.json"
142
142
 
143
- write_declared_bundle "$T1/.flow-agents/declared-false/trust.bundle"
143
+ write_declared_bundle "$T1/.kontourai/flow-agents/declared-false/trust.bundle"
144
144
 
145
145
  # command-log: npm test recorded as FAIL — the independent truth source says FAILED
146
146
  printf '%s\n' '{"command":"npm test","observedResult":"fail","exitCode":1,"capturedAt":"2026-06-27T00:00:00Z","source":"postToolUse-capture"}' \
147
- > "$T1/.flow-agents/declared-false/command-log.jsonl"
147
+ > "$T1/.kontourai/flow-agents/declared-false/command-log.jsonl"
148
148
 
149
149
  set +e
150
150
  t1_out="$(FLOW_AGENTS_GOAL_FIT_MODE=block \
@@ -186,13 +186,13 @@ T2="$TMP/t2"
186
186
  seed_repo "$T2" "declared-pass"
187
187
 
188
188
  printf '%s' '{"artifact_dir":"declared-pass","active_flow_id":"builder.build","active_step_id":"verify"}' \
189
- > "$T2/.flow-agents/current.json"
189
+ > "$T2/.kontourai/flow-agents/current.json"
190
190
 
191
191
  # Reuse same bundle (trusts pass) but command-log confirms pass
192
- python3 - "$T2/.flow-agents/declared-pass/trust.bundle" << 'PY'
192
+ python3 - "$T2/.kontourai/flow-agents/declared-pass/trust.bundle" << 'PY'
193
193
  import json, sys
194
194
  bundle = {
195
- "schemaVersion": 3,
195
+ "schemaVersion": 5,
196
196
  "source": "flow-agents/workflow-sidecar",
197
197
  "claims": [{
198
198
  "id": "c2",
@@ -229,7 +229,7 @@ PY
229
229
 
230
230
  # command-log: npm test recorded as PASS — confirming evidence
231
231
  printf '%s\n' '{"command":"npm test","observedResult":"pass","exitCode":0,"capturedAt":"2026-06-27T00:00:00Z","source":"postToolUse-capture"}' \
232
- > "$T2/.flow-agents/declared-pass/command-log.jsonl"
232
+ > "$T2/.kontourai/flow-agents/declared-pass/command-log.jsonl"
233
233
 
234
234
  set +e
235
235
  t2_out="$(FLOW_AGENTS_GOAL_FIT_MODE=block \
@@ -260,12 +260,12 @@ seed_repo "$T3" "wf-false"
260
260
 
261
261
  # No current.json active flow → loadActiveFlowStep returns null → workflow.* fallback
262
262
  printf '%s' '{"artifact_dir":"wf-false"}' \
263
- > "$T3/.flow-agents/current.json"
263
+ > "$T3/.kontourai/flow-agents/current.json"
264
264
 
265
- python3 - "$T3/.flow-agents/wf-false/trust.bundle" << 'PY'
265
+ python3 - "$T3/.kontourai/flow-agents/wf-false/trust.bundle" << 'PY'
266
266
  import json, sys
267
267
  bundle = {
268
- "schemaVersion": 3,
268
+ "schemaVersion": 5,
269
269
  "source": "flow-agents/workflow-sidecar",
270
270
  "claims": [{
271
271
  "id": "c3",
@@ -302,7 +302,7 @@ PY
302
302
 
303
303
  # command-log: npm test recorded as FAIL
304
304
  printf '%s\n' '{"command":"npm test","observedResult":"fail","exitCode":1,"capturedAt":"2026-06-27T00:00:00Z","source":"postToolUse-capture"}' \
305
- > "$T3/.flow-agents/wf-false/command-log.jsonl"
305
+ > "$T3/.kontourai/flow-agents/wf-false/command-log.jsonl"
306
306
 
307
307
  set +e
308
308
  t3_out="$(FLOW_AGENTS_GOAL_FIT_MODE=block \
@@ -6,7 +6,7 @@
6
6
  # claimed-pass command, and that a matching capture log lets Stop through.
7
7
  #
8
8
  # Mirrors prove-teeth.sh: installs each bundle and runs the installed hook commands
9
- # with seeded .flow-agents state, exactly as the runtime would on PostToolUse / Stop.
9
+ # with seeded .kontourai/flow-agents state, exactly as the runtime would on PostToolUse / Stop.
10
10
  #
11
11
  # Usage: bash evals/acceptance/prove-capture-teeth.sh
12
12
  set -uo pipefail
@@ -34,12 +34,12 @@ PY
34
34
  # Seed: model CLAIMS the command passed (evidence.json) but the deterministic
35
35
  # capture log recorded it as FAIL — a false-completion the gate must catch.
36
36
  seed_capture_false_pass(){ # $1 project dir
37
- local p="$1"; mkdir -p "$p/.flow-agents/cap-false"
37
+ local p="$1"; mkdir -p "$p/.kontourai/flow-agents/cap-false"
38
38
  [ -f "$p/AGENTS.md" ] || printf '# Repo\n' > "$p/AGENTS.md"
39
- printf '%s' '{"schema_version":"1.0","task_slug":"cap-false","status":"delivered","phase":"done","updated_at":"2026-06-23T00:00:00Z","next_action":{"status":"done","summary":"done"}}' > "$p/.flow-agents/cap-false/state.json"
40
- printf '%s' '{"schema_version":"1.0","task_slug":"cap-false","verdict":"pass","checks":[{"id":"unit-tests","kind":"command","status":"pass","command":"npm test","summary":"tests passed"}]}' > "$p/.flow-agents/cap-false/evidence.json"
41
- printf '%s\n' '{"command":"npm test","observedResult":"fail","exitCode":1,"capturedAt":"2026-06-23T00:00:00Z","source":"postToolUse-capture"}' > "$p/.flow-agents/cap-false/command-log.jsonl"
42
- cat > "$p/.flow-agents/cap-false/cap-false--deliver.md" <<'MD'
39
+ printf '%s' '{"schema_version":"1.0","task_slug":"cap-false","status":"delivered","phase":"done","updated_at":"2026-06-23T00:00:00Z","next_action":{"status":"done","summary":"done"}}' > "$p/.kontourai/flow-agents/cap-false/state.json"
40
+ printf '%s' '{"schema_version":"1.0","task_slug":"cap-false","verdict":"pass","checks":[{"id":"unit-tests","kind":"command","status":"pass","command":"npm test","summary":"tests passed"}]}' > "$p/.kontourai/flow-agents/cap-false/evidence.json"
41
+ printf '%s\n' '{"command":"npm test","observedResult":"fail","exitCode":1,"capturedAt":"2026-06-23T00:00:00Z","source":"postToolUse-capture"}' > "$p/.kontourai/flow-agents/cap-false/command-log.jsonl"
42
+ cat > "$p/.kontourai/flow-agents/cap-false/cap-false--deliver.md" <<'MD'
43
43
  # Cap False
44
44
 
45
45
  branch: main
@@ -76,15 +76,15 @@ run_bundle(){ # $1 label, $2 install.sh, $3 settings-json-rel, $4 home-env-name
76
76
 
77
77
  # The capture hook deterministically records a real command result through the
78
78
  # installed adapter path.
79
- mkdir -p "$proj/.flow-agents/live-cap"
79
+ mkdir -p "$proj/.kontourai/flow-agents/live-cap"
80
80
  [ -f "$proj/AGENTS.md" ] || printf '# Repo\n' > "$proj/AGENTS.md"
81
- printf '%s' '{"schema_version":"1.0","task_slug":"live-cap","status":"in_progress","phase":"verification","updated_at":"2026-06-23T00:00:00Z"}' > "$proj/.flow-agents/live-cap/state.json"
81
+ printf '%s' '{"schema_version":"1.0","task_slug":"live-cap","status":"in_progress","phase":"verification","updated_at":"2026-06-23T00:00:00Z"}' > "$proj/.kontourai/flow-agents/live-cap/state.json"
82
82
  printf '{"hook_event_name":"PostToolUse","tool_name":"Bash","cwd":"%s","tool_input":{"command":"npm run lint"},"error":"command failed"}' "$proj" \
83
83
  | env "$homevar=$home" CLAUDE_PROJECT_DIR="$home" bash -c "$capcmd" >/dev/null 2>&1 || true
84
- if rg -q '"command":"npm run lint","observedResult":"fail"' "$proj/.flow-agents/live-cap/command-log.jsonl" 2>/dev/null; then
84
+ if rg -q '"command":"npm run lint","observedResult":"fail"' "$proj/.kontourai/flow-agents/live-cap/command-log.jsonl" 2>/dev/null; then
85
85
  _p "$label capture hook records a real FAIL to command-log.jsonl through the installed adapter"
86
86
  else
87
- _f "$label capture hook did not record the command result: $(cat "$proj/.flow-agents/live-cap/command-log.jsonl" 2>/dev/null)"
87
+ _f "$label capture hook did not record the command result: $(cat "$proj/.kontourai/flow-agents/live-cap/command-log.jsonl" 2>/dev/null)"
88
88
  fi
89
89
 
90
90
  # --- Teeth: claims-pass-but-log-shows-fail → Stop is BLOCKED ---
@@ -95,7 +95,7 @@ run_bundle(){ # $1 label, $2 install.sh, $3 settings-json-rel, $4 home-env-name
95
95
  echo "$blk" | is_block && _p "$label BLOCKS a claimed-pass command that the capture log recorded as FAIL" || _f "$label did NOT block the captured false-completion: $blk"
96
96
 
97
97
  # control: a matching capture log (pass) lets Stop through on the capture axis.
98
- printf '%s\n' '{"command":"npm test","observedResult":"pass","exitCode":0,"capturedAt":"2026-06-23T00:00:00Z","source":"postToolUse-capture"}' > "$proj/.flow-agents/cap-false/command-log.jsonl"
98
+ printf '%s\n' '{"command":"npm test","observedResult":"pass","exitCode":0,"capturedAt":"2026-06-23T00:00:00Z","source":"postToolUse-capture"}' > "$proj/.kontourai/flow-agents/cap-false/command-log.jsonl"
99
99
  local okblk; okblk="$(printf '{"hook_event_name":"Stop","cwd":"%s"}' "$proj" | env "$homevar=$home" CLAUDE_PROJECT_DIR="$home" FLOW_AGENTS_GOAL_FIT_BACKSTOP=skip bash -c "$stopcmd" 2>&1)"
100
100
  if echo "$okblk" | grep -q 'caught false-completion'; then
101
101
  _f "$label control: a confirming capture log should not raise a false-completion"
@@ -113,7 +113,7 @@ MARKDOWN
113
113
  stop_output="$(cd "$TMP_WORK" && FLOW_AGENTS_GOAL_FIT_STRICT=true kiro-cli chat --agent dev --no-interactive "Reply with READY only." 2>&1 || true)"
114
114
  stop_clean="$(printf '%s' "$stop_output" | strip_ansi)"
115
115
  if echo "$stop_clean" | grep -q 'stop "node .*stop:goal-fit stop-goal-fit.js standard,strict" failed with exit code: 2' \
116
- && echo "$stop_clean" | grep -q '\[Hook\] Goal Fit warning:' \
116
+ && echo "$stop_clean" | grep -q '\[stop-gate\] Goal Fit warning:' \
117
117
  && echo "$stop_clean" | grep -q 'live-stop--deliver.md is still status:executing'; then
118
118
  _pass "strict Goal Fit stop hook surfaces live Kiro stop gate"
119
119
  else