@kontourai/flow-agents 3.6.0 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/build/src/builder-flow-run-adapter.d.ts +22 -2
- package/build/src/builder-flow-run-adapter.js +93 -28
- package/build/src/builder-flow-runtime.d.ts +8 -3
- package/build/src/builder-flow-runtime.js +308 -47
- package/build/src/cli/assignment-provider.d.ts +4 -7
- package/build/src/cli/assignment-provider.js +67 -13
- package/build/src/cli/builder-run.js +14 -18
- package/build/src/cli/workflow-sidecar.d.ts +28 -4
- package/build/src/cli/workflow-sidecar.js +801 -97
- package/build/src/cli/workflow.js +290 -42
- package/build/src/flow-kit/validate.d.ts +17 -0
- package/build/src/flow-kit/validate.js +340 -2
- package/build/src/index.js +5 -5
- package/build/src/lib/observed-command.d.ts +7 -0
- package/build/src/lib/observed-command.js +100 -0
- package/build/src/tools/generate-context-map.js +5 -3
- package/context/scripts/hooks/lib/kit-catalog.js +1 -1
- package/context/scripts/hooks/stop-goal-fit.js +78 -9
- package/docs/context-map.md +22 -20
- package/docs/developer-architecture.md +1 -1
- package/docs/public-workflow-cli.md +76 -7
- package/docs/skills-map.md +51 -27
- package/docs/spec/builder-flow-runtime.md +41 -40
- package/docs/workflow-usage-guide.md +109 -42
- package/evals/fixtures/hook-influence/cases.json +2 -2
- package/evals/integration/test_builder_entry_enforcement.sh +52 -41
- package/evals/integration/test_builder_step_producers.sh +297 -6
- package/evals/integration/test_bundle_install.sh +212 -63
- package/evals/integration/test_critique_supersession_roundtrip.sh +21 -8
- package/evals/integration/test_current_json_per_actor.sh +12 -0
- package/evals/integration/test_dual_emit_flow_step.sh +62 -43
- package/evals/integration/test_flowdef_session_activation.sh +145 -25
- package/evals/integration/test_flowdef_session_history_preservation.sh +23 -21
- package/evals/integration/test_gate_lockdown.sh +3 -5
- package/evals/integration/test_goal_fit_hook.sh +60 -2
- package/evals/integration/test_liveness_verdict.sh +14 -17
- package/evals/integration/test_phase_map_and_gate_claim.sh +63 -38
- package/evals/integration/test_public_workflow_cli.sh +325 -11
- package/evals/integration/test_pull_work_liveness_preflight.sh +22 -66
- package/evals/integration/test_sidecar_field_preservation.sh +36 -11
- package/evals/integration/test_workflow_sidecar_writer.sh +277 -44
- package/evals/integration/test_workflow_steering_hook.sh +15 -38
- package/evals/run.sh +2 -0
- package/evals/static/test_builder_skill_coherence.sh +247 -0
- package/evals/static/test_library_exports.sh +5 -2
- package/evals/static/test_workflow_skills.sh +13 -325
- package/kits/builder/flows/build.flow.json +22 -0
- package/kits/builder/flows/shape.flow.json +9 -9
- package/kits/builder/kit.json +70 -16
- package/kits/builder/skills/builder-shape/SKILL.md +75 -75
- package/kits/builder/skills/continue-work/SKILL.md +45 -106
- package/kits/builder/skills/deliver/SKILL.md +96 -442
- package/kits/builder/skills/design-probe/SKILL.md +40 -139
- package/kits/builder/skills/evidence-gate/SKILL.md +59 -201
- package/kits/builder/skills/execute-plan/SKILL.md +54 -125
- package/kits/builder/skills/fix-bug/SKILL.md +42 -132
- package/kits/builder/skills/gate-review/SKILL.md +60 -211
- package/kits/builder/skills/idea-to-backlog/SKILL.md +63 -244
- package/kits/builder/skills/learning-review/SKILL.md +63 -170
- package/kits/builder/skills/pickup-probe/SKILL.md +54 -111
- package/kits/builder/skills/plan-work/SKILL.md +51 -185
- package/kits/builder/skills/pull-work/SKILL.md +136 -485
- package/kits/builder/skills/release-readiness/SKILL.md +66 -107
- package/kits/builder/skills/review-work/SKILL.md +89 -176
- package/kits/builder/skills/tdd-workflow/SKILL.md +53 -147
- package/kits/builder/skills/verify-work/SKILL.md +101 -113
- package/package.json +2 -2
- package/scripts/hooks/lib/kit-catalog.js +1 -1
- package/scripts/hooks/stop-goal-fit.js +78 -9
- package/src/builder-flow-run-adapter.ts +118 -32
- package/src/builder-flow-runtime.ts +331 -61
- package/src/cli/assignment-provider-lock.test.mjs +83 -0
- package/src/cli/assignment-provider.ts +62 -13
- package/src/cli/builder-flow-run-adapter.test.mjs +4 -2
- package/src/cli/builder-flow-runtime.test.mjs +194 -8
- package/src/cli/builder-run.ts +3 -9
- package/src/cli/kit-metadata-security.test.mjs +232 -6
- package/src/cli/public-api.test.mjs +15 -0
- package/src/cli/workflow-sidecar-execution-proof.test.mjs +90 -0
- package/src/cli/workflow-sidecar.ts +724 -97
- package/src/cli/workflow.ts +277 -40
- package/src/flow-kit/validate.ts +320 -2
- package/src/index.ts +6 -5
- package/src/lib/observed-command.ts +96 -0
- package/src/tools/generate-context-map.ts +5 -3
|
@@ -251,43 +251,71 @@ fi
|
|
|
251
251
|
echo ""
|
|
252
252
|
echo "── P-d declared-only: session WITH active_flow_id=builder.build / active_step_id=verify ──"
|
|
253
253
|
|
|
254
|
-
#
|
|
255
|
-
#
|
|
254
|
+
# Each fixture starts at the declared first step with selected-work evidence,
|
|
255
|
+
# then advances by recording the durable artifacts that the declared producers
|
|
256
|
+
# require. current.json is only projected by the writer during those transitions.
|
|
256
257
|
mkdir -p "$SESSION_ROOT"
|
|
257
|
-
if flow_agents_node "$WRITER" ensure-session \
|
|
258
|
-
--artifact-root "$SESSION_ROOT" \
|
|
259
|
-
--task-slug dual-emit-test \
|
|
260
|
-
--flow-id builder.build \
|
|
261
|
-
--skip-ownership-guard \
|
|
262
|
-
--title "Declared-Only Test" \
|
|
263
|
-
--summary "Test declared-only emit for ADR 0016 P-d." \
|
|
264
|
-
--criterion "Tests pass" \
|
|
265
|
-
--timestamp "2026-06-26T00:00:00Z" >"$TMP/ensure.out" 2>"$TMP/ensure.err"; then
|
|
266
|
-
_pass "ensure-session with --flow-id succeeds at the declared first step"
|
|
267
|
-
else
|
|
268
|
-
_fail "ensure-session with --flow-id failed: $(cat "$TMP/ensure.out" "$TMP/ensure.err")"
|
|
269
|
-
fi
|
|
270
|
-
|
|
271
|
-
DUAL_DIR="$SESSION_ROOT/dual-emit-test"
|
|
272
258
|
|
|
273
259
|
advance_builder_to_verify() {
|
|
274
|
-
local
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
260
|
+
local slug="$1"
|
|
261
|
+
local title="$2"
|
|
262
|
+
local summary="$3"
|
|
263
|
+
local session_dir="$SESSION_ROOT/$slug"
|
|
264
|
+
local pull_work="$session_dir/$slug--pull-work.md"
|
|
265
|
+
local plan="$session_dir/$slug--plan-work.md"
|
|
266
|
+
local deliver="$session_dir/$slug--deliver.md"
|
|
267
|
+
local work_item="${slug/-/:}"
|
|
268
|
+
|
|
269
|
+
mkdir -p "$session_dir"
|
|
270
|
+
printf 'Selected Work Item: %s\n' "$work_item" > "$pull_work"
|
|
271
|
+
FLOW_AGENTS_ACTOR="$slug-fixture-actor" node "$ROOT/build/src/cli.js" workflow start \
|
|
272
|
+
--artifact-root "$SESSION_ROOT" \
|
|
273
|
+
--flow builder.build \
|
|
274
|
+
--work-item "$work_item" \
|
|
275
|
+
--assignment-provider local-file \
|
|
276
|
+
--title "$title" \
|
|
277
|
+
--summary "$summary" \
|
|
278
|
+
--criterion "Fixture producer artifacts reach verify." >/dev/null 2>&1 || return 1
|
|
279
|
+
flow_agents_node "$WRITER" init-plan "$deliver" \
|
|
280
|
+
--source-request "Test declared-only producer artifacts." \
|
|
281
|
+
--summary "Fixture plan for declared-only producer artifacts." \
|
|
282
|
+
--timestamp "2026-06-26T00:00:05Z" >/dev/null 2>&1 || return 1
|
|
283
|
+
node - "$session_dir" "$slug" <<'NODE'
|
|
284
|
+
const fs = require('node:fs');
|
|
285
|
+
const path = require('node:path');
|
|
286
|
+
const [dir, slug] = process.argv.slice(2);
|
|
287
|
+
fs.writeFileSync(path.join(dir, `${slug}--plan-work.md`), '# Plan\n\n- AC-1: Reach verify through declared producer artifacts.\n', 'utf8');
|
|
288
|
+
fs.writeFileSync(path.join(dir, 'acceptance.json'), JSON.stringify({
|
|
289
|
+
schema_version: '1.0',
|
|
290
|
+
task_slug: slug,
|
|
291
|
+
criteria: [{
|
|
292
|
+
id: 'AC-1',
|
|
293
|
+
description: 'Reach verify through declared producer artifacts.',
|
|
294
|
+
status: 'pending',
|
|
295
|
+
evidence_refs: [],
|
|
296
|
+
}],
|
|
297
|
+
goal_fit: { status: 'pending', summary: 'Fixture has not completed Goal Fit review.' },
|
|
298
|
+
}, null, 2), 'utf8');
|
|
299
|
+
NODE
|
|
300
|
+
FLOW_AGENTS_ACTOR="$slug-fixture-actor" node "$ROOT/build/src/cli.js" workflow evidence --session-dir "$session_dir" --status pass --expectation pickup-probe-readiness \
|
|
301
|
+
--summary "Pickup readiness is recorded in the selected-work artifact." \
|
|
302
|
+
--evidence-ref-json "{\"kind\":\"artifact\",\"file\":\"$pull_work\",\"summary\":\"Declared durable producer artifact for pickup-probe-readiness.\"}" >/dev/null 2>&1 || return 1
|
|
303
|
+
FLOW_AGENTS_ACTOR="$slug-fixture-actor" node "$ROOT/build/src/cli.js" workflow evidence --session-dir "$session_dir" --status pass --expectation probe-decisions-or-accepted-gaps \
|
|
304
|
+
--summary "Probe decisions are recorded in the selected-work artifact." \
|
|
305
|
+
--evidence-ref-json "{\"kind\":\"artifact\",\"file\":\"$pull_work\",\"summary\":\"Declared durable producer artifact for probe decisions.\"}" >/dev/null 2>&1 || return 1
|
|
306
|
+
FLOW_AGENTS_ACTOR="$slug-fixture-actor" node "$ROOT/build/src/cli.js" workflow evidence --session-dir "$session_dir" --status pass --expectation implementation-plan \
|
|
307
|
+
--summary "Implementation plan is recorded in the plan artifact." \
|
|
308
|
+
--evidence-ref-json "{\"kind\":\"artifact\",\"file\":\"$plan\",\"summary\":\"Declared durable producer artifact for implementation-plan.\"}" >/dev/null 2>&1 || return 1
|
|
309
|
+
FLOW_AGENTS_ACTOR="$slug-fixture-actor" node "$ROOT/build/src/cli.js" workflow evidence --session-dir "$session_dir" --status pass --expectation implementation-scope \
|
|
310
|
+
--summary "Implementation scope is recorded in the delivery artifact." \
|
|
311
|
+
--evidence-ref-json "{\"kind\":\"artifact\",\"file\":\"$deliver\",\"summary\":\"Declared durable producer artifact for implementation-scope.\"}" >/dev/null 2>&1 || return 1
|
|
285
312
|
}
|
|
286
313
|
|
|
287
|
-
|
|
288
|
-
|
|
314
|
+
DUAL_DIR="$SESSION_ROOT/dual-emit-test"
|
|
315
|
+
if advance_builder_to_verify "dual-emit-test" "Declared-Only Test" "Test declared-only emit for ADR 0016 P-d."; then
|
|
316
|
+
_pass "canonical Builder Flow advances through declared producer artifacts to verify"
|
|
289
317
|
else
|
|
290
|
-
_fail "canonical Builder Flow did not reach verify"
|
|
318
|
+
_fail "canonical Builder Flow did not reach verify through declared producer artifacts"
|
|
291
319
|
fi
|
|
292
320
|
|
|
293
321
|
# Verify current.json carries the flow keys
|
|
@@ -345,19 +373,10 @@ echo "── P-d declared-only: policy-kind check maps to builder.verify.policy-
|
|
|
345
373
|
|
|
346
374
|
# Use a separate canonical run because the failing tests evidence above legitimately
|
|
347
375
|
# routes its run back to execute before this independent producer assertion.
|
|
348
|
-
if
|
|
349
|
-
|
|
350
|
-
--task-slug dual-emit-policy \
|
|
351
|
-
--flow-id builder.build \
|
|
352
|
-
--skip-ownership-guard \
|
|
353
|
-
--title "Declared Policy Test" \
|
|
354
|
-
--summary "Test declared-only policy emit for ADR 0016 P-d." \
|
|
355
|
-
--criterion "Policy passes" \
|
|
356
|
-
--timestamp "2026-06-26T00:01:30Z" >/dev/null 2>&1 \
|
|
357
|
-
&& advance_builder_to_verify "$SESSION_ROOT/dual-emit-policy"; then
|
|
358
|
-
_pass "policy fixture canonical Builder Flow reaches verify"
|
|
376
|
+
if advance_builder_to_verify "dual-emit-policy" "Declared Policy Test" "Test declared-only policy emit for ADR 0016 P-d."; then
|
|
377
|
+
_pass "policy fixture canonical Builder Flow reaches verify through declared producer artifacts"
|
|
359
378
|
else
|
|
360
|
-
_fail "policy fixture canonical Builder Flow did not reach verify"
|
|
379
|
+
_fail "policy fixture canonical Builder Flow did not reach verify through declared producer artifacts"
|
|
361
380
|
fi
|
|
362
381
|
|
|
363
382
|
POLICY_DIR="$SESSION_ROOT/dual-emit-policy"
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
# correctly sets active_step_id via the phase_map at each phase.
|
|
7
7
|
#
|
|
8
8
|
# Tests:
|
|
9
|
-
# 1. ensure-session --flow-id
|
|
10
|
-
#
|
|
9
|
+
# 1. Pre-seeded concrete pull-work evidence lets ensure-session --flow-id
|
|
10
|
+
# builder.build bind the local Work Item and project the canonical Flow.
|
|
11
11
|
# 2. advance-state through phases (planning→execution→verification) sets correct
|
|
12
12
|
# active_step_id via phase_map at each transition.
|
|
13
|
-
# 3. At the verify step,
|
|
13
|
+
# 3. At the verify step, public workflow evidence for tests-evidence produces
|
|
14
14
|
# builder.verify.tests (status=verified) in the bundle — producer fires.
|
|
15
15
|
# 4. A TAMPERED builder.verify.tests bundle at the verify step BLOCKS (exit 2)
|
|
16
16
|
# with the tamper warning naming the declared claimType.
|
|
@@ -46,17 +46,17 @@ echo "=== 1. ensure-session --flow-id builder.build activates FlowDefinition-dri
|
|
|
46
46
|
MAIN_AROOT="$TMP/main-project/.kontourai/flow-agents"
|
|
47
47
|
SLUG="activation-test"
|
|
48
48
|
SESSION_DIR="$MAIN_AROOT/$SLUG"
|
|
49
|
-
mkdir -p "$
|
|
49
|
+
mkdir -p "$SESSION_DIR"
|
|
50
|
+
printf 'Selected Work Item: activation:test\n' > "$SESSION_DIR/$SLUG--pull-work.md"
|
|
50
51
|
|
|
51
|
-
if
|
|
52
|
+
if FLOW_AGENTS_ACTOR=activation-test-actor node "$ROOT/build/src/cli.js" workflow start \
|
|
52
53
|
--artifact-root "$MAIN_AROOT" \
|
|
53
|
-
--
|
|
54
|
-
--
|
|
54
|
+
--flow builder.build \
|
|
55
|
+
--work-item activation:test \
|
|
56
|
+
--assignment-provider local-file \
|
|
55
57
|
--title "Step 1 activation test" \
|
|
56
58
|
--summary "Test that --flow-id builder.build activates the FlowDefinition-driven path." \
|
|
57
|
-
--criterion "All gates produce declared claims"
|
|
58
|
-
--flow-id builder.build \
|
|
59
|
-
--timestamp "2026-06-01T00:00:00Z" >/dev/null 2>&1; then
|
|
59
|
+
--criterion "All gates produce declared claims" >/dev/null 2>&1; then
|
|
60
60
|
_pass "ensure-session starts the canonical Builder Flow from a canonical artifact root"
|
|
61
61
|
else
|
|
62
62
|
_fail "ensure-session failed to start the canonical Builder Flow"
|
|
@@ -76,17 +76,54 @@ console.log('current.json: active_flow_id=' + c.active_flow_id + ' active_step_i
|
|
|
76
76
|
&& _pass "ensure-session records trusted selection and projects the canonical Flow run at design-probe" \
|
|
77
77
|
|| _fail "ensure-session did not create and project the canonical Flow run"
|
|
78
78
|
|
|
79
|
+
if node - "$ROOT/kits/builder/kit.json" "$ROOT" <<'NODE'
|
|
80
|
+
const fs = require('node:fs');
|
|
81
|
+
const kit = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
|
|
82
|
+
const { workflowTriggersFor } = require(`${process.argv[3]}/scripts/hooks/lib/kit-catalog.js`);
|
|
83
|
+
const roles = new Map(kit.skill_roles.map((entry) => [entry.skill_id.replace(/^builder\./, ''), entry.role]));
|
|
84
|
+
const trigger = kit.workflow_triggers.find((entry) => entry.id === 'builder-build-work');
|
|
85
|
+
if (roles.get(trigger.default_skill) !== 'entrypoint') process.exit(1);
|
|
86
|
+
if (!trigger.conditional_skills.every((entry) => roles.get(entry.skill) === 'profile')) process.exit(2);
|
|
87
|
+
const [rendered] = workflowTriggersFor(process.argv[3], 'implementation-work-detected');
|
|
88
|
+
if (!rendered?.steering.includes('activate `tdd-workflow`; otherwise activate `deliver`')) process.exit(5);
|
|
89
|
+
if (!rendered.steering.includes('public `flow-agents workflow` interface') || rendered.steering.includes('workflow:sidecar')) process.exit(6);
|
|
90
|
+
for (const action of kit.flow_step_actions) {
|
|
91
|
+
if (!action.skills.every((skill) => roles.get(skill) === 'step')) process.exit(3);
|
|
92
|
+
}
|
|
93
|
+
for (const skill of ['design-probe', 'continue-work', 'gate-review', 'fix-bug']) {
|
|
94
|
+
if (kit.flow_step_actions.some((action) => action.skills.includes(skill))) process.exit(4);
|
|
95
|
+
if (rendered.steering.includes(`activate \`${skill}\``)) process.exit(7);
|
|
96
|
+
}
|
|
97
|
+
NODE
|
|
98
|
+
then
|
|
99
|
+
_pass "runtime steering activates the entrypoint/profile and keeps primitives/extensions outside automatic step actions"
|
|
100
|
+
else
|
|
101
|
+
_fail "activation metadata blurred entrypoint, profile, step, primitive, or extension roles"
|
|
102
|
+
fi
|
|
103
|
+
|
|
79
104
|
# ─── TEST 2: advance-state sets active_step_id via phase_map ─────────────────
|
|
80
105
|
echo ""
|
|
81
106
|
echo "=== 2. advance-state through phases sets active_step_id via phase_map ==="
|
|
82
107
|
|
|
83
|
-
|
|
108
|
+
PHASE_AROOT="$TMP/phase-project/.kontourai/flow-agents"
|
|
109
|
+
PHASE_SLUG="phase-map-test"
|
|
110
|
+
PHASE_DIR="$PHASE_AROOT/$PHASE_SLUG"
|
|
111
|
+
mkdir -p "$PHASE_AROOT"
|
|
112
|
+
|
|
113
|
+
flow_agents_node "$WRITER" ensure-session \
|
|
114
|
+
--artifact-root "$PHASE_AROOT" \
|
|
115
|
+
--task-slug "$PHASE_SLUG" \
|
|
116
|
+
--title "Phase map test" \
|
|
117
|
+
--summary "Test FlowDefinition phase-map projection without an active Flow run." \
|
|
118
|
+
--timestamp "2026-06-01T00:00:30Z" >/dev/null 2>&1
|
|
119
|
+
|
|
120
|
+
flow_agents_node "$WRITER" init-plan "$PHASE_DIR/$PHASE_SLUG--deliver.md" \
|
|
84
121
|
--source-request "Test" --summary "Testing" \
|
|
85
122
|
--timestamp "2026-06-01T00:00:30Z" >/dev/null 2>&1
|
|
86
123
|
|
|
87
124
|
test_phase_step() {
|
|
88
125
|
local phase="$1" expected_step="$2"
|
|
89
|
-
flow_agents_node "$WRITER" advance-state "$
|
|
126
|
+
flow_agents_node "$WRITER" advance-state "$PHASE_DIR" \
|
|
90
127
|
--status in_progress --phase "$phase" \
|
|
91
128
|
--summary "Testing phase $phase." \
|
|
92
129
|
--next-action "Continue." \
|
|
@@ -95,7 +132,7 @@ test_phase_step() {
|
|
|
95
132
|
local actual
|
|
96
133
|
actual=$(node -e "
|
|
97
134
|
const fs = require('fs');
|
|
98
|
-
const c = JSON.parse(fs.readFileSync('$
|
|
135
|
+
const c = JSON.parse(fs.readFileSync('$PHASE_AROOT/current.json', 'utf8'));
|
|
99
136
|
console.log(c.active_step_id || '');
|
|
100
137
|
" 2>/dev/null)
|
|
101
138
|
if [ "$actual" = "$expected_step" ]; then
|
|
@@ -109,18 +146,95 @@ test_phase_step "planning" "plan"
|
|
|
109
146
|
test_phase_step "execution" "execute"
|
|
110
147
|
test_phase_step "verification" "verify"
|
|
111
148
|
|
|
112
|
-
|
|
149
|
+
node - "$SESSION_DIR" <<'NODE'
|
|
150
|
+
const fs = require('node:fs');
|
|
151
|
+
const path = require('node:path');
|
|
152
|
+
const dir = process.argv[2];
|
|
153
|
+
const slug = path.basename(dir);
|
|
154
|
+
fs.writeFileSync(path.join(dir, `${slug}--plan-work.md`), '# Plan\n\n- AC-1: Verify FlowDefinition producer evidence.\n', 'utf8');
|
|
155
|
+
fs.writeFileSync(path.join(dir, 'acceptance.json'), JSON.stringify({ schema_version: '1.0', task_slug: slug, criteria: [{ id: 'AC-1', description: 'Verify FlowDefinition producer evidence.', status: 'pending', evidence_refs: [] }], goal_fit: { status: 'pending', summary: 'Fixture has not completed Goal Fit review.' } }, null, 2), 'utf8');
|
|
156
|
+
NODE
|
|
157
|
+
mkdir -p "$TMP/main-project/checks"
|
|
158
|
+
cat > "$TMP/main-project/checks/check-flow-step.test.mjs" <<'JS'
|
|
159
|
+
import test from 'node:test';
|
|
160
|
+
import assert from 'node:assert/strict';
|
|
161
|
+
import fs from 'node:fs';
|
|
162
|
+
|
|
163
|
+
test('the canonical Builder run reached verify', () => {
|
|
164
|
+
const state = JSON.parse(fs.readFileSync('.kontourai/flow-agents/activation-test/state.json', 'utf8'));
|
|
165
|
+
assert.equal(state.flow_run?.current_step, 'verify');
|
|
166
|
+
});
|
|
167
|
+
JS
|
|
168
|
+
|
|
169
|
+
record_passing_producer_evidence() {
|
|
170
|
+
local expectation="$1" artifact="$2"
|
|
171
|
+
if FLOW_AGENTS_ACTOR=activation-test-actor node "$ROOT/build/src/cli.js" workflow evidence \
|
|
172
|
+
--session-dir "$SESSION_DIR" \
|
|
173
|
+
--status pass \
|
|
174
|
+
--expectation "$expectation" \
|
|
175
|
+
--summary "Fixture records passing $expectation evidence from its declared durable artifact." \
|
|
176
|
+
--evidence-ref-json "{\"kind\":\"artifact\",\"file\":\"$artifact\",\"summary\":\"Declared durable producer artifact for $expectation.\"}" >/dev/null 2>&1; then
|
|
177
|
+
_pass "public workflow evidence records declared durable producer evidence for $expectation"
|
|
178
|
+
else
|
|
179
|
+
_fail "public workflow evidence did not record declared durable producer evidence for $expectation"
|
|
180
|
+
fi
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
# Satisfy the canonical Flow itself. advance-state above tests phase-map projection;
|
|
184
|
+
# these producer claims advance the persisted Flow run to verify.
|
|
185
|
+
record_passing_producer_evidence "pickup-probe-readiness" "$SESSION_DIR/$SLUG--pull-work.md"
|
|
186
|
+
record_passing_producer_evidence "probe-decisions-or-accepted-gaps" "$SESSION_DIR/$SLUG--pull-work.md"
|
|
187
|
+
record_passing_producer_evidence "implementation-plan" "$SESSION_DIR/$SLUG--plan-work.md"
|
|
188
|
+
record_passing_producer_evidence "implementation-scope" "$SESSION_DIR/$SLUG--deliver.md"
|
|
189
|
+
|
|
190
|
+
# ─── TEST 3: clean critique + complete tests evidence at verify ───────────────
|
|
113
191
|
echo ""
|
|
114
|
-
echo "=== 3. verify step:
|
|
192
|
+
echo "=== 3. verify step: clean critique and criterion-backed producer evidence ==="
|
|
193
|
+
|
|
194
|
+
if FLOW_AGENTS_ACTOR=activation-reviewer node "$ROOT/build/src/cli.js" workflow critique \
|
|
195
|
+
--session-dir "$SESSION_DIR" \
|
|
196
|
+
--id "activation-review" \
|
|
197
|
+
--verdict pass \
|
|
198
|
+
--summary "Fixture review found no open findings before verification." \
|
|
199
|
+
--artifact-ref "$SESSION_DIR/$SLUG--deliver.md" \
|
|
200
|
+
--lane-json "{\"id\":\"code-review\",\"status\":\"pass\",\"summary\":\"Reviewed the delivered activation fixture.\",\"evidence_refs\":[{\"kind\":\"artifact\",\"file\":\"$SESSION_DIR/$SLUG--deliver.md\",\"summary\":\"Reviewed delivery artifact.\"}]}" >/dev/null 2>&1; then
|
|
201
|
+
_pass "public workflow critique records a clean review before passing tests-evidence"
|
|
202
|
+
else
|
|
203
|
+
_fail "public workflow critique did not record a clean review before tests-evidence"
|
|
204
|
+
fi
|
|
115
205
|
|
|
116
|
-
|
|
206
|
+
TEST_COMMAND="node --test checks/check-flow-step.test.mjs"
|
|
207
|
+
CRITERION_JSON="$(node - "$TEST_COMMAND" <<'NODE'
|
|
208
|
+
const command = process.argv[2];
|
|
209
|
+
process.stdout.write(JSON.stringify({
|
|
210
|
+
id: 'AC-1',
|
|
211
|
+
status: 'pass',
|
|
212
|
+
evidence_refs: [{
|
|
213
|
+
kind: 'command',
|
|
214
|
+
excerpt: command,
|
|
215
|
+
summary: 'Asserts the exact session is at the verify Flow step.',
|
|
216
|
+
}],
|
|
217
|
+
}));
|
|
218
|
+
NODE
|
|
219
|
+
)"
|
|
220
|
+
COMMAND_REF="$(node - "$TEST_COMMAND" <<'NODE'
|
|
221
|
+
const command = process.argv[2];
|
|
222
|
+
process.stdout.write(JSON.stringify({ kind: 'command', excerpt: command, summary: 'Runs the project-local verify-step assertion.' }));
|
|
223
|
+
NODE
|
|
224
|
+
)"
|
|
225
|
+
|
|
226
|
+
TEST_EVIDENCE_OUTPUT="$(FLOW_AGENTS_ACTOR=activation-test-actor node "$ROOT/build/src/cli.js" workflow evidence \
|
|
227
|
+
--session-dir "$SESSION_DIR" \
|
|
117
228
|
--status pass \
|
|
118
|
-
--summary "
|
|
229
|
+
--summary "The substantive producer fixture command passed for every declared criterion." \
|
|
119
230
|
--expectation "tests-evidence" \
|
|
120
|
-
--
|
|
121
|
-
|
|
231
|
+
--command "$TEST_COMMAND" \
|
|
232
|
+
--criterion-json "$CRITERION_JSON" \
|
|
233
|
+
--evidence-ref-json "$COMMAND_REF" 2>&1)"
|
|
234
|
+
if [ "$?" -eq 0 ]; then
|
|
235
|
+
_pass "public workflow evidence records passing criterion-backed tests-evidence at verify"
|
|
122
236
|
else
|
|
123
|
-
_fail "record
|
|
237
|
+
_fail "public workflow evidence did not record passing criterion-backed tests-evidence: $TEST_EVIDENCE_OUTPUT"
|
|
124
238
|
fi
|
|
125
239
|
|
|
126
240
|
node -e "
|
|
@@ -128,13 +242,17 @@ const fs = require('fs');
|
|
|
128
242
|
const bundlePath = '$SESSION_DIR/trust.bundle';
|
|
129
243
|
if (!fs.existsSync(bundlePath)) throw new Error('trust.bundle not found');
|
|
130
244
|
const bundle = JSON.parse(fs.readFileSync(bundlePath, 'utf8'));
|
|
131
|
-
const declared = (bundle.claims || []).find(c => c.
|
|
245
|
+
const declared = (bundle.claims || []).find(c => c.metadata?.gate_claim?.expectation_id === 'tests-evidence');
|
|
132
246
|
if (!declared) throw new Error('MISSING builder.verify.tests; claims: ' + (bundle.claims||[]).map(c=>c.claimType).join(', '));
|
|
133
|
-
if (declared.status !== 'verified') throw new Error('expected
|
|
134
|
-
|
|
247
|
+
if (declared.claimType !== 'builder.verify.tests' || declared.status !== 'verified') throw new Error('expected verified builder.verify.tests, got ' + JSON.stringify({ claimType: declared.claimType, status: declared.status }));
|
|
248
|
+
const critique = (bundle.claims || []).find(c => c.metadata?.origin === 'critique' && c.value === 'pass' && !c.metadata?.superseded_by);
|
|
249
|
+
if (!critique) throw new Error('MISSING clean live critique before tests-evidence');
|
|
250
|
+
const criterion = (bundle.claims || []).find(c => c.metadata?.origin === 'acceptance' && c.metadata?.criterion?.id === 'AC-1');
|
|
251
|
+
if (!criterion || criterion.value !== 'pass' || criterion.status !== 'verified') throw new Error('MISSING verified completed AC-1 criterion');
|
|
252
|
+
console.log('builder.verify.tests: subjectType=' + declared.subjectType + ' status=' + declared.status + ' value=' + declared.value + '; clean critique and AC-1 verified');
|
|
135
253
|
" 2>&1 \
|
|
136
|
-
&& _pass "bundle contains
|
|
137
|
-
|| _fail "bundle
|
|
254
|
+
&& _pass "bundle contains declared tests evidence with a clean critique and completed AC-1" \
|
|
255
|
+
|| _fail "bundle lacks declared tests evidence, clean critique, or completed AC-1"
|
|
138
256
|
|
|
139
257
|
# ─── TEST 4: tampered bundle at verify step BLOCKS ────────────────────────────
|
|
140
258
|
echo ""
|
|
@@ -145,10 +263,12 @@ TAMPER_SLUG="tamper-verify-test"
|
|
|
145
263
|
mkdir -p "$TAMPER_DIR"
|
|
146
264
|
printf '# Test repo\n' > "$TAMPER_DIR/AGENTS.md"
|
|
147
265
|
mkdir -p "$TAMPER_DIR/.kontourai/flow-agents/$TAMPER_SLUG"
|
|
266
|
+
printf 'Selected Work Item: local:%s\n' "$TAMPER_SLUG" > "$TAMPER_DIR/.kontourai/flow-agents/$TAMPER_SLUG/$TAMPER_SLUG--pull-work.md"
|
|
148
267
|
|
|
149
268
|
flow_agents_node "$WRITER" ensure-session \
|
|
150
269
|
--artifact-root "$TAMPER_DIR/.kontourai/flow-agents" \
|
|
151
270
|
--task-slug "$TAMPER_SLUG" \
|
|
271
|
+
--actor tamper-verify-actor \
|
|
152
272
|
--title "Tamper verify test" \
|
|
153
273
|
--summary "Testing tamper detection at verify step." \
|
|
154
274
|
--flow-id builder.build \
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# test_flowdef_session_history_preservation.sh — Integration eval for ADR 0016 Step 0.
|
|
3
3
|
#
|
|
4
4
|
# Proves:
|
|
5
|
-
# 1. A FlowDefinition-
|
|
5
|
+
# 1. A noncanonical FlowDefinition-projected session (advance-state, step=verify)
|
|
6
6
|
# records a check via the declared builder.verify.tests path, then record-critique and
|
|
7
7
|
# record-learning PRESERVE the prior declared check + critique claims in the rebuilt
|
|
8
8
|
# bundle (no history loss).
|
|
@@ -34,18 +34,18 @@ WRITER="workflow-sidecar"
|
|
|
34
34
|
echo ""
|
|
35
35
|
echo "=== 1. FlowDefinition-driven session: record-critique/record-learning preserve declared claims ==="
|
|
36
36
|
|
|
37
|
-
FLOW_AROOT="$TMP/flow-
|
|
37
|
+
FLOW_AROOT="$TMP/flow-project/.kontourai/flow-agents"
|
|
38
38
|
SLUG="history-flow-test"
|
|
39
39
|
SESSION_DIR="$FLOW_AROOT/$SLUG"
|
|
40
40
|
mkdir -p "$FLOW_AROOT"
|
|
41
41
|
|
|
42
|
-
#
|
|
42
|
+
# Create a plain sidecar session, then project it onto the verify step. This test covers the
|
|
43
|
+
# generic ADR 0016 sidecar path; canonical Builder runs are covered by the public workflow tests.
|
|
43
44
|
flow_agents_node "$WRITER" ensure-session \
|
|
44
45
|
--artifact-root "$FLOW_AROOT" \
|
|
45
46
|
--task-slug "$SLUG" \
|
|
46
47
|
--title "History preservation test" \
|
|
47
48
|
--summary "Test that declared builder.* claims survive round-trips." \
|
|
48
|
-
--flow-id builder.build \
|
|
49
49
|
--timestamp "2026-06-01T00:00:00Z" >/dev/null 2>&1
|
|
50
50
|
|
|
51
51
|
flow_agents_node "$WRITER" init-plan "$SESSION_DIR/$SLUG--deliver.md" \
|
|
@@ -80,6 +80,8 @@ flow_agents_node "$WRITER" record-critique "$SESSION_DIR" \
|
|
|
80
80
|
--id "code-review" \
|
|
81
81
|
--verdict pass \
|
|
82
82
|
--summary "Code review passed." \
|
|
83
|
+
--lane-json "{\"id\":\"code\",\"status\":\"pass\",\"summary\":\"Code review passed.\",\"evidence_refs\":[{\"kind\":\"artifact\",\"file\":\"$SESSION_DIR/$SLUG--deliver.md\",\"summary\":\"Reviewed the delivery artifact.\"}]}" \
|
|
84
|
+
--artifact-ref "$SESSION_DIR/$SLUG--deliver.md" \
|
|
83
85
|
--timestamp "2026-06-01T00:02:00Z" >/dev/null 2>&1
|
|
84
86
|
|
|
85
87
|
# Assert builder.verify.tests survived the record-critique round-trip
|
|
@@ -93,24 +95,21 @@ console.log('after record-critique: builder.verify.tests status=' + declared.sta
|
|
|
93
95
|
&& _pass "builder.verify.tests declared claim preserved after record-critique (no history loss)" \
|
|
94
96
|
|| _fail "builder.verify.tests declared claim LOST after record-critique (history loss)"
|
|
95
97
|
|
|
96
|
-
# Also verify the critique claim itself is present
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
# workflow.critique.review is emitted in no-flow sessions only (P-d: shadow retired).
|
|
98
|
+
# Also verify the critique claim itself is present and remains distinct from optional policy
|
|
99
|
+
# evidence. The Builder runtime consumes metadata.origin=critique directly; a code review must
|
|
100
|
+
# not be relabeled as builder.verify.policy-compliance merely because that expectation is open.
|
|
100
101
|
node -e "
|
|
101
102
|
const fs = require('fs');
|
|
102
103
|
const bundle = JSON.parse(fs.readFileSync('$SESSION_DIR/trust.bundle', 'utf8'));
|
|
103
104
|
const claims = bundle.claims || [];
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (legacy) throw new Error('UNEXPECTED workflow.critique.review in flow-driven session (P-d retired shadow); id=' + legacy.id);
|
|
110
|
-
console.log('declared critique claim: claimType=' + crit.claimType + ' value=' + crit.value);
|
|
105
|
+
const crit = claims.find(c => c.claimType === 'workflow.critique.review' && c.metadata?.origin === 'critique');
|
|
106
|
+
if (!crit) throw new Error('MISSING first-class critique claim after record-critique; claims: ' + claims.map(c=>c.claimType).join(', '));
|
|
107
|
+
const mislabeled = claims.find(c => c.claimType === 'builder.verify.policy-compliance' && c.metadata?.origin === 'critique');
|
|
108
|
+
if (mislabeled) throw new Error('critique was mislabeled as optional policy-compliance evidence: ' + mislabeled.id);
|
|
109
|
+
console.log('critique claim remains distinct: claimType=' + crit.claimType + ' value=' + crit.value);
|
|
111
110
|
" 2>&1 \
|
|
112
|
-
&& _pass "
|
|
113
|
-
|| _fail "
|
|
111
|
+
&& _pass "first-class critique preserved without policy-compliance mislabeling" \
|
|
112
|
+
|| _fail "critique claim missing or mislabeled after record-critique"
|
|
114
113
|
|
|
115
114
|
# Now do record-learning (second round-trip)
|
|
116
115
|
flow_agents_node "$WRITER" record-learning "$SESSION_DIR" \
|
|
@@ -140,7 +139,7 @@ console.log('after record-learning: builder.verify.tests status=' + declared.sta
|
|
|
140
139
|
echo ""
|
|
141
140
|
echo "=== 2. workflow.* session (no --flow-id): round-trip unchanged ==="
|
|
142
141
|
|
|
143
|
-
NOFLOW_AROOT="$TMP/noflow-
|
|
142
|
+
NOFLOW_AROOT="$TMP/noflow-project/.kontourai/flow-agents"
|
|
144
143
|
NOFLOW_SLUG="history-noflow-test"
|
|
145
144
|
NOFLOW_DIR="$NOFLOW_AROOT/$NOFLOW_SLUG"
|
|
146
145
|
mkdir -p "$NOFLOW_AROOT"
|
|
@@ -167,6 +166,8 @@ flow_agents_node "$WRITER" record-critique "$NOFLOW_DIR" \
|
|
|
167
166
|
--id "noflow-review" \
|
|
168
167
|
--verdict pass \
|
|
169
168
|
--summary "Review passed." \
|
|
169
|
+
--lane-json "{\"id\":\"code\",\"status\":\"pass\",\"summary\":\"Review passed.\",\"evidence_refs\":[{\"kind\":\"artifact\",\"file\":\"$NOFLOW_DIR/$NOFLOW_SLUG--deliver.md\",\"summary\":\"Reviewed the delivery artifact.\"}]}" \
|
|
170
|
+
--artifact-ref "$NOFLOW_DIR/$NOFLOW_SLUG--deliver.md" \
|
|
170
171
|
--timestamp "2026-06-01T10:02:00Z" >/dev/null 2>&1
|
|
171
172
|
|
|
172
173
|
# Assert only workflow.* claims survived (no builder.* contamination)
|
|
@@ -189,8 +190,8 @@ console.log('after record-critique: workflow.check.test + workflow.critique.revi
|
|
|
189
190
|
echo ""
|
|
190
191
|
echo "=== 3. evidenceClean/critiqueClean correct for builder.* bundle ==="
|
|
191
192
|
|
|
192
|
-
# Create a fresh
|
|
193
|
-
DOGFOOD_AROOT="$TMP/dogfood-
|
|
193
|
+
# Create a fresh noncanonical FlowDefinition-projected session at verify for dogfood-pass.
|
|
194
|
+
DOGFOOD_AROOT="$TMP/dogfood-project/.kontourai/flow-agents"
|
|
194
195
|
DOGFOOD_SLUG="dogfood-clean-test"
|
|
195
196
|
DOGFOOD_DIR="$DOGFOOD_AROOT/$DOGFOOD_SLUG"
|
|
196
197
|
mkdir -p "$DOGFOOD_AROOT"
|
|
@@ -200,7 +201,6 @@ flow_agents_node "$WRITER" ensure-session \
|
|
|
200
201
|
--task-slug "$DOGFOOD_SLUG" \
|
|
201
202
|
--title "Dogfood clean test" \
|
|
202
203
|
--summary "Test evidenceClean/critiqueClean on builder.build session." \
|
|
203
|
-
--flow-id builder.build \
|
|
204
204
|
--timestamp "2026-06-01T20:00:00Z" >/dev/null 2>&1
|
|
205
205
|
|
|
206
206
|
flow_agents_node "$WRITER" init-plan "$DOGFOOD_DIR/$DOGFOOD_SLUG--deliver.md" \
|
|
@@ -224,6 +224,8 @@ flow_agents_node "$WRITER" record-critique "$DOGFOOD_DIR" \
|
|
|
224
224
|
--id "ev-critique" \
|
|
225
225
|
--verdict pass \
|
|
226
226
|
--summary "Critique passed." \
|
|
227
|
+
--lane-json "{\"id\":\"code\",\"status\":\"pass\",\"summary\":\"Critique passed.\",\"evidence_refs\":[{\"kind\":\"artifact\",\"file\":\"$DOGFOOD_DIR/$DOGFOOD_SLUG--deliver.md\",\"summary\":\"Reviewed the delivery artifact.\"}]}" \
|
|
228
|
+
--artifact-ref "$DOGFOOD_DIR/$DOGFOOD_SLUG--deliver.md" \
|
|
227
229
|
--timestamp "2026-06-01T20:02:00Z" >/dev/null 2>&1
|
|
228
230
|
|
|
229
231
|
# dogfood-pass --verdict pass should succeed: evidenceClean=true (builder.verify.tests passes)
|
|
@@ -1156,16 +1156,14 @@ echo "=== Diff scope check ==="
|
|
|
1156
1156
|
# Verify that ONLY the allowed files were modified.
|
|
1157
1157
|
# Round 2 (fix/gate-lockdown) scope: config-protection.js, stop-goal-fit.js, evidence-capture.js.
|
|
1158
1158
|
# The security-hardening files (config-protection.js, stop-goal-fit.js, workflow-sidecar.ts,
|
|
1159
|
-
# flow-resolver.ts, evidence-capture.js) are legitimately modified across the rounds
|
|
1160
|
-
#
|
|
1159
|
+
# flow-resolver.ts, evidence-capture.js) are legitimately modified across the rounds. This
|
|
1160
|
+
# check protects the cross-kit collision boundary; it must not reject legitimate changes to
|
|
1161
|
+
# Builder skills merely because another issue originally treated them as out of scope.
|
|
1161
1162
|
# Use grep patterns to avoid triggering the source path validator.
|
|
1162
1163
|
FORBIDDEN_MODIFIED=""
|
|
1163
1164
|
FORBIDDEN_PATTERNS=(
|
|
1164
1165
|
"kits/knowledge/"
|
|
1165
1166
|
)
|
|
1166
|
-
# continue-work: the collision boundary skill file (not in scripts/hooks/).
|
|
1167
|
-
# Use a conservative basename check to avoid a false src-path reference.
|
|
1168
|
-
FORBIDDEN_PATTERNS+=("continue-work")
|
|
1169
1167
|
for pat in "${FORBIDDEN_PATTERNS[@]}"; do
|
|
1170
1168
|
if git -C "$ROOT" diff --name-only HEAD 2>/dev/null | grep -q "$pat"; then
|
|
1171
1169
|
FORBIDDEN_MODIFIED="$FORBIDDEN_MODIFIED $pat"
|
|
@@ -2242,12 +2242,12 @@ node - "$ROOT/scripts/hooks/stop-goal-fit.js" <<'NODEEOF' 2>"$TMPDIR_EVAL/hardbl
|
|
|
2242
2242
|
const fs = require('fs');
|
|
2243
2243
|
const file = process.argv[2];
|
|
2244
2244
|
let src = fs.readFileSync(file, 'utf8');
|
|
2245
|
-
const needle = "const HARD_BLOCK = /contradicts evidence\\.json|caught false-completion|evidence verdict:|evidence check .+ status:|critique status|critique open|required sidecar is missing|command-log integrity check FAILED|gate misconfiguration:|exit-code-laundered|NOT_VERIFIED \\(ambiguous\\)/;";
|
|
2245
|
+
const needle = "const HARD_BLOCK = /contradicts evidence\\.json|caught false-completion|evidence verdict:|evidence check .+ status:|critique status|critique open|required sidecar is missing|command-log integrity check FAILED|gate misconfiguration:|exit-code-laundered|NOT_VERIFIED \\(ambiguous\\)|canonical Flow (?:run remains active|state is unsafe or malformed)/;";
|
|
2246
2246
|
if (!src.includes(needle)) {
|
|
2247
2247
|
process.stderr.write('mutation: HARD_BLOCK NOT_VERIFIED (ambiguous) pattern not found — source pattern drifted, cannot mutation-test\n');
|
|
2248
2248
|
process.exit(1);
|
|
2249
2249
|
}
|
|
2250
|
-
const mutated = "const HARD_BLOCK = /contradicts evidence\\.json|caught false-completion|evidence verdict:|evidence check .+ status:|critique status|critique open|required sidecar is missing|command-log integrity check FAILED|gate misconfiguration:|exit-code-laundered/;";
|
|
2250
|
+
const mutated = "const HARD_BLOCK = /contradicts evidence\\.json|caught false-completion|evidence verdict:|evidence check .+ status:|critique status|critique open|required sidecar is missing|command-log integrity check FAILED|gate misconfiguration:|exit-code-laundered|canonical Flow (?:run remains active|state is unsafe or malformed)/;";
|
|
2251
2251
|
src = src.split(needle).join(mutated);
|
|
2252
2252
|
fs.writeFileSync(file, src);
|
|
2253
2253
|
NODEEOF
|
|
@@ -2712,6 +2712,64 @@ else
|
|
|
2712
2712
|
_fail "mutation-test cleanup re-check REGRESSION: restored stop-goal-fit.js no longer confirms a clean end-to-end pass for the dialect fixture: $(cat "$TMPDIR_EVAL/dialect-restore-e2e.out" "$TMPDIR_EVAL/dialect-restore-e2e.err")"
|
|
2713
2713
|
fi
|
|
2714
2714
|
|
|
2715
|
+
# Canonical Flow is authoritative for an active scoped session. Parent-directory
|
|
2716
|
+
# symlinks and malformed run state must therefore fail closed instead of being
|
|
2717
|
+
# silently treated as a missing optional run.
|
|
2718
|
+
CANONICAL_UNSAFE_REPO="$TMPDIR_EVAL/canonical-unsafe-repo"
|
|
2719
|
+
CANONICAL_UNSAFE_SESSION="$CANONICAL_UNSAFE_REPO/.kontourai/flow-agents/canonical-unsafe"
|
|
2720
|
+
mkdir -p "$CANONICAL_UNSAFE_SESSION"
|
|
2721
|
+
printf '# Test Repo\n' > "$CANONICAL_UNSAFE_REPO/AGENTS.md"
|
|
2722
|
+
cat > "$CANONICAL_UNSAFE_SESSION/canonical-unsafe--deliver.md" <<'MARKDOWN'
|
|
2723
|
+
# Canonical unsafe fixture
|
|
2724
|
+
|
|
2725
|
+
status: executing
|
|
2726
|
+
type: deliver
|
|
2727
|
+
MARKDOWN
|
|
2728
|
+
cat > "$CANONICAL_UNSAFE_SESSION/state.json" <<'JSON'
|
|
2729
|
+
{
|
|
2730
|
+
"task_slug": "canonical-unsafe",
|
|
2731
|
+
"status": "executing",
|
|
2732
|
+
"phase": "execution",
|
|
2733
|
+
"flow_run": { "status": "active", "current_step": "verify" }
|
|
2734
|
+
}
|
|
2735
|
+
JSON
|
|
2736
|
+
cat > "$CANONICAL_UNSAFE_REPO/.kontourai/flow-agents/current.json" <<'JSON'
|
|
2737
|
+
{ "active_slug": "canonical-unsafe" }
|
|
2738
|
+
JSON
|
|
2739
|
+
CANONICAL_OUTSIDE="$TMPDIR_EVAL/canonical-outside"
|
|
2740
|
+
mkdir -p "$CANONICAL_OUTSIDE"
|
|
2741
|
+
ln -s "$CANONICAL_OUTSIDE" "$CANONICAL_UNSAFE_REPO/.kontourai/flow"
|
|
2742
|
+
if FLOW_AGENTS_GOAL_FIT_STRICT=true node "$ROOT/scripts/hooks/stop-goal-fit.js" >"$TMPDIR_EVAL/canonical-symlink.out" 2>"$TMPDIR_EVAL/canonical-symlink.err" <<JSON
|
|
2743
|
+
{"hook_event_name":"Stop","cwd":"$CANONICAL_UNSAFE_REPO"}
|
|
2744
|
+
JSON
|
|
2745
|
+
then
|
|
2746
|
+
_fail "strict goal-fit hook allowed an active scoped session with a symlinked canonical Flow parent"
|
|
2747
|
+
elif [[ "$?" -eq 2 ]] && grep -q 'canonical Flow state is unsafe or malformed' "$TMPDIR_EVAL/canonical-symlink.err"; then
|
|
2748
|
+
_pass "strict goal-fit hook rejects symlinked canonical Flow parent components for an active scoped session"
|
|
2749
|
+
else
|
|
2750
|
+
_fail "strict goal-fit hook did not report the unsafe canonical Flow parent: $(cat "$TMPDIR_EVAL/canonical-symlink.err")"
|
|
2751
|
+
fi
|
|
2752
|
+
|
|
2753
|
+
rm "$CANONICAL_UNSAFE_REPO/.kontourai/flow"
|
|
2754
|
+
mkdir -p "$CANONICAL_UNSAFE_REPO/.kontourai/flow/runs/canonical-unsafe"
|
|
2755
|
+
printf 'not-json\n' > "$CANONICAL_UNSAFE_REPO/.kontourai/flow/runs/canonical-unsafe/state.json"
|
|
2756
|
+
if FLOW_AGENTS_GOAL_FIT_STRICT=true node "$ROOT/scripts/hooks/stop-goal-fit.js" >"$TMPDIR_EVAL/canonical-malformed.out" 2>"$TMPDIR_EVAL/canonical-malformed.err" <<JSON
|
|
2757
|
+
{"hook_event_name":"Stop","cwd":"$CANONICAL_UNSAFE_REPO"}
|
|
2758
|
+
JSON
|
|
2759
|
+
then
|
|
2760
|
+
_fail "strict goal-fit hook allowed an active scoped session with malformed canonical Flow state"
|
|
2761
|
+
elif [[ "$?" -eq 2 ]] && grep -q 'canonical Flow state is unsafe or malformed' "$TMPDIR_EVAL/canonical-malformed.err"; then
|
|
2762
|
+
_pass "strict goal-fit hook fails closed for malformed canonical Flow state of an active scoped session"
|
|
2763
|
+
else
|
|
2764
|
+
_fail "strict goal-fit hook did not report malformed canonical Flow state: $(cat "$TMPDIR_EVAL/canonical-malformed.err")"
|
|
2765
|
+
fi
|
|
2766
|
+
|
|
2767
|
+
if cmp -s "$ROOT/scripts/hooks/stop-goal-fit.js" "$ROOT/context/scripts/hooks/stop-goal-fit.js"; then
|
|
2768
|
+
_pass "canonical Stop hook source and shipped context mirror remain byte-identical"
|
|
2769
|
+
else
|
|
2770
|
+
_fail "canonical Stop hook source and shipped context mirror diverged"
|
|
2771
|
+
fi
|
|
2772
|
+
|
|
2715
2773
|
|
|
2716
2774
|
if [[ "$errors" -eq 0 ]]; then
|
|
2717
2775
|
echo "Goal Fit hook integration passed."
|