@kontourai/flow-agents 2.2.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/CODEOWNERS +8 -0
- package/.github/workflows/ci.yml +32 -0
- package/AGENTS.md +4 -4
- package/CHANGELOG.md +46 -0
- package/CONTEXT.md +14 -0
- package/README.md +9 -3
- package/agents/dev.json +1 -1
- package/agents/tool-code-reviewer.json +1 -1
- package/agents/tool-planner.json +3 -3
- package/agents/tool-verifier.json +3 -3
- package/build/src/cli/console-learning-projection.js +3 -2
- package/build/src/cli/init.js +104 -21
- package/build/src/cli/kit.js +37 -6
- package/build/src/cli/pull-work-provider.js +1 -1
- package/build/src/cli/usage-feedback.js +3 -3
- package/build/src/cli/validate-hook-influence.js +1 -0
- package/build/src/cli/validate-workflow-artifacts.js +22 -6
- package/build/src/cli/workflow-artifact-cleanup-audit.js +36 -2
- package/build/src/cli/workflow-sidecar.d.ts +14 -7
- package/build/src/cli/workflow-sidecar.js +792 -69
- package/build/src/flow-kit/validate.d.ts +23 -0
- package/build/src/flow-kit/validate.js +67 -1
- package/build/src/index.d.ts +1 -1
- package/build/src/index.js +1 -1
- package/build/src/lib/flow-resolver.d.ts +6 -4
- package/build/src/lib/flow-resolver.js +125 -58
- package/build/src/lib/local-artifact-root.d.ts +14 -2
- package/build/src/lib/local-artifact-root.js +22 -5
- package/build/src/lib/workflow-learning-projection.js +2 -2
- package/build/src/runtime-adapters.d.ts +12 -0
- package/build/src/runtime-adapters.js +56 -16
- package/build/src/tools/build-universal-bundles.js +49 -16
- package/build/src/tools/generate-context-map.js +10 -8
- package/build/src/tools/validate-source-tree.js +23 -1
- package/console.telemetry.json +22 -0
- package/context/contracts/artifact-contract.md +38 -7
- package/context/contracts/builder-kit-workflow-state-contract.md +3 -2
- package/context/contracts/decision-registry-contract.md +263 -0
- package/context/contracts/delivery-contract.md +1 -1
- package/context/contracts/planning-contract.md +2 -1
- package/context/contracts/review-contract.md +5 -3
- package/context/contracts/verification-contract.md +9 -1
- package/context/contracts/work-item-contract.md +1 -1
- package/context/deferred/parallelization.md +1 -1
- package/context/gate-awareness.md +4 -4
- package/context/scripts/git-status.sh +2 -2
- package/context/scripts/hooks/config-protection.js +502 -2
- package/context/scripts/hooks/lib/config-protection-remedies.js +69 -0
- package/context/scripts/hooks/run-hook.js +14 -1
- package/context/scripts/hooks/stop-goal-fit.js +1140 -99
- package/context/scripts/hooks/workflow-steering.js +212 -6
- package/docs/adr/0007-flow-skill-kit-tool-boundary.md +2 -0
- package/docs/adr/0015-flow-flow-agents-boundary-reconciliation.md +35 -0
- package/docs/adr/0019-kit-dependency-ownership.md +62 -0
- package/docs/adr/0020-trust-reconcile-manifest-and-claim-classification.md +290 -0
- package/docs/adr/0021-assignment-leases-and-stale-claim-takeover.md +221 -0
- package/docs/adr/0022-fail-closed-delivery-reconciliation-with-governed-exemptions.md +283 -0
- package/docs/agent-system-guidebook.md +3 -3
- package/docs/agent-usage-feedback-loop.md +6 -6
- package/docs/context-map.md +9 -7
- package/docs/decisions/decision-records.md +57 -0
- package/docs/decisions/index.md +14 -0
- package/docs/decisions/promotion-gate.md +52 -0
- package/docs/developer-architecture.md +1 -1
- package/docs/fixture-ownership.md +3 -0
- package/docs/flow-kit-repository-contract.md +5 -5
- package/docs/getting-started.md +12 -2
- package/docs/integrations/harness-install.md +2 -1
- package/docs/kit-authoring-guide.md +64 -5
- package/docs/knowledge-kit.md +5 -1
- package/docs/learnings/2026-07-improvement-program.md +203 -0
- package/docs/migrations.md +6 -5
- package/docs/north-star.md +2 -2
- package/docs/operating-layers.md +4 -3
- package/docs/repository-structure.md +5 -5
- package/docs/skills-map.md +8 -8
- package/docs/spec/runtime-hook-surface.md +19 -15
- package/docs/spikes/graph-provider-2026-07.md +155 -0
- package/docs/standards-register.md +7 -7
- package/docs/trust-anchor-adoption.md +105 -4
- package/docs/work-item-adapters.md +1 -1
- package/docs/workflow-artifact-lifecycle.md +72 -9
- package/docs/workflow-eval-strategy.md +3 -3
- package/docs/workflow-shared-contracts.md +1 -1
- package/docs/workflow-usage-guide.md +254 -21
- package/evals/acceptance/DEMO-false-completion.md +20 -11
- package/evals/acceptance/prove-capture-teeth-declared.sh +15 -15
- package/evals/acceptance/prove-capture-teeth.sh +11 -11
- package/evals/acceptance/test_kiro_harness.sh +1 -1
- package/evals/ci/antigaming-suite.sh +5 -0
- package/evals/ci/run-baseline.sh +65 -1
- package/evals/fixtures/flow-kit-repository/invalid-bad-dependency/flows/review.flow.json +26 -0
- package/evals/fixtures/flow-kit-repository/invalid-bad-dependency/kit.json +13 -0
- package/evals/fixtures/flow-kit-repository/valid-with-dependency/flows/review.flow.json +26 -0
- package/evals/fixtures/flow-kit-repository/valid-with-dependency/kit.json +13 -0
- package/evals/fixtures/hook-influence/cases.json +32 -0
- package/evals/fixtures/pull-work-provider/github-issues.json +6 -6
- package/evals/fixtures/trust-reconcile-exploits/fabricated-attestation.json +59 -0
- package/evals/fixtures/trust-reconcile-exploits/no-label-bypass.json +36 -0
- package/evals/fixtures/trust-reconcile-exploits/skip-assumed-bypass.json +59 -0
- package/evals/fixtures/trust-reconcile-exploits/status-misassertion.json +48 -0
- package/evals/fixtures/trust-reconcile-exploits/waived-command-check.json +44 -0
- package/evals/fixtures/trust-reconcile-mixed-bundle/mixed-bundle.json +159 -0
- package/evals/fixtures/trust-reconcile-ws3/ws3-bundle.json +920 -0
- package/evals/integration/test_actor_identity.sh +254 -0
- package/evals/integration/test_builder_step_producers.sh +33 -23
- package/evals/integration/test_bundle_install.sh +22 -16
- package/evals/integration/test_bundle_lifecycle.sh +11 -11
- package/evals/integration/test_captured_fail_reconciliation.sh +50 -50
- package/evals/integration/test_checkpoint_signing.sh +43 -0
- package/evals/integration/test_claim_lookup.sh +4 -4
- package/evals/integration/test_codex_hook_resolution.sh +114 -0
- package/evals/integration/test_command_log_concurrency.sh +8 -8
- package/evals/integration/test_command_log_fork_classification.sh +3 -3
- package/evals/integration/test_command_log_integrity.sh +16 -16
- package/evals/integration/test_console_learning_projection.sh +1 -1
- package/evals/integration/test_dual_emit_flow_step.sh +145 -0
- package/evals/integration/test_enforcer_expects_driven.sh +12 -12
- package/evals/integration/test_evidence_capture_hook.sh +14 -13
- package/evals/integration/test_fixture_retirement_audit.sh +2 -2
- package/evals/integration/test_flow_agents_statusline.sh +2 -2
- package/evals/integration/test_flow_kit_install_git.sh +52 -0
- package/evals/integration/test_flow_kit_repository.sh +2 -0
- package/evals/integration/test_flowdef_session_activation.sh +6 -6
- package/evals/integration/test_flowdef_union_floor_regression.sh +429 -0
- package/evals/integration/test_gate_bypass_chain.sh +52 -40
- package/evals/integration/test_gate_lockdown.sh +97 -73
- package/evals/integration/test_gate_review_inquiry_records.sh +3 -3
- package/evals/integration/test_goal_fit_escape_hatch.sh +10 -10
- package/evals/integration/test_goal_fit_ghost_session.sh +104 -0
- package/evals/integration/test_goal_fit_hook.sh +32 -32
- package/evals/integration/test_goal_fit_rederive.sh +9 -9
- package/evals/integration/test_hook_category_behaviors.sh +103 -0
- package/evals/integration/test_hook_influence_cases.sh +1 -0
- package/evals/integration/test_install_merge.sh +262 -1
- package/evals/integration/test_kit_identity_trust.sh +6 -6
- package/evals/integration/test_liveness_conflict_injection.sh +587 -0
- package/evals/integration/test_liveness_heartbeat.sh +677 -0
- package/evals/integration/test_liveness_verdict.sh +394 -0
- package/evals/integration/test_local_flow_kit_install.sh +15 -0
- package/evals/integration/test_phase_map_and_gate_claim.sh +63 -11
- package/evals/integration/test_promote_gate.sh +178 -0
- package/evals/integration/test_publish_delivery.sh +2 -2
- package/evals/integration/test_pull_work_liveness_preflight.sh +386 -0
- package/evals/integration/test_pull_work_provider.sh +2 -2
- package/evals/integration/test_reconcile_soundness.sh +4 -4
- package/evals/integration/test_resolvefirststep_security.sh +22 -0
- package/evals/integration/test_runtime_adapter_activation.sh +87 -18
- package/evals/integration/test_session_resume_roundtrip.sh +147 -0
- package/evals/integration/test_sidecar_field_preservation.sh +249 -0
- package/evals/integration/test_trust_reconcile.sh +2 -2
- package/evals/integration/test_trust_reconcile_manifest.sh +79 -0
- package/evals/integration/test_trust_reconcile_mixed_bundle.sh +96 -0
- package/evals/integration/test_trust_reconcile_negatives.sh +151 -0
- package/evals/integration/test_usage_feedback_global.sh +5 -5
- package/evals/integration/test_validate_artifacts_portability.sh +60 -0
- package/evals/integration/test_verify_cli.sh +2 -2
- package/evals/integration/test_veritas_governance_kit.sh +117 -0
- package/evals/integration/test_workflow_artifact_cleanup_audit.sh +26 -1
- package/evals/integration/test_workflow_artifacts.sh +33 -0
- package/evals/integration/test_workflow_sidecar_writer.sh +723 -74
- package/evals/integration/test_workflow_steering_hook.sh +175 -5
- package/evals/lib/codex-provider.sh +1 -1
- package/evals/lib/node.sh +19 -1
- package/evals/run.sh +22 -0
- package/evals/static/test_decisions.sh +150 -0
- package/evals/static/test_flowdef_codeowners_coverage.sh +56 -0
- package/evals/static/test_package.sh +19 -10
- package/evals/static/test_universal_bundles.sh +48 -3
- package/evals/static/test_workflow_skills.sh +41 -8
- package/kits/builder/flows/build.flow.json +3 -74
- package/kits/builder/flows/publish-learn.flow.json +90 -0
- package/kits/builder/kit.json +11 -0
- package/kits/builder/skills/builder-shape/SKILL.md +1 -1
- package/kits/builder/skills/deliver/SKILL.md +13 -11
- package/kits/builder/skills/design-probe/SKILL.md +4 -4
- package/kits/builder/skills/evidence-gate/SKILL.md +26 -1
- package/kits/builder/skills/execute-plan/SKILL.md +2 -2
- package/kits/builder/skills/fix-bug/SKILL.md +2 -0
- package/kits/builder/skills/gate-review/SKILL.md +6 -6
- package/kits/builder/skills/idea-to-backlog/SKILL.md +1 -1
- package/kits/builder/skills/learning-review/SKILL.md +7 -7
- package/kits/builder/skills/pickup-probe/SKILL.md +5 -4
- package/kits/builder/skills/plan-work/SKILL.md +27 -5
- package/kits/builder/skills/pull-work/SKILL.md +80 -5
- package/kits/builder/skills/release-readiness/SKILL.md +4 -4
- package/kits/builder/skills/review-work/SKILL.md +24 -1
- package/kits/builder/skills/tdd-workflow/SKILL.md +2 -0
- package/kits/builder/skills/verify-work/SKILL.md +8 -1
- package/kits/catalog.json +6 -0
- package/kits/veritas-governance/adapter/readiness-to-trust-bundle.mjs +178 -0
- package/kits/veritas-governance/docs/README.md +75 -0
- package/kits/veritas-governance/fixtures/readiness/not-ready.readiness-report.json +4645 -0
- package/kits/veritas-governance/fixtures/readiness/ready.readiness-report.json +4403 -0
- package/kits/veritas-governance/flows/readiness-check.flow.json +35 -0
- package/kits/veritas-governance/kit.json +16 -0
- package/package.json +8 -4
- package/packaging/README.md +2 -2
- package/packaging/conformance/fixtures/config-protection--allow-read-state-json.json +20 -0
- package/packaging/conformance/fixtures/config-protection--block-write-state-json.json +20 -0
- package/packaging/conformance/fixtures/stop-goal-fit--block-bundle-disputed-claim.json +1 -1
- package/packaging/conformance/fixtures/stop-goal-fit--block-capture-contradicts-claimed-pass.json +1 -1
- package/packaging/manifest.json +9 -10
- package/schemas/decision-record.schema.json +109 -0
- package/schemas/workflow-evidence.schema.json +1 -1
- package/schemas/workflow-state.schema.json +5 -0
- package/scripts/README.md +8 -3
- package/scripts/check-content-boundary.cjs +1 -1
- package/scripts/check-decisions.cjs +356 -0
- package/scripts/ci/derive-claim-status.mjs +74 -0
- package/scripts/ci/trust-reconcile.js +436 -79
- package/scripts/git-status.sh +2 -2
- package/scripts/hooks/claude-telemetry-hook.js +37 -3
- package/scripts/hooks/codex-telemetry-hook.js +36 -2
- package/scripts/hooks/config-protection.js +102 -30
- package/scripts/hooks/evidence-capture.js +3 -3
- package/scripts/hooks/lib/actor-identity.js +368 -0
- package/scripts/hooks/lib/config-protection-remedies.js +69 -0
- package/scripts/hooks/lib/liveness-heartbeat.js +361 -0
- package/scripts/hooks/lib/liveness-policy.js +127 -0
- package/scripts/hooks/lib/liveness-read.js +105 -12
- package/scripts/hooks/lib/liveness-write.js +46 -0
- package/scripts/hooks/lib/local-artifact-paths.js +12 -6
- package/scripts/hooks/opencode-telemetry-hook.js +27 -1
- package/scripts/hooks/pi-telemetry-hook.js +27 -1
- package/scripts/hooks/stop-goal-fit.js +157 -26
- package/scripts/hooks/workflow-steering.js +77 -3
- package/scripts/install-codex-home.sh +138 -38
- package/scripts/statusline/flow-agents-statusline.js +2 -2
- package/src/cli/console-learning-projection.ts +3 -2
- package/src/cli/init.ts +101 -21
- package/src/cli/kit.ts +37 -6
- package/src/cli/public-api.test.mjs +58 -6
- package/src/cli/pull-work-provider.ts +1 -1
- package/src/cli/trust-bundle-policy-order.test.mjs +87 -0
- package/src/cli/usage-feedback.ts +3 -3
- package/src/cli/validate-hook-influence.ts +1 -0
- package/src/cli/validate-workflow-artifacts.ts +22 -6
- package/src/cli/veritas-readiness-adapter.test.mjs +267 -0
- package/src/cli/workflow-artifact-cleanup-audit.ts +35 -2
- package/src/cli/workflow-sidecar.ts +795 -71
- package/src/flow-kit/validate.ts +74 -1
- package/src/index.ts +6 -2
- package/src/lib/flow-resolver.ts +123 -55
- package/src/lib/local-artifact-root.ts +24 -5
- package/src/lib/workflow-learning-projection.ts +2 -2
- package/src/runtime-adapters.ts +61 -15
- package/src/tools/build-universal-bundles.ts +49 -16
- package/src/tools/generate-context-map.ts +10 -8
- package/src/tools/validate-source-tree.ts +23 -1
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_actor_identity.sh - unit-style eval for the shared actor-identity resolver
|
|
3
|
+
# (scripts/hooks/lib/actor-identity.js). Written against the frozen interface documented
|
|
4
|
+
# in .kontourai/flow-agents/kontourai-flow-agents-287/kontourai-flow-agents-287--plan-work.md
|
|
5
|
+
# Wave 1 Task 1.1 (detectRuntime/runtimeSessionId/ancestorActorSeed/sanitizeSegment/
|
|
6
|
+
# serializeActor/resolveActor).
|
|
7
|
+
set -uo pipefail
|
|
8
|
+
|
|
9
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
10
|
+
MODULE="$ROOT/scripts/hooks/lib/actor-identity.js"
|
|
11
|
+
|
|
12
|
+
errors=0
|
|
13
|
+
|
|
14
|
+
_pass() { echo " ✓ $1"; }
|
|
15
|
+
_fail() { echo " ✗ $1"; errors=$((errors + 1)); }
|
|
16
|
+
|
|
17
|
+
if [[ ! -f "$MODULE" ]]; then
|
|
18
|
+
echo "actor identity eval skipped: $MODULE does not exist yet." >&2
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# 1. serializeActor never contains "::" or a raw ":" inside any single segment, even given
|
|
23
|
+
# adversarial input (session_id containing ":", "/", spaces, unicode).
|
|
24
|
+
if node - "$MODULE" <<'NODE'
|
|
25
|
+
const path = process.argv[2];
|
|
26
|
+
const { serializeActor } = require(path);
|
|
27
|
+
const out = serializeActor({
|
|
28
|
+
runtime: "claude-code",
|
|
29
|
+
session_id: "sess:with/slash space:moreéé",
|
|
30
|
+
host: "ho:st/name",
|
|
31
|
+
human: "hu man:name",
|
|
32
|
+
});
|
|
33
|
+
if (typeof out !== "string" || out.length === 0) {
|
|
34
|
+
throw new Error(`serializeActor did not return a non-empty string: ${JSON.stringify(out)}`);
|
|
35
|
+
}
|
|
36
|
+
if (out.includes("::")) {
|
|
37
|
+
throw new Error(`serializeActor output contains "::": ${JSON.stringify(out)}`);
|
|
38
|
+
}
|
|
39
|
+
if (!/^[A-Za-z0-9_.:-]+$/.test(out)) {
|
|
40
|
+
throw new Error(`serializeActor output contains disallowed characters: ${JSON.stringify(out)}`);
|
|
41
|
+
}
|
|
42
|
+
NODE
|
|
43
|
+
then
|
|
44
|
+
_pass "serializeActor sanitizes adversarial segments (no :: and no raw : inside a segment)"
|
|
45
|
+
else
|
|
46
|
+
_fail "serializeActor did not sanitize adversarial input safely"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# 2. resolveActor({FLOW_AGENTS_ACTOR: "my-actor"}) returns {actor: "my-actor", ...} — explicit
|
|
50
|
+
# override always wins, even if a runtime var is also present in the injected env.
|
|
51
|
+
if node - "$MODULE" <<'NODE'
|
|
52
|
+
const path = process.argv[2];
|
|
53
|
+
const { resolveActor } = require(path);
|
|
54
|
+
const out = resolveActor({ FLOW_AGENTS_ACTOR: "my-actor", CLAUDE_CODE_SESSION_ID: "sess-should-be-ignored" });
|
|
55
|
+
if (!out || out.actor !== "my-actor") {
|
|
56
|
+
throw new Error(`resolveActor did not honor explicit FLOW_AGENTS_ACTOR override: ${JSON.stringify(out)}`);
|
|
57
|
+
}
|
|
58
|
+
NODE
|
|
59
|
+
then
|
|
60
|
+
_pass "resolveActor honors explicit FLOW_AGENTS_ACTOR override even with a runtime var present"
|
|
61
|
+
else
|
|
62
|
+
_fail "resolveActor did not honor explicit FLOW_AGENTS_ACTOR override"
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# 3. resolveActor({FLOW_AGENTS_ACTOR: "local"}) does NOT honor the literal "local" override
|
|
66
|
+
# (falls through to runtime/ancestry resolution) — proves "local" can never round-trip back
|
|
67
|
+
# in via the override seam.
|
|
68
|
+
if node - "$MODULE" <<'NODE'
|
|
69
|
+
const path = process.argv[2];
|
|
70
|
+
const { resolveActor } = require(path);
|
|
71
|
+
const out = resolveActor({ FLOW_AGENTS_ACTOR: "local" });
|
|
72
|
+
if (!out || typeof out.actor !== "string" || out.actor.trim().toLowerCase() === "local") {
|
|
73
|
+
throw new Error(`resolveActor honored the literal "local" override: ${JSON.stringify(out)}`);
|
|
74
|
+
}
|
|
75
|
+
NODE
|
|
76
|
+
then
|
|
77
|
+
_pass "resolveActor never honors the literal \"local\" override"
|
|
78
|
+
else
|
|
79
|
+
_fail "resolveActor incorrectly honored the literal \"local\" override"
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
# 4. resolveActor({CLAUDE_CODE_SESSION_ID: "sess-a"}) vs resolveActor({CLAUDE_CODE_SESSION_ID:
|
|
83
|
+
# "sess-b"}) (host held constant) produce two different serialized actors.
|
|
84
|
+
if node - "$MODULE" <<'NODE'
|
|
85
|
+
const path = process.argv[2];
|
|
86
|
+
const { resolveActor } = require(path);
|
|
87
|
+
const a = resolveActor({ CLAUDE_CODE_SESSION_ID: "sess-a" });
|
|
88
|
+
const b = resolveActor({ CLAUDE_CODE_SESSION_ID: "sess-b" });
|
|
89
|
+
if (!a || !b || !a.actor || !b.actor) {
|
|
90
|
+
throw new Error(`resolveActor returned an empty actor for a runtime-native session id: a=${JSON.stringify(a)} b=${JSON.stringify(b)}`);
|
|
91
|
+
}
|
|
92
|
+
if (a.actor === b.actor) {
|
|
93
|
+
throw new Error(`resolveActor produced the same actor for two distinct session ids: ${JSON.stringify(a)} vs ${JSON.stringify(b)}`);
|
|
94
|
+
}
|
|
95
|
+
NODE
|
|
96
|
+
then
|
|
97
|
+
_pass "resolveActor produces distinct actors for distinct runtime session ids"
|
|
98
|
+
else
|
|
99
|
+
_fail "resolveActor did not distinguish between distinct runtime session ids"
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# 5. Stability test: call resolveActor({}) (real ambient env, no runtime var, forcing the
|
|
103
|
+
# ancestry path) twice in the same process with a real setTimeout gap of several seconds
|
|
104
|
+
# between calls; assert byte-identical output both times.
|
|
105
|
+
if node - "$MODULE" <<'NODE'
|
|
106
|
+
const path = process.argv[2];
|
|
107
|
+
const { resolveActor } = require(path);
|
|
108
|
+
const first = resolveActor({});
|
|
109
|
+
setTimeout(() => {
|
|
110
|
+
const second = resolveActor({});
|
|
111
|
+
const a = JSON.stringify(first);
|
|
112
|
+
const b = JSON.stringify(second);
|
|
113
|
+
if (a !== b) {
|
|
114
|
+
console.error(`resolveActor({}) was not stable across a delay: first=${a} second=${b}`);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
process.exit(0);
|
|
118
|
+
}, 3000);
|
|
119
|
+
NODE
|
|
120
|
+
then
|
|
121
|
+
_pass "resolveActor({}) is byte-identical across a several-second real-time gap (ancestry stability)"
|
|
122
|
+
else
|
|
123
|
+
_fail "resolveActor({}) was not stable across a several-second real-time gap"
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
# 6. resolveActor({FLOW_AGENTS_ACTOR_TEST_FORCE_UNRESOLVED: "1", NODE_ENV: "test"}) returns
|
|
127
|
+
# {actor: "", source: "test-forced-unresolved"} — the hatch requires BOTH the force-unresolved
|
|
128
|
+
# var AND NODE_ENV=test (F4, #287 fix iteration 1); the var alone (no NODE_ENV=test) must NOT
|
|
129
|
+
# trip the hatch, proving it cannot be tripped by an accidental/malicious env var outside a
|
|
130
|
+
# real test harness.
|
|
131
|
+
if node - "$MODULE" <<'NODE'
|
|
132
|
+
const path = process.argv[2];
|
|
133
|
+
const { resolveActor } = require(path);
|
|
134
|
+
const out = resolveActor({ FLOW_AGENTS_ACTOR_TEST_FORCE_UNRESOLVED: "1", NODE_ENV: "test" });
|
|
135
|
+
if (!out || out.actor !== "" || out.source !== "test-forced-unresolved") {
|
|
136
|
+
throw new Error(`resolveActor did not honor the test-forced-unresolved escape hatch: ${JSON.stringify(out)}`);
|
|
137
|
+
}
|
|
138
|
+
NODE
|
|
139
|
+
then
|
|
140
|
+
_pass "resolveActor honors FLOW_AGENTS_ACTOR_TEST_FORCE_UNRESOLVED escape hatch when NODE_ENV=test is also set"
|
|
141
|
+
else
|
|
142
|
+
_fail "resolveActor did not honor FLOW_AGENTS_ACTOR_TEST_FORCE_UNRESOLVED escape hatch with NODE_ENV=test"
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
# 6b (F4 negative). The hatch var alone, WITHOUT NODE_ENV=test, must NOT force an unresolved actor
|
|
146
|
+
# — proves the gate is a real AND, not an OR.
|
|
147
|
+
if node - "$MODULE" <<'NODE'
|
|
148
|
+
const path = process.argv[2];
|
|
149
|
+
const { resolveActor } = require(path);
|
|
150
|
+
const out = resolveActor({ FLOW_AGENTS_ACTOR_TEST_FORCE_UNRESOLVED: "1" });
|
|
151
|
+
if (out && out.source === "test-forced-unresolved") {
|
|
152
|
+
throw new Error(`resolveActor honored the test-forced-unresolved escape hatch without NODE_ENV=test: ${JSON.stringify(out)}`);
|
|
153
|
+
}
|
|
154
|
+
NODE
|
|
155
|
+
then
|
|
156
|
+
_pass "resolveActor does NOT honor the escape hatch when NODE_ENV=test is missing (F4 gate is AND, not OR)"
|
|
157
|
+
else
|
|
158
|
+
_fail "resolveActor incorrectly honored the escape hatch without NODE_ENV=test"
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
# 7 (T1, F2). Adversarial FLOW_AGENTS_ACTOR override — containing "::", a raw newline, and
|
|
162
|
+
# exceeding 64 chars — resolves to a sanitized, capped actor: no ":" anywhere, length <= 64.
|
|
163
|
+
if node - "$MODULE" <<'NODE'
|
|
164
|
+
const path = process.argv[2];
|
|
165
|
+
const { resolveActor } = require(path);
|
|
166
|
+
const adversarial = "a::b\nc" + "x".repeat(100);
|
|
167
|
+
const out = resolveActor({ FLOW_AGENTS_ACTOR: adversarial });
|
|
168
|
+
if (!out || typeof out.actor !== "string" || !out.actor) {
|
|
169
|
+
throw new Error(`resolveActor returned no actor for adversarial override: ${JSON.stringify(out)}`);
|
|
170
|
+
}
|
|
171
|
+
if (out.actor.includes(":")) {
|
|
172
|
+
throw new Error(`resolveActor did not strip ":" from adversarial override: ${JSON.stringify(out)}`);
|
|
173
|
+
}
|
|
174
|
+
if (out.actor.includes("\n")) {
|
|
175
|
+
throw new Error(`resolveActor did not strip newline from adversarial override: ${JSON.stringify(out)}`);
|
|
176
|
+
}
|
|
177
|
+
if (out.actor.length > 64) {
|
|
178
|
+
throw new Error(`resolveActor did not cap adversarial override at 64 chars: ${JSON.stringify(out)}`);
|
|
179
|
+
}
|
|
180
|
+
NODE
|
|
181
|
+
then
|
|
182
|
+
_pass "resolveActor sanitizes and caps an adversarial FLOW_AGENTS_ACTOR override (:: / newline / >64 chars)"
|
|
183
|
+
else
|
|
184
|
+
_fail "resolveActor did not sanitize/cap an adversarial FLOW_AGENTS_ACTOR override"
|
|
185
|
+
fi
|
|
186
|
+
|
|
187
|
+
# 8 (T1, F3). Rejected FLOW_AGENTS_ACTOR=local (case-insensitive) emits exactly one stderr warning
|
|
188
|
+
# line naming FLOW_AGENTS_ACTOR — never on stdout, never a thrown exception.
|
|
189
|
+
TMPDIR_EVAL_STDERR_CAPTURE="$(mktemp)"
|
|
190
|
+
if node - "$MODULE" >"$TMPDIR_EVAL_STDERR_CAPTURE.out" 2>"$TMPDIR_EVAL_STDERR_CAPTURE.err" <<'NODE'
|
|
191
|
+
const path = process.argv[2];
|
|
192
|
+
const { resolveActor } = require(path);
|
|
193
|
+
const out = resolveActor({ FLOW_AGENTS_ACTOR: "LOCAL" });
|
|
194
|
+
process.stdout.write(JSON.stringify(out));
|
|
195
|
+
NODE
|
|
196
|
+
then
|
|
197
|
+
if grep -qF "FLOW_AGENTS_ACTOR=local" "$TMPDIR_EVAL_STDERR_CAPTURE.err" \
|
|
198
|
+
&& ! grep -qF "FLOW_AGENTS_ACTOR=local" "$TMPDIR_EVAL_STDERR_CAPTURE.out"; then
|
|
199
|
+
_pass "rejected FLOW_AGENTS_ACTOR=local (case-insensitive) emits one stderr warning, never on stdout"
|
|
200
|
+
else
|
|
201
|
+
_fail "rejected FLOW_AGENTS_ACTOR=local warning missing from stderr or leaked onto stdout: out=$(cat "$TMPDIR_EVAL_STDERR_CAPTURE.out") err=$(cat "$TMPDIR_EVAL_STDERR_CAPTURE.err")"
|
|
202
|
+
fi
|
|
203
|
+
else
|
|
204
|
+
_fail "resolveActor threw for a rejected FLOW_AGENTS_ACTOR=local override (must never throw)"
|
|
205
|
+
fi
|
|
206
|
+
rm -f "$TMPDIR_EVAL_STDERR_CAPTURE.out" "$TMPDIR_EVAL_STDERR_CAPTURE.err"
|
|
207
|
+
|
|
208
|
+
# 9 (T4, F7). resolveActor({FLOW_AGENTS_ACTOR: ":::"}) — a value that strips to empty under the
|
|
209
|
+
# allowed [A-Za-z0-9_.-] charset — must NOT adopt the shared "unknown" sentinel: it falls through
|
|
210
|
+
# to derivation (source != "explicit-override", actor != "unknown") and emits one stderr warning,
|
|
211
|
+
# never stdout, never a thrown exception.
|
|
212
|
+
TMPDIR_EVAL_STRIPEMPTY_CAPTURE="$(mktemp)"
|
|
213
|
+
if node - "$MODULE" >"$TMPDIR_EVAL_STRIPEMPTY_CAPTURE.out" 2>"$TMPDIR_EVAL_STRIPEMPTY_CAPTURE.err" <<'NODE'
|
|
214
|
+
const path = process.argv[2];
|
|
215
|
+
const { resolveActor } = require(path);
|
|
216
|
+
const out = resolveActor({ FLOW_AGENTS_ACTOR: ":::" });
|
|
217
|
+
process.stdout.write(JSON.stringify(out));
|
|
218
|
+
NODE
|
|
219
|
+
then
|
|
220
|
+
STRIPEMPTY_OUT="$(cat "$TMPDIR_EVAL_STRIPEMPTY_CAPTURE.out")"
|
|
221
|
+
if echo "$STRIPEMPTY_OUT" | node -e 'const out = JSON.parse(require("fs").readFileSync(0, "utf8")); if (!out || out.actor === "unknown" || out.source === "explicit-override") process.exit(1);' && grep -qF "FLOW_AGENTS_ACTOR" "$TMPDIR_EVAL_STRIPEMPTY_CAPTURE.err" && ! grep -qF "FLOW_AGENTS_ACTOR" "$TMPDIR_EVAL_STRIPEMPTY_CAPTURE.out"; then
|
|
222
|
+
_pass "resolveActor({FLOW_AGENTS_ACTOR: ':::'}) falls through to a derived actor (not 'unknown', not explicit-override) with a stderr warning"
|
|
223
|
+
else
|
|
224
|
+
_fail "resolveActor did not fall through correctly for a strip-to-empty override: out=$STRIPEMPTY_OUT err=$(cat "$TMPDIR_EVAL_STRIPEMPTY_CAPTURE.err")"
|
|
225
|
+
fi
|
|
226
|
+
else
|
|
227
|
+
_fail "resolveActor threw for a strip-to-empty FLOW_AGENTS_ACTOR override (must never throw)"
|
|
228
|
+
fi
|
|
229
|
+
rm -f "$TMPDIR_EVAL_STRIPEMPTY_CAPTURE.out" "$TMPDIR_EVAL_STRIPEMPTY_CAPTURE.err"
|
|
230
|
+
|
|
231
|
+
# 10 (T4, F7). resolveActor({FLOW_AGENTS_ACTOR: "unknown"}) — a deliberate literal "unknown" — is
|
|
232
|
+
# honored as-is via the explicit-override path (proves the sentinel itself is not blacklisted;
|
|
233
|
+
# only a strip-to-empty value is rejected).
|
|
234
|
+
if node - "$MODULE" <<'NODE'
|
|
235
|
+
const path = process.argv[2];
|
|
236
|
+
const { resolveActor } = require(path);
|
|
237
|
+
const out = resolveActor({ FLOW_AGENTS_ACTOR: "unknown" });
|
|
238
|
+
if (!out || out.actor !== "unknown" || out.source !== "explicit-override") {
|
|
239
|
+
throw new Error(`resolveActor did not honor a deliberate literal "unknown" override: ${JSON.stringify(out)}`);
|
|
240
|
+
}
|
|
241
|
+
NODE
|
|
242
|
+
then
|
|
243
|
+
_pass "resolveActor honors a deliberate literal FLOW_AGENTS_ACTOR=unknown override (sentinel itself is not blacklisted)"
|
|
244
|
+
else
|
|
245
|
+
_fail "resolveActor did not honor a deliberate literal FLOW_AGENTS_ACTOR=unknown override"
|
|
246
|
+
fi
|
|
247
|
+
|
|
248
|
+
if [[ "$errors" -eq 0 ]]; then
|
|
249
|
+
echo "Actor identity resolver integration passed."
|
|
250
|
+
exit 0
|
|
251
|
+
fi
|
|
252
|
+
|
|
253
|
+
echo "Actor identity resolver integration failed: $errors issue(s)."
|
|
254
|
+
exit 1
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
# 5. builder.learn.decisions (step: learn, expectation: decision-evidence)
|
|
16
16
|
# 6. builder.learn.evidence (step: learn, expectation: learning-evidence)
|
|
17
17
|
#
|
|
18
|
-
#
|
|
19
|
-
# - All 6 claims above are required:true.
|
|
18
|
+
# Flow Definition confirmation:
|
|
19
|
+
# - All 6 claims above are required:true across builder.build and builder.publish-learn.
|
|
20
20
|
# - policy-compliance remains required:false (advisory — no skill producer).
|
|
21
21
|
#
|
|
22
22
|
# Deterministic, no model spend, self-cleaning.
|
|
@@ -106,21 +106,21 @@ setup_tamper_session() {
|
|
|
106
106
|
local t_dir="$1" slug="$2" step="$3"
|
|
107
107
|
mkdir -p "$t_dir"
|
|
108
108
|
printf '# Repo\n' > "$t_dir/AGENTS.md"
|
|
109
|
-
mkdir -p "$t_dir/.flow-agents/$slug"
|
|
109
|
+
mkdir -p "$t_dir/.kontourai/flow-agents/$slug"
|
|
110
110
|
|
|
111
111
|
flow_agents_node "workflow-sidecar" ensure-session \
|
|
112
|
-
--artifact-root "$t_dir/.flow-agents" \
|
|
112
|
+
--artifact-root "$t_dir/.kontourai/flow-agents" \
|
|
113
113
|
--task-slug "$slug" \
|
|
114
114
|
--title "Tamper test: $step" \
|
|
115
115
|
--summary "Testing tamper detection." \
|
|
116
116
|
--flow-id builder.build \
|
|
117
117
|
--timestamp "2026-06-26T00:00:00Z" >/dev/null 2>&1
|
|
118
118
|
|
|
119
|
-
flow_agents_node "workflow-sidecar" init-plan "$t_dir/.flow-agents/$slug/$slug--deliver.md" \
|
|
119
|
+
flow_agents_node "workflow-sidecar" init-plan "$t_dir/.kontourai/flow-agents/$slug/$slug--deliver.md" \
|
|
120
120
|
--source-request "Test" --summary "Testing" \
|
|
121
121
|
--timestamp "2026-06-26T00:00:00Z" >/dev/null 2>&1
|
|
122
122
|
|
|
123
|
-
set_active_step "$t_dir/.flow-agents" "$slug" "$step"
|
|
123
|
+
set_active_step "$t_dir/.kontourai/flow-agents" "$slug" "$step"
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
# ─── Test: produce a gate claim at a given step ───────────────────────────────
|
|
@@ -178,12 +178,12 @@ test_tamper_blocks() {
|
|
|
178
178
|
setup_tamper_session "$t_dir" "$slug" "$step"
|
|
179
179
|
|
|
180
180
|
# Write a TAMPERED trust.bundle: stored verified, evidence passing=false
|
|
181
|
-
python3 - "$t_dir/.flow-agents/$slug/trust.bundle" "$claim_type" "$subject_type" << 'PY'
|
|
181
|
+
python3 - "$t_dir/.kontourai/flow-agents/$slug/trust.bundle" "$claim_type" "$subject_type" << 'PY'
|
|
182
182
|
import json, sys
|
|
183
183
|
claim_type = sys.argv[2]
|
|
184
184
|
subject_type = sys.argv[3]
|
|
185
185
|
bundle = {
|
|
186
|
-
"schemaVersion":
|
|
186
|
+
"schemaVersion": 5,
|
|
187
187
|
"source": "flow-agents/workflow-sidecar",
|
|
188
188
|
"claims": [{
|
|
189
189
|
"id": "c1",
|
|
@@ -254,13 +254,16 @@ PY
|
|
|
254
254
|
fi
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
-
# ─── Test 0:
|
|
257
|
+
# ─── Test 0: Flow Definition required:true confirmation ──────────────────────
|
|
258
258
|
echo ""
|
|
259
|
-
echo "=== 0.
|
|
259
|
+
echo "=== 0. Flow Definitions: confirm required:true for produced gates ==="
|
|
260
260
|
|
|
261
261
|
node -e "
|
|
262
262
|
const fs = require('fs');
|
|
263
|
-
const
|
|
263
|
+
const flows = [
|
|
264
|
+
JSON.parse(fs.readFileSync('$ROOT/kits/builder/flows/build.flow.json', 'utf8')),
|
|
265
|
+
JSON.parse(fs.readFileSync('$ROOT/kits/builder/flows/publish-learn.flow.json', 'utf8')),
|
|
266
|
+
];
|
|
264
267
|
const requiredTrue = [
|
|
265
268
|
'selected-work',
|
|
266
269
|
'pickup-probe-readiness',
|
|
@@ -271,26 +274,31 @@ node -e "
|
|
|
271
274
|
];
|
|
272
275
|
const requiredFalse = ['policy-compliance'];
|
|
273
276
|
let ok = true;
|
|
274
|
-
for (const
|
|
275
|
-
for (const
|
|
277
|
+
for (const flow of flows) {
|
|
278
|
+
for (const [gateName, gate] of Object.entries(flow.gates || {})) {
|
|
279
|
+
for (const exp of gate.expects || []) {
|
|
276
280
|
if (requiredTrue.includes(exp.id) && exp.required !== true) {
|
|
277
|
-
console.error('FAIL: ' + exp.id + ' in ' + gateName + ' should be required:true, got ' + exp.required);
|
|
281
|
+
console.error('FAIL: ' + exp.id + ' in ' + flow.id + '/' + gateName + ' should be required:true, got ' + exp.required);
|
|
278
282
|
ok = false;
|
|
279
283
|
}
|
|
280
284
|
if (requiredFalse.includes(exp.id) && exp.required !== false) {
|
|
281
|
-
console.error('FAIL: ' + exp.id + ' in ' + gateName + ' should remain required:false (advisory), got ' + exp.required);
|
|
285
|
+
console.error('FAIL: ' + exp.id + ' in ' + flow.id + '/' + gateName + ' should remain required:false (advisory), got ' + exp.required);
|
|
282
286
|
ok = false;
|
|
283
287
|
}
|
|
288
|
+
}
|
|
284
289
|
}
|
|
285
290
|
}
|
|
286
291
|
if (!ok) process.exit(1);
|
|
287
292
|
" 2>/dev/null \
|
|
288
|
-
&& _pass "
|
|
289
|
-
|| _fail "
|
|
293
|
+
&& _pass "Flow Definitions: 6 produced gates are required:true, policy-compliance is required:false" \
|
|
294
|
+
|| _fail "Flow Definitions: required flag mismatch"
|
|
290
295
|
|
|
291
296
|
node -e "
|
|
292
297
|
const fs = require('fs');
|
|
293
|
-
const
|
|
298
|
+
const flows = [
|
|
299
|
+
JSON.parse(fs.readFileSync('$ROOT/kits/builder/flows/build.flow.json', 'utf8')),
|
|
300
|
+
JSON.parse(fs.readFileSync('$ROOT/kits/builder/flows/publish-learn.flow.json', 'utf8')),
|
|
301
|
+
];
|
|
294
302
|
const producedIds = [
|
|
295
303
|
'selected-work',
|
|
296
304
|
'pickup-probe-readiness',
|
|
@@ -300,18 +308,20 @@ node -e "
|
|
|
300
308
|
'learning-evidence',
|
|
301
309
|
];
|
|
302
310
|
let ok = true;
|
|
303
|
-
for (const
|
|
304
|
-
for (const
|
|
311
|
+
for (const flow of flows) {
|
|
312
|
+
for (const [gateName, gate] of Object.entries(flow.gates || {})) {
|
|
313
|
+
for (const exp of gate.expects || []) {
|
|
305
314
|
if (producedIds.includes(exp.id) && exp.explore_hint) {
|
|
306
|
-
console.error('FAIL: ' + exp.id + ' in ' + gateName + ' still has explore_hint (remove when producer exists)');
|
|
315
|
+
console.error('FAIL: ' + exp.id + ' in ' + flow.id + '/' + gateName + ' still has explore_hint (remove when producer exists)');
|
|
307
316
|
ok = false;
|
|
308
317
|
}
|
|
318
|
+
}
|
|
309
319
|
}
|
|
310
320
|
}
|
|
311
321
|
if (!ok) process.exit(1);
|
|
312
322
|
" 2>/dev/null \
|
|
313
|
-
&& _pass "
|
|
314
|
-
|| _fail "
|
|
323
|
+
&& _pass "Flow Definitions: no explore_hint on produced gate entries" \
|
|
324
|
+
|| _fail "Flow Definitions: produced gate entries still have explore_hint"
|
|
315
325
|
|
|
316
326
|
# ─── Tests 1–6: produce + tamper-block for each of the 6 claims ──────────────
|
|
317
327
|
|
|
@@ -158,13 +158,17 @@ echo "--- Installed Layout ---"
|
|
|
158
158
|
for dir in \
|
|
159
159
|
"$KIRO_DEST/agents" \
|
|
160
160
|
"$BASE_DEST/.flow-agents" \
|
|
161
|
+
"$BASE_DEST/.kontourai/flow-agents" \
|
|
161
162
|
"$CLAUDE_DEST/.claude/agents" \
|
|
162
163
|
"$CLAUDE_DEST/.claude/skills" \
|
|
163
164
|
"$CLAUDE_DEST/.flow-agents" \
|
|
165
|
+
"$CLAUDE_DEST/.kontourai/flow-agents" \
|
|
164
166
|
"$CODEX_DEST/.codex/agents" \
|
|
165
167
|
"$CODEX_DEST/.codex/skills" \
|
|
166
168
|
"$CODEX_DEST/.flow-agents" \
|
|
167
|
-
"$
|
|
169
|
+
"$CODEX_DEST/.kontourai/flow-agents" \
|
|
170
|
+
"$CODEX_FULL_DEST/.flow-agents" \
|
|
171
|
+
"$CODEX_FULL_DEST/.kontourai/flow-agents"; do
|
|
168
172
|
if [[ -d "$dir" ]]; then
|
|
169
173
|
_pass "$dir exists"
|
|
170
174
|
else
|
|
@@ -218,7 +222,7 @@ fi
|
|
|
218
222
|
|
|
219
223
|
if rg -F -q "console_telemetry_url=$LOCAL_KONTOUR_CONSOLE_URL" "$CODEX_INIT_DEST/scripts/telemetry/telemetry.conf" \
|
|
220
224
|
&& rg -q '^console_tenant_id=tenant-a$' "$CODEX_INIT_DEST/scripts/telemetry/telemetry.conf" \
|
|
221
|
-
&& [[ -f "$CODEX_INIT_DEST/.flow-agents/
|
|
225
|
+
&& [[ -f "$CODEX_INIT_DEST/.kontourai/flow-agents/projections/codex/activation.json" ]]; then
|
|
222
226
|
_pass "flow-agents init persists Console config and activates Codex kits"
|
|
223
227
|
else
|
|
224
228
|
_fail "flow-agents init did not persist Console config or activate Codex kits"
|
|
@@ -226,6 +230,7 @@ fi
|
|
|
226
230
|
|
|
227
231
|
if [[ -f "$BASE_INIT_DEST/AGENTS.md" ]] \
|
|
228
232
|
&& [[ -d "$BASE_INIT_DEST/.flow-agents" ]] \
|
|
233
|
+
&& [[ -d "$BASE_INIT_DEST/.kontourai/flow-agents" ]] \
|
|
229
234
|
&& rg -F -q "console_telemetry_url=$LOCAL_KONTOUR_CONSOLE_URL" "$BASE_INIT_DEST/scripts/telemetry/telemetry.conf"; then
|
|
230
235
|
_pass "flow-agents init default installs base AGENTS.md workspace contract"
|
|
231
236
|
else
|
|
@@ -287,7 +292,7 @@ NODE
|
|
|
287
292
|
else
|
|
288
293
|
_fail "$dir is missing Kit Catalog or Builder Kit manifest"
|
|
289
294
|
fi
|
|
290
|
-
if [[ -f "$dir/kits/builder/flows/shape.flow.json" && -f "$dir/kits/builder/flows/build.flow.json" ]]; then
|
|
295
|
+
if [[ -f "$dir/kits/builder/flows/shape.flow.json" && -f "$dir/kits/builder/flows/build.flow.json" && -f "$dir/kits/builder/flows/publish-learn.flow.json" ]]; then
|
|
291
296
|
_pass "$dir includes Builder Kit Flow Definitions"
|
|
292
297
|
else
|
|
293
298
|
_fail "$dir is missing Builder Kit Flow Definitions"
|
|
@@ -316,13 +321,13 @@ const dest = process.argv[2];
|
|
|
316
321
|
const data = JSON.parse(fs.readFileSync(process.argv[3], "utf8"));
|
|
317
322
|
if (data.selected_adapter !== "codex-local") throw new Error("codex-local was not selected");
|
|
318
323
|
const ids = new Set((data.generated_runtime_files || []).map((item) => item.asset_id));
|
|
319
|
-
for (const expected of ["builder.shape", "builder.build", "codex-local.activation"]) {
|
|
324
|
+
for (const expected of ["builder.shape", "builder.build", "builder.publish-learn", "codex-local.activation"]) {
|
|
320
325
|
if (!ids.has(expected)) throw new Error(`missing generated runtime asset: ${expected}`);
|
|
321
326
|
}
|
|
322
327
|
for (const item of data.generated_runtime_files || []) {
|
|
323
328
|
if (!fs.existsSync(path.join(dest, item.path))) throw new Error(`generated runtime file missing: ${item.path}`);
|
|
324
329
|
}
|
|
325
|
-
if (!fs.existsSync(path.join(dest, ".flow-agents/
|
|
330
|
+
if (!fs.existsSync(path.join(dest, ".kontourai/flow-agents/projections/codex/activation.json"))) throw new Error("runtime activation manifest missing");
|
|
326
331
|
console.log("ok");
|
|
327
332
|
NODE
|
|
328
333
|
then
|
|
@@ -335,7 +340,7 @@ if node - "$KIRO_DEST" "$BASE_DEST" "$CLAUDE_DEST" "$CODEX_DEST" <<'NODE'
|
|
|
335
340
|
const fs = require("node:fs");
|
|
336
341
|
const path = require("node:path");
|
|
337
342
|
for (const root of process.argv.slice(2)) {
|
|
338
|
-
for (const rel of ["kits/catalog.json", "kits/builder/kit.json", "kits/builder/flows/shape.flow.json", "kits/builder/flows/build.flow.json"]) {
|
|
343
|
+
for (const rel of ["kits/catalog.json", "kits/builder/kit.json", "kits/builder/flows/shape.flow.json", "kits/builder/flows/build.flow.json", "kits/builder/flows/publish-learn.flow.json"]) {
|
|
339
344
|
JSON.parse(fs.readFileSync(path.join(root, rel), "utf8"));
|
|
340
345
|
}
|
|
341
346
|
}
|
|
@@ -367,25 +372,25 @@ else
|
|
|
367
372
|
_pass "installed bundles are free of machine-local absolute paths"
|
|
368
373
|
fi
|
|
369
374
|
|
|
370
|
-
if [[ -f "$CLAUDE_DEST/.flow-agents/.gitkeep" ]]; then
|
|
375
|
+
if [[ -f "$CLAUDE_DEST/.kontourai/flow-agents/.gitkeep" ]]; then
|
|
371
376
|
_pass "Claude Code task dir scaffold installed"
|
|
372
377
|
else
|
|
373
378
|
_fail "Claude Code task dir scaffold missing"
|
|
374
379
|
fi
|
|
375
380
|
|
|
376
|
-
if [[ -f "$CODEX_DEST/.flow-agents/.gitkeep" ]]; then
|
|
381
|
+
if [[ -f "$CODEX_DEST/.kontourai/flow-agents/.gitkeep" ]]; then
|
|
377
382
|
_pass "Codex task dir scaffold installed"
|
|
378
383
|
else
|
|
379
384
|
_fail "Codex task dir scaffold missing"
|
|
380
385
|
fi
|
|
381
386
|
|
|
382
|
-
if [[ -f "$OPENCODE_DEST/.flow-agents/.gitkeep" ]]; then
|
|
387
|
+
if [[ -f "$OPENCODE_DEST/.kontourai/flow-agents/.gitkeep" ]]; then
|
|
383
388
|
_pass "opencode task dir scaffold installed"
|
|
384
389
|
else
|
|
385
390
|
_fail "opencode task dir scaffold missing"
|
|
386
391
|
fi
|
|
387
392
|
|
|
388
|
-
if [[ -f "$PI_DEST/.flow-agents/.gitkeep" ]]; then
|
|
393
|
+
if [[ -f "$PI_DEST/.kontourai/flow-agents/.gitkeep" ]]; then
|
|
389
394
|
_pass "pi task dir scaffold installed"
|
|
390
395
|
else
|
|
391
396
|
_fail "pi task dir scaffold missing"
|
|
@@ -417,9 +422,10 @@ else
|
|
|
417
422
|
_fail "installed Claude permissions/statusline or Codex profile-v2 progress config is missing"
|
|
418
423
|
fi
|
|
419
424
|
|
|
420
|
-
if [[ -f "$CODEX_DEST/.codex/
|
|
421
|
-
&&
|
|
422
|
-
&& rg -q '
|
|
425
|
+
if [[ -f "$CODEX_DEST/.codex/builder.config.toml" && -f "$CODEX_DEST/.codex/personal.config.toml" ]] \
|
|
426
|
+
&& [[ "$(find "$CODEX_DEST/.codex" -maxdepth 1 -name '*.config.toml' | wc -l | tr -d ' ')" == "2" ]] \
|
|
427
|
+
&& rg -q 'Flow Agents Builder mode' "$CODEX_DEST/.codex/builder.config.toml" \
|
|
428
|
+
&& rg -q 'knowledge-capture' "$CODEX_DEST/.codex/personal.config.toml"; then
|
|
423
429
|
_pass "Codex install includes profile-v2 config files"
|
|
424
430
|
else
|
|
425
431
|
_fail "Codex install is missing profile-v2 config files"
|
|
@@ -517,7 +523,7 @@ const critique = {
|
|
|
517
523
|
}],
|
|
518
524
|
};
|
|
519
525
|
function writeFixture(root) {
|
|
520
|
-
const taskDir = path.join(root, ".flow-agents/installed-hook-demo");
|
|
526
|
+
const taskDir = path.join(root, ".kontourai/flow-agents/installed-hook-demo");
|
|
521
527
|
fs.mkdirSync(taskDir, { recursive: true });
|
|
522
528
|
fs.writeFileSync(path.join(taskDir, "state.json"), JSON.stringify(state), "utf8");
|
|
523
529
|
fs.writeFileSync(path.join(taskDir, "critique.json"), JSON.stringify(critique), "utf8");
|
|
@@ -593,7 +599,7 @@ const critique = {
|
|
|
593
599
|
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." }] }],
|
|
594
600
|
};
|
|
595
601
|
function writeFixture(root) {
|
|
596
|
-
const taskDir = path.join(root, ".flow-agents/opencode-hook-demo");
|
|
602
|
+
const taskDir = path.join(root, ".kontourai/flow-agents/opencode-hook-demo");
|
|
597
603
|
fs.mkdirSync(taskDir, { recursive: true });
|
|
598
604
|
fs.writeFileSync(path.join(taskDir, "state.json"), JSON.stringify(state), "utf8");
|
|
599
605
|
fs.writeFileSync(path.join(taskDir, "critique.json"), JSON.stringify(critique), "utf8");
|
|
@@ -652,7 +658,7 @@ const critique = {
|
|
|
652
658
|
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." }] }],
|
|
653
659
|
};
|
|
654
660
|
function writeFixture(root) {
|
|
655
|
-
const taskDir = path.join(root, ".flow-agents/pi-hook-demo");
|
|
661
|
+
const taskDir = path.join(root, ".kontourai/flow-agents/pi-hook-demo");
|
|
656
662
|
fs.mkdirSync(taskDir, { recursive: true });
|
|
657
663
|
fs.writeFileSync(path.join(taskDir, "state.json"), JSON.stringify(state), "utf8");
|
|
658
664
|
fs.writeFileSync(path.join(taskDir, "critique.json"), JSON.stringify(critique), "utf8");
|
|
@@ -231,18 +231,18 @@ OPENCODE_USER="$TMPDIR_EVAL/user-opencode"
|
|
|
231
231
|
# Create user-owned files (unknown to the bundle)
|
|
232
232
|
mkdir -p "$CLAUDE_USER/.claude/custom"
|
|
233
233
|
echo "# user custom agent" > "$CLAUDE_USER/.claude/custom/my-custom-agent.md"
|
|
234
|
-
mkdir -p "$CLAUDE_USER/.flow-agents/my-session"
|
|
235
|
-
echo '{"custom":"data"}' > "$CLAUDE_USER/.flow-agents/my-session/state.json"
|
|
234
|
+
mkdir -p "$CLAUDE_USER/.kontourai/flow-agents/my-session"
|
|
235
|
+
echo '{"custom":"data"}' > "$CLAUDE_USER/.kontourai/flow-agents/my-session/state.json"
|
|
236
236
|
|
|
237
237
|
mkdir -p "$CODEX_USER/.codex/custom"
|
|
238
238
|
printf 'name = "my-custom-agent"\n' > "$CODEX_USER/.codex/custom/my-custom-agent.toml"
|
|
239
|
-
mkdir -p "$CODEX_USER/.flow-agents/my-session"
|
|
240
|
-
echo '{"custom":"data"}' > "$CODEX_USER/.flow-agents/my-session/state.json"
|
|
239
|
+
mkdir -p "$CODEX_USER/.kontourai/flow-agents/my-session"
|
|
240
|
+
echo '{"custom":"data"}' > "$CODEX_USER/.kontourai/flow-agents/my-session/state.json"
|
|
241
241
|
|
|
242
242
|
mkdir -p "$OPENCODE_USER/.opencode/custom"
|
|
243
243
|
echo "# user custom agent" > "$OPENCODE_USER/.opencode/custom/my-custom-agent.md"
|
|
244
|
-
mkdir -p "$OPENCODE_USER/.flow-agents/my-session"
|
|
245
|
-
echo '{"custom":"data"}' > "$OPENCODE_USER/.flow-agents/my-session/state.json"
|
|
244
|
+
mkdir -p "$OPENCODE_USER/.kontourai/flow-agents/my-session"
|
|
245
|
+
echo '{"custom":"data"}' > "$OPENCODE_USER/.kontourai/flow-agents/my-session/state.json"
|
|
246
246
|
|
|
247
247
|
# Modify an installed skill file to simulate user edits
|
|
248
248
|
CLAUDE_INSTALLED_SKILL="$CLAUDE_USER/.claude/skills/plan-work/SKILL.md"
|
|
@@ -272,19 +272,19 @@ fi
|
|
|
272
272
|
(cd "$ROOT_DIR/dist/opencode" && bash install.sh "$OPENCODE_USER" >/dev/null 2>&1) || true
|
|
273
273
|
|
|
274
274
|
# Assert: user-owned unknown files survive
|
|
275
|
-
if [[ -f "$CLAUDE_USER/.claude/custom/my-custom-agent.md" && -f "$CLAUDE_USER/.flow-agents/my-session/state.json" ]]; then
|
|
275
|
+
if [[ -f "$CLAUDE_USER/.claude/custom/my-custom-agent.md" && -f "$CLAUDE_USER/.kontourai/flow-agents/my-session/state.json" ]]; then
|
|
276
276
|
_pass "claude-code re-install: user-owned files not removed by rsync"
|
|
277
277
|
else
|
|
278
278
|
_fail "claude-code re-install: user-owned files were removed"
|
|
279
279
|
fi
|
|
280
280
|
|
|
281
|
-
if [[ -f "$CODEX_USER/.codex/custom/my-custom-agent.toml" && -f "$CODEX_USER/.flow-agents/my-session/state.json" ]]; then
|
|
281
|
+
if [[ -f "$CODEX_USER/.codex/custom/my-custom-agent.toml" && -f "$CODEX_USER/.kontourai/flow-agents/my-session/state.json" ]]; then
|
|
282
282
|
_pass "codex re-install: user-owned files not removed by rsync"
|
|
283
283
|
else
|
|
284
284
|
_fail "codex re-install: user-owned files were removed"
|
|
285
285
|
fi
|
|
286
286
|
|
|
287
|
-
if [[ -f "$OPENCODE_USER/.opencode/custom/my-custom-agent.md" && -f "$OPENCODE_USER/.flow-agents/my-session/state.json" ]]; then
|
|
287
|
+
if [[ -f "$OPENCODE_USER/.opencode/custom/my-custom-agent.md" && -f "$OPENCODE_USER/.kontourai/flow-agents/my-session/state.json" ]]; then
|
|
288
288
|
_pass "opencode re-install: user-owned files not removed by rsync"
|
|
289
289
|
else
|
|
290
290
|
_fail "opencode re-install: user-owned files were removed"
|
|
@@ -528,7 +528,7 @@ fi
|
|
|
528
528
|
# and point CLAUDE_PROJECT_DIR there so the hook can resolve its scripts.
|
|
529
529
|
# This mirrors the real dogfood use case where the repo root has scripts/ from the bundle.
|
|
530
530
|
DOGFOOD_WORKSPACE="$CLAUDE_IDEM" # reuse the installed workspace from the idempotent section
|
|
531
|
-
mkdir -p "$DOGFOOD_WORKSPACE/.flow-agents"
|
|
531
|
+
mkdir -p "$DOGFOOD_WORKSPACE/.kontourai/flow-agents"
|
|
532
532
|
|
|
533
533
|
if node - "$DOGFOOD_DEST2/.claude/settings.json" "$DOGFOOD_WORKSPACE" << 'NODE'
|
|
534
534
|
const fs = require("node:fs");
|
|
@@ -537,7 +537,7 @@ const { spawnSync } = require("node:child_process");
|
|
|
537
537
|
const [settingsPath, workspace] = process.argv.slice(2);
|
|
538
538
|
|
|
539
539
|
// Write minimal fixtures for workflow-steering into the workspace
|
|
540
|
-
const taskDir = path.join(workspace, ".flow-agents", "dogfood-hook-demo");
|
|
540
|
+
const taskDir = path.join(workspace, ".kontourai", "flow-agents", "dogfood-hook-demo");
|
|
541
541
|
fs.mkdirSync(taskDir, { recursive: true });
|
|
542
542
|
const state = {
|
|
543
543
|
schema_version: "1.0",
|