@kontourai/flow-agents 2.3.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. package/.github/CODEOWNERS +16 -0
  2. package/.github/workflows/ci.yml +44 -0
  3. package/.github/workflows/trust-reconcile.yml +62 -4
  4. package/AGENTS.md +4 -4
  5. package/CHANGELOG.md +62 -0
  6. package/CONTEXT.md +35 -0
  7. package/README.md +9 -3
  8. package/agents/dev.json +1 -1
  9. package/agents/tool-code-reviewer.json +1 -1
  10. package/agents/tool-planner.json +3 -3
  11. package/agents/tool-verifier.json +3 -3
  12. package/build/src/cli/assignment-provider.d.ts +1 -0
  13. package/build/src/cli/assignment-provider.js +748 -0
  14. package/build/src/cli/console-learning-projection.js +3 -2
  15. package/build/src/cli/effective-assignment-provider-settings.d.ts +1 -0
  16. package/build/src/cli/effective-assignment-provider-settings.js +125 -0
  17. package/build/src/cli/init.js +104 -21
  18. package/build/src/cli/kit.js +37 -6
  19. package/build/src/cli/pull-work-provider.js +1 -1
  20. package/build/src/cli/usage-feedback.js +3 -3
  21. package/build/src/cli/validate-hook-influence.js +1 -0
  22. package/build/src/cli/validate-workflow-artifacts.js +27 -7
  23. package/build/src/cli/workflow-artifact-cleanup-audit.js +36 -2
  24. package/build/src/cli/workflow-sidecar.d.ts +14 -7
  25. package/build/src/cli/workflow-sidecar.js +938 -168
  26. package/build/src/cli.js +6 -0
  27. package/build/src/flow-kit/validate.d.ts +23 -0
  28. package/build/src/flow-kit/validate.js +67 -1
  29. package/build/src/index.d.ts +1 -1
  30. package/build/src/index.js +1 -1
  31. package/build/src/lib/flow-resolver.d.ts +6 -4
  32. package/build/src/lib/flow-resolver.js +125 -58
  33. package/build/src/lib/local-artifact-root.d.ts +14 -2
  34. package/build/src/lib/local-artifact-root.js +22 -5
  35. package/build/src/lib/workflow-learning-projection.js +2 -2
  36. package/build/src/runtime-adapters.d.ts +12 -0
  37. package/build/src/runtime-adapters.js +56 -16
  38. package/build/src/tools/build-universal-bundles.js +49 -16
  39. package/build/src/tools/generate-context-map.js +10 -8
  40. package/build/src/tools/validate-source-tree.js +24 -1
  41. package/console.telemetry.json +22 -0
  42. package/context/contracts/artifact-contract.md +40 -7
  43. package/context/contracts/assignment-provider-contract.md +239 -0
  44. package/context/contracts/builder-kit-workflow-state-contract.md +5 -2
  45. package/context/contracts/decision-registry-contract.md +265 -0
  46. package/context/contracts/delivery-contract.md +3 -1
  47. package/context/contracts/execution-contract.md +25 -0
  48. package/context/contracts/governance-adapter-contract.md +2 -0
  49. package/context/contracts/knowledge-store-contract.md +197 -0
  50. package/context/contracts/planning-contract.md +4 -1
  51. package/context/contracts/review-contract.md +7 -3
  52. package/context/contracts/sandbox-policy.md +2 -0
  53. package/context/contracts/standing-directives.md +13 -0
  54. package/context/contracts/verification-contract.md +11 -1
  55. package/context/contracts/work-item-contract.md +3 -1
  56. package/context/deferred/parallelization.md +1 -1
  57. package/context/gate-awareness.md +4 -4
  58. package/context/scripts/git-status.sh +2 -2
  59. package/context/scripts/hooks/config-protection.js +502 -2
  60. package/context/scripts/hooks/lib/config-protection-remedies.js +69 -0
  61. package/context/scripts/hooks/run-hook.js +14 -1
  62. package/context/scripts/hooks/stop-goal-fit.js +1140 -99
  63. package/context/scripts/hooks/workflow-steering.js +212 -6
  64. package/context/settings/assignment-provider-settings.json +33 -0
  65. package/docs/adr/0007-flow-skill-kit-tool-boundary.md +2 -0
  66. package/docs/adr/0015-flow-flow-agents-boundary-reconciliation.md +35 -0
  67. package/docs/adr/0019-kit-dependency-ownership.md +62 -0
  68. package/docs/adr/0020-trust-reconcile-manifest-and-claim-classification.md +290 -0
  69. package/docs/adr/0021-assignment-leases-and-stale-claim-takeover.md +221 -0
  70. package/docs/adr/0022-fail-closed-delivery-reconciliation-with-governed-exemptions.md +463 -0
  71. package/docs/agent-system-guidebook.md +3 -3
  72. package/docs/agent-usage-feedback-loop.md +6 -6
  73. package/docs/context-map.md +10 -7
  74. package/docs/decisions/decision-records.md +57 -0
  75. package/docs/decisions/index.md +17 -0
  76. package/docs/decisions/knowledge-store-provider.md +51 -0
  77. package/docs/decisions/model-routing.md +63 -0
  78. package/docs/decisions/promotion-gate.md +52 -0
  79. package/docs/decisions/standing-directives.md +66 -0
  80. package/docs/developer-architecture.md +1 -1
  81. package/docs/fixture-ownership.md +4 -0
  82. package/docs/flow-kit-repository-contract.md +5 -5
  83. package/docs/getting-started.md +12 -2
  84. package/docs/integrations/harness-install.md +2 -1
  85. package/docs/kit-authoring-guide.md +64 -5
  86. package/docs/knowledge-kit.md +5 -1
  87. package/docs/learnings/2026-07-improvement-program.md +203 -0
  88. package/docs/migrations.md +6 -5
  89. package/docs/north-star.md +2 -2
  90. package/docs/operating-layers.md +4 -3
  91. package/docs/repository-structure.md +5 -5
  92. package/docs/skills-map.md +8 -8
  93. package/docs/spec/runtime-hook-surface.md +19 -15
  94. package/docs/spikes/graph-provider-2026-07.md +155 -0
  95. package/docs/standards-register.md +7 -7
  96. package/docs/trust-anchor-adoption.md +105 -4
  97. package/docs/work-item-adapters.md +1 -1
  98. package/docs/workflow-artifact-lifecycle.md +72 -9
  99. package/docs/workflow-eval-strategy.md +3 -3
  100. package/docs/workflow-shared-contracts.md +3 -2
  101. package/docs/workflow-usage-guide.md +260 -21
  102. package/evals/acceptance/DEMO-false-completion.md +20 -11
  103. package/evals/acceptance/prove-capture-teeth-declared.sh +15 -15
  104. package/evals/acceptance/prove-capture-teeth.sh +11 -11
  105. package/evals/acceptance/test_kiro_harness.sh +1 -1
  106. package/evals/ci/antigaming-suite.sh +5 -0
  107. package/evals/ci/run-baseline.sh +71 -1
  108. package/evals/fixtures/assignment-provider/actor-a.json +6 -0
  109. package/evals/fixtures/assignment-provider/actor-b.json +6 -0
  110. package/evals/fixtures/assignment-provider/github-issue-claimed.json +27 -0
  111. package/evals/fixtures/assignment-provider/github-issue-unassigned.json +7 -0
  112. package/evals/fixtures/assignment-provider/liveness-fresh.json +9 -0
  113. package/evals/fixtures/assignment-provider/liveness-stale.json +9 -0
  114. package/evals/fixtures/flow-kit-repository/invalid-bad-dependency/flows/review.flow.json +26 -0
  115. package/evals/fixtures/flow-kit-repository/invalid-bad-dependency/kit.json +13 -0
  116. package/evals/fixtures/flow-kit-repository/valid-with-dependency/flows/review.flow.json +26 -0
  117. package/evals/fixtures/flow-kit-repository/valid-with-dependency/kit.json +13 -0
  118. package/evals/fixtures/hook-influence/cases.json +32 -0
  119. package/evals/fixtures/pull-work-provider/github-issues.json +6 -6
  120. package/evals/fixtures/trust-reconcile-exploits/fabricated-attestation.json +59 -0
  121. package/evals/fixtures/trust-reconcile-exploits/no-label-bypass.json +36 -0
  122. package/evals/fixtures/trust-reconcile-exploits/skip-assumed-bypass.json +59 -0
  123. package/evals/fixtures/trust-reconcile-exploits/status-misassertion.json +48 -0
  124. package/evals/fixtures/trust-reconcile-exploits/waived-command-check.json +44 -0
  125. package/evals/fixtures/trust-reconcile-mixed-bundle/mixed-bundle.json +159 -0
  126. package/evals/fixtures/trust-reconcile-ws3/ws3-bundle.json +920 -0
  127. package/evals/integration/test_actor_identity.sh +254 -0
  128. package/evals/integration/test_assignment_provider_github.sh +318 -0
  129. package/evals/integration/test_assignment_provider_local_file.sh +222 -0
  130. package/evals/integration/test_builder_step_producers.sh +33 -23
  131. package/evals/integration/test_bundle_install.sh +22 -16
  132. package/evals/integration/test_bundle_lifecycle.sh +11 -11
  133. package/evals/integration/test_captured_fail_reconciliation.sh +50 -50
  134. package/evals/integration/test_checkpoint_signing.sh +43 -0
  135. package/evals/integration/test_claim_lookup.sh +4 -4
  136. package/evals/integration/test_codex_hook_resolution.sh +114 -0
  137. package/evals/integration/test_command_log_concurrency.sh +8 -8
  138. package/evals/integration/test_command_log_fork_classification.sh +3 -3
  139. package/evals/integration/test_command_log_integrity.sh +16 -16
  140. package/evals/integration/test_console_learning_projection.sh +1 -1
  141. package/evals/integration/test_critique_supersession_roundtrip.sh +182 -0
  142. package/evals/integration/test_dual_emit_flow_step.sh +145 -0
  143. package/evals/integration/test_enforcer_expects_driven.sh +12 -12
  144. package/evals/integration/test_evidence_capture_hook.sh +14 -13
  145. package/evals/integration/test_fixture_retirement_audit.sh +2 -2
  146. package/evals/integration/test_flow_agents_statusline.sh +2 -2
  147. package/evals/integration/test_flow_kit_install_git.sh +52 -0
  148. package/evals/integration/test_flow_kit_repository.sh +2 -0
  149. package/evals/integration/test_flowdef_session_activation.sh +6 -6
  150. package/evals/integration/test_flowdef_union_floor_regression.sh +429 -0
  151. package/evals/integration/test_gate_bypass_chain.sh +52 -40
  152. package/evals/integration/test_gate_lockdown.sh +97 -73
  153. package/evals/integration/test_gate_review_inquiry_records.sh +3 -3
  154. package/evals/integration/test_goal_fit_escape_hatch.sh +10 -10
  155. package/evals/integration/test_goal_fit_ghost_session.sh +104 -0
  156. package/evals/integration/test_goal_fit_hook.sh +32 -32
  157. package/evals/integration/test_goal_fit_rederive.sh +9 -9
  158. package/evals/integration/test_hook_category_behaviors.sh +103 -0
  159. package/evals/integration/test_hook_influence_cases.sh +1 -0
  160. package/evals/integration/test_install_merge.sh +262 -1
  161. package/evals/integration/test_kit_identity_trust.sh +6 -6
  162. package/evals/integration/test_liveness_conflict_injection.sh +587 -0
  163. package/evals/integration/test_liveness_heartbeat.sh +677 -0
  164. package/evals/integration/test_liveness_verdict.sh +394 -0
  165. package/evals/integration/test_local_flow_kit_install.sh +15 -0
  166. package/evals/integration/test_phase_map_and_gate_claim.sh +63 -11
  167. package/evals/integration/test_promote_gate.sh +178 -0
  168. package/evals/integration/test_publish_delivery.sh +23 -6
  169. package/evals/integration/test_pull_work_assignment_join.sh +132 -0
  170. package/evals/integration/test_pull_work_liveness_preflight.sh +394 -0
  171. package/evals/integration/test_pull_work_provider.sh +2 -2
  172. package/evals/integration/test_reconcile_soundness.sh +37 -13
  173. package/evals/integration/test_resolvefirststep_security.sh +22 -0
  174. package/evals/integration/test_runtime_adapter_activation.sh +87 -18
  175. package/evals/integration/test_session_resume_roundtrip.sh +147 -0
  176. package/evals/integration/test_sidecar_field_preservation.sh +249 -0
  177. package/evals/integration/test_trust_reconcile.sh +11 -10
  178. package/evals/integration/test_trust_reconcile_manifest.sh +79 -0
  179. package/evals/integration/test_trust_reconcile_mixed_bundle.sh +96 -0
  180. package/evals/integration/test_trust_reconcile_negatives.sh +759 -0
  181. package/evals/integration/test_usage_feedback_global.sh +5 -5
  182. package/evals/integration/test_validate_artifacts_portability.sh +60 -0
  183. package/evals/integration/test_verify_cli.sh +2 -2
  184. package/evals/integration/test_veritas_governance_kit.sh +117 -0
  185. package/evals/integration/test_workflow_artifact_cleanup_audit.sh +26 -1
  186. package/evals/integration/test_workflow_artifacts.sh +33 -0
  187. package/evals/integration/test_workflow_sidecar_writer.sh +802 -74
  188. package/evals/integration/test_workflow_steering_hook.sh +175 -5
  189. package/evals/lib/codex-provider.sh +1 -1
  190. package/evals/lib/node.sh +19 -1
  191. package/evals/run.sh +32 -0
  192. package/evals/static/test_decisions.sh +150 -0
  193. package/evals/static/test_flowdef_codeowners_coverage.sh +62 -0
  194. package/evals/static/test_knowledge_providers.sh +23 -0
  195. package/evals/static/test_package.sh +19 -10
  196. package/evals/static/test_universal_bundles.sh +48 -3
  197. package/evals/static/test_workflow_skills.sh +41 -8
  198. package/kits/builder/flows/build.flow.json +3 -74
  199. package/kits/builder/flows/publish-learn.flow.json +90 -0
  200. package/kits/builder/kit.json +11 -0
  201. package/kits/builder/skills/builder-shape/SKILL.md +1 -1
  202. package/kits/builder/skills/deliver/SKILL.md +32 -11
  203. package/kits/builder/skills/design-probe/SKILL.md +4 -4
  204. package/kits/builder/skills/evidence-gate/SKILL.md +26 -1
  205. package/kits/builder/skills/execute-plan/SKILL.md +2 -2
  206. package/kits/builder/skills/fix-bug/SKILL.md +2 -0
  207. package/kits/builder/skills/gate-review/SKILL.md +6 -6
  208. package/kits/builder/skills/idea-to-backlog/SKILL.md +1 -1
  209. package/kits/builder/skills/learning-review/SKILL.md +7 -7
  210. package/kits/builder/skills/pickup-probe/SKILL.md +5 -4
  211. package/kits/builder/skills/plan-work/SKILL.md +27 -5
  212. package/kits/builder/skills/pull-work/SKILL.md +148 -5
  213. package/kits/builder/skills/release-readiness/SKILL.md +4 -4
  214. package/kits/builder/skills/review-work/SKILL.md +24 -1
  215. package/kits/builder/skills/tdd-workflow/SKILL.md +2 -0
  216. package/kits/builder/skills/verify-work/SKILL.md +8 -1
  217. package/kits/catalog.json +6 -0
  218. package/kits/knowledge/kit.json +35 -0
  219. package/kits/knowledge/providers/conformance/fixtures/git-repo/CONTEXT.md +12 -0
  220. package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/decisions/old-sprocket-shape.md +13 -0
  221. package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/decisions/sprocket-shape.md +14 -0
  222. package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/decisions/widget-format.md +14 -0
  223. package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/learnings/fixture-learning.md +7 -0
  224. package/kits/knowledge/providers/conformance/fixtures/work-item/issues.json +30 -0
  225. package/kits/knowledge/providers/conformance/suite.test.js +125 -0
  226. package/kits/knowledge/providers/git-repo/index.js +236 -0
  227. package/kits/knowledge/providers/health/health-pass.test.js +99 -0
  228. package/kits/knowledge/providers/health/index.js +153 -0
  229. package/kits/knowledge/providers/index.js +24 -0
  230. package/kits/knowledge/providers/lib/model.js +91 -0
  231. package/kits/knowledge/providers/lib/schema-validate.js +119 -0
  232. package/kits/knowledge/providers/markdown-vault/index.js +169 -0
  233. package/kits/knowledge/providers/work-item/index.js +204 -0
  234. package/kits/veritas-governance/adapter/readiness-to-trust-bundle.mjs +178 -0
  235. package/kits/veritas-governance/docs/README.md +75 -0
  236. package/kits/veritas-governance/fixtures/readiness/not-ready.readiness-report.json +4645 -0
  237. package/kits/veritas-governance/fixtures/readiness/ready.readiness-report.json +4403 -0
  238. package/kits/veritas-governance/flows/readiness-check.flow.json +35 -0
  239. package/kits/veritas-governance/kit.json +16 -0
  240. package/package.json +11 -5
  241. package/packaging/README.md +2 -2
  242. package/packaging/conformance/fixtures/config-protection--allow-read-state-json.json +20 -0
  243. package/packaging/conformance/fixtures/config-protection--block-write-state-json.json +20 -0
  244. package/packaging/conformance/fixtures/stop-goal-fit--block-bundle-disputed-claim.json +1 -1
  245. package/packaging/conformance/fixtures/stop-goal-fit--block-capture-contradicts-claimed-pass.json +1 -1
  246. package/packaging/manifest.json +9 -10
  247. package/schemas/assignment-provider-settings.schema.json +125 -0
  248. package/schemas/decision-record.schema.json +109 -0
  249. package/schemas/knowledge/edge.schema.json +54 -0
  250. package/schemas/knowledge/health-report.schema.json +45 -0
  251. package/schemas/knowledge/node.schema.json +49 -0
  252. package/schemas/knowledge/proposal.schema.json +53 -0
  253. package/schemas/workflow-evidence.schema.json +1 -1
  254. package/schemas/workflow-state.schema.json +5 -0
  255. package/scripts/README.md +8 -3
  256. package/scripts/check-content-boundary.cjs +1 -1
  257. package/scripts/check-decisions.cjs +356 -0
  258. package/scripts/ci/derive-claim-status.mjs +74 -0
  259. package/scripts/ci/trust-reconcile.js +957 -103
  260. package/scripts/git-status.sh +2 -2
  261. package/scripts/hooks/claude-telemetry-hook.js +37 -3
  262. package/scripts/hooks/codex-telemetry-hook.js +36 -2
  263. package/scripts/hooks/config-protection.js +102 -30
  264. package/scripts/hooks/evidence-capture.js +3 -3
  265. package/scripts/hooks/lib/actor-identity.js +368 -0
  266. package/scripts/hooks/lib/config-protection-remedies.js +69 -0
  267. package/scripts/hooks/lib/liveness-heartbeat.js +361 -0
  268. package/scripts/hooks/lib/liveness-policy.js +127 -0
  269. package/scripts/hooks/lib/liveness-read.js +105 -12
  270. package/scripts/hooks/lib/liveness-write.js +46 -0
  271. package/scripts/hooks/lib/local-artifact-paths.js +12 -6
  272. package/scripts/hooks/opencode-telemetry-hook.js +27 -1
  273. package/scripts/hooks/pi-telemetry-hook.js +27 -1
  274. package/scripts/hooks/stop-goal-fit.js +157 -26
  275. package/scripts/hooks/workflow-steering.js +77 -3
  276. package/scripts/install-codex-home.sh +138 -38
  277. package/scripts/statusline/flow-agents-statusline.js +2 -2
  278. package/src/cli/assignment-provider.ts +845 -0
  279. package/src/cli/console-learning-projection.ts +3 -2
  280. package/src/cli/effective-assignment-provider-settings.ts +112 -0
  281. package/src/cli/init.ts +101 -21
  282. package/src/cli/kit.ts +37 -6
  283. package/src/cli/public-api.test.mjs +58 -6
  284. package/src/cli/pull-work-provider.ts +1 -1
  285. package/src/cli/trust-bundle-policy-order.test.mjs +87 -0
  286. package/src/cli/usage-feedback.ts +3 -3
  287. package/src/cli/validate-hook-influence.ts +1 -0
  288. package/src/cli/validate-workflow-artifacts.ts +27 -7
  289. package/src/cli/veritas-readiness-adapter.test.mjs +267 -0
  290. package/src/cli/workflow-artifact-cleanup-audit.ts +35 -2
  291. package/src/cli/workflow-sidecar.ts +933 -168
  292. package/src/cli.ts +6 -0
  293. package/src/flow-kit/validate.ts +74 -1
  294. package/src/index.ts +6 -2
  295. package/src/lib/flow-resolver.ts +123 -55
  296. package/src/lib/local-artifact-root.ts +24 -5
  297. package/src/lib/workflow-learning-projection.ts +2 -2
  298. package/src/runtime-adapters.ts +61 -15
  299. package/src/tools/build-universal-bundles.ts +49 -16
  300. package/src/tools/generate-context-map.ts +10 -8
  301. package/src/tools/validate-source-tree.ts +24 -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,11 +109,17 @@ 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.
