@kontourai/flow-agents 3.6.0 → 3.7.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 (86) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/build/src/builder-flow-run-adapter.d.ts +22 -2
  3. package/build/src/builder-flow-run-adapter.js +93 -28
  4. package/build/src/builder-flow-runtime.d.ts +8 -3
  5. package/build/src/builder-flow-runtime.js +308 -47
  6. package/build/src/cli/assignment-provider.d.ts +4 -7
  7. package/build/src/cli/assignment-provider.js +67 -13
  8. package/build/src/cli/builder-run.js +14 -18
  9. package/build/src/cli/workflow-sidecar.d.ts +28 -4
  10. package/build/src/cli/workflow-sidecar.js +801 -97
  11. package/build/src/cli/workflow.js +290 -42
  12. package/build/src/flow-kit/validate.d.ts +17 -0
  13. package/build/src/flow-kit/validate.js +340 -2
  14. package/build/src/index.js +5 -5
  15. package/build/src/lib/observed-command.d.ts +7 -0
  16. package/build/src/lib/observed-command.js +100 -0
  17. package/build/src/tools/generate-context-map.js +5 -3
  18. package/context/scripts/hooks/lib/kit-catalog.js +1 -1
  19. package/context/scripts/hooks/stop-goal-fit.js +78 -9
  20. package/docs/context-map.md +22 -20
  21. package/docs/developer-architecture.md +1 -1
  22. package/docs/public-workflow-cli.md +76 -7
  23. package/docs/skills-map.md +51 -27
  24. package/docs/spec/builder-flow-runtime.md +41 -40
  25. package/docs/workflow-usage-guide.md +109 -42
  26. package/evals/fixtures/hook-influence/cases.json +2 -2
  27. package/evals/integration/test_builder_entry_enforcement.sh +52 -41
  28. package/evals/integration/test_builder_step_producers.sh +297 -6
  29. package/evals/integration/test_bundle_install.sh +212 -63
  30. package/evals/integration/test_critique_supersession_roundtrip.sh +21 -8
  31. package/evals/integration/test_current_json_per_actor.sh +12 -0
  32. package/evals/integration/test_dual_emit_flow_step.sh +62 -43
  33. package/evals/integration/test_flowdef_session_activation.sh +145 -25
  34. package/evals/integration/test_flowdef_session_history_preservation.sh +23 -21
  35. package/evals/integration/test_gate_lockdown.sh +3 -5
  36. package/evals/integration/test_goal_fit_hook.sh +60 -2
  37. package/evals/integration/test_liveness_verdict.sh +14 -17
  38. package/evals/integration/test_phase_map_and_gate_claim.sh +63 -38
  39. package/evals/integration/test_public_workflow_cli.sh +325 -11
  40. package/evals/integration/test_pull_work_liveness_preflight.sh +22 -66
  41. package/evals/integration/test_sidecar_field_preservation.sh +36 -11
  42. package/evals/integration/test_workflow_sidecar_writer.sh +277 -44
  43. package/evals/integration/test_workflow_steering_hook.sh +15 -38
  44. package/evals/run.sh +2 -0
  45. package/evals/static/test_builder_skill_coherence.sh +247 -0
  46. package/evals/static/test_library_exports.sh +5 -2
  47. package/evals/static/test_workflow_skills.sh +13 -325
  48. package/kits/builder/flows/build.flow.json +22 -0
  49. package/kits/builder/flows/shape.flow.json +9 -9
  50. package/kits/builder/kit.json +70 -16
  51. package/kits/builder/skills/builder-shape/SKILL.md +75 -75
  52. package/kits/builder/skills/continue-work/SKILL.md +45 -106
  53. package/kits/builder/skills/deliver/SKILL.md +96 -442
  54. package/kits/builder/skills/design-probe/SKILL.md +40 -139
  55. package/kits/builder/skills/evidence-gate/SKILL.md +59 -201
  56. package/kits/builder/skills/execute-plan/SKILL.md +54 -125
  57. package/kits/builder/skills/fix-bug/SKILL.md +42 -132
  58. package/kits/builder/skills/gate-review/SKILL.md +60 -211
  59. package/kits/builder/skills/idea-to-backlog/SKILL.md +63 -244
  60. package/kits/builder/skills/learning-review/SKILL.md +63 -170
  61. package/kits/builder/skills/pickup-probe/SKILL.md +54 -111
  62. package/kits/builder/skills/plan-work/SKILL.md +51 -185
  63. package/kits/builder/skills/pull-work/SKILL.md +136 -485
  64. package/kits/builder/skills/release-readiness/SKILL.md +66 -107
  65. package/kits/builder/skills/review-work/SKILL.md +89 -176
  66. package/kits/builder/skills/tdd-workflow/SKILL.md +53 -147
  67. package/kits/builder/skills/verify-work/SKILL.md +101 -113
  68. package/package.json +2 -2
  69. package/scripts/hooks/lib/kit-catalog.js +1 -1
  70. package/scripts/hooks/stop-goal-fit.js +78 -9
  71. package/src/builder-flow-run-adapter.ts +118 -32
  72. package/src/builder-flow-runtime.ts +331 -61
  73. package/src/cli/assignment-provider-lock.test.mjs +83 -0
  74. package/src/cli/assignment-provider.ts +62 -13
  75. package/src/cli/builder-flow-run-adapter.test.mjs +4 -2
  76. package/src/cli/builder-flow-runtime.test.mjs +194 -8
  77. package/src/cli/builder-run.ts +3 -9
  78. package/src/cli/kit-metadata-security.test.mjs +232 -6
  79. package/src/cli/public-api.test.mjs +15 -0
  80. package/src/cli/workflow-sidecar-execution-proof.test.mjs +90 -0
  81. package/src/cli/workflow-sidecar.ts +724 -97
  82. package/src/cli/workflow.ts +277 -40
  83. package/src/flow-kit/validate.ts +320 -2
  84. package/src/index.ts +6 -5
  85. package/src/lib/observed-command.ts +96 -0
  86. package/src/tools/generate-context-map.ts +5 -3
