wb-flow 1.0.7 — Detailed Release Innovations & Technical Guide
✅ Released 2026-10-02. Source of truth:
CHANGELOG.mdandRELEASE_1.0.7.md.
This page leads with the 1.0.7 technical changes. The 1.0.5 guide and the original v1.0.2 deep dive are retained below as archived history.
1.0.7 Technical Map
| Area | What changed |
|---|---|
bin/wave.js | target parser: comma sets, -> / .. ranges, mixing, whitespace tolerance, dedup, normalisation to matrix order. Exposes opts.waves (always populated); opts.wave stays its head |
bin/wave.js | generation consumes opts.waves: a multi-label target emits a driver plus one member script per wave |
bin/next.js | waveSegments(waves, risks) computed once, rendered twice — per-wave run-book and the coalesced set block |
bin/next.js | serial-risk records keyed by wave label, so the ⏸ pause line is reachable |
bin/lint.js | entry-point corpus recognises --wave=A,B and --wave=C->D |
output_conventions.md | §10.4.1 grammar table replaces the single-label rule; new §10.4.2 |
wbWork_template.md | new §10.7 — generator-editing waves may not run as one driver |
bin/session.js · bin/wave_router.js | per-chat role variables (wb-flow session); executor ≠ validator with no exemption (executorExclusion, validator cells only) |
bin/wave_generator.js | I1 in-cell BASH_MAX_TIMEOUT_MS + BACKGROUNDED · I2 WORK_PIDS + per-validator tail --pid wait (no phase barrier) · I3 MISPLACED · I4 cross-scope find roots + first-run UNVERIFIED · F21 breach attribution (exit 2 = orchestrator bookkeeping → warning) |
bin/model.js | I5 a green probe removes its ⚠️ unreachable mark; de-dup; honest count |
bin/next.js | I6 ✅-tagged work wave → -M=$VALIDATOR, pair → $PLANNER |
bin/lint.js | I7 step-9 V10 worker-authored Verify; isPlanFile lets a # Plan Backlog: H1 outrank type: |
test/regressions_20261002.test.js | 8 planner-owned cases; --selftest against frozen pre-fix copies (sha256 in pre/MANIFEST.json); --all in test:unit |
The target grammar
--wave=A # single label (unchanged)
--wave=A,B # set
--wave=C->D # range, inclusive
--wave=C..D # range, alternate spelling
--wave=A,C->E # mixedRanges expand against the matrix's own label order, never the alphabet. A plan whose labels run A, B, D does not acquire a C from --wave=A->D.
Four rejections, each non-zero with a named reason
| Target | Reason |
|---|---|
--wave=A,B:W | a role suffix cannot apply to a multi-label target |
--wave=A,all | a loop alias is not a set member |
--wave=A,Z | unknown label; the error lists the labels found |
--wave=C->A | reversed range — refused rather than silently sorted |
The driver, and why it halts
A multi-label target generates waveset_<A>-<B>_<scope>.sh, which invokes each member wave's script in matrix order and stops at the first red member. Generated filenames use hyphens and never contain commas, so a set target cannot produce a filename that a shell splits.
§10.7 — the rule that constrains its own use
A set containing a wave that edits bin/wave.js or bin/wave_generator.js may not run as one driver: the driver is the artifact under edit, so the run would grade itself with machinery it is halfway through replacing. The plan that built this feature was itself executed one wave per pass for exactly this reason.
The silent narrowing, in detail
The parser landed in one row and the consumer in the next. In between, opts.waves existed and was populated, but generation still read opts.wave — its head. A two-wave target therefore produced a single-wave script: 🌊 Wave A, with the second wave's cells dropped and no warning emitted. Measured on plan_wb-core_20260920.md: four cells discarded. Before the parser existed the same target had failed loudly, so the regression window is precisely one row wide.
Wave gates from the 2026-10-02 pilot (I1–I7)
Planner-owned reproductions first. test/regressions_20261002.test.js drives the real modules (no mocks of the unit under test) on fixtures under test/fixtures/regressions_20261002/. --selftest runs every case against frozen copies of the pre-fix files and requires each to be red for its own reason. A case that passes on the frozen copy is hollow.
Pipelined validation (I2). Work cells record WORK_PIDS[<id>]. A validator cell's subshell blocks on tail --pid="$_wp" -f /dev/null (with a kill -0 poll fallback) only for the ids it covers, so a merged /wbValid --id=1,2 still waits for both. The old phase barrier is gone, and the tests that asserted it were inverted.
First-run UNVERIFIED (I4). Before dispatch, _g9_had_report_<id> records whether a task report already exists. An unchanged tree with a prior report is an idempotent re-run (PASS). Without one it is UNVERIFIED — nothing changed (first execution). Known side effect: a read-only row also reads UNVERIFIED on its first run, because its report lives under the pruned .wb/ tree.
Breach attribution (F21). check_plan.js exits 1 when task rows were destroyed or rewritten (FAIL, the C72 case). It exits 2 when only Done/Valid, the matrix or the run-book changed, which is the orchestrator's bookkeeping, and the gate prints a ⚠️ warning and continues.
Test coverage
test/wave_gates.js 270 → 317 passing, including a waveset group of 25 cases covering the grammar row by row, all four rejections, and the comma-free filename rule.
Archived: 1.0.5 Technical Guide
1.0.5 Technical Map
| Area | What changed |
|---|---|
| Plan trust | The accepted plan trust model states that plan task text and Verify cells are untrusted executable input. |
| Sandboxed dispatch | wb-flow wave --sandbox drops permission and sandbox bypass flags on all seven CLI lanes and scores refusals as REFUSED, never PASS. |
| No-op detection | Wave scoring now uses a content-hash diff over the workspace, excluding the task's own report folder, and emits a real NO-OP verdict. |
| Model catalog | wb-flow model --sync-catalog writes a curated, merged catalog from locally reachable CLIs instead of relying on a shipped personal snapshot. |
| Fallback chains | -M and role flags accept ordered chains and advance only on Gate-1/INFRA failures. |
| Wave matrices | Cells resolve role variables such as $WORKER from the active roster instead of embedding stale model names. |
| Validation | Executor/validator independence is checked by billing pool rather than model-name inequality. |
| Packaging | bin/model.js.bak was removed, and prepublishOnly now rejects backup/editor artifacts before a tarball can ship. |
Plan trust and --sandbox
The release records a security boundary that had previously lived only in operational habit: a plan file is markdown, but wave execution sends its task text and Verify command to assistant lanes that normally run with approval and sandbox bypass flags. The ADR at the plan trust model documents that those fields are untrusted, executable input and names the contract the runner must enforce before treating an oracle as proof.
wb-flow wave --sandbox is the first operator-facing mitigation. It omits the permission and sandbox bypass flags across the seven lanes the wave runner supports: claude, codex, grok, agy, opencode, gemini, and copilot.
The scorer also distinguishes secure refusal from success. If a lane refuses to run under --sandbox, the result is REFUSED; it is never scored as PASS. That matters for lanes such as agy, which can auto-deny every tool headlessly without the bypass and otherwise appear to have completed nothing.
Content-hash no-op gate
The previous no-op check was not a scoring gate. It compared bin/-only mtimes and printed a message nothing consumed. In 1.0.5, the wave runner compares workspace content instead, excludes the current task's report directory so a report cannot hide a no-op, and feeds NO-OP into the same verdict path as the other wave gates.
Self-maintaining model catalog
wb-flow model --sync-catalog fills models.json from what the current machine can actually reach. It enumerates installed CLIs, curates the raw model list, three-way-merges with the local catalog, writes atomically, and keeps the prior file as .bak.
Scoped provider changes are explicit:
wb-flow model --sync-catalog --dry-run
wb-flow model --sync-catalog
wb-flow model --add=zen,codex,grok
wb-flow model --remove=openrouterProvider sync is transactional per provider: one failing provider does not abort the others. Providers that cannot enumerate themselves can be filled from picker text or a file with --from-picker / --from-file=.
Fallback chains and role-variable waves
Every dispatch flag now accepts a comma-separated chain. The runner validates every link when parsing the flag, then advances only when Gate 1 says the agent never ran. Gate-2 and Gate-3 failures are task failures, not infrastructure failures, so they are reported rather than retried with the next model.
Wave matrices now name role variables instead of concrete model slugs. A plan carries an active roster for Planner, Validator, Worker, and Mechanical lanes; generated cells route through those variables, which prevents old matrices from pinning a subscription-specific slug forever.
Validation also moved from name comparison to billing-pool comparison. An openai/* executor and another ChatGPT-pool validator are treated as the same provider family; the picker chooses a different pool when the chain offers one, and warns when an explicit -M= override creates a same-provider validation route.
Packaging fix
The release removes bin/model.js.bak, an 86 KB, 1,902-line backup artifact that was stale, unused, and being included in every published package because files[] allowed all of bin/. Instead of adding another negation, 1.0.5 adds a prepublishOnly release-file assert that rejects *.bak, *.orig, *.rej, and *~ in the pack manifest before publication.
Archived v1.0.2 Technical Write-Up
This archived section preserves the earlier comprehensive deep dive with code examples, terminal traces, script logic, and matrix specifications for capabilities introduced in wb-flow (v1.0.2) compared to v1.0.1.
🎛️ Universal Model Flags & Cell-Level Wave Targeting (2026-08-01)
Four role flags, one delegation flag, one cell selector — accepted by every /wb* command that dispatches work.
| Flag | Alias | Effect |
|---|---|---|
--planner= | -p | Set the 🧠 Planner chain |
--validator= | -v | Set the ✅ Validator chain |
--worker= | -w | Set the 🔨 Worker chain |
--mechanical= | -m | Set the 📋 Mechanical chain |
--model= | -M | Delegate this one run — highest priority |
--wave=<L>:<R> | -W | Run one cell: :P :V :W :M |
Role flags persist. Passing one is exactly equivalent to running /wbModel first — the choice is written to templates/commands/model_recommendations.md and survives into every later dispatch:
/wbValid <folder>/ --id="<i>" -v="go:ds4pro" -p="gemini 3.6:"
# ≡ /wbModel -v="go:ds4pro" -p="gemini 3.6:"
# /wbValid <folder>/ --id="<i>"-M delegates a single invocation and outranks role routing, the roster, and the executor≠validator rule. Run from the Claude CLI, it means Claude dispatches instead of executing:
/wbWork <folder>/ --id="<i>" -M=$WORKER
# → opencode run -m opencode-go/deepseek-v4-pro --dangerously-skip-permissions "/wbWork …"Because -M can override the executor≠validator rule, the override is printed in the routing line ([--model=… — explicitly delegated by the operator]) so a self-validation can never happen invisibly.
Cell targeting addresses one matrix cell by row × column:
/wbWork <folder>/ --wave="A:W" # only A's Worker cell
/wbWork <folder>/ --wave="A:W" -M=$WORKER # …delegated
/wbWork <folder>/ --wave="A:V" -v="go:ds4pro" # …and persist the rosterAn unrecognised role letter exits non-zero rather than falling back to the whole row — a typo that quietly runs four cells instead of one is worse than an error.
⚠️ Breaking:
-wnow means--worker, not--wave. The four role flags must be symmetrical; a set where three mean model and one means wave is a trap.--wavetakes-W.
Precedence, highest first: -M → explicit role flag → plan-header roster → model_recommendations.md → built-in defaults.
📋 Copy/Paste Command Block (2026-08-01)
/wbWork and /wbPlan now print, directly below the ## 🌊 Next Executable Sequence table, a fenced block of the same commands as bare runnable lines — no table markup, no <br>, no *(⏱️ N min)* annotations, execution order, dispatchable rows only. The matrix is for reading; this block is for running.
📋 Quick Navigation
- 1. Local Subshell Output-Error Guarding (
.wb/bin/wbRun) - 2. Wave Orchestration Engine & Autonomous Auto-Pilot (
-y/--yes) - 3. Pre-Flight Explanation Blueprints (
--as="<style>") - 4. Dynamic Per-Role Model Override Flags
- 5. Persistent Plan Model Roster Headers
- 6. Matrix Auto-Correction Engine
- 7.
/{cmd}Catalog Expansion & Standardized 9-File Docs Suite - 8. ~80% Context Window Token Optimization
1. Local Subshell Output-Error Guarding (.wb/bin/wbRun)
The Technical Problem in v1.0.1
In v1.0.1, CLI tools invoked in shell chains (e.g. opencode run ... || agy -p ...) relied solely on exit codes (? != 0). However, many AI provider CLIs return exit code 0 even when encountering fatal API errors (such as insufficient account credits or quota exhaustion). As a result, the standard || operator failed to trigger fallback models, causing silent task execution failure.
The Solution in next (v1.0.2)
wb-flow introduces .wb/bin/wbRun, a zero-dependency local wrapper that executes commands in subshells, intercepts stdout/stderr, and checks for error strings regardless of return status.
Binary Source Code (.wb/bin/wbRun):
#!/usr/bin/env bash
# .wb/bin/wbRun — Output-Guarded Subshell Wrapper for wb-flow
cmd_label="$*"
echo "--------------------------------------------------------------------------------"
echo "▶ Executing: $cmd_label"
echo "--------------------------------------------------------------------------------"
# Capture stdout and stderr
out=$("$@" 2>&1)
res=$?
echo "$out"
# Guard check: non-zero status OR presence of "error:" (case-insensitive)
if [ $res -ne 0 ] || echo "$out" | grep -qi "error:"; then
echo ""
echo "⚠️ [wbRun] Error detected in model command: $cmd_label"
echo "🔄 Switching to next fallback model..."
echo "--------------------------------------------------------------------------------"
exit 1
fi
exit 0Real-World Fallback Execution Log:
[wissemb11@localhost wb-labs]$ .wb/bin/wbRun opencode run -m openrouter/meta-llama/llama-3.3-70b-instruct --dangerously-skip-permissions "hi" || .wb/bin/wbRun agy --model gemini-3.1-pro-high --dangerously-skip-permissions -p "what s your name as a model"
--------------------------------------------------------------------------------
▶ Executing: opencode run -m openrouter/meta-llama/llama-3.3-70b-instruct --dangerously-skip-permissions hi
--------------------------------------------------------------------------------
> build · meta-llama/llama-3.3-70b-instruct
Error: Insufficient credits. This account never purchased credits. Make sure your key is on the correct account or org, and if so, purchase more at https://openrouter.ai/settings/credits
⚠️ [wbRun] Error detected in model command: opencode run -m openrouter/meta-llama/llama-3.3-70b-instruct --dangerously-skip-permissions hi
🔄 Switching to next fallback model...
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
▶ Executing: agy --model gemini-3.1-pro-high --dangerously-skip-permissions -p what s your name as a model
--------------------------------------------------------------------------------
I am currently using the Gemini 3.1 Pro model. How can I assist you with your coding tasks today?2. Wave Orchestration Engine & Autonomous Auto-Pilot (-y / --yes)
Parallel Background Dispatch (bin/wave.js)
When running /wbWork <plan.md> --wave=A, wb-flow parses the plan's ## 🌊 Next Executable Sequence DAG matrix, groups independent non-colliding tasks, and generates a background wave script (wave_A.sh).
Every task cell in wave_A.sh is automatically wrapped with .wb/bin/wbRun for fail-fast subshell execution:
#!/usr/bin/env bash
# Generated wave script: wave_A.sh
.wb/bin/wbRun opencode run -m opencode-go/deepseek-v4-pro --dangerously-skip-permissions "/wbWork plan.md --id=9 --no-plan-update" &
PID1=$!
wait $PID1Autonomous Auto-Pilot Mode (-y / --yes)
In v1.0.1, wave script spawning required interactive confirmation prompts from the user. In next, the --yes (-y) flag enables fully autonomous, zero-touch execution:
# Non-interactive CLI invocation wrapped in wbRun
.wb/bin/wbRun claude -p --permission-mode auto "/wbWork frontEnd/wbc-ui3/packages/ --wave --as='expert.steps,en,fr then diagrams' -y"Effects of --yes / -y:
- Auto-Resolves Plan Ambiguities: Automatically adopts orchestrator recommendations for held options (e.g. license texts, copyright holder strings).
- Zero-Touch Script Generation: Immediately writes
wave_A.shand spawns background subshells without pausing for terminal prompts.
3. Pre-Flight Explanation Blueprints (--as="<style>")
The --as="..." flag instructs wb-flow to generate an architectural explanation blueprint before executing any code changes.
/wbWork plan_packages_20260731.md --wave=A --as="expert,steps,en,fr then diagrams"Handoff Workflow:
- Blueprint Generation: Runs
/wbExplain plan.md --id=9 --as="..."to produce a bilingual (EN/FR) markdown report with Mermaid flowcharts (task_9_details_packages_20260731.md). - Table Linkage: Updates the
🔗 Detailscolumn inplan_packages_20260731.mdpointing to the generated blueprint. - Execution Handoff: Passes control to
/wbWork plan.md --id=9 --no-plan-updatewithin the same execution lane.
4. Dynamic Per-Role Model Override Flags
In v1.0.1, model assignments were fixed in static recommendation markdown tables. In next, users can override model fallback chains dynamically per role via command line arguments across /wbWork, /wbPlan, and /wbModel:
# Override Worker and Validator models for Wave A
/wbWork packages/ --wave=A --worker="DeepSeek V4 Pro,Kimi K3" --validator="Gemini 3.5 Pro"
# Override all 4 roles when initializing a plan
/wbPlan packages/ --planner="Claude Opus 5" --worker="DeepSeek V4 Pro,Kimi K3" --validator="Claude Sonnet 4.7" --mechanical="Gemini 3 Flash"CLI Flag Reference:
--planner="<models>"(-p): Set/override Planner model fallback chain.--validator="<models>"(-v): Set/override Validator model fallback chain.--worker="<models>"(-w): Set/override Worker model fallback chain.--mechanical="<models>"(-m): Set/override Mechanical helper model fallback chain.
5. Persistent Plan Model Roster Headers
When a plan is created or updated, wb-flow writes the configured model roster block directly into the header of ## 🌊 Next Executable Sequence:
## 🌊 Next Executable Sequence
> **Active Model Roster for this Plan:**
> 🧠 **Planner:** `Claude Opus 5 || Gemini 3.5 Pro`
> ✅ **Validator:** `DeepSeek V4 Pro`
> 🔨 **Worker:** `DeepSeek V4 Pro || Kimi K3`
> 📋 **Mechanical:** `Gemini 3 Flash || DeepSeek V4 Flash`
| Wave | 🧠 Planner | ✅ Validator | 🔨 Worker | 📋 Mechanical |
|---|---|---|---|---|
| **A · 🔨 work** | — | — | `/wbExplain plan.md --id=9 --as="expert,steps"`<br>`/wbWork plan.md --id=9`<br>→ *DeepSeek V4 Pro* *(⏱️ 15 min)* | — |
| **A · ✅ validate** | — | `/wbValid plan.md --id=9`<br>→ *DeepSeek V4 Pro* *(⏱️ 15 min)*<br><sub>pairs A · 9</sub> | — | — |Subsequent runs of /wbWork or /wbValid on this plan read and preserve this roster automatically.
6. Matrix Auto-Correction Engine
If a target plan file is missing the ## 🌊 Next Executable Sequence matrix (or if the matrix was corrupted):
# Auto-correct plan file and generate wave matrix
/wbPlan plan_packages_20260731.mdAuto-Correction Procedure:
- Scans the task table in
plan_packages_20260731.md. - Resolves task dependencies via the
DepDAG. - Constructs parallel wave rows (
Wave A,Wave B, etc.). - Injects the
> **Active Model Roster for this Plan:**header block using active selections frommodel_recommendations.md(or passed CLI flags). - Writes the repaired plan file to disk.
7. /{cmd} Catalog Expansion & Standardized 9-File Docs Suite
/wbModel Command Addition
The /wbModel command was added to inspect and override model rosters (/wbModel --validator='DSV4 pro').
Standardized 9-File Documentation Suite
Every command directory under docs/commands/wb<Command>/ now contains 9 uniform documentation files (306 files total):
wb<Command>/
├── README.md # Documentation Hub & Navigation
├── wb<Command>.md # Full Specification & Syntax Reference
├── wb<Command>_eli5.md # Simple Plain-Language Guide (ELI5)
├── wb<Command>_examples_part1.md # Baseline Invocations & Standard Use Cases
├── wb<Command>_examples_part2.md # Advanced Dispatches & Role Overrides
├── wb<Command>_exhaustive_simulation.md # Step-by-Step State Trace Simulation
├── wb<Command>_expert.md # Deep Dive Architecture & Subshell Guarding
├── wb<Command>_live_demo.md # Authentic Terminal Logs with Visual Separators
└── wb<Command>_practical.md # Production Recipes & CI/CD Workflows8. ~80% Context Window Token Optimization
By routing worker execution to background sub-agents (via --wave), raw code edits, file diffs, and verbose terminal outputs remain in external subshells.
Quantitative Comparison:
| Execution Mode | Claude Context Consumption per Task | Context Growth Over 10 Tasks | Primary Model Cost |
|---|---|---|---|
| Interactive (v1.0.1) | ~45,000 tokens | ~450,000 tokens (Context Full) | High (Claude API) |
| Wave Mode (v1.0.2) | ~1,500 tokens (task_report.md only) | ~15,000 tokens (Sustained) | ~80% Lower (Flat-rate Sub-Agents) |
9. Six wb-flow CLI Subcommands (2026-08-01—2026-08-03)
In v1.0.1, wb-flow shipped a single init subcommand that scaffolded project templates. v1.0.2 adds five more, turning wb-flow into a standalone CLI that operates on plan files, model rosters, and the wave execution engine directly — no / prefix, no AI context needed.
9.1 wb-flow init <dir> — Project Initialization
Scaffolds the full 33-command template tree, model_recommendations.md, and a wrapper script. Read-only preview via --dry --json outputs the file inventory without touching disk.
wb-flow init my-project/
# Creates: my-project/.wb/commands/wb<Cmd>/ (33 dirs × 9 files = 306 docs)
# my-project/.wb/bin/wbRun
# my-project/templates/commands/model_recommendations.md
# my-project/templates/commands/composition_toolbox.mdExit codes: 0 on success; 1 if the directory already has a .wb/ or .claude/ directory (wb-flow refuses to overwrite an existing install). --dry --json always exits 0 and prints JSON to stdout.
9.2 wb-flow model <scope> — Role-Model Roster Read/Write
Reads or writes the role-model assignment chain that every /wb* command uses to decide which AI model runs each role (Planner, Validator, Worker, Mechanical).
Read mode (no flags):
wb-flow model deployement/packages/wb-flow/next/core/
# Prints the current roster:
# 🧠 Planner: Claude (in-session) || claude-opus-4-6-thinking
# ✅ Validator: Claude (in-session) || opencode-go/kimi-k2.7-code
# 🔨 Worker: opencode-go/deepseek-v4-pro || gemini-3.1-pro-high
# 📋 Mechanical: opencode-go/qwen3.7-plus || gemini-3.6-flash-highWrite mode (any role flag):
wb-flow model deployement/packages/wb-flow/next/core/ --worker="go:ds4pro,go:kimi"Persists the chain to <scope>/templates/commands/model_recommendations.md. The format is <cli-prefix>:<model-slug>,<cli-prefix>:<model-slug> — the first succeeds or the second fires.
Exit codes: 0 on success; 1 if model_recommendations.md is missing and no role flag was passed (write to create it). When --planner and --validator are both set, the file is atomically replaced via a temp-file rename to prevent partial writes.
Probe mode (--probe --all[=<what>]) — added 2026-08-14:
wb-flow model --pick --probe --all # whole catalog, grouped by role, unreachable hidden
wb-flow model --pick --probe --all=raw # stream everything, failures included (pre-1.0.2)
wb-flow model --pick --probe --all=anthropic # one provider only
wb-flow model --pick --probe --all=planner # one role only
wb-flow model --probe --all=anthropic # identical behaviour headless, for scripts and CIEach line names the provider, pool and CLI actually invoked:
MODEL PROVIDER CLI
✅ openai/gpt-5.6-terra openai/chatgpt codex
✅ gemini-3.1-pro-high antigravity/google-one agy
⚠️ gpt-oss-120b-medium antigravity/google-one agy — substituted: using Gemini 3.1 ProWhy the columns exist. Until 1.0.2 routing was re-derived from the slug string, guarded by slug.indexOf('/') === -1 && CODEX_ONLY.test(tail). Every namespaced catalog entry failed that guard and fell through to opencode, which cannot bill it — so the probe reported "Insufficient balance" for every anthropic/* and openai/* model, while claude -p --model claude-opus-5 and codex exec -m gpt-5.6-terra answered them instantly. A misroute was indistinguishable from a billing failure because nothing on the line said which CLI had been used.
bin/cli_registry.js now maps provider → CLI and CLI → argv once, and is imported by both the picker/probe and the wave dispatcher — so a model cannot be probed through one CLI and dispatched through another. Uncatalogued slugs still fall back to the old heuristics and are marked ? so a guessed route stays attributable.
The ⚠️ substituted state is separate from ❌ on purpose: a provider that silently answers as a different model is not reachable-as-requested, and must not enter the roster — but calling it a plain failure would hide the substitution.
9.3 wb-flow wave <plan> --wave=<label> — Wave Execution Engine
The core engine that converts the plan's ## 🌊 Next Executable Sequence DAG matrix into collision-free parallel background dispatches.
--list previews the routing without spawning anything — one line per cell, naming the lane, CLI, model, and reason:
wb-flow wave plan.md --wave=H --list
# 🔨 Worker opencode-go/deepseek-v4-pro --id=15 (role routing)
# 📋 Mechanical opencode-go/qwen3.7-plus --id=19 (role routing)--summary streams only decision lines per cell — ▶ (dispatch), G1 (infra), G2 (artifact), G3 (oracle), VERDICT, and PER-ID. Full transcripts are tee'd to:
tasks/task_<ID>/task_<ID>_log_YYYYMMDD_hhmmss.log(per-cell).wb/workflows/reports/<YYYY>/<MM>/<DD>/waves/logs_<label>_<timestamp>/(per-wave)
Measured on a real 10-cell wave: 63,002 tokens unsummarised, of which none changed a single Done box. --summary is the default when /wbWork invokes --wave; pass --no-summary to get the raw stream back when debugging one cell's behaviour.
Generated script structure (what wave.js emits for a wave):
#!/usr/bin/env bash
# wave_H.sh — 2026-08-03
LOG_DIR=.wb/workflows/reports/2026/08/03/waves/logs_H_20260803_1100
mkdir -p "$LOG_DIR"
# Cell: 🔨 Worker / --id=15 → opencode-go/deepseek-v4-pro
( opencode run -m opencode-go/deepseek-v4-pro --dangerously-skip-permissions \
"/wbWork deployement/packages/wb-flow/next/.wb/.../plan.md --id=15 --no-plan-update" \
) > >(tee "$LOG_DIR/cell_15_HW.log" | grep -E '^(▶ |G[123] |VERDICT|PER-ID)' ) 2>&1 &
PID_15=$!
# Cell: 📋 Mechanical / --id=19 → opencode-go/qwen3.7-plus
( opencode run -m opencode-go/qwen3.7-plus --dangerously-skip-permissions \
"/wbWork deployement/packages/wb-flow/next/.wb/.../plan.md --id=19 --no-plan-update" \
) > >(tee "$LOG_DIR/cell_19_HM.log" | grep -E '^(▶ |G[123] |VERDICT|PER-ID)' ) 2>&1 &
PID_19=$!
wait $PID_15 $PID_199.4 wb-flow watch — Live Wave Monitoring
Monitors running wave executions. Lists cells by state with progress bars, ETA, and OVER est detection.
wb-flow watch # interactive, updates every ~30 s
wb-flow watch -1 # one cycle, then exit — non-zero if any cell is still running
wb-flow watch --run=logs_H_20260803_1100 # target a specific wave log directoryThree cell states, never two:
| State | Meaning | Detection |
|---|---|---|
✅ done | Wrote an __EXIT__=<rc> marker | ___EXIT___ file exists in the log directory |
🔄 running | No marker, live process holds it | pgrep -f -- "--id=<id> " returns a PID |
⚠️ ENDED | No marker and no live process | Neither condition holds — killed, crashed, or OOM-ed |
Liveness vs idleness. watch reports idle (seconds since log last grew), but this is not a kill signal on its own: agents buffer while composing, so a thinking cell is indistinguishable from a dead one by log growth alone. The heuristic uses a composite: the process is alive (kill -0) and its CPU time advances between samples (ps -o times=). When both signals look dead, read the log's final lines before concluding — a delegating agent usually names what it dispatched. Measured: ten concurrently-running validators all showed idle ≈ 230s simultaneously while every one was healthy (one had burned 56s of CPU).
Exit codes: 0 when all cells are done with G1≥pass; 1 if the run directory is missing; 2 if any cell is still running after a -1 cycle. --run= on a nonexistent directory exits non-zero.
9.5 wb-flow snap <plan> — State Freezing
Freezes the current plan state into .wb/snaps/<YYYYMMDD>_<label>/. Useful before a destructive wave or plan merge — the snap is git-agnostic and survives plan recomputes.
wb-flow snap plan.md # symlink → plan.md + tasks/
wb-flow snap plan.md --copy # copy content (survives upstream deletion)
wb-flow snap plan.md --label=pre-merge # names it 20260803_pre-merge/ instead of auto-labelSymlinks are the default because they're cheap and traceable; --copy is for untracked files that would disappear with a repo reset.
9.6 wb-flow next <plan> --embed — What to Dispatch Next
Shells out to derive the current wave inventory from the plan's Done/Valid state and prints:
- The wave inventory (one row per wave, with open cell count and estimated time)
- An ordered command list ready to copy-paste
- The
▶️ How to run this planblock (when--embedis passed, it replaces the block between its markers in the plan file itself)
wb-flow next plan.md # print to stdout
wb-flow next plan.md --embed # replace the ▶️ block in the plan fileThis is run automatically by /wbWork and /wbPlan after every Done/Valid change — the matrix and the ▶️ block are derived state and must agree.
10. Wave Mode Flags: --summary and --sessions
10.1 --summary — Decision-Only Streaming (Wave Mode Only)
Passed through to wb-flow wave, --summary keeps every cell's full transcript in its log file but streams only the decision lines into the orchestrator's context:
| Streamed line | Meaning |
|---|---|
▶ /wbWork plan.md --id=15 | The dispatch command |
G1: PASS | Infra gate — agent ran, no fatal error |
G2: PASS | Artifact gate — task report exists |
G3: PASS | Oracle gate — Verify command exited 0 |
VERDICT: DONE | All three gates passed |
PER-ID: DONE=[15] | Per-id rollup (for merged cells) |
Without --summary, every cell's full transcript — template reads, ls -la dumps, ANSI escape sequences — streams into the orchestrator's context. Measured on a real 10-cell wave in this repository: 63,002 tokens, of which one cell was 16,824, and none of it changed a single Done box.
--summary is the default when /wbWork invokes --wave. Drop the flag (--no-summary) only while debugging a specific cell's behaviour.
10.2 --sessions — Warm Session Reuse (Wave Mode Only)
Reuses one warm opencode session per (scope, model), resumed and forked so parallel cells don't share a conversation. Off by default.
wb-flow wave plan.md --wave=H --sessionsHow it works. The session key is (scope, model). Before dispatching a cell, wb-flow checks whether a session for that (scope, model) was started by a prior cell in this wave. If so, it forks the session — copying the conversation history up to that point — and resumes the fork. The original session is left untouched for the next cell that needs it.
⚠️ Bug-5 warning — do not use --sessions across plans yet. The session key currently omits the plan filename: sessionKeyFor(scope, model) does not include path.basename(planPath). On 2026-08-03, this caused two cells to execute the wrong plan's tasks — they resumed an 08-02 conversation into an 08-03 dispatch. A third cell overwrote a validated task report from the previous plan. The resumed session's history outranks the command line: the agent finds old context more compelling than new instructions. Until the key includes the plan name, --sessions is unsafe in any scope holding more than one plan file.
Cost trade-off. A resumed session replays its full history, so it trades a cold re-read (startup cost: reading the plan + template + scope files) for a growing transcript (cumulative cost: all prior cell context). Measure with opencode stats --days 1 --models before adopting it as a habit. For waves with large-cell budgets (≥40 min each), the startup cost is negligible; for waves with many short cells (≤10 min each), --sessions may actually increase token consumption.
11. The Three-Gate Wave Correctness Contract (2026-08-03)
The Problem It Solves
On 2026-07-31, a wave cell fixing wbRun's unanchored grep -qi "error:" was marked ❌ by the identical guard still inlined in wave.js — the bug classified its own fix as a failure. In the same run, three cells that never started were indistinguishable from it by output alone. Of four cells the old harness called "failed", three had done nothing and one had fully succeeded.
Output text is never a success signal. A /wbWork log legitimately contains Error:, failed, and cannot whenever the task is about error handling — and those are exactly the rows where a false verdict costs the most. A cell that never ran produces an empty log (no Error: at all). An unusable model can exit 0 having done nothing. Neither grep nor exit codes are reliable on their own.
The Three Gates
Every wave cell is classified by three independent checks — none of which inspect the log text for error strings:
| Gate | Question | Signal |
|---|---|---|
| 1 · Infra | Did the agent run at all? | CLI exit code + anchored fatal patterns: ^Error: Model not found, insufficient credits, rate limit, quota, authentication. Ungrepped Error: is not a signal. |
| 2 · Artifact | Did it write what it was required to write? | For /wbWork and /wbExplain cells: the task report (tasks/task_<ID>/task_<ID>_report_*.md) exists. For /wbValid cells: the validation section was appended, or the plan's ☐ Valid cell changed. |
| 3 · Oracle | Does the task's own Verify command pass? | Run the row's Verify cell verbatim; exit 0 = pass. The G3 oracle runs from the plan's directory (not the repo root — bug 2's fix), so plan-relative paths resolve correctly. |
Verdict Table
| G1 | G2 | G3 | Verdict | Done box |
|---|---|---|---|---|
| ✗ | — | — | INFRA — never ran | ⬜ |
| ✓ | ✗ | — | NO-OP — ran, produced nothing | ⬜ |
| ✓ | ✓ | ✗ | ATTEMPTED — needs a validator or the user | ⬜ |
| ✓ | ✓ | ✓ | DONE | ✅ |
Only a 1-2-3 green cell gets its Done box checked. Everything else stays ⬜ with its verdict and reason in the Wave notes.
Model Fallback Rule
Fallback fires on Gate 1 only. A Gate 2 or Gate 3 failure is reported, never retried with the next model in the chain: the blocker is the task, not the agent, so a second model meets the same wall and doubles the spend. Re-dispatch after a Gate 2/3 failure is a human decision.
Merged Cells & Per-ID Gating
Two rows may share one dispatch when they share a plan file, a model, a role column, and are both small enough that losing the parallelism costs less than paying the agent's start-up context twice. The generated script runs one agent over both rows and prints a verdict per id plus a roll-up:
G2 [3]: PASS G3 [3]: PASS VERDICT [3]: DONE
G2 [8]: NO-OP — no task report at tasks/task_8/task_8_report_*.md
VERDICT [8]: NO-OP
PER-ID: DONE=[3] UNVERIFIED=[] ATTEMPTED=[] NO-OP=[8]
VERDICT: NO-OP — 8The cell exits on its worst id, but the PER-ID: line is what you read: check 3's Done box, leave 8 at ⬜ with its verdict. Do not fail a merged cell wholesale — a red exit code routinely carries a genuinely green id.
Why This Exists
The three-gate contract makes two types of false verdicts structurally impossible:
- False negatives (a cell that succeeded is called failed) — prevented because no gate inspects the log text for error patterns. A task fixing error handling will contain the word
Error:by design; the old guard failed it for that. - False positives (a cell that did nothing is called DONE) — prevented because G2 requires a physical artifact. A
wbValidcell that no-ops landsNO-OP, notDONE, because bug 3 extended G2 gating to validators. An unusable model that exits 0 having written nothing landsNO-OPbecause no report exists.
The contract is the harness's truth record, not the agent's output. A validator that writes "✅ All passed" into its log is not trusted; the validator's own G2 is what determines whether it succeeded. The agent's claim and the harness's verdict are deliberately independent.
← Back to Documentation Hub · View Concise Overview (new.md)
⏱️ Wave Execution Session Tracking (/wbTrack)
Whenever /wbWork or /wbPlan is executed with the --wave=<label> flag, the execution pipeline automatically wraps the wave dispatches with session tracking:
# Executing /wbWork <path_scope> --wave=A follows this sequence:
1. /wbTrack <path_scope> # Starts/joins today's session tracking log
2. wave_A.sh dispatches # Executes parallel wave tasks
3. /wbTrack --stop # Stops and finalizes session tracking log🚀 Next Wave Execution Command Suggestions
Below the ## 🌊 Next Executable Sequence matrix and Wave notes, /wbPlan and /wbWork output a dedicated recommendation block calculating total estimated duration (Est. Time) and offering ready-to-run CLI commands tailored for time and cost considerations:
- Option 1 (Next Wave):
.wb/bin/wbRun claude -p --permission-mode auto "/wbWork plan.md --wave=A -y" - Option 2 (With
--as):.wb/bin/wbRun claude -p --permission-mode auto "/wbWork plan.md --wave=A --as="expert,steps" -y" - Option 3 (All Waves):
.wb/bin/wbRun claude -p --permission-mode auto "/wbWork plan.md --wave=all -y"
