@kontourai/flow-agents 3.9.0 → 3.10.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/.github/workflows/ci.yml +8 -0
- package/CHANGELOG.md +17 -0
- package/README.md +1 -1
- package/build/src/cli/assignment-provider.js +4 -18
- package/build/src/cli/continuation-adapter.d.ts +10 -8
- package/build/src/cli/continuation-adapter.js +22 -4
- package/build/src/cli/validate-workflow-artifacts.js +21 -1
- package/build/src/cli/workflow-sidecar.js +15 -20
- package/build/src/cli/workflow.js +58 -11
- package/build/src/continuation-driver.d.ts +27 -3
- package/build/src/continuation-driver.js +97 -14
- package/build/src/index.d.ts +2 -2
- package/build/src/index.js +1 -1
- package/build/src/tools/build-universal-bundles.js +52 -25
- package/build/src/tools/validate-source-tree.js +5 -2
- package/context/scripts/hooks/lib/continuation-turn-authority.js +386 -0
- package/context/scripts/hooks/stop-goal-fit.js +244 -73
- package/context/scripts/hooks/workflow-steering.js +24 -15
- package/context/scripts/telemetry/lib/pricing.sh +18 -6
- package/context/scripts/telemetry/lib/transport.sh +139 -37
- package/context/scripts/telemetry/lib/usage.sh +282 -32
- package/context/scripts/telemetry/lib/usage_model_guard.sh +42 -0
- package/context/scripts/telemetry/telemetry.sh +76 -1
- package/docs/coordination-guide.md +21 -5
- package/docs/fixture-ownership.md +2 -1
- package/docs/public-workflow-cli.md +44 -0
- package/docs/spec/runtime-hook-surface.md +8 -4
- package/evals/acceptance/prove-capture-teeth-declared.sh +13 -0
- package/evals/acceptance/prove-capture-teeth.sh +13 -0
- package/evals/ci/run-baseline.sh +2 -0
- package/evals/fixtures/codex-legacy-agents/5273878130bdafc8a024a650bb5b66c9b003f1f859b5dc6e5b588cbf4ab23228.md +25 -0
- package/evals/fixtures/telemetry/usage-transcript-adversarial.jsonl +2 -0
- package/evals/fixtures/telemetry/usage-transcript-malformed-sandwich.jsonl +3 -0
- package/evals/fixtures/telemetry/usage-transcript-negative-tokens.jsonl +1 -0
- package/evals/fixtures/telemetry/usage-transcript-oversized-prefix.jsonl +201 -0
- package/evals/fixtures/telemetry/usage-transcript-oversized-tokens.jsonl +1 -0
- package/evals/fixtures/telemetry/usage-transcript-prefixed-secret-lower.jsonl +1 -0
- package/evals/fixtures/telemetry/usage-transcript-prefixed-secret-upper.jsonl +1 -0
- package/evals/fixtures/telemetry/usage-transcript-secret-model-jwt.jsonl +1 -0
- package/evals/fixtures/telemetry/usage-transcript-secret-model.jsonl +1 -0
- package/evals/fixtures/telemetry/usage-transcript-torn-trailing.jsonl +2 -0
- package/evals/integration/test_actor_identity.sh +63 -0
- package/evals/integration/test_assignment_provider_local_file.sh +34 -0
- package/evals/integration/test_builder_entry_enforcement.sh +5 -1
- package/evals/integration/test_bundle_install.sh +106 -17
- package/evals/integration/test_bundle_lifecycle.sh +17 -3
- package/evals/integration/test_captured_fail_reconciliation.sh +11 -0
- package/evals/integration/test_ci_actor_identity.sh +2 -2
- package/evals/integration/test_command_log_concurrency.sh +11 -0
- package/evals/integration/test_command_log_fork_classification.sh +11 -0
- package/evals/integration/test_command_log_integrity.sh +11 -0
- package/evals/integration/test_current_json_per_actor.sh +416 -21
- package/evals/integration/test_enforcer_expects_driven.sh +11 -0
- package/evals/integration/test_ensure_session_ownership_guard.sh +39 -0
- package/evals/integration/test_evidence_capture_hook.sh +25 -6
- package/evals/integration/test_fixture_retirement_audit.sh +2 -2
- package/evals/integration/test_flowdef_union_floor_regression.sh +11 -0
- package/evals/integration/test_gate_bypass_chain.sh +11 -0
- package/evals/integration/test_gate_lockdown.sh +10 -0
- package/evals/integration/test_goal_fit_ghost_session.sh +11 -0
- package/evals/integration/test_goal_fit_hook.sh +257 -9
- package/evals/integration/test_goal_fit_rederive.sh +11 -0
- package/evals/integration/test_install_merge.sh +141 -3
- package/evals/integration/test_liveness_conflict_injection.sh +39 -3
- package/evals/integration/test_liveness_heartbeat.sh +46 -9
- package/evals/integration/test_phase_map_and_gate_claim.sh +10 -1
- package/evals/integration/test_public_workflow_cli.sh +6 -6
- package/evals/integration/test_session_resume_roundtrip.sh +30 -0
- package/evals/integration/test_stop_hook_release.sh +111 -11
- package/evals/integration/test_telemetry_tool_usage.sh +394 -0
- package/evals/integration/test_verify_hold.sh +64 -1
- package/evals/integration/test_workflow_sidecar_writer.sh +10 -1
- package/evals/integration/test_workflow_steering_hook.sh +20 -4
- package/evals/run.sh +2 -0
- package/evals/static/test_universal_bundles.sh +123 -0
- package/package.json +1 -1
- package/packaging/README.md +21 -0
- package/packaging/codex-legacy-agents-fingerprints.json +33 -0
- package/scripts/README.md +2 -1
- package/scripts/audit-codex-legacy-agents.js +43 -0
- package/scripts/classify-codex-legacy-agents.js +71 -0
- package/scripts/hooks/evidence-capture.js +18 -6
- package/scripts/hooks/lib/actor-identity.js +62 -29
- package/scripts/hooks/lib/continuation-turn-authority.js +386 -0
- package/scripts/hooks/lib/current-pointer.js +114 -12
- package/scripts/hooks/lib/liveness-heartbeat.js +15 -12
- package/scripts/hooks/stop-goal-fit.js +244 -73
- package/scripts/hooks/workflow-steering.js +24 -15
- package/scripts/install-codex-home.sh +8 -7
- package/scripts/telemetry/lib/pricing.sh +18 -6
- package/scripts/telemetry/lib/transport.sh +71 -0
- package/scripts/telemetry/lib/usage.sh +282 -32
- package/scripts/telemetry/lib/usage_model_guard.sh +42 -0
- package/scripts/telemetry/telemetry.sh +76 -1
- package/src/cli/assignment-provider.ts +5 -26
- package/src/cli/builder-flow-runtime.test.mjs +93 -6
- package/src/cli/continuation-adapter.ts +33 -7
- package/src/cli/continuation-driver.test.mjs +741 -2
- package/src/cli/public-api.test.mjs +1 -0
- package/src/cli/validate-workflow-artifacts.ts +19 -1
- package/src/cli/workflow-sidecar.ts +18 -23
- package/src/cli/workflow.ts +71 -11
- package/src/continuation-driver.ts +125 -19
- package/src/index.ts +4 -0
- package/src/tools/build-universal-bundles.ts +61 -25
- package/src/tools/validate-source-tree.ts +5 -2
|
@@ -1169,7 +1169,11 @@ EOF_CFG
|
|
|
1169
1169
|
|
|
1170
1170
|
cat > "$CH4_DEST/AGENTS.md" << 'EOF_AGENTS'
|
|
1171
1171
|
# User Codex Instructions
|
|
1172
|
+
|
|
1173
|
+
Arbitrary user bytes must survive exactly.
|
|
1174
|
+
Unicode: café / 雪
|
|
1172
1175
|
EOF_AGENTS
|
|
1176
|
+
CH4_AGENTS_BEFORE="$(shasum -a 256 "$CH4_DEST/AGENTS.md" | awk '{print $1}')"
|
|
1173
1177
|
|
|
1174
1178
|
cat > "$CH4_DEST/hooks.json" << 'JSON'
|
|
1175
1179
|
{
|
|
@@ -1213,7 +1217,8 @@ CODEX_REAL_HOME="$TMPDIR_EVAL/fake-real-codex" FLOW_AGENTS_SKILLS_DIR="$CH4_SKIL
|
|
|
1213
1217
|
|
|
1214
1218
|
if grep -q 'user-model' "$CH4_DEST/config.toml" \
|
|
1215
1219
|
&& grep -q 'user-profile-model' "$CH4_DEST/custom.config.toml" \
|
|
1216
|
-
&& grep -q 'User Codex Instructions' "$CH4_DEST/AGENTS.md"
|
|
1220
|
+
&& grep -q 'User Codex Instructions' "$CH4_DEST/AGENTS.md" \
|
|
1221
|
+
&& [[ "$CH4_AGENTS_BEFORE" == "$(shasum -a 256 "$CH4_DEST/AGENTS.md" | awk '{print $1}')" ]]; then
|
|
1217
1222
|
_pass "CH4: user-owned config.toml, profile, and AGENTS.md preserved"
|
|
1218
1223
|
else
|
|
1219
1224
|
_fail "CH4: user-owned config.toml, profile, or AGENTS.md was overwritten"
|
|
@@ -1308,6 +1313,22 @@ for CH5_REL in scripts kits hooks.json; do
|
|
|
1308
1313
|
fi
|
|
1309
1314
|
done
|
|
1310
1315
|
|
|
1316
|
+
CH8_FIXTURES="$ROOT_DIR/evals/fixtures/codex-legacy-agents"
|
|
1317
|
+
CH8_PREFLIGHT="$TMPDIR_EVAL/codex-home-ch8-later-preflight"
|
|
1318
|
+
mkdir -p "$CH8_PREFLIGHT/scripts"
|
|
1319
|
+
cp "$CH8_FIXTURES/5273878130bdafc8a024a650bb5b66c9b003f1f859b5dc6e5b588cbf4ab23228.md" "$CH8_PREFLIGHT/AGENTS.md"
|
|
1320
|
+
printf 'unowned collision\n' > "$CH8_PREFLIGHT/scripts/kit.js"
|
|
1321
|
+
CH8_PREFLIGHT_BEFORE="$(shasum -a 256 "$CH8_PREFLIGHT/AGENTS.md" | awk '{print $1}')"
|
|
1322
|
+
if CODEX_REAL_HOME="$TMPDIR_EVAL/fake-real-codex" FLOW_AGENTS_SKILLS_DIR="$TMPDIR_EVAL/ch8-preflight-skills" \
|
|
1323
|
+
bash "$ROOT_DIR/scripts/install-codex-home.sh" "$CH8_PREFLIGHT" >"$TMPDIR_EVAL/ch8-preflight.out" 2>&1; then
|
|
1324
|
+
_fail "CH8: later owned-overlay collision unexpectedly installed"
|
|
1325
|
+
elif [[ "$CH8_PREFLIGHT_BEFORE" == "$(shasum -a 256 "$CH8_PREFLIGHT/AGENTS.md" | awk '{print $1}')" ]] \
|
|
1326
|
+
&& [[ ! -e "$CH8_PREFLIGHT/.flow-agents/manual-recovery" && ! -e "$CH8_PREFLIGHT/hooks.json" ]]; then
|
|
1327
|
+
_pass "CH8: complete install preflight failure leaves exact legacy source and destination untouched"
|
|
1328
|
+
else
|
|
1329
|
+
_fail "CH8: later install preflight failure partially migrated or mutated destination"
|
|
1330
|
+
fi
|
|
1331
|
+
|
|
1311
1332
|
echo ""
|
|
1312
1333
|
echo "--- CH6: codex-home rejects canonical source/destination overlap before writes ---"
|
|
1313
1334
|
CH6_ROOT="$TMPDIR_EVAL/ch6-fixture-root"
|
|
@@ -1315,8 +1336,9 @@ CH6_FAKE_BIN="$TMPDIR_EVAL/ch6-bin"
|
|
|
1315
1336
|
CH_FIXTURE_SCRIPTS="scr""ipts"
|
|
1316
1337
|
CH_FIXTURE_SKILLS="sk""ills"
|
|
1317
1338
|
CH_FIXTURE_AGENTS="ag""ents"
|
|
1318
|
-
mkdir -p "$CH6_ROOT/$CH_FIXTURE_SCRIPTS" "$CH6_ROOT/dist/codex/$CH_FIXTURE_SCRIPTS" "$CH6_FAKE_BIN"
|
|
1319
|
-
cp "$ROOT_DIR/scripts/install-codex-home.sh" "$ROOT_DIR/scripts/install-owned-files.js" "$CH6_ROOT/$CH_FIXTURE_SCRIPTS/"
|
|
1339
|
+
mkdir -p "$CH6_ROOT/$CH_FIXTURE_SCRIPTS" "$CH6_ROOT/packaging" "$CH6_ROOT/dist/codex/$CH_FIXTURE_SCRIPTS" "$CH6_FAKE_BIN"
|
|
1340
|
+
cp "$ROOT_DIR/scripts/install-codex-home.sh" "$ROOT_DIR/scripts/install-owned-files.js" "$ROOT_DIR/scripts/classify-codex-legacy-agents.js" "$CH6_ROOT/$CH_FIXTURE_SCRIPTS/"
|
|
1341
|
+
cp "$ROOT_DIR/packaging/codex-legacy-agents-fingerprints.json" "$CH6_ROOT/packaging/"
|
|
1320
1342
|
printf '#!/usr/bin/env bash\nexit 0\n' > "$CH6_FAKE_BIN/npm"
|
|
1321
1343
|
chmod +x "$CH6_FAKE_BIN/npm"
|
|
1322
1344
|
printf 'fixture\n' > "$CH6_ROOT/dist/codex/$CH_FIXTURE_SCRIPTS/fixture.txt"
|
|
@@ -1658,6 +1680,122 @@ fi
|
|
|
1658
1680
|
|
|
1659
1681
|
echo ""
|
|
1660
1682
|
|
|
1683
|
+
# ─── codex-home: CH8: global instruction cleanup and preservation ───────────
|
|
1684
|
+
echo "--- CH8: codex-home does not install global instructions and safely migrates exact legacy files ---"
|
|
1685
|
+
|
|
1686
|
+
CH8_SKILLS="$TMPDIR_EVAL/codex-home-ch8-skills"
|
|
1687
|
+
CH8_CLEAN="$TMPDIR_EVAL/codex-home-ch8-clean"
|
|
1688
|
+
mkdir -p "$CH8_CLEAN"
|
|
1689
|
+
CH8_CLEAN_OUT="$TMPDIR_EVAL/ch8-clean.out"
|
|
1690
|
+
if CODEX_REAL_HOME="$TMPDIR_EVAL/fake-real-codex" FLOW_AGENTS_SKILLS_DIR="$CH8_SKILLS" \
|
|
1691
|
+
bash "$ROOT_DIR/scripts/install-codex-home.sh" "$CH8_CLEAN" >"$CH8_CLEAN_OUT" 2>&1 \
|
|
1692
|
+
&& [[ ! -e "$CH8_CLEAN/AGENTS.md" ]] \
|
|
1693
|
+
&& [[ -f "$CH8_CLEAN/hooks.json" && -f "$CH8_CLEAN/ag""ents/tool-worker.toml" ]] \
|
|
1694
|
+
&& [[ -f "$CH8_SKILLS/plan-work/SKILL.md" ]]; then
|
|
1695
|
+
_pass "CH8: clean install leaves global AGENTS.md absent while runtime assets and universal skills install"
|
|
1696
|
+
else
|
|
1697
|
+
_fail "CH8: clean install created global instructions or lost required install assets"
|
|
1698
|
+
fi
|
|
1699
|
+
|
|
1700
|
+
CH8_FIXTURES="$ROOT_DIR/evals/fixtures/codex-legacy-agents"
|
|
1701
|
+
while IFS=$'\t' read -r CH8_HASH CH8_BYTES; do
|
|
1702
|
+
CH8_LEGACY="$TMPDIR_EVAL/codex-home-ch8-legacy-$CH8_HASH"
|
|
1703
|
+
CH8_LEGACY_SKILLS="$TMPDIR_EVAL/codex-home-ch8-legacy-skills-$CH8_HASH"
|
|
1704
|
+
mkdir -p "$CH8_LEGACY"
|
|
1705
|
+
CH8_LEGACY_REAL="$(cd "$CH8_LEGACY" && pwd -P)"
|
|
1706
|
+
cp "$CH8_FIXTURES/$CH8_HASH.md" "$CH8_LEGACY/AGENTS.md"
|
|
1707
|
+
printf 'destination sentinel\n' > "$CH8_LEGACY/preflight-sentinel"
|
|
1708
|
+
printf 'skills sentinel\n' > "$CH8_LEGACY_SKILLS.sentinel"
|
|
1709
|
+
CH8_SOURCE_BEFORE="$(shasum -a 256 "$CH8_LEGACY/AGENTS.md" | awk '{print $1}')"
|
|
1710
|
+
CH8_OUT="$TMPDIR_EVAL/ch8-$CH8_HASH.out"
|
|
1711
|
+
if CODEX_REAL_HOME="$TMPDIR_EVAL/fake-real-codex" FLOW_AGENTS_SKILLS_DIR="$CH8_LEGACY_SKILLS" \
|
|
1712
|
+
bash "$ROOT_DIR/scripts/install-codex-home.sh" "$CH8_LEGACY" >"$CH8_OUT" 2>&1; then
|
|
1713
|
+
_fail "CH8: audited legacy fingerprint $CH8_HASH did not refuse installation"
|
|
1714
|
+
elif [[ "$CH8_SOURCE_BEFORE" == "$(shasum -a 256 "$CH8_LEGACY/AGENTS.md" | awk '{print $1}')" ]] \
|
|
1715
|
+
&& [[ "$(cat "$CH8_LEGACY/preflight-sentinel")" == "destination sentinel" ]] \
|
|
1716
|
+
&& [[ ! -e "$CH8_LEGACY/hooks.json" && ! -e "$CH8_LEGACY_SKILLS" ]] \
|
|
1717
|
+
&& grep -F -q "path=$CH8_LEGACY_REAL/AGENTS.md" "$CH8_OUT" \
|
|
1718
|
+
&& grep -F -q "sha256=$CH8_HASH bytes=$CH8_BYTES" "$CH8_OUT" \
|
|
1719
|
+
&& grep -q 'matching_releases=v2.4.0@bee760272b8bb770df02400ccc5881bd3dbc8806' "$CH8_OUT" \
|
|
1720
|
+
&& grep -F -q "evidence command (hash): shasum -a 256 -- '$CH8_LEGACY_REAL/AGENTS.md'" "$CH8_OUT" \
|
|
1721
|
+
&& grep -F -q "evidence command (bytes): wc -c < '$CH8_LEGACY_REAL/AGENTS.md'" "$CH8_OUT" \
|
|
1722
|
+
&& grep -q 'Stop all agent and writer processes' "$CH8_OUT" \
|
|
1723
|
+
&& grep -q 'Move the file manually without overwrite using operator-trusted tooling' "$CH8_OUT" \
|
|
1724
|
+
&& ! grep -Eq '(^|: )(mv|rm|cp)( |$)| && (mv|rm|cp) ' "$CH8_OUT"; then
|
|
1725
|
+
_pass "CH8: audited legacy fingerprint $CH8_HASH refuses before mutation with exact evidence and manual checklist"
|
|
1726
|
+
else
|
|
1727
|
+
_fail "CH8: audited legacy fingerprint $CH8_HASH refusal mutated state or lacked actionable evidence"
|
|
1728
|
+
fi
|
|
1729
|
+
mkdir -p "$CH8_LEGACY/.flow-agents/manual-recovery"
|
|
1730
|
+
mv "$CH8_LEGACY/AGENTS.md" "$CH8_LEGACY/.flow-agents/manual-recovery/legacy-generated-AGENTS.$CH8_HASH.md"
|
|
1731
|
+
if CODEX_REAL_HOME="$TMPDIR_EVAL/fake-real-codex" FLOW_AGENTS_SKILLS_DIR="$CH8_LEGACY_SKILLS" \
|
|
1732
|
+
bash "$ROOT_DIR/scripts/install-codex-home.sh" "$CH8_LEGACY" >/dev/null 2>&1 \
|
|
1733
|
+
&& cmp -s "$CH8_FIXTURES/$CH8_HASH.md" "$CH8_LEGACY/.flow-agents/manual-recovery/legacy-generated-AGENTS.$CH8_HASH.md" \
|
|
1734
|
+
&& [[ ! -e "$CH8_LEGACY/AGENTS.md" && -f "$CH8_LEGACY/hooks.json" && -f "$CH8_LEGACY_SKILLS/plan-work/SKILL.md" ]]; then
|
|
1735
|
+
_pass "CH8: simulated operator remediation enables install without recreating AGENTS.md"
|
|
1736
|
+
else
|
|
1737
|
+
_fail "CH8: operator-remediated rerun failed or recreated global instructions"
|
|
1738
|
+
fi
|
|
1739
|
+
done < <(node - "$ROOT_DIR/packaging/codex-legacy-agents-fingerprints.json" <<'NODE'
|
|
1740
|
+
const fs = require("node:fs");
|
|
1741
|
+
const manifest = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
|
|
1742
|
+
for (const entry of manifest.files || []) console.log(`${entry.sha256}\t${entry.bytes}`);
|
|
1743
|
+
NODE
|
|
1744
|
+
)
|
|
1745
|
+
|
|
1746
|
+
CH8_QUOTED="$TMPDIR_EVAL/codex-home-ch8-operator-'quoted"
|
|
1747
|
+
mkdir -p "$CH8_QUOTED"
|
|
1748
|
+
cp "$CH8_FIXTURES/5273878130bdafc8a024a650bb5b66c9b003f1f859b5dc6e5b588cbf4ab23228.md" "$CH8_QUOTED/AGENTS.md"
|
|
1749
|
+
CH8_QUOTED_OUT="$TMPDIR_EVAL/ch8-quoted.out"
|
|
1750
|
+
if node "$ROOT_DIR/scripts/classify-codex-legacy-agents.js" "$CH8_QUOTED" "$ROOT_DIR/packaging/codex-legacy-agents-fingerprints.json" >"$CH8_QUOTED_OUT" 2>&1; then
|
|
1751
|
+
_fail "CH8: quoted-path exact legacy classifier did not refuse"
|
|
1752
|
+
else
|
|
1753
|
+
CH8_QUOTED_HASH_COMMAND="$(sed -n 's/^classify-codex-legacy-agents: evidence command (hash): //p' "$CH8_QUOTED_OUT")"
|
|
1754
|
+
CH8_QUOTED_BYTES_COMMAND="$(sed -n 's/^classify-codex-legacy-agents: evidence command (bytes): //p' "$CH8_QUOTED_OUT")"
|
|
1755
|
+
if [[ "$CH8_QUOTED_HASH_COMMAND" == *"'\"'\"'"* && "$CH8_QUOTED_BYTES_COMMAND" == *"'\"'\"'"* ]] \
|
|
1756
|
+
&& bash -n -c "$CH8_QUOTED_HASH_COMMAND" && bash -n -c "$CH8_QUOTED_BYTES_COMMAND"; then
|
|
1757
|
+
_pass "CH8: evidence commands safely shell-quote apostrophes in paths"
|
|
1758
|
+
else
|
|
1759
|
+
_fail "CH8: evidence commands are not safely shell-quoted"
|
|
1760
|
+
fi
|
|
1761
|
+
fi
|
|
1762
|
+
|
|
1763
|
+
CH8_NEAR="$TMPDIR_EVAL/codex-home-ch8-near"
|
|
1764
|
+
mkdir -p "$CH8_NEAR"
|
|
1765
|
+
cp "$CH8_FIXTURES/5273878130bdafc8a024a650bb5b66c9b003f1f859b5dc6e5b588cbf4ab23228.md" "$CH8_NEAR/AGENTS.md"
|
|
1766
|
+
printf 'user modification\n' >> "$CH8_NEAR/AGENTS.md"
|
|
1767
|
+
CH8_NEAR_BEFORE="$(shasum -a 256 "$CH8_NEAR/AGENTS.md" | awk '{print $1}')"
|
|
1768
|
+
if CODEX_REAL_HOME="$TMPDIR_EVAL/fake-real-codex" FLOW_AGENTS_SKILLS_DIR="$TMPDIR_EVAL/ch8-near-skills" \
|
|
1769
|
+
bash "$ROOT_DIR/scripts/install-codex-home.sh" "$CH8_NEAR" >"$TMPDIR_EVAL/ch8-near.out" 2>&1 \
|
|
1770
|
+
&& [[ "$CH8_NEAR_BEFORE" == "$(shasum -a 256 "$CH8_NEAR/AGENTS.md" | awk '{print $1}')" ]] \
|
|
1771
|
+
&& grep -q 'preserved unrecognized or user-owned' "$TMPDIR_EVAL/ch8-near.out"; then
|
|
1772
|
+
_pass "CH8: near-match legacy-looking user instructions are byte-preserved"
|
|
1773
|
+
else
|
|
1774
|
+
_fail "CH8: near-match user instructions changed or lacked preservation diagnostic"
|
|
1775
|
+
fi
|
|
1776
|
+
|
|
1777
|
+
for CH8_KIND in symlink directory; do
|
|
1778
|
+
CH8_UNSAFE="$TMPDIR_EVAL/codex-home-ch8-$CH8_KIND"
|
|
1779
|
+
mkdir -p "$CH8_UNSAFE"
|
|
1780
|
+
printf 'must remain untouched\n' > "$CH8_UNSAFE/preflight-sentinel"
|
|
1781
|
+
case "$CH8_KIND" in
|
|
1782
|
+
symlink) printf 'target\n' > "$CH8_UNSAFE/user-target"; ln -s user-target "$CH8_UNSAFE/AGENTS.md" ;;
|
|
1783
|
+
directory) mkdir "$CH8_UNSAFE/AGENTS.md" ;;
|
|
1784
|
+
esac
|
|
1785
|
+
if CODEX_REAL_HOME="$TMPDIR_EVAL/fake-real-codex" FLOW_AGENTS_SKILLS_DIR="$TMPDIR_EVAL/ch8-$CH8_KIND-skills" \
|
|
1786
|
+
bash "$ROOT_DIR/scripts/install-codex-home.sh" "$CH8_UNSAFE" >"$TMPDIR_EVAL/ch8-$CH8_KIND.out" 2>&1 \
|
|
1787
|
+
&& [[ "$(cat "$CH8_UNSAFE/preflight-sentinel")" == "must remain untouched" ]] \
|
|
1788
|
+
&& [[ -e "$CH8_UNSAFE/hooks.json" ]] \
|
|
1789
|
+
&& { [[ "$CH8_KIND" == "symlink" && -L "$CH8_UNSAFE/AGENTS.md" && "$(cat "$CH8_UNSAFE/user-target")" == "target" ]] \
|
|
1790
|
+
|| [[ "$CH8_KIND" == "directory" && -d "$CH8_UNSAFE/AGENTS.md" ]]; }; then
|
|
1791
|
+
_pass "CH8: read-only classifier preserves ambiguous $CH8_KIND AGENTS.md object"
|
|
1792
|
+
else
|
|
1793
|
+
_fail "CH8: classifier altered ambiguous $CH8_KIND AGENTS.md object"
|
|
1794
|
+
fi
|
|
1795
|
+
done
|
|
1796
|
+
|
|
1797
|
+
echo ""
|
|
1798
|
+
|
|
1661
1799
|
# ─── pi --global: PG1: warns NOT_VERIFIED + falls back to workspace default ──
|
|
1662
1800
|
echo "--- PG1: pi --global warns NOT_VERIFIED + falls back to workspace default ---"
|
|
1663
1801
|
|
|
@@ -43,6 +43,7 @@ set -uo pipefail
|
|
|
43
43
|
|
|
44
44
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
45
45
|
LIB_DIR="$ROOT/scripts/hooks/lib"
|
|
46
|
+
CURRENT_POINTER_HELPER="$LIB_DIR/current-pointer.js"
|
|
46
47
|
HEARTBEAT_MODULE="$LIB_DIR/liveness-heartbeat.js"
|
|
47
48
|
READ_MODULE="$LIB_DIR/liveness-read.js"
|
|
48
49
|
ACTOR_MODULE="$LIB_DIR/actor-identity.js"
|
|
@@ -76,6 +77,30 @@ new_scratch() {
|
|
|
76
77
|
mktemp -d "$TMPDIR_EVAL/scratch-XXXXXX"
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
# seed_current_snapshot <root> <slug> [actor] — #440 FIXTURE-GAP: this suite's fixtures were
|
|
81
|
+
# written before #440's per-actor ownership scoping and never establish a per-actor current
|
|
82
|
+
# pointer for the invoking actor -- under a RESOLVED actor (every hook invocation below sets an
|
|
83
|
+
# explicit FLOW_AGENTS_ACTOR override), liveness-heartbeat.js's readActiveSlug (via
|
|
84
|
+
# readOwnCurrentPointer) now scopes to that actor's own (nonexistent) pointer and never reaches
|
|
85
|
+
# the fixture-under-test. Writes BOTH the legacy current.json AND, when actor is given, the
|
|
86
|
+
# per-actor current/<actor>.json pointer with the SAME minimal {"active_slug": slug} payload --
|
|
87
|
+
# mirroring test_liveness_heartbeat.sh's own seed_current_snapshot helper and, ultimately,
|
|
88
|
+
# workflow-sidecar.ts's real writeCurrent() dual-write via current-pointer.js's own
|
|
89
|
+
# writePerActorCurrent.
|
|
90
|
+
seed_current_snapshot() {
|
|
91
|
+
local root="$1" slug="$2" actor="${3:-}"
|
|
92
|
+
local artifact_root="$root/.kontourai/flow-agents"
|
|
93
|
+
mkdir -p "$artifact_root"
|
|
94
|
+
printf '{"active_slug":"%s"}\n' "$slug" >"$artifact_root/$SNAPSHOT_FILENAME"
|
|
95
|
+
if [[ -n "$actor" ]]; then
|
|
96
|
+
CP_HELPER_ARG="$CURRENT_POINTER_HELPER" FLOW_AGENTS_DIR_ARG="$artifact_root" \
|
|
97
|
+
SLUG_ARG="$slug" ACTOR_ARG="$actor" node - <<'NODE'
|
|
98
|
+
const { writePerActorCurrent } = require(process.env.CP_HELPER_ARG);
|
|
99
|
+
writePerActorCurrent(process.env.FLOW_AGENTS_DIR_ARG, process.env.ACTOR_ARG, { active_slug: process.env.SLUG_ARG });
|
|
100
|
+
NODE
|
|
101
|
+
fi
|
|
102
|
+
}
|
|
103
|
+
|
|
79
104
|
# seed_claim <scratch_root> <slug> <actor> <at_iso> [ttl_seconds]
|
|
80
105
|
# Seeds <root>/.kontourai/flow-agents/current.json (active_slug) and OVERWRITES
|
|
81
106
|
# liveness/events.jsonl with a single `claim` event for <actor>.
|
|
@@ -83,7 +108,7 @@ seed_claim() {
|
|
|
83
108
|
local root="$1" slug="$2" actor="$3" at="$4" ttl="${5:-1800}"
|
|
84
109
|
local artifact_root="$root/.kontourai/flow-agents"
|
|
85
110
|
mkdir -p "$artifact_root/liveness"
|
|
86
|
-
|
|
111
|
+
seed_current_snapshot "$root" "$slug" "$actor"
|
|
87
112
|
printf '{"type":"claim","subjectId":"%s","actor":"%s","at":"%s","ttlSeconds":%s}\n' \
|
|
88
113
|
"$slug" "$actor" "$at" "$ttl" >"$artifact_root/liveness/events.jsonl"
|
|
89
114
|
}
|
|
@@ -261,7 +286,7 @@ build_large_stream_with_conflict() {
|
|
|
261
286
|
local root="$1" slug="$2" actor="$3" claim_at="$4" other_actor="$5" other_at="$6"
|
|
262
287
|
local artifact_root="$root/.kontourai/flow-agents"
|
|
263
288
|
mkdir -p "$artifact_root/liveness"
|
|
264
|
-
|
|
289
|
+
seed_current_snapshot "$root" "$slug" "$actor"
|
|
265
290
|
SLUG_ARG="$slug" ACTOR_ARG="$actor" CLAIM_AT_ARG="$claim_at" OTHER_ACTOR_ARG="$other_actor" \
|
|
266
291
|
OTHER_AT_ARG="$other_at" STREAM_ARG="$artifact_root/liveness/events.jsonl" node - <<'NODE'
|
|
267
292
|
const fs = require('fs');
|
|
@@ -498,9 +523,20 @@ fi
|
|
|
498
523
|
# ─── G. Fail-open on a corrupted sidecar snapshot ────────────────────────────
|
|
499
524
|
echo "--- G. Fail-open on a corrupted sidecar snapshot ---"
|
|
500
525
|
|
|
526
|
+
# #440 FIX 2 (de-vacuate, delta review): corrupt agent-hb's OWN per-actor pointer file (the new
|
|
527
|
+
# collision-resistant mapping via perActorCurrentFile), not the legacy current.json -- a resolved
|
|
528
|
+
# actor never reads the legacy file at all (#440 D1), so corrupting only that no longer exercises
|
|
529
|
+
# anything (the assertion would pass via "no own pointer", not malformed-pointer tolerance).
|
|
530
|
+
# Mirrors test_liveness_heartbeat.sh's D2 precedent.
|
|
501
531
|
G_ROOT="$(new_scratch)"
|
|
502
532
|
mkdir -p "$G_ROOT/.kontourai/flow-agents/liveness"
|
|
503
|
-
|
|
533
|
+
G_PER_ACTOR_FILE="$(CP_HELPER_ARG="$CURRENT_POINTER_HELPER" ROOT_ARG="$G_ROOT/.kontourai/flow-agents" ACTOR_ARG="agent-hb" node - <<'NODE'
|
|
534
|
+
const { perActorCurrentFile } = require(process.env.CP_HELPER_ARG);
|
|
535
|
+
process.stdout.write(perActorCurrentFile(process.env.ROOT_ARG, process.env.ACTOR_ARG));
|
|
536
|
+
NODE
|
|
537
|
+
)"
|
|
538
|
+
mkdir -p "$(dirname "$G_PER_ACTOR_FILE")"
|
|
539
|
+
printf '{not valid json' >"$G_PER_ACTOR_FILE"
|
|
504
540
|
G_OUT="$(cd "$G_ROOT" && TELEMETRY_ENABLED=false FLOW_AGENTS_ACTOR=agent-hb \
|
|
505
541
|
node "$CLAUDE_HOOK" PostToolUse dev <"$POST_TOOL_USE_PAYLOAD_FILE" 2>"$TMPDIR_EVAL/g.err")"
|
|
506
542
|
G_STATUS=$?
|
|
@@ -40,6 +40,7 @@ POLICY_MODULE="$LIB_DIR/liveness-policy.js"
|
|
|
40
40
|
HEARTBEAT_MODULE="$LIB_DIR/liveness-heartbeat.js"
|
|
41
41
|
READ_MODULE="$LIB_DIR/liveness-read.js"
|
|
42
42
|
ACTOR_MODULE="$LIB_DIR/actor-identity.js"
|
|
43
|
+
CURRENT_POINTER_HELPER="$LIB_DIR/current-pointer.js"
|
|
43
44
|
CLAUDE_HOOK="$ROOT/scripts/hooks/claude-telemetry-hook.js"
|
|
44
45
|
CODEX_HOOK="$ROOT/scripts/hooks/codex-telemetry-hook.js"
|
|
45
46
|
|
|
@@ -68,6 +69,29 @@ new_scratch() {
|
|
|
68
69
|
mktemp -d "$TMPDIR_EVAL/scratch-XXXXXX"
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
# seed_current_snapshot <root> <slug> [actor] — #440: writes BOTH the legacy current.json
|
|
73
|
+
# (SNAPSHOT_FILENAME, unconditional — every fixture below keeps writing this) AND, when actor is
|
|
74
|
+
# given, the per-actor current/<actor>.json pointer with the SAME minimal {"active_slug": slug}
|
|
75
|
+
# payload — mirroring workflow-sidecar.ts's real writeCurrent() dual-write, via the shared
|
|
76
|
+
# current-pointer.js helper's own writePerActorCurrent (not a hand-rolled reimplementation of its
|
|
77
|
+
# sanitize/path rule), so #440's readOwnCurrentPointer finds this fixture's claimed subject
|
|
78
|
+
# exactly like a real session would. Every builder below that seeds a sidecar snapshot for a
|
|
79
|
+
# RESOLVED actor (the FLOW_AGENTS_ACTOR value the corresponding call_heartbeat/wrapper invocation
|
|
80
|
+
# uses) now routes through this, instead of hand-writing SNAPSHOT_FILENAME directly.
|
|
81
|
+
seed_current_snapshot() {
|
|
82
|
+
local root="$1" slug="$2" actor="${3:-}"
|
|
83
|
+
local artifact_root="$root/.kontourai/flow-agents"
|
|
84
|
+
mkdir -p "$artifact_root"
|
|
85
|
+
printf '{"active_slug":"%s"}\n' "$slug" >"$artifact_root/$SNAPSHOT_FILENAME"
|
|
86
|
+
if [[ -n "$actor" ]]; then
|
|
87
|
+
CP_HELPER_ARG="$CURRENT_POINTER_HELPER" FLOW_AGENTS_DIR_ARG="$artifact_root" \
|
|
88
|
+
SLUG_ARG="$slug" ACTOR_ARG="$actor" node - <<'NODE'
|
|
89
|
+
const { writePerActorCurrent } = require(process.env.CP_HELPER_ARG);
|
|
90
|
+
writePerActorCurrent(process.env.FLOW_AGENTS_DIR_ARG, process.env.ACTOR_ARG, { active_slug: process.env.SLUG_ARG });
|
|
91
|
+
NODE
|
|
92
|
+
fi
|
|
93
|
+
}
|
|
94
|
+
|
|
71
95
|
# seed_claim <scratch_root> <slug> <actor> <at_iso> [ttl_seconds]
|
|
72
96
|
# Seeds <root>/.kontourai/flow-agents/<SNAPSHOT_FILENAME> (active_slug) and a
|
|
73
97
|
# single `claim` event in liveness/events.jsonl for <actor>, matching the
|
|
@@ -77,7 +101,7 @@ seed_claim() {
|
|
|
77
101
|
local root="$1" slug="$2" actor="$3" at="$4" ttl="${5:-1800}"
|
|
78
102
|
local artifact_root="$root/.kontourai/flow-agents"
|
|
79
103
|
mkdir -p "$artifact_root/liveness"
|
|
80
|
-
|
|
104
|
+
seed_current_snapshot "$root" "$slug" "$actor"
|
|
81
105
|
printf '{"type":"claim","subjectId":"%s","actor":"%s","at":"%s","ttlSeconds":%s}\n' \
|
|
82
106
|
"$slug" "$actor" "$at" "$ttl" >"$artifact_root/liveness/events.jsonl"
|
|
83
107
|
}
|
|
@@ -292,8 +316,9 @@ fi
|
|
|
292
316
|
|
|
293
317
|
# C2: the sidecar snapshot names a slug with no prior events at all -> no-claim.
|
|
294
318
|
C2_ROOT="$(new_scratch)"
|
|
295
|
-
|
|
296
|
-
|
|
319
|
+
# #440 FIXTURE-GAP: agent-c2 owns c2-subj (never claimed) -- needs its own per-actor pointer so
|
|
320
|
+
# readOwnCurrentPointer finds it and the code reaches the no-claim check, not an earlier no-current.
|
|
321
|
+
seed_current_snapshot "$C2_ROOT" "c2-subj" "agent-c2"
|
|
297
322
|
C2_RESULT="$(call_heartbeat "$C2_ROOT" '{"FLOW_AGENTS_ACTOR":"agent-c2"}')"
|
|
298
323
|
if [[ "$C2_RESULT" == '{"emitted":false,"reason":"no-claim"}' ]]; then
|
|
299
324
|
_pass "maybeEmitHeartbeat returns no-claim when the named subject has no prior claim for the actor (AC4)"
|
|
@@ -360,9 +385,19 @@ fi
|
|
|
360
385
|
|
|
361
386
|
# D2: a corrupted sidecar snapshot (malformed JSON) fails open — wrapper still exits 0
|
|
362
387
|
# with its normal stdout contract, and no heartbeat is fabricated from unparseable state.
|
|
388
|
+
# #440 FIX 2 (de-vacuate, independent review): corrupt agent-hb's OWN per-actor pointer file (the
|
|
389
|
+
# new collision-resistant mapping), not the legacy current.json — a resolved actor never reads
|
|
390
|
+
# the legacy file at all (#440 D1), so corrupting only that no longer exercises anything; this
|
|
391
|
+
# now proves fail-open tolerance for a corrupt OWN pointer, the actually-reachable code path.
|
|
363
392
|
D2_ROOT="$(new_scratch)"
|
|
364
393
|
mkdir -p "$D2_ROOT/.kontourai/flow-agents/liveness"
|
|
365
|
-
|
|
394
|
+
D2_PER_ACTOR_FILE="$(CP_HELPER_ARG="$CURRENT_POINTER_HELPER" ROOT_ARG="$D2_ROOT/.kontourai/flow-agents" ACTOR_ARG="agent-hb" node - <<'NODE'
|
|
395
|
+
const { perActorCurrentFile } = require(process.env.CP_HELPER_ARG);
|
|
396
|
+
process.stdout.write(perActorCurrentFile(process.env.ROOT_ARG, process.env.ACTOR_ARG));
|
|
397
|
+
NODE
|
|
398
|
+
)"
|
|
399
|
+
mkdir -p "$(dirname "$D2_PER_ACTOR_FILE")"
|
|
400
|
+
printf '{not valid json' >"$D2_PER_ACTOR_FILE"
|
|
366
401
|
D2_OUT="$(cd "$D2_ROOT" && TELEMETRY_ENABLED=false FLOW_AGENTS_ACTOR=agent-hb \
|
|
367
402
|
node "$CLAUDE_HOOK" PostToolUse dev <"$POST_TOOL_USE_PAYLOAD_FILE" 2>"$TMPDIR_EVAL/d2.err")"
|
|
368
403
|
D2_STATUS=$?
|
|
@@ -436,7 +471,7 @@ build_large_liveness_stream() {
|
|
|
436
471
|
local root="$1" slug="$2" actor="$3" claim_at="$4" include_hb="${5:-false}" hb_at="${6:-}"
|
|
437
472
|
local artifact_root="$root/.kontourai/flow-agents"
|
|
438
473
|
mkdir -p "$artifact_root/liveness"
|
|
439
|
-
|
|
474
|
+
seed_current_snapshot "$root" "$slug" "$actor"
|
|
440
475
|
SLUG_ARG="$slug" ACTOR_ARG="$actor" CLAIM_AT_ARG="$claim_at" INCLUDE_HB_ARG="$include_hb" \
|
|
441
476
|
HB_AT_ARG="$hb_at" STREAM_ARG="$artifact_root/liveness/events.jsonl" node - <<'NODE'
|
|
442
477
|
const fs = require('fs');
|
|
@@ -564,8 +599,7 @@ build_large_liveness_stream_release() {
|
|
|
564
599
|
local root="$1" slug="$2" actor="$3" claim_at="$4" release_at="$5"
|
|
565
600
|
local artifact_root="$root/.kontourai/flow-agents"
|
|
566
601
|
mkdir -p "$artifact_root/liveness"
|
|
567
|
-
|
|
568
|
-
' "$slug" >"$artifact_root/$SNAPSHOT_FILENAME"
|
|
602
|
+
seed_current_snapshot "$root" "$slug" "$actor"
|
|
569
603
|
SLUG_ARG="$slug" ACTOR_ARG="$actor" CLAIM_AT_ARG="$claim_at" RELEASE_AT_ARG="$release_at" STREAM_ARG="$artifact_root/liveness/events.jsonl" node - <<'NODE'
|
|
570
604
|
const fs = require('fs');
|
|
571
605
|
const filler = (i) =>
|
|
@@ -624,7 +658,10 @@ const { sanitizeSegment } = require(process.env.ACTOR_MODULE_ARG);
|
|
|
624
658
|
process.stdout.write(sanitizeSegment(process.env.RAW_ARG));
|
|
625
659
|
NODE
|
|
626
660
|
)"
|
|
627
|
-
|
|
661
|
+
# #440 FIXTURE-GAP: agent-hb-i owns this (hostile-slug-named) subject -- needs its own per-actor
|
|
662
|
+
# pointer. seed_current_snapshot writes the payload's active_slug verbatim (no pre-sanitization on
|
|
663
|
+
# the write side), preserving this scenario's whole point: sanitization happens at READ time.
|
|
664
|
+
seed_current_snapshot "$I_ROOT" "$I_RAW_SLUG" "agent-hb-i"
|
|
628
665
|
printf '{"type":"claim","subjectId":"%s","actor":"agent-hb-i","at":"2026-06-25T11:00:00.000Z","ttlSeconds":1800}\n' \
|
|
629
666
|
"$I_SANITIZED_SLUG" >"$(stream_file "$I_ROOT")"
|
|
630
667
|
I_RESULT="$(call_heartbeat "$I_ROOT" '{"FLOW_AGENTS_ACTOR":"agent-hb-i"}' "2026-06-25T11:05:00.000Z")"
|
|
@@ -648,7 +685,7 @@ seed_heartbeat_only() {
|
|
|
648
685
|
local root="$1" slug="$2" actor="$3" at="$4"
|
|
649
686
|
local artifact_root="$root/.kontourai/flow-agents"
|
|
650
687
|
mkdir -p "$artifact_root/liveness"
|
|
651
|
-
|
|
688
|
+
seed_current_snapshot "$root" "$slug" "$actor"
|
|
652
689
|
printf '{"type":"heartbeat","subjectId":"%s","actor":"%s","at":"%s"}\n' \
|
|
653
690
|
"$slug" "$actor" "$at" >"$artifact_root/liveness/events.jsonl"
|
|
654
691
|
}
|
|
@@ -417,11 +417,20 @@ node -e "
|
|
|
417
417
|
" 2>/dev/null
|
|
418
418
|
rm -f "$C_DIR/.kontourai/flow-agents/$CLEAN_SLUG/$CLEAN_SLUG--deliver.md"
|
|
419
419
|
|
|
420
|
+
# #440 FIXTURE-GAP: this session was established under FLOW_AGENTS_ACTOR=clean-fixture-actor
|
|
421
|
+
# (line ~394) -- the Stop hook must run as that SAME actor so stop-goal-fit.js's ownership-scoped
|
|
422
|
+
# analyze() finds clean-fixture-actor's own per-actor pointer (established by workflow start's
|
|
423
|
+
# dual-write) rather than finding nothing for the ambient/unrelated invoking actor. Do NOT force
|
|
424
|
+
# an unresolved actor here: workflow start's own automatic selected-work gate-claim recording (and
|
|
425
|
+
# the resulting pull-work -> design-probe canonical-Flow auto-transition this section's assertion
|
|
426
|
+
# depends on) itself requires a genuinely RESOLVED actor to fire -- forcing unresolved for the
|
|
427
|
+
# setup step silently suppresses that transition, a real behavior difference, not just a
|
|
428
|
+
# fixture-reachability issue.
|
|
420
429
|
clean_out=""
|
|
421
430
|
clean_exit=0
|
|
422
431
|
for attempt in 1 2; do
|
|
423
432
|
set +e
|
|
424
|
-
attempt_out="$(FLOW_AGENTS_GOAL_FIT_MODE=block FLOW_AGENTS_GOAL_FIT_MAX_BLOCKS=2 FLOW_AGENTS_GOAL_FIT_BACKSTOP=skip \
|
|
433
|
+
attempt_out="$(FLOW_AGENTS_ACTOR=clean-fixture-actor FLOW_AGENTS_GOAL_FIT_MODE=block FLOW_AGENTS_GOAL_FIT_MAX_BLOCKS=2 FLOW_AGENTS_GOAL_FIT_BACKSTOP=skip \
|
|
425
434
|
node "$GATE" 2>&1 <<< "{\"hook_event_name\":\"Stop\",\"cwd\":\"$C_DIR\"}")"
|
|
426
435
|
attempt_exit="$?"
|
|
427
436
|
set -e
|
|
@@ -31,13 +31,13 @@ printf '#!/usr/bin/env bash\nset -eu\n( trap "" TERM; while ! sleep 5; do :; don
|
|
|
31
31
|
chmod +x "$CONSUMER/checks/check-success-background.sh"
|
|
32
32
|
|
|
33
33
|
run_candidate() {
|
|
34
|
-
(cd "$CONSUMER" && CODEX_SESSION_ID=public-workflow-eval npx --yes --package="file:$TARBALL" flow-agents workflow "$@")
|
|
34
|
+
(cd "$CONSUMER" && env -u CODEX_THREAD_ID CODEX_SESSION_ID=public-workflow-eval npx --yes --package="file:$TARBALL" flow-agents workflow "$@")
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
run_candidate_as() {
|
|
38
38
|
local actor="$1"
|
|
39
39
|
shift
|
|
40
|
-
(cd "$CONSUMER" && CODEX_SESSION_ID="$actor" npx --yes --package="file:$TARBALL" flow-agents workflow "$@")
|
|
40
|
+
(cd "$CONSUMER" && env -u CODEX_THREAD_ID CODEX_SESSION_ID="$actor" npx --yes --package="file:$TARBALL" flow-agents workflow "$@")
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
snapshot_tree() {
|
|
@@ -134,7 +134,7 @@ mkdir -p "$LOCAL_RETRY_ROOT/local-retry"
|
|
|
134
134
|
printf 'Selected Work Item: local:local-retry\n' > "$LOCAL_RETRY_ROOT/local-retry/local-retry--pull-work.md"
|
|
135
135
|
printf 'not a run-store directory\n' >"$LOCAL_RETRY_PROJECT/.kontourai/flow"
|
|
136
136
|
set +e
|
|
137
|
-
(cd "$LOCAL_RETRY_PROJECT" && CODEX_SESSION_ID=public-workflow-eval npx --yes --package="file:$TARBALL" flow-agents-workflow-sidecar ensure-session \
|
|
137
|
+
(cd "$LOCAL_RETRY_PROJECT" && env -u CODEX_THREAD_ID CODEX_SESSION_ID=public-workflow-eval npx --yes --package="file:$TARBALL" flow-agents-workflow-sidecar ensure-session \
|
|
138
138
|
--artifact-root "$LOCAL_RETRY_ROOT" --task-slug local-retry \
|
|
139
139
|
--title "Local retry" --summary "Resume the bound local workflow." --flow-id builder.build >/dev/null 2>&1)
|
|
140
140
|
LOCAL_SEED_RC=$?
|
|
@@ -147,7 +147,7 @@ rm -f "$LOCAL_RETRY_PROJECT/.kontourai/flow"
|
|
|
147
147
|
FOREIGN_RETRY_CWD="$TMP/foreign-retry-cwd"
|
|
148
148
|
mkdir -p "$FOREIGN_RETRY_CWD"
|
|
149
149
|
EXECUTABLE_RETRY="$(node -e 'process.stdout.write(process.argv[1].replace(process.argv[2], process.argv[3]))' "$LOCAL_RETRY_COMMAND" "'@kontourai/flow-agents@$VERSION'" "'file:$TARBALL'")"
|
|
150
|
-
(cd "$FOREIGN_RETRY_CWD" && CODEX_SESSION_ID=public-workflow-eval eval "$EXECUTABLE_RETRY" >/dev/null)
|
|
150
|
+
(cd "$FOREIGN_RETRY_CWD" && unset CODEX_THREAD_ID && export CODEX_SESSION_ID=public-workflow-eval && eval "$EXECUTABLE_RETRY" >/dev/null)
|
|
151
151
|
[[ -f "$LOCAL_RETRY_PROJECT/.kontourai/flow/runs/local-retry/state.json" && ! -e "$FOREIGN_RETRY_CWD/.kontourai" ]] || fail "emitted local retry mutated the caller cwd instead of the originating store"
|
|
152
152
|
pass "emitted local retry executes from a foreign cwd against its exact originating store"
|
|
153
153
|
|
|
@@ -428,7 +428,7 @@ TIMEOUT_SESSION="$ARTIFACT_ROOT/acme-widgets-107"
|
|
|
428
428
|
TIMEOUT_CHILD_PID="$TMP/command-timeout-child.pid"
|
|
429
429
|
TIMEOUT_MARKER="$TMP/command-timeout-marker"
|
|
430
430
|
set +e
|
|
431
|
-
(cd "$CONSUMER" && CODEX_SESSION_ID=public-workflow-eval FLOW_AGENTS_EVIDENCE_COMMAND_TIMEOUT_MS=1000 FLOW_AGENTS_EVIDENCE_COMMAND_KILL_GRACE_MS=250 npx --yes --package="file:$TARBALL" flow-agents workflow evidence --session-dir "$TIMEOUT_SESSION" --expectation pickup-probe-readiness --status not_verified --summary "Timeout fixture captures full process-group termination." --command "bash checks/check-command-timeout.sh '$TIMEOUT_CHILD_PID' '$TIMEOUT_MARKER'" --json) >"$TMP/command-timeout.out" 2>&1
|
|
431
|
+
(cd "$CONSUMER" && env -u CODEX_THREAD_ID CODEX_SESSION_ID=public-workflow-eval FLOW_AGENTS_EVIDENCE_COMMAND_TIMEOUT_MS=1000 FLOW_AGENTS_EVIDENCE_COMMAND_KILL_GRACE_MS=250 npx --yes --package="file:$TARBALL" flow-agents workflow evidence --session-dir "$TIMEOUT_SESSION" --expectation pickup-probe-readiness --status not_verified --summary "Timeout fixture captures full process-group termination." --command "bash checks/check-command-timeout.sh '$TIMEOUT_CHILD_PID' '$TIMEOUT_MARKER'" --json) >"$TMP/command-timeout.out" 2>&1
|
|
432
432
|
TIMEOUT_RC=$?
|
|
433
433
|
set -e
|
|
434
434
|
[[ "$TIMEOUT_RC" -ne 0 && -s "$TIMEOUT_CHILD_PID" && ! -e "$TIMEOUT_MARKER" ]] || fail "timed-out evidence command did not complete its controlled capture"
|
|
@@ -439,7 +439,7 @@ pass "timed-out evidence commands terminate their complete process group"
|
|
|
439
439
|
BACKGROUND_CHILD_PID="$TMP/success-background-child.pid"
|
|
440
440
|
BACKGROUND_MARKER="$TMP/success-background-marker"
|
|
441
441
|
TIMEOUT_PULL_REPORT="$TIMEOUT_SESSION/$(basename "$TIMEOUT_SESSION")--pull-work.md"
|
|
442
|
-
(cd "$CONSUMER" && CODEX_SESSION_ID=public-workflow-eval FLOW_AGENTS_EVIDENCE_COMMAND_KILL_GRACE_MS=50 npx --yes --package="file:$TARBALL" flow-agents workflow evidence \
|
|
442
|
+
(cd "$CONSUMER" && env -u CODEX_THREAD_ID CODEX_SESSION_ID=public-workflow-eval FLOW_AGENTS_EVIDENCE_COMMAND_KILL_GRACE_MS=50 npx --yes --package="file:$TARBALL" flow-agents workflow evidence \
|
|
443
443
|
--session-dir "$TIMEOUT_SESSION" --expectation pickup-probe-readiness --status pass \
|
|
444
444
|
--summary "Successful evidence cleans up surviving background processes." \
|
|
445
445
|
--command "bash checks/check-success-background.sh '$BACKGROUND_CHILD_PID' '$BACKGROUND_MARKER'" \
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
set -uo pipefail
|
|
14
14
|
|
|
15
15
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
16
|
+
CURRENT_POINTER_HELPER="$ROOT/scripts/hooks/lib/current-pointer.js"
|
|
16
17
|
|
|
17
18
|
TMPDIR_EVAL="$(mktemp -d)"
|
|
18
19
|
errors=0
|
|
@@ -25,6 +26,30 @@ trap cleanup EXIT
|
|
|
25
26
|
_pass() { echo " ✓ $1"; }
|
|
26
27
|
_fail() { echo " ✗ $1"; errors=$((errors + 1)); }
|
|
27
28
|
|
|
29
|
+
# seed_current_pointer <flow_agents_dir> <slug> <actor> — #440 FIXTURE-GAP: this suite's fixtures
|
|
30
|
+
# were written before #440's per-actor ownership scoping and never establish a per-actor current
|
|
31
|
+
# pointer for the invoking actor -- under a RESOLVED actor (every hook invocation below sets an
|
|
32
|
+
# explicit FLOW_AGENTS_ACTOR override, or derives one from an injected CLAUDE_CODE_SESSION_ID),
|
|
33
|
+
# workflow-steering.js's actorScopedWorkflowState now scopes to that actor's own (nonexistent)
|
|
34
|
+
# pointer and never reaches the fixture-under-test. Seeds BOTH the legacy current.json AND the
|
|
35
|
+
# per-actor current/<actor>.json pointer with the SAME payload, mirroring workflow-sidecar.ts's
|
|
36
|
+
# real writeCurrent() dual-write via current-pointer.js's own writePerActorCurrent.
|
|
37
|
+
seed_current_pointer() {
|
|
38
|
+
local flow_agents_dir="$1" slug="$2" actor="$3"
|
|
39
|
+
CP_HELPER_ARG="$CURRENT_POINTER_HELPER" DIR_ARG="$flow_agents_dir" SLUG_ARG="$slug" ACTOR_ARG="$actor" node - <<'NODE'
|
|
40
|
+
const fs = require('fs');
|
|
41
|
+
const path = require('path');
|
|
42
|
+
const { writePerActorCurrent } = require(process.env.CP_HELPER_ARG);
|
|
43
|
+
const dir = process.env.DIR_ARG;
|
|
44
|
+
const slug = process.env.SLUG_ARG;
|
|
45
|
+
const actor = process.env.ACTOR_ARG;
|
|
46
|
+
const payload = { schema_version: '1.0', active_slug: slug, artifact_dir: slug };
|
|
47
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
48
|
+
fs.writeFileSync(path.join(dir, 'current.json'), JSON.stringify(payload, null, 2) + '\n');
|
|
49
|
+
writePerActorCurrent(dir, actor, payload);
|
|
50
|
+
NODE
|
|
51
|
+
}
|
|
52
|
+
|
|
28
53
|
# ─── Portable sha256 helper ────────────────────────────────────────────────────
|
|
29
54
|
sha256_file() {
|
|
30
55
|
if command -v sha256sum >/dev/null 2>&1; then
|
|
@@ -112,6 +137,8 @@ FIVE_MIN_AGO="$(node -e "process.stdout.write(new Date(Date.now()-300000).toISOS
|
|
|
112
137
|
printf '{"type":"claim","subjectId":"test-slug-153","actor":"other-agent","at":"%s","ttlSeconds":1800}\n' "$FIVE_MIN_AGO" > "$REPO/.kontourai/flow-agents/liveness/events.jsonl"
|
|
113
138
|
printf '{"type":"heartbeat","subjectId":"test-slug-153","actor":"self-actor","at":"%s"}\n' "$FIVE_MIN_AGO" >> "$REPO/.kontourai/flow-agents/liveness/events.jsonl"
|
|
114
139
|
|
|
140
|
+
seed_current_pointer "$REPO/.kontourai/flow-agents" "$SLUG" "self-actor"
|
|
141
|
+
|
|
115
142
|
# ─── Snapshot checksums before hook run ───────────────────────────────────────
|
|
116
143
|
CKSUM_STATE_BEFORE="$(sha256_file "$TASK_DIR/state.json")"
|
|
117
144
|
CKSUM_HANDOFF_BEFORE="$(sha256_file "$TASK_DIR/handoff.json")"
|
|
@@ -260,6 +287,7 @@ cp "$TASK_DIR/handoff.json" "$TASK_DIR2/handoff.json"
|
|
|
260
287
|
cp "$TASK_DIR/trust.bundle" "$TASK_DIR2/trust.bundle"
|
|
261
288
|
printf 'test-slug-153--plan-work.md stub\n' > "$TASK_DIR2/test-slug-153--plan-work.md"
|
|
262
289
|
# No liveness directory → empty stream
|
|
290
|
+
seed_current_pointer "$REPO2/.kontourai/flow-agents" "$SLUG" "self-actor"
|
|
263
291
|
|
|
264
292
|
echo "{\"hook_event_name\":\"SessionStart\",\"cwd\":\"$REPO2\"}" | \
|
|
265
293
|
FLOW_AGENTS_ACTOR="self-actor" node "$ROOT/scripts/hooks/workflow-steering.js" > "$TMPDIR_EVAL/nolive.out" 2>&1
|
|
@@ -330,6 +358,7 @@ fi
|
|
|
330
358
|
FIVE_MIN_AGO_3="$(node -e "process.stdout.write(new Date(Date.now()-300000).toISOString().replace(/\\.\\d{3}Z$/,'Z'))")"
|
|
331
359
|
printf '{"type":"claim","subjectId":"%s","actor":"other-agent-derived","at":"%s","ttlSeconds":1800}\n' "$SLUG3" "$FIVE_MIN_AGO_3" > "$REPO3/.kontourai/flow-agents/liveness/events.jsonl"
|
|
332
360
|
printf '{"type":"heartbeat","subjectId":"%s","actor":"%s","at":"%s"}\n' "$SLUG3" "$DERIVED_SELF_ACTOR" "$FIVE_MIN_AGO_3" >> "$REPO3/.kontourai/flow-agents/liveness/events.jsonl"
|
|
361
|
+
seed_current_pointer "$REPO3/.kontourai/flow-agents" "$SLUG3" "$DERIVED_SELF_ACTOR"
|
|
333
362
|
|
|
334
363
|
echo "{\"hook_event_name\":\"SessionStart\",\"cwd\":\"$REPO3\"}" | \
|
|
335
364
|
CLAUDE_CODE_SESSION_ID="$SELF_SESSION_ID" FLOW_AGENTS_ACTOR="" node "$ROOT/scripts/hooks/workflow-steering.js" > "$TMPDIR_EVAL/derived-actor.out" 2>&1
|
|
@@ -397,6 +426,7 @@ const fs = require('fs');
|
|
|
397
426
|
const line = JSON.stringify({ type: 'claim', subjectId: process.argv[1], actor: JSON.parse(process.argv[2]), at: process.argv[3], ttlSeconds: 1800 });
|
|
398
427
|
fs.writeFileSync(process.argv[4], line + '\n');
|
|
399
428
|
" "$SLUG4" "$HOSTILE_ACTOR_JSON" "$FIVE_MIN_AGO_4" "$LIVENESS_FILE_4"
|
|
429
|
+
seed_current_pointer "$REPO4/.kontourai/flow-agents" "$SLUG4" "self-actor-t2-hostile-check"
|
|
400
430
|
|
|
401
431
|
echo "{\"hook_event_name\":\"SessionStart\",\"cwd\":\"$REPO4\"}" | \
|
|
402
432
|
FLOW_AGENTS_ACTOR="self-actor-t2-hostile-check" node "$ROOT/scripts/hooks/workflow-steering.js" > "$TMPDIR_EVAL/hostile-actor.out" 2>&1
|