Skip to content

flows check refuses a Cloud-bound f.slack flow locally (helper_slack.credential_missing) before printing REQUIRES - #618

Merged
khaliqgant merged 44 commits into
mainfrom
relayflow/flows-software-garden-020f6161
Oct 8, 2026
Merged

khaliqgant merged 44 commits into
mainfrom
relayflow/flows-software-garden-020f6161

Conversation

@agent-relay-code

@agent-relay-code agent-relay-code Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Fix: let flows check report Cloud-bound helper requirements without local mounts

A laptop without Slack mounted can inspect an authored Slack flow: check reports
REQUIRES slack and helper_credential_unresolved, then passes. Local run retains
its credential refusal before daemon attachment. The same rule applies to YAML
helpers, whose requirements are now derived from compiled steps with an authoring
fallback for invalid specs.

The warning is opt-in at the check surface, emitted once after REQUIRES.
Non-.flow.* modules receive a self-contained warning without a requirements line.
Unsupported providers and Notion appendBlock remain refusals even without mounts.
Hosted submission, execution, resume, and pure preflight retain their existing
credential checks. Watch inherits the CLI behavior through its child process.

Implementation follows reviewed-plan.md Parts A and B as separate commits.
The two existing authored Slack assertions were retargeted because their check
behavior changes; their executeAuthoredFlow rejection assertions remain intact.
No quality gates or GitHub workflow files were edited.

Validation and limits:

  • 269 regression tests and 3 daemon-free authored tests passed; build and both
    TypeScript checks passed. Commands and literal output follow.
  • Warning placement and Notion refusal were mutation-verified: each specific
    source change was reverted, its test failed, the source bytes were restored,
    and the same test passed. Both outputs are below.
  • The named-agent and unresolved-use requirements comparison preserved the
    codex requirement. The use fixture is deliberately invalid; it verifies the
    compile-failure fallback, not successful import expansion.
  • The exact example passes check and refuses local run with a temporary
    package.json module boundary. The helper script restores the checkout.
    Without that boundary, this checkout's Node import fails before preflight
    (example-check.txt and example-run.txt). An attempted Node module flag was
    unsupported (example-*-module.txt); no loader fix is included.
  • Full SDK tests were not run: Cargo and relayflowd are unavailable.
    The initial broader command failed direct-input.test.ts setup for the absent
    binary; its literal output is in evidence/helper-check/regression.txt, alongside
    an initial YAML test argument error corrected before the final run.
    The authored subset skips ten daemon-dependent cases: Slack structured posts,
    journaled effect, confirm/complete SIGKILL replay, and multi-call writeback;
    generic helper acceptance, all-provider resume, confirm/complete SIGKILL replay,
    and provider-failure journaling. Those crash gates remain unverified here.

Captured evidence (paths relative to the repository):

evidence/helper-check/build.txt

$ npm --prefix packages/sdk run build

> @relayflows/sdk@2.0.42 build
> tsc && node scripts/make-cli-executable.mjs


evidence/helper-check/typecheck.txt

$ npm --prefix packages/sdk run typecheck

> @relayflows/sdk@2.0.42 typecheck
> tsc --noEmit && tsc -p tsconfig.type-tests.json


evidence/helper-check/typecheck-tests.txt

$ npm --prefix packages/sdk run typecheck:tests

> @relayflows/sdk@2.0.42 typecheck:tests
> tsc -p tsconfig.tests.json


evidence/helper-check/regression-final.txt

$ cd packages/sdk && npx vitest run tests/check-helper-surface.test.ts tests/check-worker-surface.test.ts tests/preflight.test.ts tests/helpers-fanout.test.ts tests/helper-reference.test.ts tests/flow-requirements.test.ts tests/flow-extension-compose.test.ts

 RUN  v2.1.9 /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk

 ✓ tests/helper-reference.test.ts (30 tests) 22ms
 ✓ tests/preflight.test.ts (70 tests) 100ms
 ✓ tests/check-worker-surface.test.ts (11 tests) 88ms
 ✓ tests/flow-requirements.test.ts (14 tests) 1030ms
   ✓ flows check prints REQUIRES > names the helper, the harness and the mcp server of an authored flow 786ms
 ✓ tests/helpers-fanout.test.ts (96 tests) 156ms
 ✓ tests/check-helper-surface.test.ts (15 tests) 4150ms
   ✓ reports the declared integration once after REQUIRES and before CHECK PASSED 479ms
   ✓ keeps default and explicit opt-out checks strict, and local run refuses before attach 339ms
   ✓ preserves requirements when compilation refuses and for named agents 2248ms
 ✓ tests/flow-extension-compose.test.ts (33 tests) 5886ms
   ✓ composing flow extensions onto a base flow > composes two extensions in declaration order, and the order is the lockfile order 445ms
   ✓ composing flow extensions onto a base flow > flows check reports the composition and keeps the composed triggers deliverable 973ms
   ✓ composing flow extensions onto a base flow > flows check probes extension preflight before reporting the project healthy 305ms
   ✓ composing flow extensions onto a base flow > uses extension permissions for hosted deploy preflight and the deploy body 322ms

 Test Files  7 passed (7)
      Tests  269 passed (269)
   Start at  13:40:37
   Duration  7.93s (transform 1.99s, setup 57ms, collect 6.67s, tests 11.43s, environment 1ms, prepare 287ms)


evidence/helper-check/authored-subset.txt

$ cd packages/sdk && npx vitest run tests/authored-flow-slack.test.ts tests/authored-helpers.test.ts -t 'refuses missing credentials|on `.flow.ts` paths too|rejects malformed arguments'

 RUN  v2.1.9 /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk

 ✓ tests/authored-helpers.test.ts (6 tests | 5 skipped) 28ms
 ✓ tests/authored-flow-slack.test.ts (7 tests | 5 skipped) 727ms
   ✓ authored Slack helper effects > refuses missing credentials before running the body while flows check warns 511ms

 Test Files  2 passed (2)
      Tests  3 passed | 10 skipped (13)
   Start at  13:40:46
   Duration  2.58s (transform 1.11s, setup 27ms, collect 2.85s, tests 755ms, environment 0ms, prepare 112ms)


evidence/helper-check/requirements.txt

$ node evidence/helper-check/requirements.mjs
named-agent before: REQUIRES codex (step "review")
named-agent after: REQUIRES codex (step "review")
named-agent refusals: model_unavailable
use before: REQUIRES codex (step "review")
use after: REQUIRES codex (step "review")
use refusals: invalid_spec

evidence/helper-check/example-module-boundary.txt

$ python3 evidence/helper-check/example.py
$ env -u SLACK_BOT_TOKEN -u RELAYFLOWS_SLACK_MOCK -u RELAYFILE_MOUNT_PATH -u WORKSPACE_ROOT -u WORKFORCE_SANDBOX_ROOT -u RELAYFILE_MOUNT_ROOT -u RELAYFILE_ROOT node packages/sdk/dist/cli.js check examples/stale-issues/stale-issues.flow.ts
REQUIRES slack (tools.slack), claude (llm step)
WARNING [helper_credential_unresolved] f.slack needs a slack mount, which is not available locally. flows schedule / flows deploy / flows run --cloud check the integration against your workspace at submit and refuse if Cloud cannot connect it. A local flows run needs a relayfile slack mount (a slack/ directory under RELAYFILE_MOUNT_PATH) or RELAYFLOWS_SLACK_MOCK=1, and refuses with [helper_slack.credential_missing] without one.
CHECK PASSED examples/stale-issues/stale-issues.flow.ts
exit=0
$ env -u SLACK_BOT_TOKEN -u RELAYFLOWS_SLACK_MOCK -u RELAYFILE_MOUNT_PATH -u WORKSPACE_ROOT -u WORKFORCE_SANDBOX_ROOT -u RELAYFILE_MOUNT_ROOT -u RELAYFILE_ROOT node packages/sdk/dist/cli.js run examples/stale-issues/stale-issues.flow.ts --input '{"repo":"acme/api","channel":"#eng"}'
REFUSED [helper_slack.credential_missing] f.slack requires a relayfile slack mount; direct-token transport is not implemented.
exit=2

