@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
@@ -653,26 +653,12 @@ const state = {
653
653
  target_phase: "goal_fit",
654
654
  },
655
655
  };
656
- const critique = {
657
- schema_version: "1.0",
658
- task_slug: "installed-hook-demo",
659
- status: "fail",
660
- required: true,
661
- updated_at: "2026-05-09T00:01:00Z",
662
- critiques: [{
663
- id: "installed-hook-review",
664
- reviewer: "tool-code-reviewer",
665
- reviewed_at: "2026-05-09T00:01:00Z",
666
- verdict: "fail",
667
- summary: "Blocking installed hook verification remains.",
668
- findings: [{ id: "missing-installed-exec", severity: "high", status: "open", description: "Execute the installed hook command." }],
669
- }],
670
- };
656
+ const trustBundle = { schema_version: "1.0", claims: [] };
671
657
  function writeFixture(root) {
672
658
  const taskDir = path.join(root, ".kontourai/flow-agents/installed-hook-demo");
673
659
  fs.mkdirSync(taskDir, { recursive: true });
674
660
  fs.writeFileSync(path.join(taskDir, "state.json"), JSON.stringify(state), "utf8");
675
- fs.writeFileSync(path.join(taskDir, "critique.json"), JSON.stringify(critique), "utf8");
661
+ fs.writeFileSync(path.join(taskDir, "trust.bundle"), JSON.stringify(trustBundle), "utf8");
676
662
  fs.mkdirSync(path.join(root, "docs"), { recursive: true });
677
663
  fs.writeFileSync(path.join(root, "docs/context-map.md"), "# Context Map\n", "utf8");
678
664
  }
@@ -704,7 +690,7 @@ function runCommand(label, command, cwd, runtimeJson) {
704
690
  const ctx = runtimeJson ? (JSON.parse(result.stdout).hookSpecificOutput?.additionalContext || "") : result.stdout;
705
691
  if (!ctx.includes("WORKFLOW STATE ATTENTION")) throw new Error(`${label} installed hook did not emit workflow attention: ${result.stdout} ${result.stderr}`);
706
692
  if (!ctx.includes("STATE: installed-hook-demo is status:not_verified phase:verification")) throw new Error(`${label} installed hook missed state guidance: ${ctx}`);
707
- if (!ctx.includes("CRITIQUE: required critique is status:fail")) throw new Error(`${label} installed hook missed critique guidance: ${ctx}`);
693
+ if (ctx.includes("CRITIQUE:")) throw new Error(`${label} installed hook read retired critique sidecar guidance: ${ctx}`);
708
694
  }
709
695
  writeFixture(claudeDest);
710
696
  writeFixture(codexDest);
@@ -736,19 +722,12 @@ const state = {
736
722
  updated_at: "2026-06-01T00:00:00Z",
737
723
  next_action: { status: "needs_user", summary: "Opencode hook test.", target_phase: "goal_fit" },
738
724
  };
739
- const critique = {
740
- schema_version: "1.0",
741
- task_slug: "opencode-hook-demo",
742
- status: "fail",
743
- required: true,
744
- updated_at: "2026-06-01T00:01:00Z",
745
- critiques: [{ id: "oc-review", reviewer: "tool-code-reviewer", reviewed_at: "2026-06-01T00:01:00Z", verdict: "fail", summary: "Blocking.", findings: [{ id: "oc-open", severity: "high", status: "open", description: "Open finding." }] }],
746
- };
725
+ const trustBundle = { schema_version: "1.0", claims: [] };
747
726
  function writeFixture(root) {
748
727
  const taskDir = path.join(root, ".kontourai/flow-agents/opencode-hook-demo");
749
728
  fs.mkdirSync(taskDir, { recursive: true });
750
729
  fs.writeFileSync(path.join(taskDir, "state.json"), JSON.stringify(state), "utf8");
751
- fs.writeFileSync(path.join(taskDir, "critique.json"), JSON.stringify(critique), "utf8");
730
+ fs.writeFileSync(path.join(taskDir, "trust.bundle"), JSON.stringify(trustBundle), "utf8");
752
731
  fs.mkdirSync(path.join(root, "docs"), { recursive: true });
753
732
  fs.writeFileSync(path.join(root, "docs/context-map.md"), "# Context Map\n", "utf8");
754
733
  }
@@ -767,7 +746,7 @@ function runOpencodeAdapter(bundleDest, cwd) {
767
746
  const ctx = out.context || "";
768
747
  if (!ctx.includes("WORKFLOW STATE ATTENTION")) throw new Error("opencode adapter did not emit workflow attention: stdout=" + result.stdout + " stderr=" + result.stderr);
769
748
  if (!ctx.includes("STATE: opencode-hook-demo is status:not_verified phase:verification")) throw new Error("opencode adapter missed state guidance: " + ctx);
770
- if (!ctx.includes("CRITIQUE: required critique is status:fail")) throw new Error("opencode adapter missed critique guidance: " + ctx);
749
+ if (ctx.includes("CRITIQUE:")) throw new Error("opencode adapter read retired critique sidecar guidance: " + ctx);
771
750
  }
772
751
  writeFixture(opencodeWorkspace);
773
752
  runOpencodeAdapter(opencodeDest, opencodeWorkspace);
@@ -795,19 +774,12 @@ const state = {
795
774
  updated_at: "2026-06-01T00:00:00Z",
796
775
  next_action: { status: "needs_user", summary: "Pi hook test.", target_phase: "goal_fit" },
797
776
  };