@@ -292,23 +292,20 @@ else
292
292
  fi
293
293
 
294
294
  # ─── G. Static skill-text assertions (AC2) ──────────────────────────────────
295
- echo "--- G. Static pull-work tiebreaker/loser-release skill-text assertions ---"
296
-
297
- require_text "$PULL" '### Post-Claim Conflict Re-check' "pull-work documents the Post-Claim Conflict Re-check subsection (AC2)"
298
- require_text "$PULL" 'When a double-hold is detected, immediately run the deterministic tiebreaker' "pull-work instructs running the deterministic tiebreaker immediately on a detected double-hold (AC2)"
299
- require_text "$PULL" 'liveness verdict <subjectId> --json' "pull-work references liveness verdict <subjectId> --json by exact command name (AC1, AC2)"
300
- require_text "$PULL" 'the same pinned .self_actor. and the same .subjectId. already in scope' "pull-work reuses the same pinned self_actor/subjectId already in scope for the verdict call, never re-derived (AC2)"
301
- require_text "$PULL" 'an exact-timestamp tie breaks by ascending actor-id string comparison \(.reason: \"tie-actor-lexicographic\".\)' "pull-work documents the exact-timestamp tiebreak and its reason value verbatim (AC1, AC2)"
302
- require_text "$PULL" 'the SAME verdict for the SAME stream state regardless of which actor invokes it' "pull-work states the verdict is deterministic regardless of which actor invokes it (AC1, AC2)"
303
- require_text "$PULL" 'If .winner\.actor !== self_actor., this session is the loser' "pull-work's loser branch is keyed on winner.actor !== self_actor (AC2)"
304
- require_text "$PULL" 'immediately run .npm run workflow:sidecar -- liveness release <subjectId> --actor <self_actor>.' "pull-work's loser branch runs liveness release <subjectId> --actor <self_actor> immediately (AC2)"
305
- require_text "$PULL" 'extend .post_claim_conflict. with .\{verdict_reason, winner_actor, conceded: true\}.' "pull-work's loser branch extends post_claim_conflict with {verdict_reason, winner_actor, conceded: true} (AC2)"
306
- require_text "$PULL" 'return to .### 3\. Select Work. to reselect within the same .pull-work. pass' "pull-work's loser branch returns to Select Work to reselect within the same pass (AC2)"
307
- require_text "$PULL" 'excluding the just-released subject' "pull-work's reselect excludes the just-released subject (AC2)"
308
- require_text "$PULL" 'If .winner\.actor === self_actor., this session wins' "pull-work's winner branch is keyed on winner.actor === self_actor (AC2)"
309
- require_text "$PULL" 'record the verdict for transparency \(.\{verdict_reason, winner_actor: self_actor, conceded: false\}.\) in .post_claim_conflict. and proceed normally; do not release' "pull-work's winner branch records the verdict for transparency and proceeds without releasing (AC2)"
310
- require_text "$PULL" 'closes the .detected but advisory-only. gap ADR 0012 §4 names for THIS session.s own double-hold' "pull-work's honesty note states the verdict+release loop closes the detected-but-advisory-only gap for this session's own double-hold (AC2)"
311
- require_text "$PULL" 'still does not provide true mutual exclusion across the read-then-write race window itself' "pull-work's honesty note distinguishes the new convergence guarantee from the unchanged read-then-write race residual (AC2)"
295
+ echo "--- G. Provider-neutral pull-work conflict contract assertions ---"
296
+
297
+ require_text "$PULL" 'deterministic conflict arbitration' "pull-work uses provider conflict arbitration"
298
+ require_text "$PULL" 'same pinned caller identity, subject, and observation' "pull-work reuses one conflict snapshot"
299
+ require_text "$PULL" 'Prefer the earlier claim' "pull-work defines deterministic winner precedence"
300
+ require_text "$PULL" 'exact timestamp tie by ascending' "pull-work defines deterministic tie handling"
301
+ require_text "$PULL" 'same winner from the same state' "pull-work requires actor-independent verdicts"
302
+ require_text "$PULL" 'If the caller loses' "pull-work defines loser behavior"
303
+ require_text "$PULL" 'release or concede its claim immediately' "pull-work makes the loser concede"
304
+ require_text "$PULL" 'conceded: true' "pull-work records loser concession"
305
+ require_text "$PULL" 'exclude the contested subject' "pull-work excludes the lost subject before reselection"
306
+ require_text "$PULL" 'If the caller wins' "pull-work defines winner behavior"
307
+ require_text "$PULL" 'conceded: false' "pull-work records winner transparency"
308
+ require_text "$PULL" 'underlying read-then-write race' "pull-work distinguishes convergence from mutual exclusion"
312
309
 
