@kontourai/flow-agents 2.3.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/.github/CODEOWNERS +8 -0
  2. package/.github/workflows/ci.yml +32 -0
  3. package/AGENTS.md +4 -4
  4. package/CHANGELOG.md +39 -0
  5. package/CONTEXT.md +14 -0
  6. package/README.md +9 -3
  7. package/agents/dev.json +1 -1
  8. package/agents/tool-code-reviewer.json +1 -1
  9. package/agents/tool-planner.json +3 -3
  10. package/agents/tool-verifier.json +3 -3
  11. package/build/src/cli/console-learning-projection.js +3 -2
  12. package/build/src/cli/init.js +104 -21
  13. package/build/src/cli/kit.js +37 -6
  14. package/build/src/cli/pull-work-provider.js +1 -1
  15. package/build/src/cli/usage-feedback.js +3 -3
  16. package/build/src/cli/validate-hook-influence.js +1 -0
  17. package/build/src/cli/validate-workflow-artifacts.js +22 -6
  18. package/build/src/cli/workflow-artifact-cleanup-audit.js +36 -2
  19. package/build/src/cli/workflow-sidecar.d.ts +14 -7
  20. package/build/src/cli/workflow-sidecar.js +792 -69
  21. package/build/src/flow-kit/validate.d.ts +23 -0
  22. package/build/src/flow-kit/validate.js +67 -1
  23. package/build/src/index.d.ts +1 -1
  24. package/build/src/index.js +1 -1
  25. package/build/src/lib/flow-resolver.d.ts +6 -4
  26. package/build/src/lib/flow-resolver.js +125 -58
  27. package/build/src/lib/local-artifact-root.d.ts +14 -2
  28. package/build/src/lib/local-artifact-root.js +22 -5
  29. package/build/src/lib/workflow-learning-projection.js +2 -2
  30. package/build/src/runtime-adapters.d.ts +12 -0
  31. package/build/src/runtime-adapters.js +56 -16
  32. package/build/src/tools/build-universal-bundles.js +49 -16
  33. package/build/src/tools/generate-context-map.js +10 -8
  34. package/build/src/tools/validate-source-tree.js +23 -1
  35. package/console.telemetry.json +22 -0
  36. package/context/contracts/artifact-contract.md +38 -7
  37. package/context/contracts/builder-kit-workflow-state-contract.md +3 -2
  38. package/context/contracts/decision-registry-contract.md +263 -0
  39. package/context/contracts/delivery-contract.md +1 -1
  40. package/context/contracts/planning-contract.md +2 -1
  41. package/context/contracts/review-contract.md +5 -3
  42. package/context/contracts/verification-contract.md +9 -1
  43. package/context/contracts/work-item-contract.md +1 -1
  44. package/context/deferred/parallelization.md +1 -1
  45. package/context/gate-awareness.md +4 -4
  46. package/context/scripts/git-status.sh +2 -2
  47. package/context/scripts/hooks/config-protection.js +502 -2
  48. package/context/scripts/hooks/lib/config-protection-remedies.js +69 -0
  49. package/context/scripts/hooks/run-hook.js +14 -1
  50. package/context/scripts/hooks/stop-goal-fit.js +1140 -99
  51. package/context/scripts/hooks/workflow-steering.js +212 -6
  52. package/docs/adr/0007-flow-skill-kit-tool-boundary.md +2 -0
  53. package/docs/adr/0015-flow-flow-agents-boundary-reconciliation.md +35 -0
  54. package/docs/adr/0019-kit-dependency-ownership.md +62 -0
  55. package/docs/adr/0020-trust-reconcile-manifest-and-claim-classification.md +290 -0
  56. package/docs/adr/0021-assignment-leases-and-stale-claim-takeover.md +221 -0
  57. package/docs/adr/0022-fail-closed-delivery-reconciliation-with-governed-exemptions.md +283 -0
  58. package/docs/agent-system-guidebook.md +3 -3
  59. package/docs/agent-usage-feedback-loop.md +6 -6
  60. package/docs/context-map.md +9 -7
  61. package/docs/decisions/decision-records.md +57 -0
  62. package/docs/decisions/index.md +14 -0
  63. package/docs/decisions/promotion-gate.md +52 -0
  64. package/docs/developer-architecture.md +1 -1
  65. package/docs/fixture-ownership.md +3 -0
  66. package/docs/flow-kit-repository-contract.md +5 -5
  67. package/docs/getting-started.md +12 -2
  68. package/docs/integrations/harness-install.md +2 -1
  69. package/docs/kit-authoring-guide.md +64 -5
  70. package/docs/knowledge-kit.md +5 -1
  71. package/docs/learnings/2026-07-improvement-program.md +203 -0
  72. package/docs/migrations.md +6 -5
  73. package/docs/north-star.md +2 -2
  74. package/docs/operating-layers.md +4 -3
  75. package/docs/repository-structure.md +5 -5
  76. package/docs/skills-map.md +8 -8
  77. package/docs/spec/runtime-hook-surface.md +19 -15
  78. package/docs/spikes/graph-provider-2026-07.md +155 -0
  79. package/docs/standards-register.md +7 -7
  80. package/docs/trust-anchor-adoption.md +105 -4
  81. package/docs/work-item-adapters.md +1 -1
  82. package/docs/workflow-artifact-lifecycle.md +72 -9
  83. package/docs/workflow-eval-strategy.md +3 -3
  84. package/docs/workflow-shared-contracts.md +1 -1
  85. package/docs/workflow-usage-guide.md +254 -21
  86. package/evals/acceptance/DEMO-false-completion.md +20 -11
  87. package/evals/acceptance/prove-capture-teeth-declared.sh +15 -15
  88. package/evals/acceptance/prove-capture-teeth.sh +11 -11
  89. package/evals/acceptance/test_kiro_harness.sh +1 -1
  90. package/evals/ci/antigaming-suite.sh +5 -0
  91. package/evals/ci/run-baseline.sh +65 -1
  92. package/evals/fixtures/flow-kit-repository/invalid-bad-dependency/flows/review.flow.json +26 -0
  93. package/evals/fixtures/flow-kit-repository/invalid-bad-dependency/kit.json +13 -0
  94. package/evals/fixtures/flow-kit-repository/valid-with-dependency/flows/review.flow.json +26 -0
  95. package/evals/fixtures/flow-kit-repository/valid-with-dependency/kit.json +13 -0
  96. package/evals/fixtures/hook-influence/cases.json +32 -0
  97. package/evals/fixtures/pull-work-provider/github-issues.json +6 -6
  98. package/evals/fixtures/trust-reconcile-exploits/fabricated-attestation.json +59 -0
  99. package/evals/fixtures/trust-reconcile-exploits/no-label-bypass.json +36 -0
  100. package/evals/fixtures/trust-reconcile-exploits/skip-assumed-bypass.json +59 -0
  101. package/evals/fixtures/trust-reconcile-exploits/status-misassertion.json +48 -0
  102. package/evals/fixtures/trust-reconcile-exploits/waived-command-check.json +44 -0
  103. package/evals/fixtures/trust-reconcile-mixed-bundle/mixed-bundle.json +159 -0
  104. package/evals/fixtures/trust-reconcile-ws3/ws3-bundle.json +920 -0
  105. package/evals/integration/test_actor_identity.sh +254 -0
  106. package/evals/integration/test_builder_step_producers.sh +33 -23
  107. package/evals/integration/test_bundle_install.sh +22 -16
  108. package/evals/integration/test_bundle_lifecycle.sh +11 -11
  109. package/evals/integration/test_captured_fail_reconciliation.sh +50 -50
  110. package/evals/integration/test_checkpoint_signing.sh +43 -0
  111. package/evals/integration/test_claim_lookup.sh +4 -4
  112. package/evals/integration/test_codex_hook_resolution.sh +114 -0
  113. package/evals/integration/test_command_log_concurrency.sh +8 -8
  114. package/evals/integration/test_command_log_fork_classification.sh +3 -3
  115. package/evals/integration/test_command_log_integrity.sh +16 -16
  116. package/evals/integration/test_console_learning_projection.sh +1 -1
  117. package/evals/integration/test_dual_emit_flow_step.sh +145 -0
  118. package/evals/integration/test_enforcer_expects_driven.sh +12 -12
  119. package/evals/integration/test_evidence_capture_hook.sh +14 -13
  120. package/evals/integration/test_fixture_retirement_audit.sh +2 -2
  121. package/evals/integration/test_flow_agents_statusline.sh +2 -2
  122. package/evals/integration/test_flow_kit_install_git.sh +52 -0
  123. package/evals/integration/test_flow_kit_repository.sh +2 -0
  124. package/evals/integration/test_flowdef_session_activation.sh +6 -6
  125. package/evals/integration/test_flowdef_union_floor_regression.sh +429 -0
  126. package/evals/integration/test_gate_bypass_chain.sh +52 -40
  127. package/evals/integration/test_gate_lockdown.sh +97 -73
  128. package/evals/integration/test_gate_review_inquiry_records.sh +3 -3
  129. package/evals/integration/test_goal_fit_escape_hatch.sh +10 -10
  130. package/evals/integration/test_goal_fit_ghost_session.sh +104 -0
  131. package/evals/integration/test_goal_fit_hook.sh +32 -32
  132. package/evals/integration/test_goal_fit_rederive.sh +9 -9
  133. package/evals/integration/test_hook_category_behaviors.sh +103 -0
  134. package/evals/integration/test_hook_influence_cases.sh +1 -0
  135. package/evals/integration/test_install_merge.sh +262 -1
  136. package/evals/integration/test_kit_identity_trust.sh +6 -6
  137. package/evals/integration/test_liveness_conflict_injection.sh +587 -0
  138. package/evals/integration/test_liveness_heartbeat.sh +677 -0
  139. package/evals/integration/test_liveness_verdict.sh +394 -0
  140. package/evals/integration/test_local_flow_kit_install.sh +15 -0
  141. package/evals/integration/test_phase_map_and_gate_claim.sh +63 -11
  142. package/evals/integration/test_promote_gate.sh +178 -0
  143. package/evals/integration/test_publish_delivery.sh +2 -2
  144. package/evals/integration/test_pull_work_liveness_preflight.sh +386 -0
  145. package/evals/integration/test_pull_work_provider.sh +2 -2
  146. package/evals/integration/test_reconcile_soundness.sh +4 -4
  147. package/evals/integration/test_resolvefirststep_security.sh +22 -0
  148. package/evals/integration/test_runtime_adapter_activation.sh +87 -18
  149. package/evals/integration/test_session_resume_roundtrip.sh +147 -0
  150. package/evals/integration/test_sidecar_field_preservation.sh +249 -0
  151. package/evals/integration/test_trust_reconcile.sh +2 -2
  152. package/evals/integration/test_trust_reconcile_manifest.sh +79 -0
  153. package/evals/integration/test_trust_reconcile_mixed_bundle.sh +96 -0
  154. package/evals/integration/test_trust_reconcile_negatives.sh +151 -0
  155. package/evals/integration/test_usage_feedback_global.sh +5 -5
  156. package/evals/integration/test_validate_artifacts_portability.sh +60 -0
  157. package/evals/integration/test_verify_cli.sh +2 -2
  158. package/evals/integration/test_veritas_governance_kit.sh +117 -0
  159. package/evals/integration/test_workflow_artifact_cleanup_audit.sh +26 -1
  160. package/evals/integration/test_workflow_artifacts.sh +33 -0
  161. package/evals/integration/test_workflow_sidecar_writer.sh +723 -74
  162. package/evals/integration/test_workflow_steering_hook.sh +175 -5
  163. package/evals/lib/codex-provider.sh +1 -1
  164. package/evals/lib/node.sh +19 -1
  165. package/evals/run.sh +22 -0
  166. package/evals/static/test_decisions.sh +150 -0
  167. package/evals/static/test_flowdef_codeowners_coverage.sh +56 -0
  168. package/evals/static/test_package.sh +19 -10
  169. package/evals/static/test_universal_bundles.sh +48 -3
  170. package/evals/static/test_workflow_skills.sh +41 -8
  171. package/kits/builder/flows/build.flow.json +3 -74
  172. package/kits/builder/flows/publish-learn.flow.json +90 -0
  173. package/kits/builder/kit.json +11 -0
  174. package/kits/builder/skills/builder-shape/SKILL.md +1 -1
  175. package/kits/builder/skills/deliver/SKILL.md +13 -11
  176. package/kits/builder/skills/design-probe/SKILL.md +4 -4
  177. package/kits/builder/skills/evidence-gate/SKILL.md +26 -1
  178. package/kits/builder/skills/execute-plan/SKILL.md +2 -2
  179. package/kits/builder/skills/fix-bug/SKILL.md +2 -0
  180. package/kits/builder/skills/gate-review/SKILL.md +6 -6
  181. package/kits/builder/skills/idea-to-backlog/SKILL.md +1 -1
  182. package/kits/builder/skills/learning-review/SKILL.md +7 -7
  183. package/kits/builder/skills/pickup-probe/SKILL.md +5 -4
  184. package/kits/builder/skills/plan-work/SKILL.md +27 -5
  185. package/kits/builder/skills/pull-work/SKILL.md +80 -5
  186. package/kits/builder/skills/release-readiness/SKILL.md +4 -4
  187. package/kits/builder/skills/review-work/SKILL.md +24 -1
  188. package/kits/builder/skills/tdd-workflow/SKILL.md +2 -0
  189. package/kits/builder/skills/verify-work/SKILL.md +8 -1
  190. package/kits/catalog.json +6 -0
  191. package/kits/veritas-governance/adapter/readiness-to-trust-bundle.mjs +178 -0
  192. package/kits/veritas-governance/docs/README.md +75 -0
  193. package/kits/veritas-governance/fixtures/readiness/not-ready.readiness-report.json +4645 -0
  194. package/kits/veritas-governance/fixtures/readiness/ready.readiness-report.json +4403 -0
  195. package/kits/veritas-governance/flows/readiness-check.flow.json +35 -0
  196. package/kits/veritas-governance/kit.json +16 -0
  197. package/package.json +8 -4
  198. package/packaging/README.md +2 -2
  199. package/packaging/conformance/fixtures/config-protection--allow-read-state-json.json +20 -0
  200. package/packaging/conformance/fixtures/config-protection--block-write-state-json.json +20 -0
  201. package/packaging/conformance/fixtures/stop-goal-fit--block-bundle-disputed-claim.json +1 -1
  202. package/packaging/conformance/fixtures/stop-goal-fit--block-capture-contradicts-claimed-pass.json +1 -1
  203. package/packaging/manifest.json +9 -10
  204. package/schemas/decision-record.schema.json +109 -0
  205. package/schemas/workflow-evidence.schema.json +1 -1
  206. package/schemas/workflow-state.schema.json +5 -0
  207. package/scripts/README.md +8 -3
  208. package/scripts/check-content-boundary.cjs +1 -1
  209. package/scripts/check-decisions.cjs +356 -0
  210. package/scripts/ci/derive-claim-status.mjs +74 -0
  211. package/scripts/ci/trust-reconcile.js +436 -79
  212. package/scripts/git-status.sh +2 -2
  213. package/scripts/hooks/claude-telemetry-hook.js +37 -3
  214. package/scripts/hooks/codex-telemetry-hook.js +36 -2
  215. package/scripts/hooks/config-protection.js +102 -30
  216. package/scripts/hooks/evidence-capture.js +3 -3
  217. package/scripts/hooks/lib/actor-identity.js +368 -0
  218. package/scripts/hooks/lib/config-protection-remedies.js +69 -0
  219. package/scripts/hooks/lib/liveness-heartbeat.js +361 -0
  220. package/scripts/hooks/lib/liveness-policy.js +127 -0
  221. package/scripts/hooks/lib/liveness-read.js +105 -12
  222. package/scripts/hooks/lib/liveness-write.js +46 -0
  223. package/scripts/hooks/lib/local-artifact-paths.js +12 -6
  224. package/scripts/hooks/opencode-telemetry-hook.js +27 -1
  225. package/scripts/hooks/pi-telemetry-hook.js +27 -1
  226. package/scripts/hooks/stop-goal-fit.js +157 -26
  227. package/scripts/hooks/workflow-steering.js +77 -3
  228. package/scripts/install-codex-home.sh +138 -38
  229. package/scripts/statusline/flow-agents-statusline.js +2 -2
  230. package/src/cli/console-learning-projection.ts +3 -2
  231. package/src/cli/init.ts +101 -21
  232. package/src/cli/kit.ts +37 -6
  233. package/src/cli/public-api.test.mjs +58 -6
  234. package/src/cli/pull-work-provider.ts +1 -1
  235. package/src/cli/trust-bundle-policy-order.test.mjs +87 -0
  236. package/src/cli/usage-feedback.ts +3 -3
  237. package/src/cli/validate-hook-influence.ts +1 -0
  238. package/src/cli/validate-workflow-artifacts.ts +22 -6
  239. package/src/cli/veritas-readiness-adapter.test.mjs +267 -0
  240. package/src/cli/workflow-artifact-cleanup-audit.ts +35 -2
  241. package/src/cli/workflow-sidecar.ts +795 -71
  242. package/src/flow-kit/validate.ts +74 -1
  243. package/src/index.ts +6 -2
  244. package/src/lib/flow-resolver.ts +123 -55
  245. package/src/lib/local-artifact-root.ts +24 -5
  246. package/src/lib/workflow-learning-projection.ts +2 -2
  247. package/src/runtime-adapters.ts +61 -15
  248. package/src/tools/build-universal-bundles.ts +49 -16
  249. package/src/tools/generate-context-map.ts +10 -8
  250. package/src/tools/validate-source-tree.ts +23 -1