798
- const critique = {
799
- schema_version: "1.0",
800
- task_slug: "pi-hook-demo",
801
- status: "fail",
802
- required: true,
803
- updated_at: "2026-06-01T00:01:00Z",
804
- critiques: [{ id: "pi-review", reviewer: "tool-code-reviewer", reviewed_at: "2026-06-01T00:01:00Z", verdict: "fail", summary: "Blocking.", findings: [{ id: "pi-open", severity: "high", status: "open", description: "Open finding." }] }],
805
- };
777
+ const trustBundle = { schema_version: "1.0", claims: [] };
806
778
  function writeFixture(root) {
807
779
  const taskDir = path.join(root, ".kontourai/flow-agents/pi-hook-demo");
808
780
  fs.mkdirSync(taskDir, { recursive: true });
809
781
  fs.writeFileSync(path.join(taskDir, "state.json"), JSON.stringify(state), "utf8");
810
- fs.writeFileSync(path.join(taskDir, "critique.json"), JSON.stringify(critique), "utf8");
782
+ fs.writeFileSync(path.join(taskDir, "trust.bundle"), JSON.stringify(trustBundle), "utf8");
811
783
  fs.mkdirSync(path.join(root, "docs"), { recursive: true });
812
784
  fs.writeFileSync(path.join(root, "docs/context-map.md"), "# Context Map\n", "utf8");
813
785
  }
@@ -826,7 +798,7 @@ function runPiAdapter(bundleDest, cwd) {
826
798
  const ctx = out.context || "";
827
799
  if (!ctx.includes("WORKFLOW STATE ATTENTION")) throw new Error("pi adapter did not emit workflow attention: stdout=" + result.stdout + " stderr=" + result.stderr);
828
800
  if (!ctx.includes("STATE: pi-hook-demo is status:not_verified phase:verification")) throw new Error("pi adapter missed state guidance: " + ctx);
829
- if (!ctx.includes("CRITIQUE: required critique is status:fail")) throw new Error("pi adapter missed critique guidance: " + ctx);
801
+ if (ctx.includes("CRITIQUE:")) throw new Error("pi adapter read retired critique sidecar guidance: " + ctx);
830
802
  }
831
803
  writeFixture(piWorkspace);
832
804
  runPiAdapter(piDest, piWorkspace);
@@ -886,7 +858,9 @@ echo "--- Packed Package Builder Entry ---"
886
858
  PACKAGE_CONSUMER="$TMPDIR_EVAL/package-consumer"
887
859
  PACKAGE_PROJECT="$TMPDIR_EVAL/package-project"
888
860
  PACKAGE_AMBIENT="$TMPDIR_EVAL/package-ambient"
889
- mkdir -p "$PACKAGE_CONSUMER" "$PACKAGE_PROJECT/.kontourai/flow-agents" "$PACKAGE_AMBIENT/kits/builder/flows"
861
+ mkdir -p "$PACKAGE_CONSUMER" "$PACKAGE_PROJECT/.kontourai/flow-agents" "$PACKAGE_PROJECT/checks" "$PACKAGE_AMBIENT/kits/builder/flows"
862
+ printf '#!/usr/bin/env bash\nset -eu\ntest -f "$1"\nprintf "1..1\\nok 1 - session exists\\n"\n' > "$PACKAGE_PROJECT/checks/check-packed-workflow.sh"
863
+ chmod +x "$PACKAGE_PROJECT/checks/check-packed-workflow.sh"
890
864
  cat >"$PACKAGE_AMBIENT/kits/builder/flows/build.flow.json" <<'JSON'
891
865
  {
892
866
  "id": "builder.build",
@@ -897,35 +871,152 @@ cat >"$PACKAGE_AMBIENT/kits/builder/flows/build.flow.json" <<'JSON'
897
871
  JSON
898
872
  PACKAGE_PACK_LOG="$TMPDIR_EVAL/package-pack.log"
899
873
  PACKAGE_CLI="$PACKAGE_CONSUMER/node_modules/@kontourai/flow-agents/build/src/cli.js"
900
- PACKAGE_SESSION="$PACKAGE_PROJECT/.kontourai/flow-agents/packed-builder-entry"
874
+ PACKAGE_SESSION="$PACKAGE_PROJECT/.kontourai/flow-agents/acme-builder-901"
875
+ PACKAGE_LIFECYCLE_SESSION="$PACKAGE_PROJECT/.kontourai/flow-agents/acme-builder-902"
876
+ PACKAGE_TEST_COMMAND="bash checks/check-packed-workflow.sh .kontourai/flow-agents/acme-builder-901/state.json"
877
+ PACKAGE_CRITERION_JSON="$(node - "$PACKAGE_TEST_COMMAND" <<'NODE'
878
+ const command = process.argv[2];
879
+ process.stdout.write(JSON.stringify({
880
+ id: 'AC-1',
881
+ status: 'pass',
882
+ evidence_refs: [{ kind: 'command', excerpt: command, summary: 'The packed public workflow fixture executed this exact assertion for AC-1.' }],
883
+ }));
884
+ NODE
885
+ )"
886
+ PACKAGE_COMMAND_REF="$(node - "$PACKAGE_TEST_COMMAND" <<'NODE'
887
+ const command = process.argv[2];
888
+ process.stdout.write(JSON.stringify({ kind: 'command', excerpt: command, summary: 'Runs the packed consumer workflow assertion.' }));
889
+ NODE
890
+ )"
891
+
892
+ package_flow() {
893
+ (cd "$PACKAGE_PROJECT" && CODEX_SESSION_ID=packed-package-consumer node "$PACKAGE_CLI" workflow "$@")
894
+ }
895
+
896
+ package_review() {
897
+ (cd "$PACKAGE_PROJECT" && CODEX_SESSION_ID=packed-package-reviewer node "$PACKAGE_CLI" workflow "$@")
898
+ }
899
+
901
900
  if (cd "$ROOT_DIR" && npm pack --silent --pack-destination "$TMPDIR_EVAL" >"$PACKAGE_PACK_LOG") \