116
116
 
117
+ ### Assignment ownership: the third provider leg
118
+
119
+ Beside the `WorkItemProvider` (what work exists) and `BoardProvider` (how it is grouped/ranked) settings above, `pull-work` also reads `AssignmentProvider` settings to decide who currently owns a candidate work item before offering it. This is durable, human-visible ownership — a GitHub issue assignee, an `agent:claimed` label, and a versioned machine-readable claim comment (or, for tracker-less repos and evals, an equivalent local JSON record) — joined against the ephemeral liveness presence layer so a crashed session's stale claim never blocks a second session from picking up the same work.
120
+
121
+ Settings live at `context/settings/assignment-provider-settings.json` (validated by `schemas/assignment-provider-settings.schema.json`), mirroring the same `defaults`/`projects[]` shape as the backlog provider settings above; resolve them with `npm run effective-assignment-provider-settings -- --repo-path . --json`. See `context/contracts/assignment-provider-contract.md` for the full `claim`/`release`/`supersede`/`status`/`list` vocabulary, the assignment ⋈ liveness join table, and the human-assignee ask-first policy.
122
+
117
123
  Direct `pull-work` remains a normal workflow primitive. The Builder Kit build path adds the pickup Probe/design-probe handoff before planning; it does not require Surface/Veritas trust-backed gates and does not replace direct primitive use.