313
310
  # ─── H. Hostile-actor injection: winner.actor sanitized in --json and text (F5) ─────
314
311
  echo "--- H. Hostile-actor verdict sanitization (F5) ---"
@@ -8,7 +8,7 @@
8
8
  # 4. record-gate-claim at pull-work step produces builder.pull-work.selected claim (status=verified).
9
9
  # 5. A TAMPERED bundle (stored verified, evidence fail) at pull-work step BLOCKS (exit 2)
10
10
  # with the tamper warning naming the declared claimType.
11
- # 6. A CLEAN record-gate-claim bundle is not reported as false completion;
11
+ # 6. A CLEAN public Builder bundle is not reported as false completion;
12
12
  # its still-active canonical Flow run remains blocked from stopping.
13
13
  #
14
14
  # Deterministic, no model spend, self-cleaning.
@@ -162,10 +162,14 @@ flow_agents_node "workflow-sidecar" init-plan "$CLAIM_ROOT/gate-claim/gate-claim
162
162
  --source-request "Test" --summary "Testing" \
163
163
  --timestamp "2026-06-26T00:00:00Z" >/dev/null 2>&1
164
164
 
165
+ CLAIM_ARTIFACT="$CLAIM_ROOT/gate-claim/gate-claim--pull-work.md"
166
+ printf '# Pull Work\n\nSelected issue #177 for implementation.\n' > "$CLAIM_ARTIFACT"
167
+
165
168
  if flow_agents_node "workflow-sidecar" record-gate-claim "$CLAIM_ROOT/gate-claim" \
166
169
  --status pass \
167
170
  --summary "Selected issue #177 for implementation." \
168
171
  --expectation selected-work \
172
+ --evidence-ref-json "{\"kind\":\"artifact\",\"file\":\"$CLAIM_ARTIFACT\",\"summary\":\"Declared durable pull-work artifact for selected-work.\"}" \
169
173
  --timestamp "2026-06-26T00:01:00Z" >/dev/null 2>&1; then
170
174
  _pass "record-gate-claim exits 0 at pull-work step"
171
175
  else
@@ -188,9 +192,18 @@ node -e "
188
192
  console.error('expected status=verified, got', target.status);
189
193
  process.exit(1);
190
194
  }
195
+ const expectedArtifact = '.kontourai/flow-agents/gate-claim/gate-claim--pull-work.md';
196
+ if (!fs.existsSync('$CLAIM_ARTIFACT')) {
197
+ console.error('missing declared durable selected-work artifact:', '$CLAIM_ARTIFACT');
198
+ process.exit(1);
199
+ }
200
+ if (!target.metadata?.artifact_refs?.some(ref => ref.kind === 'artifact' && ref.file === expectedArtifact)) {
201
+ console.error('selected-work claim does not cite declared artifact:', JSON.stringify(target.metadata?.artifact_refs));
202
+ process.exit(1);
203
+ }
191
204
  " 2>/dev/null \
192
- && _pass "bundle contains builder.pull-work.selected with subjectType=work-item, status=verified" \
193
- || _fail "bundle missing or incorrect builder.pull-work.selected claim"
205
+ && _pass "bundle contains builder.pull-work.selected with declared durable artifact evidence" \
206
+ || _fail "bundle missing selected-work claim or declared durable artifact evidence"
194
207
 