902
901
  && PACKAGE_TARBALL="$(find "$TMPDIR_EVAL" -maxdepth 1 -type f -name 'kontourai-flow-agents-*.tgz' -print -quit)" \
903
902
  && [[ -n "$PACKAGE_TARBALL" ]] \
904
903
  && npm install --silent --no-audit --no-fund --ignore-scripts --prefix "$PACKAGE_CONSUMER" "$PACKAGE_TARBALL" \
905
- && (cd "$PACKAGE_AMBIENT" && CODEX_SESSION_ID=packed-package-consumer node "$PACKAGE_CONSUMER/node_modules/@kontourai/flow-agents/build/src/cli/workflow-sidecar.js" ensure-session \
906
- --artifact-root "$PACKAGE_PROJECT/.kontourai/flow-agents" \
907
- --task-slug packed-builder-entry \
908
- --title "Packed Builder entry" \
909
- --summary "Installed package should project pickup-probe." \
910
- --flow-id builder.build >/dev/null 2>&1) \
911
- && node - "$PACKAGE_PROJECT" <<'NODE' &&
904
+ && mkdir -p "$PACKAGE_SESSION" "$PACKAGE_LIFECYCLE_SESSION" \
905
+ && printf 'Selected Work Item: acme/builder#901\n' > "$PACKAGE_SESSION/acme-builder-901--pull-work.md" \
906
+ && package_flow start --artifact-root "$PACKAGE_PROJECT/.kontourai/flow-agents" \
907
+ --flow builder.build --work-item acme/builder#901 --assignment-provider local-file --summary "Packed public workflow contract fixture." >/dev/null \
908
+ && node - "$PACKAGE_SESSION" <<'NODE' &&
909
+ const fs = require('node:fs');
910
+ const path = require('node:path');
911
+ const session = process.argv[2];
912
+ const slug = path.basename(session);
913
+ const write = (name, body) => fs.writeFileSync(path.join(session, name), body, 'utf8');
914
+ write(`${slug}--pull-work.md`, '# Pull And Probe Report\n\nSelected Work Item: acme/builder#901\n\nPickup readiness and recorded decisions are reviewable here.\n');
915
+ write(`${slug}--plan-work.md`, '# Plan\n\n## Definition Of Done\n\n- AC-1: The packed workflow fixture records criterion-backed command evidence.\n');
916
+ write(`${slug}--deliver.md`, '# Execute\n\nThe packed fixture changed only its declared fixture artifacts.\n');
917
+ write(`${slug}--evidence-gate.md`, '# Evidence Gate\n\nScope and acceptance coverage are reviewable here.\n');
918
+ write('acceptance.json', JSON.stringify({ schema_version: '1.0', task_slug: slug, criteria: [{ id: 'AC-1', description: 'The packed workflow fixture records criterion-backed command evidence.', status: 'pending', evidence_refs: [] }], goal_fit: { status: 'pending', summary: 'Fixture has not completed Goal Fit review.' } }, null, 2));
919
+ write('release.json', JSON.stringify({ schema_version: '1.0', task_slug: slug, decision: 'hold', updated_at: '2026-07-11T00:00:00Z', scope: 'packed fixture', evidence_ref: `${slug}--evidence-gate.md`, gates: [{ name: 'merge', status: 'hold', summary: 'Fixture never publishes.' }], rollback_plan: { status: 'not_required', summary: 'No release.', owner: 'fixture' }, observability_plan: { status: 'not_required', summary: 'No release.' }, post_deploy_checks: [], docs: { status: 'not_needed', summary: 'Fixture.' } }, null, 2));
920
+ write('learning.json', JSON.stringify({ schema_version: '1.0', task_slug: slug, status: 'learned', updated_at: '2026-07-11T00:00:00Z', records: [{ id: 'packed-fixture-learning', recorded_at: '2026-07-11T00:00:00Z', source_refs: [`${slug}--evidence-gate.md`], outcome: 'success', facts: ['Packed consumer produced declared durable artifacts.'], interpretation: 'The public workflow contract remained usable after npm install.', routing: [{ target: 'none', action: 'No follow-up required.', status: 'completed' }], correction: { needed: false, evidence: 'Fixture contract passes.' } }] }, null, 2));
921
+ NODE
922
+ node - "$PACKAGE_PROJECT" <<'NODE' &&
912
923
  const fs = require('node:fs');
913
924
  const path = require('node:path');
914
925
  const project = process.argv[2];
915
- const state = JSON.parse(fs.readFileSync(path.join(project, '.kontourai', 'flow-agents', 'packed-builder-entry', 'state.json'), 'utf8'));
916
- const flow = JSON.parse(fs.readFileSync(path.join(project, '.kontourai', 'flow', 'runs', 'packed-builder-entry', 'state.json'), 'utf8'));
917
- const bundle = JSON.parse(fs.readFileSync(path.join(project, '.kontourai', 'flow-agents', 'packed-builder-entry', 'trust.bundle'), 'utf8'));
926
+ const state = JSON.parse(fs.readFileSync(path.join(project, '.kontourai', 'flow-agents', 'acme-builder-901', 'state.json'), 'utf8'));
927
+ const flow = JSON.parse(fs.readFileSync(path.join(project, '.kontourai', 'flow', 'runs', 'acme-builder-901', 'state.json'), 'utf8'));
928
+ const bundle = JSON.parse(fs.readFileSync(path.join(project, '.kontourai', 'flow-agents', 'acme-builder-901', 'trust.bundle'), 'utf8'));
918
929
  if (flow.current_step !== 'design-probe' || state.flow_run?.current_step !== 'design-probe') process.exit(1);
