From 3c9ab1ce3b03ee141aad4b46d0a2cad1cf781914 Mon Sep 17 00:00:00 2001 From: Relayflow Date: Tue, 6 Oct 2026 14:41:54 +0000 Subject: [PATCH 1/3] feat(check): validate declarative specs with the installed daemon --- docs/EDITOR.md | 9 + docs/SURFACE.md | 37 + evidence/daemon-check/README.md | 84 + evidence/daemon-check/cli-surface-final.txt | 10 + evidence/daemon-check/diff-check.txt | 0 evidence/daemon-check/live-cli.txt | 11 + evidence/daemon-check/live-ladder.txt | 15 + .../mutation-indices-authored-mutated.txt | 66 + .../mutation-indices-authored-restored.txt | 14 + .../mutation-indices-generated-mutated.txt | 42 + .../mutation-indices-generated-restored.txt | 14 + .../mutation-refusal-cli-mutated.txt | 37 + .../mutation-refusal-cli-restored.txt | 15 + .../mutation-refusal-unit-mutated.txt | 37 + .../mutation-refusal-unit-restored.txt | 14 + evidence/daemon-check/mutation-run.py | 56 + evidence/daemon-check/mutations.txt | 12 + evidence/daemon-check/package-gate.txt | 28 + .../daemon-check/plugin-extension-retry.txt | 10 + evidence/daemon-check/rust-targeted-final.txt | 11 + evidence/daemon-check/rust-targeted-retry.txt | 154 ++ evidence/daemon-check/rust-targeted.txt | 2 + .../daemon-check/rust-workspace-retry.txt | 673 +++++++ evidence/daemon-check/rust-workspace.txt | 42 + evidence/daemon-check/rustfmt-setup.txt | 1 + evidence/daemon-check/sdk-build-final.txt | 4 + evidence/daemon-check/sdk-build.txt | 4 + evidence/daemon-check/sdk-final.txt | 29 + evidence/daemon-check/sdk-full.txt | 1599 +++++++++++++++++ evidence/daemon-check/sdk-minimal-path.txt | 17 + evidence/daemon-check/sdk-targeted-final.txt | 28 + evidence/daemon-check/sdk-targeted.txt | 17 + evidence/daemon-check/standalone-build.txt | 6 + evidence/daemon-check/standalone-check.txt | 4 + evidence/daemon-check/timing.txt | 20 + evidence/daemon-check/typecheck-final.txt | 8 + .../daemon-check/typecheck-tests-final.txt | 4 + kernel/DAEMON-LIFECYCLE.md | 14 + kernel/relayflowd/src/lib.rs | 2 + kernel/relayflowd/src/main.rs | 11 + kernel/relayflowd/src/validate_spec.rs | 17 + kernel/relayflowd/tests/validate_spec.rs | 81 + packages/sdk/src/cli-commands.ts | 4 +- packages/sdk/src/cli-watch.ts | 8 +- packages/sdk/src/cli.ts | 39 +- packages/sdk/src/cli/check.ts | 20 +- packages/sdk/src/daemon-spec-validation.ts | 119 ++ packages/sdk/src/failure-kinds.ts | 4 +- .../sdk/tests/check-daemon-validation.test.ts | 199 ++ packages/sdk/tests/cli-watch.test.ts | 16 +- packages/sdk/tests/cli.test.ts | 2 + packages/sdk/tests/live-kernel.test.ts | 15 + packages/sdk/tests/relay-cli-surface.test.ts | 2 + packages/sdk/tsconfig.tests.json | 1 + packages/sdk/vitest.config.ts | 1 + 55 files changed, 3661 insertions(+), 28 deletions(-) create mode 100644 evidence/daemon-check/README.md create mode 100644 evidence/daemon-check/cli-surface-final.txt create mode 100644 evidence/daemon-check/diff-check.txt create mode 100644 evidence/daemon-check/live-cli.txt create mode 100644 evidence/daemon-check/live-ladder.txt create mode 100644 evidence/daemon-check/mutation-indices-authored-mutated.txt create mode 100644 evidence/daemon-check/mutation-indices-authored-restored.txt create mode 100644 evidence/daemon-check/mutation-indices-generated-mutated.txt create mode 100644 evidence/daemon-check/mutation-indices-generated-restored.txt create mode 100644 evidence/daemon-check/mutation-refusal-cli-mutated.txt create mode 100644 evidence/daemon-check/mutation-refusal-cli-restored.txt create mode 100644 evidence/daemon-check/mutation-refusal-unit-mutated.txt create mode 100644 evidence/daemon-check/mutation-refusal-unit-restored.txt create mode 100644 evidence/daemon-check/mutation-run.py create mode 100644 evidence/daemon-check/mutations.txt create mode 100644 evidence/daemon-check/package-gate.txt create mode 100644 evidence/daemon-check/plugin-extension-retry.txt create mode 100644 evidence/daemon-check/rust-targeted-final.txt create mode 100644 evidence/daemon-check/rust-targeted-retry.txt create mode 100644 evidence/daemon-check/rust-targeted.txt create mode 100644 evidence/daemon-check/rust-workspace-retry.txt create mode 100644 evidence/daemon-check/rust-workspace.txt create mode 100644 evidence/daemon-check/rustfmt-setup.txt create mode 100644 evidence/daemon-check/sdk-build-final.txt create mode 100644 evidence/daemon-check/sdk-build.txt create mode 100644 evidence/daemon-check/sdk-final.txt create mode 100644 evidence/daemon-check/sdk-full.txt create mode 100644 evidence/daemon-check/sdk-minimal-path.txt create mode 100644 evidence/daemon-check/sdk-targeted-final.txt create mode 100644 evidence/daemon-check/sdk-targeted.txt create mode 100644 evidence/daemon-check/standalone-build.txt create mode 100644 evidence/daemon-check/standalone-check.txt create mode 100644 evidence/daemon-check/timing.txt create mode 100644 evidence/daemon-check/typecheck-final.txt create mode 100644 evidence/daemon-check/typecheck-tests-final.txt create mode 100644 kernel/relayflowd/src/validate_spec.rs create mode 100644 kernel/relayflowd/tests/validate_spec.rs create mode 100644 packages/sdk/src/daemon-spec-validation.ts create mode 100644 packages/sdk/tests/check-daemon-validation.test.ts diff --git a/docs/EDITOR.md b/docs/EDITOR.md index 0685eaa61..fb2784b02 100644 --- a/docs/EDITOR.md +++ b/docs/EDITOR.md @@ -224,3 +224,12 @@ schema's entry point, even though `flows check` can also ingest it. 4. Hover `timeoutMs` and inspect completion after `type: agent`. Do not correct `identitty` to `identity` in this dialect: neither is supported. + + +`flows check --json` also reports `validation`: `mode: "daemon"` includes the +validator binary, protocol and spec version; `mode: "local"` includes a reason +why daemon acceptance is unproven. Editor/schema acceptance alone does not prove +runtime compatibility. Set `FLOWS_NO_DAEMON_CHECK=1` for spawn-free local checks, +or use `--against-daemon` to require the installed binary's stateless validator. +Authored TypeScript bodies remain local header checks. See SURFACE.md's +“What `flows check` proves about the installed runtime” for scope and old binaries. diff --git a/docs/SURFACE.md b/docs/SURFACE.md index 29c6887f3..1e87fbae6 100644 --- a/docs/SURFACE.md +++ b/docs/SURFACE.md @@ -1947,3 +1947,40 @@ captured, hash-verified authored source bytes at their original module URLs. Successful root output records the Node version, executable SHA256, and embedded payload SHA256 as `executionRuntime`. The payload remains part of the existing artifact hash, and completed child effects remain journal results on resume. + + +### What `flows check` proves about the installed runtime + +For declarative YAML/JSON specs, `flows check` now also pipes the compiled kernel +spec to the installed `relayflowd validate-spec` when available. This performs +`RunSpec::parse` and `validate`, just like `run.start` (and is stricter than +`event.submit`, which currently only parses). No socket is opened, run created, +or journal written. The input is the same spec value `run.start` submits, in +canonical form. + +- Default: automatic validation when the installed binary supports it; otherwise + local compile and preflight, with a reason on the `CHECK PASSED` line. +- `--against-daemon`: require a verdict; unavailable or incompatible validators + refuse with `daemon_validation_unavailable` and exit 2. +- `--no-daemon-check`: local compile and preflight only, without binary discovery. + +`FLOWS_NO_DAEMON_CHECK=1` and `FLOWS_CHECK_AGAINST_DAEMON=1` select the same modes +for an environment; explicit flags win. If both environment variables are set, +the offline opt-out wins. Watch mode preserves the selection. + +A daemon refusal uses `invalid_spec`, preserving the kernel field and step +location. A refusal on a generated gate step identifies an SDK lowering bug, +not an authoring mistake. JSON reports carry `validation` on success and refusal; +human output annotates the pass line, without adding a refusal terminator. + +The annotation names the binary that judged the spec. An already running daemon +from a different build may disagree; this check does not contact that service. +Older binaries (including 2.0.19) lack `validate-spec`: automatic mode warns +`daemon_unvalidated`, naming the binary and the unproven acceptance, while +required mode refuses. This catches skew going forward, not retroactively. + +Authored TypeScript bodies are not lowered until execution. Their header checks +remain local, with `authored_body` as the validation reason (required mode +refuses). This change therefore does not cover the authored-body case reported +in #489. Body capability probing and requiring daemon checks in release artifact +packaging are follow-on work. diff --git a/evidence/daemon-check/README.md b/evidence/daemon-check/README.md new file mode 100644 index 000000000..387e730a7 --- /dev/null +++ b/evidence/daemon-check/README.md @@ -0,0 +1,84 @@ +Commands were run from the repository root unless the command includes `cd`. +All `.txt` files contain captured output, not reconstructed output. + +`cd packages/sdk && npx vitest run tests/check-daemon-validation.test.ts tests/cli.test.ts tests/cli-watch.test.ts tests/relay-cli-surface.test.ts` + +[Captured output](sdk-final.txt) + +`cd packages/sdk && npx vitest run tests/check-daemon-validation.test.ts tests/cli.test.ts tests/cli-watch.test.ts` + +[Captured output](sdk-targeted-final.txt) + +`cd packages/sdk && npx vitest run tests/relay-cli-surface.test.ts` + +[Captured output](cli-surface-final.txt) + +`cd packages/sdk && npx vitest run tests/plugin-extension.test.ts` + +[Captured output](plugin-extension-retry.txt) + +`cd packages/sdk && RELAYFLOWD_BIN=/tmp/relayflows-daemon-check-target/debug/relayflowd npx vitest run tests/live-kernel.test.ts -t 'validates the canonical ladder'` + +[Captured output](live-ladder.txt) + +`CARGO_TARGET_DIR=/tmp/relayflows-daemon-check-target CARGO_PROFILE_DEV_DEBUG=0 CARGO_PROFILE_TEST_DEBUG=0 CARGO_INCREMENTAL=0 /home/daytona/.cargo/bin/cargo test --manifest-path kernel/Cargo.toml -p relayflowd --test validate_spec` + +[Captured output](rust-targeted-final.txt) + +`CARGO_TARGET_DIR=/tmp/relayflows-daemon-check-target CARGO_PROFILE_DEV_DEBUG=0 CARGO_PROFILE_TEST_DEBUG=0 CARGO_INCREMENTAL=0 /home/daytona/.cargo/bin/cargo test --manifest-path kernel/Cargo.toml -j 2` + +[Captured output](rust-workspace-retry.txt) + +`cd packages/sdk && RELAYFLOWS_TOOLCHAIN_HOME=/tmp/daemon-check-toolchain CARGO_TARGET_DIR=/tmp/relayflows-daemon-check-target npm test` + +[Captured output](sdk-full.txt) + +`cd packages/sdk && PATH=/usr/bin:/bin /usr/local/share/nvm/versions/node/v25.6.0/bin/node node_modules/vitest/vitest.mjs run tests/check-daemon-validation.test.ts tests/cli.test.ts` + +[Captured output](sdk-minimal-path.txt) + +`node scripts/cli-package-gate.mjs` + +[Captured output](package-gate.txt) + +`node scripts/build-standalone-cli.mjs bun-linux-x64 /tmp/flows-daemon-check` + +[Captured output](standalone-build.txt) + +`RELAYFLOWD_BIN=/tmp/stub-relayflowd-check /tmp/flows-daemon-check check --against-daemon --json testdata/hello-deterministic.flow.yaml` + +[Captured output](standalone-check.txt) + +`cd packages/sdk && npm run typecheck && npm run typecheck:tests` + +[Captured output](typecheck-final.txt) + +`RELAYFLOWD_BIN=/tmp/relayflows-daemon-check-target/debug/relayflowd node packages/sdk/dist/cli.js check --against-daemon testdata/hello-agent.flow.yaml` + +[Captured output](live-cli.txt) + +`python3 evidence/daemon-check/mutation-run.py` + +[Captured output](mutations.txt) + +`git diff --check` + +[Captured output](diff-check.txt) + +The standalone stub consumed stdin and returned this envelope without running a flow: + +```sh +#!/bin/sh +cat >/dev/null +printf '%s\n' '{"ok":true,"protocol":0,"spec_version":"0.1.0"}' +``` + +Additional historical captures: `rust-targeted.txt` is the private-toolchain +failure; `rust-targeted-retry.txt` contains the initial incorrect test assertion +for the cwd message; `rust-workspace.txt` contains the disk-full linker failure. +`sdk-build*.txt`, `typecheck-tests-final.txt`, and `sdk-targeted.txt` are earlier +build/typecheck/targeted captures. `rustfmt-setup.txt` records tool installation. +`timing.txt` captures shell `time` for the deterministic fixture in auto mode +with RELAYFLOWD_BIN pinned to the built binary, then with --no-daemon-check. +Each `mutation-*.txt` embeds its exact command and exit status. Temporary mutation +working directories are removed by the script; the captured outputs here persist. diff --git a/evidence/daemon-check/cli-surface-final.txt b/evidence/daemon-check/cli-surface-final.txt new file mode 100644 index 000000000..54812636c --- /dev/null +++ b/evidence/daemon-check/cli-surface-final.txt @@ -0,0 +1,10 @@ + + RUN v2.1.9 /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk + + ✓ tests/relay-cli-surface.test.ts (89 tests) 83ms + + Test Files 1 passed (1) + Tests 89 passed (89) + Start at 14:36:15 + Duration 3.18s (transform 1.91s, setup 23ms, collect 2.88s, tests 83ms, environment 0ms, prepare 44ms) + diff --git a/evidence/daemon-check/diff-check.txt b/evidence/daemon-check/diff-check.txt new file mode 100644 index 000000000..e69de29bb diff --git a/evidence/daemon-check/live-cli.txt b/evidence/daemon-check/live-cli.txt new file mode 100644 index 000000000..0238afa3d --- /dev/null +++ b/evidence/daemon-check/live-cli.txt @@ -0,0 +1,11 @@ +WARNING [permissions_unenforced] Step "edit" declares permissions (fileGlobs, accessPreset). This declaration is validated and recorded with the step spec; it is not currently enforced (gate 8 / #442). These permissions do not restrict file or network access. +WARNING [unprovable_effects] Step "greet" command "printf" resolves, but its effects cannot be proven before execution. +WARNING [unprovable_effects] Step "finish" command "printf" resolves, but its effects cannot be proven before execution. +WARNING [editor_schema_missing] For editor validation, add this first line: # yaml-language-server: $schema=https://schema.relayflows.dev/v0.1/flows.schema.json +GATE step "greet" exit_code+output_contains from data (kernel, journal-replayable) +GATE step "edit" output_contains from data (kernel, journal-replayable) +GATE step "finish" exit_code from data (kernel, journal-replayable) +RESOLVED step "edit" cli "./preflight/authenticated-cli" from project (/home/daytona/.relayflow-v2-supervisor/durable/repository/testdata/flows.json) model "test-model-v1" from step +REQUIRES claude (step "edit") +WARNING [agent_worker_unresolved] 1 agent step ("edit") requires an attached worker. For a local run, use `flows run --local-agent `, unless you already attach an agent worker for this daemon. `flows check` does not verify worker attachment: with no worker attached the run parks at the first agent step. +CHECK PASSED testdata/hello-agent.flow.yaml [validated by relayflowd at /tmp/relayflows-daemon-check-target/debug/relayflowd, protocol 0] diff --git a/evidence/daemon-check/live-ladder.txt b/evidence/daemon-check/live-ladder.txt new file mode 100644 index 000000000..0e04a9a51 --- /dev/null +++ b/evidence/daemon-check/live-ladder.txt @@ -0,0 +1,15 @@ + + RUN v2.1.9 /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk + +stdout | tests/live-kernel.test.ts +LIVE_KERNEL relayflowd=/tmp/relayflows-daemon-check-target/debug/relayflowd +LIVE_KERNEL flows=/home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/dist/cli.js + + ✓ tests/live-kernel.test.ts (33 tests | 32 skipped) 306ms + ✓ validates the canonical ladder with the built daemon without running it 303ms + + Test Files 1 passed (1) + Tests 1 passed | 32 skipped (33) + Start at 14:31:05 + Duration 4.41s (transform 2.31s, setup 62ms, collect 3.80s, tests 306ms, environment 0ms, prepare 46ms) + diff --git a/evidence/daemon-check/mutation-indices-authored-mutated.txt b/evidence/daemon-check/mutation-indices-authored-mutated.txt new file mode 100644 index 000000000..c322f9233 --- /dev/null +++ b/evidence/daemon-check/mutation-indices-authored-mutated.txt @@ -0,0 +1,66 @@ +$ 'node' '/home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/node_modules/vitest/vitest.mjs' 'run' 'tests/check-daemon-validation.test.ts' '-t' 'maps authored steps following generated steps' +cwd: /tmp/daemon-check-mutation-avim9yyh/packages/sdk + + RUN v2.1.9 /tmp/daemon-check-mutation-avim9yyh/packages/sdk + + ❯ tests/check-daemon-validation.test.ts (20 tests | 1 failed | 19 skipped) 24ms + × maps authored steps following generated steps without blaming the compiler 23ms + → expected [ { severity: 'warning', …(3) }, …(2) ] to deep equally contain { severity: 'refusal', …(4) } + +⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯ + + FAIL tests/check-daemon-validation.test.ts > maps authored steps following generated steps without blaming the compiler +AssertionError: expected [ { severity: 'warning', …(3) }, …(2) ] to deep equally contain { severity: 'refusal', …(4) } + +- Expected: +Object { + "errors": Array [ + "unknown field \"nope\" at steps[2]", + ], + "kind": "invalid_spec", + "message": "unknown field \"nope\" at steps[2]", + "severity": "refusal", + "stepId": "second", +} + ++ Received: +Array [ + Object { + "kind": "unprovable_effects", + "message": "Step \"first\" command \"echo\" resolves, but its effects cannot be proven before execution.", + "severity": "warning", + "stepId": "first", + }, + Object { + "kind": "unprovable_effects", + "message": "Step \"second\" command \"echo\" resolves, but its effects cannot be proven before execution.", + "severity": "warning", + "stepId": "second", + }, + Object { + "errors": Array [ + "unknown field \"nope\" at steps[2]", + ], + "kind": "invalid_spec", + "message": "unknown field \"nope\" at steps[2]", + "severity": "refusal", + }, +] + + ❯ tests/check-daemon-validation.test.ts:178:38 + 176| { daemonValidation: 'required', daemonValidationDeps: deps('unknow… + 177| expect(checked.report.ok).toBe(false); + 178| expect(checked.report.diagnostics).toContainEqual({ severity: 'refus… + | ^ + 179| message: 'unknown field "nope" at steps[2]', errors: ['unknown fie… + 180| }); + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯ + + Test Files 1 failed (1) + Tests 1 failed | 19 skipped (20) + Start at 14:32:45 + Duration 2.04s (transform 1.15s, setup 17ms, collect 1.80s, tests 24ms, environment 0ms, prepare 71ms) + + +exit=1 diff --git a/evidence/daemon-check/mutation-indices-authored-restored.txt b/evidence/daemon-check/mutation-indices-authored-restored.txt new file mode 100644 index 000000000..1e960a4c2 --- /dev/null +++ b/evidence/daemon-check/mutation-indices-authored-restored.txt @@ -0,0 +1,14 @@ +$ 'node' '/home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/node_modules/vitest/vitest.mjs' 'run' 'tests/check-daemon-validation.test.ts' '-t' 'maps authored steps following generated steps' +cwd: /tmp/daemon-check-mutation-avim9yyh/packages/sdk + + RUN v2.1.9 /tmp/daemon-check-mutation-avim9yyh/packages/sdk + + ✓ tests/check-daemon-validation.test.ts (20 tests | 19 skipped) 30ms + + Test Files 1 passed (1) + Tests 1 passed | 19 skipped (20) + Start at 14:32:50 + Duration 2.38s (transform 1.35s, setup 16ms, collect 2.02s, tests 30ms, environment 0ms, prepare 85ms) + + +exit=0 diff --git a/evidence/daemon-check/mutation-indices-generated-mutated.txt b/evidence/daemon-check/mutation-indices-generated-mutated.txt new file mode 100644 index 000000000..be45ae11e --- /dev/null +++ b/evidence/daemon-check/mutation-indices-generated-mutated.txt @@ -0,0 +1,42 @@ +$ 'node' '/home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/node_modules/vitest/vitest.mjs' 'run' 'tests/check-daemon-validation.test.ts' '-t' 'maps generated-step indices' +cwd: /tmp/daemon-check-mutation-avim9yyh/packages/sdk + + RUN v2.1.9 /tmp/daemon-check-mutation-avim9yyh/packages/sdk + + ❯ tests/check-daemon-validation.test.ts (20 tests | 1 failed | 19 skipped) 12ms + × maps generated-step indices to submitted steps and identifies compiler bugs 11ms + → expected { severity: 'refusal', …(4) } to match object { stepId: 'first.gate', …(1) } +(3 matching properties omitted from actual) + +⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯ + + FAIL tests/check-daemon-validation.test.ts > maps generated-step indices to submitted steps and identifies compiler bugs +AssertionError: expected { severity: 'refusal', …(4) } to match object { stepId: 'first.gate', …(1) } +(3 matching properties omitted from actual) + +- Expected ++ Received + + Object { + "kind": "invalid_spec", +- "stepId": "first.gate", ++ "stepId": "second", + } + + ❯ tests/check-daemon-validation.test.ts:41:33 + 39| verification: { type: 'artifact_exists', path: 'answer.txt' } }, f… + 40| const result = validateSpecWithDaemon(gated, 'required', deps('unkno… + 41| expect(result.diagnostics[0]).toMatchObject({ stepId: 'first.gate', … + | ^ + 42| expect(result.diagnostics[0]?.message).toContain('generated by this … + 43| expect(result.diagnostics[0]?.message).toContain('Report this rather… + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯ + + Test Files 1 failed (1) + Tests 1 failed | 19 skipped (20) + Start at 14:32:43 + Duration 2.09s (transform 1.18s, setup 14ms, collect 1.86s, tests 12ms, environment 0ms, prepare 84ms) + + +exit=1 diff --git a/evidence/daemon-check/mutation-indices-generated-restored.txt b/evidence/daemon-check/mutation-indices-generated-restored.txt new file mode 100644 index 000000000..8b033b548 --- /dev/null +++ b/evidence/daemon-check/mutation-indices-generated-restored.txt @@ -0,0 +1,14 @@ +$ 'node' '/home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/node_modules/vitest/vitest.mjs' 'run' 'tests/check-daemon-validation.test.ts' '-t' 'maps generated-step indices' +cwd: /tmp/daemon-check-mutation-avim9yyh/packages/sdk + + RUN v2.1.9 /tmp/daemon-check-mutation-avim9yyh/packages/sdk + + ✓ tests/check-daemon-validation.test.ts (20 tests | 19 skipped) 6ms + + Test Files 1 passed (1) + Tests 1 passed | 19 skipped (20) + Start at 14:32:47 + Duration 2.55s (transform 1.49s, setup 14ms, collect 2.28s, tests 6ms, environment 0ms, prepare 47ms) + + +exit=0 diff --git a/evidence/daemon-check/mutation-refusal-cli-mutated.txt b/evidence/daemon-check/mutation-refusal-cli-mutated.txt new file mode 100644 index 000000000..64b5f390e --- /dev/null +++ b/evidence/daemon-check/mutation-refusal-cli-mutated.txt @@ -0,0 +1,37 @@ +$ 'node' '/home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/node_modules/vitest/vitest.mjs' 'run' 'tests/check-daemon-validation.test.ts' '-t' 'real CLI refuses skew in auto and required modes' +cwd: /tmp/daemon-check-mutation-avim9yyh/packages/sdk + + RUN v2.1.9 /tmp/daemon-check-mutation-avim9yyh/packages/sdk + + ❯ tests/check-daemon-validation.test.ts (20 tests | 1 failed | 19 skipped) 496ms + × real CLI refuses skew in auto and required modes 495ms + → expected +0 to be 2 // Object.is equality + +⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯ + + FAIL tests/check-daemon-validation.test.ts > real CLI refuses skew in auto and required modes +AssertionError: expected +0 to be 2 // Object.is equality + +- Expected ++ Received + +- 2 ++ 0 + + ❯ tests/check-daemon-validation.test.ts:106:27 + 104| for (const flags of [[], ['--against-daemon']]) { + 105| const result = cli(f.path, f.binary, flags); + 106| expect(result.status).toBe(2); + | ^ + 107| expect(result.stderr).toContain(`REFUSED [invalid_spec] ${message}… + 108| } + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯ + + Test Files 1 failed (1) + Tests 1 failed | 19 skipped (20) + Start at 14:32:34 + Duration 2.52s (transform 1.16s, setup 16ms, collect 1.80s, tests 496ms, environment 0ms, prepare 45ms) + + +exit=1 diff --git a/evidence/daemon-check/mutation-refusal-cli-restored.txt b/evidence/daemon-check/mutation-refusal-cli-restored.txt new file mode 100644 index 000000000..0d822959c --- /dev/null +++ b/evidence/daemon-check/mutation-refusal-cli-restored.txt @@ -0,0 +1,15 @@ +$ 'node' '/home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/node_modules/vitest/vitest.mjs' 'run' 'tests/check-daemon-validation.test.ts' '-t' 'real CLI refuses skew in auto and required modes' +cwd: /tmp/daemon-check-mutation-avim9yyh/packages/sdk + + RUN v2.1.9 /tmp/daemon-check-mutation-avim9yyh/packages/sdk + + ✓ tests/check-daemon-validation.test.ts (20 tests | 19 skipped) 865ms + ✓ real CLI refuses skew in auto and required modes 864ms + + Test Files 1 passed (1) + Tests 1 passed | 19 skipped (20) + Start at 14:32:39 + Duration 3.03s (transform 1.29s, setup 47ms, collect 1.95s, tests 865ms, environment 0ms, prepare 42ms) + + +exit=0 diff --git a/evidence/daemon-check/mutation-refusal-unit-mutated.txt b/evidence/daemon-check/mutation-refusal-unit-mutated.txt new file mode 100644 index 000000000..27aa38a1b --- /dev/null +++ b/evidence/daemon-check/mutation-refusal-unit-mutated.txt @@ -0,0 +1,37 @@ +$ 'node' '/home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/node_modules/vitest/vitest.mjs' 'run' 'tests/check-daemon-validation.test.ts' '-t' 'refuses package-valid daemon-invalid skew through check' +cwd: /tmp/daemon-check-mutation-avim9yyh/packages/sdk + + RUN v2.1.9 /tmp/daemon-check-mutation-avim9yyh/packages/sdk + + ❯ tests/check-daemon-validation.test.ts (20 tests | 1 failed | 19 skipped) 29ms + × refuses package-valid daemon-invalid skew through check 28ms + → expected true to be false // Object.is equality + +⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯ + + FAIL tests/check-daemon-validation.test.ts > refuses package-valid daemon-invalid skew through check +AssertionError: expected true to be false // Object.is equality + +- Expected ++ Received + +- false ++ true + + ❯ tests/check-daemon-validation.test.ts:31:28 + 29| const result = checkAuthoredFlow(flow, '/tmp/flow.json', undefined, + 30| { daemonValidation: 'auto', daemonValidationDeps: deps(message) }); + 31| expect(result.report.ok).toBe(false); + | ^ + 32| expect(result.report.diagnostics).toContainEqual({ severity: 'refusa… + 33| message, errors: [message], stepId: 'first' }); + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯ + + Test Files 1 failed (1) + Tests 1 failed | 19 skipped (20) + Start at 14:32:32 + Duration 2.21s (transform 1.29s, setup 19ms, collect 1.94s, tests 29ms, environment 0ms, prepare 45ms) + + +exit=1 diff --git a/evidence/daemon-check/mutation-refusal-unit-restored.txt b/evidence/daemon-check/mutation-refusal-unit-restored.txt new file mode 100644 index 000000000..d844f1d2d --- /dev/null +++ b/evidence/daemon-check/mutation-refusal-unit-restored.txt @@ -0,0 +1,14 @@ +$ 'node' '/home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/node_modules/vitest/vitest.mjs' 'run' 'tests/check-daemon-validation.test.ts' '-t' 'refuses package-valid daemon-invalid skew through check' +cwd: /tmp/daemon-check-mutation-avim9yyh/packages/sdk + + RUN v2.1.9 /tmp/daemon-check-mutation-avim9yyh/packages/sdk + + ✓ tests/check-daemon-validation.test.ts (20 tests | 19 skipped) 23ms + + Test Files 1 passed (1) + Tests 1 passed | 19 skipped (20) + Start at 14:32:37 + Duration 2.05s (transform 1.15s, setup 35ms, collect 1.80s, tests 23ms, environment 0ms, prepare 43ms) + + +exit=0 diff --git a/evidence/daemon-check/mutation-run.py b/evidence/daemon-check/mutation-run.py new file mode 100644 index 000000000..dcccdae8e --- /dev/null +++ b/evidence/daemon-check/mutation-run.py @@ -0,0 +1,56 @@ +"""Run the reviewed mutations in an isolated copy so concurrent suites stay pristine.""" +from pathlib import Path +import hashlib +import shutil +import subprocess +import tempfile + +repo = Path(__file__).resolve().parents[2] +source = repo / 'packages/sdk' +evidence = repo / 'evidence/daemon-check' +with tempfile.TemporaryDirectory(prefix='daemon-check-mutation-') as directory: + root = Path(directory) + sdk = root / 'packages/sdk' + sdk.mkdir(parents=True) + for name in ['src', 'dist']: + shutil.copytree(source / name, sdk / name) + for name in ['package.json', 'vitest.config.ts']: + shutil.copy2(source / name, sdk / name) + (sdk / 'tests').mkdir() + for name in ['check-daemon-validation.test.ts', 'isolate-workspace.ts']: + shutil.copy2(source / 'tests' / name, sdk / 'tests' / name) + (sdk / 'node_modules').symlink_to(source / 'node_modules', target_is_directory=True) + (root / 'testdata').symlink_to(repo / 'testdata', target_is_directory=True) + files = [sdk / 'src/daemon-spec-validation.ts', sdk / 'dist/daemon-spec-validation.js'] + originals = {p: p.read_bytes() for p in files} + cases = [ + ('refusal', 'if (verdict.ok)', 'if (true)', [ + ('unit', 'refuses package-valid daemon-invalid skew through check'), + ('cli', 'real CLI refuses skew in auto and required modes'), + ]), + ('indices', 'submitted.steps[Number(index)]?.id', 'flow.steps[Number(index)]?.id', [ + ('generated', 'maps generated-step indices'), + ('authored', 'maps authored steps following generated steps'), + ]), + ] + for name, before, after, tests in cases: + for path, original in originals.items(): + text = original.decode() + assert before in text, (path, before) + path.write_text(text.replace(before, after)) + for phase in ['mutated', 'restored']: + if phase == 'restored': + for path, original in originals.items(): + path.write_bytes(original) + assert path.read_bytes() == original + for label, test in tests: + command = ['node', str(source / 'node_modules/vitest/vitest.mjs'), 'run', + 'tests/check-daemon-validation.test.ts', '-t', test] + result = subprocess.run(command, cwd=sdk, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + output = evidence / f'mutation-{name}-{label}-{phase}.txt' + output.write_text('$ ' + ' '.join(repr(arg) for arg in command) + '\n' + + f'cwd: {sdk}\n' + result.stdout + f'\nexit={result.returncode}\n') + print(f'{output.name}: exit={result.returncode}', flush=True) + assert (result.returncode != 0) if phase == 'mutated' else (result.returncode == 0) + for path, original in originals.items(): + print(f'Restored byte-for-byte: {path.relative_to(sdk)} sha256={hashlib.sha256(original).hexdigest()}', flush=True) diff --git a/evidence/daemon-check/mutations.txt b/evidence/daemon-check/mutations.txt new file mode 100644 index 000000000..9e6b22c6a --- /dev/null +++ b/evidence/daemon-check/mutations.txt @@ -0,0 +1,12 @@ +mutation-refusal-unit-mutated.txt: exit=1 +mutation-refusal-cli-mutated.txt: exit=1 +mutation-refusal-unit-restored.txt: exit=0 +mutation-refusal-cli-restored.txt: exit=0 +Restored byte-for-byte: src/daemon-spec-validation.ts sha256=3aac707c344f3b83a8cef39d47df4901635b071b5b34cecb0e06340a479eae4c +Restored byte-for-byte: dist/daemon-spec-validation.js sha256=16ff10bf6f1e3d2b3fe89000f85776d7b84d90f5c8aff81fd1ed3b3382d6f764 +mutation-indices-generated-mutated.txt: exit=1 +mutation-indices-authored-mutated.txt: exit=1 +mutation-indices-generated-restored.txt: exit=0 +mutation-indices-authored-restored.txt: exit=0 +Restored byte-for-byte: src/daemon-spec-validation.ts sha256=3aac707c344f3b83a8cef39d47df4901635b071b5b34cecb0e06340a479eae4c +Restored byte-for-byte: dist/daemon-spec-validation.js sha256=16ff10bf6f1e3d2b3fe89000f85776d7b84d90f5c8aff81fd1ed3b3382d6f764 diff --git a/evidence/daemon-check/package-gate.txt b/evidence/daemon-check/package-gate.txt new file mode 100644 index 000000000..d1a0ac78f --- /dev/null +++ b/evidence/daemon-check/package-gate.txt @@ -0,0 +1,28 @@ +$ /usr/local/share/nvm/versions/node/v25.6.0/bin/node scripts/pack-release.mjs surface /tmp/flows-cli-package-gXtQFF/tarballs +PACK_OK @relayflows/surface@2.0.42: package/dist/index.js, package/dist/index.d.ts, package/dist/runtime.js, package/dist/runtime.d.ts +$ /usr/local/share/nvm/versions/node/v25.6.0/bin/node scripts/pack-release.mjs sdk /tmp/flows-cli-package-gXtQFF/tarballs +PACK_OK @relayflows/sdk@2.0.42: package/dist/index.js, package/dist/index.d.ts, package/dist/cli.js +$ /usr/local/share/nvm/versions/node/v25.6.0/bin/node scripts/pack-release.mjs relayflows /tmp/flows-cli-package-gXtQFF/tarballs +PACK_OK relayflows@2.0.42: package/bin/flows.js +$ npm install --ignore-scripts --omit=optional --no-audit --no-fund /tmp/flows-cli-package-gXtQFF/tarballs/relayflows-2.0.42.tgz /tmp/flows-cli-package-gXtQFF/tarballs/relayflows-sdk-2.0.42.tgz /tmp/flows-cli-package-gXtQFF/tarballs/relayflows-surface-2.0.42.tgz + +added 150 packages in 10s +$ /tmp/flows-cli-package-gXtQFF/local/node_modules/.bin/flows check examples/dependency-upgrade-bot.flow.ts +REQUIRES claude (agent "upgrader") +CHECK PASSED examples/dependency-upgrade-bot.flow.ts [local only: authored body, so daemon acceptance is unproven] +$ /usr/local/share/nvm/versions/node/v25.6.0/bin/node /tmp/flows-cli-package-gXtQFF/local/node_modules/relayflows/bin/flows.js check examples/dependency-upgrade-bot.flow.ts +REQUIRES claude (agent "upgrader") +CHECK PASSED examples/dependency-upgrade-bot.flow.ts [local only: authored body, so daemon acceptance is unproven] +$ npm install -g --prefix /tmp/flows-cli-package-gXtQFF/prefix --ignore-scripts --omit=optional --no-audit --no-fund /tmp/flows-cli-package-gXtQFF/tarballs/relayflows-2.0.42.tgz /tmp/flows-cli-package-gXtQFF/tarballs/relayflows-sdk-2.0.42.tgz /tmp/flows-cli-package-gXtQFF/tarballs/relayflows-surface-2.0.42.tgz + +added 294 packages in 8s +$ npm install --ignore-scripts --omit=optional --no-audit --no-fund /tmp/flows-cli-package-gXtQFF/tarballs/relayflows-surface-2.0.42.tgz + +added 140 packages in 2s +$ /tmp/flows-cli-package-gXtQFF/prefix/bin/flows check examples/dependency-upgrade-bot.flow.ts +REQUIRES claude (agent "upgrader") +CHECK PASSED examples/dependency-upgrade-bot.flow.ts [local only: authored body, so daemon acceptance is unproven] +$ /usr/local/share/nvm/versions/node/v25.6.0/bin/node /tmp/flows-cli-package-gXtQFF/prefix/lib/node_modules/relayflows/bin/flows.js check examples/dependency-upgrade-bot.flow.ts +REQUIRES claude (agent "upgrader") +CHECK PASSED examples/dependency-upgrade-bot.flow.ts [local only: authored body, so daemon acceptance is unproven] +CLI_PACKAGE_OK: local and global installs diff --git a/evidence/daemon-check/plugin-extension-retry.txt b/evidence/daemon-check/plugin-extension-retry.txt new file mode 100644 index 000000000..d15fce1df --- /dev/null +++ b/evidence/daemon-check/plugin-extension-retry.txt @@ -0,0 +1,10 @@ + + RUN v2.1.9 /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk + + ✓ tests/plugin-extension.test.ts (94 tests) 428ms + + Test Files 1 passed (1) + Tests 94 passed (94) + Start at 14:37:26 + Duration 2.84s (transform 1.47s, setup 26ms, collect 2.21s, tests 428ms, environment 0ms, prepare 44ms) + diff --git a/evidence/daemon-check/rust-targeted-final.txt b/evidence/daemon-check/rust-targeted-final.txt new file mode 100644 index 000000000..15614560c --- /dev/null +++ b/evidence/daemon-check/rust-targeted-final.txt @@ -0,0 +1,11 @@ + Compiling relayflowd v0.1.0 (/home/daytona/.relayflow-v2-supervisor/durable/repository/kernel/relayflowd) + Finished `test` profile [unoptimized] target(s) in 0.19s + Running tests/validate_spec.rs (/tmp/relayflows-daemon-check-target/debug/deps/validate_spec-2e0369aa01bb2e19) + +running 3 tests +test refuses_unknown_field_with_kernel_location ... ok +test accepts_without_creating_data_directory_or_journal ... ok +test refuses_semantic_errors_and_malformed_json ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s + diff --git a/evidence/daemon-check/rust-targeted-retry.txt b/evidence/daemon-check/rust-targeted-retry.txt new file mode 100644 index 000000000..d1b8ef29a --- /dev/null +++ b/evidence/daemon-check/rust-targeted-retry.txt @@ -0,0 +1,154 @@ + Compiling proc-macro2 v1.0.107 + Compiling quote v1.0.47 + Compiling unicode-ident v1.0.24 + Compiling libc v0.2.189 + Compiling cfg-if v1.0.4 + Compiling stable_deref_trait v1.2.1 + Compiling version_check v0.9.5 + Compiling autocfg v1.5.1 + Compiling serde_core v1.0.229 + Compiling num-traits v0.2.19 + Compiling getrandom v0.3.4 + Compiling zerocopy v0.8.56 + Compiling syn v3.0.4 + Compiling syn v2.0.119 + Compiling serde v1.0.229 + Compiling smallvec v1.15.2 + Compiling synstructure v0.13.2 + Compiling litemap v0.8.3 + Compiling zerofrom-derive v0.1.7 + Compiling yoke-derive v0.8.2 + Compiling zerofrom v0.1.8 + Compiling memchr v2.8.3 + Compiling yoke v0.8.3 + Compiling writeable v0.6.4 + Compiling num-integer v0.1.47 + Compiling generic-array v0.14.7 + Compiling icu_normalizer_data v2.3.0 + Compiling utf8_iter v1.0.4 + Compiling icu_properties_data v2.3.0 + Compiling parking_lot_core v0.9.12 + Compiling zmij v1.0.23 + Compiling zerovec-derive v0.11.6 + Compiling displaydoc v0.2.7 + Compiling serde_derive v1.0.229 + Compiling typenum v1.20.1 + Compiling zerotrie v0.2.5 + Compiling ref-cast v1.0.27 + Compiling zerovec v0.11.8 + Compiling ref-cast-impl v1.0.27 + Compiling num-bigint v0.4.8 + Compiling tinystr v0.8.4 + Compiling potential_utf v0.1.6 + Compiling icu_locale_core v2.3.0 + Compiling icu_collections v2.3.0 + Compiling aho-corasick v1.1.5 + Compiling icu_provider v2.3.1 + Compiling ahash v0.8.12 + Compiling regex-syntax v0.8.11 + Compiling once_cell v1.21.4 + Compiling shlex v2.0.1 + Compiling serde_json v1.0.151 + Compiling find-msvc-tools v0.1.11 + Compiling scopeguard v1.2.0 + Compiling lock_api v0.4.14 + Compiling cc v1.4.4 + Compiling icu_normalizer v2.3.0 + Compiling icu_properties v2.3.0 + Compiling num-rational v0.4.2 + Compiling regex-automata v0.4.18 + Compiling ppv-lite86 v0.2.21 + Compiling num-iter v0.1.46 + Compiling rand_core v0.9.5 + Compiling num-complex v0.4.6 + Compiling itoa v1.0.18 + Compiling pkg-config v0.3.34 + Compiling borrow-or-share v0.2.4 + Compiling bit-vec v0.8.0 + Compiling vcpkg v0.2.15 + Compiling bit-set v0.8.0 + Compiling fluent-uri v0.3.2 + Compiling num v0.4.3 + Compiling libsqlite3-sys v0.35.0 + Compiling rand_chacha v0.9.0 + Compiling idna_adapter v1.2.2 + Compiling parking_lot v0.12.5 + Compiling block-buffer v0.10.4 + Compiling crypto-common v0.1.7 + Compiling percent-encoding v2.3.2 + Compiling uuid v1.26.0 + Compiling utf8parse v0.2.2 + Compiling lazy_static v1.5.0 + Compiling foldhash v0.1.5 + Compiling thiserror v2.0.20 + Compiling bitflags v2.13.1 + Compiling vsimd v0.8.0 + Compiling outref v0.5.2 + Compiling hashbrown v0.15.5 + Compiling fraction v0.15.4 + Compiling uuid-simd v0.8.0 + Compiling anstyle-parse v1.0.0 + Compiling referencing v0.33.0 + Compiling fancy-regex v0.16.2 + Compiling regex v1.13.1 + Compiling digest v0.10.7 + Compiling rand v0.9.5 + Compiling idna v1.1.0 + Compiling email_address v0.2.9 + Compiling thiserror-impl v2.0.20 + Compiling cpufeatures v0.2.17 + Compiling is_terminal_polyfill v1.70.2 + Compiling anstyle-query v1.1.5 + Compiling num-cmp v0.1.0 + Compiling bytecount v0.6.9 + Compiling base64 v0.22.1 + Compiling colorchoice v1.0.5 + Compiling anstyle v1.0.14 + Compiling jsonschema v0.33.0 + Compiling anstream v1.0.0 + Compiling sha2 v0.10.9 + Compiling ulid v1.2.1 + Compiling hashlink v0.10.0 + Compiling ryu-js v1.0.3 + Compiling fallible-streaming-iterator v0.1.9 + Compiling fallible-iterator v0.3.0 + Compiling strsim v0.11.1 + Compiling anyhow v1.0.104 + Compiling clap_lex v1.1.0 + Compiling heck v0.5.0 + Compiling clap_builder v4.6.6 + Compiling clap_derive v4.6.4 + Compiling relayflowd-core v0.1.0 (/home/daytona/.relayflow-v2-supervisor/durable/repository/kernel/relayflowd-core) + Compiling rustix v1.1.4 + Compiling getrandom v0.4.3 + Compiling clap v4.6.6 + Compiling wait-timeout v0.2.1 + Compiling linux-raw-sys v0.12.1 + Compiling fastrand v2.5.0 + Compiling tempfile v3.27.0 + Compiling rusqlite v0.37.0 + Compiling relayflowd-journal v0.1.0 (/home/daytona/.relayflow-v2-supervisor/durable/repository/kernel/relayflowd-journal) + Compiling relayflowd v0.1.0 (/home/daytona/.relayflow-v2-supervisor/durable/repository/kernel/relayflowd) + Finished `test` profile [unoptimized + debuginfo] target(s) in 18.85s + Running tests/validate_spec.rs (/tmp/relayflows-daemon-check-target/debug/deps/validate_spec-4ce5e52f28299220) + +running 3 tests +test refuses_unknown_field_with_kernel_location ... ok +test refuses_semantic_errors_and_malformed_json ... FAILED +test accepts_without_creating_data_directory_or_journal ... ok + +failures: + +---- refuses_semantic_errors_and_malformed_json stdout ---- + +thread 'refuses_semantic_errors_and_malformed_json' (23293) panicked at relayflowd/tests/validate_spec.rs:51:5: +assertion failed: verdict["error"]["message"].as_str().unwrap().contains("cwd") +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + + +failures: + refuses_semantic_errors_and_malformed_json + +test result: FAILED. 2 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s + +error: test failed, to rerun pass `-p relayflowd --test validate_spec` diff --git a/evidence/daemon-check/rust-targeted.txt b/evidence/daemon-check/rust-targeted.txt new file mode 100644 index 000000000..2a284cb84 --- /dev/null +++ b/evidence/daemon-check/rust-targeted.txt @@ -0,0 +1,2 @@ +error: rustup could not choose a version of cargo to run, because one wasn't specified explicitly, and no default is configured. +help: run 'rustup default stable' to download the latest stable release of Rust and set it as your default toolchain. diff --git a/evidence/daemon-check/rust-workspace-retry.txt b/evidence/daemon-check/rust-workspace-retry.txt new file mode 100644 index 000000000..e6f2633d1 --- /dev/null +++ b/evidence/daemon-check/rust-workspace-retry.txt @@ -0,0 +1,673 @@ + Compiling proc-macro2 v1.0.107 + Compiling unicode-ident v1.0.24 + Compiling quote v1.0.47 + Compiling libc v0.2.189 + Compiling syn v3.0.4 + Compiling syn v2.0.119 + Compiling synstructure v0.13.2 + Compiling zerofrom-derive v0.1.7 + Compiling yoke-derive v0.8.2 + Compiling zerofrom v0.1.8 + Compiling cfg-if v1.0.4 + Compiling stable_deref_trait v1.2.1 + Compiling version_check v0.9.5 + Compiling zerovec-derive v0.11.6 + Compiling yoke v0.8.3 + Compiling autocfg v1.5.1 + Compiling num-traits v0.2.19 + Compiling displaydoc v0.2.7 + Compiling zerovec v0.11.8 + Compiling serde_core v1.0.229 + Compiling getrandom v0.3.4 + Compiling zerocopy v0.8.56 + Compiling serde v1.0.229 + Compiling smallvec v1.15.2 + Compiling tinystr v0.8.4 + Compiling serde_derive v1.0.229 + Compiling litemap v0.8.3 + Compiling memchr v2.8.3 + Compiling writeable v0.6.4 + Compiling icu_locale_core v2.3.0 + Compiling num-integer v0.1.47 + Compiling zerotrie v0.2.5 + Compiling potential_utf v0.1.6 + Compiling generic-array v0.14.7 + Compiling icu_normalizer_data v2.3.0 + Compiling utf8_iter v1.0.4 + Compiling icu_properties_data v2.3.0 + Compiling icu_collections v2.3.0 + Compiling icu_provider v2.3.1 + Compiling parking_lot_core v0.9.12 + Compiling zmij v1.0.23 + Compiling typenum v1.20.1 + Compiling ref-cast v1.0.27 + Compiling num-bigint v0.4.8 + Compiling aho-corasick v1.1.5 + Compiling ahash v0.8.12 + Compiling ref-cast-impl v1.0.27 + Compiling regex-syntax v0.8.11 + Compiling once_cell v1.21.4 + Compiling serde_json v1.0.151 + Compiling scopeguard v1.2.0 + Compiling lock_api v0.4.14 + Compiling num-rational v0.4.2 + Compiling icu_properties v2.3.0 + Compiling regex-automata v0.4.18 + Compiling icu_normalizer v2.3.0 + Compiling num-iter v0.1.46 + Compiling ppv-lite86 v0.2.21 + Compiling rand_core v0.9.5 + Compiling num-complex v0.4.6 + Compiling itoa v1.0.18 + Compiling borrow-or-share v0.2.4 + Compiling bit-vec v0.8.0 + Compiling bit-set v0.8.0 + Compiling fluent-uri v0.3.2 + Compiling num v0.4.3 + Compiling rand_chacha v0.9.0 + Compiling parking_lot v0.12.5 + Compiling idna_adapter v1.2.2 + Compiling block-buffer v0.10.4 + Compiling crypto-common v0.1.7 + Compiling outref v0.5.2 + Compiling percent-encoding v2.3.2 + Compiling uuid v1.26.0 + Compiling lazy_static v1.5.0 + Compiling thiserror v2.0.20 + Compiling vsimd v0.8.0 + Compiling fraction v0.15.4 + Compiling uuid-simd v0.8.0 + Compiling referencing v0.33.0 + Compiling digest v0.10.7 + Compiling idna v1.1.0 + Compiling rand v0.9.5 + Compiling fancy-regex v0.16.2 + Compiling regex v1.13.1 + Compiling email_address v0.2.9 + Compiling thiserror-impl v2.0.20 + Compiling cpufeatures v0.2.17 + Compiling num-cmp v0.1.0 + Compiling bytecount v0.6.9 + Compiling base64 v0.22.1 + Compiling jsonschema v0.33.0 + Compiling sha2 v0.10.9 + Compiling ulid v1.2.1 + Compiling ryu-js v1.0.3 + Compiling shlex v2.0.1 + Compiling find-msvc-tools v0.1.11 + Compiling cc v1.4.4 + Compiling pkg-config v0.3.34 + Compiling vcpkg v0.2.15 + Compiling relayflowd-core v0.1.0 (/home/daytona/.relayflow-v2-supervisor/durable/repository/kernel/relayflowd-core) + Compiling libsqlite3-sys v0.35.0 + Compiling foldhash v0.1.5 + Compiling bitflags v2.13.1 + Compiling hashbrown v0.15.5 + Compiling hashlink v0.10.0 + Compiling fallible-streaming-iterator v0.1.9 + Compiling fallible-iterator v0.3.0 + Compiling utf8parse v0.2.2 + Compiling anstyle-parse v1.0.0 + Compiling anstyle v1.0.14 + Compiling colorchoice v1.0.5 + Compiling getrandom v0.4.3 + Compiling anstyle-query v1.1.5 + Compiling rustix v1.1.4 + Compiling is_terminal_polyfill v1.70.2 + Compiling anstream v1.0.0 + Compiling strsim v0.11.1 + Compiling clap_lex v1.1.0 + Compiling heck v0.5.0 + Compiling anyhow v1.0.104 + Compiling linux-raw-sys v0.12.1 + Compiling clap_derive v4.6.4 + Compiling rusqlite v0.37.0 + Compiling relayflowd-journal v0.1.0 (/home/daytona/.relayflow-v2-supervisor/durable/repository/kernel/relayflowd-journal) + Compiling clap_builder v4.6.6 + Compiling fastrand v2.5.0 + Compiling tempfile v3.27.0 + Compiling wait-timeout v0.2.1 + Compiling clap v4.6.6 + Compiling relayflowd v0.1.0 (/home/daytona/.relayflow-v2-supervisor/durable/repository/kernel/relayflowd) + Finished `test` profile [unoptimized] target(s) in 39.67s + Running unittests src/lib.rs (/tmp/relayflows-daemon-check-target/debug/deps/relayflowd-13dfe97f990f058a) + +running 64 tests +test engine::remote::worker_failure_detail_tests::a_non_string_output_is_rendered_rather_than_dropped ... ok +test engine::remote::worker_failure_detail_tests::a_null_or_blank_output_yields_no_detail ... ok +test engine::remote::worker_failure_detail_tests::a_string_output_is_carried_verbatim_and_trimmed ... ok +test engine::remote::worker_failure_detail_tests::an_output_at_the_boundary_is_not_truncated ... ok +test engine::boot_identity_tests::every_engine_in_this_process_shares_one_boot_id ... ok +test engine::remote::worker_failure_detail_tests::render_is_bounded_before_allocation_for_hostile_string ... ok +test engine::remote::worker_failure_detail_tests::truncation_does_not_split_a_multi_byte_char ... ok +test engine::remote::worker_failure_detail_tests::render_is_bounded_before_allocation_for_hostile_json ... ok +test engine::wake::claim_guard_tests::a_disarmed_guard_leaves_the_claim_alone ... ok +test engine::wake::claim_guard_tests::a_guard_only_releases_its_own_run ... ok +test engine::wake::claim_guard_tests::a_panic_between_claim_and_register_still_releases ... ok +test exec_det::tests::captures_deterministic_output ... ok +test exec_det::tests::failed_command_evidence_survives_completion ... ok +test engine::wake::claim_guard_tests::an_armed_guard_releases_the_claim_when_dropped ... ok +test engine::boot_identity_tests::prior_boot_registered_undriven_admission_is_recovered_by_start_retry ... ok +test exec_det::tests::timeout_has_an_explicit_completion_reason ... ok +test server::channels::tests::unknown_verb_never_falls_through_to_receive ... ok +test server::client::tests::resume_waits_while_the_heartbeat_renewed_lease_is_live ... ok +test server::liveness::tests::sweep_id_buckets_by_the_interval ... ok +test exec_det::tests::lease_override_bounds_execution_and_preserves_command_timeout ... ok +test server::liveness::tests::sweep_pass_healthy_subscription_is_a_noop ... ok +test server::liveness::tests::sweep_pass_latches_after_journaling_and_next_bucket_is_empty ... ok +test server::tests::a_lone_surrogate_in_a_request_is_refused_with_no_request_id ... ok +test engine::boot_identity_tests::failure_after_workspace_binding_releases_admission_without_exposing_effects ... ok +test server::tests::a_failed_disconnect_journal_append_is_retained_and_retried_not_dropped ... ok +test server::tests::agent::contract::a_transcript_digest_at_its_budget_rides_trajectory_tail_verbatim ... ok +test server::tests::agent::contract::an_agent_worker_attaching_without_pins_is_refused_at_attach ... ok +test server::tests::agent::contract::a_replacement_worker_that_never_reported_the_pinned_surface_is_not_dispatched_to ... ok +test server::tests::agent::contract::agent_without_a_compatible_worker_parks_without_starting ... ok +test server::tests::agent::contract::an_agent_worker_missing_a_declared_surface_parks_the_run_instead_of_erroring ... ok +test server::tests::agent::contract::an_oversized_trajectory_tail_is_refused_at_step_complete ... ok +test server::tests::agent::contract::an_llm_completion_claiming_an_effect_fails_closed_with_the_reason_journaled ... ok +test server::tests::agent::eligibility::required_streams_must_be_held_before_worker_registration ... ok +test server::tests::agent::eligibility::required_streams_keep_ordinary_steps_off_conversation_workers ... ok +test server::tests::agent::contract::human_intervention_is_durable_and_resume_requires_explicit_override ... ok +test server::tests::agent::pins::reset_worker_reporting_a_revision_other_than_its_pin_fails_closed_as_worker_error ... ok +test server::tests::agent::pins::consecutive_agent_steps_on_different_surfaces_each_start_from_their_own_pins ... ok +test server::tests::deterministic_marker_carrying_json_survives_reopen_and_refuses_changed_detail ... ok +test server::tests::dispatch_carries_the_lease_as_a_duration ... ok +test server::tests::heartbeat_cannot_revive_an_expired_assignment ... ok +test server::tests::heartbeat_carries_the_remaining_lease_as_a_duration ... ok +test server::tests::hello_enforces_protocol_version ... ok +test server::tests::run_resume_adopts_a_real_journal_whose_registry_row_is_missing ... ok +test server::tests::run_resume_asks_the_registry_instead_of_treating_an_orphan_file_as_a_run ... ok +test server::tests::run_resume_refuses_a_journal_that_never_recorded_its_run ... ok +test server::tests::run_resume_refuses_a_valid_journal_that_belongs_to_another_run ... ok +test server::tests::run_start_admission_key_recovers_the_same_run_and_refuses_spec_drift ... ok +test server::tests::run_start_fails_closed_on_an_unknown_verification_key ... ok +test server::tests::run_start_refuses_invalid_admission_keys ... ok +test server::tests::run_start_watch_replays_an_existing_admission ... ok +test exec_det::tests::timeout_kills_the_whole_process_group ... ok +test server::tests::run_start_watch_rolls_back_after_journal_creation_failure ... ok +test server::tests::run_start_watch_streams_while_the_step_is_still_blocked ... ok +test server::tests::run_start_without_watch_pushes_no_entries ... ok +test server::tests::step_wait_parks_the_attempt_and_a_human_answer_redispatches_it ... ok +test server::tests::stopped_heartbeats_past_the_deadline_journal_lease_expired_and_release_the_step ... ok +test server::tests::subscription_router::targeted_router_verbs_validate_receipts_and_preserve_wire_metadata ... ok +test socket_path::tests::deep_data_dir_produces_short_socket_path ... ok +test socket_path::tests::different_data_dirs_yield_different_sockets ... ok +test socket_path::tests::relative_and_absolute_data_dirs_agree ... ok +test socket_path::tests::same_data_dir_yields_same_socket ... ok +test server::tests::agent::pins::a_replacement_worker_at_a_different_revision_is_not_dispatched_the_stale_pins ... ok +test server::tests::an_entry_appended_during_watch_registration_is_delivered_exactly_once ... ok +test server::tests::run_start_with_watch_streams_every_entry_once_before_the_result ... ok + +test result: ok. 64 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.80s + + Running unittests src/main.rs (/tmp/relayflows-daemon-check-target/debug/deps/relayflowd-d2dbf0e59b908ab1) + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Running tests/advisory_deterministic.rs (/tmp/relayflows-daemon-check-target/debug/deps/advisory_deterministic-52e5f5ab30ba37d1) + +running 2 tests +test the_same_flow_without_the_declaration_still_fails_and_never_reaches_the_dependent ... ok +test a_recorded_red_step_completes_and_hands_its_exit_code_to_the_next_step ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.09s + + Running tests/budget_gate.rs (/tmp/relayflows-daemon-check-target/debug/deps/budget_gate-01a7a3cdccc1d405) + +running 13 tests +test carried_metered_dollars_still_stop_the_continuing_run ... ok +test a_malformed_reported_cost_is_refused ... ok +test daily_windows_reset_and_exact_limits_do_not_refuse ... ok +test carried_prior_spend_keeps_unknown_dollar_cost_unmetered ... ok +test metering_flag_is_additive_on_the_wire ... ok +test prior_spend_metering_flag_is_additive_and_fails_closed_for_older_kernels ... ok +test a_no_model_cost_must_be_zero ... ok +test reported_cost_is_journaled_but_never_charged_to_the_budget ... ok +test crossing_completion_is_durable_and_next_step_is_refused ... ok +test unmetered_spend_is_journaled_as_unknown_and_never_crosses_a_dollar_ceiling ... ok +test deterministic_spend_and_wallclock_limit_gate_parallel_batch_starts ... ok +test unmetered_tokens_still_cross_a_token_ceiling ... ok +test unmetered_usage_may_not_claim_priced_dollars ... ok + +test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s + + Running tests/crash_resume.rs (/tmp/relayflows-daemon-check-target/debug/deps/crash_resume-5c6cdbba9e81e4b8) + +running 41 tests +test agent::resume_without_a_worker_parks_immediately_instead_of_timing_out ... ok +test agent::rung_c_sigkill_after_final_effect_replays_results_without_redispatch ... ok +test agent::rung_c_reset_sigkill_mid_edit_restores_pins_dedupes_effect_and_explains_attempts ... ok +test agent::rung_c_crash_between_effect_election_and_the_provider_call_performs_it_exactly_once ... ok +test agent::rung_c_sigkill_between_agent_completion_and_final_effect_memoizes_the_agent ... ok +test channels::channels_reject_foreign_workers_stale_attempts_and_invalid_acknowledgements ... ok +test concurrency::cancel_and_completion_race_has_one_terminal_fact ... ok +test agent::rung_c_sigkill_boundaries_resume_only_unfinished_steps_via_real_cli ... ok +test concurrency::cancel_closes_the_lease_and_rejects_a_late_completion ... ok +test concurrency::concurrent_resumes_lease_exactly_one_attempt ... ok +test concurrency::run_start_dispatches_every_independent_lane_before_any_completion ... ok +test concurrency::live_resume_leaves_an_active_lease_running ... ok +test concurrency::server_restart_recovers_every_parallel_lease_without_duplicate_success ... ok +test llm::completed_llm_output_is_memoized_when_serve_dies_during_the_next_step ... ok +test llm::failing_llm_verification_schedules_a_durable_retry_and_succeeds ... ok +test llm::llm_verification_exhaustion_is_a_declared_failure_kind ... ok +test llm::serve_plumbs_watch_events_and_replayable_stream_verbs ... ok +test llm::sigkill_after_the_final_rung_b_effect_resumes_without_redispatching_llm ... ok +test llm::sigkill_under_serve_mid_llm_releases_the_lease_and_finishes_via_cli_resume ... ok +test llm::worker_killed_while_holding_a_lease_is_explained_and_released_on_cli_resume ... ok +test channels::channels_sigkill_resume_redelivers_unacked_messages_with_exactly_once_effects ... ok +test llm::sigkill_sweep_covers_before_and_between_the_rung_b_steps ... ok +test memory::memory_sigkill_after_injection_replays_pack_and_charges_it_once ... ok +test parallel_lifecycle::overlapping_agent_conflict_survives_server_crash_and_resume ... ok +test parallel_lifecycle::terminal_failure_drains_or_explains_every_live_sibling ... ok +test parallel_lifecycle::overlapping_agent_lanes_serialize_while_disjoint_lanes_merge_in_either_order ... ok +test pin_projection::rejected_completion_cannot_forge_pins_or_trigger_a_blind_retry ... ok +test placement::declared_placement_keeps_one_source_tree_across_resume ... ok +test placement::sigkill_before_first_step_preserves_the_submitted_workspace ... ok +test protocol_admission::every_mutating_run_verb_refuses_terminal_before_changing_state ... ok +test placement::sigkill_mid_step_keeps_the_route_and_source_tree ... ok +test sigkill_after_cancel_request_resumes_to_one_canceled_fact ... ok +test sigkill_mid_step_replaces_and_explains_the_dead_attempt ... ok +test sigkill_under_serve_resumes_the_socket_started_run ... ok +test surface_identity::aliases_are_rejected_and_external_ancestors_serialize_over_real_sockets ... ok +test sigkill_sweep_covers_every_hello_step_boundary ... ok +test worker_capacity::two_workers_receive_a_deterministic_fair_capacity_bounded_batch ... ok +test worker_capacity::default_capacity_one_reopens_only_after_durable_completion_or_crash ... ok +test workspace_identity::workspace_aliases_are_refused_and_canonical_subtrees_serialize_over_real_sockets ... ok +test manual_recovery::manual_recovery_parks_a_worker_reported_loss_across_daemon_restart_until_a_human_answers ... ok +test parallel_lifecycle::renewed_parallel_leases_survive_the_original_grant_and_remain_distinct ... ok + +test result: ok. 41 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 37.28s + + Running tests/daemon_lifecycle.rs (/tmp/relayflows-daemon-check-target/debug/deps/daemon_lifecycle-a08dcc505b577e02) + +running 6 tests +test clean_shutdown_removes_advertisement_and_socket ... ok +test connection_file_is_published_only_after_the_socket_is_live ... ok +test a_second_serve_on_a_served_data_dir_refuses_and_the_first_keeps_serving ... ok +test deep_data_dir_still_binds ... ok +test sigkill_leaves_a_stale_file_with_a_dead_pid ... ok +test a_sigkilled_daemons_successor_starts_cleanly ... ok + +test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s + + Running tests/event_activities.rs (/tmp/relayflows-daemon-check-target/debug/deps/event_activities-624e7d20b49b7b97) + +running 12 tests +test exact_deadline_tie_wins_and_reports_unread_range ... ok +test cancel_closes_an_open_activity_before_the_terminal_run_record ... ok +test accepted_append_is_buffered_deduplicated_and_survives_a_restart_before_next ... ok +test closing_over_a_suspended_pull_reports_closed_live_and_on_replay ... ok +test idle_wait_is_durable_and_fires_without_an_event ... ok +test immediate_event_wakes_have_durable_distinct_wait_boundaries ... ok +test normal_wake_is_not_acknowledged_until_the_following_next ... ok +test overflow_of_a_parked_next_returns_overflow_after_recovery ... ok +test prepared_open_response_replays_the_immutable_binding_snapshot ... ok +test prepared_binding_stays_invisible_across_a_crash_until_activation_then_next_suspends ... ok +test remaining_event_await_acceptance_cases_use_the_real_journal ... ok +test overflow_closes_before_the_1001st_unread_frame_and_recovery_never_reopens_it ... ok + +test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 17.53s + + Running tests/event_activity_corruption.rs (/tmp/relayflows-daemon-check-target/debug/deps/event_activity_corruption-eff5b0a2a8c71875) + +running 3 tests +test a_completed_event_range_cannot_replay_with_missing_frames ... ok +test malformed_stream_frames_fail_replay_and_future_append ... ok +test malformed_deadline_range_is_an_error_while_null_is_an_empty_range ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s + + Running tests/event_activity_parking.rs (/tmp/relayflows-daemon-check-target/debug/deps/event_activity_parking-350e689f8f4944ae) + +running 4 tests +test replay_keeps_each_acknowledged_batch_addressable_by_body_call_ordinal ... ok +test intentional_close_cancels_the_pending_pull_without_claiming_a_timeout ... ok +test activation_and_delivery_racing_the_lease_handoff_are_not_lost ... ok +test parked_attempt_survives_restart_and_only_a_ready_subscription_redispatches_it ... ok + +test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.12s + + Running tests/event_wake.rs (/tmp/relayflows-daemon-check-target/debug/deps/event_wake-04f05876d87f6739) + +running 3 tests +test two_racing_deliveries_of_one_event_produce_exactly_one_run ... ok +test matching_event_wakes_once_with_fresh_context ... ok +test a_resumed_run_dispatches_the_original_wake_context ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s + + Running tests/hn_monitor_integration.rs (/tmp/relayflows-daemon-check-target/debug/deps/hn_monitor_integration-a2744610dee58931) + +running 1 test +test hn_story_event_wakes_monitor_once_with_story_context ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.05s + + Running tests/input_binding.rs (/tmp/relayflows-daemon-check-target/debug/deps/input_binding-f42ebaaabfc91472) + +running 2 tests +test binding_schema_is_additive_and_fails_closed ... ok +test sigkill_before_consumer_resolves_original_journal_output_without_reexecuting_source ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.09s + + Running tests/invalid_schema_preflight.rs (/tmp/relayflows-daemon-check-target/debug/deps/invalid_schema_preflight-b36033d141e968b9) + +running 3 tests +test invalid_json_schema_is_refused_before_journal_or_command ... ok +test unbounded_json_schema_is_refused_before_journal_or_command ... ok +test legitimately_recursive_json_schema_still_starts ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.75s + + Running tests/late_start.rs (/tmp/relayflows-daemon-check-target/debug/deps/late_start-b29c98e96656cb46) + +running 2 tests +test a_dispatch_after_a_slow_deterministic_peer_keeps_its_full_lease ... ok +test a_start_after_a_slow_deterministic_peer_is_stamped_when_journaled ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.42s + + Running tests/manual_recovery.rs (/tmp/relayflows-daemon-check-target/debug/deps/manual_recovery-cd563a126daa15c0) + +running 2 tests +test a_park_torn_between_its_two_appends_is_repaired_on_resume ... ok +test manual_park_of_a_worker_reported_loss_survives_reopen_and_answers_to_a_human ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.10s + + Running tests/memoization.rs (/tmp/relayflows-daemon-check-target/debug/deps/memoization-f0f02cfb410d0ee2) + +running 3 tests +test refuses_missing_wrong_flow_and_unreadable_journal_before_creating_run ... ok +test actual_changed_input_invalidates_consumer_even_with_identical_consumer_spec ... ok +test reused_prefix_survives_restart_without_source_and_never_mutates_prior ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.10s + + Running tests/memory.rs (/tmp/relayflows-daemon-check-target/debug/deps/memory-0b00ea075eed4e8b) + +running 5 tests +test rejected_journal_fact_releases_reservation_and_never_dispatches ... ok +test llm_dispatch_receives_same_pack_after_resume_without_provider ... ok +test replay_and_resume_need_no_provider_and_script_receives_recorded_pack ... ok +test over_budget_and_provider_errors_fail_without_dispatch_or_charge ... ok +test semantic_retry_reuses_memory_without_a_second_charge ... ok + +test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.08s + + Running tests/memory_epoch.rs (/tmp/relayflows-daemon-check-target/debug/deps/memory_epoch-4bcca4b86db44191) + +running 1 test +test epoch_carries_pack_and_exact_charge_and_refuses_duplicate_injection ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s + + Running tests/parallel_driver.rs (/tmp/relayflows-daemon-check-target/debug/deps/parallel_driver-70f9e44889e2d0fb) + +running 4 tests +test stop_after_one_holds_for_an_independent_deterministic_batch ... ok +test pause_before_second_independent_step_holds_the_driver_boundary ... ok +test backpressured_or_mismatched_lane_does_not_drop_a_later_dispatch ... ok +test crash_boundaries_resume_the_real_driver_with_one_effect_per_lane ... ok + +test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.14s + + Running tests/placement_pins.rs (/tmp/relayflows-daemon-check-target/debug/deps/placement_pins-174eccde3f6442f5) + +running 3 tests +test unsupported_local_pty_is_refused_before_an_earlier_step_can_run ... ok +test default_worker_pins_the_declared_worktree_base_commit_and_refuses_missing_source ... ok +test a_resumed_attempt_keeps_the_original_pin_after_the_worktree_head_moves ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s + + Running tests/placement_routing.rs (/tmp/relayflows-daemon-check-target/debug/deps/placement_routing-3ea06dbd1e6c4986) + +running 3 tests +test a_failed_routing_append_never_starts_or_dispatches_work ... ok +test crash_between_routing_and_start_does_not_redecide ... ok +test worker_retry_consumes_the_original_routing_fact ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s + + Running tests/routing_diagnostics.rs (/tmp/relayflows-daemon-check-target/debug/deps/routing_diagnostics-914a18e812529456) + +running 2 tests +test duplicate_routes_have_a_distinct_diagnostic_and_leave_the_original_fact_intact ... ok +test malformed_routes_name_the_same_field_at_append_replay_and_epoch_replay ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s + + Running tests/spec_review_routing.rs (/tmp/relayflows-daemon-check-target/debug/deps/spec_review_routing-65bfdbb3b9e5faa8) + +running 4 tests +test attempt_scoped_route_is_rejected_at_append_and_replay ... ok +test malformed_epoch_routes_are_rejected_before_commit ... ok +test epoch_cannot_drop_or_replace_a_durable_route ... ok +test workspace_pin_peels_tags_and_refuses_non_commit_objects ... ok + +test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s + + Running tests/subscription_liveness.rs (/tmp/relayflows-daemon-check-target/debug/deps/subscription_liveness-a97a9e8927d09ddc) + +running 3 tests +test submit_event_upserts_subscription_row_and_sweep_flags_it_stale_after_budget ... ok +test stale_transition_is_journaled_as_subscription_stale_entry_in_the_last_known_run ... ok +test a_fresh_arrival_re_arms_the_latch_and_the_next_silence_can_stale_again ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s + + Running tests/subscription_router_delivery.rs (/tmp/relayflows-daemon-check-target/debug/deps/subscription_router_delivery-be595dc13bf9f1ba) + +running 5 tests +test canceling_a_run_closes_a_prepared_only_subscription_once_and_reports_it_closed ... ok +test inspect_reports_exactly_one_snapshot_per_subscription_across_its_lifecycle ... ok +test router_snapshots_keep_absolute_timers_and_overflow_fence_across_restart ... ok +test targeted_ingress_is_fenced_deduplicated_and_isolated_after_restart ... ok +test a_fence_torn_from_its_close_refuses_ingress_and_is_completed_once_on_retry_or_resume ... ok + +test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.13s + + Running tests/trigger_watcher.rs (/tmp/relayflows-daemon-check-target/debug/deps/trigger_watcher-961678fadacec577) + +running 3 tests +test retains_bad_and_unregistered_events_while_consuming_filter_nonmatches ... ok +test failed_archive_retries_the_same_durable_run ... ok +test journals_payload_and_filename_key_then_archives_and_dedupes_replay ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s + + Running tests/validate_spec.rs (/tmp/relayflows-daemon-check-target/debug/deps/validate_spec-2e0369aa01bb2e19) + +running 3 tests +test refuses_unknown_field_with_kernel_location ... ok +test refuses_semantic_errors_and_malformed_json ... ok +test accepts_without_creating_data_directory_or_journal ... ok + +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s + + Running unittests src/lib.rs (/tmp/relayflows-daemon-check-target/debug/deps/relayflowd_core-d431620b728b692b) + +running 86 tests +test channel::tests::delivery_replay_and_independent_acknowledged_offsets ... ok +test channel::tests::malformed_payloads_and_invalid_new_channel_appends_leave_state_unchanged ... ok +test channel::tests::forged_deliveries_and_acknowledgements_fail_closed ... ok +test channel::tests::send_retry_is_stable_and_conflicting_content_is_rejected ... ok +test clock::tests::simulated_clock_is_explicitly_advanced ... ok +test entry::attempt_started_tests::a_pre_field_attempt_started_still_reads ... ok +test entry::attempt_started_tests::max_transport_retries_is_always_journaled ... ok +test entry::completion_reason_tests::all_covers_every_serialized_label ... ok +test entry::completion_reason_tests::every_journal_label_matches_serialized ... ok +test entry::reported_cost_tests::step_completed_without_reported_cost_round_trips_unchanged ... ok +test journal::tests::memory_journal_assigns_sequences_and_rolls_epochs ... ok +test machine::parallel_tests::every_declared_mutable_surface_participates_in_conflict_selection ... ok +test machine::parallel_tests::external_ancestor_and_descendant_paths_conflict_but_siblings_do_not ... ok +test machine::parallel_tests::crash_resume_preserves_each_parallel_lease_exactly_once ... ok +test machine::parallel_tests::disjoint_agent_lanes_merge_pins_in_either_completion_order ... ok +test machine::parallel_tests::machine_starts_every_runnable_step_in_authored_order ... ok +test machine::parallel_tests::failed_run_drains_open_siblings_before_terminal_entry ... ok +test machine::parallel_tests::overlapping_agent_surfaces_are_serialized_in_authored_order ... ok +test machine::parallel_tests::parallel_lanes_do_not_cross_the_dependency_barrier_early ... ok +test machine::parallel_tests::workspace_ancestor_and_descendant_paths_conflict_but_siblings_do_not ... ok +test machine::tests::a_retryable_worker_reported_reason_is_not_rewritten_by_the_retry_branch ... ok +test machine::recovery_tests::manual_recovery_parks_a_worker_reported_transport_loss_instead_of_redispatching ... ok +test machine::recovery_tests::recovery_journals_the_wait_human_a_torn_manual_park_never_wrote ... ok +test machine::recovery_tests::reset_recovery_still_retries_a_worker_reported_transport_loss ... ok +test machine::tests::all_backing_off_steps_return_timers ... ok +test machine::tests::cancel_request_closes_the_active_lease_before_the_terminal_fact ... ok +test machine::tests::deterministic_lease_rejects_invalid_and_foreign_fields ... ok +test machine::tests::deterministic_lease_override_and_default_are_journaled ... ok +test machine::tests::crashed_attempt_does_not_consume_an_iteration ... ok +test machine::tests::classified_transport_retry_is_bounded_and_preserves_pin_and_idempotency ... ok +test machine::tests::durable_cancel_request_outranks_crash_recovery ... ok +test machine::tests::each_failed_attempt_journals_its_own_output_beside_an_identical_verdict ... ok +test machine::tests::every_reason_label_matches_its_serialized_form ... ok +test machine::tests::failed_deterministic_completion_preserves_exit_code_and_stderr ... ok +test machine::tests::machine_starts_runnable_step_with_stable_effect_key ... ok +test machine::tests::every_failed_run_terminates_with_declared_completion_reasons ... ok +test machine::tests::ordinary_worker_error_is_not_retried_by_either_budget ... ok +test machine::tests::manual_recovery_parks_needs_human_and_never_redispatches ... ok +test machine::tests::repeated_cancel_request_is_idempotent ... ok +test machine::tests::inspect_recovery_injects_the_dirty_pin_completion_reason_and_tail ... ok +test machine::tests::refused_dispatch_is_re_elected_within_max_iterations_only ... ok +test machine::tests::successful_memo_is_never_scheduled_again ... ok +test machine::tests::the_exhaustion_label_replaces_verification_failed_over_identical_evidence ... ok +test machine::tests::worker_reported_failure_without_detail_still_records_a_verification ... ok +test machine::tests::verification_failure_schedules_a_durable_retry ... ok +test machine::tests::reset_recovery_dispatches_the_original_pinned_revision ... ok +test memory::tests::caps_compare_exact_decimals_and_each_token_dimension ... ok +test retry::tests::jitter_is_repeatable_and_bounded ... ok +test schema::tests::a_property_named_ref_is_not_a_reference ... ok +test schema::tests::in_document_uri_references_resolve_to_the_node_they_name ... ok +test schema::tests::references_the_bound_leaves_opaque_are_refused_by_the_engine ... ok +test schema::tests::refusal_names_the_cycle_it_found ... ok +test schema::tests::shared_declarations_and_boolean_schemas_are_validated ... ok +test spec::tests::a_misspelled_step_level_key_is_a_parse_error ... ok +test spec::tests::a_misspelled_verification_gate_key_is_a_parse_error_not_a_dropped_gate ... ok +test spec::tests::agent_cwd_is_carried_and_must_be_run_root_relative ... ok +test spec::tests::agent_without_cwd_hashes_as_before ... ok +test spec::tests::cli_identity_is_internal_to_provider_steps ... ok +test spec::tests::cycles_are_rejected ... ok +test spec::tests::external_surface_paths_must_have_one_canonical_spelling ... ok +test spec::tests::on_non_zero_parses_the_two_declared_policies_and_refuses_anything_else ... ok +test spec::tests::preflight_data_is_fail_closed ... ok +test schema::tests::every_accepted_corpus_schema_is_accepted ... ok +test schema::tests::every_refused_corpus_schema_compiles_but_is_refused_by_the_bound ... ok +test spec::tests::spec_version_is_semver_and_gated ... ok +test spec::tests::the_default_policy_is_absent_from_the_serialized_boundary_spec ... ok +test spec::tests::the_full_ladder_parses_in_the_one_dialect ... ok +test spec::tests::unknown_root_and_nested_fields_are_rejected ... ok +test spec::tests::workspace_mounts_and_worktrees_must_have_one_canonical_spelling ... ok +test spec::tests::zero_agent_flow_is_valid ... ok +test state::budget::tests::adds_costs_exactly_beyond_machine_decimal_precision ... ok +test state::budget::tests::overflow_and_malformed_cost_leave_total_unchanged ... ok +test state::tests::a_completion_that_omits_a_surface_does_not_drop_it_from_the_pin_chain ... ok +test state::tests::budget_decimal_strings_add_without_floats ... ok +test state::tests::end_pin_chain_is_enforced_and_a_broken_chain_is_a_hard_error ... ok +test state::tests::journal_replays_data_gate_verdict_without_rerunning_completed_code ... ok +test verify::tests::a_recorded_nonzero_exit_passes_its_gate_and_names_the_code ... ok +test verify::tests::an_unbounded_schema_in_a_journal_fails_its_gate_instead_of_aborting ... ok +test verify::tests::deterministic_output_requires_successful_exit_and_content ... ok +test verify::tests::json_schema_is_a_control_gate ... ok +test verify::tests::recording_an_exit_code_does_not_relax_a_declared_content_gate ... ok +test verify::tests::recording_does_not_absorb_a_missing_or_sentinel_exit_status ... ok +test verify::tests::the_default_policy_still_fails_a_nonzero_exit ... ok +test spec::tests::sdk_boundary_accepts_a_valid_10_000_step_reverse_chain ... ok +test spec::tests::sdk_boundary_rejects_a_10_000_step_cycle_with_a_typed_error ... ok +test schema::tests::deeply_nested_schemas_do_not_overflow_the_checker ... ok + +test result: ok. 86 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.89s + + Running tests/memoization.rs (/tmp/relayflows-daemon-check-target/debug/deps/memoization-80e876e725c6a1a4) + +running 4 tests +test distinct_large_kernel_integers_do_not_alias_through_float_rounding ... ok +test match_reuses_output_with_provenance_and_zero_cost_without_dispatch ... ok +test changed_spec_or_input_dispatches_and_legacy_or_failed_records_miss ... ok +test canonical_corpus_agrees_with_typescript_and_key_permutations ... ok + +test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Running tests/spec_parity.rs (/tmp/relayflows-daemon-check-target/debug/deps/spec_parity-0655a83e180272d9) + +running 18 tests +test agent_cwd_is_not_accepted_on_other_verbs_or_under_another_name ... ok +test a_non_string_agent_cwd_fails_closed_rather_than_defaulting ... ok +test agent_timeout_bounds_match_the_kernel_corpus ... ok +test an_undeclared_agent_cwd_is_not_serialized ... ok +test agent_timeout_has_identical_canonical_bytes_and_hash ... ok +test agent_cwd_declaration_acceptance_matches_the_sdk_corpus ... ok +test memory_declaration_acceptance_matches_the_sdk_corpus ... ok +test placement_requirements_have_identical_canonical_bytes_and_hash ... ok +test agent_working_directories_have_identical_canonical_bytes_and_hash ... ok +test the_kernel_parses_the_deterministic_rung_and_stamps_the_same_hash ... ok +test step_memory_has_identical_canonical_bytes_and_hash ... ok +test placement_declaration_acceptance_matches_the_sdk_corpus ... ok +test the_kernel_parses_the_event_triggered_spec_and_stamps_the_same_hash ... ok +test the_kernel_parses_the_rung_c_agent_spec_and_stamps_the_same_hash ... ok +test the_kernel_round_trips_declared_agent_transports_and_rejects_unknown_values ... ok +test the_kernel_parses_the_sdk_compiled_spec_and_stamps_the_same_hash ... ok +test the_kernel_parses_the_rung_b_spec_and_stamps_the_same_hash ... ok +test the_kernel_parses_the_advisory_repair_spec_and_stamps_the_same_hash ... ok + +test result: ok. 18 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s + + Running unittests src/lib.rs (/tmp/relayflows-daemon-check-target/debug/deps/relayflowd_journal-4910689de8b32a5a) + +running 32 tests +test channel::tests::stale_attempts_and_raw_forged_acknowledgements_cannot_change_offsets ... ok +test channel::tests::channels_cross_segment_boundaries_and_terminal_runs_reject_mutations ... ok +test registry::tests::a_pre_migration_registry_gains_boot_id_and_its_claims_are_repairable ... ok +test channel::tests::failed_channel_writes_never_expose_delivery_or_advance_acknowledged_offset ... ok +test registry::tests::a_previous_boots_claim_with_no_run_is_repaired ... ok +test registry::tests::a_registered_run_dedupes_across_boots ... ok +test registry::tests::a_same_boot_claim_with_no_run_yet_is_a_duplicate_not_wreckage ... ok +test registry::tests::concurrent_new_boot_retries_have_one_recovery_owner ... ok +test registry::tests::prior_boot_unregistered_run_admission_is_repaired ... ok +test registry::tests::concurrent_same_boot_run_admissions_have_one_owner ... ok +test registry::tests::registry_is_a_rebuildable_run_locator ... ok +test registry::tests::releasing_a_claim_lets_the_same_boot_retry ... ok +test registry::tests::releasing_is_scoped_to_the_claiming_run ... ok +test registry::tests::run_admission_reuses_registered_run_and_rejects_spec_drift ... ok +test subscriptions::tests::detect_without_latch_stays_available_for_the_next_sweep ... ok +test subscriptions::tests::last_run_for_subscription_returns_none_before_first_arrival ... ok +test subscriptions::tests::last_run_for_subscription_returns_the_lex_greatest_ulid_regardless_of_insertion ... ok +test subscriptions::tests::prune_sweep_claims_deletes_only_rows_older_than_cutoff ... ok +test subscriptions::tests::latch_is_a_no_op_if_a_fresh_event_arrived_between_detect_and_latch ... ok +test subscriptions::tests::sweep_does_not_re_emit_the_same_stale_row_on_a_later_tick ... ok +test subscriptions::tests::sweep_ignores_subscriptions_whose_silence_is_still_within_budget ... ok +test subscriptions::tests::sweep_election_gives_the_first_caller_the_result_and_second_gets_empty ... ok +test subscriptions::tests::sweep_marks_row_stale_when_silence_exceeds_budget ... ok +test subscriptions::tests::upsert_after_stale_re_arms_and_next_silence_can_re_emit ... ok +test subscriptions::tests::upsert_is_idempotent_across_bumps_and_preserves_event_type_updates ... ok +test tests::append_is_durable_and_monotonic_after_reopen ... ok +test tests::an_unconfirmed_election_is_reclaimed_by_the_next_attempt_not_treated_as_done ... ok +test tests::effects_are_deduplicated_at_the_journal_boundary ... ok +test tests::failed_commit_is_returned_not_swallowed ... ok +test tests::rollover_is_atomic_scaffolding_for_epoch_resume ... ok +test tests::terminal_run_refuses_every_later_entry_atomically ... ok +test channel::tests::independent_connections_serialize_send_receive_and_acknowledgement ... ok + +test result: ok. 32 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.28s + + Doc-tests relayflowd + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests relayflowd_core + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests relayflowd_journal + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + diff --git a/evidence/daemon-check/rust-workspace.txt b/evidence/daemon-check/rust-workspace.txt new file mode 100644 index 000000000..7c8d438b3 --- /dev/null +++ b/evidence/daemon-check/rust-workspace.txt @@ -0,0 +1,42 @@ + Compiling relayflowd-core v0.1.0 (/home/daytona/.relayflow-v2-supervisor/durable/repository/kernel/relayflowd-core) + Compiling relayflowd-journal v0.1.0 (/home/daytona/.relayflow-v2-supervisor/durable/repository/kernel/relayflowd-journal) + Compiling relayflowd v0.1.0 (/home/daytona/.relayflow-v2-supervisor/durable/repository/kernel/relayflowd) +error: linking with `cc` failed: exit status: 1 + | + = note: "cc" "-m64" "/tmp/relayflows-daemon-check-target/debug/deps/rustcadSFU0/symbols.o" "<65 object files omitted>" "-Wl,--as-needed" "-Wl,-Bstatic" "/tmp/relayflows-daemon-check-target/debug/deps/{libtempfile-9536fa684b3153e7,libgetrandom-4b55eae43c41b808,libfastrand-c3dd370172903a25,librustix-ac99c31c323027d8,liblinux_raw_sys-6909a3bb65c2afb1}.rlib" "/lib/rustlib/x86_64-unknown-linux-gnu/lib/{libtest-*,libgetopts-*,librustc_std_workspace_std-*}.rlib" "/tmp/relayflows-daemon-check-target/debug/deps/{librelayflowd-982eee23c6fa573f,libwait_timeout-d662a9a2f10eb8f0,librelayflowd_journal-732b611254b6de12,librusqlite-ab0fc325ee05c8a7,libbitflags-9100470b2f2ecebc,libhashlink-c9f6fe959c0b48b6,libhashbrown-1c2bb10fc4ea0603,libfoldhash-2b11b30984b507d4,liblibsqlite3_sys-3752f73801891bb3,libfallible_streaming_iterator-a347b7ef8b80f8d8,libfallible_iterator-27e9842272616c44,libanyhow-95160b83a69d28f1,librelayflowd_core-4dee9ff709ccce6a,libryu_js-379fee581d4e4cf6,libjsonschema-1346d624d341b04b,libbytecount-b25e5171a2633f4e,libregex-410ffc66da49fa09,libidna-e94098e37dc02e73,libidna_adapter-988b373e3ea8033e,libicu_properties-050075a714712b80,libicu_properties_data-dc1ccafdd0d637be,libicu_normalizer-63735c110f076c93,libicu_normalizer_data-33689e7acd25cef2,libicu_collections-d7a59cc03624c389,libutf8_iter-599cfec92559ec54,libpotential_utf-9887f2172f111fd9,libicu_provider-564c1d1fc8d08098,libicu_locale_core-a0a916db6ad389b8,libtinystr-38fd63dfef755d6c,liblitemap-dbf974391a511422,libwriteable-5abcc97df5a65912,libzerovec-900082f3c6916124,libzerotrie-8180931e727bd3ce,libyoke-b47f4278c3d13e58,libstable_deref_trait-bf6ee564ab40ea72,libzerofrom-f9ebfa44f9874bc4,libuuid_simd-30e6fe3a290737a1,libuuid-e8fa9dc5c3716442,liboutref-12add1ee7a41ce58,libvsimd-8588db60a425d5f5,libfancy_regex-f5424de6cb149387,libbit_set-ecbd1f809b659a1a,libbit_vec-2dde4e28e53e1e30,libregex_automata-30b727269a1ac78e,libaho_corasick-84481b7457a84fc2,libemail_address-fa9206456703cee3,libfraction-2366378c72c2df44,liblazy_static-e89dfa456fdef5b8,libnum-2b36d6f665b1efb1,libnum_iter-b45c706e3750f11b,libnum_rational-5a3e5bdb340b978b,libnum_complex-df5794162c1ed6d0,libnum_bigint-b48bf0e502f65e24,libnum_integer-ef150102445ba2a4,libnum_traits-e9cfbafadb0db7e3,libnum_cmp-e1600a8c1cfbeb5c,libregex_syntax-1e55c064ae9af1da,libbase64-6ee8ec72165049e2,libreferencing-a0b1881a1961e55a,libpercent_encoding-0afa79f3260a3d39,libparking_lot-4033cdce020a10a2,libparking_lot_core-c997a008dfcf686a,libsmallvec-a1337c150357a38d,liblock_api-052372b6b50f420c,libscopeguard-dd7e42edb450752a,libfluent_uri-dda813dd8b9adace,libborrow_or_share-a1acdbc5ba4e7ded,libref_cast-d724d1309653b4c7,libahash-1903d715d5764112,libonce_cell-428095ce5d8434a6,libulid-0df584f54db22bac,librand-16e86ffc2370eff6,librand_chacha-f382a29c59c3a926,libppv_lite86-9177260696bccdc2,libzerocopy-8345b37130afddcb,librand_core-9ca7818f42aa0237,libgetrandom-4efc230564bd55de,liblibc-cfc6bd596224c782,libsha2-fb8428613fe2c18d,libcpufeatures-c66ff8dcdd82082d,libcfg_if-3bc98562b73372be,libdigest-0a771f46afd19aae,libblock_buffer-a49e7b74df05ed56,libcrypto_common-1294f8b1d82dd2b1,libgeneric_array-d6b2188deed735b3,libtypenum-9af964a71b8f1c20,libthiserror-8da186ef68ca92fb,libserde_json-76dc87088753b61e,libmemchr-e16633741164b756,libitoa-5b56393bc94cfad5,libzmij-9d3e9e7b02b1a066,libserde-76697550497350f7,libserde_core-b468be6fa8c1f936}.rlib" "/lib/rustlib/x86_64-unknown-linux-gnu/lib/{libstd-*,libpanic_unwind-*,libobject-*,libmemchr-*,libaddr2line-*,libgimli-*,libcfg_if-*,librustc_demangle-*,libstd_detect-*,libhashbrown-*,librustc_std_workspace_alloc-*,libminiz_oxide-*,libadler2-*,libunwind-*,liblibc-*,librustc_std_workspace_core-*,liballoc-*,libcore-*,libcompiler_builtins-*}.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-L" "/tmp/relayflows-daemon-check-target/debug/deps/rustcadSFU0/raw-dylibs" "-B/lib/rustlib/x86_64-unknown-linux-gnu/bin/gcc-ld" "-fuse-ld=lld" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/tmp/relayflows-daemon-check-target/debug/build/libsqlite3-sys-9c48f08c189498ba/out" "-L" "/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/tmp/relayflows-daemon-check-target/debug/deps/budget_gate-7247170737f0493b" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs" + = note: some arguments are omitted. use `--verbose` to show all linker arguments + = note: collect2: fatal error: ld terminated with signal 7 [Bus error], core dumped + compilation terminated. + + +error: linking with `cc` failed: exit status: 1 + | + = note: "cc" "-m64" "/tmp/relayflows-daemon-check-target/debug/deps/rustc97chRG/symbols.o" "<44 object files omitted>" "-Wl,--as-needed" "-Wl,-Bstatic" "/lib/rustlib/x86_64-unknown-linux-gnu/lib/{libtest-*,libgetopts-*,librustc_std_workspace_std-*}.rlib" "/tmp/relayflows-daemon-check-target/debug/deps/{libtempfile-9536fa684b3153e7,libgetrandom-4b55eae43c41b808,libfastrand-c3dd370172903a25,librustix-ac99c31c323027d8,liblinux_raw_sys-6909a3bb65c2afb1,librelayflowd-982eee23c6fa573f,libwait_timeout-d662a9a2f10eb8f0,librelayflowd_journal-732b611254b6de12,librusqlite-ab0fc325ee05c8a7,libbitflags-9100470b2f2ecebc,libhashlink-c9f6fe959c0b48b6,libhashbrown-1c2bb10fc4ea0603,libfoldhash-2b11b30984b507d4,liblibsqlite3_sys-3752f73801891bb3,libfallible_streaming_iterator-a347b7ef8b80f8d8,libfallible_iterator-27e9842272616c44,libanyhow-95160b83a69d28f1,librelayflowd_core-4dee9ff709ccce6a,libryu_js-379fee581d4e4cf6,libjsonschema-1346d624d341b04b,libbytecount-b25e5171a2633f4e,libregex-410ffc66da49fa09,libidna-e94098e37dc02e73,libidna_adapter-988b373e3ea8033e,libicu_properties-050075a714712b80,libicu_properties_data-dc1ccafdd0d637be,libicu_normalizer-63735c110f076c93,libicu_normalizer_data-33689e7acd25cef2,libicu_collections-d7a59cc03624c389,libutf8_iter-599cfec92559ec54,libpotential_utf-9887f2172f111fd9,libicu_provider-564c1d1fc8d08098,libicu_locale_core-a0a916db6ad389b8,libtinystr-38fd63dfef755d6c,liblitemap-dbf974391a511422,libwriteable-5abcc97df5a65912,libzerovec-900082f3c6916124,libzerotrie-8180931e727bd3ce,libyoke-b47f4278c3d13e58,libstable_deref_trait-bf6ee564ab40ea72,libzerofrom-f9ebfa44f9874bc4,libuuid_simd-30e6fe3a290737a1,libuuid-e8fa9dc5c3716442,liboutref-12add1ee7a41ce58,libvsimd-8588db60a425d5f5,libfancy_regex-f5424de6cb149387,libbit_set-ecbd1f809b659a1a,libbit_vec-2dde4e28e53e1e30,libregex_automata-30b727269a1ac78e,libaho_corasick-84481b7457a84fc2,libemail_address-fa9206456703cee3,libfraction-2366378c72c2df44,liblazy_static-e89dfa456fdef5b8,libnum-2b36d6f665b1efb1,libnum_iter-b45c706e3750f11b,libnum_rational-5a3e5bdb340b978b,libnum_complex-df5794162c1ed6d0,libnum_bigint-b48bf0e502f65e24,libnum_integer-ef150102445ba2a4,libnum_traits-e9cfbafadb0db7e3,libnum_cmp-e1600a8c1cfbeb5c,libregex_syntax-1e55c064ae9af1da,libbase64-6ee8ec72165049e2,libreferencing-a0b1881a1961e55a,libpercent_encoding-0afa79f3260a3d39,libparking_lot-4033cdce020a10a2,libparking_lot_core-c997a008dfcf686a,libsmallvec-a1337c150357a38d,liblock_api-052372b6b50f420c,libscopeguard-dd7e42edb450752a,libfluent_uri-dda813dd8b9adace,libborrow_or_share-a1acdbc5ba4e7ded,libref_cast-d724d1309653b4c7,libahash-1903d715d5764112,libonce_cell-428095ce5d8434a6,libulid-0df584f54db22bac,librand-16e86ffc2370eff6,librand_chacha-f382a29c59c3a926,libppv_lite86-9177260696bccdc2,libzerocopy-8345b37130afddcb,librand_core-9ca7818f42aa0237,libgetrandom-4efc230564bd55de,liblibc-cfc6bd596224c782,libsha2-fb8428613fe2c18d,libcpufeatures-c66ff8dcdd82082d,libcfg_if-3bc98562b73372be,libdigest-0a771f46afd19aae,libblock_buffer-a49e7b74df05ed56,libcrypto_common-1294f8b1d82dd2b1,libgeneric_array-d6b2188deed735b3,libtypenum-9af964a71b8f1c20,libthiserror-8da186ef68ca92fb,libserde_json-76dc87088753b61e,libmemchr-e16633741164b756,libitoa-5b56393bc94cfad5,libzmij-9d3e9e7b02b1a066,libserde-76697550497350f7,libserde_core-b468be6fa8c1f936}.rlib" "/lib/rustlib/x86_64-unknown-linux-gnu/lib/{libstd-*,libpanic_unwind-*,libobject-*,libmemchr-*,libaddr2line-*,libgimli-*,libcfg_if-*,librustc_demangle-*,libstd_detect-*,libhashbrown-*,librustc_std_workspace_alloc-*,libminiz_oxide-*,libadler2-*,libunwind-*,liblibc-*,librustc_std_workspace_core-*,liballoc-*,libcore-*,libcompiler_builtins-*}.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-L" "/tmp/relayflows-daemon-check-target/debug/deps/rustc97chRG/raw-dylibs" "-B/lib/rustlib/x86_64-unknown-linux-gnu/bin/gcc-ld" "-fuse-ld=lld" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/tmp/relayflows-daemon-check-target/debug/build/libsqlite3-sys-9c48f08c189498ba/out" "-L" "/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/tmp/relayflows-daemon-check-target/debug/deps/late_start-412141e23ddc6467" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs" + = note: some arguments are omitted. use `--verbose` to show all linker arguments + = note: collect2: fatal error: ld terminated with signal 7 [Bus error], core dumped + compilation terminated. + + +error: could not compile `relayflowd` (test "budget_gate") due to 1 previous error +warning: build failed, waiting for other jobs to finish... +error: could not compile `relayflowd` (test "late_start") due to 1 previous error +error: linking with `cc` failed: exit status: 1 + | + = note: "cc" "-m64" "/tmp/relayflows-daemon-check-target/debug/deps/rustcfHOFrQ/symbols.o" "<95 object files omitted>" "-Wl,--as-needed" "-Wl,-Bstatic" "/tmp/relayflows-daemon-check-target/debug/deps/{libtempfile-9536fa684b3153e7,libgetrandom-4b55eae43c41b808,libfastrand-c3dd370172903a25,librustix-ac99c31c323027d8,liblinux_raw_sys-6909a3bb65c2afb1}.rlib" "/lib/rustlib/x86_64-unknown-linux-gnu/lib/{libtest-*,libgetopts-*,librustc_std_workspace_std-*}.rlib" "/tmp/relayflows-daemon-check-target/debug/deps/{librelayflowd-982eee23c6fa573f,libwait_timeout-d662a9a2f10eb8f0,librelayflowd_journal-732b611254b6de12,librusqlite-ab0fc325ee05c8a7,libbitflags-9100470b2f2ecebc,libhashlink-c9f6fe959c0b48b6,libhashbrown-1c2bb10fc4ea0603,libfoldhash-2b11b30984b507d4,liblibsqlite3_sys-3752f73801891bb3,libfallible_streaming_iterator-a347b7ef8b80f8d8,libfallible_iterator-27e9842272616c44,libanyhow-95160b83a69d28f1,librelayflowd_core-4dee9ff709ccce6a,libryu_js-379fee581d4e4cf6,libjsonschema-1346d624d341b04b,libbytecount-b25e5171a2633f4e,libregex-410ffc66da49fa09,libidna-e94098e37dc02e73,libidna_adapter-988b373e3ea8033e,libicu_properties-050075a714712b80,libicu_properties_data-dc1ccafdd0d637be,libicu_normalizer-63735c110f076c93,libicu_normalizer_data-33689e7acd25cef2,libicu_collections-d7a59cc03624c389,libutf8_iter-599cfec92559ec54,libpotential_utf-9887f2172f111fd9,libicu_provider-564c1d1fc8d08098,libicu_locale_core-a0a916db6ad389b8,libtinystr-38fd63dfef755d6c,liblitemap-dbf974391a511422,libwriteable-5abcc97df5a65912,libzerovec-900082f3c6916124,libzerotrie-8180931e727bd3ce,libyoke-b47f4278c3d13e58,libstable_deref_trait-bf6ee564ab40ea72,libzerofrom-f9ebfa44f9874bc4,libuuid_simd-30e6fe3a290737a1,libuuid-e8fa9dc5c3716442,liboutref-12add1ee7a41ce58,libvsimd-8588db60a425d5f5,libfancy_regex-f5424de6cb149387,libbit_set-ecbd1f809b659a1a,libbit_vec-2dde4e28e53e1e30,libregex_automata-30b727269a1ac78e,libaho_corasick-84481b7457a84fc2,libemail_address-fa9206456703cee3,libfraction-2366378c72c2df44,liblazy_static-e89dfa456fdef5b8,libnum-2b36d6f665b1efb1,libnum_iter-b45c706e3750f11b,libnum_rational-5a3e5bdb340b978b,libnum_complex-df5794162c1ed6d0,libnum_bigint-b48bf0e502f65e24,libnum_integer-ef150102445ba2a4,libnum_traits-e9cfbafadb0db7e3,libnum_cmp-e1600a8c1cfbeb5c,libregex_syntax-1e55c064ae9af1da,libbase64-6ee8ec72165049e2,libreferencing-a0b1881a1961e55a,libpercent_encoding-0afa79f3260a3d39,libparking_lot-4033cdce020a10a2,libparking_lot_core-c997a008dfcf686a,libsmallvec-a1337c150357a38d,liblock_api-052372b6b50f420c,libscopeguard-dd7e42edb450752a,libfluent_uri-dda813dd8b9adace,libborrow_or_share-a1acdbc5ba4e7ded,libref_cast-d724d1309653b4c7,libahash-1903d715d5764112,libonce_cell-428095ce5d8434a6,libulid-0df584f54db22bac,librand-16e86ffc2370eff6,librand_chacha-f382a29c59c3a926,libppv_lite86-9177260696bccdc2,libzerocopy-8345b37130afddcb,librand_core-9ca7818f42aa0237,libgetrandom-4efc230564bd55de,liblibc-cfc6bd596224c782,libsha2-fb8428613fe2c18d,libcpufeatures-c66ff8dcdd82082d,libcfg_if-3bc98562b73372be,libdigest-0a771f46afd19aae,libblock_buffer-a49e7b74df05ed56,libcrypto_common-1294f8b1d82dd2b1,libgeneric_array-d6b2188deed735b3,libtypenum-9af964a71b8f1c20,libthiserror-8da186ef68ca92fb,libserde_json-76dc87088753b61e,libmemchr-e16633741164b756,libitoa-5b56393bc94cfad5,libzmij-9d3e9e7b02b1a066,libserde-76697550497350f7,libserde_core-b468be6fa8c1f936}.rlib" "/lib/rustlib/x86_64-unknown-linux-gnu/lib/{libstd-*,libpanic_unwind-*,libobject-*,libmemchr-*,libaddr2line-*,libgimli-*,libcfg_if-*,librustc_demangle-*,libstd_detect-*,libhashbrown-*,librustc_std_workspace_alloc-*,libminiz_oxide-*,libadler2-*,libunwind-*,liblibc-*,librustc_std_workspace_core-*,liballoc-*,libcore-*,libcompiler_builtins-*}.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-L" "/tmp/relayflows-daemon-check-target/debug/deps/rustcfHOFrQ/raw-dylibs" "-B/lib/rustlib/x86_64-unknown-linux-gnu/bin/gcc-ld" "-fuse-ld=lld" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/tmp/relayflows-daemon-check-target/debug/build/libsqlite3-sys-9c48f08c189498ba/out" "-L" "/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/tmp/relayflows-daemon-check-target/debug/deps/event_activities-756a1a69731ac806" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs" + = note: some arguments are omitted. use `--verbose` to show all linker arguments + = note: rust-lld: error: failed to open /tmp/relayflows-daemon-check-target/debug/deps/event_activities-756a1a69731ac806: No space left on device + collect2: error: ld returned 1 exit status + + +error: could not compile `relayflowd` (test "event_activities") due to 1 previous error +error: linking with `cc` failed: exit status: 1 + | + = note: "cc" "-m64" "/tmp/relayflows-daemon-check-target/debug/deps/rustcj6CPUY/symbols.o" "<60 object files omitted>" "-Wl,--as-needed" "-Wl,-Bstatic" "/tmp/relayflows-daemon-check-target/debug/deps/{libtempfile-9536fa684b3153e7,libgetrandom-4b55eae43c41b808,libfastrand-c3dd370172903a25,librustix-ac99c31c323027d8,liblinux_raw_sys-6909a3bb65c2afb1}.rlib" "/lib/rustlib/x86_64-unknown-linux-gnu/lib/{libtest-*,libgetopts-*,librustc_std_workspace_std-*}.rlib" "/tmp/relayflows-daemon-check-target/debug/deps/{librelayflowd_journal-732b611254b6de12,librusqlite-ab0fc325ee05c8a7,libbitflags-9100470b2f2ecebc,libhashlink-c9f6fe959c0b48b6,libhashbrown-1c2bb10fc4ea0603,libfoldhash-2b11b30984b507d4,liblibsqlite3_sys-3752f73801891bb3,libfallible_streaming_iterator-a347b7ef8b80f8d8,libfallible_iterator-27e9842272616c44,librelayflowd_core-4dee9ff709ccce6a,libryu_js-379fee581d4e4cf6,libjsonschema-1346d624d341b04b,libbytecount-b25e5171a2633f4e,libregex-410ffc66da49fa09,libidna-e94098e37dc02e73,libidna_adapter-988b373e3ea8033e,libicu_properties-050075a714712b80,libicu_properties_data-dc1ccafdd0d637be,libicu_normalizer-63735c110f076c93,libicu_normalizer_data-33689e7acd25cef2,libicu_collections-d7a59cc03624c389,libutf8_iter-599cfec92559ec54,libpotential_utf-9887f2172f111fd9,libicu_provider-564c1d1fc8d08098,libicu_locale_core-a0a916db6ad389b8,libtinystr-38fd63dfef755d6c,liblitemap-dbf974391a511422,libwriteable-5abcc97df5a65912,libzerovec-900082f3c6916124,libzerotrie-8180931e727bd3ce,libyoke-b47f4278c3d13e58,libstable_deref_trait-bf6ee564ab40ea72,libzerofrom-f9ebfa44f9874bc4,libuuid_simd-30e6fe3a290737a1,libuuid-e8fa9dc5c3716442,liboutref-12add1ee7a41ce58,libvsimd-8588db60a425d5f5,libfancy_regex-f5424de6cb149387,libbit_set-ecbd1f809b659a1a,libbit_vec-2dde4e28e53e1e30,libregex_automata-30b727269a1ac78e,libaho_corasick-84481b7457a84fc2,libemail_address-fa9206456703cee3,libfraction-2366378c72c2df44,liblazy_static-e89dfa456fdef5b8,libnum-2b36d6f665b1efb1,libnum_iter-b45c706e3750f11b,libnum_rational-5a3e5bdb340b978b,libnum_complex-df5794162c1ed6d0,libnum_bigint-b48bf0e502f65e24,libnum_integer-ef150102445ba2a4,libnum_traits-e9cfbafadb0db7e3,libnum_cmp-e1600a8c1cfbeb5c,libregex_syntax-1e55c064ae9af1da,libbase64-6ee8ec72165049e2,libreferencing-a0b1881a1961e55a,libpercent_encoding-0afa79f3260a3d39,libparking_lot-4033cdce020a10a2,libparking_lot_core-c997a008dfcf686a,libsmallvec-a1337c150357a38d,liblock_api-052372b6b50f420c,libscopeguard-dd7e42edb450752a,libfluent_uri-dda813dd8b9adace,libborrow_or_share-a1acdbc5ba4e7ded,libref_cast-d724d1309653b4c7,libahash-1903d715d5764112,libonce_cell-428095ce5d8434a6,libulid-0df584f54db22bac,librand-16e86ffc2370eff6,librand_chacha-f382a29c59c3a926,libppv_lite86-9177260696bccdc2,libzerocopy-8345b37130afddcb,librand_core-9ca7818f42aa0237,libgetrandom-4efc230564bd55de,liblibc-cfc6bd596224c782,libsha2-fb8428613fe2c18d,libcpufeatures-c66ff8dcdd82082d,libcfg_if-3bc98562b73372be,libdigest-0a771f46afd19aae,libblock_buffer-a49e7b74df05ed56,libcrypto_common-1294f8b1d82dd2b1,libgeneric_array-d6b2188deed735b3,libtypenum-9af964a71b8f1c20,libthiserror-8da186ef68ca92fb,libserde_json-76dc87088753b61e,libmemchr-e16633741164b756,libitoa-5b56393bc94cfad5,libzmij-9d3e9e7b02b1a066,libserde-76697550497350f7,libserde_core-b468be6fa8c1f936}.rlib" "/lib/rustlib/x86_64-unknown-linux-gnu/lib/{libstd-*,libpanic_unwind-*,libobject-*,libmemchr-*,libaddr2line-*,libgimli-*,libcfg_if-*,librustc_demangle-*,libstd_detect-*,libhashbrown-*,librustc_std_workspace_alloc-*,libminiz_oxide-*,libadler2-*,libunwind-*,liblibc-*,librustc_std_workspace_core-*,liballoc-*,libcore-*,libcompiler_builtins-*}.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-L" "/tmp/relayflows-daemon-check-target/debug/deps/rustcj6CPUY/raw-dylibs" "-B/lib/rustlib/x86_64-unknown-linux-gnu/bin/gcc-ld" "-fuse-ld=lld" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/tmp/relayflows-daemon-check-target/debug/build/libsqlite3-sys-9c48f08c189498ba/out" "-L" "/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/tmp/relayflows-daemon-check-target/debug/deps/advisory_deterministic-35ee7ddbc1eb05b0" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs" + = note: some arguments are omitted. use `--verbose` to show all linker arguments + = note: PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace and instructions to reproduce the bug. + Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it): + collect2: fatal error: ld terminated with signal 7 [Bus error], core dumped + compilation terminated. + + +error: could not compile `relayflowd` (test "advisory_deterministic") due to 1 previous error diff --git a/evidence/daemon-check/rustfmt-setup.txt b/evidence/daemon-check/rustfmt-setup.txt new file mode 100644 index 000000000..d3678c828 --- /dev/null +++ b/evidence/daemon-check/rustfmt-setup.txt @@ -0,0 +1 @@ +info: downloading component rustfmt diff --git a/evidence/daemon-check/sdk-build-final.txt b/evidence/daemon-check/sdk-build-final.txt new file mode 100644 index 000000000..efe21185f --- /dev/null +++ b/evidence/daemon-check/sdk-build-final.txt @@ -0,0 +1,4 @@ + +> @relayflows/sdk@2.0.42 build +> tsc && node scripts/make-cli-executable.mjs + diff --git a/evidence/daemon-check/sdk-build.txt b/evidence/daemon-check/sdk-build.txt new file mode 100644 index 000000000..efe21185f --- /dev/null +++ b/evidence/daemon-check/sdk-build.txt @@ -0,0 +1,4 @@ + +> @relayflows/sdk@2.0.42 build +> tsc && node scripts/make-cli-executable.mjs + diff --git a/evidence/daemon-check/sdk-final.txt b/evidence/daemon-check/sdk-final.txt new file mode 100644 index 000000000..d25cb99c3 --- /dev/null +++ b/evidence/daemon-check/sdk-final.txt @@ -0,0 +1,29 @@ + + RUN v2.1.9 /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk + + ✓ tests/relay-cli-surface.test.ts (89 tests) 45ms + ✓ tests/check-daemon-validation.test.ts (20 tests) 2853ms + ✓ real CLI refuses skew in auto and required modes 982ms + ✓ real CLI labels daemon and offline passes, including JSON 1363ms + ✓ real CLI refuses an invalid RELAYFLOWD_BIN in required mode 418ms + ✓ tests/cli.test.ts (71 tests) 3012ms + ✓ flows check CLI > binds a checked relative wrapper to the flow directory for worker execution 484ms + ✓ flows check CLI > resolves a bare PATH-resolved claude with no declared model, in an isolated PATH 558ms + ✓ flows run/resume CLI over the journal protocol > follows a worker wait past a locally expired lease until the daemon settles it 1040ms + ✓ tests/cli-watch.test.ts (11 tests) 17841ms + ✓ flows check --watch > forwards required daemon validation through watch despite the offline test environment 979ms + ✓ flows check --watch > rechecks syntax errors, clears once, and returns the last refusal on Ctrl-C 1394ms + ✓ flows check --watch > streams JSON lines without ANSI, recovers after atomic saves, and exits zero after repair 1965ms + ✓ flows check --watch > coalesces 20 concurrent saves into at most two rechecks 1894ms + ✓ flows check --watch > watches transitive relative use imports, cycles, and nearest config changes 2492ms + ✓ flows check --watch > refreshes the import graph and notices missing imports being created 2502ms + ✓ flows check --watch > reloads authored TypeScript instead of reusing the first imported definition 1864ms + ✓ flows check --watch > detects a nearer config appearing and falls back after it is deleted 1992ms + ✓ flows check --watch > keeps watching after the target is deleted and recreated 1981ms + ✓ flows check --watch > queues changes during a slow check without overlapping checks 773ms + + Test Files 4 passed (4) + Tests 191 passed (191) + Start at 14:40:27 + Duration 20.31s (transform 1.35s, setup 40ms, collect 5.28s, tests 23.75s, environment 0ms, prepare 184ms) + diff --git a/evidence/daemon-check/sdk-full.txt b/evidence/daemon-check/sdk-full.txt new file mode 100644 index 000000000..e70b7c915 --- /dev/null +++ b/evidence/daemon-check/sdk-full.txt @@ -0,0 +1,1599 @@ + +> @relayflows/sdk@2.0.42 test +> sh scripts/test.sh + + +> @relayflows/sdk@2.0.42 test:prep +> ( cd ../../kernel && sh ../ops/cargo.sh build ) && ( [ ! -d ../../testdata/preflight ] || find ../../testdata/preflight -name '*-cli' -type f -exec chmod +x {} + ) + + Compiling proc-macro2 v1.0.107 + Compiling unicode-ident v1.0.24 + Compiling quote v1.0.47 + Compiling stable_deref_trait v1.2.1 + Compiling libc v0.2.189 + Compiling version_check v0.9.5 + Compiling autocfg v1.5.1 + Compiling cfg-if v1.0.4 + Compiling serde_core v1.0.229 + Compiling num-traits v0.2.19 + Compiling zerocopy v0.8.56 + Compiling getrandom v0.3.4 + Compiling syn v3.0.4 + Compiling syn v2.0.119 + Compiling smallvec v1.15.2 + Compiling serde v1.0.229 + Compiling synstructure v0.13.2 + Compiling memchr v2.8.3 + Compiling zerofrom-derive v0.1.7 + Compiling yoke-derive v0.8.2 + Compiling zerofrom v0.1.8 + Compiling writeable v0.6.4 + Compiling litemap v0.8.3 + Compiling num-integer v0.1.47 + Compiling yoke v0.8.3 + Compiling zerovec-derive v0.11.6 + Compiling displaydoc v0.2.7 + Compiling serde_derive v1.0.229 + Compiling zerotrie v0.2.5 + Compiling generic-array v0.14.7 + Compiling icu_normalizer_data v2.3.0 + Compiling zerovec v0.11.8 + Compiling icu_properties_data v2.3.0 + Compiling utf8_iter v1.0.4 + Compiling typenum v1.20.1 + Compiling parking_lot_core v0.9.12 + Compiling zmij v1.0.23 + Compiling ref-cast v1.0.27 + Compiling tinystr v0.8.4 + Compiling icu_locale_core v2.3.0 + Compiling potential_utf v0.1.6 + Compiling icu_collections v2.3.0 + Compiling num-bigint v0.4.8 + Compiling ref-cast-impl v1.0.27 + Compiling icu_provider v2.3.1 + Compiling aho-corasick v1.1.5 + Compiling ahash v0.8.12 + Compiling serde_json v1.0.151 + Compiling scopeguard v1.2.0 + Compiling shlex v2.0.1 + Compiling regex-syntax v0.8.11 + Compiling find-msvc-tools v0.1.11 + Compiling cc v1.4.4 + Compiling num-rational v0.4.2 + Compiling lock_api v0.4.14 + Compiling icu_normalizer v2.3.0 + Compiling regex-automata v0.4.18 + Compiling icu_properties v2.3.0 + Compiling ppv-lite86 v0.2.21 + Compiling num-iter v0.1.46 + Compiling rand_core v0.9.5 + Compiling num-complex v0.4.6 + Compiling borrow-or-share v0.2.4 + Compiling once_cell v1.21.4 + Compiling bit-vec v0.8.0 + Compiling pkg-config v0.3.34 + Compiling itoa v1.0.18 + Compiling vcpkg v0.2.15 + Compiling num v0.4.3 + Compiling bit-set v0.8.0 + Compiling libsqlite3-sys v0.35.0 + Compiling fluent-uri v0.3.2 + Compiling rand_chacha v0.9.0 + Compiling idna_adapter v1.2.2 + Compiling parking_lot v0.12.5 + Compiling block-buffer v0.10.4 + Compiling crypto-common v0.1.7 + Compiling thiserror v2.0.20 + Compiling uuid v1.26.0 + Compiling foldhash v0.1.5 + Compiling utf8parse v0.2.2 + Compiling outref v0.5.2 + Compiling percent-encoding v2.3.2 + Compiling vsimd v0.8.0 + Compiling lazy_static v1.5.0 + Compiling fraction v0.15.4 + Compiling referencing v0.33.0 + Compiling uuid-simd v0.8.0 + Compiling hashbrown v0.15.5 + Compiling anstyle-parse v1.0.0 + Compiling digest v0.10.7 + Compiling idna v1.1.0 + Compiling regex v1.13.1 + Compiling fancy-regex v0.16.2 + Compiling rand v0.9.5 + Compiling email_address v0.2.9 + Compiling thiserror-impl v2.0.20 + Compiling is_terminal_polyfill v1.70.2 + Compiling anstyle-query v1.1.5 + Compiling bytecount v0.6.9 + Compiling cpufeatures v0.2.17 + Compiling num-cmp v0.1.0 + Compiling colorchoice v1.0.5 + Compiling anstyle v1.0.14 + Compiling base64 v0.22.1 + Compiling anstream v1.0.0 + Compiling jsonschema v0.33.0 + Compiling sha2 v0.10.9 + Compiling ulid v1.2.1 + Compiling hashlink v0.10.0 + Compiling heck v0.5.0 + Compiling anyhow v1.0.104 + Compiling bitflags v2.13.1 + Compiling fallible-streaming-iterator v0.1.9 + Compiling fallible-iterator v0.3.0 + Compiling strsim v0.11.1 + Compiling ryu-js v1.0.3 + Compiling clap_lex v1.1.0 + Compiling clap_builder v4.6.6 + Compiling clap_derive v4.6.4 + Compiling relayflowd-core v0.1.0 (/home/daytona/.relayflow-v2-supervisor/durable/repository/kernel/relayflowd-core) + Compiling clap v4.6.6 + Compiling wait-timeout v0.2.1 + Compiling rusqlite v0.37.0 + Compiling relayflowd-journal v0.1.0 (/home/daytona/.relayflow-v2-supervisor/durable/repository/kernel/relayflowd-journal) + Compiling relayflowd v0.1.0 (/home/daytona/.relayflow-v2-supervisor/durable/repository/kernel/relayflowd) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 18.04s + +> @relayflows/sdk@2.0.42 typecheck +> tsc --noEmit && tsc -p tsconfig.type-tests.json + + +> @relayflows/sdk@2.0.42 build +> tsc && node scripts/make-cli-executable.mjs + + +> @relayflows/sdk@2.0.42 typecheck:tests +> tsc -p tsconfig.tests.json + + + RUN v2.1.9 /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk + +stdout | tests/live-kernel.test.ts +LIVE_KERNEL relayflowd=/tmp/relayflows-daemon-check-target/debug/relayflowd +LIVE_KERNEL flows=/home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/dist/cli.js + + ✓ tests/preflight.test.ts (70 tests) 148ms + ✓ tests/cli.test.ts (71 tests) 2895ms + ✓ flows check CLI > binds a checked relative wrapper to the flow directory for worker execution 490ms + ✓ flows check CLI > resolves a bare PATH-resolved claude with no declared model, in an isolated PATH 438ms + ✓ flows run/resume CLI over the journal protocol > follows a worker wait past a locally expired lease until the daemon settles it 1042ms + ✓ tests/cloud-read.test.ts (46 tests) 52ms +stdout | tests/live-kernel.test.ts > surface resume after a real daemon kill > resumes a three-step run with each successful completion exactly once +LIVE_KERNEL kill -9 pid=30472 run=01M48SNXMEN8Y114WDV9HZPMR2 while step=two state=Running + + ❯ tests/live-kernel.test.ts (33 tests | 8 failed) 59035ms + ✓ built flows CLI against live relayflowd > twenty-six-step reuses 25 durable completions after editing the failed final step 2368ms + ✓ built flows CLI against live relayflowd > runs rung (a), parks rung (b), and keeps JSON report-shaped 2704ms + ✓ built flows CLI against live relayflowd > allows a deterministic run to exceed the bounded request timeout 32595ms + ✓ built flows CLI against live relayflowd > follows a live worker dispatch through flows run 725ms + ✓ built flows CLI against live relayflowd > runs an agent CLI end to end through the SDK worker 618ms + ✓ built flows CLI against live relayflowd > f.agent lowers to a real agent step and dispatches through a live worker 749ms + ✓ built flows CLI against live relayflowd > can always get a parked run to a late-attaching worker 5597ms + ✓ built flows CLI against live relayflowd > reports a real manual-recovery NeedsHuman state as parked 477ms + × built flows CLI against live relayflowd > runs hn-monitor analyze-story end-to-end via a stub agent CLI (gate 2 clause 2 demo) 767ms + → expected { …(12) } to match object { output: { …(3) }, …(1) } +(22 matching properties omitted from actual) + × built flows CLI against live relayflowd > hn-monitor analyze-story FAILS verification when the CLI omits required schema fields 754ms + → expected { …(12) } to match object { …(3) } +(21 matching properties omitted from actual) + × built flows CLI against live relayflowd > agent step preserves the CliResult wrapper as output when the CLI emits non-JSON text 776ms + → expected null not to be null + × built flows CLI against live relayflowd > AgentWorker exposes wake_context to the CLI via RELAYFLOW_WAKE_CONTEXT env var (real analyzer prerequisite) 709ms + → Cannot read properties of null (reading 'story_title') + × built flows CLI against live relayflowd > AgentWorker leaves RELAYFLOW_WAKE_CONTEXT UNSET when the run has no wake_context (undefined-vs-null pin) 636ms + → Cannot read properties of null (reading 'env_present') + ✓ built flows CLI against live relayflowd > AgentWorker passes a declared model to an identified wrapper as RELAYFLOW_MODEL 967ms + ✓ built flows CLI against live relayflowd > AgentWorker refuses a nonconforming journal-submitted wrapper before exposing RELAYFLOW_MODEL 1047ms + ✓ built flows CLI against live relayflowd > AgentWorker executes the raw claude adapter with its real model flag 868ms + ✓ built flows CLI against live relayflowd > AgentWorker executes the raw codex adapter with its real model flag 595ms + × built flows CLI against live relayflowd > AgentWorker leaves RELAYFLOW_MODEL UNSET when the step declares no model 618ms + → Cannot read properties of null (reading 'story_title') + × built flows CLI against live relayflowd > hn-monitor analyze-story reaches done through the real Claude analyzer CLI 30ms + → LIVE_ANALYZER_UNAVAILABLE: "/home/daytona/.relayflow-v2-supervisor/durable/repository/testdata/preflight/analyze-story-claude-cli" does not identify as relayflows-agent-cli-v1 — failing because gate-2 acceptance requires the real analyzer to execute. Set RELAYFLOWS_ALLOW_ANALYZER_SKIP=1 only if this run is not gate evidence. + ✓ built flows CLI against live relayflowd > preflights before journaling and names an unreachable socket 887ms + ✓ built flows CLI against live relayflowd > starts exactly one daemon when two runs race for one empty data dir 593ms + ✓ subprocess_gate output capture against live relayflowd > journals the gate command output and reports both tails 1161ms + ✓ surface resume after a real daemon kill > resumes a three-step run with each successful completion exactly once 1075ms + × a relayflow can be scheduled: tick source against live relayflowd > a tick spawns a real run whose step reports the SCHEDULED instant 618ms + → expected null to deeply equal { schedule_id: 'heartbeat-1m', …(3) } + ✓ tests/cloud-live.test.ts (55 tests) 2060ms + ✓ argv and wiring > routes both live invocations through runCli, and refuses a missing run id 2005ms + ❯ tests/hosted-extension-isolation.test.ts (22 tests | 13 failed) 4020ms + × hosted extension capability isolation > executes the exact capability-only handler for a queued receipt 235ms + → Hosted extension sandbox exited without a valid completion (exit 1): bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted + + × hosted extension capability isolation > executes the exact capability-only handler for a duplicate receipt 232ms + → Hosted extension sandbox exited without a valid completion (exit 1): bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted + + × hosted extension capability isolation > launches through the captured process primitive after builtin export synchronization 224ms + → promise rejected "Error: Hosted extension sandbox exited wi… { code: '…' }" instead of resolving + × hosted extension capability isolation > ignores inherited launcher overrides and decodes manifests with the captured Buffer intrinsic 216ms + → promise rejected "Error: Hosted extension sandbox exited wi… { code: '…' }" instead of resolving + × hosted extension capability isolation > streams verified bytes when the live store is replaced and no writable staging path exists 270ms + → promise rejected "Error: Hosted extension sandbox exited wi… { code: '…' }" instead of resolving + × hosted extension capability isolation > mounts pinned private Surface bytes when the live package changes before launch 242ms + → promise rejected "Error: Hosted extension sandbox exited wi… { code: '…' }" instead of resolving + × hosted extension capability isolation > shields verified Surface files before async settlement 229ms + → promise rejected "Error: Hosted extension sandbox exited wi… { code: '…' }" instead of resolving + × hosted extension capability isolation > preserves a typed host refusal while disclosing only a fixed marker to the child 224ms + → expected Error: Hosted extension sandbox exited wi… { code: '…' } to be Error: private Cloud policy detail { code: '…' } // Object.is equality + × hosted extension capability isolation > denies ambient credentials, host files, writes, network, subprocesses, and undeclared context verbs 231ms + → Hosted extension sandbox exited without a valid completion (exit 1): bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted + + × hosted extension capability isolation > enforces OS address-space and data bounds on native Buffer allocation 228ms + → expected Error: Hosted extension sandbox exited wi… { code: '…' } to match object { code: 'plugin_unsupported', …(1) } + × hosted extension capability isolation > blocks extra handler fields and authority-bearing receipt fields at the parent port 224ms + → expected Error: Hosted extension sandbox exited wi… { code: '…' } to match object { code: 'plugin_event_unroutable' } + × hosted extension capability isolation > constructs adapter authority with the captured freeze intrinsic 220ms + → Hosted extension sandbox exited without a valid completion (exit 1): bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted + + × hosted extension capability isolation > writes the Surface manifest and protocol without inherited toJSON behavior 226ms + → promise rejected "Error: Hosted extension sandbox exited wi… { code: '…' }" instead of resolving + ✓ hosted extension capability isolation > fails closed when the handler omits or repeats the single capability call 689ms + ✓ tests/cloud-deploy.test.ts (117 tests) 5262ms + ✓ flows deploy / flows deployments > deploys and lists GitLab repositories (json=false) 323ms + ✓ flows deploy / flows deployments > deploys and lists GitLab repositories (json=true) 682ms + ✓ flows deploy / flows deployments > passes --agents and --draft through, and names a structured refusal 493ms + ✓ flows deploy / flows deployments > explains a 403 as a missing cli:auth login and exits 2 320ms + ✓ flow versions (cloud#4115) > updates a GitLab listener without adding a target integration requirement 395ms + ✓ flow versions (cloud#4115) > names an unknown or ambiguous flow 485ms + ✓ flow versions (cloud#4115) > reports the version a create-form deploy made 405ms + ✓ tests/cloud-sync.test.ts (40 tests) 2787ms + ✓ flows run --cloud --sync-code / flows sync > runs an authored flow with --input and syncs the invoking directory 402ms + ✓ tests/observer-link.test.ts (44 tests) 598ms + ✓ tests/authored-flow.test.ts (38 tests) 896ms + ❯ tests/plugin-extension.test.ts (94 tests | 11 failed) 491ms + × flows add > resolves a branch to its commit, materializes, and records flows.json plus the lockfile 113ms + → expected 2 to be +0 // Object.is equality + × flows add > keeps the digest stable across identical installs and distinct across content changes 1ms + → ENOSPC: no space left on device, mkdtemp '/tmp/plugin-ext-XXXXXX' + × flows add > recovers both crash points in the flows.json and lock transaction 1ms + → ENOSPC: no space left on device, mkdtemp '/tmp/plugin-ext-XXXXXX' + × flows add > refuses github:AgentWorkforce/flows@nope#examples/babysitter with plugin_source_unresolved and writes nothing 1ms + → ENOSPC: no space left on device, mkdtemp '/tmp/plugin-ext-XXXXXX' + × flows add > refuses github:AgentWorkforce/flows@main#examples/babysitter with plugin_source_unresolved and writes nothing 0ms + → ENOSPC: no space left on device, mkdtemp '/tmp/plugin-ext-XXXXXX' + × flows add > refuses github:AgentWorkforce/flows@feat/babysitter-v2#examples with plugin_manifest_missing and writes nothing 0ms + → ENOSPC: no space left on device, mkdtemp '/tmp/plugin-ext-XXXXXX' + × flows add > refuses github:AgentWorkforce/flows@bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb#examples/babysitter with plugin_source_unresolved and writes nothing 0ms + → ENOSPC: no space left on device, mkdtemp '/tmp/plugin-ext-XXXXXX' + × flows add > refuses github:AgentWorkforce/flows@feat/babysitter-v2#../x with plugin_source_invalid and writes nothing 0ms + → ENOSPC: no space left on device, mkdtemp '/tmp/plugin-ext-XXXXXX' + × flows add > refuses a plugin whose compat excludes this runtime 0ms + → ENOSPC: no space left on device, mkdtemp '/tmp/plugin-ext-XXXXXX' + × flows add > refuses a manifest whose declared source is not where it was fetched from 1ms + → ENOSPC: no space left on device, mkdtemp '/tmp/plugin-ext-XXXXXX' + × flows add > refuses a second source under an already-installed name 0ms + → ENOSPC: no space left on device, mkdtemp '/tmp/plugin-ext-XXXXXX' + ✓ tests/cloud-transcript-codex.test.ts (44 tests) 19ms + ✓ tests/worker-cli.test.ts (25 tests) 29921ms + ✓ registered CLI model defaults > passes the same priced Claude default to the real provider invocation 663ms + ✓ registered CLI model defaults > uses the explicitly supplied agent environment for the provider subprocess 742ms + ✓ registered CLI model defaults > dispatches canonical generic bytes with the preflight-proved adapter identity 615ms + ✓ direct transport lifecycle evidence > classifies only the exact Codex stdin lifecycle signature as retryable 706ms + ✓ direct transport lifecycle evidence > records a signal close separately from an ordinary nonzero exit 1018ms + ✓ direct transport lifecycle evidence > records a spawn error code without treating a missing executable as transient 507ms + ✓ direct transport lifecycle evidence > journals classified lifecycle evidence and reports crashed instead of generic worker_error 811ms + ✓ step discovery environment > names the run, step, attempt and an absolute data dir for a direct agent spawn 824ms + ✓ step discovery environment > exports none of the four without a data dir, even when the worker inherited them 530ms + ✓ wrapper discovery environment > sets the four names from the dispatch and still refuses ambient values and other secrets 643ms + ✓ wrapper discovery environment > exports none of the four to a wrapper without a data dir, even when the worker inherited them 542ms + ✓ custom wrapper execution identity > passes an explicit safe environment at identification and execution 545ms + ✓ custom wrapper execution identity > refuses a wrapper symlink retarget before delivering private values 547ms + ✓ custom wrapper execution identity > bounds wrapper execution after acknowledgement 627ms + ✓ custom wrapper execution identity > bounds captured wrapper output 530ms + ✓ custom wrapper execution identity > refuses a duplicate execute protocol frame 575ms + ✓ custom wrapper execution bounds are reader-owned > resolves when a conforming wrapper leaks a stdio pipe to a background helper 801ms + ✓ custom wrapper execution bounds are reader-owned > resolves when the leaked helper inherits stderr only 716ms + ✓ custom wrapper execution bounds are reader-owned > resolves when a wrapper leaks a stdio pipe and exits before identifying 3524ms + ✓ custom wrapper execution bounds are reader-owned > journals a completionReason at the default bound when a wrapper leaks a stdio pipe 11473ms + ✓ custom wrapper execution bounds are reader-owned > accepts an execute token and an over-8KiB payload flushed in one write 552ms + ✓ custom wrapper execution bounds are reader-owned > accepts the same over-8KiB payload whether or not it coalesces with the execute token 1389ms + ✓ custom wrapper execution bounds are reader-owned > still bounds an un-terminated handshake buffer and names the bound 465ms + ✓ delivers the journaled memory pack to the real wrapper and excludes its charge from completion usage 574ms + ✓ tests/flow-extension-compose.test.ts (33 tests) 6984ms + ✓ composing flow extensions onto a base flow > appends the Babysitter handler surface after the base, in lock order, without touching the base definition 321ms + ✓ composing flow extensions onto a base flow > composes two extensions in declaration order, and the order is the lockfile order 491ms + ✓ composing flow extensions onto a base flow > flows check reports the composition and keeps the composed triggers deliverable 1014ms + ✓ composing flow extensions onto a base flow > uses extension permissions for hosted deploy preflight and the deploy body 303ms +(node:33905) ExperimentalWarning: SQLite is an experimental feature and might change at any time +(Use `node --trace-warnings ...` to show where the warning was created) + ✓ tests/cli-status.test.ts (27 tests) 1404ms + ✓ flows status > resolves the run with no arguments from inside a worker-spawned agent 1015ms + ✓ tests/run-state.test.ts (28 tests) 12ms + ✓ tests/cloud-run.test.ts (59 tests) 827ms + ❯ tests/relay-cli-surface.test.ts (87 tests | 1 failed) 43ms + × relay-cli surface: drift between `commands` and `run` > carries every declared flag on an invocation that parses 7ms + → expected [ Array(2) ] to deeply equal [] + ✓ tests/agent-transcript.test.ts (29 tests) 283ms + ❯ tests/babysitter-native-extension.test.ts (41 tests | 1 failed) 1844ms + × native Babysitter extension > runs the exact published 2.0.26 native bytes in the isolated capability path 235ms + → promise rejected "Error: Hosted extension sandbox exited wi… { code: '…' }" instead of resolving + ✓ tests/shipped-source-worker-call-forms.test.ts (1 test) 16543ms + ✓ shipped-source worker call forms > fails closed for computed keys, assertions, binds, call, and apply 16542ms +(node:34309) [FLOWS_ROOT_LEASE_LOST] Warning: authored root run_id=root-run attempt=1: lease_conflict: attempt has no active worker lease. Waiting for the kernel to retry it. +(Use `node --trace-warnings ...` to show where the warning was created) + ✓ tests/authored-root.test.ts (22 tests) 423ms + ✓ tests/authored-completion-detail.test.ts (59 tests) 208ms + ✓ tests/shipped-source-model-provenance.test.ts (1 test) 146276ms + ✓ shipped-source model provenance > does not treat mutable aliases or incomplete named agents as pinned 146275ms + ✓ tests/step-failure-diagnostic.test.ts (26 tests) 109ms + ✓ tests/stop-process-group.test.ts (11 tests) 15705ms + ✓ every stop reaches the process group, not just the direct child > exits the run after an execution-timeout stop 1212ms + ✓ every stop reaches the process group, not just the direct child > exits the run after a protocol terminate stop 755ms + ✓ every stop reaches the process group, not just the direct child > kills a SIGTERM-deaf grandchild after a protocol terminate stop 1748ms + ✓ every stop reaches the process group, not just the direct child > kills a SIGTERM-deaf grandchild after an execution-timeout stop 2339ms + ✓ every stop reaches the process group, not just the direct child > holds the loop open long enough for the escalation to run 1139ms + ✓ every stop reaches the process group, not just the direct child > bounds forced-stop confirmation when a group remains unprovable 1014ms + ✓ a wrapper that exits with no execution deadline still drains > reports the wrapper result and reaps a grandchild holding its pipes 902ms + ✓ a wrapper that exits with no execution deadline still drains > reaps a SIGTERM-deaf grandchild holding its pipes 1939ms + ✓ a wrapper that exits with no execution deadline still drains > settles on its own deadline when an escaped holder withholds close 4307ms + ✓ tests/mcp.test.ts (34 tests) 15481ms + ✓ MCP preflight and transports > flows check refuses an undeclared server with exit 2 and no daemon 1081ms + ✓ MCP preflight and transports > flows check reports a refusing server and leaves no PID 1178ms + ✓ MCP preflight and transports > kills a SIGTERM-resistant silent child after a parent-owned handshake deadline 1322ms + ✓ MCP preflight and transports > reaps a SIGTERM-resistant descendant with inherit stdio before cleanup finishes 1068ms + ✓ MCP preflight and transports > reaps a SIGTERM-resistant descendant with ignore stdio before cleanup finishes 2027ms + ✓ MCP preflight and transports > rejects close when forced group death remains unprovable 2073ms + ✓ MCP preflight and transports > cancels force escalation when close follows an exited child 1183ms + ✓ MCP preflight and transports > waits for child close when the transport cannot own a process group 1053ms + ✓ MCP preflight and transports > cancels force escalation for an already-closed child without a process group 1174ms + ✓ MCP preflight and transports > reports malformed connection configuration as config_invalid 1376ms + ✓ authored MCP effects against the real kernel > journals one MCP receipt per call with args, result, stable logical key, and a confirmed effect 313ms + ✓ authored MCP effects against the real kernel > reports a dropped tool connection as a failed CLI run 838ms + ✓ tests/step-attempt-history.test.ts (23 tests) 41ms + ✓ tests/daemon-lifecycle.test.ts (42 tests) 51ms + ❯ tests/hosted-extension-protocol.test.ts (27 tests | 8 failed) 42286ms + × hosted extension hostile protocol > uses captured JSON intrinsics for the complete parent boundary 244ms + → Hosted extension sandbox exited without a valid completion (exit 1): bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted + + × hosted extension hostile protocol > rejects an import-time different PR frame with zero adapter calls 223ms + → expected Error: Hosted extension sandbox exited wi… { code: '…' } to match object { code: 'plugin_event_unroutable' } + × hosted extension hostile protocol > rejects an import-time different delivery frame with zero adapter calls 265ms + → expected Error: Hosted extension sandbox exited wi… { code: '…' } to match object { code: 'plugin_event_unroutable' } + × hosted extension hostile protocol > rejects an import-time different event frame with zero adapter calls 289ms + → expected Error: Hosted extension sandbox exited wi… { code: '…' } to match object { code: 'plugin_event_unroutable' } + × hosted extension hostile protocol > rejects two forged calls after the authoritative first outcome settles 10154ms + → hostile child did not invoke the adapter + × hosted extension hostile protocol > waits for a pending adapter to reject after a forged child error 10168ms + → hostile child did not invoke the adapter + × hosted extension hostile protocol > waits for a pending adapter to resolve after a forged child error 10155ms + → hostile child did not invoke the adapter + × hosted extension hostile protocol > returns a typed adapter rejection even when the hostile child hangs 10206ms + → hostile child did not invoke the adapter + ❯ tests/authored-node-runtime.test.ts (16 tests | 16 skipped) 13ms + ✓ tests/artifact-gates.test.ts (27 tests) 252ms + ✓ tests/hosted-base-snapshot.test.ts (18 tests) 2328ms + ✓ hosted base private snapshot > stops streaming project entries at the shared count limit 2233ms + ✓ tests/close-pr-flow.test.ts (35 tests) 21545ms + ✓ close-pr journaled repair loop > reads an existing Bugbot finding, repairs in the same worktree, pushes and re-verifies before merging 2378ms + ✓ close-pr journaled repair loop > opens a PR and feeds failed CI logs into the repair agent 2531ms + ✓ close-pr journaled repair loop > pins the current generated model for codex when no override is supplied 3829ms + ✓ close-pr journaled repair loop > pins the current generated model for claude when no override is supplied 4280ms + ✓ close-pr journaled repair loop > parks after exactly three nonconverging repairs, with accumulated blockers 2423ms + ✓ close-pr journaled repair loop > can converge on the third repair 2796ms + ✓ close-pr journaled repair loop > executes the deterministic commit and force-push steps against a local Git remote, including a no-op repair 3058ms + ✓ tests/cloud-connect.test.ts (24 tests) 3497ms + ✓ hosted verbs connect before they submit > flows run --cloud submits once the prompt connected the integration 2243ms + ✓ tests/journal-client.test.ts (17 tests) 92ms + ✓ tests/authored-node-result.test.ts (45 tests) 21ms + ✓ tests/verb-field-lint.test.ts (99 tests) 592ms + ✓ tests/bundle.test.ts (26 tests) 11718ms + ✓ immutable bundles > returns exit 2 naming a byte-flipped payload and refuses to reuse corruption 683ms + ✓ immutable bundles > verifies with --verify in any position and answers --json with one object 1412ms + ✓ immutable bundles > refuses --out with --verify rather than ignoring the destination 866ms + ✓ immutable bundles > builds and verifies the canonical YAML fixture through the compiled CLI 1869ms + ✓ immutable bundles > emits the ephemeral warning on CLI stderr and uses the default output directory 851ms + ✓ immutable bundles > refuses build-provable CLI resolution errors without environment probes 438ms + ✓ immutable bundles > builds a standalone TS fixture twice with identical executable hashes 2643ms + ✓ immutable bundles > refuses to label installed dependency drift with lockfile pins 430ms + ✓ immutable bundles > refuses invalid CLI arguments %j 403ms + ✓ immutable bundles > refuses invalid CLI arguments "--out" 423ms + ✓ immutable bundles > refuses invalid CLI arguments "--verify" 397ms + ✓ immutable bundles > refuses invalid CLI arguments "--verify" 524ms + ✓ immutable bundles > refuses invalid CLI arguments "--out" 602ms + ✓ tests/validate.test.ts (68 tests) 44ms + ✓ tests/shipped-source-flow-provenance-repairs.test.ts (1 test) 36974ms + ✓ shipped-source flow provenance repairs > audits repaired writer, alias, binding, and cyclic provenance forms 36974ms + ✓ tests/agent-relay-transport.test.ts (17 tests) 2300ms + ✓ Relay completion at the journal boundary > does not complete at readiness and journals exact output, receipt, and priced accounting 1010ms + ✓ Relay completion at the journal boundary > aborts polling on rejected renewal and never writes a stale completion 1002ms + ✓ tests/tick-source.test.ts (33 tests) 22ms + ✓ tests/flow-executor-chain.test.ts (14 tests) 12534ms + ✓ flow executor LLM and output-binding chain > runs f.llm -> f.agent -> f.run with schema-verified journal output and the exact allowed model 1319ms + ✓ flow executor LLM and output-binding chain > runs a dollar-budgeted authored Claude agent with the same default used by preflight 949ms + ✓ flow executor LLM and output-binding chain > fails invalid LLM output before the next step: not JSON 327ms + ✓ flow executor LLM and output-binding chain > fails invalid LLM output before the next step: {"message":7} 398ms + ✓ flow executor LLM and output-binding chain > preserves JSON values without promoting them to process wrappers: null 371ms + ✓ flow executor LLM and output-binding chain > preserves JSON values without promoting them to process wrappers: [1,2] 330ms + ✓ flow executor LLM and output-binding chain > preserves JSON values without promoting them to process wrappers: "hello" 386ms + ✓ flow executor LLM and output-binding chain > runs the exact authored flagship f.llm -> f.agent -> f.run path through the durable CLI root 2082ms + ✓ flow executor LLM and output-binding chain > resumes an interrupted durable authored root without replaying completed flagship effects 3606ms + ✓ flow executor LLM and output-binding chain > passes a declarative verified value through an agent into a deterministic artifact 842ms + ✓ flow executor LLM and output-binding chain > flows run consumes YAML bindings and resume reuses the original journal output 1265ms + ✓ tests/authored-step-graph.test.ts (25 tests) 832ms + ✓ the authored step DAG > does not walk a long-running step's own polling chain to find its dependents' edges 309ms + ✓ tests/cloud-mirror.test.ts (14 tests) 35ms + ✓ tests/pr-review-post.test.ts (21 tests) 2163ms + ✓ tests/authored-flow-lifecycle-executor.test.ts (27 tests) 616ms + ✓ tests/authored-step-terminal-completion.test.ts (13 tests) 4884ms + ✓ readCompletedStepOutput on an adopted gated child whose gate has not started > refuses a producer success when the wait ends with the run still not terminal 2007ms + ✓ readCompletedStepOutput given a suspended known status (#441) > refuses a producer success while the child is still not terminal 2008ms + ✓ tests/step-failure-excerpt.test.ts (42 tests) 200ms + ✓ tests/cloud-mirror-step.test.ts (17 tests) 14ms +(node:44209) ExperimentalWarning: SQLite is an experimental feature and might change at any time +(Use `node --trace-warnings ...` to show where the warning was created) + ✓ tests/cli-replay.test.ts (38 tests) 1349ms + ✓ flows replay > --json is byte-identical across two CLI invocations (diff) 915ms + ✓ tests/authored-flow-slack.test.ts (7 tests) 2171ms + ✓ authored Slack helper effects > replays after SIGKILL before confirm with the same token and one successful completion 682ms + ✓ authored Slack helper effects > replays after SIGKILL before complete with the same token and one successful completion 856ms + ✓ tests/wrapper-execution-duration.test.ts (7 tests) 10841ms + ✓ keeps the handshake deadline independent of the removed execution deadline 10053ms + ✓ still lets a lease abort stop an unlimited wrapper before it produces output 519ms + ✓ tests/tick-runner.test.ts (22 tests) 2442ms + ✓ CLI argument parsing refuses coercion rather than accepting it > refuses --interval-ms fractional as an invocation error 401ms + ✓ CLI argument parsing refuses coercion rather than accepting it > refuses --interval-ms exponent notation as an invocation error 405ms + ✓ CLI argument parsing refuses coercion rather than accepting it > refuses --interval-ms hex as an invocation error 397ms + ✓ CLI argument parsing refuses coercion rather than accepting it > refuses --interval-ms trailing text as an invocation error 414ms + ✓ CLI argument parsing refuses coercion rather than accepting it > refuses --interval-ms empty as an invocation error 404ms + ✓ CLI argument parsing refuses coercion rather than accepting it > accepts an exact integer and proceeds past parsing 394ms + ✓ tests/authored-step-index.test.ts (17 tests) 18ms + ✓ tests/named-gate-diagnostics.test.ts (20 tests) 703ms + ✓ tests/authored-agent-artifacts.test.ts (5 tests) 366ms + ✓ tests/direct-input.test.ts (6 tests) 6640ms + ✓ direct .flow.ts input through the built CLI and live runtime > returns exit 3 for an authored human handoff and persists its outcome 676ms + ✓ direct .flow.ts input through the built CLI and live runtime > returns exit 1 for an authored step_failed verdict and persists its outcome 682ms + ✓ direct .flow.ts input through the built CLI and live runtime > executes inline and file JSON input through relayflowd 2736ms + ✓ direct .flow.ts input through the built CLI and live runtime > refuses missing and malformed input before contacting relayflowd 1613ms + ✓ direct .flow.ts input through the built CLI and live runtime > does not run the authored body before daemon availability 504ms + ✓ direct .flow.ts input through the built CLI and live runtime > refuses oversized file input before contacting relayflowd 427ms + ✓ tests/worker-transcript.test.ts (9 tests) 924ms + ✓ tests/gate-contract.test.ts (20 tests) 150ms +Stopped: a Linear ticket must carry its identifier (like TECH-42) so the pull request can link back. +Stopped: a Linear ticket must carry its identifier (like TECH-42) so the pull request can link back. +Stopped: invalid pull-request metadata (duplicate-linear-closing-reference). No branch was pushed and no pull request was opened. +Stopped: invalid pull-request metadata (duplicate-linear-closing-reference). No branch was pushed and no pull request was opened. +Stopped: invalid pull-request metadata (duplicate-github-closing-reference). No branch was pushed and no pull request was opened. +Stopped: invalid pull-request metadata (malformed-review-scope). No branch was pushed and no pull request was opened. +Stopped: invalid pull-request metadata (malformed-review-scope). No branch was pushed and no pull request was opened. +Stopped: could not read the reviewed head commit. Nothing was pushed. +Stopped: could not read the reviewed head commit. Nothing was pushed. +Stopped: could not read the reviewed head commit. Nothing was pushed. + ✓ tests/canonical-software-factory.test.ts (25 tests) 853ms + ✓ tests/authored-human.test.ts (13 tests) 95ms + ✓ tests/wrapper-exit-drain.test.ts (10 tests) 3531ms + ✓ reports a signalled wrapper death while a pipe is held, with its output intact 380ms + ✓ lets a lease abort outrank a successful exit still being drained 536ms + ✓ does not let a later drain overwrite abort while group confirmation is pending 637ms + ✓ tests/worker-cli-result-exit.test.ts (7 tests) 34868ms + ✓ a Claude agent step completes on its result, not only on process exit > settles a hung, successful run within the grace and stops its whole tree 32623ms + ✓ a Claude agent step completes on its result, not only on process exit > maps an error result on a hung run to a failed exit 31623ms + ✓ a Claude agent step completes on its result, not only on process exit > leaves a hang before any result to the existing stops 32015ms + ✓ an agent tree does not outlive the process that spawned it > kills the agent group when the run process is terminated by SIGTERM 801ms + ✓ an agent tree does not outlive the process that spawned it > removes a wrapper alias on host exit after group death stays unprovable 1352ms + ✓ tests/cloud-schedule.test.ts (17 tests) 5656ms + ✓ schedule lowering > marks a non-grid cron as Cloud-only rather than approximating it, with a silence budget from its own cadence 2998ms + ✓ flows check prints declared schedules > shows the lowering for a fixed interval and the Cloud-only note for a real cron 2025ms + ✓ tests/authored-run-failure-evidence.test.ts (9 tests) 981ms + ✓ the child index after the process that wrote it is gone > still names every child, with its own run id, after a daemon restart 488ms + ✓ tests/cli-hn-monitor.test.ts (16 tests) 94ms + ✓ tests/spec-parity.test.ts (46 tests) 512ms + ✓ tests/authored-activity.test.ts (16 tests) 111ms + ✓ tests/run-projection.test.ts (13 tests) 11ms + ✓ tests/agent-artifacts-live.test.ts (5 tests) 4693ms + ✓ agent artifacts and gates through the built CLI, a real daemon and the local agent > journals the files the agent wrote, including under a dot-directory, and every artifact gate passes on that journal 1321ms + ✓ agent artifacts and gates through the built CLI, a real daemon and the local agent > fails the run when the artifact_exists gate names a file the agent did not write 882ms + ✓ agent artifacts and gates through the built CLI, a real daemon and the local agent > fails the run with the author reason when a predicate gate returns false, journaling the verdict 828ms + ✓ review follow-ups > applies a predicate gate on a helper step too, and journals its verdict 930ms + ✓ review follow-ups > records predicate verdicts on the root run so a resume reuses them instead of re-running the closure 730ms + ✓ tests/daemon-lifecycle-live.test.ts (9 tests) 7184ms + ✓ flows run against a data dir with no daemon (§6 test 7) > cold start spawns exactly one daemon, the run succeeds, and the daemon outlives the CLI 490ms + ✓ flows run against a data dir with no daemon (§6 test 7) > polls, bounded, for a daemon that holds the lock before it binds 1407ms + ✓ flows run against a data dir with no daemon (§6 test 7) > attaches to a serving daemon that has not published a connection file 488ms + ✓ flows run against a data dir with no daemon (§6 test 7) > a second run attaches to the daemon the first one started, spawning nothing 1082ms + ✓ flows run against a data dir with no daemon (§6 test 7) > detects a stale connection file left by a hard kill and starts a fresh daemon 1051ms + ✓ concurrent invocations against one empty data dir (§6 test 15) > ends with exactly one daemon owning the socket, and both runs succeed 1280ms + ✓ refusals from a spawn that cannot produce a daemon > names relayflowd_not_found rather than falling through to PATH 429ms + ✓ refusals from a spawn that cannot produce a daemon > names daemon_start_failed and quotes the daemon log when startup dies 483ms + ✓ refusals from a spawn that cannot produce a daemon > refuses a daemon speaking another protocol version instead of binding over it 471ms + ✓ tests/agent-transcript-live.test.ts (4 tests) 3811ms + ✓ the transcript digest through the built CLI, a real daemon and the local agent > preserves structured agent failure details and its completed root index 1067ms + ✓ the transcript digest through the built CLI, a real daemon and the local agent > preserves structured llm failure details and its completed root index 887ms + ✓ the transcript digest through the built CLI, a real daemon and the local agent > journals the digest in trajectory_tail on a successful agent step and writes the file it points at 915ms + ✓ the transcript digest through the built CLI, a real daemon and the local agent > on a failed agent step, names the failure and the transcript in the terminal diagnostic, redacted 941ms + ✓ tests/resume-local-agent.test.ts (8 tests) 19ms +(node:47667) Warning: Transcript tail for run-9/analyze attempt 1 (stdout) could not be written; the step continues without it: EACCES: permission denied, mkdir '/tmp/transcript-tail-tdIcb8/runs/run-9/steps' +(Use `node --trace-warnings ...` to show where the warning was created) + ✓ tests/transcript-tail.test.ts (11 tests) 1842ms + ✓ direct agent spawn > tees stdout and stderr into tail files that name the dispatch 748ms + ✓ direct agent spawn > completes the step when the tail directory cannot be created 805ms + ✓ tests/advisory-outcome.test.ts (23 tests) 80ms + ✓ tests/shipped-source-cubic-regressions.test.ts (5 tests) 12584ms + ✓ shipped-source adversarial provenance regressions > retains callable writes through member mutation, spreads, this, holes, and alternate receivers 6908ms + ✓ shipped-source adversarial provenance regressions > does not manufacture intrinsic writes or comma-left callables 2491ms + ✓ shipped-source adversarial provenance regressions > treats constructor and implicit-this receiver escapes as possible worker writes 602ms + ✓ shipped-source adversarial provenance regressions > maps pure helper parameters and refuses mutations or conditional assignments as proofs 1953ms + ✓ shipped-source adversarial provenance regressions > does not apply later alias assignments retroactively to copied values 628ms + ✓ tests/agent-cwd-live.test.ts (4 tests) 3322ms + ✓ agents drive two checkouts under one run root > runs each CLI in its declared directory, and the run is not refused as an unknown field 1029ms + ✓ agents drive two checkouts under one run root > gates on an artifact path relative to the agent directory 862ms + ✓ a declaration this contract cannot honour is refused at its own edge > refuses a path that climbs out of the run root without starting a run at all 680ms + ✓ a declaration this contract cannot honour is refused at its own edge > fails the step with the reason when the directory is not there, rather than running somewhere else 749ms + ✓ tests/authored-helpers.test.ts (6 tests) 3173ms + ✓ runs every available provider through the real kernel and resumes completed effects without a second write 1689ms + ✓ replays after SIGKILL before confirm with the same token and one successful completion 590ms + ✓ replays after SIGKILL before complete with the same token and one successful completion 547ms + ✓ tests/cloud-mirror-session.test.ts (12 tests) 14ms + ✓ tests/agent-cwd.test.ts (53 tests) 49ms + ✓ tests/authored-flow-operation.test.ts (24 tests) 547ms + ✓ tests/budget-attribution.test.ts (10 tests) 9ms + ✓ tests/pty-sidechannel.test.ts (14 tests) 9271ms + ✓ view attach preserves worker completion and marks only drive 1089ms + ✓ drive attach preserves worker completion and marks only drive 590ms + ✓ passthrough attach preserves worker completion and marks only drive 1121ms + ✓ none attach preserves worker completion and marks only drive 1047ms + ✓ none subscriber lets an unattended CLI read EOF 630ms + ✓ view subscriber lets an unattended CLI read EOF 586ms + ✓ passthrough subscriber lets an unattended CLI read EOF 609ms + ✓ incomplete subscriber lets an unattended CLI read EOF 560ms + ✓ unattended Codex receives closed stdin before startup instead of entering its additional-input lifecycle 640ms + ✓ ends driven stdin when the last drive peer disconnects after spawn 669ms + ✓ rejects drive after EOF without marking human intervention 866ms + ✓ delivers all drive bytes in order across child stdin backpressure 824ms + ✓ tests/helper-reference.test.ts (30 tests) 27ms + ✓ tests/flow-requirements.test.ts (14 tests) 911ms + ✓ flows check prints REQUIRES > names the helper, the harness and the mcp server of an authored flow 620ms + ✓ tests/backlog-picker.test.ts (14 tests) 45ms + ✓ tests/plugin-store-bounds.test.ts (11 tests) 88ms + ✓ tests/cloud-mirror-live.test.ts (3 tests) 2999ms + ✓ a local run on the Cloud dashboard > registers, publishes its steps and its log, and reports terminal last 584ms + ✓ a local run on the Cloud dashboard > sends nothing at all unless it is asked to 1407ms + ✓ a local run on the Cloud dashboard > publishes a composed authored flow as one connected dashboard DAG 884ms + ✓ tests/classify-outcome.test.ts (11 tests) 5924ms + ✓ classifyOutcome > gives up and reports when a running run never becomes classifiable 2008ms + ✓ the remedy on a worker park > follows a step through a retry backoff longer than the unclassified bound 3004ms + ✓ the remedy on a worker park > follows a run.start outcome that is already running on a retried attempt 503ms + ✓ tests/authored-completion-recovery.test.ts (5 tests) 489ms + ✓ tests/backlog-picker-flow.test.ts (6 tests) 280ms + ✓ tests/hosted-extension-protocol-intrinsics.test.ts (6 tests) 14ms + ✓ tests/preflight-permissions-unenforced.test.ts (17 tests) 725ms + ✓ an authored TypeScript body is out of reach, and spec.ts says so > checks clean on a .flow.ts whose body declares permissions 677ms + ✓ tests/budget-unmetered-live.test.ts (4 tests) 1760ms + ✓ unmetered budget spend through the live kernel > runs an unpriced step under a dollar budget without tripping it, journaling unknown dollars 658ms + ✓ unmetered budget spend through the live kernel > journals an unpriced claude step's reported cost beside unchanged metered usage, and the verdict is unchanged 477ms + ✓ unmetered budget spend through the live kernel > still counts an unpriced step toward a token budget 333ms + ✓ tests/stuck-run-triage.test.ts (22 tests) 3158ms + ✓ stuck-run-triage shell text > collects tails with no GNU timeout on PATH, as on a stock macOS 3109ms + ✓ tests/hosted-extension-routing.test.ts (7 tests) 11ms + ✓ tests/webhook.test.ts (9 tests) 1641ms + ✓ webhook ingress > checks TS declarations against flows.json without invoking handlers 1320ms + ✓ tests/redact.test.ts (54 tests) 8ms + ✓ tests/cli-probe.test.ts (17 tests) 2009ms + ✓ tests/authored-advisory-run-live.test.ts (16 tests) 1105ms + ✓ tests/webhook-live.test.ts (6 tests) 10509ms + ✓ executes and deduplicates 'app_mention' only for its provider and matching payload 1642ms + ✓ executes and deduplicates 'reaction_added' only for its provider and matching payload 1613ms + ✓ executes and deduplicates 'pull_request' only for its provider and matching payload 1671ms + ✓ flows serve-webhook writes JSON before the daemon starts, then journals and archives exactly once 1552ms + ✓ replays a dropped file after SIGKILL before spawn 497ms + ✓ resumes the same journal after SIGKILL after spawn and before acknowledgement 3533ms + ✓ tests/local-agent-live.test.ts (7 tests) 42677ms + ✓ built CLI local agent against a real daemon > dispatches through the wrapper and keeps --json stdout report-shaped 957ms + ✓ built CLI local agent against a real daemon > runs beyond the initial 30-second lease without a second invocation 35972ms + ✓ built CLI local agent against a real daemon > renders actual agent completion in text output 841ms + ✓ built CLI local agent against a real daemon > returns a failed run when the agent process fails 787ms + ✓ built CLI local agent against a real daemon > prints a remedy that runs the parked authored flow to completion 1451ms + ✓ built CLI local agent against a real daemon > prints a remedy that runs, for a run started from an input file too long to be one 2000ms + ✓ built CLI local agent against a real daemon > refuses a workspace it cannot pin before invoking the agent 667ms + ✓ tests/authored-parallel-agents.test.ts (8 tests) 9354ms + ✓ authored steps under local workers with capacity > runs more concurrent f.llm calls than the worker holds side by side, never more than its capacity 1160ms + ✓ authored steps under local workers with capacity > completes more concurrent f.agent calls than the worker holds: the overflow waits for a slot instead of parking 1545ms + ✓ authored steps under local workers with capacity > runs agents in distinct working directories side by side (the kernel carries cwd) 652ms + ✓ authored steps under local workers with capacity > serializes agents whose cwd is a symlink alias of the same directory 1046ms + ✓ authored steps under local workers with capacity > serializes agents whose cwd is a directory nested inside the other 1068ms + ✓ authored steps under local workers with capacity > never starts queued agents once the body has failed 1605ms + ✓ authored steps under local workers with capacity > never starts a queued agent when the agent holding the only slot fails 1625ms + ✓ authored steps under local workers with capacity > parks the overflow when the body is not told the capacity (the defect this closes) 652ms + ✓ tests/check-daemon-validation.test.ts (20 tests) 2815ms + ✓ real CLI refuses skew in auto and required modes 923ms + ✓ real CLI labels daemon and offline passes, including JSON 1355ms + ✓ real CLI refuses an invalid RELAYFLOWD_BIN in required mode 420ms + ✓ tests/human-live.test.ts (3 tests) 7344ms + ✓ f.human against a real daemon > parks with the question, refuses wrong answers, records one, and resumes to success 4367ms + ✓ f.human against a real daemon > a "no" is a value the body branches on: declined, exit 0, no effect 1797ms + ✓ f.human against a real daemon > refuses to answer a run the daemon does not know 1179ms + ✓ tests/check-worker-surface.test.ts (11 tests) 104ms + ✓ tests/authored-step-failed.test.ts (10 tests) 41ms +(node:52746) ExperimentalWarning: SQLite is an experimental feature and might change at any time +(Use `node --trace-warnings ...` to show where the warning was created) + ✓ tests/authored-status-detail.test.ts (20 tests) 137ms + ✓ tests/budget-preflight.test.ts (25 tests) 18ms + ✓ tests/cli-watch.test.ts (11 tests) 18517ms + ✓ flows check --watch > forwards required daemon validation through watch despite the offline test environment 846ms + ✓ flows check --watch > rechecks syntax errors, clears once, and returns the last refusal on Ctrl-C 1419ms + ✓ flows check --watch > streams JSON lines without ANSI, recovers after atomic saves, and exits zero after repair 1989ms + ✓ flows check --watch > coalesces 20 concurrent saves into at most two rechecks 1958ms + ✓ flows check --watch > watches transitive relative use imports, cycles, and nearest config changes 2705ms + ✓ flows check --watch > refreshes the import graph and notices missing imports being created 2593ms + ✓ flows check --watch > reloads authored TypeScript instead of reusing the first imported definition 1982ms + ✓ flows check --watch > detects a nearer config appearing and falls back after it is deleted 2017ms + ✓ flows check --watch > keeps watching after the target is deleted and recreated 2187ms + ✓ flows check --watch > queues changes during a slow check without overlapping checks 817ms + ✓ tests/authored-retried-child-resume-live.test.ts (2 tests) 1587ms + ✓ an authored step whose child run retried an attempt > resumes after a kill mid-step and reads the terminal completion, not the crashed one 1420ms + ✓ tests/provider-trigger-contract.test.ts (7 tests) 1239ms + ✓ provider trigger contract > accepts one subscription from each of five providers and refuses a bogus event on any of them 699ms + ✓ provider trigger contract > fails `flows check` before deployment and passes once the event is real 535ms + ✓ tests/work-package-consumer.test.ts (13 tests) 147ms + ✓ tests/shipped-source-flow-header-provenance.test.ts (1 test) 3082ms + ✓ shipped-source flow header provenance > fails closed for unsafe authored and declarative headers 3081ms + ✓ tests/helpers-fanout.test.ts (96 tests) 177ms +(node:53157) ExperimentalWarning: SQLite is an experimental feature and might change at any time +(Use `node --trace-warnings ...` to show where the warning was created) + ✓ tests/authored-step-graph-live.test.ts (1 test) 972ms + ✓ the authored step DAG through the live kernel > carries labels and predecessors on every index record and journal step, ids unchanged 971ms + ✓ tests/named-gate-journal.test.ts (5 tests) 2092ms + ✓ a lowered subprocess_gate journals the gate command's streams > persists what the gate printed before a timeout killed it 844ms + ✓ a gate on an agent step > journals the command's streams and surfaces them in the authored failure 871ms + ✓ tests/yaml-local-agent-live.test.ts (9 tests) 6855ms + ✓ YAML --local-agent through the built CLI and real daemon > runs with the checked step CLI and model and journals done 725ms + ✓ YAML --local-agent through the built CLI and real daemon > runs with the checked named CLI and model and journals done 676ms + ✓ YAML --local-agent through the built CLI and real daemon > runs with the checked flow CLI and model and journals done 701ms + ✓ YAML --local-agent through the built CLI and real daemon > runs with the checked project CLI and model and journals done 661ms + ✓ YAML --local-agent through the built CLI and real daemon > still parks without --local-agent 589ms + ✓ YAML --local-agent through the built CLI and real daemon > resumes a parked spec run with --local-agent instead of parking identically again 1072ms + ✓ YAML --local-agent through the built CLI and real daemon > parks a second time with a different message when the attached worker is not eligible 1035ms + ✓ YAML --local-agent through the built CLI and real daemon > reports the agent process failure 807ms + ✓ YAML --local-agent through the built CLI and real daemon > preserves declared workspace surfaces that the local worker cannot pin 588ms + ✓ tests/generate-triggers.test.ts (7 tests) 1225ms + ✓ discovers new adapters, preserves exact event names, and prefers adapter-local mappings 411ms + ✓ tests/local-agent-remedy.test.ts (16 tests) 16ms + ✓ tests/yaml-helpers.test.ts (34 tests) 73ms + ✓ tests/direct-run-failure.test.ts (12 tests) 20ms + ✓ tests/authored-agent-permissions.test.ts (27 tests) 956ms + ✓ tests/authored-detail-live.test.ts (3 tests) 4060ms + ✓ carries done("step_failed", { detail }) into the report, the journal and flows status 2153ms + ✓ reports a detail a caller sliced through an emoji, instead of hanging on it 668ms + ✓ leaves a one-argument done("step_failed") reporting exactly as it always did 1238ms + ✓ tests/live-event-activities.test.ts (2 tests) 402ms + ❯ tests/software-garden-babysitter-composition.test.ts (6 tests | 2 failed) 988ms + × canonical Software Garden + Babysitter composition > executes the pinned composition and preserves queued/duplicate replay receipts 396ms + → Hosted extension sandbox exited without a valid completion (exit 1): bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted + + × canonical Software Garden + Babysitter composition > propagates capability denial without a retry or fallback 500ms + → expected Error: Hosted extension sandbox exited wi… { code: '…' } to be Error: live babysit label is absent // Object.is equality + ✓ tests/worker-lease.test.ts (7 tests) 47ms + ✓ tests/worker-lease-lost.test.ts (17 tests) 18ms + ✓ tests/webhook-hardening.test.ts (11 tests) 79ms + ✓ tests/authored-probe-cache.test.ts (5 tests) 22230ms + ✓ authored run CLI probe cache > nine calls have no expired attempts at capacity 1 7637ms + ✓ authored run CLI probe cache > nine calls have no expired attempts at capacity 4 5526ms + ✓ authored run CLI probe cache > does not serialize nine starts behind nine probes, and probes again on a new run 7258ms + ✓ authored run CLI probe cache > caches probe failures while refusing all nine calls 441ms + ✓ authored run CLI probe cache > shares probe results across agent calls too 1367ms + ✓ tests/yaml-declared-streams-live.test.ts (8 tests) 5576ms + ✓ YAML declared streams through the built CLI and real daemon > runs with the checked step CLI and model and journals done 661ms + ✓ YAML declared streams through the built CLI and real daemon > runs with the checked named CLI and model and journals done 643ms + ✓ YAML declared streams through the built CLI and real daemon > runs with the checked flow CLI and model and journals done 682ms + ✓ YAML declared streams through the built CLI and real daemon > runs with the checked project CLI and model and journals done 661ms + ✓ YAML declared streams through the built CLI and real daemon > still parks without --local-agent 589ms + ✓ YAML declared streams through the built CLI and real daemon > reports the agent process failure 673ms + ✓ YAML declared streams through the built CLI and real daemon > resumes the journaled stream declarations after the input YAML changes 1092ms + ✓ YAML declared streams through the built CLI and real daemon > preserves declared workspace surfaces that the local worker cannot pin 575ms + ✓ tests/human-to.test.ts (8 tests) 16ms + ✓ tests/worker-lease-drift-live.test.ts (3 tests) 36342ms + ✓ worker lease under clock skew between worker and daemon > renews before the daemon deadline when the worker clock runs 75s behind the daemon 36112ms +(node:56287) ExperimentalWarning: SQLite is an experimental feature and might change at any time +(Use `node --trace-warnings ...` to show where the warning was created) + ✓ tests/plugin-loader.test.ts (9 tests) 193ms + ✓ tests/authored-dispatch-live.test.ts (2 tests) 2778ms + ✓ durable child flow dispatch > runs a declared child in the parent DAG and replays it without repeating effects 1895ms + ✓ durable child flow dispatch > lowers a named gate attached to the dispatch receipt 882ms + ✓ tests/deploy.test.ts (11 tests) 5694ms + ✓ flows deploy file buckets > publishes the full signed layout byte-for-byte and redeploys as a noop 896ms + ✓ flows deploy file buckets > answers --json with one object per outcome 905ms + ✓ flows deploy file buckets > reports a refusal as JSON under --json 413ms + ✓ flows deploy file buckets > refuses a missing local bundle before creating the bucket 447ms + ✓ flows deploy file buckets > refuses an unreachable bucket before copying 441ms + ✓ flows deploy file buckets > refuses an unwritable bucket 413ms + ✓ flows deploy file buckets > refuses local tampering of spec.canonical.json 416ms + ✓ flows deploy file buckets > refuses local tampering of identity.json 415ms + ✓ flows deploy file buckets > refuses asset bundles instead of using daemon-relative files 431ms + ✓ flows deploy file buckets > never labels a corrupt existing deployment as a noop 874ms + ✓ tests/bin.test.ts (12 tests) 6549ms + ✓ built flows binary > prints the installed SDK version for --version 426ms + ✓ built flows binary > prints the installed SDK version for -V 425ms + ✓ built flows binary > keeps version flags strict when extra arguments are supplied 412ms + ✓ built flows binary > prints the installed SDK version from the standalone binary for --version 1116ms + ✓ built flows binary > prints the installed SDK version from the standalone binary for -V 1105ms + ✓ built flows binary > refuses through a symlink to the built artifact 416ms + ✓ built flows binary > refuses through a symlinked directory component 429ms + ✓ built flows binary > classifies a signal-terminated auth probe as probe_failed 936ms + ✓ built flows binary > classifies an unavailable PATH resolver as probe_failed 436ms + ✓ built flows binary > does not describe a present non-executable CLI as missing 424ms + ✓ built flows binary > runs one auth probe for three steps sharing a flow CLI 420ms + ✓ tests/babysitter-catalog-export.test.ts (14 tests) 862ms + ✓ Babysitter catalog artifact export > CLI refuses an existing output and leaves no file on validation failure 696ms + ✓ tests/communication.test.ts (10 tests) 13ms + ✓ tests/communication-review.test.ts (6 tests) 338ms + ✓ tests/transport-evidence-bounds.test.ts (7 tests) 10ms + ✓ tests/step-lease.test.ts (36 tests) 66563ms + ✓ f.run leases against the live kernel > enforces 10000 ms for 'sleep 5; printf ok' 5138ms + ✓ f.run leases against the live kernel > enforces 40000 ms for 'sleep 31; printf ok' 31090ms + ✓ f.run leases against the live kernel > enforces 30000 ms for 'sleep 31; printf ok' 30113ms + ✓ tests/typed-output.test.ts (14 tests) 293ms + ✓ tests/agent-timeout-live.test.ts (3 tests) 5179ms + ✓ journals timeout, stops the process, runs a predicate gate and publishes under a budget header 1790ms + ✓ replays timeout after killing the root and daemon, without executing the agent again 2239ms + ✓ lets an author reject timeout through a predicate gate 1149ms + ✓ tests/effect-channel.test.ts (5 tests) 620ms + ✓ tests/worker-slots.test.ts (8 tests) 7ms + ✓ tests/json-schema-bound.test.ts (71 tests) 2403ms + ✓ JSON Schema termination bound > walks a deep schema with an explicit stack rather than recursion 1957ms + ✓ tests/communication-worker.test.ts (17 tests) 1709ms + ✓ tests/cloud-mirror-transport.test.ts (5 tests) 8ms + ✓ tests/mcp-lifecycle.test.ts (4 tests) 13ms + ✓ tests/shipped-source-models.test.ts (2 tests) 65858ms + ✓ first-party shipped source model pins > gives every TypeScript agent and LLM an explicit supported pair or a pinned named-agent declaration 65814ms + ✓ tests/model-selection.test.ts (10 tests) 22ms + ✓ tests/f-memory.test.ts (7 tests) 855ms + ✓ tests/relayflowd-path.test.ts (10 tests) 6ms + ✓ tests/agent-artifacts.test.ts (9 tests) 34ms + ✓ tests/authored-plugin-effect.test.ts (6 tests) 64ms + ✓ tests/agent-timeout-worker.test.ts (9 tests) 2226ms + ✓ stops claude at its declared limit, preserving work and stopping descendants 526ms + ✓ stops codex at its declared limit, preserving work and stopping descendants 516ms + ✓ stops wrapper at its declared limit, preserving work and stopping descendants 541ms + ✓ tests/hosted-hardening.test.ts (11 tests) 14ms + ✓ tests/local-dev-ux.test.ts (8 tests) 18ms + ✓ tests/authored-preflight.test.ts (5 tests) 246ms + ✓ tests/deterministic-llm.test.ts (5 tests) 51ms + ↓ tests/relay-cli-surface-live.test.ts (3 tests | 3 skipped) + ✓ tests/authored-declined.test.ts (13 tests) 54ms + ✓ tests/resume-failure.test.ts (2 tests) 5ms + ✓ tests/dependency-validation.test.ts (6 tests) 733ms + ✓ dependency validation > accepts a valid 10,000-step reverse chain through every direct public boundary 408ms + ✓ tests/worker-lease-lost-live.test.ts (3 tests) 1159ms + ✓ reports journal success after completion rejects with lease_conflict 444ms + ✓ reports journal success after completion rejects with run_terminal 340ms + ✓ reports journal success when a renewal rejects after completion landed 376ms + ✓ tests/authored-hooks.test.ts (5 tests) 5ms + ✓ tests/input-binding.test.ts (12 tests) 229ms + ✓ tests/yaml-helper-effect.test.ts (4 tests) 77ms + ✓ tests/cloud-mirror-ledger.test.ts (7 tests) 1153ms + ✓ the mirror ledger > keeps the newest entries when the count cap bites 1142ms + ✓ tests/scope-preflight.test.ts (6 tests) 9ms + ✓ tests/run-from-digest.test.ts (6 tests) 4657ms + ✓ flows run digest input > submits the sealed canonical spec through the normal journal path without checkout 470ms + ✓ flows run digest input > uses a verified cache hit even after the bucket is removed 434ms + ✓ flows run digest input > resolves deploy.bucket from flows.json and honors explicit override 1251ms + ✓ flows run digest input > refuses an unconfigured bucket 826ms + ✓ flows run digest input > refuses tampered spec.canonical.json before creating run data 844ms + ✓ flows run digest input > refuses tampered identity.json before creating run data 831ms + ✓ tests/build-gate.test.ts (3 tests) 1219ms + ✓ flows build gates on flows check green (#318) > refuses a flow with an unresolvable named-agent CLI and leaves no artifacts 408ms + ✓ flows build gates on flows check green (#318) > --json emits one CheckReport object on stdout on refusal, exits 2, no artifacts 401ms + ✓ flows build gates on flows check green (#318) > builds the bundle on success (regression: gate must not block valid flows) 409ms + ✓ tests/scope-compiler.test.ts (25 tests) 11ms + ✓ tests/worker-cli-abort.test.ts (3 tests) 3809ms + ✓ stops claude and its process group when lease ownership is lost 1386ms + ✓ stops wrapper.mjs and its process group when lease ownership is lost 1378ms + ✓ fails closed when abort cannot prove the process group is gone 1043ms + ✓ tests/hn-poller.test.ts (6 tests) 7ms + ✓ tests/plugin-add.test.ts (7 tests) 1166ms + ✓ typechecks the augmented verb and rejects unknown namespaces 872ms + ✓ tests/authored-step-failed-exit.test.ts (3 tests) 10ms + ✓ tests/retried-step-failure.test.ts (1 test) 620ms + ✓ a retried step failing differently through the live kernel > reports both attempts and says the evidence differs 619ms + ✓ tests/dir-watcher-poller.test.ts (6 tests) 5ms + ✓ tests/subscription-report.test.ts (8 tests) 5ms + ✓ tests/model-pricing.test.ts (10 tests) 6ms + ✓ tests/authored-use-loader.test.ts (6 tests) 1233ms + ✓ authored use graph loader > refuses two direct children with the same dispatch name 322ms + ✓ tests/yaml-helper-live.test.ts (1 test) 1047ms + ✓ runs compiled YAML helpers through the built CLI and kernel effect journal 1046ms + ✓ tests/provider-trigger-executor.test.ts (4 tests) 259ms + ✓ tests/journal-client-completion.test.ts (6 tests) 103ms + ✓ tests/transcript-tail-close.test.ts (2 tests) 1779ms + ✓ a stalled transcript-tail close > does not hold the spawn open past its bounded window 867ms + ✓ a stalled tail close beside a transcript that finished > still journals the transcript pointer 911ms + ✓ tests/wrapper-artifacts-cwd.test.ts (2 tests) 107ms + ✓ tests/hello-deterministic.test.ts (5 tests) 20ms + ✓ tests/worker-lease-sweep.test.ts (4 tests) 18ms + ✓ tests/transcript-exclusion-timeout.test.ts (1 test) 297ms + ✓ tests/cli-adapter.test.ts (4 tests) 5ms + ✓ tests/communication-mixed-resume.test.ts (1 test) 195ms + ✓ tests/work-package-validator.test.ts (7 tests) 6ms + ✓ tests/activity-preflight.test.ts (10 tests) 20ms + ✓ tests/authored-declined-live.test.ts (1 test) 1773ms + ✓ runs an input guard and resumes its completed declined root without repeated effects 1773ms + ✓ tests/cli-answer.test.ts (15 tests) 10ms + ✓ tests/bundle-preflight.test.ts (4 tests) 971ms + ✓ bundle execution preflight > ignores surrounding cache configuration on a verified cache hit 469ms + ✓ bundle execution preflight > uses the built alias for a nameless flow even in a digest-only cache directory 461ms + ✓ tests/agent-timeout-outcome.test.ts (6 tests) 17ms + ✓ tests/agent-relay-hardening.test.ts (12 tests) 13ms + ✓ tests/communication-preflight.test.ts (13 tests) 39ms + ↓ tests/real-cli-adapters.test.ts (3 tests | 3 skipped) + ✓ tests/memoization.test.ts (57 tests) 67ms + ✓ tests/shipped-source-call-array-forwarding.test.ts (1 test) 3077ms + ✓ shipped-source call-array forwarding > maps rest indexes and nested same-helper actuals for workers and flows 3076ms + ✓ tests/local-agent-environment.test.ts (8 tests) 7ms + ✓ tests/fs-descriptor.test.ts (1 test) 4ms + ✓ tests/runtime-dependencies.test.ts (1 test) 235ms + ✓ tests/parse-json-output.test.ts (7 tests) 4ms + ✓ tests/reported-cost.test.ts (4 tests) 3ms + ✓ tests/journal-client-subscriptions.test.ts (1 test) 7ms + ✓ tests/authored-model-probe-kinds.test.ts (2 tests) 177ms + ✓ tests/communication-environment-preflight.test.ts (6 tests) 5ms + ✓ tests/budget-authored-live.test.ts (2 tests) 245ms + ✓ tests/adapters/codex.test.ts (7 tests) 5ms + ✓ tests/slack-writeback.test.ts (1 test) 262ms + ✓ tests/authored-surface-authority.test.ts (2 tests) 19ms + ✓ tests/adapters/claude.test.ts (7 tests) 4ms + ✓ tests/worker-cli-cwd.test.ts (2 tests) 529ms + ✓ runAgentCli — cwd propagation (flows#357) > omits cwd when not provided (inherits parent cwd) 522ms + ✓ tests/shipped-source-parameter-provenance.test.ts (1 test) 1826ms + ✓ shipped-source parameter provenance > keeps caller-supplied identifier and destructured keys unknown 1825ms + ✓ tests/slack-block-kit.test.ts (5 tests) 15ms + ✓ tests/agent-timeout.test.ts (23 tests) 38ms + ✓ tests/communication-history.test.ts (1 test) 4ms + ✓ tests/resume-worker-lease.test.ts (2 tests) 4ms + ✓ tests/adapters/registry.test.ts (4 tests) 5ms + ✓ tests/direct-run-worker-lease.test.ts (2 tests) 7ms + ✓ tests/authored-declined-report.test.ts (6 tests) 7ms + ✓ tests/promise-ancestry.test.ts (2 tests) 286ms + ✓ tests/agent-cwd-validation.test.ts (2 tests) 427ms + ✓ declarative agent cwd > is refused by `flows check` on a YAML flow before anything runs 423ms + ✓ tests/communication-refusal.test.ts (1 test) 16ms + ✓ tests/bundle-transport.test.ts (20 tests) 4383ms + ✓ digest references > accepts and deploys the build output for hello 679ms + ✓ digest references > accepts and deploys the build output for Hello 664ms + ✓ digest references > accepts and deploys the build output for hello.world 860ms + ✓ digest references > accepts and deploys the build output for hello_world 834ms + ✓ digest references > accepts and deploys the build output for 123 635ms + ✓ digest references > accepts and deploys the build output for A_b.c-1 706ms + ✓ tests/cli-cloud-mirror-flag.test.ts (4 tests) 4ms + ✓ tests/catalog-plugins.test.ts (2 tests) 3ms + ✓ tests/check-command-cwd.test.ts (1 test) 14ms + ✓ tests/communication-lazy.test.ts (1 test) 5ms + ✓ tests/cli-progress-wait.test.ts (2 tests) 4ms + ↓ tests/run-digest-live.test.ts (1 test | 1 skipped) + ✓ tests/placement.test.ts (54 tests) 22ms + ✓ tests/preflight-run-cache.test.ts (1 test) 5ms + ✓ tests/canonical-tree.test.ts (1 test) 2ms + ✓ tests/local-agent-stream-selection.test.ts (1 test) 5ms + ✓ tests/run-digest.test.ts (4 tests) 2341ms + ✓ digest run configuration refusals > reports config_invalid before fetching or starting a run for {invalid json 712ms + ✓ digest run configuration refusals > reports config_invalid before fetching or starting a run for {"deploy":{}} 575ms + ✓ digest run configuration refusals > reports config_invalid before fetching or starting a run for {"deploy":{"bucket":123}} 534ms + ✓ digest run configuration refusals > reports config_invalid before fetching or starting a run for {"deploy":{"bucket":""}} 520ms + ✓ tests/communication-tools.test.ts (1 test) 88ms + ✓ tests/authored-admission.test.ts (2 tests) 4ms + ✓ tests/memory.test.ts (18 tests) 8ms + ✓ tests/worker-platform.test.ts (1 test) 3ms +$ /usr/local/share/nvm/versions/node/v25.6.0/bin/node scripts/pack-release.mjs surface /tmp/flows-cli-package-9jWQB5/tarballs +PACK_OK @relayflows/surface@2.0.42: package/dist/index.js, package/dist/index.d.ts, package/dist/runtime.js, package/dist/runtime.d.ts +$ /usr/local/share/nvm/versions/node/v25.6.0/bin/node scripts/pack-release.mjs sdk /tmp/flows-cli-package-9jWQB5/tarballs +PACK_OK @relayflows/sdk@2.0.42: package/dist/index.js, package/dist/index.d.ts, package/dist/cli.js +$ /usr/local/share/nvm/versions/node/v25.6.0/bin/node scripts/pack-release.mjs relayflows /tmp/flows-cli-package-9jWQB5/tarballs +PACK_OK relayflows@2.0.42: package/bin/flows.js +$ npm install --ignore-scripts --omit=optional --no-audit --no-fund /tmp/flows-cli-package-9jWQB5/tarballs/relayflows-2.0.42.tgz /tmp/flows-cli-package-9jWQB5/tarballs/relayflows-sdk-2.0.42.tgz /tmp/flows-cli-package-9jWQB5/tarballs/relayflows-surface-2.0.42.tgz + +added 150 packages in 6s +$ /tmp/flows-cli-package-9jWQB5/local/node_modules/.bin/flows check examples/dependency-upgrade-bot.flow.ts +REQUIRES claude (agent "upgrader") +CHECK PASSED examples/dependency-upgrade-bot.flow.ts [local only: disabled, so daemon acceptance is unproven] +$ /usr/local/share/nvm/versions/node/v25.6.0/bin/node /tmp/flows-cli-package-9jWQB5/local/node_modules/relayflows/bin/flows.js check examples/dependency-upgrade-bot.flow.ts +REQUIRES claude (agent "upgrader") +CHECK PASSED examples/dependency-upgrade-bot.flow.ts [local only: disabled, so daemon acceptance is unproven] +$ npm install -g --prefix /tmp/flows-cli-package-9jWQB5/prefix --ignore-scripts --omit=optional --no-audit --no-fund /tmp/flows-cli-package-9jWQB5/tarballs/relayflows-2.0.42.tgz /tmp/flows-cli-package-9jWQB5/tarballs/relayflows-sdk-2.0.42.tgz /tmp/flows-cli-package-9jWQB5/tarballs/relayflows-surface-2.0.42.tgz + +added 294 packages in 4s +$ npm install --ignore-scripts --omit=optional --no-audit --no-fund /tmp/flows-cli-package-9jWQB5/tarballs/relayflows-surface-2.0.42.tgz + +added 140 packages in 2s +$ /tmp/flows-cli-package-9jWQB5/prefix/bin/flows check examples/dependency-upgrade-bot.flow.ts +REQUIRES claude (agent "upgrader") +CHECK PASSED examples/dependency-upgrade-bot.flow.ts [local only: disabled, so daemon acceptance is unproven] +$ /usr/local/share/nvm/versions/node/v25.6.0/bin/node /tmp/flows-cli-package-9jWQB5/prefix/lib/node_modules/relayflows/bin/flows.js check examples/dependency-upgrade-bot.flow.ts +REQUIRES claude (agent "upgrader") +CHECK PASSED examples/dependency-upgrade-bot.flow.ts [local only: disabled, so daemon acceptance is unproven] +CLI_PACKAGE_OK: local and global installs + ✓ tests/cli-package-gate.test.ts (1 test) 20992ms + ✓ checks authored flows with locally and globally installed release tarballs 20991ms +node:internal/modules/run_main:107 + triggerUncaughtException( + ^ + +AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + +2 !== 4 + + at expectPark (file:///home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/tests/fixtures/event-await-cli-probe.mjs:58:10) + at file:///home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/tests/fixtures/event-await-cli-probe.mjs:63:3 { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: 2, + expected: 4, + operator: 'strictEqual', + diff: 'simple' +} + +Node.js v25.6.0 + ❯ tests/event-await-cli.test.ts (1 test | 1 failed) 588ms + × parks, restarts, and replays two event wakes through the actual CLI 587ms + → Command failed: /usr/local/share/nvm/versions/node/v25.6.0/bin/node /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/tests/fixtures/event-await-cli-probe.mjs /home/daytona/.relayflow-v2-supervisor/durable/repository +node:internal/modules/run_main:107 + triggerUncaughtException( + ^ + +AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + +2 !== 4 + + at expectPark (file:///home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/tests/fixtures/event-await-cli-probe.mjs:58:10) + at file:///home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/tests/fixtures/event-await-cli-probe.mjs:63:3 { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: 2, + expected: 4, + operator: 'strictEqual', + diff: 'simple' +} + +Node.js v25.6.0 + + ✓ tests/authored-parallel-llm.test.ts (6 tests) 117019ms + ✓ parallel llm capacity 1 > deduplicates concurrent preflight probes 2840ms + ✓ parallel llm capacity 1 > completes nine calls without expired child leases during slow preflight 8746ms + ✓ parallel llm capacity 1 > keeps the durable root lease alive across two cold models 48478ms + ✓ parallel llm capacity 4 > deduplicates concurrent preflight probes 1652ms + ✓ parallel llm capacity 4 > completes nine calls without expired child leases during slow preflight 8096ms + ✓ parallel llm capacity 4 > keeps the durable root lease alive across two cold models 47205ms + ✓ tests/shipped-source-worker-invocations.test.ts (1 test) 654714ms + ✓ shipped-source worker invocation resolution > fails closed across direct, extracted, bound, mutable, and escaped callables 654713ms + +⎯⎯⎯⎯⎯⎯ Failed Suites 1 ⎯⎯⎯⎯⎯⎯⎯ + + FAIL tests/authored-node-runtime.test.ts [ tests/authored-node-runtime.test.ts ] +AssertionError: expected '1.3.6' to be '1.4.0' // Object.is equality + +Expected: "1.4.0" +Received: "1.3.6" + + ❯ tests/authored-node-runtime.test.ts:18:77 + 16| + 17| beforeAll(() => { + 18| expect(spawnSync(bun, ['--version'], { encoding: 'utf8' }).stdout.tr… + | ^ + 19| expect(existsSync(daemon), 'build the current kernel or set RELAYFLO… + 20| stage = mkdtempSync(join(tmpdir(), 'authored-standalone-build-')); + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/46]⎯ + +⎯⎯⎯⎯⎯⎯ Failed Tests 45 ⎯⎯⎯⎯⎯⎯⎯ + + FAIL tests/babysitter-native-extension.test.ts > native Babysitter extension > runs the exact published 2.0.26 native bytes in the isolated capability path +AssertionError: promise rejected "Error: Hosted extension sandbox exited wi… { code: '…' }" instead of resolving + ❯ tests/babysitter-native-extension.test.ts:153:7 + 151| return { receiptId: `bst_${'a'.repeat(64)}`, status: 'queued' … + 152| } }, + 153| })).resolves.toEqual({ completionReason: 'success', capabilityCall… + | ^ + 154| expect(calls).toEqual([{ delivery: { + 155| deliveryId: 'gh-delivery-7', provider: 'github', eventType: 'pul… + +Caused by: Error: Hosted extension sandbox exited without a valid completion (exit 1): bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted + + ❯ refuse src/hosted-extension-protocol.ts:135:21 + ❯ ChildProcess. src/hosted-extension-protocol.ts:234:21 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ +Serialized Error: { code: 'plugin_unsupported' } +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/46]⎯ + + FAIL tests/event-await-cli.test.ts > parks, restarts, and replays two event wakes through the actual CLI +Error: Command failed: /usr/local/share/nvm/versions/node/v25.6.0/bin/node /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/tests/fixtures/event-await-cli-probe.mjs /home/daytona/.relayflow-v2-supervisor/durable/repository +node:internal/modules/run_main:107 + triggerUncaughtException( + ^ + +AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + +2 !== 4 + + at expectPark (file:///home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/tests/fixtures/event-await-cli-probe.mjs:58:10) + at file:///home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/tests/fixtures/event-await-cli-probe.mjs:63:3 { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: 2, + expected: 4, + operator: 'strictEqual', + diff: 'simple' +} + +Node.js v25.6.0 + + ❯ expectPark tests/fixtures/event-await-cli-probe.mjs:58:10 + ❯ tests/event-await-cli.test.ts:9:18 + 7| + 8| it('parks, restarts, and replays two event wakes through the actual CL… + 9| const output = execFileSync(process.execPath, [resolve(here, 'fixtur… + | ^ + 10| resolve(here, '../../..')], { encoding: 'utf8', timeout: 90_000 }); + 11| expect(output).toContain('E2E_PASS:'); + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/46]⎯ + + FAIL tests/hosted-extension-isolation.test.ts > hosted extension capability isolation > executes the exact capability-only handler for a queued receipt + FAIL tests/hosted-extension-isolation.test.ts > hosted extension capability isolation > executes the exact capability-only handler for a duplicate receipt + FAIL tests/hosted-extension-protocol.test.ts > hosted extension hostile protocol > uses captured JSON intrinsics for the complete parent boundary + FAIL tests/software-garden-babysitter-composition.test.ts > canonical Software Garden + Babysitter composition > executes the pinned composition and preserves queued/duplicate replay receipts +Error: Hosted extension sandbox exited without a valid completion (exit 1): bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted + + ❯ refuse src/hosted-extension-protocol.ts:135:21 + 133| : new PluginError('plugin_unsupported', 'Hosted capability rejec… + 134| const refuse = (message: string) => { + 135| const error = new PluginError('plugin_unsupported', message); + | ^ + 136| CHILD_PROCESS_KILL(child, 'SIGKILL'); + 137| if (capabilityState === 'pending') { + ❯ ChildProcess. src/hosted-extension-protocol.ts:234:21 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/46]⎯ + + FAIL tests/hosted-extension-isolation.test.ts > hosted extension capability isolation > launches through the captured process primitive after builtin export synchronization +AssertionError: promise rejected "Error: Hosted extension sandbox exited wi… { code: '…' }" instead of resolving + ❯ tests/hosted-extension-isolation.test.ts:283:11 + 281| input: descriptor(), + 282| babysitterTurn: { queue: async () => ({ receiptId: 'receipt-… + 283| })).resolves.toEqual({ completionReason: 'success', capability… + | ^ + 284| } finally { + 285| process.execPath = originalExecPath; + +Caused by: Error: Hosted extension sandbox exited without a valid completion (exit 1): bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted + + ❯ refuse src/hosted-extension-protocol.ts:135:21 + ❯ ChildProcess. src/hosted-extension-protocol.ts:234:21 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ +Serialized Error: { code: 'plugin_unsupported' } +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/46]⎯ + + FAIL tests/hosted-extension-isolation.test.ts > hosted extension capability isolation > ignores inherited launcher overrides and decodes manifests with the captured Buffer intrinsic +AssertionError: promise rejected "Error: Hosted extension sandbox exited wi… { code: '…' }" instead of resolving + ❯ tests/hosted-extension-isolation.test.ts:373:11 + 371| input: descriptor('delivery-options'), + 372| babysitterTurn: { queue: async () => ({ receiptId: 'receipt-… + 373| })).resolves.toEqual({ completionReason: 'success', capability… + | ^ + 374| } finally { + 375| Buffer.prototype.toString = bufferToString; + +Caused by: Error: Hosted extension sandbox exited without a valid completion (exit 1): bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted + + ❯ refuse src/hosted-extension-protocol.ts:135:21 + ❯ ChildProcess. src/hosted-extension-protocol.ts:234:21 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ +Serialized Error: { code: 'plugin_unsupported' } +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/46]⎯ + + FAIL tests/hosted-extension-isolation.test.ts > hosted extension capability isolation > streams verified bytes when the live store is replaced and no writable staging path exists +AssertionError: promise rejected "Error: Hosted extension sandbox exited wi… { code: '…' }" instead of resolving + ❯ tests/hosted-extension-isolation.test.ts:431:7 + 429| return { receiptId: 'receipt-1', status: 'queued' }; + 430| } }, + 431| })).resolves.toEqual({ completionReason: 'success', capabilityCall… + | ^ + 432| expect(calls).toBe(1); + 433| expect(readFileSync(join(installed.directory, 'babysitter.flow.ts'… + +Caused by: Error: Hosted extension sandbox exited without a valid completion (exit 1): bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted + + ❯ refuse src/hosted-extension-protocol.ts:135:21 + ❯ ChildProcess. src/hosted-extension-protocol.ts:234:21 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ +Serialized Error: { code: 'plugin_unsupported' } +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[7/46]⎯ + + FAIL tests/hosted-extension-isolation.test.ts > hosted extension capability isolation > mounts pinned private Surface bytes when the live package changes before launch +AssertionError: promise rejected "Error: Hosted extension sandbox exited wi… { code: '…' }" instead of resolving + ❯ tests/hosted-extension-isolation.test.ts:456:7 + 454| return { receiptId: 'receipt-1', status: 'queued' }; + 455| } }, + 456| })).resolves.toEqual({ completionReason: 'success', capabilityCall… + | ^ + 457| expect(calls).toBe(1); + 458| expect(readFileSync(join(surfaceRoot, 'dist/flow.js'), 'utf8')).to… + +Caused by: Error: Hosted extension sandbox exited without a valid completion (exit 1): bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted + + ❯ refuse src/hosted-extension-protocol.ts:135:21 + ❯ ChildProcess. src/hosted-extension-protocol.ts:234:21 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ +Serialized Error: { code: 'plugin_unsupported' } +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[8/46]⎯ + + FAIL tests/hosted-extension-isolation.test.ts > hosted extension capability isolation > shields verified Surface files before async settlement +AssertionError: promise rejected "Error: Hosted extension sandbox exited wi… { code: '…' }" instead of resolving + ❯ tests/hosted-extension-isolation.test.ts:532:9 + 530| surfaceRoot, + 531| babysitterTurn: { queue: async () => ({ receiptId: 'receipt-1'… + 532| })).resolves.toEqual({ completionReason: 'success', capabilityCa… + | ^ + 533| } finally { + 534| if (previous === undefined) delete (Array.prototype as { then?: … + +Caused by: Error: Hosted extension sandbox exited without a valid completion (exit 1): bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted + + ❯ refuse src/hosted-extension-protocol.ts:135:21 + ❯ ChildProcess. src/hosted-extension-protocol.ts:234:21 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ +Serialized Error: { code: 'plugin_unsupported' } +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[9/46]⎯ + + FAIL tests/hosted-extension-isolation.test.ts > hosted extension capability isolation > preserves a typed host refusal while disclosing only a fixed marker to the child +AssertionError: expected Error: Hosted extension sandbox exited wi… { code: '…' } to be Error: private Cloud policy detail { code: '…' } // Object.is equality + +- Expected ++ Received + +- [Error: private Cloud policy detail] ++ [Error: Hosted extension sandbox exited without a valid completion (exit 1): bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted ++ ] + + ❯ tests/hosted-extension-isolation.test.ts:560:5 + 558| provider: 'github', eventType: 'pull_request.labeled', deliveryI… + 559| }); + 560| await expect(runVerifiedNativeExtensionSandbox({ + | ^ + 561| artifact: await artifact(source), manifest: validateFlowExtensio… + 562| babysitterTurn: { queue: async () => { throw refusal; } }, + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[10/46]⎯ + + FAIL tests/hosted-extension-isolation.test.ts > hosted extension capability isolation > denies ambient credentials, host files, writes, network, subprocesses, and undeclared context verbs + FAIL tests/hosted-extension-isolation.test.ts > hosted extension capability isolation > constructs adapter authority with the captured freeze intrinsic +Error: Hosted extension sandbox exited without a valid completion (exit 1): bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted + + ❯ refuse src/hosted-extension-protocol.ts:135:21 + 133| : new PluginError('plugin_unsupported', 'Hosted capability rejec… + 134| const refuse = (message: string) => { + 135| const error = new PluginError('plugin_unsupported', message); + | ^ + 136| CHILD_PROCESS_KILL(child, 'SIGKILL'); + 137| if (capabilityState === 'pending') { + ❯ ChildProcess. src/hosted-extension-protocol.ts:234:21 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[11/46]⎯ + + FAIL tests/hosted-extension-isolation.test.ts > hosted extension capability isolation > enforces OS address-space and data bounds on native Buffer allocation +AssertionError: expected Error: Hosted extension sandbox exited wi… { code: '…' } to match object { code: 'plugin_unsupported', …(1) } + +- Expected ++ Received + +- Object { ++ PluginError { + "code": "plugin_unsupported", +- "message": StringMatching /(?:Failed to allocate memory|Array buffer allocation failed)/u, + } + + ❯ tests/hosted-extension-isolation.test.ts:646:5 + 644| }); + 645| let calls = 0; + 646| await expect(runVerifiedNativeExtensionSandbox({ + | ^ + 647| artifact: installed, + 648| manifest: validateFlowExtensionManifest(manifest()), + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[12/46]⎯ + + FAIL tests/hosted-extension-isolation.test.ts > hosted extension capability isolation > blocks extra handler fields and authority-bearing receipt fields at the parent port +AssertionError: expected Error: Hosted extension sandbox exited wi… { code: '…' } to match object { code: 'plugin_event_unroutable' } + +- Expected ++ Received + +- Object { +- "code": "plugin_event_unroutable", ++ PluginError { ++ "code": "plugin_unsupported", + } + + ❯ tests/hosted-extension-isolation.test.ts:675:5 + 673| }); + 674| let calls = 0; + 675| await expect(runVerifiedNativeExtensionSandbox({ + | ^ + 676| artifact: await artifact(source), manifest: validateFlowExtensio… + 677| babysitterTurn: { queue: async () => { calls += 1; return { rece… + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[13/46]⎯ + + FAIL tests/hosted-extension-isolation.test.ts > hosted extension capability isolation > writes the Surface manifest and protocol without inherited toJSON behavior +AssertionError: promise rejected "Error: Hosted extension sandbox exited wi… { code: '…' }" instead of resolving + ❯ tests/hosted-extension-isolation.test.ts:788:11 + 786| babysitterTurn: { queue: async () => ({ receiptId: 'receipt-… + 787| timeoutMs: 3_000, + 788| })).resolves.toEqual({ completionReason: 'success', capability… + | ^ + 789| } finally { + 790| if (previous === undefined) delete (Object.prototype as { toJS… + +Caused by: Error: Hosted extension sandbox exited without a valid completion (exit 1): bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted + + ❯ refuse src/hosted-extension-protocol.ts:135:21 + ❯ ChildProcess. src/hosted-extension-protocol.ts:234:21 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ +Serialized Error: { code: 'plugin_unsupported' } +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[14/46]⎯ + + FAIL tests/hosted-extension-protocol.test.ts > hosted extension hostile protocol > rejects an import-time different PR frame with zero adapter calls + FAIL tests/hosted-extension-protocol.test.ts > hosted extension hostile protocol > rejects an import-time different delivery frame with zero adapter calls + FAIL tests/hosted-extension-protocol.test.ts > hosted extension hostile protocol > rejects an import-time different event frame with zero adapter calls +AssertionError: expected Error: Hosted extension sandbox exited wi… { code: '…' } to match object { code: 'plugin_event_unroutable' } + +- Expected ++ Received + +- Object { +- "code": "plugin_event_unroutable", ++ PluginError { ++ "code": "plugin_unsupported", + } + + ❯ tests/hosted-extension-protocol.test.ts:447:5 + 445| ])('rejects an import-time %s frame with zero adapter calls', async … + 446| let calls = 0; + 447| await expect(runVerifiedNativeExtensionSandbox({ + | ^ + 448| artifact: await artifact(hostileImport([frame, { type: 'error', … + 449| manifest: validateFlowExtensionManifest(manifest()), dispatch: d… + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[15/46]⎯ + + FAIL tests/hosted-extension-protocol.test.ts > hosted extension hostile protocol > rejects two forged calls after the authoritative first outcome settles + FAIL tests/hosted-extension-protocol.test.ts > hosted extension hostile protocol > waits for a pending adapter to reject after a forged child error + FAIL tests/hosted-extension-protocol.test.ts > hosted extension hostile protocol > waits for a pending adapter to resolve after a forged child error + FAIL tests/hosted-extension-protocol.test.ts > hosted extension hostile protocol > returns a typed adapter rejection even when the hostile child hangs +Error: hostile child did not invoke the adapter + ❯ Timeout._onTimeout tests/hosted-extension-protocol.test.ts:135:45 + 133| async function waitForInvocation(invoked: Promise): Promise((resolve, reject) => { + 135| const timeout = setTimeout(() => reject(new Error('hostile child d… + | ^ + 136| void invoked.then(() => { clearTimeout(timeout); resolve(); }, rej… + 137| }); + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[16/46]⎯ + + FAIL tests/live-kernel.test.ts > built flows CLI against live relayflowd > runs hn-monitor analyze-story end-to-end via a stub agent CLI (gate 2 clause 2 demo) +AssertionError: expected { …(12) } to match object { output: { …(3) }, …(1) } +(22 matching properties omitted from actual) + +- Expected ++ Received + + Object { +- "output": Object { +- "reasoning": "stub agent runtime — deterministic output for gate-2 clause-2 demo", +- "relevance_score": 5, +- "story_title": "stub", +- }, ++ "output": null, + "verification": Object { +- "gate": "json_schema", +- "verdict": "pass", ++ "gate": "execution", ++ "verdict": "fail", + }, + } + + ❯ tests/live-kernel.test.ts:657:36 + 655| && (entry as { step_id?: string }).step_id === 'analyze-story', + 656| ) as { payload: { output: unknown; verification: unknown } } | und… + 657| expect(stepCompleted?.payload).toMatchObject({ + | ^ + 658| output: { + 659| story_title: 'stub', + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[17/46]⎯ + + FAIL tests/live-kernel.test.ts > built flows CLI against live relayflowd > hn-monitor analyze-story FAILS verification when the CLI omits required schema fields +AssertionError: expected { …(12) } to match object { …(3) } +(21 matching properties omitted from actual) + +- Expected ++ Received + + Object { +- "completionReason": "retries_exhausted", ++ "completionReason": "worker_error", + "output": null, + "verification": Object { +- "gate": "json_schema", ++ "gate": "execution", + "verdict": "fail", + }, + } + + ❯ tests/live-kernel.test.ts:752:36 + 750| // its verification record names the json_schema rejection. The re… + 751| // parsed value is nulled before the completion is persisted. + 752| expect(stepCompleted?.payload).toMatchObject({ + | ^ + 753| completionReason: 'retries_exhausted', + 754| output: null, + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[18/46]⎯ + + FAIL tests/live-kernel.test.ts > built flows CLI against live relayflowd > agent step preserves the CliResult wrapper as output when the CLI emits non-JSON text +AssertionError: expected null not to be null + ❯ tests/live-kernel.test.ts:823:24 + 821| // here (parseJsonOutput returned null on non-JSON stdout) and + 822| // these assertions would all fail. + 823| expect(output).not.toBeNull(); + | ^ + 824| expect(output.exit_code).toBe(0); + 825| expect(output.stdout_tail).toContain('looked at the story'); + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[19/46]⎯ + + FAIL tests/live-kernel.test.ts > built flows CLI against live relayflowd > AgentWorker exposes wake_context to the CLI via RELAYFLOW_WAKE_CONTEXT env var (real analyzer prerequisite) +TypeError: Cannot read properties of null (reading 'story_title') + ❯ tests/live-kernel.test.ts:891:42 + 889| ) as { payload: { output: { story_title: string; reasoning: string… + 890| expect(stepCompleted).toBeDefined(); + 891| expect(stepCompleted!.payload.output.story_title).toBe(`echoed:${s… + | ^ + 892| expect(stepCompleted!.payload.output.reasoning).toContain(String(s… + 893| + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[20/46]⎯ + + FAIL tests/live-kernel.test.ts > built flows CLI against live relayflowd > AgentWorker leaves RELAYFLOW_WAKE_CONTEXT UNSET when the run has no wake_context (undefined-vs-null pin) +TypeError: Cannot read properties of null (reading 'env_present') + ❯ tests/live-kernel.test.ts:958:38 + 956| ) as { payload: { output: { env_present: boolean } } } | undefined; + 957| expect(completed).toBeDefined(); + 958| expect(completed!.payload.output.env_present).toBe(false); + | ^ + 959| + 960| delete process.env.RELAYFLOW_WAKE_CONTEXT; + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[21/46]⎯ + + FAIL tests/live-kernel.test.ts > built flows CLI against live relayflowd > AgentWorker leaves RELAYFLOW_MODEL UNSET when the step declares no model +TypeError: Cannot read properties of null (reading 'story_title') + ❯ tests/live-kernel.test.ts:1194:38 + 1192| expect(completed).toBeDefined(); + 1193| // UNSET, not EMPTY and not the leaked parent value. + 1194| expect(completed!.payload.output.story_title).toBe('model:UNSET'); + | ^ + 1195| + 1196| delete process.env.RELAYFLOW_MODEL; + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[22/46]⎯ + + FAIL tests/live-kernel.test.ts > built flows CLI against live relayflowd > hn-monitor analyze-story reaches done through the real Claude analyzer CLI +Error: LIVE_ANALYZER_UNAVAILABLE: "/home/daytona/.relayflow-v2-supervisor/durable/repository/testdata/preflight/analyze-story-claude-cli" does not identify as relayflows-agent-cli-v1 — failing because gate-2 acceptance requires the real analyzer to execute. Set RELAYFLOWS_ALLOW_ANALYZER_SKIP=1 only if this run is not gate evidence. + ❯ tests/live-kernel.test.ts:1223:15 + 1221| const notice = `LIVE_ANALYZER_UNAVAILABLE: ${readiness.detail}`; + 1222| if (process.env['RELAYFLOWS_ALLOW_ANALYZER_SKIP'] !== '1') { + 1223| throw new Error( + | ^ + 1224| `${notice} — failing because gate-2 acceptance requires the … + 1225| + 'Set RELAYFLOWS_ALLOW_ANALYZER_SKIP=1 only if this run is … + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[23/46]⎯ + + FAIL tests/live-kernel.test.ts > a relayflow can be scheduled: tick source against live relayflowd > a tick spawns a real run whose step reports the SCHEDULED instant +AssertionError: expected null to deeply equal { schedule_id: 'heartbeat-1m', …(3) } + +- Expected: +Object { + "lag_ms": 43000, + "schedule_id": "heartbeat-1m", + "scheduled_for_ms": 1764000000000, + "slot": 29400000, +} + ++ Received: +null + + ❯ tests/live-kernel.test.ts:1739:39 + 1737| // The bound: the run reports the grid instant and its own lag, so… + 1738| // backfilled run can tell it is running for a slot from the past. + 1739| expect(completed!.payload.output).toEqual({ + | ^ + 1740| schedule_id: 'heartbeat-1m', + 1741| slot: 29_400_000, + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[24/46]⎯ + + FAIL tests/plugin-extension.test.ts > flows add > resolves a branch to its commit, materializes, and records flows.json plus the lockfile +AssertionError: expected 2 to be +0 // Object.is equality + +- Expected ++ Received + +- 0 ++ 2 + + ❯ tests/plugin-extension.test.ts:82:168 + 80| it('resolves a branch to its commit, materializes, and records flows… + 81| const gh = github(); const p = project({ cli: 'claude' }); + 82| expect(await addPlugin('github:AgentWorkforce/flows@feat/babysitte… + | ^ + 83| const config = JSON.parse(readFileSync(join(p.cwd, 'flows.json'), … + 84| expect(config).toEqual({ cli: 'claude', plugins: [REF] }); + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[25/46]⎯ + + FAIL tests/plugin-extension.test.ts > flows add > keeps the digest stable across identical installs and distinct across content changes +Error: ENOSPC: no space left on device, mkdtemp '/tmp/plugin-ext-XXXXXX' + ❯ project tests/plugin-extension.test.ts:32:15 + 30| } + 31| function project(config: unknown = {}) { + 32| const cwd = mkdtempSync(join(tmpdir(), 'plugin-ext-')); dirs.push(cw… + | ^ + 33| writeFileSync(join(cwd, 'flows.json'), JSON.stringify(config)); + 34| const messages: string[] = []; + ❯ tests/plugin-extension.test.ts:107:36 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[26/46]⎯ + + FAIL tests/plugin-extension.test.ts > flows add > recovers both crash points in the flows.json and lock transaction +Error: ENOSPC: no space left on device, mkdtemp '/tmp/plugin-ext-XXXXXX' + ❯ project tests/plugin-extension.test.ts:32:15 + 30| } + 31| function project(config: unknown = {}) { + 32| const cwd = mkdtempSync(join(tmpdir(), 'plugin-ext-')); dirs.push(cw… + | ^ + 33| writeFileSync(join(cwd, 'flows.json'), JSON.stringify(config)); + 34| const messages: string[] = []; + ❯ tests/plugin-extension.test.ts:119:17 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[27/46]⎯ + + FAIL tests/plugin-extension.test.ts > flows add > refuses github:AgentWorkforce/flows@nope#examples/babysitter with plugin_source_unresolved and writes nothing + FAIL tests/plugin-extension.test.ts > flows add > refuses github:AgentWorkforce/flows@main#examples/babysitter with plugin_source_unresolved and writes nothing + FAIL tests/plugin-extension.test.ts > flows add > refuses github:AgentWorkforce/flows@feat/babysitter-v2#examples with plugin_manifest_missing and writes nothing + FAIL tests/plugin-extension.test.ts > flows add > refuses github:AgentWorkforce/flows@bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb#examples/babysitter with plugin_source_unresolved and writes nothing + FAIL tests/plugin-extension.test.ts > flows add > refuses github:AgentWorkforce/flows@feat/babysitter-v2#../x with plugin_source_invalid and writes nothing +Error: ENOSPC: no space left on device, mkdtemp '/tmp/plugin-ext-XXXXXX' + ❯ project tests/plugin-extension.test.ts:32:15 + 30| } + 31| function project(config: unknown = {}) { + 32| const cwd = mkdtempSync(join(tmpdir(), 'plugin-ext-')); dirs.push(cw… + | ^ + 33| writeFileSync(join(cwd, 'flows.json'), JSON.stringify(config)); + 34| const messages: string[] = []; + ❯ tests/plugin-extension.test.ts:153:36 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[28/46]⎯ + + FAIL tests/plugin-extension.test.ts > flows add > refuses a plugin whose compat excludes this runtime +Error: ENOSPC: no space left on device, mkdtemp '/tmp/plugin-ext-XXXXXX' + ❯ project tests/plugin-extension.test.ts:32:15 + 30| } + 31| function project(config: unknown = {}) { + 32| const cwd = mkdtempSync(join(tmpdir(), 'plugin-ext-')); dirs.push(cw… + | ^ + 33| writeFileSync(join(cwd, 'flows.json'), JSON.stringify(config)); + 34| const messages: string[] = []; + ❯ tests/plugin-extension.test.ts:161:36 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[29/46]⎯ + + FAIL tests/plugin-extension.test.ts > flows add > refuses a manifest whose declared source is not where it was fetched from +Error: ENOSPC: no space left on device, mkdtemp '/tmp/plugin-ext-XXXXXX' + ❯ project tests/plugin-extension.test.ts:32:15 + 30| } + 31| function project(config: unknown = {}) { + 32| const cwd = mkdtempSync(join(tmpdir(), 'plugin-ext-')); dirs.push(cw… + | ^ + 33| writeFileSync(join(cwd, 'flows.json'), JSON.stringify(config)); + 34| const messages: string[] = []; + ❯ tests/plugin-extension.test.ts:167:15 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[30/46]⎯ + + FAIL tests/plugin-extension.test.ts > flows add > refuses a second source under an already-installed name +Error: ENOSPC: no space left on device, mkdtemp '/tmp/plugin-ext-XXXXXX' + ❯ project tests/plugin-extension.test.ts:32:15 + 30| } + 31| function project(config: unknown = {}) { + 32| const cwd = mkdtempSync(join(tmpdir(), 'plugin-ext-')); dirs.push(cw… + | ^ + 33| writeFileSync(join(cwd, 'flows.json'), JSON.stringify(config)); + 34| const messages: string[] = []; + ❯ tests/plugin-extension.test.ts:172:36 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[31/46]⎯ + + FAIL tests/relay-cli-surface.test.ts > relay-cli surface: drift between `commands` and `run` > carries every declared flag on an invocation that parses +AssertionError: expected [ Array(2) ] to deeply equal [] + +- Expected ++ Received + +- Array [] ++ Array [ ++ "flows check --against-daemon", ++ "flows check --no-daemon-check", ++ ] + + ❯ tests/relay-cli-surface.test.ts:383:23 + 381| } + 382| + 383| expect(uncarried).toEqual([]); + | ^ + 384| }); + 385| + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[32/46]⎯ + + FAIL tests/software-garden-babysitter-composition.test.ts > canonical Software Garden + Babysitter composition > propagates capability denial without a retry or fallback +AssertionError: expected Error: Hosted extension sandbox exited wi… { code: '…' } to be Error: live babysit label is absent // Object.is equality + +- Expected ++ Received + +- [Error: live babysit label is absent] ++ [Error: Hosted extension sandbox exited without a valid completion (exit 1): bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted ++ ] + + ❯ tests/software-garden-babysitter-composition.test.ts:139:7 + 137| const refusal = new Error('live babysit label is absent'); + 138| let calls = 0; + 139| await expect(runHostedSoftwareGardenBabysitter({ + | ^ + 140| flowPath: installed.flowPath, + 141| dispatch: dispatch(), + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[33/46]⎯ + +⎯⎯⎯⎯⎯⎯ Unhandled Errors ⎯⎯⎯⎯⎯⎯ + +Vitest caught 1 unhandled error during the test run. +This might cause false positive tests. Resolve unhandled errors to make sure your tests are not affected. + +⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯ +Error: Hosted extension sandbox exited without a valid completion (exit 1): bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted + + ❯ refuse src/hosted-extension-protocol.ts:135:21 + 133| : new PluginError('plugin_unsupported', 'Hosted capability rejec… + 134| const refuse = (message: string) => { + 135| const error = new PluginError('plugin_unsupported', message); + | ^ + 136| CHILD_PROCESS_KILL(child, 'SIGKILL'); + 137| if (capabilityState === 'pending') { + ❯ ChildProcess. src/hosted-extension-protocol.ts:234:21 + ❯ ChildProcess.emit node:events:520:22 + ❯ maybeClose node:internal/child_process:1084:16 + ❯ Process.ChildProcess._handle.onexit node:internal/child_process:304:5 + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ +Serialized Error: { code: 'plugin_unsupported' } +This error originated in "tests/hosted-extension-protocol.test.ts" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. +The latest test that might've caused the error is "rejects two forged calls after the authoritative first outcome settles". It might mean one of the following: +- The error was thrown, while Vitest was running this test. +- If the error occurred after the test had been completed, this was the last documented test before it was thrown. +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ + + Test Files 9 failed | 239 passed | 3 skipped (251) + Tests 45 failed | 3801 passed | 23 skipped (3869) + Errors 1 error + Start at 14:24:31 + Duration 655.90s (transform 5.84s, setup 1.28s, collect 83.28s, tests 1770.17s, environment 32ms, prepare 11.03s) + diff --git a/evidence/daemon-check/sdk-minimal-path.txt b/evidence/daemon-check/sdk-minimal-path.txt new file mode 100644 index 000000000..aece65cf1 --- /dev/null +++ b/evidence/daemon-check/sdk-minimal-path.txt @@ -0,0 +1,17 @@ + + RUN v2.1.9 /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk + + ✓ tests/check-daemon-validation.test.ts (20 tests) 3104ms + ✓ real CLI refuses skew in auto and required modes 1124ms + ✓ real CLI labels daemon and offline passes, including JSON 1394ms + ✓ real CLI refuses an invalid RELAYFLOWD_BIN in required mode 420ms + ✓ tests/cli.test.ts (71 tests) 3794ms + ✓ flows check CLI > binds a checked relative wrapper to the flow directory for worker execution 1246ms + ✓ flows check CLI > resolves a bare PATH-resolved claude with no declared model, in an isolated PATH 440ms + ✓ flows run/resume CLI over the journal protocol > follows a worker wait past a locally expired lease until the daemon settles it 1045ms + + Test Files 2 passed (2) + Tests 91 passed (91) + Start at 14:31:06 + Duration 8.70s (transform 3.86s, setup 51ms, collect 9.02s, tests 6.90s, environment 0ms, prepare 210ms) + diff --git a/evidence/daemon-check/sdk-targeted-final.txt b/evidence/daemon-check/sdk-targeted-final.txt new file mode 100644 index 000000000..49cf7658a --- /dev/null +++ b/evidence/daemon-check/sdk-targeted-final.txt @@ -0,0 +1,28 @@ + + RUN v2.1.9 /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk + + ✓ tests/cli.test.ts (71 tests) 4358ms + ✓ flows check CLI > binds a checked relative wrapper to the flow directory for worker execution 945ms + ✓ flows check CLI > resolves a bare PATH-resolved claude with no declared model, in an isolated PATH 699ms + ✓ flows run/resume CLI over the journal protocol > follows a worker wait past a locally expired lease until the daemon settles it 1066ms + ✓ tests/check-daemon-validation.test.ts (20 tests) 4465ms + ✓ real CLI refuses skew in auto and required modes 1449ms + ✓ real CLI labels daemon and offline passes, including JSON 2162ms + ✓ real CLI refuses an invalid RELAYFLOWD_BIN in required mode 616ms + ✓ tests/cli-watch.test.ts (11 tests) 19871ms + ✓ flows check --watch > forwards required daemon validation through watch despite the offline test environment 1371ms + ✓ flows check --watch > rechecks syntax errors, clears once, and returns the last refusal on Ctrl-C 2275ms + ✓ flows check --watch > streams JSON lines without ANSI, recovers after atomic saves, and exits zero after repair 2472ms + ✓ flows check --watch > coalesces 20 concurrent saves into at most two rechecks 1927ms + ✓ flows check --watch > watches transitive relative use imports, cycles, and nearest config changes 2580ms + ✓ flows check --watch > refreshes the import graph and notices missing imports being created 2559ms + ✓ flows check --watch > reloads authored TypeScript instead of reusing the first imported definition 1902ms + ✓ flows check --watch > detects a nearer config appearing and falls back after it is deleted 2014ms + ✓ flows check --watch > keeps watching after the target is deleted and recreated 1987ms + ✓ flows check --watch > queues changes during a slow check without overlapping checks 779ms + + Test Files 3 passed (3) + Tests 102 passed (102) + Start at 14:30:03 + Duration 22.30s (transform 1.83s, setup 58ms, collect 6.59s, tests 28.69s, environment 0ms, prepare 196ms) + diff --git a/evidence/daemon-check/sdk-targeted.txt b/evidence/daemon-check/sdk-targeted.txt new file mode 100644 index 000000000..81c527552 --- /dev/null +++ b/evidence/daemon-check/sdk-targeted.txt @@ -0,0 +1,17 @@ + + RUN v2.1.9 /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk + + ✓ tests/check-daemon-validation.test.ts (16 tests) 2751ms + ✓ real CLI refuses skew in auto and required modes 944ms + ✓ real CLI labels daemon and offline passes, including JSON 1321ms + ✓ real CLI refuses an invalid RELAYFLOWD_BIN in required mode 410ms + ✓ tests/cli.test.ts (71 tests) 3028ms + ✓ flows check CLI > binds a checked relative wrapper to the flow directory for worker execution 576ms + ✓ flows check CLI > resolves a bare PATH-resolved claude with no declared model, in an isolated PATH 429ms + ✓ flows run/resume CLI over the journal protocol > follows a worker wait past a locally expired lease until the daemon settles it 1044ms + + Test Files 2 passed (2) + Tests 87 passed (87) + Start at 14:22:14 + Duration 5.02s (transform 1.47s, setup 30ms, collect 3.58s, tests 5.78s, environment 0ms, prepare 89ms) + diff --git a/evidence/daemon-check/standalone-build.txt b/evidence/daemon-check/standalone-build.txt new file mode 100644 index 000000000..53e220c27 --- /dev/null +++ b/evidence/daemon-check/standalone-build.txt @@ -0,0 +1,6 @@ +Bundled 824 modules in 64ms + + authored-node.mjs 3.54 MB (entry point) + + [421ms] bundle 779 modules + [25ms] compile /tmp/flows-daemon-check diff --git a/evidence/daemon-check/standalone-check.txt b/evidence/daemon-check/standalone-check.txt new file mode 100644 index 000000000..a2fd42bac --- /dev/null +++ b/evidence/daemon-check/standalone-check.txt @@ -0,0 +1,4 @@ +WARNING [unprovable_effects] Step "greet" command "echo" resolves, but its effects cannot be proven before execution. +WARNING [unprovable_effects] Step "shout" command "echo" resolves, but its effects cannot be proven before execution. +WARNING [editor_schema_missing] For editor validation, add this first line: # yaml-language-server: $schema=https://schema.relayflows.dev/v0.1/flows.schema.json +{"validation":{"mode":"daemon","binary":"/tmp/stub-relayflowd-check","protocol":0,"specVersion":"0.1.0"},"ok":true,"path":"testdata/hello-deterministic.flow.yaml","projectConfigPath":"/home/daytona/.relayflow-v2-supervisor/durable/repository/testdata/flows.json","gates":[{"stepId":"greet","kind":"data","checks":["exit_code","output_contains"],"evaluator":"kernel","preflightable":true,"replayable":true},{"stepId":"shout","kind":"data","checks":["exit_code","output_contains"],"evaluator":"kernel","preflightable":true,"replayable":true}],"resolutions":[],"diagnostics":[{"severity":"warning","kind":"unprovable_effects","stepId":"greet","message":"Step \"greet\" command \"echo\" resolves, but its effects cannot be proven before execution."},{"severity":"warning","kind":"unprovable_effects","stepId":"shout","message":"Step \"shout\" command \"echo\" resolves, but its effects cannot be proven before execution."},{"severity":"warning","kind":"editor_schema_missing","message":"For editor validation, add this first line: # yaml-language-server: $schema=https://schema.relayflows.dev/v0.1/flows.schema.json"}],"requirements":{"integrations":[],"harnesses":[],"harnessUses":[],"mcp":[]}} diff --git a/evidence/daemon-check/timing.txt b/evidence/daemon-check/timing.txt new file mode 100644 index 000000000..1d313925d --- /dev/null +++ b/evidence/daemon-check/timing.txt @@ -0,0 +1,20 @@ +WARNING [unprovable_effects] Step "greet" command "echo" resolves, but its effects cannot be proven before execution. +WARNING [unprovable_effects] Step "shout" command "echo" resolves, but its effects cannot be proven before execution. +WARNING [editor_schema_missing] For editor validation, add this first line: # yaml-language-server: $schema=https://schema.relayflows.dev/v0.1/flows.schema.json +GATE step "greet" exit_code+output_contains from data (kernel, journal-replayable) +GATE step "shout" exit_code+output_contains from data (kernel, journal-replayable) +CHECK PASSED testdata/hello-deterministic.flow.yaml [validated by relayflowd at /tmp/relayflows-daemon-check-target/debug/relayflowd, protocol 0] + +real 0m0.505s +user 0m0.471s +sys 0m0.126s +WARNING [unprovable_effects] Step "greet" command "echo" resolves, but its effects cannot be proven before execution. +WARNING [unprovable_effects] Step "shout" command "echo" resolves, but its effects cannot be proven before execution. +WARNING [editor_schema_missing] For editor validation, add this first line: # yaml-language-server: $schema=https://schema.relayflows.dev/v0.1/flows.schema.json +GATE step "greet" exit_code+output_contains from data (kernel, journal-replayable) +GATE step "shout" exit_code+output_contains from data (kernel, journal-replayable) +CHECK PASSED testdata/hello-deterministic.flow.yaml [local only: disabled, so daemon acceptance is unproven] + +real 0m0.491s +user 0m0.493s +sys 0m0.107s diff --git a/evidence/daemon-check/typecheck-final.txt b/evidence/daemon-check/typecheck-final.txt new file mode 100644 index 000000000..445266dd4 --- /dev/null +++ b/evidence/daemon-check/typecheck-final.txt @@ -0,0 +1,8 @@ + +> @relayflows/sdk@2.0.42 typecheck +> tsc --noEmit && tsc -p tsconfig.type-tests.json + + +> @relayflows/sdk@2.0.42 typecheck:tests +> tsc -p tsconfig.tests.json + diff --git a/evidence/daemon-check/typecheck-tests-final.txt b/evidence/daemon-check/typecheck-tests-final.txt new file mode 100644 index 000000000..1b561ede4 --- /dev/null +++ b/evidence/daemon-check/typecheck-tests-final.txt @@ -0,0 +1,4 @@ + +> @relayflows/sdk@2.0.42 typecheck:tests +> tsc -p tsconfig.tests.json + diff --git a/kernel/DAEMON-LIFECYCLE.md b/kernel/DAEMON-LIFECYCLE.md index 3d739fab2..ca0009171 100644 --- a/kernel/DAEMON-LIFECYCLE.md +++ b/kernel/DAEMON-LIFECYCLE.md @@ -382,6 +382,20 @@ Not found → exit 2, `relayflowd_not_found`, message naming both ## 4. What changes, and what is new +`flows check` does not call `ensureDaemon` or open a socket. It may invoke the +resolved binary as `relayflowd validate-spec < spec.json`, a stateless stdin / +JSON-stdout admission check dispatched before engine construction. It parses and +validates like `run.start`, writes no run or journal, and leaves the data dir +untouched. Exit 0 carries an accepting envelope; exit 2 carries an invalid-spec +envelope. An older binary's clap exit 2 is not a verdict: consumers must validate +the stdout envelope. The envelope reports `ok`, `protocol`, `spec_version`, and +on refusal `error: {code: "invalid_spec", message}`. + +Automatic mode falls back to labelled local validation when no validator answers; +`--against-daemon` requires a verdict and `--no-daemon-check` disables discovery. +This judges the resolved binary, not a different build already serving a socket. + + ### New — `packages/sdk/src/daemon-lifecycle.ts` Mirrors `../relay/packages/cli/src/cli/lib/broker-lifecycle.ts` + diff --git a/kernel/relayflowd/src/lib.rs b/kernel/relayflowd/src/lib.rs index 8cf501261..e89dfa47c 100644 --- a/kernel/relayflowd/src/lib.rs +++ b/kernel/relayflowd/src/lib.rs @@ -14,3 +14,5 @@ pub use engine::{ }; mod workspace; + +pub mod validate_spec; diff --git a/kernel/relayflowd/src/main.rs b/kernel/relayflowd/src/main.rs index 03c0050b8..5206091ce 100644 --- a/kernel/relayflowd/src/main.rs +++ b/kernel/relayflowd/src/main.rs @@ -19,6 +19,8 @@ struct Cli { #[derive(Debug, Subcommand)] enum Command { + /// Validate a kernel spec from stdin without creating a run or journal. + ValidateSpec, /// Start and execute a run spec JSON file. Run { spec: PathBuf, @@ -56,8 +58,17 @@ enum Command { fn main() -> Result<()> { let cli = Cli::parse(); + if matches!(cli.command, Command::ValidateSpec) { + let verdict = match serde_json::from_reader::<_, serde_json::Value>(std::io::stdin()) { + Ok(value) => relayflowd::validate_spec::validate_spec(&value), + Err(error) => relayflowd::validate_spec::refusal(error.to_string()), + }; + println!("{}", verdict); + std::process::exit(if verdict["ok"] == true { 0 } else { 2 }); + } let engine = Engine::new(&cli.data_dir); match cli.command { + Command::ValidateSpec => unreachable!(), Command::Run { spec, event, diff --git a/kernel/relayflowd/src/validate_spec.rs b/kernel/relayflowd/src/validate_spec.rs new file mode 100644 index 000000000..3ef3ff2aa --- /dev/null +++ b/kernel/relayflowd/src/validate_spec.rs @@ -0,0 +1,17 @@ +//! Stateless admission check: identical to run.start (parse then validate). +//! This is intentionally stricter than event.submit, which currently only parses. +use relayflowd_core::{PROTOCOL_VERSION, RunSpec, spec::SPEC_VERSION}; +use serde_json::{Value, json}; + +pub fn validate_spec(value: &Value) -> Value { + let result = RunSpec::parse(value).and_then(|spec| spec.validate()); + match result { + Ok(()) => json!({"ok": true, "protocol": PROTOCOL_VERSION, "spec_version": SPEC_VERSION}), + Err(error) => refusal(error.to_string()), + } +} + +pub fn refusal(message: String) -> Value { + json!({"ok": false, "protocol": PROTOCOL_VERSION, "spec_version": SPEC_VERSION, + "error": {"code": "invalid_spec", "message": message}}) +} diff --git a/kernel/relayflowd/tests/validate_spec.rs b/kernel/relayflowd/tests/validate_spec.rs new file mode 100644 index 000000000..f64125cfb --- /dev/null +++ b/kernel/relayflowd/tests/validate_spec.rs @@ -0,0 +1,81 @@ +use serde_json::{Value, json}; +use std::{ + io::Write, + process::{Command, Stdio}, +}; + +fn check(value: &[u8], data_dir: &std::path::Path) -> (i32, Value) { + let mut child = Command::new(env!("CARGO_BIN_EXE_relayflowd")) + .arg("--data-dir") + .arg(data_dir) + .arg("validate-spec") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .unwrap(); + child.stdin.take().unwrap().write_all(value).unwrap(); + let output = child.wait_with_output().unwrap(); + assert!(output.stderr.is_empty(), "{:?}", output); + let verdict: Value = serde_json::from_slice(&output.stdout).unwrap(); + assert!(verdict.get("run_id").is_none()); + assert_eq!(verdict["protocol"], 0); + assert_eq!(verdict["spec_version"], "0.1.0"); + (output.status.code().unwrap(), verdict) +} + +#[test] +fn accepts_without_creating_data_directory_or_journal() { + let root = tempfile::tempdir().unwrap(); + let absent = root.path().join("absent"); + let source = include_bytes!("../../../testdata/hello-agent.spec.canonical.json"); + let (code, verdict) = check(source, &absent); + assert_eq!(code, 0); + assert_eq!( + verdict, + json!({"ok": true, "protocol": 0, "spec_version": "0.1.0"}) + ); + assert!(!absent.exists()); + check(source, root.path()); + assert_eq!(std::fs::read_dir(root.path()).unwrap().count(), 0); +} + +#[test] +fn refuses_unknown_field_with_kernel_location() { + let root = tempfile::tempdir().unwrap(); + let mut spec: Value = serde_json::from_slice(include_bytes!( + "../../../testdata/hello-agent.spec.canonical.json" + )) + .unwrap(); + spec["steps"][1]["nope"] = json!(true); + let (code, verdict) = check(&serde_json::to_vec(&spec).unwrap(), root.path()); + assert_eq!(code, 2); + assert_eq!(verdict["error"]["code"], "invalid_spec"); + assert!( + verdict["error"]["message"] + .as_str() + .unwrap() + .contains("unknown field \"nope\" at steps[1]") + ); + assert_eq!(std::fs::read_dir(root.path()).unwrap().count(), 0); +} + +#[test] +fn refuses_semantic_errors_and_malformed_json() { + let root = tempfile::tempdir().unwrap(); + let mut spec: Value = serde_json::from_slice(include_bytes!( + "../../../testdata/hello-agent.spec.canonical.json" + )) + .unwrap(); + spec["steps"][1]["cwd"] = json!("/absolute"); + let (code, verdict) = check(&serde_json::to_vec(&spec).unwrap(), root.path()); + assert_eq!(code, 2); + assert!( + verdict["error"]["message"] + .as_str() + .unwrap() + .contains("working directory \"/absolute\"") + ); + assert_eq!(check(b"{", root.path()).0, 2); + assert_eq!(std::fs::read_dir(root.path()).unwrap().count(), 0); +} diff --git a/packages/sdk/src/cli-commands.ts b/packages/sdk/src/cli-commands.ts index 75a3a0fbd..ba8d97414 100644 --- a/packages/sdk/src/cli-commands.ts +++ b/packages/sdk/src/cli-commands.ts @@ -145,9 +145,9 @@ export const CLI_VERBS = [ }, { name: 'check', - description: 'Compile and preflight a flow without running it, or opening a daemon socket', + description: 'Compile, preflight, and validate with the installed runtime when available; never start a run', args: [{ name: 'source', description: 'flow.ts, flow.yaml, or spec.json', required: true }], - options: [JSON_OPTION, { flags: '--watch', description: 'Re-check on every change to the flow and its imports' }], + options: [{ flags: '--against-daemon', description: 'Require acceptance by the installed relayflowd validator (FLOWS_CHECK_AGAINST_DAEMON=1)' }, { flags: '--no-daemon-check', description: 'Local compile and preflight only (FLOWS_NO_DAEMON_CHECK=1)' }, JSON_OPTION, { flags: '--watch', description: 'Re-check on every change to the flow and its imports' }], variants: ['check'], }, { diff --git a/packages/sdk/src/cli-watch.ts b/packages/sdk/src/cli-watch.ts index 2fe2a6dbb..e94035fff 100644 --- a/packages/sdk/src/cli-watch.ts +++ b/packages/sdk/src/cli-watch.ts @@ -1,3 +1,4 @@ +import type { DaemonValidationMode } from './daemon-spec-validation.js'; import { spawn } from 'node:child_process'; import { accessSync, constants, existsSync, readFileSync, watch, type FSWatcher } from 'node:fs'; import { dirname, join, resolve, sep } from 'node:path'; @@ -15,12 +16,13 @@ export async function watchCheck( io: CliIo, /** Cancellation, owned by the caller; this function installs no signal handler. */ signal: AbortSignal, + daemonValidation: DaemonValidationMode = 'auto', ): Promise { try { return await watchChecks({ path, signal, - check: () => checkOnce(path, json, io), + check: () => checkOnce(path, json, io, daemonValidation), clear: () => { if (!json) io.stdout('\x1b[2J\x1b[H'); }, }); } catch (error) { @@ -140,13 +142,13 @@ function discoverWatchPaths(path: string, imports: Map): Set { +function checkOnce(path: string, json: boolean, io: CliIo, mode: DaemonValidationMode): Promise { // Importing an authored TS file in this process again would return its stale // cached definition (including transitive imports). A fresh CLI process uses // precisely the same check pipeline and reporting as a one-shot invocation. const entry = fileURLToPath(new URL(import.meta.url.endsWith('.ts') ? './cli.ts' : './cli.js', import.meta.url)); return new Promise((resolveCode, reject) => { - const child = spawn(process.execPath, [...process.execArgv, entry, 'check', ...(json ? ['--json'] : []), path], { + const child = spawn(process.execPath, [...process.execArgv, entry, 'check', ...(mode === 'required' ? ['--against-daemon'] : mode === 'off' ? ['--no-daemon-check'] : []), ...(json ? ['--json'] : []), path], { stdio: ['ignore', 'pipe', 'pipe'], // Ctrl-C belongs to the watcher; let an active check finish so its report // and exit status remain a pair, then release all resources. diff --git a/packages/sdk/src/cli.ts b/packages/sdk/src/cli.ts index 7989f596e..652e05fa2 100644 --- a/packages/sdk/src/cli.ts +++ b/packages/sdk/src/cli.ts @@ -1,4 +1,5 @@ #!/usr/bin/env node +import { unavailableValidation, validationAnnotation, type DaemonValidationMode } from './daemon-spec-validation.js'; import { withSubscriptionMetadata } from './cli/subscription-report.js'; import { addPlugin } from './cli/add.js'; import { parsePluginArgs, runPluginCommand, type PluginArgs } from './cli/plugin.js'; @@ -103,7 +104,7 @@ export type ParsedArgs = | CloudScheduleArgs | { command: 'schedules'; json: boolean } | { command: 'unschedule'; scheduleId: string; json: boolean } - | { command: 'check'; json: boolean; watch: boolean; value: string } + | { command: 'check'; json: boolean; watch: boolean; daemonValidation: DaemonValidationMode; value: string } | { command: 'run'; bucket: string | undefined; reuseFromRunId: string | undefined; localAgent: boolean; agentCapacity: number | undefined; dataDir: string; input: string | undefined; json: boolean; spawn: boolean; noObserverLink: boolean; cloudMirror: boolean; allowHumanInfluenced: boolean; value: string } | { command: 'resume'; localAgent: boolean; agentCapacity: number | undefined; dataDir: string; json: boolean; spawn: boolean; noObserverLink: boolean; cloudMirror: boolean; allowHumanInfluenced: boolean; value: string } | { command: 'answer'; dataDir: string; json: boolean; spawn: boolean; note: string | undefined; by: string | undefined; runId: string; waitId: string; answer: boolean } @@ -136,7 +137,7 @@ const USAGE = [ 'flows unschedule [--json] ', 'flows deploy @sha256: --to ', 'flows run @sha256: [--bucket ] [--data-dir ] [--json]', - 'flows check [--watch] [--json] ', + 'flows check [--against-daemon|--no-daemon-check] [--watch] [--json] ', 'flows serve-webhook --data-dir --port

[--allow [,]]', 'flows run [--json] [--no-spawn] [--no-observer-link] [--cloud-mirror] [--data-dir

] [--local-agent [--agent-capacity ]] [--reuse-from ] ', 'flows run --cloud [--json] [--wait] [--sync-code] [--no-connect] ', @@ -289,20 +290,20 @@ export async function runCli( if (parsed.command === 'check') { if (parsed.watch) { - return withInterrupt(options.signal, (signal) => watchCheck(parsed.value, parsed.json, io, signal)); + return withInterrupt(options.signal, (signal) => watchCheck(parsed.value, parsed.json, io, signal, parsed.daemonValidation)); } - // Deliberately daemon-free (kernel/DAEMON-LIFECYCLE.md §4). `checkFlow` is - // a compile-and-preflight that opens no daemon socket, and the parser - // refuses `--data-dir` on `check`, so there is no data dir to attach to. - // `flows check` keeps working with no daemon, no relayflowd binary and no - // data directory at all -- a property worth keeping, not an omission. - // Only this invocation opts into `agent_worker_unresolved`: `flows check` - // attaches no worker and, being daemon-free, cannot see one attached - // elsewhere. The authored `.flow.ts` path checks header declarations - // without compiling steps, so it has no agent steps to count. + // Stateless binary validation opens no socket or data dir (DAEMON-LIFECYCLE §4). + // Authored bodies are only lowered at run time, so their acceptance is unproven. const checked = /\.(?:[cm]?[jt]s)$/.test(parsed.value) ? await checkAuthoredFlowComposed(parsed.value) - : checkFlow(parsed.value, { warnUnresolvedAgentWorker: true }); + : checkFlow(parsed.value, { warnUnresolvedAgentWorker: true, daemonValidation: parsed.daemonValidation }); + if (checked.report.validation === undefined) { + const unavailable = unavailableValidation(parsed.daemonValidation, + checked.report.ok ? (parsed.daemonValidation === 'off' ? 'disabled' : 'authored_body') : 'not_reached'); + checked.report.validation = unavailable.validation; + if (checked.report.ok) checked.report.diagnostics.push(...unavailable.diagnostics); + checked.report.ok = checked.report.ok && !checked.report.diagnostics.some(d => d.severity === 'refusal'); + } emitCheckReport(checked.report, parsed.json, io); return checked.report.ok ? 0 : 2; } @@ -661,6 +662,7 @@ function parseArgs(args: readonly string[]): ParsedArgs | undefined { let json = false; let watch = false; + let daemonValidation: DaemonValidationMode | undefined; let cloud = false; let wait = false; let syncCode = false; @@ -707,6 +709,11 @@ function parseArgs(args: readonly string[]): ParsedArgs | undefined { if (!isAgentCapacity(agentCapacity)) return undefined; continue; } + if (argument === '--against-daemon' || argument === '--no-daemon-check') { + if (command !== 'check' || daemonValidation !== undefined) return undefined; + daemonValidation = argument === '--against-daemon' ? 'required' : 'off'; + continue; + } if (argument === '--watch') { if (command !== 'check' || watch) return undefined; watch = true; @@ -792,7 +799,9 @@ function parseArgs(args: readonly string[]): ParsedArgs | undefined { if (command === 'run' && input !== undefined && !isAuthoredFlowPath(positionals[0]!)) return undefined; return command === 'check' - ? { command, json, watch, value: positionals[0]! } + ? { command, json, watch, value: positionals[0]!, + daemonValidation: daemonValidation ?? (process.env['FLOWS_NO_DAEMON_CHECK'] === '1' ? 'off' + : process.env['FLOWS_CHECK_AGAINST_DAEMON'] === '1' ? 'required' : 'auto') } : command === 'run' ? { command, bucket, reuseFromRunId, localAgent, agentCapacity, dataDir, input, json, spawn, noObserverLink, cloudMirror, allowHumanInfluenced, value: positionals[0]! } : { command, localAgent, agentCapacity, dataDir, json, spawn, noObserverLink, cloudMirror, allowHumanInfluenced, value: positionals[0]! }; @@ -1110,7 +1119,7 @@ function emitCheckReport(report: CheckReport, json: boolean, io: CliIo): void { const requires = report.requirements === undefined ? '' : describeFlowRequirements(report.requirements); if (requires) io.stdout(`REQUIRES ${requires}`); emitDiagnostics(deferred, io); - if (report.ok) io.stdout(`CHECK PASSED ${report.path ?? ''}`.trimEnd()); + if (report.ok) io.stdout(`CHECK PASSED ${report.path ?? ''}`.trimEnd() + validationAnnotation(report.validation)); } function emitRunReport( diff --git a/packages/sdk/src/cli/check.ts b/packages/sdk/src/cli/check.ts index cc6c42779..b49f2b6de 100644 --- a/packages/sdk/src/cli/check.ts +++ b/packages/sdk/src/cli/check.ts @@ -1,3 +1,4 @@ +import { validateSpecWithDaemon, type DaemonValidation, type DaemonValidationMode, type DaemonValidationDeps } from '../daemon-spec-validation.js'; import { communicationInstruction } from '../communication/spec.js'; import { rememberResolvedCliIdentities } from '../resolved-cli-identity.js'; import { checkCommunicationEnvironment } from '../communication/preflight.js'; @@ -44,6 +45,7 @@ export interface ProjectConfig { } export interface CheckReport { + validation?: DaemonValidation; mcpTools?: Readonly>; ok: boolean; path?: string; @@ -125,6 +127,8 @@ type CliProbeOutcomeMap = Map; * anything that depends on how the flow is about to be invoked opts in here. */ export interface CheckInvocation { + daemonValidation?: DaemonValidationMode; + daemonValidationDeps?: DaemonValidationDeps; /** Environment used only by provider executable/auth/model probes. */ environment?: NodeJS.ProcessEnv; /** @@ -159,7 +163,7 @@ export function checkFlow(path: string, invocation: CheckInvocation = {}): Check execution = checkAuthoredFlow(readFlow(source, absolutePath), path, undefined, invocation); } catch (error) { if (!(error instanceof CheckFailure)) throw error; - execution = { report: inputFailureReport(error, path) }; + execution = { report: inputFailureReport(error, path, invocation) }; } // The editor-schema hint is a documentation nudge, emitted for every // .flow.yaml without a first-line yaml-language-server comment. @@ -172,7 +176,7 @@ export function checkFlow(path: string, invocation: CheckInvocation = {}): Check const failure = error instanceof CheckFailure ? error : new CheckFailure('invalid_spec', `Flow "${path}" could not be checked as a Relayflow spec.`); - return { report: inputFailureReport(failure, path) }; + return { report: inputFailureReport(failure, path, invocation) }; } } @@ -246,14 +250,20 @@ export function checkAuthoredFlow( const workerSurface = invocation.warnUnresolvedAgentWorker === true ? agentWorkerDiagnostics(authoring) : []; + const daemon = invocation.daemonValidation === undefined ? undefined + : result.ok && flow !== undefined + ? validateSpecWithDaemon(flow, invocation.daemonValidation, invocation.daemonValidationDeps) + : { validation: { mode: 'local' as const, reason: 'not_reached' as const }, diagnostics: [] }; + if (daemon?.diagnostics.some(d => d.severity === 'refusal')) result.ok = false; return { report: { + ...(daemon === undefined ? {} : { validation: daemon.validation }), ok: result.ok, path, ...(config.path !== undefined ? { projectConfigPath: config.path } : {}), gates: result.gates, resolutions: result.resolutions, - diagnostics: [...result.diagnostics, ...workerSurface], + diagnostics: [...result.diagnostics, ...workerSurface, ...(daemon?.diagnostics ?? [])], requirements: safeRequirements(authoring, config.cli), }, ...(result.ok && flow !== undefined ? { flow } : {}), @@ -262,7 +272,7 @@ export function checkAuthoredFlow( const failure = error instanceof CheckFailure ? error : new CheckFailure('invalid_spec', `Flow "${path}" could not be checked as a Relayflow spec.`); - return { report: inputFailureReport(failure, path) }; + return { report: inputFailureReport(failure, path, invocation) }; } } @@ -335,8 +345,10 @@ export async function checkBuildableFlow(path: string): Promise export function inputFailureReport( failure: { kind: CheckFailureKind; message: string }, path?: string, + invocation: CheckInvocation = {}, ): CheckReport { return { + ...(invocation.daemonValidation === undefined ? {} : { validation: { mode: 'local' as const, reason: 'not_reached' as const } }), ok: false, ...(path !== undefined ? { path } : {}), gates: [], diff --git a/packages/sdk/src/daemon-spec-validation.ts b/packages/sdk/src/daemon-spec-validation.ts new file mode 100644 index 000000000..9addef6ce --- /dev/null +++ b/packages/sdk/src/daemon-spec-validation.ts @@ -0,0 +1,119 @@ +import { spawnSync } from 'node:child_process'; +import { canonicalize } from './canonical.js'; +import { CompileError, toKernelSpec } from './compile.js'; +import { PROTOCOL_VERSION } from './protocol.js'; +import { RelayflowdNotFoundError, resolveRelayflowdBinary } from './relayflowd-path.js'; +import type { FlowSpec } from './spec.js'; +import type { CheckReport } from './cli/check.js'; + +export type DaemonValidationMode = 'auto' | 'required' | 'off'; +type Unavailable = 'disabled' | 'not_reached' | 'authored_body' | 'relayflowd_not_found' + | 'relayflowd_bin_invalid' | 'no_verdict' | 'spawn_failed' | 'timed_out' | 'protocol_mismatch'; +export interface DaemonValidation { + mode: 'daemon' | 'local'; + binary?: string; + protocol?: number; + specVersion?: string; + reason?: Unavailable; +} +export interface DaemonValidationDeps { + resolveBinary(): string; + run(binary: string, input: string): { + status: number | null; stdout: string; stderr: string; error?: Error; + }; +} +type Result = { validation: DaemonValidation; diagnostics: CheckReport['diagnostics'] }; +// Cache discovery, never verdicts. Changed resolution inputs get a fresh lookup. +const binaries = new Map(); +const defaults: DaemonValidationDeps = { + resolveBinary() { + const key = JSON.stringify([process.env['RELAYFLOWD_BIN'], process.env['PATH'], process.cwd(), process.argv[1]]); + let found = binaries.get(key); + if (found === undefined) { + try { found = resolveRelayflowdBinary(); } + catch (error) { found = error instanceof Error ? error : new Error(String(error)); } + binaries.set(key, found); + } + if (found instanceof Error) throw found; + return found; + }, + run: (binary, input) => spawnSync(binary, ['validate-spec'], { + input, encoding: 'utf8', timeout: 5000, killSignal: 'SIGKILL', maxBuffer: 1024 * 1024, + stdio: ['pipe', 'pipe', 'pipe'], + }), +}; + +export function unavailableValidation( + mode: DaemonValidationMode, reason: Unavailable, detail = '', binary?: string, +): Result { + const message = `${binary === undefined ? 'relayflowd' : `relayflowd at ${binary}`}: ${detail || reason.replaceAll('_', ' ')}. ` + + 'Daemon acceptance is unproven; an already attached daemon from another build is not checked.'; + return { + validation: { mode: 'local', reason, ...(binary === undefined ? {} : { binary }) }, + diagnostics: mode === 'required' + ? [{ severity: 'refusal', kind: 'daemon_validation_unavailable', message }] + : reason === 'disabled' || reason === 'not_reached' || reason === 'relayflowd_not_found' + ? [] : [{ severity: 'warning', kind: 'daemon_unvalidated', message }], + }; +} + +/** Same spec value run.start submits, in canonical form; no socket or run. */ +export function validateSpecWithDaemon( + flow: FlowSpec, mode: DaemonValidationMode, deps: DaemonValidationDeps = defaults, +): Result { + if (mode === 'off') return unavailableValidation(mode, 'disabled'); + let submitted: ReturnType; + let input: string; + try { submitted = toKernelSpec(flow); input = canonicalize(submitted); } + catch (error) { + // run refuses this lowering too: degrading to local would fail open. + const errors = error instanceof CompileError ? error.errors : [String(error)]; + return { validation: { mode: 'local', reason: 'not_reached' }, + diagnostics: [{ severity: 'refusal', kind: 'invalid_spec', message: errors.join('; '), errors }] }; + } + let binary: string; + try { binary = deps.resolveBinary(); } + catch (error) { + const reason = error instanceof RelayflowdNotFoundError + ? error.attempts[0]?.startsWith('RELAYFLOWD_BIN=') ? 'relayflowd_bin_invalid' : 'relayflowd_not_found' + : 'spawn_failed'; + return unavailableValidation(mode, reason, String(error)); + } + try { + const result = deps.run(binary, input); + if (result.error) return unavailableValidation(mode, + (result.error as NodeJS.ErrnoException).code === 'ETIMEDOUT' ? 'timed_out' : 'spawn_failed', result.error.message, binary); + let verdict: unknown; + try { verdict = JSON.parse(result.stdout); } catch { /* Old binaries have no envelope. */ } + if (!isVerdict(verdict)) return unavailableValidation(mode, 'no_verdict', + 'no validation verdict (the binary may predate validate-spec)', binary); + if (verdict.protocol !== PROTOCOL_VERSION) return unavailableValidation(mode, 'protocol_mismatch', + `validator protocol ${verdict.protocol}, SDK protocol ${PROTOCOL_VERSION}`, binary); + const validation: DaemonValidation = { mode: 'daemon', binary, + protocol: verdict.protocol, specVersion: verdict.spec_version }; + if (verdict.ok) return { validation, diagnostics: [] }; + const message = verdict.error.message; + const index = /steps\[(\d+)\]/.exec(message)?.[1]; + const stepId = index === undefined ? undefined : submitted.steps[Number(index)]?.id; + const generated = stepId !== undefined && !flow.steps.some(step => step.id === stepId); + return { validation, diagnostics: [{ severity: 'refusal', kind: 'invalid_spec', + message: message + (generated ? `\nStep "${stepId}" is generated by this compiler's gate lowering, not declared in the flow; ` + + 'the installed relayflowd and this @relayflows/sdk disagree (SDK lowering bug). Report this rather than editing the flow.' : ''), + errors: [message], ...(stepId === undefined ? {} : { stepId }) }] }; + } catch (error) { return unavailableValidation(mode, 'spawn_failed', String(error), binary); } +} + +type Verdict = { protocol: number; spec_version: string } & + ({ ok: true } | { ok: false; error: { code: 'invalid_spec'; message: string } }); +function isVerdict(value: unknown): value is Verdict { + if (typeof value !== 'object' || value === null) return false; + const v = value as Partial; + return Number.isInteger(v.protocol) && typeof v.spec_version === 'string' + && (v.ok === true || (v.ok === false && v.error?.code === 'invalid_spec' && typeof v.error.message === 'string')); +} + +export function validationAnnotation(validation?: DaemonValidation): string { + return validation?.mode === 'daemon' + ? ` [validated by relayflowd at ${validation.binary}, protocol ${validation.protocol}]` + : ` [local only: ${(validation?.reason ?? 'not_reached').replaceAll('_', ' ')}, so daemon acceptance is unproven]`; +} diff --git a/packages/sdk/src/failure-kinds.ts b/packages/sdk/src/failure-kinds.ts index 7a05c98a2..fbc06490f 100644 --- a/packages/sdk/src/failure-kinds.ts +++ b/packages/sdk/src/failure-kinds.ts @@ -48,7 +48,9 @@ export const CHECK_INPUT_FAILURE_KINDS = [ ...SHARED_SPEC_FAILURE_KINDS, ] as const; +// run never enables daemon validation; fromCheckReport needs a mapping if that changes. export const CHECK_FAILURE_KINDS = [ + 'daemon_validation_unavailable', ...CHECK_INPUT_FAILURE_KINDS, ...PREFLIGHT_ENVIRONMENT_FAILURE_KINDS, ] as const; @@ -172,7 +174,7 @@ export const RUN_WARNING_KINDS = [ * daemon-free, can see none either — so it opts in, while `flows run`, `flows * build` and SDK submissions do not (cli/check-worker-surface.ts). */ -export const CHECK_WARNING_KINDS = ['editor_schema_missing', 'agent_worker_unresolved'] as const; +export const CHECK_WARNING_KINDS = ['editor_schema_missing', 'daemon_unvalidated', 'agent_worker_unresolved'] as const; export type CheckWarningKind = (typeof CHECK_WARNING_KINDS)[number]; export type PreflightFailureKind = (typeof PREFLIGHT_FAILURE_KINDS)[number]; diff --git a/packages/sdk/tests/check-daemon-validation.test.ts b/packages/sdk/tests/check-daemon-validation.test.ts new file mode 100644 index 000000000..e363290f9 --- /dev/null +++ b/packages/sdk/tests/check-daemon-validation.test.ts @@ -0,0 +1,199 @@ +import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join, resolve } from 'node:path'; +import { spawnSync } from 'node:child_process'; +import { afterEach, expect, it, vi } from 'vitest'; +import { checkAuthoredFlow } from '../src/cli/check.js'; +import { canonicalize } from '../src/canonical.js'; +import * as compiler from '../src/compile.js'; +import { toKernelSpec } from '../src/compile.js'; +import { validateSpecWithDaemon, type DaemonValidationDeps } from '../src/daemon-spec-validation.js'; +import { RelayflowdNotFoundError, defaultRelayflowdPathDeps, resolveRelayflowdBinary } from '../src/relayflowd-path.js'; +import { runCli } from '../src/cli.js'; +import type { FlowSpec } from '../src/spec.js'; + +const flow: FlowSpec = { version: '0.1.0', steps: [ + { id: 'first', type: 'deterministic', command: 'echo hello', timeoutMs: 1000 }, + { id: 'second', type: 'deterministic', command: 'echo done' }, +] }; +const message = 'unknown field "timeout_ms" at steps[0] — refusing to guess (fail closed)'; +const envelope = (error?: string) => ({ ok: error === undefined, protocol: 0, spec_version: '0.1.0', + ...(error === undefined ? {} : { error: { code: 'invalid_spec', message: error } }) }); +const deps = (error?: string): DaemonValidationDeps => ({ resolveBinary: () => '/stub/relayflowd', + run: vi.fn(() => ({ status: error ? 2 : 0, stdout: JSON.stringify(envelope(error)), stderr: '' })) }); +const roots: string[] = []; +afterEach(() => { vi.restoreAllMocks(); vi.unstubAllEnvs(); roots.splice(0).forEach(p => rmSync(p, { recursive: true, force: true })); }); + +it('refuses package-valid daemon-invalid skew through check', () => { + expect(checkAuthoredFlow(flow, '/tmp/flow.json').report.ok).toBe(true); + const result = checkAuthoredFlow(flow, '/tmp/flow.json', undefined, + { daemonValidation: 'auto', daemonValidationDeps: deps(message) }); + expect(result.report.ok).toBe(false); + expect(result.report.diagnostics).toContainEqual({ severity: 'refusal', kind: 'invalid_spec', + message, errors: [message], stepId: 'first' }); + expect(result.report.validation?.mode).toBe('daemon'); +}); + +it('maps generated-step indices to submitted steps and identifies compiler bugs', () => { + const gated: FlowSpec = { ...flow, steps: [{ ...flow.steps[0]!, + verification: { type: 'artifact_exists', path: 'answer.txt' } }, flow.steps[1]!] }; + const result = validateSpecWithDaemon(gated, 'required', deps('unknown field "nope" at steps[1]')); + expect(result.diagnostics[0]).toMatchObject({ stepId: 'first.gate', kind: 'invalid_spec' }); + expect(result.diagnostics[0]?.message).toContain('generated by this compiler'); + expect(result.diagnostics[0]?.message).toContain('Report this rather than editing the flow'); +}); + +it('submits the canonical spec value used by run.start', () => { + const transport = deps(); + expect(validateSpecWithDaemon(flow, 'required', transport).validation.mode).toBe('daemon'); + expect(transport.run).toHaveBeenCalledWith('/stub/relayflowd', canonicalize(toKernelSpec(flow))); +}); + +it('does not resolve or spawn in off mode or after local refusal', () => { + const transport = { resolveBinary: vi.fn(), run: vi.fn() }; + expect(validateSpecWithDaemon(flow, 'off', transport).validation.reason).toBe('disabled'); + checkAuthoredFlow({ ...flow, steps: [] }, '/tmp/flow.json', undefined, + { daemonValidation: 'required', daemonValidationDeps: transport }); + expect(transport.resolveBinary).not.toHaveBeenCalled(); + expect(transport.run).not.toHaveBeenCalled(); +}); + +it.each(['auto', 'required'] as const)('handles absent and invalid explicit binaries in %s', mode => { + for (const explicit of [false, true]) { + const transport = { ...deps(), resolveBinary() { + throw new RelayflowdNotFoundError(explicit ? 'RELAYFLOWD_BIN is invalid' : 'not found', explicit ? ['RELAYFLOWD_BIN=/bad'] : []); + } }; + const result = validateSpecWithDaemon(flow, mode, transport); + expect(result.validation.reason).toBe(explicit ? 'relayflowd_bin_invalid' : 'relayflowd_not_found'); + expect(result.diagnostics.length).toBe(mode === 'required' || explicit ? 1 : 0); + if (mode === 'required') expect(result.diagnostics[0]?.severity).toBe('refusal'); + else if (explicit) expect(result.diagnostics[0]?.severity).toBe('warning'); + } +}); + +it.each([ + { status: 2, stdout: '', stderr: 'unrecognized subcommand', reason: 'no_verdict' }, + { status: 0, stdout: '{}', stderr: '', reason: 'no_verdict' }, + { status: null, stdout: '', stderr: '', error: Object.assign(new Error('timeout'), { code: 'ETIMEDOUT' }), reason: 'timed_out' }, + { status: null, stdout: '', stderr: '', error: new Error('spawn'), reason: 'spawn_failed' }, + { status: 0, stdout: JSON.stringify({ ...envelope(), protocol: 99 }), stderr: '', reason: 'protocol_mismatch' }, +])('reports $reason without claiming daemon acceptance', outcome => { + for (const mode of ['auto', 'required'] as const) { + const result = validateSpecWithDaemon(flow, mode, { ...deps(), run: () => outcome }); + expect(result.validation).toMatchObject({ mode: 'local', reason: outcome.reason }); + expect(result.diagnostics[0]).toMatchObject({ severity: mode === 'auto' ? 'warning' : 'refusal' }); + expect(result.diagnostics[0]?.message).toContain('/stub/relayflowd'); + expect(result.diagnostics[0]?.message).toContain('acceptance is unproven'); + } +}); + +function fixture() { + const root = mkdtempSync(join(tmpdir(), 'daemon-check-')); roots.push(root); + const binary = join(root, 'relayflowd'); + writeFileSync(binary, `#!${process.execPath}\nlet input='';process.stdin.on('data',x=>input+=x);process.stdin.on('end',()=>{const spec=JSON.parse(input);const bad=spec.steps.some(s=>s.timeout_ms);console.log(JSON.stringify(bad?${JSON.stringify(envelope(message))}:${JSON.stringify(envelope())}));process.exitCode=bad?2:0;});\n`, { mode: 0o755 }); + const path = join(root, 'flow.json'); writeFileSync(path, JSON.stringify(flow)); + return { root, binary, path }; +} +function cli(path: string, binary: string, flags: string[] = []) { + return spawnSync(process.execPath, [resolve('dist/cli.js'), 'check', ...flags, path], { + encoding: 'utf8', env: { ...process.env, RELAYFLOWD_BIN: binary, FLOWS_NO_DAEMON_CHECK: '', FLOWS_CHECK_AGAINST_DAEMON: '' }, + }); +} +it('real CLI refuses skew in auto and required modes', () => { + const f = fixture(); + for (const flags of [[], ['--against-daemon']]) { + const result = cli(f.path, f.binary, flags); + expect(result.status).toBe(2); + expect(result.stderr).toContain(`REFUSED [invalid_spec] ${message}`); + } +}); +it('real CLI labels daemon and offline passes, including JSON', () => { + const f = fixture(); + expect(cli(f.path, f.binary, ['--no-daemon-check']).stdout).toContain('[local only: disabled'); + writeFileSync(f.path, JSON.stringify({ ...flow, steps: [{ id: 'a', type: 'deterministic', command: 'echo ok' }] })); + const result = cli(f.path, f.binary); + expect(result.status).toBe(0); + expect(result.stdout).toContain(`[validated by relayflowd at ${f.binary}, protocol 0]`); + expect(JSON.parse(cli(f.path, f.binary, ['--json']).stdout).validation.mode).toBe('daemon'); +}); +it('real CLI refuses an invalid RELAYFLOWD_BIN in required mode', () => { + const f = fixture(); + const result = cli(f.path, join(f.root, 'missing'), ['--against-daemon']); + expect(result.status).toBe(2); + expect(result.stderr).toContain('REFUSED [daemon_validation_unavailable]'); + expect(result.stderr).toContain('RELAYFLOWD_BIN'); +}); +it('CLI refuses when no binary anchor exists in required mode', async () => { + // Exercise the real resolver and CLI with deterministic absent anchors. + vi.stubEnv('RELAYFLOWD_BIN', ''); + vi.spyOn(defaultRelayflowdPathDeps, 'isExecutable').mockReturnValue(false); + vi.spyOn(defaultRelayflowdPathDeps, 'resolveFrom').mockReturnValue(null); + vi.spyOn(defaultRelayflowdPathDeps, 'which').mockReturnValue(null); + expect(() => resolveRelayflowdBinary()).toThrow(RelayflowdNotFoundError); + const f = fixture(); const stderr: string[] = []; + expect(await runCli(['check', '--against-daemon', f.path], { stdout: () => {}, stderr: s => stderr.push(s) })).toBe(2); + expect(stderr.join('\n')).toContain('REFUSED [daemon_validation_unavailable]'); +}); +it('flags win over environment and are check-only and mutually exclusive', async () => { + const f = fixture(); vi.stubEnv('RELAYFLOWD_BIN', f.binary); vi.stubEnv('FLOWS_CHECK_AGAINST_DAEMON', '1'); + const io = { stdout: vi.fn(), stderr: vi.fn() }; + expect(await runCli(['check', '--no-daemon-check', f.path], io)).toBe(0); + for (const args of [['check', '--against-daemon', '--no-daemon-check', f.path], + ['run', '--against-daemon', f.path], ['resume', '--no-daemon-check', 'run-id']]) { + expect(await runCli(args, io)).toBe(2); + } +}); + +it('fails closed if kernel lowering throws after local preflight passes', () => { + vi.spyOn(compiler, 'toKernelSpec').mockImplementation(() => { + throw new compiler.CompileError(['cannot lower this declaration']); + }); + const transport = deps(); + const checked = checkAuthoredFlow(flow, '/tmp/flow.json', undefined, + { daemonValidation: 'auto', daemonValidationDeps: transport }); + expect(checked.report.ok).toBe(false); + expect(checked.report.diagnostics).toContainEqual({ severity: 'refusal', kind: 'invalid_spec', + message: 'cannot lower this declaration', errors: ['cannot lower this declaration'] }); + expect(transport.run).not.toHaveBeenCalled(); +}); + +it('refuses agent cwd skew accepted by package preflight', () => { + const agent: FlowSpec = { version: '0.1.0', steps: [{ id: 'edit', type: 'agent', + cli: resolve('../../testdata/preflight/authenticated-cli'), instruction: 'edit', cwd: 'worktree' }] }; + const refusal = 'unknown field "cwd" at steps[0] — refusing to guess (fail closed)'; + expect(checkAuthoredFlow(agent, '/tmp/flow.json').report.ok).toBe(true); + const checked = checkAuthoredFlow(agent, '/tmp/flow.json', undefined, + { daemonValidation: 'required', daemonValidationDeps: deps(refusal) }); + expect(checked.report.ok).toBe(false); + expect(checked.report.diagnostics).toContainEqual({ severity: 'refusal', kind: 'invalid_spec', + message: refusal, errors: [refusal], stepId: 'edit' }); +}); + +it('maps authored steps following generated steps without blaming the compiler', () => { + const gated: FlowSpec = { ...flow, steps: [{ ...flow.steps[0]!, + verification: { type: 'artifact_exists', path: 'answer.txt' } }, flow.steps[1]!] }; + const checked = checkAuthoredFlow(gated, '/tmp/flow.json', undefined, + { daemonValidation: 'required', daemonValidationDeps: deps('unknown field "nope" at steps[2]') }); + expect(checked.report.ok).toBe(false); + expect(checked.report.diagnostics).toContainEqual({ severity: 'refusal', kind: 'invalid_spec', + message: 'unknown field "nope" at steps[2]', errors: ['unknown field "nope" at steps[2]'], stepId: 'second' }); +}); + +it('memoizes binary discovery without memoizing validation verdicts', async () => { + vi.stubEnv('RELAYFLOWD_BIN', ''); + vi.stubEnv('PATH', '/missing/check-discovery'); + vi.spyOn(defaultRelayflowdPathDeps, 'isExecutable').mockReturnValue(false); + vi.spyOn(defaultRelayflowdPathDeps, 'resolveFrom').mockReturnValue(null); + const which = vi.spyOn(defaultRelayflowdPathDeps, 'which').mockReturnValue(null); + const f = fixture(); + const io = { stdout: vi.fn(), stderr: vi.fn() }; + // The local command probe needs a real absolute executable with this PATH. + writeFileSync(f.path, JSON.stringify({ ...flow, steps: [{ id: 'a', type: 'deterministic', command: '/bin/echo ok' }] })); + await runCli(['check', '--against-daemon', f.path], io); + await runCli(['check', '--against-daemon', f.path], io); + expect(which).toHaveBeenCalledTimes(1); + const transport = deps(); + validateSpecWithDaemon(flow, 'auto', transport); + validateSpecWithDaemon(flow, 'auto', transport); + expect(transport.run).toHaveBeenCalledTimes(2); +}); diff --git a/packages/sdk/tests/cli-watch.test.ts b/packages/sdk/tests/cli-watch.test.ts index cce83e005..b43c7966f 100644 --- a/packages/sdk/tests/cli-watch.test.ts +++ b/packages/sdk/tests/cli-watch.test.ts @@ -40,11 +40,11 @@ async function project(): Promise<{ directory: string; path: string }> { return { directory, path }; } -function start(path: string, json = false) { +function start(path: string, json = false, flags: string[] = [], environment: NodeJS.ProcessEnv = {}) { // The package's flows binary is produced by the ordinary SDK build. - const child = spawn(process.env['BUN_BIN'] ?? 'bun', ['run', 'flows', 'check', '--watch', ...(json ? ['--json'] : []), path], { + const child = spawn(process.env['BUN_BIN'] ?? 'bun', ['run', 'flows', 'check', '--watch', ...flags, ...(json ? ['--json'] : []), path], { cwd: sdk, - env: { ...process.env, PATH: `${bin}:${process.env['PATH']}` }, + env: { ...process.env, ...environment, PATH: `${bin}:${process.env['PATH']}` }, }); children.push(child); let stdout = ''; @@ -71,6 +71,16 @@ async function touch(path: string): Promise { } describe('flows check --watch', () => { + it('forwards required daemon validation through watch despite the offline test environment', async () => { + const { path } = await project(); + // The test environment selects offline; an explicit flag must survive the child. + const running = start(path, true, ['--against-daemon'], { RELAYFLOWD_BIN: '/missing/watch-relayflowd' }); + await until(() => running.reports().length > 0); + expect(running.reports()[0].ok).toBe(false); + expect(running.reports()[0].validation.reason).toBe('relayflowd_bin_invalid'); + await stop(running.child); + }); + it('rechecks syntax errors, clears once, and returns the last refusal on Ctrl-C', async () => { const { path } = await project(); const output = start(path); diff --git a/packages/sdk/tests/cli.test.ts b/packages/sdk/tests/cli.test.ts index c8b6138ad..bc13755e8 100644 --- a/packages/sdk/tests/cli.test.ts +++ b/packages/sdk/tests/cli.test.ts @@ -666,6 +666,7 @@ steps: expect(pass.code).toBe(0); expect(JSON.parse(pass.stdout.join('\n')) as CheckReport).toEqual({ ok: true, + validation: { mode: 'local', reason: 'disabled' }, path: passPath, projectConfigPath: configPath, gates: [{ @@ -688,6 +689,7 @@ steps: const report = JSON.parse(refusal.stdout.join('\n')) as CheckReport; expect(report).toEqual({ ok: false, + validation: { mode: 'local', reason: 'not_reached' }, path: refusalPath, projectConfigPath: configPath, gates: [{ diff --git a/packages/sdk/tests/live-kernel.test.ts b/packages/sdk/tests/live-kernel.test.ts index a51786f17..c30b4423f 100644 --- a/packages/sdk/tests/live-kernel.test.ts +++ b/packages/sdk/tests/live-kernel.test.ts @@ -2057,3 +2057,18 @@ function isObject(value: unknown): value is Record { function delay(ms: number): Promise { return new Promise((resolveDelay) => setTimeout(resolveDelay, ms)); } + + +it('validates the canonical ladder with the built daemon without running it', () => { + for (const name of ['hello-deterministic', 'hello-llm', 'hello-agent']) { + const checked = checkFlow(join(TESTDATA, `${name}.flow.yaml`), { + daemonValidation: 'required', + daemonValidationDeps: { + resolveBinary: () => RELAYFLOWD, + run: (binary, input) => spawnSync(binary, ['validate-spec'], { input, encoding: 'utf8' }), + }, + }); + expect(checked.report.ok, JSON.stringify(checked.report)).toBe(true); + expect(checked.report.validation?.mode).toBe('daemon'); + } +}); diff --git a/packages/sdk/tests/relay-cli-surface.test.ts b/packages/sdk/tests/relay-cli-surface.test.ts index cbb9b27d5..c5289cc04 100644 --- a/packages/sdk/tests/relay-cli-surface.test.ts +++ b/packages/sdk/tests/relay-cli-surface.test.ts @@ -77,6 +77,8 @@ const INVOCATIONS: readonly { verb: string; argv: readonly string[]; variant: Pa { verb: 'build', argv: ['build', '--verify', '--json', BUNDLE_DIR], variant: 'build' }, { verb: 'build', argv: ['build', BUNDLE_DIR, '--verify'], variant: 'build' }, { verb: 'check', argv: ['check', 'flow.yaml'], variant: 'check' }, + { verb: 'check', argv: ['check', '--against-daemon', 'flow.yaml'], variant: 'check' }, + { verb: 'check', argv: ['check', '--no-daemon-check', 'flow.yaml'], variant: 'check' }, { verb: 'check', argv: ['check', '--watch', '--json', 'flow.yaml'], variant: 'check' }, // Both `deploy` forms: the positional decides which variant the verb produces. { verb: 'deploy', argv: ['deploy', DIGEST, '--to', 'file:///tmp/bucket'], variant: 'deploy' }, diff --git a/packages/sdk/tsconfig.tests.json b/packages/sdk/tsconfig.tests.json index 773c991c6..3390d6f8f 100644 --- a/packages/sdk/tsconfig.tests.json +++ b/packages/sdk/tsconfig.tests.json @@ -12,6 +12,7 @@ "include": [ "src/**/*.ts", "tests/isolate-workspace.ts", + "tests/check-daemon-validation.test.ts", "tests/runtime-dependencies.test.ts", "tests/cli-package-gate.test.ts", "tests/run-projection.test.ts", diff --git a/packages/sdk/vitest.config.ts b/packages/sdk/vitest.config.ts index 04c7db2fc..965ae127a 100644 --- a/packages/sdk/vitest.config.ts +++ b/packages/sdk/vitest.config.ts @@ -3,6 +3,7 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { environment: 'node', + env: { FLOWS_NO_DAEMON_CHECK: '1' }, include: ['tests/**/*.test.ts'], globals: false, setupFiles: ['tests/isolate-workspace.ts'], From dfb8700b68cc18fac930e1aa0f69063d13d5edd5 Mon Sep 17 00:00:00 2001 From: Relayflow Date: Tue, 6 Oct 2026 15:16:31 +0000 Subject: [PATCH 2/3] test(check): pin the installed daemon's own refusal reaching flows check The acceptance case for #489's class is that a field the installed relayflowd rejects is refused by `flows check`, naming the same field and step. The stubbed-daemon cases pin the diagnostic shaping; the live case only pinned acceptance of the canonical ladder. This adds the negative half against the built binary: the field is injected into the submitted JSON (no such skew exists at this commit, since both halves ship from it), the real validator refuses it, and the report must name `steps[1]`, map it to the authored step `edit`, and not blame the compiler for an authored step. Mutation-verified: evidence/daemon-check/live-skew-{mutated,restored}.txt. Co-Authored-By: Claude Opus 5 --- evidence/daemon-check/README.md | 9 ++++ evidence/daemon-check/live-skew-mutated.txt | 44 ++++++++++++++++++++ evidence/daemon-check/live-skew-restored.txt | 17 ++++++++ packages/sdk/tests/live-kernel.test.ts | 30 +++++++++++++ 4 files changed, 100 insertions(+) create mode 100644 evidence/daemon-check/live-skew-mutated.txt create mode 100644 evidence/daemon-check/live-skew-restored.txt diff --git a/evidence/daemon-check/README.md b/evidence/daemon-check/README.md index 387e730a7..2d72382b1 100644 --- a/evidence/daemon-check/README.md +++ b/evidence/daemon-check/README.md @@ -57,6 +57,15 @@ All `.txt` files contain captured output, not reconstructed output. [Captured output](live-cli.txt) +`cd packages/sdk && RELAYFLOWD_BIN=/kernel/target/release/relayflowd ./node_modules/.bin/vitest run tests/live-kernel.test.ts -t 'naming the step the daemon named'` + +Mutation verification for the live skew case: `stepId` mapping removed from +`daemon-spec-validation.ts`, the test run and its failure captured, the file +restored byte-for-byte (`git diff --exit-code` clean) and the test re-run. + +[Captured output, mutated](live-skew-mutated.txt) · +[Captured output, restored](live-skew-restored.txt) + `python3 evidence/daemon-check/mutation-run.py` [Captured output](mutations.txt) diff --git a/evidence/daemon-check/live-skew-mutated.txt b/evidence/daemon-check/live-skew-mutated.txt new file mode 100644 index 000000000..807e1f459 --- /dev/null +++ b/evidence/daemon-check/live-skew-mutated.txt @@ -0,0 +1,44 @@ +$ sed -i "s/const stepId = index === undefined ? undefined : submitted.steps[Number(index)]?.id;/const stepId: string | undefined = undefined;/" packages/sdk/src/daemon-spec-validation.ts +$ cd packages/sdk && RELAYFLOWD_BIN=$PWD/../../kernel/target/release/relayflowd ./node_modules/.bin/vitest run tests/live-kernel.test.ts -t "naming the step the daemon named" + + RUN v2.1.9 /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk + +stdout | tests/live-kernel.test.ts +LIVE_KERNEL relayflowd=/home/daytona/.relayflow-v2-supervisor/durable/repository/kernel/target/release/relayflowd +LIVE_KERNEL flows=/home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/dist/cli.js + + ❯ tests/live-kernel.test.ts (34 tests | 1 failed | 33 skipped) 61ms + × refuses a field the built daemon does not know, naming the step the daemon named 59ms + → expected { severity: 'refusal', …(3) } to match object { Object (kind, stepId) } +(3 matching properties omitted from actual) + +⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯ + + FAIL tests/live-kernel.test.ts > refuses a field the built daemon does not know, naming the step the daemon named +AssertionError: expected { severity: 'refusal', …(3) } to match object { Object (kind, stepId) } +(3 matching properties omitted from actual) + +- Expected ++ Received + + Object { + "kind": "invalid_spec", +- "stepId": "edit", + } + + ❯ tests/live-kernel.test.ts:2101:19 + 2099| // The daemon names the field and its own index; the report adds the… + 2100| // step id for that index, and claims no compiler bug for an authore… + 2101| expect(refusal).toMatchObject({ kind: 'invalid_spec', stepId: 'edit'… + | ^ + 2102| expect(refusal?.message).toContain('unknown field "from_a_newer_sdk"… + 2103| expect(refusal?.message).not.toContain('generated by this compiler'); + +⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯ + + Test Files 1 failed (1) + Tests 1 failed | 33 skipped (34) + Start at 15:16:07 + Duration 1.92s (transform 1.06s, setup 20ms, collect 1.67s, tests 61ms, environment 0ms, prepare 44ms) + +exit=1 diff --git a/evidence/daemon-check/live-skew-restored.txt b/evidence/daemon-check/live-skew-restored.txt new file mode 100644 index 000000000..ac8f2a879 --- /dev/null +++ b/evidence/daemon-check/live-skew-restored.txt @@ -0,0 +1,17 @@ +$ git checkout -- packages/sdk/src/daemon-spec-validation.ts # restored byte-for-byte; git diff --exit-code is clean +$ cd packages/sdk && RELAYFLOWD_BIN=$PWD/../../kernel/target/release/relayflowd ./node_modules/.bin/vitest run tests/live-kernel.test.ts -t "naming the step the daemon named" + + RUN v2.1.9 /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk + +stdout | tests/live-kernel.test.ts +LIVE_KERNEL relayflowd=/home/daytona/.relayflow-v2-supervisor/durable/repository/kernel/target/release/relayflowd +LIVE_KERNEL flows=/home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/dist/cli.js + + ✓ tests/live-kernel.test.ts (34 tests | 33 skipped) 61ms + + Test Files 1 passed (1) + Tests 1 passed | 33 skipped (34) + Start at 15:16:15 + Duration 2.59s (transform 1.45s, setup 24ms, collect 2.31s, tests 61ms, environment 0ms, prepare 50ms) + +exit=0 diff --git a/packages/sdk/tests/live-kernel.test.ts b/packages/sdk/tests/live-kernel.test.ts index c30b4423f..469b420ad 100644 --- a/packages/sdk/tests/live-kernel.test.ts +++ b/packages/sdk/tests/live-kernel.test.ts @@ -2072,3 +2072,33 @@ it('validates the canonical ladder with the built daemon without running it', () expect(checked.report.validation?.mode).toBe('daemon'); } }); + +it('refuses a field the built daemon does not know, naming the step the daemon named', () => { + // The skew this gate exists for: packages that emit a field the installed + // relayflowd predates (#489's `cwd`). No such field exists at this commit — + // both halves ship from it — so inject one into the submitted JSON and let + // the REAL binary judge it. check-daemon-validation.test.ts pins the + // diagnostic shaping against a stub; this pins that the installed + // validator's own refusal is what reaches `flows check`. + const checked = checkFlow(join(TESTDATA, 'hello-agent.flow.yaml'), { + daemonValidation: 'required', + daemonValidationDeps: { + resolveBinary: () => RELAYFLOWD, + run: (binary, input) => { + const submitted = JSON.parse(input) as { steps: Record[] }; + submitted.steps[1]!['from_a_newer_sdk'] = true; + return spawnSync(binary, ['validate-spec'], { + input: JSON.stringify(submitted), encoding: 'utf8', + }); + }, + }, + }); + expect(checked.report.ok, JSON.stringify(checked.report)).toBe(false); + expect(checked.report.validation?.mode).toBe('daemon'); + const refusal = checked.report.diagnostics.find((entry) => entry.severity === 'refusal'); + // The daemon names the field and its own index; the report adds the authored + // step id for that index, and claims no compiler bug for an authored step. + expect(refusal).toMatchObject({ kind: 'invalid_spec', stepId: 'edit' }); + expect(refusal?.message).toContain('unknown field "from_a_newer_sdk" at steps[1]'); + expect(refusal?.message).not.toContain('generated by this compiler'); +}); From a0b66c6a88974c6b01b9772534a93765df95e769 Mon Sep 17 00:00:00 2001 From: agentrelaybot Date: Wed, 7 Oct 2026 23:47:56 -0700 Subject: [PATCH 3/3] Require exit 0 before treating a validate-spec verdict as daemon acceptance spawnSync reports no error for a nonzero exit or a signal, so a validator that printed an accepting envelope and then failed or was killed was reported as validation.mode: daemon. That now yields abnormal_exit (refusal in required mode, daemon_unvalidated warning in auto mode). Daemon refusals are still reported whatever the exit status. Co-Authored-By: Claude Opus 5.5 (1M context) Session-Id: 0e078a4c-f89d-4e74-9919-00e1e9a08879 --- docs/SURFACE.md | 5 ++++- packages/sdk/src/daemon-spec-validation.ts | 13 ++++++++--- .../sdk/tests/check-daemon-validation.test.ts | 22 +++++++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/docs/SURFACE.md b/docs/SURFACE.md index 1e87fbae6..482896e67 100644 --- a/docs/SURFACE.md +++ b/docs/SURFACE.md @@ -1977,7 +1977,10 @@ The annotation names the binary that judged the spec. An already running daemon from a different build may disagree; this check does not contact that service. Older binaries (including 2.0.19) lack `validate-spec`: automatic mode warns `daemon_unvalidated`, naming the binary and the unproven acceptance, while -required mode refuses. This catches skew going forward, not retroactively. +required mode refuses. The same applies to a validator that exits nonzero or +is killed after printing an accepting verdict (`abnormal_exit`): acceptance +requires both the verdict and exit 0. A daemon refusal is reported whatever the +exit status. This catches skew going forward, not retroactively. Authored TypeScript bodies are not lowered until execution. Their header checks remain local, with `authored_body` as the validation reason (required mode diff --git a/packages/sdk/src/daemon-spec-validation.ts b/packages/sdk/src/daemon-spec-validation.ts index 9addef6ce..edeb50998 100644 --- a/packages/sdk/src/daemon-spec-validation.ts +++ b/packages/sdk/src/daemon-spec-validation.ts @@ -8,7 +8,7 @@ import type { CheckReport } from './cli/check.js'; export type DaemonValidationMode = 'auto' | 'required' | 'off'; type Unavailable = 'disabled' | 'not_reached' | 'authored_body' | 'relayflowd_not_found' - | 'relayflowd_bin_invalid' | 'no_verdict' | 'spawn_failed' | 'timed_out' | 'protocol_mismatch'; + | 'relayflowd_bin_invalid' | 'no_verdict' | 'spawn_failed' | 'timed_out' | 'protocol_mismatch' | 'abnormal_exit'; export interface DaemonValidation { mode: 'daemon' | 'local'; binary?: string; @@ -19,7 +19,7 @@ export interface DaemonValidation { export interface DaemonValidationDeps { resolveBinary(): string; run(binary: string, input: string): { - status: number | null; stdout: string; stderr: string; error?: Error; + status: number | null; signal?: NodeJS.Signals | null; stdout: string; stderr: string; error?: Error; }; } type Result = { validation: DaemonValidation; diagnostics: CheckReport['diagnostics'] }; @@ -91,7 +91,14 @@ export function validateSpecWithDaemon( `validator protocol ${verdict.protocol}, SDK protocol ${PROTOCOL_VERSION}`, binary); const validation: DaemonValidation = { mode: 'daemon', binary, protocol: verdict.protocol, specVersion: verdict.spec_version }; - if (verdict.ok) return { validation, diagnostics: [] }; + if (verdict.ok) { + // Acceptance is the envelope AND exit 0; a validator that fails or is + // killed after printing it has not completed validation. + if (result.status !== 0) return unavailableValidation(mode, 'abnormal_exit', + `validator ${result.status === null ? `was terminated by ${result.signal ?? 'a signal'}` : `exited ${result.status}`} after an accepting verdict`, binary); + return { validation, diagnostics: [] }; + } + // A refusal fails closed whatever the exit status, so it is reported as-is. const message = verdict.error.message; const index = /steps\[(\d+)\]/.exec(message)?.[1]; const stepId = index === undefined ? undefined : submitted.steps[Number(index)]?.id; diff --git a/packages/sdk/tests/check-daemon-validation.test.ts b/packages/sdk/tests/check-daemon-validation.test.ts index e363290f9..6a829713b 100644 --- a/packages/sdk/tests/check-daemon-validation.test.ts +++ b/packages/sdk/tests/check-daemon-validation.test.ts @@ -77,6 +77,8 @@ it.each([ { status: null, stdout: '', stderr: '', error: Object.assign(new Error('timeout'), { code: 'ETIMEDOUT' }), reason: 'timed_out' }, { status: null, stdout: '', stderr: '', error: new Error('spawn'), reason: 'spawn_failed' }, { status: 0, stdout: JSON.stringify({ ...envelope(), protocol: 99 }), stderr: '', reason: 'protocol_mismatch' }, + { status: 2, stdout: JSON.stringify(envelope()), stderr: '', reason: 'abnormal_exit' }, + { status: null, signal: 'SIGTERM' as const, stdout: JSON.stringify(envelope()), stderr: '', reason: 'abnormal_exit' }, ])('reports $reason without claiming daemon acceptance', outcome => { for (const mode of ['auto', 'required'] as const) { const result = validateSpecWithDaemon(flow, mode, { ...deps(), run: () => outcome }); @@ -107,6 +109,26 @@ it('real CLI refuses skew in auto and required modes', () => { expect(result.stderr).toContain(`REFUSED [invalid_spec] ${message}`); } }); +it('keeps a daemon refusal whatever the validator exit status', () => { + const result = validateSpecWithDaemon(flow, 'auto', { ...deps(), + run: () => ({ status: null, signal: 'SIGTERM', stdout: JSON.stringify(envelope(message)), stderr: '' }) }); + expect(result.validation.mode).toBe('daemon'); + expect(result.diagnostics).toMatchObject([{ severity: 'refusal', kind: 'invalid_spec', stepId: 'first' }]); +}); + +it.each([['exit 2', 'process.exitCode=2'], ['SIGTERM', "process.kill(process.pid,'SIGTERM')"]])( + 'real CLI does not claim daemon acceptance after an accepting verdict then %s', (_label, ending) => { + const f = fixture(); + writeFileSync(f.binary, `#!${process.execPath}\nprocess.stdin.resume();process.stdin.on('end',()=>{process.stdout.write(${JSON.stringify(JSON.stringify(envelope()) + '\n')},()=>{${ending};});});\n`, { mode: 0o755 }); + writeFileSync(f.path, JSON.stringify({ ...flow, steps: [{ id: 'a', type: 'deterministic', command: 'echo ok' }] })); + const required = cli(f.path, f.binary, ['--against-daemon', '--json']); + expect(required.status, required.stderr).toBe(2); + expect(JSON.parse(required.stdout)).toMatchObject({ ok: false, validation: { mode: 'local', reason: 'abnormal_exit' } }); + const auto = cli(f.path, f.binary, ['--json']); + expect(JSON.parse(auto.stdout)).toMatchObject({ validation: { mode: 'local', reason: 'abnormal_exit' }, + diagnostics: expect.arrayContaining([expect.objectContaining({ severity: 'warning', kind: 'daemon_unvalidated' })]) }); + }); + it('real CLI labels daemon and offline passes, including JSON', () => { const f = fixture(); expect(cli(f.path, f.binary, ['--no-daemon-check']).stdout).toContain('[local only: disabled');