@@ -3,7 +3,11 @@
3
3
  * Workflow Steering Hook
4
4
  *
5
5
  * Injects phase-transition reminders after use_subagent calls complete and
6
- * ambient workflow-state reminders at the start of the next user turn.
6
+ * re-grounds the active workflow state at the start of every user turn and on
7
+ * SessionStart. SessionStart fires after context compaction and on resume, so
8
+ * re-injecting the goal/phase/next-step there is what makes an in-flight goal
9
+ * survive context loss instead of relying on the model voluntarily re-reading
10
+ * the sidecar.
7
11
  *
8
12
  * Non-blocking — always exits 0.
9
13
  */
@@ -12,6 +16,9 @@
12
16
 
13
17
  const fs = require('fs');
14
18
  const path = require('path');
19
+ const { readLivenessEvents, freshHolders } = require('./lib/liveness-read');
20
+ const { resolveActor } = require('./lib/actor-identity');
21
+ const { flowAgentsArtifactRootsForRead } = require('./lib/local-artifact-paths');
15
22
 
16
23
  const STEERING = {
17
24
  'tool-planner': [
@@ -96,7 +103,8 @@ function readJson(file) {
96
103
  }
97
104
 
98
105
  function latestWorkflowState(root) {
99
- const states = walkStateFiles(path.join(root, '.flow-agents'))
106
+ const states = flowAgentsArtifactRootsForRead(root)
107
+ .flatMap(artifactRoot => walkStateFiles(artifactRoot))
100
108
  .map(file => {
101
109
  let stat;
102
110
  try { stat = fs.statSync(file); } catch { return null; }
@@ -198,6 +206,169 @@ function contextMapSteering(root) {
198
206
  ].join(' ');
199
207
  }
200
208
 
209
+ function promptText(input) {
210
+ const candidates = [
211
+ input && input.prompt,
212
+ input && input.user_prompt,
213
+ input && input.message,
214
+ input && input.text,
215
+ input && input.tool_input && input.tool_input.prompt,
216
+ ];
217
+ for (const candidate of candidates) {
218
+ if (typeof candidate === 'string' && candidate.trim()) return candidate;
219
+ }
220
+ return '';
221
+ }
222
+
223
+ function looksLikeBuilderWork(text) {
224
+ const normalized = safeStateText(text, 2000).toLowerCase();
225
+ if (!normalized) return false;
226
+ const readOnlyIntent = /\b(read[- ]only|review[- ]only|no (source |code |file )?(changes?|edits?|modifications?)|do not (modify|edit|change|write|fix|implement|touch)|don't (modify|edit|change|write|fix|implement|touch)|dont (modify|edit|change|write|fix|implement|touch))\b/.test(normalized);
227
+ if (readOnlyIntent) return false;
228
+ const questionOnlyIntent = /^(what|which|who|when|where|why|how)\b/.test(normalized);
229
+ if (questionOnlyIntent) return false;
230
+ const noCodeQuestion = /\b(explain|describe|summari[sz]e|what is|how does|how do i|why does|review|critique)\b/.test(normalized)
231
+ && !/\b(build|create|implement|ship|deliver|fix|debug|refactor|add|update|change|modify|wire|migrate|install|scaffold|write|code)\b/.test(normalized);
232
+ if (noCodeQuestion) return false;
233
+ const verificationOnlyIntent = /\b(validate|test|verify|check)\b/.test(normalized)
234
+ && !/\b(build|create|implement|ship|deliver|fix|debug|refactor|add|update|change|modify|wire|migrate|install|scaffold|write|code)\b/.test(normalized);
235
+ if (verificationOnlyIntent) return false;
236
+ const hasWorkVerb = /\b(build|create|implement|ship|deliver|fix|debug|refactor|add|update|change|modify|wire|migrate|install|scaffold|write|code)\b/.test(normalized);
237
+ const hasWorkObject = /\b(api|endpoint|component|feature|bug|failing test|regression|integration|hook|script|cli|config|schema|migration)\b/.test(normalized);
238
+ const hasRequestLanguage = /\b(can you|please|let's|lets|need|should|make|ensure|support|add|update|fix|work on)\b/.test(normalized);
239
+ return hasWorkVerb || (hasWorkObject && hasRequestLanguage);
240
+ }
241
+
242
+ function builderWorkflowSteering(input) {
243
+ if (!looksLikeBuilderWork(promptText(input))) return '';
244
+ return [
245
+ 'BUILDER WORKFLOW ROUTE: this user prompt looks like coding/build work.',
246
+ 'Builder lifecycle: shape raw ideas -> build selected work -> publish verified branch/PR -> learn correction feedback.',
247
+ 'Before source edits or implementation commands, activate Builder Kit delivery workflow.',
248
+ 'If the user explicitly requested TDD, activate `tdd-workflow`; otherwise activate `deliver` and keep the session on `builder.build`.',
249
+ 'Use `npm run workflow:sidecar -- ensure-session --flow-id builder.build ...` when the repo provides the sidecar writer.',
250
+ 'After local verification, continue to publish/release-readiness and learning-review; do not treat local verification as terminal delivery.',
251
+ 'Do not bypass plan-work -> execute-plan -> review-work -> verify-work for coding tasks; direct implementation is only acceptable when the user explicitly asks for no workflow or explanation-only help.',
252
+ ].join(' ');
253
+ }
254
+
255
+ /**
256
+ * Compose the RESUME block for SessionStart.
257
+ *
258
+ * Reads trust.bundle, handoff.json, and the liveness stream beside state.json;
259
+ * all reads are fail-open (errors → skip that section, never throw).
260
+ *
261
+ * Returns a multi-line string starting with "RESUME: <slug> status:<s> phase:<p>"
262
+ * or '' if the current state has status 'done', 'archived', or 'accepted'.
263
+ *
264
+ * @param {string} root Repository root
265
+ * @param {{ file: string, payload: object }} current Latest active state entry
266
+ * @returns {string}
267
+ */
268
+ function resumeSteering(root, current) {
269
+ try {
270
+ const state = current.payload;
271
+ const workflowDir = path.dirname(current.file);
272
+ const slug = state.task_slug || path.basename(workflowDir);
273
+ const next = state.next_action || {};
274
+
275
+ if (next.status === 'done' || state.status === 'archived' || state.status === 'accepted') return '';
276
+
277
+ const lines = [];
278
+
279
+ // Header line
280
+ lines.push(`RESUME: ${slug} status:${safeStateText(state.status, 60)} phase:${safeStateText(state.phase, 60)}`);
281
+
282
+ // Full next action (240-char display path, not the 80-char normalization)
283
+ const nextSummary = next.summary ? safeStateText(next.summary, 240) : 'none';
284
+ lines.push(`Next action: ${nextSummary}`);
285
+
286
+ // Plan artifact path
287
+ let planPath = 'not found';
288
+ try {
289
+ const artifactPaths = Array.isArray(state.artifact_paths) ? state.artifact_paths : [];
290
+ const planEntry = artifactPaths.find(p => typeof p === 'string' && p.endsWith('--plan-work.md'));
291
+ if (planEntry) {
292
+ planPath = planEntry;
293
+ } else {
294
+ const candidate = path.join(workflowDir, `${slug}--plan-work.md`);
295
+ if (fs.existsSync(candidate)) planPath = candidate;
296
+ }
297
+ } catch { /* skip */ }
298
+ lines.push(`Plan: ${planPath}`);
299
+
300
+ // Handoff: next_steps[0] and blockers
301
+ let nextStep = 'none';
302
+ let blockers = 'none';
303
+ try {
304
+ const handoff = readJson(path.join(workflowDir, 'handoff.json'));
305
+ if (handoff) {
306
+ const steps = Array.isArray(handoff.next_steps) ? handoff.next_steps : [];
307
+ if (steps.length > 0) nextStep = safeStateText(String(steps[0]), 240);
308
+ const bArr = Array.isArray(handoff.blockers) ? handoff.blockers : [];
309
+ if (bArr.length > 0) blockers = bArr.map(b => safeStateText(String(b), 120)).join(', ');
310
+ }
311
+ } catch { /* skip */ }
312
+ lines.push(`Next step: ${nextStep}`);
313
+ lines.push(`Blockers: ${blockers}`);
314
+
315
+ // Trust bundle
316
+ try {
317
+ const bundle = readJson(path.join(workflowDir, 'trust.bundle'));
318
+ if (bundle) {
319
+ const claims = Array.isArray(bundle.claims) ? bundle.claims : [];
320
+ let verified = 0;
321
+ let disputed = 0;
322
+ const unresolved = [];
323
+ for (const claim of claims) {
324
+ if (!claim || typeof claim !== 'object') continue;
325
+ const status = String(claim.status || '');
326
+ if (status === 'verified') {
327
+ verified++;
328
+ } else if (status === 'disputed' || status === 'unknown') {
329
+ disputed++;
330
+ unresolved.push(claim);
331
+ }
332
+ }
333
+ const total = claims.length;
334
+ lines.push(`Trust: ${verified} verified / ${disputed} disputed / ${total} total`);
335
+ for (const claim of unresolved) {
336
+ const id = safeStateText(String(claim.id || ''), 120);
337
+ const st = safeStateText(String(claim.status || ''), 30);
338
+ lines.push(` - ${id} (${st}) → npm run workflow:sidecar -- claim ${id} ${workflowDir}`);
339
+ }
340
+ } else {
341
+ lines.push('Trust: no trust data available');
342
+ }
343
+ } catch { /* skip */ }
344
+
345
+ // Liveness advisory
346
+ try {
347
+ const resolved = resolveActor(process.env);
348
+ const selfActor = resolved.actor || 'local';
349
+ const events = flowAgentsArtifactRootsForRead(root)
350
+ .flatMap(artifactRoot => readLivenessEvents(path.join(artifactRoot, 'liveness', 'events.jsonl')));
351
+ if (events.length > 0) {
352
+ // selfActor (for exclusion matching) stays raw here; only the display strings pushed
353
+ // into `lines` are sanitized below, since holders come from the shared multi-writer
354
+ // liveness stream and must be treated as untrusted display input (#287 fix iteration 1).
355
+ const holders = freshHolders(events, slug, selfActor, Date.now());
356
+ for (const h of holders) {
357
+ lines.push(`[LIVENESS WARNING: another agent appears live on this work: actor ${safeStateText(h.actor)}, last seen ${h.lastAt}]`);
358
+ }
359
+ }
360
+ lines.push(`ACTOR: ${safeStateText(selfActor)} (${resolved.source})`);
361
+ } catch { /* skip */ }
362
+
363
+ // Pull-work route hint
364
+ lines.push('To continue: resume this work. Or run pull-work to assess WIP and start new/parallel work.');
365
+
366
+ return lines.join('\n');
367
+ } catch {
368
+ return '';
369
+ }
370
+ }
371
+
201
372
  function run(rawInput) {
202
373
  try {
203
374
  const input = JSON.parse(rawInput);
@@ -217,9 +388,31 @@ function run(rawInput) {
217
388
  shouldAppendWorkflowContext = hints.length > 0;
218
389
  }
219
390
 
220
- if (event === 'UserPromptSubmit' && current && stateNeedsAmbientSteering(current.payload)) {
221
- hints.push('WORKFLOW STATE ATTENTION: current sidecars show unresolved workflow state at turn start.');
222
- shouldAppendWorkflowContext = true;
391
+ if ((event === 'UserPromptSubmit' || event === 'SessionStart') && current) {
392
+ const stateHint = stateSteering(root);
393
+ if (stateHint) {
394
+ hints.push(stateNeedsAmbientSteering(current.payload)
395
+ ? 'WORKFLOW STATE ATTENTION: current sidecars show unresolved workflow state at turn start.'
396
+ : 'WORKFLOW STATE: an active task is in progress — re-ground the recorded goal and resume the next step before doing anything else.');
397
+ hints.push(stateHint);
398
+ const contextHint = contextMapSteering(root);
399
+ if (contextHint) hints.push(contextHint);
400
+ }
401
+ }
402
+
403
+ if (event === 'UserPromptSubmit') {
404
+ const builderHint = builderWorkflowSteering(input);
405
+ if (builderHint) {
406
+ hints.push(builderHint);
407
+ const contextHint = contextMapSteering(root);
408
+ if (contextHint) hints.push(contextHint);
409
+ }
410
+ }
411
+
412
+ // SessionStart only: append the RESUME block for richer situational awareness
413
+ if (event === 'SessionStart' && current) {
414
+ const resumeBlock = resumeSteering(root, current);
415
+ if (resumeBlock) hints.push(resumeBlock);
223
416
  }
224
417
 
225
418
  if (shouldAppendWorkflowContext) {
@@ -247,4 +440,17 @@ if (require.main === module) {
247
440
  });
248
441
  }
249
442
 
250
- module.exports = { run, stateSteering, critiqueSteering, contextMapSteering, latestWorkflowState, findRepoRoot, safeStateText, stateNeedsAmbientSteering };
443
+ module.exports = {
444
+ run,
445
+ stateSteering,
446
+ critiqueSteering,
447
+ contextMapSteering,
448
+ latestWorkflowState,
449
+ findRepoRoot,
450
+ safeStateText,
451
+ stateNeedsAmbientSteering,
452
+ resumeSteering,
453
+ promptText,
454
+ looksLikeBuilderWork,
455
+ builderWorkflowSteering,
456
+ };
@@ -7,6 +7,8 @@ title: "ADR 0007: Flow / Skill / Kit / Tool Boundary"
7
7
  **Date:** 2026-06-15
8
8
  **Status:** Accepted
9
9
 
10
+ > **Cross-Kit Skill Sharing resolved by [ADR 0019](./0019-kit-dependency-ownership.md) (2026-07-01).** The "Cross-Kit Skill Sharing: DEFERRED" question below is resolved: the npm-dependency-style model (Kit B declares a peer dependency on Kit A and consumes its skills by reference) is adopted at the Flow Agents extension layer via a `kit.json` `dependencies` field. The single-kit-ownership principle recorded here still stands; only the deferral is lifted.
11
+
10
12
  ---
11
13
 
12
14
  ## Context
@@ -97,6 +97,41 @@ The broader boundary reconciliation (issue #174) is phased:
97
97
 
98
98
  **Invariant that must survive migration (#183 Finding 2):** `WorkflowRun.status.conditions` are writable summaries; the gate re-derives from Hachure claims via Surface; `conditions[].evidenceRefs` cite claim IDs. **Do not fuse Resource and claim** — the separation (a friendly mutable surface over an un-gameable derived core) is the architecture.
99
99
 
100
+ ### Status update (2026-07-01) — Tier 2 Phase 3 landed and hardened
101
+
102
+ **Phase 3 ("hooks") of the Tier 2 migration has landed — this is not an unfinished step.**
103
+ PRs #204–#209 (P-a through P-d, ADR 0016 Abstraction A) implemented the FlowDefinition →
104
+ enforcement bridge: P-a, a shared `src/lib/flow-resolver.ts` resolver
105
+ (`resolveActiveFlowStep`) that turns `(active_flow_id, active_step_id)` into a gate's
106
+ `expects[]`; P-b, declared-claim producers (`record-gate-claim` stamps kit-namespaced
107
+ claims per the active FlowDefinition's gate); P-c, gate enforcement in
108
+ `scripts/hooks/stop-goal-fit.js` reading `activeFlowStep.gateExpects[]` to select claims by
109
+ declared `claimType`; and P-d, `phase_map`-driven `advance-state --flow-definition` plus
110
+ retirement of the `-legacy` dual-emit shadow, so a live FlowDefinition-driven session emits
111
+ only kit-namespaced claims.
112
+
113
+ **It was then red-teamed in PR #215, which found the literal end-state unsafe.** The literal
114
+ Abstraction A text — declared-only enforcement, no `workflow.*` fallback — composed into a
115
+ HIGH-severity (OWASP A01/A04) gate-bypass chain: a forged `current.json` pointing at an
116
+ agent-authored `.flow.json` with an empty `expects: []` made the pure if/else selection logic
117
+ return `false` for every claim, silently skipping all re-derivation, tamper-detection, and
118
+ high/critical enforcement. PR #215 closed this by replacing the if/else with a **permanent
119
+ union-enforcement floor** — `workflow.*` claims are *always* enforced alongside whatever a
120
+ declared FlowDefinition adds, never instead of it — plus an empty-`expects[]`
121
+ `gate misconfiguration:` `HARD_BLOCK`. **This union floor and HARD_BLOCK are an intentional,
122
+ tested departure from Abstraction A's literal "instead of" wording, not an unfinished step
123
+ toward it, and must not be "completed" by removing them** (see ADR 0016 and ADR 0018, which
124
+ independently forbids adding new local `config-protection.js` patterns for this class of
125
+ vector and routes new self-tamper/kill-switch findings to Layer 4 instead).
126
+
127
+ **The one gap this left named and open — kit FlowDefinition files
128
+ (`kits/*/flows/*.flow.json`) had no CODEOWNERS coverage, so a narrowed-but-nonempty
129
+ `expects[]` edit had no owner-review trip-wire — is closed as of 2026-07-01, per ADR 0018
130
+ Decision #2** (a self-tamper/kill-switch vector routes to Layer 4 — CODEOWNERS + a required
131
+ regression test — not a new `config-protection.js` matcher). This closes the last open item
132
+ from this Reassessment's Phase 3; Phases 4–6 (resume/evals, retire sidecars, Flow kernel)
133
+ remain scoped as described above.
134
+
100
135
  ## Consequences
101
136
 
102
137
  - **No bespoke trust-bundle schema validator in flow-agents.** Surface is the canonical
@@ -0,0 +1,62 @@
1
+ ---
2
+ title: "ADR 0019: Kit Dependency Ownership"
3
+ ---
4
+
5
+ # ADR 0019: Kit Dependency Ownership
6
+
7
+ **Date:** 2026-07-01
8
+ **Status:** Accepted
9
+
10
+ ---
11
+
12
+ ## Context
13
+
14
+ ADR 0007 ("Flow / Skill / Kit / Tool Boundary") left one question explicitly open under **Cross-Kit Skill Sharing: DEFERRED**: can a skill be shared across kits, and if so, how is that dependency declared and enforced? The motivating case is concrete and already live in this repository — the Builder Kit's `learning-review` skill invokes the Knowledge Kit's `knowledge-capture` skill (`kits/knowledge/skills/knowledge-capture/SKILL.md`) for durable knowledge storage. Today that cross-kit call is undeclared: nothing validates that Knowledge is present when Builder is installed or activated, so the dependency fails silently at agent-invocation time rather than loudly at install/activate time.
15
+
16
+ ADR 0007 named the leading candidate: *"an npm-dependency model: Kit B declares a peer dependency on Kit A and invokes Kit A's skills as a consumer, without absorbing them into Kit B's skill list."* It deferred adoption "until a concrete cross-kit case requires it." That case is now required, so this ADR resolves the deferral.
17
+
18
+ The open design question is **ownership**: does a cross-kit `dependencies` field belong in the Flow-owned container schema (`@kontourai/flow`'s `flow-kit-container.schema.json`), or in the Flow Agents extension layer?
19
+
20
+ ---
21
+
22
+ ## Decision
23
+
24
+ **Kit `dependencies` lives entirely in the Flow Agents extension layer. No `@kontourai/flow` schema or code change is required or planned.**
25
+
26
+ The `dependencies` field is a `kit.json` array of `{ kit_id, reason? }` entries validated and enforced exclusively by Flow Agents code:
27
+
28
+ - **Shape** (`src/flow-kit/validate.ts` `parseKitDependencies`, wired into `validateKitRepository`): each entry must be an object with a kebab-case `kit_id` (`^[a-z][a-z0-9-]*$`), no self-reference, and no duplicate `kit_id`. `reason` is an optional string. Shape errors are hard errors at `flow-agents kit install` / `inspect` / `validate` time.
29
+ - **Install-time presence** (`src/cli/kit.ts`): a non-blocking warning is printed when a declared dependency is absent from the destination's **local** registry. This check is deliberately scoped to the local registry only (not the built-in catalog) — an accepted v1 limitation, documented, not hidden.
30
+ - **Activation-time presence** (`src/runtime-adapters.ts`): a **hard error** (non-zero exit) when a declared `kit_id` is absent from the union of built-in catalog kits and locally-installed kits at activation time.
31
+
32
+ ### Grounding: why the extension layer, not Flow
33
+
34
+ Three independent lines of evidence put `dependencies` on the Flow Agents side of the boundary:
35
+
36
+ 1. **The container schema already permits it, agent-blind.** `@kontourai/flow`'s `flow-kit-container.schema.json` declares `"additionalProperties": true` and states that *"Consumer products (such as Flow Agents) may define additional asset-class fields as extensions; unknown top-level fields are consumer extensions and core validation ignores-but-permits them."* A `dependencies` array is schema-legal in Flow today with zero Flow-side change. Flow's `validateKitContainer` only enforces `schema_version` / `id` / `name` / `flows`; it never touches unknown top-level fields.
37
+
38
+ 2. **ADR 0008's Dividing Test lands it on Flow Agents.** ADR 0008 asks: *"Does the operation need to INTERPRET the agent extension (what a skill or adapter MEANS), or only the container ... Container-only → Flow. Extension-interpreting → Flow Agents."* A kit-to-kit dependency exists **only** because one kit's *skill* invokes another kit's *skill* — that is interpreting what a skill means, squarely on the Flow Agents side of the line. Flow's gate / FlowDefinition engine has no concept of "kit A needs kit B" and needs none to evaluate gates.
39
+
40
+ 3. **There is an existing precedent for exactly this pattern.** `src/flow-kit/validate.ts` already derives K0/K1/K2 conformance and consumer targets purely from Flow Agents-owned code reading the same `kit.json` that Flow validates agent-blind. `dependencies` follows the identical pattern: a new Flow Agents-recognized (not Flow-recognized) metadata field, added to a `KNOWN_METADATA_FIELDS` allowlist so it is never misreported as an unknown third-party extension namespace.
41
+
42
+ ### First declared dependency
43
+
44
+ `kits/builder/kit.json` declares `dependencies: [{ "kit_id": "knowledge", "reason": "learning-review invokes knowledge-capture ... for durable knowledge storage" }]`, making the previously-implicit Builder→Knowledge relationship explicit and enforceable.
45
+
46
+ ---
47
+
48
+ ## Consequences
49
+
50
+ - **Single-repo change.** Because no cross-repo change is needed, there is no cross-repo prerequisite: everything lands in one `flow-agents` change. The Flow container contract is untouched.
51
+ - **Install stays non-blocking; activation is the hard gate.** Keeping install-time checks advisory avoids breaking a consumer repo that has forked/customized `kits/catalog.json`; activation-time enforcement is where a genuinely-missing dependency fails loudly. This split is itself documented in `docs/kit-authoring-guide.md`.
52
+ - **`dependencies` is metadata, not a K-level input.** It is presence/metadata, not evaluable-gate content, so it does NOT participate in K0/K1/K2 conformance scoring.
53
+ - **Not a package manager.** `dependencies` declares and validates presence; it does not fetch, resolve version ranges, or order installs. Version-range resolution and transitive-dependency graphs remain out of scope (a possible future extension, still extension-layer-owned by this decision).
54
+
55
+ ---
56
+
57
+ ## References
58
+
59
+ - [ADR 0007: Flow / Skill / Kit / Tool Boundary](./0007-flow-skill-kit-tool-boundary.md) — resolves its "Cross-Kit Skill Sharing: DEFERRED" question by adopting the npm-dependency-style model at the Flow Agents extension layer.
60
+ - [ADR 0008: Kit Operation Boundary](./0008-kit-operation-boundary.md) — the "Dividing Test" that locates extension-interpreting operations in Flow Agents.
61
+ - `@kontourai/flow`'s `flow-kit-container.schema.json` (`additionalProperties: true`, "ignores-but-permits" consumer extensions) and `validateKitContainer`.
62
+ - `src/flow-kit/validate.ts` (`parseKitDependencies`, `KNOWN_METADATA_FIELDS`, `deriveKitTargets`), `src/cli/kit.ts` (install-time warning), `src/runtime-adapters.ts` (activation-time enforcement).