118
124
 
119
125
  Builder Kit build is the product-level entry point for implementation pickup. In that mode, `pull-work` may guide the next step automatically as `pull-work -> design-probe / pickup-probe`; direct `pull-work` still stops with a `plan-work` handoff unless you ask to continue.
@@ -136,7 +142,7 @@ Your WIP can block starting new work. Other people's WIP should block only when
136
142
 
137
143
  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
144
 
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.
145
+ 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
146
 
141
147
  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
148
 
@@ -184,7 +190,7 @@ Then use `plan-work`.
184
190
  Example prompt:
185
191
 
186
192
  ```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.
193
+ 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
194
  ```
189
195
 
190
196
  Then use `execute-plan` only after the plan is accepted.
@@ -200,7 +206,7 @@ When `pull-work` chooses a worktree, record `worktree_lifecycle`: path, branch,
200
206
  Example prompt:
201
207
 
202
208
  ```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.
209
+ 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
210
  ```
205
211
 
206
212
  ## 5. Review, Then Verify
@@ -212,7 +218,7 @@ Review checks quality, security triggers, architecture fit, project standards, r
212
218
  Example prompt:
213
219
 
214
220
  ```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.
221
+ 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
222
  ```
217
223
 
218
224
  Then use `verify-work` for implementation verification. Verification is evidence: it asks what proves the accepted behavior works.