919
930
  if (JSON.stringify(state.next_action?.skills) !== JSON.stringify(['pickup-probe'])) process.exit(1);
920
931
  if (!(bundle.claims || []).some((claim) => claim.claimType === 'builder.pull-work.selected' && claim.status === 'verified')) process.exit(1);
921
932
  NODE
922
- node --input-type=module - "$PACKAGE_PROJECT" "$PACKAGE_CONSUMER" <<'NODE' &&
933
+ package_flow evidence --session-dir "$PACKAGE_SESSION" --expectation pickup-probe-readiness --status pass \
934
+ --summary "Packed fixture records the declared pickup readiness artifact." \
935
+ --evidence-ref-json "{\"kind\":\"artifact\",\"file\":\"$PACKAGE_SESSION/acme-builder-901--pull-work.md\",\"summary\":\"Durable pickup readiness artifact.\"}" >/dev/null \
936
+ && package_flow evidence --session-dir "$PACKAGE_SESSION" --expectation probe-decisions-or-accepted-gaps --status pass \
937
+ --summary "Packed fixture records the declared probe decisions artifact." \
938
+ --evidence-ref-json "{\"kind\":\"artifact\",\"file\":\"$PACKAGE_SESSION/acme-builder-901--pull-work.md\",\"summary\":\"Durable probe decisions artifact.\"}" >/dev/null \
939
+ && package_flow evidence --session-dir "$PACKAGE_SESSION" --expectation implementation-plan --status pass \
940
+ --summary "Packed fixture records the declared implementation plan artifact." \
941
+ --evidence-ref-json "{\"kind\":\"artifact\",\"file\":\"$PACKAGE_SESSION/acme-builder-901--plan-work.md\",\"summary\":\"Durable implementation plan artifact.\"}" >/dev/null \
942
+ && package_flow evidence --session-dir "$PACKAGE_SESSION" --expectation implementation-scope --status pass \
943
+ --summary "Packed fixture records the declared execution scope artifact." \
944
+ --evidence-ref-json "{\"kind\":\"artifact\",\"file\":\"$PACKAGE_SESSION/acme-builder-901--deliver.md\",\"summary\":\"Durable execution scope artifact.\"}" >/dev/null \
945
+ && ! package_flow evidence --session-dir "$PACKAGE_SESSION" --expectation tests-evidence --status pass \
946
+ --command "$PACKAGE_TEST_COMMAND" --criterion-json "$PACKAGE_CRITERION_JSON" \
947
+ --summary "This must reject tests evidence before a clean review." \
948
+ --evidence-ref-json "$PACKAGE_COMMAND_REF" >/dev/null 2>&1 \
949
+ && package_review critique --session-dir "$PACKAGE_SESSION" --verdict pass \
950
+ --summary "Authenticated packed review found no blocking fixture findings." \
951
+ --artifact-ref "$PACKAGE_SESSION/acme-builder-901--deliver.md" \
952
+ --lane-json "{\"id\":\"code-review\",\"status\":\"pass\",\"summary\":\"Reviewed the packed consumer delivery.\",\"evidence_refs\":[{\"kind\":\"artifact\",\"file\":\"$PACKAGE_SESSION/acme-builder-901--deliver.md\",\"summary\":\"Reviewed packed delivery artifact.\"}]}" >/dev/null \
953
+ && package_flow evidence --session-dir "$PACKAGE_SESSION" --expectation tests-evidence --status pass \
954
+ --command "$PACKAGE_TEST_COMMAND" --criterion-json "$PACKAGE_CRITERION_JSON" \
955
+ --summary "Packed fixture records criterion-backed command evidence after review." \
956
+ --evidence-ref-json "$PACKAGE_COMMAND_REF" >/dev/null \
957
+ && node - "$PACKAGE_SESSION" <<'NODE' &&
958
+ const fs = require('node:fs');
959
+ const path = require('node:path');
960
+ const session = process.argv[2];
961
+ const bundle = JSON.parse(fs.readFileSync(path.join(session, 'trust.bundle'), 'utf8'));
962
+ const criterionClaim = (bundle.claims || []).find((claim) =>
963
+ claim.metadata?.origin === 'acceptance'
964
+ && claim.metadata?.criterion?.id === 'AC-1'
965
+ && claim.metadata.criterion.status === 'pass'
966
+ && claim.metadata.criterion.evidence_refs?.some((ref) => ref.kind === 'command'));
967
+ if (!criterionClaim) {
968
+ console.error('AC-1 is missing criterion-backed command evidence');
969
+ process.exit(1);
970
+ }
971
+ NODE
972
+ package_flow evidence --session-dir "$PACKAGE_SESSION" --expectation merge-readiness --status pass \
973
+ --summary "Packed fixture records the declared merge-readiness artifact." \
974
+ --evidence-ref-json "{\"kind\":\"artifact\",\"file\":\"$PACKAGE_SESSION/acme-builder-901--evidence-gate.md\",\"summary\":\"Durable merge-readiness artifact.\"}" >/dev/null \
975
+ && package_flow evidence --session-dir "$PACKAGE_SESSION" --expectation pull-request-opened --status pass \
976
+ --summary "Packed fixture records the declared release artifact." \
977
+ --evidence-ref-json "{\"kind\":\"artifact\",\"file\":\"$PACKAGE_SESSION/release.json\",\"summary\":\"Durable release artifact.\"}" >/dev/null \
978
+ && package_flow evidence --session-dir "$PACKAGE_SESSION" --expectation ci-merge-readiness --status pass \
979
+ --summary "Packed fixture records the declared CI readiness artifact." \
980
+ --evidence-ref-json "{\"kind\":\"artifact\",\"file\":\"$PACKAGE_SESSION/release.json\",\"summary\":\"Durable CI readiness artifact.\"}" >/dev/null \
981
+ && package_flow evidence --session-dir "$PACKAGE_SESSION" --expectation decision-evidence --status pass \
982
+ --summary "Packed fixture records the declared learning decision artifact." \
983
+ --evidence-ref-json "{\"kind\":\"artifact\",\"file\":\"$PACKAGE_SESSION/learning.json\",\"summary\":\"Durable learning decision artifact.\"}" >/dev/null \
984
+ && package_flow evidence --session-dir "$PACKAGE_SESSION" --expectation learning-evidence --status pass \
985
+ --summary "Packed fixture records the declared learning evidence artifact." \
986
+ --evidence-ref-json "{\"kind\":\"artifact\",\"file\":\"$PACKAGE_SESSION/learning.json\",\"summary\":\"Durable learning evidence artifact.\"}" >/dev/null \
987
+ && node - "$PACKAGE_SESSION" <<'NODE' &&
988
+ const fs = require('node:fs');
989
+ const path = require('node:path');
990
+ const session = process.argv[2];
991
+ const bundle = JSON.parse(fs.readFileSync(path.join(session, 'trust.bundle'), 'utf8'));
992
+ const types = new Set((bundle.claims || []).filter((claim) => claim.status === 'verified').map((claim) => claim.claimType));
993
+ const requiredTypes = [
994
+ 'builder.pull-work.selected',
995
+ 'builder.design-probe.pickup-readiness',
996
+ 'builder.design-probe.decisions',
997
+ 'builder.plan.implementation',
998
+ 'builder.execute.scope',
999
+ 'builder.verify.tests',
1000
+ 'builder.merge-ready.readiness',
1001
+ 'builder.learn.decisions',
1002
+ 'builder.learn.evidence',
1003
+ ];
1004
+ const missingTypes = requiredTypes.filter((type) => !types.has(type));
1005
+ if (missingTypes.length > 0) {
1006
+ console.error(`missing verified public workflow claims: ${missingTypes.join(', ')}`);
1007
+ process.exit(1);
1008
+ }
1009
+ NODE
1010
+ printf 'Selected Work Item: acme/builder#902\n' > "$PACKAGE_LIFECYCLE_SESSION/acme-builder-902--pull-work.md" \
1011
+ && package_flow start --artifact-root "$PACKAGE_PROJECT/.kontourai/flow-agents" \
1012
+ --flow builder.build --work-item acme/builder#902 --assignment-provider local-file --summary "Packed public lifecycle fixture." >/dev/null \
1013
+ && node --input-type=module - "$PACKAGE_PROJECT" "$PACKAGE_LIFECYCLE_SESSION" "$PACKAGE_CONSUMER" <<'NODE' &&
923
1014
  import fs from 'node:fs';
