Skip to content

Run flow-next on a flowctl you choose with FLOW_NEXT_FLOWCTL - #571

Merged
gmickel merged 6 commits into
mainfrom
spike/flowctl-bin-delegation
Oct 19, 2026
Merged

gmickel merged 6 commits into
mainfrom
spike/flowctl-bin-delegation

Conversation

@gmickel

@gmickel gmickel commented Oct 19, 2026

Copy link
Copy Markdown
Owner

Why

Anyone who wants flow-next to run on a different flowctl (their own build, a port, a fork under test) had to edit the FLOWCTL= line in every skill. That breaks make-pr and map, which look for helper scripts beside $FLOWCTL. This PR puts the hand-off in the two bundled launchers instead. Every skill, agent and host already calls one of them.

What changes for a user or operator

  • Set FLOW_NEXT_FLOWCTL=/path/to/flowctl and every call goes there with its arguments, before the launcher looks for Python. The launcher returns that flowctl's exit code.
  • A value the system cannot run stops with a clear error and never falls back to the bundled flowctl, so two flowctls can't write one .flow/.
  • A value that names the launcher itself runs the launcher as usual.
  • With the variable unset nothing changes. Over 80 alternating list --json calls, main took 315.9 ms per call and this branch 315.6 ms.

I named it FLOW_NEXT_FLOWCTL because an unrelated CLI also called flowctl (withObsrvr's) tells its users to set FLOWCTL_BIN.

Scope

  • plugins/flow-next/scripts/flowctl: exec under shopt -s execfail, so the OS decides what runs. Git Bash finds flowctl.exe from a bare flowctl path. A self check (-ef) covers both a direct path and a symlink.
  • plugins/flow-next/scripts/flowctl.cmd: normalizes the value with %%~fI, skips itself and its bash twin, checks the exact file or a PATHEXT variant, then runs CALL. A failed CALL sets errorlevel 1, which a real flowctl can also return, so the error message needs this pre-check.
  • plugins/flow-next/tests/test_startup_bootstrap.py: 5 tests:
    • hand-off with no Python on PATH;
    • loud failure for a missing file, a directory and a non-executable file;
    • self path, direct and through a symlink;
    • empty value;
    • FLOWCTL_BIN ignored.
  • .github/workflows/test-flow-next.yml: two steps on windows-latest.
    • PowerShell to flowctl.cmd and Git Bash to flowctl. Each covers a hand-off returning exit 7, an extensionless path, a missing path or directory failing loudly with no .flow/ created, FLOWCTL_BIN ignored, and a self path.
    • The PowerShell step also checks a forward-slash self path.
    • The Git Bash step also checks a backslash path.
  • plugins/flow-next/docs/flowctl.md and its Codex mirror: one paragraph.
  • 9.1.0 version bump and changelog.

Verification

  • Full suite locally: 4070 tests, 0 failures.
  • sync-codex.sh --check reports the Codex mirror is fresh.
  • Spike CI run 39177634927: all 12 jobs green, Windows included.
  • End to end against a TypeScript flowctl, using the skill-shaped Claude Code and Codex FLOWCTL chains: init, spec create and list went to it with the variable set and to Python without it. make-pr's and map's helper scripts still resolve beside the launcher.

https://claude.ai/code/session_01LbHwMnkD6ovBw6n8cnD8hQ

A host orchestrator that ships its own flowctl sets FLOWCTL_BIN; the bash
launcher execs it before the Python probe, fails loudly on a non-executable
value, and ignores a value naming itself. Skill resolution lines are
unchanged, so copy-less loading and dirname-relative helpers keep working.

Claude-Session: https://claude.ai/code/session_01LbHwMnkD6ovBw6n8cnD8hQ
Same contract as the bash launcher: delegate before the Python probe, fail
loudly on a missing file or directory, and run normally when the value names
this launcher or its bash twin (paths normalized so forward slashes cannot
recurse). Covered by a new step in the windows-latest stub job.

Claude-Session: https://claude.ai/code/session_01LbHwMnkD6ovBw6n8cnD8hQ
The pre-checks rejected values the system would run (Git Bash resolves a bare
flowctl path to flowctl.exe). Exec with execfail and print the hint only when
exec itself fails; a delegated flowctl's exit code passes through.

Claude-Session: https://claude.ai/code/session_01LbHwMnkD6ovBw6n8cnD8hQ
FLOWCTL_BIN is documented by an unrelated `flowctl` CLI, so a user who
exports it for that tool would have every flow-next call handed to the wrong
program. flowctl.cmd now lets cmd.exe resolve the value (PATHEXT) and prints
the hint only on 9009. Tests and the Windows CI steps pin that FLOWCTL_BIN
changes nothing and cover extensionless, backslash and unrunnable values.

Claude-Session: https://claude.ai/code/session_01LbHwMnkD6ovBw6n8cnD8hQ
CI showed a failed CALL sets errorlevel 1, so the 9009 branch never fired.
Pre-check the path and its PATHEXT variants instead. Regenerate the Codex
docs mirror; let the Git Bash CI step run checks that exit non-zero.

Claude-Session: https://claude.ai/code/session_01LbHwMnkD6ovBw6n8cnD8hQ
@gmickel
gmickel merged commit 4eba935 into main Oct 19, 2026
14 checks passed
@gmickel
gmickel deleted the spike/flowctl-bin-delegation branch October 19, 2026 08:18
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.

1 participant