@kontourai/flow-agents 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/.github/CODEOWNERS +8 -0
  2. package/.github/workflows/ci.yml +32 -0
  3. package/AGENTS.md +4 -4
  4. package/CHANGELOG.md +46 -0
  5. package/CONTEXT.md +14 -0
  6. package/README.md +9 -3
  7. package/agents/dev.json +1 -1
  8. package/agents/tool-code-reviewer.json +1 -1
  9. package/agents/tool-planner.json +3 -3
  10. package/agents/tool-verifier.json +3 -3
  11. package/build/src/cli/console-learning-projection.js +3 -2
  12. package/build/src/cli/init.js +104 -21
  13. package/build/src/cli/kit.js +37 -6
  14. package/build/src/cli/pull-work-provider.js +1 -1
  15. package/build/src/cli/usage-feedback.js +3 -3
  16. package/build/src/cli/validate-hook-influence.js +1 -0
  17. package/build/src/cli/validate-workflow-artifacts.js +22 -6
  18. package/build/src/cli/workflow-artifact-cleanup-audit.js +36 -2
  19. package/build/src/cli/workflow-sidecar.d.ts +14 -7
  20. package/build/src/cli/workflow-sidecar.js +792 -69
  21. package/build/src/flow-kit/validate.d.ts +23 -0
  22. package/build/src/flow-kit/validate.js +67 -1
  23. package/build/src/index.d.ts +1 -1
  24. package/build/src/index.js +1 -1
  25. package/build/src/lib/flow-resolver.d.ts +6 -4
  26. package/build/src/lib/flow-resolver.js +125 -58
  27. package/build/src/lib/local-artifact-root.d.ts +14 -2
  28. package/build/src/lib/local-artifact-root.js +22 -5
  29. package/build/src/lib/workflow-learning-projection.js +2 -2
  30. package/build/src/runtime-adapters.d.ts +12 -0
  31. package/build/src/runtime-adapters.js +56 -16
  32. package/build/src/tools/build-universal-bundles.js +49 -16
  33. package/build/src/tools/generate-context-map.js +10 -8
  34. package/build/src/tools/validate-source-tree.js +23 -1
  35. package/console.telemetry.json +22 -0
  36. package/context/contracts/artifact-contract.md +38 -7
  37. package/context/contracts/builder-kit-workflow-state-contract.md +3 -2
  38. package/context/contracts/decision-registry-contract.md +263 -0
  39. package/context/contracts/delivery-contract.md +1 -1
  40. package/context/contracts/planning-contract.md +2 -1
  41. package/context/contracts/review-contract.md +5 -3
  42. package/context/contracts/verification-contract.md +9 -1
  43. package/context/contracts/work-item-contract.md +1 -1
  44. package/context/deferred/parallelization.md +1 -1
  45. package/context/gate-awareness.md +4 -4
  46. package/context/scripts/git-status.sh +2 -2
  47. package/context/scripts/hooks/config-protection.js +502 -2
  48. package/context/scripts/hooks/lib/config-protection-remedies.js +69 -0
  49. package/context/scripts/hooks/run-hook.js +14 -1
  50. package/context/scripts/hooks/stop-goal-fit.js +1140 -99
  51. package/context/scripts/hooks/workflow-steering.js +212 -6
  52. package/docs/adr/0007-flow-skill-kit-tool-boundary.md +2 -0
  53. package/docs/adr/0015-flow-flow-agents-boundary-reconciliation.md +35 -0
  54. package/docs/adr/0019-kit-dependency-ownership.md +62 -0
  55. package/docs/adr/0020-trust-reconcile-manifest-and-claim-classification.md +290 -0
  56. package/docs/adr/0021-assignment-leases-and-stale-claim-takeover.md +221 -0
  57. package/docs/adr/0022-fail-closed-delivery-reconciliation-with-governed-exemptions.md +283 -0
  58. package/docs/agent-system-guidebook.md +3 -3
  59. package/docs/agent-usage-feedback-loop.md +6 -6
  60. package/docs/context-map.md +9 -7
  61. package/docs/decisions/decision-records.md +57 -0
  62. package/docs/decisions/index.md +14 -0
  63. package/docs/decisions/promotion-gate.md +52 -0
  64. package/docs/developer-architecture.md +1 -1
  65. package/docs/fixture-ownership.md +3 -0
  66. package/docs/flow-kit-repository-contract.md +5 -5
  67. package/docs/getting-started.md +12 -2
  68. package/docs/integrations/harness-install.md +2 -1
  69. package/docs/kit-authoring-guide.md +64 -5
  70. package/docs/knowledge-kit.md +5 -1
  71. package/docs/learnings/2026-07-improvement-program.md +203 -0
  72. package/docs/migrations.md +6 -5
  73. package/docs/north-star.md +2 -2
  74. package/docs/operating-layers.md +4 -3
  75. package/docs/repository-structure.md +5 -5
  76. package/docs/skills-map.md +8 -8
  77. package/docs/spec/runtime-hook-surface.md +19 -15
  78. package/docs/spikes/graph-provider-2026-07.md +155 -0
  79. package/docs/standards-register.md +7 -7
  80. package/docs/trust-anchor-adoption.md +105 -4
  81. package/docs/work-item-adapters.md +1 -1
  82. package/docs/workflow-artifact-lifecycle.md +72 -9
  83. package/docs/workflow-eval-strategy.md +3 -3
  84. package/docs/workflow-shared-contracts.md +1 -1
  85. package/docs/workflow-usage-guide.md +254 -21
  86. package/evals/acceptance/DEMO-false-completion.md +20 -11
  87. package/evals/acceptance/prove-capture-teeth-declared.sh +15 -15
  88. package/evals/acceptance/prove-capture-teeth.sh +11 -11
  89. package/evals/acceptance/test_kiro_harness.sh +1 -1
  90. package/evals/ci/antigaming-suite.sh +5 -0
  91. package/evals/ci/run-baseline.sh +65 -1
  92. package/evals/fixtures/flow-kit-repository/invalid-bad-dependency/flows/review.flow.json +26 -0
  93. package/evals/fixtures/flow-kit-repository/invalid-bad-dependency/kit.json +13 -0
  94. package/evals/fixtures/flow-kit-repository/valid-with-dependency/flows/review.flow.json +26 -0
  95. package/evals/fixtures/flow-kit-repository/valid-with-dependency/kit.json +13 -0
  96. package/evals/fixtures/hook-influence/cases.json +32 -0
  97. package/evals/fixtures/pull-work-provider/github-issues.json +6 -6
  98. package/evals/fixtures/trust-reconcile-exploits/fabricated-attestation.json +59 -0
  99. package/evals/fixtures/trust-reconcile-exploits/no-label-bypass.json +36 -0
  100. package/evals/fixtures/trust-reconcile-exploits/skip-assumed-bypass.json +59 -0
  101. package/evals/fixtures/trust-reconcile-exploits/status-misassertion.json +48 -0
  102. package/evals/fixtures/trust-reconcile-exploits/waived-command-check.json +44 -0
  103. package/evals/fixtures/trust-reconcile-mixed-bundle/mixed-bundle.json +159 -0
  104. package/evals/fixtures/trust-reconcile-ws3/ws3-bundle.json +920 -0
  105. package/evals/integration/test_actor_identity.sh +254 -0
  106. package/evals/integration/test_builder_step_producers.sh +33 -23
  107. package/evals/integration/test_bundle_install.sh +22 -16
  108. package/evals/integration/test_bundle_lifecycle.sh +11 -11
  109. package/evals/integration/test_captured_fail_reconciliation.sh +50 -50
  110. package/evals/integration/test_checkpoint_signing.sh +43 -0
  111. package/evals/integration/test_claim_lookup.sh +4 -4
  112. package/evals/integration/test_codex_hook_resolution.sh +114 -0
  113. package/evals/integration/test_command_log_concurrency.sh +8 -8
  114. package/evals/integration/test_command_log_fork_classification.sh +3 -3
  115. package/evals/integration/test_command_log_integrity.sh +16 -16
  116. package/evals/integration/test_console_learning_projection.sh +1 -1
  117. package/evals/integration/test_dual_emit_flow_step.sh +145 -0
  118. package/evals/integration/test_enforcer_expects_driven.sh +12 -12
  119. package/evals/integration/test_evidence_capture_hook.sh +14 -13
  120. package/evals/integration/test_fixture_retirement_audit.sh +2 -2
  121. package/evals/integration/test_flow_agents_statusline.sh +2 -2
  122. package/evals/integration/test_flow_kit_install_git.sh +52 -0
  123. package/evals/integration/test_flow_kit_repository.sh +2 -0
  124. package/evals/integration/test_flowdef_session_activation.sh +6 -6
  125. package/evals/integration/test_flowdef_union_floor_regression.sh +429 -0
  126. package/evals/integration/test_gate_bypass_chain.sh +52 -40
  127. package/evals/integration/test_gate_lockdown.sh +97 -73
  128. package/evals/integration/test_gate_review_inquiry_records.sh +3 -3
  129. package/evals/integration/test_goal_fit_escape_hatch.sh +10 -10
  130. package/evals/integration/test_goal_fit_ghost_session.sh +104 -0
  131. package/evals/integration/test_goal_fit_hook.sh +32 -32
  132. package/evals/integration/test_goal_fit_rederive.sh +9 -9
  133. package/evals/integration/test_hook_category_behaviors.sh +103 -0
  134. package/evals/integration/test_hook_influence_cases.sh +1 -0
  135. package/evals/integration/test_install_merge.sh +262 -1
  136. package/evals/integration/test_kit_identity_trust.sh +6 -6
  137. package/evals/integration/test_liveness_conflict_injection.sh +587 -0
  138. package/evals/integration/test_liveness_heartbeat.sh +677 -0
  139. package/evals/integration/test_liveness_verdict.sh +394 -0
  140. package/evals/integration/test_local_flow_kit_install.sh +15 -0
  141. package/evals/integration/test_phase_map_and_gate_claim.sh +63 -11
  142. package/evals/integration/test_promote_gate.sh +178 -0
  143. package/evals/integration/test_publish_delivery.sh +2 -2
  144. package/evals/integration/test_pull_work_liveness_preflight.sh +386 -0
  145. package/evals/integration/test_pull_work_provider.sh +2 -2
  146. package/evals/integration/test_reconcile_soundness.sh +4 -4
  147. package/evals/integration/test_resolvefirststep_security.sh +22 -0
  148. package/evals/integration/test_runtime_adapter_activation.sh +87 -18
  149. package/evals/integration/test_session_resume_roundtrip.sh +147 -0
  150. package/evals/integration/test_sidecar_field_preservation.sh +249 -0
  151. package/evals/integration/test_trust_reconcile.sh +2 -2
  152. package/evals/integration/test_trust_reconcile_manifest.sh +79 -0
  153. package/evals/integration/test_trust_reconcile_mixed_bundle.sh +96 -0
  154. package/evals/integration/test_trust_reconcile_negatives.sh +151 -0
  155. package/evals/integration/test_usage_feedback_global.sh +5 -5
  156. package/evals/integration/test_validate_artifacts_portability.sh +60 -0
  157. package/evals/integration/test_verify_cli.sh +2 -2
  158. package/evals/integration/test_veritas_governance_kit.sh +117 -0
  159. package/evals/integration/test_workflow_artifact_cleanup_audit.sh +26 -1
  160. package/evals/integration/test_workflow_artifacts.sh +33 -0
  161. package/evals/integration/test_workflow_sidecar_writer.sh +723 -74
  162. package/evals/integration/test_workflow_steering_hook.sh +175 -5
  163. package/evals/lib/codex-provider.sh +1 -1
  164. package/evals/lib/node.sh +19 -1
  165. package/evals/run.sh +22 -0
  166. package/evals/static/test_decisions.sh +150 -0
  167. package/evals/static/test_flowdef_codeowners_coverage.sh +56 -0
  168. package/evals/static/test_package.sh +19 -10
  169. package/evals/static/test_universal_bundles.sh +48 -3
  170. package/evals/static/test_workflow_skills.sh +41 -8
  171. package/kits/builder/flows/build.flow.json +3 -74
  172. package/kits/builder/flows/publish-learn.flow.json +90 -0
  173. package/kits/builder/kit.json +11 -0
  174. package/kits/builder/skills/builder-shape/SKILL.md +1 -1
  175. package/kits/builder/skills/deliver/SKILL.md +13 -11
  176. package/kits/builder/skills/design-probe/SKILL.md +4 -4
  177. package/kits/builder/skills/evidence-gate/SKILL.md +26 -1
  178. package/kits/builder/skills/execute-plan/SKILL.md +2 -2
  179. package/kits/builder/skills/fix-bug/SKILL.md +2 -0
  180. package/kits/builder/skills/gate-review/SKILL.md +6 -6
  181. package/kits/builder/skills/idea-to-backlog/SKILL.md +1 -1
  182. package/kits/builder/skills/learning-review/SKILL.md +7 -7
  183. package/kits/builder/skills/pickup-probe/SKILL.md +5 -4
  184. package/kits/builder/skills/plan-work/SKILL.md +27 -5
  185. package/kits/builder/skills/pull-work/SKILL.md +80 -5
  186. package/kits/builder/skills/release-readiness/SKILL.md +4 -4
  187. package/kits/builder/skills/review-work/SKILL.md +24 -1
  188. package/kits/builder/skills/tdd-workflow/SKILL.md +2 -0
  189. package/kits/builder/skills/verify-work/SKILL.md +8 -1
  190. package/kits/catalog.json +6 -0
  191. package/kits/veritas-governance/adapter/readiness-to-trust-bundle.mjs +178 -0
  192. package/kits/veritas-governance/docs/README.md +75 -0
  193. package/kits/veritas-governance/fixtures/readiness/not-ready.readiness-report.json +4645 -0
  194. package/kits/veritas-governance/fixtures/readiness/ready.readiness-report.json +4403 -0
  195. package/kits/veritas-governance/flows/readiness-check.flow.json +35 -0
  196. package/kits/veritas-governance/kit.json +16 -0
  197. package/package.json +8 -4
  198. package/packaging/README.md +2 -2
  199. package/packaging/conformance/fixtures/config-protection--allow-read-state-json.json +20 -0
  200. package/packaging/conformance/fixtures/config-protection--block-write-state-json.json +20 -0
  201. package/packaging/conformance/fixtures/stop-goal-fit--block-bundle-disputed-claim.json +1 -1
  202. package/packaging/conformance/fixtures/stop-goal-fit--block-capture-contradicts-claimed-pass.json +1 -1
  203. package/packaging/manifest.json +9 -10
  204. package/schemas/decision-record.schema.json +109 -0
  205. package/schemas/workflow-evidence.schema.json +1 -1
  206. package/schemas/workflow-state.schema.json +5 -0
  207. package/scripts/README.md +8 -3
  208. package/scripts/check-content-boundary.cjs +1 -1
  209. package/scripts/check-decisions.cjs +356 -0
  210. package/scripts/ci/derive-claim-status.mjs +74 -0
  211. package/scripts/ci/trust-reconcile.js +436 -79
  212. package/scripts/git-status.sh +2 -2
  213. package/scripts/hooks/claude-telemetry-hook.js +37 -3
  214. package/scripts/hooks/codex-telemetry-hook.js +36 -2
  215. package/scripts/hooks/config-protection.js +102 -30
  216. package/scripts/hooks/evidence-capture.js +3 -3
  217. package/scripts/hooks/lib/actor-identity.js +368 -0
  218. package/scripts/hooks/lib/config-protection-remedies.js +69 -0
  219. package/scripts/hooks/lib/liveness-heartbeat.js +361 -0
  220. package/scripts/hooks/lib/liveness-policy.js +127 -0
  221. package/scripts/hooks/lib/liveness-read.js +105 -12
  222. package/scripts/hooks/lib/liveness-write.js +46 -0
  223. package/scripts/hooks/lib/local-artifact-paths.js +12 -6
  224. package/scripts/hooks/opencode-telemetry-hook.js +27 -1
  225. package/scripts/hooks/pi-telemetry-hook.js +27 -1
  226. package/scripts/hooks/stop-goal-fit.js +157 -26
  227. package/scripts/hooks/workflow-steering.js +77 -3
  228. package/scripts/install-codex-home.sh +138 -38
  229. package/scripts/statusline/flow-agents-statusline.js +2 -2
  230. package/src/cli/console-learning-projection.ts +3 -2
  231. package/src/cli/init.ts +101 -21
  232. package/src/cli/kit.ts +37 -6
  233. package/src/cli/public-api.test.mjs +58 -6
  234. package/src/cli/pull-work-provider.ts +1 -1
  235. package/src/cli/trust-bundle-policy-order.test.mjs +87 -0
  236. package/src/cli/usage-feedback.ts +3 -3
  237. package/src/cli/validate-hook-influence.ts +1 -0
  238. package/src/cli/validate-workflow-artifacts.ts +22 -6
  239. package/src/cli/veritas-readiness-adapter.test.mjs +267 -0
  240. package/src/cli/workflow-artifact-cleanup-audit.ts +35 -2
  241. package/src/cli/workflow-sidecar.ts +795 -71
  242. package/src/flow-kit/validate.ts +74 -1
  243. package/src/index.ts +6 -2
  244. package/src/lib/flow-resolver.ts +123 -55
  245. package/src/lib/local-artifact-root.ts +24 -5
  246. package/src/lib/workflow-learning-projection.ts +2 -2
  247. package/src/runtime-adapters.ts +61 -15
  248. package/src/tools/build-universal-bundles.ts +49 -16
  249. package/src/tools/generate-context-map.ts +10 -8
  250. package/src/tools/validate-source-tree.ts +23 -1
