Repository navigation
6.0.0: the Fusebase Flow 6.0 core, released
The Fusebase Flow 6.0 core as a release: the lifecycle (three git hooks and their modules, the six scripts in hooks/local/, the 8 rules, three agents, six skills, one lifecycle page and four document templates), with everything the seven pre-releases built (6.0.0-m2 to 6.0.0-m8, each note in this folder) and five changes since 6.0.0-m8: an agent may mint any approval after the operator's go-ahead in chat, the secret scans read git's diff whatever the user's colour or external-diff settings, only a test runner's skip marker counts as a skip, health names brew install coreutils where bounded runs cannot work, and the test suite names a class that hangs. Both live projects upgrade from 6.0.0-m8 in one step (see "Upgrading a 6.0.0-m8 project to 6.0.0"); a Flow 5.x project migrates in one commit (see "Migrating a Flow 5.x project to 6.0.0").
The main branch stays the Flow 5.x line, frozen at v5.11; 6.0.0 is published from the core branch and is the latest release.
Needs git, bash and Python 3.10+ with PyYAML; on macOS also brew install coreutils (FR-27's bounded runs need timeout or gtimeout). The hooks and the two approval scripts run the first of python3, python and py -3 that is 3.10+. Get this release; the second line installs PyYAML with the pip of the interpreter Flow will use:
git clone --depth 1 --branch 6.0.0 https://github.com/fusebase-dev/fusebase-flow.git fusebase-flow-6.0.0
<the interpreter Flow will use> -m pip install -r fusebase-flow-6.0.0/hooks/requirements.txt
That is usually python3 on Linux and macOS, where Debian and Ubuntu may refuse a pip install into the system Python: use sudo apt install python3-yaml, or a venv. On Windows with python.org's Python it is python (python -m pip install -r fusebase-flow-6.0.0/hooks/requirements.txt) or py -3. Windows' default App execution aliases make python3 the Microsoft Store shortcut, which fails the 3.10+ check, so Flow goes on to python and py -3; when none of the three is Python 3.10+, the commit is refused until you turn off the python.exe and python3.exe App execution aliases (Settings, Apps, Advanced app settings) or install Python 3.10+. The clone may print warning: refs/tags/6.0.0 ... is not a commit!: an annotated tag in a shallow clone, harmless (the checkout is the tag's commit). The bash lines below run in Git Bash; in PowerShell, the mint line is $env:FF_BOOTSTRAP_TTL_MIN=30; & "$env:ProgramFiles\Git\bin\bash.exe" hooks/local/write-bootstrap-approval.sh (a bare bash there can be WSL's).
New project: the install line of the upgrade below, with the providers you want, such as --providers claude,codex, at the top level of a git repository's main worktree; then commit the result as the protected commit workflows/lifecycle.md describes. Add --wire-hooks to merge Flow's two tool handlers into .claude/settings.json and .codex/hooks.json, and --register-reader to schedule the heartbeat reader every 5 minutes. If you edited policies/approval-policy.yml before 6.0.0-m3, an install keeps your copy, which has no gate_rounds; the gate then allows 2 rounds, the shipped value.
Upgrading a 6.0.0-m8 project to 6.0.0
Both live projects take this, from 6.0.0-m8. Run these in Git Bash, in the folder that holds your project's folder; <your-project> is that folder's name. Start from a clean tree: commit your own work first, and close the agent sessions open on the project (a session keeps the rules it loaded). The same lines serve every 6.0.0-m8 install, wired or not. On the install line, give the providers audit/flow-install.json lists under providers (both live projects have claude,codex). Leave --wire-hooks off: the wiring stays as it is. 6.0.0 adds no file under hooks/, so git add -u stages the whole upgrade.
git clone --depth 1 --branch 6.0.0 https://github.com/fusebase-dev/fusebase-flow.git fusebase-flow-6.0.0
<the interpreter Flow will use> -m pip install -r fusebase-flow-6.0.0/hooks/requirements.txt
python fusebase-flow-6.0.0/maintainer/flow.py install --target <your-project> --providers <the providers audit/flow-install.json lists>
cd <your-project>
git status --short
git add -u
FF_BOOTSTRAP_TTL_MIN=30 bash hooks/local/write-bootstrap-approval.sh
git commit -m "flow: upgrade Fusebase Flow 6.0.0-m8 to 6.0.0"
bash hooks/local/write-bootstrap-approval.sh --consume
cd ..
python fusebase-flow-6.0.0/maintainer/flow.py health --target <your-project>
What each step shows, as on the copy these steps were tested on (Onboarding at b176a73d migrated by the published 6.0.0-m4, upgraded by the 6.0.0-m6, 6.0.0-m7 and 6.0.0-m8 notes, Claude Code alone wired, a reader task registered and Ready):
- the install lists
replaced:the Flow files that changed:VERSION,hooks/local/lib/commit_msg.py,hooks/local/lib/evidence_rows.py,hooks/local/lib/flow_docs.py,hooks/local/lib/range_check.py,hooks/shared/command_rules.py,hooks/shared/staged_secret_scan.pyandpolicies/command-policy.yml, and nothingadded:. Its lineprovider hook wiring: not writtenmeans it wrote no wiring this time; the wiring you have stays. Its lineheartbeat reader: registered earlier: <the recorded line>(not scheduledwhen none is recorded) means it registered nothing this time: your task stays as it is. When other local branches or worktrees have no Flow 6.0 files, a WARNING names each one; git status --shortlistsMlines only: those Flow files andaudit/flow-install.json. If it lists anything else, stop and set your own files aside without touching Flow's, for example withgit stash push -- <your files>, then go on (a plaingit stashwould take Flow's changes too).git add -ustages them all;- the mint prints one line; the commit runs the secret scan, the protected paths and the module size, then your lint and typecheck in
commit-msg, which also puts the protected-path changes on record for the push check, inside the 30-minute mint; - health ends
HEALTH: healthyand exits 0, withversion: 6.0.0 installed, the four git hooks installed,wiring: claude wiredandwiring: codex unenforcedfor an install with Claude Code alone wired, and its reader line still says what the scheduler says:the task is Ready.
In a wired Claude Code session, every line goes through the project's own hooks/handlers/pre_tool_use.py; the agent mints only on the operator's go-ahead in chat, as its own command. The lines inside the project are allowed, and so are the lines from the folder above (the handler judges them by the rules of the project they belong to).
Other clones of the project (a teammate's, a second machine): after pulling the upgrade, run bash hooks/local/install-git-hooks.sh in each. Git hooks are per clone.
Migrating a Flow 5.x project to 6.0.0
First run the dry run. It prints what it would remove, replace and add, what it never touches, and the open branches and worktrees that do not carry Flow 6.0 yet, and changes nothing. The dry run runs the structural preflight; --apply runs the project's lint and typecheck before any change; so a dry run that exits 0 means the migration will go through only once those two also pass by hand. Then run the migration, from the main worktree, in the folder that holds your project's folder (after the clone and pip lines above):
python fusebase-flow-6.0.0/maintainer/flow.py migrate --target <your-project> --providers claude,codex
python fusebase-flow-6.0.0/maintainer/flow.py migrate --target <your-project> --providers claude,codex --apply --go-ahead
python fusebase-flow-6.0.0/maintainer/flow.py health --target <your-project>
--apply first checks everything that could stop it, and changes nothing if a check fails. It copies every file it removes or replaces into .flow-migrate-<UTC time>/. The migration lands as one commit, flow: migrate Fusebase Flow <5.x version> to 6.0.0, through the new git hooks, and writes the Flow .gitignore block. It also removes the 5.x repair backups (*.pre-flow-repair, *.pre-flow-repair.<n>) left under .claude/, .agents/ and .codex/ whose file goes with the migration or is already gone; a folder that holds its own .git (a worktree under .claude/worktrees/, a submodule) is another checkout, and nothing in it is touched. Flow 5.x's line-ending pin lines go from .gitattributes (the dry run lists them). Its report, at the top of docs/flow/ledger.md, says what to do next (carry the open 5.x work into docs/flow/ with the plan skill, one backlog item per evidence row, and keep the gate as your own step, never a builder's task) and the health check to run after that commit, and ends with the undo block below. Health then ends HEALTH: healthy. After the migration, check each linked worktree by hand (Limits and procedures).
Undo. The end of --apply and the migration report in docs/flow/ledger.md print this block with the values filled in. Paste it into Git Bash at the project's top level, as one unit. It checks out the pre-migration commit on a new branch, puts the 5.x git hooks back and restores the untracked files the migration removed or overwrote, from the backup folder. If that folder is gone, the block takes the hooks from the 5.x tree's hooks/git/ and says so, and those untracked files cannot be restored; if that tree has no hooks/git/ copies either, it stops before anything changes. It stops at the first step that fails, and your shell stays open. The branch you migrated keeps the migration commit.
( set -e
if [ -d ".flow-migrate-<UTC time>" ]; then :; elif git cat-file -e "<the pre-migration commit>:hooks/git/pre-commit" 2>/dev/null && git cat-file -e "<the pre-migration commit>:hooks/git/commit-msg" 2>/dev/null && git cat-file -e "<the pre-migration commit>:hooks/git/pre-push" 2>/dev/null; then echo "NOTE: the backup folder .flow-migrate-<UTC time>/ is gone: the git hooks come from the 5.x tree's hooks/git/, and the untracked files the migration removed or overwrote cannot be restored"; else echo "STOPPED before any change: the backup folder .flow-migrate-<UTC time>/ is gone, and the pre-migration commit does not hold all three of hooks/git/pre-commit, commit-msg and pre-push to put the 5.x git hooks back from"; false; fi
git checkout -b flow-5x-<UTC time> <the pre-migration commit>
for h in pre-commit commit-msg pre-push; do if [ -f ".flow-migrate-<UTC time>/.git-hooks/$h" ]; then cp -p -- ".flow-migrate-<UTC time>/.git-hooks/$h" "$(git rev-parse --git-common-dir)/hooks/$h"; elif [ -d ".flow-migrate-<UTC time>" ]; then rm -f -- "$(git rev-parse --git-common-dir)/hooks/$h"; else git cat-file blob "<the pre-migration commit>:hooks/git/$h" > "$(git rev-parse --git-common-dir)/hooks/$h"; chmod +x -- "$(git rev-parse --git-common-dir)/hooks/$h"; fi; done
if [ -d ".flow-migrate-<UTC time>" ]; then while IFS= read -r p; do mkdir -p -- "$(dirname -- "$p")"; cp -p -- ".flow-migrate-<UTC time>/$p" "$p"; done < ".flow-migrate-<UTC time>/.migrate/untracked.txt"; fi
)
What is new
| Piece | What it does |
|---|---|
hooks/shared/command_rules.py, policies/command-policy.yml (kernel) |
One whole approve-local.sh invocation, for any approval action, is judged with its --command argument blanked, so a production_deploy mint binding git push origin master is allowed while the push itself stays denied until the approval exists; chaining or substitution around the mint gets no exemption (T389, the owner's option A of 2026-10-23, reversing T284) |
maintainer/tests/parallel.py (not installed) |
Each suite worker prints [suite-worker-start] <module.Class> before it runs a class, and the parent passes it on at once, so a class that never returns is named in the CI log (T390) |
maintainer/release-notes/6.0.0.md |
This note; its limits and procedures (T391) |
hooks/shared/staged_secret_scan.py (kernel), hooks/local/lib/range_check.py |
The commit, merge and push secret scans read git's diff with --no-color --no-ext-diff, so color.ui=always, color.diff=always or a diff.external tool no longer hides every added line (T393, audit #3) |
hooks/local/lib/flow_docs.py, evidence_rows.py, commit_msg.py |
An added skip is a test runner's marker (.skip(, .todo(, xit(, a { skip: } option, a TAP # SKIP/# TODO, @unittest.skip, pytest.mark.skip, .skipTest(), never the word: a // TODO comment or a test about todos no longer fails a passing test row (T394, audit #4) |
maintainer/health.py, README.md |
Health prints a WARNING naming brew install coreutils when neither timeout nor gtimeout is on PATH (the verdict is unchanged); the README's requirements say so (T395, audit #5) |
VERSION, maintainer/tests/test_migrate.py |
6.0.0; the upgrade-steps test reads this note's upgrade from the last pre-release, 6.0.0-m8, and keeps the m-series rule (T396, audit #1) |
Kernel changes and the 5.x line
Each kernel change landed on 5.x first and was copied here with maintainer/kernel.lock updated in the same commit; the lock holds 38 files, as 6.0.0-m8's did. Since 6.0.0-m8, on the local branch 5x-kernel, each with 5.x's two manifests restamped (hook-layer first): 127a2e55 (T389, the mint for any approval action) and fd9a1712 (T393, the staged scan's diff flags). range_check.py is core only. None is public: no GitHub branch carries them, and pushing them to 5.x needs the owner's approval; main stays at v5.11.
Line counts, every changed script against its ceiling
| File | 6.0.0-m8 | 6.0.0 | Ceiling |
|---|---|---|---|
hooks/shared/command_rules.py (kernel) |
296 | 297 | 800 (module size) |
hooks/shared/staged_secret_scan.py (kernel) |
190 | 192 | 800 (module size) |
hooks/local/lib/range_check.py |
273 | 275 | 300 |
hooks/local/lib/flow_docs.py |
207 | 213 | 250 |
hooks/local/lib/evidence_rows.py |
105 | 105 | 120 |
hooks/local/lib/commit_msg.py |
233 | 233 | 800 (module size) |
maintainer/health.py |
139 | 142 | 800 (module size) |
maintainer/tests/parallel.py (not installed) |
126 | 133 | 800 (module size) |
No ceiling was raised. The tests: test_m8d.py 110 to 187, test_core.py 711 to 723, test_commits.py 694 to 698, test_m8b.py 279 to 284, each under 800. The builder card stays under its 6,000 bytes (maintainer/measure_boot.py).
Limits and procedures
-
A timed-out Windows leg blocks a pre-release until a re-run passes, two re-runs at most. This keeps the owner's rule of 2026-10-16 (owner, 2026-10-23).
-
Silence alerts during an owner wait are expected. While a run is waiting for the owner's answer, nothing is supposed to happen, so an alert that the run has gone quiet is not a fault.
-
After a migration, check each linked worktree by hand. List the worktrees, then ask for each one whether its branch already holds the migration commit:
git worktree list git merge-base --is-ancestor <migration commit> <branch> && echo "has the migration" || echo "merge first"A worktree whose branch does not hold it merges the migrated branch before its next commit (
git -C <worktree> merge <migrated branch>). -
A 5.x backlog kept as one folder per item (
docs/backlog/<item>/README.md) is carried as one item per folder indocs/flow/backlog.md, each with a pointer to its folder. The folders stay as records. -
Before tagging,
VERSIONat the tagged commit equals the tag (checked by hand; a CI check is added only if a tag ever mismatches). -
Open owner decision: whether a hook that errors, or an event it cannot parse, blocks the tool call; the behaviour stays as in 6.0.0-m8.
-
A mint runs as its own command: nothing before it and nothing piped after it. A mint chained with
cd ... &&, or piped intotail, is judged whole and refused by the rule it mints for. -
A commit message, or any text outside the allowlist, that quotes a gated command is refused (
git commit -m "note: fusebase deploy needs the approval first"): reword it, or commit withgit commit -F <file>. The corpus (maintainer/tests/command-gate-corpus.json, 188 rows) lists the rows the gate leaves the other way undermatcher_residue: 40 commands it should gate and does not, 12 it should not gate and does. -
Text the gate does not see run: text in an allowlisted context that another program runs (sed's
ecommand in a quoted sed script,sed -n '1e rm -rf /tmp/x' file); a heredoc read by a program not on the runner list (make -f - <<EOF, or a script written bycat > s.sh <<EOFand then run); a quote-split word (fusebase de'pl'oy); and a command built at run time (the corpus'saccepted_misses, D2-no). Until a field refusal or incident names one, these stay a documented limit. -
The other limits of 6.0.0-m8 stand as its note lists them (symlinks under
core.symlinks=false, PowerShell lines, the approval record,.git/flow/range-baseper clone, Codex unproven,CREATE_NO_WINDOWin a terminal, Englishschtasksoutput, the CLI checkpoints, an editedpolicies/evidence-classes.yml).
For 6.0.0, CI gates on both legs: Linux and Windows (Git Bash).