195
208
  echo ""
196
209
  echo "=== 4b. composed publish-learn gate claim emits builder.pr-open.pull-request ==="
@@ -215,10 +228,14 @@ flow_agents_node "workflow-sidecar" advance-state "$COMPOSED_ROOT/composed-gate-
215
228
  --flow-definition builder.build \
216
229
  --timestamp "2026-06-26T00:00:30Z" >/dev/null 2>&1
217
230
 
231
+ COMPOSED_ARTIFACT="$COMPOSED_ROOT/composed-gate-claim/release.json"
232
+ printf '{"schema_version":"1.0","task_slug":"composed-gate-claim","status":"pr_open"}\n' > "$COMPOSED_ARTIFACT"
233
+
218
234
  if flow_agents_node "workflow-sidecar" record-gate-claim "$COMPOSED_ROOT/composed-gate-claim" \
219
235
  --status pass \
220
236
  --summary "PR opened with verification evidence." \
221
237
  --expectation pull-request-opened \
238
+ --evidence-ref-json "{\"kind\":\"artifact\",\"file\":\"$COMPOSED_ARTIFACT\",\"summary\":\"Declared durable release artifact for pull-request-opened.\"}" \
222
239
  --timestamp "2026-06-26T00:01:00Z" >/dev/null 2>&1; then
223
240
  _pass "record-gate-claim exits 0 at composed pr-open step"
224
241
  else
@@ -244,9 +261,18 @@ node -e "
244
261
  console.error('expected status=verified, got', target.status);
245
262
  process.exit(1);
246
263
  }
264
+ const expectedArtifact = '.kontourai/flow-agents/composed-gate-claim/release.json';
265
+ if (!fs.existsSync('$COMPOSED_ARTIFACT')) {
266
+ console.error('missing declared durable pr-open artifact:', '$COMPOSED_ARTIFACT');
267
+ process.exit(1);
268
+ }
269
+ if (!target.metadata?.artifact_refs?.some(ref => ref.kind === 'artifact' && ref.file === expectedArtifact)) {
270
+ console.error('pr-open claim does not cite declared artifact:', JSON.stringify(target.metadata?.artifact_refs));
271
+ process.exit(1);
272
+ }
247
273
  " 2>/dev/null \
248
- && _pass "composed bundle contains builder.pr-open.pull-request with active parent flow" \
249
- || _fail "composed bundle missing or incorrect builder.pr-open.pull-request claim"
274
+ && _pass "composed bundle contains builder.pr-open.pull-request with declared durable artifact evidence" \
275
+ || _fail "composed bundle missing pr-open claim or declared durable artifact evidence"
250
276
 
251
277
  # ─── Tamper-blocks: stored verified + evidence fail → BLOCK (exit 2) ─────────
252
278
  echo ""
@@ -355,59 +381,58 @@ fi
355
381
 
356
382
  # ─── Clean gate-claim: no false completion, active Flow still blocks ─────────
357
383
  echo ""
358
- echo "=== 6. CLEAN record-gate-claim remains governed by active canonical Flow ==="
384
+ echo "=== 6. CLEAN public Builder evidence remains governed by active canonical Flow ==="
359
385
 
360
386
  C_DIR="$TMP/clean-test"
387
+ CLEAN_SLUG="provider-clean"
361
388
  mkdir -p "$C_DIR"
362
389
  printf '# Repo\n' > "$C_DIR/AGENTS.md"
390
+ mkdir -p "$C_DIR/.kontourai/flow-agents/$CLEAN_SLUG"
391
+ CLEAN_ARTIFACT="$C_DIR/.kontourai/flow-agents/$CLEAN_SLUG/$CLEAN_SLUG--pull-work.md"
392
+ printf 'Selected Work Item: provider:clean\n' > "$CLEAN_ARTIFACT"
363
393
 
364
- flow_agents_node "workflow-sidecar" ensure-session \
394
+ if ! FLOW_AGENTS_ACTOR=clean-fixture-actor node "$ROOT/build/src/cli.js" workflow start \
365
395
  --artifact-root "$C_DIR/.kontourai/flow-agents" \
366
- --task-slug clean \
396
+ --flow builder.build \
397
+ --work-item provider:clean \
398
+ --assignment-provider local-file \
367
399
  --title "Clean Test" \