924
1015
  import path from 'node:path';
925
1016
  import { generateKeyPairSync, sign } from 'node:crypto';
926
1017
  import { pathToFileURL } from 'node:url';
927
- const [project, consumer] = process.argv.slice(2);
928
- const slug = 'packed-builder-entry';
1018
+ const [project, session, consumer] = process.argv.slice(2);
1019
+ const slug = path.basename(session);
929
1020
  const assignment = JSON.parse(fs.readFileSync(path.join(project, '.kontourai', 'flow-agents', 'assignment', `${slug}.json`), 'utf8'));
930
1021
  const state = JSON.parse(fs.readFileSync(path.join(project, '.kontourai', 'flow-agents', slug, 'state.json'), 'utf8'));
931
1022
  const packageEntry = path.join(consumer, 'node_modules', '@kontourai', 'flow-agents', 'build', 'src', 'index.js');
@@ -957,15 +1048,15 @@ for (const operation of ['cancel', 'archive']) {
957
1048
  fs.writeFileSync(path.join(project, `${operation}.authorization.json`), JSON.stringify(authorization, null, 2));
958
1049
  }
959
1050
  NODE
960
- (cd "$PACKAGE_PROJECT" && CODEX_SESSION_ID=packed-package-consumer node "$PACKAGE_CLI" builder-run pause --session-dir "$PACKAGE_SESSION" --reason "packed pause" >/dev/null) \
961
- && (cd "$PACKAGE_PROJECT" && CODEX_SESSION_ID=packed-package-consumer node "$PACKAGE_CLI" builder-run resume --session-dir "$PACKAGE_SESSION" --reason "packed resume" >/dev/null) \
962
- && (cd "$PACKAGE_PROJECT" && node "$PACKAGE_CLI" builder-run cancel --session-dir "$PACKAGE_SESSION" --authorization-file "$PACKAGE_PROJECT/cancel.authorization.json" >/dev/null) \
963
- && (cd "$PACKAGE_PROJECT" && node "$PACKAGE_CLI" builder-run archive --session-dir "$PACKAGE_SESSION" --authorization-file "$PACKAGE_PROJECT/archive.authorization.json" >/dev/null) \
1051
+ package_flow pause --session-dir "$PACKAGE_LIFECYCLE_SESSION" --reason "packed pause" >/dev/null \
1052
+ && package_flow resume --session-dir "$PACKAGE_LIFECYCLE_SESSION" --reason "packed resume" >/dev/null \
1053
+ && package_flow cancel --session-dir "$PACKAGE_LIFECYCLE_SESSION" --authorization-file "$PACKAGE_PROJECT/cancel.authorization.json" >/dev/null \
1054
+ && package_flow archive --session-dir "$PACKAGE_LIFECYCLE_SESSION" --authorization-file "$PACKAGE_PROJECT/archive.authorization.json" >/dev/null \
964
1055
  && node - "$PACKAGE_PROJECT" <<'NODE'
965
1056
  const fs = require('node:fs');
966
1057
  const path = require('node:path');