@@ -222,7 +228,7 @@ Verification runs build/type/lint/test/security/browser/runtime checks as releva
222
228
  Example prompt:
223
229
 
224
230
  ```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.
231
+ 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
232
  ```
227
233
 
228
234
  ## 6. Build Trust With Evidence Gate
@@ -253,7 +259,7 @@ Pickup Probe may update durable docs only when the decision is no longer a trans
253
259
 
254
260
  ## 7. Check Goal Fit Before Stopping
255
261
 
256
- Goal Fit is the local stop condition before a final answer. The working artifact in `.flow-agents/<slug>/` should answer:
262
+ Goal Fit is the local stop condition before a final answer. The working artifact in `.kontourai/flow-agents/<slug>/` should answer:
257
263
 
258
264
  - What did the user originally ask for?
259
265
  - Can the user run, understand, inspect, or act on the result now?
@@ -272,7 +278,7 @@ npm run workflow:sidecar -- ensure-session \
272
278
  --summary "<summary>" \
273
279
  --criterion "<acceptance criterion>"
274
280
 
275
- npm run workflow:sidecar -- init-plan .flow-agents/<slug>/<slug>--deliver.md \
281
+ npm run workflow:sidecar -- init-plan .kontourai/flow-agents/<slug>/<slug>--deliver.md \
276
282
  --source-request "<request>" \