368
- --summary "Testing clean gate claim." \
369
- --flow-id builder.build \
370
- --skip-ownership-guard \
371
- --timestamp "2026-06-26T00:00:00Z" >/dev/null 2>&1
400
+ --summary "Testing clean gate claim." >"$TMP/clean-start.out" 2>&1; then
401
+ _fail "public clean fixture failed to start: $(cat "$TMP/clean-start.out")"
402
+ fi
372
403
 
373
- flow_agents_node "workflow-sidecar" init-plan "$C_DIR/.kontourai/flow-agents/clean/clean--deliver.md" \
404
+ flow_agents_node "workflow-sidecar" init-plan "$C_DIR/.kontourai/flow-agents/$CLEAN_SLUG/$CLEAN_SLUG--deliver.md" \
374
405
  --source-request "Test" --summary "Testing" \
375
406
  --timestamp "2026-06-26T00:00:00Z" >/dev/null 2>&1
376
407
 
377
- flow_agents_node "workflow-sidecar" advance-state "$C_DIR/.kontourai/flow-agents/clean" \
378
- --status in_progress \
379
- --phase pickup \
380
- --summary "In progress." \
381
- --next-action "done" \
382
- --flow-definition builder.build \
383
- --timestamp "2026-06-26T00:00:30Z" >/dev/null 2>&1
384
-
385
408
  # Fix next_action so it reads as "done" for the gate
386
409
  node -e "
387
410
  const fs = require('fs');
388
- const f = '$C_DIR/.kontourai/flow-agents/clean/state.json';
411
+ const f = '$C_DIR/.kontourai/flow-agents/$CLEAN_SLUG/state.json';
389
412
  const s = JSON.parse(fs.readFileSync(f, 'utf8'));
390
413
  s.next_action = { status: 'done', summary: 'Work complete.' };
391
414
  s.status = 'verified';
415
+ delete s.flow_run;
392
416
  fs.writeFileSync(f, JSON.stringify(s, null, 2) + '\n');
393
417
  " 2>/dev/null
394
-
395
- flow_agents_node "workflow-sidecar" record-gate-claim "$C_DIR/.kontourai/flow-agents/clean" \
396
- --status pass \
397
- --summary "Selected issue #177 for implementation." \
398
- --expectation selected-work \
399
- --timestamp "2026-06-26T00:01:00Z" >/dev/null 2>&1
400
-
401
- set +e
402
- clean_out="$(FLOW_AGENTS_GOAL_FIT_MODE=block FLOW_AGENTS_GOAL_FIT_BACKSTOP=skip \
403
- node "$GATE" 2>&1 <<< "{\"hook_event_name\":\"Stop\",\"cwd\":\"$C_DIR\"}")"
404
- clean_exit="$?"
405
- set -e
406
-
407
- if [ "$clean_exit" -eq 2 ] && echo "$clean_out" | grep -q 'release skipped for active Flow run "clean"'; then
408
- _pass "clean claim advances to the next Flow step and active run blocks premature Stop"
418
+ rm -f "$C_DIR/.kontourai/flow-agents/$CLEAN_SLUG/$CLEAN_SLUG--deliver.md"
419
+
420
+ clean_out=""
421
+ clean_exit=0
422
+ for attempt in 1 2; do
423
+ set +e
424
+ attempt_out="$(FLOW_AGENTS_GOAL_FIT_MODE=block FLOW_AGENTS_GOAL_FIT_MAX_BLOCKS=2 FLOW_AGENTS_GOAL_FIT_BACKSTOP=skip \
425
+ node "$GATE" 2>&1 <<< "{\"hook_event_name\":\"Stop\",\"cwd\":\"$C_DIR\"}")"
426
+ attempt_exit="$?"
427
+ set -e
428
+ clean_out="$clean_out$attempt_out"
429
+ if [ "$attempt_exit" -ne 2 ]; then clean_exit="$attempt_exit"; fi
430
+ done
431
+
432
+ if [ "$clean_exit" -eq 0 ] && echo "$clean_out" | grep -q 'canonical Flow run remains active at step design-probe'; then
433
+ _pass "canonical Flow blocks repeated Stop after the sidecar projection and Markdown are removed"
409
434
  else
410
- _fail "clean active Flow run returned the wrong Stop decision (exit $clean_exit): $clean_out"
435
+ _fail "canonical Flow could be hidden or auto-released after sidecar rewrite (exit $clean_exit): $clean_out"
411
436
  fi
412
437
 
413
438
  if echo "$clean_out" | grep -q "caught false-completion"; then