evidence/helper-check/mutation-placement-fail.txt

$ npx vitest run tests/check-helper-surface.test.ts -t reports the declared integration once

 RUN  v2.1.9 /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk

 ❯ tests/check-helper-surface.test.ts (11 tests | 1 failed | 10 skipped) 542ms
   × reports the declared integration once after REQUIRES and before CHECK PASSED 541ms
     → expected 0 to be greater than 1

⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯

 FAIL  tests/check-helper-surface.test.ts > reports the declared integration once after REQUIRES and before CHECK PASSED
AssertionError: expected 0 to be greater than 1
 ❯ tests/check-helper-surface.test.ts:57:19
     55|   const passed = result.lines.findIndex(line => line.includes('CHECK P…
     56|   expect(requires).toBeGreaterThanOrEqual(0);
     57|   expect(warning).toBeGreaterThan(requires);
       |                   ^
     58|   expect(passed).toBeGreaterThan(warning);
     59|   expect(result.stderr.filter(line => line.includes('[helper_credentia…

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯

 Test Files  1 failed (1)
      Tests  1 failed | 10 skipped (11)
   Start at  13:39:05
   Duration  2.45s (transform 1.09s, setup 16ms, collect 1.70s, tests 542ms, environment 0ms, prepare 42ms)


exit=1

evidence/helper-check/mutation-placement-pass.txt

$ npx vitest run tests/check-helper-surface.test.ts -t reports the declared integration once

 RUN  v2.1.9 /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk

 ✓ tests/check-helper-surface.test.ts (11 tests | 10 skipped) 577ms
   ✓ reports the declared integration once after REQUIRES and before CHECK PASSED 576ms

 Test Files  1 passed (1)
      Tests  1 passed | 10 skipped (11)
   Start at  13:39:08
   Duration  2.58s (transform 1.19s, setup 17ms, collect 1.80s, tests 577ms, environment 0ms, prepare 53ms)


exit=0

evidence/helper-check/mutation-notion-fail.txt

$ npx vitest run tests/check-helper-surface.test.ts -t still refuses unsupported notion

 RUN  v2.1.9 /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk

 ❯ tests/check-helper-surface.test.ts (11 tests | 1 failed | 10 skipped) 590ms
   × still refuses unsupported notion appendBlock without a mount 588ms
     → expected +0 to be 2 // Object.is equality

⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯

 FAIL  tests/check-helper-surface.test.ts > still refuses unsupported notion appendBlock without a mount
AssertionError: expected +0 to be 2 // Object.is equality

- Expected
+ Received

- 2
+ 0

 ❯ tests/check-helper-surface.test.ts:80:23
     78| ])('still refuses unsupported %s without a mount', async (_name, body)…
     79|   const result = await check(fixture(body));
     80|   expect(result.exit).toBe(2);
       |                       ^
     81|   expect(result.stderr.join('\n')).toContain('[helper_provider.unsuppo…
     82| });

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯

 Test Files  1 failed (1)
      Tests  1 failed | 10 skipped (11)
   Start at  13:39:11
   Duration  2.56s (transform 1.12s, setup 18ms, collect 1.72s, tests 590ms, environment 0ms, prepare 84ms)


exit=1

evidence/helper-check/mutation-notion-pass.txt

$ npx vitest run tests/check-helper-surface.test.ts -t still refuses unsupported notion

 RUN  v2.1.9 /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk

 ✓ tests/check-helper-surface.test.ts (11 tests | 10 skipped) 90ms

 Test Files  1 passed (1)
      Tests  1 passed | 10 skipped (11)
   Start at  13:39:14
   Duration  2.16s (transform 1.23s, setup 20ms, collect 1.87s, tests 90ms, environment 0ms, prepare 50ms)


exit=0

Checks

The checks fail on the base commit too, so these failures were not introduced by this change: they come from the repository itself or from the environment the checks ran in. This pull request is a draft until someone looks.

What ran (.relayflow/check.sh)
#!/bin/sh
# Reproduce this repository's CI checks on a fresh machine.
#
# Mirrors the GitHub Actions workflows that gate a pull request:
#   .github/workflows/cloud-runtime-artifact.yml      kernel + SDK + surface
#   .github/workflows/schema-publish.yml              its `validate` job
#   .github/workflows/surface-package.yml             its `packed-consumer` job
#   .github/workflows/review-swarm-wrapper-guard.yml  the two lens parity checks
#
# Deliberately NOT run here, and why:
#
#   * .github/workflows/publish.yml, and schema-publish.yml's version/publish
#     jobs: they npm-publish through a trusted publisher, off a release tag,
#     with registry credentials this machine does not have.
#   * .github/workflows/review-swarm.yml, and the wrapper guard's
#     swarm-wrapper-guard.sh step: both need cloud secrets (RELAY_API_KEY,
#     GH_TOKEN) and a live pull request to review.
#   * cloud-runtime-artifact.yml's trailing steps (build-standalone-cli.mjs,
#     cloud-artifact.mjs build/verify, the artifact smoke and upload): they
#     package and publish the Linux artifact after the suites have run, so they
#     are not checks that precede the tests. Their contract test,
#     scripts/cloud-artifact.test.mjs, is run below.
#   * packages/sdk tests/live-kernel.test.ts's live-analyzer case: it execs a
#     `claude` binary with model access. RELAYFLOWS_ALLOW_ANALYZER_SKIP=1 below
#     is what CI sets for exactly this reason, and it means the same thing
#     here that the workflow says it means: this run is not gate-2 acceptance
#     evidence. Everything else in the suite still runs.

set -e

repo_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
cd "$repo_root"

# ---------------------------------------------------------------- toolchain
#
# CI pins node 22 (actions/setup-node), bun 1.4.0 (oven-sh/setup-bun) and rust
# stable (dtolnay/rust-toolchain). node and npm come with the machine image, so
# require them rather than installing a second copy; rust usually does not, so
# install it the way ops/cargo.sh's fallback does.
for tool in node npm; do
  command -v "$tool" >/dev/null || {
    echo "check: $tool is required (CI pins node 22)" >&2
    exit 1
  }
done

# bun is pinned, not merely required. The machine image ships 1.3.6, and
# tests/authored-node-runtime.test.ts asserts `bun --version` is exactly the
# version every workflow's oven-sh/setup-bun pins -- the standalone build it
# exercises embeds that runtime, so a different bun is a different artifact.
# Install the pin beside the image copy (bun.sh/install is what setup-bun
# downloads from) and put it first on PATH, as setup-bun does.
bun_version=1.4.0
if [ "$(bun --version 2>/dev/null)" != "$bun_version" ]; then
  bun_install="$HOME/.bun-$bun_version"
  if [ ! -x "$bun_install/bin/bun" ]; then
    curl --proto '=https' --tlsv1.2 -fsSL https://bun.sh/install \
      | BUN_INSTALL="$bun_install" bash -s "bun-v$bun_version"
  fi
  PATH="$bun_install/bin:$PATH"
  export PATH
fi
command -v bun >/dev/null || {
  echo "check: bun is required (CI pins bun $bun_version)" >&2
  exit 1
}
bun --version

if ! command -v cargo >/dev/null; then
  if [ ! -x "$HOME/.cargo/bin/cargo" ]; then
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
      | sh -s -- -y --profile minimal --default-toolchain stable --no-modify-path
  fi
  PATH="$HOME/.cargo/bin:$PATH"
  export PATH
fi

# Plain `cargo` throughout, NOT ops/cargo.sh: that wrapper redirects RUSTUP_HOME
# at a private toolchain home which is empty here, leaving the rustup shim with
# no toolchain to choose. It exists for a cloud sandbox that drops large files
# between steps; this machine has neither that constraint nor that tree.
cargo --version

# The hosted-extension isolation tests exec /usr/bin/bwrap
# (packages/sdk/src/hosted-extension-sandbox.ts), so install bubblewrap as CI
# does.
if ! command -v bwrap >/dev/null && command -v sudo >/dev/null; then
  sudo apt-get update
  sudo apt-get install --yes --no-install-recommends bubblewrap
fi

# Best-effort, unlike CI, which runs on a dedicated ephemeral VM and can clear
# the restriction outright. Inside a container this sysctl is not writable, so
# unprivileged user namespaces stay blocked and bwrap cannot unshare at all:
#   bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted
# tests/hosted-extension-isolation.test.ts and
# tests/babysitter-native-extension.test.ts then fail for want of a kernel
# facility rather than for a defect in the tree. Attempted anyway because it is
# free where it works; `|| true` keeps set -e from aborting where it does not.
if command -v sudo >/dev/null \
  && sysctl kernel.apparmor_restrict_unprivileged_userns >/dev/null 2>&1; then
  sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true
fi

# ------------------------------------------------- dependencies and builds
#
# The authoring surface first: packages/sdk depends on it.
cd "$repo_root/packages/surface"
bun install --frozen-lockfile --ignore-scripts
bun run build

# --ignore-scripts because the surface is already built above; without it npm
# runs the file: dependency's prepare before its own devDependencies exist.
# The SDK's own build is below, so nothing is skipped.
cd "$repo_root"
npm ci --prefix packages/sdk --ignore-scripts

# `npm ci` installs @relayflows/surface from the REGISTRY, not the copy built
# two steps up, so an SDK change importing a symbol that has landed locally but
# is not yet published fails typecheck. Override the registry copy with the
# local directory; --no-save keeps the committed manifest unchanged. The `./`
# prefix is load-bearing -- without it npm reads the path as a GitHub shorthand.
npm install ./packages/surface --prefix packages/sdk --no-save --ignore-scripts

# workflows/*.flow.ts and `flows check` resolve @relayflows/surface from the
# repo root, where nothing is installed: everything above lands under
# packages/sdk. Link the same local surface at the root.
mkdir -p node_modules/@relayflows
ln -sfn ../../packages/sdk/node_modules/@relayflows/surface \
  node_modules/@relayflows/surface
node -e "console.log(require.resolve('@relayflows/surface'))"

# The release binary the SDK's live tests exec through RELAYFLOWD_BIN. CI builds
# it before the kernel suite and reuses it for both, which also avoids
# compiling a second debug copy.
cd "$repo_root/kernel"
cargo build --locked --release -p relayflowd

# Code generation: the committed schema must match what the generator emits,
# and emit identically twice (schema-publish.yml, "Regenerate and check
# committed schema").
cd "$repo_root"
first_schema=$(mktemp)
node scripts/generate-json-schema.mjs
git diff --exit-code -- packages/schema/flows.schema.json
cp packages/schema/flows.schema.json "$first_schema"
node scripts/generate-json-schema.mjs
diff -q "$first_schema" packages/schema/flows.schema.json
rm -f "$first_schema"

# ------------------------------------------------------------------- tests

# The cloud artifact's manifest and verifier contract.
cd "$repo_root"
node --test scripts/cloud-artifact.test.mjs

# The kernel: journal, scheduler, leases, durable timers, streams.
cd "$repo_root/kernel"
cargo test --workspace

# Schema parity and smoke.
cd "$repo_root/packages/schema"
bun run test

# The SDK: typecheck, build, test typecheck, then the whole vitest suite. The
# build is required, not redundant -- several test files fail at collection
# without packages/sdk/dist.
cd "$repo_root/packages/sdk"
# test:prep's surviving half (its other half shells out to ops/cargo.sh for a
# binary already built above): re-assert the executable bit on the preflight
# CLI fixtures, which are committed 100755. The failure it prevents is an
# opaque EACCES deep inside a preflight test.
[ ! -d "$repo_root/testdata/preflight" ] \
  || find "$repo_root/testdata/preflight" -name '*-cli' -type f -exec chmod +x {} +
npm run typecheck
npm run build
npm run typecheck:tests
RELAYFLOWS_ALLOW_ANALYZER_SKIP=1 \
  RELAYFLOWD_BIN="$repo_root/kernel/target/release/relayflowd" \
  ./node_modules/.bin/vitest run

# surface-package.yml: surface source tests, the regression typechecks,
# and both packed consumers (runtime and TypeScript). Last, because
# it re-runs `npm ci` under packages/sdk and swaps the local surface install
# for a freshly packed tarball.
cd "$repo_root"
bash scripts/surface-package-gate.sh

# review-swarm-wrapper-guard.yml's two parity guards. PRESWARM_ALLOW_MISSING_CLI
# as the workflow sets it: the CLI-presence half of the check belongs to the
# pre-swarm runner, not to a generic machine.
cd "$repo_root"
sh ops/preswarm-check/lens-parity-check.sh
PRESWARM_ALLOW_MISSING_CLI=1 sh ops/preswarm-check/lens-cli-parity-check.sh
Output on this branch (last 80 lines)
 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,

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[22/31]⎯

 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(),

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[23/31]⎯

⎯⎯⎯⎯⎯⎯ 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.<anonymous> src/hosted-extension-protocol.ts:234:21
 ❯ ChildProcess.emit node:events:520:22
 ❯ maybeClose node:internal/child_process:1084:16
 ❯ Socket.<anonymous> node:internal/child_process:456:11
 ❯ Socket.emit node:events:508:20
 ❯ Pipe.<anonymous> node:net:347:12

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
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  5 failed | 245 passed | 1 skipped (251)
      Tests  31 failed | 3828 passed | 4 skipped (3863)
     Errors  1 error
   Start at  14:19:58
   Duration  590.85s (transform 5.39s, setup 1.03s, collect 66.63s, tests 1639.42s, environment 30ms, prepare 9.43s)

Output on the base commit (last 80 lines)
1.4.0
cargo 1.99.0 (5f94df478 2026-08-27)
sysctl: permission denied on key "kernel.apparmor_restrict_unprivileged_userns"
/tmp/relayflow-recipe.LsHJ2G: 108: cd: can't cd to //packages/surface

What the repair agent found

Repair notes — .relayflow/check.sh on relayflow/flows-software-garden-020f6161

.relayflow/check.log recorded 6 failed vitest files / 31 failed tests. Three
distinct causes; one was missing setup and is fixed, two are facts about this
machine that no change to the tree can remove. Fixing the first then surfaced a
fourth — a pre-existing race between two test files — reported below, not fixed.

Fixed: bun was 1.3.6, CI pins 1.4.0 (missing setup)

tests/authored-node-runtime.test.ts failed at collection:

AssertionError: expected '1.3.6' to be '1.4.0' // Object.is equality
 ❯ tests/authored-node-runtime.test.ts:18:77

The machine image ships bun 1.3.6 at /usr/local/share/nvm/current/bin/bun;
every workflow pins 1.4.0 (oven-sh/setup-bun@v2, bun-version: "1.4.0" in
cloud-runtime-artifact.yml, surface-package.yml, schema-publish.yml,
publish.yml). The standalone artifact that test builds embeds the bun runtime,
so a different bun is a different artifact and the assertion is load-bearing.

.relayflow/check.sh now installs the pin the way setup-bun does — from
https://bun.sh/install into $HOME/.bun-1.4.0, first on PATH — instead of
merely requiring that some bun exist. Verified:

$ PATH="/home/daytona/.bun-1.4.0/bin:$PATH" ... vitest run tests/authored-node-runtime.test.ts
 ✓ tests/authored-node-runtime.test.ts (16 tests) 48354ms
 Test Files  1 passed (1)
      Tests  16 passed (16)

Not fixable here: bwrap cannot unshare in this container

24 of the 31 failures are tests/hosted-extension-isolation.test.ts (13),
tests/hosted-extension-protocol.test.ts (8),
tests/babysitter-native-extension.test.ts (1) and
tests/software-garden-babysitter-composition.test.ts (2), all with:

Error: Hosted extension sandbox exited without a valid completion (exit 1):
bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted

/usr/bin/bwrap is installed. What is missing is the kernel facility:
unprivileged user namespaces stay blocked because the sysctl is not writable
inside the container —

$ sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
sysctl: permission denied on key "kernel.apparmor_restrict_unprivileged_userns"

— which .relayflow/check.sh already documents and already attempts. CI runs
these on a dedicated ephemeral VM that can clear the restriction. No tree
change affects this; left alone.

Not fixable here: /home/daytona/package.json declares "type": "commonjs"

The remaining 7 failures are tests/live-kernel.test.ts agent/tick cases.
Each dispatches an agent step to one of the extensionless Node fixtures under
testdata/preflight/ (analyze-story-stub-cli, analyze-story-missing-fields-cli,
analyze-story-text-only-cli, analyze-story-echo-wake-cli,
wake-context-probe-cli, echo-model-cli, tick-slot-report-cli — all
#!/usr/bin/env node). The journal records the same shape every time:

"completionReason": "worker_error",
"output": null,
"verification": {
  "gate": "execution",
  "verdict": "fail",
  "detail": "{\"artifacts\":[],\"exit_code\":null,\"stderr_tail\":\"CLI
    \\\"<repo>/testdata/preflight/analyze-story-stub-cli\\\" exited before
    completing the relayflows-agent-cli-v1 same-process handshake.\",
    \"stdout_tail\":\"\"}"
}

The fixtures exit 0 having written nothing, so the handshake never starts.
Cause: this checkout lives under /home/daytona, and
/home/daytona/package.json (the supervisor harness's own manifest, outside the
repository) declares "type": "commonjs". That is the nearest package.json for
every extensionless file in the tree, so Node loads these ESM fixtures as
CommonJS and they produce nothing instead of their identify token. Same bytes,
same node (v25.6.0), run from a directory with no ancestor package.json:

$ cp -r testdata/preflight /tmp/pf-copy && cd /tmp/pf-copy
$ ./analyze-story-stub-cli --relayflows-adapter-v1 </dev/null
relayflows-agent-cli-v1

$ cd …/repository/testdata/preflight
$ ./analyze-story-stub-cli --relayflows-adapter-v1 </dev/null
(no output)

A GitHub runner checkout has no ancestor package.json, so CI never sees this.
Nothing in .relayflow/check.sh can undo it: Node resolves module type from
the file's own ancestry, not from cwd or any flag CI sets. The two ways out are
editing /home/daytona/package.json, which belongs to the harness and not to
this repository, or committing a testdata/preflight/package.json with
{"type":"module"} — a change to test fixtures that only this environment
needs. Left alone.

Surfaced by the bun pin, pre-existing, not fixed: a race on tests/fixtures/

With bun at the CI pin, tests/authored-node-runtime.test.ts runs for the first
time on this machine, and the run picks up one failure check.log did not have:

FAIL  tests/flow-executor-chain.test.ts > flow executor LLM and output-binding
      chain > runs a dollar-budgeted authored Claude agent with the same default
      used by preflight
Error: journal client: not connected (journal.read): journal client: closed
  after EACCES: permission denied, open
  '<repo>/packages/sdk/tests/fixtures/.5d7667b82ed153d0-00000000.bun-build'
 ❯ walk src/agent-artifacts.ts:68:15
 ❯ Module.snapshotWorkspaceFiles src/agent-artifacts.ts:37:3

It is not that test's defect, and not this branch's. bun build --compile
writes its intermediate output into the current working directory, mode
----------, for the length of the compile:

$ bun build --compile --env=disable --no-compile-autoload-dotenv \
    --no-compile-autoload-bunfig --outfile /tmp/bunperm/out a.ts &   # bun 1.4.0
$ ls -la /tmp/bunperm/.*bun-build
---------- 1 daytona daytona 80761952 Oct  6 14:14 /tmp/bunperm/.2eb700b79da25674-00000000.bun-build

bundle-typescript.ts:59 runs that build with the flow's own directory as cwd,
and tests/bundle.test.ts:285 builds packages/sdk/tests/fixtures/build.flow.ts
— so an 80 MB unreadable file exists inside the shared fixtures directory for
seconds. tests/flow-executor-chain.test.ts concurrently snapshots that same
directory as an agent workspace, walk opens the file, and the EACCES closes
the journal client and fails the run.

bun 1.3.6 does not leave that file in cwd (polled 200 times through a build; the
only artifacts were a.ts and the outfile), which is why the pin surfaced it.
CI pins 1.4.0 too, so CI is exposed to the same race.

Reproduces only under full-suite parallelism. The file alone, and the two
colliding files together, both pass:

$ ./node_modules/.bin/vitest run tests/flow-executor-chain.test.ts
 ✓ tests/flow-executor-chain.test.ts (14 tests) 9521ms
 Test Files  1 passed (1)
      Tests  14 passed (14)

$ for i in 1 2 3; do ./node_modules/.bin/vitest run \
    tests/flow-executor-chain.test.ts tests/bundle.test.ts; done
      Tests  40 passed (40)
      Tests  40 passed (40)
      Tests  40 passed (40)

Left unfixed, deliberately. The two candidate fixes both reach outside this
task: stage bundle.test.ts's input in a temp directory instead of the shared
tests/fixtures/ (an unrelated test), or make snapshotWorkspaceFiles skip
entries it cannot read (a change to what a workspace snapshot promises, which
RFC-0001's fail-closed rail makes a decision rather than a cleanup). Reported
here so it is a decision and not an oversight.

Also observed (not a failure)

testdata/preflight/signal-probe-cli ends in kill -SEGV "$$" by design, and
this machine's /proc/sys/kernel/core_pattern is the literal core, so running
the suite drops an untracked testdata/preflight/core dump beside the fixture.
It is test debris, not a defect; deleted, not committed, not gitignored.

Gates that the aborted run never reached

set -e stopped .relayflow/check.sh at the SDK vitest step, so the three
steps after it were unverified in check.log. All three pass here:

$ cd packages/schema && bun run test            -> ok
$ bash scripts/surface-package-gate.sh          -> PACKED_RUNTIME_OK /
   PACKED_RUNTIME_REFUSAL_OK invalidHeaders=9 forgedHandle=refused /
   PACKED_TYPESCRIPT_OK, tests/authored-flow.test.ts 38 passed
$ sh ops/preswarm-check/lens-parity-check.sh
lens-parity-check: PASS — all three lenses carry every canonical clause.
$ PRESWARM_ALLOW_MISSING_CLI=1 sh ops/preswarm-check/lens-cli-parity-check.sh
lens-cli-parity-check: PASS — three lenses, runner and swarm agree, every CLI present.

Where the suite stands after the setup fix

$ cd packages/sdk && npm run typecheck && npm run typecheck:tests \
  && RELAYFLOWS_ALLOW_ANALYZER_SKIP=1 RELAYFLOWD_BIN=.../relayflowd ./node_modules/.bin/vitest run
 Test Files  6 failed | 244 passed | 1 skipped (251)
      Tests  32 failed | 3826 passed | 4 skipped (3862)

Was 31 failed / 3811 passed / 20 skipped. The 16 formerly-skipped
authored-node-runtime tests now run and pass; the one new failure is the
tests/fixtures/ race above. The remaining 31 are the bwrap group (24, across 4 files)
and the live-kernel Node-fixture group (7, 1 file) — the two environment causes.

RELAYFLOWS_ALLOW_ANALYZER_SKIP=1 is set here as CI sets it, so, as
.relayflow/check.sh already says, this run is not gate-2 acceptance evidence.

Fixes #473


Summary by cubic

flows check no longer refuses a Cloud-bound authored flow that uses f.slack when the laptop has no local helper mount: the flow passes, reports the integration under REQUIRES, and emits a single helper_credential_unresolved warning after it. Local flows run still refuses before executing the body, and Cloud submission still checks the workspace integration at submit.

  • YAML helpers get the same check behavior; RELAY_API_KEY refusals and the budget warning no longer depend on whether the helper is mounted, though a warning path never admits the flow for execution. A YAML flow whose only refusal was the downgraded mount still runs the daemon validation gate, so --against-daemon cannot pass without a kernel verdict.
  • Unsupported providers (airtable, Notion appendBlock) and unknown diagnostic shapes remain refusals. appendBlock is detected from the parse via an allowlist of read-only positions: direct use stays precise, while aliased, destructured, computed, string-keyed, handed-into-a-call, run through eval/Function, or reached through the root function's arguments (regardless of its parameter shape) keeps the refusal with an explanation; a body with no named context parameter is unprovable. Aliases are followed, so a plain alias calling only supported methods passes and guard reads (f.notion && …) do not over-refuse. Discarded reads (comma operands, ${f.notion} interpolation, comparisons) do not hand the value on; a tagged template does. Any inherited Object.prototype member on f.notion — including one reached through a destructured binding — prototype machinery (prototype, __proto__, constructor, defineProperty, set/getPrototypeOf, toPrimitive, __lookupGetter__/__lookupSetter__, Reflect, Proxy), eval, or Function anywhere in the body also makes the call unprovable — except when an enclosing scope of that use binds the name, or for a prototype member of a class or function the body defines itself and never reassigns. Static string-keyed members count like their dotted forms. An unshadowed use of Object/Symbol/Reflect/Function/globalThis/Proxy is accepted only as a static non-machinery member (Object.keys, Symbol.iterator); any other use — alias, argument, destructuring, computed key — makes the call unprovable. Globals reached through any global-object root — globalThis, global, window, self, or nested root chains — follow the same rule, so extracting one via a root is unprovable, while dynamic indexing of ordinary values and ordinary root members (global.setTimeout) stay allowed. Any unshadowed use of eval counts, including indirect forms ((0, eval)(…), const run = eval, globalThis.eval).
  • Cloud requirement reading covers every authored flow extension (.flow.mjs, .flow.mts, .flow.js). Cloud submission refuses those sources as unsupported, and ensureFlowConnections returns before prompting or connecting for them; .flow.ts submissions keep the workspace-integration check at submit.
  • An activity refusal no longer discards helper diagnostics and integration requirements; check keeps both alongside the activity refusal. An early trigger-leg failure likewise no longer drops them: the composed check keeps helper diagnostics unless the trigger report repeats the same kind and message.

Checks

Suites fail on the base commit too; failures are environmental — bwrap user namespaces blocked in containers and live-kernel fixtures under a "type": "commonjs" ancestor package.json — plus a pre-existing tests/fixtures/ race between bundle.test.ts and flow-executor-chain.test.ts under the pinned bun 1.4.0. Build, typechecks, schema, surface-package, and lens-parity gates pass locally. The helper-surface suite is split by concern across four test files with shared staging in helper-surface-fixture.ts.

Written for commit 2b03dd4. Summary will update on new commits.

View guided diff Turn on auto-fix


Note

Medium Risk
Changes preflight/check semantics for helper credentials and adds substantial static analysis in the check path; execution and cloud submit refusals are preserved but reviewers should verify edge cases around Notion appendBlock and composed check diagnostics.

Overview
flows check no longer exits on missing local helper mounts (e.g. Slack). It lists integrations under REQUIRES, emits a single helper_credential_unresolved warning after that line (same footnote placement as agent_worker_unresolved), and can pass. Local flows run still refuses with helper_slack.credential_missing before the body runs; schedule / deploy / run --cloud still verify workspace integrations at submit.

Mount refusals are downgraded only when check opts in via warnUnresolvedHelperCredential (check-helper-surface.ts). YAML and authored .flow.ts paths share this behavior; check can still compile and run daemon validation when the only blocker was a downgraded mount, without admitting the flow for execution.

Notion appendBlock detection moves from a regex to AST analysis (helper-operation-use.ts): direct f.notion.appendBlock stays a refusal even without a mount; aliasing, computed access, eval/Function, prototype tampering, and similar patterns stay fail-closed. Cloud connect reads requirements from all authored flow extensions but skips connect prompts for sources Cloud will not submit (non-.flow.ts).

Docs (SLACK-HELPER, SURFACE, YAML-HELPERS) and broad vitest coverage document the new warning and refusal rules.

Reviewed by Cursor Bugbot for commit a96c88b. Bugbot is set up for automated code reviews on this repo. Configure here.

Relayflow and others added 4 commits October 6, 2026 13:39
The fix for `flows check` refusing a Cloud-bound f.slack flow is covered by
synthetic fixtures, but nothing in the suite guards the artifact the defect was
reported against. `examples/stale-issues/stale-issues.flow.ts` declares
`tools: { slack: true }` and calls `f.slack.post`, and on a machine with no
Slack mount it used to exit 2 on `helper_slack.credential_missing` before
printing `REQUIRES`.

Copy the shipped example into a staged directory the way
tests/flow-requirements.test.ts already does, and assert the three things the
report asked for: no REFUSED line, exit 0, and
`REQUIRES slack (tools.slack), claude (llm step)` on stdout with exactly one
`helper_credential_unresolved` footnote.

The copy writes its own `{"type":"module"}` boundary: `examples/` carries no
package.json, so under a checkout whose ancestry declares `"type": "commonjs"`
the authored `.flow.ts` cannot be imported at all, which is a property of the
checkout and not of the flow.

Mutation-verified in evidence/helper-check/mutation-shipped-example.md:
reverting cli.ts's `warnUnresolvedHelperCredential` opt-in fails the test on the
exact refusal from the report, and the restored file is byte-identical
(sha256 83e60fd1…).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2dfbc11f-271c-4a87-a6ea-bc8fc0900424

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@agent-relay-code

Copy link
Copy Markdown
Contributor Author

Relayflow: the adversarial review did not pass. This branch is not approved: the flow stopped here and did not mark it ready to merge.

PR #618 review

Reviewed head 5cd190dacd42370f5e31110a54ac55e2e2df00f1 against base
fe60dd4c3ae602b06581aa3f47f7b03348c1a6be in
PR #618.

Changes requested: two reproducible issues remain. No review.clean was created.
The existing implementation and regression tests were reviewed; this review does
not change production code, existing tests, or workflow files.

Findings

F1 — P2: Preserve communication preflight when downgrading a missing helper mount

Location: packages/sdk/src/cli/check.ts:255-259 (also the result.ok gate at 229-238).

A YAML flow with two communicating agents and a Slack helper, with no Slack
mount and no RELAY_API_KEY, now reports ok: true. The mount refusal makes
result.ok false, so flow is never constructed and the communication
environment check is skipped. The new diagnostic conversion then removes the
only refusal and declares success. Providing a Slack mount to the same flow
makes it refuse with probe_failed: Agent communication requires RELAY_API_KEY for an existing workspace. The missing mount also suppresses the existing
budget_unmetered communication warning.

This exceeds the intended helper-only relaxation: an unrelated environment
check disappears based on whether Slack is mounted. Perform inspection of
communication requirements even when only a helper mount is unresolved, while
keeping executable flow admission strict. Add a regression combining a YAML
helper with communication and missing Relay credentials; cover both mount
states and preserve the communication refusal and budget warning.

F2 — P2: Do not recommend unsupported YAML mock modes

Location: packages/sdk/src/cli/check-helper-surface.ts:32-35.

The shared warning always recommends ${helper.mockEnv}=1. For YAML Linear
(and GitHub), that is not a working alternative to a mount. The YAML execution
path in yaml-helper-effect.ts:21-23 recognizes mock mode only for Slack, and
non-Slack writeback at lines 28-32 requires an actual mount. A Linear YAML
check recommends RELAYFLOWS_LINEAR_MOCK=1; setting it still produces
helper_mount_required in the execution preflight.

Keep the warning specific to the execution surface: for YAML (helper_mount_required),
only offer mock mode for Slack; retain the provider-specific mock advice for
authored TS helpers. Add Linear and GitHub YAML warning tests so every suggested
local remedy is supported by that execution path.

Reproduction of both findings

The self-contained script creates temporary fixtures, uses a local shell stub
for CLI readiness, calls the actual checkAuthoredFlow path used by YAML check
and local execution, and restores its environment and temporary directory.
No daemon, provider connection, or external side effect is needed.

Command, from the repository root:

bun evidence/helper-check-review/reproduce.ts

Captured output:

{
  "label": "communication, no Slack mount, no RELAY_API_KEY",
  "ok": true,
  "diagnostics": [
    {
      "severity": "warning",
      "kind": "helper_credential_unresolved",
      "message": "f.slack needs a slack mount, which is not available locally. flows schedule / flows deploy / flows run --cloud check the integration against your workspace at submit and refuse if Cloud cannot connect it. A local flows run needs a relayfile slack mount (a slack/ directory under RELAYFILE_MOUNT_PATH) or RELAYFLOWS_SLACK_MOCK=1, and refuses with [helper_mount_required] without one."
    }
  ]
}
{
  "label": "communication, Slack mount present, no RELAY_API_KEY",
  "ok": false,
  "diagnostics": [
    {
      "severity": "refusal",
      "kind": "probe_failed",
      "message": "Agent communication requires RELAY_API_KEY for an existing workspace."
    },
    {
      "severity": "warning",
      "kind": "budget_unmetered",
      "message": "Managed communication sessions do not report token or dollar usage. Budget ceilings cannot bound their spend; communication.timeoutMs bounds their duration."
    }
  ]
}
{
  "label": "YAML Linear check advice",
  "ok": true,
  "diagnostics": [
    {
      "severity": "warning",
      "kind": "helper_credential_unresolved",
      "message": "f.linear needs a linear mount, which is not available locally. flows schedule / flows deploy / flows run --cloud check the integration against your workspace at submit and refuse if Cloud cannot connect it. A local flows run needs a relayfile linear mount (a linear/ directory under RELAYFILE_MOUNT_PATH) or RELAYFLOWS_LINEAR_MOCK=1, and refuses with [helper_mount_required] without one."
    }
  ]
}
{
  "label": "YAML Linear execution preflight after following mock advice",
  "ok": false,
  "diagnostics": [
    {
      "severity": "refusal",
      "kind": "helper_mount_required",
      "message": "linear helper requires a relayfile mount."
    }
  ]
}

PR discussion and checks

All three discussion endpoints were queried with pagination. Each returned an
empty array; there were no issue comments, reviews, or inline review comments
to resolve at review time.

gh api --paginate repos/AgentWorkforce/flows/issues/618/comments
[]
gh api --paginate repos/AgentWorkforce/flows/pulls/618/reviews
[]
gh api --paginate repos/AgentWorkforce/flows/pulls/618/comments
[]
gh pr checks 618
no checks reported on the 'relayflow/flows-software-garden-020f6161' branch

The PR description includes historical validation and later full-suite failures.
Those are author-provided evidence, not a full-suite pass from this review.
This review did not rerun the entire SDK suite or Cloud submission/activation.

Focused verification

Command (cwd: packages/sdk):

RELAYFLOWD_BIN="$PWD/../../kernel/target/debug/relayflowd" ./node_modules/.bin/vitest run tests/check-helper-surface.test.ts tests/check-worker-surface.test.ts tests/flow-requirements.test.ts tests/authored-flow-slack.test.ts tests/preflight.test.ts

Exit status: 0. Captured output:


 RUN  v2.1.9 /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk

 ✓ tests/preflight.test.ts (70 tests) 115ms
 ✓ tests/flow-requirements.test.ts (14 tests) 1001ms
   ✓ flows check prints REQUIRES > names the helper, the harness and the mcp server of an authored flow 732ms
 ✓ tests/check-worker-surface.test.ts (11 tests) 93ms
 ✓ tests/authored-flow-slack.test.ts (7 tests) 2439ms
   ✓ authored Slack helper effects > refuses missing credentials before running the body while flows check warns 615ms
   ✓ authored Slack helper effects > replays after SIGKILL before confirm with the same token and one successful completion 555ms
   ✓ authored Slack helper effects > replays after SIGKILL before complete with the same token and one successful completion 537ms
   ✓ authored Slack helper effects > writes two files for two calls and supports dm, reply, and react 359ms
 ✓ tests/check-helper-surface.test.ts (16 tests) 4073ms
   ✓ reports the declared integration once after REQUIRES and before CHECK PASSED 479ms
   ✓ preserves requirements when compilation refuses and for named agents 2169ms

 Test Files  5 passed (5)
      Tests  118 passed (118)
   Start at  14:32:05
   Duration  6.74s (transform 1.72s, setup 59ms, collect 5.49s, tests 7.72s, environment 1ms, prepare 246ms)

The first attempt omitted RELAYFLOWD_BIN. It failed five daemon-dependent
Slack tests because the default toolchain path had no binary. Selecting the
existing kernel/target/debug/relayflowd produced the result above. The daemon
was not rebuilt in this review.

Initial command (cwd: packages/sdk):

./node_modules/.bin/vitest run tests/check-helper-surface.test.ts tests/check-worker-surface.test.ts tests/flow-requirements.test.ts tests/authored-flow-slack.test.ts tests/preflight.test.ts

Exit status: 1. Captured output:


 RUN  v2.1.9 /home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk

 ✓ tests/preflight.test.ts (70 tests) 105ms
 ❯ tests/authored-flow-slack.test.ts (7 tests | 5 failed) 763ms
   × authored Slack helper effects > snapshots structured posts into the journal and delivers the same Block Kit body 7ms
     → Build relayflowd first: /home/daytona/.relayflows-toolchain/target/2962130851/debug/relayflowd: expected false to be true // Object.is equality
   × authored Slack helper effects > journals exactly one effect with the authored params and typed receipt, without network 1ms
     → Build relayflowd first: /home/daytona/.relayflows-toolchain/target/2962130851/debug/relayflowd: expected false to be true // Object.is equality
   ✓ authored Slack helper effects > refuses missing credentials before running the body while flows check warns 543ms
   × authored Slack helper effects > replays after SIGKILL before confirm with the same token and one successful completion 1ms
     → Build relayflowd first: /home/daytona/.relayflows-toolchain/target/2962130851/debug/relayflowd: expected false to be true // Object.is equality
   × authored Slack helper effects > replays after SIGKILL before complete with the same token and one successful completion 1ms
     → Build relayflowd first: /home/daytona/.relayflows-toolchain/target/2962130851/debug/relayflowd: expected false to be true // Object.is equality
   × authored Slack helper effects > writes two files for two calls and supports dm, reply, and react 1ms
     → Build relayflowd first: /home/daytona/.relayflows-toolchain/target/2962130851/debug/relayflowd: expected false to be true // Object.is equality
 ✓ tests/flow-requirements.test.ts (14 tests) 954ms
   ✓ flows check prints REQUIRES > names the helper, the harness and the mcp server of an authored flow 701ms
 ✓ tests/check-worker-surface.test.ts (11 tests) 94ms
 ✓ tests/check-helper-surface.test.ts (16 tests) 4271ms
   ✓ reports the declared integration once after REQUIRES and before CHECK PASSED 482ms
   ✓ keeps default and explicit opt-out checks strict, and local run refuses before attach 317ms
   ✓ preserves requirements when compilation refuses and for named agents 2262ms

⎯⎯⎯⎯⎯⎯⎯ Failed Tests 5 ⎯⎯⎯⎯⎯⎯⎯

 FAIL  tests/authored-flow-slack.test.ts > authored Slack helper effects > snapshots structured posts into the journal and delivers the same Block Kit body
AssertionError: Build relayflowd first: /home/daytona/.relayflows-toolchain/target/2962130851/debug/relayflowd: expected false to be true // Object.is equality

- Expected
+ Received

- true
+ false

 ❯ start tests/authored-flow-slack.test.ts:48:67
     46| 
     47| async function start(dataDir: string): Promise<{ daemon: ChildProcess;…
     48|   expect(existsSync(binary), `Build relayflowd first: ${binary}`).toBe…
       |                                                                   ^
     49|   const daemon = spawn(binary, ['--data-dir', dataDir, 'serve'], { std…
     50|   children.push(daemon);
 ❯ tests/authored-flow-slack.test.ts:73:30

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/5]⎯

 FAIL  tests/authored-flow-slack.test.ts > authored Slack helper effects > journals exactly one effect with the authored params and typed receipt, without network
AssertionError: Build relayflowd first: /home/daytona/.relayflows-toolchain/target/2962130851/debug/relayflowd: expected false to be true // Object.is equality

- Expected
+ Received

- true
+ false

 ❯ start tests/authored-flow-slack.test.ts:48:67
     46| 
     47| async function start(dataDir: string): Promise<{ daemon: ChildProcess;…
     48|   expect(existsSync(binary), `Build relayflowd first: ${binary}`).toBe…
       |                                                                   ^
     49|   const daemon = spawn(binary, ['--data-dir', dataDir, 'serve'], { std…
     50|   children.push(daemon);
 ❯ tests/authored-flow-slack.test.ts:104:30

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/5]⎯

 FAIL  tests/authored-flow-slack.test.ts > authored Slack helper effects > replays after SIGKILL before confirm with the same token and one successful completion
 FAIL  tests/authored-flow-slack.test.ts > authored Slack helper effects > replays after SIGKILL before complete with the same token and one successful completion
AssertionError: Build relayflowd first: /home/daytona/.relayflows-toolchain/target/2962130851/debug/relayflowd: expected false to be true // Object.is equality

- Expected
+ Received

- true
+ false

 ❯ start tests/authored-flow-slack.test.ts:48:67
     46| 
     47| async function start(dataDir: string): Promise<{ daemon: ChildProcess;…
     48|   expect(existsSync(binary), `Build relayflowd first: ${binary}`).toBe…
       |                                                                   ^
     49|   const daemon = spawn(binary, ['--data-dir', dataDir, 'serve'], { std…
     50|   children.push(daemon);
 ❯ tests/authored-flow-slack.test.ts:166:25

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/5]⎯

 FAIL  tests/authored-flow-slack.test.ts > authored Slack helper effects > writes two files for two calls and supports dm, reply, and react
AssertionError: Build relayflowd first: /home/daytona/.relayflows-toolchain/target/2962130851/debug/relayflowd: expected false to be true // Object.is equality

- Expected
+ Received

- true
+ false

 ❯ start tests/authored-flow-slack.test.ts:48:67
     46| 
     47| async function start(dataDir: string): Promise<{ daemon: ChildProcess;…
     48|   expect(existsSync(binary), `Build relayflowd first: ${binary}`).toBe…
       |                                                                   ^
     49|   const daemon = spawn(binary, ['--data-dir', dataDir, 'serve'], { std…
     50|   children.push(daemon);
 ❯ tests/authored-flow-slack.test.ts:217:30

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/5]⎯

 Test Files  1 failed | 4 passed (5)
      Tests  5 failed | 113 passed (118)
   Start at  14:30:50
   Duration  6.67s (transform 1.56s, setup 53ms, collect 5.17s, tests 6.19s, environment 1ms, prepare 196ms)

Command (cwd: packages/sdk):

npm run typecheck && npm run typecheck:tests

Exit status: 0. Captured output:


> @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

The passing focused tests cover the reported stale-issues example, warning
placement/deduplication, strict local execution, unsupported providers, and
Slack crash/replay behavior. They do not cover F1 or F2; the reproduction above
shows why this is not a clean review despite those passing tests.

…n a helper mount is missing

F1: when the only refusal is a missing local helper mount that `flows check`
restates as helper_credential_unresolved, still compile the flow for
inspection so the communication environment refusal (RELAY_API_KEY) and the
budget_unmetered warning no longer depend on whether the helper is mounted.
The compiled flow is still returned only when preflight admitted it.

F2: YAML execution honours a mock only for Slack; the warning no longer
recommends RELAYFLOWS_LINEAR_MOCK / RELAYFLOWS_GITHUB_MOCK for YAML helpers.
Authored TS helpers keep their provider-specific mock advice.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
@khaliqgant

Copy link
Copy Markdown
Member

Addressed the adversarial review in c8eb1b0:

  • F1 (communication preflight hidden by a missing helper mount): checkAuthoredFlow now compiles the flow for inspection when the only refusal is a helper mount that flows check restates as helper_credential_unresolved, so probe_failed (RELAY_API_KEY) and budget_unmetered are reported in both mount states. The compiled flow is still returned only when preflight admitted it. Tests: keeps the communication refusal and budget warning whether or not Slack is mounted (failed before on mounted=false), inspecting past a missing mount never admits the flow for execution.
  • F2 (unsupported YAML mock advice): for helper_mount_required the warning offers <PROVIDER>_MOCK=1 only for Slack; authored TS helpers keep their mock remedy. Tests: YAML linear/github offers only remedies its execution path supports (failed before), YAML Slack keeps the mock remedy, authored TS helpers keep their provider-specific mock remedy.

Local: check-helper-surface, check-worker-surface, preflight, helpers-fanout, helper-reference, flow-requirements, flow-extension-compose, communication-preflight, communication-environment-preflight, budget-preflight → 321 passed; authored-flow-slack with relayflowd → 7 passed; typecheck + typecheck:tests clean.

@khaliqgant
khaliqgant marked this pull request as ready for review October 8, 2026 06:04
@khaliqgant

Copy link
Copy Markdown
Member

@coderabbitai review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-08T14:18:49.240293Z 2b03dd4 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review skipped.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@khaliqgant

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c8eb1b051d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/sdk/src/helper-preflight.ts Outdated
…text

The unsupported-operation check matched any `.appendBlock` in the body — an
unrelated object's method, a string, a comment — so a Notion flow with no local
mount still exited 2 after its mount refusal became a warning.
helperMethodsUsed reads `f.<namespace>.<method>` references with the same
scoped parse as helperNamespacesUsed; only a body whose context name cannot be
resolved keeps the permissive text match.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
@khaliqgant

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 72043aaaf1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/sdk/src/helper-preflight.ts Outdated
…ed f.notion

helperMethodsUsed only saw `f.notion.appendBlock`, so `const notion = f.notion`
or `const { notion } = f` followed by `notion.appendBlock(...)` passed check
while execution always rejects it. helperMemberUses also reports namespaces
that leave the context without a method (aliased, destructured, passed on) and
every member name the body accesses; when f.notion escapes, any appendBlock
access keeps the unsupported-operation refusal. Direct, non-escaping use stays
precise, so an unrelated object's appendBlock still does not refuse.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
@khaliqgant

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aea5c1aad8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/sdk/src/helper-reference.ts Outdated

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/sdk/src/helper-reference.ts Outdated
Comment thread packages/sdk/src/helper-reference.ts Outdated
… not hand-offs

`const { appendBlock } = f.notion` escaped detection because the method name
was a binding, not a member access, and every extra `f.notion` mention —
including `if (f.notion)` — counted as an escape. helperMemberUses now records
destructured property names as member accesses, and marks a namespace escaped
only when its value is handed on: a declaration or assignment initializer, a
call/new argument, a property value, an array element, a return, spread or
yield (through ?:, ||/??, await and parentheses).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
@khaliqgant

Copy link
Copy Markdown
Member

@codex review

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/sdk/src/helper-reference.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e8d22fd760

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/sdk/src/helper-reference.ts Outdated
Enumerating hand-off shapes kept missing some (arrow expression bodies,
parameter and destructuring defaults, nested destructuring). helperMemberUses
now links parents once and treats a use of `f.<ns>` as safe only as a method
access or a read-only check (typeof/!/comparison operand, condition); every
other position escapes the namespace, and a bare `f` outside those positions
escapes the whole context. Destructured keys are collected at any depth,
through defaults and array patterns.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
@khaliqgant

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5205d7f3c5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/sdk/src/helper-preflight.ts Outdated
…how to avoid it

When f.notion escapes, flows check cannot attribute a later .appendBlock to it
or to another object, and deliberately refuses rather than admit a flow that
execution always rejects. The refusal now says so and names the remedy: call
f.notion methods directly so check can tell them apart. Direct use stays
precise (an unrelated doc.appendBlock with f.notion.createPage passes).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
@khaliqgant

Copy link
Copy Markdown
Member

@codex review

…ng mount; destructured machinery

- With main's daemon validation merged in, a YAML flow whose only refusal was
  a downgraded helper mount still skipped the daemon gate (validation keyed off
  the original result.ok), so --against-daemon could pass without a kernel
  verdict. The validator now runs for inspect-only flows too.
- Destructuring a prototype-machinery member (const { prototype: p } = Object,
  const { toPrimitive: t } = Symbol) is detected like the member access.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
@khaliqgant

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a6d253d0b7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/sdk/src/helper-operation-use.ts Outdated
Object[d], Symbol[t] and the like with a key built at runtime may name any
prototype machinery, so a computed non-literal member on an unshadowed
Object/Symbol/Reflect/Function/globalThis/Proxy now makes Notion appendBlock
unprovable. Dynamic indexing of ordinary values stays allowed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
@khaliqgant

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d1bb462099

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/sdk/src/helper-operation-use.ts
Computed keys (const { [d]: define } = Object) and rest elements can pick out
any machinery the literal-key check cannot name, so destructuring an unshadowed
Object/Symbol/Reflect/Function/globalThis/Proxy at all now makes Notion
appendBlock unprovable.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
@khaliqgant

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ac06f686ca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/sdk/src/helper-operation-use.ts Outdated
…achinery member

Aliasing Object/Symbol/Reflect/Function/globalThis/Proxy (const O = Object)
let runtime-keyed access reach the machinery under another name. Instead of
chasing alias shapes, an unshadowed use of these globals is now accepted only
as the object of a static member that is not prototype machinery
(Object.keys, Symbol.iterator); any other use — alias, argument,
destructuring, computed key — makes Notion appendBlock unprovable.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
@khaliqgant

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ec7b706e35

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/sdk/src/helper-operation-use.ts Outdated
…inery

Every member named prototype counted as helper prototype machinery, so a flow
with class Local {}; Local.prototype.flag = true was refused although it can
neither expose nor modify f.notion. X.prototype is now allowed when X is a
class or function the body defines itself (declaration or const initializer)
and never reassigns; other routes to a prototype (__proto__,
getPrototypeOf, constructor.prototype, the globals) stay refused.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
@khaliqgant

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f568c07cf6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/sdk/src/helper-operation-use.ts Outdated
Comment thread packages/sdk/src/helper-operation-use.ts
… rule

globalThis.Object and globalThis.Symbol are the same globals by another
route; extracting them (const O = globalThis.Object) let runtime-keyed access
reach prototype machinery. A globalThis member naming a prototype-bearing
global is now judged like the bare global: safe only as the object of a static,
non-machinery member.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
@khaliqgant

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 14bb42e2a0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/sdk/src/helper-operation-use.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 14bb42e. Configure here.

Comment thread packages/sdk/src/helper-operation-use.ts Outdated
…ded globals

Only globalThis, one level deep, was treated as a route to Object/Symbol/...;
Node's global, window, self and nested chains (globalThis.globalThis.Object,
globalThis.global.Symbol) were not. Global roots now follow root-to-root
chains and judge a named prototype-bearing global like the bare one; any
other use of a root (alias, computed key) makes Notion appendBlock unprovable.
Ordinary root members (global.setTimeout) stay allowed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
@khaliqgant

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aaac44894f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/sdk/src/helper-operation-use.ts Outdated
…nery

Object.constructor, (() => {}).constructor and the like are the Function
constructor, which compiles source text the scan never sees. Any member
named constructor (and the legacy __lookupGetter__/__lookupSetter__) now
makes Notion appendBlock unprovable.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
@khaliqgant

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a96c88b775

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/sdk/src/helper-operation-use.ts Outdated
(0, eval)(src), const run = eval and globalThis.eval(src) all run source text,
but only a direct eval(...) call was refused. Any unshadowed eval identifier,
and any member named eval (globalThis.eval), now makes Notion appendBlock
unprovable.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Session-Id: b8262468-ea26-4a6e-bee9-d193832e77dc
@khaliqgant

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

Reviewed commit: 2b03dd483f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@khaliqgant
khaliqgant merged commit 11d7be5 into main Oct 8, 2026
8 of 9 checks passed
@khaliqgant
khaliqgant deleted the relayflow/flows-software-garden-020f6161 branch October 8, 2026 14:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

flows check refuses a Cloud-bound f.slack flow locally (helper_slack.credential_missing) before printing REQUIRES

1 participant