How the aina-intelligence-os session (2026-08-04, Claude Fable session 2c9b77d8) stood up a
founder-visible program board with a live Cloudflare page, and the operating rhythm that keeps it
true. Give this file to any session that should run the same way.
Reference implementation: aina-intelligence-os — tools/flow_board_html.py,
ops/board-activity.json, ops/dispatch/, live at https://aina-flow-board.pages.dev
1. flow-next plugin installed in Claude Code (flowctl lives at ~/.claude/plugins/cache/flow-next/flow-next/<version>/bin/flowctl).
2. Wrangler authenticated on the Mac (npx wrangler whoami) with Cloudflare Pages access.
3. A git repo for the program (board state and renderer are committed; the board is code).
4. For build lanes: VDS ssh (ssh -o BatchMode=yes aina-vds-ts) with codex CLI available.
5. Linear access for milestone write-backs.
"Loaded" ≠ "installed". An installed repo has ALL of:
setup_mode present in .flow/meta.json<!-- BEGIN FLOW-NEXT --> rail block in both CLAUDE.md and AGENTS.md(that block is the agent rail — without it, nothing instructs future sessions to keep writing
board state, and the board silently goes stale)
Install commands (rail write to CLAUDE.md/AGENTS.md is a founder hard gate — show the block
text and get approval first):
FC=~/.claude/plugins/cache/flow-next/flow-next/3.4.5/bin/flowctl
T=~/.claude/plugins/cache/flow-next/flow-next/3.4.5/codex/skills/flow-next-setup/templates
$FC setup-block apply --file CLAUDE.md --template $T/claude-md-snippet-plugin.md --json
$FC setup-block apply --file AGENTS.md --template $T/agents-md-snippet.md --json
$FC setup-mode set plugin --json # refuses until the rail exists — that's the point
1. Mission + non-negotiable doctrine live in .flow/STRATEGY.md (the anms canon lock — founder decisions, model assignments, hard exclusions). One mission, never a second plan file.
2. One spec per milestone; slices are the spec's ordered tasks:
$FC spec create --title "M-NAME: what this milestone delivers" --json
$FC spec set-plan <id> --file plan.md # canon block + numbered slices, acceptance in the slice text
$FC task create --spec <id> --title "S1: ..." [--deps <prev-task-id>]
3. Falsify the plan before building (mandatory): dispatch an independent adversarial review on a DIFFERENT engine (we use gpt-5.6-terra) with a non-leading brief ("find what is wrong, not endorse"). Apply every accepted finding to the specs BEFORE the first build lane. Our round 1 returned BLOCK with 7 findings — all real.
0. Enable flow-next's own HTML artifact mode first (shipped with the plugin; we missed it initially because the install was incomplete): flowctl config set artifacts.html.enabled true. With it on, the capture/plan/make-pr skills generate a rich per-spec review document at .flow/artifacts/<spec-id>/spec.html (and pr.html per PR) following the plugin's own design contract (references/html-artifacts.md — "render lens, not record", self-contained single file, self-check grep before publishing). These per-spec lenses are the deep-dive layer; the board page below is the live program overview — deploy the lenses alongside the board (cp -R .flow/artifacts <deploy-dir>/artifacts) and link spec titles to them.
1. Renderer: a small script (tools/flow_board_html.py) that emits one static index.html. Critical lesson: read spec/task status from flowctl specs --json / flowctl tasks --json — NEVER from the .flow/specs|tasks/*.json files (those are creation snapshots; the founder caught the stale-status bug live). Stamp generated-at + source commit on the page.
2. Include ops/board-activity.json — a hand-appended lane-activity feed. Task checkboxes move slowly; the feed is what makes the page feel alive between status flips.
3. Auto-refresh meta tag: 600s (founder-tuned; 120s was too aggressive).
4. Deploy:
npx wrangler pages project create <board-project> --production-branch main # once
python3 tools/flow_board_html.py /tmp/flow-board
npx wrangler pages deploy /tmp/flow-board --project-name=<board-project> --commit-dirty=true
curl -s https://<board-project>.pages.dev/ | grep "generated" # fetch receipt — always verify
5. One-link hub + VDS-owned auto-publish (Mac-independent): the board page carries a Links section (spec lenses, Linear issue, active PR, reconciliation ledger, RUNLOG, this playbook served at /playbook.html) so the founder needs exactly one URL. Publishing runs ON THE VDS — founder requirement: nothing depends on the Mac being online. A VDS cron job (*/10, tools/flow_board_publish_vds.sh) pulls main, renders from the committed git tree (no flowctl), and redeploys via the VDS's own wrangler ONLY when content changed (timestamp-stripped hash compare), logging to /srv/aina/ops/flow-board-publish.log. Sessions (which may run over SSH) just commit + push board state; the VDS does the rest. Integration with the pkm-drop → pkmlive surface is the intended next hop once that pipeline lands — the board docs are all committed files, so pkm-drop can pick them up directly.
6. Honesty rules (from the Terra plan review): pre-existing/legacy board content is labeled "historical/unreconciled" until a reconciliation ledger (every old spec/task dispositioned as DONE-EVIDENCED / SUPERSEDED / CARRY-FORWARD / UNRESOLVED, each with a citation) is produced and applied; counts cover the active program only; a failed deploy is reported, never silently skipped.
Roles: GPT-5.6 Luna High builds (and runs E2E) · GPT-5.6 Terra verifies (adversarial,
falsification briefs) · Claude leads — writes briefs, rules on findings, reads diffs itself,
lands. Builder ≠ verifier, always cross-model. No lane result trusted without reading it.
Per lane:
1. Brief to a file first: ops/dispatch/YYYY-MM-DD-NN-<engine>-<name>.md — objective, binding context docs, acceptance criteria IN the brief (loose specs cause multi-round fix loops), rules (zero real mutations, sign your work), and the exact report path.
2. Dispatch on the VDS, detached:
b64=$(base64 < ops/dispatch/<brief>.md)
ssh aina-vds-ts "echo $b64 | base64 -d > /srv/aina/ops/<program>/brief.md; \
cd /srv/repos/<repo> && nohup ~/.npm-global/bin/codex exec -m <model> \
-c model_reasoning_effort=\"high\" --skip-git-repo-check \"\$(cat /srv/aina/ops/<program>/brief.md)\" \
> /srv/aina/ops/<program>/lane-NN.log 2>&1 & echo PID=\$!"
3. Watch the process exit, not the report file (lanes write reports mid-flight): ssh aina-vds-ts 'while kill -0 <PID> 2>/dev/null; do sleep 45; done' as a background task.
4. Health heuristic (Donna): a live lane log is 100KB+; a ~4KB log that "succeeded" is a silently dead lane.
5. Verifier verdict is a hard gate: BLOCK → fixup lane scoped to EXACTLY the findings, then re-review. Repeat until PASS (S1 took 3 rounds; every round found something real).
6. Land through the repo's PR gate at milestones, not per lane. Lead reads the cumulative diff itself before raising the PR.
ops/board-activity.jsonThe rule: if a surface (board/RUNLOG/Linear/docs) would disagree with reality after your action,
updating it is part of the action, not a follow-up.
| Failure | Fix |
| Board stale, program ran off-board | Step 0 rail was missing — verify setup_mode before trusting any .flow board |
| Renderer showed done tasks as todo | Renderer read .json snapshots; status truth is flowctl … --json only |
| Board "looked frozen" during lane rounds | Activity feed (Step 2.2) — task checkboxes alone are not visibility |
| Lane died silently at exit 0 | Disk full (Mac AND VDS same day); check df when a lane dies mid-write; lanes must clean their worktrees even on failure |
| ssh dispatch hangs the session | nohup … & inside the remote command + background watcher; never hold ssh open for a long lane |
| Review lane green but scope unchecked | Always verify a checker's own scope/freshness (Donna's checker-scope failure class) |
— claude-lead (claude-fable-5) · 2026-08-04 America/Toronto · session 2c9b77d8 ·
reference program: DAT-151 resume, aina-intelligence-os