967
1058
  const project = process.argv[2];
968
- const slug = 'packed-builder-entry';
1059
+ const slug = 'acme-builder-902';
969
1060
  const archived = JSON.parse(fs.readFileSync(path.join(project, '.kontourai', 'flow-agents', 'archive', slug, 'state.json'), 'utf8'));
970
1061
  const flow = JSON.parse(fs.readFileSync(path.join(project, '.kontourai', 'flow', 'runs', slug, 'state.json'), 'utf8'));
971
1062
  const consumed = fs.readdirSync(path.join(project, '.kontourai', 'flow-agents', 'lifecycle-authority', 'consumed'));
@@ -973,13 +1064,42 @@ if (archived.status !== 'archived' || flow.status !== 'canceled' || consumed.len
973
1064
  if (fs.existsSync(path.join(project, '.kontourai', 'flow-agents', slug))) process.exit(1);
974
1065
  NODE
975
1066
  then
976
- _pass "packed npm consumer projects Builder entry and executes lifecycle commands"
1067
+ _pass "packed npm consumer follows the public Builder workflow contract and lifecycle commands"
977
1068
  else
978
- _fail "packed npm consumer did not execute canonical Builder entry and lifecycle commands"
1069
+ _fail "packed npm consumer did not complete the public Builder workflow contract and lifecycle commands"
979
1070
  fi
980
1071
 
981
- if [[ -d "$CODEX_FULL_DEST/.codex/skills/plan-work" && -d "$CODEX_FULL_DEST/.codex/skills/deliver" && -d "$CODEX_FULL_DEST/.codex/skills/agentic-engineering" ]]; then
982
- _pass "Codex full install ships kit-skills and standalone skills together"
1072
+ if node - "$CODEX_FULL_DEST/.codex/skills" "$CODEX_FULL_DEST" <<'NODE'
1073
+ const fs = require('node:fs');
1074
+ const path = require('node:path');
1075
+ const [root, installRoot] = process.argv.slice(2);
1076
+ const builder = [
1077
+ 'builder-shape', 'continue-work', 'deliver', 'design-probe', 'evidence-gate',
1078
+ 'execute-plan', 'fix-bug', 'gate-review', 'idea-to-backlog', 'learning-review',
1079
+ 'pickup-probe', 'plan-work', 'pull-work', 'release-readiness', 'review-work',
1080
+ 'tdd-workflow', 'verify-work',
1081
+ ];
1082
+ for (const skill of [...builder, 'agentic-engineering']) {
1083
+ const file = path.join(root, skill, 'SKILL.md');
1084
+ if (!fs.existsSync(file)) throw new Error(`missing installed skill ${skill}`);
1085
+ if (builder.includes(skill)) {
1086
+ const text = fs.readFileSync(file, 'utf8');
1087
+ if (/npm run workflow:sidecar|flow-agents-workflow-sidecar|flow-agents builder-run/.test(text)) {
1088
+ throw new Error(`${skill} exposes a private workflow command`);
1089
+ }
1090
+ if (/--reviewer/.test(text)) {
1091
+ throw new Error(`${skill} exposes a caller-selected review identity`);
1092
+ }
1093
+ if (skill === 'verify-work' && (!/--criterion-json/.test(text) || /--command\s+"(?:true|node --version)"/.test(text))) {
1094
+ throw new Error('verify-work must ship criterion-backed substantive verification evidence guidance');
1095
+ }
1096
+ const refs = [...text.matchAll(/\b(?:context|docs|kits)\/[A-Za-z0-9_./-]+\.(?:md|json)\b/g)].map((match) => match[0]);
1097
+ for (const ref of refs) if (!fs.existsSync(path.join(installRoot, ref))) throw new Error(`${skill} has unresolved installed resource ${ref}`);
1098
+ }
1099
+ }
1100
+ NODE
1101
+ then
1102
+ _pass "Codex full install ships all 17 coherent Builder skills and standalone skills"
983
1103
  else
984
1104
  _fail "Codex full install is missing skills"
985
1105
  fi
@@ -997,8 +1117,37 @@ else
997
1117
  _fail "opencode full install is missing base agents"
998
1118
  fi
999
1119
 
1000
- if [[ -d "$OPENCODE_FULL_DEST/.opencode/skills/plan-work" && -d "$OPENCODE_FULL_DEST/.opencode/skills/deliver" && -d "$OPENCODE_FULL_DEST/.opencode/skills/agentic-engineering" ]]; then
1001
- _pass "opencode full install ships kit-skills and standalone skills together"
1120
+ if node - "$OPENCODE_FULL_DEST/.opencode/skills" "$OPENCODE_FULL_DEST" <<'NODE'
1121
+ const fs = require('node:fs');
1122
+ const path = require('node:path');
1123
+ const [root, installRoot] = process.argv.slice(2);
1124
+ const builder = [
1125
+ 'builder-shape', 'continue-work', 'deliver', 'design-probe', 'evidence-gate',
1126
+ 'execute-plan', 'fix-bug', 'gate-review', 'idea-to-backlog', 'learning-review',
1127
+ 'pickup-probe', 'plan-work', 'pull-work', 'release-readiness', 'review-work',
1128
+ 'tdd-workflow', 'verify-work',
1129
+ ];
1130
+ for (const skill of [...builder, 'agentic-engineering']) {
1131
+ const file = path.join(root, skill, 'SKILL.md');
1132
+ if (!fs.existsSync(file)) throw new Error(`missing installed skill ${skill}`);
1133
+ if (builder.includes(skill)) {
1134
+ const text = fs.readFileSync(file, 'utf8');
1135
+ if (/npm run workflow:sidecar|flow-agents-workflow-sidecar|flow-agents builder-run/.test(text)) {
1136
+ throw new Error(`${skill} exposes a private workflow command`);
1137
+ }
1138
+ if (/--reviewer/.test(text)) {
1139
+ throw new Error(`${skill} exposes a caller-selected review identity`);
1140
+ }
1141
+ if (skill === 'verify-work' && (!/--criterion-json/.test(text) || /--command\s+"(?:true|node --version)"/.test(text))) {
1142
+ throw new Error('verify-work must ship criterion-backed substantive verification evidence guidance');
1143
+ }
1144
+ const refs = [...text.matchAll(/\b(?:context|docs|kits)\/[A-Za-z0-9_./-]+\.(?:md|json)\b/g)].map((match) => match[0]);
1145
+ for (const ref of refs) if (!fs.existsSync(path.join(installRoot, ref))) throw new Error(`${skill} has unresolved installed resource ${ref}`);
1146
+ }
1147
+ }
1148
+ NODE
1149
+ then
1150
+ _pass "opencode full install ships all 17 coherent Builder skills and standalone skills"
1002
1151
  else
1003
1152
  _fail "opencode full install is missing skills"
1004
1153
  fi
@@ -48,15 +48,20 @@ seed() {
48
48
  # ─── (a) same-reviewer supersession: fail → pass ──────────────────────────────
49
49
  echo ""
50
50
  echo "=== (a) record-critique fail→pass (same reviewer) supersedes, history intact, reconcile 0 ==="
51
- A_AROOT="$TMP/a/aroot"; A_SLUG="supersede-same"; A_DIR="$A_AROOT/$A_SLUG"
51
+ A_AROOT="$TMP/a/.kontourai/flow-agents"; A_SLUG="supersede-same"; A_DIR="$A_AROOT/$A_SLUG"
52
52
  seed "$A_AROOT" "$A_SLUG"
53
53
  flow_agents_node "$WRITER" record-evidence "$A_DIR" --verdict pass \
54
54
  --check-json '{"id":"c1","kind":"diff","status":"pass","summary":"diff check"}' \
55
55
  --timestamp "2026-07-01T00:01:00Z" >/dev/null 2>&1
56
56
  flow_agents_node "$WRITER" record-critique "$A_DIR" --id rv --reviewer alice --verdict fail \
57
- --summary "found a bug" --timestamp "2026-07-01T00:02:00Z" >/dev/null 2>&1
57
+ --summary "found a bug" \
58
+ --lane-json "{\"id\":\"code\",\"status\":\"fail\",\"summary\":\"bug found\",\"evidence_refs\":[{\"kind\":\"artifact\",\"file\":\"$A_DIR/$A_SLUG--deliver.md\",\"summary\":\"Reviewed the failing delivery artifact.\"}]}" \
59
+ --timestamp "2026-07-01T00:02:00Z" >/dev/null 2>&1
58
60
  flow_agents_node "$WRITER" record-critique "$A_DIR" --id rv --reviewer alice --verdict pass \
59
- --summary "bug fixed" --timestamp "2026-07-01T00:03:00Z" >/dev/null 2>&1
61
+ --summary "bug fixed" \
62
+ --lane-json "{\"id\":\"code\",\"status\":\"pass\",\"summary\":\"fix verified\",\"evidence_refs\":[{\"kind\":\"artifact\",\"file\":\"$A_DIR/$A_SLUG--deliver.md\",\"summary\":\"Reviewed the corrected delivery artifact.\"}]}" \
63
+ --artifact-ref "$A_DIR/$A_SLUG--deliver.md" \
64
+ --timestamp "2026-07-01T00:03:00Z" >/dev/null 2>&1
60
65
 
61
66
  node - "$A_DIR/trust.bundle" << 'NODE'
62
67
  const fs = require('fs');
@@ -80,7 +85,7 @@ if [[ $? -eq 0 ]]; then _pass "(a) trust-reconcile exits 0 (resolved session con
80
85
  # ─── (b) HEADLINE: --flow-id session converges ────────────────────────────────
81
86
  echo ""
82
87
  echo "=== (b) HEADLINE --flow-id builder.build/verify: mixed evidence + critique → reconcile 0 ==="
83
- B_AROOT="$TMP/b/aroot"; B_SLUG="flowid-converge"; B_DIR="$B_AROOT/$B_SLUG"
88
+ B_AROOT="$TMP/b/.kontourai/flow-agents"; B_SLUG="flowid-converge"; B_DIR="$B_AROOT/$B_SLUG"
84
89
  seed "$B_AROOT" "$B_SLUG" --flow-id builder.build
85
90
  flow_agents_node "$WRITER" advance-state "$B_DIR" --status in_progress --phase verification \
86
91
  --summary "Testing at verify." --next-action "Record evidence." \
@@ -91,7 +96,10 @@ flow_agents_node "$WRITER" record-evidence "$B_DIR" --verdict pass \
91
96
  --check-json '{"id":"k-ext","kind":"external","status":"pass","summary":"attested"}' \
92
97
  --timestamp "2026-07-01T00:01:00Z" >/dev/null 2>&1
93
98
  flow_agents_node "$WRITER" record-critique "$B_DIR" --id code-review --reviewer alice --verdict pass \
94
- --summary "looks good" --timestamp "2026-07-01T00:02:00Z" >/dev/null 2>&1
99
+ --summary "looks good" \
100
+ --lane-json "{\"id\":\"code\",\"status\":\"pass\",\"summary\":\"review passed\",\"evidence_refs\":[{\"kind\":\"artifact\",\"file\":\"$B_DIR/$B_SLUG--deliver.md\",\"summary\":\"Reviewed the delivery artifact.\"}]}" \
101
+ --artifact-ref "$B_DIR/$B_SLUG--deliver.md" \
102
+ --timestamp "2026-07-01T00:02:00Z" >/dev/null 2>&1
95
103
 
96
104
  node - "$B_DIR/trust.bundle" << 'NODE'
97
105
  const fs = require('fs');
@@ -132,15 +140,20 @@ if [[ $? -eq 0 ]]; then _pass "(c) critique history survives a later record-evid
132
140
  # ─── (d) ANTI-GAMING: cross-reviewer cannot supersede a reviewer fail ─────────
133
141
  echo ""
134
142
  echo "=== (d) ANTI-GAMING: a different reviewer cannot supersede a reviewer's fail ==="
135
- D_AROOT="$TMP/d/aroot"; D_SLUG="supersede-cross"; D_DIR="$D_AROOT/$D_SLUG"
143
+ D_AROOT="$TMP/d/.kontourai/flow-agents"; D_SLUG="supersede-cross"; D_DIR="$D_AROOT/$D_SLUG"
136
144
  seed "$D_AROOT" "$D_SLUG"
137
145
  flow_agents_node "$WRITER" record-evidence "$D_DIR" --verdict pass \
138
146
  --check-json '{"id":"c1","kind":"diff","status":"pass","summary":"diff check"}' \
139
147
  --timestamp "2026-07-01T00:01:00Z" >/dev/null 2>&1
140
148
  flow_agents_node "$WRITER" record-critique "$D_DIR" --id rv --reviewer reviewer-bob --verdict fail \
141
- --summary "reviewer found a real bug" --timestamp "2026-07-01T00:02:00Z" >/dev/null 2>&1
149
+ --summary "reviewer found a real bug" \
150
+ --lane-json "{\"id\":\"code\",\"status\":\"fail\",\"summary\":\"bug found\",\"evidence_refs\":[{\"kind\":\"artifact\",\"file\":\"$D_DIR/$D_SLUG--deliver.md\",\"summary\":\"Reviewed the failing delivery artifact.\"}]}" \
151
+ --timestamp "2026-07-01T00:02:00Z" >/dev/null 2>&1
142
152
  flow_agents_node "$WRITER" record-critique "$D_DIR" --id rv --reviewer worker-mallory --verdict pass \
143
- --summary "worker claims fixed" --timestamp "2026-07-01T00:03:00Z" >/dev/null 2>&1
153
+ --summary "worker claims fixed" \
154
+ --lane-json "{\"id\":\"code\",\"status\":\"pass\",\"summary\":\"worker claims review passed\",\"evidence_refs\":[{\"kind\":\"artifact\",\"file\":\"$D_DIR/$D_SLUG--deliver.md\",\"summary\":\"Reviewed the claimed correction.\"}]}" \
155
+ --artifact-ref "$D_DIR/$D_SLUG--deliver.md" \
156
+ --timestamp "2026-07-01T00:03:00Z" >/dev/null 2>&1
144
157
  node - "$D_DIR/trust.bundle" << 'NODE'
145
158
  const fs = require('fs');
146
159
  const b = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
@@ -371,6 +371,7 @@ set -e
371
371
  echo "--- 5. record-gate-claim resolves A's own per-actor flow/step, not B's legacy pointer (AC11) ---"
372
372
 
373
373
  AC11_ROOT="$TMPDIR_EVAL/ac11-project/.kontourai/flow-agents"
374
+ AC11_WORK_ITEM="local:gate-actor-a"
374
375
 
375
376
  flow_agents_node "workflow-sidecar" ensure-session \
376
377
  --artifact-root "$AC11_ROOT" \
@@ -388,6 +389,16 @@ flow_agents_node "workflow-sidecar" init-plan "$AC11_DELIVER_MD" \
388
389
  --source-request "Test" --summary "Testing" \
389
390
  --timestamp "2026-07-01T00:00:30Z" >"$TMPDIR_EVAL/ac11-a-initplan.out" 2>"$TMPDIR_EVAL/ac11-a-initplan.err"
390
391
 
392
+ # A passing selected-work claim must cite pull-work's declared durable artifact. Keep it in A's
393
+ # session so the generic producer/evidence contract is exercised without weakening AC11's
394
+ # per-actor pointer differential.
395
+ AC11_PULL_WORK_ARTIFACT="$AC11_ROOT/gate-actor-a/gate-actor-a--pull-work.md"
396
+ safe_write "$AC11_PULL_WORK_ARTIFACT" <<EOF
397
+ # Pull Work
398
+
399
+ Selected Work Item: $AC11_WORK_ITEM
400
+ EOF
401
+
391
402
  # Actor B runs a LATER, plain (non-FlowDefinition) ensure-session on an unrelated subject on the
392
403
  # SAME artifact root -- this overwrites the LEGACY global current.json (last-writer-wins, no
393
404
  # active_flow_id/active_step_id at all) while leaving actor A's own per-actor current file
@@ -416,6 +427,7 @@ if flow_agents_node "workflow-sidecar" record-gate-claim "$AC11_GATE_ACTOR_A_DIR
416
427
  --status pass \
417
428
  --summary "Selected issue #291 for implementation." \
418
429
  --expectation selected-work \
430
+ --evidence-ref-json "{\"kind\":\"artifact\",\"file\":\"$AC11_PULL_WORK_ARTIFACT\",\"summary\":\"Declared pull-work artifact naming bound Work Item $AC11_WORK_ITEM.\"}" \
419
431
  --timestamp "2026-07-01T00:02:00Z" \
420
432
  >"$TMPDIR_EVAL/ac11-gate-claim.out" 2>"$TMPDIR_EVAL/ac11-gate-claim.err"; then
421
433
  pass "record-gate-claim (actor A) succeeds by resolving A's OWN per-actor current pointer, not B's stale legacy pointer (AC11)"