@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
@@ -0,0 +1,239 @@
1
+ # Assignment Provider Contract
2
+
3
+ This contract defines the provider-neutral vocabulary for durable work-item ownership: who has
4
+ claimed a subject, how that claim is represented, and how it is read back safely. It formalizes
5
+ [ADR 0021: Assignment Leases and Stale-Claim Takeover](../../docs/adr/0021-assignment-leases-and-stale-claim-takeover.md)
6
+ as the third provider leg beside `WorkItemProvider` and `BoardProvider`
7
+ (`context/contracts/work-item-contract.md`) and `ChangeProvider`. GitHub is the first concrete
8
+ mapping for this contract, not the generic vocabulary — Linear, Jira, GitLab, and a local-file
9
+ provider for tracker-less repos and evals all map the same operations.
10
+
11
+ ## Terminology: three distinct "claim" concepts
12
+
13
+ This repository uses the word "claim" for three unrelated things. Always qualify in prose; never
14
+ write bare "claim" where the meaning is not obvious from surrounding context:
15
+
16
+ | Term | Layer | Meaning | Where it lives |
17
+ | --- | --- | --- | --- |
18
+ | **assignment claim** | this contract | Durable, provider-recorded ownership of a work item | `AssignmentProvider.claim()`, this doc |
19
+ | **liveness claim** | ADR 0012 | Ephemeral, TTL-reaped presence/heartbeat event | `liveness claim` (`workflow-sidecar.ts`), `freshHolders` (`liveness-read.js`) |
20
+ | **trust claim** | ADR 0010 / ADR 0017 | A Hachure `trust.bundle` evidence claim (kit-typed, verification status) | `workflow-sidecar.ts claim <id> <dir>` (`claimLookup`) |
21
+
22
+ An assignment claim's *effective state* is never computed from the assignment layer alone — it is
23
+ always joined against a liveness claim's freshness (see the join table below).
24
+
25
+ ## AssignmentProvider Operations
26
+
27
+ Provider-neutral interface, verbatim from ADR 0021 §2:
28
+
29
+ ```
30
+ AssignmentProvider:
31
+ claim(subjectId, actor, meta) # assign + attach machine-readable claim record
32
+ release(subjectId, actor, meta) # unassign + handoff note
33
+ supersede(subjectId, from, to, meta) # reassign with audit trail
34
+ status(subjectId) -> {assignee?, actor?, claimedAt?, meta?}
35
+ list(actor?) -> [subjectId]
36
+ ```
37
+
38
+ | Operation | Arguments | Effect | Returns |
39
+ | --- | --- | --- | --- |
40
+ | `claim` | `subjectId`, `actor`, `meta` | Records durable ownership: assignee (or local-file record), a claim marker, and a versioned claim record carrying the full actor struct. | void (caller re-reads via `status` to confirm) |
41
+ | `release` | `subjectId`, `actor`, `meta` | Clears durable ownership and leaves a handoff note. Normally invoked by the incumbent actor at clean session end. | void |
42
+ | `supersede` | `subjectId`, `from`, `to`, `meta` | Reassigns ownership from a lapsed actor to a successor, with an audit-trail note explaining why (staleness, explicit human confirmation). | void |
43
+ | `status` | `subjectId` | Reads current assignment state without joining liveness. | `{ assignee?, actor?, claimedAt?, meta? }` — the raw assignment-layer read |
44
+ | `list` | `actor?` (optional filter) | Enumerates subject ids currently claimed, optionally filtered to one actor. | `[subjectId]` |
45
+
46
+ `status()` alone is **assignment-layer truth only** — it does not tell a caller whether the claim
47
+ is fresh. Callers that need to decide "is this work available" must compute the join described
48
+ next, not call `status()` in isolation.
49
+
50
+ **Actor identity is runtime-agnostic**: `{ runtime, session_id, host, human? }`. Claude Code,
51
+ codex, opencode, and pi sessions, and humans, are all actors. Reuse the exact struct
52
+ `scripts/hooks/lib/actor-identity.js`'s `resolveActor`/`serializeActor` already define — do not
53
+ fork a second actor concept. `subject_id` reuses `workItemSlug`'s `owner/repo#id` convention
54
+ (the same string the deterministic session slug derives from) rather than inventing a second
55
+ identifier scheme.
56
+
57
+ ## The assignment ⋈ liveness join
58
+
59
+ Effective claim state is always a **join** of two layers with distinct jobs. Neither layer is
60
+ trusted alone:
61
+
62
+ | Layer | Records | Lifetime | Medium |
63
+ | --- | --- | --- | --- |
64
+ | **Assignment** | intent / ownership | durable, survives crashes | provider (issue assignee/label/comment, or local-file record) |
65
+ | **Liveness** | presence / freshness | ephemeral, TTL-reaped | ADR 0012 claim stream (`freshHolders`) |
66
+
67
+ Readers compute the join from `{ assignment: AssignmentStatus, freshHolders: FreshHolder[] }` to
68
+ one of five effective states:
69
+
70
+ | Assignment | Liveness | Effective state | `pull-work` treatment |
71
+ | --- | --- | --- | --- |
72
+ | assigned | fresh heartbeat | **held** | excluded |
73
+ | assigned | stale / absent | **reclaimable** | offered, via takeover protocol (ADR 0021 §5, out of this contract's scope) |
74
+ | assigned (human) | n/a (humans don't heartbeat) | **human-held** | surfaced, never auto-taken (see below) |
75
+ | unassigned | fresh (claim only) | **held** (assignment lagging) | excluded |
76
+ | unassigned | absent | **free** | offered |
77
+
78
+ This join rule is what makes lost locks structurally impossible: staleness — not assignment — is
79
+ what excludes, so an orphaned assignee/label/record from a dead session can never gate work.
80
+
81
+ The join is a pure function; reuse `scripts/hooks/lib/liveness-read.js`'s exported
82
+ `freshHolders(events, slug, selfActor, nowMs)` for the liveness half rather than re-implementing
83
+ TTL/staleness arithmetic. The join table itself (assignment status × liveness freshness →
84
+ effective state) is the only new logic — it has no other implementation in this repository.
85
+
86
+ ## Lazy-correction transition table
87
+
88
+ Nothing updates the provider on a timer; every mutation has a responsible actor, invoked at a
89
+ specific lifecycle moment (ADR 0021 §4). This table names every transition for completeness; it
90
+ also records which issue implements each row so a future reader does not assume any single issue
91
+ delivers all four:
92
+
93
+ | Transition | Mutator | When | Owning issue |
94
+ | --- | --- | --- | --- |
95
+ | `claim` | the claiming session | at selection | **#290 (this issue)** |
96
+ | `clean_release` | the incumbent (Stop hook / terminal `advance-state`) | session end — unassign + handoff comment | #292 |
97
+ | `supersede` | the successor, inside the takeover protocol | after the grace beat | #294 |
98
+ | `crash_no_successor` | nobody, initially; corrected by the next actor to want the subject, or the janitor | lazily, or on a janitor sweep | out of scope — the Console relay's first cross-machine duty (ADR 0021 §4/§7) |
99
+
100
+ Lazy correction is *safe* by the join rule above (stale assignment excludes nothing), but leaves
101
+ the human-visible board stale in the crash case until the next claim attempt or a janitor sweep
102
+ corrects it.
103
+
104
+ ## Human-assignee ask-first policy
105
+
106
+ Humans do not heartbeat, so `assigned-to-human + no-liveness` is **normal, not stale** — it must
107
+ never be treated as `reclaimable`. Agents surface idle human assignments ("assigned to `brian` 3
108
+ days ago, no linked activity — reclaim?") and act only on explicit confirmation.
109
+
110
+ - Default behavior: `ask_first`.
111
+ - The threshold (`idle_threshold_days`, default `3`, matching ADR 0021 §6's own illustrative
112
+ example) and behavior (`ask_first` | `never_reclaim`) are a policy knob in
113
+ `assignment-provider-settings.schema.json` (`policy.human_assignee_policy`), not a hardcoded
114
+ constant.
115
+ - **Non-goal:** never auto-supersede a human. The join function must gate on the actor struct's
116
+ `human` field being present (not absent/null) — never on a heuristic over the GitHub login name
117
+ — so a human assignee is always classified `human-held`, regardless of idle duration, and is
118
+ never silently reclaimed by any automated path.
119
+ - This contract and its settings schema define the policy knob and the read-side data a caller
120
+ needs to surface the question (assignee identity, idle duration). The actual "ask the user"
121
+ interaction belongs to whichever skill consumes the join result (`pull-work`), which records it
122
+ as an alignment question rather than a new UI surface.
123
+
124
+ ## Versioned claim-record format
125
+
126
+ The durable claim record is a single JSON object, versioned via `schema_version` so a reader can
127
+ detect an incompatible future shape before parsing fields it does not understand:
128
+
129
+ ```json
130
+ {
131
+ "schema_version": "1.0",
132
+ "role": "AssignmentClaimRecord",
133
+ "subject_id": "kontourai/flow-agents#290",
134
+ "actor": { "runtime": "claude-code", "session_id": "...", "host": "...", "human": null },
135
+ "claimed_at": "2026-07-02T00:00:00Z",
136
+ "ttl_seconds": 1800,
137
+ "branch": "agent/<actor>/<slug>",
138
+ "artifact_dir": ".kontourai/flow-agents/<slug>",
139
+ "status": "claimed"
140
+ }
141
+ ```
142
+
143
+ | Field | Required | Description |
144
+ | --- | --- | --- |
145
+ | `schema_version` | yes | Version of this record shape, `"1.0"`. |
146
+ | `role` | yes | Constant `"AssignmentClaimRecord"`, for readers scanning mixed content (e.g. a GitHub comment thread) for this record type. |
147
+ | `subject_id` | yes | The claimed work item, in `owner/repo#id` form — the same string `workItemSlug` derives the deterministic session slug from. |
148
+ | `actor` | yes | `{ runtime, session_id, host, human? }` — the exact struct `actor-identity.js` defines. `human` is set (non-null) only for a human assignee; its presence, not a username heuristic, gates the human-held join state. |
149
+ | `claimed_at` | yes | ISO-8601 timestamp the claim was recorded. Mirrors the liveness stream's own claim-event field so a reader compares freshness with one mental model across both layers, even though the two are stored in different media. |
150
+ | `ttl_seconds` | yes | Same field name/semantics as the liveness stream's `ttlSeconds` (default `1800`). |
151
+ | `branch` | yes | The branch this actor is working on, per the `agent/<actor>/<slug>` convention. |
152
+ | `artifact_dir` | yes | The deterministic workflow artifact directory for this subject, so a successor inherits the same session. |
153
+ | `status` | yes | `"claimed"` today; future transitions may add `"released"` / `"superseded"` values as the corresponding mutator (see the transition table) is implemented. |
154
+
155
+ **Versioning rule:** bump `schema_version` only on an incompatible change (matching
156
+ `artifact-contract.md`'s existing sidecar rule: "Keep `schema_version` at `1.0` until the schema
157
+ changes incompatibly"). Additive, optional fields do not require a bump.
158
+
159
+ ### GitHub mapping
160
+
161
+ The GitHub implementation represents the record as:
162
+
163
+ - **assignee**: the notification/board hook.
164
+ - **a single `agent:claimed` label** (default name, configurable via
165
+ `policy.label_name`): the board filter.
166
+ - **a machine-readable claim comment**: human-readable prose above a fenced JSON block containing
167
+ the record above, located via a fixed marker (default
168
+ `<!-- flow-agents:assignment-claim -->`, configurable via `policy.claim_comment_marker`) so the
169
+ board stays legible to a human without tooling, while remaining machine-parseable.
170
+
171
+ The comment carries identity because the assignee field cannot: N agent sessions typically share
172
+ one GitHub account, so per-session identity lives in the attached record. Each provider decides
173
+ what it can natively represent versus what goes in the record.
174
+
175
+ ### local-file mapping
176
+
177
+ For tracker-less repos and evals, the same record is written to a JSON file under the artifact
178
+ root (`<artifact-root>/assignment/<sanitized-subject-id>.json`), full read-modify-write, no
179
+ external process involved. This path is the one that does real I/O inside the CLI, since there is
180
+ no external mutation to defer to a skill.
181
+
182
+ **`--actor-json` asymmetry (fix-plan iteration 1, F5, intentional):** local-file `claim`/`release`
183
+ falls back to auto-resolving the actor from the live environment (`resolveActor(process.env)`) when
184
+ `--actor-json` is omitted; `render-claim`/`render-supersede` (GitHub) always require `--actor-json`
185
+ explicitly and fail loud without it. This is intentional, not a footgun to fix later — the
186
+ currently-wired `pull-work` skill always passes `--actor-json` for both provider kinds, so the
187
+ asymmetry has no effect on the shipped path today, but a future direct-CLI caller relying on
188
+ local-file `claim` to fail the same way `render-claim` does when the actor can't be determined
189
+ should pass `--actor-json` explicitly rather than relying on the auto-resolve fallback.
190
+
191
+ ## Implementation Note
192
+
193
+ `schemas/assignment-provider-settings.schema.json` is the settings schema companion to this
194
+ contract — it configures `provider.kind` (`github` | `local-file`), the label name, the claim
195
+ comment marker, and the human-assignee policy knob described above.
196
+
197
+ The GitHub write path follows this repository's `publish-change-helper.ts` precedent — **render,
198
+ don't execute**: the CLI emits the exact, deterministic `gh` argv and comment body for a mutation
199
+ as a pure function; the calling skill runs that exact argv via its Bash tool (never freehand `gh`
200
+ text), then re-fetches and feeds the result back into the CLI's `status` parser to close the round
201
+ trip. This is a deliberate divergence from a literal "CLI shells out to `gh` itself" design: it
202
+ matches the one existing same-shaped precedent in this repository, keeps operational concerns
203
+ (auth, rate limiting, retries, partial-failure sequencing across separate `gh` calls) at the skill
204
+ layer where Bash-tool error handling already lives, and avoids introducing this repository's first
205
+ `execFileSync`-to-`gh` code path inside compiled code without a dedicated decision. A future
206
+ implementer of the GitHub mutation path should read this note, not re-derive the reasoning from
207
+ scratch — see the `#290` plan artifact's Design Decision 1 for the full evidence trail if more
208
+ detail is needed.
209
+
210
+ **`gh_commands` execution contract (fix-plan iteration 1, F4):** every `render-*` subcommand's
211
+ `gh_commands` field is a JSON array of **argv arrays** (one element per `gh` argument), not a
212
+ shell-command string. The calling skill MUST execute each entry as argv — every element passed as
213
+ its own separate Bash-tool argument — and MUST NOT concatenate the elements into a single shell
214
+ string or run them via `bash -c`/shell re-interpretation. This matters specifically because
215
+ `claim_comment_body` (and, for `render-supersede`, `previous_record`) can carry attacker-influenced
216
+ text: any GitHub user who can comment on the issue can post a forged claim-marker comment (see the
217
+ sanitization note below), and reconstructing a shell string from that content would reintroduce a
218
+ shell-injection surface the render/execute split is designed to avoid. This is stated explicitly in
219
+ `kits/builder/skills/pull-work/SKILL.md`'s "Assignment Claim On Selection" section as well — this
220
+ paragraph is the contract-side mirror of that instruction, not a duplicate source of truth to drift
221
+ from it.
222
+
223
+ **Untrusted claim-record fields (fix-plan iteration 1, F2):** `extractClaimRecord()` parses the
224
+ fenced JSON claim record out of *any* issue comment matching the claim marker — posting a comment
225
+ requires no elevated access, unlike the assignee/label mutations this contract otherwise gates. Its
226
+ returned record's string fields (`subject_id`, `branch`, `artifact_dir`, `actor.*`, including
227
+ `actor.human`, and each `audit_trail` entry's actor/`reason`) are therefore treated as untrusted
228
+ display input and are run through a control-character-strip + length-cap sanitizer (mirroring
229
+ `workflow-sidecar.ts`'s `stripControlCharsForDisplay`, the established #287/#320 mitigation for
230
+ this input class) before they leave the module in `status`/`list` output. This is display-only — it
231
+ never changes the assignment ⋈ liveness join's classification logic, only the surfaced string
232
+ content.
233
+
234
+ No part of this system writes to the provider on a timer or heartbeats to it (ADR 0021 §4/§7).
235
+ Every write is triggered by a specific lifecycle moment named in the transition table above. The
236
+ one optional exception is a comment-body refresh at an explicit phase transition
237
+ (`policy.comment_refresh_on_phase_transition`, default `false`) — cheap because it reuses the same
238
+ render path already built for `claim`, just called again with an updated `status`/`claimed_at`
239
+ field.
@@ -1,5 +1,7 @@
1
1
  # Builder Kit Workflow State Contract
2
2
 
3
+ > Read [`context/contracts/standing-directives.md`](standing-directives.md) — ratified owner directives that override default engineering conservatism.
4
+
3
5
  This contract defines the durable routing state for Builder Kit build-flow pickup and resume. It keeps `state.json` limited to canonical Flow Agents workflow routing and stores rich pickup decisions in a documented Probe record sidecar referenced by `state.json`, `handoff.json`, and the session artifact.
4
6
 
5
7
  Builder Kit owns product-level build-flow coordination. Flow Agents owns the workflow harness, artifact conventions, primitive execution, and evidence gates. Flow remains the owner of Flow gate semantics. Builder Kit contracts may route to Flow Agents primitives, but must not redefine pass, fail, approval, or release authority semantics.
@@ -25,6 +27,7 @@ Every Builder Kit build-flow pause, resume, or step transition must make the fol
25
27
  | Field | Durable location | Required meaning |
26
28
  | --- | --- | --- |
27
29
  | current step | `state.phase`, `state.status`, and Probe record `current_step` | The current Builder Kit step or direct Flow Agents primitive. |
30
+ | branch | `state.json.branch`; seeded in the session Markdown's `branch:` line | The `agent/<actor>/<slug>` routing branch `ensure-session` derives for a new session (optional field for migration honesty — see `schemas/workflow-state.schema.json`); an explicit `--branch` overrides; an existing session's already-recorded branch is never re-derived (see [ADR 0021](../../docs/adr/0021-assignment-leases-and-stale-claim-takeover.md) §3 and §5). |
28
31
  | next step | `state.next_action` and Probe record `next_step` | The next step to run after resume or after the current primitive exits. |
29
32
  | route reason | Probe record `route_reason`; summarized in `state.next_action.summary` | Why this route was selected, including missing state, evidence, decisions, or blockers. |
30
33
  | selected work item refs | Probe record `selected_work_items`; optionally linked in `handoff.json` | Neutral work item references from the work-item contract. |
@@ -44,7 +47,7 @@ Every Builder Kit build-flow pause, resume, or step transition must make the fol
44
47
 
45
48
  ## Probe Record
46
49
 
47
- A Builder Kit Probe record is a JSON sidecar beside the other workflow artifacts, for example `.flow-agents/<slug>/builder-kit-probe.json`. It is the durable location for pickup/design context that would otherwise make `state.json` too broad.
50
+ A Builder Kit Probe record is a JSON sidecar beside the other workflow artifacts, for example `.kontourai/flow-agents/<slug>/builder-kit-probe.json`. It is the durable location for pickup/design context that would otherwise make `state.json` too broad.
48
51
 
49
52
  Required shape:
50
53
 
@@ -168,7 +171,7 @@ Baseline freshness example for missing historical `planned_base_sha`:
168
171
  {
169
172
  "id": "source_artifact",
170
173
  "description": "Source shaped artifact or pickup artifact used to accept the fallback baseline.",
171
- "source": ".flow-agents/<slug>/<slug>--idea-to-backlog.md",
174
+ "source": ".kontourai/flow-agents/<slug>/<slug>--idea-to-backlog.md",
172
175
  "example_value": ".flow-agents/builder-kit-not-verified-resolution-hints/builder-kit-not-verified-resolution-hints--idea-to-backlog.md"
173
176
  }
174
177
  ],
@@ -0,0 +1,265 @@
1
+ # Decision Registry Contract
2
+
3
+ > Read [`context/contracts/standing-directives.md`](standing-directives.md) — ratified owner directives that override default engineering conservatism.
4
+
5
+ Normative contract for the topic-keyed decision registry. This is the source of
6
+ truth for how NEW decisions are recorded in a Kontour repository. Numbered ADRs
7
+ under `docs/adr/` are frozen history and are never written for new decisions
8
+ (see [Relationship to ADRs](#relationship-to-adrs-and-grill-with-docs)).
9
+
10
+ Machine-checkable structure lives in `schemas/decision-record.schema.json`; the
11
+ validator/generator is `scripts/check-decisions.cjs`, exposed as
12
+ `npm run check:decisions` and `npm run gen:decisions-index` and wired into the
13
+ required `source-and-static` CI lane.
14
+
15
+ ## Why topic-keyed living records
16
+
17
+ Numbered ADRs accumulate three failure modes at multi-contributor, multi-repo
18
+ scale: contradictions (a new ADR appends a conflicting decision instead of
19
+ replacing the old one), bloat (derivation context is inlined alongside the
20
+ decision), and numbering collisions (two contributors both grab `0021-`). The
21
+ registry removes all three by construction:
22
+
23
+ - **One file per decision subject**, keyed by a vocabulary noun, not a number.
24
+ - **Supersession is an edit**, so the current answer is always the only answer.
25
+ - **Derivation context is linked, not inlined**, so files stay lean.
26
+
27
+ ## File location and shape
28
+
29
+ - Each decision lives at `docs/decisions/<topic-slug>.md`.
30
+ - Exactly **one file per decision SUBJECT**. If two questions have the same
31
+ subject, they share a file; if a file is answering two subjects, split it.
32
+ - A generated index lives at `docs/decisions/index.md` (see [Index](#index)).
33
+ - The file holds ONLY the current decision plus lean rationale. It does not
34
+ inline transcripts, exploration, meeting notes, or superseded prose.
35
+
36
+ A topic file is YAML frontmatter followed by a short Markdown body:
37
+
38
+ ```markdown
39
+ ---
40
+ status: current
41
+ subject: Decision records
42
+ decided: 2026-07-03
43
+ evidence:
44
+ - kind: issue
45
+ ref: https://github.com/kontourai/flow-agents/issues/310
46
+ - kind: session-archive
47
+ ref: .kontourai/flow-agents/decision-registry-shape/decision-registry-shape--idea-to-backlog.md
48
+ ---
49
+
50
+ # Decision records
51
+
52
+ The current answer, plus lean rationale. Derivation context is linked via
53
+ `evidence[]`, never pasted here.
54
+ ```
55
+
56
+ ## Slug rules: vocabulary is the namespace
57
+
58
+ - Slugs are **nouns from the repository's CONTEXT.md domain vocabulary**. The
59
+ ubiquitous language is the topic namespace; topic identity is downstream of it.
60
+ - Slugs are lowercase kebab-case: `^[a-z0-9]+(-[a-z0-9]+)*$`, matching a
61
+ glossary term (e.g. the glossary term "Decision Records" ->
62
+ `decision-records.md`).
63
+ - **If the subject term is absent from CONTEXT.md, add it to CONTEXT.md first**,
64
+ then create the topic file. This keeps slugs stable and collision-free: two
65
+ contributors deciding the same subject land on the same slug rather than
66
+ competing for the next number.
67
+
68
+ ## Frontmatter fields
69
+
70
+ Validated by `schemas/decision-record.schema.json`:
71
+
72
+ | Field | Required | Meaning |
73
+ | --- | --- | --- |
74
+ | `status` | yes | `current` \| `superseded` \| `merged` \| `needs-decision` (see [Status](#status-values)). |
75
+ | `subject` | yes | The decision subject, a CONTEXT.md vocabulary noun phrase. |
76
+ | `decided` | yes | ISO date `YYYY-MM-DD` the current decision was ratified. |
77
+ | `evidence[]` | yes | `{kind, ref}` links to durable provenance. Never a secret. |
78
+ | `supersedes[]` | no | Topic slugs whose subjects this file absorbed. |
79
+ | `superseded_by` | when superseded | The slug that now carries the answer. |
80
+ | `merged_into` | when merged | The slug this subject was folded into. |
81
+
82
+ ### Status values
83
+
84
+ - **`current`** — this file holds the live answer for its subject. Must not
85
+ carry `superseded_by` or `merged_into`.
86
+ - **`superseded`** — a tombstone: the subject moved to a different topic slug
87
+ (renamed or split). Requires `superseded_by`.
88
+ - **`merged`** — a tombstone: the subject was folded into another topic. Requires
89
+ `merged_into`.
90
+ - **`needs-decision`** — a stub that names an open subject with no ratified answer
91
+ yet. It carries provenance `evidence[]` (e.g. the frozen ADR whose subject is
92
+ still open) but no live decision body. This value exists so ADR freeze tooling
93
+ (issue #314) can record that a frozen ADR's subject still needs a living
94
+ decision without inventing an answer. A `needs-decision` stub is not a
95
+ tombstone and must not carry `superseded_by`/`merged_into`.
96
+
97
+ ### Evidence refs
98
+
99
+ Derivation context is LINKED, never inlined. Each `evidence[]` entry is
100
+ `{kind, ref}` where `kind` is one of `issue`, `pr`, `commit`, `session-archive`,
101
+ `adr`, `doc`, `url`:
102
+
103
+ - **`session-archive`** — a path to an archived session artifact (under
104
+ `.kontourai/`) promoted at retirement. Grilling/probe transcripts retire into
105
+ the session archive and are linked here as decision provenance.
106
+ - **`adr`** — a frozen numbered ADR under `docs/adr/` that is provenance for this
107
+ decision.
108
+ - **`issue`/`pr`/`commit`** — GitHub references (number, URL, or SHA).
109
+ - **`doc`/`url`** — a repository document path or external permalink.
110
+
111
+ `ref` values must never contain secret-shaped material (AWS access keys, GitHub
112
+ tokens, private-key blocks, JWTs, or `secret:`/`token:`-style literals). The
113
+ validator refuses them; link durable provenance, never a credential.
114
+
115
+ ## Supersession by edit
116
+
117
+ The normal way a decision changes is: **edit the topic file in place.** Update
118
+ the body to the new decision, bump `decided`, and add an `evidence[]` ref to the
119
+ PR/issue that changed it. There is no second file, no appendix, and no
120
+ contradiction — the file always states exactly one current answer.
121
+
122
+ Example — the pre-edit file:
123
+
124
+ ```markdown
125
+ ---
126
+ status: current
127
+ subject: Artifact retention window
128
+ decided: 2026-04-01
129
+ evidence:
130
+ - kind: pr
131
+ ref: https://github.com/kontourai/flow-agents/pull/120
132
+ ---
133
+
134
+ # Artifact retention window
135
+
136
+ Runtime artifacts are retained for 30 days, then archived.
137
+ ```
138
+
139
+ After a later decision to shorten the window, the SAME file becomes:
140
+
141
+ ```markdown
142
+ ---
143
+ status: current
144
+ subject: Artifact retention window
145
+ decided: 2026-07-01
146
+ evidence:
147
+ - kind: pr
148
+ ref: https://github.com/kontourai/flow-agents/pull/120
149
+ - kind: pr
150
+ ref: https://github.com/kontourai/flow-agents/pull/305
151
+ ---
152
+
153
+ # Artifact retention window
154
+
155
+ Runtime artifacts are retained for 14 days, then archived.
156
+ ```
157
+
158
+ The old answer is gone from the head; its provenance survives in the linked PRs.
159
+
160
+ ## Tombstones: when a topic moves or merges
161
+
162
+ Editing covers a decision changing. The rarer case is a SUBJECT moving to a
163
+ different slug. Then the old file becomes a tombstone so inbound links and
164
+ retrieval still resolve.
165
+
166
+ ### `superseded_by` — the subject moved to another slug
167
+
168
+ Use when a subject is renamed or split and its answer now lives under a different
169
+ slug. The old file:
170
+
171
+ ```markdown
172
+ ---
173
+ status: superseded
174
+ subject: Artifact retention window
175
+ decided: 2026-07-02
176
+ superseded_by: artifact-lifecycle
177
+ evidence:
178
+ - kind: pr
179
+ ref: https://github.com/kontourai/flow-agents/pull/330
180
+ ---
181
+
182
+ # Artifact retention window
183
+
184
+ Superseded. The current decision for this subject lives in
185
+ [artifact-lifecycle](./artifact-lifecycle.md).
186
+ ```
187
+
188
+ ### `merged_into` — the subject folded into an existing topic
189
+
190
+ Use when a subject is absorbed by another existing topic. The absorbed file:
191
+
192
+ ```markdown
193
+ ---
194
+ status: merged
195
+ subject: Artifact retention window
196
+ decided: 2026-07-02
197
+ merged_into: artifact-lifecycle
198
+ evidence:
199
+ - kind: pr
200
+ ref: https://github.com/kontourai/flow-agents/pull/330
201
+ ---
202
+
203
+ # Artifact retention window
204
+
205
+ Merged into [artifact-lifecycle](./artifact-lifecycle.md).
206
+ ```
207
+
208
+ The surviving file may record what it absorbed with `supersedes: [artifact-retention-window]`.
209
+
210
+ Both tombstone forms require the target slug to exist (a validator error
211
+ otherwise) and must not point at their own slug.
212
+
213
+ ## Retrieval-at-write
214
+
215
+ Before recording a decision, **consult `docs/decisions/index.md`** (the
216
+ always-loaded slug + one-liner index) and decide **revise-vs-create**:
217
+
218
+ 1. If an existing topic covers the subject, **revise that file** (edit in place,
219
+ or tombstone + move if the subject itself is changing identity).
220
+ 2. If no topic covers it, confirm the subject noun exists in CONTEXT.md (add it
221
+ if absent), then **create** `docs/decisions/<slug>.md`.
222
+
223
+ This retrieval-at-write step, plus a health check at rest (the validator + index
224
+ freshness in CI), is sufficient at this cardinality; no embeddings or semantic
225
+ topic matching are required.
226
+
227
+ ## Index
228
+
229
+ `docs/decisions/index.md` is generated by `npm run gen:decisions-index`
230
+ (`scripts/check-decisions.cjs gen-index`). It is deterministic and idempotent: a
231
+ second run with no topic change is diff-clean. It lists each topic slug, its
232
+ status, and a one-line summary (the `subject`, falling back to the first body
233
+ line). Do not edit it by hand. `npm run check:decisions` fails if the index is
234
+ missing or stale, so CI catches an out-of-date index.
235
+
236
+ The index is referenced from `CONTEXT.md` so retrieval-at-write always has a
237
+ cheap, loaded pointer into the registry.
238
+
239
+ ## Validation
240
+
241
+ `npm run check:decisions` (wired into the required `source-and-static` CI lane)
242
+ exits nonzero on any of:
243
+
244
+ - invalid frontmatter (missing `status`/`subject`/`decided`/`evidence`);
245
+ - unknown `status`;
246
+ - a secret-shaped `evidence[].ref`;
247
+ - a `superseded_by`/`merged_into`/`supersedes` slug that points at a missing
248
+ topic file;
249
+ - a status/relationship mismatch (e.g. `current` carrying a tombstone field, or
250
+ `superseded` without `superseded_by`);
251
+ - a stale or missing `docs/decisions/index.md`.
252
+
253
+ ## Relationship to ADRs and grill-with-docs
254
+
255
+ - **CONTEXT.md is the only external compatibility surface.** Grill-with-docs
256
+ interop reads the domain vocabulary from CONTEXT.md; the registry keys its
257
+ slugs off that same vocabulary. We inherit the grill-with-docs behavior, not
258
+ the numbered-ADR file format.
259
+ - **Numbered ADRs (`docs/adr/NNNN-*.md`) are frozen history.** They are never
260
+ written for new decisions and are never converted into topic files. Existing
261
+ ADRs are frozen-and-indexed by separate tooling (issue #314); a topic file may
262
+ cite a frozen ADR via an `adr` evidence ref as provenance.
263
+
264
+ The pilot record self-hosting this contract is
265
+ [docs/decisions/decision-records.md](../../docs/decisions/decision-records.md).
@@ -1,5 +1,7 @@
1
1
  # Delivery Contract
2
2
 
3
+ > Read [`context/contracts/standing-directives.md`](standing-directives.md) — ratified owner directives that override default engineering conservatism.
4
+
3
5
  Delivery chains planning, execution, review, verification, Goal Fit, and final acceptance until the user-facing goal is genuinely handled.
4
6
 
5
7
  ## Workflow
@@ -59,7 +61,7 @@ After CI passes and the work is merged, released, or otherwise accepted:
59
61
  - [ ] working artifacts archived or linked
60
62
  - [ ] long-lived docs updated with why and how the feature was built
61
63
  - [ ] durable docs link back to the provider record, archived plan, or session artifact when useful
62
- - [ ] local `.flow-agents/` runtime artifacts remain untracked, and durable outcomes are promoted before merge to `main`
64
+ - [ ] local `.kontourai/flow-agents/` runtime artifacts remain untracked, and durable outcomes are promoted before merge to `main`
63
65
  - [ ] follow-up issues or learning-review items created for deferred work
64
66
  - [ ] **workspace cleaned up after a confirmed merge**: the merge is verified from the provider's own merge record (a merge commit / `mergedAt`), not a green check or a command exit code; then the isolated worktree is removed and the now-merged branch is deleted locally and on the remote, honoring the `worktree_lifecycle` (`retain_until: pr_merged`) recorded at selection. Never delete a branch or worktree before the merge is confirmed. A delivery is not complete while it leaves a stale worktree or merged branch behind.
65
67
 
@@ -1,5 +1,7 @@
1
1
  # Execution Contract
2
2
 
3
+ > Read [`context/contracts/standing-directives.md`](standing-directives.md) — ratified owner directives that override default engineering conservatism.
4
+
3
5
  Execution turns an approved plan artifact into code and local evidence while preserving parallel safety.
4
6
 
5
7
  ## Required Inputs
@@ -42,6 +44,29 @@ Execution may upgrade to a stronger mode when risk increases. Downgrades require
42
44
  - Worker delegation must name the exact worker role (`tool-worker`) rather than spawning an unnamed/default implementation agent.
43
45
  - After each wave, collect results, check conflicts, and update the session artifact before starting the next wave.
44
46
 
47
+ ## Delegation: Model Routing
48
+
49
+ Model-routing policy is data, not code: `.datum/config.json` (schema:
50
+ `@kontourai/datum`, provider/role registry) is the source of truth for which
51
+ model backs which delegate role. This contract only says where to read it.
52
+
53
+ - When spawning a delegate, resolve its role via `npx @kontourai/datum resolve <role> --json`
54
+ (or the `@kontourai/datum` library's `resolve()`) and pass the resolved
55
+ `model` explicitly to the delegate invocation. Do not hardcode a model name
56
+ in a skill, agent definition, or generated file — the role name is the
57
+ stable reference; the model it resolves to can change without touching
58
+ delegation call sites.
59
+ - Role selection follows task shape, not delegate identity: fully-specified
60
+ mechanical tasks resolve `delegate-mechanical`; precisely-planned
61
+ implementation tasks resolve `delegate-implementation`; tasks needing design
62
+ latitude resolve `delegate-design`. The orchestrator's own model (planning,
63
+ gates, adversarial verification) resolves `orchestrator`, typically inherited
64
+ rather than overridden.
65
+ - When `datum` (the CLI/library) or `.datum/config.json` is absent or a role
66
+ fails to resolve, fall back to the runtime's inherited model and note the
67
+ fallback in the session/task artifact — do not block delegation on datum
68
+ being present.
69
+
45
70
  ## Completion Rules
46
71
 
47
72
  Execution is complete only when:
@@ -1,5 +1,7 @@
1
1
  # Governance Adapter Contract
2
2
 
3
+ > Read [`context/contracts/standing-directives.md`](standing-directives.md) — ratified owner directives that override default engineering conservatism.
4
+
3
5
  Governance adapters let Flow Agents ask an external tool for policy, proof, or trust evidence without baking repo-specific policy into Flow Agents core.
4
6
 
5
7
  ## Boundary