@kontourai/flow-agents 3.6.0 → 3.8.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.
- package/CHANGELOG.md +19 -0
- package/build/src/builder-flow-run-adapter.d.ts +22 -2
- package/build/src/builder-flow-run-adapter.js +93 -28
- package/build/src/builder-flow-runtime.d.ts +8 -3
- package/build/src/builder-flow-runtime.js +407 -51
- package/build/src/cli/assignment-provider.d.ts +4 -7
- package/build/src/cli/assignment-provider.js +80 -14
- package/build/src/cli/builder-run.js +14 -18
- package/build/src/cli/workflow-sidecar.d.ts +28 -4
- package/build/src/cli/workflow-sidecar.js +825 -103
- package/build/src/cli/workflow.js +301 -43
- package/build/src/flow-kit/validate.d.ts +17 -0
- package/build/src/flow-kit/validate.js +340 -2
- package/build/src/index.js +5 -5
- package/build/src/lib/observed-command.d.ts +7 -0
- package/build/src/lib/observed-command.js +100 -0
- package/build/src/tools/build-universal-bundles.js +53 -3
- package/build/src/tools/generate-context-map.js +5 -3
- package/context/scripts/hooks/lib/kit-catalog.js +1 -1
- package/context/scripts/hooks/stop-goal-fit.js +78 -9
- package/docs/context-map.md +22 -20
- package/docs/developer-architecture.md +1 -1
- package/docs/getting-started.md +9 -1
- package/docs/public-workflow-cli.md +76 -7
- package/docs/skills-map.md +51 -27
- package/docs/spec/builder-flow-runtime.md +75 -40
- package/docs/workflow-usage-guide.md +109 -42
- package/evals/fixtures/hook-influence/cases.json +2 -2
- package/evals/integration/test_builder_entry_enforcement.sh +52 -41
- package/evals/integration/test_builder_step_producers.sh +330 -6
- package/evals/integration/test_bundle_install.sh +318 -65
- package/evals/integration/test_bundle_lifecycle.sh +4 -6
- package/evals/integration/test_critique_supersession_roundtrip.sh +21 -8
- package/evals/integration/test_current_json_per_actor.sh +12 -0
- package/evals/integration/test_dual_emit_flow_step.sh +62 -43
- package/evals/integration/test_flowdef_session_activation.sh +145 -25
- package/evals/integration/test_flowdef_session_history_preservation.sh +23 -21
- package/evals/integration/test_gate_lockdown.sh +3 -5
- package/evals/integration/test_goal_fit_hook.sh +60 -2
- package/evals/integration/test_install_merge.sh +18 -8
- package/evals/integration/test_liveness_verdict.sh +14 -17
- package/evals/integration/test_phase_map_and_gate_claim.sh +63 -38
- package/evals/integration/test_public_workflow_cli.sh +334 -14
- package/evals/integration/test_pull_work_liveness_preflight.sh +22 -66
- package/evals/integration/test_sidecar_field_preservation.sh +36 -11
- package/evals/integration/test_workflow_sidecar_writer.sh +277 -44
- package/evals/integration/test_workflow_steering_hook.sh +15 -38
- package/evals/run.sh +2 -0
- package/evals/static/test_builder_skill_coherence.sh +247 -0
- package/evals/static/test_library_exports.sh +5 -2
- package/evals/static/test_universal_bundles.sh +44 -1
- package/evals/static/test_workflow_skills.sh +13 -325
- package/kits/builder/flows/build.flow.json +22 -0
- package/kits/builder/flows/shape.flow.json +9 -9
- package/kits/builder/kit.json +70 -16
- package/kits/builder/skills/builder-shape/SKILL.md +75 -75
- package/kits/builder/skills/continue-work/SKILL.md +45 -106
- package/kits/builder/skills/deliver/SKILL.md +96 -442
- package/kits/builder/skills/design-probe/SKILL.md +40 -139
- package/kits/builder/skills/evidence-gate/SKILL.md +59 -201
- package/kits/builder/skills/execute-plan/SKILL.md +54 -125
- package/kits/builder/skills/fix-bug/SKILL.md +42 -132
- package/kits/builder/skills/gate-review/SKILL.md +60 -211
- package/kits/builder/skills/idea-to-backlog/SKILL.md +63 -244
- package/kits/builder/skills/learning-review/SKILL.md +63 -170
- package/kits/builder/skills/pickup-probe/SKILL.md +54 -111
- package/kits/builder/skills/plan-work/SKILL.md +51 -185
- package/kits/builder/skills/pull-work/SKILL.md +136 -485
- package/kits/builder/skills/release-readiness/SKILL.md +66 -107
- package/kits/builder/skills/review-work/SKILL.md +89 -176
- package/kits/builder/skills/tdd-workflow/SKILL.md +53 -147
- package/kits/builder/skills/verify-work/SKILL.md +101 -113
- package/package.json +2 -2
- package/packaging/README.md +1 -1
- package/scripts/README.md +1 -1
- package/scripts/hooks/lib/kit-catalog.js +1 -1
- package/scripts/hooks/stop-goal-fit.js +78 -9
- package/scripts/install-codex-home.sh +63 -23
- package/scripts/install-owned-files.js +18 -2
- package/src/builder-flow-run-adapter.ts +118 -32
- package/src/builder-flow-runtime.ts +426 -64
- package/src/cli/assignment-provider-lock.test.mjs +83 -0
- package/src/cli/assignment-provider.ts +75 -14
- package/src/cli/builder-flow-run-adapter.test.mjs +4 -2
- package/src/cli/builder-flow-runtime.test.mjs +436 -18
- package/src/cli/builder-run.ts +3 -9
- package/src/cli/kit-metadata-security.test.mjs +232 -6
- package/src/cli/public-api.test.mjs +15 -0
- package/src/cli/workflow-sidecar-execution-proof.test.mjs +90 -0
- package/src/cli/workflow-sidecar.ts +746 -103
- package/src/cli/workflow.ts +288 -41
- package/src/flow-kit/validate.ts +320 -2
- package/src/index.ts +6 -5
- package/src/lib/observed-command.ts +96 -0
- package/src/tools/build-universal-bundles.ts +51 -3
- package/src/tools/generate-context-map.ts +5 -3
|
@@ -285,7 +285,7 @@ else
|
|
|
285
285
|
_fail "pi install failed"
|
|
286
286
|
fi
|
|
287
287
|
|
|
288
|
-
USER_SKILLS_DIR="$CODEX_FULL_DEST/.
|
|
288
|
+
USER_SKILLS_DIR="$CODEX_FULL_DEST/.agents/sk""ills/user-skill"
|
|
289
289
|
mkdir -p "$CODEX_FULL_DEST/.codex/ag""ents" "$USER_SKILLS_DIR"
|
|
290
290
|
printf 'name = "user-agent"\n' > "$CODEX_FULL_DEST/.codex/ag""ents/user-agent.toml"
|
|
291
291
|
printf '# user skill\n' > "$USER_SKILLS_DIR/SKILL.md"
|
|
@@ -309,7 +309,7 @@ for dir in \
|
|
|
309
309
|
"$CLAUDE_DEST/.flow-agents" \
|
|
310
310
|
"$CLAUDE_DEST/.kontourai/flow-agents" \
|
|
311
311
|
"$CODEX_DEST/.codex/agents" \
|
|
312
|
-
"$CODEX_DEST/.
|
|
312
|
+
"$CODEX_DEST/.agents/skills" \
|
|
313
313
|
"$CODEX_DEST/.flow-agents" \
|
|
314
314
|
"$CODEX_DEST/.kontourai/flow-agents" \
|
|
315
315
|
"$CODEX_FULL_DEST/.flow-agents" \
|
|
@@ -653,26 +653,12 @@ const state = {
|
|
|
653
653
|
target_phase: "goal_fit",
|
|
654
654
|
},
|
|
655
655
|
};
|
|
656
|
-
const
|
|
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, "
|
|
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 (
|
|
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
|
|
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, "
|
|
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 (
|
|
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
|
|
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, "
|
|
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 (
|
|
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/
|
|
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
|
-
&&
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
--
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
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', '
|
|
916
|
-
const flow = JSON.parse(fs.readFileSync(path.join(project, '.kontourai', 'flow', 'runs', '
|
|
917
|
-
const bundle = JSON.parse(fs.readFileSync(path.join(project, '.kontourai', 'flow-agents', '
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
961
|
-
&&
|
|
962
|
-
&&
|
|
963
|
-
&&
|
|
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 = '
|
|
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,47 @@ 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
|
|
1067
|
+
_pass "packed npm consumer follows the public Builder workflow contract and lifecycle commands"
|
|
977
1068
|
else
|
|
978
|
-
_fail "packed npm consumer did not
|
|
1069
|
+
_fail "packed npm consumer did not complete the public Builder workflow contract and lifecycle commands"
|
|
979
1070
|
fi
|
|
980
1071
|
|
|
981
|
-
if
|
|
982
|
-
|
|
1072
|
+
if node - "$CODEX_FULL_DEST/.agents/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) {
|
|
1098
|
+
const skillLocal = path.join(path.dirname(file), ref);
|
|
1099
|
+
if (!fs.existsSync(skillLocal) && !fs.existsSync(path.join(installRoot, ref))) {
|
|
1100
|
+
throw new Error(`${skill} has unresolved installed resource ${ref}`);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
NODE
|
|
1106
|
+
then
|
|
1107
|
+
_pass "Codex full install ships all 17 coherent Builder skills and standalone skills"
|
|
983
1108
|
else
|
|
984
1109
|
_fail "Codex full install is missing skills"
|
|
985
1110
|
fi
|
|
@@ -990,6 +1115,105 @@ else
|
|
|
990
1115
|
_fail "Codex full install removed unknown user files"
|
|
991
1116
|
fi
|
|
992
1117
|
|
|
1118
|
+
echo ""
|
|
1119
|
+
echo "--- Dedicated Codex Home Universal Skills ---"
|
|
1120
|
+
GLOBAL_HOME="$TMPDIR_EVAL/global-home"
|
|
1121
|
+
GLOBAL_CODEX="$TMPDIR_EVAL/global-codex"
|
|
1122
|
+
GLOBAL_SKILLS="$TMPDIR_EVAL/global-agents/skills"
|
|
1123
|
+
mkdir -p "$GLOBAL_SKILLS/user-skill"
|
|
1124
|
+
printf '# user skill\n' > "$GLOBAL_SKILLS/user-skill/SKILL.md"
|
|
1125
|
+
GLOBAL_OUTPUT="$TMPDIR_EVAL/global-install.out"
|
|
1126
|
+
if HOME="$GLOBAL_HOME" CODEX_REAL_HOME="$GLOBAL_CODEX" bash "$ROOT_DIR/scripts/install-codex-home.sh" "$GLOBAL_CODEX" --skills-dir "$GLOBAL_SKILLS" >"$GLOBAL_OUTPUT"; then
|
|
1127
|
+
_pass "dedicated Codex installer accepts independent hermetic runtime and skill roots"
|
|
1128
|
+
else
|
|
1129
|
+
_fail "dedicated Codex installer failed with independent hermetic roots"
|
|
1130
|
+
fi
|
|
1131
|
+
if [[ -f "$GLOBAL_SKILLS/plan-work/SKILL.md" && -f "$GLOBAL_SKILLS/plan-work/context/contracts/planning-contract.md" && -f "$GLOBAL_SKILLS/user-skill/SKILL.md" && ! -e "$GLOBAL_CODEX/skills/plan-work" ]]; then
|
|
1132
|
+
_pass "global install uses self-contained universal skills and preserves user skills"
|
|
1133
|
+
else
|
|
1134
|
+
_fail "global universal skill layout, resource closure, or user preservation is wrong"
|
|
1135
|
+
fi
|
|
1136
|
+
if rg -q 'Installed Flow Agents into Codex home at .+/global-codex$' "$GLOBAL_OUTPUT" && rg -q 'Installed portable skills at .+/global-agents/skills$' "$GLOBAL_OUTPUT"; then
|
|
1137
|
+
_pass "installer reports both runtime and portable skill destinations"
|
|
1138
|
+
else
|
|
1139
|
+
_fail "installer output does not report both destinations"
|
|
1140
|
+
fi
|
|
1141
|
+
if HOME="$GLOBAL_HOME" CODEX_REAL_HOME="$GLOBAL_CODEX" bash "$ROOT_DIR/scripts/install-codex-home.sh" "$GLOBAL_CODEX" --skills-dir "$GLOBAL_SKILLS" >/dev/null; then
|
|
1142
|
+
_pass "split-root reinstall is idempotent"
|
|
1143
|
+
else
|
|
1144
|
+
_fail "split-root reinstall is not idempotent"
|
|
1145
|
+
fi
|
|
1146
|
+
|
|
1147
|
+
CONFLICT_CODEX="$TMPDIR_EVAL/conflict-codex"
|
|
1148
|
+
CONFLICT_SKILLS="$TMPDIR_EVAL/conflict-agents/skills"
|
|
1149
|
+
mkdir -p "$CONFLICT_SKILLS/plan-work" "$CONFLICT_CODEX/skills/plan-work" "$CONFLICT_CODEX/.flow-agents"
|
|
1150
|
+
printf '# user collision\n' > "$CONFLICT_SKILLS/plan-work/SKILL.md"
|
|
1151
|
+
cp "$ROOT_DIR/dist/codex/.agents/skills/plan-work/SKILL.md" "$CONFLICT_CODEX/skills/plan-work/SKILL.md"
|
|
1152
|
+
printf 'runtime sentinel\n' > "$CONFLICT_CODEX/runtime-sentinel"
|
|
1153
|
+
node - "$CONFLICT_CODEX" <<'NODE'
|
|
1154
|
+
const fs=require('node:fs'), path=require('node:path'), crypto=require('node:crypto'); const root=process.argv[2];
|
|
1155
|
+
const rel='skills' + '/plan-work/SKILL.md'; const sha256=crypto.createHash('sha256').update(fs.readFileSync(path.join(root,rel))).digest('hex');
|
|
1156
|
+
fs.writeFileSync(path.join(root,'.flow-agents/codex-install-manifest.json'), JSON.stringify({schema_version:'1.0',files:[{path:rel,sha256}]},null,2)+'\n');
|
|
1157
|
+
NODE
|
|
1158
|
+
CONFLICT_RUNTIME_BEFORE="$(shasum -a 256 "$CONFLICT_CODEX/skills/plan-work/SKILL.md" "$CONFLICT_CODEX/.flow-agents/codex-install-manifest.json" "$CONFLICT_CODEX/runtime-sentinel")"
|
|
1159
|
+
if HOME="$GLOBAL_HOME" CODEX_REAL_HOME="$CONFLICT_CODEX" bash "$ROOT_DIR/scripts/install-codex-home.sh" "$CONFLICT_CODEX" --skills-dir "$CONFLICT_SKILLS" >/dev/null 2>&1; then
|
|
1160
|
+
_fail "global installer overwrote a user-owned skill collision"
|
|
1161
|
+
else
|
|
1162
|
+
CONFLICT_RUNTIME_AFTER="$(shasum -a 256 "$CONFLICT_CODEX/skills/plan-work/SKILL.md" "$CONFLICT_CODEX/.flow-agents/codex-install-manifest.json" "$CONFLICT_CODEX/runtime-sentinel")"
|
|
1163
|
+
if [[ "$(cat "$CONFLICT_SKILLS/plan-work/SKILL.md")" == "# user collision" && "$CONFLICT_RUNTIME_BEFORE" == "$CONFLICT_RUNTIME_AFTER" && ! -e "$CONFLICT_SKILLS/.flow-agents" ]]; then
|
|
1164
|
+
_pass "cross-root collision preflight preserves universal content, legacy skill, runtime sentinel, and manifests"
|
|
1165
|
+
else
|
|
1166
|
+
_fail "cross-root collision failure partially mutated an install root"
|
|
1167
|
+
fi
|
|
1168
|
+
fi
|
|
1169
|
+
|
|
1170
|
+
SYMLINK_TARGET="$TMPDIR_EVAL/symlink-target"
|
|
1171
|
+
SYMLINK_PARENT="$TMPDIR_EVAL/symlink-parent"
|
|
1172
|
+
mkdir -p "$SYMLINK_TARGET" "$SYMLINK_PARENT"
|
|
1173
|
+
ln -s "$SYMLINK_TARGET" "$SYMLINK_PARENT/catalog"
|
|
1174
|
+
if HOME="$GLOBAL_HOME" CODEX_REAL_HOME="$TMPDIR_EVAL/symlink-codex" bash "$ROOT_DIR/scripts/install-codex-home.sh" "$TMPDIR_EVAL/symlink-codex" --skills-dir "$SYMLINK_PARENT/catalog/skills" >/dev/null 2>&1; then
|
|
1175
|
+
_fail "global installer followed a symlinked skill destination component"
|
|
1176
|
+
else
|
|
1177
|
+
[[ ! -e "$SYMLINK_TARGET/skills" ]] && _pass "global installer refuses symlinked skill destination components without mutation" || _fail "symlink rejection mutated its target"
|
|
1178
|
+
fi
|
|
1179
|
+
|
|
1180
|
+
SOURCE_ANCESTOR="$(dirname "$ROOT_DIR")"
|
|
1181
|
+
SOURCE_DESCENDANT="$ROOT_DIR/.fa-550-forbidden-skills"
|
|
1182
|
+
for unsafe in / "$SOURCE_ANCESTOR" "$SOURCE_DESCENDANT"; do
|
|
1183
|
+
rm -rf "$SOURCE_DESCENDANT"
|
|
1184
|
+
if HOME="$GLOBAL_HOME" CODEX_REAL_HOME="$TMPDIR_EVAL/unsafe-codex" bash "$ROOT_DIR/scripts/install-codex-home.sh" "$TMPDIR_EVAL/unsafe-codex" --skills-dir "$unsafe" >/dev/null 2>&1; then
|
|
1185
|
+
_fail "global installer accepted unsafe skill namespace root: $unsafe"
|
|
1186
|
+
elif [[ ! -e "$SOURCE_DESCENDANT" && ! -e "$TMPDIR_EVAL/unsafe-codex" ]]; then
|
|
1187
|
+
_pass "unsafe root/overlap rejection occurs before destination mutation: $unsafe"
|
|
1188
|
+
else
|
|
1189
|
+
_fail "unsafe root/overlap rejection left destination mutations: $unsafe"
|
|
1190
|
+
fi
|
|
1191
|
+
done
|
|
1192
|
+
|
|
1193
|
+
MIGRATE_CODEX="$TMPDIR_EVAL/migrate-codex"
|
|
1194
|
+
MIGRATE_SKILLS="$TMPDIR_EVAL/migrate-agents/skills"
|
|
1195
|
+
LEGACY_SKILLS_SEGMENT="skills"
|
|
1196
|
+
LEGACY_USER_DIR="$MIGRATE_CODEX/$LEGACY_SKILLS_SEGMENT/user-legacy"
|
|
1197
|
+
mkdir -p "$MIGRATE_CODEX/skills/plan-work" "$LEGACY_USER_DIR" "$MIGRATE_CODEX/.flow-agents"
|
|
1198
|
+
cp "$ROOT_DIR/dist/codex/.agents/skills/plan-work/SKILL.md" "$MIGRATE_CODEX/skills/plan-work/SKILL.md"
|
|
1199
|
+
printf '# modified legacy user content\n' > "$LEGACY_USER_DIR/SKILL.md"
|
|
1200
|
+
node - "$MIGRATE_CODEX" <<'NODE'
|
|
1201
|
+
const fs = require('node:fs'); const path = require('node:path'); const crypto = require('node:crypto');
|
|
1202
|
+
const root = process.argv[2];
|
|
1203
|
+
const rels = ['skills' + '/plan-work/SKILL.md', 'skills' + '/user-legacy/SKILL.md'];
|
|
1204
|
+
const files = rels.map(p => ({path:p, sha256:crypto.createHash('sha256').update(fs.readFileSync(path.join(root,p))).digest('hex')}));
|
|
1205
|
+
fs.writeFileSync(path.join(root,'.flow-agents/codex-install-manifest.json'), JSON.stringify({schema_version:'1.0',files},null,2)+'\n');
|
|
1206
|
+
fs.appendFileSync(path.join(root,'skills' + '/user-legacy/SKILL.md'), 'kept modification\n');
|
|
1207
|
+
NODE
|
|
1208
|
+
if HOME="$GLOBAL_HOME" CODEX_REAL_HOME="$MIGRATE_CODEX" bash "$ROOT_DIR/scripts/install-codex-home.sh" "$MIGRATE_CODEX" --skills-dir "$MIGRATE_SKILLS" >/dev/null 2>"$TMPDIR_EVAL/migrate.err" \
|
|
1209
|
+
&& [[ ! -e "$MIGRATE_CODEX/skills/plan-work/SKILL.md" ]] \
|
|
1210
|
+
&& rg -q 'kept modification' "$LEGACY_USER_DIR/SKILL.md" \
|
|
1211
|
+
&& [[ -f "$MIGRATE_SKILLS/plan-work/SKILL.md" ]]; then
|
|
1212
|
+
_pass "legacy migration removes unchanged owned skills and preserves modified legacy content"
|
|
1213
|
+
else
|
|
1214
|
+
_fail "legacy Flow Agents skill migration was destructive or incomplete"
|
|
1215
|
+
fi
|
|
1216
|
+
|
|
993
1217
|
OPENCODE_AGENTS_DIR="$OPENCODE_FULL_DEST/.opencode/agents"
|
|
994
1218
|
if [[ -f "$OPENCODE_AGENTS_DIR/tool-planner.md" && -f "$OPENCODE_AGENTS_DIR/dev.md" ]]; then
|
|
995
1219
|
_pass "opencode full install ships the complete agent base"
|
|
@@ -997,8 +1221,37 @@ else
|
|
|
997
1221
|
_fail "opencode full install is missing base agents"
|
|
998
1222
|
fi
|
|
999
1223
|
|
|
1000
|
-
if
|
|
1001
|
-
|
|
1224
|
+
if node - "$OPENCODE_FULL_DEST/.opencode/skills" "$OPENCODE_FULL_DEST" <<'NODE'
|
|
1225
|
+
const fs = require('node:fs');
|
|
1226
|
+
const path = require('node:path');
|
|
1227
|
+
const [root, installRoot] = process.argv.slice(2);
|
|
1228
|
+
const builder = [
|
|
1229
|
+
'builder-shape', 'continue-work', 'deliver', 'design-probe', 'evidence-gate',
|
|
1230
|
+
'execute-plan', 'fix-bug', 'gate-review', 'idea-to-backlog', 'learning-review',
|
|
1231
|
+
'pickup-probe', 'plan-work', 'pull-work', 'release-readiness', 'review-work',
|
|
1232
|
+
'tdd-workflow', 'verify-work',
|
|
1233
|
+
];
|
|
1234
|
+
for (const skill of [...builder, 'agentic-engineering']) {
|
|
1235
|
+
const file = path.join(root, skill, 'SKILL.md');
|
|
1236
|
+
if (!fs.existsSync(file)) throw new Error(`missing installed skill ${skill}`);
|
|
1237
|
+
if (builder.includes(skill)) {
|
|
1238
|
+
const text = fs.readFileSync(file, 'utf8');
|
|
1239
|
+
if (/npm run workflow:sidecar|flow-agents-workflow-sidecar|flow-agents builder-run/.test(text)) {
|
|
1240
|
+
throw new Error(`${skill} exposes a private workflow command`);
|
|
1241
|
+
}
|
|
1242
|
+
if (/--reviewer/.test(text)) {
|
|
1243
|
+
throw new Error(`${skill} exposes a caller-selected review identity`);
|
|
1244
|
+
}
|
|
1245
|
+
if (skill === 'verify-work' && (!/--criterion-json/.test(text) || /--command\s+"(?:true|node --version)"/.test(text))) {
|
|
1246
|
+
throw new Error('verify-work must ship criterion-backed substantive verification evidence guidance');
|
|
1247
|
+
}
|
|
1248
|
+
const refs = [...text.matchAll(/\b(?:context|docs|kits)\/[A-Za-z0-9_./-]+\.(?:md|json)\b/g)].map((match) => match[0]);
|
|
1249
|
+
for (const ref of refs) if (!fs.existsSync(path.join(installRoot, ref))) throw new Error(`${skill} has unresolved installed resource ${ref}`);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
NODE
|
|
1253
|
+
then
|
|
1254
|
+
_pass "opencode full install ships all 17 coherent Builder skills and standalone skills"
|
|
1002
1255
|
else
|
|
1003
1256
|
_fail "opencode full install is missing skills"
|
|
1004
1257
|
fi
|
|
@@ -164,7 +164,7 @@ OPENCODE_UPGRADE="$TMPDIR_EVAL/upgrade-opencode"
|
|
|
164
164
|
# Touch a marker into skill files in the COPIES (not dist/ originals)
|
|
165
165
|
UPGRADE_MARKER="# flow-agents-upgrade-test-marker"
|
|
166
166
|
CLAUDE_SKILL_FILE="$CLAUDE_BUNDLE_COPY/.claude/skills/plan-work/SKILL.md"
|
|
167
|
-
CODEX_SKILL_FILE="$CODEX_BUNDLE_COPY/.
|
|
167
|
+
CODEX_SKILL_FILE="$CODEX_BUNDLE_COPY/.agents/skills/plan-work/SKILL.md"
|
|
168
168
|
OPENCODE_SKILL_FILE="$OPENCODE_BUNDLE_COPY/.opencode/skills/plan-work/SKILL.md"
|
|
169
169
|
|
|
170
170
|
if [[ -f "$CLAUDE_SKILL_FILE" ]]; then
|
|
@@ -191,12 +191,10 @@ else
|
|
|
191
191
|
_fail "claude-code upgrade: skill change did not propagate to workspace"
|
|
192
192
|
fi
|
|
193
193
|
|
|
194
|
-
if [[ -f "$CODEX_SKILL_FILE" ]] && grep -qF "$UPGRADE_MARKER" "$CODEX_UPGRADE/.
|
|
194
|
+
if [[ -f "$CODEX_SKILL_FILE" ]] && grep -qF "$UPGRADE_MARKER" "$CODEX_UPGRADE/.agents/skills/plan-work/SKILL.md" 2>/dev/null; then
|
|
195
195
|
_pass "codex upgrade: modified skill file propagated to workspace"
|
|
196
|
-
elif [[ ! -f "$CODEX_SKILL_FILE" ]]; then
|
|
197
|
-
_pass "codex upgrade: skill file not in bundle (skipped)"
|
|
198
196
|
else
|
|
199
|
-
_fail "codex upgrade: skill change did not propagate
|
|
197
|
+
_fail "codex upgrade: required universal skill missing or change did not propagate"
|
|
200
198
|
fi
|
|
201
199
|
|
|
202
200
|
if [[ -f "$OPENCODE_SKILL_FILE" ]] && grep -qF "$UPGRADE_MARKER" "$OPENCODE_UPGRADE/.opencode/skills/plan-work/SKILL.md" 2>/dev/null; then
|
|
@@ -246,7 +244,7 @@ echo '{"custom":"data"}' > "$OPENCODE_USER/.kontourai/flow-agents/my-session/sta
|
|
|
246
244
|
|
|
247
245
|
# Modify an installed skill file to simulate user edits
|
|
248
246
|
CLAUDE_INSTALLED_SKILL="$CLAUDE_USER/.claude/skills/plan-work/SKILL.md"
|
|
249
|
-
CODEX_INSTALLED_SKILL="$CODEX_USER/.
|
|
247
|
+
CODEX_INSTALLED_SKILL="$CODEX_USER/.agents/skills/plan-work/SKILL.md"
|
|
250
248
|
OPENCODE_INSTALLED_SKILL="$OPENCODE_USER/.opencode/skills/plan-work/SKILL.md"
|
|
251
249
|
|
|
252
250
|
USER_EDIT_MARKER="# USER EDIT - should be overwritten by re-install"
|