@@ -6,12 +6,17 @@ import * as path from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { parseArgs, flagBool, flagString } from "../lib/args.js";
8
8
  import { assertPathContained, copyDir, isoNow, readJson, walkFiles, writeJson } from "../lib/fs.js";
9
- import { assertKitRepository, deriveKitTargets } from "../flow-kit/validate.js";
9
+ import { assertKitRepository, deriveKitTargets, parseKitDependencies } from "../flow-kit/validate.js";
10
10
  import { activateCodexLocal, activateStrandsLocal } from "../runtime-adapters.js";
11
+ import { defaultCodexHome } from "../lib/local-artifact-root.js";
11
12
  const REGISTRY_REL = path.join("kits", "local", "installed-kits.json");
12
13
  const REPOSITORIES_REL = path.join("kits", "local", "repositories");
13
14
  function registryPath(dest) { return path.join(dest, REGISTRY_REL); }
14
15
  function installedPath(dest, kitId) { return path.join(dest, REPOSITORIES_REL, kitId); }
16
+ function resolveDest(flags) {
17
+ const explicit = flagString(flags, "dest");
18
+ return path.resolve(explicit ?? defaultCodexHome());
19
+ }
15
20
  function loadRegistry(dest) {
16
21
  const file = registryPath(dest);
17
22
  if (!fs.existsSync(file))
@@ -19,6 +24,28 @@ function loadRegistry(dest) {
19
24
  const data = readJson(file);
20
25
  return { schema_version: data.schema_version ?? "1.0", kits: Array.isArray(data.kits) ? data.kits.filter((item) => typeof item === "object" && item !== null) : [] };
21
26
  }
27
+ /**
28
+ * Emit a non-blocking warning for each declared kit dependency that is not present
29
+ * in the destination's LOCAL registry.
30
+ *
31
+ * Scope limitation (v1, accepted): this check only sees the local installed-kits
32
+ * registry at `dest`, NOT the built-in Kit Catalog. A dependency satisfied solely by
33
+ * a built-in catalog kit will still warn here. Presence is enforced authoritatively
34
+ * (hard error) at activation time against the full discovered inventory
35
+ * (built-in catalog + local registry) — see src/runtime-adapters.ts.
36
+ */
37
+ function warnUninstalledDependencies(manifest, manifestPath, dest) {
38
+ const { entries } = parseKitDependencies(manifest, manifestPath);
39
+ if (!entries.length)
40
+ return;
41
+ const installed = new Set(loadRegistry(dest).kits.map((entry) => String(entry.id ?? "")));
42
+ const kitId = String(manifest.id);
43
+ for (const dep of entries) {
44
+ if (!installed.has(dep.kit_id)) {
45
+ console.log(`warning: kit '${kitId}' declares a dependency on '${dep.kit_id}'${dep.reason ? ` (${dep.reason})` : ""} which is not installed at ${dest}; install it first with 'flow-agents kit install <source>' or activation will fail`);
46
+ }
47
+ }
48
+ }
22
49
  function contentHash(root) {
23
50
  const hash = crypto.createHash("sha256");
24
51
  for (const file of walkFiles(root)) {
@@ -73,7 +100,7 @@ async function install(argv) {
73
100
  }
74
101
  async function installLocalSource(source, argv) {
75
102
  const args = parseArgs(argv);
76
- const dest = path.resolve(flagString(args.flags, "dest", ".") ?? ".");
103
+ const dest = resolveDest(args.flags);
77
104
  let manifest;
78
105
  try {
79
106
  manifest = await assertKitRepository(source);
@@ -85,10 +112,12 @@ async function installLocalSource(source, argv) {
85
112
  return 1;
86
113
  }
87
114
  const kitId = String(manifest.id);
115
+ warnUninstalledDependencies(manifest, path.join(source, "kit.json"), dest);
88
116
  const hash = contentHash(source);
89
117
  const registry = loadRegistry(dest);
90
118
  const existing = registry.kits.find((entry) => entry.id === kitId);
91
119
  const target = installedPath(dest, kitId);
120
+ fs.mkdirSync(dest, { recursive: true });
92
121
  assertPathContained(dest, target);
93
122
  const sourceText = source;
94
123
  if (existing && existing.source !== sourceText && !flagBool(args.flags, "update")) {
@@ -127,7 +156,7 @@ async function installGitSource(rawUrl, argv) {
127
156
  }
128
157
  if (!ref)
129
158
  ref = flagString(args.flags, "ref") ?? null;
130
- const dest = path.resolve(flagString(args.flags, "dest", ".") ?? ".");
159
+ const dest = resolveDest(args.flags);
131
160
  const force = flagBool(args.flags, "force") ?? false;
132
161
  const update = flagBool(args.flags, "update") ?? false;
133
162
  // Shallow-clone into a temporary directory.
@@ -161,10 +190,12 @@ async function installGitSource(rawUrl, argv) {
161
190
  }
162
191
  // Delegate to the shared install logic (copy + registry update).
163
192
  const kitId = String(manifest.id);
193
+ warnUninstalledDependencies(manifest, path.join(tmpBase, "kit.json"), dest);
164
194
  const hash = kitContentHash(tmpBase);
165
195
  const registry = loadRegistry(dest);
166
196
  const existing = registry.kits.find((entry) => entry.id === kitId);
167
197
  const target = installedPath(dest, kitId);
198
+ fs.mkdirSync(dest, { recursive: true });
168
199
  assertPathContained(dest, target);
169
200
  const sourceText = repoUrl + (ref ? `#${ref}` : "");
170
201
  if (existing && existing.source !== sourceText && !update) {
@@ -197,7 +228,7 @@ async function installGitSource(rawUrl, argv) {
197
228
  }
198
229
  function list(argv) {
199
230
  const args = parseArgs(argv);
200
- const dest = path.resolve(flagString(args.flags, "dest", ".") ?? ".");
231
+ const dest = resolveDest(args.flags);
201
232
  const entries = loadRegistry(dest).kits;
202
233
  if (!entries.length) {
203
234
  console.log("No local Flow Kits installed.");
@@ -210,7 +241,7 @@ function list(argv) {
210
241
  }
211
242
  function status(argv) {
212
243
  const args = parseArgs(argv);
213
- const dest = path.resolve(flagString(args.flags, "dest", ".") ?? ".");
244
+ const dest = resolveDest(args.flags);
214
245
  let entries = loadRegistry(dest).kits;
215
246
  const kitId = args.positionals[0];
216
247
  if (kitId) {
@@ -233,7 +264,7 @@ function status(argv) {
233
264
  const AVAILABLE_ADAPTERS = ["codex-local", "strands-local"];
234
265
  function activate(argv) {
235
266
  const args = parseArgs(argv);
236
- const dest = path.resolve(flagString(args.flags, "dest", ".") ?? ".");
267
+ const dest = resolveDest(args.flags);
237
268
  const sourceRoot = path.resolve(flagString(args.flags, "source-root", path.resolve(path.dirname(process.argv[1]), "..")) ?? ".");
238
269
  const adapter = flagString(args.flags, "adapter");
239
270
  if (adapter && !AVAILABLE_ADAPTERS.includes(adapter)) {
@@ -1,7 +1,7 @@
1
1
  import * as fs from "node:fs";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import { parseArgs, flagList, flagString } from "../lib/args.js";
4
- const FLOW_ARTIFACT_PATTERN = /(?<path>\.flow-agents\/[^\s`'")]+)/g;
4
+ const FLOW_ARTIFACT_PATTERN = /(?<path>\.kontourai\/flow-agents\/[^\s`'")]+)/g;
5
5
  const GITHUB_ISSUE_PATTERN = /(?:(?<owner>[A-Za-z0-9_.-]+)\/(?<repo>[A-Za-z0-9_.-]+))?#(?<number>\d+)/g;
6
6
  const GITHUB_URL_PATTERN = /https:\/\/github\.com\/(?<owner>[^/\s]+)\/(?<repo>[^/\s]+)\/(?:issues|pull)\/(?<number>\d+)/g;
7
7
  const BLOCKER_PATTERN = /\b(?:blocked by|depends on|waiting on|requires|should follow|coordinate with)\b[:\s-]*(?<refs>[^\n]+)/gi;
@@ -3,7 +3,7 @@ import { fileURLToPath } from "node:url";
3
3
  import * as os from "node:os";
4
4
  import * as path from "node:path";
5
5
  import { parseArgs, flagBool, flagList, flagString } from "../lib/args.js";
6
- import { defaultArtifactRootForRead, defaultTelemetryDirForRead, defaultTelemetryDirsForRead, telemetryDataDir, flowAgentsArtifactRoot, legacyFlowAgentsArtifactRoot } from "../lib/local-artifact-root.js";
6
+ import { defaultArtifactRootForRead, defaultTelemetryDirForRead, defaultTelemetryDirsForRead, telemetryDataDir, flowAgentsArtifactRoot } from "../lib/local-artifact-root.js";
7
7
  const VALID_RESULTS = new Set(["success", "partial", "failure", "not_verified"]);
8
8
  function telemetryDir(flags) {
9
9
  const explicit = flagString(flags, "telemetry-dir") ?? process.env.TELEMETRY_DATA_DIR;
@@ -152,7 +152,7 @@ function syncArtifacts(argv) {
152
152
  const dir = telemetryDir(flags);
153
153
  ensureSafeDir(dir);
154
154
  const artifacts = flagList(flags, "artifact-dir");
155
- const records = (artifacts.length ? artifacts : [flowAgentsArtifactRoot(), legacyFlowAgentsArtifactRoot()]).flatMap((item) => fs.existsSync(item) ? artifactOutcomes(item, flags) : []);
155
+ const records = (artifacts.length ? artifacts : [flowAgentsArtifactRoot()]).flatMap((item) => fs.existsSync(item) ? artifactOutcomes(item, flags) : []);
156
156
  writeJsonlUpsert(path.join(dir, "outcomes.jsonl"), records, "outcome_id");
157
157
  if (!flagBool(flags, "quiet"))
158
158
  console.log(`synced ${records.length} artifact outcome(s) to ${path.join(dir, "outcomes.jsonl")}`);
@@ -382,7 +382,7 @@ function discoverProjects(root) {
382
382
  if (!fs.existsSync(root))
383
383
  return [];
384
384
  const candidates = [root, ...fs.readdirSync(root).map((name) => path.join(root, name))];
385
- return candidates.filter((candidate) => fs.existsSync(flowAgentsArtifactRoot(candidate)) || fs.existsSync(legacyFlowAgentsArtifactRoot(candidate))).map((repoRoot) => {
385
+ return candidates.filter((candidate) => fs.existsSync(flowAgentsArtifactRoot(candidate))).map((repoRoot) => {
386
386
  const name = path.basename(repoRoot);
387
387
  return { name, repo: name, repo_root: repoRoot, artifact_dir: defaultArtifactRootForRead(repoRoot), input_telemetry_dir: defaultTelemetryDirForRead(repoRoot), runtime: "codex", skill_ids: [] };
388
388
  });
@@ -7,6 +7,7 @@ const validEvents = new Set(["UserPromptSubmit", "PostToolUse", "Stop"]);
7
7
  const requiredCases = {
8
8
  "dev-builder-build-requires-pickup-probe-before-plan": { description: "missing pickup Probe before plan", tier: "design-target", must_include: ["design-probe", "accepted gaps", "provider_state", "conflict_risks"], agent_must_do: ["route decision_gap back to design-probe/pickup Probe"] },
9
9
  "dev-builder-review-before-verify-after-execute": { description: "review-before-verify after execute", tier: "adapter", event: "PostToolUse", must_include: ["Next: review", "then verify", "report only"], agent_must_do: ["review-work for report-only critique before verify-work", "not count critique.json as verification evidence"] },
10
+ "dev-builder-route-fresh-coding-prompt": { description: "fresh coding prompt routes into Builder workflow", tier: "adapter", event: "UserPromptSubmit", must_include: ["BUILDER WORKFLOW ROUTE", "activate `deliver`", "--flow-id builder.build", "plan-work -> execute-plan -> review-work -> verify-work", "publish/release-readiness and learning-review"], agent_must_do: ["activate Builder Kit delivery workflow before source edits", "use deliver by default for coding/build work", "do not bypass plan-work -> execute-plan -> review-work -> verify-work", "publish, release readiness, and learning feedback"] },
10
11
  "dev-verify-fail-preserves-trace-before-rework": { description: "verify failure route-back with preserved FAIL evidence", tier: "design-target", must_include: ["If verdict=FAIL: record the FAIL artifact"], agent_must_do: ["write the FAIL artifact before routing back", "route to plan"] },
11
12
  "codex-claude-strict-stop-adapter-contract": { description: "Goal Fit stop adapter behavior", tier: "adapter", hook: "stop-goal-fit", event: "Stop", must_include: ["Goal Fit warning"], agent_must_do: ["treat strict Stop guidance as a blocker"] },
12
13
  "codex-live-context-gap": { description: "Codex documented runtime gap", tier: "documented-runtime-gap", must_include: ["STATE:", "not_verified"], agent_must_do: ["record NOT_VERIFIED for live influence"] },
@@ -1,7 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- const root = path.resolve(".");
4
+ import { fileURLToPath } from "node:url";
5
+ // Resolve bundled JSON Schemas relative to this compiled script's own package
6
+ // location (build/src/cli/validate-workflow-artifacts.js -> ../../../schemas), NOT
7
+ // process.cwd(), so `flow-agents-validate-artifacts` works from any repo (the artifact
8
+ // paths to validate are still taken from argv/cwd). Mirrors the package-relative pattern
9
+ // used for liveness-read.js in workflow-sidecar.ts.
10
+ const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
5
11
  const statusRe = /\[(PASS|FAIL|NOT_VERIFIED|SKIP|PARTIAL)\]/i;
6
12
  const dateTimeRe = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
7
13
  const sidecarSchemas = {
@@ -311,11 +317,12 @@ function readJson(file) {
311
317
  }
312
318
  function validateSidecar(file) {
313
319
  const { value, issues } = readJson(file);
320
+ const warnings = [];
314
321
  if (value === undefined)
315
- return issues;
322
+ return { issues, warnings };
316
323
  const schemaFile = sidecarSchemas[path.basename(file)];
317
324
  if (schemaFile) {
318
- const schema = JSON.parse(readText(path.join(root, schemaFile)));
325
+ const schema = JSON.parse(readText(path.join(packageRoot, schemaFile)));
319
326
  validateSchemaValue(file, value, schema, path.basename(file), issues, schema);
320
327
  }
321
328
  if (path.basename(file) === "evidence.json") {
@@ -359,7 +366,10 @@ function validateSidecar(file) {
359
366
  issues.push({ path: file, message: "deploy decision requires post_deploy_checks to be planned or pass" });
360
367
  }
361
368
  }
362
- return issues;
369
+ if (path.basename(file) === "state.json" && value && typeof value === "object" && !Array.isArray(value) && !("branch" in value)) {
370
+ warnings.push({ path: file, message: "state.json has no branch field (legacy/pre-#289 session) — re-run `ensure-session` to backfill agent/<actor>/<slug>." });
371
+ }
372
+ return { issues, warnings };
363
373
  }
364
374
  function validateLearningCorrections(file, status, records, issues) {
365
375
  records.forEach((record, index) => {
@@ -486,12 +496,18 @@ function main() {
486
496
  const markdown = artifactPaths(pathsIn);
487
497
  const sidecars = sidecarPaths(pathsIn);
488
498
  const issues = [];
499
+ const warnings = [];
489
500
  if (!skipMarkdown)
490
501
  for (const file of markdown)
491
502
  issues.push(...validateArtifact(file));
492
- for (const file of sidecars)
493
- issues.push(...validateSidecar(file));
503
+ for (const file of sidecars) {
504
+ const result = validateSidecar(file);
505
+ issues.push(...result.issues);
506
+ warnings.push(...result.warnings);
507
+ }
494
508
  issues.push(...validateSidecarGroup(pathsIn, markdown, requireSidecars, requireCritique));
509
+ for (const w of warnings)
510
+ console.error(`WARN ${w.path}: ${w.message}`);
495
511
  if (issues.length) {
496
512
  for (const issue of issues)
497
513
  console.error(`${issue.path}: ${issue.message}`);
@@ -16,7 +16,7 @@ const ACTIVE_STATUSES = new Set([
16
16
  const KNOWN_LEARNING_STATUSES = new Set(["pending", "learned", "followup_required", "blocked"]);
17
17
  const KNOWN_LEARNING_ROUTE_TARGETS = new Set(["rule", "skill", "power", "agent", "eval", "doc", "backlog", "knowledge", "none"]);
18
18
  const KNOWN_LEARNING_ROUTE_STATUSES = new Set(["completed", "open", "deferred", "accepted", "rejected"]);
19
- const SKIPPED_ROOT_ENTRIES = new Set(["archive", "changes", "delivery-history"]);
19
+ const SKIPPED_ROOT_ENTRIES = new Set(["archive", "changes", "delivery-history", "liveness"]);
20
20
  const MAX_SIDECAR_BYTES = 1024 * 1024;
21
21
  function printHelp() {
22
22
  console.log("Usage: flow-agents workflow-artifact-cleanup-audit [--artifact-root <path>] [--json]");
@@ -24,7 +24,7 @@ function printHelp() {
24
24
  console.log("Read-only dry-run audit for local workflow artifact directories.");
25
25
  console.log("");
26
26
  console.log("Options:");
27
- console.log(" --artifact-root <path> Local artifact root to scan (default: .kontourai/flow-agents, with .flow-agents read fallback)");
27
+ console.log(" --artifact-root <path> Local artifact root to scan (default: .kontourai/flow-agents)");
28
28
  console.log(" --json Print stable JSON buckets instead of text");
29
29
  console.log(" --help Show this help");
30
30
  console.log("");
@@ -148,6 +148,34 @@ function invalidItem(slug, workflowPath, reason) {
148
148
  reasons: [reason],
149
149
  };
150
150
  }
151
+ // Promote-then-archive gate (issue #312). A delivered session's durable residue
152
+ // (decisions, vocabulary, learnings, doc updates) must be PROMOTED before the session
153
+ // is archived. Promotion is recorded as a session-local promotion claim in the session
154
+ // trust.bundle (workflow-sidecar promote), detectable here via claim.metadata.promotion
155
+ // — no new manifest entry required. A delivered/accepted session that reached a terminal
156
+ // shape WITHOUT that claim is a cleanup_candidate (blocked from archive) with a remedy
157
+ // naming the promote step. Already-`archived` sessions are past the gate and are never
158
+ // re-flagged (issue #312 non-goal: do not backfill already-archived sessions).
159
+ const PROMOTE_REMEDY = "run `flow-agents workflow-sidecar promote <dir> --evidence-path <durable-doc> ...`" +
160
+ " (or `promote --none --reason \"<why nothing durable>\"`) to record the promotion claim before archiving";
161
+ function hasPromotionClaim(workflowDir) {
162
+ const bundlePath = path.join(workflowDir, "trust.bundle");
163
+ if (!fs.existsSync(bundlePath))
164
+ return false;
165
+ const parsed = readJson(bundlePath, "trust.bundle");
166
+ if (!parsed.ok)
167
+ return false;
168
+ const bundle = objectValue(parsed.value);
169
+ if (!bundle || !Array.isArray(bundle.claims))
170
+ return false;
171
+ return bundle.claims.some((entry) => {
172
+ const claim = objectValue(entry);
173
+ if (!claim)
174
+ return false;
175
+ const meta = objectValue(claim.metadata);
176
+ return meta !== null && objectValue(meta.promotion) !== null;
177
+ });
178
+ }
151
179
  function classifyWorkflow(slug, workflowPath) {
152
180
  const statePath = path.join(workflowPath, "state.json");
153
181
  if (!fs.existsSync(statePath))
@@ -185,9 +213,15 @@ function classifyWorkflow(slug, workflowPath) {
185
213
  return { ...base, classification: "cleanup_candidate", reasons: ["verified workflow has next_action.status done"] };
186
214
  }
187
215
  if (["delivered", "accepted", "archived"].includes(status) && phase === "done") {
216
+ if (status !== "archived" && !hasPromotionClaim(workflowPath)) {
217
+ return { ...base, classification: "cleanup_candidate", reasons: [`${status} workflow reached phase done without a promotion claim; ${PROMOTE_REMEDY}`] };
218
+ }
188
219
  return { ...base, classification: "terminal_done", reasons: [`${status} workflow is in phase done`] };
189
220
  }
190
221
  if ((status === "accepted" || status === "archived") && learning.reasons.length === 0) {
222
+ if (status !== "archived" && !hasPromotionClaim(workflowPath)) {
223
+ return { ...base, classification: "cleanup_candidate", reasons: [`${status} workflow has closed learning routing but no promotion claim; ${PROMOTE_REMEDY}`] };
224
+ }
191
225
  return { ...base, classification: "terminal_done", reasons: [`${status} workflow has no open learning routing`] };
192
226
  }
193
227
  return { ...base, classification: "invalid", reasons: [`unrecognized lifecycle shape: status=${status}, phase=${phase}, next_action.status=${nextStatus ?? "missing"}`] };
@@ -80,7 +80,7 @@ type SurfaceModule = {
80
80
  status: string;
81
81
  policyId: string | undefined;
82
82
  };
83
- generateClaimId: (subjectId: string, surface: string, fieldOrBehavior: string) => string;
83
+ generateClaimId: (subjectId: string, facet: string, fieldOrBehavior: string) => string;
84
84
  statusFunctionVersion: string;
85
85
  resolveInquiry: (bundle: Record<string, unknown>, inquiry: SurfaceInquiry, options?: {
86
86
  now?: Date;
@@ -208,10 +208,17 @@ export declare function sealTrustCheckpoint(dir: string, slug: string, sealedAt:
208
208
  * trust.checkpoint.intoto.json / trust.checkpoint.sig.json from the
209
209
  * session artifact dir to <repoRoot>/delivery/.
210
210
  *
211
- * Fail-soft: if trust.bundle is absent, returns without throwing.
211
+ * Fail-soft on a missing bundle: if trust.bundle is absent, returns without throwing.
212
+ * Fail-CLOSED on repo-root resolution: repoRoot must be a real, resolved kits/ ancestor
213
+ * (see findRepoRootFromDirStrict) — null (no ancestor found) skips the publish with a
214
+ * visible warning instead of writing to whatever process.cwd() happens to be. This
215
+ * prevents a scratch/test session dir (no kits/ ancestor) from silently clobbering an
216
+ * unrelated real repo's delivery/trust.bundle when invoked with that repo as cwd (see
217
+ * evals/integration/test_checkpoint_signing.sh TEST 2 and the WS5 session findings at
218
+ * .kontourai/flow-agents/ws5-governance-kit-slice1 for the root cause this fixes).
212
219
  * Idempotent: overwrites on re-delivery.
213
220
  */
214
- export declare function publishDelivery(dir: string, repoRoot: string): Promise<void>;
221
+ export declare function publishDelivery(dir: string, repoRoot: string | null): Promise<void>;
215
222
  export declare function validateLearningCorrection(record: AnyObj): void;
216
223
  export declare function normalizeLearning(raw: AnyObj, timestamp: string): AnyObj;
217
224
  /** Shape of a claim from the trust.bundle */
@@ -219,7 +226,7 @@ export interface TrustClaim {
219
226
  id: string;
220
227
  subjectType: string;
221
228
  subjectId: string;
222
- surface: string;
229
+ facet?: string;
223
230
  claimType: string;
224
231
  fieldOrBehavior: string;
225
232
  value: string;
@@ -236,7 +243,7 @@ export interface BundleFile {
236
243
  events: AnyObj[];
237
244
  policies: AnyObj[];
238
245
  }
239
- /** The gate block signal read from .flow-agents/.goal-fit-block-streak.json */
246
+ /** The gate block signal read from .kontourai/flow-agents/.goal-fit-block-streak.json */
240
247
  export interface GateBlockSignal {
241
248
  /** True when the streak file exists AND count >= 1 */
242
249
  blocked: boolean;
@@ -251,13 +258,13 @@ export interface GateBlockSignal {
251
258
  */
252
259
  export type GateCalibration = "correct" | "false_block" | "missed_block";
253
260
  /**
254
- * Read the gate block signal from .flow-agents/.goal-fit-block-streak.json
261
+ * Read the gate block signal from .kontourai/flow-agents/.goal-fit-block-streak.json
255
262
  * (written by scripts/hooks/stop-goal-fit.js when block mode fires).
256
263
  * The file sits at <artifact-root>/.goal-fit-block-streak.json — one level
257
264
  * above the session artifact dir. Fail-open: returns { blocked: false } when
258
265
  * the file is absent or unreadable.
259
266
  *
260
- * @param artifactRoot The .flow-agents root dir (parent of session slug dir).
267
+ * @param artifactRoot The runtime artifact root dir (parent of session slug dir).
261
268
  */
262
269
  export declare function readGateBlockSignal(artifactRoot: string): GateBlockSignal;
263
270
  /**