277
283
  --summary "<summary>" \
278
284
  --next-action "<next step>"
@@ -306,10 +312,113 @@ Rules:
306
312
  - Issue-backed sessions should prefer `--work-item` over hand-supplied `--task-slug` so that
307
313
  liveness subjectId alignment is automatic.
308
314
 
315
+ #### Branch convention
316
+
317
+ `ensure-session` derives a routing branch name for every newly created session and records it
318
+ in `state.json.branch`, seeds it into the session Markdown's `branch:` line, and mirrors it into
319
+ `current.json.branch` for the currently active session. The derived format is:
320
+
321
+ ```
322
+ agent/<actor>/<slug>
323
+ ```
324
+
325
+ `<actor>` comes from the same actor resolver used by liveness tracking
326
+ (`scripts/hooks/lib/actor-identity.js`'s `resolveActor`), and `<slug>` is the session's task slug
327
+ (or the deterministic `--work-item` slug described above).
328
+
329
+ Neither the resolved actor string nor an arbitrary `--task-slug` is guaranteed to be a valid git
330
+ ref component, so `ensure-session` runs both through an incremental `sanitizeBranchSegment` pass
331
+ before joining them into `agent/<actor>/<slug>`:
332
+
333
+ 1. Replace any `:` with `-` (the actor resolver's own `:` delimiter is not a legal ref character).
334
+ 2. Re-run the actor resolver's own charset restriction (`[A-Za-z0-9_.-]`, 64-char cap).
335
+ 3. Collapse repeated `..` sequences to a single `-` (git forbids two consecutive dots in a ref
336
+ component).
337
+ 4. Strip a leading `.` (git forbids a component starting with `.`).
338
+ 5. Strip a trailing `.` (fix-plan iteration 1, F1 — git forbids a component ending in `.`; the
339
+ prior pass only handled a run of 2+ trailing dots via the `..`-collapse step above and missed
340
+ the single-trailing-dot case, e.g. a `--task-slug` of `my-fix.`). This runs *before* the next
341
+ step so a `.lock` suffix hidden behind trailing dots (e.g. `foo.lock.`) is exposed and still
342
+ rewritten rather than left dangling.
343
+ 6. Rewrite a trailing `.lock` to `-lock` (git forbids a component ending in `.lock`).
344
+ 7. If the FINAL result after the above steps is empty, OR is exactly the literal string
345
+ `unknown`, fall back to `unknown-<hash>` where `<hash>` is the first 6 hex characters of
346
+ `sha256(<raw input before step 1>)` (fix-plan iteration 1 F4, tightened by iteration 2 F4').
347
+ The hash is deterministic (same raw input always derives the same fallback, preserving the
348
+ "never re-derive an existing session's branch" guarantee below) but makes two *different*
349
+ raw inputs that both land on the fallback derive distinct branches instead of silently
350
+ colliding on the bare literal `unknown`. This covers three distinct ways a raw input can land
351
+ on the fallback, all disambiguated identically with **no exceptions**: an all-garbage input
352
+ (e.g. `--task-slug "???"`) whose charset filter in step 2 collapses it to nothing; a near-miss
353
+ input like `"unknown."` or `".unknown"` that step 4/5's leading/trailing-dot stripping
354
+ collapses down to the literal `unknown`; and a raw input that genuinely *is* the literal
355
+ string `unknown` verbatim. Iteration 1 exempted that last case (a literal `"unknown"` input
356
+ was left undisambiguated); iteration 2 (F4') removed that carve-out because it let a literal
357
+ `--actor unknown` collide, undisambiguated, with a near-miss input like `"unknown."` that
358
+ collapses to the same segment — every input that reaches the fallback token is now
359
+ disambiguated uniformly.
360
+
361
+ As of fix-plan iteration 1 (re-verified against the sanitizer above, see F1 in
362
+ `.kontourai/flow-agents/kontourai-flow-agents-289/kontourai-flow-agents-289--fix-plan-iteration-1.md`),
363
+ this pass empirically closes every concrete git-check-ref-format(1) failure mode reachable
364
+ through it: `sanitizeSegment`'s own `[A-Za-z0-9_.-]` charset restriction (step 2) already strips
365
+ every character git-check-ref-format forbids elsewhere in a ref component — `@`, `{`/`}` (so
366
+ `@{` cannot survive), `/` (so a derived segment can never end with `/`, contain `//`, or itself
367
+ be a bare `@`), whitespace, and other control characters — so none of those were ever actually
368
+ reachable in a *derived* segment even before this iteration; only the single-trailing-dot case
369
+ (step 5, above) was a real, reachable gap, and it is now closed. There is no longer a known
370
+ accepted gap for the derived path. This is still not a from-scratch reimplementation of
371
+ `git-check-ref-format(1)` (e.g. it has not been exhaustively fuzzed against every future git
372
+ version's rule set) — but it is not a partial/best-effort pass either.
373
+
374
+ - Pass `--branch <value>` to `ensure-session` to record an explicit value verbatim instead of the
375
+ derived name. The override only applies while creating a brand-new session.
376
+ - Unlike the derived path, an explicit `--branch` is **not** sanitized — it is caller intent and
377
+ may legitimately contain `/` (to nest under an existing convention), so `ensure-session`
378
+ strictly *validates* it instead and dies with a remediation message, before writing any session
379
+ artifact. Validation runs in two passes:
380
+ 1. Whole-string lexical checks (fix-plan iteration 1, F2) — the value must not:
381
+ - contain a control character, newline, or space;
382
+ - start or end with `/`, or contain `//`;
383
+ - start with `.`, or contain a `..` sequence;
384
+ - end with `.` or `.lock`; or
385
+ - contain any character outside `[A-Za-z0-9_./-]`.
386
+ 2. Per-`/`-component lexical checks (fix-plan iteration 2, F2') — the whole-string checks above
387
+ only examine the very start/end of the full value, so a charset-legal value can still smuggle
388
+ an invalid path component past them (e.g. `-lead`, `a/.b`, `foo.lock/bar`, `a/./b`). Every
389
+ `/`-delimited component must not be empty, must not be exactly `.`, must not start with `.`
390
+ or `-` (applied uniformly to every component, not just the first — intentionally stricter
391
+ than git strictly requires, which is fine for a caller-facing override flag), and must not
392
+ end with `.` or `.lock`.
393
+ 3. Belt-and-braces (fix-plan iteration 2, F2'): once both lexical passes above succeed, the real
394
+ `git check-ref-format --branch <value>` binary is invoked (argv-array, no shell, 5-second
395
+ timeout) as the final authority. It can only ever *reject* a value the lexical checks already
396
+ let through — never re-legalize one they rejected — so it closes any residual gap between this
397
+ hand-rolled lexical pass and git's actual ref-name rules. When git cannot be spawned at all
398
+ (not installed) or does not complete within the timeout, this step is skipped silently and the
399
+ lexical checks above remain the sole authority.
400
+
401
+ A value that passes all of the above is recorded exactly as given (no trimming, no
402
+ sanitization).
403
+ - `ensure-session` only records the branch **name** — in `state.json`, the session Markdown, and
404
+ `current.json`. It does not run `git checkout -b` or `git worktree add`; creating and checking
405
+ out the actual branch/worktree remains the calling skill's responsibility (see
406
+ [ADR 0021](adr/0021-assignment-leases-and-stale-claim-takeover.md) §3).
407
+ - Resuming an existing session directory never re-derives or overwrites its already-recorded
408
+ `branch` — the session Markdown's existing `branch:` line always wins, regardless of which
409
+ actor next calls `ensure-session` against that slug. This is what makes takeover continuity
410
+ ("resume the incumbent's branch, never a parallel one") true by construction; see
411
+ [ADR 0021](adr/0021-assignment-leases-and-stale-claim-takeover.md) §5.
412
+ - `branch` is an optional field in `workflow-state.schema.json` for migration honesty: legacy
413
+ `state.json` files that predate this convention have no `branch` key and still validate.
414
+ `workflow:validate-artifacts` prints a non-blocking `WARN` line to stderr (not a hard failure)
415
+ when a `state.json` has no `branch` field, naming the gap without breaking legacy
416
+ sessions/fixtures.
417
+
309
418
  Reviewer Markdown artifacts can be imported into `critique.json`:
310
419
 
311
420
  ```bash
312
- npm run workflow:sidecar -- import-critique .flow-agents/<slug> .flow-agents/<slug>/<slug>--review.md
421
+ npm run workflow:sidecar -- import-critique .kontourai/flow-agents/<slug> .kontourai/flow-agents/<slug>/<slug>--review.md
313
422
  ```
314
423
 
315
424
  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 +490,17 @@ Use release-readiness for this evidence-gate PASS. Decide whether to MERGE, RELE
381
490
 
382
491
  ## 10. Promote Final Acceptance Docs
383
492
 
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.
493
+ `.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
494
 
386
495
  Use the helper:
387
496
 
388
497
  ```bash
389
- npm run promote-workflow-artifact -- .flow-agents/<slug>/<slug>--deliver.md
498
+ npm run promote-workflow-artifact -- .kontourai/flow-agents/<slug>/<slug>--deliver.md
390
499
  ```
391
500
 
392
501
  Expected behavior:
393
502
 
394
- - copy the source artifact into `.flow-agents/<slug>/archive/<date>/`
503
+ - copy the source artifact into `.kontourai/flow-agents/<slug>/archive/<date>/`
395
504
  - create or update a durable doc under `docs/delivery/`
396
505
  - include the plan, evidence, Goal Fit, and Final Acceptance sections when present
397
506
  - link the durable doc back to the archived artifact so future readers can inspect why and how the feature was built
@@ -430,7 +539,7 @@ flow-agents console-learning-projection --artifact-root .flow-agents --kontour-r
430
539
 
431
540
  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
541
 
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.
542
+ 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
543
 
435
544
  ## Quick Prompt Templates
436
545
 
@@ -455,7 +564,7 @@ Use evidence-gate. Map acceptance criteria to evidence, inspect CI and scope int
455
564
  Check local goal fit:
456
565
 
457
566
  ```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.
567
+ 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
568
  ```
460
569
 
461
570
  Release decision:
@@ -484,7 +593,7 @@ The `RESUME:` block supplements the existing `STATE:` line and contains:
484
593
  - **Next step** — the first `handoff.json next_steps` entry.
485
594
  - **Blockers** — any recorded blockers from `handoff.json`, or "none".
486
595
  - **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.
596
+ - **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
597
  - **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
598
 
490
599
  The `RESUME:` block appears on `SessionStart` only. `UserPromptSubmit` and `PostToolUse`
@@ -493,16 +602,24 @@ behavior is unchanged.
493
602
  All reads are fail-open: a missing `handoff.json`, `trust.bundle`, or liveness stream
494
603
  degrades gracefully — the section is omitted or shows "no data", and the hook never throws.
495
604
 
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.
605
+ The `RESUME:` advisory read above is read-only (ADR 0012); liveness *writes* happen
606
+ elsewhere in the lifecycle and already exist today: `init-plan` claims the active slug and
607
+ `advance-state` heartbeats/releases it. As of #288, `FLOW_AGENTS_LIVENESS` defaults **on** —
608
+ presence is ambient by default. Set it to `off`/`0`/`false`/`no`/`disabled`
609
+ (case-insensitive) to opt back out. Claim TTL defaults to `1800` seconds and is
610
+ configurable via `FLOW_AGENTS_LIVENESS_TTL_SECONDS`; per ADR 0012 §4, tuning this value is
611
+ itself the operational risk — too tight manufactures false reclaims, so treat liveness as
612
+ advisory and double-check against real branch/PR state before tightening it. A throttled
613
+ tool-activity heartbeat also rides ordinary tool use (see "Tool-activity heartbeat"
614
+ below), so a long-running wave never goes stale on TTL alone just because it hasn't hit a
615
+ phase transition. The session-level event log (Layer 2) is still deferred.
499
616
 
500
617
  ### Shared liveness helper
501
618
 
502
619
  The freshness logic is centralised in `scripts/hooks/lib/liveness-read.js` (pure CJS,
503
620
  zero dependencies). It exports:
504
621
 
505
- - `readLivenessEvents(streamPath)` — reads a `.flow-agents/liveness/events.jsonl` file
622
+ - `readLivenessEvents(streamPath)` — reads a `.kontourai/flow-agents/liveness/events.jsonl` file
506
623
  line-by-line, JSON-parses each, and tolerates malformed lines.
507
624
  - `freshHolders(events, slug, selfActor, nowMs)` — returns actors (excluding `selfActor`)
508
625
  who hold a within-TTL claim or heartbeat on `subjectId === slug`.
@@ -510,3 +627,125 @@ zero dependencies). It exports:
510
627
  Both the hook (`scripts/hooks/workflow-steering.js`) and the compiled CLI
511
628
  (`build/src/cli/workflow-sidecar.js`) consume this helper so the TTL/freshness logic lives
512
629
  in one place.
630
+
631
+ ### Tool-activity heartbeat
632
+
633
+ `scripts/hooks/lib/liveness-heartbeat.js` (pure CJS, zero dependencies — same sharing pattern
634
+ as `liveness-read.js` and `actor-identity.js`) exports `maybeEmitHeartbeat({ cwd, env, now })`.
635
+ All four telemetry hook wrappers (`scripts/hooks/{claude,codex,opencode,pi}-telemetry-hook.js`)
636
+ call it whenever the current invocation's canonical event resolves to `postToolUse`. Per
637
+ [`docs/spec/runtime-hook-surface.md`](spec/runtime-hook-surface.md) row 280, `PostToolUseFailure`
638
+ is a **Claude Code-only** additional native event mapped onto canonical `postToolUse` — Codex,
639
+ Kiro, opencode, and pi each map only their own single native `postToolUse`-equivalent event
640
+ (`PostToolUse`, `PostToolUse`, `tool.execute.after`, `tool_result` respectively); no other
641
+ runtime has a distinct "failure" variant. The heartbeat call runs independently of the existing
642
+ `telemetry.sh` spawn. On each call it:
643
+
644
+ 1. Checks `isLivenessEnabled()` — no-ops (`disabled`) if `FLOW_AGENTS_LIVENESS` is off.
645
+ 2. Reads `current.json`'s `active_slug` and resolves the actor (`actor-identity.js`) — no-ops
646
+ if there is no active slug or the actor cannot be resolved. These two checks run *before*
647
+ actor resolution specifically so a repo with liveness disabled, or with no active session,
648
+ never pays the actor resolver's process-ancestry `ps` spawn cost.
649
+ 3. Reads a **bounded tail** (the last 64KB, newline-aligned so a partial line at the truncation
650
+ boundary is dropped) of the `liveness/events.jsonl` stream, filtered for that
651
+ `(subjectId, actor)` pair. Because the stream is append-only, this bounded read is exact for
652
+ any pair whose most recent event lies within that window — which, in steady state, is always
653
+ true within one throttle period. Only if the pair has **zero** matching events anywhere in
654
+ the tail (the rare case of the first heartbeat after a claim old enough to have scrolled out
655
+ of the last 64KB) does it fall back to one full read of the stream. No-ops if there is no
656
+ prior `claim` event from this actor for this subject, or if the actor's most recent event for
657
+ the subject is a `release`.
658
+ 4. Throttles: only appends a new `heartbeat` event once at least
659
+ `FLOW_AGENTS_LIVENESS_HEARTBEAT_THROTTLE_SECONDS` (default `60`) seconds have elapsed since
660
+ that actor's last recorded event for the subject.
661
+
662
+ The throttle window is derived from the actor-scoped stream tail itself — there is
663
+ deliberately no separate, mutable "last heartbeat" state file. Per ADR 0021 §3 (which lists
664
+ "advance-state + tool activity" heartbeats as one of the cross-cutting liveness touchpoints,
665
+ riding existing writes rather than a bespoke timer), a shared state file keyed on anything
666
+ less specific than the actor would reintroduce the same last-writer-wins race that ADR 0021
667
+ is designed to close. Each actor only ever contends with its own prior writes, so there is no
668
+ cross-session race to guard against.
669
+
670
+ `maybeEmitHeartbeat` is fail-open, matching the #287 actor-identity convention below: any
671
+ error is caught, a diagnostic is written to stderr, and the call returns without throwing — it
672
+ never blocks the tool call, never alters a wrapper's stdout/success-output shape, and never
673
+ changes a wrapper's exit code. It also does not read or depend on `TELEMETRY_ENABLED` —
674
+ disabling telemetry does not disable liveness heartbeats, and vice versa; the two are
675
+ independent, sibling concerns.
676
+
677
+ ### Overlap detection and correction
678
+
679
+ Two composable mechanisms (#320) tighten the gap ADR 0012 §4 names — a false-stale
680
+ double-hold is *detected*, not *prevented*, and liveness stays advisory, never a lock: a
681
+ deterministic **tiebreaker CLI** (`liveness verdict`) that lets a losing session
682
+ self-correct within the same `pull-work` pass, and a **mid-turn conflict injection** that
683
+ surfaces another actor's fresh claim on a session's own held subject inside its own
684
+ tool-call context, on the runtimes whose hook contract supports it. Neither mechanism
685
+ mutates GitHub/provider state or introduces a new state file (ADR 0012 §5); each closes
686
+ one of the cross-cutting liveness touchpoints ADR 0021 §3 lists, without implementing
687
+ that ADR's still-Draft takeover/supersede or publish-gate slices.
688
+
689
+ The table below documents the full selection/mid-flight/publish latency budget — where a
690
+ double-hold can occur, what detects it, what corrects it, and the typical latency before
691
+ correction happens:
692
+
693
+ | Window | What can go wrong | Detector | Corrector | Typical latency |
694
+ | --- | --- | --- | --- | --- |
695
+ | **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. |
696
+ | **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. |
697
+ | **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. |
698
+
699
+ The Publish row is **not implemented by this issue** — it is named here so the table is
700
+ read as an honest latency-budget map, not mistaken for a claim that overlap detection is
701
+ fully closed end-to-end. See [ADR 0012](adr/0012-agent-coordination-as-liveness-claims.md)
702
+ §4 and [ADR 0021](adr/0021-assignment-leases-and-stale-claim-takeover.md) §3 for the
703
+ underlying design rationale.
704
+
705
+ ### Actor identity and liveness writes
706
+
707
+ Liveness claims/heartbeats/releases are attributed to a runtime-agnostic **actor struct**
708
+ `{runtime, session_id, host, human?}`, serialized (via `serializeActor()` in the shared
709
+ `scripts/hooks/lib/actor-identity.js` resolver — same sharing pattern as `liveness-read.js`
710
+ above) into a single string safe for the existing `${subjectId}::${actor}` grouping key. The
711
+ serialized actor string embeds this machine's `os.hostname()` — so `liveness/events.jsonl` (and
712
+ therefore `.kontourai/flow-agents/` as a whole) can indirectly reveal hostnames of every host
713
+ that has claimed work in this repo; `.kontourai/` must stay gitignored (it already is, by
714
+ default) and must never be force-added (`git add -f`) or bundled into a shareable support
715
+ bundle without first reviewing it for this kind of incidental host-identifying information. Both
716
+ `workflow-sidecar`'s `liveness` command and `workflow-steering.js`'s SessionStart advisory
717
+ consume the same `resolveActor()` function, so a session's own claim/heartbeat events are
718
+ never mistaken for "another agent."
719
+
720
+ `resolveActor()` derives the actor automatically, with zero required configuration, via a
721
+ priority chain:
722
+
723
+ 1. An explicit `FLOW_AGENTS_ACTOR` env override, if set (and not the literal `"local"`,
724
+ case-insensitive) — always wins.
725
+ 2. A runtime-native session-id env var already ambient in the current process's own
726
+ environment — confirmed for Claude Code (`CLAUDE_CODE_SESSION_ID`); the equivalent var for
727
+ Codex/opencode/pi is unverified as of this writing.
728
+ 3. A process-ancestry fallback (the resolver's parent PID plus that parent process's exact
729
+ start timestamp, hashed into a short opaque token) — needs no runtime cooperation, is
730
+ stable across repeated invocations within one session, and is distinct across concurrent
731
+ sessions on one host. This is the correctness backstop for runtimes whose native
732
+ session-id env var is not yet confirmed.
733
+
734
+ **Liveness writes fail loud.** `liveness claim`, `liveness heartbeat`, and `liveness release`
735
+ exit nonzero with remediation text whenever the resolved actor is empty or the literal
736
+ `"local"` (case-insensitive) — they never silently fall back to the old shared `"local"`
737
+ default. Fix: pass `--actor <id>` explicitly, set `FLOW_AGENTS_ACTOR=<id>`, or run inside a
738
+ supported runtime so the derivation chain above can resolve a real identity.
739
+
740
+ **Liveness reads stay backward-compatible.** `liveness status` and `freshHolders()` (in
741
+ `liveness-read.js`) continue to parse, group, and label pre-existing events whose `actor`
742
+ field is the literal string `"local"` without error or behavior change — only *new* writes
743
+ are rejected, not historical data.
744
+
745
+ This actor struct is the same shape referenced by
746
+ [ADR 0012](adr/0012-agent-coordination-as-liveness-claims.md) and by
747
+ [ADR 0021](adr/0021-assignment-leases-and-stale-claim-takeover.md) §2 (`AssignmentProvider`)
748
+ as the forward-looking identity model for durable assignment. ADR 0021 is still Draft, and its
749
+ other slices (`AssignmentProvider`, the janitor, takeover/supersede, the verify-hold publish
750
+ gate) are **not** implemented by this change — only the actor struct and derivation chain
751
+ 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"