@midhunxavier/code-flow 0.1.0-preview.3 → 0.1.0-preview.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/remote-host.json +1 -1
- package/package.json +1 -1
- package/public/docs/accessibility.html +1 -1
- package/public/docs/acp-agents.html +1 -1
- package/public/docs/agent-containers.html +1 -1
- package/public/docs/api.html +1 -1
- package/public/docs/application-improvements.html +1 -1
- package/public/docs/architecture-explorer.html +1 -1
- package/public/docs/architecture.html +1 -1
- package/public/docs/auto-team.html +1 -1
- package/public/docs/automations.html +1 -1
- package/public/docs/checkpoint-review.html +1 -1
- package/public/docs/code-hosts.html +1 -1
- package/public/docs/code-intelligence.html +1 -1
- package/public/docs/configuration.html +1 -1
- package/public/docs/context-inspection.html +1 -1
- package/public/docs/coordinated-changes.html +1 -1
- package/public/docs/deferred-runs.html +1 -1
- package/public/docs/dependency-discovery.html +1 -1
- package/public/docs/documentation.html +1 -1
- package/public/docs/dynamic-teams-evaluation.html +1 -1
- package/public/docs/dynamic-teams-implementation-plan.html +1 -1
- package/public/docs/dynamic-teams-implementation-review-1.html +1 -1
- package/public/docs/dynamic-teams-plan-review-0.html +1 -1
- package/public/docs/extensions.html +1 -1
- package/public/docs/external-sources.html +1 -1
- package/public/docs/filesystem-policy.html +1 -1
- package/public/docs/filtered-tasks.html +1 -1
- package/public/docs/getting-started.html +1 -1
- package/public/docs/github-review-threads.html +1 -1
- package/public/docs/github.html +1 -1
- package/public/docs/help.html +1 -1
- package/public/docs/index.html +1 -1
- package/public/docs/installation.html +6 -6
- package/public/docs/launch-profiles.html +1 -1
- package/public/docs/maintenance.html +1 -1
- package/public/docs/memory-claims.html +1 -1
- package/public/docs/memory-storage.html +1 -1
- package/public/docs/notifications.html +1 -1
- package/public/docs/patterns.html +1 -1
- package/public/docs/performance-results.html +1 -1
- package/public/docs/performance.html +1 -1
- package/public/docs/project-conventions.html +1 -1
- package/public/docs/project-first-navigation-plan.html +1 -1
- package/public/docs/project-teams-implementation-plan.html +1 -1
- package/public/docs/project-teams.html +1 -1
- package/public/docs/provider-compatibility.html +1 -1
- package/public/docs/releasing.html +27 -8
- package/public/docs/remote-workers.html +1 -1
- package/public/docs/review-inbox.html +1 -1
- package/public/docs/run-storage.html +1 -1
- package/public/docs/runs.html +1 -1
- package/public/docs/search.json +1 -1
- package/public/docs/security.html +1 -1
- package/public/docs/shared-projects.html +1 -1
- package/public/docs/ssh-projects.html +1 -1
- package/public/docs/task-reruns.html +1 -1
- package/public/docs/team-collaboration.html +1 -1
- package/public/docs/verification.html +1 -1
- package/public/docs/vscode.html +1 -1
- package/public/docs/worker-permission-verification.html +1 -1
package/public/docs/search.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
[{"group":"Get started","id":"index","source":"docs/README.md","body":"# Your agents. A clear path forward.\n\nCode Flow brings coding CLIs into one workspace for planning, implementation, review and verification. Start with a small task, follow the evidence, and decide when the result is ready to ship.\n\n## Try the preview\n\nStart Code Flow locally and open it in your browser:\n\n```bash\nnpx --yes --package=https://github.com/midhunxavier/code-flow/releases/download/v0.1.0-preview.2/midhunxavier-code-flow-0.1.0-preview.2.tgz codeflow\n```\n\nUse **Node.js 22.13+**, **Git 2.38+** and a supported coding CLI installed and signed in. Runs on macOS, Linux and Windows through **WSL2**. Keep the terminal open while you work; **Ctrl+C** stops the app.\n\nPrefer a macOS app? Download **0.1.0-preview.2**:\n\n- [Download for Apple Silicon (M1 or later)](https://github.com/midhunxavier/code-flow/releases/download/v0.1.0-preview.2/code-flow-0.1.0-preview.2-macos-arm64-unnotarized.dmg)\n- [Download for Intel Mac](https://github.com/midhunxavier/code-flow/releases/download/v0.1.0-preview.2/code-flow-0.1.0-preview.2-macos-x64-unnotarized.dmg)\n\nRequires **macOS 13+**. The app opens its own window and keeps agents running when you close it. Node is included; Git, a signed-in coding CLI and Chrome or Edge for the shared browser are still required. The DMGs are **unnotarized preview builds** and macOS may block them; use npm if blocked.\n\n[Installation and setup](getting-started.md#install-and-run) · [Release notes and checksums](https://github.com/midhunxavier/code-flow/releases/tag/v0.1.0-preview.2)\n\n<div class=\"cards\">\n<a href=\"getting-started.md\"><strong>Start your first run ↗</strong><span>Install the app, connect a CLI and take a task from prompt to review.</span></a>\n<a href=\"patterns.md\"><strong>Choose a workflow ↗</strong><span>Plan and build, test first, compare candidates or ask a team to investigate.</span></a>\n<a href=\"runs.md\"><strong>Find your way around ↗</strong><span>Follow agent activity, ask questions and open files, changes and terminals.</span></a>\n<a href=\"verification.md\"><strong>Review with confidence ↗</strong><span>Understand checks, review evidence and when a result is ready to ship.</span></a>\n</div>\n\n## From task to reviewed result\n\n1. **Open a project.** Choose Open repository, confirm its project, then describe the outcome and add context.\n2. **Choose the team.** Assign roles yourself or review a proposed Auto-team plan.\n3. **Follow the work.** Answer questions, inspect changes and watch the checks.\n4. **Review and ship.** Read the evidence before merging or opening a pull request.\n\n<picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"images/run-dark.png\">\n <img src=\"images/run-light.png\" alt=\"The Code Flow workspace with a completed demo run and review actions.\" width=\"100%\">\n</picture>\n\n*Captured from the local application with demo agents. See [screenshot provenance](documentation.md#screenshots).*\n\n## Work with agents\n\n- [Team patterns](patterns.md) · [Auto team](auto-team.md) · [Project missions](project-teams.md)\n- [Runs and the workspace](runs.md) · [Review inbox](review-inbox.md) · [Verification](verification.md)\n- [GitHub and shipping](github.md) · [GitHub review threads](github-review-threads.md)\n- [Selective task reruns](task-reruns.md) · [Deferred starts](deferred-runs.md)\n\n## Make the workspace yours\n\n- [Configuration](configuration.md) · [ACP agents](acp-agents.md) · [Context inspection](context-inspection.md)\n- [Project conventions](project-conventions.md) · [Checkpoints and reversions](checkpoint-review.md)\n- [Service launch profiles](launch-profiles.md) · [Code intelligence](code-intelligence.md) · [Architecture explorer](architecture-explorer.md)\n- [Project memory and knowledge graph](memory-storage.md) · [Semantic claim review](memory-claims.md)\n- [Coordinated repository changes](coordinated-changes.md)\n\n## Connect and collaborate\n\n- [Team collaboration](team-collaboration.md) · [Shared project accounts](shared-projects.md)\n- [SSH projects](ssh-projects.md) · [Remote workers](remote-workers.md)\n- [GitLab and Bitbucket workflows](code-hosts.md) · [External sources](external-sources.md)\n- [Automations and remote access](automations.md) · [Notifications](notifications.md)\n- [VS Code integration](../integrations/vscode/README.md)\n\n## Operate safely\n\n- [Security and execution boundaries](security.md) · [Filesystem policies](filesystem-policy.md)\n- [Agent containers](agent-containers.md) · [Filtered Docker tasks](filtered-tasks.md)\n- [Dependency discovery](dependency-discovery.md) · [Backup and maintenance](maintenance.md)\n\n## Build and extend\n\n- [Architecture and development](architecture.md) · [API and CLI](api.md) · [Extensions](extensions.md)\n- [Provider compatibility](provider-compatibility.md) · [Accessibility](accessibility.md)\n- [Performance](performance.md) · [Run storage](run-storage.md)\n- [Documentation and GitHub Pages](documentation.md)\n\n## Engineering records\n\nThese documents record implementation decisions and evidence at a particular point in time. Use the guides above for current product instructions.\n\n- [Application improvement program](application-improvements.md)\n- [Project navigation review and implementation plan](project-first-navigation-plan.md) (proposed)\n- [Dynamic-team evaluation](dynamic-teams-evaluation.md) and [implementation plan](dynamic-teams-implementation-plan.md)\n- [Implementation review](dynamic-teams-implementation-review-1.md) and [original plan review](dynamic-teams-plan-review-0.md)\n- [Standing-team implementation plan](project-teams-implementation-plan.md)\n- [Measured performance](performance-results.md)\n\n[Project README](../README.md) · [Getting started](getting-started.md)\n\nProject collaboration lives in **More → Board** and **More → People**. Scheduled tasks, backlogs and missions live in **More → Automation**; configured Board and Automation tabs stay visible. Global Inbox and Scheduling cover the current computer.\n","title":"Your agents. A clear path forward."},{"group":"Get started","id":"installation","source":"docs/installation.md","body":"# Install Code Flow preview\n\nOpen the macOS desktop app, or start the local browser app with one command. You do not need to clone Code Flow or add it to your project. The preview uses your installed coding CLIs and their existing logins.\n\n**Desktop preview 0.1.0-preview.3.** Download the macOS app or use the exact release package below. The npm `preview` channel is published separately and may be older. See the [preview release notes and checksums](https://github.com/midhunxavier/code-flow/releases/tag/v0.1.0-preview.3).\n\n## Choose an installation method\n\n<div class=\"install-picker\" data-install-picker>\n<div class=\"install-tabs\" role=\"tablist\" aria-label=\"Installation method\">\n<button type=\"button\" id=\"install-tab-unix\" role=\"tab\" aria-controls=\"install-unix\" aria-selected=\"true\">macOS / Linux</button>\n<button type=\"button\" id=\"install-tab-windows\" role=\"tab\" aria-controls=\"install-windows\" aria-selected=\"false\" tabindex=\"-1\">Windows</button>\n<button type=\"button\" id=\"install-tab-npm\" role=\"tab\" aria-controls=\"install-npm\" aria-selected=\"false\" tabindex=\"-1\">npm</button>\n<button type=\"button\" id=\"install-tab-homebrew\" role=\"tab\" aria-controls=\"install-homebrew\" aria-selected=\"false\" tabindex=\"-1\">Homebrew</button>\n</div>\n<section id=\"install-unix\" role=\"tabpanel\" aria-labelledby=\"install-tab-unix\" tabindex=\"0\">\n<h3>Try it with one command</h3>\n<p>Install Node.js 22.13+ and Git 2.38+, then run:</p>\n<pre><code class=\"language-sh\">npx --yes --package=https://github.com/midhunxavier/code-flow/releases/download/v0.1.0-preview.3/midhunxavier-code-flow-0.1.0-preview.3.tgz codeflow</code></pre>\n<p>Code Flow starts locally and opens its sign-in link in your default browser. Keep the terminal open while you work. Press <strong>Ctrl+C</strong> to stop the server.</p>\n</section>\n<section id=\"install-windows\" role=\"tabpanel\" aria-labelledby=\"install-tab-windows\" tabindex=\"0\">\n<h3>Use Windows Subsystem for Linux</h3>\n<p>This preview runs inside <strong>WSL2</strong>. Install Node.js 22.13+, Git 2.38+ and your agent CLIs inside WSL, sign in there, and keep your repository in the Linux filesystem. In your WSL terminal, run:</p>\n<pre><code class=\"language-sh\">npx @midhunxavier/code-flow@preview</code></pre>\n<p>The launcher tries to open your Windows browser. If it cannot, copy the local sign-in link from the terminal. Native PowerShell and Command Prompt execution are not supported in this preview.</p>\n</section>\n<section id=\"install-npm\" role=\"tabpanel\" aria-labelledby=\"install-tab-npm\" tabindex=\"0\">\n<h3>Install for regular use</h3>\n<p>With Node.js 22.13+ and Git 2.38+ installed on macOS, Linux or inside WSL2:</p>\n<pre><code class=\"language-sh\">npm install -g @midhunxavier/code-flow@preview\ncodeflow</code></pre>\n<p>To update, run the same install command again. To remove the launcher:</p>\n<pre><code class=\"language-sh\">npm uninstall -g @midhunxavier/code-flow</code></pre>\n</section>\n<section id=\"install-homebrew\" role=\"tabpanel\" aria-labelledby=\"install-tab-homebrew\" tabindex=\"0\">\n<h3>Install with Homebrew</h3>\n<p>The preview release includes a Homebrew formula. Once the maintainer has published the <code>midhunxavier/code-flow</code> tap, install it on macOS or Linux:</p>\n<pre><code class=\"language-sh\">brew install midhunxavier/code-flow/code-flow\ncodeflow</code></pre>\n<p>Homebrew installs Node and Git as dependencies. Update with <code>brew update && brew upgrade code-flow</code>. If the tap is unavailable, use npm.</p>\n</section>\n</div>\n\n## macOS app download\n\nPrefer an app you can open from Applications? Download **0.1.0-preview.3** for your Mac:\n\n- [Download Apple Silicon DMG (M1 or later)](https://github.com/midhunxavier/code-flow/releases/download/v0.1.0-preview.3/code-flow-0.1.0-preview.3-macos-arm64-unnotarized.dmg)\n- [Download Intel DMG](https://github.com/midhunxavier/code-flow/releases/download/v0.1.0-preview.3/code-flow-0.1.0-preview.3-macos-x64-unnotarized.dmg)\n\n1. Choose **arm64** for Apple Silicon, or **x64** for an Intel Mac. macOS 13 or later is required.\n2. Open the DMG and drag **Code Flow** to **Applications**.\n3. Open Code Flow in its own desktop window. Closing or reloading the window keeps agents running.\n4. Click its Dock icon or use **Code Flow → Show Code Flow** to return. Use **Code Flow → Quit** to stop; it asks before interrupting active agents.\n\nNode is included in the app. You still need Git 2.38+, a signed-in supported agent CLI and Chrome or Edge for the shared browser. Install those before your first run. Quit Code Flow before replacing the app with a newer download. Do not run the npm and macOS launchers together against the same data directory.\n\nFiles named **unnotarized** are developer preview builds. macOS may block them. For ordinary installation, use npm until a signed and notarized DMG is available.\n\n## Connect an agent and start\n\nInstall at least one supported CLI: `claude`, `codex` or `agy`. Run it interactively and sign in before starting a task. Open **Settings → Agents & models** in Code Flow to inspect availability. Additional ACP agents can be configured in Settings.\n\nChoose a Git repository, describe your task, pick a workflow, and start your first run. Follow the [getting started guide](getting-started.md) for permissions and review.\n\n## Startup options\n\n```sh\ncodeflow --no-open # start without opening a browser\ncodeflow --port 5320 # use another local port\ncodeflow --version\ncodeflow --help\ncodeflow client --help # authenticated API client\n```\n\nIf the browser does not open, use the **Open http://localhost:…/?token=…** link printed in the terminal. Keep that link private. A restart creates a new session; use the new link if your browser asks you to sign in.\n\nIf another instance is already running, use its browser tab or stop it before starting again. A busy port can be changed with `--port`; an existing server using the same data directory must be stopped first.\n\n## Settings, updates and removal\n\nIn desktop versions with the updater, open **Settings → General → App updates** or **Code Flow → Check for Updates…**. The app also checks on launch and every six hours, and shows **Update available** in the sidebar. Preview installs receive newer previews and stable releases; stable installs receive stable releases only.\n\nClick **Download update**, then **Restart to update** when ready. If agents are working, Code Flow asks whether to keep running or save progress and restart. In-app installation requires a signed, notarized app and a release containing the matching update package. Developer previews show **Download installer** instead: download the DMG, quit Code Flow, and replace the app in Applications. Older versions without this button need one manual upgrade first.\n\nSettings and history live in `~/.codeflow`, outside the installation. Reinstalling, upgrading or removing the launcher preserves that folder. Set `CODEFLOW_HOME` to an absolute path to use a separate data directory. Avoid switching an existing data directory back to an older preview; back it up before trying new versions.\n\nThe preview does not install or sign in to agent CLIs for you. Provider access and usage limits depend on your account. See [security](security.md), [configuration](configuration.md) and [preview release preparation](releasing.md).\n\n[Documentation library](README.md) · [Getting started](getting-started.md)\n","title":"Install Code Flow preview"},{"group":"Get started","id":"getting-started","source":"docs/getting-started.md","body":"# Getting started\n\nTake a small, well-defined task from your own repository to a reviewed result. This guide covers installation, your first run and the settings that influence what the agents receive.\n\n## Requirements\n\n- Node 22.13+ for npm or source installation. The macOS app includes Node.\n- git 2.38 or newer: **Merge** relies on `git merge-tree --write-tree`, which 2.38 added. Check yours with `git --version`.\n- At least one of `claude`, `codex` and `agy` installed and signed in. Run each CLI once interactively first. Open **Settings → Agents & models** to inspect provider health and model choices.\n\n## Install and run\n\n### Try the preview\n\nOn macOS, Linux or inside Windows **WSL2**, run:\n\n```bash\nnpx --yes --package=https://github.com/midhunxavier/code-flow/releases/download/v0.1.0-preview.2/midhunxavier-code-flow-0.1.0-preview.2.tgz codeflow\n```\n\nThe launcher starts Code Flow locally and opens your default browser with its sign-in link. Keep the terminal open while you work; press **Ctrl+C** to stop. If the browser does not open, copy the sign-in link printed in the terminal. Keep that link private.\n\nOn Windows, install Node, Git and your agent CLI inside WSL2, sign in there, and keep your repository in the Linux filesystem. Native PowerShell and Command Prompt execution are not supported in this preview.\n\nFor regular use, install the launcher globally, then start it with `codeflow`:\n\n```bash\nnpm install -g @midhunxavier/code-flow@preview\ncodeflow\n```\n\nRun the same install command to update. Remove the launcher with `npm uninstall -g @midhunxavier/code-flow`.\n\n### Download the macOS app\n\nChoose **0.1.0-preview.2** for your Mac (**macOS 13+**):\n\n- [Download Apple Silicon DMG (M1 or later)](https://github.com/midhunxavier/code-flow/releases/download/v0.1.0-preview.2/code-flow-0.1.0-preview.2-macos-arm64-unnotarized.dmg)\n- [Download Intel DMG](https://github.com/midhunxavier/code-flow/releases/download/v0.1.0-preview.2/code-flow-0.1.0-preview.2-macos-x64-unnotarized.dmg)\n\nOpen the DMG, drag **Code Flow** to **Applications**, then open Code Flow. The Electron app opens its own desktop window. Closing or reloading that window keeps agents running. Click the Dock icon or **Code Flow → Show Code Flow** to return. Use **Code Flow → Quit** to stop the app; it asks before interrupting active agents. Node is bundled; install Git and sign in to a supported agent CLI before your first run. Install Chrome or Edge for the shared browser.\n\nThese are **unnotarized preview builds**. macOS may block them; use npm if blocked. Quit Code Flow before replacing it with a newer download. Run one launcher at a time. [Release notes and checksums](https://github.com/midhunxavier/code-flow/releases/tag/v0.1.0-preview.2).\n\nThe preview stores settings and history in `~/.codeflow`, outside the installation. Updating or removing the launcher preserves that folder.\n\n### Install from source\n\n```bash\ngit clone https://github.com/midhunxavier/code-flow.git\ncd code-flow\nnpm ci\nnpm start # http://localhost:4317\n```\n\n### Signing in\n\nThe first browser that opens code-flow is signed in automatically. That stays possible until a browser signs in (with or without the link) or code-flow first starts an agent, a check or an app. After that, other browsers need the link code-flow prints when it starts (`Open http://localhost:4317/?token=…`).\n\nThe session token exists only in the server's memory, so the agents code-flow runs can't use its API, even sandboxed ones with network access. A restart starts a new session. If the browser asks you to sign in, use the new owner link printed by that server.\n\n## Development\n\nFor working on Code Flow itself, use either command after `npm ci`:\n\n```bash\nnpm run dev # deterministic demo agents; no real provider work\nnpm run dev:real # installed real CLIs; sign in to each CLI first\n```\n\nBoth modes use http://localhost:4318 and `.codeflow-dev` as their home directory, rebuild the UI and restart the server on source changes. For real-agent mode, install the CLIs you intend to use and run each interactively to sign in before starting. The normal `npm start` workflow above uses http://localhost:4317.\n\n## Your first run\n\n\n\n1. Choose **Open repository**, browse to a Git repository on the displayed computer, and choose **Open this repository**. Accept or change the suggested project name, then choose **Create project**. If the repository already belongs to projects, choose its destination. This opens the composer without starting an agent; no workspace or team setup is required.\n2. Describe the task. Drag files from the **Files** panel, or from Finder, onto the task box to give the agents context. **Improve** (at the right of the task box) rewrites a rough task so agents can act on it: the same intent, with the goal first, the specifics, how to tell it's done, and any open questions. **Undo** puts your text back.\n - It's one short turn by your configured agent (Auto by default), on your subscription, using its read-only permissions in a disposable private Git directory. Improve never resets your working checkout.\n - **Settings → Agents & models → Prompt improvement** configures Auto, Claude Code, Codex or Antigravity independently of the main task. Choose separate Quick and Repo models and supported reasoning effort for each provider, plus the default mode. Settings save in this browser for this computer. Auto preserves the Claude → Codex → Antigravity preference; a pinned agent reports availability, login or quota errors without switching agents. The Improve control shows the configured agent, and the completion note names the agent that performed the rewrite. Reset restores Haiku for Quick, Sonnet for Repo and low effort for Codex/Antigravity.\n - The arrow next to it picks a mode for the current composer without changing the saved default: **Quick rewrite** (the default) works from your text alone (by default, Claude's Haiku, or low effort for Codex and Antigravity). **Use the repository** reads a snapshot of the current committed version so the rewrite can name real files and commands; it takes longer. Uncommitted changes, untracked/ignored files and executable provider configuration are not copied.\n - **Stop** cancels. Editing the task or changing its repository, context, source or execution profile discards the old request; Undo applies only while the rewritten text is still current.\n - Improve supports the **local** execution profile. SSH and container selections disable it, and filesystem restrictions pause it. Other Code Flow mutations in that repository are refused until the turn and cleanup finish. Repository snapshots allow up to 5,000 regular files, 2 MiB per file and 128 MiB in total; unsupported symlinks, submodules and larger snapshots fail explicitly.\n3. Open **Template library** and choose an optional intent starter: **Fix a bug**, **Build a feature**, **Review code**, or **Explore an idea**. Each selects an existing workflow and explains why. Existing task text is preserved; exploration disables the build/review roles for a design-only debate. You can still pick any workflow manually. Pick how the agents work together, and click an agent for each role. Under each role you can set that agent's model and effort for the role. The sentence under the team, such as *\"Claude plans → Codex builds → Claude reviews\"*, shows what will happen.\n4. Review the **computer**, **project**, repository/branch, team, checks and execution limit in the compact summary. Open **Run options** to pick the base branch, the result branch name, rounds, limits, permissions, and the default model and effort per provider. Set **Worktree setup command** (for example, `npm ci`) and **Done when this passes** for this run; save reusable commands in **Settings → Projects**. For local runs using a single check, **Repository readiness → Inspect readiness** checks the repository and installed providers. **Validate commands** executes setup and the check at the selected commit in a temporary worktree, so you can resolve setup failures before spending agent calls.\n Composer readiness checks the selected workflow and providers. Unused signed-out providers are skipped, while selected providers that are unavailable, incompatible or signed out block readiness. Auto still needs at least one usable eligible provider within its permitted pool.\n\n5. Press **Start run** (⌘↵).\n - The run reads like a conversation: your task, then the agents' work as one list of steps (open any step to see its live transcript), then the result. A reply box stays at the bottom.\n - When the run finishes, each result is a branch such as `codeflow/<run>/build`, with buttons to review it, merge it, open a PR, or copy its diff command.\n - **Needs more work** records feedback for the current commit and opens its branch review. Add an overall or line comment, then **Send comments** to start a revision. Sending waits for the original writer to load unless you explicitly choose a recipient. A failed send retains your draft. Project-mission results use their mission's revision controls.\n - **More run actions → Edit & run again** restores the task, original base commit, provider/model choices, permissions, checks, limits and execution location into an editable draft. It creates fresh result branches and retains the original run's source restrictions. Reselecting the same repository retains the draft; editing CI-log options refreshes source context while keeping the pinned commit. Reattach any original attachments you still need. A restored custom verification profile is retained; choose a preset before saving it as a task template.\n\nReview and merge results yourself if you prefer the command line:\n\n```bash\ngit diff main...codeflow/<run>/build\ngit merge codeflow/<run>/build\n```\n\n## Return to a project\n\nOpen a project in the sidebar. **Overview** shows work needing attention, running work and recent results; **Runs** searches that project's history and can include archived runs. **Repositories** opens files, verification, context, history and services for a specific repository or run worktree. Existing Workspace bookmarks remain supported. **Project settings** keeps project editing, outcome metrics and recorded scenarios accessible. **More → Board** opens shared tasks on this project’s owning computer; **More → People** shows members, repository keys and authorized access controls. **More → Automation** contains Run once later, Repeat, Process a backlog and Pursue a mission. Board and Automation become visible tabs when configured, even when the optional Board shortcut is off. Command search finds every destination.\n\n\n\n**New run** opens the project's saved draft. Text, attachments, selected files, workflow and run options are saved separately for each project on each computer. Browsing another project's page or filtering Inbox does not change the draft's destination. The composer shows one repository directly or a selector for multiple repositories. **Open another repository** asks you to choose or explicitly extend a project before launching.\n\nExisting browser drafts migrate once. Ambiguous drafts remain **Unassigned draft** in the composer project picker until you choose a destination. A removed project or repository blocks launching into it; the draft remains available. If draft storage fails, keep the tab open and use **Retry saving**.\n\n## Find help in the app\n\nOpen **Application menu → Help & documentation** for the searchable local guides. **Ask code-flow** answers questions from these guides using an installed CLI. See [the help guide](help.md) for supported providers, privacy and limits.\n\n## Giving agents context\n\nThe **Files** tab of the [side panel](runs.md#the-side-panel) (the **Files** button under the task, or <kbd>\\</kbd>) shows the selected repo: ignored files are hidden, files with uncommitted changes are marked **M**, and untracked files **U**. To add something to the task's context, drag it onto the task box, click **+**, or double-click it; clicking a file opens it to read. You can also add files from your computer, such as specs, screenshots or logs (up to 10 MB each, 25 MB in total). Drop them from Finder, or use **Attach**.\n\nEvery agent's first prompt gets a *Context from the user* section listing these items. Nothing is pasted into the prompt:\n\n- Repo files and folders that are identical in the base branch are referenced by path; every worktree already has them.\n- Your **working-copy** version of a file is copied into `.codeflow-context/repo/<path>` in each worktree when the file is uncommitted or differs from the base branch. The agent sees exactly what you see.\n- Dropped files are copied into `.codeflow-context/attachments/`.\n- `.codeflow-context/` is listed in `.git/info/exclude`, so it never ends up in an agent's commit.\n- Folders that aren't in the base branch are rejected. Commit them, or drop the individual files.\n\n## Models and effort\n\n**Settings → Agents & models** sets a model and a reasoning effort per provider. Every role that uses that provider runs with them. Leave either on *Default* to use the CLI's own configuration.\n\n| Provider | Model list | Flags passed |\n|---|---|---|\n| Claude Code | aliases `fable`, `opus`, `sonnet`, `haiku`, or any full model name via *Other…* | `--model`, `--effort` (low … max) |\n| Codex | `codex debug models`, with each model's supported efforts; the default comes from `~/.codex/config.toml` | `-m`, `-c model_reasoning_effort=…` |\n| Antigravity | `agy models` | `--model`, `--effort` (low … max) |\n\n**Per role:** under each role in the team, every agent you picked has a model picker and an effort picker. They override the provider's setting for that role only, for example Opus to plan and Sonnet to review. Left on *Default · …*, they show what the agent will use.\n\nModel lists are cached for 10 minutes. Your choices are remembered in the browser.\n\n## Worktrees and branches\n\nEvery worktree a pattern needs (`build`, `candidate-a`, `integration`, …) is created with:\n\n```bash\ngit worktree add -b <prefix>/<name> <repo>/.worktrees/<run-id>/<name> <base>\n```\n\n- **Base branch:** the starting point for every worktree in the run. By default this is the repo's current `HEAD`. The dropdown also lists local branches, remote branches, and the result branches of earlier runs, so you can build on a previous result. Uncommitted changes in your checkout aren't included.\n- **Result branch prefix:** defaults to `codeflow/<run-id>`. If you set it to, say, `feat/rate-limit`, results land on `feat/rate-limit/build`. A prefix can only be used once, because git can't have both a `feat/rate-limit` branch and `feat/rate-limit/…` branches.\n- **Submodules:** a worktree of a repo with a `.gitmodules` gets its submodules checked out (`git submodule init`, then `git submodule update --init --recursive`, for up to 10 minutes). `init` registers them in the repo's `.git/config`, as it would in your checkout. If cloning one fails (say a private submodule git would have to prompt for), the worktree is still created, without it.\n- **Location:** worktrees go in `<repo>/.worktrees/`. code-flow adds `/.worktrees/` to `.git/info/exclude`, which keeps them out of `git status` without editing your `.gitignore`. Tools that don't read git's ignore files (some test runners, linters, `tsc` globs) may still scan `.worktrees/`. Exclude it in their config, or set `CODEFLOW_WORKTREE_DIR` to an absolute path to keep worktrees outside the repo.\n- **Cleanup:** remove a run's worktrees with `git worktree remove .worktrees/<run-id>/<name>`, or delete the folder and run `git worktree prune`. The branches stay until you delete them. **Archive…** on a run, and **Clean up** in [Insights](runs.md#insights-comparisons-and-finding-runs), do this for you.\n\nNext: [Team patterns](patterns.md) · [Talking to a run](runs.md)\n","title":"Getting started"},{"group":"Get started","id":"patterns","source":"docs/patterns.md","body":"# Team patterns\n\nStart in Auto to let the coordinator build and adapt a plan. Solo is directly accessible for one agent. Open Advanced to choose reusable built-in workflows such as Plan → Build → Review, a loop, council, or debate, or to design and save a versioned template. Fixed workflows retain the execution and verification behavior described below.\n\n## Team patterns\n\n| Pattern | Roles | Flow | Reach for it when |\n|---|---|---|---|\n| **Auto team** | coordinator, generated workers, independent reviewer | Propose and approve a team → ready-task scheduling → addressed questions and bounded revisions → integrate, check, review and repair | You want code-flow to choose the team and workflow; see [Auto team](auto-team.md) for communication, memories, budgets and recovery |\n| **Plan → Build → Review** | planner, builder, reviewer | Plan (read-only) → implement → **check** (the builder fixes it first if it fails) → independent review against the task, with the plan as context → code-flow runs the reviewer's tests → the builder fixes what blocks → re-check and re-review, up to *N* rounds | Most features and bug fixes. Cross-vendor review catches what a model misses in its own work |\n| **Loop until green** | workers (1+, in hand-off order) | Baseline check → the agent works → code-flow runs the check → the failure goes back to the agent → repeat until it passes. A round that makes more tests fail is undone. After *K* rounds without progress, the next vendor takes over with a handover note | \"Done\" is a command: failing tests, type errors, a broken build. Needs a check |\n| **Best-of-N race** | contestants (2+), judge | All contestants solve the task in parallel, each in its own worktree → judge compares **anonymized** diffs (Candidate A/B…) and picks one | The solution space is wide, or the task is risky enough to want several independent attempts |\n| **Debate → Synthesize → Build** | debaters (2+), synthesizer, builder and reviewer (optional) | Independent proposals → critique rounds where each debater sees the others' proposals (**anonymized**: Proposal A/B…) and revises → synthesizer merges and records each decision → builder implements → reviewer checks it against the plan → code-flow runs the reviewer's tests → builder fixes what blocks → re-check and re-review, up to *N* rounds | Architecture, API design, big refactors. Leave the builder empty for design only |\n| **Supervisor → Workers** | supervisor, workers (1+) | Supervisor splits the task into parallel subtasks and assigns workers → workers build on separate branches → branches merge into an integration branch; the supervisor resolves conflicts, a reviewer reviews, and the supervisor fixes what blocks | Larger tasks that split cleanly (backend / frontend / tests) |\n| **Test-first (adversarial tester)** | tester, builder | Tester writes failing tests → builder makes them pass without editing them or how they run (code-flow undoes it if it does) → tester runs them, adds edge-case tests, and sends failures back | Behavior you can specify with tests. Stops an agent from grading its own homework |\n| **Review council** | reviewers (1+), chair | Reviewers review the PR's or branch's changes independently, read-only → code-flow runs the tests they show bugs with → the chair merges the findings without knowing who wrote what. Findings rank by severity, then confirmed before the rest, then by how many reviewers raised them | Reviewing a PR before merging. Nothing is changed or posted without you |\n| **Single agent** | agent | One agent works on the task in its own worktree → code-flow commits and runs the check if you set one → you reply, and each reply continues the same session on the same branch | Quick changes, questions and exploring, where a team is more than the task needs. Start a team from its result when you want a second opinion |\n\nEvery pattern works with any mix of agents. A good default is to have a **different vendor review or judge** than the one that wrote the code.\n\n**Auto** in a role picks an installed agent when the run starts. It skips plans that are at 90% or more of a usage window or recently hit a limit, until the limit resets (Claude reports its windows; the others report only when they're limited), and for 15 minutes a CLI that turned out not to be signed in. Once agents have a few finished runs in a repo, it prefers the one whose code passed the check most often there. The pick and the reason are written to the run's activity log. Quota is saved, so a restart remembers which plans are out.\n\nVerdicts, judge picks, subtask lists and test specs are **schema-constrained**: `claude --json-schema`, `codex --output-schema` and `agy --json-schema`. An answer that still doesn't fit gets one retry in the same session, and then the run **fails**: an unreadable review is never treated as approval.\n\nJudging in a race is blind: no author summaries, a shuffled order (seeded per run, so a resumed run replays identically), and an optional **panel** of judges that vote. Pick vendors that aren't competing; the UI suggests one, and the log warns when a judge also competes.\n\nThe same goes for the other roles that weigh someone else's work. Unless you pick one yourself, the composer fills each with a vendor that didn't do that work, and the log warns when a run has one agent doing both:\n- Plan → Build → Review: the reviewer is a vendor other than the planner's (and the builder's, when a third is installed). A reviewer that shares the planner's view lets a wrong plan through, so it's told to judge the change against the task, with the plan only as context.\n- Debate: the synthesizer is a vendor that isn't debating. Debaters and the synthesizer see the proposals as *Proposal A/B…*, never by vendor, so nobody sides with its own by name.\n- Supervisor: the reviewer is a vendor other than the supervisor's (see *Independent review* below).\n\nFor the same reason, reviewers are anonymous to a review council's chair (*Reviewer A/B…*).\n\n## Checks, loops and limits\n\ncode-flow treats each pattern as a loop, and what it engineers is the loop around the agents:\n\n- **Done when:** a check command such as `npm test`, `pytest` or `cargo test`. It's auto-detected from the repo and editable per repo.\n - Python projects locked with uv or Poetry get their own environment, since system Python doesn't have their packages: with `uv.lock`, setup `uv sync` (`uv sync --all-packages` in a uv workspace) and check `uv run --no-sync pytest -q`; with `poetry.lock`, setup `poetry install` and check `poetry run pytest -q`. When the pytest config lives only in `tests/` (a workspace member of its own), the check runs from there. The composer says what it detected from.\n - **code-flow runs it itself** after each change, in that worktree, and feeds the failing output back to the agent.\n - Loops end on that evidence, not on an agent saying it's done. Every check shows in the run's steps with its output.\n - Pipeline: reviewers only see code that passes.\n - Race: a candidate that fails can't win if another one passes.\n - Supervisor and Debate: the integrated or built result is checked, and the agent fixes failures.\n - Test-first: the new tests must fail first, then pass.\n - Every pattern: a passing result is proven fail-to-pass (see below).\n - When a run finishes but the check still fails, the run is shown as a warning, not a success.\n- **A check that can't run isn't a failing check.** Some failures say the environment is wrong, not the code: the command isn't installed (exit 127), Python has neither pytest nor the project's package, pytest can't load `conftest.py` or collects nothing (exit 4 or 5).\n - When the run's first check fails like that, code-flow runs it once more on the base commit, in a throwaway checkout (with your setup). If it fails the same way there, the change isn't to blame.\n - The check is then set aside for the rest of the run: no \"Fix failing check\" rounds (agents would only rewrite the test setup to get around it), and later checks are skipped.\n - The review still runs, and the reviewer is told nothing was tested. The verdict stays unknown instead of failing.\n - The log, the result card, the inbox and the phone view say *The check can't run here: \\<first error line\\>. Set `setup` and `check` in .codeflow/config.json*. *Loop until green* stops before its first round, since no round could pass.\n - If the check runs on the base commit, the change broke it, and it's a failing check like any other.\n- **A pass only counts if the tests are still the tests.** After every change, code-flow looks for edits that make a check pass without fixing anything:\n - deleted test files, and test files renamed to a name the runner doesn't pick up (`parse.test.ts` → `parse.ts`);\n - added `.skip`, `.only`, `xit`, `it.skipIf(…)`, `test.runIf(…)`, `test.skip.each(…)`, `test.fails`, `this.skip()`, `{ skip: true }`, `@pytest.mark.skip`, `pytest.importorskip`, `t.Skip()`…; in Python and JS/TS, not inside a string or comment (a test of a guard against skips may write one into a fixture);\n - test files that lost assertions;\n - a changed test config or CI workflow, including the runner's settings in a file shared with the rest of the project: pytest's `[tool.pytest…]` tables in `pyproject.toml`, and the `jest`, `vitest`, `mocha` or `ava` settings in any `package.json` (`addopts = \"--deselect …\"`, `testPathIgnorePatterns`…). The rest of those files (dependencies, other tools' settings) isn't flagged;\n - a changed `scripts.test` (or whichever script the check runs);\n - fewer tests running than before, read from the runner's summary. Each result branch keeps the counts of its check (the first and the latest, and at which commit), so a follow-up after the run compares with how many ran before its change.\n\n If git can't show the change, that is reported too (*couldn't verify test integrity*), as a serious issue: not being able to look isn't the same as finding nothing.\n\n What happens next depends on the pattern:\n - Loop until green: a pass that relies on such changes doesn't end the loop. The agent is told to undo them and fix the code. If it keeps them (the task may need it), or the rounds run out before it can answer, the result stays but is flagged, and the pass doesn't count: the run gets no verdict (as when the check can't run), its summary says *The check passes only with changes to the tests* and lists them, and no worker is credited with getting it green, so it doesn't feed Auto's picks. When the runner's summary doesn't say how many tests ran, removed assertions count as such a change too, since a drop in tests can't be seen.\n - Pipeline, Supervisor and Debate: the changes go to the reviewer, who raises each as an issue (so it blocks approval) unless the task needs them.\n - Race: a candidate that passes only that way can't beat one that passes honestly.\n - Test-first: the builder's edits to the tests are undone, and so are the tester's edits outside them. Only test files count as the tests (plus any the tester names): a stub module the tester adds for its tests to import is the builder's to fill in. The builder's changes to the test runner's own config are undone too, a new `conftest.py` or `vitest.config.*` included. What can't be put back file by file (the runner's settings in `package.json` or `pyproject.toml`, the check's script, a skip in an existing test, fewer tests running) goes back to the builder to undo, and the tests aren't probed or counted as passing until it has; if it never does, the run gets no verdict. Other changes the tests read (`vite.config.*`, a CI workflow) are passed on to the tester to look at.\n - PR follow-through: nobody reviews these fixes before they're pushed, so one that touches the tests at all isn't pushed: any of the changes above, not only the serious ones (a changed CI workflow or test config, a skip or an assertion fewer). The watch hands it to you instead.\n\n Every flagged result shows **⚠ Changes to tests to check before merging**.\n- **A pass should prove the change (fail-to-pass).** A check that passes with the change might pass without it too, and then it shows nothing about the change. So once a result's check passes, code-flow proves it:\n - it puts the change's tests onto the base code, in a checkout of its own (set up once per run): the test files, their fixtures and snapshots, and the runner's settings (`jest.config.*`, `conftest.py`, `pytest.ini`…). A test file the change deleted is deleted there too. When the change also adds or updates dependencies (`package.json`, `uv.lock`, `go.mod`…), those files come along and setup runs again, so a package only the change adds can't make the tests fail there as if they needed the change's code;\n - it runs the check there. **It should fail**: then at least one test needs the change, and the step reads *Fails without the change, as it should*. A test that imports code only the change adds fails there too, but only because it can't load (an import, collection or build error), which shows the code is there, not that it works: when that is the only way it fails, the result is a **weak proof**. A test that fails on what it checks (an assertion, an expected value) is full proof, also when another can't load;\n - if it passes there as well, the result is **not proven**: no test fails without the change. A run that never reached the tests (no test runner, nothing collected, setup failed, a timeout) proves nothing either way and says so.\n\n When a fix makes a failing check pass, the tests that failed before are the proof, and no new test is needed. When the check already passed before any change (a new feature, say), only a new test can prove it. What happens next depends on the pattern:\n - Builders are told up front that their tests will run on the original code (Pipeline, Race, Debate, Supervisor's workers, Loop until green).\n - Loop until green: an unproven pass doesn't end the loop the first time. The agent is asked for a test that fails without its change, or to say why the task can't be tested that way (docs, a pure refactor). If it adds none, the pass stands and the result is flagged. When the change touches no tests, the baseline check is the run on the base code, so the proof needs no extra run.\n - Pipeline, Debate and Supervisor: before review, the builder (the supervisor, in Supervisor) is asked once for such a test. If the check then fails and the fix attempts don't help, code-flow undoes the new test, so asking for a proof never costs a green result. The reviewer sees the proof either way, and is told to ask for a test when it's missing.\n - Race: each passing candidate is proven, and the judge sees which ones are, still blind. It's evidence for the judge, not a rule: an unproven candidate can win.\n - Test-first: the tester's tests must fail before anything is implemented. If they pass, the tester is asked once to make them fail for the missing behaviour.\n - Review council: with a check, code-flow runs it on the PR's changes and proves them against the code the PR started from, and every reviewer sees both. The proof goes into the review you can post.\n - Single agent: the result is proven and the proof reported; nobody is asked for a test.\n\n Each result shows **✓ Proven: a test fails without this change** (with how it fails), **⚠ Weak proof** (the tests fail without it only because they can't load), or **⚠ Not proven**. Reviewers and judges are told which. Insights counts proven results per pattern, out of all the proven, weak and unproven ones. A proof costs one more run of the check (plus setup once per run). Turn it off for a repo with `\"prove\": false` in `.codeflow/config.json`.\n- **Review findings are checked, and only what blocks holds approval back.** A reviewer's word is an opinion: a false positive costs a fix round, and the builder can't answer it. So in Pipeline, Debate, Supervisor and the Review council, a reviewer rates each issue high, medium or low, and shows a bug with a *repro*: a test file (its path and whole contents) that fails on the result because of the bug, a plain test-runner command that runs it, or both. A command alone can run tests that are already there.\n - code-flow writes the test into a checkout of the result's current commit, apart from the builder's worktree, and runs it with the reviewer's command, or else the run's check. That checkout is set up once per run, and again only when a file that says which dependencies to install changes (`package.json`, `uv.lock`, `go.mod`…). It's the reverse of a fail-to-pass proof: there the tests should fail without the change; here the reviewer's test should fail with it.\n - The step reads *Fails: the problem is there* or *Passes: a false alarm*. A finding is **confirmed** (its test fails), **not confirmed** (it passes: a false alarm), or **unconfirmed** (no test came with it). Sometimes code-flow can't tell: the test runner didn't start, it timed out, the check already fails on that code without the test, or the test never ran: as many tests ran with it as without (with the reviewer's own command, code-flow counts once more without the test, unless the command names it), no test ran at all, or the runner doesn't say how many ran and nothing in its output mentions the test.\n - Only a plain invocation of a known test runner runs, and only a file named like a test goes in, so a repro can't replace the code it's about. The test is never written through a symbolic link in the code under review, so a PR's tracked link can't send it outside the checkout. At most 6 run per review; low issues aren't run.\n - **Approval goes by the findings, not the verdict word.** High and medium issues block; low ones are suggestions. A false alarm never blocks. A high-severity issue claims a bug, so it blocks only when its test fails, or when code-flow couldn't run the test it came with. Without a test it's marked unconfirmed and doesn't block; reviewers are told to rate a problem no test can show medium. So a reviewer that approves with a confirmed bug doesn't approve, and one that requests changes over false alarms and suggestions does.\n - Findings that block go back to the builder (the supervisor, in Supervisor). Confirmed ones come with the reviewer's test and how it fails: the builder makes it pass and adds it (or one like it) to the change. The ones no test shows it fixes, or disputes with evidence (a test it ran, the code that handles it) instead of changing code. False alarms and suggestions are listed as needing no change. In the next review the reviewer sees the builder's answer, and keeps a disputed bug only with a repro that fails.\n - Each run of a reviewer's test is journaled, so a resumed run replays it instead of running it again, and the same test on the same commit runs once.\n - Review council: each reviewer's tests run before the chair merges the reviews, and the chair sees what each one showed and keeps a finding's repro. Findings rank by severity, then confirmed before the rest (false alarms last), then by how many reviewers raised them. The council's verdict goes by the findings too: it requests changes when a finding blocks, and approves when none does, whatever the chair said. Nor can the chair drop a finding a test confirms, or rate it lower: code-flow puts it back as its reviewer raised it, marked *Restored by code-flow* in the review, and the log says so. The review you can post says how each finding was checked.\n\n The result shows the latest review's findings, each marked **✓ Confirmed** (with how it fails, and the test), **✗ Not confirmed** (a false alarm), **⚠ Unconfirmed** or *couldn't run*, and which block. The summary counts unconfirmed high-severity claims and false alarms.\n- **Independent review:** Supervisor has a *Reviewer* role (the composer picks a different vendor than the supervisor) that checks the integrated result instead of the supervisor checking its own work. The supervisor also writes the *contracts* (interfaces, names, data shapes) that every worker gets. If you skip the reviewer, the supervisor reviews its own integration and fixes what it finds, but nobody reviews those fixes; the result says \"fixed by the supervisor, not re-reviewed\". Debate can have a reviewer check the build against the agreed plan; after the builder addresses its review, the check runs and the reviewer looks again, up to *Max review rounds*.\n- **Missing parts are reported:** in Supervisor, a worker that finishes without changing anything is listed as *no changes* with the first line of its answer, not as done. Its branch isn't merged, the supervisor and reviewer are told which parts are missing, and Insights counts it as a failed step. If the supervisor splits the task into more subtasks than *Max subtasks*, the extra ones are added to the last subtask (the log says so) rather than dropped.\n- **Parallel work stops together:** in Race, Supervisor and Debate, an agent's failed turn is part of the result (a failed candidate, a missing part), but when anything else fails in one branch (its worktree, setup, a commit or a check), the other branches are stopped too. The run ends once every branch has, so no agent keeps running after it, and Stop and the slots stay accurate.\n- **What agents see:** long diffs keep whole source files first, put lockfiles and generated files last, and list any file that didn't fit with the command to read it. A long check log keeps the lines around each failure plus the runner's summary, not just the tail. Agents also get the repo's own conventions files (`AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, …), since each CLI otherwise reads only its own. They're also told about problems reviewers raised in more than one earlier run in that repo (turn this off with `\"learn\": false`).\n- **No progress means hand off:** in *Loop until green*, a round counts as progress when it commits a change that leaves fewer tests failing, read from the runner's summary. A different failure with as many failing tests, or more, isn't progress. When the runner doesn't say how many fail, or no test fails and the check still does (a lint step after the tests, say), any different failure counts as progress. A round with no new commit, or the same failure as before, is stuck. After *K* stuck rounds, the next vendor takes over.\n- **A round that makes it worse is undone:** *Loop until green* keeps the state closest to passing so far, by the runner's count: the fewest failing tests, and a pass before a failing check with none failing (at first, the code before any change). A round that ends further from passing is undone: more tests failing, a check that fails where it passed, or no test results where there were some. code-flow puts the files back to that state in a commit of its own, runs the check again, and tells the agent why. So the branch never ends worse than the best state the loop reached, and a run that ends failing says how many tests still fail. The rounds stay in the branch's history. Without a count from the runner, nothing is undone.\n- **Limits:** every run stops at a maximum number of minutes or agent steps (60 and 40 by default). The clock doesn't run while the run waits for you (a checkpoint), or only waits for a free slot (see *One machine, many runs*). A limit that isn't a positive number is ignored (the default applies). A stopped run keeps its work: each worktree with changes is listed as \"Work so far\".\n- **Plan usage:** if a CLI says its plan is out of usage, the run stops instead of failing, so you can resume it once the plan resets. That goes for a race contestant or a Supervisor worker too: nothing is judged or merged without it. Auto skips that plan until the reset time the CLI gave (or the reset times it reported before). A CLI that isn't signed in fails the step with a message saying so, and Auto skips it for a while. A contestant or worker whose CLI isn't signed in, or stalled, fails its step without that failure being recorded as its result: a resume gives it its turn again.\n- **Retries:** a turn that fails for a passing reason (overloaded, rate-limited, a network error, a CLI that stalled) is retried after 20 s, then 60 s, with its agent slot free meanwhile. A write turn picks up where it stopped: it continues its own session, or, without one, starts over from the worktree as the step found it, so a retry never redoes the work on top of half of it. Whether a failure is passing comes from the CLI's own error type; for a CLI that gives none, from its message (an HTTP status counts only with words around it, such as \"status 503\", not a bare 500 in a test log).\n- **One machine, many runs:** at most 6 agent CLIs and 3 checks run at once across all runs (`CODEFLOW_MAX_AGENTS`, `CODEFLOW_MAX_CHECKS`). Others wait their turn, and the step says so. The wait doesn't count against the run's time limit (a sweep's runs queued behind each other don't run out of time before they start), unless another of its steps runs meanwhile.\n- **Stopping really stops:** each agent CLI and each check runs in its own process group. Cancelling, a time limit or the CLI exiting stops everything it started, such as a dev server it left in the background, and escalates to SIGKILL if needed.\n- **Setup:** an optional command, such as `npm ci` or `uv sync`, that runs once in every new worktree before the agents start. code-flow runs it itself, outside the agents' sandboxes, so dependencies are installed even though Codex has no network in Standard mode. A worktree is marked as set up (in its own git folder, not in its files) once setup finishes; one whose setup never finished, because code-flow stopped during `npm ci` say, is set up again when a resumed run reuses it.\n\nAgents may suggest their own test command (the tester in Test-first does). code-flow only runs it if it's a plain invocation of a known test runner, with no shell operators. Otherwise it falls back to your check.\n\n> **Security:** a check runs the agents' code (tests, scripts) on your machine, outside Codex's and agy's sandboxes, as your CI would. Use code-flow on repos and tasks you trust, or run checks in Docker (see [Automations](automations.md#isolation)) or code-flow itself inside a container or VM. More in [Security](security.md).\n\nAdding your own pattern: see [Architecture](architecture.md#adding-a-pattern).\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Team patterns"},{"group":"Get started","id":"acp-agents","source":"docs/acp-agents.md","body":"# ACP coding agents\n\nOpen Settings → Agents & models, select a preset and check the executable and arguments. Run Check connection before saving, then select a discovered model for an eligible run.\n\n## Connect your CLI\n\nSettings → Agents & models provides **Connect OpenCode**, **Connect Kilo** and **Connect Cline** presets through the shared Agent Client Protocol (ACP) v1 adapter. **Add compatible CLI** connects other agents implementing the same stdio protocol.\n\n## Connect a supported CLI\n\nInstall and sign in on the machine running code-flow:\n\n| CLI | Install | Sign in | Preset invocation |\n| --- | --- | --- | --- |\n| [OpenCode](https://opencode.ai/docs/acp/) | `npm install -g opencode-ai` | `opencode auth login` | `opencode acp` |\n| [Kilo](https://kilo.ai/docs/code-with-ai/platforms/cli-reference) | `npm install -g @kilocode/cli` | `kilo auth login` | `kilo acp` |\n| [Cline](https://docs.cline.bot/usage/acp) | `npm install -g cline` | `cline auth` | `cline --acp` |\n\nUse a current CLI version with native ACP support. The Kilo preset uses the current `kilo` executable; Cline uses the official CLI's `--acp` flag.\n\n1. Install and sign in with the commands above. Complete provider/model setup in the CLI.\n2. Open Settings → Agents & models and choose the CLI's **Connect** button. If code-flow cannot find it, enter its absolute executable path. Reopening a saved preset edits its existing settings, preserving custom paths and arguments.\n3. Choose **Check connection**. This initializes ACP and creates a session in a temporary directory without sending a prompt. The result shows protocol compatibility, available models and advertised session loading. It does not verify inference, account quota or access to every advertised model.\n4. Choose **Save connection**. It appears immediately in provider and model selection; no server restart is needed.\n5. Select the agent for a run and choose **Full access** in Settings → Run defaults or the composer's permissions controls.\n\nCline asks for approval before tools, including reads, edits and commands, by default. Enable **Ask me for tool permissions** in the run's permissions controls and approve requests in the approval inbox. Without it, requests are declined. The preset does not enable Cline's auto-approval flag. Cline starts new ACP sessions in Act mode; read-only code-flow turns still apply the boundaries below. Provider selection stays in the CLI's authentication setup; code-flow's model picker changes the model within that provider.\n\nThe CLI keeps its own authentication. code-flow does not read or copy its credential files. A connection check can perform the CLI's normal startup work, including loading the user's configured plugins. Use executable invocations you trust.\n\n## Other compatible CLIs\n\nEnter a stable connection ID, display name, executable, and arguments, one argument per line. For example, an agent whose documented ACP invocation is `my-agent --acp` uses executable `my-agent` and one argument `--acp`. Executables must be names on the server's PATH or absolute paths. Arguments are passed directly with no shell expansion; paths with spaces need no quoting. Do not place credentials in these fields.\n\nUse **Edit**, **Disable**, **Enable**, or **Remove** to manage connections. Disabled connections retain their settings but are unavailable for new dispatch. **Reload connections** recovers after an edit in another window. The API rejects saves based on an older settings revision.\n\nModels and effort options are discovered from ACP session configuration; legacy `models` responses are also supported. Only advertised model/effort choices can be selected. Agents without model discovery can still use their CLI default. Changes to the command or arguments invalidate cached catalogs and the connection identity pinned by existing runs. Restore the original invocation to resume those runs, or start a new run. An already-running process retains its original invocation; subsequent operations check its pin again. This pin covers the invocation, not the executable's bytes or external CLI configuration.\n\n## Execution boundaries\n\n- ACP connections require **Full access** on the current application host. ACP does not provide a standard sandbox contract. Safe, Standard and Trusted permission profiles, SSH workers, agent containers, automated triggers and locked-down external-source runs are unavailable for these connections. Auto role selection excludes ACP unless Full access is selected.\n- Read-only turns request read-only behavior and decline permission requests for file changes and execution. The engine's existing worktree change checks still apply. This is not OS-level read-only isolation; the CLI may allow actions without consulting the client.\n- With **Ask me for tool permissions** enabled for an interactive run, ACP permission requests enter the existing approval inbox. Only `allow_once` can be approved; persistent permission grants are never selected. Requests are otherwise declined. Cancellation aborts pending approvals and sends `session/cancel` before bounded process cleanup.\n- The client advertises no filesystem or terminal capabilities. The agent uses its own tools. Agents that require host-provided file or terminal operations are not supported by this first version.\n- Session loading is used only when advertised. Replayed history is excluded from new turn output. Live mid-turn steering and the scoped code-flow team-tool bridge are unavailable; normal subsequent turns and structured team outcomes remain available.\n- ACP has no standard constrained-JSON output option. The adapter adds the requested schema to the prompt and the engine validates the returned result. Context-window occupancy and cumulative session costs are not reported as per-turn token usage or cost.\n\n## Storage and API\n\nConnections are owner-managed and saved in `CODEFLOW_HOME/acp-connections.json` with private file permissions and atomic replacement. The file is excluded from portable backups because it authorizes executable invocations. Reconnect agents explicitly after restoring on another host. Corrupt settings are not overwritten; repair the file and restart.\n\nOwner-only endpoints:\n\n- `GET /api/acp-connections`: version, revision and saved connections.\n- `POST /api/acp-connections`: `{ revision, connection: { id, name, command, args, enabled } }`. IDs use `acp:<lowercase-name>`.\n- `POST /api/acp-connections/check`: `{ connection }`, checks an unsaved or saved invocation without prompting.\n- `DELETE /api/acp-connections/:id?revision=N`: removes the saved connection.\n\nCustom filesystem restrictions block connection mutation/probing and agent execution. Shared-project accounts cannot manage or probe connections.\n\nProtocol references: [ACP v1](https://agentclientprotocol.com/protocol/v1/initialization), [session configuration](https://agentclientprotocol.com/protocol/v1/session-config-options). Cline's [native ACP implementation](https://github.com/cline/cline/blob/main/apps/cli/src/acp/acpAgent.ts) advertises both provider and model selectors in the model category; the adapter selects the model control explicitly.\n\nValidation includes process fixtures for streaming, session replay, model selection, permissions, cancellation, failures and timeouts; an engine run that changes a worktree and passes its check; and browser tests covering the Settings lifecycle.\n\n## Real CLI acceptance test\n\nRun the acceptance script explicitly with an installed, authenticated CLI:\n\n```sh\nnpm run test:acp:live # OpenCode (default)\nnpm run test:acp:live -- --agent kilo\nnpm run test:acp:live -- --agent cline\n```\n\nThis is outside the default test suite and consumes normal model usage. Set `CODEFLOW_LIVE_OPENCODE`, `CODEFLOW_LIVE_KILO` or `CODEFLOW_LIVE_CLINE` to an absolute executable path if needed; `CODEFLOW_LIVE_MODEL` optionally selects an advertised model ID. Otherwise the test uses the CLI's discovered default. Missing executables fail before creating a test repository or starting code-flow. Use `--help` for options.\n\nThe script creates a temporary repository and a separate code-flow home, saves the connection through the Settings API, and starts a real coding task. It verifies failing baseline tests become passing without test edits, streamed text and tool events, and a clean commit. It then restarts code-flow and sends a follow-up that must reuse the same CLI session and remember a phrase from the first turn. Both code-flow's check and an independent test run must pass. Only reads of the task files and tests, searches for fixture names, edits to the two task files, and the exact `node --test` command can be approved automatically; unrecognized permission requests stop the test for inspection. Temporary run logs, commits, and a `receipt.json` remain at the printed evidence path; the server stops automatically. The original project and code-flow settings are unchanged.\n\nThis two-turn test passed locally on October 19, 2026 with OpenCode 1.18.30 and `opencode/big-pickle`, including session loading after restart and two committed changes. This establishes compatibility for the observed installation, not every ACP agent or OpenCode release.\n\nBoth Kilo and Cline passed the same two-turn acceptance test on October 19, 2026 using their installed CLIs and normal account authentication:\n\n| CLI | Model | Duration | One-time approvals | Result |\n| --- | --- | --- | --- | --- |\n| Kilo 7.8.12 | `kilo/kilo-auto/free` | 52.0 s | 2 | Passed both turns and session restoration |\n| Cline 3.0.73 | `anthropic/claude-sonnet-5.5` | 24.8 s | 5 | Passed both turns and session restoration |\n\nThe [sanitized acceptance receipts](evaluations/acp-live-2026-10-19.json) record run IDs, commits, streamed event counts and checked assertions. Each run changed a baseline with three failing tests into four passing tests without modifying those tests, then produced a second clean commit after restoring the same session. The Cline run exposed and verified a fix for file-edit approvals: its native editor supplies `rawInput.path` without ACP `locations`, which the adapter now carries into the approval inbox. These observations establish compatibility for the listed versions and models; model availability and task success can vary.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"ACP coding agents"},{"group":"Get started","id":"help","source":"docs/help.md","body":"# Ask code-flow\n\nGet help with setup, workflows and troubleshooting from the documentation, using a coding CLI already installed on your machine. The assistant is available in the local app's documentation.\n\n## Open the assistant\n\n1. Start Code Flow and open the owner app using the link printed in the terminal.\n2. Choose **Application menu → Help & documentation**, or search for it in the command palette.\n3. Use the **Ask code-flow** side panel, or open it from the header, and choose an installed CLI.\n4. Ask a question such as “Which workflow should I use to fix a failing test?”\n5. Continue with follow-up questions or open source guides without losing the conversation. Press **Enter** to send and **Shift + Enter** for a new line.\n\nThe companion sits beside the guide on desktop and expands to a full-width panel on a phone. Close and reopen it to switch between reading and chatting. **New chat** clears the conversation and draft.\n\nThe assistant supports **Claude Code, Codex and Antigravity**. It uses the provider's existing CLI authentication. Model and effort preferences saved in the local app's **Settings → Agents & models** are forwarded; when unset, the CLI's defaults apply. ACP and extension providers are not offered in this help flow. ACP currently requires Full access for normal runs.\n\n## What it receives\n\nEach question includes excerpts from up to five relevant bundled guides, the page you are viewing as a retrieval hint, and up to three earlier question/answer pairs from the chat. Questions are limited to 4,000 characters; answers to 12,000. Source excerpts are capped at 10,000 characters each. Engineering records are excluded from answer retrieval so historical decisions do not substitute for current instructions.\n\nThe latest 12 messages, up to three completed question/answer pairs and the current draft are stored in this tab’s session storage. They survive guide navigation and reloads. **New chat** clears them; the browser manages their lifetime when the tab closes or is restored. Code Flow does not add it to run history or project memory. The selected provider processes the prompt under its own account and logging behavior. Requests consume the same CLI usage allowance as other invocations.\n\n## Execution boundaries\n\nHelp runs with read-only, Safe adapter settings in a fresh temporary Git directory. No selected repository, run transcript, attachment, secret file or project configuration is copied into it. The prompt instructs the assistant to answer from the supplied text without tools; this instruction is not an additional OS sandbox. Provider-level settings, integrations and hooks remain subject to the normal CLI trust boundary described in [Security](security.md).\n\nHelp does not expose merge, terminal, configuration or publishing actions. Source links are chosen by the server from the retrieved guides. Answers are displayed as text, not executed as HTML. Treat generated answers as guidance and check the sources before running suggested commands.\n\nHelp currently supports macOS and Linux, with absolute, nonempty PATH entries. Only an authenticated owner on the main application listener can use help. Shared-project accounts, runner credentials and the phone listener cannot invoke it. Filesystem restrictions pause help as they do other host CLI execution. Two simultaneous help requests are allowed, with a two-minute limit per turn. **Stop**, **New chat** or leaving the page cancels the request. Closing the panel lets an active answer finish; the disposable directory is removed after the adapter exits.\n\n## Troubleshooting\n\n| What you see | Next step |\n| --- | --- |\n| No supported CLI found | Install a supported CLI on the application host and sign in through its terminal command. |\n| Not signed in | Open the owner app using its current terminal link, then reopen the docs. |\n| Provider signed out | Sign in through that provider's CLI and retry. |\n| Usage limit reached | Choose another installed provider or wait for your plan's limit to reset. |\n| CLI took too long | Ask a smaller question, or check the CLI directly in a terminal. |\n| Documentation unavailable | Run `npm run build:docs` from the Code Flow checkout. |\n| Filesystem restrictions pause execution | Read the guides directly; review policy in the main app if execution is needed. |\n\n## Hosted documentation\n\nGitHub Pages serves the same searchable guides. It cannot launch your local CLIs. The deployed site omits the companion panel and its controls. It does not connect to localhost or request credentials. To chat, open **Help & documentation** from your running local app.\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Security](security.md)\n","title":"Ask code-flow"},{"group":"Work with agents","id":"runs","source":"docs/runs.md","body":"# Runs and workspace\n\nOpen a run from the sidebar or Inbox. Read the result and verification evidence, then use the suggested next action to answer a question, request a fix or review changes.\n\n## The run view\n\nA run reads like a conversation, top to bottom:\n\n- **The header** shows execution status, the task and workflow. **More run actions** contains Edit & run again, Archive and Delete; a running task keeps Stop run visible.\n- **Evidence and next action** separate execution, automated checks, agent review and human review. A contextual action opens the pending plan/question, reviews changes, or recovers unfinished work. A finished execution can still have failed, blocked or unverified checks.\n- **Your task**, as the message that started it, with any context you gave.\n- **The agents' work**, as one list of steps (\"4 steps · 6m 2s\"). It's open while the run is going and folds away once there's a result. Open any step to see its transcript inline: the agent's messages (rendered as Markdown), its tool calls as one-line rows that expand to the full input and output, and its thinking, collapsed. <kbd>[</kbd> and <kbd>]</kbd> move between steps.\n- **The result**, with its branches and what you can do with them (see [Review and ship](github.md#review-and-ship)).\n- **The reply box** at the bottom (below).\n\n<picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"images/transcript-dark.png\">\n <img src=\"images/transcript-light.png\" alt=\"A step opened in a run: the agent’s recorded tool calls and output (demo data)\">\n</picture>\n\n## Recovering work\n\nThe next-action card explains what happened and what is retained. **Retry unfinished work**, **Resume work**, and **Extend budget & resume** reuse recorded results with a fresh allowance of the displayed calls and minutes. **Reconnect provider** opens provider health and login instructions; resume after reconnecting.\n\n**Request a fix** prepares an editable follow-up without sending it. **Fix check command** opens a continuation dialog: choose the saved result branch and correct setup/check commands. Starting it creates a child run with the same team and permissions and a single configured check. Original history and evidence remain available.\n\nIndependent agent review applies to the reviewed commit. When a follow-up changes that result, **Review outdated** keeps the earlier findings as history. Inspect the updated changes and checks before accepting or publishing them; a passing follow-up check does not renew independent approval.\n\n## Talking to a run\n\n[Auto teams](auto-team.md) add individual member targeting and structured questions. Questions addressed to you pause the workflow and appear in **Needs you**; answering saves the reply before work resumes. The team panel exposes tasks, dependencies, messages, coverage, checkpoints, context references and coordination usage. Its project-memory panel supports revision-aware edits, acceptance and deletion. A stopped Auto team with unresolved gaps resumes its existing workflow with a fresh bounded allocation.\n\nRuns are conversations, not fire-and-forget jobs:\n\n- **Approve the plan:** tick *Let me approve the plan* (the subtasks for Supervisor, the tests for Test-first). The run pauses at that checkpoint, and its time budget with it.\n - **Approve:** the agents start building.\n - **Ask for changes:** your note goes to the author, who revises in its own session and asks again.\n - **Reject:** the run ends without building anything.\n - A pending checkpoint survives restarts. Waiting runs show under *Needs you*, with a notification.\n- **Message the team** while it runs, from the reply box. **To** picks the agent, or *Next agent* gives the note to the first one that can take it. A line next to it says where the note will go.\n - **Into a running Codex turn:** Codex runs through `codex app-server`, so a note for Codex (or for anyone) reaches its running turn right away, as a new message on the live thread (`turn/steer`). Codex takes it into account from its next step, without waiting for the turn to end.\n - **At the start of the next turn:** Claude, agy, and Codex on the `exec` fallback can't take input mid-turn. Their notes are added to the start of that agent's next turn.\n - The run view shows each note as queued or delivered, and which step it reached (\"✓ delivered to codex mid-turn (Implement plan)\"). A mid-turn note also shows in that step's transcript. If the turn ended just as the note arrived, the note goes back in the queue for the next turn. If the step is retried, the retry gets the note in its prompt.\n - **When no turn comes:** a note sent during the last turn, which couldn't take it, has no turn left to go to. When the run finishes, it's marked as not delivered and the log says so, with its text: reply to send it. It never reaches a later reply's turn as if you had just sent it. (A [single agent](#a-single-agent) takes it as your next message instead; a run that stops keeps it for its resume.)\n- **Reply** after it finishes, from the same box:\n - to an agent in its worktree (**To:** e.g. *Codex · build*): it continues its own session, then code-flow commits and re-runs the check;\n - or to *Whole team · new run*: a new run that starts from this run's result branch, with your feedback added to the task.\n\n<kbd>↵</kbd> sends and <kbd>⇧</kbd><kbd>↵</kbd> adds a line.\n\n### A single agent\n\nA **Single agent** run is one conversation with one agent, as in that agent's own app:\n\n- The run reads as a chat: your messages, and each turn the agent takes. A turn streams live while the agent works. Once it's done, it folds into the agent's answer, with **Show work** for its tool calls and thinking.\n- There's no **To**: every message goes to that agent. A message you send while it works goes into its running turn if the turn takes input (Codex). Otherwise it's sent as your next message as soon as the turn ends, so nothing waits for a reply you'd have to send again.\n- A turn that fails ends that turn, not the conversation: reply to try again or to change course. Whatever it changed is kept on its branch.\n- code-flow commits after every turn and runs your check if you set one. The branch, and merging it or opening a pull request, are at the bottom of the thread.\n\nPrompts are passed on **stdin**, not as command-line arguments (`claude -p` reading stdin, Codex's JSON-RPC or `codex exec -`, and agy's stream-json input), so long tasks and diffs aren't limited by the operating system's argument size.\n\n## Projects\n\nRuns are grouped into projects. A project has a name and the repositories its runs work in.\n\n- **No setup:** the first time you start a run in a repository that no project holds, code-flow makes a project named after it. The first time you start a code-flow that has projects, every repository that already has runs gets one, so your runs are grouped from the start.\n- **The sidebar** lists every project with its runs under it, the projects with the newest runs first. Click a project's name to open its page, or the arrow next to it to fold it. Hover it for **+** (a new run in that project) and **⋯** (rename it, change its repositories, or delete it). Projects with nothing to list fold into one *more projects* line, each with how many archived runs it has. **+** next to *Projects* makes a new one.\n- **A project's page** shows what needs you, what's running, recent results with their check and pull request state, the project's automations and inbox, and the week's numbers (runs, checks passed, cost). Its task box opens the composer in the project. The side panel shows the project meanwhile.\n- **The project button** (in the composer, in Insights and Automations, or <kbd>p</kbd>) picks the project you're in: new runs start in it, and Insights and the [Automations page](automations.md#projects) show it. With *All projects*, a new run goes in its repository's project.\n - Automations says when other projects have results or automations too, with a link to show all.\n - The read-only [phone view](automations.md#phone-view) lists every project's runs, each under its project's name.\n- **Switching projects** moves the composer to the project's repository you used last, and to the pattern and team you last started a run with there from the composer (chats in the side panel don't change it). Each project remembers its own.\n- **Several repositories:** a project can hold several, and a repository can be in several projects. Each run still works in one repository. **New project** and **Edit** in the switcher name a project and add or remove its repositories.\n- **A run's repository always belongs to its project.** Starting a run in a project, or moving one there, adds the repository if it's missing. The composer says so before you start.\n- **Moving a run:** click the project chip at the top of the run. Its branches and worktrees stay where they are.\n- **A side-panel chat** starts in the project you're in, like a run from the composer.\n- **Runs started from other runs stay in that run's project:** replies to the whole team, chats started from a run's result, comparisons, sweeps and automations. An automation whose project was deleted starts its runs in its repository's project.\n- **Deleting a project** keeps its runs; they show under *No project*. Taking a repository out of a project keeps the runs already in it.\n- Projects are saved in `~/.codeflow/projects.json`. Each run records its project in its own log.\n\n## The side panel\n\nThe panel on the right (the button at the top right, or <kbd>\\</kbd>) follows what you open, like the side pane in Claude's or Codex's app. On a run (or its review), every tab is that run's: its project, its repository and its worktrees. On a [project's page](#projects), the composer and elsewhere, it's that project and repository. The line at the top of the panel says which. The pin keeps the panel on one run or project while you open others; **Follow** lets go. **↗** opens the tab in a window of its own, for that run or project, and the panel here closes. Each run and project remembers its own tab, chats, terminals, folders and browser pages, so going back to a run brings its panel back. Drag the panel's left edge to resize it; a narrow panel names only the open tab.\n\nChat, Terminal and Browser keep what's open in a strip, like browser tabs: **+** opens another, a pill shows when one is working or has news, and **×** closes it.\n\n- **Chat.** On a run, a side chat works in the run's own worktree, as a conversation of its own with the agent you pick: messages to the run's agents never reach it, and a reply to the run never continues it. **Ask** only reads, and leaves the result and its check as they were. code-flow doesn't undo what changes in the worktree while an Ask runs, since you may be working there too; a warning names the changes instead (agy can write even in an Ask: see [caveats](security.md#caveats)). **Change** edits the worktree: code-flow commits the change on the run's branch, runs the check, proves it again and updates the result, as it does for a reply; the steps it takes (the check, the proof) are the side chat's, not in the run's timeline. A message sent while the team is still working waits for it to finish, then gets a time and step budget of its own. If the run fails or is stopped before the message's turn starts, it ends with a note saying so: resume the run, then send it again. A turn the run stopped in the middle of waits instead, and resuming the run finishes it. The run's thread stays the team's: a side chat's turns aren't in it, and a change one made gets one line there, with a link back. **☰** lists every chat, closed ones too, and **Separate chat** starts a [Single agent](#a-single-agent) run on a branch of its own, from the run's result. In a project, a chat is such a run (it has no worktree to share): it shows in the sidebar, keeps its session and its branch, and **Open** shows it as a run.\n- **Changes** (on a run) lists what it changed: the whole result branch, or just its **last step** (the latest commit, a side chat's change, say), with a diff per file, kept up to date while the run works. Click a line to comment; comments go to the agent that wrote the code, and their drafts are shared with the full-width [review](github.md#review-and-ship) (**Full review**).\n- **Files** lists the folder's files (ignored files hidden; **M** changed, **U** new), with search. A file opens in the shared editor used by Repository tools → Code, with version-checked saves, three-way conflict resolution and recoverable drafts. Active run worktrees remain protected against manual saves. Open **What could this change affect?** for static import evidence. On a run, the folder picker offers the run's worktrees and starts on its result's, so you can read what the agent wrote before merging; a project with several repositories offers each. From the composer, **+** and dragging add a file to the task's context. With the keyboard, <kbd>Enter</kbd> opens a file or folder, <kbd>→</kbd>/<kbd>←</kbd> open and close a folder, and <kbd>↓</kbd>/<kbd>↑</kbd> move through the list.\n- **Terminal** is a real terminal in a run's worktree (its result's, to start with) or the repository: your login shell in a pseudo-terminal, so prompts, colours, pagers and full-screen programs (`vim`, `htop`, `less`, `git log`) work, mouse clicks and scrolling too, and it fits the panel as you resize it. The first one opens when you show the tab. **+** opens another in any of them, and **Split** shows two, one above the other (drag the line between them). An app the run started with **Run app** can be shown as a read-only log, which stays after the app exits, marked *exited*. A shell stays open between commands, so `cd`, exported variables and virtualenvs stick. Its pill names what's running in the foreground; <kbd>Ctrl</kbd>+<kbd>C</kbd> stops the command, and **Stop** ends the shell with everything it started and opens a new one in the same folder. Copy and paste with <kbd>⌘C</kbd>/<kbd>⌘V</kbd> (<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>C</kbd>/<kbd>V</kbd> elsewhere). A terminal you come back to, or reload, shows what it showed before (the last 256 KB). Up to 12 shells stay open; a new one takes the place of the oldest idle one, never one that's running something, in the foreground or in the background (`npm run dev &`, a stopped `vim`). If all 12 are, opening another says so: stop or close one first. Closing one ends everything it started, `nohup`'d and disowned jobs too, and stopping code-flow closes every shell the same way. The pseudo-terminal needs `python3` (3.6 or later, standard library only; `CODEFLOW_PYTHON` picks another). Without it a shell is fed through a pipe: commands run one at a time and each reports its exit code, there are no pagers, prompts or full-screen programs, colours are off, and <kbd>↑</kbd>/<kbd>↓</kbd> recall commands.\n- **Browser** shows pages of its own: on a run, the app it started with **Run app** first (the result card's **Show in panel** opens it too), or any address. **Phone**, **Tablet** and **Full** set the width, and **Compare** shows two pages side by side (another worktree's app, a race contestant's). An app on this computer (an `http://` address on `localhost` or `127.0.0.1`) is shown through a preview on a port of its own, which lets you annotate it:\n - **Pick** an element, or draw a **Box** over an area: each is a numbered pin with a snapshot, and you say what's wrong. **Adjust** tries style changes (size, colour, spacing) on a picked element right in the page; what you try goes with the pin. Pins stay when the page loads again, but **Adjust** can't reach them any more: their elements went with the old page.\n - **Console** lists the page's errors, failed requests and files that didn't load; they can go with the pins.\n - **Add to main chat** puts the pins and their snapshots in the run's message box, to add a line to and send; **Send to** picks a side chat that's running or done, a new side chat (once the run has a result), or a new run's task instead. Pins from different pages each say which page they're on. Snapshots sent with a reply are saved in the worktree's `.codeflow-context/` folder, which is never committed, and named in the message.\n - The preview is a different origin from code-flow (its own port), so the app's scripts can't use your code-flow session, and code-flow's cookies are taken off every request before it reaches the app. It uses the host name you opened code-flow with, so the app's own sign-in works in the frame. It passes WebSockets through (hot reload works) and takes requests only addressed to itself. It keeps the app's Content-Security-Policy, adding only what lets code-flow's page show it in a frame and run the annotations, and leaves out X-Frame-Options. Snapshots are drawn from the page's own elements: images from other sites and some fonts don't show in them, and a password you typed shows as dots.\n - Sites that refuse to be shown inside another page open in a new tab with **↗**.\n- **Snapshot** works on any page, on this computer or not: code-flow opens the address in a headless Chrome (or Chromium, Edge or Brave; `CODEFLOW_CHROME` picks one) with a fresh, empty profile, at the width you picked (Phone 390, Tablet 768, Full the page's width in the panel), and shows the picture in place of the page. **Whole page** takes the full height (up to 12,000 pixels), zoomed out as a phone would show it when the page isn't made for phones, **Retake** takes it again, and **Live page** goes back. Drag on it to draw boxes (on a touch screen, turn on **Draw** first; otherwise a finger scrolls): each is a numbered pin with its crop, sent like any other, and the whole snapshot with the boxes numbered on it goes along. A new snapshot asks before it drops boxes drawn on the old one, and going to another address goes back to the live page. It's what a signed-out visitor sees: no cookies or sign-in from your own browser, and anything that needs a click or typing first won't be in it. A snapshot takes a second or two and one Chrome while it lasts; one is taken at a time, an `alert()` on the page is answered OK, and its Chrome and profile are gone when it's done.\n- **Notes** is a scratchpad per repository (the run's, or the project's) plus a general one, saved as you type in `~/.codeflow/notes.json`, so it's the same on your phone. **→ Task** adds the selection (or the whole note) to the task in the composer; **→ Chat** sends it to a new chat.\n\n## Runs survive restarts\n\nEvery run is saved to disk as it happens (`~/.codeflow/runs/<run>.jsonl`), and the server rebuilds the run list from those files when it starts.\n\n- **Interrupted runs:** if code-flow stops while a run is going (Ctrl-C, a restart, a crash), the run comes back as **Interrupted**.\n - **Resume** re-runs the pattern from the top. Each step that already finished (agent turns, checks, commits, worktrees) is replayed instantly from a journal (`<run>.journal.jsonl`), and real work continues at the first unfinished step. Nothing finished is redone.\n - Runs that hit a time or step limit, failed, or were stopped by you can be resumed the same way, with a fresh budget.\n - A recorded check is replayed only if the code it checked is still the code at that point. A recorded commit is replayed only if it's still in the branch's history. Anything else runs again.\n - While it replays, code-flow reads what changed at the commit the replay has reached, not at the newest one already in the worktree. So the pattern decides as it did the first time: Test-first doesn't take the builder's later rounds for the tester's changes and undo them.\n - A race contestant or worker whose CLI wasn't signed in, or stalled, isn't recorded as failed: the resume gives it its turn. One that ran out of plan usage stopped the run, so its turn comes after the reset.\n - Follow-ups on a finished run don't replay the pattern at all; only the follow-ups run, each with a full time and step budget, and the run's check result stands until one of them runs the check again. A follow-up that fails doesn't undo the finished run.\n - Resume is claimed at once, so a double click or a PR watch can't start the same run twice. If the last attempt is still wrapping up (listing its work), the new one waits for it. Stop pressed meanwhile keeps it from starting: the run stays as it was. Archiving, merging, pushing and opening a pull request are refused while the run works (a follow-up or a side chat on it too, from any tab or your phone) and until a resume has started.\n - Notes delivered to a turn that never finished, or failed, go back in the queue for the next turn (the retry, or the resumed run).\n- **Sessions that are gone:** an agent continues its own conversation only from a turn that worked. If that conversation no longer exists (the CLI says so), the turn runs once more in a new conversation, given the task from the start. A conversation that couldn't be resumed is never recorded as the step's.\n- **At startup:** a step still marked as running in a run that isn't (code-flow stopped or crashed while it ran) is closed as interrupted. Folders in a worktree root that no run owns are listed in `~/.codeflow/server.log`; code-flow never deletes them. A run log without a run in it is logged there too, rather than skipped silently.\n- **Clean shutdown:** Ctrl-C or SIGTERM stops running agents and marks their runs interrupted. A second Ctrl-C exits at once.\n- **One server per data folder:** a second server on the same `CODEFLOW_HOME` waits for the first to stop (as in a restart), then refuses to start. A crash mid-write can leave a line half-written; it's closed off before the next write, so it never corrupts the next line. Failed writes, such as a full disk, are counted and reported at `/api/health`.\n- **Needs you:** finished runs you haven't opened, and interrupted runs, are listed at the top of the sidebar, whatever their project, instead of under it. The browser tab shows the count, and the bell at the bottom of the sidebar turns on desktop notifications for when a run finishes or needs you.\n\n## Insights, comparisons and finding runs\n\n- **Insights** (in the sidebar, or <kbd>i</kbd>) shows, per agent and per pattern, for the project you're in, one repo, or everything, over 7 days, 30 days or all time:\n - runs, how often the result passed its check, and how often it was proven (a test fails without the change; a weak proof, where the tests only fail to load without it, doesn't count);\n - race wins, loops each agent got green, and review findings other reviewers agreed with;\n - failed steps, tokens (input, output, cached) and time per step;\n - Claude's API-equivalent cost. Your plan isn't billed per token; it's a measure of size.\n- **Insights → Experiments → Compare setups…** starts the same task with several patterns at once, each with the team you last picked for it, optionally 2–3 times each. Insights shows them side by side: result, time, steps, tokens, cost, whether tests were touched, and whether the result was proven. The best value in each column is highlighted.\n- **Tokens and cost** also show on each step, in the run's details under the header (with a per-agent breakdown on hover) and when you hover a run in the sidebar.\n- **Disk:** every worktree is a full checkout, often with its own `node_modules`. Insights measures them and **Clean up** removes the worktrees, not the branches, of runs that finished more than *N* days ago and need nothing from you.\n - **Archive…** with its worktrees removed also clears the rest of the run's folder in `.worktrees/`: scratch checkouts an interrupted attempt left (where proofs and reviewers' tests ran), and, when the branches go too, the context staged for its agents (kept otherwise: a resume or a reply copies it into the worktree it checks out again). The folder goes once it's empty. The pull request's head code-flow fetched for a run started from one (`codeflow-pr/<n>`) is deleted with the last run still using it, unless it's checked out somewhere; it can be fetched again.\n- **Finding runs:** the sidebar lists every [project](#projects)'s runs, one line each: a status dot, the task and its age (hover for the rest). It has search (<kbd>/</kbd>) and, behind the button next to it, grouping by project or by status, filters (needs you, running, passed, failing, tests changed; pattern; agent; this repo) and **show archived runs** (also the archive button at the bottom of the sidebar). While you search or filter, matching archived runs show too. <kbd>j</kbd>/<kbd>k</kbd> move between runs. In a run, <kbd>[</kbd>/<kbd>]</kbd> move between steps. <kbd>\\</kbd> shows or hides the side panel. <kbd>?</kbd> lists every shortcut. Run status changes are announced to screen readers.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Runs and workspace"},{"group":"Work with agents","id":"auto-team","source":"docs/auto-team.md","body":"# Team\n\nStart with an Agent conversation and enable **Team** whenever the task needs collaboration. Choose a template and providers in Team setup. You can turn Team off and continue with the same agent, task, workspace and conversation.\n\n## Review the proposed team\n\nOpen **Team**, enable it, and select the template, coordinator and providers code-flow may use. A coordinator inspects the repository and proposes individual members, tasks, dependencies, file ownership, and acceptance criteria. You can approve, revise, or reject that plan before workers start. Planning itself uses a CLI invocation. Approval defaults on; an explicitly saved preference or automation policy still applies.\n\nTeam is available unless disabled with `CODEFLOW_AUTO_TEAM_ENABLED=false`. Existing saved workflow and project choices are preserved. Simple conversational questions are answered by the lead without starting another team; explicit team requests and substantial work use the configured workflow. Team progress stays collapsed under its chat message. Corrections sent while a team works stop the current execution and enter a revised request, with a visible acknowledgement and preserved partial work. Existing manual patterns are available under Advanced as built-in workflow templates. It uses the same official CLI adapters, worktrees, checks, permissions, review/shipping actions, and subscription logins as other runs.\n\n## Reusable templates in Advanced\n\nOpen **Advanced** to choose a built-in workflow, design a task graph, import/export definitions, or publish a new immutable template version. Templates can be personal or scoped to a project. Use **Import browser library** to migrate existing presets without deleting the browser originals; role/model preferences and workflow options survive, while permission grants, credentials, host bindings, and budgets do not.\n\nAuto generates plans without needing a template. It may also reuse enabled task graphs, assign their roles, combine instances, and append task-specific instructions. The runtime preserves dependencies, acceptance criteria, and shared contracts, then applies normal plan validation and independent verification. Up to six enabled definitions of 16,000 characters each are offered per run, in ID order. Fixed workflows such as test-first, races, and councils keep their existing executors in Advanced; they are not advertised as composable Auto task graphs.\n\nA manually selected template is mandatory. Its exact version and inputs are pinned to the run and survive cancellation/recovery; editing the library does not change an active run. The run menu's **Save as team template…** opens an editable preview. Review task-specific instructions and replace them with parameters before publishing. Saving does not enable Auto reuse automatically.\n\n## Run permissions across providers\n\nAll new runs use one versioned permission policy, including background jobs and collaborative launches. Every planning, worker, review, repair, retry, and follow-up launch resolves that policy against current restrictions and the selected provider. Read-only planning does not reduce implementation workers to read-only access. Templates and agent messages cannot grant permissions; only an explicit user change creates a new policy revision. Child runs inherit the parent’s current trust ceiling, approval preference, mandatory restrictions, and container or remote execution environment. A child cannot increase its parent’s permissions or remove its execution boundary. Historical saved runs without a policy retain their legacy behavior.\n\nClaude and Codex receive their native permission settings, with provider-native delegation disabled. Matching trust labels do not imply identical isolation: Codex uses its provider sandbox, while Claude's local tool rules are not an operating-system filesystem boundary. Advanced can require an agent workspace write boundary, denied agent-command network access, or disabled external tools. Unsupported combinations fail before launch or replacement dispatch. The run view records the effective settings for each turn. By default, Antigravity uses the local macOS isolated executor with supported CLI 1.4.4 or 1.4.5 and an existing native Keychain login. Interactive Agent and Team turns receive task-scoped Codeflow browser and computer tools when external tools are permitted; computer use requires Full access. It uses protected fresh settings, has no native terminal/browser/delegation or ambient MCP servers, and never resumes native sessions. Mandatory external-tool restrictions retain its file-only profile. Shared-tool live validation covers 1.4.5. Advanced also offers explicit unrestricted native Antigravity on the local host; see below. Remote/container Antigravity, ACP and extension adapters remain blocked. Claude/Codex controls are probed again at each launch, in the selected container when applicable. Unknown or missing controls stop execution.\n\nRemote workers receive and validate the complete policy, including all mandatory requirements; remote/container profiles that cannot prove a requested boundary are rejected. Required interactive approvals need the local Codex app-server and an approval handler; they cannot silently fall back to exec or combine with mandatory sandbox boundaries that approval could relax.\n\nClaude’s external-tool restriction supplies a strict MCP configuration, disables Chrome integration, and denies WebSearch/WebFetch; Code Flow’s scoped team bridge remains available. Codex managed turns override ambient approval settings, additional writable roots, and command-network settings. A mandatory workspace write boundary also removes temporary-directory write exemptions. Native-delegation controls disable provider agent tools; they are not process-level isolation from programs invoked through an authorized shell.\n\nThese requirements describe agent execution. Browser/MCP authorization is separate from terminal network access. Setup commands, checks, and previews use their separately configured execution environment. Worktrees do not restrict file reads. The built-in shared browser/computer bridge applies the same task-scoped tool checks across Claude, Codex and Antigravity. Arbitrary provider-configured integrations and filesystem reads do not share a cross-provider sandbox.\n\n## How work proceeds\n\n1. The runtime validates the proposed plan against installed providers, quota observations, configured model/effort pins, member/task limits, dependency cycles, and review independence.\n2. New runs start ready tasks as earlier tasks finish, without waiting for unrelated workers. Saved wave-based runs retain their recorded scheduling behavior. Each member has its own identity and session, even when several members use the same provider. Tasks with overlapping file ownership run separately.\n3. A worker returns a structured outcome: submitted work, a failure, or a question with a progress checkpoint. Submitted changes are integrated serially and checked before dependent work starts.\n4. When a decision is needed, the coordinator can add bounded work or workers, reassign unfinished work, retire idle workers, and add shared contracts. It cannot remove approved requirements or raise its own limits.\n5. An independent read-only member reviews the integrated result. Blocking findings can trigger bounded repairs, followed by another check and review of the current commit.\n\nAn implementation is verified only when required work is present, the configured check passes, independent review has no blocking findings, and fail-to-pass proof succeeds when enabled. Unresolved tasks, blocking review findings and failing checks are gaps that stop the team. A repository without a runnable check cannot be blocked on it: the result finishes as reviewed but unverified, with no pass/fail verdict, and nothing from it is accepted into team or project memory. An investigation can finish with evidence-bearing artifacts and independent review without a code check. The run does not merge or publish its result automatically.\n\n## Communication and blockers\n\nMembers send addressed questions and replies through their structured outcomes. The runtime attaches the sender, task, request ID, and origin; an agent message never becomes user authorization. A worker needing information ends its turn, saving its partial work and checkpoint. Its CLI slot is then available for other work.\n\nThe next scheduling boundary delivers an existing answer or runs a short read-only response turn for the addressed member. A question addressed to you appears in the run view and in **Needs you**. Your answer is saved before the waiting workflow is released. The run clock pauses while waiting for you. An unanswered peer request or failed task can prompt a bounded coordinator revision; exhausted limits produce an explicit blocker.\n\nYou can also send a note to an individual member. Codex app-server can receive user notes during a turn; other transports receive them on the next turn. Native permission requests remain denied under the existing adapter policy. Auto members use the structured question protocol for missing information.\n\nClaude and Codex members also receive scoped collaboration tools when their installed CLI supports the configuration. They can send messages, ask/reply, read their inbox, publish findings, save notes, or propose project memory during a turn. Calls acknowledge durable acceptance promptly; the scheduler applies commands at recorded boundaries. A blocked member still ends its turn to release the process slot. Each turn may make at most 256 collaboration tool calls (messages, questions, memory reads and writes together); after that, calls fail and the member finishes with its structured outcome. New controlled runs support Claude, Codex and isolated Antigravity. Explicit unrestricted Antigravity is an exception to the native-delegation restriction; other adapters remain rejected. Older saved runs retain their recorded execution behavior.\n\nQuestion and wait state are one logical journal transaction. Duplicate operation IDs do not duplicate messages. Expired deadlines, unavailable recipients and cyclic waits route to the coordinator within the existing budget. Reassigned or retired attempts cannot overwrite current work. Knowledge dependencies can use evidence-bearing replies; artifact dependencies require integrated code, and completion dependencies require the upstream task to finish.\n\n## Three memory scopes\n\n| Scope | Contents | Who receives it |\n|---|---|---|\n| Private | Observable progress, attempts, unresolved questions, and a portable checkpoint | That member; a replacement receives a separately authored shared handover and artifacts |\n| Team | Accepted contracts, task outcomes, questions, findings, and artifact references | Members whose assignment needs the information; reviewers receive independent review context |\n| Project | Accepted conventions and reusable facts with source, revision, and applicability | Future matching runs in the same project and repository |\n\nPrivate scope controls context assembly and memory APIs; it is not filesystem secrecy from a fully trusted process on the same machine. Checkpoints describe observable work, not hidden model reasoning.\n\nThe team view shows members, dependencies, messages, requirement coverage, checkpoints, and each worker turn's context references. The project-memory panel shows included and omitted entries with reasons, provenance, and branch applicability. You can create, edit, accept, delete, or rate a memory as useful, irrelevant, stale or incorrect. Saving an edit never accepts a proposal: a proposal stays a proposal until you accept it, and an accepted entry you edit is recorded as accepted by you. Edits require the displayed revision, so concurrent changes cannot silently overwrite one another.\n\nStructured worker findings become project **proposals** after result verification. Live tools can save an earlier proposal, explicitly unverified and requiring user review. They remain tied to the result branch and commit. Accepting a branch-specific proposal does not make it a convention for every branch; changing it to **Use across this repository** is an explicit user decision. Repository membership, branch/commit ancestry, expiry, and source revisions are checked when selecting memory. Retrieval uses bounded text/path/tag matching, without embeddings. User feedback adjusts ranking only for that revision; stale or incorrect revisions are excluded. Feedback never raises trust or authorizes promotion.\n\nLocked-down, untrusted, or unknown-origin material cannot automatically become accepted project knowledge, including through a derived summary. The user must approve the exact revision. Auto teams do not consume historical recurring lessons whose provenance is missing; explicit repository conventions and accepted project memory are separate inputs. Automatic learning is also disabled for untrusted sources.\n\nDeleting a memory erases its stored body, removes it from retrieval, and retains deletion metadata. Entries that list it in their provenance (it was in a member's context when they were written) keep their own text and the origins they inherited from it, so a running or resumed team that still cites it keeps working. Private checkpoints and team records are retired when their run is archived with cleanup; project knowledge is never removed that way. A run may hold at most 512 private and team records. Source run transcripts, task checkpoints in run history, provider conversations, and user backups have separate retention. Deleting memory does not erase those source histories or a prompt already supplied to a running CLI.\n\n## Budgets, restart, and resume\n\nThe run's maximum steps is also Auto team's actual CLI invocation ceiling; an explicit smaller `maxInvocations` lowers that ceiling for planning and scheduling too. Provider retries and schema repairs count. Up to one quarter of the invocation allowance may be coordinator calls, and the runtime reserves a turn for independent review when admitting work. New runs default to 200 calls over 240 minutes, allowing up to 50 coordinator calls. At least four invocations are required for a new team. These are bounds on authorized invocations, not a claim that coordination will always be 25% of actual usage.\n\nWhen a run stops, edit **Maximum calls** and **Maximum minutes** before resuming, or choose **Use extended budget** for 200 calls and 240 minutes. Resume grants a fresh allocation, reuses completed results and worktrees, and retains lifetime usage. Saved runs keep their existing limits until you explicitly change them.\n\nThe team view reports lifetime invocations, coordination share, questions, blocked turns, and revisions. Provider-reported tokens and API-equivalent cost remain unknown where the provider does not report them; cost is not a subscription bill.\n\nLaunch reservations are recorded before starting a CLI. After a crash in the interval between that write and process launch, lifetime counts conservatively retain the reservation; the runtime cannot prove whether an external process started.\n\nAccepted decisions and outcomes use the existing fsynced replay journal. A failed authoritative write stops dependent dispatch. Recovery ignores an incomplete final journal record, removes that incomplete tail before the next append, and rejects malformed complete records. Event logs remain best-effort projections. SSE reconnects across server restarts reconcile an authoritative snapshot before continuing the stream.\n\nA blocked Auto run is **stopped**, with its work available and its workflow unfinished. **Resume** replays completed allocations, including their final review and repair steps, then grants a fresh bounded allocation. Interrupted allocations continue their recorded workflow. Lifetime usage remains visible across resumes; replayed calls do not spend again. A missing provider session falls back to a fresh session with explicit task context. As with other patterns, a CLI can perform work immediately before a crash, so recovery does not promise exactly-once external execution.\n\nDuring a run, **Revise goal** stops the old execution, retains its branches and supplied context, then opens a linked team plan for your changed requirements. It uses a new explicit budget and the configured approval checkpoint. Ordinary member notes leave the accepted requirement set intact. Source trust and lockdown follow both forms of continuation.\n\nAfter completion, **Whole team · new run** plans and verifies your follow-up against the result branch. The reply box exposes its new call/time allocation; direct follow-ups can use a one-call budget and also enforce an explicit launch ceiling including retries. A provider-addressed follow-up starts outside the team's private member sessions, then can continue its own conversation. A direct agent follow-up that changes the integrated result clears its previous team approval, even if its check passes. The run displays that it needs whole-team verification again.\n\n## Adapter capabilities\n\n| Capability in code-flow | Claude Code | Codex | Antigravity |\n|---|---|---|---|\n| Structured outcomes and session continuation | Supported | Supported | Structured outcomes; fresh turns with portable context |\n| Addressed questions at turn boundaries | Supported | Supported | Supported |\n| User notes during a running turn | Next turn | App-server steering; next-turn fallback with exec | Next turn |\n| Native permission requests | Existing allowlist policy | Declined | Protected file-only policy; no interactive approvals |\n| Agent-accessible collaboration tools | Scoped per-turn MCP and allowlist; `--strict-mcp-config`, so your own and the repository's MCP servers are not loaded in these turns | Scoped per-turn MCP, app-server and exec | Structured-outcome fallback |\n\nThe capabilities panel distinguishes tested, unsupported and unknown behavior using adapter protocol fixtures and installed CLI probes. Inventory probe results are kept for ten minutes; an unknown result (a CLI missing or not signed in, or a probe that timed out) is kept for two, while managed launch controls are checked afresh before each turn. Model behavior is measured separately. Native Claude and Codex agent-spawning tools are disabled for scoped team turns so code-flow retains ownership of the roster. Default Antigravity uses a protected file-tool allowlist and macOS process/filesystem restrictions; native delegation is excluded. The explicit unrestricted option does not enforce these restrictions. Its built-in local task bookkeeping tool can remain visible. Separate vendor-native token accounting remains unknown.\n\nThe live bridge binds a random loopback port and issues an expiring credential for one active member attempt. Credentials are stored in temporary files with mode 0600 and removed when the turn exits; they are absent from prompts, command-line arguments, run records and logs. The path of the credential file is on the command line, and every CLI on this machine runs as your user: a member that can run shell commands could read another member's credential while that turn is starting. The bridge client deletes the file as soon as it has read it, which narrows the window to process start; this is isolation between members and the application's own token, not between members. The engine derives identity and provenance. A read-only reviewer can communicate without receiving source-write permissions; sandbox/network restrictions are not relaxed to enable collaboration.\n\n## Implementation and evaluation\n\nThe implementation uses `src/teams.ts`, `src/patterns/auto-team-model.ts`, and `src/patterns/auto-team.ts`, with engine metadata and replay helpers. `src/memory.ts` applies versioned memory rules over `src/memory-sqlite.ts`; `src/team-memory.ts` applies run provenance and Git/project applicability. Structured memory uses SQLite with automatic JSON migration and evidence-linked graph retrieval. Node 22.13 or newer is required; no native npm addon is needed. See [Memory storage](memory-storage.md).\n\nTests use fake vendor CLIs and isolated repositories. They exercise plan validation/approval, same-provider identities, dependency waves, peer and user questions, restart/resume, member replacement, independent verification, storage failures, memory trust/revision/deletion, and the browser workflow. They demonstrate behavior, not model quality or subscription savings. `npm run evaluate:teams -- --fake` runs an isolated comparison without model calls. Use `--real --runs 1 --providers claude,codex --max-steps 12 --max-minutes 5 --out /absolute/output` for a bounded comparison through signed-in CLIs. Reports record mode, CLI versions, actual calls, coverage, coordinator overhead, peer waiting and journal-load size/time. Estimated avoidable barrier delay is labeled separately from observed waiting.\n\nInsights shows evaluations and the recorded rollout decision. Fake or unknown runs cannot justify beta/default promotion. Promotion beyond the experimental stage requires the documented evidence thresholds; default rollout requires a larger representative sample. See [implementation evidence](dynamic-teams-evaluation.md).\n\nSet `CODEFLOW_AUTO_TEAM_ENABLED=false` to disable creation of new Auto runs. Existing teams remain inspectable and resumable; manual patterns and saved automations retain their compatibility. The feature does not require a database or new Node baseline.\n\nThe evaluation runner also accepts `--register-home /absolute/codeflow-home` to copy immutable reports into a chosen installation for Insights. This is explicit: it does not copy transcripts or change rollout settings. Reports are also available in the isolated evaluation home's `team-evaluations/` folder.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n\n## Automatic worker models\n\nNew Auto runs discover candidates through the connected CLI: Claude's stream-control initialization, Codex app-server `model/list`, and Antigravity `agy models`. The coordinator keeps the provider, model and effort selected by the user, including existing CLI defaults. Workers and reviewers use automatic selection unless their Advanced pool/model/effort settings pin or restrict them. Global coordinator defaults do not silently pin workers.\n\nAdvanced → Automatic worker selection shows cached model IDs, exact effort support, stale/unknown states and refresh. Official-document research is opt-in. Local catalog previews do not establish remote/container availability: those environments discover their own models when a run starts. Antigravity worker discovery runs inside the same macOS executor with a separate catalog identity. Its model listings do not establish per-model inference or effort support. The coordinator sees the effective file and task-tool capabilities and rejects unsupported execution environments. Every launch rechecks the CLI version and creates a clean protected profile. See [permission verification](worker-permission-verification.md#existing-login-reuse-and-managed-file-workers).\n\nCatalogs live under `CODEFLOW_HOME/model-catalogs`, partitioned by provider and execution environment. Discovery has a ten-minute cache; failed refreshes retain previous evidence. `CODEFLOW_MODEL_NETWORK=offline` disables discovery and documentation fetches and uses cached entries. This switch does not disable provider inference or the run's separately configured tools. Documentation research uses official origins, bounded downloads and a closed schema, with a seven-day attempt cache. Web text cannot grant permissions or prove account access. Unknown context, pricing, capabilities and effort support remain unknown; an explicit empty effort list means no effort control.\n\nCLI-reported availability, advertised compatibility, documented facts and measured run observations are distinct. A listed model is a candidate, not a guarantee that a paid inference call will succeed. Exact accepted worker choices, rationale, environment and catalog revision/source are saved with the step; discovery revisions have immutable snapshots. Recovery reuses recorded choices and rechecks eligibility before another execution. Failed members can be replaced within existing revision/invocation limits using portable checkpoints; native sessions stay with their provider/member.\n\nThe first version uses existing routing and budget checks, not a learned performance ranker. Costs and token limits retain their existing provider-reporting limitations; unknown pricing cannot supply an exact prospective cost guarantee. Run observations are diagnostic evidence, not benchmark scores or authority to enable a model. Public documentation only augments CLI-reported IDs; it does not make a public-only model eligible.\n\nSee [the live permission verification report](worker-permission-verification.md) for tested versions, native-control fixes and remaining boundaries.\n\nIf an isolated Antigravity worker cannot use or refresh its native login, check or unlock the login in Antigravity's normal interactive session and select **Resume**. Code Flow verifies it again through isolated model discovery before clearing the signed-out cooldown. Cached models or offline discovery cannot establish login access. Recovery keeps the recorded assignment and portable work, then starts a fresh restricted worker; it never imports a native conversation or widens permissions to refresh credentials. Native authentication diagnostics are withheld from run history.\n\n### Unrestricted Antigravity with an existing login\n\nEnable **Advanced → Antigravity execution → Use unrestricted Antigravity with my existing login** for a new local attended run. This explicitly grants Antigravity full native access using the existing login and provider configuration. Shared Codeflow tools are added through a private temporary configuration overlay. It skips the isolated profile and vendor sandbox and uses the CLI's permission-skip flag. It requires a valid saved login; it does not provide anonymous access or guarantee that the provider will never require reauthentication.\n\nTerminal, browser, MCP, hooks and native agents follow the native provider configuration. Code Flow cannot enforce workspace boundaries or read-only access in this mode. Native agent usage is outside Code Flow's worker budget. The selection is visible in Advanced, run details, coordinator inventory and each step's permission receipt (`agy-native-v1`). Claude and Codex keep the ordinary run policy.\n\nThe option is rejected with mandatory permission requirements, Safe mode, narrowed turn trust, interactive tool approvals, remote/container workers or unattended/locked-down execution. Templates and model choices cannot enable it. A continuation inherits the parent's choice and may narrow it to isolated execution, but cannot upgrade an isolated parent. Existing saved runs stay isolated; there is no automatic fallback on authentication or sandbox errors.\n\nNative model discovery uses the existing profile, with a catalog identity distinct from isolated discovery. Both modes keep portable checkpoints and start fresh managed turns without native session reuse. Recovery after an authentication failure requires a fresh successful discovery in the recorded mode; cached/offline results cannot clear the failure. Private authentication diagnostics are checked for observed persistence failures and deleted without entering run history. Actual expired-token refresh remains unverified unless observed during normal provider operation.\n\n### Steering and recovery in a conversation\n\nKeep using the main composer while Team works. Name a worker, assignment, or owned file when correcting a specific part—for example, “For api.txt, use the revised wording.” Code Flow stops that assignment and invalidates dependent work while unrelated teammates continue. Its acknowledgement means the revised instructions reached the assignment, not that the change has passed review. Ambiguous corrections affect the whole graph. Corrections during planning or final verification safely restart the delegated execution with its preserved context and remaining budget.\n\nThe Browser panel has **Take control** and **Let agents continue**. Taking control waits for the current action to settle, then pauses queued agent browser calls. You and the agents keep the same tabs, cookies, uploads and downloads. Ordinary mouse/keyboard input briefly takes control and releases it afterward. Agents can release their browser lease through `browser_control`; idle leases expire after 30 seconds, and queued calls have a bounded 90-second wait. A new owner must inspect the current page before acting.\n\nTeam coordination, workers and review can continue with another eligible provider from the configured pool after a provider failure. The replacement receives the current assignment, files and recorded tool-action history in a fresh provider session. Model pins, tool permissions, independence requirements and the remaining budget still apply. An unavailable strict independent reviewer blocks completion when no eligible replacement exists. The advanced Auto workflow option `providerFailover: false` retains manual provider recovery. Provider changes are recorded under Team activity.\n\nIf your edits overlap a team result, open **Team activity → Resolve conflicts**. Compare the starting version, your version and the team version; keep either version or edit a text result. Non-overlapping edits merge automatically. Code Flow rejects stale comparisons and preserves your branch and staging area. Applying a resolution runs the configured checks, including verification-profile gates, against the combined workspace. This check does not replace independent review of newly edited text. Binary and symbolic-link conflicts require choosing an existing version; complex repository conflicts can still be resolved in an editor.\n","title":"Team"},{"group":"Work with agents","id":"project-teams","source":"docs/project-teams.md","body":"# Project missions (preview)\n\nEnable the project-mission preview, create a mission, and review its first proposal before accepting an implementation cycle. Keep budgets and publication decisions explicit.\n\n## Start with a bounded preview\n\nA project assignment connects an agent team and owner mission to one repository. Give it tasks directly or enable recurring discovery. Existing Automations supplies the clock, and existing Auto team runs do the investigation, implementation, checks and independent review. The team brings opportunities and verified drafts back to the owner. It never merges or publishes automatically.\n\n## Reusable agent teams and project assignments\n\nOpen **Project → Project missions → Reusable agent teams** and choose **Create agent team**. Give it a name, choose its coordinator and worker/reviewer providers, and set model preferences. Optional role settings let the coordinator and workers use different models or effort for the same provider. Saving creates a versioned definition without starting agents or granting access to a repository.\n\nChoose **Assign to this project**, select a repository and configure the mission, base branch, allowed paths, checks and budgets. Reusable-team assignments initially have **Run recurring discovery** turned off. Save the draft and activate it when ready. The same reusable team can be assigned to other projects with separate authority, memory and allowances. One non-archived assignment owns each underlying Git repository, including all its linked worktrees.\n\nExisting missions are already assignments. **Edit mission → Agent team** can attach a reusable definition to one without replacing its ID, cycles, decisions, results, launch receipts or budget ledger. **Save as reusable team** copies only provider/model preferences from an existing assignment; it does not copy mission instructions, private checkpoints, sessions, project memory or permissions.\n\nEditing a reusable team creates a new version. Assignments keep their pinned configuration until you choose a version in **Edit mission** or **Use team version …**. Each cycle and child run records its own snapshot; updates never silently change other assignments or running work. Adopting changed execution settings requires fresh verification for existing ready/accepted results and retains all prior history and charged usage.\n\n## Give an assigned team a task\n\nActivate the assignment, select **Give team a task**, and enter a title, instructions, acceptance criteria and an allowed category. A required verification check must be configured. The task starts an implementation Auto team immediately, skipping discovery. It uses the assignment's existing scope, permissions, implementation allocation and rolling weekly allowance. It works even with recurring discovery disabled, or when discovery is set to proposals only.\n\nTasks appear alongside opportunities with **Owner-assigned task** attribution. They do not claim independently discovered code evidence. Checks, independent review, exact-commit approval and publication controls are the same as for discovered opportunities. A task never resets usage or inherits a previous run's sessions. Finish or stop the active cycle before assigning another task. The task form retains the exact request after an ambiguous connection failure, so retrying cannot admit the same task twice.\n\nWorkers submit completed artifacts; Code Flow commits and integrates them, runs the configured checks and obtains independent review. Pending engine gates belong in the worker handover, not in unresolved implementation requirements. The final check can reuse passing evidence only for the same unchanged commit, checkout and verification profile. Plain documentation changes are identified from the actual diff: checks and independent review remain required, while executable fail-to-pass proof is marked not applicable. Code, configuration, executable files, binary changes and mixed diffs retain the proof requirement.\n\n## Create a mission\n\n1. Open **Project → More → Automation → Pursue a mission** and click **Enable project missions**. The choice applies immediately to every project on this host and is saved across restarts; no startup flag is required. Auto team must also be permitted by the host (`CODEFLOW_AUTO_TEAM_ENABLED` defaults to enabled).\n2. Open a project that explicitly contains the repository. Select **Create a mission**.\n3. Enter its mission, intended users, priorities, exclusions and named local base branch. Select a coordinator and worker/reviewer providers. Claude, Codex and Antigravity are eligible when installed; ACP and extension providers are currently excluded from unattended project missions.\n4. Choose **Build within scope** and supply a required check command, or choose proposals only. Set allowed paths, categories and change limits. Models and reasoning use the existing provider catalog.\n5. Review the schedule and budgets, save the draft, then **Activate**. Use **Preview next cycle** before **Run cycle now**.\n\n**Disable project missions** prevents new cycles on this host. Stop active or unresolved cycles first. Individual teams keep their activation state; enabling the feature again resumes schedules for teams already active. `CODEFLOW_PROJECT_TEAMS_ENABLED` remains an optional initial default until a UI choice is saved in `CODEFLOW_HOME/project-teams/settings.json`. Separate local and SSH hosts keep their own settings.\n\nThe default schedule is Monday at 09:00 in the browser's time zone. Daily, selected weekdays and intervals use the existing Automations schedule editor and DST behavior. Code Flow must be running for its local scheduler to work. Cycles pin the current local branch commit; they do not fetch remote changes.\n\nThe default scope permits the repository except `.codeflow` and `.github/workflows`, with eight changed files and 400 added/deleted lines. Credential paths and binary changes are excluded. File bounds enforce mechanical scope; a model's risk and benefit estimates remain judgments to review.\n\nDiscovery reserves eight calls and 15 minutes. Implementation reserves 16 calls and 45 minutes. The rolling seven-day allowance is 48 calls and 120 minutes. Each launch reserves its full limit before starting. An exact, durable terminal-run receipt replaces that reservation with observed calls and elapsed time rounded up to whole minutes, including cancelled, failed and stopped runs. Original allocation fields and historical mission budgets remain saved. Unknown or interrupted launches remain fully reserved even after seven days; missing evidence never becomes zero usage. Legacy runs can gain additive usage receipts without resetting their history. Increase budgets deliberately in the mission editor when the defaults do not fit your cadence.\n\n## What a cycle does\n\nDiscovery uses read-only Auto team workers, a separate structured report turn, then an independent reviewer. It returns at most three opportunities with source quotes, affected users, a proposed change, expected benefit, risk and acceptance criteria. Empty findings are a valid result. Discovery does not run setup, checks, proof writers or code repairs.\n\nThe host verifies quotes at the pinned Git commit, suppresses previously reported problems, and ranks eligible proposals deterministically. Automatic implementation requires allowed category/paths, low risk, sufficient confidence/alignment, and a required verification profile. Hypotheses require the explicit experiment category. A cycle can build only one proposal; reports with uncertain evidence remain proposals.\n\nImplementation uses a fresh ordinary Auto team within its reserved budget. File, path and line limits are checked before commits and publication. A result becomes **ready** only when required checks and the independent review refer to the final branch commit. Existing fail-to-pass proof settings still apply. Failures and stale results remain visible with a reason.\n\nThere is one active cycle per repository, at most two unresolved implementation results, and at most 12 open opportunities. Unchanged inputs skip agent work until the rescan interval (28 days by default). Relevant owner decisions, local activity, configuration or base changes permit new discovery. An unchanged failed automatic cycle needs an explicit owner retry or changed inputs.\n\n## Review and feedback\n\nProject → Automation → Pursue a mission contains the full opportunity cards, cycle history, selection reasons, budgets and links to existing run diffs/checks. Project Overview and the global Inbox contain compact links to actionable mission opportunities. Their approval and evidence requirements stay the same.\n\n- **Build proposal** allocates a bounded whole Auto team. High-risk, unsupported or stale work requires a revised mission or an ordinary owner-directed task.\n- **Request revision** records feedback and launches a fresh bounded whole team from the pinned base, with the previous result referenced as evidence. It does not silently continue a single writer or refresh the old run's allowance.\n- **Approve this commit** accepts the exact reviewed result. Open that run to use the existing PR or merge controls. Approval alone does not ship anything. A successful local merge is recorded as shipped; opening a PR is not counted as shipped.\n- **Defer one week** retains the idea and feedback; once due it may be rediscovered. **Reject** suppresses the problem in later discovery. Owners may explicitly reconsider a saved proposal.\n- **Pause** prevents new cycles and the next implementation stage. An already running discovery may finish into proposals. **Stop cycle** cancels owned work, retaining unresolved reservations. **Archive team** stops scheduling and retains its history.\n\nChanging execution policy makes previously ready/accepted results require new verification. Changing only the schedule, recurring-discovery toggle or assignment name preserves those results and can be done during a cycle. The current schedule is stored separately; historical mission and cycle snapshots retain their original timing. Full-mission schedule updates from older clients receive the same behavior. Moving a result branch after review invalidates readiness. Generic run resumes, selective reruns, writer follow-ups, automatic PR-fix watchers and triage revision shortcuts are blocked for managed runs; use the project mission's revision controls.\n\nProject mission runs can be archived individually or in a sidebar selection. They cannot be deleted: cycle recovery, budgets and exact-result approvals retain their run history. Ordinary runs still support individual and bulk deletion. The mission editor's searchable model picker includes the same free-model filter and badges as the composer; its selections update only that team's mission.\n\nExisting notification destinations receive actionable cycle outcomes using their saved event/project scope. Child completions and no-op cycles do not produce duplicate alerts. Destinations remain opt-in.\n\n## Recovery and storage\n\nTeam aggregates are private files under `$CODEFLOW_HOME/project-teams/`. Each mutation validates and fsyncs its snapshot before acknowledgment. A cycle claim and its full budget allocation are saved before starting a run; the initial run identity is fsynced and attached to the claim before any agent dispatch.\n\nRecovery adopts only a matching team/cycle/claim/input/attempt receipt. An ambiguous launch is marked **unknown** and is never automatically replayed. Inspect its run and host process state, then stop the cycle if abandoning it; stopping does not pretend unknown usage is zero. A storage error remains visible, and corrupt team files are preserved for repair. Duplicate repository ownership blocks both records.\n\nOn first load, legacy assignments gain a shared Git metadata identity. This additive upgrade preserves their IDs, immutable mission/cycle history, operation receipts and usage. Linked worktree aliases cannot acquire separate ownership. Replacing a repository's Git metadata blocks dispatch rather than silently adopting the replacement. Reusable definitions are separate `at-*.json` files in the same directory, with at most 100 definitions, 100 versions per definition and a 2 MiB per-definition ceiling; damaged definitions are preserved and reported without preventing unrelated existing missions from loading.\n\nAutomations stores only a managed target and a display projection. Mission settings remain authoritative in the team aggregate. Managed schedules cannot be edited through generic automation controls. Schedule occurrences use stable operation IDs, so replaying a due occurrence cannot launch a second cycle. The existing one-hour missed-run grace and DST rules apply.\n\nHistory is bounded to 100 cycles, 200 opportunities, 2,000 decisions and 2,000 operation receipts, with a 16 MiB aggregate ceiling. At the limit, archive the team and retain its file before creating a replacement. History is not silently pruned; rejection feedback is never silently forgotten within a team.\n\nBackups include team evidence and missions. Restore pauses teams, cancels active cycle admission while retaining unknown reservations, and invalidates ready/accepted results. Inspect repository membership, providers and retained receipts before reactivation. ACP executable settings and other existing credential exclusions remain excluded from portable backups.\n\nOwner APIs, managed child runs and the project-scoped event topic are unavailable to shared accounts, because the child tasks include private owner mission and decision context. Custom filesystem restrictions block the feature alongside other host execution/private-data surfaces. Removing project membership stops owned work. There is no cloud scheduler or new agent runtime.\n\n## API\n\nAll mutations use a client-generated `operationId`. Team edits and opportunity decisions require the current `revision`; opportunity decisions also require `opportunityRevision`. A replay with the same operation and content is idempotent; changing its content or acting on a stale revision returns a conflict.\n\n| Method and path | Purpose |\n|---|---|\n| `GET /api/project-teams?project=ID` | Scoped assignments, reusable team summaries, storage faults and provider eligibility |\n| `GET /api/project-teams/definitions` | Reusable team summaries and definition storage faults |\n| `GET /api/project-teams/definitions/:id` | One definition and its retained versions |\n| `POST /api/project-teams/definitions` | Create from name/configuration, or name/sourceTeamId |\n| `POST /api/project-teams/definitions/:id` | Save a new version with revision, name and configuration |\n| `POST /api/project-teams` | Create a draft with projectId, repo, name, mission, optional definition `{id, revision}` and scheduleEnabled |\n| `GET /api/project-teams/:id` | Aggregate and remaining allowance |\n| `POST /api/project-teams/:id` | Update mission/name/status, schedule/scheduleEnabled or adopt a definition with revision; definition `null` detaches the reusable reference while keeping configuration |\n| `POST /api/project-teams/:id/tasks` | Admit an owner task with revision, title, instructions, acceptance array and optional category |\n| `GET /api/project-teams/:id/preview` | Readiness and schedule preview without model calls |\n| `POST /api/project-teams/:id/cycle` | Explicit cycle admission |\n| `POST /api/project-teams/:id/stop` | Stop owned cycle with revision |\n| `POST /api/project-teams/:id/opportunities/:opportunityId` | approve, build, revise, reject or defer |\n\nThe existing stream broker multiplexes `project-teams:<projectId>`. Generic run bodies cannot supply managed metadata or launch callbacks.\n\n## Validation and rollout\n\n`npm run evaluate:project-teams -- --fake --cycles 10 --out /tmp/my-standing-pilot` exercises two isolated fixture repositories and saves a report plus private run evidence. The default is synthetic. `--real --confirm` explicitly opts into signed-in CLI use, capped at 24 invocations and 60 allocated minutes per cycle. Use `--help` first. Neither mode merges or pushes.\n\nTests cover the full cycle, exact-commit approval, scope violations, failed checks, provider exclusions, budget limits, duplicate admission, durable receipt failure, restart adoption/ambiguity, cancellation, membership removal, feedback retention, storage corruption, restored authority, notifications, owner-only APIs, filesystem restrictions and shared browser transport.\n\nThe initial synthetic ten-cycle pilot produced two ready drafts and eight no-ops across two fixture repositories. It establishes mechanics only. Real-provider quality, novelty, usefulness and owner acceptance have not been measured; keep the flag opt-in until that separate pilot is assessed.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Project missions (preview)"},{"group":"Work with agents","id":"review-inbox","source":"docs/review-inbox.md","body":"# Review inbox\n\nOpen Inbox from the sidebar. Filter to a project or attention type, then open an item to answer its question or inspect its result. Sorting the inbox does not approve or publish work.\n\n## Work through attention items\n\nThe **Inbox** sidebar destination groups attention for manual and unattended runs.\nQuestions and approvals, blocked teams, unread failed/interrupted runs and pending\nautomation results appear there. Archived and handled items are excluded.\n\nRun priority comes first, followed by attention type and then the oldest waiting\nitem. Group by project, repository or attention type, or use one list. Search task\ntext, run IDs, repository paths, source titles and trigger names. Project scope\ncontinues to apply; the page reports attention items outside the current scope.\n\nOpen the run to answer questions, inspect failures or review evidence. Tool\npermissions remain in their scoped approval inbox. No result is merged or approved\nby sorting, filtering or opening this page. Grouping is a presentation choice and\ndoes not change dispatch order; the explicit run priority controls dispatch too.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Review inbox"},{"group":"Work with agents","id":"verification","source":"docs/verification.md","body":"# Verification and review\n\nOpen Project → Repository tools → Verification to inspect the repository’s checks. Select the profile in the composer, then review each required gate and criterion mapping on the final result.\n\n## Verify a result\n\nIn the composer, select a verification profile instead of the single legacy check. Lightweight, standard and thorough presets detect existing repository commands. In **Project → Repository tools → Verification**, review/edit the detected commands, add bounded custom gates, map acceptance criteria to checks, and run them at a pinned commit. A baseline comparison runs the same profile on both commits; repeated attempts distinguish consistent failures from flaky results. Export the report or profile for reuse through the API.\n\nEvery required gate must pass. Skipped gates, mixed results, missing criterion mappings and failing mapped checks cannot produce `verified`. A criterion mapping is your assertion that the chosen checks exercise its behavior; passing a command is not a semantic proof of an arbitrary requirement. Finished work without independent verification is labeled accordingly.\n\nThorough detection recognizes `test:mutation`/`mutation`, `test:visual`, and `test:a11y`/`test:accessibility` scripts when present. These are explicit mutation, visual and accessibility gates. The repository supplies its testing tools, mutation thresholds, image baselines, target URLs and accessibility rules. code-flow executes the commands and retains their exit/output evidence; it does not install those tools or invent screenshot baselines. Set optional gates to required when their failure must block verification.\n\nProfiles support at most 12 checks, 48 criteria, 30 minutes per check, three repeats and a one-hour suite deadline. Setup has a separate two-minute bound. Profile checks must preserve tracked files, the index and HEAD; changing tested code or hiding files with Git index flags blocks the result before another gate can run. Ignored or untracked build output is allowed. Temporary worktrees are removed on failure and cancellation.\n\nThe **Changes** side tab records human review of individual files at an exact commit, exposes heuristic change-risk reasons, and records time spent correcting a result. Review coverage becomes stale when the branch moves. Agent activity is not counted as human rework, and a clean test run does not mark files reviewed.\n\nThe result's merge panel can **Validate merge result** against the current local target branch. This constructs the combined tree in a disposable checkout and runs the configured setup/profile. Requiring validation pins source, target and merged tree; moving refs, rewriting checks or a different merge result invalidates it. Commit-hook changes are detected, with rollback of the application's own clean merge when possible. GitHub PR merges still use the repository's remote checks and policies; this local validation does not replace a remote merge queue.\n\nChecks execute repository code. Use a check container or explicitly trust host execution, especially with imported source text. Agent isolation and check isolation are separate settings. See [security](security.md), [agent containers](agent-containers.md), and the [API contract](api.md).\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Verification and review"},{"group":"Work with agents","id":"github","source":"docs/github.md","body":"# GitHub, review and shipping\n\nReview the result diff and its check evidence. Use the run’s publishing controls to merge locally or open a pull request; confirm the repository and target branch before applying the action.\n\n## Review and ship\n\n**Review changes** opens a workspace containing the diff, verification evidence, commit-scoped human review coverage and review comments. Mark files reviewed in the file pane. Expand **Publish changes** to use the existing merge, PR and validation controls without leaving review. **Refresh changes** reloads the diff and coverage after an external branch update; coverage is reset when the commit changes.\n\n\nWhen a run finishes, each result branch has **Review changes**, **Merge…** and **Open PR…** buttons.\n\n- **Review changes** opens a per-file diff. Click any line to comment on it, and add an overall comment if you like.\n - **Send N comments to [agent]** queues a *follow-up* in the same run: that agent continues its own session in the worktree, addresses the comments, and code-flow commits and re-runs the check.\n - Follow-ups are recorded on the run, so they survive restarts. The result header and summary reflect the latest follow-up.\n - Draft comments are kept if you navigate away.\n- **Merge…** merges the result into one of your branches, and only when that's safe:\n - If the target branch is checked out (e.g. `main` in your checkout), the merge happens there, and only when it has no uncommitted changes to tracked files (staged or not).\n - If something else stops it — untracked files the merge would overwrite, another git holding the repository's lock, a failing hook, git not knowing your name and email — you're told which (and which files), and your checkout is put back as it was.\n - That merge runs your hooks, with up to 30 minutes for them (`CODEFLOW_MERGE_TIMEOUT_SEC`), so a post-merge `npm install` or a Git LFS download has time. If git is stopped after it made the merge commit, the merge is done, and you're warned the hook may not have finished.\n - If the target branch isn't checked out anywhere, code-flow builds the merge with `git merge-tree` and moves the branch, so no checkout is touched. That needs git 2.38 or later; with an older git, check the branch out and merge there.\n - A branch that's being rebased or bisected in one of your worktrees isn't merged into: finishing or aborting the rebase would undo the merge. You're told where, and what to run there first.\n - On a real conflict you see the files and can **ask an agent to resolve them**: code-flow merges the target into the result branch and the agent fixes the markers. Then merge again.\n - By default the run is archived after merging (worktrees removed, branches kept).\n - Merging a branch that's already in the target says *Nothing to merge* instead of recording a second merge.\n- **Merge PR…** takes the place of **Merge…** once the branch has a pull request, and merges that pull request on GitHub (`gh pr merge`):\n - Pick how, from the ways the repository allows: a merge commit, squash, or rebase. You confirm before anything happens. The panel shows the PR's CI, and says so if GitHub reports a conflict.\n - It merges only while the pull request is exactly the branch you reviewed here. If the branch has commits the PR doesn't have yet (a follow-up that wasn't pushed), or someone pushed to the PR, you're told which and nothing is merged; GitHub also refuses if the PR moves in the meantime.\n - A draft or closed PR isn't merged; one already merged on GitHub is recorded as merged. No branch is deleted, and `origin/<base>` is fetched afterwards.\n - With a merge queue, or rules that hold the merge until required checks pass, GitHub accepts it and merges later.\n - **Merge into a local branch instead…** still offers the local merge above.\n- **Open PR…** pushes the branch to `origin` and opens a pull request with `gh`, using the task and summary as the description (cut to fit GitHub's 65,536-character limit). If the push works but `gh` then fails, or a pull request for the branch is already open, code-flow finds that pull request and records it, so trying again doesn't fail with \"already exists\".\n- **Archive…** removes a run's worktrees (optionally its branches too). Kept branches can still be merged or resumed later. A worktree git won't remove (a locked one, one with submodules) is tried again with `--force` twice; whatever still can't be removed is reported rather than left behind silently.\n- **Try it:** each result also has **Terminal**, **Editor** and **Folder** buttons and **▶ Run app**; see [Configuration](configuration.md#per-repo-config).\n\n## Start from where the work lives\n\nRuns can start from GitHub instead of a blank task box, using GitHub CLI (`gh`) with your own login.\n\n### Templates\n\n**Start from** above the task box sets up common jobs:\n\n| Template | Pattern | Starts from |\n|---|---|---|\n| **Fix an issue** | Plan → Build → Review | an issue; the PR you open later says `Closes #N` |\n| **Fix failing CI** | Plan → Build → Review | a PR, with its failing checks and their job logs (`gh run view --log-failed`) given to the agents |\n| **Review a PR as a council** | Review council | a PR |\n| **Upgrade a dependency** | Plan → Build → Review | the task box (replace `PACKAGE`) |\n\nThe task names the issue or PR by number. Its title isn't put in the task (whoever opened it wrote it, and agents read the task as instructions); it reaches them with the issue's or PR's text.\n\n### Sweeps: many at once\n\n**Sweep** under *Start from* lists a whole kind of work, with a checkbox per item. The ones you pick start **one run each**, with the team, check and settings you set up in the composer. Each result waits in the [inbox](automations.md#the-inbox) for you to approve, revise or reject, and nothing is merged automatically.\n\n| Sweep | Lists | Picked by default | Each run |\n|---|---|---|---|\n| **Fix open issues** | open issues | all, except ones labelled *question*, *wontfix*, *duplicate*, *invalid*… | fixes the issue and adds a test; **Approve & open PR** says `Closes #N` |\n| **Fix Dependabot PRs** | Dependabot's open PRs (`gh pr list --app dependabot`), with their CI | only the ones whose CI fails; green ones say *ready for you to merge* | gets the failing checks and their logs, adapts the code to the new version, with the PR's text and logs [fenced as untrusted](#untrusted-text); **Approve & push to PR #N** pushes the fix to Dependabot's branch (never forced) |\n| **Fix security alerts** | Dependabot alerts, code scanning alerts and leaked secrets | high and medium alerts that have a fix | Dependabot alerts: one run per package, upgrading past every alert on it. Code scanning: one run per alert, fixing the cause rather than suppressing it |\n\n- An item that already has a run (running, or finished and not rejected) starts unpicked and links to that run, so sweeping again doesn't redo work. For a package's Dependabot alerts that means a run covering every one of them: a new alert on the package is new work.\n- Lists read every page GitHub has, up to 1,000 items of each kind (200 Dependabot PRs), and say so when there were more.\n- Dependabot alerts that an open Dependabot PR already upgrades start unpicked, and so do alerts with no patched version yet (that run would have to mitigate instead).\n- **Leaked secrets are never handed to an agent.** They're listed so you can revoke and rotate them yourself.\n- At most 25 runs start per sweep; agents and checks still queue behind `CODEFLOW_MAX_AGENTS` and `CODEFLOW_MAX_CHECKS`. A run that can't start (an untrusted issue without Docker, say) is reported, and the rest go on.\n- *Also check every day at…* saves the sweep as an [automation](automations.md#sweeps) that starts runs for new items.\n- Reading security alerts needs the `security_events` scope for private repositories: `gh auth refresh -s security_events` (and admin or security manager access). The sweep says so if it's missing, or if a feature isn't turned on for the repo.\n\n### Issues and pull requests\n\n**GitHub** in the task box starts from any open issue or PR:\n\n- An **issue's** title and description go to every agent with the task.\n- A **PR's** branch is fetched (`pull/N/head`, so forks work too) and becomes the run's base. Its description goes to the agents, and its changes are compared against the PR's target branch.\n- When a run came from a PR, a result has **Push to PR #N**. It pushes to the PR's branch and is never forced, so it fails if someone else pushed in the meantime. You can't push to a PR from a fork; you can still review it.\n\n### Review council\n\nA read-only pattern: several agents review the same changes independently, and a chair merges their findings.\n\n- Reviewers are anonymous to the chair (\"Reviewer A/B\"). code-flow runs the tests reviewers show their bugs with before the chair merges, and findings rank by severity, then by whether a test confirms them, then by how many reviewers agree. The verdict follows the findings, not the chair: changes are requested when a finding blocks (see [checked review findings](patterns.md#checks-loops-and-limits)).\n- Nothing is changed. **Post to PR #N** posts the review as a comment, and only when you click it.\n\n### Follow-through\n\n**Watch PR** works on a result that has a PR. code-flow checks it every minute. A PR that stays green with no new comments is checked less often, up to every 10 minutes, and every minute again once something changes:\n\n- **Failing CI:** the failed jobs' logs go to the agent you picked, which fixes the cause. code-flow commits and pushes to the PR.\n- **New comments** from you (the account `gh` is signed in as), or from people you list under *Also act on comments from*, go to the agent in the same way. Nobody reviews these fixes before they're pushed, so anyone else's comments are logged and ignored, even an org member's. Comments that were there before you started watching are never acted on.\n - PR comments and a review's summary are read. Line comments on the diff aren't read yet; the run's log says so when watching starts.\n- These unattended fixes never run with more than *standard* permissions, and a fix that touches the tests at all isn't pushed: a deleted, skipped or weakened test, a changed test config or CI workflow, the runner's settings in `package.json` or `pyproject.toml`, or fewer tests running than before the fix. It's handed back to you instead.\n- **Stops:** after the number of fixes you allow, if someone else pushed to the PR, or when the PR is merged or closed. It then shows under *Needs you* with a notification.\n- **Can't check:** when checking the PR fails (`gh` signed out, rate-limited, offline), code-flow waits longer before each retry. After three failures in a row the watch shows under *Needs you* with the reason, e.g. *gh isn't signed in — run `gh auth login`* (it asks `gh auth status` to tell). It keeps retrying, and goes back to watching once `gh` answers again.\n- It **never merges**.\n\n### Untrusted text\n\nText from outside the repo is untrusted. Issue and PR titles and descriptions, comments and CI logs can carry instructions aimed at the agents. code-flow asks GitHub who wrote an issue or PR. Unless it's the repo's owner, a member or a collaborator, and not a fork, the run is locked down (🔒 in the run view):\n\n- agents run in Safe mode;\n- files like `.env` aren't copied in;\n- checks run in Docker without network.\n\n**Dependabot's PR text is untrusted too.** GitHub opens the PRs, but their descriptions quote the new version's release notes and commit messages word for word, and their CI logs print the new package's output. That text is fenced as data. The run itself isn't locked down: the branch is this repository's and installs what merging it would, so setup can install the new version (with network) and the check can verify it. Pushing a fix to Dependabot's branch still works.\n\n**A code scanning alert's message is fenced too.** It quotes the code it flags (names, strings), which whoever wrote that code chose. The rule's own description and help aren't fenced.\n\nWithout Docker, code-flow asks before running that person's tests on your machine; automations never do. **▶ Run app** on a locked-down run asks first too: the app is that code, running on your machine outside any container. The text itself — title, branch name and description — is quoted in a fence it can't break out of, marked as information rather than instructions. Conventions files are read from where the PR started, not from the PR.\n\nTo run these jobs on a schedule or from webhooks, see [Automations](automations.md).\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"GitHub, review and shipping"},{"group":"Work with agents","id":"github-review-threads","source":"docs/github-review-threads.md","body":"# GitHub review threads\n\nInspect the actual GitHub review thread and its current revision before replying or changing its resolved state. Refresh a stale thread before retrying an action.\n\n## Work through PR feedback\n\nRuns started from a GitHub pull request and result branches with an opened pull request show a **GitHub review threads** disclosure. It reads actual GraphQL thread IDs, diff sides, line ranges, original locations, outdated flags, resolution state, authors and replies. This complements conversation comments and review summaries; a REST root-comment ID is not used as a resolution thread ID.\n\nGitHub discussion is untrusted reference material. Comments are rendered as plain text, credential-like values are redacted and shortened bodies are labeled. Reading or resolving a thread does not send its content to an agent, approve a pull request or mark local checks and requirements complete.\n\nThe owner can explicitly press **Resolve thread on GitHub** or **Reopen thread on GitHub** when the current GitHub login has permission. There is no automatic resolution based on agent output, check results or a local review. A server-issued snapshot pins the repository, pull request node ID and current head, together with the thread location, resolution, permission state and all loaded comments and updates. Before a mutation, code-flow re-reads that exact thread and PR, and rejects changed heads, edited comments, new replies, changed locations or resolution state. Refresh and review the current discussion before acting again.\n\nGitHub’s resolve/unresolve mutation does not accept an expected head or thread revision. Therefore a remote edit can race between the final comparison and the mutation. Code-flow rechecks afterward and reports a warning if the current head/state cannot be confirmed. It does not claim that this is an atomic GitHub compare-and-swap. A dropped mutation response is explicitly ambiguous, old snapshots are invalidated, and code-flow never retries the mutation automatically. Inspect the fresh state before another action.\n\nListings use four explicit pages of 25 threads. Each initially shows up to ten comments. **Load complete thread** reads at most four pages of 25 comments; threads with more than 100 comments remain read-only here and must be handled on GitHub. Thread and comment truncation are visible. Displayed bodies are capped at 8,000 characters per comment and 2 MiB of characters for the list. Each GraphQL subprocess has a ten-second deadline and an 8 MiB response bound. Snapshots expire after 20 minutes and at most 200 are kept; restart requires a refresh.\n\nThe application uses the owner’s installed `gh` login against the explicit hostname from the run’s stored HTTPS PR URL. Arbitrary client URLs and unknown result branches are rejected. GraphQL bodies go to stdin, not a shell command, and pagination is bounded by the application rather than `gh --paginate`. The CLI owns its authentication and HTTP redirect policy; this feature does not read or export GitHub tokens or claim a separate application-enforced no-redirect transport. See the official [GitHub CLI API manual](https://cli.github.com/manual/gh_api), [review thread fields](https://docs.github.com/en/graphql/reference/pulls#pullrequestreviewthread) and [resolution mutation inputs](https://docs.github.com/en/graphql/reference/pulls#resolvereviewthreadinput).\n\n## API\n\n`GET /api/runs/:id/pr-threads?branch=<owned-result-branch>` returns a snapshot with `snapshotId`, `prId`, `head`, `state`, `threads`, truncation flags and `expiresAt`. Without a branch, the run’s source PR is used, or its sole stored result PR. An unknown branch or missing PR returns 400. No caller-supplied PR URL is used.\n\n`POST` on the same route with `{action:\"load\",snapshotId,threadId}` returns the bounded complete thread and its updated `revision`.\n\n`POST` with `{action:\"resolve\",snapshotId,threadId,expectedHead,expectedRevision,resolved:true|false}` performs one explicit resolution action. A stale/foreign/expired snapshot, changed PR/thread or incomplete comments returns 409; missing GitHub permission returns 403; malformed requests return 400. A failed or ambiguous remote mutation returns 502 with instructions to refresh. A confirmed action returns `{applied:true,refreshRequired:true,thread?,warning?}`. Any old snapshot for that PR is invalidated before mutation.\n\nAutomated tests use injected GraphQL and a fake `gh` process only. Browser acceptance covers stale-reply rejection, refresh, resolve and reopen, forged snapshot rejection and separation from local verification. No live GitHub mutations were performed during development.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"GitHub review threads"},{"group":"Work with agents","id":"task-reruns","source":"docs/task-reruns.md","body":"# Selective task reruns\n\nOpen an idle Auto-team result, select tasks to rerun and inspect the dependency preview. Allocate a fresh budget; the new child run must verify its final result independently.\n\n## Repeat only the work that changed\n\nOpen an idle Auto-team result and expand **Rerun selected tasks**. Select the accepted tasks to execute again, optionally replace instructions for those selected tasks, review the dependency preview, and allocate a fresh call/time budget. Every transitive dependent task is invalidated, including completion, artifact and knowledge dependencies. Unselected tasks with unfinished or stale work must also be selected; their old evidence cannot be silently retained.\n\nThe action creates a child run at the exact integration commit shown in the form. It does not edit, resume, or change the original run. Uncommitted worker changes are excluded and remain in the original checkouts. Failed, stopped and interrupted results can be rerun when they have an accepted plan and owned integration checkpoint. A baseline without passing parent verification is explicitly marked unverified. If the integration branch moves after the preview, refresh and review its new commit before starting.\n\nThe child executes the pinned accepted plan directly; it does not ask a coordinator to create a replacement plan. Stable task/member IDs, requirements, dependencies, acceptance, ownership paths, contracts, provider pool and model/effort settings remain fixed. Selected tasks and every dependent start pending, without prior attempts, heads, artifacts, handovers, sessions, private checkpoints or verification evidence. Other completed tasks are labeled **retained baseline inputs**, with their source run, source task commit and integration baseline. Their commits must be ancestors of that baseline. Retained public artifact references and handovers are source data, not new instructions or authorization.\n\nAll checks, reviews, fail-to-pass claims, human coverage, messages, context manifests and execution metrics start independently in the child. Every original requirement receives a fresh final independent review. A retained task count is not a new execution count; the evaluation records retained tasks and newly executed tasks separately. If the source member was retired or its pinned provider/model is no longer eligible, the rerun fails explicitly instead of silently changing the team or permissions.\n\nAn implementation rerun can legitimately produce no new diff when the accepted behavior already exists. This is measured by Git, not accepted from an agent's assertion. Such a result still requires a passing check on the child commit and independent approval of its acceptance. An entirely unchanged result does not fabricate fail-to-pass evidence: it records `proofNotRequired: \"unchanged-rerun\"` after the fresh check and review. Any new code diff, including a repair, retains the normal proof policy.\n\nPlan approval follows the source's setting. A rerun's held plan cannot be replaced through the plan editor or a coordinator revision request; reject it and create another selection to change its task set or instructions. Runtime questions and bounded repairs continue through normal Auto-team controls. An internal, checksummed seed is journaled before execution, so restart/resume restores the same selected work and pinned baseline projection. The checksum detects changed replay data; it is not an authorization token.\n\n`POST /api/runs/:id/rerun-tasks` accepts:\n\n```json\n{\n \"expectedHead\": \"EXACT_CURRENT_INTEGRATION_COMMIT_SHA\",\n \"taskIds\": [\"implement-api\"],\n \"instructions\": {\"implement-api\": \"Recheck the error path and its acceptance tests.\"},\n \"budget\": {\"maxSteps\": 12, \"maxInvocations\": 12, \"maxMinutes\": 20}\n}\n```\n\nIt returns `201 {\"id\":\"CHILD_RUN_ID\"}`. Requests need 1–24 distinct known tasks and an explicit fresh allocation of 4–200 calls and 1–720 minutes. The effective call budget must fund the invalidated tasks, independent review and one recovery allowance. Unknown fields, forged seeds and instructions for unselected tasks are rejected. Stale source state, unavailable pinned roles, or overlapping source operations return 409. Concurrent identical creation requests share one child while pending; after that creation finishes, a repeated request deliberately creates another child. A dropped response should therefore be reconciled by inspecting run history before retrying.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Selective task reruns"},{"group":"Work with agents","id":"deferred-runs","source":"docs/deferred-runs.md","body":"# Deferred runs\n\nConfigure a task in the composer and choose a time, provider capacity, or both as its start condition.\n\n## Queue a one-time task\n\nInspect and manage the queue in **Scheduling → Run once later**. Save a new deferred request from the composer after configuring its task.\n\nDeferred runs save one explicit run request for later execution. They are separate from recurring automations. A job can start at or after a chosen local time, when selected providers have capacity, or after both conditions are satisfied. The application must be running; closing it preserves the queue for the next launch.\n\nThe queue uses the application's reported provider windows and current availability. A known provider reset is a waiting condition. An unknown reset is periodically checked; it is not presented as a promised reset time, and the queue never makes a billable inference call to probe quota. Provider selection and source references are validated again when the job launches.\n\nDeferred requests use Safe mode, disable native tool and plan approval waits, and suppress ignored-file copying. Uploaded attachments cannot be saved in the queue. Repository context paths remain references to the repository and are revalidated on launch. GitHub sources are stored as issue, PR or alert references. External sources are stored as configured integration/kind/key references and fetched again; imported text and client-supplied authority are never persisted as trusted execution context. Missing parent runs must fail validation, rather than silently losing inherited source restrictions. Deferred runs never grant permission to run untrusted source checks on the host.\n\nEach launch claim is written atomically and synced before the launch callback executes. Local queue instances share a transaction lock; the application server's exclusive ownership lock is required across processes. Only one automatic claim can win for a queued revision. The launch callback must persist and sync the created run's initial snapshot before returning its run ID, and include the job ID, claim ID and attempt number in that snapshot.\n\nA crash, timeout or unexpected launch error leaves an **unknown** outcome, because a run may already exist. Unknown launches never retry automatically. The recovery UI displays the exact claim and lets you link a matching run, cancel queue recovery, or explicitly acknowledge that another attempt may duplicate work. The server must verify the linked run's persisted claim and repository/project scope. Late receipts are retained in bounded history, including receipts for older attempts. A known rejection before launch side effects is distinct from an unknown outcome and can be explicitly retried after correction.\n\nCancellation before claiming prevents execution. Cancellation during launch aborts the callback and may produce an unknown outcome; if a late receipt identifies a run, the configured cancellation callback receives it. Cancelling unknown queue recovery does not imply that an unknown existing run stopped. Started runs are stopped through the normal run controls.\n\nLimits are explicit: at most 100 active/unknown jobs, 200 retained jobs, 30 history events per job, three user-requested launch attempts, 96 KiB per request, and a 32 MiB queue file. Old terminal job records are removed first. No run is retried solely because the server restarted. Corrupt or unsupported queue files fail closed and are preserved for repair. Configuration and history are saved in `CODEFLOW_HOME/deferred-runs.json` with owner-only permissions and atomic synced replacement.\n\nThe saved request includes the exact task and command text you entered. Reference environment variable names for credentials; inline credential values would become part of that local configuration. Provider authentication files and environment values are never automatically captured. Diagnostic history is redacted, while executable configuration is preserved so redaction cannot silently change a command's behavior.\n\nThe reusable service is `DeferredRuns` in `src/deferred-runs.ts`; `ready`, `launch` and optional `cancelRun` callbacks connect it to the application. Call `load()` once, reconcile unknown claims against durable run snapshots, then `start()`. Stop the queue before shutting down the engine. Tests use fake launch callbacks and provider windows, without real provider credentials or quota.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Deferred runs"},{"group":"Your workspace","id":"configuration","source":"docs/configuration.md","body":"# Configuration\n\nUse Settings for application preferences and connections. Open a project’s Repository tools for verification, history and services. Use `.codeflow/config.json` for committed repository defaults.\n\n## Application settings\n\nOpen **Settings** at the bottom of the sidebar, press **⌘, / Ctrl+,**, or search for a settings category in the command palette. Categories have shareable addresses such as `#/settings/ssh`; browser Back and Forward preserve navigation. Search settings by a feature name such as “theme”, “permissions”, “VM”, or “Tailscale”.\n\n- **General:** light, dark, or system appearance; sidebar grouping; archived runs; keyboard shortcuts. Terminals follow the selected appearance.\n- **Agents & models:** provider health and usage, model and effort defaults. Sign in through the provider CLI on the current machine.\n- **Run defaults:** workflow, plan approval, agent permissions and run limits. These update the composer’s remembered choices and the current draft; running work is unchanged. Project defaults and per-role choices can override model preferences.\n- **Notifications:** browser notifications and outbound notification destinations.\n- **SSH connections:** save and open remote VMs, with optional port and identity-file overrides. See [SSH projects](ssh-projects.md).\n- **Remote access:** Tailscale, Wi-Fi, pairing and device access to the local workspace.\n- **Team:** connect this computer to a team server as a runner and map repository keys to local clones; on the team server, define each project's repository keys and the owner's display name, read the `https://` address teammates connect to and revoke runners. See [Team collaboration](team-collaboration.md).\n- **Integrations, Projects & access, Extensions, Data & storage:** existing source/code-host/worker integrations, project conventions, filesystem policies, shared accounts, extensions and retention controls.\n- **Advanced:** collect/export diagnostics and find the server configuration reference. Process-level settings and credentials remain environment configuration and require a restart.\n\nAppearance, run defaults and sidebar preferences save in this browser, separately for each SSH workspace. SSH definitions and the team link with its repository mappings save on the local server; project, access, notification and retention settings save on the machine hosting that workspace; team board tasks, checkpoints and runner registrations save on the team server the link points to. Controls with a **Save** action keep their existing explicit save behavior. Numeric run limits save when you leave the field; invalid values restore the previous setting.\n\n**Project → Repository tools** contains verification, code editing, dependency and context inspection, checkpoints, services, coordinated changes and filtered tasks. **Inbox** and **Automations** are sidebar destinations. Open **Insights** and the workspace from the application menu or command palette. Older Settings URLs redirect to the current destinations.\n\n## Per-repo config\n\nCommit `.codeflow/config.json` and every run in that repo (and every teammate) gets the same defaults. Anything you set in the UI wins.\n\n```json\n{\n \"check\": \"npm test\",\n \"setup\": \"npm ci\",\n \"copy\": [\".env\", \".env.local\", \"config/*.local.json\"],\n \"run\": { \"command\": \"npm run dev\", \"port\": 3000 },\n \"models\": { \"claude\": { \"model\": \"opus\", \"effort\": \"high\" } },\n \"limits\": { \"maxMinutes\": 90, \"maxSteps\": 60 },\n \"trust\": \"standard\",\n \"image\": \"node:22\",\n \"learn\": true,\n \"prove\": true\n}\n```\n\n- `check` and `setup`: the composer suggests both from the repo (see [Checks](patterns.md#checks-loops-and-limits)), but set them here when the guess is wrong. A uv workspace whose tests live in their own member might use:\n\n ```json\n { \"setup\": \"uv sync --all-packages\", \"check\": \"cd tests && uv run --no-sync pytest -q\" }\n ```\n\n Setup runs once per worktree outside the agents' sandboxes, so the check can run without network (`--no-sync`). When a run's check can't run at all (not installed, nothing collected, `conftest.py` can't import the project), the run says so and asks you to set these.\n- `copy` brings files git doesn't carry (like `.env`) from your checkout into every new worktree. Only regular files that git ignores are copied. Symlinks are refused, since a cloned repo's `.env` could point at your credentials, and so are files git would commit. Patterns can't reach outside the repo, and nothing is copied through a symlink in the worktree.\n - Copied files are never committed, even when an agent un-ignores one or runs `git add -f`. Neither are new files named like secrets (`.env`, `.env.*` other than `.env.example`, `.env.sample` and `.env.template`, `*.pem`, `id_rsa*`, `id_ed25519*`). They stay in the worktree, and a warning names what was left out. Commit such a file yourself if it belongs in the repo.\n- `image` is the Docker image for running checks in isolation (automations, untrusted sources). `learn` passes recurring review problems on to later runs. `prove` checks that each passing result has a test that fails without it ([fail-to-pass](patterns.md#checks-loops-and-limits)); set it to `false` when the check is too slow to run twice.\n- A result's worktree that was removed (archived) is set up again when you follow up on it: files copied, setup run, context restored, and the files you sent with the follow-up added. If its branch was deleted, that follow-up fails with a clear message; the run itself stays finished. A follow-up also fails, rather than delete anything, when the worktree's folder is there but holds something else: move that folder away, then send it again.\n- **Try it:** each result has **Terminal**, **Editor** (Cursor, VS Code, Zed or Sublime, whichever is installed) and **Folder** buttons that open its worktree, plus **▶ Run app**.\n - Run app starts your dev server in the worktree on a free port (passed as `$PORT`), with logs and **Stop app**. Servers stop when the run is archived or code-flow shuts down.\n\n## What agents may do while building\n\n**Settings → Run defaults → Agent permissions:**\n\n| Mode | Claude Code | Codex | Antigravity |\n|---|---|---|---|\n| Safe | edits files; no shell beyond read-only git | edits inside its sandbox | sandboxed to the worktree |\n| Standard (default) | edits + common build/test commands (npm, node, pytest, uv, poetry, cargo, go, make…) | sandboxed, no network | sandboxed to the worktree |\n| Trusted | any command | sandboxed, with network | sandboxed to the worktree |\n| Full access | any tool, no permission checks (`bypassPermissions`) | no sandbox (`danger-full-access`) | no sandbox (no `--sandbox`) |\n\n**Full access** turns every agent's sandbox off in steps that build: an agent can run anything, use the network, and change files outside its worktree with your credentials. Use it only on repos and tasks you trust completely. To keep it a choice you make:\n\n- It's picked per run in the composer, which warns while it's on, and the run shows ⚠ full access. A repo's `.codeflow/config.json` can't turn it on: `\"trust\": \"full\"` there is ignored.\n- Planning, reviewing and judging steps stay read-only, as in every mode.\n- Runs nobody watches get less: automations and sweeps run with Trusted, and fixes for watched pull requests with Standard.\n- A run started from someone outside the repo is locked down to Safe whatever you picked (see [Untrusted text](github.md#untrusted-text)).\n\nWhatever the mode, code-flow never merges or pushes on its own.\n\n## Models\n\nPer provider in **Settings → Agents & models**, and per role right under each role's agents in the team. See [Getting started](getting-started.md#models-and-effort).\n\n## Light or dark\n\ncode-flow follows your system’s appearance until you choose Light or Dark in **Settings → General**. The pick is kept in that browser, and a side panel popped out into its own window follows it.\n\n## Health\n\nClick the agent dots at the bottom of the sidebar to see, for each CLI, whether it's installed, its version, and whether it's signed in. It uses `claude auth status`, `codex login status`, and for agy whether it can list models; credentials are never read. Claude's 5-hour and 7-day usage windows are shown from its own reports, with how to fix anything that's wrong. A run won't start with an agent that's signed out, and Auto skips an agent that turned out to be signed out for 15 minutes. If agy can't reach its servers, its sign-in shows as unknown rather than signed out.\n\nHealth also checks that each installed CLI still accepts the flags and subcommands code-flow passes (from its `--help`), and warns when one is missing: after a CLI update, that's the difference between \"healthy\" and every step failing on an unknown option. It also checks for git 2.38 or newer, which Merge needs.\n\nOnce an agent CLI reports its final result, it gets 10 seconds to exit; one that's still running then (a hook or MCP server that hangs on shutdown) is stopped and its result kept. A single output line over 16 MB is skipped, not read into memory.\n\n## Environment variables\n\n| Env var | Default | Purpose |\n|---|---|---|\n| `PORT` | `4317` | Server port |\n| `CODEFLOW_HOME` | `~/.codeflow` | Run logs and code-flow's own files (see [below](#files-in-codeflow)) |\n| `CODEFLOW_WORKTREE_DIR` | `.worktrees` | Worktree folder; relative paths are inside each repo, absolute paths are used as-is |\n| `CODEFLOW_STEP_TIMEOUT_MIN` | `30` | Timeout per agent step |\n| `CODEFLOW_STEP_IDLE_MIN` | `15` | An agent CLI that prints nothing for this long is stuck (a login prompt, a stalled network, a hook that never returns) and is stopped. Keep it above your longest silent test run |\n| `CODEFLOW_GIT_TIMEOUT_SEC` | `300` | Longest a git command may run, hooks included; cloning a new worktree's submodules gets 10 minutes |\n| `CODEFLOW_MERGE_TIMEOUT_SEC` | `1800` | Longest **Merge…** may take in a checkout, your hooks included (a post-merge `npm install`, Git LFS). If git is stopped after it made the merge commit, the merge counts as done, with a warning |\n| `CODEFLOW_MAX_AGENTS` | `6` | Agent CLIs running at once, across all runs |\n| `CODEFLOW_MAX_CHECKS` | `3` | Check and setup commands running at once, across all runs |\n| `CODEFLOW_RETRY_DELAYS_MS` | `20000,60000` | Waits before retrying an agent turn that failed for a passing reason |\n| `CODEFLOW_TOKEN` | random per start | The session token (set it to keep browsers signed in across restarts; anything that can read it can use the API) |\n| `CODEFLOW_MAX_DIFF_CHARS` | `60000` | Diffs longer than this are fitted (whole files first) before being passed to another agent |\n| `CODEFLOW_CLAUDE_WRITE_TOOLS` | build/test allow-list | Comma-separated Claude tool rules for write mode, e.g. `Edit,Bash` |\n| `CODEFLOW_REMOTE_PORT` | `PORT+1` | Port for the phone view (or the full app, with phone control) and webhooks on your network, when Wi-Fi or Tailscale is selected under **Remote access** |\n| `CODEFLOW_TLS_CERT`, `CODEFLOW_TLS_KEY` | unset | Optional certificate/key file paths overriding saved HTTPS setup in Settings → Connections → Share access. Configure both together; full control requires TLS on Wi-Fi and Tailscale so browser session cookies remain Secure |\n| `CODEFLOW_ALLOW_INSECURE_PHONE_CONTROL` | unset | Explicit `1` permits full phone control over HTTP for development |\n| `CODEFLOW_INTEGRATIONS_FILE`, `CODEFLOW_INTEGRATIONS_JSON` | unset | Explicit external source definitions with server credential references; imports are read-only, while [GitLab/Bitbucket code actions](code-hosts.md) require separate explicit previews/actions |\n| `CODEFLOW_REMOTE_WORKERS_FILE`, `CODEFLOW_REMOTE_WORKERS_JSON` | unset | Explicit provisioned SSH targets and repository mapping names; see [remote workers](remote-workers.md) |\n| `CODEFLOW_EXTENSIONS_FILE`, `CODEFLOW_EXTENSIONS_JSON` | unset | Explicit trusted bundled provider/workflow plugins with API negotiation and SHA-256 pins; choose one configuration source; see [extensions](extensions.md) |\n| `CODEFLOW_NOTIFICATIONS_FILE`, `CODEFLOW_NOTIFICATIONS_JSON` | unset | Explicit outbound destinations; targets remain disabled until event/project opt-in; see [notifications](notifications.md) |\n| `CODEFLOW_SCHEDULER_TICK_MS` | `30000` | How often due automations are checked |\n| `CODEFLOW_WATCH_INTERVAL_MS` | `60000` | How often watched PRs are checked for CI results and new comments. A PR that stays green with no new comments is checked less often, up to every 10 minutes |\n| `CODEFLOW_CODEX_TRANSPORT` | `auto` | `auto`: `codex app-server` when the installed Codex has it, else `codex exec`. A Codex without app-server is re-checked every 10 minutes; a slow or failed app-server start fails only that turn. `app-server`: require it. `exec`: always `codex exec` (your notes then wait for Codex's next turn) |\n| `CODEFLOW_PYTHON` | `python3` | The Python 3 that runs the side panel Terminal's pseudo-terminal (`scripts/pty.py`). It's found once and run by its full path, so a repository that pins another Python (pyenv, asdf, mise) still gets a terminal. Without a working one, terminals are line-based (`interactiveTerminal: false` in `/api/health`) |\n| `CODEFLOW_CHROME` | first one installed | The Chrome, Chromium, Edge or Brave the side panel Browser's **Snapshot** runs headless. Without one, Snapshot says so |\n| `CODEFLOW_AGY_SKIP_PERMISSIONS` | `1` | Set to `0` to stop passing `--dangerously-skip-permissions` to agy; then configure `permissions.allow` in `~/.gemini/antigravity-cli/settings.json` |\n\nA numeric setting that isn't a valid number (`30m`, say) or is out of range falls back to its default, with a warning in the terminal.\n\nChild processes exclude the application API token, scoped team capabilities, integration/notification configuration and their registered server credential variables. They also exclude npm lifecycle variables about code-flow itself and its own dependency bin paths; your `npm_config_*` settings remain. A check's tools come from its repository. Explicit agent containers use a smaller environment and only their configured authentication volume; see [container setup](agent-containers.md).\n\n## Files in `~/.codeflow`\n\n| File | What it is |\n|---|---|\n| `runs/<run>.jsonl` | Each run's event log, appended as it happens; runs are rebuilt from it at startup. A run snapshot after the first is written as what changed; logs from older versions are compacted once, in the background. Unreadable lines (a crash mid-write) are skipped and noted in `server.log` |\n| `runs/<run>.journal.jsonl` | The result of each completed step, written to disk at once, so **Resume** replays them instead of running them again |\n| `runs/<run>.cold-index.json`, `runs/<run>.cold-detail.json` | Optional source-validated archived projections; corrupt/stale projections rebuild from the event log. Full archived detail uses a bounded cache; see [run storage](run-storage.md) |\n| `shared-accounts.json` | Private password/session hashes, expiring accounts and project roles. Never exported in backups; see [shared projects](shared-projects.md) |\n| `shared-reviews/<run>.json` | Commit-anchored human comments, separate from agent notes and prompts |\n| `team/` | The team board this instance serves: `board.json` (each project's repository keys and its tasks with attempts, checkpoint manifests, discussion, decisions, instructions, reviews, hand-overs and audit), `runners.json` (runner names and credential hashes, never credentials), `inbox.json` and `checkpoints/<checkpoint id>.bundle`. Written whole through temporary files (heartbeats and progress reports reach `board.json` with the next durable change or within 30 s); one that can't be read is moved aside to `<name>.corrupt-<time>` and logged. At startup, temporary files and bundles that no available checkpoint lists are removed from `checkpoints/` and the removal is logged. Included in backups; see [team collaboration](team-collaboration.md) |\n| `team-link.json` | This computer's link to a team server: its address, this computer's runner credential, your account, your repository mappings and defaults (mode 0600). Never exported in backups, like `shared-accounts.json`, and skipped by a restore; after restoring a backup on a linked computer, connect it again in **Settings → Connections → Share access → People** and re-enter its repository mappings and defaults |\n| `team-link/attempts/<attempt>.json` | Which local run belongs to which board attempt, and what was published; read at startup, by the recovery pass every minute and after a restore to reconcile with the team server, never to start anything again |\n| `team-link/bundles/<checkpoint id>.bundle` | A checkpoint bundle this computer created, kept until the team server has received it; a failed upload is retried with the same file, and the file of a checkpoint that was given up is deleted |\n| `filesystem-policies/<project>.json` | Revisioned read/write rules and content hashes; see [filesystem policy](filesystem-policy.md) |\n| `filtered-tasks/` | Private filtered exports, exact policy/source/result records and explicit import receipts; never a startup execution queue |\n| `remote-worker-private/` | Private SSH receipt capabilities; excluded from ordinary result projections and restore authority |\n| `code-host-operations.json` | Safe confirmed/unknown code-host mutation receipts; no automatic replay after restart or restore |\n| `extension-cache/` | Captured trusted plugin bundles; excluded from backup/restore and rebuilt only from explicit reviewed configuration |\n| `projects.json` | Your [projects](runs.md#projects): names, repositories, and the team each one last used. Written whole (through a temporary file); a change that can't be saved fails and is undone. One that can't be read is moved aside to `projects.json.corrupt-<time>` and logged; if it can't be moved either, projects are off (runs start without one) until it's fixed |\n| `server.log` | code-flow's own log, one JSON line per entry: failed requests, unhandled errors, failed writes, processes it cleaned up. Past 5 MB it moves to `server.log.1` (one old file is kept) |\n| `processes.json` | The process groups code-flow has started and not seen finish. If code-flow dies without a clean exit (a crash, `kill -9`, a closed terminal), the next start stops what's left before resuming anything, so an orphaned agent can't keep editing a worktree. Pids from before a reboot, or reused by another program, are left alone |\n| `server.lock` | One server per `CODEFLOW_HOME`. It records the server's pid, start time and the boot, so a lock left by a crash is taken over even when another program now has that pid, or after a reboot |\n\n## Dynamic teams\n\n`CODEFLOW_AUTO_TEAM_ENABLED=false` disables new Auto team runs while preserving saved-run viewing and recovery. The rollout record and evaluation evidence are shown in Insights; the composer selects Auto initially only at the `default` stage when there is no saved workflow preference. The recorded stage is informational (and a composer default when set to `default`); only `CODEFLOW_AUTO_TEAM_ENABLED` changes whether new Auto runs can start. Existing runs without the `eventDriven` option retain dependency-wave replay; newly created Auto runs default it on.\n\nThe authenticated run API accepts `loop.maxInvocations` as an additional per-allocation ceiling on actual CLI launches, including retries. Auto already caps launches with its step budget. Follow-up requests can specify `budget: { maxSteps, maxInvocations, maxMinutes }`; the UI exposes this fresh allocation and retains lifetime usage.\n\nProject memory is in `memory.sqlite`, with automatic verified import from `memory.json`, indexed keyword search and a lightweight knowledge graph. See [Memory storage](memory-storage.md) for migration, rollback, runtime requirements and backup behavior. Evaluation reports and the explicit rollout decision still use durable replacement files under the evaluation store in `CODEFLOW_HOME`. See [Auto team](auto-team.md) for scope, trust, revision and deletion semantics.\n\n## Project missions\n\nClick **Enable project missions** on a project page to enable the feature immediately and save the choice for this host across restarts. `CODEFLOW_PROJECT_TEAMS_ENABLED=true` is an optional initial default; a saved UI choice takes precedence. `CODEFLOW_AUTO_TEAM_ENABLED` must also permit Auto team execution. Each team is separately activated by its owner. Schedules, budgets, missions, recovery and provider limits are described in [Project missions](project-teams.md).\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Configuration"},{"group":"Your workspace","id":"context-inspection","source":"docs/context-inspection.md","body":"# Context and budgets\n\nOpen Context inspector before starting a run. Review file sources, omissions and size estimates; select individual modified files when you need the current working copy included.\n\n## Check what you are sending\n\nThe composer’s **Context inspector** previews explicit repository selections and attachment metadata before a run. Folder selections expand tracked files from the chosen committed base. Current modified or uncommitted descendants are not implicitly copied by a folder selection; select those individual files to stage their current copies. Explicit file previews use the current file where present and fall back to the selected base when it is missing locally. Each preview identifies its source and content hash.\n\nInspection is bounded to 50 selections, 200 result entries, 512 KiB per text file and 8 MiB of inspected text. Larger, binary, invalid UTF-8, symbolic and Git LFS material is reported as omitted. Credential names and Git internals are not previewed. Per-file sizes and approximate text-token counts are shown with aggregate inspected, omitted and attachment bytes. Unscanned file sizes and binary/image tokens remain unknown. Uploaded text estimates use byte size; uploaded contents are not sent to the inspector again.\n\nPotential credential paths such as `.env`, private-key files, credential stores and `.git` are also rejected by actual context preparation and message attachment staging. A preview omission is not the sole defense. Selecting a symbolic file, a symbolic ancestor or a base symlink/submodule is refused before copying. Preview redaction only hides recognized secret patterns from this display; it does not rewrite other repository files. Agents can still read files already in their worktree according to their existing permissions.\n\nReference files are not pasted wholesale into prompts: the assembled request identifies their locations so agents can read them. Consequently, reference-file estimates and invocation-input budgets measure different things.\n\nEnable an explicit **Invocation context budget** to choose a byte ceiling, an approximate token ceiling, or both. Its policy either records a warning or stops the run before a provider launch. Budgets default to unset and never infer a provider’s context-window size. The server measures the fully assembled per-turn prompt (including notes and selected memory already added by code-flow) plus its explicit JSON schema immediately before the adapter is invoked. A blocked attempt does not launch a CLI. Retrying with the same exceeded ceiling will block again; deliberately choose a suitable budget for a new run. Retries and fresh-session fallbacks are measured again with their actual current prompt.\n\nSaved team presets capture this budget, including previous revisions and library import/export. Applying an older preset without a budget clears a current ceiling. The Effective settings inspector identifies the browser preference or exact preset revision that supplied the current budget. Version 1 preset libraries remain compatible when the optional `contextBudget` field is absent.\n\nEvidence records exact UTF-8 prompt/schema byte counts, their aggregate, an approximate token count of one token per four bytes, a content hash, resumed-session status, exclusions and any exceeded ceilings. Prompt bodies are not duplicated into this size evidence. Earlier CLI conversations, hidden provider system instructions, protocol wrappers, tool outputs, later file reads, and image/audio tokenization are outside this measurement. An approximate guard cannot guarantee that the provider’s complete context fits its window.\n\n`POST /api/workspace/context` accepts `{repo,baseRef?,paths,attachments?:[{name,size}]}` and returns source-labeled file results, attachment metadata, byte/token aggregates, omissions and limits. Run settings carry `loop.contextBudget = {maxPromptBytes?,maxApproximateTokens?,policy:'warn'|'block'}`. Both ceilings require positive bounded integers. The warning and block policies are enforced on the server and persisted with per-step input-size evidence.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Context and budgets"},{"group":"Your workspace","id":"project-conventions","source":"docs/project-conventions.md","body":"# Versioned project conventions\n\nSave a private convention draft, inspect its version history, and preview publication to a project repository. Commit the staged conventions file if future runs should inherit it.\n\n## Keep project guidance current\n\nSettings → Projects & access keeps human-authored project guidance privately beneath `CODEFLOW_HOME/conventions/`. A saved draft does not change a repository and is never inserted into agent context automatically. External run content cannot author guidance through the team capability API.\n\nEach save requires the exact observed revision. A concurrent edit returns HTTP 409 and preserves the editor draft. Compare the latest saved text, then deliberately keep your draft against that newer base or export it. Versions retain their original text, timestamp, hash, and note. The latest 32 revisions are retained; each holds at most 64 KiB of UTF-8 text. Line endings normalize to LF and nonempty guidance ends with a newline. History files use atomic durable replacement with private file permissions. Corrupt or inaccessible histories are left untouched, and a failed operation does not poison later writes.\n\nSelect a retained version and **Preview saved revision** before restoring. **Restore as new revision** requires both the current revision and the previewed version hash. It copies the old text into a new revision; it does not rewrite history or publish anything. Save or revert unsaved editor changes before restoring.\n\nTo activate guidance, select a repository belonging to this project and inspect its current file. **Publish and stage file** explicitly writes and stages `.codeflow/conventions.md`. The repository must be clean and idle. Publication checks the saved private revision, repository HEAD, and exact source file hash while holding the private revision lock and the repository lock. Non-project repositories, symbolic `.codeflow` components, symbolic files, symbolic `.git` markers, binary files, and oversized source files are refused. Code-flow stages this single guidance path even if it is ignored; it does not commit or push.\n\nReview and commit the staged file yourself. Only `.codeflow/conventions.md` at a run's committed base ref is read into context, before existing vendor guidance files. The existing aggregate convention limit remains 6,000 characters; longer guidance is truncated with a prompt to read the repository file. Publishing a draft without committing it does not change conventions for future runs. Locked-down imported runs continue to inherit trusted parent/base guidance according to existing run creation rules.\n\nPublication attempts restore the prior target file if staging fails and the source has not changed concurrently. If another tool changes HEAD or the target during publication, those changes are preserved and the UI requires a new inspection. Review any reported Git index error before retrying.\n\nAPI contracts: `GET /api/projects/:id/conventions`, `PUT` with `{revision,text,note?}`, `POST /restore-preview` with `{version}`, and `POST /restore` with `{revision,version,hash}`. `GET /publication?repo=PATH` returns the observed `{sourceHash,head,clean,text,...}`. Explicit `POST /publish` requires `{repo,revision,sourceHash,head}`. Publication authorization is checked against the project's repository membership and current activity on the server; private drafts are not automatic execution instructions.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Versioned project conventions"},{"group":"Your workspace","id":"checkpoint-review","source":"docs/checkpoint-review.md","body":"# Checkpoints and reversions\n\nOpen Project → Repository tools → History, compare the checkpoint, then preview the inverse commit. Apply only after inspecting the patch; the inverse change remains staged for your own commit.\n\n## Review before reverting\n\nProject → Repository tools → History compares the selected checkpoint with the observed HEAD across\nall changed paths. The patch preview is bounded to 2 MiB; large comparisons are\nexplicitly marked truncated. Both comparison endpoints resolve to immutable commits.\n\nFor an owned idle run worktree, **Preview inverse commit** computes a normal Git\nrevert on a disposable checkout. Conflicting reversions do not change the user's\nworktree. Merge commits require an explicit mainline decision in Git and are refused\nby this interface. Commits outside the worktree's ancestry are also refused.\n\n**Stage reviewed inverse change** recomputes the preview, verifies its fingerprint\nand the observed HEAD, and applies the server-generated inverse patch to a clean\nindex and working copy. Tracked-file hiding flags are refused. An in-process workspace\nlease prevents another Code Flow start, resume or mutation from racing the operation.\nThe change remains staged for review and a normal user commit; history is preserved.\nExisting modifications, unknown files that would be overwritten and stale previews\nprevent application. External programs are outside Code Flow's process coordination;\nGit's index checks still guard the actual application.\n\nThe API operations are `POST /api/v1/workspace/compare-checkpoint`,\n`/preview-revert` and `/apply-revert`. Comparison accepts `repo`, `from`, `to`.\nRevert preview accepts `repo`, `runId`, `commit`, `expectedHead`; apply also requires\nthe returned `fingerprint`. Caller-authored patches are not accepted.\n\nValidation covers complete comparison, staged inverse changes, history preservation,\nconflicts, stale/forged previews, dirty worktrees, hidden tracked-file changes, and\nthe actual browser preview/application flow.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Checkpoints and reversions"},{"group":"Your workspace","id":"launch-profiles","source":"docs/launch-profiles.md","body":"# Service launch profiles\n\nOpen Project → Repository tools → Services to define a named launch profile. Check dependencies and readiness before starting the profile, then use its logs and stop controls to manage the services.\n\n## Start the services you need\n\nCreate `.codeflow/launch.json` in the repository. Project → Repository tools → Services reads the selected run's worktree configuration. It does not execute anything until you click **Start launch profile**. Review commands first: they execute repository code on your computer with your ordinary development environment. Runs from an untrusted external source require an additional explicit confirmation.\n\n```json\n{\n \"version\": 1,\n \"profiles\": [\n {\n \"id\": \"development\",\n \"name\": \"API and web app\",\n \"services\": [\n {\n \"id\": \"api\",\n \"command\": \"npm run dev\",\n \"cwd\": \"packages/api\",\n \"health\": { \"path\": \"/health\", \"timeoutMs\": 30000 }\n },\n {\n \"id\": \"web\",\n \"command\": \"npm run dev -- --host 127.0.0.1 --port $PORT\",\n \"cwd\": \"packages/web\",\n \"dependsOn\": [\"api\"],\n \"env\": { \"VITE_API_URL\": \"${API_URL}\" },\n \"health\": { \"path\": \"/\", \"timeoutMs\": 30000 }\n }\n ]\n }\n ]\n}\n```\n\nEach profile has a unique lowercase ID, a display name, and one to six services. The file supports up to twelve profiles and is limited to 128 KiB. Every service needs a unique lowercase ID and a shell command. `cwd` defaults to `.` and must be a regular directory beneath the worktree; absolute paths, `..`, `.git`, and symbolic links are rejected. Dependencies must name existing services and form an acyclic graph. Commands are launched in dependency order; each dependency must pass its readiness check before dependent commands start.\n\nBefore starting any service, code-flow allocates distinct ports and generates `PORT`, `HOST=127.0.0.1`, and `SERVICE_NAME` for each service, plus `<SERVICE_ID>_URL` and `<SERVICE_ID>_PORT` for every peer. IDs use uppercase environment names with hyphens replaced by underscores. For example `user-api` produces `USER_API_URL=http://127.0.0.1:<allocated port>`. A command must honor `PORT` to use the assigned port; code-flow does not rewrite a command or its configuration. Services should bind to `HOST` to remain on this computer.\n\nOptional `env` values can substitute these generated variables using `${NAME}`. Unknown substitutions and attempts to replace reserved variables are rejected before any command executes. Service environment entries are limited to forty bounded strings. Code-flow's server session and registered integration credentials are excluded from child environments. Ordinary credentials you exported for development remain available to the repository commands.\n\nThe default readiness check requests `/` on the service's assigned `127.0.0.1` port and waits up to 15 seconds for a 2xx response. Custom paths must be local paths; redirects are not followed. Each check is bounded to 100–120,000 ms, and the complete stack has a three-minute startup deadline. Set `health: false` for a background worker without HTTP. This only checks that its process remains running and is displayed as **HTTP readiness not confirmed**; it does not prove application health.\n\nThe panel exposes each service's status, local URL, and its last 100 bounded log lines. **Stop services** cancels pending startup and terminates all process groups in the stack. Failed health checks or process exits stop the other services and preserve recent logs for diagnosis. Only one stack per run/worktree may run at a time; stop it before choosing a different profile. Service state is ephemeral and profiles do not restart automatically after code-flow restarts.\n\nThe API uses `GET /api/runs/:id/launch-profiles?worktree=NAME` for `{profiles,found,error?,session}`, `POST` with `{worktree,profile,allowUntrusted?}` to explicitly start, and `DELETE` with the worktree query to stop. The backend resolves run-owned worktrees; callers cannot supply an arbitrary filesystem root. A start returns a `LaunchSession` after readiness succeeds. Errors retain session logs, retrievable through `GET`.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Service launch profiles"},{"group":"Your workspace","id":"code-intelligence","source":"docs/code-intelligence.md","body":"# Code intelligence\n\nOpen Project → Repository tools → Code, search for a JavaScript or TypeScript symbol, and choose Find references. Save editor changes first: the inspection reads saved files.\n\n## Find a symbol\n\nProject → Repository tools → Code offers text search, simple multi-language declaration search,\nand JavaScript/TypeScript symbols backed by TypeScript's language service.\nSemantic symbol results include a **Find references** action. Imported aliases\nare resolved; ordinary text mentions are not treated as references. Each request\nuses saved repository files, so unsaved editor drafts are excluded.\n\nLanguage-service computation runs in a separate worker with a 10-second deadline and a\n192 MiB heap ceiling, and only one inspection runs at once. The in-memory file\nhost contains at most 500 regular source files and 8 MiB of source. Results stop\nat 200 locations and explicitly report truncation. Credential selections,\nsymbolic links, external libraries, compiler plugins and generated directories\nare excluded. Repository compiler configuration is not executed or loaded; the\ninitial implementation uses a fixed safe compiler configuration and relative\nmodule resolution. Project-specific path aliases may therefore be unresolved.\n\nThe API supports `GET /api/v1/workspace/search?repo=...&mode=semantic&q=...`\nand `GET /api/v1/workspace/references?repo=...&path=...&line=...&column=...`.\nLocations are one-based and columns use UTF-16 code units, matching the language\nservice. Results include the actual language-service version and scope limits.\n\nUnit fixtures check imported aliases, string-mention exclusions, current-content\nrefresh, symlink/credential/plugin boundaries and cancellation. Browser acceptance\nexercises symbol search and cross-file reference navigation against saved JavaScript.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Code intelligence"},{"group":"Your workspace","id":"architecture-explorer","source":"docs/architecture-explorer.md","body":"# Architecture explorer\n\nUse static dependency evidence to investigate how a change may affect other files. Unresolved imports still need interpretation.\n\n## Explore repository structure\n\nOpen **What could this change affect?** while editing a file in Files or Repository tools → Code, or while reviewing a result branch. This inspects saved tracked source without executing repository code, build hooks, compilers or package managers. It resolves JavaScript/TypeScript syntax with TypeScript's parser, including literal dynamic imports and CommonJS requires. Conservative static scans also cover Python imports, Go import groups and local module packages, Rust modules and basic use declarations, and Java class imports.\n\nThe view includes file relationships, directory coupling, dependency cycles, declared npm/Python/Go/Rust/Maven packages, unresolved imports and computed runtime targets. Export the same evidence as JSON. An unresolved import may be an external or standard library package; it is not evidence that a dependency is missing. Runtime loaders, path aliases, Rust macros, Java wildcards, generated source and conditional builds still need manual inspection. No package installation or dependency-tree network lookup occurs.\n\nThe scan considers at most 600 tracked files and 8 MiB of readable source, with 4,000 relationships, 1,000 manifest dependencies, 500 unresolved imports and 200 computed targets. Truncation is visible. Existing symlink and regular-text file checks apply, and credential paths and dependency/build directories are excluded. Untracked and unsaved editor drafts are excluded. Cancellation stops the scan.\n\nThe import rules follow the [Python import system](https://docs.python.org/3/reference/import.html), [Go import declarations](https://go.dev/ref/spec#Import_declarations), and [Rust use declarations](https://doc.rust-lang.org/reference/items/use-declarations.html); these static scans do not reproduce every language's runtime resolution.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Architecture explorer"},{"group":"Your workspace","id":"memory-storage","source":"docs/memory-storage.md","body":"# Project memory\n\nOpen Project memory to search saved guidance, then use Knowledge graph to follow links to files, runs, commits and source memories. Review provenance before applying an entry to new work.\n\n## Explore project knowledge\n\nCode Flow stores private, team and project memory in `CODEFLOW_HOME/memory.sqlite` (normally `~/.codeflow/memory.sqlite`). Node **22.13 or newer** is required for the built-in `node:sqlite` driver. No database server, native npm addon, downloaded extension or extra SSH deployment asset is needed. This replaces the earlier Node 20 memory-storage baseline. SQLite is experimental in Node 22 and may print a startup warning.\n\n## Storage and migration\n\nThe first memory access imports an existing `memory.json`. Import validates the complete legacy document, creates the schema, records every entry including tombstones and provenance, and compares the imported records with the originals before committing. Unsupported/corrupt JSON or database schemas fail closed. IDs, revisions, approvals, operation receipts and feedback are preserved. Passing the old JSON filename to `MemoryStore` resolves to the corresponding SQLite file.\n\nAfter verified import, the original becomes `memory.json.pre-sqlite` with private permissions. It is a temporary rollback copy, retained until the **first mutation that changes a record**. Before writing changed records, Code Flow removes that copy and old JSON temporary snapshots so edits and deletions do not leave previous bodies in application-owned migration files. A failed database transaction can therefore leave the rollback copy removed, while the original database records remain intact. Import finalization can resume after a crash without importing duplicate records. If JSON conflicts with the import or reappears after a mutation, Code Flow reports the conflict rather than choosing a version.\n\nTo roll back before any mutation, stop Code Flow, move `memory.sqlite` aside, and rename `memory.json.pre-sqlite` to `memory.json` before using the older release. After mutations, use an explicit backup/restore workflow; the temporary import copy is no longer a current rollback source. External backups and run transcripts have their own retention.\n\nEach mutation uses `BEGIN IMMEDIATE` and updates changed memory rows, their keyword index and graph relationships in one transaction. Existing permission, exact-revision conflict, approval, provenance and publication checks still run inside that transaction. Connections close after each operation. The database uses full synchronization, DELETE rollback journaling, private file permissions and SQLite secure deletion. Successful deletion removes the entry's text, indexed terms and owned graph edges; descendants keep their own text and provenance references. This is logical/application-file redaction, not a guarantee of erasure from filesystem snapshots or storage hardware.\n\nThe existing 32 MiB logical-document limit, 5,000 live project entries and 512 private/team entries per run remain. Mutations still inspect the bounded collection to enforce provenance and collection-wide rules, but only changed rows are written. Scoped reads use a repository/project index. This change does not claim unbounded scale or measured throughput improvements.\n\nOffline encrypted backup recovers any interrupted SQLite transaction before copying the closed database. Restore retains its memories, indexes and graph. Retention uses database fingerprints and existing revision/provenance checks. A preview of an unmigrated JSON store does not itself migrate it.\n\n## Search and graph\n\nThe `memories` table holds current versioned entries. `memory_terms` is an indexed Unicode keyword table; title and tag matches carry more weight than body matches. It works on Node builds without FTS5. Search is literal token matching, with at most 32 query terms and 512 matching seeds; it does not use embeddings or an external model.\n\nThe graph uses `entities` and `relationships` tables. Every relationship identifies its supplying memory and exact revision. Entities are keyed by repository and project, so identical file names in different projects do not join their graphs.\n\n| Entity or relationship | Source |\n| --- | --- |\n| Memory → applies to → file/path | The entry's explicit `paths` |\n| Memory → learned from → run | Its run ID and `run:` source references |\n| Memory → learned from/applies to → commit | `commit:` evidence and branch applicability |\n| Memory → verified at → commit | Runtime verification metadata |\n| Memory → derived from → memory | Exact `derivedFrom` references |\n\n“Derived from” means the source was in context; it does not assert that its text was copied or its claims are logically proven. The graph does not infer imports, function calls, test coverage or arbitrary relationships from prose. Agent-supplied evidence retains the memory's existing proposal/trust status. Outdated, deleted or inaccessible source-memory revisions are not resolved to current labels.\n\nFor retrieval, Code Flow first filters to accepted, visible, current, branch-applicable and path-applicable memories. Text matches seed a traversal of at most two hops through the recorded relationships (at most 1,000 edges). Connected memories receive a small ranking boost, and the selection manifest explains it. This never broadens access or path scope and cannot use a private or stale memory as a hidden bridge. Existing character and entry budgets still apply; replay resolves the exact recorded revisions rather than running search again.\n\nExpand **Project memory → Knowledge graph** to filter connections and inspect their source revisions. The inspector includes proposals for review; it does not approve them. `GET /api/projects/:id/memory/graph?repo=<absolute-path>` exposes the same bounded, authenticated project view and reports truncation.\n\nRun journals, settings and the older recurring-review lessons under `learnings/` retain their existing formats. SQLite is used for the structured agent-memory system; this is not a migration of all application state.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Project memory"},{"group":"Your workspace","id":"memory-claims","source":"docs/memory-claims.md","body":"# Semantic claim review\n\nOpen Project memory → Semantic claim review. Compare the cited revisions and applicability before deciding whether to edit either entry; findings do not change memory automatically.\n\n## Review conflicting guidance\n\nProject memory → **Advanced memory tools** → **Semantic claim review** compares supported facts with overlapping applicability. It identifies equivalent claims and potential contradictions with the exact source entry revisions, character offsets, evidence snippets and normalized values. Findings are suggestions for human review. Analysis never selects a winner, changes status, combines text or publishes guidance automatically.\n\nThe local grammar recognizes standalone policy instructions, including a small documented action vocabulary and policy polarity: “Always permit guest requests” is equivalent to “Do not block guest requests” and conflicts with “Never allow guest requests”. Setting statements normalize numeric dimensions: “Set cache TTL to 5 minutes” equals “Cache time-to-live is 300 seconds”. Numeric constraints conflict only when their intervals are disjoint, including exclusive `<` and `>` boundaries. Overlapping ranges are not treated as contradictions. Time and byte dimensions are never compared with each other or with counts.\n\nConditional or compound prose is deliberately not flattened into unconditional advice. Statements containing conditions, alternatives, conjunctions or multiple sentences are unsupported by this grammar. Unsupported prose is counted, and a report with no findings does not prove consistency. This is a bounded deterministic semantic interpretation, not unrestricted natural-language entailment, embeddings or an external model.\n\nFor precise facts, edit a knowledge entry to add a fenced JSON array:\n\n````markdown\n```codeflow-claims\n[\n {\"subject\":\"guest requests\",\"predicate\":\"allow\",\"value\":false},\n {\"subject\":\"package manager\",\"predicate\":\"setting\",\"value\":\"pnpm\"},\n {\"subject\":\"cache ttl\",\"predicate\":\"setting\",\"min\":5,\"max\":10,\"unit\":\"minutes\"}\n]\n```\n````\n\nA fact has a subject (up to 120 characters), a lowercase predicate (up to 40 characters), and either a boolean/string/number `value`, or numeric `min`/`max` bounds. Unknown fields, invalid units and mixed scalar/range values are rejected as unsupported. `min` and `max` are inclusive. Units accept counts, milliseconds/seconds/minutes/hours, bytes, decimal KB/MB/GB and binary KiB/MiB/GiB. A missing numeric unit means count. Different explicit string settings for the same subject/predicate are a potential conflict; the analyzer does not infer application-specific aliases or which configuration is appropriate.\n\nSubjects use Unicode normalization, case/spacing normalization, a small noun normalization vocabulary, and the `time-to-live`/`TTL` alias. Policy action aliases include use/utilize/employ, allow/permit/accept, deny/block/disallow/forbid/reject, require/enforce, cache/memoize, validate/verify and log/record. These aliases remain review hints: context outside the supported statement can still matter. `enable` and `allow` remain different predicates. Structured boolean predicates can use the same aliases; other explicit predicates are compared exactly.\n\nComparisons stay inside the already authorized repository and project. Private/deleted entries are excluded. Paths must intersect by exact path or directory ancestry; repository-wide entries overlap every path. Two branch-specific entries must name the same branch, and conflicting exact commit applicability is excluded. Source revision metadata is retained. Opposite supported facts inside one entry can also be flagged; repeated equivalent facts inside one entry are not listed as duplicate entries.\n\nLimits: 500 entries; 20,000 source characters per body; 12 claims per entry; 1,000 claims total; 50,000 candidate comparisons; 100 findings; 512-character evidence snippets; 8 KiB-character structured blocks. Offsets refer to UTF-16 positions in the source body, as used by the browser editor. Every truncation and unsupported fact is reported. The feature runs locally without provider quota, network downloads, shell execution or automatic memory mutation.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Semantic claim review"},{"group":"Your workspace","id":"coordinated-changes","source":"docs/coordinated-changes.md","body":"# Coordinated changes\n\nUse coordinated changes when one repository depends on another repository’s result. Pin the dependency inputs, inspect each repository’s verification, and review publishing separately for each result.\n\n## Plan across repositories\n\nOpen **Project → Repository tools → Coordinated changes**, choose a project containing 2–10 local Git repositories and create one task per repository. Select a provider and verification command for each task, and mark its explicit dependencies. Commands left blank use the repository configuration. The graph must be acyclic and every repository must be distinct. This is local coordination; it does not clone remote repositories or supply credentials.\n\nCreation pins each authorized canonical repository, its Git metadata ownership and starting commit. Linked worktrees sharing one underlying repository cannot masquerade as separate shipping targets. Replaced repository metadata blocks dispatch and result validation. Tasks execute in separate managed workspaces with safe trust, disabled native approval waits, no inherited uploads or parent-run sessions, and no copied ignored files. The interface defaults to 20 agent invocations and 15 minutes per task. The service caps each task at 50 calls, 60 minutes, two million tokens and $100 API-equivalent usage, with defaults of 500,000 tokens and $25. These are usage limits, not subscription charges. Existing external-source trust restrictions continue to apply at launch.\n\nA downstream task starts only after all dependencies have current successful verification tied to their exact owned result branches. Its server-created launch claim records factual dependency receipts: repository, run ID, branch, immutable commit and verification identity. It does not turn sibling agent output into trusted instructions, expose sibling credentials, or mount sibling repositories. No broad external-source execution exemption is inherited.\n\nEvery launch claim is saved, synced and atomically renamed before starting an agent. The engine must save and sync the initial run's exact `{jobId,nodeId,claimId,attempt}` coordination identity before returning a receipt. A crash, timeout or ordinary callback failure after claiming produces an unknown outcome. It is never automatically retried. Link only a run whose persisted identity matches the exact claim, or acknowledge that an explicit retry can duplicate work. Each task permits at most three explicit attempts. Retrying invalidates the selected task and all downstream results and receipts; active downstream work or shipping must first be stopped or resolved.\n\nThe status panel shows dependencies, pinned bases, current run links, verified results, errors, exact unknown claims and bounded operation history. Cancelling stops pending dispatch and sends cancellation to known runs. A run receipt arriving after cancellation is stopped as well. Cancellation cannot undo commits already merged by an explicit shipping operation.\n\n## Review and ship\n\nEvery task must have a current verified immutable result before shipping review. Choose a local target branch per repository and select **Validate and preview shipping matrix**. Actual merge-target verification records source commit, current target commit and exact merged tree. The matrix expires after 30 minutes.\n\nReview each matrix entry and deliberately merge repositories one at a time. Before each action, the coordinator checks every owned result and its dependency receipts, verifies the selected source/target/tree again, and saves a durable shipping claim. The engine's merge operation must enforce the reviewed source/target/tree with compare-and-swap. A changed result, target, tree, dependency receipt or expired preview refuses shipping before the merge begins. This flow never automatically pushes or creates remote pull requests.\n\nGit repositories do not share an atomic transaction. A successful merge in one repository remains merged if another fails; the status and history show partial progress. An ambiguous shipping receipt becomes `shipping-unknown` and cannot be blindly retried. Manual recovery must prove the exact reviewed source/target/tree using the resulting local commit and engine-owned merge receipt. Do not reset unrelated commits to simulate an atomic rollback.\n\n## Persistence and recovery\n\n`CODEFLOW_HOME/coordinated-changes.json` stores revisioned jobs, sanitized task configuration, pinned commits and bounded histories in owner-only files. It can include user-entered task text and command strings; keep credentials in environment references rather than task text. The service permits ten active jobs, twenty retained jobs, ten repositories per job, one hundred history entries per job and a 32 MiB storage limit. Incomplete or invalid storage fails closed and is left untouched.\n\nNormal restart converts claimed launches and shipping operations to unknown outcomes and does not repeat them. Verified dependency facts are checked again before further work. Encrypted backup restore disables all implicit coordinated dispatch while retaining claim and shipping metadata for investigation. Review restored jobs explicitly before any retry. Private remote-worker capability receipts remain excluded from backups and restore.\n\nThe versioned authenticated HTTP interface uses `/api/coordinated-changes`: create/list jobs, cancel, explicitly retry tasks, link exact unknown runs, preview a shipping matrix, merge one repository and recover a proven unknown shipping receipt. Mutating existing jobs requires their current revision. The coordinator's callback interface is defined in `src/repo-coordination.ts`; callbacks must supply actual authorization, durable engine receipts, verification evidence and compare-and-swap merging. A callback's success is not synthetic verification evidence.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Coordinated changes"},{"group":"Connect and collaborate","id":"team-collaboration","source":"docs/team-collaboration.md","body":"# Team collaboration\n\nConnect each teammate’s computer as a runner for its local repositories and CLI logins. Use explicit host-execution or terminal grants only when teammates should work on the team server itself.\n\nProject → More → **Board** contains tasks on the project’s owning computer. Project → More → **People** contains members, roles and repository keys; the owner also gets existing account and execution/terminal grant controls. Configured boards become project tabs. The optional **Show Board in the app menu** preference only controls an unused shortcut: it never hides configured collaboration or its settings. Command search includes Board and People, with Team as a search alias.\n\nLinked servers remain available through the connection-scoped **Board** entry and its **Team connection** selector. A linked project is not attached to a local project by a matching ID, name or repository path. The URL’s `team` query pins requests, downloads and the stream to that connection. Opening a local project’s Board or People switches the document to `team=local`; previous requests and subscriptions cannot become actions against the new source. Missing projects show an unavailable state instead of another board. Shared users see Board and Results plus People from their authorized board projection, including projects with repository keys but no server clone.\n\n## Start with what you want to connect\n\nOpen **Settings → Connections → Share access** and choose:\n\n| Your goal | Choose | Next step |\n| --- | --- | --- |\n| Continue your work on a phone or tablet | **Connect my devices** | Choose Wi-Fi or Tailscale, configure HTTPS for full control, then scan your private device QR code. |\n| Join someone else’s project | **Join a team** | Open their invitation, choose your own account, then connect your own CodeFlow when you want to run tasks. |\n| Give people access to a project | **Host a team** | Choose this computer or an SSH server, configure repositories and secure access, then invite teammates. |\n\n**Manage existing connections and access** retains the account, role, device, runner and execution-grant settings. Old Settings → Team and Remote access bookmarks still open the appropriate settings.\n\n## Invite and join a project\n\nThe server owner opens **Project → People → Invite a teammate**, or the **Teammates** step in team-server setup. Choose a project and role, then **Create invitation**. Copy the link or let the recipient scan its QR code. Each link admits one person, expires after 24 hours, and can be revoked from **Pending invitations**. Only the token hash is saved; copy the link before leaving the page. Share it privately with its intended recipient.\n\nInvitation creation in the app becomes available after secure network access is configured. The link uses the HTTPS server origin, without the owner's phone-pairing capability. The recipient must be able to reach that origin over the same Wi-Fi or permitted Tailscale network.\n\nThe recipient opens the invitation, checks the project and role, then creates their own account with a password of at least 15 characters or signs in with an existing shared account. Acceptance creates the account/membership and browser session atomically. Existing accounts keep other project roles and any stronger role already held in this project. Used, expired and revoked invitations explain how to request a replacement. A removed project cannot be joined.\n\nAfter joining, **Open project board** opens the invited project immediately. Contributors and maintainers also see a credential-free **connection link**: paste it in their own CodeFlow under **Join a team**, sign in, and choose a local clone using **Choose folder**. The project checklist shows which folders still need mapping and provides a board link for each project. The shared dashboard keeps **Connect your computer to run team tasks** available later.\n\nA project invitation grants only its project role. It never grants the owner's access, a terminal, or permission to run agents on the server. Those require the existing separate grants. The browser can collaborate on tasks and reviews; a connected computer runs the work its owner starts with their own agent logins. Personal device pairing remains a separate flow.\n\n## Choose where work runs\n\nTeam collaboration lets the people who work on a project share one **team board** of tasks and hand work between their computers. One ordinary code-flow instance acts as the **team server**: teammates reach it over HTTPS through Tailscale or another network and sign in with the existing [shared project accounts](shared-projects.md). Each teammate's own code-flow installation can be linked to that server as a **runner** (\"this computer\"): it executes board tasks locally, in a Git worktree, with that person's own agent CLIs and logins; publishes bounded progress and a final **hand-over checkpoint** (a verified Git bundle plus structured context) to the board; and can hand the task over to a teammate, who continues from the checkpoint with any agent. Reviews attach to checkpoint commits.\n\nThe team server never commands another machine. In the linked-runner workflow every connection is made outbound, from a teammate's computer to the server; execution starts from that computer's own code-flow, by its owner; and nothing on the server can start, steer or stop a run elsewhere. The server holds the board, the checkpoints and who is doing what. The work happens where the agents and their logins already are.\n\nVocabulary in this document: *team* and *teammates* are people, not the agents' [Auto team](auto-team.md); a *board task* is an entry on the team board, not an Auto-team task; a *hand-over checkpoint* is a published Git bundle, not one of the [commit checkpoints](checkpoint-review.md) under **Project → Repository tools → History**; a *runner* is a teammate's own code-flow, not an [SSH worker](remote-workers.md). In the code and the tests the feature is called `collab`.\n\nDeliberately absent in this version: live pairing in the same terminal; moving a provider's live session between machines; automatic merging of combined results (fetching checkpoints into local branches and the existing merge and verification flows is the integration path); issue-tracker links; the server pushing anything to teammates' machines. Opt-in host tasks and individual interactive terminals for shared users are described below.\n\n## Set up the team server\n\nOpen **Settings → Connections → Share access → Host a team** for the guided setup. Choose **This computer** or a saved SSH server under **Server**. Setup targets that server’s own board, independently of the team selected in your browser tab. For an SSH server, choose the HTTPS port and click **Set up on this server**. Disconnected saved servers offer **Connect and set up on this server**. Then complete the five steps:\n\n1. Choose an existing project or create one, and set your name on the board. A local clone on the server is optional.\n2. Define the shared repository keys, labels, Git URLs and default branches.\n3. Choose Tailscale or another HTTPS network, enable team access, and create or import a trusted certificate using the existing secure-access controls. For another network, you manage the hostname and reachability. The wizard installs a separate team server on remote machines; the managed SSH workspace keeps its network listeners disabled.\n4. Create one-use project invitations so teammates choose their own credentials. Direct account creation and granting existing accounts access remain available. Other project roles are preserved.\n5. Review the saved configuration and copy connection instructions containing the HTTPS server origin. Passwords and phone pairing links are never included.\n\nSaved steps remain if you close and reopen setup. You can defer secure access or accounts; the review then identifies the unfinished steps instead of reporting the server as ready. Keep the host running and ask a teammate to confirm they can connect through your network’s access rules. The individual settings below remain available.\n\nFor SSH hosting, the wizard uses the saved SSH connection and the bundled Code Flow release; no manual checkout or npm install is needed. The existing SSH requirements (Node.js 20+ and Git on the login PATH) apply. Installation lives under `~/.codeflow/team-server` on the remote machine, separate from `~/.codeflow/ssh-host`. Projects, accounts, certificates and team data live in `team-server/state`; the private owner token and process identity live in `team-server/runtime.json`. Setup forwards only the project, account, repository and secure-access operations it needs, through the authenticated SSH channel. The browser never receives the remote owner token.\n\nReopening setup reuses a running server and its port, including after disconnecting and reconnecting SSH. Disconnecting SSH or closing the local app leaves the team server running. **Stop team server** in the wizard stops an idle server and keeps its data; starting setup again restarts it. A running older release must be stopped explicitly before setup installs the current release. For Linux servers with systemd, choose **Enable automatic startup** after setup. Code Flow installs `~/.config/systemd/user/codeflow-team-server.service` and verifies both service enablement and [user lingering](https://www.freedesktop.org/software/systemd/man/latest/loginctl.html), which starts the user service manager at boot without an SSH login. If server policy blocks enabling linger, setup shows the administrator command and does not claim startup is ready. **Check startup status** rechecks the actual server configuration; **Disable automatic startup** disables boot startup without stopping the current server or disabling other user services.\n\nAutomatic startup uses the installed release, saved HTTPS port and existing data. Its foreground supervisor restarts on failures under systemd, forwards shutdown to the server, and never prints the owner credential. Enabling startup does not interrupt or duplicate a server that is already running. **Stop team server** stops the current process but leaves startup enabled for the next reboot; disable startup as well to keep it off across reboots. Non-Linux servers and systems without a working systemd user manager report that automatic startup is unavailable; you can still start them from setup. DNS, firewall permissions and certificate trust still need to allow teammates to reach the chosen HTTPS port. Create an account for yourself too if you want to add this server as a team connection on your own computer.\n\nThe team server is a normal code-flow instance with nothing extra installed. Its owner is a maintainer of every project on the board and executes board tasks in-process, on that computer, like any other run.\n\n**Network.** Turn on **Settings → Connections → Share access**, choose **Tailscale · access anywhere** and choose **Set up secure remote access** to configure HTTPS, as described in [Remote access with Tailscale](automations.md#remote-access-with-tailscale). Teammates sign in and runners enrol through that network listener; both are accepted only over HTTPS or a direct loopback connection, so the HTTP read-only link is not enough, and the listener refuses connections from the server computer itself. Allow the remote port (normally 4318) for your teammates' devices in your tailnet's [access rules](https://tailscale.com/docs/features/access-control/grants), and issue the certificate for this computer's tailnet name with [Tailscale HTTPS certificates](https://tailscale.com/docs/how-to/set-up-https-certificates); the link teammates use carries that name once TLS is configured.\n\nAlternatively, choose **Other HTTPS network** in the wizard and import a trusted certificate for your server hostname. Configure DNS and network access to the remote port yourself; Tailscale is optional.\n\n**Accounts.** Create an account for each teammate under **Settings → Connections → Share access → People** and assign a role per project. Each role includes everything the roles before it may do:\n\n| Role | On the team board |\n|---|---|\n| **viewer** | reads the board, tasks, checkpoint manifests and discussion, and their own inbox |\n| **reviewer** | also comments and @mentions teammates, and records a review (approve or request changes) against a checkpoint |\n| **contributor** | also creates and edits tasks, takes an unowned task, adds decisions, requests a review from a named teammate, proposes **Send to agent** instructions, starts, stops and hands over attempts on their own tasks, accepts hand-overs offered to them, downloads checkpoint bundles and connects their computer as a runner |\n| **maintainer** | also assigns any owner, deletes tasks and checkpoints, defines a project's repositories and cancels any hand-over |\n\n**Repositories.** Under **Settings → Connections → Share access → People → Team server**, give each project its **repository keys**: a short key (up to 64 lowercase letters, digits or dashes, starting with a letter), a label and, for information only, the remote URL and default branch. A board task names its repository by key, every teammate maps the key to their own clone, and no local path ever reaches the server. The same card sets the owner's display name as teammates see it (`@owner` in a comment reaches the owner), links to the accounts page and, once remote access with HTTPS is on, shows the server's `https://` address under **Address**, without any path: that is what teammates enter when they connect their computers. Never hand out the phone link from **Settings → Connections → Share access** instead; its path is a secret. With remote access on but no TLS, the card says so and points to secure access setup in Settings.\n\n## Let teammates run tasks on the host\n\nThe host owner can enable **Settings → Connections → Share access → People → Run teammates’ tasks on this computer** per project. First define repository keys and map them to canonical repository folders belonging to that project. Choose the installed CLI (Codex, Claude Code or Antigravity), Safe or Standard agent permissions, time and step budgets, and the individual contributors or maintainers allowed to execute. A project role alone never grants host execution. Check that the provider account or billing arrangement supports your intended team usage; Code Flow launches the installed CLI with its existing login and does not transfer credentials.\n\nAn authorized teammate signs into the shared dashboard, takes a task, and chooses **Run on host computer**. The host runs a single-agent workflow in a Git worktree with the owner-selected CLI, starting from the latest checkpoint when one exists. The browser may close after queuing. Progress, requester attribution and downloadable checkpoint bundles appear on the task. Teammates can download the bundle or use their linked Code Flow installation to fetch it into their own checkout. Files and test commands execute on the host, not the teammate’s computer. Agent approval requests remain with the host owner.\n\nThe **Host queue** shows pending and active tasks in projects the viewer can access. The owner sets 1–4 concurrent host tasks (default 1); a FIFO queue admits at most 50 pending/active requests, and repeated clicks return the existing request. Requesters may remove or stop their own jobs; the host owner may stop any job. Other project members can view progress but cannot cancel someone else’s job. Existing local-runner and owner runs are outside this queue’s concurrency limit.\n\nQueued work and grants are stored privately in `CODEFLOW_HOME/host-execution/queue.json`. The queue retains up to 200 recent completed records alongside active requests. Restart recovery reconciles already-started attempts without replaying them; ambiguous starts become interrupted and may require recovery on the task. A queued request fails if its task content, owner, repository mapping or execution policy changes before launch. Account, project-role and execution-grant revocation are rechecked before dispatch and on the queue’s two-second activity pass; active affected runs are stopped. Changed policies require submitting a new request. Checkpoint publication finishes before capacity is released.\n\nHost execution uses this computer’s own board and repository mappings, independently of saved remote team connections. It is unavailable while filesystem restrictions are active. The agent-task grant does not grant an interactive terminal, a raw shell-command endpoint, arbitrary paths, CLI arguments, or owner-level run controls. However, the agents and repository checks can execute commands with the host’s existing authority. Git worktrees and project grants are not operating-system isolation: grant this feature only to people and projects trusted with that execution authority.\n\nThe closed API surface is `GET /api/host-execution`, owner-only `PUT /api/host-execution/settings {revision, policy?, concurrency?}`, `POST /api/host-execution/tasks/:taskId {revision}` and `POST /api/host-execution/jobs/:jobId/cancel {}`. Browser mutations require same-origin JSON. Runner bearer credentials cannot reach these routes. Shared queue projections omit repository paths, execution configuration and other users’ grants.\n\n## Give trusted teammates an interactive terminal\n\nUnder **Settings → Connections → Share access → People → Teammate terminals**, enable a project and select individual contributors or maintainers. This is a separate, default-off grant from agent tasks. Define the project's repository keys and map them to canonical local folders first. Python 3 PTY support is required on the host; terminals use this computer’s own board and repository mappings and are unavailable while filesystem restrictions are active.\n\nA permitted teammate opens **Board → Your host terminals**, selects a starting repository and chooses **Open terminal**. Typing, pasting, Ctrl-C, full-screen CLI programs and window resizing use a real pseudo-terminal. They can use their own browser on their own computer, but commands, files and installed CLIs run on the host computer. They can reconnect to the same session after refreshing the page. These terminals run independently of the agent-task queue and are not attached to task worktrees or checkpoints.\n\n**This grants trusted access to the host's operating-system account.** A project controls who may open a shell and its starting folder; it does not restrict what commands, files, network resources or credentials that shell can reach. Shell users can edit files, start processes and use installed CLI logins. Separate sessions and application-level ownership are not isolation between people who share an OS account. Use only for teammates trusted with that authority.\n\nEach teammate may have two terminals, with eight total on the host. Only the creator can read or type into a session through Code Flow. The owner sees session metadata and can close any session from Settings. Closing the browser or signing out disconnects the view but leaves the shell available until closed or timed out. After 30 minutes without keyboard input the shell closes, including any foreground command; receiving output or resizing does not reset that timer. Permission, account revision, role, repository mapping and host availability changes reject further input and close affected sessions on the next one-second cleanup pass. Code Flow shutdown closes sessions, and restart never restores them. Closing a terminal does not undo changes or guarantee termination of detached processes.\n\nGrants and up to 500 open/close records are saved privately in `CODEFLOW_HOME/terminal-access.json`; settings show the most recent activity. Command text and output are not written to that audit, although shell history and CLI programs can write their own files. A bounded in-memory output buffer supports reconnects. Terminal output is raw and can contain host paths and secrets; it is not a sanitized shared-review projection.\n\nThe dedicated API is `GET/POST /api/team-terminals`, owner-only `PUT /api/team-terminals/settings {revision, grant}`, `DELETE /api/team-terminals/:id {}`, `GET /api/team-terminals/:id/events`, and `POST /api/team-terminals/:id/input` or `/resize`. Mutations require same-origin JSON. Streams recheck the browser session and terminal access on every frame and once per second, without renewing an idle login. Runner bearer credentials cannot reach these routes, and generic owner shell APIs remain unavailable to shared users.\n\n## Connect your own computer\n\nOpen **Settings → Connections → Share access → People → This computer** in your own code-flow, enter the server's `https://` address (the **Address** its owner sees under **Settings → Connections → Share access → People → Team server**), your account's username and password, and a name for this machine (your hostname by default), and choose **Connect this computer**. Your code-flow sends the credentials once to the server's enrolment route, which verifies them exactly like sign-in (same rate limits, same HTTPS-or-loopback rule) without creating a browser session, requires a contributor role in at least one project, and answers with a per-machine **runner credential**, returned once and kept in the link. Plain `http://` is accepted only for loopback addresses. The credentials, and later the runner credential, go only to the address you entered: a server that redirects the request is refused with 502 \"The team server at `<url>` redirected the request; code-flow talks only to the address you entered, so enter the address the server itself answers at\", and a certificate this computer does not trust with 502 \"This computer does not trust the team server's certificate at `<url>` (…)\": give the server a Tailscale HTTPS certificate for its tailnet name, or trust its certificate on this computer with `NODE_EXTRA_CA_CERTS`. Up to 20 team servers can be saved at once. Your own board remains available even when it holds tasks. Adding the same server twice is refused; choose its saved connection instead. The team server and every linked computer must run the same code-flow version: the checkpoint protocol is not negotiated between versions, and an older team server refuses a newer computer's checkpoint manifests (400, unknown field `id`).\n\nUse **Add team connection** in **Settings → Connections → Share access → People → This computer** for another team. The **Team connection** selector in Settings and on the board switches between saved servers and **This computer’s own board**. Each server supplies its own projects and account roles; map repositories after choosing that server. Projects and repository keys that happen to have the same IDs on two servers remain separate. Switching leaves all connections and ongoing runs active. Each browser tab keeps its selection in its URL (`?team=<connection id>`), so bookmarked task links and tabs for different teams retain their destination.\n\nConnections are stored under `CODEFLOW_HOME/team-connections/<connection id>.json` (mode 0600), with separate credentials, repository mappings and defaults. Passwords are exchanged once for a runner credential and never saved. The old single connection in `team-link.json` is automatically moved to `team-connections/legacy.json`; its runner identity and existing attempt journal are preserved. An unlinked `team-link.json` still holds the local board's mappings.\n\nUnder **Settings → Connections → Share access → People → Repositories**, map each repository key of each project to a clone on this computer (**Use current repository** fills in the repository you have open). A task whose key is unmapped asks you to map it before it can start. Each saved connection has its own event stream and runner, so progress, checkpoints and recovery always go to the team that owns the task, regardless of the visible team.\n\n**Disconnect this team** removes only the selected connection and revokes its runner credential (best effort). Stop or reconcile active/unknown attempts and settle pending checkpoints first. Its old mappings and local attempt records remain on disk for inspection; a new connection gets its own mappings and runner identity. Other teams and the local board keep working.\n\nWhat never leaves this computer: your agent CLI logins, the files `copy` brings into worktrees (`.env` and the like), run transcripts and agent events, local paths and the run's private context. Of the run itself, the server receives only the progress fields and checkpoints described below. Uploaded text is filtered for recognized credentials, environment secrets and local absolute paths before it is truncated. This filtering does not inspect committed Git objects: only commit files intended to be shared with the project. Each teammate runs tasks with their own subscriptions and CLI logins on their own machine, which is the [boundary each vendor draws](security.md#how-code-flow-uses-your-subscriptions).\n\n## The team board and tasks\n\n**Team** in the sidebar (also the command palette entry **Team board**; `#/team`, or `#/team/project/<id>` for one project) shows a column per status, **Planned**, **Working**, **Blocked**, **In review** and **Done**, filtered by project, by owner (**Mine**) and by status, next to your personal inbox. Shared users see the same board under the **Team** tab of their review dashboard. Contributors can run their own tasks on their linked computer, or queue them on the host when its owner has granted execution for the project.\n\nA board task belongs to one project and one repository key and has a title (1–200 characters), a goal (up to 16 KiB) and acceptance criteria (8 KiB), an optional base branch or tag, an owner, up to 20 collaborators, a reviewer, up to 20 tasks it depends on, up to 10 labels and a `revision`. Every save carries the revision it was based on; a stale save fails with 409 \"Task changed; reload before saving\", and the page reloads the task while keeping your draft: the title editor and the blocker form stay open with your text, **Goal** and **Acceptance** keep **Save**, **Discard** and their **Unsaved changes** mark for as long as your text differs from the task (also once the reload shows a teammate's text), and a refused **Depends on** change snaps back to the task's list. A board holds at most 500 tasks, and a project at most 50 repository keys.\n\nStatus rules: any contributor may set any status, except that **Done** is refused (409) while an attempt is claimed, running or unknown or a hand-over is still offered, and **Blocked** requires a blocker text (up to 1000 characters) that is cleared on leaving Blocked. Claiming an attempt moves a Planned, Blocked or In review task to Working on its own, and is refused on a Done task (409 \"Move the task out of Done before starting an attempt\") until someone moves it back; finishing an attempt and accepting a hand-over leave the status alone, so the owner decides when the task goes to review or is done.\n\nOwnership rules: a contributor takes a task nobody owns and gives back a task they own; a maintainer assigns or unassigns anyone; nobody changes the owner while an attempt is claimed, running or unknown on the task (409, naming the runner to stop first). The other way a task changes hands is an accepted hand-over. Either way, attempts the previous owner left paused or stopped become **superseded**: the next attempt starts from a checkpoint, never from one of those worktrees.\n\nAn **attempt** is one execution of a task on one runner, at most 50 per task. It records who ran it where, the exact base commit it started from, the checkpoint it continued from, its run id on that computer, its state (claimed, running, paused, finished, failed, stopped, unknown or superseded), its last progress report and the checkpoint it produced. Only one attempt can hold execution (claimed, running or unknown) on a task at a time, and nothing can be claimed while a hand-over is offered; the same rule refuses a report that would revive a paused, stopped or unknown attempt (409 \"Stop the attempt running on `<runner>` before resuming this one\", or \"Accept, decline or cancel the offered hand-over before resuming the attempt\"); a revive is refused on a Done task as well (409 \"Move the task out of Done before resuming the attempt\"), and for a machine whose user no longer owns the task (403 \"The task now belongs to `<name>`; they continue it from its latest checkpoint, or a project maintainer reassigns it\", or \"The task has no owner now; take it to start a new attempt from its latest checkpoint\"). Once an hour the server marks a claimed or running attempt it has not heard from for 24 hours **unknown**, with a reason.\n\n## Drafting a task with an agent\n\nOn **Team**, choose **Draft with agent**, select a project and repository, describe the idea, and choose an installed agent and the local repository folder. Drafting runs remain owner-only in local history, including when the repository belongs to a shared project. **Generate draft** starts a local read-only run with Safe permissions, a five-minute budget and at most two agent calls. It uses the selected base branch (or the clone's HEAD), skips repository setup/check commands and copied files, and does not create a shared task or claim an execution attempt. The run remains available in run history; **Open drafting run** shows its details and **Stop drafting** cancels it.\n\nReview the proposed title, goal (including scope, implementation plan and questions) and acceptance criteria, then choose **Use draft in form**. Edit those fields and the human owner/reviewer before **Create task**. Saving opens the task, ready for **Build with agents**; creating a task never starts a build automatically. The ordinary **New task** form remains available without an agent. Agent drafting and execution are local-computer controls, unavailable in the shared browser dashboard.\n\n## Running a task on your computer\n\nOpen the task and choose **Build with agents**: you must own the task, and this computer must be linked or be the team server. Pick the repository folder (prefilled from your mapping), workflow, agents and permissions. Single agent suits small changes; Auto team supports a coordinator and multiple worker/reviewer providers. Models and reasoning can be adjusted in the dialog, along with the check command, time budget and agent call budget. Leave the check command blank to use the repository default (workflows requiring a check ask for an explicit command). Select your settings, and tick **Continue from the latest checkpoint** when one exists. Your code-flow then, in order:\n\n1. resolves the base commit: the latest checkpoint's commit when continuing (downloaded and imported first, as described under [Handing a task over](#handing-a-task-over)), otherwise the task's base branch, or `HEAD` of your clone, and checks that the repository can carry checkpoints: one with a submodule, a checkout filter such as Git LFS in `.gitattributes`, or a tree over 20,000 files, 256 MiB or 100 attribute files is refused before anything is claimed (400 \"This repository cannot carry team checkpoints (…); use a repository without submodules or checkout filters for board tasks\");\n2. writes a local attempt record, then **claims** the attempt on the server under a fresh attempt id: a repeated claim with the same id is idempotent, the server refuses while another attempt is claimed, running or unknown or a hand-over is offered, and a continuation must start from the latest checkpoint unless you are a maintainer; an earlier attempt of the task that is stuck on this very computer (claimed, with nothing running behind it) is settled first instead of blocking the start;\n3. starts an ordinary run in a worktree of your clone, on the engine's usual `codeflow/<run id>/<worktree>` branch (shown as the attempt's branch; only fetched checkpoints land on `team/<task id>/…` branches), with a prompt made of the task's title, goal, acceptance criteria and decisions and, when continuing, the checkpoint's context fenced as information; the prompt names the board task, and the run page shows a **Team task** link back to it;\n4. publishes **progress** while the run goes: the current stage, a summary (4 KiB), the run status, up to 100 changed file paths, the last check (its command and whether it passed), the verdict, up to 10 blockers, the branch and head commit, and the step counts. Reports are numbered, so the server ignores one that arrives late and an attempt never moves backwards; publishing is throttled to one report every two seconds and stops when the run ends (the final snapshot, with the head and the changed files, travels with the finish), and a heartbeat every 20 seconds keeps the attempt live and brings back approved instructions;\n5. when the run ends, creates a **checkpoint** if the branch moved past the base (its id is minted here, and the bundle is kept under `CODEFLOW_HOME/team-link/bundles/<checkpoint id>.bundle` until the server has it), uploads it, and finishes the attempt as *finished* (the run is done), *failed*, *stopped* (a budget or limit stop; resumable) or *paused* (cancelled, or interrupted by a restart: resume from this computer). When no checkpoint can be published for good (a repository with checkout filters or submodules, a bundle over 64 MiB, a manifest an older team server refuses), the attempt is still finished on the server, as *failed* when the run was done and otherwise in its paused or stopped state, with the reason \"No checkpoint was published: `<message>`\"; the task is released and the work stays on this computer's branch.\n\nAttempt ids are stable: a restart of your code-flow reconciles its local attempt records with the server instead of starting anything again. A run that is still going is re-attached and the reports the server missed are re-sent; a run that ended is published (an existing checkpoint and its kept bundle are reused); a run that is missing marks the attempt **unknown** with a reason and retains the reservation. Only a journal still at the pre-launch claiming step proves no run was started, allowing that claim to finish as failed automatically. Reconciliation never re-executes anything. A publication that failed is retried at once on reconnect, by a recovery pass that runs every minute with backoff (30 seconds, doubling to 30 minutes), or on demand with `POST /api/team-link/reconcile`; after 48 failed tries, about a day, the retries stop and this computer keeps the run's outcome in its local record. A refusal the server will never lift (401, the credential is gone; 403, this computer was re-linked under another runner id; 409, the attempt is already closed) ends the retries at once, and a bundle kept for a checkpoint that was given up is deleted.\n\nA board card shows a live dot while the server has heard from the runner within the last 90 seconds. After that the task shows **Connection lost; execution status unknown**: the run may still be going on that computer, or may have ended without the server hearing about it. An expired heartbeat never releases execution ownership: starting another attempt, changing the owner and handing over remain blocked. The runner can reconnect and reconcile its state. If it cannot, the task owner or a project maintainer uses **Recover attempt…**, confirms the original agent has stopped (for example, by shutting down that computer), and records how they verified this. Recovery supersedes the old attempt and records the decision in History before releasing the task. A stale recovery is refused if the runner has already reconnected. **Stop** cancels the run (it stays resumable) and records the attempt as paused; **Resume** continues the same attempt under the same id, and is offered for a paused attempt, one stopped at a budget or limit and one marked unknown, when it ran on this computer. The server has the last word: Resume is refused with 409 once the task changed hands (\"The task now belongs to `<name>`; they continue it from its latest checkpoint, or a project maintainer reassigns it\"), on a Done task (\"Move the task out of Done before resuming the attempt\"), when the attempt was superseded or already ended, and while another attempt is claimed, running or unknown or a hand-over is offered; the server reserves execution before the local agent resumes, and an unreachable server leaves the agent stopped. If a running agent later loses its credential or its attempt is superseded, this computer stops it as well as its reports. **Open run** opens the run page for any attempt that ran on this computer, and the latest attempt stays on the task page after it ended (\"No attempt yet.\" appears only on a task without attempts).\n\n## Discussion, review requests and Send to agent\n\nEvery task has a **Discussion** (comments of up to 4000 characters, 500 per task), **Decisions** (2000 characters each, 100 per task; they are snapshotted into every checkpoint and read by the next agent), **Reviews** and a collapsed **History** of up to 200 audit entries, oldest dropped. `@username` in a comment mentions a project member. Assignments, comments, mentions, review requests, recorded reviews, hand-over offers and answers, proposed instructions, blocked tasks and finished or failed attempts create entries in the personal **inbox** of the people concerned, 500 per person, oldest read items dropped first; reading is personal, so one teammate's read marks never clear another's, and you are never notified of your own actions. Removing project access stops new notifications and hides that project’s existing inbox items and unread counts. Only the hub owner can list or revoke other accounts’ runners; project maintainer permissions do not grant account-wide administration.\n\nA contributor can request a review from a named teammate (403 \"A project contributor role is required to request reviews\" otherwise); a reviewer or higher records a review against the latest checkpoint. A comment can instead be sent as an instruction with the **Send to agent** toggle: it is *proposed*; the task owner, or a maintainer of the project, *approves* or *declines* it; an approved instruction is *delivered* on the runner's next heartbeat as a note to the attempt's run, the way your own messages to a running team are, exactly once, and marked so. The owner's own instructions are approved at once. The approval is the trust boundary: an approved instruction reaches the agent as a message to the run, attributed to its author (\"Instruction from `<name>` via the team board\"), whereas a checkpoint's context reaches the next agent fenced as information from teammates, the way [webhook and issue text](github.md#untrusted-text) does, with a note that embedded instructions to change its task, tools or trust are not to be followed. Human comments otherwise never enter a prompt, as on the [shared review](shared-projects.md) pages.\n\n## Handing a task over\n\nChoose **Hand over…** on a task you own, pick a teammate who is a contributor in the project, write a note (up to 4000 characters) and, if the worktree still has uncommitted tracked changes, tick **Include uncommitted changes** to commit them on the run branch as \"Hand-over checkpoint\"; without it the hand-over is refused with 409 \"Commit, discard, or include uncommitted changes first\". The sequence is:\n\n1. the current attempt must not be claimed, running or unknown: stop it or complete recovery first (409 otherwise);\n2. your code-flow takes the attempt's latest checkpoint, or creates one from the worktree now; a retry after a failed offer reuses the pending checkpoint and uploads its kept bundle rather than creating another. When the task's current attempt is a teammate's (the task came back to you after a round trip), the task's latest checkpoint is offered, and the offer is refused with 409 when your old branch holds commits made after your attempt was superseded (\"Branch `<branch>` on this computer holds work made after its attempt was superseded by a change of owner, which cannot be handed over; start a new attempt from the latest checkpoint to continue from it\");\n3. the server records an **offer** to the teammate and notifies them; the task stays yours, with its status;\n4. the teammate chooses **Accept and download** on their computer: the bundle is downloaded and imported into their clone of the repository *before* ownership moves; a comment or review that lands during the download does not fail the acceptance, only an offer that no longer stands does (409 \"This hand-over was already `<state>`\");\n5. the server then marks the hand-over accepted, makes them the owner and supersedes your paused or stopped attempts;\n6. they continue with **Build with agents** and **Continue from the latest checkpoint**, with whatever pattern and agents they choose; the new attempt's base is the checkpoint commit, and the agent reads the checkpoint's context.\n\nA checkpoint is a cumulative Git bundle of all contributions since the original shared base commit, including earlier teammates’ work. Continuing an attempt starts at the prior checkpoint’s head, but the next bundle retains that checkpoint’s original base so a fresh teammate needs only the latest bundle, even after earlier bundles are deleted. The bundle is limited to 64 MiB and has a SHA-256 digest. Its manifest (up to 1.25 MiB) records: the checkpoint id, commit, base commit, branch, your note, the task's goal and acceptance criteria at that moment (16 KiB), the decisions, the last test result, the remaining work (8 KiB) and the changed files. Importing requires the base commit to exist in the recipient's repository; otherwise the import fails with 409 \"Fetch `<base>` into this repository first\" and the recipient fetches it from the repository's normal remote. Imports verify the file's size and digest, that the bundle's only ref names this checkpoint (otherwise 409 \"The downloaded bundle does not belong to checkpoint `<id>`; it advertises `refs/codeflow/team/<other>`. Delete this checkpoint and create a new one from the computer that holds the work\", and the download is removed), the bundle's advertised head, Git's object checks, the ancestry from the base and the tree (no submodules, no checkout filters such as Git LFS), land only in `refs/codeflow/team/<checkpoint id>`, and never touch `HEAD`, branches or the working tree. The checkpoint id is minted on the computer that creates the bundle and travels in the manifest, so the ref inside the bundle, the id on the board, the kept bundle file and the branch a fetch creates all carry the same id.\n\nA failed download or import, a declined offer or a cancelled offer changes nothing on the server: the task stays with its current owner and keeps its checkpoints. The sender or a maintainer can **Cancel hand-over** while it is offered. Repositories with submodules or checkout filters cannot carry checkpoints and so cannot be used for board tasks; **Build with agents** refuses them before anything is claimed.\n\n## Reviews and integration\n\nA review (approve or request changes, with up to 4000 characters) is bound to a checkpoint and its commit. When a newer checkpoint appears, earlier reviews are marked **stale**, and the task's review state (approved, changes requested, pending, none) counts only the latest checkpoint's current reviews, the most recent verdict winning. Requesting a review notifies the reviewer and shows on the card.\n\nTo review or integrate on any computer, choose **Fetch to this computer…** on a checkpoint (contributor or higher, on a linked instance or the team server): the bundle is imported into `refs/codeflow/team/<id>` and the local branch `team/<task id>/<first 8 characters of the checkpoint id>` is pointed at it without being checked out; a branch that is checked out somewhere, or that holds commits the checkpoint lacks, is never moved. From there the existing flows apply: compare, validate and merge the branch as you would any result branch, or open a pull request from it. Merging the results of several tasks together is a manual step in this version; nothing merges on its own.\n\n## Storage, limits and boundaries\n\nOn the team server everything lives under `CODEFLOW_HOME/team/` (mode 0700): `board.json` (each project's repository keys and its tasks with their attempts, checkpoint manifests, discussion, decisions, instructions, reviews, hand-overs and audit), `runners.json` (runner ids, names, last-seen times, the address each last connected from and the SHA-256 hashes of their credentials, never the credentials), `inbox.json` and `checkpoints/<checkpoint id>.bundle` (mode 0600). Files are written whole through a temporary file and renamed; a corrupt file is moved aside to `<name>.corrupt-<time>` and logged, and the board starts empty but writable; a file that cannot be read at all makes team storage unavailable, and every change fails with 503 until it is fixed. Every change is on disk before it is announced on the change streams, with one exception: heartbeats and progress reports on a running attempt (and a runner's last-seen time) are applied in memory and announced at once but reach `board.json` with the next durable change, the 30-second write-behind or shutdown; the first report of a claimed attempt, a resume, a finish and a checkpoint are written at once. At startup the team server removes what an interrupted upload left in `checkpoints/`: every `<id>.bundle.<hex>.tmp`, and every `<id>.bundle` that no checkpoint lists as available (not when `board.json` was set aside as corrupt, so a hand-repaired board keeps its bundles); the removal is logged. On a linked computer, `CODEFLOW_HOME/team-connections/<connection id>.json` holds each link. `CODEFLOW_HOME/team-runners/<connection id>/attempts/<attempt id>.json` ties local runs to attempts, and its sibling `bundles/` holds pending uploads. Local and migrated legacy records stay in `team-link/`, filtered by their hub origin.\n\nBounds: 500 tasks per board; per task 50 attempts, 50 checkpoints, 500 comments, 100 decisions, 50 instructions, 100 reviews, 50 hand-overs of history and 200 audit entries (oldest dropped); 50 repository keys per project; 20 collaborators, 20 dependencies and 10 labels of 40 characters per task; titles of 200 characters, goals of 16 KiB, acceptance criteria of 8 KiB, comments, notes, instructions and reviews of 4000 characters, decisions of 2000, blockers and reasons of 1000; progress summaries of 4 KiB, 100 changed-file paths of 300 characters and 10 blockers of 500 characters; checkpoint bundles of 64 MiB and 200 bundles on disk (when full, the oldest bundle of a Done task is deleted and its manifest kept; when none can be, the upload fails with 507 until checkpoints of finished tasks are deleted); 64 runners, 8 active per person; 500 inbox items per person and 5000 in all; request bodies of 64 KiB for task saves and repository keys, 192 KiB for progress reports, 1.25 MiB for checkpoint manifests (both sized so the character bounds fit in four-byte UTF-8), 64 MiB for bundles, 8 KiB for enrolment and 16 KiB otherwise; 16 MiB per board file, after which every change fails with 413 \"The team board has reached its 16 MiB storage limit; delete finished tasks first\" (deleting a checkpoint keeps its manifest, so only deleting tasks shrinks the board); change streams 4 per person and 128 in all, a stream being closed once 2 MiB of frames wait on its socket (the client reconnects with its cursor). An attempt counts as live for 90 seconds after its last heartbeat, is marked unknown after 24 hours without one, and a runner shows as online for 90 seconds after its last request.\n\nBoundaries: a runner credential is a bearer token (`tr-` and 43 characters, shown once at enrolment) bound to one account and one machine. It reaches only `/api/team/*`: every other API route, the owner's and the shared review routes included, answers 403 \"This credential only reaches the team API\", the pages treat it as an anonymous visitor (it never becomes a session), and cookies sent beside it are ignored; it stops working as soon as the account is disabled or the runner is revoked (401 \"This runner credential is not valid; connect this computer again in Settings → Connections → Share access → People\"); and the server stores only its hash. The owner's own `CODEFLOW_TOKEN` is matched first, so an owner token that happens to have that shape still signs the owner in on the local listener. Runner enrolment is accepted over HTTPS or loopback only, like shared sign-in, and a linked computer refuses to store a plain `http://` server address that is not loopback. The network listener refuses the server computer's own connections, so the server's owner executes in-process rather than through a runner. Tasks are projected per role: a teammate sees only the projects where their account holds a role, people appear as an account id and display name, and no local path, run transcript, token or hash ever leaves the server. The other way round, a linked computer reads at most 64 KiB of an error answer from the server, and its progress, summaries, blockers, test commands, checkpoint text and failure reasons use the existing secret redaction before truncation and have its repository, worktree and home folders and any other absolute path replaced by `<local path>`. Every mutation carries a revision and fails with 409 when stale; a missing role answers 403 naming the role; a task you may not see is 404. Custom [filesystem policies](filesystem-policy.md) pause starting attempts like other app-managed execution.\n\nEncrypted [backups](maintenance.md) include `team/` (the board, the runner registrations with their credential hashes, the inbox and the bundles) and the `team-link/` and `team-runners/` attempt journals, but leave out `team-connections/` and `team-link.json`, which carries this computer's runner credential, like the other credential stores; a restore of any archive skips it too and lists it under `omitted`. A restored computer therefore comes up not linked, without repository mappings or defaults: connect it again under **Settings → Connections → Share access → People** and map its repositories; its restored attempt records are read only to reconcile, never to start anything. The server side holds only credential hashes, and agent CLI logins are not in `CODEFLOW_HOME` at all. Revoke a runner on the team server (**Settings → Connections → Share access → People → Runners**) if a copy of a connection credential file leaves your control.\n\n## API\n\nPaths are absolute; `/api/v1/team/*` and `/api/v1/team-link/*` are aliases. Browser sessions (the owner and shared accounts) and runner credentials (`Authorization: Bearer tr-…`) use the same team routes; the table says who may call. Runner mutations send `content-type: application/json` (the bundle upload `application/octet-stream`) and need no Origin header; browser mutations keep the same-origin JSON requirement of shared accounts. Mutations return the updated task; a stale `revision` fails with 409, a missing role with 403 naming the role, an unknown or invisible id with 404 \"Task not found\". Unknown team routes answer 404 \"Unknown team operation\" and never fall through to owner routes. Progress reports and heartbeats do not change the task revision.\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `POST /api/team/runners/connect` | `username`, `password`, `name` | Anonymous enrolment over HTTPS or loopback: verifies the credentials like sign-in without creating a session, requires a contributor role in some project, and returns 201 with the runner, its credential (shown once), the user and the server's name. |\n| `GET /api/team/me` | none | The caller's identity, project roles and own runners. |\n| `GET /api/team/board` | optional `project` query | The board: the projects where the caller holds a role with their members and repository keys, task cards with the current attempt, latest checkpoint and hand-over, and the unread inbox count (viewer). |\n| `GET /api/team/tasks/:id` | none | The full task (viewer in its project). |\n| `POST /api/team/tasks` | `projectId`, `repository`, `title`, `goal`, `acceptance`, optional `baseRef`, `owner`, `reviewer`, `collaborators`, `dependsOn`, `labels`, `status` | Create a task (contributor); 201. |\n| `PUT /api/team/tasks/:id` | `revision` plus the changed fields | Partial save under the status and ownership rules; `null` clears an optional field. |\n| `DELETE /api/team/tasks/:id` | `revision` | Delete a task (maintainer); `{deleted: true}`. |\n| `POST /api/team/tasks/:id/comments` | `text` | Comment, with `@username` mentions (reviewer). |\n| `POST /api/team/tasks/:id/decisions` | `text` | Record a decision (contributor). |\n| `POST /api/team/tasks/:id/review-request` | `reviewer`, `revision` | Ask a teammate to review (contributor). |\n| `POST /api/team/tasks/:id/reviews` | `checkpointId`, `verdict=approve\\|request-changes`, `text`, `revision` | Record a review against a checkpoint (reviewer). |\n| `POST /api/team/tasks/:id/instructions` | `text` | Propose a **Send to agent** instruction (contributor); the task owner's own are approved at once. |\n| `POST /api/team/tasks/:id/instructions/:iid` | `action=approve\\|decline\\|delivered`, optional `attemptId` | Approve or decline (the task owner or a maintainer); `delivered` is recorded by the attempt's runner. |\n| `POST /api/team/tasks/:id/attempts` | `attemptId`, `runnerId`, `runnerName`, `baseCommit`, optional `fromCheckpointId`, `revision` | Claim an attempt (the task owner through their runner, or the server's owner in-process as runner `local`); idempotent on `attemptId`; 201. Refused (409) while another attempt is claimed, running or unknown, while a hand-over is offered, and on a Done task. |\n| `POST /api/team/attempts/:aid/progress` | `seq`, optional `runId`, the progress fields | Publish a numbered progress snapshot (the attempt's runner); a late number is ignored; 192 KiB. A report that would revive a paused, stopped or unknown attempt is refused (409) while another attempt of the task is claimed, running or unknown or a hand-over is offered, on a Done task (409 \"Move the task out of Done before resuming the attempt\"), and for a machine whose user no longer owns the task (403 \"The task now belongs to `<name>`; they continue it from its latest checkpoint, or a project maintainer reassigns it\"). |\n| `POST /api/team/attempts/:aid/heartbeat` | empty body | Keep the attempt live; returns the approved, undelivered instructions. |\n| `POST /api/team/attempts/:aid/finish` | `state`, optional `head`, `summary`, `reason` | End the attempt as finished, failed, stopped, paused or unknown. |\n| `POST /api/team/attempts/:aid/recover` | `revision`, `reason`, `confirmedStopped=true` | Recover an unknown attempt after confirming its original execution stopped (task owner or project maintainer). Supersedes it, records the decision and releases the task; stale revisions or a reconnected runner return 409. |\n| `POST /api/team/attempts/:aid/checkpoints` | optional `id`, `commit`, `baseCommit`, `branch`, `size`, `sha256`, `note`, `requirements`, `decisions`, optional `testResults`, `remaining`, `changedFiles` | Register a checkpoint manifest with `bundle: \"pending\"` (the attempt's runner); 201; 1.25 MiB. `id` (32 lowercase hexadecimal characters) becomes the checkpoint's id and ref `refs/codeflow/team/<id>`; an id already on the board is refused (409), a malformed one 400, and without one the server generates the id. |\n| `PUT /api/team/checkpoints/:cid/bundle` | `application/octet-stream` body, `x-codeflow-sha256` header | Stream the bundle to disk (the checkpoint's runner); size and digest are verified before the checkpoint becomes available. A refusal (415, 400 on a size or digest mismatch, 409 when already uploaded or deleted, 507 when storage is full, 503 when the file could not be stored) is answered at once and the declared body is then drained, so a retry reads the answer; a body without a content-length (411) or declared over 64 MiB (413) is cut off once the answer is written. |\n| `GET /api/team/checkpoints/:cid` | none | The checkpoint manifest (viewer). |\n| `GET /api/team/checkpoints/:cid/bundle` | none | Download the bundle with `content-length` and `x-codeflow-sha256` (contributor). |\n| `DELETE /api/team/checkpoints/:cid` | none | Delete a checkpoint and its bundle (maintainer); `{deleted: true}`. |\n| `POST /api/team/tasks/:id/handoff` | `to`, `checkpointId`, `note`, `revision` | Offer a hand-over from an available checkpoint of the task (the task owner or a maintainer); refused while an attempt runs. |\n| `POST /api/team/tasks/:id/handoff/:hid` | `action=accept\\|decline\\|cancel`, `revision` | Accept or decline (the recipient); cancel (the sender or a maintainer). |\n| `GET /api/team/inbox` | optional `after` query | The caller's inbox items and unread count. |\n| `POST /api/team/inbox/read` | `ids`, or `all: true` | Mark the caller's items read; returns `{unread}`. |\n| `GET /api/team/runners` | none | The caller's runners; only the hub owner sees all. |\n| `DELETE /api/team/runners/:id` | none | Revoke a runner (its user or the hub owner); `{revoked: true}`. |\n| `PUT /api/team/projects/:id/repositories` | `repositories`, `revision` | Define the project's repository keys (maintainer). |\n| `PUT /api/team/profile` | `displayName` | The owner's display name on the board (owner). |\n| `GET /api/team/events` | optional `after=<epoch:seq>` query | Server-sent events for the caller: `hello` with the epoch and sequence, then `change` events for the board and tasks in the caller's projects and `inbox` unread counts; a cursor the server no longer holds gets `resync`. Closed when the account changes or the runner is revoked, and when 2 MiB of frames wait on the socket. |\n\nThe owner-only routes below live on the local listener of each computer. On a linked computer they drive execution against the team server; on the team server itself they work in-process, and `linked` is false.\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /api/team-link` | none | The link: `linked`, the server address, user, server name, machine name, mappings and defaults, the connection state, the runner as the server reports it, the last sync and the last error. |\n| `POST /api/team-link/connect` | `url`, `username`, `password`, optional `name` | Enrol this computer: `https://` only (plain `http://` for loopback), never following a redirect and only with a trusted certificate (502 otherwise), refused (409) while already connected or while the local board has tasks; saves the link and starts the change stream and reconciliation; 201. |\n| `DELETE /api/team-link` | none | Revoke the runner on the server (best effort) and forget it here; mappings, defaults and local attempt records stay. |\n| `PUT /api/team-link/mappings` | `projectId`, `repository`, `path` | Map a repository key to an absolute folder that is a Git repository. |\n| `POST /api/team-link/draft` | `projectId`, `repository`, `idea`, `agent`, optional `repo`, `baseRef`, `agentSettings` | Start a bounded read-only task-drafting run on this computer; returns `{runId}`; 201. Requires contributor access to the project and an installed agent. Poll the ordinary run endpoint for the validated draft JSON in its completed summary. |\n| `POST /api/team-link/tasks/:id/start` | optional `repo`, `pattern`, `roles`, optional `agentSettings`, `roleSettings`, `trust`, `loop` (`check`, `setup`, `maxMinutes`, `maxSteps`), `fromCheckpointId` | Claim an attempt and start the run here; returns `{attemptId, runId}`; 201. Refused (400) before anything is claimed when the repository cannot carry checkpoints (a submodule, a checkout filter such as Git LFS, or a tree over 20,000 files, 256 MiB or 100 attribute files). |\n| `POST /api/team-link/tasks/:id/stop` | empty body | Cancel the current attempt's run (resumable) and record it as paused. |\n| `POST /api/team-link/tasks/:id/resume` | empty body | Resume this computer's paused, stopped or unknown attempt of the task under the same attempt id; refused (409) when the attempt was superseded or has ended, the task changed hands or is Done, another attempt is busy or a hand-over is offered. |\n| `POST /api/team-link/tasks/:id/handover` | `to`, `note`, optional `includeUncommitted` | Create or reuse the checkpoint (a retry uploads the kept bundle), then offer the hand-over. |\n| `POST /api/team-link/tasks/:id/accept` | `handoffId`, optional `repo` | Download and import the checkpoint, then accept the offer. |\n| `POST /api/team-link/checkpoints/:id/fetch` | `repo` | Import a checkpoint and point `team/<task id>/<first 8 characters of the checkpoint id>` at it; returns `{commit, branch, taskId, created}`. |\n| `GET /api/team-link/attempts` | none | This computer's attempt records: which run belongs to which attempt, and what has been published. |\n| `POST /api/team-link/reconcile` | empty body | Run the recovery pass now: settle claims whose answer was lost and retry unacknowledged finishes; returns `{attached, finished, removed, unknown, retried}`. |\n\nIn the owner's app the browser's own event stream carries the same board changes as a `team` topic, and the Team page also polls every 15 seconds; the shared dashboard's Board tab holds one `/api/team/events` stream instead (its runs stream is closed while the tab is shown), reloads on its frames and when the tab is shown again, and sends no periodic request of its own. Nothing in this API replays automatically: a restart reconciles local attempt records with the server and never starts a run again, and a dropped response is not proof that a mutation did not happen; reload the task before repeating one.\n\n## Validation status\n\n`npx tsx --test test/collab-model.test.ts` covers the validators and bounds, status transitions, ownership rules, mentions, board summaries and the review state. `test/collab-store.test.ts` covers persistence and atomic writes, a corrupt file moved aside, revision conflicts, role enforcement inside the store, claim idempotency and the one-busy-attempt rule, progress ordering and liveness, finish states, checkpoint manifests with bundle storage and digest verification, the hand-over state machine, personal inbox read state, hashed and revoked runner credentials and the bounds. `test/collab-api.test.ts` drives the team API with fake requests: the closed route table, role refusals, a runner that cannot report on another person's attempt, projection privacy, the change stream, the enrolment handshake (bad passwords, viewer-only accounts and insecure transports refused), a refused upload answered over a real socket, and body limits that fit the character bounds in UTF-8. `test/collab-checkpoints.test.ts` creates a bundle from a repository with a base and two commits and imports it into a clone that has the base (ref created, `HEAD` untouched), a clone without it (clear error), with tampered bytes and over the size limit, and checks that the checkpoint context fences every teammate text. `test/collab-runner.test.ts` covers the runner against both hubs: progress and checkpoints, stop and resume with delivered instructions, hand-over and acceptance, reconciliation and the recovery pass, local paths scrubbed from reasons, refused finishes and resumes, a kept bundle retried, repositories that cannot carry checkpoints, the redirect and certificate refusals, bounded error bodies and the quiet-board stream. `test/collab-link.test.ts` is the acceptance test: it spawns a team server and two linked instances with their own homes and ports over loopback HTTP, with the fake agent CLIs, and walks a task from creation through an attempt, a checkpoint, a hand-over to the second teammate, a continuation from the checkpoint, a restart that starts nothing again and a revoked runner, and checks that a viewer session reads the board but cannot create tasks. `test/e2e/team.e2e.ts` opens the board in headless Chrome, defines a repository key under Settings → Connections → Share access → People and maps it, creates a task from the board form, opens it, adds a comment, a decision and a blocker, audits the board, the task page and Settings → Connections → Share access → People with axe in light, dark and phone layouts, and signs a shared contributor in who finds the task under the Board tab and comments without seeing any execution control.\n\nNo real Tailscale network, second physical machine, TLS certificate or real provider login was exercised by these tests; the acceptance test uses loopback HTTP, which enrolment accepts in place of HTTPS. Validate the Tailscale and HTTPS setup on your own tailnet before relying on it.\n\n### Selecting connections through the owner API\n\n`GET /api/team-link/connections` lists saved connection IDs and public connection state, including `local`. `POST /api/team-link/connect` adds a server and returns its ID. Scope `/api/team/*` and `/api/team-link/*` calls with `?teamConnection=<id>` or the `X-Codeflow-Team` header; this selector is consumed locally and is never sent to another server. Downloads use the query form. `DELETE /api/team-link?teamConnection=<id>` removes that connection. An unknown explicit ID returns 404; an omitted selector with multiple remote connections returns 409 rather than choosing a team. With one remote connection, unscoped clients continue to use it; with none, they use the local board. Shared accounts and runner credentials always access the receiving server's own board and cannot select the owner's other connections. The owner event stream uses `team:<connection id>` topics.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Team collaboration"},{"group":"Connect and collaborate","id":"shared-projects","source":"docs/shared-projects.md","body":"# Shared project accounts\n\nCreate a local account and grant roles only for the projects that person should see. Review the role table before allowing contributions or maintenance, and revoke access when it is no longer needed.\n\n## Share review access\n\nThe server owner can create local accounts and assign one of four project roles; each role includes everything the roles before it may do. A **viewer** reads project run results, safe verification metadata, changed-file diffs and sanitized result exports. A **reviewer** also records human file-review coverage and comments in a separate thread anchored to the exact result commit. A **contributor** also owns tasks on the project's team board and runs them on their own computer with their own agents. A **maintainer** also assigns any task owner, defines a project's repository keys, deletes board tasks and checkpoints, revokes runners, cancels hand-overs and approves instructions to agents; accounts and roles stay with the owner. The contributor and maintainer capabilities are described in [Team collaboration](team-collaboration.md). The owner reads and replies to comments through **Changes → Human collaboration comments** on the normal run page. Comments are collaboration records: they never enter an agent's prompt or resume work.\n\nThe owner retains administration and generic run controls. Project roles alone do not allow users to start or resume arbitrary runs on this server, approve agent/tool requests, send agent notes, run checks/setup/services, change repository files, choose Docker images or authentication volumes, use terminals, merge/push/post to GitHub, edit private memory/conventions, configure integrations, manage accounts or inspect global filesystem/settings/quotas. Project assignment is not an operating-system sandbox. An explicit owner grant under Settings → Connections → Share access → People allows a contributor to queue their own team tasks on the host through the dedicated [host execution queue](team-collaboration.md#let-teammates-run-tasks-on-the-host). This delegates host execution authority to trusted teammates; it does not provide an operating-system sandbox. Generic run and shell APIs remain owner-only. A separate [interactive terminal grant](team-collaboration.md#give-trusted-teammates-an-interactive-terminal) lets a trusted contributor open their own shell on the host. That shell has the host OS user’s authority, including file changes and access outside the project; the review API restrictions do not sandbox terminal commands.\n\nThe review dashboard uses independent bootstrap requests so shared users do not load the normal composer's global health/configuration APIs. Lists, searches, cursors, attention items, run events, reconnect snapshots and exports must be filtered by current project membership on the server. Unauthorized object identities are answered as unavailable. The dashboard holds one board/review event stream for the tab it shows (`/api/events` on Results, `/api/team/events` on Board), reloads on its frames and loads the bell's unread count once with the page, keeping it live only while the Board tab is shown; it sends no periodic request of its own, so an idle dashboard does not renew its session by polling. An open interactive terminal has its own authenticated output stream. Removing a repository from a project removes shared access to its runs. An ambiguous legacy run without an explicit project is not shared automatically.\n\nShared review projections omit local paths, run commands, environment/volume configuration, raw agent tool events/logs, session identifiers, capability receipts, context/manifests, notes, memory and private conventions. Result summaries and final output are project-visible and have recognized secret patterns redacted; arbitrary sensitive prose cannot be detected reliably. Credential files and rename sources are omitted from shared diffs and coverage. Coverage describes only shared-reviewable files and reports omitted files requiring owner review. Parent/continuation references are included only when that related run is also authorized. Private conventions drafts are owner-only even when a user is assigned to their project.\n\nShared results bound task text to 24 KiB, summaries to 32 KiB and errors to 4 KiB of UTF-8. Only the latest 200 steps are included, with 8 KiB final-output and 4 KiB error previews per step. Truncation and omitted-step counts are explicit in the API and dashboard. Exports retain these same safe preview limits. Raw transcripts are not shared exports.\n\n## Authentication and storage\n\nAccounts live in private `CODEFLOW_HOME/shared-accounts.json`; the server never returns password hashes or session hashes. Passwords require at least 15 characters and at most 1024 UTF-8 bytes, without normalization or truncation. Built-in scrypt uses a fresh 16-byte salt, a 32-byte key and parameters `N=131072, r=8, p=1`. Verification compares keys in constant time and performs a dummy derivation for unknown accounts. At most two password derivations run concurrently; a full queue is refused. Login attempts have bounded global, socket-address and username rate limits.\n\nSessions use 32 random bytes and store only SHA-256 hashes. They expire after 12 hours or 30 minutes idle; each account retains at most eight sessions. Role/display-name/enabled/password changes and owner revocation invalidate every session for the account. Session activity extends idle time in memory; a restart can expire a session earlier than its last activity. Revoked identities must lose open streams immediately. Corrupt or symbolic account storage disables shared authentication without overwriting the file; the owner's printed recovery-token link remains available.\n\nNetwork sign-in and authenticated shared requests require actual HTTPS, independently of the phone development override. Cookies are HttpOnly and SameSite=Strict, Secure on HTTPS. The server validates Host/Origin and mutation request type. Shared cookies are distinct from owner and phone sessions. An explicit valid owner bearer credential on the local listener takes precedence; otherwise a valid shared cookie takes precedence. The network listener refuses local owner bearer credentials and recovery links; use its separate phone pairing or shared sign-in. Shared sign-in clears the owner browser cookie, and an explicit local owner recovery link clears the shared cookie. Account creation disables first-browser owner claiming.\n\nBackup and restore omit account/session stores, including older archives. Recreate accounts and assign project roles deliberately after restoring. Human collaboration comments can be retained with application data but convey no authentication authority.\n\n## API and enforcement contract\n\n`POST /api/auth/login {username,password}` sets the session cookie. `GET /api/auth/self` returns only the current public identity; `POST /api/auth/logout` revokes the current session. Owner administration uses `GET/POST /api/accounts`, `PUT /api/accounts/:id` and `POST /api/accounts/:id/revoke-sessions`. Every mutation carries the exact current account-state `revision`; stale updates fail with 409 while preserving the existing state. Project IDs must be validated against the current project store before assigning roles.\n\nRun collaboration uses `GET /api/runs/:id/collaboration` and, for reviewers and higher roles, `POST /api/runs/:id/collaboration {branch,expectedHead,text}`. The server authenticates the actor and resolves an owned result branch; the store rejects a changed commit. Comments are bounded to 4000 characters and 200 entries per run, with immutable author/time/commit metadata. File reviews use the existing revision-guarded `POST /api/runs/:id/review-coverage` with authenticated actor audit. User-supplied actor fields never grant identity.\n\n`canonicalSharedPath` checks the original request target before URL normalization, accepts the versioned `/api/v1` alias, and rejects encoded separators, double encoding, dot segments, empty segments and extra ambiguous suffixes. `sharedRoutePolicy` allows only explicitly enumerated method/path pairs; unknown routes default to owner-only. Global authorization must run before all API/workspace handlers, and resource authorization must precede Git/filesystem operations. Public pairing/webhook routes retain separate secret capabilities and do not inherit a shared user's project roles.\n\nThe account, access-policy and comment-store tests cover hash-only storage, modes, exact revisions, role escalation refusal, revocation, expiry, bounded login work, malformed paths/aliases, cross-project membership, projection privacy and durable commit-anchored comments. End-to-end server/browser tests are required before exposing the shared routes.\n\nThe password parameters and authorization model follow the [OWASP Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html), [Authorization Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html) and [Session Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html).\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Shared project accounts"},{"group":"Connect and collaborate","id":"ssh-projects","source":"docs/ssh-projects.md","body":"# SSH projects\n\nAdd a connection in Settings → Connections → Work on another computer, connect to the host and select a repository there. The remote workspace has its own execution environment and installed CLIs.\n\n## Open a remote workspace\n\nOpen an existing repository on a Linux or macOS server and use the complete code-flow workspace there. File browsing and editing, Git, agent sessions, approvals, terminals, worktrees, setup, builds, verification, and development servers all run on that server. Local and connected remote projects appear together in the sidebar. Click a project to work on its computer; the top bar shows the computer name. Everything stays on the main app address, with remote projects under `/computer/<saved-host-id>/`.\n\n## Connect\n\n1. Install Node.js 22.13 or newer and Git on the server. Install and sign in to the agent CLIs you want to use on that server. Python 3 enables interactive terminals; Chrome/Chromium on the server enables snapshots. The server must allow SSH TCP forwarding to its loopback interface.\n2. Configure keys, ports, jump hosts and hostnames in your normal `~/.ssh/config`, for example:\n\n ```sshconfig\n Host devbox\n HostName server.example.com\n User developer\n IdentityFile ~/.ssh/id_ed25519\n Port 22\n ```\n\n3. Run `ssh devbox` in a terminal once to verify its host key and authentication. Check that `node --version` and `git --version` work in the remote login shell. Unlock passphrase-protected keys in your local SSH agent. Password prompts are not supported in the app.\n4. Start code-flow with `npm start`. The normal `npm run build` builds both the UI and portable remote backend. For an existing development server, run `npm run build:host` after code changes.\n5. Open **Settings → Connections → Work on another computer** (or **This computer → Manage connections…** in the top bar). Select an alias or enter `user@hostname` and name it. Optionally enter an **SSH port** and an **Identity file** path on your computer, such as `~/.ssh/id_ed25519`. Leave them blank to inherit your SSH config. Choose **Save connection** to keep the definition for later or **Connect and open** to start the remote workspace.\n6. The folder picker now browses the server. Choose a Git repository and use the app normally. Agent availability and login status refer to the server.\n\nConnecting installs the application backend, without npm installation or administrator privileges, into `~/.codeflow/ssh-host/releases/<version>` on the server. Its projects, runs, settings and logs live in `~/.codeflow/ssh-host/state`. Local provider credentials, repositories, SSH agents and environment variables are not copied. Credentials and tools must be set up independently on the server.\n\n## Reconnect and stop\n\nThe remote backend is detached from SSH. Closing the page, disconnecting, removing a saved connection, or stopping the local app leaves remote work running. Reopen the saved connection to restore projects and runs. Reloading a remote project restores its connection automatically. You can also reopen it from **Settings → Connections → Work on another computer**. Reconnecting does not resubmit actions. After a connection loss during a mutation, inspect the run or files before repeating the action.\n\nSaved connections, including optional ports and key paths, survive local app restarts. Key contents are never stored by code-flow. If opening a newly saved connection fails, it stays in the list so you can retry with **Open** after resolving SSH authentication. Remove and re-add a connection to change its definition. The backend and work survive SSH interruptions, but not a server reboot or an administrator terminating the process. On backend restart, existing code-flow recovery marks interrupted work for explicit resumption. A backend that still has a live PID but fails its authenticated health check is not automatically replaced.\n\n**Disconnect** closes only the SSH channels and preview forwards. **Stop backend** stops an idle backend and is refused while runs, terminals or development services are active. Finish work and close these first. Scheduled work also stops with the backend. On the next connection, the current application build is installed. A running backend is reused during client upgrades so active work is not interrupted; stop it when idle to update it.\n\n## Remote development previews\n\nIn the side panel's Browser, enter the development server's `http://localhost:<port>` URL. Code-flow forwards that remote port over SSH and shows it on an isolated local preview origin, including hot reload WebSockets. Remote app cookies are separate from code-flow's session cookies. Arbitrary external browsing and screenshots continue to use the remote backend's existing capabilities. A remote localhost URL copied directly into a separate local browser tab is not automatically forwarded; use the app's Browser preview.\n\n## Connection boundaries\n\nThe app uses OpenSSH and your existing config, including ProxyJump. It enforces known host keys, noninteractive authentication, no agent or X11 forwarding, no configured port forwarding, and no local SSH commands. SSH command arguments never include project paths or prompts. Both the local app and remote backend bind only to loopback. Backend authentication cannot be claimed by the first browser, and its private token stays between the local gateway and the remote backend.\n\nAll projects use the local app’s owner session and trusted UI. Requests under `/computer/<saved-host-id>/api/` are routed over that computer’s SSH connection; they never fall back to the local backend. Drafts, event streams and popped-out panels are scoped to their computer. Remote API responses are sandboxed and cannot execute scripts on the app origin. Local cookies, bearer tokens and forwarding headers are removed before requests cross SSH; remote response cookies cannot overwrite local sessions. Remote previews retain the existing preview isolation and the remote filesystem-policy gate.\n\nOnly the local app owner can manage SSH connections. Shared-account and phone sessions cannot create SSH connections. Up to eight connections can be open and 32 definitions saved. Connections use the remote OS account's authority, so only connect to accounts and servers you trust.\n\n## Troubleshooting\n\n- **Host key or authentication failed:** run the exact alias with `ssh` in a terminal and resolve the error there. The app never accepts unknown fingerprints automatically.\n- **Node not found:** put Node.js 22.13+ on the remote login shell's PATH. No Node runtime is downloaded automatically.\n- **Connection refused / forwarding prohibited:** check the server's `AllowTcpForwarding`/`PermitOpen` settings and `~/.codeflow/ssh-host/host.log`.\n- **Backend still running but unavailable:** inspect the log and process before stopping it. The app avoids restarting a process that might still be doing work.\n- **Another host setup is running:** wait for it to finish. If setup crashed, verify no bootstrap process remains before removing `~/.codeflow/ssh-host/bootstrap.lock`.\n- **Remote agent unavailable:** install and sign in to its official CLI on the server. The app's health dialog shows that server's tools.\n\nThis feature differs from [SSH workers](remote-workers.md): workers transfer individual agent turns and their commits. SSH projects keep the complete project and application backend on the server.\n\n## Validation\n\n`npx tsx --test test/ssh-projects.test.ts` runs the packaged backend in a separate host home using an injected SSH transport. Browser coverage is in `test/e2e/ssh-projects.e2e.ts`. Run `CODEFLOW_TEST_LOOPBACK_SSH=1 npx tsx --test test/ssh-projects-ssh.test.ts` for the real OpenSSH acceptance test. It uses temporary keys, known hosts and an unprivileged loopback SSH daemon, and never edits personal SSH configuration.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"SSH projects"},{"group":"Connect and collaborate","id":"remote-workers","source":"docs/remote-workers.md","body":"# Remote agent workers\n\nProvision and verify a worker before assigning it work. Keep source and result receipts available so interrupted connections can be reconciled without launching duplicate work.\n\n## Choose an execution target\n\nAn explicitly registered SSH worker runs agent turns in disposable Git worktrees on another machine. The application transfers an exact commit, the turn prompt and regular files from the run’s `.codeflow-context` folder. Official agent CLIs use the worker’s own login. SSH agent forwarding, environment forwarding, port forwarding and automatic host-key acceptance are disabled.\n\nThis is an operator provisioned service: it does not discover hosts, install remote dependencies, copy local provider credentials or automatically fetch repositories from the network. Commands and checks outside agent turns still run where the application server runs. Choose the remote worker in the composer’s advanced controls.\n\n## Provision a worker\n\nUse Node 22.13 or newer, Git and the official CLI for each selected provider. Sign in on the worker itself. A dedicated OS account and disk/process limits are recommended for executable repository content. The worker account needs write access to its own state directory and provisioned repository mappings. A mapping can be a clone or a bare Git repository with an existing commit; it is not an arbitrary path supplied by a run.\n\nBuild the standalone entry point in this checkout:\n\n```sh\nnpm run build:worker\n```\n\nCopy `dist/remote-worker.mjs` to the worker through your normal administration process. Configure an existing SSH host alias with its known host key and noninteractive authentication. Code-flow runs `ssh` with strict host-key checking and never accepts a new fingerprint for you. [OpenSSH’s manual](https://man.openbsd.org/ssh) describes identity selection and forwarding options.\n\nCreate a private worker configuration, for example `/srv/codeflow/worker.json`:\n\n```json\n{\n \"version\": 1,\n \"root\": \"/srv/codeflow/state\",\n \"repos\": { \"application\": \"/srv/codeflow/repos/application.git\" },\n \"providers\": [\"claude\", \"codex\"],\n \"allowedTrust\": [\"safe\", \"standard\"]\n}\n```\n\nRegister it on the application server with `CODEFLOW_REMOTE_WORKERS_FILE=/absolute/remote-workers.json` (or the same JSON in `CODEFLOW_REMOTE_WORKERS_JSON`):\n\n```json\n{\n \"version\": 1,\n \"workers\": [{\n \"id\": \"build-linux\",\n \"name\": \"Linux build worker\",\n \"sshHost\": \"codeflow-linux\",\n \"command\": \"node /srv/codeflow/remote-worker.mjs --config /srv/codeflow/worker.json\",\n \"repoMappings\": [\"application\"]\n }]\n}\n```\n\nThe command is an explicit operator configuration; task content never becomes part of the SSH command. Keep these files outside agent workspaces. Public worker metadata contains names, IDs, mapping names and optional container profile names, without SSH commands, host details, credentials or recovery capabilities.\n\nThe worker view probes the registered channel and installed CLIs. A successful probe does not claim a provider login is valid; the official CLI reports authentication failures when a turn runs. Missing mappings, providers or container profiles fail explicitly. Remote selection never falls back to this computer.\n\n## Exact source and returned changes\n\nThe client pins the clean tracked checkout commit. If the worker already has it, no source upload is needed. Otherwise code-flow creates a Git bundle using an exclusive temporary `refs/codeflow/transfers/<requestId>` reference, deletes that reference with compare-and-swap, and uploads the bounded bundle over SSH. When the worker confirms the repository’s original base is present, the bundle excludes that history. If the bundle is too large, sync the provisioned mapping through your administration process and retry explicitly. No remote URL from task content is fetched.\n\nThe worker verifies the bundle’s sole advertised reference, enables Git object validation on import and creates a detached request worktree. It never checks out or changes the mapping’s main branch. Read turns must preserve `HEAD`, the staged tree and tracked files; changed files are discarded and the turn fails. Write turns return a worker-created commit descended from the exact accepted base. Agent attempts to rewrite checkout history fail.\n\nReturned result bundles have an exact request-owned reference and SHA-256 digest. Before importing, the client verifies the digest, advertised commit, Git object integrity and ancestry, then requires the local worktree’s original commit and clean tracked state. It fast-forwards that owned checkout only; concurrent local changes cause a visible failure. Disposable worktrees and imported temporary references are cleaned after execution. See Git’s [bundle](https://git-scm.com/docs/git-bundle) and [worktree](https://git-scm.com/docs/git-worktree) documentation for those primitives.\n\nGitlinks/submodules and repositories declaring checkout filters such as Git LFS are rejected: a parent Git bundle does not contain their materialized contents. Configured clean, smudge and process filters are suppressed during scoped checkout, staging and result import, so these steps do not secretly execute filters or fetch LFS content. Use local execution for those repositories.\n\nOnly staged reference files are copied as context. Symlinks, special files, `.env` files, private key extensions, traversal and arbitrary host paths are rejected. There are at most 100 regular files, 8 MiB per file and 16 MiB total. Host home directories, agent sessions and host environment variables are not part of the transfer.\n\n## Receipts, cancellation and ambiguous outcomes\n\nEach invocation has a random request ID, a distinct private capability and a run/step/thread scope. Before invoking a provider, the worker exclusively claims the ID, fsyncs its receipt and directories, and reports acceptance. It records heartbeats and terminal results. The private capability is stored in the server’s `remote-worker-private` directory with owner-only file permissions and pinned to the exact configured SSH host, command and container profile. Replacing a worker alias cannot send an old recovery capability to its new target. Run records contain only worker ID, request ID, fingerprint and outcome metadata.\n\nA reconnect inspects the same durable receipt. Reusing an ID never launches another provider process. A broken connection, missing receipt or expired heartbeat becomes **unknown**, even when the provider might already have performed work. There is no automatic execution retry or local fallback for that outcome. Inspect or cancel the request, and deliberately create a new invocation only after assessing its prior effects. Terminal receipts can re-download the validated result bundle without repeating execution. Open the agent step’s transcript and choose **Inspect remote receipt**. **Cancel remote execution** requires an explicit confirmation. These controls never launch a new turn; Resume is a separate owner action after reconciliation.\n\nCancellation uses the stored capability and a separate SSH control channel. The active worker checks the cancellation marker, stops the official CLI and records cancellation. A disconnected channel also aborts the scoped worker process. During a worker crash or lost host, termination cannot be confirmed until the host is inspected; an unknown receipt must not be interpreted as proof that execution stopped.\n\nFour worker execution slots and 100 request directories are retained at most. A crashed worker can leave its slot unresolved. Inspect its recorded PID, request receipt and host processes before manually archiving the terminal request directory and clearing a stale slot file. Code-flow deliberately does not reclaim an ambiguous slot and launch duplicate work. The client keeps at most 2,000 private receipts and 32 downloaded result spools pending import or discard. Metadata-only inspection does not download a result bundle. Encrypted application backups exclude private worker capabilities, so a restored server cannot cancel or recover old remote authority automatically.\n\n## Worker-local containers and boundaries\n\nOptional named `containers` profiles use the same explicit agent-container configuration as local runs. Register a `containerProfile` on the server target to select a worker-local profile. Its image and authentication volume must exist and be provisioned on the worker. Local container images, host credential volumes and home folders are never inferred or copied to the worker. Availability probes run the provider’s version command inside that exact prebuilt image with no network or credential volumes; a host CLI installation is unnecessary for a container profile. The image is never pulled automatically.\n\nRemote turns currently start fresh CLI sessions and use structured question/reply outcomes across turns. Live team tools, mid-turn steering and native interactive approval prompts are unsupported. Every result omits resumable session IDs. Final verification and independent reviews still apply to imported results. The server must enforce run/project access before exposing receipt or cancellation operations.\n\nSource and result transfers are each limited to 64 MiB, with binary Git output capped before a transfer file is written. A source tree has at most 20,000 entries and 256 MiB of file data. Protocol frames are capped at 2 MiB, streamed events at 16 MiB and provider turns at 30 minutes. Uploads have a 30-second deadline. These transfer and application bounds do not replace OS disk quotas or a container sandbox: executable repository commands and Git object decompression can consume additional worker resources. Do not give mutually untrusted users a shared worker OS account or broader SSH credentials than their work requires.\n\n## Validation status\n\nThe automated acceptance fixture launches the standalone worker in a separate process through an injected SSH transport. It exercises the official adapter against a fake CLI, source/context transfer, bundle import, read-only rejection, private capability ownership, restart recovery, dropped transport, cancellation, tampered bundles and changed local heads. No provider quota or external messages are used.\n\nAn additional opt-in acceptance test passed over real encrypted loopback OpenSSH: a temporary unprivileged daemon bound to 127.0.0.1, temporary host/client keys and isolated ssh -F configuration, the bundled worker process, a fake CLI, exact source transfer, result import and receipt recovery. Run it with `CODEFLOW_TEST_LOOPBACK_SSH=1 npx tsx --test test/remote-workers-ssh.test.ts`. It never modifies user SSH configuration or system settings, and reports a skip if unprivileged sshd is unavailable. No external SSH host or live provider login was tested. Validate those on an explicitly configured host before relying on unattended work. Container inventory uses an injected Docker fixture; an actual Docker worker image was not exercised in this acceptance run.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Remote agent workers"},{"group":"Connect and collaborate","id":"code-hosts","source":"docs/code-hosts.md","body":"# GitLab and Bitbucket code workflows\n\nConfigure the GitLab or Bitbucket connection, fetch the change into an isolated review workflow, and inspect the pinned result before commenting, pushing or opening a pull request.\n\n## Review on another code host\n\nSettings → Integrations → GitLab and Bitbucket code hosts provides explicit owner workflows for GitLab and Bitbucket Cloud. Jira, Linear and source-provider extensions remain read-only. No provider account is contacted in the fixture tests.\n\nConfigure integrations with `CODEFLOW_INTEGRATIONS_JSON` or `CODEFLOW_INTEGRATIONS_FILE`:\n\n```json\n[\n {\"id\":\"gitlab\",\"provider\":\"gitlab\",\"project\":\"group/repository\",\"credentialEnv\":\"GITLAB_TOKEN\"},\n {\"id\":\"bitbucket\",\"provider\":\"bitbucket\",\"project\":\"workspace/repository\",\"credentialEnv\":\"BITBUCKET_TOKEN\"}\n]\n```\n\nTokens remain server environment references. GitLab defaults to its v4 API and GitLab HTTPS Git root; Bitbucket defaults to its Cloud v2 API and bitbucket.org Git root. Self-hosted GitLab may set `baseUrl` and `gitBaseUrl`. A custom Bitbucket API base requires an explicit HTTPS `gitBaseUrl`; Bitbucket Data Center APIs are not supported. Neither root may contain credentials, query parameters or fragments. Fork paths must remain within the configured Git host/root; cross-host credential redirects are refused.\n\nChoose a configured host, PR number and local Git repository. Loading a preview reads bounded provider metadata, discussions and available merge policy. Fetch explicitly imports exact source/target objects and their actual merge base into random application refs, without checking out or executing incoming code. Each snapshot expires after 15 minutes and all snapshots disappear on restart. Changed heads, targets, discussion revisions and project membership require a fresh preview. Truncated discussion lists cannot be resolved or replied to here; inspect the remaining discussion in the provider UI.\n\nFetched-code review runs require an explicitly prepared agent Docker image and optional named authentication volume. Build the image with the official CLI and authenticate inside that image/volume first. No host credential directory is mounted, images are never pulled automatically, and there is no native host fallback. The container runs with the host UID, dropped capabilities and resource limits; the CLI needs its own configured network access for inference. Check/setup commands run in a separate network-disabled container. Safe mode alone is not filesystem isolation. See the existing agent-container setup in the application architecture documentation.\n\nThe run begins at the fetched source commit, reads conventions from the trusted merge base, uses Safe mode, inherits no uploads or parent run, copies no ignored files, and cannot wait for native tool approvals. It needs at least one required verification command. Raw client `externalSource` or commit-provenance objects cannot establish authority. A restored or resumed run still needs its prepared agent container.\n\nReview actions include general and new-file inline comments, replies, resolution/reopening, and GitLab exact-SHA approval/removal. Comments label the observed source and target commits. GitLab inline positions include its base/head/start diff identities. Bitbucket inline comments and approvals do not expose an atomic expected-head precondition: comments explicitly label the observed commit, postflight drift is reported, and commit-bound Bitbucket approval is deliberately unavailable here. Use Bitbucket's native UI for its approval action. Review content remains untrusted reference text.\n\nPush starts with a preview of a completed, owned, currently verified result branch. The result must descend from the observed PR source and include current required check evidence. Push sends only that exact commit to the observed source branch with `--force-with-lease=<ref>:<observed-head>`; arbitrary force pushes are not available. GitLab fork pushes require a separately configured source integration. No push occurs when merely loading the preview.\n\nNew PR creation accepts a completed owned verified result, configured destination host, title/body and target branch. The result must contain new commits descending from that exact current target. Its preview shows a unique server-generated source branch, source/target commits and outgoing text. The explicit action creates the source branch with an expected-absent Git lease, then sends the native GitLab MR or Bitbucket PR creation request. Those two remote actions are not atomic. A timeout preserves the exact branch/commit/run receipt as unknown; inspect that branch and the provider before another attempt. The application never retries creation automatically or deletes ambiguous remote branches.\n\nMerge is an explicitly labelled **pinned Git merge**, not a provider-native merge queue. A completed owned result must exactly match the current PR source and required verification/review evidence. The actual source-plus-target tree is built and checked in a disposable worktree using network-disabled check isolation and tracked-file/index/HEAD drift guards. The final commit has exactly the pinned target and source as parents. Its push uses an atomic exact-target Git lease. Source advancement cannot add unreviewed code; target advancement refuses the push. The local target branch is unchanged.\n\nProvider policy must be readable and pass at preview and immediately before the action. GitLab approval state, exact-head pipeline requirements, discussion requirements, external checks and protected-branch metadata are inspected. Native trains, merged pipelines and enforced squash/rebase workflows are blocked. Bitbucket mergeability checks and complete branch restrictions must be readable, including the admin scope required by the restrictions API. Native queues, custom on-merge checks, dependency-merge requirements, incomplete results and unknown restriction types block merging. Direct target push permission is required and the Git server still enforces its push permissions. Provider APIs do not offer an atomic approval-policy snapshot tied to the Git ref update; organizations requiring exclusively native policy enforcement should merge in the provider UI.\n\nGitLab's native merge endpoint pins the source SHA but does not pin the target SHA. Bitbucket's native merge endpoint does not expose an expected-head precondition and can queue its operation. Consequently this application does not call either native merge endpoint while claiming an exact validated target. A confirmed Git merge receipt does not imply atomic PR closure; the current provider state is reported separately and the PR may remain open. No native merge queue parity is claimed.\n\nCredentialed Git runs in a private temporary bare repository with no user/global Git config, hooks, credential helper, redirects or submodules. Authentication is a process-only URL-scoped HTTPS header. Local object imports receive no provider credential. Fetches are bounded in time, output, file count and temporary storage (256 MiB); oversized histories fail closed. Local application refs keep imported objects reachable and are normal repository data; no credential is stored in them.\n\nThe bounded `code-host-operations.json` ledger retains at most 200 explicit actions and safe identity/commit/run/branch facts. Unknown receipts are never silently evicted: if all slots are unresolved, new actions stop until the owner explicitly records manual inspection of one exact outcome. Inspection is labelled separately from provider confirmation and never replays the action. The ledger stores no tokens, comment bodies, tasks or executable callbacks. Claims are fsynced before remote mutation; interrupted or restored claims become unknown metadata, all server previews are lost, and there is no startup dispatch or automatic replay. Retention protects referenced run receipts. Encrypted restore also excludes executable extension caches and live identity/session credentials. Any custom filesystem policy blocks all code-host POST operations, and shared project sessions cannot access these owner-only routes.\n\nProvider protocol references: [GitLab merge requests](https://docs.gitlab.com/api/merge_requests/), [GitLab discussions](https://docs.gitlab.com/api/discussions/), [GitLab approvals](https://docs.gitlab.com/api/merge_request_approvals/), [GitLab external status checks](https://docs.gitlab.com/api/status_checks/), [Bitbucket Cloud pull requests](https://developer.atlassian.com/cloud/bitbucket/rest/api-group-pullrequests/), [Bitbucket branch restrictions](https://developer.atlassian.com/cloud/bitbucket/rest/api-group-branch-restrictions/), [GitLab HTTPS tokens](https://docs.gitlab.com/user/profile/personal_access_tokens/), [Bitbucket HTTPS access tokens](https://support.atlassian.com/bitbucket-cloud/docs/using-access-tokens/).\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"GitLab and Bitbucket code workflows"},{"group":"Connect and collaborate","id":"external-sources","source":"docs/external-sources.md","body":"# Read-only external sources\n\nConfigure the source integration, select an issue or pull request, and review the imported context before starting work. Imported descriptions remain untrusted task data.\n\n## Bring source context into a run\n\ncode-flow can list and import GitLab issues and merge requests, Bitbucket Cloud issues and pull requests, Linear issues, and Jira Cloud issues. Import reads the item's text and metadata. It does not fetch a review request's branch, publish comments, transition issues, push commits or merge remote work.\n\nConfigure integrations on the server with `CODEFLOW_INTEGRATIONS_FILE` pointing to a JSON file, or `CODEFLOW_INTEGRATIONS_JSON` containing the array directly. The file takes precedence. Restart code-flow after changing configuration. Credentials are read from named environment variables; the browser receives connection names and availability, never credential values or environment variable names.\n\n```json\n[\n {\n \"id\": \"gitlab-project\",\n \"name\": \"GitLab application\",\n \"provider\": \"gitlab\",\n \"project\": \"my-group/my-project\",\n \"credentialEnv\": \"MY_GITLAB_READ_TOKEN\"\n },\n {\n \"id\": \"bitbucket-project\",\n \"name\": \"Bitbucket application\",\n \"provider\": \"bitbucket\",\n \"project\": \"my-workspace/my-repository\",\n \"credentialEnv\": \"MY_BITBUCKET_READ_TOKEN\"\n },\n {\n \"id\": \"linear-team\",\n \"name\": \"Linear engineering\",\n \"provider\": \"linear\",\n \"project\": \"ENG\",\n \"credentialEnv\": \"MY_LINEAR_READ_KEY\"\n },\n {\n \"id\": \"jira-project\",\n \"name\": \"Jira application\",\n \"provider\": \"jira\",\n \"baseUrl\": \"https://my-company.atlassian.net/\",\n \"project\": \"APP\",\n \"credentialEnv\": \"MY_JIRA_READ_TOKEN\",\n \"usernameEnv\": \"MY_JIRA_EMAIL\"\n }\n]\n```\n\nUse credentials restricted to reading the configured project or team. Authentication defaults are `private-token` for GitLab, `bearer` for Bitbucket, `api-key` for Linear personal keys, and `basic` for Jira email/API-token pairs. An explicit `auth` may choose any of those modes; `basic` also requires `usernameEnv`. Linear OAuth tokens use `bearer`.\n\nGitLab's default API base is `https://gitlab.com/api/v4/`; a self-managed instance can provide its own HTTPS API base. Bitbucket uses the Cloud API at `https://api.bitbucket.org/2.0/`; Bitbucket Data Center is not supported by this adapter. Linear uses `https://api.linear.app/`; `project` is the team's key or UUID. Jira requires the site API base and a project key. Its adapter uses enhanced `/rest/api/3/search/jql`, rather than the older search endpoint being removed.\n\nThe external-sources screen lists a page of up to 50 items. Selecting **Use as task context** imports the full description, up to 20,000 characters. Server-held imports expire after 30 minutes and a restart; reimport if necessary. A run stores its accepted source snapshot for later inspection and recovery.\n\nImported content is always untrusted. The browser sends a server-issued `externalSourceId` when starting a run. The server resolves that ID to its own saved snapshot and the engine applies the same restrictions as other untrusted sources: safe agent permissions, no copied secrets, and isolation for commands unless the user explicitly permits host execution. A client-provided body, `trusted` field or text marker cannot establish source authority.\n\nConnections require HTTPS. Requests remain within the configured API origin and path, do not follow redirects, time out after 15 seconds and stop reading at 2 MiB. Credential values and remote error bodies are excluded from error messages. GitLab/Bitbucket numeric pagination stops at page 20 and reports truncation; the UI also limits displayed items to 1,000. Some Bitbucket repositories do not enable their built-in issue tracker; their issue API will report unavailable access while pull requests remain usable.\n\n## Provider extensions\n\n`ReadOnlySourceProvider` in `src/integrations.ts` defines `kinds`, `list` and `get`. Pass additional adapters through the `Integrations` constructor's `adapters` option. Extensions receive the same bounded request client. REST requests use GET; GraphQL accepts named read-only queries and variables. The request client rejects mutation operations and requests outside the configured API base. Normalize results to `ExternalSourceItem`; the class enforces output bounds and issues untrusted import identities.\n\n## API\n\n| Operation | Request |\n| --- | --- |\n| Connection availability | `GET /api/integrations` |\n| List one page | `GET /api/integrations/:id/items?kind=issue` (or `pr`) |\n| Continue a listing | Add the returned opaque `cursor` |\n| Import full source | `POST /api/integrations/:id/import` with `{ \"kind\": \"issue\", \"key\": \"123\" }` |\n| Attach imported context to a run | Include the returned `id` as `externalSourceId` in `POST /api/runs` |\n\nThe API requires the normal code-flow session. Service credentials are configured on the server and cannot be changed through this interface.\n\n## Provider references\n\nImplementation references checked on 18 October 2026:\n\n- [GitLab project issues](https://docs.gitlab.com/api/issues/) and [merge requests](https://docs.gitlab.com/api/merge_requests/).\n- [Bitbucket Cloud REST API](https://developer.atlassian.com/cloud/bitbucket/rest/), [pull requests](https://developer.atlassian.com/cloud/bitbucket/rest/api-group-pullrequests/), and [issue tracker](https://developer.atlassian.com/cloud/bitbucket/rest/api-group-issue-tracker/).\n- [Linear GraphQL authentication and queries](https://linear.app/developers/graphql).\n- [Jira Cloud enhanced issue search](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-search/) and [email/API-token authentication](https://developer.atlassian.com/cloud/jira/platform/basic-auth-for-rest-apis/).\n\nAutomated coverage uses deterministic HTTP fixtures. Live account compatibility depends on each account's current API access and credentials; tests do not spend quota or create remote content.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Read-only external sources"},{"group":"Connect and collaborate","id":"automations","source":"docs/automations.md","body":"# Automations and away mode\n\nOpen Project → More → Automation → Repeat for recurring work, Inbox for results that need attention, and Automation → Run once later for a single task that should start later. Keep the application running when a scheduled task is due.\n\n\n\n\n## Choose the right trigger\n\nSet up a run in the composer (task, team, check, settings), then choose **More run actions → Schedule… → Repeat**. It runs unattended.\n\nOpen **Scheduling** from the sidebar or press **a** to manage recurring tasks, deferred runs, backlog processing and project missions. **Inbox** is a separate sidebar destination for questions, blocked work, failures and results. Older `#/settings/automations` links open the Scheduling page.\n\n## Projects\n\nAn automation's runs start in the [project](runs.md#projects) you were in when you saved it (from *All projects*, in its repository's project). If that project is deleted later, they start in the repository's project. Turning it off and on, or making a new webhook secret, keeps its project.\n\n**Project → Automation** always lists that project’s saved work, regardless of the sidebar filter or composer draft. Choose its repository here before configuring a task or opening backlog items. Run once later and Repeat open the canonical project composer to describe the task; choosing backlog items restores that project’s saved agent workflow without leaving Automation. Multi-repository projects require a visible choice. Saved schedules for other repositories in the same project stay discoverable.\n\nGlobal **Scheduling → Repeat** uses the project filter beside its title. Inbox lists the results waiting for you. When other projects have some too, a line says how many, with **show all** to switch to *All projects*. The **Inbox** count in the sidebar counts attention items across projects, so nothing waiting for you is out of sight.\n\n## When it runs\n\n- at a time on chosen weekdays (e.g. weekdays at 07:00), or every *N* minutes (at least 5);\n- from a webhook;\n- or when you press **Run now** on the **Scheduling → Repeat** page.\n\nThe next time is saved, so restarting code-flow doesn't move it. A time missed while code-flow was closed or the computer asleep fires once when code-flow is back, if it's at most an hour late and today is still one of the schedule's days; otherwise it's skipped and the schedule moves on to its next time. A schedule never fires on a day you left out.\n\nIf the previous run is still going (or still starting), a scheduled run or **Run now** is skipped, so automations never pile up. A webhook event that arrives meanwhile waits instead, and fires once that run ends; the same event sent twice waits once.\n\n## Sweeps\n\nA [sweep](github.md#sweeps-many-at-once) saved with *Also check every day at…* is an automation too. Each time it fires it lists the open issues, Dependabot PRs or security alerts again and starts a run for each **new** item that would be picked by default, up to 5 at a time. It remembers which items it already started, so nothing is swept twice; alerts are remembered by number, so a new alert on a package that was swept before is new work. **Run now** does the same on demand.\n\nA webhook that reaches a sweep names one item, and goes through the same rules:\n\n- it has to be the sweep's kind: an issue for *Fix open issues*, a pull request for *Fix Dependabot PRs*, an alert for *Fix security alerts*;\n- the sweep's list has to pick it: not labelled *question* or *wontfix*, not a Dependabot PR whose CI passes (or is still running), not a low-severity alert or one with no fix yet, not one that already has a run;\n- webhooks start at most the sweep's limit of runs (5 by default) per hour, so someone opening fifty issues doesn't start fifty runs.\n\n## Webhooks\n\n`POST /hooks/<id>` with `Authorization: Bearer <secret>` and a JSON body. Both fields are optional:\n\n- `details` (or `task`) is added to the task as information for the agents, not instructions: it goes in a fenced block they're told not to take orders from;\n- `source` (e.g. `{\"kind\": \"pr\", \"number\": 12}`) starts from that issue or PR, or from a security alert (`{\"kind\": \"alert\", \"tool\": \"dependabot\", \"number\": 3}`, or `\"tool\": \"code-scanning\"`).\n\nAnyone with the secret can send `details`, and programs on this computer can read the secret in `~/.codeflow/triggers.json`, the agents included. So a run with `details` is marked as carrying untrusted text, like one from a Dependabot PR: its reviews aren't learned as lessons for later runs, and project memory it proposes waits for your approval. Runs from a schedule, **Run now**, or a webhook without `details` are as before.\n\nGitHub webhooks work directly through a tunnel. Use the secret as the webhook secret; code-flow checks `X-Hub-Signature-256`. These events start a run, and all others are ignored:\n\n- an opened issue starts from that issue;\n- an opened or updated pull request starts from that PR;\n- a failed workflow run starts from its PR, with the failure logs;\n- a new or reopened Dependabot alert or code scanning alert starts from that alert.\n\nLeaked-secret alerts never start a run.\n\ncode-flow answers `202` at once (GitHub gives up on a delivery after 10 seconds) and starts the run in the background. If it can't start, the reason shows on the automation (*couldn't start: …*). A delivery GitHub sends again is ignored: one with the same `X-GitHub-Delivery` id, or a signed one with the same body whatever its id says, or with none (the signature covers the body, not the id). An automation that isn't a sweep starts at most 10 runs an hour from webhooks.\n\n## The inbox\n\nAutomation and sweep results wait in **Inbox**, with an attention count in the sidebar:\n\n- **Approve:** keeps the result. **Approve & open PR** also pushes the branch and opens a pull request. For a run that started from a pull request (a Dependabot PR, say), **Approve & push to PR #N** pushes the fix to that PR instead.\n- **Revise:** your note goes to the agent that wrote the code, in its own session. The result comes back to the inbox.\n- **Reject:** deletes the run's worktrees and branches.\n- Runs that changed nothing are archived on their own, and their branches deleted.\n- Automations never pause for plan approval, and **nothing is ever merged automatically**.\n\n## Isolation\n\nTick *Run checks and setup in a Docker container* and code-flow runs those commands with `docker run`:\n\n- only the worktree is mounted read-write, plus the repo's `.git` read-only, so git works;\n- none of your environment variables or your home folder are visible inside;\n- the files it creates belong to you.\n\nCheck isolation runs the repository's scripts in the selected image. Agents normally use their own CLI sandboxes; explicit [agent container isolation](agent-containers.md) is a separate setting with its own prebuilt image and optional authentication volume. The default check image comes from `image` in [`.codeflow/config.json`](configuration.md#per-repo-config).\n\n## Phone view\n\nTurn on **Enable remote access** in **Settings → Connections → Share access**. Choose **This computer only**, **Tailscale · access anywhere**, or **Wi-Fi · local network**.\n\n- On its own it's a read-only view: what's running and what needs you, and each run's summary, review, results and steps. It refreshes itself and has no buttons.\n- Runs are listed under their project's name. Projects with runs that need you come first, then those with runs going; runs in no project come last.\n- **Wi-Fi · local network** also serves it, and webhooks, on your network at port `PORT+1`. Choose **Set up secure remote access** to import a PEM certificate and private key trusted by your phone, with the hostname or IP address covered by the certificate. Without HTTPS the listener remains read-only.\n- With network access on, the page shows a **QR code** for the selected connection: scan it with your phone's camera. (The `localhost` link only opens on the computer running code-flow, so there's no QR code until a network link exists.) If another program already has the port, the page says so instead; start code-flow with `CODEFLOW_REMOTE_PORT` set to a free one.\n- The network listener refuses connections from the computer itself, whichever of its addresses they use: programs on it, the agents included, can read `~/.codeflow`, phone secrets and all.\n- **New link** revokes the old one. So does turning off remote access or choosing **This computer only**: turning it back on gives a new link.\n\n### Remote access with Tailscale\n\n1. Install [Tailscale](https://tailscale.com/download) on the computer running code-flow and your phone, tablet or second computer. Sign in to the same tailnet and connect both devices.\n2. Open **Settings → Connections → Share access**, enable remote access and select **Tailscale · access anywhere**.\n3. Code-flow detects the local Tailscale client and shows its connection status. If it is missing, signed out, awaiting approval or disconnected, follow the displayed instructions and select **Refresh Tailscale**. Detection also refreshes automatically.\n4. Once connected and listening, copy the link or scan the QR code on the other device. The link uses this computer's Tailscale IPv4 address and `CODEFLOW_REMOTE_PORT` (normally 4318). For full control, choose **Set up secure remote access → Use Tailscale → Get Tailscale certificate**. Enable HTTPS certificates in your tailnet DNS settings first. Once setup succeeds, turn on **Full control from remote devices** and use the new pairing link. No environment variables or application restart are needed.\n\nThe Tailscale mode binds only to the detected Tailscale address, never to the Wi-Fi address or all interfaces. Your tailnet access rules and host firewall must allow the remote port. The default link is HTTP **inside Tailscale's encrypted connection**; it is not a public URL. If you already configured TLS, code-flow retains HTTPS and uses the device's Tailscale DNS name when available; that name must resolve on the other device and match your certificate. Code-flow does not run `tailscale up`, change access rules or enable Serve/Funnel.\n\nThe computer must stay awake and online with code-flow running. Disconnecting Tailscale removes the link and closes its listener on the next check (within about ten seconds plus detection time); reconnecting restores it. A changed Tailscale address updates the link and QR code. Switching between Wi-Fi and Tailscale revokes previous links and paired-device sessions. Full control requires HTTPS on either connection. Tailscale encrypts network traffic, but browsers can send HTTP cookies to other services on the same host, so Tailscale alone does not protect full-control sessions.\n\nUse the link on another device: connections from the server computer itself remain blocked to keep local agent processes from pairing. Direct access currently requires a Tailscale IPv4 address and an OS network interface; userspace-only Tailscale is not supported. Browser features that require an HTTPS secure context may be unavailable on the HTTP link. Local app previews still use separate localhost ports and are not published by this setting; this release provides remote access to Codeflow itself.\n\n### Set up HTTPS in Settings\n\nOn the computer running Codeflow, open **Settings → Connections → Share access**, enable remote access and select a network connection. Choose **Set up secure remote access**:\n\n- **Use Tailscale** requests a certificate for the connected computer's detected tailnet DNS name through `tailscale cert`. Tailscale HTTPS must already be enabled in your tailnet's DNS settings, and the current OS user must be allowed to request certificates. Codeflow never logs you in, elevates privileges or changes your tailnet configuration. The setup explains that the DNS name appears in public certificate transparency logs; the app remains private to your network. See [Tailscale HTTPS setup](https://tailscale.com/docs/how-to/set-up-https-certificates).\n- **Import certificate files** accepts a PEM certificate (including its chain) and unencrypted PEM private key. Enter the hostname or IP address your device will use. Codeflow checks the matching key, validity dates and certificate identity. The device must trust the issuer and resolve the hostname to this computer; importing does not install device trust or configure DNS.\n\nThe certificate and key are stored together in `CODEFLOW_HOME/remote-tls.json` with owner-only permissions and atomic replacement. The API never returns the private key. HTTPS setup is limited to the local owner app, and is unavailable through phone/shared sessions or SSH backends. It reloads only the network listener, leaving runs and the local app active. Certificate replacement revokes paired sessions and leaves full control off until explicitly enabled again.\n\nCodeflow automatically renews certificates obtained through Tailscale within 30 days of expiry while the app is running and Tailscale remote access is enabled. It checks once a minute and retries failures after 1 hour, doubling up to 24 hours; retry timing survives restarts. Renewal keeps full control and paired devices signed in. Settings shows the next attempt, last successful renewal and any failure. Imported certificates and environment-managed certificates remain manually maintained; use **Renew or replace certificate** for those. After time offline, Codeflow can renew an expired Tailscale certificate for the same computer name. An expired or unreadable saved certificate disables the network listener instead of falling back to HTTP. Fix it from the local app.\n\nExisting `CODEFLOW_TLS_CERT` and `CODEFLOW_TLS_KEY` overrides remain supported and take precedence. When those are set, Settings reports that HTTPS is externally managed; remove the overrides and restart to switch to saved configuration.\n\n### Full control from your phone\n\nWith HTTPS configured for **Tailscale** or **Wi-Fi**, you can turn on **Full control from remote devices**. The pairing link signs your phone in to the whole app: start runs, answer approvals, message agents, review diffs, merge results and manage automations. `CODEFLOW_ALLOW_INSECURE_PHONE_CONTROL=1` explicitly permits HTTP for development. Full control requires TLS even over Tailscale, so session cookies can be marked Secure and withheld from other HTTP services on the same host.\n\n- The QR code is a separate sign-in link that expires after ten minutes, including across server restarts. The read-only link never signs a phone in.\n- Scanning pairs one device with a separate 30-day session; the server stores only its token hash. The device list can revoke one phone immediately. **New link**, or disabling full control/network access, rotates the pairing generation and signs all devices out. TLS cookies are Secure. Legacy pairing links expire closed after upgrade.\n- *Terminal*, *Editor* and *Folder* on a result open on your computer, not the phone. *Run app* starts the app on your computer; its link uses the computer's Wi-Fi address, so it only opens if the app listens on your network too (e.g. `vite --host`).\n- Paired devices have owner control and can run commands on the computer. This pairing does not provide separate user identities or project roles.\n\nAutomations live in `~/.codeflow/triggers.json` and the phone settings in `~/.codeflow/remote.json`. Both files are readable only by you, and are written whole (through a temporary file), so a crash can't leave half of one. One that can't be read is moved aside to `triggers.json.corrupt-<time>` (or `remote.json.corrupt-<time>`) and logged in `~/.codeflow/server.log`; code-flow then starts without those automations, or with phone access off, rather than write over it. Automations in it that can be read are kept, and one whose schedule can't be read is kept but turned off.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Automations and away mode"},{"group":"Connect and collaborate","id":"notifications","source":"docs/notifications.md","body":"# Outbound notifications\n\nOpen Settings → Notifications, configure a destination and its scope, then inspect delivery history. Outbound destinations are opt-in and can disclose the event details selected for delivery.\n\n## Choose where alerts go\n\nNotifications require an explicitly configured destination and a saved opt-in for its events and project scope. New destinations start disabled. No account is connected and no destination can be created from the browser.\n\nSet `CODEFLOW_NOTIFICATIONS_FILE` to a server-side JSON file, or set `CODEFLOW_NOTIFICATIONS_JSON` to its contents:\n\n```json\n[\n {\n \"id\": \"team-status\",\n \"name\": \"Team status\",\n \"protocol\": \"webhook\",\n \"urlEnv\": \"TEAM_STATUS_WEBHOOK_URL\",\n \"secretEnv\": \"TEAM_STATUS_WEBHOOK_TOKEN\"\n },\n {\n \"id\": \"slack-status\",\n \"name\": \"Slack status\",\n \"protocol\": \"slack\",\n \"urlEnv\": \"SLACK_STATUS_WEBHOOK_URL\"\n }\n]\n```\n\nSupply those referenced environment variables to the server. URLs must use HTTPS and cannot contain user credentials or a fragment. `secretEnv` is optional and sends its value as a Bearer authorization header. The Slack protocol uses an existing [Slack incoming webhook](https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks/), with a literal text block and link previews disabled. There are no email, Teams account, or channel-management adapters; a separately configured HTTPS relay can consume the generic webhook protocol.\n\nIn Settings → Notifications, select the destination, its events, and its projects, then save. Available events are run completion, run or verification failure, and requests for attention. “Without a project” covers runs with no project; “All projects” includes future projects. A run that finishes with failed or blocked verification produces a failure event. Sending a test is a separate, explicit action and works before enabling automatic notifications.\n\nGeneric webhook requests contain JSON with `version`, `id`, `event`, `runId`, optional `project`, `pattern`, `status`, `result`, and a task `title` limited to 200 characters. A manual test additionally has `test: true`. They exclude repository paths, transcripts, command output, and imported source content. Task titles can contain sensitive business information: opt in only for an appropriate destination. Recognizable secrets and configured credential values are redacted. The browser API exposes destination hostname, scope settings, and delivery metadata; it never exposes the webhook URL, credential names, tokens, or saved payloads.\n\nThe local outbox persists an event before sending, keeps at most 200 deliveries and 2,000 recent event identities, and sends at most 20 requests per worker pass. The worker checks every ten seconds, uses a ten-second request timeout, and refuses redirects. HTTP 429, server errors, and connection failures retry up to three total attempts with bounded exponential backoff. `Retry-After` seconds can extend the delay to at most five minutes. Other HTTP errors fail once. Pending deliveries expire after 24 hours without an attempt. When the queue is full, the newest event is dropped and its count and reason appear in the UI. Delivery history exposes failures, attempt counts, and an explicit retry action.\n\nThe `Idempotency-Key` request header is the persistent delivery ID. Recipients should deduplicate it. Exactly-once delivery is not guaranteed: a timeout or crash can happen after a recipient accepts a message. A delivery interrupted while sending becomes an unknown-outcome failure on restart and requires an explicit retry. Slack incoming webhooks do not provide an application-level deduplication guarantee. After 2,000 newer event identities have displaced an old identity, replaying that old event may deliver it again.\n\nChanging the configured URL or protocol requires renewed opt-in. Disabling a destination or narrowing its scope cancels pending automatic deliveries, but cannot retract a request already in flight. Restoring an encrypted backup disables saved notification opt-ins and cancels pending or sending deliveries. Corrupt saved state leaves notifications unavailable and preserves the file for repair. Delivery state belongs to one server process; do not share its storage across multiple running servers.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Outbound notifications"},{"group":"Operate safely","id":"security","source":"docs/security.md","body":"# Security, subscriptions and caveats\n\nBefore starting work, decide whether you trust the repository, which agent permissions it needs, and where its checks should run. Read the boundaries below before enabling remote access or Full access.\n\n## How code-flow uses your subscriptions\n\ncode-flow does not read agent login files. Each adapter launches the vendor's **official, unmodified CLI** in headless mode, and that CLI uses its own login. Explicit external integrations and notification destinations use server-side environment credential references; those variables are stripped from child processes and filtered from exports:\n\n| Agent | Command used | Login | Sandbox / permissions |\n|---|---|---|---|\n| Claude Code | `claude -p --output-format stream-json` | Pro/Max (or API key) via `claude` | read: edit tools disabled, git read-only commands allowed (never with `--output`, which writes anywhere, or `--no-index`, which reads anywhere) · write: `acceptEdits` + an allow-list of build/test commands (`bypassPermissions` with Full access) |\n| Codex | `codex app-server` (JSON-RPC over stdio: `thread/start` or `thread/resume`, `turn/start`, `turn/steer`); `codex exec --json` / `codex exec resume` when app-server isn't available | ChatGPT plan (or API key) via `codex login` | Codex sandbox: `read-only` / `workspace-write` (`danger-full-access` with Full access); optional one-action approval inbox on supported app-server turns |\n| Antigravity | `agy -p --output-format stream-json` | Google AI Pro/Ultra via an interactive `agy` login | `--sandbox` (verified: blocks writes outside the worktree; dropped in write steps with Full access) + `--dangerously-skip-permissions` (see [caveats](#caveats)) |\n\n**Read-only is enforced by code-flow itself, for every agent.** Before a read-only step (plan, review, judge, debate) code-flow snapshots its worktree: the commit, uncommitted changes and untracked files. Afterwards it puts back anything the step changed, including commits, and logs a warning. A side chat's **Ask** is the exception: you may be working in that worktree at the same time, so what changed is named in a warning and left in place.\n\nThis is the boundary each vendor draws for personal use. Don't extend code-flow to read or reuse the CLIs' OAuth tokens; Anthropic and Google both prohibit that, and Google has suspended accounts for it. If you ever turn this into a product for other people, each user must sign in with their own account, and Anthropic's terms then require API keys for products.\n\n> Gemini CLI stopped serving Google AI Pro/Ultra/free personal accounts on 2026-06-18. `agy` is the CLI that serves those plans now.\n\n## The server\n\n- It listens on `127.0.0.1` only, checks the Host and Origin headers, and wants a session cookie or Bearer token on every API call (see [signing in](getting-started.md#signing-in)).\n- The first browser to open code-flow signs in without the link only when it connects directly from this machine, and only until a browser has signed in (that way, with the link, or with its cookie) or code-flow has started an agent, a check or an app. After that, a program here can't get a session by asking for the page. A tunnel (ngrok, cloudflared) forwarding to the port can rewrite the Host header to `localhost`, so a request carrying forwarding headers (`X-Forwarded-For`, `Forwarded`…) never signs in that way.\n- code-flow's pages are never shown inside another page (`X-Frame-Options: DENY`, `frame-ancestors 'none'`), so a page elsewhere can't lay them under a decoy and have you click. The side panel's Browser frames your app through its preview, on a port of its own, never code-flow itself.\n- Choosing **Wi-Fi · local network** under **Remote access** exposes the phone view and webhooks on your local network. Configure HTTPS through **Set up secure remote access**, using a Tailscale certificate or an imported certificate/key pair. Legacy `CODEFLOW_TLS_CERT` and `CODEFLOW_TLS_KEY` overrides are also supported. Saved keys have owner-only permissions; setup is local-owner-only, validates identity and dates, and revokes pairing when replacing certificates. Invalid or expired saved certificates disable the listener without an HTTP fallback. Full phone control on Wi-Fi requires HTTPS; `CODEFLOW_ALLOW_INSECURE_PHONE_CONTROL=1` is an explicit development exception. See [Automations](automations.md#phone-view).\n- **Tailscale · access anywhere** binds the remote listener only to the IPv4 address reported by the local Tailscale client. Read-only links can use HTTP inside Tailscale's encrypted connection. Full control still requires HTTPS: Tailscale does not stop browsers from sending non-Secure cookies to other HTTP services on the same IP. Non-Tailscale source addresses and this computer's own connections are refused. Missing/disconnected Tailscale and bind errors never fall back to a LAN listener. Code-flow reads Tailscale status and requests a certificate during secure-access setup. It subsequently renews that saved Tailscale certificate automatically for the same verified computer name; renewal never changes the host identity or enables full control. It does not configure the tailnet or enable public access. Switching transports rotates access tokens and invalidates paired sessions; switching to Wi-Fi without TLS disables full control. See [Tailscale setup](automations.md#remote-access-with-tailscale).\n- That network listener refuses connections from this computer itself, from loopback or any of its own addresses. Programs here, the agents code-flow runs among them, can read `~/.codeflow`, phone secrets included; through the network listener they could otherwise sign in with full control and run commands outside their sandboxes. (Binding it to the Wi-Fi addresses alone wouldn't stop that: a program here can connect to those too.)\n- **Full control from remote devices** signs in with a separate pairing link that expires after ten minutes, including across restarts. Pairing creates a separate 30-day session for each device; only token hashes are stored, TLS cookies are Secure, and individual devices can be revoked. **New link** and disabling remote/full control rotate the generation and invalidate earlier sessions. Cross-origin requests are refused. Paired devices have the owner's full control; this is not a shared-project roles system. See [Full control from your phone](automations.md#full-control-from-your-phone).\n- Webhooks: each automation has its own secret (a Bearer token, or GitHub's signature). Secrets live in `~/.codeflow` (webhook secrets in `triggers.json`), which programs running as you can read, the agents included. So text a webhook sends (`details`) reaches the agents fenced as information, not instructions, and its run's reviews aren't learned as lessons. A GitHub delivery sent again is ignored, whatever its delivery id (the signature doesn't cover the id), and webhooks start a limited number of runs an hour. See [Webhooks](automations.md#webhooks).\n- The Browser's **Snapshot** loads pages in a headless Chrome on this computer, so it refuses addresses on this computer and link-local ones (where cloud machines keep their metadata service), checking every address a name resolves to. What the page asks for as it loads, redirects included, is checked the same way. The exception is a local app at its `http://localhost:<port>` address, as the Browser shows it, never code-flow's own port. Private network addresses are fine. Not covered: WebSockets, requests from workers, and a name that resolves differently a moment later (DNS rebinding).\n\n## Running the agents' code\n\nComposer Improve uses an empty private Git repository for Quick mode and a bounded\ncommitted snapshot for repository mode. It copies no source Git history, remotes,\nhooks, working-copy drafts or executable provider configuration. Cleanup removes\nonly that private directory and never resets the source checkout. Native Safe/read\npermissions remain a CLI boundary rather than an OS filesystem sandbox; filesystem\nrestrictions block Improve entirely. See [Improve setup and limits](getting-started.md#your-first-run).\nApp-managed subprocesses use absolute PATH entries, including explicit environment\noverrides. On macOS/Linux, an absent or unusable PATH falls back to `/usr/bin:/bin`\nso repository-owned tools cannot be selected through the current directory.\n\nLocal shared-project accounts grant scoped viewer, reviewer, contributor and\nmaintainer access through closed shared review and team-board API surfaces. Roles alone do not delegate execution or the owner's phone capability. Separately, the owner can grant per-project host execution to individual contributors through the [host queue](team-collaboration.md#let-teammates-run-tasks-on-the-host). Its dedicated routes accept only task identity and revision, use owner-selected CLIs and limits, and enforce queue ownership and live grants. This is trusted host execution, not an OS sandbox; agents and repository checks retain the host's available authority. Generic owner shell APIs remain unavailable. A separate, default-off [terminal grant](team-collaboration.md#give-trusted-teammates-an-interactive-terminal) permits individual contributors to open their own interactive shell as the host OS user. Project grants determine the starting folder, not a filesystem boundary. Shell users can reach host files, credentials and other processes; only grant this to trusted people. The app enforces session ownership, bounded input/output, live revocation and a 30-minute keyboard idle timeout. Closing a session does not undo changes or reliably stop detached processes.\nBrowser sessions expire and revoke immediately. Shared-review projections omit raw transcripts, private context and\nhost paths; explicitly granted terminals return raw output to their creator and can reveal those details. See [shared projects](shared-projects.md).\n\n[Team collaboration](team-collaboration.md) adds a per-machine runner credential\nbound to a shared account. It is shown once, stored only as a hash on the team\nserver, reaches only the team routes and is revoked there in one step; the\nowner's own token is matched before a bearer is read as a runner credential, and\nthe files that hold credentials on a linked computer (`team-connections/` and legacy `team-link.json`) are\nleft out of backups like the other credential stores. The team server never\nreaches into a teammate's machine: every connection is outbound from that\nmachine, to the exact address its owner entered (a redirect or an untrusted\ncertificate is refused), and execution starts only from each computer's own\ncode-flow, with that person's own CLI logins, in a worktree there. Progress,\ncheckpoints and discussion are the only things that travel; transcripts, local\npaths and copied secret files stay where the run is, and the reasons published\nfor failed, stopped or lost attempts have local paths replaced by `<local path>`.\nTeammates' comments never enter a prompt; an instruction they send to an agent\nreaches it only after the task's owner, or a maintainer of its project (the team\nserver's owner is one everywhere), approves it, and then as a message to the run\nattributed to its author; a hand-over checkpoint's context arrives fenced as\ninformation, not instructions.\n\nCustom [filesystem policies](filesystem-policy.md) enforce application reads,\nwrites and filtered diffs, and pause ordinary code execution globally where its\nbackend cannot enforce those rules. Saved restrictions are checked before\ntrusted extension code is imported. [Extensions](extensions.md) are explicit,\nversion/hash-pinned owner-trusted server code with full process authority, not\nuntrusted sandboxed plugins.\n\n[SSH workers](remote-workers.md) use provisioned targets, strict host keys,\nworker-local credentials and durable private receipts. A disconnected accepted\nturn is uncertain; reconnecting does not execute it again. [GitLab/Bitbucket\ncode workflows](code-hosts.md) require prepared agent containers for fetched PR\ncode and use exact-ref Git leases for publication. Provider-native approval\npolicy and PR closure cannot be transacted atomically with a Git push; unknown\npolicies and unsupported queues fail closed.\n\nA check runs the agents' code (tests, scripts) on your machine, outside Codex's and agy's sandboxes, as your CI would. With **Full access** the agents themselves run without a sandbox too (see [What agents may do](configuration.md#what-agents-may-do-while-building)). Use code-flow on repos and tasks you trust, or run checks in Docker ([isolation](automations.md#isolation)), or code-flow itself inside a container or VM.\n\nText from issues, PRs, comments and CI logs is treated as untrusted, and runs started from people outside the repo are locked down. Dependabot's PR descriptions and CI logs are fenced as untrusted text too, and so are code scanning messages, which quote the code they flag. **▶ Run app** on a locked-down run asks before it starts that code on your machine. See [Untrusted text](github.md#untrusted-text).\n\nFiles copied into worktrees from your checkout (`copy`, e.g. `.env`) never go into the agents' commits, and neither do new files named like secrets. They're never copied through a symlink an agent left in the worktree. See [Per-repo config](configuration.md#per-repo-config).\n\n## Your checkout and the worktrees\n\nA broken worktree is never mistaken for your checkout. Worktrees live inside your repository (`.worktrees/<run>/<name>`), and the `.git` file that ties each one to it is a file in the worktree that any agent can change. Without it, git run in the worktree would find your checkout instead. So code-flow guards its own git:\n\n- code-flow's git in a worktree never reads the worktree's `.git`. It's told the worktree's git folder (`GIT_DIR`, `GIT_WORK_TREE`), which it finds from your repository's side: the folder in your repository's git folder that names the worktree. That holds for everything code-flow runs there, reads included: the Files panel, diffs, status, the test-integrity checks, snapshots. So an agent that points `.git` at a git folder of its own can't bring in its config, whose filter, textconv or external diff code-flow would otherwise run outside the agent's sandbox just by looking.\n- Before it changes a worktree (undoing a read-only step, putting back test files, committing, merging, resetting a scratch checkout, removing it, or starting an agent there), code-flow also checks that the worktree's `.git` is still the file git wrote: it points to the worktree's own folder in your repository's git folder, and that folder points back. Before anything that moves a branch (undoing commits, committing, merging), the worktree must be on its run's branch. If any of that fails, the step fails and the run log says what's wrong (\"its .git is missing\", \"it's on another branch…\"). Nothing in the worktree or your checkout is changed. Remove the run's worktree, or put its `.git` back, before running it again. The git that then makes the change uses the git folder that was checked, so a `.git` rewritten in between changes nothing.\n- code-flow's git never looks above the folder it runs in (`GIT_CEILING_DIRECTORIES`), so a worktree without `.git` is an error, not your checkout.\n- code-flow's git runs without `core.fsmonitor` and without hooks, whatever a config file says. Your own hooks still run where you'd expect them: when code-flow merges a result into your checkout, and when it pushes (Git LFS uploads its files in `pre-push`).\n- code-flow's diffs never run an external diff program or a textconv filter (`--no-ext-diff --no-textconv`), and a snapshot saves untracked files as they are, without clean filters.\n- Not covered: submodules. To tell whether a submodule has changes, git runs itself in the submodule's folder, and that git goes by the `.git` there, which an agent can rewrite too. In a worktree with submodules (or a git repository an agent made inside it and code-flow committed), `git status` and `git add` can still run a filter that the agent's config names. Run untrusted tasks on repositories with submodules in a container or VM.\n\n## Caveats\n\n- **agy permissions** (tested with agy 1.3.5): when headless agy hits a tool it isn't allowed to use, it doesn't work around it. The turn ends with an empty answer, which code-flow reports as a failed step that names the denied action. That's why code-flow passes `--dangerously-skip-permissions` together with `--sandbox` by default. The sandbox confines writes to the worktree, and code-flow reverts any changes made during read-only steps. Older agy versions printed nothing when piped; code-flow reports that as an error too.\n- Native permission requests are declined by default. Interactive Codex app-server runs can explicitly enable the one-action approval inbox. Requests expire, are scoped to the active run/step/thread/turn and worktree, and cannot grant session-wide permission or policy amendments. Read-only work cannot gain file writes. Unattended and locked-down source runs never wait for native approvals. Other transports keep their existing denial behavior. Team questions remain separate from tool permissions.\n- **Mid-turn notes are Codex-only, and app-server is experimental.** Mid-turn delivery needs `codex app-server`, which Codex labels experimental (written against codex-cli 0.162). If the installed Codex lacks it, or speaks a protocol this client doesn't, code-flow falls back to `codex exec` and logs that once. It then sticks with exec until the server restarts, so restart code-flow after upgrading Codex. A note is marked delivered as soon as Codex accepts it into the turn. If Codex rejects it (the turn just ended), it goes to the next turn instead. A note for \"anyone\" goes to a running Codex turn, not to whoever starts next.\n- Claude's write mode can run allow-listed commands such as `npm`, `node` and `make`, and those can run arbitrary project scripts. Only run it on repos you trust. Codex is additionally confined by its own sandbox.\n- agy needs `--dangerously-skip-permissions` for read-only steps too, so for agy the snapshot-and-restore is what enforces read-only. A side chat's **Ask** isn't restored, so what agy changes there stays, and the next **Change** commits it: ask Claude or Codex when that matters. Changes to gitignored files during a read-only step aren't detected.\n- The test-integrity checks catch the common ways to game a check, not every one. A flagged result is a prompt to look, and an unflagged one isn't proof.\n\nAuto team's [memory boundary](auto-team.md#three-memory-scopes) keeps source trust separate from execution permissions. Agent messages cannot authorize actions. Private memory is isolated in context assembly and APIs, not from fully trusted local processes. Untrusted/unknown project-memory proposals require explicit revision approval; source provenance follows derived memories. Deleting a memory removes it from retrieval; entries written with it in context keep their own text and inherit its provenance. Deletion does not touch source run transcripts, provider conversations or backups.\n\nAuto team live collaboration uses a separate loopback bridge and an expiring capability for a single run/member/attempt. The general API token is never delegated. Credentials are kept in mode-0600 temporary files, revoked after the turn and excluded from prompts, logs and arguments. Members run as the same user, so a member with a shell could read another member's credential in the brief window before the bridge client deletes it; such a capability is scoped to that member's attempt, never to the application token or to you. Claude turns receive the bridge with `--strict-mcp-config`, so your own and the repository's MCP servers are not loaded in Auto team turns. Every mutation rechecks the active assignment and uses runtime-derived provenance. Collaboration does not widen the CLI's existing filesystem or network permissions. Native nested agents are disabled for supported Claude/Codex team turns; unsupported capabilities stay explicit. See [Auto team](auto-team.md#adapter-capabilities).\n\n## Documentation assistant\n\nThe local documentation assistant uses the owner's installed built-in CLI in a disposable empty Git directory with read-only, Safe adapter settings. It receives bounded documentation excerpts and the question/conversation supplied by the browser. Code Flow does not copy a project, run transcript or credential file into the help directory. Provider-level configuration and hooks still belong to the CLI trust boundary; the “do not use tools” prompt is not an OS isolation mechanism.\n\nThe endpoint inherits owner authentication, origin validation and the global filesystem-policy pause. Shared accounts and runner credentials cannot invoke it. Answers render as text and source links are selected from known guide IDs. GitHub Pages hosts static guides only and never probes localhost for an assistant. Read [Ask code-flow](help.md) for retention, quota, cancellation and limits.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Security, subscriptions and caveats"},{"group":"Operate safely","id":"filesystem-policy","source":"docs/filesystem-policy.md","body":"# Project filesystem policy\n\nChoose explicit read and write rules for the project. Review the execution boundary before enabling restrictions: operations that cannot enforce those paths pause rather than silently bypassing the policy.\n\n## Set project path rules\n\nSettings → Projects & access stores owner-managed read and write rules per project. Each save uses the exact displayed revision; conflicting saves preserve the browser draft. Private files live at `CODEFLOW_HOME/filesystem-policies/<project>.json`, with version 1, a revision, timestamp and SHA-256 content hash. Corrupt or symbolic policy storage fails closed without replacing it with unrestricted defaults.\n\nRules use literal relative repository paths. `.` covers the whole repository; `src` covers that file or subtree. Each read/write operation must match an allow entry and no deny entry. Empty allow lists deny everything. Denies also match conservatively across case and Unicode compatibility variants. There are no globs, traversal components, absolute paths or Git internals. Credential paths, private keys and symbolic filesystem ancestors have fixed application-level denies. Policies from every matching project and the run's pinned snapshots intersect: choosing another project or weakening today's policy cannot silently weaken an older run's pinned policy.\n\nThe application enforces paths for repository file reads, tree and filename search, textual content search, file editing and filtered run diffs. Read and write checks run again before returning content or publishing a file edit. Both sides of a renamed diff must be readable. Restricted projects' raw run history, event transcripts, exports, configuration, memory and other evidence that cannot be reliably filtered are unavailable. Restricted results are omitted before list pagination, search and totals, including shared review dashboards. Existing feeds close and reconnect with a new snapshot generation when policy changes. Unscoped statistics, private notes, memory, saved automation/queue requests, diagnostics, notification payloads and maintenance data are also unavailable while any custom restriction is active. Project names, repository membership and policy metadata remain available to the owner.\n\n## Execution boundary\n\n**Any custom read or write restriction pauses normal application-managed execution across every project.** The owner-only [Filtered Docker task](filtered-tasks.md) is the dedicated enforcing exception: it runs one manual turn against an isolated fresh Git snapshot and imports permitted changes only into a separately reviewed branch. Native agents, commands, ordinary Docker mounts, SSH workers, full Git object databases and trusted JavaScript extensions do not currently provide an enforcing filtered filesystem. The application refuses these backends rather than representing prompts or a filtered checkout as a security boundary. This includes run creation, resumption, follow-ups, verification, repository readiness execution, terminals, previews, development services, code-host actions and queued dispatch. Health responses avoid Python/provider probes under a restriction. Public read-only phone views are disabled under any custom restriction.\n\nPolicy activation requires globally idle execution, closed terminals, stopped development services, no preparing/importing or unconfirmed filtered task, and no unresolved accepted/running/unknown remote receipts. Unavailable filtered-task receipt storage also blocks activation until repaired. Background PR watches, source sweeps, deferred/coordinated readiness checks and notifications pause and finish their current work before the durable policy commit. Their underlying preparation promises remain tracked even if a queue timeout has already expired. The manager holds a global execution lease during the commit. An operation still running in another repository makes activation fail instead of racing it. Policy changes never implicitly contact a remote worker to resolve uncertain receipts: inspect or cancel them deliberately first.\n\nTrusted extensions can retain host authority after importing, including bundles rejected after initialization. If extensions were attempted in the current process, restrictive activation is refused: disable extension configuration, restart, then apply the restriction. On later startup, saved restrictions are checked before importing any extension bundle; imports remain deferred until restrictions are deliberately removed and the application restarts. Automatic launches, watches and notification delivery stay paused until all current policies are unrestricted. Deliberately clearing restrictions resumes those services; it does not erase a run's pinned policy.\n\nThis is an application policy with fail-closed execution, not an OS sandbox or retroactive erasure. The owner, other local processes, existing external backups and manually started processes remain outside this boundary. Historical journal bytes are retained privately; the application blocks unsafe presentation instead of rewriting provenance. Filtered tasks provide an isolated filesystem and synthetic Git database for their narrow manual execution path. Native agents, SSH workers and normal automated/multi-agent workflows remain unsupported under restrictions.\n\n## HTTP contract\n\nOwner-only `GET /api/projects/:id/filesystem-policy` returns the committed snapshot. `PUT` accepts `{revision, read:{allow,deny}, write:{allow,deny}}`; each list has at most 100 paths, each at most 500 characters. Saves return the next committed snapshot. Revision, activity or unsupported execution conflicts return 409; denied data returns 403; invalid paths return 400; unreadable private policy storage returns 503. `/api/v1` follows the same authorization and policy. Shared accounts cannot edit policy. Parsed JSON is reused by authorization and dispatch, while a later endpoint-specific byte limit remains enforced.\n\nTests cover exact CAS/hash/private storage, failures before activation, pinned policy intersection, case/traversal/credential/symlink rejection, actual browser allowed/denied reads and writes, version aliases, global execution denial and cross-repository activation races. Startup regressions ensure saved policies prevent trusted plugin initialization, interpreter probes and public phone history, and remove restricted results from shared feeds.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Project filesystem policy"},{"group":"Operate safely","id":"agent-containers","source":"docs/agent-containers.md","body":"# Agent containers\n\nChoose an explicit image and authentication volume before selecting container execution. A Docker installation alone does not make a provider ready to run inside that image.\n\n## Prepare an isolated agent\n\nAgent isolation runs the actual vendor CLI in a prepared Docker image. It is separate from\nthe existing container option for setup and verification commands. Set `loop.agentContainer`\non a run, for example:\n\n```json\n{\n \"agentContainer\": {\n \"image\": \"my-codeflow-agents:local\",\n \"authVolume\": \"codeflow-agent-login\",\n \"network\": true,\n \"memoryMb\": 4096,\n \"cpus\": 2\n }\n}\n```\n\nThe image must already exist locally and contain the official `claude`, `codex`, or `agy`\nbinaries used by the run. Missing images fail rather than being pulled or falling back to\nhost execution. Images should also provide the language tools required by the repository.\n\nThe process runs as your host UID and GID (65532:65532 where those IDs are unavailable).\nPrepare `/home/codeflow` so that this user can read and write it. `HOME` is `/home/codeflow`\nand `CODEX_HOME` is `/home/codeflow/.codex`. For persistent authentication and conversations,\nuse a named Docker volume created and authenticated by you inside the image. For example,\non macOS or Linux:\n\n```sh\ndocker run --rm -it \\\n --user \"$(id -u):$(id -g)\" \\\n --mount type=volume,source=codeflow-agent-login,target=/home/codeflow \\\n --env HOME=/home/codeflow --env CODEX_HOME=/home/codeflow/.codex \\\n --entrypoint codex my-codeflow-agents:local login\n```\n\nUse each vendor's own login flow in that container. The image or volume must have the correct\nownership before login; Docker's default root-owned empty volumes may need initialization.\nCode-flow never mounts your host home, reads or copies your cached provider tokens, or\nforwards host API keys into the container. Credentials baked into an image are accessible to\nanyone who can access that image; a privately managed named volume is preferable. A volume\npersists agent conversations as well as login state. Without one, CLI changes inside the\ncontainer are discarded, including newly written conversations needed to resume later.\n\nOnly the selected workspace is bound into the container, writable for write turns and\nread-only for read turns. Its `.git` marker and the verified repository Git metadata are\nmounted read-only. This allows inspection while preventing agents from rewriting repository\nmetadata; code-flow performs commits on the host after the turn. Git global and system\nconfiguration are disabled inside the container. Extra files generated by code-flow, such\nas a Codex output schema, are mounted individually read-only when needed.\n\nThe container uses Docker's isolated network by default because inference requires network\naccess. Setting `network: false` passes `--network none`; inference will then be unavailable\nunless the configured image supports an offline provider. There is no host networking or\nDocker socket mount. Linux capabilities are dropped, privilege escalation is disabled,\nprocesses are limited to 256, and memory and CPU limits are enforced. The container's own\ntemporary filesystem layer remains writable for vendor CLI state and installed-tool needs;\nit is discarded when the turn ends.\n\nLive team-tool bridges are not supported inside containers. The UI and adapter status report\nthis limitation, and team members use structured outcomes and replies on subsequent turns.\nCodex app-server still supports streaming, steering, and scoped one-shot approval over stdin.\nAdditional filesystem/network permission requests and session-wide policy amendments remain\ndeclined. Selecting full CLI trust does not remove the outer Docker container boundary.\n\nCancellation, timeout, stall, and shutdown remove the daemon-owned container as well as the\nDocker client. Container names are recorded in the process recovery journal so startup can\nalso remove containers left behind by a server crash before recovering worktrees.\n\nRun `npm run test:containers` for native ordinary and filtered runtime checks.\nThe tests build and remove small fake-CLI fixture images and never\nuse provider credentials or model quota. The regular suite also tests launch arguments,\nall adapter transports, daemon/client separation, cancellation, and timeout with a fake\nDocker runtime.\n\nPer-project read/write restrictions use the separate [filtered task\nbackend](filesystem-policy.md), with a fresh allowed-file Git namespace and\nexplicit review-branch import. Ordinary agent containers mount their worktree's\nfull Git common directory and cannot enforce those restricted reads.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Agent containers"},{"group":"Operate safely","id":"filtered-tasks","source":"docs/filtered-tasks.md","body":"# Filtered Docker tasks\n\nUse a filtered task when only selected project files should reach a container. Inspect the snapshot, provision its runtime explicitly, and review the returned branch before importing changes.\n\n## Run against a bounded snapshot\n\nProject → Repository tools → Filtered tasks → Filtered Docker task runs one explicitly requested provider turn against a bounded, isolated committed snapshot. This is the enforcing execution path for custom project file policies. Normal native, SSH, automated and multi-agent runs remain refused while any custom restriction is active.\n\nSelect the project/repository, supported provider (`claude`, `codex`, `agy`), an already prepared local Docker image, instructions and a 1–30 minute limit. Network defaults off; enable it deliberately when the provider needs remote inference. The service resolves the local image to an immutable SHA-256 ID and uses that ID for dispatch. No images are pulled, no host-provider probe or login runs, and no fallback launches a host CLI. A local Docker context is required.\n\n## Filesystem and runtime boundary\n\nOnly policy-readable regular committed files are exported. Every applicable current project policy and pinned policy intersects; choosing a different project cannot widen access. The export is a fresh Git repository with one synthetic commit and no original objects, history, remotes, alternates, hooks or configuration. Ignored/uncommitted files, private application memory, notes, context attachments and host credentials are not copied. Readable source symlinks/Gitlinks, path collisions and unsupported names are refused. Export limits are 500 files, 512 KB per file and 16 MB aggregate content.\n\nThe container receives only that export, its synthetic Git metadata mounted read-only, any readable copied files denied writes mounted read-only, and an exact generated schema file when the official adapter needs one. The parent job store, original repository/common Git directory, host home, authentication volumes, environment and Docker socket are never mounted or forwarded. Container labels and extra readonly mounts come from a server-only object-identity capability; serialized client settings cannot grant it.\n\nThe image root filesystem is read-only. `/tmp` is a 256 MB tmpfs and a fresh `/home/codeflow` is a 128 MB tmpfs, owned by the runtime UID/GID. Capabilities are dropped, privilege escalation is disabled, and PID, memory and CPU limits apply. The export is watched for excessive entries, depth, logical/allocated bytes and unsupported links; excess aborts the task and requires confirmed daemon removal. Final scans have the same aggregate entry/deadline bounds. Bind-directory monitoring is best-effort polling, **not an OS disk quota**: output can overshoot between scans. Newly generated forbidden output is refused wholesale; it never reaches the original repository.\n\nAn owner-prepared image and Docker's own isolation are trusted inputs. Its intentionally provisioned provider identity can access that provider, and enabled network can reach external services; this feature is not an internet-egress allowlist. The app does not inspect image credentials. Other owner-controlled OS processes and external repository edits are outside the container boundary.\n\n## Preparing authentication without host caches\n\nA login baked under `/home/codeflow` is hidden by the fresh home mount. Ordinary `agentContainer.authVolume` profiles are intentionally unsupported for filtered tasks: a shared volume can retain conversations or repository data from another policy. Prepare a private runtime with a launcher that copies **only its deliberately provisioned credential file** from an immutable in-image seed into the fresh runtime home, then executes the official CLI. Do not copy a host home, provider session directory, Git configuration or repository into the image.\n\nFor example, install the official Codex binary in a reviewed base image at `/opt/vendor/bin/codex`, and provide this `/usr/local/bin/codex` launcher:\n\n```sh\n#!/bin/sh\nset -eu\numask 077\nmkdir -p \"$CODEX_HOME\"\ncp /opt/codeflow-credential-seed/codex/auth.json \"$CODEX_HOME/auth.json\"\nprintf 'cli_auth_credentials_store = \"file\"\\n' > \"$CODEX_HOME/config.toml\"\nexec /opt/vendor/bin/codex \"$@\"\n```\n\nProvision a separate container from that base, with no host mounts or repository, using the vendor's own login flow. For Codex, choose file credential storage, set `CODEX_HOME=/opt/codeflow-credential-seed/codex`, then run the installed binary's `login --device-auth` flow if your account permits device login. Keep only the credential file in the seed, set its ownership for the runtime UID/GID, and save the deliberately authenticated runtime as a **private local image**. The app runs containers as the host UID/GID (65532 where unavailable). Follow [OpenAI's authentication documentation](https://developers.openai.com/codex/auth/) for file storage, device-login requirements and token handling.\n\nThe seed is itself a credential: anyone who can read the image can read it. Never publish that image. Its runtime copy can refresh during a turn, but the refreshed home is discarded; when its seed expires or is invalidated, provision a fresh private image instead of copying shared session caches back into the application. Claude and Antigravity need equivalent provider-specific launchers using their own supported login/storage flow. The app never reads, exports or copies OAuth credentials itself. An offline prepared provider needs no credential seed and can keep networking disabled.\n\nThe real Docker fixture tests this exact fresh-home launcher mechanism with an in-image dummy credential and fake provider binary, plus actual namespace/readonly enforcement. It does not authenticate real providers or consume model quota.\n\n## Review, import, cancellation and cleanup\n\nAfter the turn, daemon container absence must be confirmed before inspecting output. Untrusted exported `.git` is removed; the host never asks it to execute Git commands. Bounded regular bytes, additions, deletions and executable modes are validated directly. Changed paths must be both readable and writable under the pinned/current policies. Policy/project membership and source HEAD are rechecked before dispatch, after completion, preview and import. Stored policy snapshots must have valid content hashes; enforcement uses freshly authorized canonical rules. Stored source blob identities must match their exact original committed paths.\n\nPreview shows bounded original/result content, hashes and any binary/truncation flags. **Import into new review branch** is separate and explicit. Import checks the exact job revision/result hash/source HEAD, current policy identities, original changed-file bytes/modes, original staged blob/mode and new-path absence. These checks repeat after the durable importing receipt. Host Git uses a sanitized environment, disabled replacements/lazy fetch/fsmonitor/hooks/global/system attributes, disabled automatic maintenance and object-only writes without filters. A private index starts from the original source tree; only permitted changes are replaced. An atomic Git transaction verifies original HEAD and creates a uniquely named `codeflow/filtered-…` branch. Original checkout, index and denied base-tree files stay unchanged. The permanent commit message contains only the task ID; private instructions stay in the application record.\n\nPreparing/running/importing receipts are fsynced before their effects. A restart converts unfinished phases to `unknown`, never retries or imports them. A running/preparing cancellation aborts preparation/provider work, waits for it to settle, removes owned labelled daemon containers and verifies absence before marking stopped. An interrupted import requires **Inspect interrupted import receipt**: an exact matching branch proves publication; proven absence permits a separately requested future import; a mismatched branch stays blocked. Unknown daemon receipts also block policy activation until explicitly cancelled and confirmed stopped.\n\nHistory holds at most 100 jobs. **Delete private task and export** uses the current revision and is available only for terminal, confirmed-stopped jobs without an ambiguous import. It removes the private record/export, fsyncs the parent and preserves any imported review branch. There is no implicit retention deletion, branch removal or automatic merge.\n\n## API and tests\n\nAll endpoints are owner-only, including `/api/v1` aliases. `GET /api/filtered-tasks?project=…` returns metadata; `GET /:id` returns detail only while its current policy identity still matches. `POST /api/filtered-tasks` accepts `{project,repo,task,agent,image,network?,maxMinutes?}` and returns 202. `POST /:id/preview-import` produces a bounded preview; `/import` accepts `{revision,expectedHead,resultHash}`. `/cancel` and `/receipt` accept `{revision}`. `DELETE /:id` accepts `{revision}`. Closed routes reject extra path segments. These requests participate in the filesystem activation barrier; policy activation refuses any preparation, import or unconfirmed daemon work.\n\nRun the regular filtered task, lifecycle, limits and browser suites for CAS/races/restore/UI evidence. Run `CODEFLOW_TEST_DOCKER=1 node --import tsx --test test/filtered-tasks-docker.test.ts` for real Docker namespace, original Git/denied blob absence, readonly mounts, fresh-home credential seeding, branch import and cancellation removal. The Docker fixture builds/removes its own small image and never contacts a model provider.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Filtered Docker tasks"},{"group":"Operate safely","id":"dependency-discovery","source":"docs/dependency-discovery.md","body":"# Dependency discovery\n\nExpand Getting started in the composer and select Check all dependencies. Resolve required tools first; optional Docker, terminal and snapshot capabilities can be configured when you need them.\n\n## Check readiness\n\nExpand **Getting started** and choose **Check all dependencies**. The report separates required tools from optional capabilities, explains missing authentication or compatibility, and provides a concrete next step. A successful run requires Node.js 22.13 or newer, Git 2.38 or newer, and at least one compatible provider CLI signed in through its own authentication flow. Unknown authentication or unverified compatibility is displayed as unknown rather than ready.\n\nProvider and Git checks reuse the application's doctor implementation. Optional terminal inspection checks whether the configured Python can load its pseudo-terminal modules and reports the actual shell fallback. Browser snapshot inspection uses the same Chrome-compatible browser discovery as snapshots and runs a bounded version probe. Docker inspection checks the CLI version and asks its daemon for its version; a present CLI with an unavailable daemon is reported separately. Docker availability does not prove a provider image or authenticated volume has been prepared.\n\nThese are read-only checks. They do not execute repository commands, install packages, sign into accounts, pull images, or change server configuration. Optional failures do not block normal host CLI runs. A missing Python helper still permits the line-based terminal, and a missing snapshot browser does not remove the embedded app preview. Existing `CODEFLOW_PYTHON` and `CODEFLOW_CHROME` choices are inspected without being changed.\n\n`GET /api/doctor/dependencies` returns `{checkedAt,node,git,providers,optional}`. Optional entries identify `terminal`, `snapshots`, and `docker`, each with installation/readiness state, detected version/path when available, details, and remediation text. Provider probes retain the existing doctor timeouts; optional browser/Docker probes are bounded to five seconds and pseudo-terminal discovery retains its ten-second limit. Probe commands use executable argument arrays, never a shell command assembled from browser input.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Dependency discovery"},{"group":"Operate safely","id":"maintenance","source":"docs/maintenance.md","body":"# Backup and maintenance\n\nCreate an encrypted backup before restoring or removing stored data. Read the offline requirements and inspect the retention preview before applying a maintenance operation.\n\n## Protect your local history\n\nStop the code-flow server before running maintenance. Every operation against an existing data directory takes the same exclusive `server.lock` as the server and refuses to run while another server holds it. A restore writes a new directory and never merges into existing data.\n\nThe commands use `CODEFLOW_HOME` by default. Pass `--home /absolute/data/path` to choose another data directory.\n\n## Encrypted backup and restore\n\nSupply a password through `CODEFLOW_BACKUP_PASSWORD` using your shell or secret manager. Do not put it in command arguments. Passwords must contain 12–1,024 characters. The CLI never prints the password or file contents.\n\n```sh\nnpx tsx scripts/maintenance.ts backup --output /backups/codeflow.cfb\nnpx tsx scripts/maintenance.ts restore --input /backups/codeflow.cfb --destination /new/codeflow-data\n```\n\nBackups use AES-256-GCM authenticated encryption with a fresh 16-byte salt, 12-byte nonce, and a 32-byte key derived with scrypt (`N=32768`, `r=8`, `p=1`). The compressed manifest contains each file's length and SHA-256 digest. No plaintext archive is written to disk. Output files use mode `0600`; restored directories use `0700` and files `0600` on supported platforms.\n\nBackups are limited to 256 MiB of file data and 20,000 files. Restores bound archive and decompression size and reject invalid manifests, duplicates, traversal paths, damaged data, wrong passwords, and existing destinations. The output directory must exist and the backup must be outside the data directory. Output files are created exclusively and existing backups are never overwritten.\n\nSymbolic links, special files, temporary files, server locks, process registries, phone settings/device sessions, shared account password/session stores, session credential files, team connection/capability files, saved team credentials under `team-connections/` and the legacy/local team link file `team-link.json` (this computer's runner credential for a team server, with its temporary and set-aside copies), remote-worker private capability receipts, `.env` files and private-key files are omitted. The command reports every omitted path. Restore also excludes this material from older archives. Recreate shared accounts, reconfigure phone pairing and credentials and connect a linked computer to its team server again after restoring; the team board itself (`team/`) and the local attempt records (`team-link/attempts/` and `team-runners/<connection id>/attempts/`) are included.\n\nRestored automations are disabled, their webhook tokens are rotated, and queued deliveries are cleared. Review each automation, update its webhook sender with the new token, and deliberately enable it again. This prevents old schedules or deliveries from running immediately after recovery.\n\nRestored external notification settings are also disabled. Pending or in-flight notification deliveries are cancelled; destinations must be reviewed and deliberately enabled again.\n\nRestored deferred jobs are never dispatched automatically. Pending, waiting, claimed, failed and unknown jobs become cancelled with a restore-history entry. Existing started receipts remain metadata. Review the recovered configuration and explicitly retry a cancelled job only when you intend to launch it; saved claim and run IDs remain available for investigation. Malformed deferred storage aborts the restore.\n\nRestored coordinated changes also disable all implicit dispatch. Exact launch, dependency and shipping receipts remain available for manual investigation; pending tasks become cancelled, and claimed side effects become unknown. Deliberately recover or retry only after reviewing existing runs and local commits. Invalid coordinated storage aborts the restore.\n\nGit worktrees and repositories outside `CODEFLOW_HOME` are not included. Back them up separately when they contain needed commits or uncommitted work. The password is required for recovery and cannot be recovered from code-flow.\n\n## Saved per-type retention\n\nOpen **Settings → Data & storage** to save separate ages for archived run records, attachments, private/team memory and linked workspaces. Every type starts disabled; a blank age keeps that data. You can protect specific run and memory IDs. Settings are revisioned, and concurrent saves cannot overwrite each other. Saving settings never deletes data.\n\nSelect **Preview saved retention policy**, review the exact targets and protected reasons, then download the manifest. The preview fixes the cutoff, policy revision, source references and file identities. It contains paths and IDs, without memory bodies or titles. It expires after 24 hours. Stop the server before applying that downloaded file:\n\n```sh\nnpm run maintenance -- retention-policy --plan /downloads/codeflow-retention.json --apply --home /absolute/data/path\n```\n\nFor an entirely offline workflow, save settings from a JSON file, produce a preview and then apply that exact preview. An existing output file is never overwritten:\n\n```sh\nnpm run maintenance -- retention-settings --input /path/retention-settings.json\nnpm run maintenance -- retention-policy --output /path/reviewed-plan.json\nnpm run maintenance -- retention-policy --plan /path/reviewed-plan.json --apply\n```\n\nExample settings keep attachments and workspaces indefinitely, retain private/team memory for 180 days, and retain eligible archived run records for 90 days:\n\n```json\n{\n \"days\": { \"archivedRuns\": 90, \"attachments\": null, \"memory\": 180, \"worktrees\": null },\n \"keepRunIds\": [\"important-run-id\"],\n \"keepMemoryIds\": []\n}\n```\n\nApply takes the exclusive server lock and rebuilds the manifest using its original cutoff. Any changed policy, file identity, source reference, worktree state or supplied manifest rejects the entire plan before deletion starts. A partial scan cannot be applied. The scan bounds are 2,000 runs, 50,000 live memory entries, 1,000 targets, 100,000 files and 256 MiB of read data; individual logs are limited to 64 MiB. Keep other tools from changing repositories during offline apply. If an I/O error occurs after deletion begins, the result lists applied, failed and remaining targets; stop and create a fresh preview before another attempt.\n\nRunning, paused, interrupted, stopped, unarchived, pinned and watched runs are protected. Retained child runs, selective-rerun baselines, enabled automations and pending deferred jobs preserve their parent sources. Retained coordinated-change launch, dependency and shipping receipts protect their exact run records. Project knowledge is never bulk-deleted. Pinned memory, memory used by active/resumable/kept runs, and sources referenced by retained derived memory are protected. Remaining live memory preserves its origin transcripts, attachments and workspaces.\n\nEligible old private/team memory bodies are erased atomically while their provenance tombstones remain. Attachment retention deletes only bounded regular files in known staging/generated context directories and refuses tracked repository files or symlinked ancestors. Workspace retention requires verified managed linked Git ownership, a clean index and worktree, no hidden tracked changes, no nested repository/submodule, and no Git lock. Removal uses Git without force and preserves branches. Run retention removes only the selected terminal archived logs, journals and checkpoints. These changes remove recovery evidence, so review the preview and back up data you still need.\n\nThe older `retention --days 90` command remains an age-only report and does not assess pins or provenance. Its unguarded `--apply` behavior has been removed. Existing scripts must migrate to a reviewed `retention-policy` manifest; the compatibility alias `retention --plan /path/reviewed-plan.json --apply` uses the same protected exact-plan apply.\n\n## Log compaction\n\n```sh\nnpx tsx scripts/maintenance.ts compact --id RUN_ID\nnpx tsx scripts/maintenance.ts compact --id RUN_ID --apply\n```\n\nCompaction previews savings unless `--apply` is supplied. It keeps the first and latest run metadata snapshots and every transcript, step, check, and log event. Recovery journals remain byte-for-byte unchanged. Intermediate metadata snapshots are removed; their event sequence numbers are not renumbered. The replacement is synced and renamed atomically, and its old optional checkpoint is invalidated. Only terminal run logs up to 64 MiB are supported; incomplete or corrupt logs are rejected.\n\n## Startup checkpoints\n\n`appendLine(file, event, { checkpoint: { seq, run } })` captures a complete run snapshot, including steps, at call time. The existing serialized writer appends the authoritative event, reads its exact file identity, and atomically writes `<run-id>.checkpoint.json` before the next queued append.\n\n`readRunCheckpoint<T>(id)` returns `{ run, seq }` only when the version, checksum, run ID, steps, safe sequence number and source file identity match. Source identity includes length, nanosecond modification/change times, device and inode. A subsequent append, replacement, malformed or oversized cache, or missing file falls back to journal reconstruction. Reads are bounded to 16 MiB. Cache write failures update `checkpointHealth` and never reject an authoritative append or poison the write queue.\n\nCheckpoints speed up startup but are optional projections. JSONL logs and recovery journals remain the source of truth and are preserved until an explicit retention or compaction operation.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Backup and maintenance"},{"group":"Build and extend","id":"architecture","source":"docs/architecture.md","body":"# Architecture and development\n\nUse `npm run dev` for UI work without provider quota. Read the source layout below, then run type checking and the tests relevant to your change. The documentation site shares the app’s theme and builds alongside the UI.\n\n## Layout\n\n```\nui/*.tsx UI: Preact + TypeScript, compiled against the server's own types\n (app: shell and top bar · rail: the sidebar · composer · run: the run thread · review · insights · automations · projects\n · project: a project's page · side: the side panel, its files and notes · side-chat, changes, terminal, browser: its other tabs\n · panel-bits: what the tabs share (folders, the strip of open sessions, menus))\nui/bits.tsx small shared pieces: agent tags, the Markdown renderer, QR codes\nui/collab-*.tsx the Team page: the board and its inbox (collab-board), a task with its attempt, checkpoints, reviews and discussion (collab-task), Settings → Connections → Share access → People (collab-settings); collab-state.ts holds their signals and API helpers\nui/styles.css, ui/fonts the stylesheet (the paper theme, light and dark) and its font (Inter; licence included)\nui/theme.ts light or dark: follows the system until you pick one with the top bar's theme button\nscripts/build-ui.mjs builds hashed ESM chunks and CSS, then atomically publishes public/index.html (npm start builds it)\nscripts/check-node.mjs stops npm start and npm run dev on a Node older than 20, saying why\nscripts/pty.py the Terminal's pseudo-terminal (Python 3 standard library, no native npm module): runs your login shell in a pty, relays its bytes, takes resizes and reports the shell's pid, its terminal and the foreground on fd 3; when it ends, it hangs up everything in the shell's session\nsrc/server.ts HTTP API + SSE on 127.0.0.1 (Host/Origin checked, session cookie or Bearer token)\nsrc/stream.ts one event stream per browser window (/api/stream), carrying every topic it watches: runs, run:<id>, shell:<id>\nsrc/store.ts run logs and replay journal, per-run batched write queues, the one-server lock\nsrc/work-queue.ts bounded concurrent work with ordering within each run\nsrc/transcript.ts bounded reverse byte-offset transcript pages\nsrc/sse-writer.ts byte-bounded backpressure-aware stream output\nsrc/run-history.ts indexed summaries/search and stable cursor pagination\nsrc/performance.ts bounded local runtime diagnostics\nsrc/run-log.ts what a window is sent to show a run (its latest snapshots); run snapshots on disk as patches, and compacting old logs\nsrc/teams.ts provider-independent team members, tasks, messages and context projections\nsrc/memory.ts private/team/project memory, revisions, provenance and scoped retrieval\nsrc/memory-sqlite.ts SQLite transactions, JSON migration, indexed keywords and evidence-linked graph\nsrc/team-memory.ts run/project scope and Git applicability for memory retrieval and proposals\nsrc/team-mailbox.ts validated atomic collaboration commands, requests and waits\nsrc/team-scheduler.ts recorded completion batches with concurrent worker lifetimes\nsrc/team-tools.ts expiring member capability service; stdio MCP client in team-tool-client.mjs\nsrc/team-evaluation.ts immutable evaluations and evidence-gated rollout decisions\nsrc/projects.ts projects: named groups of repos, and which project a new run goes in\nsrc/log.ts code-flow's own log, ~/.codeflow/server.log (rotated at 5 MB)\nsrc/env.ts the environment of everything code-flow starts: yours, minus its token, npm_* and its own node_modules/.bin\nsrc/integrity.ts finds changes to tests that could make a check pass without a fix\nsrc/diff.ts fits a long diff into a prompt (whole files first)\nsrc/learnings.ts the repo's conventions files, and problems reviewers keep raising\nsrc/slots.ts caps how many agents and checks run at once\nsrc/stats.ts Insights: per agent, per pattern, comparisons; ranking for Auto\nsrc/ship.ts per-file diffs, safe merges, pull requests, worktree cleanup\nsrc/github.ts gh: issues, PRs, checks, failed-job logs, comments, pushes\nsrc/watch.ts PR follow-through: polls watched PRs and sends CI failures and comments back\nsrc/routing.ts \"Auto\" roles: pick an agent whose plan has room (and that passes most in this repo)\nsrc/templates.ts starting points (fix an issue, fix failing CI, council review…) and sweeps\nsrc/sweeps.ts sweeps: list issues, Dependabot PRs and alerts; start one run per picked item\nsrc/automation.ts automations: schedules, webhooks (Bearer or GitHub signature), firing runs\nsrc/tailscale.ts bounded, read-only Tailscale detection for the remote listener\nsrc/remote.ts the phone link: read-only view (server-rendered), settings for full phone control\nsrc/collab/*.ts the team board: model (types, bounds, validators, the task state machine), store (CODEFLOW_HOME/team/, atomic writes with a write-behind for heartbeats and progress only, authorization checked in the store), api (/api/team/* for owner, shared and runner principals, with its SSE stream), checkpoints (Git bundles whose only ref is refs/codeflow/team/<checkpoint id>, the id minted by the runner before bundling), hub-client (LocalHub and RemoteHub: the same runner code against the in-process store or a team server over HTTPS), link, runner and link-api (the linked computer: team-link.json, local attempt records, hand-overs, reconciliation and the recovery pass, /api/team-link/*)\nsrc/engine.ts RunManager, TeamContext (ask / worktree / commit / diff), event log\nsrc/patterns/*.ts the team patterns, written against TeamContext\nsrc/adapters/*.ts AgentAdapter per CLI → normalized AgentEvent stream\nsrc/git.ts every git call (bounded, never prompts), worktrees with their submodules, branches, commits, diffs, merges\nsrc/repo-tree.ts file tree, search and file contents for the Files tab\nsrc/shell.ts the side panel's Terminal: shells kept open, in a pseudo-terminal through scripts/pty.py (busy while a program other than the shell is in the foreground; never closed to make room while it has jobs in the background, found with ps); line-based with a marker after each command when there's no python3\nsrc/preview.ts the side panel's Browser: a proxy per local app, on a port of its own, that adds the overlay (src/preview-overlay.js: pick, box, console, adjust), rewrites the app's Content-Security-Policy so only code-flow (and the app itself) can frame it, and keeps code-flow's cookies from the app\nsrc/snapshot.ts the Browser's Snapshot: a screenshot of any address in a headless Chrome with a fresh profile, driven over its DevTools pipe (no debugging port), one at a time\nsrc/notes.ts the side panel's Notes, ~/.codeflow/notes.json\nsrc/context.ts validates and stages task context for agents\n```\n\n- **AgentAdapter** (`src/adapters/types.ts`): `run({prompt, cwd, mode, resumeSessionId}, onEvent) → {ok, finalText, sessionId, errorKind, resetsAt}`. `sessionId` is only ever one the CLI confirmed in that turn, never a session that failed to resume. A failed turn says why in `errorKind` (`usage_limit`, `auth`, `session_not_found`, `cli_incompatible`, `stalled`, `transient`…), read from the CLI's own signals (`src/adapters/errors.ts`), and the engine retries, stops or starts a fresh session on that. Adapters convert each CLI's JSONL into `text | thinking | tool_call | tool_result | usage | status | error` events. An adapter whose turns take input while they run calls `onSteerable(steer)` once its turn can be steered. The engine then hands queued notes to `steer`. `src/adapters/codex-app-server.ts` turns app-server notifications into the events `codex exec --json` prints, so both Codex transports share one handler.\n- **TeamContext** gives patterns a few primitives. `ask()` runs one agent turn as a visible step, and `resume` continues that agent's own conversation. `worktree()`, `commit()`, `diff()` and `artifact()` handle git. `parallel()` runs branches side by side (race contestants, workers, debaters); when one throws, the others are stopped and waited for, so nothing keeps running after the run ends. Structured hand-offs (review verdicts, judge picks, subtask lists) use a fenced ```` ```json ```` block that `extractJson` parses. A pattern reads what changed through `changedFiles()` and `testChanges()`, not git directly: on a resume the worktree on disk already has the later commits, and these read at the commit the replay has reached.\n- Every event is appended to `~/.codeflow/runs/<run>.jsonl`. A run snapshot that follows another is written as what changed (`run-patch`: fields set or removed, and the new end of a string or list that kept its start), with a full snapshot first, each time the run stops running, and now and then; reading a log turns patches back into snapshots (`src/run-log.ts`). Logs from before this are compacted the same way once loaded, in the background, and only when they read back exactly as they were. Worktrees live in `<repo>/.worktrees/<run-id>/` (see [Worktrees and branches](getting-started.md#worktrees-and-branches)).\n- **Live updates**: a browser allows 6 connections per host over HTTP/1.1, shared by all its tabs, and an event stream holds one while it's open. So there's one stream (`GET /api/stream`), with topics subscribed on it (`POST /api/stream/<client>`), and the browser's windows share it (`ui/stream.ts`): the first to take a Web Lock holds it for all, the others send it what they watch and get their events over a BroadcastChannel, and when it closes the next one takes over (on the phone, over plain HTTP, there are no Web Locks: each window has its own). The topics are `runs` (the run list), `run:<id>` (the run as it is now, then its latest snapshots, every agent event and log line, then live events; its cursor is the run's own, so it holds across a restart) and `shell:<id>` (a terminal). A run shown in two places (the run view and a side chat) is one subscription (`ui/run-feed.ts`), panes that are hidden or closed unsubscribe, and a subscription that comes back (or a stream that reconnects) passes a cursor, so it gets only what it missed. The older per-feature streams (`/api/events`, `/api/runs/<id>/events`, `/api/shell/<id>/events`) still answer, for a page left open across an update.\n\n**Auto team** uses the same engine with validated plans and bounded event-driven scheduling (with saved-wave compatibility) (`src/patterns/auto-team-model.ts`, `auto-team.ts`). Stable member IDs separate identity from provider; turn metadata binds settings, session, task, usage and selected context. Questions yield a turn rather than holding a slot. Accepted plans and admission decisions replay from the journal; strict journal failures stop dependent execution. A stopped workflow resumes through completed waves and final verification before beginning another user-authorized allocation. Project memory selections journal references, not bodies; replay resolves only still-applicable exact revisions. See [Auto team](auto-team.md) for the public behavior and trust/retention boundaries.\n\n### Adding a pattern\n\nWrite a `Pattern` (roles, options, `run(ctx)`) in `src/patterns/`, then add it to `src/patterns/index.ts`. The UI builds its form from the pattern definition.\n\n`ctx.artifact()` proves a result whose check passed (fail-to-pass, see `TeamContext.prove`), so a new pattern gets proofs without doing anything. To act on one before publishing, as Pipeline does before review, use `proveOrAsk()` from `src/patterns/prompts.ts`, and `proofEvidence()` to show it to a reviewer or judge.\n\nA pattern with a reviewer asks for `REVIEW_SCHEMA` with `reproRule()` and `REVIEW_SHAPE`, then passes the verdict to `checkReview()` (all in `src/patterns/findings.ts`): it runs each finding's repro on the result (`TeamContext.reproduce`), records the review on the run, and marks what blocks approval (`blocks` in the engine). `approves()` is the gate, and `findingsForBuilder()` is what the builder gets back.\n\n### Adding an agent\n\nFor a CLI that speaks ACP v1 over stdio, use [Settings → Agents & models](acp-agents.md). The shared adapter in `src/adapters/acp.ts` handles transport and normalized events; `src/acp-connections.ts` persists owner-configured invocations and pins them to runs.\n\nImplement `AgentAdapter` in `src/adapters/`, then register it in `src/adapters/index.ts`. Add a fake for it in `test/fake-agent.mjs`.\n\nOwner-configured providers and workflows can also use [versioned extensions](extensions.md) without modifying the built-in registry. They use namespaced IDs, explicit capability/API negotiation, reviewed bundle hashes and per-run version pins. Additional provider capabilities remain unknown until separately verified.\n\nThe HTTP boundaries for shared access, the team board and its linked runners, filesystem policy, coordinated changes, code hosts and workspace operations live in separate modules. SSH framing/artifacts/receipts, durable queues, memory claim analysis and Git shipping each have independent contracts and focused tests. [Run storage](run-storage.md) separates recovery/projection from execution and bounds archived detail hydration. These boundaries retain server-owned authority and replay identities rather than moving them into client request bodies.\n\n## Development\n\n`npm start` is your server (port 4317, `~/.codeflow`). `npm run dev` is a development server (port 4318, `.codeflow-dev/`) with **fake agents**, so working on code-flow never touches your real runs or quota. It puts `test/fake-bin` on `PATH` by absolute path and refuses to start if `claude`, `codex` or `agy` would still resolve to a real CLI. `npm run dev:real` is the same dev server with the real CLIs. Both rebuild the UI and restart the server as you edit.\n\n```bash\nnpm run dev # http://localhost:4318, fake agents\nnpm run typecheck\n```\n\n## Tests\n\n```bash\nnpm test # unit + workflow tests against fake CLIs\nnpm run test:e2e # the real UI in Chrome (headless) against a server with fake agents\n```\n\nCI (`.github/workflows/ci.yml`) runs the typecheck and `npm test` on every push and pull request: Ubuntu with Node 22.13 and 24, macOS with Node 22. The required browser job exercises critical workflows and accessibility. A separate job tests the packaged VS Code extension in the actual editor host. [Provider compatibility](provider-compatibility.md) continuously probes official version/help contracts without account or inference requests.\n\nThe suite runs every pattern end to end against fake `claude` / `codex` / `agy` binaries (`test/fake-bin`). Each fake emits that vendor's real JSONL format, so tests cost no quota.\n\n- The fake `codex` also serves the app-server protocol (`test/fake-codex-app-server.mjs`). Its threads persist across processes, so resume and per-turn token counts are tested. A prompt containing `WAIT-FOR-STEER` keeps a turn open until a note is steered in. `test/codex-app-server.test.ts` covers mid-turn notes, declined approvals, cancellation, and the `exec` fallback.\n- A fake `gh` keeps issues, PRs, checks, comments and security alerts in a JSON file and pushes to a local bare repository, so the GitHub flows (fix CI → push → watch → fix again, and sweeps of issues, Dependabot PRs and alerts) are tested without touching GitHub.\n- A fake `docker` records arguments for deterministic orchestration tests. Opt-in native Docker cases separately test actual filesystem mounts, credentials and cancellation; fixtures alone do not establish container isolation.\n- The suite covers review loops, races with a failing contestant, critique rounds, merge-conflict resolution, and the guarantee that your checkout is untouched.\n- `test/ship.test.ts` covers side chats too: sessions and messages kept apart from the run's thread, a budget of their own behind the team, an Ask that keeps the verdict and your edits, and files sent to a run whose worktrees were removed.\n\n`test/hardening.test.ts` covers the guarantees about processes, runs and tests:\n\n- a background process an agent leaves behind neither hangs a step nor survives it;\n- a run can't be resumed twice at once;\n- a commit made in a read-only step is undone;\n- a loop doesn't accept a pass made by deleting the failing test;\n- race commits name no vendor;\n- the builder's test edits are undone;\n- retries, usage-limit stops and token accounting work.\n\nReliability has tests of its own:\n\n- `test/foundation.test.ts`: the child environment, numeric settings, bounded git, the repo lock, and orphaned processes reaped at the next start;\n- `test/store-reliability.test.ts`: a failed write can't corrupt the next line of a run log; the server lock is taken over from a reused pid or a previous boot, and of several servers starting at once exactly one gets it;\n- `test/stream.test.ts` and `test/run-log.test.ts`: a window's one stream (subscriptions changed while it's open, topics that can't be watched, a cursor from another server start), a run's replay with only its latest snapshots (and how many fewer bytes that is), snapshots written as patches that read back exactly, full ones where they belong, and old logs compacted on load. `test/stream-client.test.ts` is the UI's side (requests batched, a refused topic not asked for again, reconnecting with cursors), and `test/e2e/connections.e2e.ts` opens a run view, a side chat, two terminals and a second tab in one browser and checks requests and keystrokes still go through, hidden panes stop watching, and the stream passes to the other tab when its tab closes;\n- `test/git-reliability.test.ts`: worktrees get their submodules (nested, in parallel, or a warning when one can't be cloned), and the Files tab's git calls;\n- `test/side-panel.test.ts`: the side panel's server side: reading a file (and never one outside the repo), notes that persist, shells whose `cd` holds between commands and whose exit codes come back, a new shell never closing a busy one or one with a job in the background, terminals that hang up their `nohup`'d and disowned jobs when code-flow stops and that still start in a repo pinning a Python its version manager doesn't have, the list of running apps, and the browser's preview proxy (the overlay added, frames allowed, the app's own host and origin, WebSockets, and the Host check);\n- `test/preview.test.ts`: the preview proxy against a stand-in dev server: where the overlay goes in a page, the app's policy kept but frameable, the host name and code-flow's cookies, compressed pages, connections that close when the browser's do, and the overlay itself in headless Chrome (Box doesn't click through, no passwords in snapshots, console reports; skipped without Chrome);\n- `test/snapshot.test.ts`: Snapshot's checks on the address and size, the viewport and the whole page checked pixel by pixel (a desktop page at phone width zoomed out, not cut off), one Chrome at a time, a page that can't be opened, an `alert()`, a Chrome that dies (and the next snapshot still working), and no profiles left behind (skipped without Chrome);\n- `test/browser.test.ts` and `test/chat-views.test.ts`: the side panel's UI logic, bundled with esbuild: which addresses the Browser believes from the page, where pins can be sent and how they're named, comment drafts shared by the review and the Changes tab, and side chats kept out of the run's thread;\n- `test/engine-reliability.test.ts`: parallel branches that stop together, steps that always end, read-only that fails closed, worktrees set up again after an interrupted setup, retries that continue instead of redoing, dead sessions, usage limits with reset times, lockdown without network, and unique run ids;\n- `test/engine-review.test.ts`: a Test-first run stopped in its second round and resumed ends with the same tree as one that wasn't, Stop reaching a resume that's starting, a second conflict resolution committed on its own, signed-out contestants not journaled and usage limits stopping a race, notes no turn took, the council restoring confirmed findings, reviewers' commands that never ran their test, slot waits outside the time budget, and archiving (refused while busy; the run's folder and fetched PR head cleaned up);\n- `test/security-review.test.ts`: a webhook caller's text fenced and its run marked, signed deliveries sent again with a new id, the app page's framing and caching headers, the first-browser claim ending at any sign-in, and Snapshot keeping away from loopback and metadata addresses;\n- `test/adapters-review.test.ts` and `test/github-review.test.ts`: Claude's read-only git that can't write files, error kinds read from the CLI's own error line, events a CLI prints that code-flow doesn't know, Codex falling back when resume rejects a parameter, `gh` output decoded whole, code scanning messages fenced, copied secrets kept out of commits, copies that never go through a symbolic link, merges refused mid-rebase, slow post-merge hooks, and quiet PR watches polled less often;\n- `test/ui-review.test.ts`: the run's own check winning over the repo's on Edit & run again, stale Files answers dropped, `safeHref`, and the Markdown renderer keeping raw HTML and `javascript:` links as text;\n- `test/adapters-reliability.test.ts` and `test/health-routing.test.ts`: the stall watchdog, the grace after a result, crashes mid-stream, dead sessions, unknown flags, huge lines, typed errors, Health's flag checks, and Auto skipping a signed-out CLI. The fakes simulate these with `FAKE_SLEEP_VENDOR`, `FAKE_HANG_AFTER_RESULT_VENDOR`, `FAKE_CRASH_VENDOR`, `FAKE_UNKNOWN_FLAG`, `FAKE_HUGE_LINE_MB`, `FAKE_CLAUDE_RESETS_AT`, and for app-server `FAKE_CODEX_SLOW_INIT_MS`, `FAKE_CODEX_INIT_CRASH`, `FAKE_CODEX_HANG_METHOD` and `FAKE_CODEX_SERVER_REQUEST`.\n\nThe GitHub tests cover untrusted issues (lockdown, fenced text, Docker or your confirmation) and PR watches that act only on comments from you and the people you allow. The e2e test also covers sign-in, Insights and run search. Its terminals run bash with a home of their own, so your dotfiles and shell history stay out of it.\n\n## What's next\n\n- Real approvals through `codex app-server` (it already declines approval requests): ask you in the UI instead.\n- A container option for the agents themselves (today only checks and setup run in Docker).\n- Read PR line comments (review threads), not just conversation comments and review summaries.\n- Linear as a source, next to GitHub issues.\n\n## Dynamic team control and replay\n\nA live tool command and its receipt are fsynced together before acknowledgement. The scheduler records which command batch it consumes at each boundary, so replay cannot inject a future reply into an earlier task. Completion batches likewise record observed order before serial integration. One server owns the journal; no dispatch lease or outbox is needed. See the [implementation plan](dynamic-teams-implementation-plan.md) for the invariants.\n\nThe scoped loopback bridge is separate from the user API and its bearer token. Official adapters receive a temporary stdio MCP configuration for supported turns. Memory tools inherit the active member, project, task and source provenance from the engine. Native spawning controls, sandbox support and nested usage limitations are exposed through the capability inventory. Supervisor shares decomposition/integration primitives while retaining its public ID and historical replay identities.\n\nSee [Performance and resource boundaries](performance.md) for the paged UI stream, lazy assets, worker reuse, storage ordering, diagnostics and load benchmark.\n\n## Documentation system\n\n`scripts/build-docs.mjs` renders the Markdown guides into `public/docs` for the local application and `dist/docs` for static hosting. The build reads the theme/font section from `ui/styles.css`, so documentation colors and typography follow the app. `docs/site` contains the navigation groups and the small browser layer for search, appearance and help. The application menu and command palette both link to `/docs/`.\n\n`src/docs-help.ts` serves the local files and runs bounded, owner-authenticated CLI questions using the same provider adapters as the application. Source selection is deterministic local keyword retrieval; there is no embedding service or hosted AI backend. Static builds omit the local capability marker. The host packager includes the documentation's UTF-8 assets, with screenshots/fonts embedded in the generated files.\n\nSee [Documentation and GitHub Pages](documentation.md) for authoring, preview, screenshot capture and deployment. `npm run test:docs` checks guide links and the help service; `test/e2e/docs.e2e.ts` exercises local and static browser behavior.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Architecture and development"},{"group":"Build and extend","id":"api","source":"docs/api.md","body":"# Local API and command-line client\n\nUse the owner token with the local API or the bundled command-line client. Keep credentials out of repository files and shell history, and inspect an operation’s request and response contract before scripting mutations.\n\n## Use the authenticated client\n\nUse `npm run client -- --help` for the HTTP client. It requires the explicit\n`CODEFLOW_TOKEN` printed/configured by the server and accepts `CODEFLOW_URL` for\nanother configured origin. It never reads agent login files. JSON files and\nshell arguments are treated as data, not executed. Output files are created\nprivately and never overwritten.\n\n`/api/v1/*` exposes the same authenticated operations as `/api/*`.\n`GET /api/v1/capabilities` lists the new stable operation families. Existing\nunversioned clients remain compatible. Authentication, Host, Origin and phone\nsession checks apply identically to both prefixes.\n\nPaths below are relative to `/api` (or `/api/v1`). Requests with bodies use JSON.\nSuccessful mutations use the status shown where relevant; validation errors use\n`400`, missing resources `404`, and explicit concurrency conflicts `409`.\nErrors contain an `error` string. No client-side mutation retry is automatic.\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `POST /projects/:id/repositories` | `repo` absolute Git path | Owner-only explicit membership addition; canonicalizes the root and returns the updated project. Existing membership is idempotent; the 50-repository limit applies. |\n| `POST /runs` | Existing run fields; optional `projectSelection: \"explicit\"` with `project` | Explicit selection requires an existing project and current canonical repository membership. Rejected membership never silently creates or extends a project. Callers omitting the marker retain legacy placement behavior. |\n| `POST /improve-prompt` | `repo`, `text`, optional `mode=quick\\|repo`, `agent`, `context`, `source`, `execution=local` | Owner-only local prompt rewrite in a disposable private Git directory; returns `{prompt, summary, agent}` after cleanup. |\n| `POST /readiness` | `repo`, optional `baseRef`, `setup`, `check`, `execute`, `pattern`, `roles`, `providers` | Inspection by default; `execute: true` runs commands in a temporary worktree at a pinned commit. Provider scope is described below. |\n| `GET /verification/profile` | `repo`, `preset` query | Detect lightweight, standard or thorough checks. |\n| `POST /verification` | `repo`, `profile` or `preset`, optional `baseRef`, `baseline`, `setup`, `repeats` | Execute bounded required gates on isolated checkouts and return structured evidence plus Markdown. |\n| `GET /workspace/file` | `repo`, `path` query | Load a bounded regular text file and its hash. |\n| `POST /workspace/file` | `repo`, `path`, `text`, `expectedHash` | Save only the loaded version; conflicts return 409. Active run worktrees and symlinks are refused. |\n| `POST /workspace/merge-file` | `repo`, `path`, `base`, `draft` | Compute a three-way merge against the current file without writing. Returns `current`, merged `text`, `conflicts`, and `clean`; conflict markers need manual resolution before saving. |\n| `GET /workspace/search` | `repo`, `q`, `mode=text\\|symbols` query | Bounded text or declaration search with source line numbers. |\n| `GET /workspace/dependencies` | `repo` query | Bounded static architecture graph across JavaScript/TypeScript, Python, Go, Rust and Java; packages, cycles and unresolved/computed edges are explicit. |\n| `POST /workspace/context` | `repo`, `paths` | Preview explicit file context, approximate token size and sensitive-file omissions. |\n| `GET /workspace/checkpoints` | `repo`, optional `ref` query | List exact commit checkpoints. |\n| `POST /workspace/apply-checkpoint` | `repo`, `runId`, `source`, `paths`, `expectedHead` | Apply selected files to an idle, clean run worktree without committing. |\n| `POST /workspace/checkpoint-hunks` | `repo`, `runId`, `source`, `path` | Preview server-generated hunks for one existing regular text file in an idle run worktree. Returns pinned `head`, `source`, `path`, and bounded `hunks` with IDs. |\n| `POST /workspace/apply-hunks` | `repo`, `runId`, `source`, `path`, `expectedHead`, `hunks` | Apply 1–200 distinct preview IDs to a clean, unchanged run checkout. Arbitrary client patches are never accepted; files stay unstaged. |\n| `POST /runs/:id/pause` | `paused` | Hold new dispatch at operation boundaries; active work finishes. |\n| `POST /runs/:id/priority` | `priority` from -10 to 10 | Update dispatch priority, including waiting work. Equal priorities favor less-busy projects. |\n| `POST /runs/:id/provider-chat` | `agent`, optional `settings: {model, effort}`, `message` | Start a single-agent child with another provider, a portable conversation transcript, saved context files and a new worktree from the source result commit. Requires an idle source and clean worktree; retains project, computer and permission restrictions. Provider sessions and side-chat transcripts are excluded. |\n| `POST /runs/:id/fork` | exact `commit`, optional `task` | Begin a child run from an owned checkpoint, retaining source trust restrictions. |\n| `POST /runs/:id/revise-goal` | `message`, optional `budget` | Create a new Auto-team run with a user-authored goal revision and prior work references; returns 201 with its `id`. |\n| `POST /runs/:id/rerun-tasks` | `expectedHead`, `taskIds`, optional selected-task `instructions`, explicit `budget` | Execute accepted selected tasks and every dependency descendant in a child at the pinned integration commit. Retained work is labeled baseline input; final checks/review are independent. Returns 201 `{id}`. |\n| `POST /runs/:id/team-plan` | `revision`, `tasks`, `contracts`, `rationale` | Edit a pending Auto-team plan before dispatch. Keeps member identities and requirements fixed; validates owners, dependencies, paths and acceptance mappings. Returns `team`; stale revisions return 409. |\n| `GET /runs/:id/review-coverage` | `branch` query | Return current commit, changed files, reviewed files, percentage, heuristic risk, stale-review indicator and human rework history. Only run-owned branches are allowed. |\n| `POST /runs/:id/review-coverage` | `branch`, `expectedHead`, `path`, `reviewed` | Record or remove review of a changed file at that exact commit. Branch movement invalidates prior coverage; stale requests are rejected. |\n| `POST /runs/:id/human-rework` | `branch`, `expectedHead`, `minutes`, optional `note` | Append a user-entered correction-time record (1–1,440 minutes, note ≤2,000 characters, at most 100 records/run). Returns 201 with updated coverage. |\n| `POST /runs/:id/validate-merge` | `branch`, `into`, optional `allowUntrusted` | Verify the combined result with the current target in a disposable checkout. Returns pinned `source`, `target`, merge `tree`, `report` or `conflicts`. Requires a configured check/profile. |\n| `POST /runs/:id/merge` | `branch`, `into`, optional `archiveAfter`, `requireValidation` | Merge an owned result into the selected local branch. `requireValidation: true` requires passing verification for unchanged source/target and the exact merged tree. |\n| `POST /runs/:id/archive` | `archived`, optional `cleanup`, `deleteBranches` | Archive or unarchive a finished run. Use `cleanup: false` to hide it while keeping worktrees and branches. |\n| `DELETE /runs/:id` | — | Permanently remove a finished run's conversation/history and private run memory. Active runs and standing project team runs are rejected; archive managed runs to retain recovery and approval evidence. Git branches, worktrees and repository files remain. Run-list streams publish a summary with `deleted: true`; clients remove that ID. |\n| `GET /runs/:id/export` | optional `transcript=1` query | Export sanitized run metadata, checks, and requested transcript. |\n| `GET /approvals` | optional `run` query | Inspect bounded per-action permission requests. |\n| `POST /approvals/:id` | `decision=approve\\|decline` | Answer one still-active request once; expired or cancelled requests return 409. |\n| `GET /notes/history` | `key` query | Read bounded saved note revisions. |\n| `GET /notes/search` | `q` query | Search current saved notes. |\n| `POST /notes/restore` | `key`, `revision`, `expectedRevision` | Restore a prior note as a new revision. |\n| `POST /remote/tls` | `{mode: \"tailscale\"}` or `{mode: \"import\", hostname, cert, key}` with PEM strings | Local owner only. Validate and save HTTPS configuration, rotate pairing, disable full control and reload the network listener without restarting runs. Tailscale mode uses the connected computer's DNS name. Environment-managed TLS cannot be replaced here. Returns remote status and public certificate metadata, never PEM material. |\n| `GET /remote/devices` | none | List paired device metadata, never session credentials. |\n| `DELETE /remote/devices/:id` | none | Revoke one device immediately. |\n| `GET /diagnostics` | optional `repo` query | Export versions, queue/write health and sanitized recent logs. |\n\nReadiness accepts `roles` as an object of provider IDs (strings or arrays). Concrete assignments must be installed, compatible and not signed out; unused providers are skipped. `providers` alone is an explicit required-provider list. With `roles`, it limits the eligible pool for `auto` assignments; without that limit, Auto considers registered providers. An Auto-team's `roles.pool` contains alternatives and needs at least one usable member (also within `providers`, if supplied), while its coordinator is required. An explicit empty scope fails readiness. Unknown authentication remains a warning. Omitting both `roles` and `providers` preserves the original checks of all installed providers.\n\nScope IDs must be 1–128 letters, digits, underscores, colons, slashes, periods or hyphens, beginning with a letter or digit. Provider values must be registered IDs or `\"auto\"`; lists allow at most 128 entries and `roles` allows at most 64 keys. Malformed scope returns HTTP 400. The composer sends its current workflow and assignments and discards displayed or in-flight reports when that selection changes.\n\nPrompt improvement accepts up to 20,000 input/output characters and a 1,000-character\nsummary. Context is limited to 50 names of 300 characters; optional source metadata\ncontains `kind=issue|pr|alert`, integer `number` and a title of at most 200 characters.\nQuick mode supplies only the text and metadata; repository mode exports a pinned\ncommitted-HEAD snapshot with a fresh Git namespace. Source Git configuration, hooks,\nremotes, history, ignored/untracked content and executable provider configuration\nare not copied. Both use safe provider permissions and an in-process repository\nmutation lease; cleanup removes only the private directory after failure or\ncancellation, never resetting the source checkout or its index. See\n[snapshot bounds](getting-started.md#your-first-run). Snapshot preparation has a\n60-second deadline. Inherited Git routing/configuration overrides are refused so\nthe provider cannot accidentally target another checkout through its environment.\nImprove also requires absolute, nonempty PATH entries and currently supports\nmacOS/Linux; Windows is refused until executable discovery is pinned and tested.\nExplicit SSH/container profiles return\n400, shared accounts return 403, repository/policy conflicts return 409, the three-turn\nglobal concurrency limit returns 429, and provider/snapshot failures return 502.\nClosing the request cancels the turn; clients must not automatically retry it.\n\nVerification profiles contain `name`, up to 12 `checks`, optional `setup`, and\nup to 48 `requirements`. Each check has a unique `id`, `kind`, `command`, optional\n`title`, `required` (default true), and `timeoutMs` (default five minutes, maximum\n30 minutes). Kinds are `build`, `typecheck`, `lint`, `unit`, `integration`,\n`browser`, `mutation`, `visual`, `accessibility`, and `custom`. A requirement is\n`{id, description, checks: [checkId, ...]}`; an empty mapping remains uncovered.\nA failed mapped gate or uncovered criterion prevents `verified` status. Repeats\nare limited to 1–3; a mixed passing/failing result remains flaky. Baseline\nfailures are evidence, not permission to ignore a required gate.\n\nStandalone verification pins the requested commit, excludes uncommitted changes,\nand runs setup and gates on temporary worktrees. A setup or gate that changes\ntracked files, the staged tree, or HEAD is rejected with 409 before subsequent\ngates execute. Ignored and untracked generated output is permitted. Setup failure\nreturns 422. Run-attached profile verification similarly produces a blocked\nreport on candidate drift. Requirement coverage records the checks selected by\nthe user; it does not prove those checks semantically exercise the criterion.\nHuman file coverage is a separate, user-authored commit-bound record.\nSee [selective task reruns](task-reruns.md) for budget, replay, retained-evidence\nand unchanged-result semantics. Client-authored `teamRerun` seeds are never\naccepted by run creation.\n\n## Paginated history and event feeds\n\n`GET /runs` keeps the legacy full-list response when no `limit` query is supplied.\nWith `limit=1..100`, it returns `{runs, total, nextCursor?, nextBefore?}`. Pass the\nopaque `nextCursor` as `cursor` on the next request; cursors preserve ties for\nruns created in the same millisecond. Optional `q` filters all indexed run\nmetadata and `project` selects a project (including legacy repository-based membership).\n`status=all|attention|running|passed|failing|flagged`, `pattern`, `agent`, and\n`repo` filter the complete authorized index before totals and pagination. Passed\nrequires completed execution and passing verification; blocked or unverified\nresults are excluded. `section=waiting|running|results` selects a non-archived\nproject-page section. The legacy `before` timestamp is\nsupported but can omit timestamp ties; prefer `cursor`. Keep filters consistent\nbetween pages. Invalid cursors, status/section values and page sizes return 400.\n\n`GET /projects/:id/summary` returns `{live, archived, pending, week, sections}`.\n`week` contains `{runs, passed, costUsd}` for the last seven days, including\narchived history. `sections` contains cursor pages for `waiting`, `running` and\n`results`, with complete totals. Fetch additional section pages through `/runs`.\nThese totals never depend on which sidebar pages a browser has loaded.\n\nThe shared `/stream` connection accepts the `approvals` topic. It sends a\n`{type: \"snapshot\", records: [...]}` frame initially and whenever pending tool\napprovals change. Reconnects receive a fresh snapshot; approval decisions still\nuse the single-action POST endpoint. Older hosts without this topic use the\nclient's visibility-aware polling fallback.\n\n`GET /events?limit=1..100` is the bounded SSE run feed. Its initial `hello`\ncontains the recent page, every running run and at most 100 older attention\nitems, `total`, `nextCursor`, `attentionTotal`, `attentionTruncated`, and an\noptional `attentionNextCursor`. Older attention remains accessible through\nhistory; the rail displays the truncation. Without `limit`, the legacy full\ninitial list remains available. Policy changes invalidate the feed generation.\nReconnect using `Last-Event-ID`; a valid retained cursor resumes snapshots, while\na stale cursor triggers a fresh `hello`. `GET /runs/:id/events` streams that run's\nretained events with a run-bound replay cursor. These are live feeds, not durable\nclient acknowledgment queues.\n\n## Read-only external sources\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /integrations` | none | List explicitly server-configured provider metadata and availability without credential names or values. |\n| `GET /integrations/:id/items` | `kind=issue\\|pr`, optional `cursor` query | Return `{items, nextCursor?, truncated}` from a bounded provider listing. Supported kinds depend on the provider. |\n| `POST /integrations/:id/import` | `kind`, `key` | Read one item and return 201 with a server-issued source snapshot ID and untrusted marker. |\n\nRun creation accepts `externalSourceId` from the import response, or\n`externalSourceRef: {integration, kind, key}` for a server-side import. It rejects\nclient-authored `externalSource` snapshots, invalid/expired IDs, and simultaneous\nID/ref inputs. Snapshots expire after 30 minutes and the server retains at most\n200. Imported context forces Safe permissions, removes secret-file copying, and\nrequires network-disabled checks in a container or explicit host authorization.\nForks and continuations inherit the trust boundary. Source text is task context;\nPR imports do not fetch or check out remote code. See [external source setup and\nprovider limits](external-sources.md).\n\n## Outbound notifications\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /notifications` | none | List configured destination name/protocol/hostname, availability, opt-in settings, drop count and last failure. No URL paths, tokens, credential references or payloads. |\n| `POST /notifications/:id` | `enabled`, `events`, `projects` | Save explicit target opt-in. Events are `done`, `failure`, `needs-attention`; scope is `\"all\"` or project IDs (`\"unassigned\"` covers no project). Enabling requires an event and nonempty scope. |\n| `GET /notifications/deliveries` | none | List at most 200 delivery metadata records, newest first, without saved message payloads. |\n| `POST /notifications/:id/test` | empty body | Explicitly queue a test, including for a disabled destination. Returns 202; delivery proceeds in the background. |\n| `POST /notifications/deliveries/:id/retry` | empty body | Explicitly requeue a failed delivery, preserving lifetime attempts and manual-retry count. Returns 202. |\n\nDestinations are configured only through server environment JSON/file with\nenvironment-variable references for HTTPS URLs and credentials. New destinations\nare disabled. Delivery uses no redirects, a ten-second timeout and at most three\nautomatic attempts per batch. Changing the URL requires renewed opt-in. Unknown\noutcomes after restart require manual retry; recipients should deduplicate the\npersistent `Idempotency-Key`. Notification storage/configuration failure returns\n503 and leaves delivery off. See [configuration, disclosure, and delivery\nlimitations](notifications.md).\n\n## Project memory analysis and revision commands\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /projects/:id/memory` | optional `repo` query | List authorized project entries. A supplied repository must belong to the project. |\n| `POST /projects/:id/memory/commands` | `op=create\\|approve\\|update\\|delete\\|feedback` plus operation fields | Run user-authorized revision commands. All existing-entry mutations require exact `id` and `expectedRevision`; conflicts return 409. Client authority/origins are not forwarded. |\n| `GET /projects/:id/memory/analysis` | `repo` query (optional for a single-repo project) | Return bounded provenance, freshness, duplicates/semantic claims, deletion impact and curated onboarding with exact source revisions and truncation indicators. |\n| `POST /projects/:id/memory/analysis/publish` | `repo`, `expectedSources` from `analysis.onboarding.sourceRefs` | Recompute and publish eligible user-authored onboarding guidance with pinned provenance. Changed sources return 409; no eligible sources returns 400. |\n\nCreate accepts `repo`, `title`, `body`, optional `paths`, `tags`, and\n`applicability`. Update accepts editable fields and optional `accept: true`; an\nordinary edit never accepts a proposal automatically. Feedback accepts `rating`,\noptional `note` and `runId`; a run reference must have selected that exact entry\nrevision. Analysis is heuristic and bounded: lexical similarity does not infer\ncontradictions on its own. Deterministic semantic claims compare supported\npolicies and structured boolean/text/numeric intervals only across overlapping\napplicability; unsupported prose is counted. Git freshness checks do not\nestablish semantic correctness. See [supported claims](memory-claims.md).\nPublication excludes imported/private/untrusted material and prior onboarding\nsummaries. Memory APIs remain owner-only; shared project roles use the separate\nclosed review surface described below.\n\n## Multi-service launch profiles\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /runs/:id/launch-profiles` | `worktree` query | Read `.codeflow/launch.json` from the owned worktree and return `{profiles, found, error?, session}` with sanitized configuration and session logs. |\n| `POST /runs/:id/launch-profiles` | `worktree`, `profile`, optional `allowUntrusted` | Start a configured dependency-ordered group; returns 201 with session and assigned local URLs/ports. Locked-down host execution requires explicit authorization. |\n| `DELETE /runs/:id/launch-profiles` | `worktree` query | Stop the owned launch session and return `{stopped}`. |\n\nProfiles configure at most six services and bounded commands, relative working\ndirectories, dependencies, environment values and optional local HTTP health\nchecks. The server assigns ports and peer URL variables. This starts local\nprocesses; it is not remote deployment or hosting. See [launch profile\nconfiguration and lifecycle](launch-profiles.md).\n\n## Automation inspection and replay\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /automations` | none | List configured local schedules and webhook triggers. |\n| `POST /automations` | `name`, `run`, `schedule` and/or `webhook`, optional `enabled`, `isolate`, `image` | Validate saved run settings and create a trigger (201). |\n| `POST /automations/:id` | changed fields; optional `rotateToken` | Update validated settings or rotate the webhook credential. |\n| `POST /automations/schedule-preview` | `schedule`, optional `after`, `count` | Return `{times}` (at most ten UTC millisecond timestamps) without scheduling work. |\n| `POST /automations/:id/preview` | optional webhook event input | Dry-run saved settings, selected sweep items, permissions and limits without starting runs or consuming the event. |\n| `GET /automations/:id/events` | none | Inspect bounded persistent event records including queued, started, failed and dropped deliveries. |\n| `POST /automations/:id/events/:eventId/retry` | empty body | Replay an eligible saved event through the same validation, deduplication and queue limits. |\n| `POST /automations/:id/run` | empty body | Explicitly fire now; returns 201 with run IDs, or 409 if skipped. |\n| `DELETE /automations/:id` | none | Remove the trigger. |\n\nDaily schedules use `{at: \"HH:MM\", timezone?: \"IANA/Zone\", days?: [0..6]}`;\nSunday is zero and an explicit empty weekday list is invalid. Omitted timezone\nuses server local time. Interval schedules use `{every: minutes}`. During a\nrepeated local clock time, a daily schedule fires once on its first occurrence;\na missing local time shifts forward by the clock gap. Webhook event history\nkeeps at most 100 records and the pending queue at most ten. Replay preserves the\nsaved event content, while rechecking current configuration and limits. It never\nmerges results automatically.\n\nRun creation accepts `loop.verification`, `loop.maxTokens`, `loop.maxCostUsd`,\n`loop.agentContainer`, and explicit `approvals.tools`. Token/cost limits count\nonly provider-reported usage and are checked between operations. API-equivalent\ncost is not a subscription charge. Automated and locked-down runs never wait for\nnative tool permissions. See [agent containers](agent-containers.md),\n[external sources](external-sources.md), and [maintenance](maintenance.md).\n\nThe client has no automatic retry for mutations. A dropped response is not proof\nthat an operation did not execute; inspect the run before repeating a creation.\n\n## Shared identities and project review\n\n`GET /auth/self`, `POST /auth/login {username,password}` and `POST /auth/logout`\nbootstrap the local viewer/reviewer dashboard. Login sets a distinct session\ncookie; tokens and password/session hashes are never returned. Shared sessions\nrequire actual TLS or a direct loopback connection without forwarding headers.\nOwner account administration uses `GET/POST /accounts`, `PUT /accounts/:id`, and\n`POST /accounts/:id/revoke-sessions`, with the current account-state `revision`.\n\nShared routing is a closed allowlist. Project/run lists, result detail, safe\nevents, diffs, coverage and exports are scoped before counts and cursors.\nReviewers may record file coverage and `POST /runs/:id/collaboration\n{branch,expectedHead,text}`; both recheck current project membership and owned\nresult HEAD. `GET` returns the separate human discussion. These comments never\nresume an agent. All execution, administrative and private-context operations\nremain owner-only. See [roles, projections and session limits](shared-projects.md).\n\n## Team collaboration\n\nA team board served by one code-flow instance (the team server) and the linked\nrunners on teammates' computers share the routes below. Browser sessions (the\nowner and shared accounts) and per-machine runner credentials\n(`Authorization: Bearer tr-…`) use the same paths; the table says who may call.\nA runner credential reaches only `/team/*` and the enrolment route; every other\nAPI route answers 403 \"This credential only reaches the team API\", and the pages\ntreat it as an anonymous visitor. The owner token is matched before a bearer is\nread as a runner credential. Runner mutations send\n`content-type: application/json` (the bundle upload `application/octet-stream`)\nand need no Origin header; browser mutations keep the same-origin JSON\nrequirement of shared accounts. Board tasks are visible only in projects where\nthe caller holds a role; people appear as an account id and display name, never\nwith paths, transcripts, tokens or hashes. `/v1/team/*` and `/v1/team-link/*`\naliases work, and `GET /v1/capabilities` lists the family as\n`team-collaboration`.\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `POST /team/runners/connect` | `username`, `password`, `name` | Anonymous enrolment over HTTPS or loopback: verifies the credentials like sign-in without creating a session, requires a contributor role in some project, and returns 201 with the runner, its credential (shown once), the user and the server's name. |\n| `GET /team/me` | none | The caller's identity, project roles and own runners. |\n| `GET /team/board` | optional `project` query | The board: the projects where the caller holds a role with their members and repository keys, task cards with the current attempt, latest checkpoint and hand-over, and the unread inbox count (viewer). |\n| `GET /team/tasks/:id` | none | The full task (viewer in its project). |\n| `POST /team/tasks` | `projectId`, `repository`, `title`, `goal`, `acceptance`, optional `baseRef`, `owner`, `reviewer`, `collaborators`, `dependsOn`, `labels`, `status` | Create a task (contributor); 201. |\n| `PUT /team/tasks/:id` | `revision` plus the changed fields | Partial save under the status and ownership rules; `null` clears an optional field. |\n| `DELETE /team/tasks/:id` | `revision` | Delete a task (maintainer); `{deleted: true}`. |\n| `POST /team/tasks/:id/comments` | `text` | Comment, with `@username` mentions (reviewer). |\n| `POST /team/tasks/:id/decisions` | `text` | Record a decision (contributor). |\n| `POST /team/tasks/:id/review-request` | `reviewer`, `revision` | Ask a teammate to review (contributor). |\n| `POST /team/tasks/:id/reviews` | `checkpointId`, `verdict=approve\\|request-changes`, `text`, `revision` | Record a review against a checkpoint (reviewer). |\n| `POST /team/tasks/:id/instructions` | `text` | Propose a **Send to agent** instruction (contributor); the task owner's own are approved at once. |\n| `POST /team/tasks/:id/instructions/:iid` | `action=approve\\|decline\\|delivered`, optional `attemptId` | Approve or decline (the task owner or a maintainer); `delivered` is recorded by the attempt's runner. |\n| `POST /team/tasks/:id/attempts` | `attemptId`, `runnerId`, `runnerName`, `baseCommit`, optional `fromCheckpointId`, `revision` | Claim an attempt (the task owner through their runner, or the server's owner in-process as runner `local`); idempotent on `attemptId`; 201. Refused (409) while another attempt is claimed, running or unknown, while a hand-over is offered, and on a Done task. |\n| `POST /team/attempts/:aid/progress` | `seq`, optional `runId`, the progress fields | Publish a numbered progress snapshot (the attempt's runner); a late number is ignored. A report that would revive a paused, stopped or unknown attempt is refused (409) while another attempt of the task is claimed, running or unknown or a hand-over is offered, on a Done task (409 \"Move the task out of Done before resuming the attempt\"), and for a machine whose user no longer owns the task (403 \"The task now belongs to `<name>`; they continue it from its latest checkpoint, or a project maintainer reassigns it\"). |\n| `POST /team/attempts/:aid/heartbeat` | empty body | Keep the attempt live; returns the approved, undelivered instructions. |\n| `POST /team/attempts/:aid/finish` | `state`, optional `head`, `summary`, `reason` | End the attempt as finished, failed, stopped, paused or unknown. |\n| `POST /team/attempts/:aid/recover` | `revision`, `reason`, `confirmedStopped=true` | Recover an unknown attempt after confirming its original execution stopped (task owner or project maintainer). Supersedes it, records the decision and releases the task; stale revisions or a reconnected runner return 409. |\n| `POST /team/attempts/:aid/checkpoints` | optional `id`, `commit`, `baseCommit`, `branch`, `size`, `sha256`, `note`, `requirements`, `decisions`, optional `testResults`, `remaining`, `changedFiles` | Register a checkpoint manifest with `bundle: \"pending\"` (the attempt's runner); 201. `id` (32 lowercase hexadecimal characters) becomes the checkpoint's id and ref `refs/codeflow/team/<id>`; an id already on the board is refused (409), a malformed one 400, and without one the server generates the id. |\n| `PUT /team/checkpoints/:cid/bundle` | `application/octet-stream` body, `x-codeflow-sha256` header | Stream the bundle to disk (the checkpoint's runner); size and digest are verified before the checkpoint becomes available. A refusal (415, 400 on a size or digest mismatch, 409 when already uploaded or deleted, 507 when storage is full, 503 when the file could not be stored) is answered at once and the declared body is then drained, so a retry reads the answer; a body without a content-length (411) or declared over 64 MiB (413) is cut off once the answer is written. |\n| `GET /team/checkpoints/:cid` | none | The checkpoint manifest (viewer). |\n| `GET /team/checkpoints/:cid/bundle` | none | Download the bundle with `content-length` and `x-codeflow-sha256` (contributor). |\n| `DELETE /team/checkpoints/:cid` | none | Delete a checkpoint and its bundle (maintainer); `{deleted: true}`. |\n| `POST /team/tasks/:id/handoff` | `to`, `checkpointId`, `note`, `revision` | Offer a hand-over from an available checkpoint of the task (the task owner or a maintainer); refused while an attempt runs. |\n| `POST /team/tasks/:id/handoff/:hid` | `action=accept\\|decline\\|cancel`, `revision` | Accept or decline (the recipient); cancel (the sender or a maintainer). |\n| `GET /team/inbox` | optional `after` query | The caller's inbox items and unread count. |\n| `POST /team/inbox/read` | `ids`, or `all: true` | Mark the caller's items read; returns `{unread}`. |\n| `GET /team/runners` | none | The caller's runners; only the hub owner sees all. |\n| `DELETE /team/runners/:id` | none | Revoke a runner (its user or the hub owner); `{revoked: true}`. |\n| `PUT /team/projects/:id/repositories` | `repositories`, `revision` | Define the project's repository keys (maintainer). |\n| `PUT /team/profile` | `displayName` | The owner's display name on the board (owner). |\n| `GET /team/events` | optional `after=<epoch:seq>` query | Server-sent events for the caller: `hello` with the epoch and sequence, then `change` events for the board and tasks in the caller's projects and `inbox` unread counts; a cursor the server no longer holds gets `resync`. Closed when the account changes or the runner is revoked, and when 2 MiB of frames wait on the socket. |\n\nMutations return the updated task and carry the task `revision`; a stale one\nfails with 409 \"Task changed; reload before saving\". A missing role answers 403\nnaming the role, an unknown or invisible id 404 \"Task not found\", an unknown\nteam route 404 \"Unknown team operation\" (never falling through to owner routes)\nand unavailable team storage 503. Task saves and repository keys accept 64 KiB\nbodies, progress reports 192 KiB, checkpoint manifests 1.25 MiB, bundles 64 MiB,\nenrolment 8 KiB and other routes 16 KiB; a board file at its 16 MiB limit refuses\nevery change with 413. Progress reports and heartbeats do not change the task\nrevision.\n\nOn a linked computer the owner's `/team/*` requests are forwarded to the team\nserver with the runner credential, and the owner-only local routes below drive\nexecution; on the team server itself the same routes work in-process and\n`linked` is false.\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /team-link` | none | The link: `linked`, the server address, user, server name, machine name, mappings and defaults, the connection state, the runner as the server reports it, the last sync and the last error. |\n| `POST /team-link/connect` | `url`, `username`, `password`, optional `name` | Enrol this computer: `https://` only (plain `http://` for loopback), never following a redirect and only with a trusted certificate (502 otherwise), refused (409) while already connected or while the local board has tasks; saves the link and starts the change stream and reconciliation; 201. |\n| `DELETE /team-link` | none | Revoke the runner on the server (best effort) and forget it here; mappings, defaults and local attempt records stay. |\n| `PUT /team-link/mappings` | `projectId`, `repository`, `path` | Map a repository key to an absolute folder that is a Git repository. |\n| `POST /team-link/tasks/:id/start` | optional `repo`, `pattern`, `roles`, optional `agentSettings`, `roleSettings`, `trust`, `loop` (`check`, `setup`, `maxMinutes`, `maxSteps`), `fromCheckpointId` | Claim an attempt and start the run here; returns `{attemptId, runId}`; 201. Refused (400) before anything is claimed when the repository cannot carry checkpoints (a submodule, a checkout filter such as Git LFS, or a tree over 20,000 files, 256 MiB or 100 attribute files). |\n| `POST /team-link/tasks/:id/stop` | empty body | Cancel the current attempt's run (resumable) and record it as paused. |\n| `POST /team-link/tasks/:id/resume` | empty body | Resume this computer's paused, stopped or unknown attempt of the task under the same attempt id; refused (409) when the attempt was superseded or has ended, the task changed hands or is Done, another attempt is busy or a hand-over is offered. |\n| `POST /team-link/tasks/:id/handover` | `to`, `note`, optional `includeUncommitted` | Create or reuse the checkpoint (a retry uploads the kept bundle), then offer the hand-over. |\n| `POST /team-link/tasks/:id/accept` | `handoffId`, optional `repo` | Download and import the checkpoint, then accept the offer. |\n| `POST /team-link/checkpoints/:id/fetch` | `repo` | Import a checkpoint and point `team/<task id>/<first 8 characters of the checkpoint id>` at it; returns `{commit, branch, taskId, created}`. |\n| `GET /team-link/attempts` | none | This computer's attempt records: which run belongs to which attempt, and what has been published. |\n| `POST /team-link/reconcile` | empty body | Run the recovery pass now: settle claims whose answer was lost and retry unacknowledged finishes; returns `{attached, finished, removed, unknown, retried}`. |\n\nNothing here replays automatically: a restart, the recovery pass that runs every\nminute and `POST /team-link/reconcile` reconcile local attempt records with the\nteam server and never start a run again. See [team\ncollaboration](team-collaboration.md) for roles, hand-overs, checkpoints and\nlimits.\n\n## Explicit workers, coordination and extensions\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /remote-workers` | none | List explicitly configured SSH targets without private capabilities. |\n| `POST /remote-workers/:id/probe` | `repoMapping` | Probe the provisioned worker and its own provider inventory. |\n| `POST /runs/:id/remote-steps/:step/receipt` | empty body | Inspect an accepted execution receipt; never replay the turn. |\n| `POST /runs/:id/remote-steps/:step/cancel` | empty body | Explicitly request cancellation and inspect its receipt. |\n| `GET /coordinated-changes` | none | List durable multi-repository jobs and their current claims. |\n| `POST /coordinated-changes` | `project`, `name`, `nodes`, optional `operationId` | Create 2–10 explicitly configured repository tasks with a verified dependency DAG. |\n| `POST /coordinated-changes/:id/cancel` | `expectedRevision` | Cancel further dispatch; preserve accepted execution receipts. |\n| `POST /coordinated-changes/:id/shipping-preview` | `expectedRevision`, `targets` | Validate the current source/target/tree matrix without merging. |\n| `POST /coordinated-changes/:id/nodes/:node/retry` | `expectedRevision`, optional `acknowledgeUnknown` | Explicitly retry after inspecting an uncertain claim. |\n| `POST /coordinated-changes/:id/nodes/:node/attach` | `expectedRevision`, `runId` | Attach only an exact matching durable launch receipt. |\n| `POST /coordinated-changes/:id/nodes/:node/ship` | `expectedRevision`, `previewId` | Merge one unchanged verified local candidate; no automatic remote push. |\n| `POST /coordinated-changes/:id/nodes/:node/recover-shipping` | `expectedRevision`, `commit` | Confirm the exact reviewed parents/tree after an uncertain local merge. |\n| `GET /extensions` | none | Inspect host API version and explicitly configured trusted provider/workflow bundles. |\n\nRuns select SSH using `loop.remoteWorker: {workerId,repoMapping}`. Worker\ncredentials stay on the provisioned worker; accepted turns are never retried\nautomatically after a disconnected transport. See [worker setup and receipt\nrecovery](remote-workers.md). Coordination ships each repository separately;\nthere is no atomic transaction across repositories. See [coordinated\nchanges](coordinated-changes.md). Extension installation is server configuration,\nnot an HTTP operation; immutable version/hash pins are rechecked before\nexecution. See [trusted extension contracts](extensions.md).\n\n## Code-host review and publication\n\nOwner-only `GET /code-hosts` lists configured GitLab/Bitbucket code adapters and\nsafe operation receipts. `POST /code-hosts/preview {integration,key,repo,project?}`\ncreates a short-lived server snapshot. `POST /code-hosts/:id/fetch` imports exact\nsource/target Git objects without executing them; `/:id/run` accepts the prepared\nagent container and required verification configuration. `/:id/review` performs\nan explicit supported comment, reply, thread resolution or GitLab approval.\n\nPush and merge each have distinct `/:id/push-preview` or `/:id/merge-preview`\noperations taking `runId` and an owned `branch`; their actions require the\nreturned `planId`. `POST /code-hosts/create-preview` takes `integration`, `runId`,\n`branch`, `targetBranch`, `title`, and `body`; `/:id/create` explicitly creates\nthe expected-absent source branch and native PR. An unknown receipt can only be\nacknowledged explicitly at `/code-hosts/history/:id/acknowledge` with\n`{acknowledge:true}`. No startup replay occurs.\n\nGit updates use exact expected-ref leases. Merge is a pinned Git merge and may\nleave provider PR closure pending; native merge queues and atomic provider-policy\nsnapshots are not claimed. Unsupported/unknown protected-branch policies fail\nclosed. See [provider-specific operations and limits](code-hosts.md).\n\nGitHub threads use `GET /runs/:id/pr-threads?branch=<owned-branch>` and `POST` with\n`{action:\"load\",snapshotId,threadId}` or\n`{action:\"resolve\",snapshotId,threadId,expectedHead,expectedRevision,resolved}`.\nResolution rechecks fresh remote state and reports postflight uncertainty.\nSee [thread pagination, permissions and mutation races](github-review-threads.md).\n\n## Filesystem policies\n\nOwner-only `GET /projects/:id/filesystem-policy` returns the committed policy.\n`PUT` accepts `{revision,read:{allow,deny},write:{allow,deny}}` with literal\nrepository-relative paths. Policies intersect across matching projects and\npinned run snapshots. App file/tree/search/write/diff access rechecks current\nrules; raw history and other unfilterable evidence are withheld. Custom\nrestrictions pause ordinary application-managed execution globally, including\nbackground preparation. See [activation, execution boundaries and HTTP error\nsemantics](filesystem-policy.md).\n\nRestricted execution uses a separate owner-only manual task surface:\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /filtered-tasks` | optional `project` query | Return bounded lifecycle/receipt metadata; historical source/output content is not listed. |\n| `POST /filtered-tasks` | `project`, `repo`, `task`, `agent`, `image`, optional `network`, `maxMinutes` | Prepare a filtered committed snapshot and return 202 after a durable claim; only built-in providers and an explicit local immutable image are supported. |\n| `GET /filtered-tasks/:id` | none | Read task/output under unchanged current canonical policy and project membership. |\n| `POST /filtered-tasks/:id/preview-import` | empty body | Validate source/result/policy and preview bounded allowed file changes. |\n| `POST /filtered-tasks/:id/import` | `revision`, `expectedHead`, `resultHash` | Create a new review branch with an atomic exact-source ref transaction; leave checkout and index unchanged. |\n| `POST /filtered-tasks/:id/cancel` | `revision` | Cancel preparation/execution and confirm the owned daemon container is absent. |\n| `POST /filtered-tasks/:id/receipt` | `revision` | Explicitly inspect an interrupted branch-import receipt; never replay a provider turn. |\n| `DELETE /filtered-tasks/:id` | JSON `revision` | Remove a stopped terminal task's private export/record; preserve any imported review branch. |\n\nThe task container receives only permitted committed regular files and a new Git\nobject database. It mounts no original repository metadata, host login directory,\nauthentication volume or application memory. The image layer and copied files\ndenied writes are read-only. Network starts off; enabling inference access is an\nexplicit owner choice. Input/output limits are 500 files, 512 KiB per file and\n16 MiB total; live monitoring is a polling safeguard, not a filesystem quota.\nRestarted effectful phases become unknown and require explicit inspection or\ncancellation. See [runtime provisioning, recovery and limitations](filesystem-policy.md).\n\n## SSH projects\n\nOwner-only connection management is available on the local listener. SSH host\nbackends and phone/shared sessions cannot manage additional SSH connections.\n\n| Endpoint | Input | Result |\n| --- | --- | --- |\n| `GET /workspace-projects` | none | Local and connected remote project IDs and names, grouped by computer; never repository paths or credentials. |\n| `GET /ssh-hosts` | none | Saved host IDs, names, aliases and connection state, plus concrete aliases discovered from SSH config. |\n| `POST /ssh-hosts` | `sshHost`, optional `name` | Save an alias or `user@hostname`; port/key/jump configuration stays in OpenSSH. |\n| `POST /ssh-hosts/:id/connect` | none | Install or reuse the remote backend and return a stable, same-origin `/computer/:id/` workspace path. |\n| `POST /ssh-hosts/:id/disconnect` | none | Close SSH channels and preview forwards, leaving remote work running. |\n| `POST /ssh-hosts/:id/stop` | none | Stop an idle remote backend, then disconnect. Refuses active execution, terminals and development services. |\n| `DELETE /ssh-hosts/:id` | none | Disconnect and remove the saved definition; remote data remains. |\n| `GET /ssh-context` | none | Local workspace context. At `/computer/:id/api/ssh-context`, returns the saved computer ID, name and connection state. |\n\nThe returned workspace uses the same local app origin and owner session. Its\n`/computer/:id/api/` endpoints and event streams address the remote backend, so project paths, Git,\nfiles, checks, agent tools and terminals all refer to that server. Mutations are\nnever replayed automatically. See [SSH projects](ssh-projects.md) for setup,\nreconnection, remote previews, supported hosts and authentication boundaries.\n\nManaged Tailscale certificates renew automatically while Tailscale remote access is enabled. `GET /remote` includes `tls.renewal` with `automatic`, `running`, `nextAttempt`, optional `lastSuccess` and a safe `error`. Renewal preserves paired sessions and control settings. Imported/environment certificates are excluded.\n\nFor multi-agent roles in Race, Debate, Review council, Supervisor and Loop workflows, a role setting can include additional models from the same provider: `roleSettings.reviewers.claude = { model: \"first\", variants: [{ model: \"second\", effort: \"low\" }] }`. Each entry runs separately with its own settings and session. Pipeline and other single-agent roles still use one model. Auto team pools also accept variants in `roleSettings.pool`: these are allowed model/effort choices for workers and reviewers. The coordinator selects among them within the member limit; it does not launch every selected model. Coordinator settings stay fixed. Additional models use the same validation as primary models (maximum 128 additional models per provider).\n\n### Result experiences and browser scenarios\n\nThese operations are owner-only and use the existing origin, authentication,\nfilesystem-policy and host boundaries. New run requests can include `intent`\n(`bug`, `feature`, `review`, `explore`) and an existing same-repository `journeyId`.\nComparison variants can include separate `task` instructions while sharing the\nbase run configuration. Comparison launches can partially succeed; inspect the\nreturned/visible group before retrying an interrupted request.\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /experience` | optional `repo`, `project`, `intent` query | Outcome metrics and repository/workflow sample counts, Wilson intervals and supporting run IDs. |\n| `POST /runs/:id/outcome` | `branch`, `expectedHead`, `decision=accepted\\|needs-work` | Record feedback on an idle, exact result commit; stale heads return 409. Does not publish or approve managed shipping. |\n| `POST /runs/:id/task-priority` | `taskId`, `revision`, `priority` (-10 to 10) | Prioritize undispatched tasks on the current Auto-team plan without relaxing scheduling constraints. |\n| `GET /runs/:id/worktrees/:name/serve/ready` | none | Report process state and whether its local app port accepts a connection. |\n| `GET /journeys` | absolute `repo` query | List saved immutable browser scenarios in this repository. |\n| `POST /journeys` | `repo`, `title`, local `url`, `width`, `actions`, `expected: {selector, text}` | Validate and save a scenario; returns 201 with its ID and data. Actions use `kind=click\\|fill\\|select\\|press`, `selector`, and optional `value`. |\n| `GET /journeys/:id` | none | Return scenario, generated Playwright spec, and up to four replay receipts. |\n| `POST /journeys/:id/replay` | optional local `url`, `runId`, `branch` | Replay in fresh Chrome, save screenshots and assertion status, return 201. Run-associated replays require that result's registered running app. Preserves the scenario's path/query/hash on the selected origin. |\n| `DELETE /journeys/:id` | none | Delete the scenario and retained screenshots. Existing runs keep their original attached test context. |\n\nA replay receipt's `passed` status means only that the expected visible text\nappeared after the recorded interactions. It is separate from Git verification;\nsee [result experiences](application-improvements.md#result-first-product-experiences).\n\n## Documentation and help\n\n`GET /docs/` serves the bundled documentation without requiring a session; it contains only repository documentation. Page URLs use `/docs/<guide>.html`. Static files do not expose project state.\n\n`GET /api/docs-help` lists installed built-in CLI providers for the authenticated owner. `POST /api/docs-help` invokes the selected CLI with documentation excerpts:\n\n```json\n{\n \"agent\": \"claude\",\n \"question\": \"How do I choose a workflow?\",\n \"page\": \"patterns\",\n \"settings\": {\"model\": \"sonnet\"},\n \"history\": []\n}\n```\n\nThe response contains `answer` (plain text) and `sources` (guide IDs and titles). `page` is an optional guide ID; it is never a filesystem path. `history` accepts at most three `{question,answer}` pairs. The JSON body is limited to 64 KiB, questions to 4,000 characters and answers to 12,000. Settings accept a model and effort for the selected provider.\n\nThe endpoint is owner-only on the main listener, subject to the existing origin checks and filesystem execution policy. It does not support ACP, extension providers, repository paths or remote/container execution. Closing the request aborts the turn. Two help requests can run at once; extra requests return 429. See [Ask code-flow](help.md) for execution and privacy boundaries.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n\n\n## Auto model catalog\n\n`GET /api/model-catalog` returns provider discovery catalogs, managed-provider eligibility, `allowResearch` and offline status. It uses the cached catalog and performs bounded CLI discovery when missing or stale. `POST /api/model-catalog` explicitly refreshes; its closed body accepts optional booleans `allowResearch` and `offline`. Research is disabled by default. `CODEFLOW_MODEL_NETWORK=offline` prevents new discovery/research while retaining cached evidence.\n\nEach catalog identifies its provider/environment/revision and separates reported model/effort availability, documented facts and run observations. It contains no authentication credentials. The existing `/api/models` endpoint retains its legacy behavior. Remote/container discovery happens in the selected execution environment, independently of this local preview. New Auto steps persist `modelAssignment` (provider, exact ID, effort, rationale, catalog revision/source and environment). Advanced worker `roleSettings.pool` pins are checked again at dispatch and on recovery; global coordinator defaults do not pin workers.\n\n\n### Antigravity execution authority\n\nNew run requests accept `antigravityExecution: \"isolated\" | \"native\"`. Omission defaults to isolation, or inherits the parent's recorded choice for continuations. `\"native\"` explicitly opts into unrestricted host execution using the existing login and configuration; it leaves Claude/Codex permissions unchanged. An isolated parent cannot be upgraded by a child. Native mode rejects mandatory permission requirements, Safe/narrowed trust, tool approvals, remote/container and unattended/locked-down runs. It is not a template setting and is persisted in the run policy and step receipt.\n\n`GET /api/model-catalog?antigravityExecution=native` and the POST refresh body's `antigravityExecution` choose native catalog discovery for previews; they do not grant execution authority. Default previews use isolated discovery. Both modes have distinct cache identities. Native execution records advisory read-only requests, unrestricted terminal access, provider-configured browser/MCP/hooks/native agents, and native child usage outside Code Flow's budget.\n","title":"Local API and command-line client"},{"group":"Build and extend","id":"extensions","source":"docs/extensions.md","body":"# Provider and workflow extensions\n\nInspect the extension bundle and its declared API version before enabling it. Pin the version you intend to run; an extension is trusted executable code, not a sandboxed configuration file.\n\n## Connect a trusted extension\n\nFor OpenCode or another CLI that speaks Agent Client Protocol, use [ACP connections in Settings](acp-agents.md). No JavaScript extension bundle is needed for a compatible stdio agent.\n\nThe owner can register additional provider adapters and workflow patterns with an explicit server environment configuration. There is no repository discovery, remote download, HTTP plugin installation or shared-user administration. Settings → Extensions reports loaded/rejected bundles and negotiated API versions. Restart to apply configuration changes.\n\nAn extension is trusted JavaScript server code, with the server owner's filesystem, network and process permissions. Review its source and bundle dependencies before enabling it. Hash pinning detects changed bundle bytes; it does not make malicious code safe or sandbox its behavior. Use self-contained `.mjs` bundles; relative imports resolve from the private captured-bundle cache, not the original module directory. Provider authors must honor abort signals, timeouts, trust modes and the normalized event/result contracts. Built-in compatibility probes, container isolation and SSH workers do not certify or execute additional plugin providers.\n\nSet `CODEFLOW_EXTENSIONS_FILE` to an absolute regular JSON file:\n\n```json\n{\n \"version\": 1,\n \"extensions\": [{\n \"id\": \"example\",\n \"version\": \"1.0.0\",\n \"module\": \"/absolute/path/example.mjs\",\n \"sha256\": \"replace-with-the-64-character-sha256-of-the-reviewed-bundle\",\n \"api\": { \"major\": 1, \"minimumMinor\": 0 },\n \"requires\": [\"provider-adapter\", \"workflow-pattern\"]\n }]\n}\n```\n\n`CODEFLOW_EXTENSIONS_JSON` accepts the same object; choose one source. The host supports API 1.0 and rejects a newer major/minimum minor or unknown required capability before importing the bundle. Up to 16 extensions can each supply up to eight providers and eight workflows. Configuration is bounded to 128 KiB and bundles to 512 KiB; symlink files and changed hashes are refused. Captured bytes are copied to a private hash-named cache before import, so a change to the original file between review and import cannot substitute unpinned content.\n\nThe default export has this shape:\n\n```js\nexport default {\n api: { major: 1, minor: 0 },\n providers: [/* AgentAdapter objects from src/adapters/types.ts */],\n workflows: [/* Pattern objects from src/engine.ts */]\n};\n```\n\nEach ID must be `plugin:example/name`. Provider adapters implement `available()`, `catalog()` and `run(options, onEvent)` and return normalized `AgentEvent`/`RunResult` data. Workflows declare names, descriptions, bounded role/default-provider and option schemas, then execute through `TeamContext` for worktrees, agent turns, commits, checks, artifacts and replayable remembered operations. New providers appear in agent selection and workflows in the normal composer. Built-in IDs cannot be replaced, duplicate registrations are refused and workflow default roles must refer to registered providers. Plugin capabilities remain unknown until independently established; built-in tool bridges and live approval prompts are unavailable.\n\nRuns pin every selected extension's version and SHA-256. Resume or further dispatch refuses a missing or changed pin; restore the exact reviewed bundle or create a fresh run. A restrictive filesystem policy also blocks plugin execution. The fixture acceptance test loads a real captured bundle, registers a new provider/workflow, runs it through the actual engine, commits its result and executes its real check without provider quota or external requests.\n\n`GET /api/extensions` is owner-only and returns status, hashes, negotiated versions and registered IDs, with bounded redacted error details. It does not return module source or the configuration document. Extension caches are executable code, not portable project data; restore does not enable plugins without an explicit environment configuration.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Provider and workflow extensions"},{"group":"Build and extend","id":"provider-compatibility","source":"docs/provider-compatibility.md","body":"# Provider compatibility\n\nRun the provider diagnostics when an installed CLI stops accepting an invocation. Protocol fixtures and live version/help probes provide different evidence; a fixture passing does not verify a real provider login.\n\n## Check the installed CLI\n\nThe Provider compatibility workflow runs on adapter changes, pushes to main, manual dispatch and weekly. It installs official Claude Code 2.1.300/2.1.302 and Codex 0.162.0/0.162.1 in isolated directories, plus the current official Linux x64 Antigravity build. Antigravity downloads are verified against the SHA-512 checksum in the official updater manifest. The exact observed version, platform, Node version, timestamp, required flags and missing capabilities are retained in a 30-day JSON artifact per matrix entry. A missing required flag, unknown probe or unexpected pinned version fails its job.\n\nThese real binary probes run `--version` and `--help` only with a new empty home and no account credentials. npm installs use the explicit public registry and empty user/global configuration, and probes execute the installed absolute binary path rather than falling back to a host CLI. Pinned versions compare complete semantic versions. They never sign in, list account models, ask an agent to perform work or spend inference quota. Installing an official package may run its vendor installer. Maintained process fixtures separately exercise stream normalization, error typing, structured results, sessions, cancellation and Codex app-server behavior. Help compatibility and fixture success do not prove live account/inference behavior.\n\nRun a pinned probe locally with `node --import tsx scripts/check-provider-compatibility.ts codex 0.162.1 /tmp/codex-compatibility.json`. The current Antigravity channel probe runs on Linux x64/arm64 (glibc or musl). Successful probes define evidence for those exact versions; versions outside the matrix remain untested and installed health checks still apply. Update pins explicitly after reviewing release notes and the reports. CI must run on GitHub before its scheduled evidence is available.\n\nVendor references: [Claude Code releases](https://github.com/anthropics/claude-code/releases), [Codex releases](https://github.com/openai/codex/releases), [Antigravity installation](https://antigravity.google/docs/cli/install/).\n\nLocal acceptance on October 18, 2026 passed official Claude Code 2.1.300/2.1.302 and Codex 0.162.0/0.162.1 probes on macOS arm64, plus the official current Antigravity 1.4.2 binary in an isolated Linux arm64 musl Docker container. Exact receipts are in [provider-compatibility-evidence.json](provider-compatibility-evidence.json). Linux x64 CI execution remains a separate platform check.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Provider compatibility"},{"group":"Build and extend","id":"accessibility","source":"docs/accessibility.md","body":"# Accessibility acceptance\n\nTest the relevant flow with a keyboard, at a narrow viewport and in both themes. Automated audits supplement focus, navigation and readable status checks; they do not replace them.\n\n## Check the complete interaction\n\nThe main browser suite runs local axe-core checks against WCAG 2 A/AA, 2.1 AA and 2.2 AA rules. It covers the composer, Insights, Settings (including Automations), completed runs, Workspace tools, command palette, folder picker and the Files/Notes/Chat side panel in light and dark themes, plus mobile composer/workspace layouts. The suites also check Settings categories, shared login/reviewer views, human collaboration, filtered-task imports and the Improve mode menu. Reports and screenshots are generated under `output/playwright/`.\n\nThe audit fixed helper/status/provider text contrast, named dialogs, small breadcrumb targets, keyboard access to scrolling results, focusable controls inside the hidden side panel, empty tab strips and keyboard resizing of the panel. A visible-on-focus skip link moves focus to the main content. The command palette supports arrow keys/Enter/Escape, and Escape returns focus to its trigger. The side panel supports arrow-key tab selection and keyboard resizing; closed panels are inert. Mobile Workspace uses a labelled grouped selector so its tools do not overflow into a long strip of tiny buttons. Reduced-motion preferences disable sustained animation and smooth scrolling.\n\nRun `node --import tsx --test test/e2e/accessibility.e2e.ts` with Google Chrome installed. This uses the real server and browser with deterministic fake agents; it makes no account requests. The test asserts every enabled automatic rule and writes incomplete/manual-check results rather than treating them as passing assertions. Decorative status glyphs have adjacent text and are hidden from assistive technology.\n\nAutomatic checks and scripted keyboard flows do not establish complete WCAG conformance or replace testing with assistive technology and users. The mobile screenshots were also inspected for clipped controls and unreadable content. New forms, dialogs and interactive states should extend the browser coverage. Native screen-reader/device acceptance remains an ongoing product-quality check.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Accessibility acceptance"},{"group":"Build and extend","id":"performance","source":"docs/performance.md","body":"# Performance\n\nUse the diagnostic and benchmark commands below to identify the affected boundary. Compare the same workload and record variance before changing concurrency, caching or storage.\n\n## Measure the workload first\n\nThe backend remains Node/TypeScript and the UI remains Preact. Execution journals,\nprovider receipts and repository leases retain their existing authority. The changes\nbelow reduce repeated work and bound presentation buffers without changing agent\npermissions, verification requirements or retrying ambiguous remote executions.\n\n## History and transcripts\n\n`RunIndex` maintains ordered run summaries, complete normalized search text and\nrepository/worktree membership. Ordinary updates replace the indexed record without\nsorting every run again. Unfiltered history uses a binary cursor lookup; filtered\nsearch still scans the in-memory search strings. Only the selected page is cloned.\nAuthorization filtering precedes totals and pagination.\n\nCold projections now include their full searchable text. Legacy cold indexes hydrate\nonce during asynchronous startup to obtain that text. Listing and searching no longer\nread every cold detail file or synchronously validate every archived source. Opening\na detail or using it for an operation still validates the source and rebuilds stale\nprojections. HTTP detail hydration uses asynchronous I/O; synchronous compatibility\nremains for existing internal mutation callers. Indexes grow with run count, and\nlive/resumable runs still retain their execution metadata. Legacy log compaction\nhydrates one archive and invalidates its cold summary before replacing its source;\nshutdown waits for the projection refresh to finish.\n\nNew UI subscriptions use `run-page:<id>`. The initial frame contains the latest run\nand step snapshots, a recent transcript page, and persistence status. A reconnect\nreuses a sequence cursor if the missed events fit the page; otherwise it explicitly\nresets to a recent page. The old `run:<id>` topic and per-run events endpoint remain\navailable for older clients. Those legacy endpoints and full exports still load full\nhistory; they are not the bounded UI path.\n\n`GET /api/runs/:id/transcript?step=:step&before=:cursor` reads backwards directly from\na file offset. The opaque cursor binds the offset to the run and file identity.\nAppending does not invalidate older pages; replacing/compacting a log does, returning\n409 so the viewer can reload Latest. There is no initial full scan or separate index\nfile to recover. Partial pages can be empty when a bounded scan crosses only metadata\nor events from other steps; their continuation cursor still advances.\n\nDefault limits:\n\n| Resource | Bound |\n| --- | --- |\n| Transcript response | 500 events and 1 MiB of event JSON |\n| Scan per page | 16 MiB, plus the record crossing the boundary |\n| Individual log record | 64 MiB |\n| Displayed event string | 32 Ki UTF-16 units, with a visible omission notice |\n| Browser transcript cache per run | 8 MiB estimated serialized memory / 10,000 events |\n| Idle browser run feeds | Six feeds, within a 32 MiB estimate |\n| Initial transcript rows | 200; older pages replace the step window |\n| Global run replay ring | 500 snapshots / 32 MiB serialized data |\n\nComplete event text remains in the authoritative log and full export. Historical\nstep windows do not append live text into an older page; Latest returns to current\nevents. A tool result whose call is outside the page is still displayed explicitly.\nThe estimates bound retained payloads, not total JavaScript heap or DOM allocation.\n\n## Streaming and storage\n\nSSE stops writing when Node reports backpressure, resumes in order on `drain`, and\ndisconnects a client whose queued plus writable bytes exceed the budget. The default\nis 8 MiB per connection (`CODEFLOW_SSE_BUFFER_BYTES`). The existing\n`CODEFLOW_SSE_STALL_MS` timeout also applies. An individual oversized frame is refused\nby the same bound. Slow consumers never block agent execution.\n\nDisk writes are ordered by run ID, including that run's event log and execution\njournal. Up to four unrelated runs can write concurrently. Ordinary contiguous\nappends batch up to 128 records / 256 KiB. Synced writes, rewrites and deletion form\nordering boundaries. The append backlog has a 64 MiB byte budget: overflow is a\nreported write failure, and strict journal callers fail closed.\n\nOptional startup checkpoints use a separate single-writer queue with a 32 MiB\npayload admission budget. Their captured source identity prevents a stale checkpoint\nfrom describing newer events. Durability barriers can be scoped to a run; reading\ntranscripts waits for that run's appends without fsyncing every other run. Shutdown\nstill flushes writes. The run view reports saving and persistence failures separately\nfrom workflow completion.\n\n## UI and analysis\n\nThe UI build produces hashed ESM chunks and a hashed stylesheet. Settings, Terminal,\nBrowser and Changes load on demand. Both owner and teammate terminal renderers\nuse dynamic imports; a bundle test rejects xterm in the startup import graph. Assets have immutable caching, compression,\nETags and a bounded server cache. HTML remains revalidated. Old chunks remain on the\nlocal build directory for already-open tabs; SSH release packages include only the\ncurrent manifest's files. Packaging checks that HTML and manifest belong together.\n\nCode intelligence reuses at most two isolated TypeScript workers, each with the\nexisting 192 MiB heap limit. Every request still checks and reads the current allowed\nsaved sources. Only changed source text and removals are sent to the language service.\nThis preserves freshness and filesystem checks without rebuilding the service each\ntime. Workers expire after 60 seconds idle and are terminated on cancellation,\ntimeout, policy changes and server shutdown. Only one inspection runs at a time.\n\nApprovals use snapshots on the shared SSE connection, with polling only for older\nhosts that do not expose the topic. Inventory, statistics, automation, notification,\ndeferred-run and visible diff polling use the common scheduler. Shared polling waits for completion before scheduling another request, pauses in\nhidden tabs and refreshes when visible. Background inventories explicitly opt in to\nsharing concurrent identical GETs; selection-sensitive reads always issue fresh requests.\nMutations invalidate the request-sharing map. Writes are never automatically retried.\n\n## Measurement and verification\n\n`/api/health` includes bounded HTTP timing samples, event-loop delay/utilization,\nprocess memory, disk queue depth/bytes, slow-client disconnects, storage-cache counts\nand language-worker reuse counts. Settings diagnostics include this snapshot and\nbrowser shell-ready/long-task measurements. Metrics contain no request bodies or\nquery strings. These are local diagnostic counters, not exported telemetry.\n\nRun:\n\n```sh\nnpm run build\nnpm run benchmark:performance\nnpx tsx --test test/performance-boundaries.test.ts test/transcript-pages.test.ts\nnpx tsx --test test/e2e/history-launch.e2e.ts\n```\n\nThe benchmark uses a temporary synthetic home, 10,000 indexed runs and 100,000\ntranscript events. It reports timings, scanned/response bytes and built asset sizes;\nit does not access the user's actual run history or invoke providers. Treat timings\nas machine-dependent observations. The deterministic tests enforce ordering, buffer\nbounds, cursor correctness and failure behavior instead of fragile timing thresholds.\n\nFurther optimization should follow real profiles. SQLite-backed search, Rust modules,\nadditional worker offloading and adaptive process limits remain architectural options,\nnot requirements for these bounded paths.\n\n## Before/after and native comparison\n\nSee the [measured results](performance-results.md) and committed raw samples.\n\n`npm run benchmark:compare -- --baseline <commit> --trials 7` builds the baseline\nfrom a Git archive, the current TypeScript app, and a temporary copy with the\nexperimental Rust transcript reader. It uses identical synthetic fixtures and real\nHTTP servers plus Chrome. One browser warmup precedes repeated fresh browser\ncontexts, and variant order rotates. The OS filesystem cache remains warm. Default\nfixtures have 1,000, 5,000 and 100,000 events. `--scenarios small,medium` selects a\nsubset. `--api-repeats 1` makes one API measurement per browser trial instead of\nthe default five. Raw samples, machine/tool versions, commit IDs and failures are saved to\n`output/performance/comparison.json`; `--output` overrides that destination.\n\nThe UI measurement ends when the latest event is visible after opening Show work.\nThe old app transfers/replays all events; the new app transfers a bounded latest\npage with Earlier navigation. API timing likewise compares full replay to the\nlatest page, not equal-size responses. Browser received bytes are decoded resource\nbytes, not compressed wire bytes. Heap is JavaScript heap after forced GC, not\nwhole-process memory. Timeouts are failures, never silently replaced with a timing.\nAgent execution and provider inference are excluded. See the Rust experiment's\n[README](../experiments/rust-transcript/README.md) for its scope and parity test.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Performance"},{"group":"Build and extend","id":"run-storage","source":"docs/run-storage.md","body":"# Run storage\n\nArchive completed work to reduce retained detail in memory. Open an archived run to load its full evidence; archival caches do not replace the durable event log or delete worktrees by themselves.\n\n## Understand archived history\n\nThe append-only run event log and execution journal remain the source of truth. Startup recovers at most four runs concurrently, consuming fallback logs one record at a time. Recovery retains the latest run snapshot and latest state of each step; provider transcripts are read only when that run’s history is requested. It no longer builds an array containing every historical event log. A recovery record has a 64 MiB bound; exceeding it fails recovery visibly instead of silently dropping an ID.\n\nOnly archived, completed runs with no pending notes, follow-ups, approval, watch or unresolved remote execution can use cold storage. Running, interrupted, failed, stopped and unarchived runs remain in the normal live/resumable map. No worktrees or execution journals are removed by this optimization.\n\nEach eligible run has optional `runs/<id>.cold-index.json` and `runs/<id>.cold-detail.json` projections. The compact index is at most 128 KiB; the full detail projection is at most 16 MiB. Oversized runs stay in the normal map. The projections carry checksums and exact source-file identities. A missing, damaged, changed or stale projection is rebuilt from the authoritative event log. They are caches, not a second source of truth.\n\nCold list/history entries explicitly return `cold: true` and `detailAvailable: true`. They preserve IDs, timestamps, repository/worktree ownership, parent/source/project provenance, source trust, pinned filesystem policies, permissions, usage, statistical counters and review attribution. Large task/summary previews are shortened, and heavy team graphs, context evidence, conventions and review prose are omitted. Open the run to read its complete metadata, transcript and evidence. Full-text history searches use indexed complete task and source/title text, including text beyond the preview. Legacy indexes obtain this text once during asynchronous startup; normal searches do not hydrate archived details.\n\nFull metadata is read on demand through an eight-entry, 32 MiB least-recently-used cache. Individual reads are bounded to 16 MiB. A cold read returns a detached copy; mutation and Resume promote the complete run before changing it. Weak references preserve any still-held original live run handle during promotion without retaining archived full state. Updated data stays live until its queued event snapshot has been flushed and a matching cold projection has been saved. Source changes invalidate cached detail. Cold metadata is never used to reconstruct execution claims or substitute for journal replay.\n\nHistory keeps every run ID and count. Cursor ordering uses creation time and run ID so runs created in the same millisecond are not skipped. Attention pagination includes every running run, plus at most 100 older attention items per page. Authorization filtering happens before totals and cursors. The response reports total attention, truncation and a continuation cursor; history remains available for older entries.\n\nReviewed retention manifests include both cold projection files when deleting their owner run. Their recent cache-generation time does not reset the original run’s retention age. Offline log compaction removes the derived cold projections so the next start rebuilds them. The normal backup and filesystem-policy boundaries continue to apply to these files.\n\nThis bounds retained full archived metadata and recovery batches. The compact index still grows with the number of recorded runs, and active/resumable runs remain in memory deliberately. The current UI reads bounded transcript pages directly by byte offset. Legacy stream clients and full exports can still request complete event history. Search uses an in-memory index, not a database. See [Performance and resource boundaries](performance.md) for limits, diagnostics and benchmarks.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Run storage"},{"group":"Build and extend","id":"documentation","source":"docs/documentation.md","body":"# Documentation and GitHub Pages\n\nThe Markdown files in this repository are the source for both the local help library and a static documentation site. The site uses the same warm paper surfaces, olive controls, mint accents, Inter fonts, Georgia headings and light/dark theme tokens as the application.\n\n<picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"images/docs-dark.png\">\n <img src=\"images/docs-light.png\" alt=\"The Code Flow documentation library, using the application's colors and typography with grouped navigation, search and local help.\" width=\"100%\">\n</picture>\n\n## Read locally\n\n`npm start` builds the documentation with the app. Open **Application menu → Help & documentation** or visit `/docs/` on the application server. Search and navigation work locally; the **Ask code-flow** chat side panel uses your installed CLI after owner authentication. The conversation and draft follow you between guides in the same tab.\n\nTo preview only the static site:\n\n```bash\nnpm ci\nnpm run docs:preview\n```\n\nOpen [127.0.0.1:4319](http://127.0.0.1:4319). This preview intentionally behaves like GitHub Pages, so companion controls are omitted. Restart the preview after editing Markdown or site assets.\n\n## Publish to GitHub Pages\n\n1. Push this repository to GitHub with `main` as the publishing branch. If your default branch differs, update `.github/workflows/docs.yml` accordingly.\n2. Open the repository's **Settings → Pages** and choose **GitHub Actions** as the build source.\n3. Run the **Documentation** workflow manually, or push a documentation change to `main`.\n4. Open the deployment URL reported by the workflow's `github-pages` environment.\n\nThe upstream site is [Code Flow documentation](https://midhunxavier.github.io/code-flow/). GitHub may redirect this address to the owner’s configured Pages domain. Forks use their own owner and repository names. Adding the workflow alone does not activate or publish a site.\n\nThe workflow builds `dist/docs`, runs the documentation tests, uploads the Pages artifact and deploys it. Pull requests build and test without deploying. All site assets and links are relative, so project subpaths and custom domains work without a base-URL setting. There are no CDN fonts, analytics scripts or client framework dependencies.\n\nFor another static host, build and upload the contents of `dist/docs`:\n\n```bash\nnpm run build:docs\n```\n\n## Edit a guide\n\n1. Edit its Markdown file under `docs/`. Keep task instructions first, followed by limits and API details.\n2. Add new guides to `docs/site/navigation.json` and link them from the docs index.\n3. Link to other guides using relative `.md` paths. The builder rewrites those links to `.html` and preserves heading anchors.\n4. Run `npm run build:docs` and `npm run test:docs`.\n5. Preview desktop and mobile layouts in both themes when changing presentation.\n\nThe generated `public/docs` and `dist/docs` directories are ignored by Git. Commit the source Markdown, site assets and screenshots instead. `npm run build` packages the docs with remote host bundles too. App development builds the docs at startup; rerun `npm run build:docs` after changing guide content during a dev session.\n\n## Screenshots\n\nThe README screenshots are captured from the real application UI with deterministic fake CLIs and a disposable example repository. They are demonstration data, not live customer work or evidence of provider performance.\n\nRun the reproducible capture script with Chrome installed:\n\n```bash\nnpm run screenshots:docs\n```\n\nIt builds the app, starts a private fixture server, creates a sample run and captures the composer, run and transcript in light and dark mode. It also captures documentation in both themes. Screenshots are written to `docs/images`; transient browser artifacts belong in `output/playwright`. The fixture server and temporary repository are removed afterward. The script does not use real provider quota.\n\nKeep images focused, use descriptive alternative text, and label demo data wherever a screenshot could be mistaken for verification evidence. Re-capture when navigation or the run layout changes.\n\n## Implementation\n\n| File | Responsibility |\n| --- | --- |\n| `scripts/build-docs.mjs` | Markdown rendering, links, navigation, table of contents and search corpus |\n| `docs/site/navigation.json` | Guide groups and reading order |\n| `docs/site/style.css` | Documentation layout, using app theme tokens and fonts |\n| `docs/site/app.js` | Search, theme, code copying and local chat panel |\n| `src/docs-help.ts` | Static local serving, bounded retrieval and CLI help requests |\n| `.github/workflows/docs.yml` | Build validation and Pages deployment |\n\n[Documentation library](README.md) · [Architecture](architecture.md) · [Ask code-flow](help.md)\n","title":"Documentation and GitHub Pages"},{"group":"Build and extend","id":"releasing","source":"docs/releasing.md","body":"# Preparing a preview release\n\nThe source repository stays private to npm publishing (`private: true`). The release builder creates a separate, publishable package in `dist/npm` with compiled JavaScript, UI, local docs, workers, SSH helpers and licenses. It has no install scripts or runtime npm dependencies. The npm package and macOS app use that same runtime.\n\n## Build and validate\n\nUpdate the root package version and lockfile to a new `x.y.z-preview.N` value, then run:\n\n```sh\nnpm ci\nnpm run typecheck\nnpm test\nnpm run release:prepare\nnpm run test:package\n```\n\n`release:prepare` writes the npm tarball, SHA-256 checksum, generated Homebrew formula and draft release notes to `output/releases`. The packaging acceptance test installs that actual tarball in a temporary directory outside the checkout, starts the server, checks the browser opener and bundled docs/assets, exercises the semantic worker, then verifies graceful shutdown and headless startup. It uses fake agent CLIs and never spends provider quota.\n\nInspect the package before publishing:\n\n```sh\nnpm pack ./dist/npm --dry-run\nnode dist/npm/bin/codeflow.mjs --help\n```\n\nThe **Preview release** GitHub workflow builds and tests the tarball on macOS and Linux, builds both macOS architectures, and creates a **draft prerelease** with the exact artifacts. It accepts an existing Git ref and checks that its version matches the requested tag. It does not publish npm or create a Homebrew tap. Run it against a committed, reviewed release ref.\n\n## npm preview channel\n\nThe published package is scoped as `@midhunxavier/code-flow` because npm rejected the unscoped name as too similar to the existing `codeflow` package. The installed commands remain `codeflow` and `code-flow`.\n\nAuthenticate to the npm account that owns `@midhunxavier/code-flow`. On first publication, confirm the package name is available and that you are publishing from the intended account. Then publish the tested tarball:\n\n```sh\nnpm whoami\nnpm publish output/releases/midhunxavier-code-flow-0.1.0-preview.3.tgz --tag preview --access public\nnpm view @midhunxavier/code-flow@preview version\n```\n\nUse the actual version's filename when publishing subsequent previews. Never publish this preview with the `latest` tag. The staged package also sets `publishConfig.tag` to `preview`. Test `npx @midhunxavier/code-flow@preview` from a clean directory after registry publication.\n\n## macOS DMG\n\nBuild on macOS with Xcode Command Line Tools installed:\n\n```sh\nnpm run package:macos -- arm64\nnpm run package:macos -- x64\nnpm run test:macos\n```\n\nThe builder downloads an official Node runtime with a pinned SHA-256, packages a pinned Electron runtime with the prepared backend and Node license, compiles the native computer-use helper, and creates a drag-to-Applications DMG. The app requires macOS 13+. Its sandboxed renderer has no Node access, and the separate local backend survives window reloads and closing. Lifecycle tests launch the actual packaged app, verify renderer isolation and backend continuity, and check normal quit and crash cleanup. Both architectures are tested on matching GitHub runners. Git and the agent CLIs remain user-installed. The launcher reads the login shell's PATH to find common CLI and version-manager installations.\n\nWithout signing credentials the output is explicitly named `-unnotarized.dmg`. These builds are for developer testing; do not describe them as signed, notarized or Gatekeeper approved.\n\nFor distributable DMGs, install a **Developer ID Application** identity in the build keychain and save notarization credentials with `xcrun notarytool store-credentials`. Set:\n\n```sh\nexport CODEFLOW_SIGN_IDENTITY='Developer ID Application: Your Name (TEAMID)'\nexport CODEFLOW_NOTARY_PROFILE='code-flow-notary'\nnpm run package:macos -- arm64\nnpm run package:macos -- x64\n```\n\nThe builder signs Node with its JIT entitlements, signs the app and DMG, submits to Apple's notarization service, staples the ticket, and verifies the result. Never commit certificates, credentials or keychain files.\n\n### Desktop updates\n\nSigned and notarized builds also produce `code-flow-VERSION-macos-ARCH.zip`, its SHA-256 file, and `releases-ARCH.json`. The ZIP contains the notarized, stapled app; the feed uses Electron's [Squirrel JSON format](https://www.electronjs.org/docs/latest/tutorial/updates) with a version, exact asset URL, checksum and size. The release workflow attaches these automatically. Publish the DMG, ZIP and feed together under the matching `vVERSION` GitHub release, and keep the same Developer ID signing identity across updates. Unnotarized builds never produce a native update feed.\n\nThe desktop main process checks public GitHub releases on launch, every six hours, and on demand. It compares stable/preview versions numerically, skips drafts and missing architecture assets, and never selects an older version. It downloads through Electron's native updater only after the user chooses **Download update**. Restarting uses the normal active-agent confirmation and backend shutdown. Unnotarized installations offer the matching DMG instead. No GitHub token is needed, and browser/npm clients have no desktop update controls.\n\nBefore publishing the first signed updater release, install it in Applications and test updating to a second signed version on both architectures. Unit and lifecycle tests cover release selection, failure/retry states, bridge isolation and restart cancellation; only this two-version acceptance check validates Apple's signing/notarization and Squirrel's actual app replacement. Existing desktop previews without the updater need a manual upgrade to acquire it.\n\nThe GitHub workflow can import the following repository secrets into a temporary build keychain: `MACOS_CERTIFICATE_BASE64`, `MACOS_CERTIFICATE_PASSWORD`, `MACOS_SIGN_IDENTITY`, `APPLE_ID`, `APPLE_TEAM_ID` and `APPLE_APP_PASSWORD`. Supply all six for signed and notarized builds; with none set, it produces clearly labeled unnotarized developer builds. Partial credential configuration fails instead of silently dropping signing.\n\n## Homebrew\n\n`release:prepare` generates `output/releases/Formula/code-flow.rb` from the actual npm tarball's SHA-256. After the GitHub prerelease assets are published, copy this formula into `Formula/code-flow.rb` in the `midhunxavier/homebrew-code-flow` repository, then test:\n\n```sh\nbrew install --build-from-source midhunxavier/code-flow/code-flow\nbrew test midhunxavier/code-flow/code-flow\ncodeflow --version\n```\n\nThe tap must exist and contain the generated formula before advertising the Homebrew command as available. Regenerate the formula whenever the tarball changes; it references the GitHub release asset, so npm registry availability is independent.\n\n## Publish the GitHub prerelease\n\nReview the draft notes and attached checksums. Verify each DMG's signing status and label it accurately. Publish the draft as a **prerelease**, leaving the stable latest release unchanged. The draft notes include an exact GitHub tarball command so the preview can be tried even before the npm channel is available.\n\nKeep platform claims precise: macOS and Linux are supported; Windows users use WSL2. Native Windows execution is not supported. On a real Windows machine, confirm WSL networking and the Windows browser opener before claiming end-to-end Windows validation.\n\n[Documentation library](README.md) · [Installation](installation.md)\n","title":"Preparing a preview release"},{"group":"Engineering records","id":"application-improvements","source":"docs/application-improvements.md","body":"# Application improvement program\n\n> Engineering record. Status, measurements and decisions below describe the recorded implementation or review. For current usage, start with the [documentation library](README.md).\n\nThis work continues the application review on branch `codex/application-improvements`, created from `main` at `abbb6be`. The original review contained defects, 14 broader product problems, and 73 feature ideas; those are tracked separately below.\n\nThe later `main` update at `d1677e1` is also integrated. Its composer Improve feature\nretains Quick/Repository modes with cancellation, guarded Undo, safe provider\npermissions, disposable private committed snapshots and mutation leases. Shared access, filesystem\nrestrictions and nonlocal execution profiles cannot bypass their execution rules\nthrough this endpoint.\n\nBefore publication, this branch also integrates `main` at `7a593cc`: pinned\nworktree Git namespaces, stronger test-integrity checks, compact run-log patches\nand shared live-update transport. New workspace/verification helpers retain those\nnamespace guards. Cold recovery decodes patches incrementally and refreshes source\nidentities after compaction. Owner stream sharing uses a private channel secret\nand an independent enumerable lock ID; shared accounts receive neither, and\nidentity changes close old owner connections. Targeted namespace, storage, broker,\nauthorization and same-tick repository-picker regressions cover the integration.\n\nCurrent scope: all six priority correctness packages implemented; all 14 broader\nproblems have concrete improvements; all 73 feature items have implemented\nacceptance scopes. Full-suite acceptance passed. Results are recorded\nbelow; feature boundaries remain explicit rather than claiming unrestricted\nprovider parity or complete accessibility conformance.\nAn item is complete only when its behavior is implemented, reachable from its\nintended interface, documented, and covered by appropriate validation. A service\ninterface, mock, or roadmap entry alone is not an implemented feature.\n\n## Correctness and quality gates\n\n- [x] Destination symlink containment and exclusive worktree copying.\n- [x] Repository selection and configuration responses cannot overwrite newer choices.\n- [x] Notes initialization, failed-write recovery, revision history, and conflicts.\n- [x] Invalid routes recover without blanking the application.\n- [x] Memory deletion descriptions match actual semantics; editing resolves conflicts.\n- [x] Critical browser flows are required by CI, with stable test synchronization.\n\n## Existing product problems\n\n- [x] 1. Simplify the first-run decisions.\n- [x] 2. Give the composer room instead of opening an empty side panel.\n- [x] 3. Make mobile execution controls readily accessible.\n- [x] 4. Use consistent completion and verification states.\n- [x] 5. Surface native tool approvals.\n- [x] 6. Make execution isolation explicit and enforceable.\n- [x] 7. Secure phone transport and individual sessions.\n- [x] 8. Bound history startup, storage, and browsing. Streaming recovery uses four concurrent runs; archived detail uses an eight-entry/32 MiB cache and compact source-validated projections; old attention is capped and visibly paginated. Compact IDs and active/resumable metadata still scale with history. See [run storage](run-storage.md).\n- [x] 9. Include inline GitHub review comments.\n- [x] 10. Enforce browser correctness in CI.\n- [x] 11. Make evaluation reproducible and retain experimental evidence gates.\n- [x] 12. Separate core responsibilities behind tested modules. Persistence projection/recovery/cold storage, workspace operations, shared authorization, policy enforcement, remote execution, code-host operations and repository coordination have explicit tested modules. The engine retains execution authority and the server composes the route/lifecycle boundaries; further size reduction is ongoing maintenance.\n- [x] 13. Improve keyboard, focus, touch, and screen-reader behavior. Theme/viewport browser audits, keyboard navigation and focus restoration, inert closed panels, contrast, target size, mobile tool selection and reduced motion are covered. Assistive-technology/user testing remains ongoing; no complete WCAG certification is claimed. See [accessibility acceptance](accessibility.md).\n- [x] 14. Attribute provider results with samples and uncertainty.\n\n## Feature acceptance backlog\n\n- [x] 1. Guided onboarding and dependency/authentication health, including explicit terminal/browser/Docker discovery and refresh.\n- [x] 2. Repository readiness inspection and isolated command validation.\n- [x] 3. Simple and advanced composer modes.\n- [x] 4. Named versioned team presets with import/export.\n- [x] 5. Editable acceptance criteria.\n- [x] 6. Parameterized task templates.\n- [x] 7. Effective settings and provenance inspection.\n- [x] 8. Command palette.\n- [x] 9. Scoped native tool approval inbox.\n- [x] 10. Editable validated workflow graph.\n- [x] 11. Priority queue and project fairness.\n- [x] 12. Safe checkpoint pause.\n- [x] 13. Fork from a checkpoint.\n- [x] 14. Selective task reruns and downstream evidence invalidation, with pinned retained baselines and fresh final review/checks.\n- [x] 15. Quota-aware deferred execution with durable claims, reported reset windows and explicit ambiguous-launch recovery.\n- [x] 16. Token and API-equivalent usage ceilings.\n- [x] 17. Conflict-aware task assignments.\n- [x] 18. SSH remote workers with explicit repository mappings, worker-local credentials, bounded exact source/result bundles, heartbeats, cancellation, durable claims and receipt-only recovery. Actual encrypted loopback SSH acceptance passed. See [worker setup](remote-workers.md).\n- [x] 19. Multiple verification gates.\n- [x] 20. Repository verification profiles.\n- [x] 21. Requirement-to-evidence mapping across workflows.\n- [x] 22. Review coverage reporting.\n- [x] 23. Change risk summaries.\n- [x] 24. Baseline and flaky-check diagnosis.\n- [x] 25. Optional mutation verification.\n- [x] 26. Visual and accessibility verification gates.\n- [x] 27. GitHub review threads with inline locations/replies, bounded complete-thread loading, explicit remote resolve/reopen, fresh revision/head checks and honest postflight race reporting. See [thread synchronization](github-review-threads.md).\n- [x] 28. Validate the current merge target.\n- [x] 29. Export verification reports.\n- [x] 30. Symbol-aware code search and JavaScript/TypeScript imported references through an isolated language service.\n- [x] 31. Static architecture exploration across JavaScript/TypeScript, Python, Go, Rust and Java, including manifest packages, directory boundaries, cycles, explicit unresolved/computed edges and exports. No project configuration executes. See [architecture explorer](architecture-explorer.md).\n- [x] 32. File editing with save previews and conflicts.\n- [x] 33. Selective file/hunk application.\n- [x] 34. Three-way conflict resolution.\n- [x] 35. Multi-service workspace launch profiles.\n- [x] 36. Context inspection before dispatch: bounded directory expansion, explicit assembled-input measurements and warn/block ceilings. Hidden provider history and later file/image tokens are outside this scope.\n- [x] 37. Concurrent workspace activity coordination with in-process workspace mutation leases across starts, resumes and owned worktrees. External/manual programs remain outside the lease system.\n- [x] 38. Full checkpoint comparison and guarded staged inverse commits, alongside selective file/hunk restoration. Merge-commit mainline decisions remain manual.\n- [x] 39. Memory provenance graph.\n- [x] 40. Memory edit conflict resolution.\n- [x] 41. Memory deletion impact preview.\n- [x] 42. Stale-memory detection.\n- [x] 43. Memory duplicates and supported semantic contradictions with policy synonyms/negation, structured boolean/text/numeric interval claims, unit conversion, exact source revisions and overlapping applicability. Unsupported prose is counted and no winner is selected automatically. See [semantic claims](memory-claims.md).\n- [x] 44. Dedicated versioned project conventions with immutable history, conflict-safe drafts, preview restore and guarded explicit publication.\n- [x] 45. Searchable revisioned notes.\n- [x] 46. Curated project onboarding summaries.\n- [x] 47. Explicit schedule time zones and previews.\n- [x] 48. Inspectable webhook queue.\n- [x] 49. Failed delivery replay.\n- [x] 50. Automation dry runs.\n- [x] 51. Grouped prioritized review inbox for manual and unattended questions, blocked work, failures and results.\n- [x] 52. GitLab/Bitbucket code fetch, prepared-container review, comments/replies/resolution, verified-result push, new PR creation and pinned Git merge. Durable unknown-operation receipts prevent automatic replay. Unsupported native queue/policy/atomic approval capabilities are explicit and fail closed. See [code-host workflows](code-hosts.md).\n- [x] 53. Linear and Jira sources.\n- [x] 54. Opt-in external notifications.\n- [x] 55. Documented CLI and versioned API.\n- [x] 56. Multi-repository dependency DAGs, pinned verification/source policy, durable child receipts, current accepted result evidence, reviewed shipping matrices and explicit per-repository CAS merges. Actual two-repository browser acceptance passed. See [coordinated changes](coordinated-changes.md).\n- [x] 57. Isolated agent containers.\n- [x] 58. Secure paired phone sessions.\n- [x] 59. Per-project CAS read/write allow/deny rules enforce app access and intersect current/pinned policies. Manual filtered Docker tasks use immutable prepared images, allowed committed files in a fresh Git namespace, read-only denied-write mounts, bounded outputs, confirmed cancellation and explicit source/policy/content-checked review-branch imports. Ordinary native/SSH/automated execution remains paused under restrictions. Actual Docker isolation/import/cancellation and browser acceptance passed. See [filesystem policy](filesystem-policy.md).\n- [x] 60. Secret filtering for exported content.\n- [x] 61. Saved per-type retention policies for archived runs, attachments, run memory and worktrees, with protected exact plans and explicit offline application.\n- [x] 62. Backup and restore.\n- [x] 63. History compaction and pagination.\n- [x] 64. Sanitized diagnostic bundles.\n- [x] 65. Persistent connection status.\n- [x] 66. Continuous official supported-version CLI version/help matrix, bounded isolated installs, exact executable/version checks, protocol fixtures and retained reports. Five actual official CLI probes passed locally; GitHub Actions execution awaits a pushed branch. See [compatibility evidence](provider-compatibility.md).\n- [x] 67. Task-specific provider comparisons.\n- [x] 68. Human rework tracking.\n- [x] 69. Critical-path timing analysis.\n- [x] 70. Reproducible benchmark fixtures and model identities.\n- [x] 71. Local scrypt identities, revocable expiring sessions, scoped viewer/reviewer project roles, safe results/feeds/exports and commit-anchored human review. Owner remains the sole executor. See [shared projects](shared-projects.md).\n- [x] 72. Local VS Code integration with SecretStorage connection, task creation, run inspection, cancellation and result worktrees; packaged and tested in the actual editor host.\n- [x] 73. Explicit trusted bundled provider/workflow plugins, negotiated API/capabilities, captured hash-verified cache bytes, atomic registration and immutable run version/hash pins. No repository auto-discovery or HTTP installation. See [extension contracts](extensions.md).\n\n## Implementation boundaries\n\nMutation, visual and accessibility gates execute explicitly configured repository\nscripts; they do not supply mutation engines or image baselines. External\nprovider APIs were validated with HTTP fixtures and real local Git repositories,\nnot live third-party account writes. GitLab/Bitbucket code fetching is explicit;\nordinary source-text imports remain read-only. Native containers and encrypted\nloopback SSH use fake CLIs, with no inference quota. Official compatibility\nprobes execute only version/help. Shared roles grant review access; phone pairing\ngrants owner control. Critical-path metrics use observed time, not predictions.\n\nArchitecture and semantic-memory analysis are bounded static/deterministic tools,\nnot complete runtime dependency discovery or unrestricted natural-language\nreasoning. Extensions are trusted full-authority server code. Remote workers\nmust be provisioned explicitly. Code-host native queues and unsupported policy\nsemantics require the provider UI. These are documented implementation boundaries,\nnot silently simulated success.\n\n## Validation\n\nUse fake agents for development. Never spend real provider quota, publish results,\nsend external notifications, or mutate third-party accounts as a side effect of\nthe test suite. Account-dependent features require explicit configured credentials\nfor live acceptance testing; fixture coverage is recorded separately.\n\nThe first checkpoint passed 621 unit tests with three opt-in Docker cases skipped;\nthose three native Docker cases passed separately. Its full browser suite passed\n35/35. The second batch's unit run passed 688/688 executed checks (three optional\nDocker cases skipped). Focused browser acceptance also covers conventions,\ndependency discovery, deferred restart/receipt recovery, selective reruns,\nretention, context budgets, commit reversions and language-service references.\nThe final full browser rerun passed 45/45 after the folder-picker race fix.\nThe VS Code extension passed actual host acceptance on installed VS Code 1.141.0,\nand the packaged VSIX passed the same host acceptance after isolated installation,\nincluding delayed-response connection/disconnection regressions. No real provider\nquota, external notifications or third-party account mutations were used.\nThe test runner limits unit concurrency to four files and browser concurrency to\ntwo because these suites launch real Git, server and browser processes.\n\nThe final implementation checkpoint passed 812 unit checks with six optional\nnative-runtime checks skipped. Those five real Docker checks and the encrypted\nloopback SSH check passed separately. The full browser suite passed 64/64.\nThe last filtered-task concurrency changes passed 31 focused unit checks and\n2/2 browser flows after the main suite had started. Native acceptance includes\nfresh-home credential seeding with dummy credentials, denied-file/original-Git\nabsence, read-only mounts, exact review-branch import and confirmed cancellation.\nThe packaged VSIX passed actual installed VS Code host acceptance again.\nTypecheck, UI/worker builds, 68 accessibility renderings and local documentation\nlinks passed. Provider version/help probes used actual official binaries; no\nlive inference, account mutation or notification delivery occurred.\n\nAfter integrating the newer `main` composer feature, 19 existing browser and\naccessibility checks, four new Improve browser flows and two shared/policy security\nflows passed. Eleven prompt-improvement unit checks and all 14 server-reliability\nchecks passed, including a delayed-turn lease/cancellation test that exposed and\nfixed an incorrect 400 response for concurrent run starts. A subsequent source\nreview moved Improve out of the working checkout entirely so external edits can\nnever be discarded by read-only cleanup. Final typecheck and\nUI/worker builds passed. The Improve menu adds two mobile theme audits, bringing\nthe accessibility acceptance total to 70 renderings; both mobile screenshots were\ninspected for readable controls and overflow.\n\nThe post-merge full unit run passed 828 checks with six optional native-runtime\ncases skipped (already passed separately). After private-snapshot hardening,\nall 15 Improve checks and all 15 server-reliability checks passed again, including\nexternal source edits/commits, private cleanup, executable-configuration exclusion,\nsnapshot bounds and early environment checks across both API prefixes. The four\nImprove browser flows passed with the isolated backend; the final menu-wrapping\nchange also passed its mobile/profile regression and visual inspection.\nThe final subprocess PATH fix passed 15 foundation/filesystem-policy unit checks\nand the policy browser flow. Actual malicious cwd/relative-bin executables were\nrefused for inherited and overridden PATHs; explicit absolute fixture binaries\nremain usable. The exact-route regression passed again after adding malformed\nsuffix/method checks across both API prefixes.\n\n## Result-first product experiences\n\nThe composer retains its outcome starters and now records an explicit task type\non new runs. Expand **Workflow advice from this repository** to inspect check\nsuccess, acceptance feedback, correction samples and supporting runs, then apply\na workflow deliberately. Suggestions require three completed samples per\nworkflow, retain failures in their denominator and do not infer task types from\nolder prose. They describe history, not task difficulty or a predicted winner.\n\nRunning work has a compact progress card with dispatch pause and run priority.\nAuto teams also have a **Who's doing what** map: select a task to inspect its\nowner, paths, dependency blockers and open questions. A task priority applies at\nthe next dispatch and cannot override dependencies, occupied members/paths,\nreview reservations or budgets. Existing journaled dispatches remain pinned on\nresume. Standing-team task priorities remain controlled by their mission.\n\nFinished results lead with **Try this result**, evidence, and commit-specific\n**Accept result / Needs more work** feedback. The preview waits for its allocated\nport before opening. Feedback neither publishes nor substitutes for standing-team\napproval. Project pages and Insights show historical acceptance, median elapsed\ntime to a verified result, recorded correction time and merged runs. Unrated\nruns and missing correction records are explicit; missing corrections are not\nzero-minute samples. Acceptance refers to the latest recorded decision, which\ncan belong to an earlier commit. The result screen flags that difference.\n\n**Show me what changed** captures the running app beside an optional original\nURL at the same viewport. Snapshots use fresh browser profiles and can be\ndownloaded; they are visual observations rather than automated proof. For a\nrecorded scenario, the result instead also offers a replay and a step-by-step\nscreenshot walkthrough beside the original replay.\n\n**Try two alternatives** launches two ordinary runs from the same exact result\ncommit with separate instructions and budgets. Each result has its own preview,\ncheck state and continuation link. No branch is chosen, merged or published\nautomatically. If a comparison launch fails partway through, inspect recent runs\nbefore requesting another; successful children remain visible in their group.\n\n### Record a bug and prepare its regression test\n\n1. Open a local HTTP app in **Browser** and choose **Record a bug**.\n2. Reproduce the flow. Clicks, text input, single-select changes and\n Enter/Tab/Escape are supported. The recording follows same-origin document\n navigation. Shadow DOM, nested frames, file uploads, gestures and authenticated\n fixtures require adapting the exported test.\n3. Stop recording, inspect/remove interactions, name the scenario and specify\n an expected element selector and visible text. Save the immutable scenario.\n4. **Replay original scenario** checks that assertion in fresh Chrome and saves\n a screenshot walkthrough. Password/credential fields are excluded; review\n other recorded form values before saving. Replays can mutate the app's data.\n5. **Prepare verified fix** attaches a generated Playwright regression test to a\n new pipeline draft. The agent is instructed to demonstrate failure before the\n fix and success afterward, adapting it to the repository's test setup. Review\n the draft and its check configuration before starting.\n6. On the result, start its preview and **Replay scenario on this result** to\n compare observations. A replay never changes the run's independent check or\n proof verdict. Only the specified visible-text assertion is tested. The\n recorded branch head is informational: dev servers can serve an older build\n or uncommitted files, so a browser replay does not certify a Git tree.\n\nRecordings are owner-only, stored privately under `CODEFLOW_HOME/journeys`,\nincluded by ordinary backups, and available from each project's **Recorded\nscenarios** library after restart. Export the generated test or delete an unused\nscenario and its evidence there. Limits: 100 scenarios per host, 30 interactions\nper scenario, 1,920px recording width, four retained replay receipts per scenario,\n2.5 million base64 screenshot characters per receipt, 16 MiB per evidence file,\none replay at a time, and the existing 45-second snapshot execution deadline.\nUncaptured frames are omitted, never synthesized. Replay checks local app URLs,\nrefuses code-flow's own listener ports and keeps document navigation on the\nselected origin. The existing snapshot network caveats also apply. Filesystem\nrestrictions pause these operations; shared reviewers cannot access recordings.\n\nProject pages now lead standing teams with a **Project briefing** showing up to\nthree actionable opportunities. Ready drafts come first, followed by reported\nimpact and recency. Cards expose users, benefit, risk, source quotes and the\npinned source commit. **Dismiss opportunity** records a rejection through the\nexisting team decision service, so subsequent discovery respects it. Schedules\nand standing teams remain explicitly activated by the owner.\n\n[Documentation library](README.md) · [Development guide](architecture.md)\n","title":"Application improvement program"},{"group":"Engineering records","id":"project-first-navigation-plan","source":"docs/project-first-navigation-plan.md","body":"# Project navigation review and implementation plan\n\nStatus: Phases 1–5 implemented on 21 October 2026. Phase 6 software validation is complete; the participant pilot remains an external release activity. Original review baseline: `d82a2e1`.\n\nMake **Project** the main place to start, follow and review work. Repositories, runs, shared boards, people and scheduled work should have predictable places inside that project. Show the computer that owns the project and, where relevant, the computer that executes the work.\n\nThe initial experience should be: **Open a project → describe the task → start a run → review the result.** A person using one repository on one computer should not have to configure a workspace, a team or a connection first.\n\n## Implemented navigation contract\n\nThe owner experience uses **Project** for a group of repositories, drafts and runs; **Repository** for a Git root; **Run** for an execution and its evidence; and **Computer** for the host that owns files and project records. **Repository tools** replaces Workspace in the owner navigation. Board contains shared tasks; People contains membership and access. Shared dashboards retain independent safe projections. Project Automation and global Scheduling use the existing schedule, backlog and mission operations.\n\n| Destination / visible label | Address and source identity | Access and compatibility |\n| --- | --- | --- |\n| New run / project draft | `#/project/:id/new`; current computer + project ID | Owner; legacy `#/` and `#/new` resume the last draft; `#/new/unassigned` opens the unassigned draft. |\n| Overview | `#/project/:id` | Owner projection from that computer's projects and runs. |\n| Runs | `#/project/:id/runs` | Owner; independent project query, archived option and pagination. |\n| Repositories / Repository tools | `#/project/:id/repositories/:encodedPath/:tool` | Owner; only project roots and known project run worktrees. Full path stays visible. |\n| Project settings | `#/project/:id/settings` | Owner; project editing, outcomes and scenarios. Missions live in Automation. |\n| Legacy repository tools | `#/workspace`, `#/settings/workspace`, `#/project/:id/workspace` | Global bookmarks retain composer repository context; project bookmarks render Repositories with their project context. |\n| Run / result review | `#/run/:id`, `#/review/:id/:branch` | Existing run identity, branch decoding and review permissions. |\n| Inbox / Insights | `#/inbox`, `#/insights` (including settings aliases) | Existing global filters. Filtering cannot alter a composer destination. |\n| Project Board / People | `#/project/:id/board`, `#/project/:id/people` | Owning computer’s board only (`team=local`). Switching from a linked board replaces the document before rendering collaboration controls. |\n| Linked Board / People | `?team=:connection#/team/project/:id`, `#/team/people/:id` | Connection pinned per document; server-authorized membership and repository keys. Account/grant administration remains owner-only. |\n| Project Automation | `#/project/:id/automation/:kind` | Explicit project and repository; independent of sidebar filters and the previously open composer draft. |\n| Scheduling | `#/automations/:kind` | Current computer and existing saved schedule identity. The global project filter controls the list; choose a project/repository here before configuring new work. |\n| Settings / Connections | `#/settings/:section`, connection groups | Existing owner settings. Old SSH, remote-access and team settings aliases remain supported. |\n| Board / task | `#/team`, `#/team/project/:id`, `#/team/task/:id` | Existing selected collaboration connection + board ID. No local-project identity inferred from matching names or IDs. |\n| Detached side panel | `#/side/:tab/:scope` | Existing run/project/repository pin; target remains independent of composer navigation. |\n| Another computer | `/computer/:computer/` plus these routes | Existing server request prefix and browser-storage namespace. No local fallback when disconnected. |\n| Shared-user dashboard | Existing shared-account entry and projections | Existing grants and safe APIs; owner pages are not rendered for shared accounts. |\n| Invalid/unavailable target | Malformed project route, missing project or removed repository | Explicit recovery state. Retain draft; block a new launch into an invalid destination. |\n\nScreen sketches used for this slice:\n\n```text\nFirst visit Project Overview Project composer\nComputer: This computer Project · Computer Project · repository\nOpen repository Overview | Runs | Repos Task + context\nJoin shared project Describe task / Continue Workflow + run options\nOn another computer Needs you · Running Computer / Project / Repo\n Recent results Start run\n\nMultiple repositories Shared board (retained)\nProject · Computer Collaboration connection\nRepository cards + paths Board / task details\nSelected root or worktree Existing membership/grants\nRepository tool selector No inferred local mapping\n```\n\nThere is one task editor. Overview opens the existing composer rather than maintaining a second task field. Only the visible project section mounts its specialized panels.\n\nDraft storage is versioned IndexedDB, scoped by computer and project, with a retained legacy backup and an unassigned destination for ambiguous migration. Text, attachments, selected paths, workflow roles/models, checks, limits, approvals, source, scenario, context budget and execution options travel together. The current implementation maintains one saved draft per project in this browser; simultaneous editing of the same project's draft in multiple tabs has last-writer behavior.\n\nNew UI launches send an explicit-selection marker. The server verifies that the project exists and the canonical repository remains a member before preparation and again at placement. Adding a repository is an explicit API operation. Existing API/automation callers retain their placement behavior unless they request strict selection.\n\nThe automated scenario baseline covers route aliases, computer namespaces, delayed repository responses, draft switching and launch placement. No human completion-time or wrong-turn baseline was collected in this implementation session. The participant pilot and before/after measurements remain required release validation in Phase 6; automated tests do not establish usability improvement.\n\n## Phases 4–6 implementation and validation\n\nPhase 4 reuses the existing board API, account CAS updates, repository-key editor and explicit host-execution/terminal grants. Owner projects embed only their own computer’s board. Linked boards remain connection-scoped; there is no inferred remote-to-local relationship. URLs retain `team` identity, requests/downloads and subscriptions stay pinned to that document, and a source switch reloads the document. An unavailable project does not expand to all projects. Refused board/task reads clear stale content and controls. Shared People uses only authorized board projections and never mounts owner account controls.\n\nPhase 5 adds Automation with Run once later, Repeat, Process a backlog and Pursue a mission. Project lists ignore the global filter. Repository selection is explicit; opening backlog items waits for the chosen project’s saved agent workflow to restore and retains its destination. Missions move from settings to Automation; actionable opportunities link there from Overview and Inbox. More and command search always expose specialized destinations. Saved collaboration, schedules and missions remain discoverable when the optional Board shortcut is off.\n\nNo rollout flag is needed for this implementation: it uses additive routes, existing permissions/APIs and the compatible draft format from phases 1–3. No execution records, repository keys, roles, schedules or mission history are migrated. Legacy routes and search aliases remain available. No deployment or user-data reset was performed.\n\nValidation record (21 October 2026):\n\n- Full unit suite: 1,212 tests, 1,204 passed, eight opt-in integration checks skipped, zero failures.\n- Project-navigation browser suite: all ten scenarios pass, including colliding linked project IDs, access refusal, project-specific scheduling/backlogs and mobile light/dark accessibility.\n- Shared collaboration/account browser suites: all 24 scenarios pass, including an empty shared project with repository keys and no server clone, maintainer People access without owner requests, live revocation, host execution and terminal grants.\n- Existing mission, experience and scheduling browser scenarios pass after moving mission navigation. An initialization race found by the new backlog scenario was fixed: an unsuccessful draft restore cannot open the picker with the prior repository.\n- Build, TypeScript and 12 documentation checks pass. The documentation capture script now produces project Overview and Automation screenshots with disposable demo data. Mobile screenshots were inspected; More, project tabs and scheduling controls fit at 390 pixels.\n- Final full browser suite: all 153 scenarios pass, zero failures. The earlier full run found six failures. Fixes preserve Auto team answer drafts when a question moves into the attention card, keep the mobile computer header within the viewport while reconnecting, remove duplicate global scheduling filters, and update tests to wait for explicit project selection and use the current repository/backlog controls.\n- Final full unit rerun: 1,204 passed, eight opt-in skips, zero failures. All 52 focused collaboration/API/UI checks also pass after the final visibility and terminology updates.\n- Additional integrations: VS Code extension-host acceptance passes; the opt-in Rust reader test and both real loopback SSH tests pass. After recovering Docker Desktop from stale backend processes, `npm run test:containers` passes all five real Docker checks with zero failures or skips: read/write isolation, host-secret and Git-metadata protection, cancellation, hard timeout, and filtered edit import. Together these runs cover all eight integration checks skipped by the default unit command. Live provider-usage tests were not invoked.\n- Final build, typecheck, documentation checks and whitespace validation pass. Documentation screenshots were regenerated and inspected after the UI fixes.\n\nPR integration against `main` at `0968718` preserves complete project history counts and the newer editable-retry behavior. Retry identity, custom verification and execution options now travel with each project draft, including when projects share a repository. The retry action waits for draft initialization. The full unit rerun passes 1,231 of 1,239 checks with zero failures and eight opt-in skips; all five Docker checks pass separately. The full browser run exercised 165 scenarios: 160 passed, four retry-navigation checks failed, and one reconnect test raced the expected page reload. After fixing those issues, the complete 11-scenario daily-workflow suite (including a new initialization regression) and seven-scenario Tailscale suite pass with zero failures. This verifies 166 distinct browser scenarios across the full run and focused reruns. Final build, typecheck, all 12 documentation checks and whitespace validation pass; refreshed project screenshots were visually inspected.\n\nThe automated scenario matrix is exercised by the project-navigation, team/shared-projects, SSH/computer, automation, mission, workspace and accessibility suites. Automated keyboard/axe checks support validation but do not establish human usability improvement. No participant pilot or completion-time comparison was conducted. The original five-person pilot and its wrong-destination threshold remain an external release gate; no pilot results are claimed.\n\n## Review of the idea\n\nThe direction is sound, but changing labels alone will not resolve the confusion. The app has overlapping navigation destinations, implicit project selection and several legitimate meanings of workspace and team. Those need consistent behavior as well as consistent names.\n\nThe current implementation already provides a useful foundation:\n\n| Finding | Evidence | Design consequence |\n| --- | --- | --- |\n| Projects already group repositories, runs and workflow defaults. A repository can occur in more than one project. | [Project model](../src/projects.ts) | Build on the existing project entity; avoid a new organizational layer or assuming exclusive repository ownership. |\n| Starting a run can create a project or add a repository to one. The composer can change the active project after a repository choice. | [Selection state](../ui/state.ts), [project placement](../src/projects.ts) | Make the destination understandable before launch and make membership changes explicit. |\n| The project page has a task box, metrics, scenarios, missions, results, repository details and automations together. | [Project page](../ui/project.tsx) | Give immediate work priority and move configuration and specialized workflows to predictable destinations. |\n| Human collaboration is partly called People, but its main destination is a task board. | [Application breadcrumbs](../ui/app.tsx), [board](../ui/collab-board.tsx) | Call the task board Board; reserve People for membership and access. |\n| Repository tools opens a page called Repository workspace or the project's workspace. | [Project page](../ui/project.tsx), [activity page](../ui/activity.tsx) | Use Repositories for the destination and Repository tools for actions inside it. |\n| SSH computers have separate API paths, browser state and drafts. The sidebar already lists projects by computer. | [Client routing](../ui/lib.ts), [connected projects](../ui/connected-projects.tsx), [routing regression](../test/workspace-routing.test.ts) | Keep computer identity visible and preserve those boundaries. |\n| A selected collaboration connection can address a different server from the current computer's project store. Shared repositories use keys mapped to local clones. | [Collaboration connections](../src/collab/connections.ts), [collaboration state](../ui/collab-state.ts) | A project name or ID alone cannot identify a board across connections. |\n| Connections, optional People controls and purpose-based Scheduling already exist. | [Connections](../ui/connections.tsx), [Scheduling](../ui/scheduling.tsx), [existing acceptance tests](../test/e2e/simplification.e2e.ts) | Extend the simplification already in progress and retain its useful workflows. |\n\nThree refinements are necessary to the original proposal:\n\n1. **Computer remains visible context.** Removing workspace as a navigation concept must not obscure where files, CLIs, terminals and settings live. Delegating an agent turn to a worker is a different operation from opening a project on another computer.\n2. **Runs and board tasks retain distinct meanings.** A run is an execution with results and evidence. A board task records ownership, discussion, reviews and execution attempts. A combined task system would require a separate product and data-model decision.\n3. **Project organization must feel optional for a simple start.** Opening a repository should suggest a project name automatically. Users should not complete an administrative form before describing their task.\n\nThe expected benefit is fewer decisions and fewer context switches. That remains a usability hypothesis until the proposed flows are tested with users.\n\n## Terminology contract\n\n| Term | Meaning and placement |\n| --- | --- |\n| Project | The main navigation destination for related work. Its identity includes the source that owns it. |\n| Repository | Code attached to a project. A single repository is selected automatically; multiple repositories require a visible selection. |\n| Run | One execution of a requested task. Keep New run, Start run, run history and review terminology consistent. |\n| Board | Optional shared task management within a project. Board cards open task details and their linked attempts or runs. |\n| People | Members, project roles and access. Administrative account creation remains subject to owner authority. |\n| Agents | Providers and roles performing a run. Use Agent workflow for their configuration. |\n| Computer | The location of project files and the Code Flow instance serving them. A run can additionally name an execution worker. |\n| Automation | Scheduled, deferred or triggered work within a project. Keep existing distinctions between repeat tasks, backlogs and missions. |\n| Mission | An optional ongoing goal under Automation. Retain preview eligibility and existing review controls. |\n| Workspace | Acceptable descriptive prose for the app as a whole; remove it from competing primary destinations and ambiguous context labels. |\n\nKeep established workflow names such as Auto team during this change. Explain them under Agent workflow. Renaming every public workflow, internal class, API and storage key would broaden the change without improving navigation.\n\nUse the same display terms in visible labels, accessible names, breadcrumbs, command search, empty states and documentation. Old words remain search aliases so existing users can find relocated features.\n\n## Proposed navigation\n\nThe navigation for the common local-owner case should look like this:\n\n```text\nNew run\nInbox\n\nProjects\n Code Flow\n Storefront\n Add project\n\nApplication menu\n Scheduling across projects\n Insights\n Connections\n Settings\n Help\n```\n\nProjects remain expandable to recent runs, preserving fast access from the sidebar. Show computer groups only when multiple computers are configured. Keep an unobtrusive computer label in the project header, including on a single-computer installation. On a hosted session, use the server's available name or Remote server rather than calling the browser's device This computer.\n\nWithin a project:\n\n```text\nStorefront Computer: Development VM\nOverview Runs Repositories More\n\nMore\n Board\n People\n Automation\n Project settings\n```\n\nOverview, Runs and Repositories are the owner view's primary destinations. Board becomes a visible tab when collaboration is configured; Automation can become visible when the project has active scheduled work. Keep their positions stable for that project rather than changing tabs on every transient response. People stays an access-management destination. Shared users receive destinations allowed by their existing access, such as Board and Results; they do not receive owner tools merely because the shell is shared.\n\nOverview shows the task entry point, items requiring a decision, active work and recent results. Put usage summaries, scenario libraries and detailed mission configuration behind their relevant destinations. Existing mission decisions still appear in the overview and Inbox when actionable.\n\nKeep the global Inbox and Scheduling views as cross-project tools on the current computer. Label their scope. They must not imply that they aggregate every connected computer or collaboration server. Cross-server aggregation is outside the initial release.\n\n## User flows\n\n### First project and first run\n\n1. The empty state offers **Open repository** and **Join shared project**. Remote setup is available through **On another computer** in the repository flow.\n2. Open repository validates the selected repository and suggests its folder name as the project name. Reuse an unambiguous existing project; if several contain it, show their names and require a choice.\n3. Create or open the project through the existing project API. Keep the name editable. Creating the project succeeds independently of launching an agent.\n4. Open the composer with the project and repository already set. Present a recommended installed agent/workflow using existing defaults and availability rules.\n5. The user describes the task, reviews the compact summary and selects Start run. The summary includes repository, branch, execution computer, agents, checks and limits.\n\nAdvanced run options remain available. Preserve current permission, check and approval defaults; reduced visual complexity must not silently change execution behavior.\n\n### Returning to a project\n\nOpen its Overview from the sidebar. Its task box and New run action lead to the same composer with explicit project context. A global New run action reuses the current valid project, or offers a lightweight project choice when none is selected. An existing unsent draft is restored rather than overwritten by a navigation action.\n\nUse one canonical composer for both entry points. Avoid two independent prompt forms with different defaults, attachment behavior or launch validation.\n\n### Working with multiple repositories\n\nShow the selected repository in the composer summary even when there is only one. For multiple repositories, use the last valid selection for that project and computer; show a selector before launch. Repositories displays the full list with paths and branch context sufficient to distinguish identical folder names.\n\nChoosing a repository outside the current project offers explicit **Open its project** or **Add to this project** actions. Adding uses the project's membership operation and reports success before launch. Canceling leaves the current project and draft intact. The server revalidates project membership at launch, including if another tab removed the repository.\n\n### Working on another computer\n\nOpen the remote project directly from the existing computer-grouped sidebar. Preserve the owning computer in the URL, API routing, drafts and settings. A disconnected project shows a reconnect action and its computer name; execution stays unavailable until the intended connection is restored.\n\nSwitching the computer opens that computer's project context. It does not move a project, copy repository files or reuse a local path on the remote machine. The run summary names a delegated worker separately when configured.\n\n### Collaborating with people\n\nAn owner opens Project → People to view members and roles. Existing owner administration supplies account creation and access grants. A maintainer sees only actions the existing API authorizes.\n\nProject → Board shows shared task ownership and progress. Join shared project reuses the existing connection flow, then lists authorized projects and maps shared repository keys to local clones when execution requires them. Joining must not create an unrelated local project with the same name and imply that it is the shared project.\n\nFor a linked server, a board link carries its connection identity as well as its project identity. A local project may show a linked board shortcut only when the relationship has been explicitly established; matching names, paths or IDs is insufficient. Keep the existing connection-scoped board entry available until that relationship is supported reliably.\n\n### Scheduling and ongoing missions\n\nProject → Automation opens the existing Scheduling purposes scoped to that project. Each purpose obtains its repository from the project context rather than directing users to New run just to select one. Keep Run once later, Repeat, Process a backlog and Pursue a mission as distinct choices.\n\nGlobal Scheduling can filter across projects on the current computer. Managed mission schedules remain controlled by mission settings; moving their navigation does not grant generic automation controls authority to edit them.\n\n## Context and routing design\n\n### Resolve context explicitly\n\nIntroduce a small UI context module that derives the current project, source, repository and available capabilities. Separate the project being viewed, the composer draft's destination and the filter used by global lists. Today these purposes share parts of the active-project state, which makes unrelated screens influence one another.\n\nAn owner project reference needs the computer identity and project ID. A linked shared project reference additionally needs the collaboration connection and its project ID, qualified by the computer holding that connection. Run and board-task references likewise retain their source. These are client routing identities, not new organization records or replacements for server authorization.\n\nThe context module should implement these rules:\n\n- A valid project route determines the page's project. Global filters cannot silently override it.\n- Explicit draft choices take precedence over remembered defaults. Defaults apply only to unset fields or an intentional reset.\n- An empty project has no runnable repository. It still has a useful add-repository or board-setup state.\n- Drafts and selected repositories are isolated by source and project. A repository removed from membership becomes an explicit unresolved destination; it must not silently redirect the draft elsewhere.\n- Reads, subscriptions and async selection responses are scoped and invalidated when context changes. A late response cannot overwrite a later choice.\n- The composer, side panel, repository tools and launch summary use the same resolved destination unless the side panel is explicitly pinned. A pin remains clearly labeled with its actual scope.\n- Unavailable or unauthorized sources stop resolution; they never fall back to a different computer or project for a mutation.\n\nKeep server-side identity, permission, membership and checkout validation authoritative. A project ID accepted by a UI component is not evidence of access.\n\n### Add project destinations without breaking links\n\nExtend the project route with a validated section and optional repository/tool selection. Build links centrally rather than concatenating them in every component. Persist repository/tool context needed to reopen a deep link; do not depend on whatever the composer last selected.\n\n| Existing entry | Proposed behavior |\n| --- | --- |\n| `#/project/:id` | Project Overview. |\n| New `#/project/:id/runs` | Project run history. |\n| New `#/project/:id/repositories` | Repository list and tools with explicit repository selection. |\n| `#/project/:id/workspace` | Compatibility alias for the project's Repositories destination. |\n| New `#/project/:id/automation/:kind` | Existing Scheduling purpose with an explicit project. |\n| New project Board and People routes | Resolve only against the owning server or an explicitly identified collaboration source. |\n| `#/run/:id` and `#/review/:id/:branch` | Keep working and derive project breadcrumbs from the result's recorded context. |\n| `#/team`, project and task variants | Keep connection identity and open the corresponding board/task view. Never reinterpret against an unrelated active project. |\n| `#/workspace` and `#/settings/workspace` | Open repository tools for a valid current destination; otherwise show an explicit repository/project chooser. |\n| Legacy SSH, team and remote-access settings links | Continue resolving through Connections. |\n\nRetain `/computer/:id/` prefixes, existing team query context and URL encoding. Malformed links produce a recoverable state. Unsupported project subroutes should not silently appear to be Overview. Browser back/forward must restore both the destination and its context.\n\n### Reuse existing screens\n\n| Area | Main implementation locations | Required change |\n| --- | --- | --- |\n| Routes and context | `ui/navigation.ts`, `ui/state.ts`, `ui/lib.ts`, new focused context module | Explicit view/draft/filter contexts, qualified references, aliases and consistent link building. |\n| Shell and sidebar | `ui/app.tsx`, `ui/rail.tsx`, `ui/connected-projects.tsx` | Project destinations, scope labels, breadcrumbs and discoverable global tools. |\n| Project and composer | `ui/project.tsx`, `ui/projects.tsx`, `ui/composer.tsx` | Focused overview, direct repository onboarding and one composer path. |\n| Repository tools | `ui/activity.tsx`, `ui/workbench.tsx`, side-panel scope in `ui/state.ts` | Explicit repository context, consistent labels and preserved worktree targets. |\n| Collaboration | `ui/collab-board.tsx`, `ui/collab-state.ts`, `ui/collab-settings.tsx`, `ui/shared.tsx`, `ui/team-connections.tsx` | Board versus People distinction, correct connection scope and role-appropriate views. |\n| Automation | `ui/scheduling.tsx`, `ui/automations.tsx`, `ui/project-teams.tsx` | Explicit project parameters and one configuration home for each schedule or mission. |\n| Connections and settings | `ui/connections.tsx`, `ui/ssh-projects.tsx`, `ui/team-server-wizard.tsx`, `ui/settings-sections.ts`, `ui/preferences.ts` | Computer-oriented language, shared-project setup and consistent search aliases. |\n| Server integration | `src/projects.ts`, `src/server.ts`, `src/collab/connections.ts`, existing access-policy modules | Reuse existing project operations and enforce launch membership for the new explicit UI flow. Add narrowly scoped projections only when required. |\n\nExtract reusable content from existing pages so an embedded project view does not render duplicate headers, pickers or event subscriptions. Hidden tabs should not fetch owner-only data or maintain unnecessary live feeds.\n\n## Implementation sequence\n\n### Phase 1 Establish the design contract\n\nCreate a route and label inventory covering owner, SSH, linked-server and shared-user views. Record the identity source and access requirements of each destination. Build lightweight screen sketches for the empty state, project overview, composer summary, multiple repositories and shared board. Check whether each location answers “Which project?”, “Which repository?” and “Which computer?” when those answers matter.\n\nDeliver the terminology map, route matrix and scenario checklist as the implementation contract. Capture baseline completion time and wrong turns for the usability scenarios below. This is a small design and investigation phase; it should expose any shared-project mapping gaps before route work begins.\n\n**Exit criterion:** every existing destination has a proposed home or compatibility entry, and the first-run flow requires no separate workspace or team setup.\n\n### Phase 2 Implement explicit context and route compatibility\n\nAdd the context module and project sections. Separate global list filters from composer destination state. Add project/source-qualified draft and repository preferences with a versioned migration from existing browser state. Preserve the existing per-computer namespaces.\n\nWire route resolution, late-response guards, deep links and back/forward behavior before restructuring pages. Use the existing project APIs for explicit membership changes. The new project-bound UI launch path should request strict membership validation; retain deliberate compatibility behavior for existing automation and API clients.\n\n**Exit criterion:** routing and selection tests show that a project, repository or connection switch cannot redirect a launch, leak a draft or overwrite a more recent selection. Old bookmarks continue to resolve.\n\n### Phase 3 Deliver the owner project experience\n\nImplement the focused overview, Runs and Repositories destinations. Reuse existing result rows and tools. Add Open repository onboarding and simplify the single-repository composer. Update sidebar entry points, breadcrumbs, repository changes and the compact execution summary together.\n\nKeep cross-project Inbox available. Move global Scheduling only when its project destination and command-search entry are working. Preserve keyboard shortcuts and recent-run navigation.\n\n**Exit criterion:** a new local user can open a repository, launch a fake-agent run and review its result through one project; an existing user can recover drafts, archives and repository tools without losing context.\n\nThis is the first useful release slice, dependent on Phases 1 and 2. It can ship before the collaboration restructuring.\n\n### Phase 4 Integrate collaboration with correct source identity\n\nSeparate Board from People and embed authorized project views. Reuse existing account administration, grants and board-task operations. Add explicit collaboration-source handling to navigation, requests and subscriptions. Preserve the connection-scoped board when no reliable local-project relationship exists.\n\nAdapt shared dashboards using their existing safe data projections; do not embed the owner project page with controls merely hidden. Test empty shared projects that have repository keys but no server-side local clone. Expose existing terminal or host-execution grants only to their authorized recipients.\n\n**Exit criterion:** two servers with identical project or task IDs remain distinct, permission changes remove access promptly, and selecting another board cannot redirect an existing task execution.\n\n### Phase 5 Complete project automation and discoverability\n\nScope Scheduling, backlogs and missions explicitly to the project and repository. Surface actionable outcomes in Overview and Inbox using the existing evidence and approval rules. Align settings search, command search, connection setup, errors and help text with the terminology contract.\n\nApply predictable visibility: hide unused specialized controls initially, retain access through More or search, and surface already-configured features even when an old optional-navigation preference is off. Authorization decides availability; a preference decides presentation only.\n\n**Exit criterion:** users can find and manage existing schedules, missions, shared work and connection settings from their expected project or app location. All first-run instructions match the implemented navigation.\n\n### Phase 6 Validate and roll out\n\nRun the relevant behavioral and browser suites, then the full required checks for the final code change. Conduct the usability scenarios and inspect narrow screens, light/dark themes and keyboard/screen-reader behavior. Refresh documentation and screenshots after the routes stabilize.\n\nUse a temporary navigation rollout switch if the implementation needs staged exposure. Both paths should use compatible storage so reverting navigation does not require restoring user data. Remove the temporary branch after validation rather than maintaining two permanent information architectures.\n\n**Exit criterion:** the scenario matrix passes, no unexplained source or destination switches occur, and pilot users can find previously available capabilities.\n\nPhases 2 through 5 are substantial UI and integration work. Treat collaboration as the highest-uncertainty portion. Estimate delivery after Phase 1 resolves source mapping and draft migration; a simple label-change estimate would understate the scope.\n\n## Migration and compatibility\n\nPreserve project IDs, repository membership, run logs, board tasks, shared repository keys, account roles, automation records and managed mission history. Introduce browser preference versions and additive route aliases rather than rewriting durable execution history.\n\nFor legacy browser drafts, migrate once into a valid source/project destination when unambiguous. Otherwise retain them as a recoverable unassigned draft and ask for a destination when opened. Keep the original recovery data until migration is verified. A failed migration must not create a project or start work.\n\nKeep the existing semantics for historical runs whose projects were deleted or whose membership was inferred. Show them under Unassigned and retain review/archive access allowed by current rules. New explicit selection behavior does not justify silently regrouping old runs or sharing previously private results.\n\nDo not merge projects by name, repository basename, Git remote URL or matching IDs across computers. Those can describe related work without establishing common identity or access. Repository membership changes may affect shared access, so expose their real consequences in the project settings action.\n\nThe initial scope excludes a global project database, automatic synchronization of clones, a merged run/board-task data model, cross-server Inbox federation, new permission levels and changes to execution engines. Each would need its own design and validation.\n\n## Validation plan\n\n| Scenario | Required result |\n| --- | --- |\n| New local installation, one repository | Open repository leads to a named project and ready composer with no team/workspace configuration. |\n| Returning user with an unsent draft | Navigation and reload preserve the draft, its attachments and destination. |\n| One repository in multiple projects | An explicit project selection wins; ambiguous onboarding asks the user to choose. |\n| Multiple repositories with delayed API responses | The last valid selection controls checks, defaults, tools and launch; old responses cannot win. |\n| Empty project or removed repository | Helpful setup/recovery state; no launch using a stale repository. |\n| Same project name and path on two computers | Requests, drafts, terminals and file recovery remain isolated. |\n| Disconnected SSH computer | Reconnect the intended computer; never substitute local execution. |\n| Local project with a delegated worker | The project computer and actual execution worker are both clear. |\n| Multiple collaboration servers with colliding IDs | Correct board, task, membership and inbox scope for each connection. |\n| Viewer, reviewer, contributor and maintainer | Only authorized projections and actions are available; server enforcement still rejects forged requests. |\n| Revoked or expired shared access | Stale views/subscriptions cease exposing data and present a recoverable access state. |\n| Shared board without a server clone | Board/review workflows work; local clone mapping appears when needed for execution. |\n| Existing schedules and managed missions | Same scope, budgets, controls and result links; mission readiness/approval rules remain intact. |\n| Old bookmarks, encoded branches and browser history | Correct destination and source context, or a clear recoverable invalid-link state. |\n| Pinned side panel or selected run worktree | Actual panel/tool target stays visible and cannot be mistaken for the composer repository. |\n| Deleted project and archived runs | Existing history remains discoverable with correct access and ownership semantics. |\n\nExtend behavioral coverage around `test/projects.test.ts`, `test/repo-selection-ui.test.ts`, `test/workspace-routing.test.ts`, `test/team-connections.test.ts`, `test/team-connection-ui.test.ts` and `test/shared-access.test.ts`. Add focused route/context tests where existing tests do not exercise the new behavior. Retain checkout-namespace and workspace-activity regressions when touching tool targeting.\n\nExercise browser flows in the existing simplification, connections, SSH projects, shared projects, team, project missions, workspace, settings and accessibility suites. Check visible results and request destinations rather than snapshotting implementation strings. Run `npm run typecheck`, the relevant targeted suites and documentation checks as changes land; run the full repository-required test set before release.\n\nFor usability, pilot with a small mix of new solo users, remote users and collaborators. Ask them to start a run, switch repository, identify the execution computer, find a result, open a shared board and locate access settings. Record completion without assistance, wrong turns, time to start and incorrect destination assumptions. Compare the same tasks against the current UI.\n\nProposed pilot targets are at least four of five participants completing the primary start-and-review flow without assistance, and no incorrect computer or repository assumption at the launch checkpoint. These are initial decision thresholds, not statistical proof. Any wrong-target launch or lost draft blocks release regardless of the aggregate score.\n\n## Principal risks and controls\n\n| Risk | Control |\n| --- | --- |\n| Project becomes another mandatory setup burden | Open repository suggests and creates the project through a short flow. |\n| Simplification hides execution location | Persistent computer context and a concrete pre-launch summary. |\n| Renaming People leaves the board hard to find | Dedicated Board destination, contextual links and legacy search terms. |\n| Shared and local projects look falsely unified | Qualified source identities and explicit relationships; retain connection-scoped access where needed. |\n| Useful tools disappear into menus | Stable project destinations, searchable aliases and task-based discoverability tests. |\n| Context refactoring loses drafts or misapplies defaults | Versioned migration, preserved recovery data and async-switch regression tests. |\n| Shared views expose owner data | Reuse shared projections and enforce server permissions; do not rely on hidden controls. |\n| A large rewrite delays the basic improvement | Ship the owner project flow after Phase 3, then integrate collaboration and automation. |\n\nThe recommended first implementation is the explicit context and owner project flow: one clear project destination, one composer path, predictable repository selection and visible computer scope. That creates the foundation the remaining simplification depends on.\n","title":"Project navigation review and implementation plan"},{"group":"Engineering records","id":"dynamic-teams-evaluation","source":"docs/dynamic-teams-evaluation.md","body":"# Dynamic teams: implementation evidence and rollout decision\n\n> Engineering record. Status, measurements and decisions below describe the recorded implementation or review. For current usage, start with the [documentation library](README.md).\n\nRecorded 17 October 2026 for the P0–P8 implementation on top of `main` at `8cb364143716a3feda3edd12e7fa0a310c92ecf5`. The implementation is described in the [plan](dynamic-teams-implementation-plan.md) and [Auto team guide](auto-team.md). The [machine-readable evidence](evaluations/dynamic-teams-2026-10-17.json) preserves the trial reports, limitations, assessment and explicit rollout decision.\n\n**Decision: keep Auto experimental and opt-in.** All implementation packages are delivered. The evidence supports exercising the feature with bounded budgets; it does not support replacing Supervisor by default. Earlier unsuccessful trials are retained. A successful final trial does not erase them.\n\n**Provenance of this evidence.** The JSON artifact was produced from an uncommitted working tree (`implementation.state`) by an earlier version of the runner than the committed one: its reports carry `pairId: \"pair-1\"` and, for pair A, `caseId: sum-numbers-v1`, where the committed runner writes a batch-scoped pair ID and the v2 case, and pair C predates the worker-turn attribution fix. The source runs and journals are not committed. The real numbers are therefore historical measurements of the development tree and cannot be regenerated from this repository; a fresh `--fake` run reproduces the 5/2 and 4/1 call counts, and only a new `--real --confirm` batch can produce reproducible real numbers. Per-trial model identity was not recorded (the runner passes provider defaults), and the matched Supervisor runs were pinned to two subtasks, so their 25% coordination share is by construction rather than observed.\n\n## Verification\n\n- Full automated suite: 397 tests passed at the time of the trials (416 after the post-delivery review below, 420 after [review 1](dynamic-teams-implementation-review-1.md)), including recovery, storage faults, mailbox ordering, immediate replies, identity isolation, memory trust, live capabilities, legacy compatibility and rollout controls.\n- Browser suite: 17 tests passed, including Auto approval, blockers, inspection and an in-flight goal revision that stops the old team and requires the configured approval for its child.\n- After final trust/diagnostic adjustments: 11 focused composition and rollout-engine tests passed.\n- After correcting worker-turn attribution: 8 evaluation tests passed. Worker answer and repair turns count even without task IDs; coordinator/reviewer turns do not.\n- Typecheck, UI build and `git diff --check` passed. Validation ran on Node `v22.14.0`; the existing Node 20/22 CI matrix is retained, but this report does not claim a separate local Node 20 run.\n\nThe asynchronous scheduling regression runs a fast dependency, its successor and an unrelated slow worker. It asserts that the successor starts before the slow worker ends. A separate saved-wave compatibility case asserts the old barrier, and restart tests preserve recorded completion ordering. These fixtures demonstrate scheduling behavior, not a real-provider speedup.\n\n## Real task comparisons\n\nEach pair used the same generated repository, base commit, task and verification command: implement `sumNumbers(values)` with finite-number filtering, non-array rejection, empty-input handling, input preservation and `node:test` coverage. Runs used isolated storage and repositories, official signed-in CLIs and a maximum of 12 invocations. The generated task forbids fetching packages, accessing external services or publishing. Plan approval was explicitly disabled by this unattended evaluation harness; interactive approval is covered by the browser and engine tests.\n\nThe CLIs were Claude Code `2.1.302` and Codex `0.162.1`. Pair A used Claude as coordinator; pairs B/C used Codex. These are development trials of one small task, with code fixes between them, not independent samples of the final implementation or a representative benchmark. They were not sequential: the report timestamps show pair B starting before pair A finished, and between 12:44:56 and 12:46:06 UTC three real runs (A supervisor, B supervisor, C auto) ran at the same time on one machine, so the elapsed times in the table were measured under shared CPU, network and rate-limit contention.\n\n| Pair | Pattern | Result | Calls / coordination | Worker turns | Elapsed | Selected memory | Journal bytes / load |\n|---|---|---|---:|---:|---:|---:|---:|\n| A | auto-team | stopped; unverified | 3 / 2 (67%) | 1 | 300.48 s | 0 | 38,298 / 2.95 ms |\n| A | supervisor | done; verified | 4 / 1 (25%) | 2 | 216.60 s | 0 | 23,817 / 3.53 ms |\n| B | auto-team | failed; unverified | 1 / 1 (100%) | 0 | 5.55 s | 0 | 2,538 / 0.34 ms |\n| B | supervisor | done; verified | 4 / 1 (25%) | 2 | 233.43 s | 0 | 20,345 / 3.37 ms |\n| C | auto-team | done; verified | 5 / 3 (60%) | 1 | 294.59 s | 1 | 54,147 / 0.87 ms |\n| C | supervisor | done; verified | 4 / 1 (25%) | 2 | 259.62 s | 0 | 20,789 / 1.46 ms |\n\nPair A exhausted its five-minute limit after a plan validation retry and implementation. Its initial IDs did not satisfy the lowercase identifier policy. The repaired implementation did not reach independent review before the deadline, so the runtime correctly reported an unverified stopped result. Prompts and strict schemas now communicate the identifier rules directly.\n\nPair B exposed a real Codex schema incompatibility before worker dispatch: strict object schemas must include every property in `required`, including `subplans`. Wire schemas now satisfy that restriction recursively, with an explicit regression test. A repository-path canonicalization defect also prevented the synthetic memory seed from being selected; it was fixed and tested before pair C.\n\nPair C exercised the corrected strict schemas and selected one seeded project memory. Its first plan unnecessarily assigned a task to the independent reviewer. The runtime rejected it, and a planning retry assigned the verification task to the coordinator; the runtime still performed its own independent final review. The final prompt and rejection diagnostic now tell planners to omit that redundant final-review task. Pair C therefore includes this observed extra coordination and predates the final wording improvement; no reduced overhead is claimed for that change.\n\nThe verified Auto result covered all five structured requirements, passed checks and independent review, and passed the fail-to-pass proof. Reported elapsed time includes planning and verification. Coordinator fractions count actual CLI launches, including planning repair, rather than only logical steps. Provider-reported tokens/costs can be partial; API-equivalent costs are not subscription charges and are not a complete cross-provider price comparison.\n\nNone of these real Auto runs needed peer information. They therefore supply no useful estimate of real peer-question frequency or asynchronous latency gains. The final successful Auto run used one worker; its 60% coordinator fraction is not a multi-worker overhead measurement. The 20-Auto-run, 100-worker-turn and 20-matched-pair default-rollout targets have not been met.\n\n## Live bridge evidence\n\nBoth adapters were exercised against the scoped local MCP bridge using a real CLI invocation. The server returned a unique test response only after an authenticated `read_inbox` call; the model's final response matched it.\n\n| Provider | Transport | Elapsed | Accepted callbacks | Result |\n|---|---|---:|---:|---|\n| Claude Code 2.1.302, Haiku | stream-json with MCP configuration | 8.630 s | 1 | Correct member and response |\n| Codex 0.162.1 | app-server, read-only | 13.623 s | 1 | Correct member and response |\n\nClaude first supplied an unsupported `input` field; schema validation rejected it and the model corrected the call. Codex initially showed that per-tool approval configuration was required even with a noninteractive approval policy. Its adapter now explicitly permits only the bounded collaboration/memory tool names; source-write and other tool permissions are unchanged. These successful smoke checks prove transport and identity plumbing, not complete real-provider coverage of every collaboration operation. Fake adapter and engine tests cover authentication, scope, revocation, durable writes, steering fallback and replay. The Claude summary was reconstructed from its retained successful tool output after the later Codex smoke overwrote the temporary combined report; that capture provenance is included in the JSON artifact.\n\nUnsupported or unproven capabilities remain visible in the capability inventory. Structured outcomes remain the fallback. Vendor-native nested-agent usage cannot be counted when the CLI does not expose it; the implementation does not invent that usage.\n\n## Deterministic workflow and memory evidence\n\nThe labeled fake comparison completed and verified both patterns. Auto used five calls, two coordinator calls, one blocking peer question and two worker turns. The question was answered after 796 ms; the observed post-answer delay was 226 ms. That delay is the event scheduler's own dispatch latency after an answer arrives (every trial ran event-driven, so there was no wave barrier to avoid); it is not a measured speedup. Supervisor used four calls and one coordinator call.\n\nAuto selected one synthetic accepted project memory about dependency-free `node:test`; Supervisor selected none through the new memory layer. This tests retrieval and attribution, not whether a learned memory helps a human. The real pair C selected the same kind of seed. Human usefulness/staleness feedback was zero in these trials. The application supports revision-specific useful/irrelevant/stale/incorrect feedback, exclusion of stale or incorrect revisions, and bounded relevance adjustments.\n\nSeparate memory tests verify later-run reuse of an eligible lesson, member-private isolation, exact source revisions, locked-down and inherited-source promotion restrictions, branch applicability, deletion and session-independent reconstruction. Untrusted material remains attributable and cannot become a future project instruction through an automatic summary or follow-up.\n\nThe fake Auto journal held 17,594 bytes / 58 records and loaded in 19.11 ms; Supervisor held 5,030 bytes / 13 records and loaded in 6.44 ms. Real journal measurements are in the table and JSON artifact. These are single local **journal-load** timings, not full process-restart benchmarks. Restart behavior is independently tested. The measurements provide no reason to add a database, leases or an outbox; fsynced logical envelopes and deterministic replay remain the storage decision.\n\n## Reproduction and future rollout\n\n### Implementation review after initial delivery\n\nA second review covered the complete implementation and fixed the following issues before commit:\n\n- Ready-work selection now filters unavailable and exhausted tasks before packing non-overlapping work. Live and replay scheduler waits respond to cancellation even without a deadline. Retried event-driven tasks merge the captured input commit, so a later peer commit cannot silently change their inputs.\n- Coordinator revisions validate the combined contract set against the existing bounds. A smaller explicit invocation ceiling constrains planning and review reservations; refused retries do not create false launch records. Rejected outcomes update both provider and member failure counts.\n- Provider-addressed follow-ups cannot resume a team's private member session. Direct replies retain their own subsequent conversation and can use a one-call budget; new teams still require at least four calls.\n- Live memory reads honor task paths, search text and entry limits, keeping returned entries and recorded provenance consistent. Whole-repository `*` assignments can retrieve applicable path-scoped memory. Valid namespaced operation IDs remain within storage limits.\n- Malformed JSON-RPC envelopes no longer crash the MCP client.\n- Interrupting the evaluation runner stops the whole batch, including a signal during run preparation. Separate batches use unique pair IDs. Beta eligibility requires the verified Auto result to belong to the matched comparison, and multi-worker measurements count workers that actually executed.\n- The capability panel clears stale errors when reopened after a successful retry.\n- The browser test waits for the active run's waiting status after its question appears; those are separate scheduler snapshots, which exposed a timing race on Linux CI.\n\nEvery fix has regression coverage. Final post-review validation is recorded below after the complete test and browser runs. The real measurements above remain the original immutable development-trial evidence; this review did not relabel or overwrite those trials.\n\n- `npm test`: 416 passed, zero failures or skips.\n- `npm run test:e2e`: 17 passed, including direct-follow-up and new-team budget checks.\n- `npm run typecheck` and `npm run build`: passed.\n- Final contract/model regressions: 10 passed after the last contract-bound correction.\n- `git diff --cached --check`: passed.\n\n### Commands\n\n```sh\nnpm test\nnpm run test:e2e\nnpm run typecheck\nnpm run build\nnpm run evaluate:teams -- --fake --out /tmp/codeflow-team-fixture\nnpm run evaluate:teams -- --real --confirm --runs 1 --providers codex,claude --max-steps 12 --max-minutes 8 --out /tmp/codeflow-team-real\n```\n\nUse a fresh output directory for each invocation. `--real` consumes signed-in CLI quota; ordinary tests use fakes. The runner saves immutable measurement reports and source runs under its isolated directory. Optional `--register-home DIR` copies measurement reports into another installation without copying transcripts or changing rollout. Generated fixtures and private CLI transcripts are not committed with this evidence.\n\nThe recorded decision selects all three real development comparisons, including the failures, and stays experimental. It was written into an isolated evaluation store and copied into the evidence artifact; the user's normal app configuration was not changed. The app starts at experimental in the absence of a separate explicit decision. Beta/default changes require a written rationale and qualifying real reports through the rollout API/UI; fake runs cannot qualify them, and since review 1 eligibility is judged over every real report in the store, not only the cited ones. The stage is a recorded label shown in Insights and used as a composer default; the only switch that changes whether Auto teams can start is `CODEFLOW_AUTO_TEAM_ENABLED`. More representative tasks, verified multi-worker runs, meaningful memory feedback and lower observed coordination overhead are needed before promotion.\n\n[Documentation library](README.md) · [Development guide](architecture.md)\n","title":"Dynamic teams: implementation evidence and rollout decision"},{"group":"Engineering records","id":"dynamic-teams-implementation-plan","source":"docs/dynamic-teams-implementation-plan.md","body":"# Dynamic teams, workflows, messaging, and memory\n\n> Engineering plan and record. Revision 4 below records the implementation scope and remaining extensions; the revision 3 material that follows records the delivered baseline. For current usage, start with the [documentation library](README.md).\n\nStatus: **revision 4 core implemented**, updated 21 October 2026. Dynamic Auto is the fresh-session default, Solo is directly accessible, fixed workflows and versioned templates live in Advanced, and all new runs resolve a shared permission policy at every agent launch. The revision 3 scheduler and live-tool bridge remain the execution foundation. See the [user guide](auto-team.md) and [measured evidence and rollout decision](dynamic-teams-evaluation.md).\n\n## Revision 4 — dynamic Auto, Solo, and reusable templates\n\nThis revision supersedes the earlier opt-in product/default decision and the proposal to make template selection the main Auto behavior. It extends the existing dynamic engine; it does not restart P0–P8. Historical measurements and implementation claims below remain unchanged.\n\n### Delivered scope and explicit limits\n\n- Added closed, versioned task-graph/fixed-workflow template definitions, an atomic server library with immutable history, project/personal scope, migration of browser presets, import/export, JSON editing with execution preview, and save-from-run. Auto may compose enabled task graphs; manually selected definitions are mandatory and pinned across recovery.\n- Moved fixed workflows into Advanced without replacing their executors or weakening test-first, race, council, loop, or review behavior. Typed composition of those fixed executors remains a future extension; only validated task graphs are currently Auto-compatible.\n- Added a shared per-launch policy resolver, recorded effective permissions, narrower turn access, provider eligibility checks, explicit user policy revisions, and inherited mandatory restrictions for child runs. All engine-created runs use the policy, including background and collaborative starts; historical saved runs without a policy retain their existing behavior.\n- Controlled launches currently support Claude/Codex. Unsupported provider capabilities fail closed. This is not an identical cross-provider sandbox: filesystem read isolation, general browser/MCP allowlists, and a unified sandbox for setup/check/preview processes remain outside this implementation. Real-provider enforcement acceptance is still required before claiming equivalent isolation; deterministic adapter tests only verify configuration and orchestration.\n- Auto retains its existing independent verification model. Coordinator-only investigations, typed artifact/control-flow composition, automatic template-upgrade proposals, and a visual graph designer remain future extensions. The initial designer uses validated JSON and a readable task preview.\n- The application default is decoupled from historical quality-evaluation results. Existing explicit mode choices and the Auto disable switch remain intact; no evaluation evidence is synthesized by this change.\n\nThe D0–D6 entries below describe the full design, including these remaining extensions. They should not be read as a claim that every listed extension or release acceptance probe has been completed.\n\nInitial implementation validation on 21 October 2026: typecheck and production build passed; 29 focused policy/template/adapter/composer checks and 11 browser scenarios passed. The full regression run reported 1,208 passes, eight skips, and two compatibility failures; both failures were corrected and passed in focused reruns. The full suite was not rerun after those fixes. Browser and adapter scenarios use deterministic CLIs, not live-provider sandbox enforcement probes.\n\n### Product decisions\n\n| Surface | Intended behavior |\n|---|---|\n| **Auto — default** | The coordinator investigates the goal, builds an appropriate plan, delegates useful work, and adapts to results. It can start without a template, reuse one, or adapt/combine compatible templates. |\n| **Solo — directly accessible** | One execution agent handles the task; delegation is disabled. Keep it beside Auto in the composer, without opening Advanced. |\n| **Advanced — templates and configuration** | Users choose existing patterns as reusable templates, design their own, edit versions, and configure roles, dependencies, provider/model preferences, and required checks. |\n| **Reasoning and resource controls** | Model effort, concurrency, time, invocation, and spending limits remain separate from execution mode. Do not introduce another Ultra mode in this revision. |\n\nAuto is allowed to use the smallest useful execution plan. An investigation may stay with the coordinator; a small code change may need one implementation worker and the verification required by the run. Do not create multiple implementation workers merely because Auto is selected. Required independent review remains independent; choosing Solo must never silently start a reviewer agent. If a selected policy requires another agent, explain that incompatibility before execution.\n\nCode Flow owns scheduling, workspaces, communication, recovery, permissions, budgets, and verification across providers. The coordinator proposes work and revisions through validated records. Use the existing declarative plan and event scheduler; arbitrary generated orchestration scripts and untracked provider-native teams are outside this revision.\n\n### Current foundations and gaps\n\n| Existing foundation | Revision 4 work |\n|---|---|\n| `src/patterns/auto-team.ts`, `auto-team-model.ts`, `src/teams.ts`, and `src/team-scheduler.ts` already support generated tasks, dependencies, bounded revisions, messaging, and recovery. | Extend this execution path and its contracts. Keep generated tasks usable without any template lookup or match. |\n| `src/patterns/team-strategies.ts` expands three declarative subplans. | Generalize the reusable-strategy contract to versioned templates with explicit inputs, outputs, and preserved guarantees. |\n| Fixed patterns have established execution and verification behavior. | Expose them in Advanced, then share guarded execution primitives so Auto can reuse them without losing those guarantees. |\n| `ui/composer-tools.ts` and `ui/onboarding.tsx` already save personal/project task templates and presets in browser storage. | Add a server-accessible, versioned team-template library and migrate existing entries. Current entries are mainly prompts/settings, not executable team definitions. |\n| `src/engine.ts` passes `params.trust ?? run.trust` to adapters; adapters map those labels differently. | Resolve and validate effective permissions centrally at every launch, including replacements and resumed turns. |\n| `ui/state.ts` defaults to Pipeline; `ui/composer.tsx` remembers a team workflow; evaluation metadata can change the default. | Separate execution-mode preference from template selection and evaluation evidence. Make Auto the application default while preserving explicit user choices. |\n\n### D0 — define run policy and execution contracts\n\n**Outcome:** every execution path derives authority from the run, independently of the selected provider or template.\n\n- Introduce a versioned run-policy record and a common permission resolver at the engine launch boundary. Cover filesystem reads/writes, terminal actions, network access, browser access, MCP tools, approval behavior, execution environment, and delegation where supported. These are separate capabilities: terminal network permission does not automatically grant a browser or connector.\n- Derive effective permissions from the authorized run policy, current project/host restrictions, and any narrower turn access. A coordinator's read-only planning turn must not accidentally make its implementation workers read-only. Conversely, templates, model output, messages, role settings, and provider changes cannot grant additional authority.\n- Treat tool availability separately from permission. Inherit the run's allowed tools where the provider and execution environment support them; route only to eligible providers. Record unsupported or unavailable capabilities explicitly. Do not pass credentials or provider-specific session handles between agents/providers.\n- Have each adapter report the enforcement it can provide. Use existing supported isolation when needed; otherwise reject the affected launch with a concrete explanation. Never interpret matching permission labels as proof of equivalent enforcement. Worktrees alone do not restrict reads, and prompt instructions do not constitute a sandbox.\n- Apply the resolver to planning, workers, review, integration/repair, retries, replacement members, manual templates, and Solo. Audit setup, checks, previews, remote execution, and container paths too: commands outside the native agent sandbox need their own policy enforcement or an explicit run-authorized execution boundary.\n- Persist the policy revision and effective capability summary with each launch. On recovery, retain the authorized policy and revalidate against current restrictions; a revoked permission or weaker replacement provider blocks new affected work. Any explicit user policy change creates a new revision. Reconcile an active process before continuing under changed restrictions.\n- Keep Code Flow as the delegation authority. Disable unmanaged provider-native delegation for controlled runs, including Solo; providers unable to enforce a required delegation restriction are ineligible for that mode.\n\n**Primary code:** `src/engine.ts`, `src/adapters/types.ts`, provider adapters, `src/provider-eligibility.ts`, team-tool grants, execution/container/remote-worker boundaries, and shared policy types.\n\n**Exit evidence:** mixed-provider launches receive the intended effective policy; read-only turns remain narrower; retries and replacements cannot increase access; missing enforcement is surfaced rather than silently downgraded.\n\n### D1 — strengthen dynamic Auto on the existing runtime\n\n**Outcome:** Auto succeeds without a template library and adapts its plan within the run's constraints.\n\n- Keep initial planning and bounded revisions as the core behavior. The coordinator chooses eligible members, tasks, ownership, dependencies, and sequencing from the current goal and observed evidence. Provider/model pins and runtime verification requirements remain authoritative.\n- Preserve task/member/attempt identities and journal accepted revisions before dispatch. Start ready work through the existing scheduler; ask the coordinator for decisions when evidence requires judgment, rather than on every scheduler event.\n- Support the smallest useful plan, including coordinator-only investigations where allowed. Keep independent verification for code changes and reserve its budget before admitting additional work. Make task-kind changes explicit so an investigation cannot become an unauthorized implementation task.\n- Reassign unfinished work through a checkpoint/handover, without transferring a provider's native session to another provider. Validate file ownership, serialize conflicting writers, and bind artifact dependencies to recorded commits.\n- Preserve completed work when replanning. Invalidate only evidence affected by changed inputs/contracts and require fresh checks before reporting success. Bound expansion, repair, retries, and coordination overhead using the existing run limits.\n- Expose the accepted plan, decision rationale, active workers, revisions, blockers, and remaining budget in the run view. Permission changes are runtime/user decisions, never coordinator decisions.\n\n**Primary code:** `src/patterns/auto-team.ts`, `src/patterns/auto-team-model.ts`, `src/teams.ts`, `src/team-scheduler.ts`, `src/workflow-control.ts`, and `ui/team.tsx`.\n\n**Exit evidence:** an empty template catalog does not prevent a small task or a parallel feature task from completing; a worker failure causes a bounded revision and recovery without replaying completed work unnecessarily.\n\n### D2 — versioned templates using the same execution contracts\n\n**Outcome:** users can save reusable team behavior, and the server can validate it before execution.\n\n- Define a shared schema for identity/version, scope, description/applicability, input parameters, role slots, preferred providers/models, tasks/dependencies, ownership, input/output artifacts, required checks, immutable guarantees, and permitted adaptations. Keep the existing starter-prompt catalog distinct from executable team templates.\n- Add a durable server-side store/API for built-in, personal, and project templates, with existing account/project access rules, version-conflict checks, and bounded import/export. Built-ins are immutable; users create their own versions or copies.\n- Normalize a generated plan and an instantiated template into the same validated execution records. Use typed, bounded control primitives for behavior that a simple task DAG cannot express, such as candidate selection and repair loops. Avoid a separate untracked execution engine.\n- Pin the template version, definition snapshot, resolved inputs, expansion, and adaptation patch in each run. Editing or removing a library entry must not change an existing or resumed run.\n- Templates can declare required capabilities and suggested resource limits; they cannot grant permissions, weaken required verification, or enlarge the authorized budget. Exclude credentials, auth-volume bindings, session IDs, old results, private transcripts, and machine-specific workspace paths from reusable definitions.\n- Migrate existing browser libraries idempotently and retain a recoverable copy. Preserve names, task inputs, criteria, role preferences, and supported workflow options. Stop applying saved trust/tool-approval settings or disabling current isolation. Translate unsupported settings into visible migration notes rather than pretending they are enforced.\n- Default user/imported templates to manual use until enabled for Auto. Supply the coordinator only the enabled, accessible, compatible catalog; a template's text is source material, not authority.\n\n**Primary code:** new shared template schema/store/API modules; `src/teams.ts`, `src/server.ts`, `src/shared-access.ts`, `ui/composer-tools.ts`, and `ui/onboarding.tsx`.\n\n**Exit evidence:** a template selected on another client resolves to the same immutable definition; importing or applying a template cannot change the run's permissions; existing browser libraries migrate without duplicate entries or lost originals.\n\n### D3 — convert existing patterns into reusable templates\n\n**Outcome:** familiar workflows remain available in Advanced with their original behavior.\n\nInitially register built-in template definitions that invoke the existing executors. Preserve legacy pattern IDs and replay identities for saved runs, automations, API clients, and extensions. Extract shared execution primitives incrementally; do not reimplement a pattern as a loose sequence of prompts.\n\n| Built-in template | Guarantees that must survive reuse |\n|---|---|\n| Plan / Build / Review | Plan checkpoint, implementation, required checks, independent review, and bounded fixes. |\n| Supervisor / Workers | Decomposition contracts, separate writer workspaces, integration, conflict handling, and final verification. |\n| Test first | Failing baseline evidence, protected tests, implementation, and passing verification. |\n| Iterative loop | Bounded iteration, progress/regression checks, and the existing rollback behavior. |\n| Candidate race | Isolated candidates, anonymized evaluation, deterministic recorded selection inputs, and required checks. |\n| Debate | Recorded proposals/decision, optional implementation, and verification appropriate to the selected behavior. |\n| Review council | Read-only source review, independent findings/reproductions, and evidence-preserving synthesis. |\n\nFor Auto composition, each reusable executor needs explicit input/output commits or artifacts, unique operation namespaces, and a scoped result. It shares the parent run's permissions, budgets, cancellation, and history, and cannot overwrite the parent's final verdict or reset counters. Prevent recursive template cycles and bound expansion. Mark Auto compatibility only after these guarantees are tested; Advanced availability alone does not establish composability.\n\n**Primary code:** `src/patterns/index.ts`, existing pattern modules, `src/patterns/team-strategies.ts`, and engine execution-context helpers.\n\n**Exit evidence:** parity fixtures pass for every built-in pattern; each template advertised for Auto composition preserves its guarantees under the parent run's limits and recovery behavior.\n\n### D4 — optional reuse, adaptation, and saving from Auto\n\n**Outcome:** Auto can build fresh, reuse, or combine compatible templates without being required to choose one.\n\n- Give the coordinator a compact eligible catalog with applicability, inputs/outputs, required capabilities, estimated overhead where known, and adaptation limits. Preserve direct task generation as an equally valid path.\n- Accept a versioned template reference, inputs, role assignments, and bounded patch. Resolve and expand it on the server, then validate it alongside generated tasks against the same requirements, permissions, ownership, verification rules, and budgets.\n- Show which work was generated, reused, or adapted and why. Validate composition boundaries explicitly; reject cycles, incompatible artifacts, invalid role assignments, and attempts to remove required checks. An unavailable optional template can lead to a fresh valid plan; an explicitly selected mandatory template produces a visible blocker.\n- Distinguish an in-run adaptation from a library upgrade. Adapting the current plan does not mutate its source template. An agent may propose an improved version with a comparison and supporting evidence; a user saves/publishes it explicitly. Revalidate the new version before enabling it for Auto.\n- Add “Save as template” for a completed run or selected reusable portion. Preview parameterized inputs, roles, steps, and guarantees before saving. Capture the reusable method, not a transcript, one-off findings, secret values, or a promise that future runs will pass.\n\n**Primary code:** coordinator schemas/prompts, template resolution/validation, `src/patterns/team-strategies.ts`, `ui/run.tsx`, `ui/run-actions.tsx`, and template-library UI.\n\n**Exit evidence:** the same task can use a fresh plan, a template, or an adapted template through the same runtime; saved template versions stay unchanged until explicitly published.\n\n### D5 — composer, Advanced, and default migration\n\n**Outcome:** the primary choice is Auto or Solo; reusable patterns and their design controls live in Advanced.\n\n- Replace the main pattern selector with directly accessible Auto and Solo controls. Put template selection and the template designer in Advanced. Keep the active template name visible in the composer summary when one is selected, so a constrained template run is not mistaken for unrestricted dynamic planning.\n- The Advanced designer supports parameters, roles/provider preferences, task dependencies, artifact contracts, required checks, and supported bounded control primitives. Include validation errors and an expanded-plan preview; support editing, version comparison, copying, and import/export using the same server schema.\n- Keep reasoning effort and resource controls separate. Validate each selected provider's supported settings; do not translate an effort label into additional tool authority or assume provider labels have identical meaning.\n- Default fresh/unconfigured sessions to Auto, and make the Auto control explicitly return to dynamic planning without a mandatory template. Preserve existing explicit Solo/project/workflow choices; map saved fixed-pattern choices to their Advanced template. When old storage cannot distinguish an implicit default from an explicit choice, preserve it and make Auto easy to select.\n- Decouple the application default from the historical evaluation stage. Retain measured evidence, diagnostics, and the new-run disable switch; changing the product default must not fabricate a successful quality evaluation. If Auto is disabled or cannot launch under the current policy, explain the reason and offer Solo or a compatible template without silently changing modes.\n- Preserve configured plan approvals, unattended authorization, external-source restrictions, and legacy automation/API behavior. A default change does not authorize spending, tool access, or an extra approval bypass.\n\n**Primary code:** `ui/composer.tsx`, `ui/state.ts`, `ui/onboarding.tsx`, project defaults, run views, `src/team-evaluation.ts`, and API/automation compatibility paths. Update usage documentation only when the behavior ships.\n\n**Exit evidence:** a new user starts in Auto, switches to Solo with one action, and can design/run a template in Advanced; existing saved choices and automations retain their meaning.\n\n### D6 — acceptance and delivery sequence\n\nImplement **D0 → D1 → D2 → D3 → D4 → D5**, adding focused tests with each change. D6 is the release acceptance pass. The template designer and migration work may be developed once D2's schema is stable, but changing the default follows runtime and permission verification. Do not rebuild the scheduler or storage engine merely to support this revision.\n\n| Acceptance scenario | Required evidence |\n|---|---|\n| Fresh install, no templates | Auto generates and completes an appropriate plan; template lookup is optional. |\n| Small task and Solo | Auto avoids unnecessary parallel workers; explicit Solo launches no delegated agents, including hidden native delegation. |\n| Mixed-provider feature | Eligible Claude/Codex or other workers inherit the run policy, use narrower turn access where required, and complete checks and independent review. |\n| Provider replacement | A replacement preserves the run policy; unsupported mandatory enforcement prevents dispatch instead of weakening restrictions. |\n| Template parity and composition | Advanced and Auto use the same tested template semantics; test-first protection, race isolation, loop bounds, and review-only constraints survive adaptation. |\n| Template migration and isolation | Legacy library import is idempotent; permissions/auth bindings are not restored; personal/project access boundaries hold. |\n| Recovery during execution | Recorded plan/template/policy revisions remain pinned; completed results are retained; current revocations stop affected new work. |\n| Adapt and save | An adapted run can create a new template version; the original and all previously started runs remain unchanged. |\n| Compatibility | Saved runs, explicit preferences, automations, extension patterns, remote execution, and the Auto disable switch retain documented behavior. |\n\nUse focused tests in the existing Auto/team scheduler/replay/strategy suites, adapter and approval tests, composer/library tests, and browser flows. Add bounded real-provider checks for enforcement claims; fake-agent tests verify orchestration but cannot establish sandbox equivalence. Run typecheck, relevant tests, the build, and documentation/link checks appropriate to the implementation. Report unavailable provider checks as unverified.\n\nThe complete release demonstration starts in default Auto, builds a plan without requiring a template, optionally reuses an enabled template, runs workers across providers under the run policy, recovers from interruption, and delivers verified results. The user can then save the reusable strategy, inspect it in Advanced, or start the next task in Solo.\n\nCritical review fixes (21 October 2026):\n\n- Removed the engine startup opt-out, inherited parent trust/approval ceilings and execution boundaries, and sent the full validated policy to remote workers.\n- Revalidated installed native controls on every launch; rejected unavailable approval transports and strict-boundary/approval combinations. Codex overrides ambient writable roots, temporary write exemptions, approval policy and network settings. Claude explicitly disables Chrome when external tools are forbidden.\n- Serialized follow-up permission mutations, validated recovered policy records, and preserved mandatory requirements across revisions and child runs.\n- Preserved template dependency types and global acceptance criteria; template revisions cannot replace original instructions or widen owned paths.\n- Updated built-in provider defaults to Claude/Codex. New runs using unverified Antigravity, ACP or extension adapters are rejected; legacy adapter-level compatibility remains separately tested.\n\nThese checks establish adapter behavior using deterministic CLIs and local capability probes. They do not establish equivalent operating-system enforcement across providers, or disable arbitrary subprocesses launched through an authorized shell.\n\nReview validation: the full regression run passed 1,218 tests with eight skips and no failures. All 11 targeted browser scenarios passed, along with 28 focused inheritance/recovery/template tests. The final adapter and approval refinements passed nine adapter tests and the focused approval test. Typecheck, production build, and diff whitespace checks passed. Live-provider sandbox enforcement was unverified at that checkpoint; the 2026-10-21 report below records the subsequent bounded live probes.\n\n## Revision 3 baseline and historical implementation record\n\nPrepared 17 October 2026 against repository revision `ee246c7`. Revised against [Review 0](dynamic-teams-plan-review-0.md). Phase numbers below supersede the original ordering; the review remains a record of the original proposal.\n\nImplementation is based on `main` at `8cb3641`, including its checked review findings and fail-to-pass proofs. Sections describing the original repository baseline below are historical design context, not claims about the updated code.\n\n### Implementation status\n\n| Work package | Delivered |\n|---|---|\n| P0 + Slice 0 | Opt-in Auto team, strict provider-compatible schemas, composition rules, approval, independent members, capability inventory, verification and launch budgets |\n| P1 | Fail-closed fsynced journal, incomplete-tail recovery, deterministic completion/command replay, crash recovery, SSE restart reconciliation and measured replay bytes/time |\n| P2 | Member/session/settings/usage identity, isolated writers, typed knowledge/artifact/completion dependencies, integration SHA inputs, stale-attempt rejection and bounded replacement |\n| P3 | Private checkpoints, team proposals/contracts/findings, attributable project retrieval/promotion, inspector/CAS/retention/deletion, exact-revision relevance feedback and fixture/real-run retrieval measurements |\n| P4 | Event-driven ready-work dispatch, atomic mailbox commands/receipts, request/wait/deadline state, idle answer turns, immediate/duplicate replies, cycle/unavailable escalation and replayed completion batches |\n| P5 | Bounded expansion/retirement, safe handovers, compact coordinator checkpoints, declarative reusable subplans, affected-evidence invalidation, shared Supervisor decomposition/integration primitives and saved-wave compatibility |\n| P6 | Scoped Claude/Codex MCP bridge, expiring member credentials, server-derived authority, durable inbox/memory tools, agent-origin live steering with queued fallback, explicit capability/nested-agent limits and real CLI smoke checks |\n| P7 | Composer and workflow/inbox/context/memory views, approval and blockers, phone/automation integration, bounded follow-ups, explicit in-flight goal revisions, parent-context/source-trust retention and new-run disable switch |\n| P8 | Fault/compatibility/browser tests, isolated fake and real paired evaluation runner, immutable versioned reports, approval/memory/replay/coordination measurements and a recorded rollout decision whose beta/default stages are evidence-gated (the stage is a label and a composer default; availability is controlled by `CODEFLOW_AUTO_TEAM_ENABLED`) |\n\nThe user's instruction to implement all remaining phases explicitly opened P4/P6 on 17 October 2026. Their original sampling gates remain below as historical decision criteria; the implementation does not claim that a 20-run/100-turn latency experiment has been completed. The reproducible no-barrier fixture demonstrates the scheduling order (a dependent starts before an unrelated slow worker ends); it measures no benefit. Real model quality, overhead and default rollout are assessed separately in the evidence report.\n\nThe storage decision is to retain the single-writer JSONL journal. One durable command envelope contains question/wait effects and its receipt; replayed drain batches and completion order provide the required logical atomicity. Disposable projections, rather than an outbox, database, leases or a new runtime baseline, remain sufficient. Node 20/22 CI configuration is preserved.\n\nThe rollout decision is **experimental/opt-in**. Implementing the rollout gate does not force promotion: the initial real comparisons expose bounded-time failures and coordination overhead, so there is no evidence for making Auto the default. Unknown vendor-native usage remains unknown; unsupported adapters retain their structured-outcome path.\n\n## 1. Product promise and delivery decision\n\n> Give code-flow a task; it assembles an appropriate team from your available agents, coordinates their work, and delivers a verified result with reusable project knowledge.\n\nThe user should not need to choose an orchestration pattern. A coordinator investigates the goal, proposes members and a workflow, gets plan approval according to the user's settings, and adapts the work as evidence arrives. Small tasks should produce small teams. Success means satisfying the task with evidence, not creating more agents.\n\n**Build Slice 0 before a storage migration, a new scheduler, or a mailbox service.** It is an opt-in **Auto team** pattern on the existing engine: coordinator planning, plan approval, dependency waves, addressed questions in structured outcomes, bounded revisions, and independent verification. Minimal private checkpoints and team contracts are part of that slice. Reusable project memory follows execution data in Phase 3; Slice 0 does not claim to deliver that part of the promise yet.\n\nThe long-term architecture stays the same: code-flow owns member identities, workflows, communication, and three memory scopes across official vendor CLI sessions. The delivery order changes so real runs determine how much infrastructure is needed.\n\n```mermaid\nflowchart TD\n Goal[User goal and constraints] --> Plan[Coordinator proposes team and tasks]\n Plan --> Policy[Runtime validates composition and limits]\n Policy --> Approval[Approve, revise, or reject under user settings]\n Approval --> Waves[Execute ready tasks; replay saved waves]\n Waves --> Outcomes[Artifacts, checkpoints, findings, questions]\n Outcomes --> Revise[Bounded coordinator revision when needed]\n Revise --> Policy\n Outcomes --> Verify[Integrate, check, independent review]\n Verify --> Result[Result with evidence and unresolved gaps]\n Result --> Promotion[Reviewed project-memory proposals]\n Waves <--> Journal[Existing replay journal]\n Outcomes --> View[Run snapshot and SSE team view]\n```\n\n## 2. What we reuse, and what actually needs changing\n\n| Foundation | Existing behavior | Planned extension |\n|---|---|---|\n| `src/patterns/supervisor.ts` | Read-only decomposition, contracts, approval, parallel workers, integration and review | Generate individual members, execute dependency waves, revise assignments and route questions |\n| `TeamContext.decide()` / `remember()` | Completed decisions and operations replay from the journal | Stable task/wave/attempt identities and deterministic application of recorded revisions |\n| `src/store.ts` | Ordered JSONL writes, a synced replay journal, one-server lock | Surface control-write failures; add batch records/snapshots only where required |\n| `TeamContext.ask()` and adapters | Official CLIs, sessions, structured output, retries, cancellation | Member identity and validated member-specific settings, without replacing adapters |\n| `src/routing.ts`, `src/stats.ts` | Availability/quota signals and repository outcome ranking | Apply these inputs to a validated team composition policy; allow repeated providers |\n| `src/git.ts`, engine worktree/check methods | Isolated worktrees, replay-aware commits and checks | Dependency inputs pinned to integration commits, explicit verification coverage |\n| Notes and Codex steering | User-origin notes addressed by provider; some live input | Member addressing and agent-origin messages kept separate from user instructions |\n| `src/context.ts`, `src/learnings.ts`, `src/projects.ts` | Conventions, attachments, recurring findings, project membership | Scoped checkpoints and attributable memory, with enforced promotion rules |\n| Run snapshot, SSE and composer | Existing pattern selection, approvals and live steps | Auto team choice, roster/tasks/questions and coordination metrics from Slice 0 |\n\n### Recovery correction\n\n`remember()` already journals the result of `decide()`'s underlying agent call. A deterministic pattern replays completed coordinator decisions; it does not ask the model to invent its team again. Dynamic planning therefore does not, by itself, require a new recovery model.\n\n`journalEntry()` calls `appendLine(..., { sync: true })`, and the journal write is awaited before the pattern continues. Ordinary event-log writes do not fsync each event. However, **both paths currently use `appendLine()`, which catches write errors, increments `writeHealth`, and resolves**. The journal has a durability mechanism but does not fail closed when that mechanism fails. Slice 0 should add narrowly scoped error propagation for authoritative journal writes and update the in-memory journal only after successful persistence. This is a small hardening change, not a database migration.\n\nReplay remains appropriate while execution is a bounded sequence of waves and recorded outcomes. Long-lived mailboxes, asynchronous wakeups outside those waves, or expensive replay could justify another driver later. Phase 4 has an evidence gate for that decision.\n\n### Compatibility facts\n\n- `AgentId` is a provider (`claude`, `codex`, `agy`), not an individual worker. Existing role routing deduplicates providers within a role; dynamic member IDs must bypass that deduplication without changing manual-pattern semantics.\n- Node 20 is supported and in CI. Preserve it for this plan. SQLite is not a prerequisite; any later driver proposal must explicitly address Node, packaging and platform support.\n- Resume currently gives a fresh step/time budget. Slice 0 preserves this documented behavior for an explicit user resume, while reporting lifetime usage separately. A replayed wave never resets its recorded wave/repair counters. Internal retries do not silently grant a fresh budget.\n- There are four `findPattern()` call sites in engine/server code. Register `auto-team` normally. Older versions already reject an unknown pattern on resume; no new execution-mode framework is necessary for Slice 0. Future incompatible plan schemas need an explicit version check.\n- SSE reconnect cursors currently apply within one server start. Improving cross-restart reconnect helps legacy runs too and can ship independently.\n\n## 3. Invariants to keep\n\n1. **Agents propose; the runtime validates and verifies.** A coordinator cannot delete user requirements, increase its budget, raise trust, or authorize publishing through a plan revision.\n2. **Individual identity is independent of provider.** Two Codex members have different IDs, context, sessions, assignments and usage attribution.\n3. **One active turn per member/session, one writer per worktree.** Reassignment reconciles the previous process before starting another writer. Never pass a session ID between providers.\n4. **Persist accepted control state before acting on it.** A failed authoritative write stops new dependent dispatch. UI projections are not a second source of truth.\n5. **Replay completed decisions.** Stable identities, recorded inputs and deterministic ordering reproduce accepted plans. Current clock/quota/filesystem state must not silently change an already-recorded branch during replay.\n6. **Messages are not user authorization.** Sender identity and source trust come from code-flow. Ordinary output text is never parsed as executable control commands.\n7. **Waiting releases capacity.** A member needing information ends its turn with a checkpoint; it does not occupy a CLI slot waiting for another member.\n8. **Verification applies to evidence.** Unknown or unrunnable is not a pass. A gap must never become success merely because all runnable tasks ended.\n9. **Project memory has an enforced trust boundary.** A locked-down or otherwise untrusted source cannot automatically create instructions for future runs.\n10. **No exactly-once model-execution claim.** A process can finish external work before its result is stored. Recovery reconciles artifacts and uses idempotent logical operations; transport or execution can repeat.\n\n## 4. Define “appropriate team” before implementation\n\nThese are proposed, testable defaults for the opt-in experiment. Record them in the plan and fixtures, rather than leaving them to a coordinator prompt.\n\n| Rule | Initial policy and validation |\n|---|---|\n| User constraints | Preserve provider/model pins, trust, allowed paths, check commands and budgets. Reject revisions that exceed them. |\n| Availability | Choose installed, usable providers. Reuse `exhausted()` signals, including near-full windows and recent authentication failures. Unknown quota is labeled unknown. |\n| All providers exhausted | Auto team pauses or reports a routing blocker; it does not silently spend against a known exhausted provider. This is stricter than current `resolveAuto()`, which can fall back to exhausted candidates with a warning. Explicit user choices remain visible. |\n| Provider ranking | Use existing repository pass-history ranking among eligible providers, with its existing fallback order. Treat it as a prior with limited evidence, not a universal skill score. |\n| Small teams | Start with a coordinator, one worker and an independent reviewer. Allow a second worker only for tasks whose ownership/dependencies permit useful parallel work, with a stated reason. A read-only task may use the coordinator as its investigator. |\n| Size bounds | Default maximum four active members including coordinator/reviewer, twelve tasks across accepted revisions, four execution waves, and two repair rounds. Reject expansion beyond the approved limits. These are experiment settings, not measured optima. |\n| Review independence | Reviewer is a separate member/session, read-only, and cannot approve work it authored. Prefer a different provider from the primary author; record any same-provider fallback. If the user's policy requires different providers and none is eligible, report the blocker. |\n| Same-provider workers | Allowed when useful. Member IDs, not provider strings or display names, identify tasks and communications. |\n| Models and effort | Choose from the supplied supported/configured settings or the provider default. Do not invent a model name. Explicit user settings win. |\n| Workflow validity | Unique IDs, existing owners, acyclic dependencies, bounded expansion, acceptance criteria for required tasks, and requirement coverage. Reject silent requirement removal. |\n| Ownership | Concurrent tasks need compatible declared ownership; actual changed paths are checked before integration. Shared-file work is ordered or assigned to an integration task. |\n| Task kind | Implementation requires applicable checks/review; investigation/design can succeed with an accepted evidence-bearing artifact and no code changes. |\n\nThe first plan includes stable requirement IDs, editable acceptance criteria, members and rationale, provider/model/effort settings, tasks/dependencies, ownership, contracts, verification requirements, and a turn estimate with a hard cap. Estimates must distinguish known bounds from uncertain token use.\n\n### Approval and revision policy\n\nUse the existing `ctx.approve(\"plan\", ...)` checkpoint before dispatching workers. Show the proposed team, settings, tasks, acceptance criteria, constraints and estimated turns. Approve, revise and reject must all work. Approval pauses the run clock and its recorded answer replays on resume.\n\nFor a newly selected Auto team, default plan approval on; honor explicit saved choices and existing automation authorization. The initial planning call itself consumes quota, which the composer should make clear. When approval is disabled, still record the validated plan and why no interactive checkpoint was required.\n\nRoutine reassignment or repair within the approved scope can proceed and remains visible. A user-requested goal change produces a new requirement/plan revision and goes through the same configured checkpoint before affected new work. Any proposal exceeding authorized providers, spending limits, permissions or scope requires the relevant user decision; the coordinator cannot approve that increase. Stop or finish the current turn safely before changing its inputs.\n\n### Coordination budget and measurements\n\nUse actual CLI invocations, including schema repair and retry invocations, for coordination accounting. Existing logical `agentSteps` alone may not expose every underlying retry; add attribution at the launch boundary. Report steps, input/output/cache tokens, elapsed time, and provider-reported API-equivalent cost separately. Missing token/cost reports are unknown, not zero, and API-equivalent cost is not a subscription bill.\n\nFor the initial experiment, with an approved invocation budget `B >= 4`, reserve at most `min(4, floor(B / 4))` coordinator invocations. Count initial planning, approval-driven replanning and wave revisions against this ceiling. Enforce the total execution limits too; reserve room for required review and checks before admitting more work. If the budget cannot fund a valid plan, explain that before worker dispatch.\n\nThis caps coordination at 25% of the *authorized invocation budget*, not necessarily 25% of actual spend. A tiny run may use one coordinator turn out of three total turns. Report the observed fraction honestly. Use 25% observed coordinator turns on multi-worker runs as an initial evaluation target, not an asserted result or a runtime rule that could deadlock startup.\n\nAt the coordination ceiling, run only already-authorized ready work and verification that fit the remaining budget. Do not create additional tasks or keep calling the coordinator. Return explicit gaps if another planning decision is required. User resume retains today's fresh-budget behavior; expose both the new allocation and cumulative spend. Automatic crash recovery must not be used as a budget-reset mechanism.\n\n## 5. Slice 0 — demonstrate the promise on the existing engine\n\n**Outcome:** a user chooses Auto team, describes a task, approves the generated team/plan, and sees it execute and adapt without choosing a team pattern. This is the first user-visible milestone.\n\nImplement Slice 0 alongside the small Phase 0 contract work. It does not wait for Phases 1–4.\n\n### 5.1 Plan and replay\n\nAdd `src/patterns/auto-team.ts`, register `auto-team`, and use `TeamContext.decide()` with `TEAM_PLAN_SCHEMA`. Reuse supervisor planning, approval, worktree, integration and review behavior where practical.\n\nJournal the planning inventory and relevant policy inputs before using them: provider availability/quota observations, configured settings, task limits and base commit. Validate completed decisions against their recorded input revision on replay; refresh availability at a new dispatch boundary through a new recorded observation. Do not recompute historical routing from today's quota or clock.\n\nUse stable identities such as `plan:0`, `wave:2`, and `task:api:attempt:1`. Create all task/attempt identities and a sorted wave membership before parallel dispatch. Keep unrelated operations out of those identity counters. Reconstruct the roster, sessions and questions from recorded outcomes; an `onStart` callback that does not run during replay cannot be the sole source of that state.\n\n### 5.2 Individual members and workspace handling\n\nStore a small member map within the pattern's replayable state. Add an optional `memberId` to step/ask metadata and a validated per-call settings override so two members on the same provider can actually use different configured models. Preserve existing role/provider settings as the fallback for legacy patterns.\n\nAssociate each member with its current session lineage and workspace. Resume the same session only with its compatible provider and workspace. A new task in a different worktree gets a fresh session plus a portable checkpoint; do not assume a provider can safely resume a conversation in a different directory. Keep one active assignment per member.\n\nCreate a task's worktree from the recorded integration HEAD containing its required upstream artifacts. A yielded task keeps its existing worktree and checkpoint. Reconcile any newer dependency commits only while that task is idle, then record the new input revision. A message describing code does not make that code available in another worktree.\n\n### 5.3 Execute dependency waves\n\nSelect ready tasks deterministically, respecting dependencies, ownership, available members and global slots. Use `ctx.parallel()` and `allowFailure: true` for expected worker failures; retain run-wide stopping for unsafe process/storage/workspace failures.\n\nEach turn produces a validated `TASK_OUTCOME_SCHEMA` with:\n\n- task/member/attempt identity and `submitted`, `needs_info`, or `failed` status;\n- an observable checkpoint, artifact references and unresolved requirements;\n- addressed questions/replies, contract proposals and evidence-bearing findings;\n- the input/contract revisions the result relied on.\n\nA `needs_info` result ends the CLI turn and releases its slot. Preserve partial edits for that task; do not merge them as a completed dependency. Integrate submitted artifacts serially, handle conflicts through existing controlled repair, and run applicable checks. Downstream work starts only once its required artifacts and acceptance conditions are available.\n\nAn agent saying “submitted” does not make its task verified. Code changes must pass the declared verification policy. No-change investigation tasks are evaluated against their artifact criteria.\n\n### 5.4 Questions, answers and revision between waves\n\nRepresent communication as addressed records in the structured outcomes. A question has a stable request ID, sender, recipient, task, body, source provenance, and whether it blocks progress. A reply references that request ID. The runtime supplies authenticated identities rather than trusting model-written sender fields.\n\nAfter a wave, deterministically route known replies and evaluate ready work. Call the coordinator only when a decision is needed, at most once per wave within its budget. `REVISION_SCHEMA` can propose new bounded tasks, reassignments, accepted contracts, answer tasks, evidence-backed answers, or a completion proposal. Validate the patch against the previous revision and apply it before scheduling.\n\nIf the recipient already supplied the answer, deliver it in the requester's next prompt. Otherwise schedule a short response task for that member in the next wave. The coordinator may answer from evidence or reroute the question, but cannot manufacture a missing requirement. Questions requiring user judgment become a visible input blocker. No-ready-work and cyclic waits produce a bounded escalation instead of empty waves or endless coordination.\n\nThis is cross-agent communication at turn boundaries. No separate mailbox daemon, live tool bridge, delivery table, outbox, or lease is required. The coordinator does not need to rewrite every answer: addressed contents pass through to the appropriate member.\n\nUpdate the Auto team worker framing in `src/patterns/prompts.ts` so it explains `needs_info` rather than saying nobody can answer. Update relevant Codex app-server input-denial guidance to point Auto members at the structured protocol. Keep native permission requests subject to existing policy; supporting questions does not authorize new tool permissions.\n\n### 5.5 Minimal memory and context\n\nStart with three small replayable objects:\n\n- **Private checkpoint:** current assignment, attempted approaches, observable progress, unresolved questions and relevant session/worktree references, scoped to that member.\n- **Team state:** accepted plan/contracts, shared findings, decisions, questions/replies and artifact references, selected for the current task.\n- **Context manifest:** the input commit, plan/contract revisions, message IDs and checkpoint used for each turn.\n\nKeep prompts bounded and send relevant deltas on resume. Record a compact coordinator checkpoint too: accepted plan, completed work, open decisions and remaining budget. Start a fresh coordinator session from that checkpoint when its context grows or resume fails; do not repeatedly send all worker transcripts.\n\nUse existing conventions and attributable lessons as inputs; do not implement a new project-memory index yet. Auto team must not automatically write project knowledge from locked-down runs through `learn()` or another side path. `learn()` currently checks replay and the learning setting, not lockdown; the implementation must explicitly close that bypass for automatic learning, with a regression test.\n\n### 5.6 Verify, display and measure\n\nUse an independent read-only reviewer, bounded repairs and final checks on the integrated result. A review after repairs must refer to the current commit. Start check identity with commit SHA plus command and a setup fingerprint when applicable; requirement/contract changes invalidate affected acceptance/review evidence without invalidating every unrelated check merely because the plan number changed.\n\nCompletion is a runtime predicate: every required criterion has applicable evidence, required questions are resolved, and the integrated result matches that evidence. Use existing `setVerdict()`/`setFacts()` where their meaning fits, plus an additive team outcome/coverage projection. Do not force “completed with gaps” or an unknown check into a passed verdict.\n\nAdd the opt-in composer choice and a minimal roster/task/question view now, using the existing run snapshot and SSE. Agent-origin communications must not go through the existing user-note formatter unchanged. Show coordination usage as soon as the first planning call completes, then update it throughout execution.\n\n**Likely files:** new `src/patterns/auto-team.ts` and a small focused helper if needed; changes to `src/patterns/index.ts`, `src/patterns/schemas.ts`, `src/patterns/prompts.ts`, `src/engine.ts`, `src/store.ts`, `src/routing.ts`, `src/stats.ts`, `src/server.ts`, `ui/composer.tsx`, `ui/run.tsx`, shared UI types, and fake-agent fixtures. Avoid three new subsystem directories at this stage.\n\n**Exit criteria:** the composer-to-verified-result scenario in section 10 passes with fake agents; a completed initial decision replays without another model call; two same-provider members remain distinct; a question blocks only its dependent work; coordinator usage and question frequency are recorded; review/permission settings still apply. Real opt-in runs supply the first product measurements, not a prerequisite for offline CI.\n\n## 6. Records, persistence and communication as they grow\n\nThe original record names remain useful domain concepts. They do not all need separate durable tables or independent state machines.\n\n| Concept | Slice 0 representation | Split or extend when justified |\n|---|---|---|\n| Run / workflow revision | Existing run plus versioned accepted plan and journaled revisions | Standalone workflow projection if event-driven execution needs it |\n| Member | ID, role/provider/settings, session/workspace lineage | Derive running/waiting state from attempts and blockers; persist retirement/replacement decisions |\n| Task / attempt | Accepted task graph plus recorded turn outcomes and existing steps | First-class attempt history during execution hardening |\n| Message / request | Typed addressed records with optional request/reply reference | Durable per-member inbox when asynchronous delivery is needed |\n| Delivery | Context manifest lists messages included in an attempt | Separate transport receipt only for live steering/tools |\n| Memory revision | Member checkpoints and accepted team entries | Project store, revisions, retention and retrieval in Phase 3 |\n| Artifact / verification | Existing Git/check outputs plus task/criterion references | Additional evidence types as real tasks need them |\n| Event / command receipt | Existing journal entries and stable operation IDs | Atomic batch and deduplication receipt for multi-record async commands |\n| Outbox / lease | Not required by the wave pattern | Only if a measured execution architecture requires independent dispatch ownership |\n\nTasks can be planned, ready, running, waiting, submitted, verified, failed, cancelled or superseded. Attempts distinguish interrupted execution from a failed task. Member display status is derived. Keep explicit run outcomes for succeeded, completed with gaps, awaiting input, failed, stopped and cancelled; legacy cards can project these without changing their historical meaning.\n\n### Storage requirements and default\n\nStart with the existing single-writer store and journal. Required properties are durable acceptance, stable operation IDs, ordered replay, idempotent application and recoverable snapshots. If one command must update several control records, append one versioned batch envelope containing all those changes, then fsync before acknowledging it.\n\nA filesystem append is **not guaranteed physically atomic**, even with one writer. Recovery must validate the complete framed record and apply the entire batch or none of it; an incomplete tail cannot expose partial logical changes. A damaged committed record in the middle of control history is a recovery error, not permission to skip a dependency. Test those semantics explicitly, including uncertain completion after a failed sync.\n\nUse snapshots as disposable projections tied to a journal sequence/checksum, written through a temporary file and replacement with platform-appropriate durability checks. Replay the remaining tail; do not discard authoritative history before snapshot/retention rules make that safe. Keep existing one-server ownership and process cleanup.\n\nEvaluate SQLite only if measured replay time, query volume, memory search or genuinely concurrent writers outgrow this model. Any proposal must compare complexity and durability, retain Node 20 or explicitly propose a platform change, and state install/backup/migration behavior. There is no mandatory database spike blocking Slice 0.\n\n### Delivery semantics\n\nAt turn boundaries, distinguish queued content from content recorded in an attempt's context manifest. A manifest describes intended inclusion; a completed turn provides evidence of execution. A protocol acknowledgement is not proof of comprehension. A question is resolved by an accepted reply or an explicit recorded resolution, not a delivery receipt.\n\nUse stable IDs and idempotent application across repeated turns. Live transport later adds accepted-steering receipts without changing request semantics. Never allow two concurrent resumes of one session, and never acknowledge a control operation before its authoritative write succeeds.\n\nKeep domain operations independent of HTTP/MCP: `send_message`, `ask_member`, `reply_request`, `yield_task`, `publish_finding`, `propose_contract`, `submit_task`, and `request_help`. Structured results are the first transport across all adapters. Add bounded inbox/memory reads only when those stores exist.\n\nExtend existing run snapshots and SSE before adding dedicated endpoints. Agent tools later call the same validated handlers with member-scoped credentials; user origin is derived from authenticated user requests, not a caller-supplied field.\n\n## 7. Private, team and project memory\n\n| Scope | Purpose | Visibility and lifetime |\n|---|---|---|\n| Private | Portable progress checkpoint, attempted approaches, unresolved work and session references | Only that member's assembled context; explicitly selected handover export; retained with the run under policy |\n| Team | Accepted contracts, plan, decisions, findings, questions and artifact references | Relevant members of the run; preserved across restart; archived with the run |\n| Project | Durable conventions, architecture facts, test instructions and verified lessons | Applicable future runs in the project/repository; revision, invalidation and deletion controls |\n\nPrivate means isolation in code-flow's context assembly and APIs. It is not filesystem secrecy from a fully trusted process on the same machine. Store concise observable checkpoints, not a claimed export of a model's internal reasoning. The user can inspect code-flow-owned records according to existing access policy.\n\n### Provenance and promotion rules\n\nEvery shared item carries source run/task/artifact, originating source trust/lockdown, author, evidence, scope, relevant paths/commit, revision and status. Execution permission (`safe`, `standard`, etc.) and content provenance are separate: a read-only run can still read hostile source text.\n\n- User-authored project rules can be saved directly under user authority.\n- Eligible agent-authored facts may be promoted under the configured verification policy after their evidence and target-branch applicability are established.\n- A locked-down run, or a proposal derived from untrusted content, cannot promote automatically. It stays a proposal until the user explicitly approves the specific content/revision.\n- Derived summaries, replies and handovers retain source provenance. A coordinator or independent reviewer repeating a claim cannot remove its untrusted origin. The server enforces this; agents cannot set their own trust fields.\n- Historical learnings with missing provenance are attributed observations, not automatically trusted conventions. The same promotion gate covers `learn()`, tool commands, summaries and imports.\n- Retrieval labels provenance, separates facts from suggestions, and cannot override current user constraints or tool permissions. Team messages inherit the run's restrictions and source labels.\n\nMemory about an unmerged implementation stays team-scoped unless explicitly marked and accepted as branch-specific project knowledge. Project-wide conventions must not be inferred from one branch's code. Recheck applicability when branch, repository membership or project ownership changes.\n\n### Selection, conflicts and deletion\n\nBuild each turn's context from the goal, assignment, applicable project entries, relevant team contracts, private checkpoint, pending messages and artifact/check references. Select by task, paths, tags, freshness and text search first. Store included revisions and omissions in the context manifest; introduce embeddings only when an evaluation shows a need.\n\nShared updates use expected revisions. Conflicts are visible and assigned to an owner; they do not silently overwrite accepted contracts. A changed contract identifies affected tasks and acceptance evidence.\n\nDeletion covers memory-store bodies and derived memory records, including its snapshots and retrieval data; retained memory history contains deletion metadata. Project selection journals contain references rather than bodies. Source run transcripts and task checkpoints already recorded in the run journal, prompts already delivered to a CLI, provider-managed conversations and user-managed backups have separate retention. The current implementation does not claim to redact those source histories; broader run-history erasure requires a separate retention design. Test exclusions and scope isolation with actual workflow data.\n\n## 8. Phases after Slice 0\n\nThe original default path was **P0 + Slice 0 → P1 → P2 → P3 → P5 → P7 → P8**, with conditional P4/P6 enhancements. The requested complete implementation includes all packages. Reliability, UI visibility and measurement begin in Slice 0, not at the final phase. Phase labels identify work packages, not a requirement to build every subsystem before release.\n\n### Phase 0 — contracts and composition policy, alongside Slice 0\n\n**Deliver:** plan/revision/outcome schemas, the section 4 validation rules, task-kind verification policies, fixtures and an adapter capability inventory. Record structured-output, session-resume, live-input, tool-configuration and sandbox support as tested/unsupported/unknown. Include Claude's tool allowlist and vendor-native subagent observability.\n\n**Exit:** valid/invalid plan fixtures cover duplicate members, unsupported settings, cycles, over-budget teams, reviewer independence and requirement removal. Slice 0's first PR can begin immediately; no platform migration blocks it.\n\n**Code impact:** existing pattern schemas and focused policy helpers. No broad engine extraction or new storage tree.\n\n### Phase 1 — prove recovery and harden storage where needed\n\n**Deliver:** fault-injected recovery for Slice 0 and an architecture decision based on observed requirements. Verify strict journal writes, completed-plan replay, deterministic parallel identities, crash-after-Git recovery and no concurrent writer after restart. Add batch records/snapshots only for properties the existing journal cannot provide adequately.\n\n**Exit:** restart preserves accepted teams, completed decisions, pending questions and task identities; failed writes prevent dependent dispatch; incomplete records cannot yield partial plans; Node 20/22 CI remains supported. Record replay duration and data size before choosing an optimization.\n\n**Likely files:** `src/store.ts`, `src/engine.ts`, store/recovery tests. Keep JSONL; no outbox, leases or migrations merely to match the original plan.\n\n### Phase 2 — harden dynamic execution and member identity\n\n**Deliver:** stable member/session metadata, task attempts, deterministic readiness, artifact inputs, stale-result rejection and fair admission through existing global slots. Make wave execution reusable only where another caller needs it. Keep verification and cancellation in the existing engine.\n\nDefine knowledge, artifact and completion dependencies separately. Do not dispatch a consumer merely because a message arrived when it actually needs merged code. Reassignment must stop/reconcile the old attempt and preserve portable progress. Reviewers retain scoped, independent context.\n\n**Exit:** same-provider members cannot receive one another's private context; downstream work gets the required integration SHA; stale attempts cannot overwrite replacements; cancellation terminates child processes/checks; changed evidence is reverified; missing required work cannot pass. Coordinator and worker usage remain separately visible.\n\n**Likely files:** Auto team helpers, `src/engine.ts`, `src/routing.ts`, `src/git.ts`, verification/recovery fixtures. Extract small shared helpers from supervisor where duplication is demonstrated; do not introduce an entirely separate executor pre-emptively.\n\n### Phase 3 — grow memory from observed workflow data\n\n**Deliver:** expand Slice 0's private/team records into versioned memory and add project retrieval, promotion, retention and deletion. Implement a basic inspector showing provenance and why an item was included. Use saved fixture traces and opt-in run data to tune relevance selection.\n\nApply the section 7 trust boundary at every write/promotion path. Add user approval for specific untrusted proposals, conflict resolution, branch applicability, lost-session reconstruction and project/repository scope checks.\n\n**Exit:** a later run reuses an eligible verified lesson without private-note leakage; an untrusted issue cannot inject a project convention directly or through a trusted-looking summary; stale branch facts are excluded; deletion removes content from retrieval and derived records; coordinator and worker recovery works without vendor sessions.\n\n**Likely files:** focused memory/context/promotion modules, `src/context.ts`, `src/learnings.ts`, `src/projects.ts`, a small memory UI. Introduce a directory only if the actual modules justify it.\n\n### Phase 4 — event-driven messaging\n\n**Decision gate:** evaluate at least 20 opt-in runs and 100 worker turns, alongside deliberately blocking fake-agent fixtures. These are initial sampling targets, not statistical guarantees. Open this phase if either at least 10% of worker turns yield for peer information, or at least 20% of runs lose more than 25% of active elapsed time to an avoidable wave barrier. Label counterfactual avoidable-delay estimates separately from directly measured waiting time. A reproducible correctness/liveness requirement beyond bounded waves can also justify the phase.\n\nBefore implementation, use traces or a small prototype to show that asynchronous scheduling reduces that delay within the approved coordination budget. If the sample is insufficient or waves are adequate, retain wave-based communication and record the decision.\n\n**Deliver if opened:** per-member inboxes, durable request/reply state, waiting/deadline handling, idempotent commands, answer turns for idle members, cycle/no-runnable-work detection and event coalescing. Persist a question and its wait state together so a fast reply cannot be lost. Retired/unavailable recipients route to the coordinator. An unanswered dependency never consumes a process slot.\n\nUse the existing storage interfaces and validated outcome protocol. Introduce a new driver only for execution that cannot be expressed clearly as recorded waves; bridge it through existing adapter/Git/check services.\n\n**Exit:** immediate and duplicate replies cannot duplicate work; questions survive restart; late replies after reassignment are handled explicitly; cyclic waits escalate once with a bounded resolution path; all-waiting teams release their slots. The UI exposes whom each task is waiting on.\n\n### Phase 5 — improve the coordinator already shipped in Slice 0\n\n**Deliver:** evidence-driven expansion/retirement, safe replacement handovers, compact coordinator checkpoints, completion evaluation and reusable sub-plans. Improve task sizing and routing using observed outcomes. Start with investigate, implement, independent review and bounded repair; expand into visible validated tasks.\n\nKeep user requirements distinct from implementation choices. Coalesce events if P4 is enabled; otherwise continue at wave boundaries. A session reset uses the accepted plan and checkpoint, not full transcripts. Preserve blind-comparison boundaries when using competing proposals.\n\n**Exit:** a new blocker can add an eligible specialist within limits; retired members cannot accept new assignments; invalid/stale revisions leave state unchanged; the coordinator stays within its budget; review and completion coverage hold after repairs and replans. Compare with the original supervisor on the same tasks.\n\n**Code retirement:** make supervisor's useful decomposition/integration behavior a reusable strategy. Keep its public pattern ID and saved runs working through a compatibility wrapper until parity is demonstrated; then remove duplicated helper logic. Other manual patterns migrate only when their behavior is preserved.\n\n### Phase 6 — live tools and steering\n\n**Deliver:** expose the established operations through a scoped MCP or CLI bridge where the capability matrix proves support. Derive run/member identity server-side, use per-member credentials, and retain next-turn fallback. `CODEFLOW_TOKEN` is already withheld from agents; preserve that boundary.\n\nAdd collaboration tools explicitly to Claude's per-mode/trust allowlists. A read-only reviewer may communicate without gaining source-write permission. Test sandbox access without broadening filesystem/network permissions just to make the bridge work. Supported Codex steering retains origin and message identity; rejected/unsupported steering queues the message.\n\nOnly now add transport delivery receipts where needed. A tool request returns promptly; a blocked task yields through the existing protocol. Inventory native nested-agent controls and usage visibility; unsupported accounting must remain an explicit limitation.\n\n**Exit:** invalid/cross-run credentials fail; agents cannot spoof users; live and queued delivery have the same logical result; no double session resume; no occupied-slot deadlock; unsupported adapters remain fully functional at turn boundaries.\n\n### Phase 7 — complete product integration and migration\n\n**Deliver:** richer workflow/message/memory views, member-addressed user steering, context inspection, follow-up revisions and integration with projects, automations, sweeps, inbox/phone and review/shipping flows. The composer and basic team view already exist from Slice 0.\n\nPrefer additive run snapshots. Improve persistent SSE cursors with an explicit reset/reload path, benefiting legacy runs as well. A follow-up receives an explicit execution budget and preserves applicable artifacts/memory. Existing shipping permissions remain authoritative.\n\n**Exit:** users can inspect and revise the team, answer blockers, see memory provenance, reconnect after restart and continue completed work; existing manual runs and saved automations remain usable. Disabling new Auto teams preserves compatible viewing and recovery for existing runs.\n\n### Phase 8 — evaluation and rollout gate\n\n**Deliver:** expand the measurements started in Slice 0, publish comparison results, and move from experimental to opt-in beta, then default only if quality/cost justify it. Cover missing sessions, exhausted quota, failed setup, worktree loss, storage full, stale outputs, cancellation and crash boundaries. Record CLI versions for real trials; paid calls are not required by ordinary CI.\n\n**Exit:** no silent requirement loss or false verification passes; no duplicate logical tasks/concurrent writers after restart; trust and memory tests pass; legacy compatibility holds; observed coordinator overhead and waiting rates have an explicit product decision. P4/P6 are not release prerequisites if the turn-boundary product meets its goals.\n\n## 9. Measurements and decisions\n\nCapture the following locally from Slice 0. Fake agents prove behavior; real opt-in runs measure product usefulness. Do not conflate them.\n\n| Measurement | Capture point | Decision it informs | Current result |\n|---|---|---|---|\n| Coordinator invocations, tokens, time and reported cost | First planning call, every revision and retry | Coordinator budget and wake frequency | Captured; see [evidence report](dynamic-teams-evaluation.md) for observed values and sample limits |\n| Plan approvals/revisions/rejections and reasons | Existing approval checkpoint | Composition and task-sizing policy | Partly: the code records them, but every evidence trial disabled approval, so the report holds no approval numbers |\n| Questions and blocked turns / worker turns | Every structured outcome | Need for asynchronous mailboxes | Captured; see [evidence report](dynamic-teams-evaluation.md) for observed values and sample limits |\n| Blocked duration and estimated avoidable wave delay | Yield, reply, ready and resume times; exclude user waits | P4 threshold and likely benefit | Captured; see [evidence report](dynamic-teams-evaluation.md) for observed values and sample limits |\n| Same-provider identity/addressing errors | Fixtures and actual member events | Scope of member-identity changes | Not captured: the report format has no such field; same-provider isolation is covered by tests only |\n| Verification coverage, completion quality and repair turns | Final runtime evaluation | Auto team versus supervisor/manual baselines | Captured; see [evidence report](dynamic-teams-evaluation.md) for observed values and sample limits |\n| Journal size, replay time and failed writes | Recovery fixtures and runs | Snapshot/store investment | Partly: journal bytes, records and load time are reported; failed writes are not counted |\n| Memory selected, useful, stale or corrected | Context manifest and later outcomes from P3 | Retrieval and promotion policy | Captured; see [evidence report](dynamic-teams-evaluation.md) for observed values and sample limits |\n\nA coordinator outcome proposes team changes; it does not mark its own experiment successful. Record the validated plan, actual usage and runtime evidence so decisions can be reproduced. Do not fill this table with invented targets presented as measurements.\n\nDecisions resolved for implementation: keep Node 20; default to the current journal/store; start communication at turn boundaries; enforce composition rules in code; retain configured plan approval; keep explicit-resume budget semantics visible; prevent automatic untrusted project promotion; make mailbox/live layers conditional. The numerical experiment defaults above can change only with a recorded rationale and updated fixtures.\n\n## 10. Acceptance scenarios and reviewable implementation sequence\n\n### First scenario: Slice 0\n\n1. Start Auto team from the composer on a fixture with backend, frontend and final-check requirements. No manual worker roster is supplied.\n2. A coordinator proposes the small team, contracts and graph. The user revises acceptance criteria once, then approves; a rejection variant starts no workers.\n3. Independent tasks run in isolated worktrees. A variant uses two members on the same provider with different settings and sessions.\n4. Frontend returns `needs_info` with a question to backend. Its process ends; unrelated ready work proceeds. A backend outcome or short answer task supplies evidence next wave.\n5. Frontend receives the addressed reply and accepted contract. Its checkpoint resumes in the correct workspace; missing-session recovery uses a fresh session with selected context.\n6. Integrate submitted changes. A downstream task requiring actual backend code starts from the required integration commit.\n7. Independent review requests one repair. After repair, current checks and review determine the result and list any gaps. Unknown checks never become passes.\n8. Display coordinator usage, wave/question counts and requirement evidence. No new project memory is promoted in this slice.\n9. Restart after a completed planning decision in a variant. The decision replays, preserving roster and task IDs without another planning invocation.\n\n### Later recovery, messaging and memory scenario\n\nIn P1, stop after a question outcome is journaled but before its next prompt, then resume with the same request IDs. In P4 if enabled, repeat at asynchronous delivery boundaries with an immediate/duplicate reply, expired recipient, stale attempt and cyclic wait. In P3, promote an eligible lesson and verify that a later run retrieves it without private notes; repeat from an untrusted issue and confirm that promotion remains pending user action.\n\n### Suggested first pull requests\n\n| PR | Reviewable scope | Acceptance |\n|---|---|---|\n| S0-A | Plan/outcome schemas, composition policies, fake-agent fixtures, strict journal-write error propagation | Invalid plans rejected; failed journal writes cannot advance accepted state |\n| S0-B | Auto team planning/approval, member settings, dependency waves, minimal context and final verification | Generated team executes; decisions replay; two same-provider members stay distinct |\n| S0-C | Questions/replies, bounded revisions, portable checkpoints and automatic-learning trust guard | A blocked member resumes; coordinator budget enforced; untrusted learning cannot bypass the gate |\n| S0-D | Opt-in composer, roster/tasks/questions, coordination metrics and end-to-end fixture | User-visible Slice 0 with measured counters; existing patterns still work |\n| P1+ | Choose the next narrow change from the phase gates and recorded measurements | No speculative storage or mailbox prerequisite |\n\nThe four S0 PRs make one vertical milestone; wire usage instrumentation in S0-B so the first coordinator call is measurable, then expose it in S0-D. Avoid releasing incomplete intermediate PRs as a finished Auto team feature.\n\nFor implementation, use `npm run typecheck` and focused `node:test` suites per change, the full `npm test` suite at shared engine/adapter boundaries, and `npm run test:e2e` for composer, approval, waiting and reconnect flows. Fault injection should test real failure boundaries, not merely serialize records. The revision-3 implementation is validated by the full test/build workflow and real CLI trials documented in the evidence report.\n\nThese phases are now implemented. Further model-quality tuning or a default rollout must use new, explicitly labeled evidence; it is not an unfinished infrastructure dependency.\n\n## 11. Review disposition and boundaries\n\n| Review finding | Revision 1 action |\n|---|---|\n| Promise arrives too late | Slice 0 delivers automatic team creation and basic collaboration immediately |\n| Recovery justification already met | Reuse `remember()`; distinguish later asynchronous needs from dynamic planning |\n| SQLite predetermines platform | Keep Node 20 and current store; technology change requires measured justification |\n| References use simpler persistence | Do not infer a requirement for leases/outbox from vendor examples |\n| “Appropriate team” undefined | Composition rules, budgets and fixtures are specified in section 4 |\n| Plan approval missing | Existing checkpoint is explicit before dispatch and on relevant scope revisions |\n| Project memory trust gap | Enforced provenance/promotion rule, including the existing learning path |\n| Coordination cost measured too late | Instrument the first planning call and cap coordination from Slice 0 |\n| Memory precedes useful execution data | Minimal runtime checkpoints now; full project memory follows execution |\n| Too many stored state machines | Derive member/delivery status; split records only when behavior needs it |\n\nTwo review details are refined rather than copied literally: a single append needs complete-record recovery to provide logical batch atomicity; and at the reviewed baseline, the synced journal swallowed write failures through the shared append helper. The implementation now propagates authoritative write failures and recovers incomplete tails, without making a database a prerequisite.\n\nInitial boundaries remain local official CLI execution, one coordinator per run, structured graphs, bounded expansion, isolated writers and controlled integration. Remote worker fleets, arbitrary generated workflow code, live cross-run agent messaging, vector databases and recursive autonomous teams are deferred. Manual patterns remain available while useful behavior is migrated into reusable strategies.\n\n## References\n\nRepository references: [Review 0](dynamic-teams-plan-review-0.md), [architecture](architecture.md), [patterns](patterns.md), [runs](runs.md), [configuration](configuration.md), and [security](security.md).\n\nDesign references retained from the original proposal:\n\n- [Claude dynamic workflows](https://code.claude.com/docs/en/workflows)\n- [Claude agent teams](https://code.claude.com/docs/en/agent-teams)\n- [Codex subagents](https://learn.chatgpt.com/docs/agent-configuration/subagents)\n- [LangGraph memory concepts](https://docs.langchain.com/oss/python/concepts/memory)\n\nThese are architectural references, not dependencies on a vendor's internal team implementation. code-flow owns the cross-provider protocol and validates adapter capabilities in its own environment.\n\n## Revision 3 implementation decisions\n\n- New Auto runs default to the event scheduler; saved runs lacking `eventDriven` retain their wave schemas, ordering and operation identities. Strict wire schemas satisfy Codex requirements; internal typed dependency maps are normalized from closed wire arrays.\n- Live tool calls return after durable acceptance. They do not hold a worker open waiting for a peer. A steering receipt proves protocol acceptance only; messages keep their original IDs and agent origin in live and next-turn paths.\n- A user changing the goal chooses **Revise goal**. The old execution is stopped and awaited, its branches remain available, and a linked run plans the revised requirements from the integration branch with a fresh explicit budget and configured approval. Normal member notes cannot rewrite the runtime's accepted requirement set.\n- Parent attachments, source trust and lockdown survive whole-team continuation. Reading private memory cannot silently publish it through live memory tools; provenance references are persisted separately from erasable bodies.\n- User memory feedback affects relevance for the exact revision, never trust. Synthetic seeded-memory evaluation measures retrieval plumbing; it does not claim human-rated usefulness.\n- Supervisor retains its public ID and saved-run behavior through shared decomposition/integration helpers. Other manual patterns retain their established behavior; moving them to Auto is not necessary to complete this plan.\n\n[Documentation library](README.md) · [Development guide](architecture.md)\n\n## Model discovery and worker routing — implemented 2026-10-21\n\n- New Auto executions use a persistent, validated catalog. Existing saved runs without `modelRouting` and other workflows retain legacy model selection.\n- Claude control initialization and Codex app-server model discovery resolve exact IDs, aliases and per-model efforts without an inference prompt. Antigravity `agy models` supplies reported IDs; per-model effort support remains unknown. Remote probes optionally return catalogs from that worker/profile; containers discover in their selected image. Host catalogs never establish remote compatibility.\n- Catalog records separate CLI reports, documented facts, environment identity and bounded measured observations. Immutable discovery snapshots support step provenance. Account changes discard prior observations/documentation. Concurrent cache reads coalesce; observations merge without dropping concurrent records. Invalid proposals fail closed.\n- Optional official-document research uses origin/redirect/size/time limits and a closed schema. No page text becomes an instruction. Research cannot change permissions, establish account access or promote compatibility. Offline mode keeps the last verified evidence; missing information remains unknown.\n- Coordinator identity/settings remain fixed. Worker pool and Advanced role pins constrain model/effort proposals. Every new worker execution checks the accepted model, effort, current environment, inherited run permissions and existing budgets. Recovery preserves decisions and rechecks before execution. Existing bounded member replacement supplies checkpoint-based reassignment without cross-provider session transfer.\n- UI exposes automatic selection, catalog refresh/research opt-in, stale/unknown/unsupported states and per-task/member assignment rationale/provenance. More sophisticated performance ranking is deferred.\n- Validation adds mocked catalog/discovery/research/cache/offline/effort tests, routing/provenance assertions, thread-start/resume policy tests and browser UI/API coverage. Real-provider findings are recorded separately in `docs/worker-permission-verification.md`; mock passing tests are not real-provider certification.\n\n### Native-control gaps found by live verification\n\nCodex 0.162.1 can still expose model-driven v2 delegation when only `features.multi_agent=false` and `features.multi_agent_v2=false` are set. Managed turns now also set `agents.enabled=false`, explicitly disable the structured v2 setting and hooks, and apply overrides to both process and thread start/resume configuration. Managed Codex requires at least the tested CLI version for this control. A resumed session at Ultra effort was probed after the fix.\n\nClaude managed turns explicitly disable workflows, Ultracode and hooks, deny native delegation/scheduling tools, and use strict scoped MCP plus `--no-chrome` when external tools are forbidden. Help capture uses a private regular file because the installed Claude CLI truncated piped help and Antigravity prints help on stderr. Antigravity 1.4.4 remains blocked for managed runs; discovery alone does not establish equivalent permission enforcement.\n\nThe Antigravity follow-up tested a real custom-agent tool allowlist with `gemini-3.8-flash-low`. Fixture reads succeeded and omitted terminal/write tools were not called, but this does not establish per-run control over shared permissions, hooks, plugins, browser/MCP or recovery. `init.tools` advertised tools outside the observed model allowlist and cannot attest enforcement. Managed rejection now has one shared diagnostic, and help-only sandbox evidence is marked unknown. Tests cover trust levels, Solo, fresh/resumed turns, container/remote selection and the legacy skip-permissions environment override. Enabling Antigravity requires a verified scoped policy API or a separately designed isolated executor with protected policy and mediation of every tool surface; global settings mutation, prompts and worktree reverts are not acceptable substitutes. See [the verification report](worker-permission-verification.md#antigravity-auto-feasibility-follow-up).\n\nAn isolated-execution feasibility prototype now exercises a clean home and protected policy under macOS Seatbelt. Actual fixture tests passed for workspace writes and denials of policy tampering, outside paths, ambient workspace hooks and terminal TCP access. Antigravity 1.4.4 starts under that policy; authenticated discovery/inference does not work in the prototype. A separate clean-home discovery requires sign-in, and the installed Docker daemon was unavailable after a startup attempt. Keep this code in opt-in research probes, with no production eligibility receipt or fallback. Remaining work is a separately authenticated runtime, separated inference/command networking, protected customization mounts, and provider-level forbidden-tool/recovery tests. Full Auto support remains **unimplemented and blocked**, not certified by the OS fixture checks. See [isolated execution findings](worker-permission-verification.md#isolated-execution-investigation).\n\n\n### Antigravity existing-login follow-up (2026-10-21)\n\nThe earlier isolated-execution blocker is resolved for a narrow local macOS executor with CLI 1.4.4. It reuses the native Keychain login without exporting credentials, and grants only the OS security metadata/cache-lock access needed by the official CLI/helper. Protected per-turn settings and a file-only custom agent exclude terminal, browser, MCP, hooks and native delegation. Workspace writes remain mode-dependent; authentication/runtime paths are also denied to model file tools. Fresh project records are seeded before configuration is sealed.\n\nAuto inventory exposes this tool limitation; runtime preflight is repeated before launch. Unsupported platforms, remote/container execution, interactive approvals and other CLI versions fail closed. Legacy skip-permissions flags cannot affect managed turns. The coordinator's selected provider/model remains unchanged. Isolated discovery uses a separate catalog environment; assignments retain its provenance, and steps record `macos-file-only-v1`. Native sessions are never retained or transferred; recovery uses portable context and revalidates eligibility. Native `manage_task` bookkeeping may remain available, but delegation tools do not.\n\nReal existing-login discovery returned six candidates. Real file-editing, structured-output and fresh read-only adapter turns succeeded; fixture escape/policy denials, absent hook markers and zero browser requests were observed. A real Auto run with Codex coordinating and Antigravity editing completed with a passed check/review verdict, five completed steps and no failures. Tests cover protected OS profiles, environment scrubbing, forged/unsupported launches, separate catalog caching and mocked Auto dispatch with fresh workers. Full general-purpose Antigravity execution remains outside this implementation: inference networking stays enabled and Code Flow runs checks separately. Other platforms/models/efforts and arbitrary tool-call fuzzing remain unverified. See [the latest verification report](worker-permission-verification.md#existing-login-reuse-and-managed-file-workers).\n\n### Antigravity permission/recovery review (2026-10-21)\n\nImplemented sandboxed version inspection, tracked discovery process groups, nested workspace customization protection, safe native-auth error classification/output, unexpected-tool failure detection, and guaranteed cleanup after initial callback failure. Explicit recovery rechecks login through fresh isolated discovery before clearing signed-out status; cached/offline results cannot clear it. Auto can retain an unavailable idle member without authorizing additional work, allowing bounded workflow stop and later recovery. Deterministic stop/reload/repair/resume tests preserve model pins, recorded provenance, partial checkpoints and inherited permissions. Real existing-login adapter probes passed with the strengthened profile; forced expired-token refresh remains unverified and never triggers an authority-expanding fallback. See [the review evidence and limitations](worker-permission-verification.md#antigravity-permission-and-recovery-review-2026-10-21).\n\n### Non-destructive refresh evidence (2026-10-21)\n\nAdded an opt-in native refresh observer that uses ordinary isolated discovery and reports only bounded metadata. Valid-token login reuse is distinct from observed refresh, and credential persistence remains separately unverified. No documented force-refresh interface was found in CLI 1.4.4. Real probes reported six models and an unexpired token, so expired-token renewal remains pending natural expiry. Added detection of native credential-save/fallback failures in managed turns and discovery, including failure despite a successful inference result. No permission widening, credential export, forced expiry, account mutation or logout is used. See [the investigation](worker-permission-verification.md#non-destructive-native-refresh-investigation-2026-10-21).\n\n\n### Explicit native Antigravity execution (implemented)\n\n- Add run authority `antigravityExecution: \"native\" | \"isolated\"`, defaulting to isolation when absent. Native is an explicit Advanced opt-in, never a model/template decision. Parent continuations cannot upgrade isolated authority.\n- Resolve native Antigravity to a truthful unrestricted receipt with provider-configured external tools and native delegation. Other providers retain ordinary inherited permissions. Reject incompatible mandatory requirements, Safe/narrowed trust, approvals, remote/container and unattended contexts.\n- Execute against the existing host profile and saved login, without sandbox flags or credential copies. Retain fresh portable turns, tracked process lifecycle and private authentication diagnostics. Keep native and isolated catalog identities separate and recover only with fresh discovery in the recorded mode.\n- Verified a real mixed-provider Auto run: Codex coordinator, native Antigravity file edit and terminal check, independent Codex review, configured checks passed. No new sign-in requested. Native discovery reported six models. Expired-token refresh and browser/MCP/hooks/native-child behavior were not exercised.\n- Deterministic tests cover opt-in receipts, forged/mismatched options, unchanged other-provider permissions, incompatible constraints, parent narrowing, private auth persistence failures, both catalog modes, and recovery with portable work and preserved assignment provenance.\n\n### Main integration verification\n\nAutomatic role defaults now require verified Antigravity managed-runtime availability, separately from CLI flag compatibility. Unsupported isolated reviewers are rejected before a local run starts; explicit native opt-in retains its separate eligibility. Remote execution uses the remote inventory and dispatch checks rather than host runtime evidence. Auto’s free-model picker uses the same discovered catalog as its model controls. Editable retries and project drafts preserve recorded permission requirements and the explicit Antigravity mode.\n","title":"Dynamic teams, workflows, messaging, and memory"},{"group":"Engineering records","id":"dynamic-teams-implementation-review-1","source":"docs/dynamic-teams-implementation-review-1.md","body":"# Review 1: the dynamic teams implementation\n\n> Engineering record. Status, measurements and decisions below describe the recorded implementation or review. For current usage, start with the [documentation library](README.md).\n\nReviewed: commits `2cf0736` (Add dynamic agent teams with durable collaboration and scoped memory) and `dfd752d`, on top of `8cb3641`, together with the evidence document `dynamic-teams-evaluation.md`. About 8,600 lines across 80 files: the Auto team pattern and plan model, the event scheduler, the mailbox, the memory store, the live tool bridge and its adapters, the evaluation runner and rollout store, the UI, and the documentation.\n\nMethod: four independent read-throughs (runtime and scheduler; memory store and trust boundary; live bridge, adapters and credentials; evaluation, rollout and documentation claims) plus a UI pass. Suspected defects were reproduced with scripts or tests before they were counted. The evidence JSON was checked number by number against the document. Everything marked **fixed** below was changed in this review and is covered by a test; everything marked **open** is not.\n\nBaseline before this review: typecheck clean, 416 of 416 unit tests passing, 17 browser tests passing. After: 420 of 420 unit tests, both browser suites, typecheck and the UI build pass. After merging `main` (the side panel, PR 13) and the fixes from the Codex review of PR 14: 475 of 475 unit tests, 19 browser tests.\n\n## Verdict\n\nThe implementation is more careful than most: identity, authority and provenance are derived by the server and never from model output; control state is journaled before it is acted on; schemas are closed; the test suite exercises crashes, replays and spoofing rather than object shapes. The trust boundary around project memory holds. Nothing reviewed lets an agent accept its own knowledge, borrow another member's session, or reach the application token.\n\nIts failures are of calibration, not of design: several safeguards are sized wrong. A repository without a test command could never finish a run; deleting one memory erased every record of every run that had merely seen it; one shared cap let a looping member fill the memory store for every project; every live tool call rebuilt the whole mailbox from scratch; the provider inventory re-probed the CLIs on every scheduler event. All of these are fixed here. The evidence document is honest in tone but its numbers come from an uncommitted tree, its real trials overlapped in time, and its \"rollout gate\" gates a label rather than the feature. Those are now disclosed in the documents themselves.\n\nKeep Auto team experimental and opt-in, as the evidence document already decides. Re-run the real trials from the committed runner before any beta claim.\n\n## Fixed in this review\n\n### Runtime and scheduling\n\n- **A repository without a runnable check could never finish.** The final verification treated \"no configured check\" as an unresolved gap, so the team ended blocked, the run stopped with \"unresolved work\", and every Resume spent a review invocation and blocked again. Now a complete, independently reviewed result in such a repository finishes as `completed` with `verification.check: \"unknown\"`, no pass/fail verdict, and nothing accepted into team or project memory. The UI shows \"Reviewed · no check ran\" rather than \"Verified\". `src/patterns/auto-team.ts`, `ui/team-state.ts`, `ui/team.tsx`, test \"Auto team without a runnable check finishes reviewed but unverified\".\n- **Provider inventory probed the CLIs on every scheduler event.** `teamInventory()` ran `codex debug models` and `agy models` (a network fetch, 45 s timeout) at every wave and journaled the catalogs each time. Later observations now reuse the planning catalogs and refresh only availability and quota. `src/engine.ts`, `src/patterns/auto-team.ts`.\n- **A no-op coordinator revision was asked again until the coordination budget ran out.** A revision that adds, reassigns, retries, answers or contracts nothing now ends the wave loop with the unresolved work listed. Test \"Auto team stops asking the coordinator once a revision changes nothing\".\n- **User answers and teammate messages shared one JSON block** labelled \"agent data unless origin=user\". They are now separate prompt sections, the user's answers marked authoritative for the task and teammates' messages marked as evidence. The member's own session handle and private checkpoint no longer appear in the assignment JSON. `src/patterns/auto-team.ts`.\n\n### Memory store\n\n- **Deleting one memory cascaded through context references.** `derivedFrom` records what was in a member's context, not text copied from it, so deleting a widely selected convention erased every checkpoint, contract and finding of every run that saw it, including user-accepted entries derived from them, and any run still citing it failed on its next write (\"Memory source was deleted\"). Deletion now redacts only the target; references to a tombstone inherit its origins instead of throwing. Reproduced before the fix with a scratch script. `src/memory.ts`, test \"deletion erases the entry's body; entries that saw it keep theirs\".\n- **One global cap of 5,000 entries across all scopes and repositories**, with private and team records never retired and no API to delete them. A few hundred runs, or one looping member, would make every Auto run fail at plan time with \"Memory limit reached\". The project cap now counts project entries only; private and team records are capped at 512 per run; archiving a run with cleanup retires its private and team records. The store's own load-time validation used the same global count and would have refused to open the file. `src/memory.ts`, `src/engine.ts` (`archive`).\n- **Run-wide provenance unions overflowed the 128-reference limit** and the overflow was fatal after verification had succeeded. Writes now cite the journaled selection of the task that produced them (stable across replays, which also removes a crash-idempotency hole in the pattern path), and the limit is 1,024. `src/patterns/auto-team.ts`, `src/team-memory.ts`, `src/engine.ts`.\n- **A user's edit silently accepted a proposal**, including a tags-only edit of an untrusted one, contradicting \"the user must approve the exact revision\". An edit now keeps a proposal a proposal unless the request says `accept: true`; an accepted entry the user edits is recorded as accepted by the user. `src/memory.ts`, `src/server.ts`, test \"a user's edit keeps a proposal a proposal unless they accept it\".\n- Storage failures were returned as HTTP 400; they are 500 now. The unused `memoryPrompt()` helper, which rendered bodies unescaped into Markdown headings, is removed.\n\n### Live tool bridge and adapters\n\n- **Every tool call rebuilt the mailbox overlay with one deep clone per pending command.** Measured: 1,000 pending commands cost 1.8 s per call, 4,096 cost 36 s, all synchronous on the main thread and serialized ahead of every other member's calls and the scheduler's own drains. The overlay now clones once and applies commands in place, and each member turn may make at most 256 tool calls. `src/team-mailbox.ts` (`applyMailboxCommandInPlace`), `src/engine.ts`.\n- The client answered over-limit requests with `id: null`, leaving the CLI's request waiting until its own tool timeout; it now answers with the request's id. The credential file is deleted as soon as the client has read it. `src/team-tool-client.mjs`.\n- The capability TTL was clamped to 2 hours while a step may run 24; a failed listen was cached forever; the bridge was never closed at shutdown; an inactive grant answered 400 instead of 403. `src/team-tools.ts`, `src/server.ts`.\n\n### UI\n\n- The run view's event sequence numbers (the manager's global counter) and the sidebar feed's per-start counter were gated through one map, so a run you had opened stopped updating in the sidebar and never announced that it needed you until a reload. The side panel change merged to `main` as PR 13 removed the run view's pushes into the list altogether: the list is the global feed's. The merge keeps that, so the run view's stream, including its hello snapshot, no longer touches the list. `ui/run.tsx`, `ui/state.ts`.\n- \"Waiting for you\" was shown for a team question regardless of run status, although the server accepts answers only while the run is running, and a crash left the flag set. The server clears it when it marks a run interrupted; the UI, phone view and inbox show it only for a running run, with the Resume instruction otherwise. `src/engine.ts` (`load`), `ui/team-state.ts` (`teamAwaitingAnswer`), `ui/run.tsx`, `ui/rail.tsx`, `ui/automations.tsx`, `src/remote.ts`.\n- The \"approve the plan\" preference was one shared key, so unticking it once for Plan → Build → Review dispatched Auto teams without their approval checkpoint. It is now remembered per pattern; the old shared choice still applies to manual patterns. `ui/state.ts`, `ui/composer.tsx`.\n- The open user question is now a card under the run header, like plan approval, instead of the last item in the message list. `ui/team.tsx` (`TeamQuestionCard`), `ui/run.tsx`.\n- The check-command detection could land in the composer field after the user had started typing into it (the browser test recorded `npm testnpm test` as the check); a late detection result now fills only a field nobody has touched. `ui/state.ts` (`loadCheck`).\n- Measurements polled the evaluation endpoint every 5 s while collapsed, which rebuilt the report from the whole journal each time; they load only while open. Delete in the memory panel is a two-step inline confirmation. The composer refuses an Auto team with fewer than 4 steps or an empty provider pool before the run exists. An invalid follow-up budget is explained next to the composer instead of only inside the collapsed budget disclosure; \"Revise goal\" is hidden during plan approval; the answer box has a visible label. The hello event of a run stream now always carries the snapshot.\n\n### Evaluation and runner\n\n- `repairTurns` counted any step whose title matched `/repair|fix/`, including model-authored task titles; it now counts engine-titled steps only. `reportedTokens` omitted cache-read tokens. The \"avoidable barrier\" label is \"dispatch delay after an answer\": every trial ran event-driven, so there was no barrier to avoid.\n- The rollout decision assessed only the reports a caller cited, so a caller could cherry-pick verified pairs to reach \"default\". Eligibility is now judged over every real report in the store. `src/team-evaluation.ts`.\n- The runner created an empty temporary directory on every invocation even with `--out`; `--real` now requires `--confirm` and prints the maximum invocation count; the trial settings that differ from app defaults (approval off, `maxRepairs: 1`, `maxSubtasks: 2`, provider-default models) are written to `configuration.json`. `scripts/` is now typechecked. `scripts/evaluate-teams.ts`, `tsconfig.json`.\n\n### Documents\n\n- README no longer headlines an experimental feature with one verified real run out of three as the product's primary flow; Auto team is presented as experimental and opt-in.\n- `dynamic-teams-evaluation.md` now states where its numbers come from (an uncommitted tree, an older runner, source runs not committed), that the real trials overlapped in time, what the dispatch-delay figure is, and that the rollout stage does not gate availability. Test counts are reconciled (397 at trial time, 416 after the delivery review, 420 now).\n- `dynamic-teams-implementation-plan.md`: \"validates the scheduling benefit\" is corrected to \"demonstrates the scheduling order\"; three measurement rows marked \"Captured\" are now \"Partly\" or \"Not captured\"; the rollout decision is described as a label.\n- `auto-team.md`, `security.md`, `configuration.md`: `--strict-mcp-config` disables the user's and the repository's MCP servers in Claude team turns; members run as one user and the credential file is readable in the window before the client deletes it; deletion and edit semantics as implemented; the per-turn tool budget and per-run memory cap; the stage is informational.\n\n## Open findings, ranked\n\n1. **The real evidence is not reproducible and must be regenerated.** The committed runner, a committed fixture task and recorded model identities are the minimum for a number anyone can check. Until then the evidence document supports exactly one claim: the feature runs end to end on real CLIs. Run `npm run evaluate:teams -- --real --confirm` from the committed tree, sequentially, and replace the JSON.\n2. **The rollout stage does not change behaviour.** Either make `beta`/`default` affect availability and the composer (and keep `CODEFLOW_AUTO_TEAM_ENABLED` as the kill switch), or rename the feature \"rollout record\". A gate that gates nothing invites the wrong conclusion.\n3. **Coordination budget is tight by construction.** `min(4, floor(limit / 4))` coordinator calls include initial planning and every approval revision; two \"revise\" clicks at approval leave one call for the whole run. Consider `min(8, ceil(limit / 4))` or counting approval-driven replanning separately, then measure.\n4. **Steering race.** Fixed after the Codex review of PR 14. A Codex `turn/steer` that had not answered by the 2 s deadline was journaled as not accepted, so a message Codex did take was shown again in the next context. The deadline now records an unknown outcome; the CLI's answer is journaled when it comes and settles the outcome at the next drain; the message shows as *steering* meanwhile. An answer that never comes (a crash) leaves the message available to the next context under the same ID, as an interrupted acknowledgement always did. `src/engine.ts` (`steerTeamMessages`), the late-acceptance case of the steering test.\n5. **Capability probes reran on every turn after an unknown result.** Fixed after the Codex review of PR 14. An unknown result (a CLI missing or not signed in, an older Codex refusing `codex mcp get`, a probe that timed out) was evicted from the cache at once, so every Auto turn paid the serial probe stages again. It is now kept for two minutes, against ten for a settled result. The cache key still embeds a test hook. `src/adapters/team-tools.ts`, test \"an unknown capability probe is not repeated on the next turn\".\n6. **Bridge handler errors are returned verbatim** (file paths, git errors). Whitelist domain errors as 400; everything else 500 with a generic message, logged server side. `src/team-tools.ts`.\n7. **Git fan-out per prompt**: up to 128 `merge-base --is-ancestor` calls per selection, run twice per `projectMemory()`. Memoize `(commit, head)` per run and build the query once. `src/team-memory.ts`.\n8. **Memory editor after a 409** keeps the stale revision; the only way out discards the draft. Refetch, rebase the revision, keep the typed fields. `ui/memory.tsx`.\n9. **Reconnect handling is tested only through a pure helper** with a scenario the server cannot produce. Add a server test for `Last-Event-ID` beyond `newest` and a client test for the three hello cases. `test/server-reliability.test.ts`, `test/team-ui.test.ts`.\n10. **`read_memory` results are frozen per attempt and arguments** (intentional for provenance); new team proposals are invisible to an identical later call in the same attempt. Say so in the tool description.\n11. **Answering a question after a restart requires Resume first.** The UI now says so; a better flow would accept the answer while interrupted and deliver it on Resume.\n12. **The team view is a dashboard at the top of the thread.** The question card is now under the header; the remaining roster, workflow, coverage, messages and measurements should fold into one disclosure outside plan approval, with measurements and capabilities living in Insights.\n\n## What to avoid next time\n\n- A safeguard whose blast radius is \"everything that touched it\": the deletion cascade over context references, the global memory cap across scopes and repositories, and the overlay rebuilt from scratch per call all protected one thing by endangering everything around it. Size a safeguard to the record it protects.\n- A gap nobody can close. If the team can do nothing about a condition (no test command, no reviewer quota), it must not become a blocking gap that every Resume pays to rediscover.\n- Probing external processes inside a loop. Catalog and version probes belong at plan time; a scheduler event should be a function of journaled state.\n- One JSON blob for text of different authority. User answers and teammate messages, or a member's private checkpoint and its public record, must not share a section.\n- A single preference key across patterns when one pattern makes the preference a safety control.\n- Evidence written from an uncommitted tree, trials run concurrently and called sequential, counts (\"11 focused tests\") that match no file, and estimates labelled as measurements (\"avoidable barrier\", \"repair turns\" by regex). The evidence document was candid everywhere except in these specifics, and those are what a reader checks first.\n- Calling a recorded label a gate. If a decision does not change what the software does, do not describe it as controlling rollout.\n- Headlining an experimental feature in the README before its own evidence supports it.\n\n## Suggestions\n\n- Treat the three memory scopes differently in storage: project knowledge in the curated store; private checkpoints and team records in the run's own journal directory, deleted with the run. Most of the cap, GC and cascade problems disappear with that split.\n- Replace the per-event coordinator wake with a per-wave decision unless a measurement shows per-event decisions change outcomes; the fixture data so far shows no peer questions in real runs at all.\n- Record model identity per turn in the evaluation report and pin it in the runner; otherwise two trials are not comparable even on the same day.\n- Add a `check` detection step to the Auto plan itself: when no check command exists, ask the coordinator to propose one as an investigation task, so verification becomes possible rather than unknown.\n- The plan document has become a hybrid of history and status. Move the status table and the storage decision into the evidence document, and keep the plan as the design record it was.\n\n## Verification performed\n\n| Check | Result |\n|---|---|\n| `npm run typecheck` | passes (now includes `scripts/`) |\n| `npm test` | 420 passed, 0 failed (416 before; 4 added) |\n| `npm run test:e2e` (`auto-team.e2e.ts`, `ui.e2e.ts`) | passes after the UI changes |\n| `npm run build` | passes |\n| `git diff --check` | clean |\n| Memory cascade, cap and reference-limit reproductions | executed before the fixes; covered by tests after |\n| Overlay cost benchmark | executed before the fix (36 s per call at 4,096 pending commands) |\n| Real CLI probes (`claude --help`, `codex mcp get`, `codex features list`) | executed against the installed CLIs; claims in `auto-team.md` about them hold |\n\n[Documentation library](README.md) · [Development guide](architecture.md)\n","title":"Review 1: the dynamic teams implementation"},{"group":"Engineering records","id":"worker-permission-verification","source":"docs/worker-permission-verification.md","body":"# Live worker permission verification\n\nTested locally on macOS, 2026-10-21, using temporary Git fixtures and the user's existing CLI authentication. No credentials were read/exported and no global provider settings were changed. These are bounded observations of these versions/models, not certification of every model, OS, container or remote worker.\n\n| Provider | Version/model | Observed result |\n| --- | --- | --- |\n| Codex | CLI 0.162.1, `gpt-6.1-sol`, low effort | In-workspace edit and terminal write succeeded. A sibling fixture write and localhost HTTP request from the terminal returned `EPERM`; the outside sentinel stayed unchanged and server received zero requests. |\n| Codex | Same CLI/model, app-server | Scoped `codeflow_team.read_inbox` reached only the issued local fixture capability and returned `LOCAL_MCP_FIXTURE`. |\n| Codex | Same CLI/model, browser | A new browser tab read `LOCAL_BROWSER_FIXTURE` from the local HTTP server and was closed. This worked with terminal network denied, demonstrating the separate browser permission surface. Existing tabs were not inspected. |\n| Codex | Fresh low-effort and resumed Ultra session | Before the fix, native spawn succeeded despite both feature flags being false. After adding `agents.enabled=false` and explicit v2/thread controls, native spawn was unavailable. The resumed test reused one of the earlier fixture sessions; no cross-provider session was transferred. |\n| Claude | CLI 2.1.306, `claude-sonnet-5-5`, low effort | Safe profile allowed a fixture file edit and denied the one requested Node terminal command with “requires approval.” No outside write or network request occurred. This is tool-rule enforcement, not an OS sandbox. |\n| Claude | Same CLI/model, external tools disabled | Native web/browser and external MCP were absent in the observed tool surface. After repairing truncated CLI help detection, the explicitly granted local `codeflow_team` MCP fixture remained callable and returned its marker. No external website or third-party MCP was called. |\n| Antigravity | CLI 1.4.4 | `agy models` returned six candidates. Help exposes sandbox and persistent MCP management, but no verified scoped MCP/native-delegation controls. A managed adapter call was rejected before inference. Legacy sandbox execution and external integrations were not certified. |\n\n## Fixes and boundaries\n\nCode Flow now passes Codex delegation, hook and sandbox overrides in both the CLI arguments and thread start/resume configuration. Feature-list output alone had incorrectly suggested that delegation was disabled: model-driven v2 required the additional `agents.enabled` control. Claude managed settings disable workflows, Ultracode and hooks and deny native delegation/scheduling tools. Regular-file help capture avoids truncated piped output and handles Antigravity's stderr help.\n\nMandatory workspace/terminal-network boundaries currently require a compatible local Codex environment. Claude without a container provides tool permissions, not filesystem isolation. Worktrees do not prevent reads outside their directory. With external tools left provider-configured, browser and MCP access continue to follow provider configuration. The adapter rejects mandatory external-tool isolation for Codex instead of claiming its terminal sandbox covers browser/MCP. Antigravity now has a local macOS file-only managed executor; see the latest follow-up below. Other environments remain unsupported.\n\nCLI discovery does not verify every listed model's account entitlement. Real inference observations above apply only to the named models. Remote/container controls and all other model/effort combinations remain unverified. The later Antigravity section records its separately tested local executor. Mock tests cover launch flags, policy intersection, recovery and fail-closed behavior separately.\n\nOfficial references: [Codex configuration](https://learn.chatgpt.com/docs/config-file/config-reference), [Codex app-server](https://learn.chatgpt.com/docs/app-server), [Claude workflows](https://code.claude.com/docs/en/workflows), [Antigravity CLI reference](https://www.antigravity.google/docs/cli/reference/).\n\n\nRepeatable opt-in probes are in `scripts/live-permission-probes/`; their README explains fixture creation, provider usage and the bounded checks. Runtime bookkeeping is isolated from the application server. The validated catalog service was also exercised against the installed CLIs: 12 Claude models (11 reporting effort controls), seven Codex models, and six Antigravity models (per-model effort support unknown).\n\n## Antigravity Auto feasibility follow-up\n\nOn 2026-10-21, two additional real `agy` 1.4.4 turns used `gemini-3.8-flash-low`, `--sandbox`, `--disable-slash-commands` and a workspace custom agent allowing only `view_file`, with `subagent: false` and `commandExecutionPolicy: off`. Neither turn used `--dangerously-skip-permissions`.\n\nBoth turns read the fixture successfully. In the second, the model was asked to attempt a fixture write and a harmless terminal command **only if those tools were offered**. It reported them unavailable; only `view_file` calls were observed and no write marker appeared. This is evidence that a custom-agent allowlist narrowed the observed model surface. It is not a forced-call rejection test. The `init.tools` event still listed terminal, write, browser, MCP, workflow and native delegation tools; that event is therefore not an attestation of the effective allowlist. A fixture SessionStart hook did not produce its marker; this does not establish that all hooks are disabled.\n\nThe current supported CLI interface does not provide verified per-run settings isolation, hook suppression, or scoped MCP configuration. Documented permissions live in shared settings; allowed paths, domains and `unsandboxed` rules can change the terminal sandbox's effective boundary. Hooks may come from workspace, global settings and plugins. A custom-agent file cannot establish that these independent surfaces obey the run policy. See [CLI permissions](https://www.antigravity.google/docs/permissions), [terminal sandbox](https://www.antigravity.google/docs/sandbox), [custom agents](https://www.antigravity.google/docs/subagents/) and [hooks](https://www.antigravity.google/docs/hooks/).\n\n**Decision: keep managed Antigravity unavailable.** The adapter and routing now share an actionable diagnostic, and capability reporting labels its sandbox enforcement unknown rather than tested merely because help exists. Regression tests reject every trust level, read/write turns, Solo, resumed assignments and container/remote choices. The legacy skip-permissions environment setting cannot override that rejection.\n\nEnabling Auto requires either a supported invocation-scoped policy interface or a separately implemented isolated executor. That executor must keep policy/configuration outside worker write access, exclude ambient hooks/plugins/MCP, mediate every exposed tool (including browser and delegation), separate inference connectivity from command networking, and reapply/recheck policy on resume. A container alone does not establish these properties. Temporarily editing global settings would race other sessions; a prompt instruction or post-turn revert is not enforcement. No global settings were modified and no credentials were copied for this investigation.\n\n`scripts/live-permission-probes/antigravity.ts` repeats the bounded custom-agent check. It remains an opt-in research probe, not a supported managed execution path.\n\n## Isolated execution investigation\n\nThe next investigation on 2026-10-21 implemented an **offline macOS containment prototype**, separate from the adapter. `agy-isolation-fixture.ts` creates a fresh home, restrictive settings, empty MCP/hook configuration and a file-reading custom agent. Its OS policy allows workspace and scratch writes, protects the entire home from changes, excludes workspace customizations from reads, and denies network access for the whole process tree. The child environment is constructed from a small allowlist; it does not inherit provider keys, SSH sockets or Code Flow credentials. Standard system file reads and filesystem metadata remain allowed. This is not a complete cross-platform executor.\n\nReal kernel checks passed for a local workspace write and scratch write, and rejected:\n\n- Reading a fixture outside the workspace through a symlink.\n- Reading workspace `.agents/hooks.json`.\n- Changing settings permissions, overwriting settings, or replacing their parent directory.\n- Writing an outside sentinel or connecting to a local TCP server (zero accepted connections).\n\nThe actual Antigravity 1.4.4 executable returned its version inside this boundary. `agy models` exited unsuccessfully there; no inference was attempted. A separate discovery-only check with a fresh home and normal network access returned “Please sign in.” Existing host authentication was not copied. This establishes that starting the binary and enforcing fixture containment are possible, but it does not establish authenticated model access, native tool enforcement, hook suppression by the vendor runtime, or session recovery.\n\nAntigravity's [official authentication documentation](https://www.antigravity.google/docs/cli/install/) describes interactive sign-in and alternative API-key/enterprise ADC paths. Those alternatives are not interchangeable with the user's current subscription and were not enabled as a workaround.\n\nDocker is installed on this machine, but its daemon socket was unavailable before and after a Desktop startup attempt. The ordinary container wrapper is also insufficient by itself: its writable home/auth volume could retain ambient settings, and its network permits inference and shell traffic together. No container compatibility claim was made and no Docker security settings were relaxed.\n\n**At this stage Auto remained blocked (superseded by the login-reuse follow-up below).** To proceed, the isolated executor needs separately provisioned authentication, inference connectivity separated from command networking, and real tests of browser/MCP/delegation denial and fresh/resumed execution. A production container implementation would additionally need immutable policy mounts and a clean workspace customization namespace. The experimental profile deliberately grants no inference network access and cannot be promoted into production by a successful fixture test or a user-supplied readiness flag. The current adapter and recovery gates are unchanged.\n\n\n## Existing login reuse and managed file workers\n\nThe 2026-10-21 follow-up found a working path for **macOS / Antigravity CLI 1.4.4**. A clean `HOME` can reuse the existing login through the native macOS Keychain. Code Flow creates a temporary `Library/Keychains` symlink, grants read-only access to its target, and lets the official CLI call `/usr/bin/security`. It never extracts a password, exports an OAuth token, copies a credential file, or substitutes API-key billing. The CLI also needs reads of macOS security-module metadata and write-data access to the existing `mds.lock` cache lock. Broad cache writes are not granted. Merely linking legacy OAuth files did not authenticate the CLI.\n\n`src/adapters/agy-isolation.ts` now implements the narrowly scoped executor. Each turn gets a fresh private home, an allowlisted environment, immutable provider settings/custom agent, empty MCP/hook configuration and a protected default project record. The macOS policy restricts writes to the workspace (write turns only), runtime state, scratch and the one native cache lock. Policy files and ancestors, workspace customizations, Git metadata and host login files are protected. Only the verified CLI binary and Apple's security helper can execute. File tools are separately denied access to authentication, security-cache and runtime paths, including canonicalized symlink targets. Terminal, browser, MCP, workflow and native agent tools are excluded. The vendor's local `manage_task` bookkeeping tool can still appear; it is not the native delegation tool.\n\nInference networking remains enabled. This is **file-only execution**, not a general-purpose network-denied shell sandbox. Code Flow's check/setup commands retain their separately configured execution environment. System runtime files/metadata remain readable. This is not a complete VM boundary or a claim that every provider has identical isolation.\n\nBounded observations from the real CLI and `gemini-3.8-flash-low`:\n\n- Six model candidates were discovered inside this executor with the existing login.\n- A file-reading/editing turn completed, and its JSON response was parsed through Code Flow's normal structured-result fallback.\n- A later read-only turn read the saved result with fresh provider state. Neither turn retained a native session ID.\n- A synthetic outside-file symlink read failed. A direct write and reads/writes through a symlink into protected profile state matched the provider deny rule; sentinels remained unchanged.\n- The workspace hook marker remained absent. The local browser fixture received zero requests. Terminal/browser/native delegation tools were not used; this is observed behavior plus configured controls, not arbitrary forced-tool-call fuzzing.\n- Deterministic tests exercise both read/write OS profiles, protected files, outside paths, Git metadata, child execution, environment scrubbing, forged policy rejection, unknown CLI versions, native-session rejection, catalog separation and mocked Auto dispatch/recovery.\n- A real end-to-end Auto fixture completed with verdict **passed**: `gpt-6.1-sol`/low coordinated, `gemini-3.8-flash-low` made the requested README edit, and the configured Node check plus independent review completed. Five steps completed with no failed steps. The Antigravity step recorded `os-sandbox`, `macos-file-only-v1`, isolated catalog revision 1 and no native session ID. This verifies that the adapter is reached through actual Auto dispatch, beyond standalone CLI probes.\n\nManaged eligibility now permits this local executor and rejects remote/container execution, interactive approvals and unverified CLI versions. Every launch rechecks the environment; `CODEFLOW_AGY_SKIP_PERMISSIONS` cannot bypass managed controls. Inventory describes the file-only restriction to the coordinator. The effective executor version is recorded on steps, while model assignments retain the isolated catalog revision/environment/provenance. A native session supplied during recovery is rejected as unavailable so the engine rebuilds a fresh turn from portable context. No session is transferred across providers.\n\nThe exact CLI version is deliberately pinned. Updating Antigravity requires re-verifying controls before changing that pin. Windows/Linux, container authentication reuse, other models/efforts and arbitrary forbidden-call injection are unverified. Authentication tests used a valid existing login; expired-login refresh was not tested. Read-only Keychain file rules do not claim to constrain the macOS security service's own credential lifecycle. The old offline prototype and earlier blocked decisions above remain historical investigation evidence, not the current production path.\n\nRepeat the adapter probe with `node --import tsx scripts/live-permission-probes/managed-antigravity.ts`; repeat a real mixed-provider Auto fixture with `node --import tsx scripts/live-permission-probes/managed-antigravity-auto.ts`. Both consume provider usage and touch only synthetic project files. Official control references: [custom agents](https://www.antigravity.google/docs/subagents/) and [permission rules](https://www.antigravity.google/docs/permissions).\n\n## Antigravity permission and recovery review (2026-10-21)\n\nThe review found and fixed these gaps in the managed executor:\n\n- Version inspection previously executed the candidate binary outside isolation. It now runs in a separate default-deny profile without host HOME, credentials or networking. Version and model probes use tracked process groups, so timeout and server recovery cover their children too.\n- Nested workspace `.agents`, `.gemini` and `.git` paths were not protected. Kernel rules now cover nested paths and canonical symlink targets, including workspace names containing regex metacharacters.\n- Native authentication/refresh errors could be classified as ordinary failures, and raw diagnostics could reach run history. Managed output now withholds native diagnostics and buffers response text until success; expired/revoked tokens and locked Keychains produce a safe auth failure. No automatic interactive login, global settings edits, credential export or permissive retry was added.\n- Recovery could retain the signed-out cooldown after the user repaired the login. Explicit Resume now requires fresh isolated discovery before clearing that cooldown. Failed/offline discovery and stale cached models leave it blocked. The original assignments/provenance and partial work remain recorded, and new execution rechecks model eligibility and effective permissions with a fresh provider home.\n- Workflow revisions previously rejected even an unchanged idle member whose login became unavailable. They can now retain that member and stop cleanly; adding, reassigning or retrying work still requires an eligible provider and preserves user pins.\n- Unexpected runtime tool events stop the worker and override a subsequent success result. This is drift detection, **not pre-execution mediation**; protected native policy and the OS sandbox remain the enforcement boundary. Preparation cleanup also runs if the initial event listener throws.\n\nDeterministic tests cover auth-result and diagnostic-only failures, secret-marker non-persistence, fresh-turn recovery, unchanged permission receipts, forbidden tool drift, cleanup, and a full Auto stop/reload/login-repair/resume with preserved partial files and model pins. Actual macOS kernel probes cover nested configuration, policy and authentication writes, outside symlinks and unauthorized executable launch.\n\nThe real CLI 1.4.4 passed the updated adapter read/write probes with `gemini-3.8-flash-low`: the edit and structured result succeeded, the outside read was denied, the outside file stayed unchanged, no hook marker appeared, no browser requests reached the fixture, and no native session was retained. These are bounded existing-login observations. Expired-login refresh is still **not real-provider verified**: tests simulate failures and repair without revoking, replacing or exporting the user's login. The native CLI owns refresh inside the existing boundary; if refresh needs additional access, the turn fails instead of expanding permissions. macOS security-service credential lifecycle access remains a documented trust boundary. See the provider's [locked-Keychain troubleshooting](https://www.antigravity.google/docs/cli/troubleshooting/).\n\nThe updated real mixed-provider Auto probe also completed successfully: Codex coordinated, one isolated Antigravity turn edited the fixture, and independent review plus the check passed (five completed steps, zero failed steps). The worker recorded its catalog revision and `macos-file-only-v1` permissions without retaining a native session.\n\n## Non-destructive native refresh investigation (2026-10-21)\n\nThe [official CLI command reference](https://www.antigravity.google/docs/cli/reference/) and installed 1.4.4 help expose no documented force-refresh command. The [authentication guide](https://www.antigravity.google/docs/install?tab=cli) describes silent native-Keychain reuse and a destructive `/logout`; logout was not used. Inspection of the installed executable's static messages identified native refresh and credential-persistence diagnostics. This inspected the executable, not stored credential values, and does not establish a supported refresh API.\n\nThe new opt-in `scripts/live-permission-probes/antigravity-refresh.ts` runs only a normal `agy models` request in the production read-only sandbox with a fresh provider home. It observes a private native log using bounded, symlink-rejecting reads, emits only a closed set of boolean/status fields, and removes the temporary profile/log afterward. It does not export tokens, read Keychain values directly, change the clock or expiry, revoke credentials, change account settings, intercept TLS, or redirect OAuth endpoints. Normal token renewal is owned by the official CLI and may update its stored tokens; the probe does not promise byte-for-byte unchanged native credential storage.\n\nActual isolated requests returned six model candidates. Native logging explicitly reported a **non-expired loaded token**, with no refresh event, interactive authentication attempt or credential-persistence failure observed. This verifies ordinary login reuse. **Actual expired-token renewal and successful persistence remain unverified.** Repeating valid-token requests is not proof of refresh. The script returns exit code 2 when refresh was not observed, 1 when the native request/authentication failed, and 0 only when a native refresh event and successful discovery were observed. `credentialPersistence: not-attested` remains explicit even then; `expiredTokenRefreshVerified` additionally requires a native expired-at-load observation. Evidence cannot grant runtime permissions or clear a known failure on its own.\n\nThis investigation also found native messages for refresh persistence failure and fallback to credential files that were missing from Code Flow's error handling. Managed inference and isolated discovery now inspect their private native auth logs before accepting success. Known persistence/fallback or interactive-auth diagnostics reject the operation safely, even if inference returned success; raw diagnostics and structured failure payloads are not copied into run history. These are detection checks after native execution, not interception of the macOS security service or a guarantee that the CLI never attempted a fallback write. The existing sandbox and model-tool restrictions remain unchanged. Unreadable/missing/oversized logs are explicitly inconclusive to the refresh probe, never evidence that renewal succeeded.\n\nDeterministic tests cover valid-token reuse versus refresh, persistence/fallback errors, successful-result override, secret-marker non-persistence, bounded/symlink-safe log reads and interactive authentication observation. No live credentials were deliberately expired, replaced, exported or revoked to exercise these cases. Safe completion of expired-token verification requires observing a naturally due refresh or a provider-supported test interface; Code Flow does not bypass that constraint.\n\n\n## Explicit unrestricted native mode (2026-10-21)\n\nUser-authorized `antigravityExecution: \"native\"` reuses the installed CLI's existing host login and normal configuration. It deliberately removes Code Flow's file-only isolation and vendor sandbox for Antigravity, and records `agy-native-v1`, `isolation: none`, full trust, allowed command networking, provider-configured external tools and native delegation. This is an explicit permission exception, not verified sandbox enforcement. Default and historical isolated runs retain their original behavior.\n\nReal acceptance: `node --import tsx scripts/live-permission-probes/managed-antigravity-auto.ts --native` completed with five successful steps, one Antigravity write turn, final verdict `passed`, expected README marker and no native session reuse. The recorded tools included Antigravity `run_command`; the synthetic task requested its terminal test. Codex coordinated and independently reviewed. No login/logout command, credential export, forced expiry, revocation or user configuration edit was performed. Native catalog discovery also returned six models through the existing profile.\n\nBrowser, MCP, hooks and native child agents were not exercised in the live probe. They are not restricted by this mode; native child usage cannot be bounded by Code Flow's roster/invocation budget. Native expired-token refresh remains unverified. Deterministic tests simulate refresh-persistence failures, verify private diagnostic handling and cleanup, and exercise restart/recovery for both native and isolated modes without widening authority.\n\n\n## Antigravity 1.4.5 compatibility (2026-10-22)\n\nThe installed CLI updated to 1.4.5. The explicit supported-version list now includes 1.4.4 and 1.4.5; unknown versions still stop before an isolated launch. Agent mode preserves the Antigravity executor's native-delegation setting instead of applying Claude/Codex conversation permissions to it.\n\nThe production adapter's live write/read probe passed on 1.4.5: a workspace edit and structured answer succeeded, the outside symlink read produced a tool denial, the outside sentinel stayed unchanged, no hook marker appeared, no request reached the browser fixture, and no native session was retained. Kernel fixture tests still cover policy/configuration/authentication-write denials. A real Claude → Codex → Antigravity UI fork also recovered the carried conversation token. These observations retain the existing file-only scope and authentication limitations; they do not certify arbitrary future runtime versions or unrestricted browser/MCP behavior.\n\n## Task-scoped Antigravity tools (2026-10-22, later update)\n\nInteractive Antigravity Agent and Team turns can now use the shared Codeflow browser and computer bridge. Their isolated receipt is `macos-task-tools-v2` with `externalTools: task-scoped`; required `no-external-tools` retains the existing `macos-file-only-v1` profile and exposes no bridge. This supersedes earlier statements that isolated Antigravity is always file-only. Native shell/browser, ambient customizations and provider delegation remain unavailable in the isolated profile.\n\nA fresh private HOME contains sealed settings and a custom agent exposing only the known `codeflow_agent` tool names. HTTP MCP uses a loopback endpoint and an expiring invocation token. Its transport shares the same read/write, task ownership, approval, cancellation and revocation checks as the Claude/Codex stdio bridge. MCP resources are empty; discovery does not grant access to host resources. The native-mode path uses a private configuration overlay and leaves global settings unchanged. No ambient MCP server is imported into the isolated profile.\n\nComputer tools operate outside the workspace and remain gated by Full access for Antigravity, plus macOS Screen Recording and Accessibility permissions. Every action requires a fresh screenshot token and the same foreground app; foreground identity is checked before and after capture. Pixel coordinates remain the default for existing callers. Antigravity explicitly selects the normalized 0–1000 coordinate format, which the host converts to display points. Invocation-private offloaded screenshot files are readable so the model can inspect its tool results; private settings, MCP configuration and authentication data remain protected.\n\nSchema-constrained turns permit Antigravity's built-in `finish` control tool. Only its injected `toolAction` and `toolSummary` metadata are removed when not requested by the schema; other unexpected properties remain subject to validation. Successful browser/computer effects record tool names and timestamps, without arguments, as host evidence. These receipts establish that an action succeeded at the tool boundary, not that the user's task is correct; independent review and configured checks still apply.\n\nReal Antigravity 1.4.5 validation passed the shared browser form, file/folder uploads and authenticated downloads after human/Claude/Codex use. Real Agent and Team computer turns filled and submitted a dedicated native fixture, with exact output verified by Codeflow. Earlier failed foreground/coordinate attempts were retained and corrected without removing the app guard. See `output/agent-mode/agy-browser-structured-final.log`, `agy-transfers-live.log` and `agy-computer-normalized-final.log`, and the detailed validation record in `docs/plans/agent-mode-validation.md`. These are bounded observations of the installed versions, not certification of arbitrary native tools, future versions or expired-token renewal.\n","title":"Live worker permission verification"},{"group":"Engineering records","id":"dynamic-teams-plan-review-0","source":"docs/dynamic-teams-plan-review-0.md","body":"# Review 0: Dynamic teams implementation plan\n\n> Engineering record. Status, measurements and decisions below describe the recorded implementation or review. For current usage, start with the [documentation library](README.md).\n\nReviewed: `docs/dynamic-teams-implementation-plan.md`, uncommitted, dated 17 October 2026, written against `ee246c7` (the current `main`).\n\nLens: the product promise the plan exists to serve.\n\n> Give code-flow a task; it assembles an appropriate team from your available agents, coordinates their work, and delivers a verified result with reusable project knowledge.\n\nMethod: every claim the plan makes about the current code was checked against the working tree, and the four external references were fetched. Findings cite files and functions so they can be re-checked.\n\n## Verdict\n\nThe plan is a careful systems design. Its invariants are the right ones: no silent success, unknown is not a pass, persist before dispatch, at-least-once delivery with idempotent application, agents propose and the runtime verifies. The record model is coherent and the acceptance tests are specific. None of the factual claims about the codebase are wrong.\n\nThe problem is the order. The promise becomes visible to a user in Phase 5 of 8, after a storage migration (P1), a memory subsystem (P2), a new scheduler (P3) and a messaging layer (P4) have each shipped. The plan's own stated reason for a new recovery model is already met by the existing journal, so the biggest early investment rests on a justification that does not hold. The first user-visible milestone the plan names, \"the restart-safe question-and-answer scenario at the end of Phase 4\", is a reliability demo, not the promise.\n\nRecommendation: do not approve the phase order as written. Approve the invariants, the schemas, and the adapter capability inventory. Add a thin vertical slice of the promise on the existing engine before Phase 1, and let what it measures decide the storage, scheduler and messaging investments. Details are in \"Proposed re-sequencing\" below.\n\n## What the plan gets right\n\n- **code-flow owns the team.** This is confirmed by the vendor docs the plan cites: Claude's agent teams do not spawn under `-p`, which is how code-flow runs Claude, and they are experimental with no session resumption for in-process teammates. There is nothing to delegate to.\n- **Member identity separate from provider identity.** `AgentId` is `\"claude\" | \"codex\" | \"agy\"` (`src/adapters/types.ts:9`). `resolveAuto` deduplicates within a role, and `addNote` targets an `AgentId`, so a note to \"codex\" goes to whichever Codex turn starts next (`src/engine.ts:617`). Two Codex workers really are indistinguishable today for notes, usage and Insights.\n- **Verification in the runtime, bound to evidence.** This extends what `check()`, `integrity.ts` and `fixUntilGreen` already do, and keeps the product's distinguishing rule: a result is done when the tests pass, not when an agent says so.\n- **Turn-boundary messaging first, live steering as an enhancement.** Only Codex app-server can take input mid-turn (`turn/steer`); Claude takes its prompt on stdin and agy has no input path. Making correctness independent of live transport is correct.\n- **Candour about unknowns.** The adapter matrix marks untested capabilities unknown, nested vendor subagents are called out as unaccountable, and the plan refuses to estimate dates before the spikes. That is the right posture.\n- **Explicit outcomes with gaps.** \"A gap must never become success merely because all runnable tasks ended\" is the single most important product rule in the document.\n\n## Major findings\n\n### 1. The promise arrives last\n\nPhases 1 to 4 build durable state, memory, a scheduler and mailboxes. A user sees \"describe the task, watch the team work\" only in P5, and only from the normal composer in P7. The \"first collaboration release\" after P4 still requires a hand-configured team and a hand-supplied workflow, which is a worse version of the supervisor pattern that exists today.\n\nThe existing supervisor pattern already does more than half of the promise: a model decomposes the task, writes contracts, assigns workers, workers build in parallel worktrees, the runtime integrates, checks, and gets an independent review, and the user approves the split before anything runs (`src/patterns/supervisor.ts`). The delta to the promise is: the coordinator chooses members and models rather than assigning among a user-picked roster; more than one round; and members that can ask each other questions. The first two are cheap on the existing engine. Only the third needs new machinery, and the plan has not shown how often it is needed.\n\nRecommendation: build the delta first, on the engine that exists, and measure. See \"Proposed re-sequencing\".\n\n### 2. The new runtime's recovery justification is already met\n\nSection 2, point 2 says a dynamic workflow \"must not reconstruct the team by asking the model to plan again\", and presents this as the reason pattern replay is unsuitable. But `TeamContext.remember()` journals the result of every `decide()` and `ask()` under `kind:identity#occurrence` and returns the recorded value on resume (`src/engine.ts:714`). A coordinator's plan produced through `decide()` is replayed, not re-asked. As long as the pattern's control flow is a deterministic function of journaled values, which is the existing contract, a dynamic pattern recovers without re-planning. Claude's own dynamic workflows use exactly this model: replay by prompt equality, with the first differing agent and everything after it re-run.\n\nWhere the journal genuinely falls short is different and should be stated as such: long-lived members with mailboxes that outlive any one pattern pass, and event-driven wakeups that are not expressible as a linear replay. Those are the real reasons to consider a new runtime. Whether they are worth it depends on how often members need to talk, which nobody has measured.\n\n### 3. SQLite is a platform decision presented as a spike\n\nThe repository has five runtime dependencies and no native module. `package.json` says `node >= 20`, and CI runs Node 20 on Linux. `node:sqlite` does not exist on Node 20. That leaves three options, each a real cost: a native dependency with prebuilt binaries and a node-gyp fallback; a WASM build whose durability semantics differ; or dropping Node 20. Phase 0 defers the choice, but every later phase is designed around SQLite semantics (transactions, outbox, leases, migrations), so the \"spike\" is not free to fail.\n\nWhat the plan actually needs is narrower than a database: atomic commit of a small batch of related records, crash-safe recovery, and idempotent operation application. The server is already the single writer (the one-server lock in `src/store.ts`). One fsynced append of a JSON line holding the whole batch is atomic under that constraint, torn lines are already detected and skipped (`test/store-reliability.test.ts`), and in-memory projections rebuilt at load are how runs work now. The journal is already synced and awaited before the pattern moves on (`src/store.ts:167`); only the event log append is best-effort. The plan's phrase \"the current best-effort logging behavior is not sufficient\" is true of the event log and not of the journal, and should say so.\n\nRecommendation: state the required storage properties, not the technology. Default to extending the existing append-only store with batch records and a snapshot, and keep SQLite as the fallback if a measured need appears (query volume, project memory search at scale). If SQLite is chosen anyway, make the Node 20 decision explicitly in P0 rather than letting the driver choice make it.\n\n### 4. The reference designs use simpler persistence than the plan\n\nThe plan cites Claude agent teams and Claude dynamic workflows as references. Both were checked. Agent teams use one JSON file per mailbox and file locking for task claims; dynamic workflows persist per-agent results and replay by prompt equality; neither uses a transactional store, an outbox or leases. They also document limitations the plan should expect to inherit: task status can lag, teammates stop early, the lead can declare done before tasks finish. Those are prompt and policy problems, not storage problems, and the plan's verification-in-runtime stance is the right answer to them. The reference designs therefore support a lighter P1 than the plan proposes.\n\n### 5. \"Appropriate team\" has no definition\n\nThe promise's load-bearing word is \"appropriate\". The plan says routing must stop deduplicating providers and must expose fallback reasons, but it never states the composition policy the coordinator is constrained by. The existing product encodes composition wisdom that should carry over as rules, not be rediscovered by a model:\n\n- A reviewer or judge should be a different provider from the author (\"a good default is to have a different vendor review\", `docs/patterns.md`). The supervisor pattern warns when it cannot do this.\n- Routing skips providers at 90 percent of a usage window or recently limited (`src/routing.ts`), and prefers the provider whose code passes most often in this repo (`src/stats.ts`).\n- A small task uses one worker. The plan says this but gives the coordinator no rule for it.\n\nRecommendation: write the composition policy as validation rules in P0 (minimum and maximum members by task size signal, independence constraints for review, quota and ranking inputs, user-pinned choices), and have the coordinator propose within them. The acceptance tests in P5 then test the policy, not a model's taste.\n\n### 6. The plan-approval checkpoint is missing from the promise's flow\n\n`ctx.approve(\"plan\")` exists and the supervisor pattern uses it to show the split and contracts before any worker starts (`src/engine.ts:595`, `src/patterns/supervisor.ts`). For a product that assembles a team and spends the user's subscription quota, approving the team and plan is the most important control, and it already exists. The plan mentions \"existing review/permission choices\" in P7 and an \"awaiting input\" outcome, but never names the checkpoint: show the proposed members, models, tasks, acceptance criteria and estimated turns, and let the user approve, revise, or reject before dispatch. Section 3.1's \"requirements with stable IDs\" should be the editable list the user approves here, rather than a model-extracted artifact treated as ground truth.\n\n### 7. Project memory has no trust boundary\n\nRuns started from issues, PRs or alerts by people outside the repo are locked down: Safe mode, no `.env`, checks in Docker (`src/engine.ts:2168`). The plan adds `publish_finding` and promotion to project memory, which every later run reads. An untrusted issue that persuades a worker to publish a \"convention\" would then reach every future run in that project. Section 6.3 says memory is \"not an instruction source allowed to override the current user or permissions\", but offers no mechanism.\n\nRecommendation: memory entries carry the originating run's trust level; locked-down runs cannot promote automatically and their proposals wait for the user; retrieval marks entries by provenance; the lockdown rules extend to messages between members in a locked-down run. Add this to P2's acceptance tests. The existing `UntrustedSourceError` and `lockdown` fields are the hooks.\n\n### 8. Coordinator cost is unmeasured until Phase 8\n\nThe product's premise is subscriptions, and the vendor docs the plan cites say teams \"use significantly more tokens\". Each coordinator wake is a CLI session resume with full context. The plan measures \"coordinator overhead\" in P8, after the architecture is fixed. It should be the first number produced, because it decides how event-driven the coordinator can afford to be (per event, per wave, or per phase) and therefore how much of P3 and P4 is needed. A related gap: a long-lived coordinator session grows until the CLI compacts it; the plan's portable checkpoint is the answer but is never connected to the coordinator.\n\n### 9. Memory before execution is the wrong order\n\nP2 builds relevance selection, context budgets, manifests and promotion before P3 produces any workflow data to select from. Relevance cannot be tested without tasks, contracts and findings flowing. Split P2: the parts P3 and P4 need (private checkpoints, accepted team contracts, context manifest) come first; project memory, promotion, retention and deletion come after P5, when there are real findings to promote and the lockdown rule from finding 7 can be tested against real runs.\n\n### 10. The record model is heavier than turn-boundary messaging needs\n\nTwelve records and three stored state machines. At turn boundaries, \"delivery\" is \"included in the prompt of attempt X\", which the context manifest already records; a separate Delivery record only earns its keep with live steering in P6. Member status (idle, running, waiting) is derivable from the member's active attempt and open requests, so storing it invites drift. Message, Request and Delivery can start as one record with a type. Keep the plan's names but defer the splits until a phase needs them.\n\n## Minor findings\n\n- **Budget semantics change silently.** P3 says \"resume alone does not grant an unlimited new budget\". Today resume explicitly grants \"a fresh budget\" (`src/engine.ts:2285`, `docs/runs.md:82`), and that is a documented product decision. Changing it for team runs is defensible; say that legacy and team runs differ, and why.\n- **Verification binding is over-specified.** Binding evidence to commit, contract revision, workflow revision, check definition and environment fingerprint is five dimensions where two (commit SHA and check command, plus setup fingerprint when `.codeflow/config.json` sets one) cover the cases that occur. Start there; add dimensions when a test shows they are needed.\n- **`findPattern` is a small problem.** Four call sites (`src/server.ts:406`, `:956`, `src/engine.ts:1830`, `:2291`), not a broad audit. And an unknown pattern id already makes resume fail safely with \"Unknown pattern\". Recording team runs under an id older code-flow does not know gives P8's \"older executables must not execute team-v1 runs\" for free; say so instead of designing a schema-version rejection.\n- **SSE cursors.** `Last-Event-ID` is honoured only within one server start (`src/server.ts:626`). The plan's persistent cursor requirement is real; note that it benefits legacy runs too and could ship independently.\n- **`CODEFLOW_TOKEN` is already withheld from agents** (`src/env.ts:8`). P6 presents this as a new rule; cite the existing one and extend it to the scoped credential.\n- **Claude's write mode uses an `--allowedTools` allow-list** (`src/adapters/claude.ts:81`). P6's MCP bridge tools must be added to it per trust level; the capability inventory in P0 should include this.\n- **Three new source directories** (`src/team/`, `src/workflows/`, `src/memory/`) in a codebase that is flat apart from `adapters/` and `patterns/`. Not wrong, but the architecture doc's layout table will need the same treatment, and roughly forty new files for a 22k-line codebase is a scale the plan should state plainly.\n- **Mermaid diagram.** `Runtime --> UI` hides that the UI today is driven by the run snapshot plus SSE; the plan's \"projections\" should be additions to that snapshot where possible rather than five new GET routes with their own pagination.\n- **Section 5.1 operations through \"validated turn results first\".** This is the right first transport and is under-sold: it is also the only transport that works identically for all three CLIs, which makes it the one to design the protocol around, with live tools conforming to it rather than the reverse.\n- **\"Dynamic-worker prompt\" in P4** refers to `preamble()` in `src/patterns/prompts.ts:10` (\"nobody can answer questions\"). The Codex adapter has the same assumption when declining input requests (`src/adapters/codex-app-server.ts:242`). Both change together.\n\n## Claims checked against the code\n\n| Plan claim | Status | Evidence |\n|---|---|---|\n| `AgentId` means a provider | Confirmed | `src/adapters/types.ts:9` |\n| Role resolution deduplicates providers | Confirmed | `resolveAuto` in `src/routing.ts`: `[...new Set(chosen)]` |\n| Notes can target a provider | Confirmed | `RunManager.addNote(id, text, agent?: AgentId)`, `takeNotes` |\n| Replay uses operation identities plus occurrence counts | Confirmed | `remember()` keys `kind:identity#n`, `src/engine.ts:714` |\n| Prompt framing says nobody can answer | Confirmed | `src/patterns/prompts.ts:10` |\n| JSONL history, replay journal, server lock in `store.ts` | Confirmed | `src/store.ts:13`, `:167`, `:329` |\n| Journal writes are best-effort | Partly wrong | Journal entries are fsynced and awaited; the event log is best-effort (`writeHealth`) |\n| CI matrix Node 20/22 Linux, 22 macOS | Confirmed | `.github/workflows/ci.yml` |\n| No SQLite today | Confirmed | `package.json`: five runtime deps, none native |\n| Codex live steering exists; others do not | Confirmed | `turn/steer` in `src/adapters/codex-app-server.ts`; Claude prompt on stdin |\n| APIs assume `findPattern` succeeds | Confirmed, small | Four call sites |\n| Projects hold several repos | Confirmed | `Project.repos: string[]` in `src/projects.ts` |\n| Recurring findings threshold | Confirmed | `RECURRING = 2` in `src/learnings.ts` |\n| Four external references | All resolve | Claude workflows, Claude agent teams, Codex subagents, LangGraph memory |\n\n## Proposed re-sequencing\n\n### Slice 0: the promise on the existing engine\n\nAdd one opt-in pattern, \"Auto team\", written against `TeamContext` like every other pattern. No new storage, no mailboxes.\n\n1. **Plan.** The coordinator (user's choice or routed) runs one read-only `decide()` against a `TEAM_PLAN_SCHEMA`: acceptance criteria, members (`id`, role, provider, model, effort), tasks (`id`, instructions, owner, depends-on, owned paths), contracts. The runtime validates it against the composition policy from finding 5: installed providers, quota, member cap, acyclic dependencies, reviewer independence.\n2. **Approve.** `ctx.approve(\"plan\")` shows the team and plan. Revise and reject work as they do in the supervisor pattern.\n3. **Execute in waves.** Ready tasks run with `ctx.parallel()`; each task is an `ask()` with `identity: task.id` so the journal key is stable; each member keeps a `memberId` to session map so two Codex members stay distinct; each task's worktree starts from the integration branch head, which is the plan's \"artifact dependency through integration\".\n4. **Coordinate per wave.** After integrating and checking a wave, the coordinator gets one bounded `decide()` against a `REVISION_SCHEMA`: done, new tasks, reassignments, answers to questions members raised in their structured outcomes. Cap the rounds. This is event-driven coordination at wave granularity.\n5. **Verify.** Independent review by a different provider, bounded repair, final check, `setVerdict` and `setFacts`, result with gaps listed.\n\nMembers ask questions by including them in a structured turn outcome; the owner or coordinator answers in the next wave's prompt. Coarse, but it is turn-boundary messaging, and it produces the number nobody has: how often a member needs an answer before it can continue.\n\nRecovery is the existing journal. Interrupted runs resume without re-planning (finding 2).\n\nWhat Slice 0 proves or refutes, with fake agents in CI and a few real opt-in runs:\n\n- whether a coordinator produces teams a user accepts at the approval step, and how often it is revised;\n- coordinator turns and tokens per task as a fraction of worker spend (finding 8);\n- how often a wave stalls on a question, which decides whether P4 is needed at all or whether wave-granularity is enough;\n- whether two same-provider members cause any confusion beyond notes and Insights, which sizes P1.\n\nExit criteria: fake-agent run of the section 9 scenario minus restart-under-delivery; a user can start it from the composer behind an opt-in flag; measurements recorded in the plan.\n\n### Then\n\n- **P0 in parallel with Slice 0:** schemas (Slice 0 needs them), adapter capability matrix, composition policy, verification policies by task kind.\n- **Storage decision after Slice 0,** from the required properties in finding 3. The likely outcome is batch records on the existing store plus a per-run snapshot. If SQLite wins, make the Node 20 call explicitly.\n- **P3 before P2,** and P2 split as in finding 9.\n- **P4 only if Slice 0 shows waves are not enough.** If a member rarely needs an answer mid-task, the mailbox layer can be a later enhancement rather than a prerequisite for the MVP.\n- **P5 becomes** hardening Slice 0's coordinator (reassignment with handover, member retirement, strategies as reusable sub-plans) rather than building it.\n- **P6, P7, P8** as planned, with finding 7's trust rule in P2's tests and finding 6's checkpoint in P7's composer work.\n\n## Questions to answer before approval\n\n1. What is the composition policy? Write the rules the coordinator must satisfy; they are the product.\n2. What team-messaging frequency would justify P4's mailboxes over wave-boundary answers? Pick the threshold before measuring.\n3. Is dropping Node 20 acceptable if the storage spike points at `node:sqlite`? If not, say now that native and WASM drivers are the only candidates.\n4. Who may promote to project memory from a locked-down run? The answer should be \"nobody, without the user\".\n5. What does the user see and approve before any member is dispatched, and does changing the goal mid-run go through the same checkpoint?\n6. What is the cost ceiling for coordination as a share of a run's agent turns, and what does the coordinator do when it reaches it?\n\n## Notes on the document itself\n\n- Tight, well-structured, and unusually honest about unknowns. The invariants in 3.3, 3.5, 5.2 and 5.3 should be lifted verbatim into the architecture doc when any of this ships.\n- \"Likely files\" per phase is useful. Add \"likely files removed or shrunk\": the plan adds a great deal and retires nothing, and the supervisor pattern in particular should be slated to become a strategy under the new mode rather than a parallel implementation.\n- The `docs/README.md` link added alongside this plan describes it as proposed. Keep that word until Slice 0 has numbers.\n\n[Documentation library](README.md) · [Development guide](architecture.md)\n","title":"Review 0: Dynamic teams implementation plan"},{"group":"Engineering records","id":"project-teams-implementation-plan","source":"docs/project-teams-implementation-plan.md","body":"# Standing project teams implementation plan\n\n> Engineering record. Status, measurements and decisions below describe the recorded implementation or review. For current usage, start with the [documentation library](README.md).\n\nStatus: implemented as an opt-in preview on 19 October 2026, synchronized with `origin/main` at `7933ecf` before PR review. The requirements below retain the design context. The implementation record here and [the operating guide](project-teams.md) describe the implemented interfaces and deliberate initial boundaries. Real-provider usefulness remains an unmeasured rollout gate.\n\n## Implementation record\n\n| Phase | Implemented result |\n|---|---|\n| P0 | Strict mission/report/aggregate contracts, provider eligibility, immutable mission history, private atomic store, operation IDs, repository uniqueness and rolling reservations |\n| P1 | Managed Automations targets, stable scheduled occurrence IDs, shared timer, engine dispatch barrier, durable launch receipt, exact reconciliation and fail-closed unknown launches |\n| P2 | Existing Auto team investigation workers, coordinator report synthesis, independent report review, typed discovery receipts and no-diff preservation |\n| P3 | Deterministic ranking, verified source quotes, deduplication, one implementation child, enforced scope/size/call limits, final-commit checks/review and approval-bound publishing |\n| P4 | Project setup, provider/model choices, existing schedule editor, opportunity actions, run evidence links, inbox summaries, managed automation links and scoped shared stream |\n| P5 | Owner feedback and defer expiry, bounded recent local activity/commit context, unchanged-input detection, notification reuse/deduplication and conservative restore handling |\n| P6 | Fault/restart/scope/budget/privacy/compatibility tests, browser workflow and connection checks, bounded fake/real evaluation runner, documentation and an opt-in release flag |\n\nImplementation choices relative to the proposed design:\n\n- `Trigger.target` identifies the managed team while `Trigger.run` retains a minimal display/routing projection for compatibility with existing automation screens. It contains no duplicated execution configuration. Trigger IDs are deterministic from team IDs.\n- The aggregate service is in `src/project-teams.ts`, scope guards in `src/project-team-policy.ts`, and the UI in `ui/project-teams.tsx`; separate context/discovery/state modules were unnecessary for the current bounded workflow.\n- Report synthesis is an explicit coordinator read turn followed by the existing independent reviewer. Discovery therefore requires at least eight allocated calls under Auto team's coordinator allowance. Report defects stop the cycle; an owner can start another bounded discovery rather than silently repair it.\n- Pausing between stages finishes discovery as proposals. After activation the owner can build a proposal explicitly. A changed base/configuration requires rediscovery; the initial preview does not attempt an automatic rebase or extra revalidation allocation.\n- Owner revision launches a new whole team from the pinned base, with previous-result references and feedback. It preserves scope and independent verification without relying on the generic single-writer follow-up route.\n- Initial evidence is local Git source/history, bounded owner-origin local run summaries, accepted memory through existing Auto team retrieval, and durable owner decisions. External issue/feedback ingestion and product analytics are not enabled sources in this preview.\n- Mission model choices reuse the existing model controls. Setup uses existing Safe/Standard/Trusted host execution; remote worker, agent-container, ACP and extension execution are excluded from managed cycles. Owner-chosen setup/check commands use the current host execution boundary.\n- APIs use `POST /:id` for pause/activate/edit, `GET /:id/preview`, and `POST /:id/opportunities/:opportunityId` for decisions; see the operating guide for the complete actual table.\n- Core tests and synthetic fixtures establish mechanics. The ten-cycle synthetic pilot across two fixtures produced two verified drafts and eight unchanged-input no-ops. It is not the proposed real-provider/owner-quality pilot below. The feature stays opt-in until that evidence is available.\n\n\nCode Flow will let an owner attach a standing agent team to a repository, give it a product mission, and choose a schedule. The team will discover opportunities, select worthwhile work, build bounded improvements, and bring concrete results back for review. The owner decides what becomes part of the product.\n\nBuild this as a project service above the existing Automations and Auto team systems. Automations owns the schedule. Auto team owns agent execution, worktrees, messaging, checks, and independent review. The new service owns the mission, opportunity backlog, cycle state, admission decisions, and feedback across runs.\n\n## 1 Product behavior\n\nThe owner opens a project and selects **Set up project team**. Setup captures the repository and target branch, intended users, product mission, current priorities, excluded work, agent preferences, schedule, and budget. Code Flow may draft a product description from repository evidence, but that description becomes direction only when the owner saves it.\n\nFor example:\n\n> Improve the first experience for developers using this product. Investigate confusing setup, broken flows, and small usability improvements. Work weekly. Build one small improvement that I can try, and explain why you chose it.\n\nA cycle produces up to three useful opportunities and, when permitted, one draft implementation. An opportunity contains the problem, affected users, supporting evidence, expected benefit, proposed solution, effort estimate, acceptance criteria, and uncertainties. An implementation adds a branch, verification evidence, and a way to inspect or try the result.\n\nThe project page shows **Opportunities**, **In progress**, **Ready for review**, and **History**. The existing run view remains the place to inspect individual agents, messages, tasks, checks, and changes. The existing review inbox receives actionable results and blockers.\n\nThe team remembers owner feedback. “Not relevant to our users” influences future selection; “later” defers an opportunity; “do not suggest this again” suppresses it. Feedback does not silently change the team's execution authority.\n\n### Initial defaults\n\nThese are proposed defaults to validate in a pilot, not measured optimal settings.\n\n| Setting | Initial value |\n|---|---|\n| Scope | One repository and one explicitly selected base branch per standing team |\n| Team count | One active standing team per canonical repository, including across projects |\n| Cadence | Weekly at a user-selected local time, with an explicit IANA timezone |\n| Other schedules | Daily, selected weekdays, or an interval using the existing schedule editor |\n| Autonomy | Discover and build small improvements within saved scope; proposals only is also available |\n| Visible output | At most three new opportunities and one new implementation per cycle |\n| Active work | One active cycle per team; no overlapping standing-team builds for its repository |\n| Review backlog | At most two unresolved implementations, including approved but unshipped work |\n| Opportunity backlog | At most twelve open opportunities; defer discovery when the backlog needs triage |\n| Cycle budget | At most 24 CLI invocations and 60 active run minutes |\n| Allocation | Discovery: 8 invocations and 15 minutes; implementation: 16 invocations and 45 minutes |\n| Rolling budget | At most 48 invocations and 120 active run minutes over seven days |\n| Shipping | Owner action through existing PR or merge flows |\n\nChanging cadence does not expand the rolling budget. Setup previews the next runs and explains when a chosen daily schedule can exhaust its allowance. The app must be running and its execution machine available, as with current automations.\n\n### First release boundary\n\nDeliver persistent project responsibility, scheduled discovery, bounded automatic implementation, reviewed results, and feedback in the first release. Use local repositories and the installed providers eligible for unattended Auto team runs. Start with the validated built-in CLI adapters; a provider being connected or available for an interactive run does not make it eligible for a standing team. A minimal team is sufficient; additional specialists are created only when useful within the allocation.\n\nKeep cross-repository initiatives, remote-worker execution, multiple standing teams on one repository, automatic shipping, autonomous external posting, analytics integrations, and an always-running agent process for later releases. A project containing several repositories can initially select one repository for its standing team.\n\nACP/OpenCode connections remain available through the existing interactive experience. Their current adapter requires Full access and rejects automated triggers, so the first standing-team release cannot use them for discovery, implementation, or **Run now**. Adding unattended ACP support is a separate adapter capability milestone; this feature must not bypass that restriction.\n\nThe team identity persists as mission, preferences, decisions, and knowledge. Provider sessions and private worker conversations remain scoped to individual runs. Persistent responsibility does not require a permanently running CLI process.\n\n## 2 Existing foundations and required extensions\n\n| Existing component | Current behavior | Planned use or change |\n|---|---|---|\n| [Projects](../src/projects.ts) | Groups repository paths and remembers composer/team defaults | Link a standing team to explicit project membership; keep its mission separate from last-used composer defaults |\n| [Automations](../src/automation.ts) | Schedules, timezone handling, missed-run rules, manual runs, webhook history, per-trigger busy checks | Add a managed project-team target and cycle receipts; retain the existing clock and schedule computation |\n| [Auto team](../src/patterns/auto-team.ts) | Plans work, schedules dependencies, routes questions, integrates changes, checks and independently reviews them | Run discovery and implementation as separate Auto team runs with server-owned contracts |\n| [Auto team model](../src/patterns/auto-team-model.ts) | Validates plans, revisions, ownership and outcomes | Validate discovery-only plans, typed opportunity output, and implementation scope on initial plans and revisions |\n| [Provider registry](../src/adapters/index.ts), [routing](../src/routing.ts) and [team capabilities](../src/adapters/team-tools.ts) | Registers built-in, extension and ACP provider IDs; resolves automatic choices and reports capabilities | Derive a restricted unattended inventory before resolving any team role; preserve dynamic provider IDs and validate them against that inventory |\n| [ACP connections](../src/acp-connections.ts) and [shared ACP adapter](../src/adapters/acp.ts) | Settings-managed connections, discovered models, invocation fingerprints and local Full-access execution | Reuse connection discovery/display; show why ACP is currently ineligible for standing teams without changing its execution policy |\n| [Engine](../src/engine.ts) | Runs providers, limits invocations, manages worktrees, approvals, follow-ups and result triage | Add standing-team provenance, durable admission hooks, and guarded continuation/result handling |\n| [Team memory](../src/team-memory.ts) and [memory store](../src/memory.ts) | Attributed private, team and project memory with revision and applicability checks | Supply accepted knowledge to later cycles; keep candidate ideas and product decisions in their own records |\n| [Deferred runs](../src/deferred-runs.ts) and [repository coordination](../src/repo-coordination.ts) | Durable claims, bounded execution, explicit unknown states and recovery | Reuse their storage and launch-receipt patterns without adding a competing agent queue |\n| [Coordination API](../src/coordinated-api.ts) | Validates exact owned run identity, pinned verification and shipping evidence | Extract narrow receipt/result-validation helpers where genuinely shared |\n| [Review inbox](../src/review-inbox.ts) | Prioritizes questions, blocked work, failures and pending run results | Add opportunity decisions and group child-run attention under a project-team cycle |\n| [Notifications](../src/notifications.ts) | Configured deliveries with deduplication and retries | Reuse delivery handling for meaningful cycle changes and avoid duplicate child-run notifications |\n| [Verification](../src/verification.ts) | Required gates, criterion mappings, pinned reports and merge-result checks | Require real evidence for implementation readiness and revalidate moving results |\n| [Project UI](../ui/project.tsx) and [automation UI](../ui/automations.tsx) | Project overview, schedules, review results and run navigation | Add the team workspace and expose its schedule as a managed automation |\n| [Shared browser stream](../ui/stream.ts) and [stream server](../src/stream.ts) | Multiplexes run, Team inbox and terminal subscriptions across owner tabs | Carry project-team updates through the existing broker and preserve its connection and identity boundaries |\n\n### Behaviors that cannot be reused unchanged\n\n1. `RunManager.startResolved()` clears plan approvals for triggered runs. A saved team policy must authorize autonomous work before launch; “this is scheduled” is insufficient authority to implement any idea the model invents.\n2. `Automations.busy()` checks the most recent run for a trigger. A cycle can contain discovery and implementation, so durable cycle state must remain authoritative between those runs.\n3. `Automations.save()` currently logs persistence errors without rejecting the operation. Team activation and launch admission cannot depend on a successful-looking save that failed. Add a strict committed-write path and use it for managed triggers.\n4. `toInbox()` can archive unchanged automation results and remove their worktrees. Discovery findings must be durably imported before normal cleanup, and evidence-only results must not disappear because their Git diff is empty.\n5. `triage({decision: \"revise\"})` currently routes a revision to a writer follow-up. Project-team revisions must preserve or renew whole-team verification before becoming ready again.\n6. `Projects.place()` supports lenient placement for triggered runs. Standing-team children must use `requireProjectRepo` and explicit membership checks, with no fallback into another project after deletion or removal.\n7. Current notification payloads and review grouping are run-oriented. A cycle with two child runs should present one coherent result while preserving direct access to child failures and questions.\n8. `validateRoles()` uses requested/repository trust when resolving providers, while `startResolved()` applies the unattended Full-to-Trusted cap later. Managed requests must calculate effective execution eligibility before role selection and validate explicit choices too; otherwise an ACP selection can pass preparation and fail only at engine launch.\n\n### Provider eligibility on the current baseline\n\nThe updated Auto team schema accepts an exact provider ID from the eligible inventory instead of a fixed three-provider enum. Preserve that schema behavior in the new discovery contract. Validate IDs through the registry and host policy; do not restore a hard-coded schema enum or accept arbitrary strings merely because they match an `acp:` or `plugin:` prefix.\n\n| Provider class | Initial standing-team treatment |\n|---|---|\n| Built-in Claude, Codex and Antigravity CLI adapters | Eligible when installed, selected by the owner, compatible with the effective execution profile and within observed capacity; retain current adapter-specific limits |\n| ACP/OpenCode connections | Ineligible for all managed phases because the current engine rejects automated triggers and requires Full access |\n| Owner-installed provider extensions | Keep out of the initial standing-team pool until their unattended execution and structured-outcome behavior have explicit compatibility evidence; ordinary extension runs retain their existing behavior |\n\nAdd a small shared eligibility helper, proposed as `src/provider-eligibility.ts`, to compute provider IDs and rejection reasons from the execution context. Reuse current registry, availability and adapter restrictions. Keep the standing-team compatibility allowlist as an additional policy input, so factoring the helper does not change existing interactive or ordinary-automation behavior.\n\nResolve the effective unattended trust profile before invoking `resolveAuto()`. Apply the helper to coordinator selection, worker/reviewer pools, explicit IDs, omitted/default pools, inherited project presets, planning inventory, coordinator revisions and every later dispatch. Filter defaults with a visible explanation; reject an explicitly saved ineligible selection before activation. Recheck after Settings changes. A missing or changed selected provider blocks work or uses only an already-authorized eligible fallback; it cannot silently expand the pool or enable Full access.\n\nProvider display names and model catalogs come from the existing registry/Settings flow. Preserve opaque model IDs and use the current provider-specific settings validators. A successful ACP connection probe establishes protocol/session/catalog observations, not available inference quota, sandboxing or unattended compatibility. Scoped live tools and constrained JSON are separate capabilities: structured outcomes can use prompt-and-validation fallback where supported, while an unavailable tool bridge stays unavailable.\n\nA future ACP compatibility milestone must establish an accepted unattended execution/isolation policy, report validation and retries, cancellation/recovery and accurate usage accounting. Preserve `Run.acpConnections` fingerprints and `assertAcpBindings()` if that milestone is later delivered; fingerprints pin command/arguments, not executable bytes or external CLI configuration. Two connection IDs are not evidence of independent model vendors. Until that work is complete, no managed request may omit its trigger metadata to evade the engine's ACP restriction.\n\n## 3 Architecture and execution ownership\n\nAdd a `ProjectTeams` service. It performs deterministic state transitions, accepts validated model output, and prepares normal run requests. It does not invoke provider CLIs directly or implement a second task scheduler.\n\n```mermaid\nflowchart TD\n Owner[Owner saves mission and limits] --> Team[Standing project team]\n Schedule[Existing Automations scheduler] --> Cycle[Durable cycle admission]\n Team --> Cycle\n Cycle --> Discovery[Auto team discovery run]\n Discovery --> Opportunities[Validated opportunities and evidence]\n Opportunities --> Selection[Policy and backlog checks]\n Selection --> Proposal[Proposal for owner decision]\n Selection --> Build[Auto team implementation run]\n Build --> Verification[Existing checks and independent review]\n Verification --> Inbox[Project workspace and review inbox]\n Proposal --> Inbox\n Inbox --> Decision[Owner feedback or shipping action]\n Decision --> Team\n Decision --> Opportunities\n```\n\nUse one managed automation for each team. A cycle contains a discovery child and zero or one implementation child. Completion events advance the cycle; periodic reconciliation through the existing automation tick recovers missed events. The cycle service subscribes to `RunManager.subscribeAll()` and treats events as wakeups, not as the authoritative record of completion.\n\nBefore advancing a child, reload its settled state and validate identity, evidence, and persistence. The run's `endedAt` and final bookkeeping must be present; an intermediate `status: done` event alone is insufficient.\n\nThe existing Auto team task scheduler remains responsible for members and task dependencies within each child. Existing global agent slots continue to govern capacity across manual and scheduled work.\n\nRegister the service with `startPolicyBackground()`, `pausePolicyBackground()` and shutdown in `src/server.ts`. Stop and drain in-flight preparation/launch work before filesystem-policy changes, as existing background services do. Reconciliation can use the existing tick and subscriptions; it needs no additional scheduling clock.\n\n## 4 Data model and persistence\n\nAdd versioned types in `src/project-team-model.ts`. Keep the following objects distinct so that product feedback cannot accidentally become execution permission or a verified technical fact.\n\n| Record | Required information |\n|---|---|\n| `ProjectTeam` | ID, revision, project ID, canonical repository identity/path, name, status, managed trigger ID, current mission revision, creation/update times |\n| `ProjectTeamMission` | Immutable revision, intended users, mission, priorities, exclusions, base branch, enabled evidence sources, provider/model preferences, autonomy mode, execution policy and budget settings |\n| `ProjectTeamCycle` | ID, team ID, immutable mission snapshot/hash, trigger occurrence or manual operation ID, pinned base SHA, source manifest, eligible-provider snapshot with reasons and effective trust, child claims/runs, state, selection receipt, usage reservations and timestamps |\n| `ProjectOpportunity` | ID/revision, team/repository identity, stable problem key, category, problem, affected workflow, evidence refs, hypothesis/observation distinction, proposal, benefit, effort/risk estimates, acceptance criteria, rank explanation, status and source cycle |\n| `ProjectTeamDecision` | ID, opportunity and exact revision, authenticated actor, decision, optional reason, result commit/report hash where applicable, timestamp and operation ID |\n| `ProjectTeamLaunch` | Cycle/stage/attempt, claim ID, input hash, reserved allowance, run ID if known, receipt state and reconciliation data |\n| `ProjectTeamResult` | Opportunity revision, child run ID, owned result branch and SHA, verification/report references, independent-review identity, preview references, readiness and shipping receipts |\n\n`ProjectTeamMission` includes two autonomy modes: `propose` and `build-within-scope`. Store explicit allowed work categories, included/excluded relative paths, maximum change size, pinned verification profile, and the owner's chosen existing execution/isolation settings. Semantic judgments such as “small UX improvement” need an explanation and conservative escalation; they are not completely enforceable through a file count.\n\nAdd optional `Run.projectTeam` metadata containing `teamId`, `cycleId`, `missionRevision`, `phase`, `claimId`, `attempt`, and, for implementation, `opportunityId` and `opportunityRevision`. Preserve it in run snapshots, replay, exports, and UI types. The server constructs this metadata. Generic run-creation bodies, webhook payloads, agent messages, and collaboration tools cannot supply or alter it.\n\n### Store design\n\nUse `CODEFLOW_HOME/project-teams/<team-id>.json` as one versioned aggregate per team for configuration revisions, opportunities, cycles, decisions and launch claims. Follow the strict atomic write pattern already used by deferred/coordinated services: serialize mutations, validate bounded input, write a private temporary file, sync it, rename it, and sync the directory where supported. Publish in-memory state only after the write succeeds. Use optimistic revisions and operation IDs for HTTP mutations.\n\nA single team aggregate makes opportunity selection, budget reservation and stage claiming one atomic mutation. Use the existing single-server ownership model plus an in-process canonical-repository guard for the initial one-active-team constraint. Validate all teams at startup; duplicate active ownership is an explicit configuration blocker.\n\nBound the aggregate, history and payload sizes. Start with a 16 MiB file ceiling, 100 retained cycle summaries and 200 full opportunity records. Store large evidence in existing run artifacts or bounded evidence files with immutable digests, not in the aggregate. Preserve compact rejection/deduplication records separately within a fixed cap; reaching retention limits must not silently forget “do not suggest again.” Archive or request cleanup before that cap is exceeded.\n\nInclude the new aggregates and retained evidence in the existing backup/export and maintenance inventory. Evidence cleanup follows live references from opportunities and decisions, so archiving a discovery child cannot break a pending review card. Redaction and deletion must cover copied evidence as well as run links.\n\nPersist provider references and execution-binding metadata, not copied ACP executable configuration or credentials. Keep `acp-connections.json` excluded from portable backups under the current maintenance policy. Restoring a team on another host leaves it paused until project paths, providers, referenced connections and execution settings are explicitly reconciled; portable mission data cannot authorize a new host invocation.\n\nDo not automatically delete pending results, active claims, or owner decisions to meet a size limit. Keep all claims needed to account for unresolved work. A corrupt/unsupported aggregate blocks that team's launches while leaving ordinary runs and other teams usable.\n\n## 5 Scheduling and launch recovery\n\n### Managed trigger contract\n\nExtend the trigger model into an explicit union. Existing records without a discriminator remain saved-run triggers; existing sweeps retain their behavior. A new `kind: \"project-team\"` target stores `teamId`, `projectId`, name, enabled state and schedule, without a duplicated saved run configuration. Its execution configuration comes from the current saved mission.\n\nManaged triggers initially reject webhooks. Schedule and **Run now** both call the same `ProjectTeams.requestCycle()` admission path. Add a managed-target callback to `Automations`; keep provider launching in `RunManager`. Update `FireResult`, event history and preview responses to include `cycleId`, child run IDs when available, and meaningful dispositions such as `accepted`, `skipped`, or `blocked`.\n\nUse the existing `Schedule`, `validateSchedule()`, `previewSchedule()`, `nextFire()` and missed-run behavior. Supply a stable occurrence key from the trigger ID and the scheduled UTC `nextAt` captured before advancement. Manual requests use a client operation ID; retries reuse it. Repeated ticks, duplicate HTTP delivery or restart recovery return the same cycle for that key.\n\nCreation spans two existing stores. Use a recoverable activation sequence: create a draft team, create a disabled managed trigger idempotently, save the linkage, then activate the linked pair. On restart, incomplete setup remains disabled until reconciliation verifies both records. A trigger with no valid team or a team with a missing trigger cannot launch. Managed-trigger enable, schedule changes and deletion route through the team service so Settings cannot bypass team state.\n\nUpdate all trigger readers, previews, project filters, serializers and UI types that currently assume `trigger.run` exists. Preserve old saved-run/sweep payloads and their tests. Unknown target kinds remain disabled and diagnosable; they must never be interpreted as ordinary saved prompts. The managed-trigger persistence path commits or fails explicitly without overwriting the last committed state or poisoning subsequent writes.\n\n### Admission and advancement\n\nAt cycle admission, check active status, exact project membership, canonical repository, configured branch, effective unattended provider eligibility/readiness, filesystem policy, remaining budget and backlog capacity. Pin the base commit, configuration and allowed provider choices. Never use whichever branch happens to be checked out later. Do not implicitly pull, rebase, or fetch; show the observed ref and last known freshness. Optional remote refresh belongs in a later explicit source setting.\n\nRecheck these conditions before each child launch and continuation. A later restrictive policy change or repository removal blocks further work even though a historical mission snapshot exists. An expanded mission requires a new cycle or explicit revised request; it cannot enlarge an already-admitted allocation.\n\n| Failure point | Required behavior |\n|---|---|\n| Before a durable launch claim | No run starts; a later tick can retry admission |\n| Claim saved, no dispatch attempted | Retry only when the service can prove dispatch never began |\n| Dispatch started, receipt missing | Mark `unknown`; inspect exact owned run identities and retain reservations |\n| One matching persisted child found | Attach the child idempotently and continue observing it |\n| No conclusive match or multiple matches | Block automatic replay; expose recovery with the available evidence |\n| Child finished before cycle state was updated | Import its validated result once, using run ID, report hash and input revision |\n| State write fails | Stop dependent dispatch and expose a storage blocker |\n\nExtract a narrowly scoped receipt helper from the existing coordination/deferred patterns. Add an internal admission barrier for managed child starts so the initial run identity and claim binding are synced before any CLI execution begins. Do not acknowledge an admitted launch based solely on best-effort event logging. Prove this ordering with an instrumented fake CLI.\n\nThis supports idempotent control transitions, not a promise of exactly-once external side effects. A process can act immediately before a crash. Preserve unknown states and existing execution-isolation limits.\n\n### Lifecycle rules\n\nCycle states are `requested`, `discovering`, `selecting`, `building`, and terminal `completed`, `no-op`, `skipped`, `blocked`, `failed`, `cancelled`, or `unknown`. A cycle completes when its results are durably published; an opportunity can remain awaiting human review after the active-cycle lock is released.\n\nPause prevents new child starts and new cycles; running children can finish and publish their result. **Stop current work** also invokes existing cancellation and retains partial work. **Resume** rechecks policy and capacity. Automatic restart never grants a fresh budget to a blocked or interrupted child. An explicit resume is a new bounded allocation with cumulative usage visible.\n\nA paused cycle waiting between stages keeps its last stage and a durable wait reason; it neither holds a provider slot nor silently becomes a new cycle. **Run now** requires an active team. Repeated failures with the same unresolved cause block further automatic attempts until the relevant configuration/input changes or the owner explicitly retries; a new scheduled date alone does not authorize repeating a failed build.\n\nProject deletion or repository removal blocks pending dispatch and cancels active owned children through existing cancellation. Retain history and branches for inspection. Archiving a team disables its trigger without treating its unmerged results as shipped or silently deleting them.\n\n## 6 Discovery and opportunity selection\n\n### Evidence collection\n\nBuild a bounded source manifest from the selected repository, recent changes since the previous cycle, existing run findings, open opportunity/decision records, accepted project memory, and explicitly enabled issue/PR integrations already available in Code Flow. Also include known manual work so a team can avoid proposing work already underway.\n\nRecord source IDs, revisions, timestamps, branch/commit applicability, content digests, and omissions caused by limits or unavailable connections. Resolve evidence references in the host; the model cannot assert that an invented issue, path or test result is verified. External text retains its provenance and current untrusted-source treatment through summaries and candidate reports.\n\nRepository access is sufficient for technical findings. Product feature ideas without user evidence are labeled hypotheses. Live app exploration is optional and uses an owner-configured launch profile and existing browser capability where available. Do not claim a workflow was exercised when the team only inspected source.\n\nSkip expensive discovery when the input fingerprint is unchanged: base commit, mission revision, enabled source revisions, decisions and relevant memory revisions. Add a configurable full rescan interval, initially four weeks, so unchanged code can still receive a fresh review. A skipped or empty cycle is a successful outcome and creates no artificial improvement quota.\n\n### Auto team discovery contract\n\nAdd a server-owned discovery contract to Auto team rather than registering a new agent runtime. It must:\n\n- Permit only `investigation` tasks in both the initial plan and every coordinator revision.\n- Designate one reporting task whose dependencies include all required investigations.\n- Use a discovery-specific plan/outcome schema with a versioned `project-opportunities/v1` report. Keep ordinary and legacy Auto team schemas compatible.\n- Preserve the current dynamic provider-ID schema and validate every initial or revised member against the recorded unattended inventory. Do not assume a provider supplies native constrained JSON, live steering or scoped team tools.\n- Bound candidate count, text lengths, source references and acceptance criteria; publish at most three new visible opportunities per cycle.\n- Collect the report from structured output and journal it. Never parse proposals from the final Markdown summary or scrape private memory.\n- Include the exact report and digest in independent review context; publish only the report revision that was reviewed.\n- Allow an empty report with an evidence-based reason.\n\nThe reporter can be a worker or the coordinator when the allocation permits. Initial planning and coordinator synthesis both count against Auto team's existing coordination limit. Reserve the independent review turn before admitting optional investigation tasks. With the default eight discovery invocations, the coordinator ceiling remains two.\n\nPersist a discovery review receipt binding the report digest, source manifest, run ID, reviewer step and acceptance verdict. The cycle imports exactly that report revision. A candidate is not approved simply because the same Git HEAD passed an unrelated review.\n\nDiscovery has no implementation repair turn. If review finds a report defect, bounded repairs remain read-only and may revise the report; the generic Auto team repair path must not introduce a write turn. Existing read-mode snapshot/restore, filesystem policy and adapter isolation still apply. Runtime-created worktrees and persisted report artifacts are allowed; source changes are not discovery output.\n\n### Deduplication and ranking\n\nFirst compare against active opportunities, rejected/deferred decisions, existing issues/PRs and recent manual runs. Use source keys plus normalized problem identity, affected paths/symbols and acceptance criteria. A repository commit changing must not by itself turn an old idea into a new one. Model similarity suggestions can assist grouping, but the host preserves distinct IDs and decision history.\n\nFor each eligible candidate, record mission alignment, expected impact, evidence confidence, estimated effort and risk with short explanations. Use a deterministic, versioned ranking policy over those estimates, with stable tie-breaking; do not present scores as measured user benefit. Preserve the selection receipt, including why other candidates were deferred.\n\nAutomatic implementation requires all of the following:\n\n- `build-within-scope` is enabled in the saved mission.\n- The problem and proposed work fit the allowed categories and paths.\n- Evidence is sufficient for the proposed action and has current applicability.\n- No active or suppressed matching work exists.\n- Acceptance criteria are testable with a configured verification profile.\n- The change fits budget, review capacity and size limits.\n- No unresolved product decision or excluded-risk change is needed.\n\nOtherwise keep the idea as a proposal with the reason it needs owner input. Initial exclusions should cover broad rewrites, schema migrations, authentication/permission changes and billing changes unless the owner explicitly authorizes the particular work. New feature experiments may be built when explicitly included in the mission, but their claimed benefit remains a hypothesis until evaluated.\n\n## 7 Implementation and review\n\nConvert the selected opportunity revision into a normal `auto-team` request containing the problem, evidence references, intended outcome, acceptance criteria, allowed paths, exclusions and pinned verification profile. Attach the immutable mission/cycle provenance through the server. Agent-generated report text is contextual evidence, not a new owner instruction.\n\nUse fresh implementation worktrees from the selected base. If the target branch moved after discovery, mark applicability stale and use a bounded read-only revalidation before implementation; that turn consumes the same cycle budget. If the candidate or base must materially change, record a new candidate revision and selection receipt. Do not silently build an obsolete proposal or carry unused approval across a changed scope.\n\nApply saved scope constraints to initial planning, coordinator revisions, every write/integration/repair path, and the final diff. The Auto team validator must reject ownership outside allowed paths or tasks outside the approved goal. After write turns, inspect actual changed files, including untracked files and rename destinations. Excluded paths, changed verification commands, excessive changes or unresolved scope deviations stop automatic integration/readiness and produce an explicit result to inspect.\n\nEnforce managed ownership at the engine boundary as well as in the new API. Resume, follow-up, revise-goal, whole-team continuation and selective-rerun actions on a managed run must obtain a fresh valid allocation from the team service, preserve lineage, and recheck pause/membership/scope. Generic run endpoints cannot bypass these checks. For an in-place resume, bind the new allocation in the existing run journal; keep the original launch identity intact. Linked new runs receive their own launch claim and receipt.\n\nThese are execution and result gates layered on existing sandbox policies; path checks do not create an OS security boundary. Agents do not gain wider shell/network privileges merely because they belong to a standing team. Check/setup isolation and agent isolation remain separate existing settings and are pinned for each allocation.\n\n### Readiness conditions\n\nA result becomes **Ready for review** only when the implementation exists, required acceptance evidence is present, configured required checks pass, independent review approves the same final commit, and existing fail-to-pass proof succeeds when enabled. Validate branch ownership and current HEAD against the exact recorded evidence, as the coordinated-result path does today.\n\nMissing checks, skipped required gates, stale reports, incomplete work or a failed review produce **Needs attention**, not a ready result. An investigation can finish with reviewed evidence without code checks; an implementation cannot borrow that exemption.\n\nEach review card includes:\n\n- What problem was addressed and why this opportunity was chosen.\n- A concise before/after description, with screenshots for visual changes when captured.\n- The result branch/commit and links to the diff, checks and independent review.\n- A preview action when a configured launch profile exists, otherwise concrete reproduction steps.\n- Expected benefit clearly separated from measured improvement.\n- Known limitations, usage, and the owner's available decisions.\n\nPreviews are tied to the result worktree and commit. Reuse existing service-launch controls; do not leave previews running indefinitely between cycles. Missing previews do not become fabricated demonstrations.\n\n### Owner decisions and shipping\n\n| Action | Effect |\n|---|---|\n| Build this proposal | Authorizes its exact revision and a displayed bounded allocation; uses the same admission and verification path |\n| Request changes | Saves feedback and starts a linked whole-team continuation with a new explicit allocation |\n| Approve result | Records product acceptance of the exact result commit; does not merge or publish |\n| Open PR | Uses the existing owner-authorized PR flow and records the PR link |\n| Merge | Uses existing merge validation and shipping controls against the current source and target |\n| Later | Saves a defer-until value and keeps the opportunity out of selection until then |\n| Reject | Saves the reason and suppression policy before any branch cleanup |\n\nRecord `accepted` and `shipped` separately. Approving a result or opening a PR is not evidence it reached the product. A local merge receipt or confirmed merged PR can mark it shipped. Moving the result branch invalidates its prior approval/readiness. Remote changes are considered observed only when an existing integration has actually refreshed them.\n\nThe full lifecycle is `proposed`, `selected`, `implementing`, `ready`, `accepted`, `shipped`, with explicit `deferred`, `rejected`, `blocked`, and `expired` alternatives. A new revision or failed continuation does not erase old result evidence or decisions.\n\n## 8 Budgets and attention limits\n\nEnforce cross-run budgets by reserving a child's complete maximum allocation atomically before launch, then use the existing per-run invocation/time ceilings. A reservation includes retries, schema repair and coordination; it is not only a count of tasks. This avoids adding a second per-provider dispatch counter.\n\nAt settlement, reconcile against durable invocation reservations/metrics, not model-reported usage. Retain the full reservation while a launch is unknown. Release unused allowance only when authoritative accounting proves it was unused; if reliable elapsed-active-time accounting is unavailable, retain the time reservation conservatively through its budget window.\n\nCharge settled usage to a rolling seven-day window. Outstanding reservations count even when their creation time falls outside that window, so a stuck run cannot become free by aging out. Restart, reschedule, manual **Run now**, agent-requested continuation and schema failures cannot reset allowances. An owner-requested extra allocation is explicit, recorded, and shown beside cumulative use.\n\nA weekly cycle can finish discovery without launching implementation if the remaining allowance cannot fund planning, work and independent review. Never use the remainder to build a result that cannot be reviewed. Known exhausted providers wait through existing readiness rules within a bounded window; do not silently choose providers outside the configured pool.\n\nStop new implementation while two earlier drafts await review or shipping. Accepted but unshipped work remains outstanding unless the owner explicitly parks it. Stop discovery when twelve open opportunities need triage. Suppressed and unchanged candidates do not generate repeat notifications.\n\nTrack provider-reported tokens and API-equivalent cost when available, with unknown values preserved. Invocation counts and active-run time are enforceable product limits; missing provider usage is not zero consumption or evidence that work was free.\n\n## 9 Memory and feedback\n\nAssemble each cycle's context from the saved mission, current priorities, relevant decisions, bounded recent cycle summaries, existing work, and accepted project memory selected through the existing applicability rules.\n\nKeep three kinds of information separate:\n\n| Information | Authority and lifetime |\n|---|---|\n| Product direction | Owner-authored mission and explicit revisions |\n| Opportunity and decision history | Evidence and authenticated feedback for selection, with revision-specific provenance |\n| Technical project knowledge | Existing memory proposals and acceptance rules, including branch/commit applicability |\n\nA rejected idea does not become a technical convention. A merged implementation does not automatically accept every memory proposed by its workers. Unverified ideas never become accepted project facts merely because they appeared in several cycles.\n\nPreserve source trust and allow the owner to inspect why a decision or memory influenced a proposal. Do not copy one worker's private checkpoints into future members as shared team knowledge. New child runs receive portable, attributed context and fresh member identities.\n\nFeedback can adjust selection preferences within the saved mission. “Avoid this topic” should create an explicit suppression rule visible in team settings; an inferred preference remains a suggestion until the owner accepts it. Permission, budget and source-setting changes always use the mission revision path.\n\n## 10 API and user interface\n\nAdd `src/project-teams-api.ts` as an owner-only boundary consistent with current automation/coordinated execution APIs. Shared accounts must not fall through into it. Scope all read models and notifications to their project and existing permissions; support broader shared-team management separately later.\n\nRegister the new routes in `src/filesystem-access.ts` for both execution mutations and private evidence access. Owner authentication alone is insufficient when custom filesystem restrictions pause app-managed execution or unscoped private-data access. Apply the same restriction checks to readiness previews, source discovery and evidence reads as well as actual launches.\n\n| Endpoint | Purpose |\n|---|---|\n| `GET /api/project-teams?project=<id>` | Team summaries and attention counts |\n| `POST /api/project-teams` | Create a draft with an idempotency operation ID |\n| `GET /api/project-teams/:id` | Mission, status, bounded backlog and recent cycles |\n| `PUT /api/project-teams/:id` | Save a new mission/settings revision with optimistic concurrency |\n| `POST /api/project-teams/:id/activate` | Validate and activate the linked managed automation |\n| `POST /api/project-teams/:id/pause` | Prevent new launches |\n| `POST /api/project-teams/:id/stop` | Pause and cancel current owned work |\n| `POST /api/project-teams/:id/resume` | Revalidate and re-enable the schedule |\n| `POST /api/project-teams/:id/preview` | Show readiness, schedule, budgets and current blockers without model calls |\n| `POST /api/project-teams/:id/cycles` | Run now through normal admission |\n| `GET /api/project-teams/:id/cycles/:cycleId` | Exact inputs, selection, children, claims, usage and result |\n| `POST /api/project-teams/:id/cycles/:cycleId/recover` | Reconcile a reviewed unknown claim; never blindly replay it |\n| `POST /api/project-teams/:id/opportunities/:opportunityId/decisions` | Build, revise, accept, defer or reject a specific revision |\n| `POST /api/project-teams/:id/archive` | Disable future work and preserve history |\n\nMutation bodies have closed schemas, bounded strings/collections, an expected revision and an operation ID. Stale decisions return `409` with the current version. Result acceptance also supplies the exact current commit/report identity. A model response is never a substitute for this authenticated action.\n\nAdd `ui/project-team.tsx` and `ui/project-team-state.ts`; integrate them with `ui/project.tsx`, `ui/state.ts`, `ui/types.ts` and the current route machinery. Reuse the existing schedule editor and preview, provider/model controls, check-profile selector, run cards, diff panel, and memory inspector.\n\nUse `loadAgents()` and existing catalog refresh behavior so the setup screen reflects connections added, disabled or removed in Settings. Show connected but ineligible ACP providers with the reason **Unavailable for scheduled teams** and a link to the existing Agents & models settings; do not offer connection configuration or an execution-policy override inside team setup. Saved interactive presets containing ACP require an eligible replacement before activation.\n\nThe setup screen ends with a concrete summary of what the team may build, when it will run, its budget and how results reach the owner. After activation, the project page shows its mission, next run, remaining budget, active work and review backlog. Keep protocol IDs and orchestration settings in details rather than the main product flow.\n\nGeneralize inbox presentation to `run` and `project-opportunity` items while preserving existing `prioritizedReviewRuns()` behavior for legacy callers. Group managed children under their opportunity/cycle, without counting the same blocker twice. Discovery proposals remain visible even if their child run has no diff. Opening or marking a card read does not approve it.\n\nPublish project-team state through a scoped topic on the existing `src/stream.ts` / `ui/stream.ts` broker, using `subscribe()` and cleanup on unmount. Do not open a new `EventSource` per team, card or browser tab. Preserve the current Team inbox subscription and owner/shared-account separation. A server restart reloads authoritative team state; optimistic client updates cannot revive a paused team or replay a decision.\n\n### Notifications\n\nExtend the existing notification candidate/payload model with a project-team subject, retaining old run payload compatibility. Use durable identities such as team/cycle/result-revision or blocker revision. Notify when a result is ready, an actionable proposal needs a decision, or execution fails/needs input. Suppress duplicate child completion alerts for the same published outcome.\n\nNo notification is needed for unchanged input, an empty successful cycle, normal waiting, or repeated identical blockers. Use only destinations already configured by the owner. A weekly digest and additional external integrations can follow after the core workflow works.\n\n## 11 Delivery phases\n\nAll new behavior remains behind `CODEFLOW_PROJECT_TEAMS_ENABLED` and explicit per-team activation. The existing `CODEFLOW_AUTO_TEAM_ENABLED` gate must also permit new child runs. Initial implementation proceeds in these dependent slices; implementing the plan does not require a database migration or a new Node baseline.\n\n| Phase | Deliverables and main files | Completion criteria |\n|---|---|---|\n| P0 Contracts and durable state | `project-team-model.ts`, `project-team-store.ts`, `provider-eligibility.ts`, mission revisions, lifecycle reducer, budgets and operation receipts | Concurrent edits fail clearly; invalid/corrupt state cannot launch; dynamic IDs are preserved while ACP/ineligible extensions are rejected for managed work |\n| P1 Managed scheduling and recovery | `automation.ts`, `project-teams.ts`, `server.ts`, `filesystem-access.ts`, engine admission/receipt helper | One scheduled occurrence creates one cycle; effective provider eligibility precedes role resolution; two-store activation recovers; ambiguous launch blocks replay; ordinary automations/sweeps still behave as before |\n| P2 Discovery on Auto team | `patterns/auto-team.ts`, `patterns/auto-team-model.ts`, `teams.ts`, `project-team-discovery.ts`, engine report persistence | A fake-agent cycle yields independently reviewed typed opportunities or a no-op; no discovery path dispatches a write turn; reports survive no-diff cleanup |\n| P3 Selection and bounded implementation | `project-team-policy.ts`, `project-teams.ts`, engine scope/continuation hooks | A valid opportunity creates one implementation child; scope, backlog, budget and membership gates prevent ineligible launches; exact final evidence controls readiness |\n| P4 Project workspace and decisions | `project-teams-api.ts`, `ui/project-team.tsx`, `ui/project-team-state.ts`, `project.tsx`, `automations.tsx`, `review-inbox.ts`, existing stream broker | Owner can activate, inspect, approve, revise, defer, reject, pause and recover; provider availability/reasons refresh from Settings; project-team updates share the existing stream |\n| P5 Feedback and notification integration | `project-team-context.ts`, existing memory retrieval, notifications, UI history/usage | Next cycle respects rejected/deferred work; technical memory remains separately governed; one actionable change produces one notification candidate |\n| P6 Reliability and pilot release | Fault tests, browser tests, bounded evaluation script and user docs | Restart, stale evidence, quota, cancellation, deletion, privacy and old-data compatibility pass; pilot results are recorded before expanding rollout |\n\nP0–P2 provide an internal proposal-only milestone. P3–P5 deliver the first complete user promise, including an implemented improvement before owner acceptance. P6 is the release gate. Do not present the proposal-only milestone as completion of the feature.\n\n### Concrete implementation order\n\n1. Define types, reducers, store semantics and effective provider eligibility before connecting live launches.\n2. Add managed automation previews and cycle admission using a fake launch adapter.\n3. Establish durable engine admission and exact child ownership/recovery.\n4. Add read-only discovery contracts and report capture; prove every repair path stays read-only.\n5. Implement host validation, duplicate handling, selection and allocation.\n6. Launch an ordinary Auto team implementation from one selected opportunity.\n7. Validate final results, preserve discovery evidence and support verified whole-team revision.\n8. Add project setup/workspace, inbox decisions and managed automation controls.\n9. Add feedback context, notifications and meaningful usage/outcome reporting.\n10. Complete fault/compatibility tests and run the opt-in pilot.\n\nKeep helper extraction small. Reuse deferred/coordinated persistence and receipt patterns where appropriate, but do not rewrite those services into a general workflow framework to deliver this feature.\n\n## 12 Verification strategy\n\nExtend the current fake-provider and isolated Git-repository harnesses. Core CI must not spend real provider quota or require connected external services.\n\n| Test area | Required cases |\n|---|---|\n| Model/store | Mission revision conflicts; request idempotency with changed-body rejection; one-active-team constraint; malformed state; bounded retention; atomic claim/selection/budget writes |\n| Provider eligibility | Explicit ACP selection, default pool, `auto` resolution, inherited Full-access preset and managed Run now are all rejected/filtered before launch; coordinator revisions cannot add an ineligible provider; unavailable permitted fallback blocks clearly; ordinary dynamic-ID behavior remains supported |\n| Provider settings and restore | Settings refresh updates inventory/reasons; disabled/removed/changed bindings stop dependent dispatch; opaque catalog IDs survive validation; unknown usage stays unknown; portable team backups cannot restore executable authority |\n| Scheduling | Daily/weekly/interval timezone previews; DST; missed-run policy; restart before/after occurrence advancement; concurrent manual/scheduled admission; no overlapping cycles |\n| Activation | Crash after each cross-store write; missing/orphan trigger; activation persistence failure; managed-trigger edits cannot bypass pause or settings |\n| Launch recovery | Failure before claim, before dispatch, during dispatch, before receipt, after child completion; one matching run, no matching run and conflicting matches |\n| Discovery | All tasks and revisions stay investigations; write-mode repair rejected; malformed/oversized/empty reports; invented evidence; independent report review; no-diff preservation |\n| Selection | Duplicate wording, same issue after new commit, rejected topic, defer expiry, existing manual/PR work, low evidence, stale source and insufficient budget |\n| Implementation | Allowed paths and actual changed paths agree; untracked/renamed excluded files; changed check configuration; stale base; missing acceptance work; exact final-head review/proof |\n| Continuation | Request changes uses whole-team verification; direct follow-up invalidates readiness; generic resume/follow-up cannot bypass team budget or paused state |\n| Budget | Retries counted; concurrent reservations; unknown receipts retain allowance; rolling-window boundaries; restart/resume cannot reset usage; unknown token cost remains unknown |\n| Human decisions | Stale revision/commit rejected; approval differs from merge; rejected reason retained before cleanup; accepted/unshipped work occupies review capacity |\n| Access/lifecycle | Shared identities cannot dispatch; custom filesystem restrictions block new private/execution routes; removed project/repository blocks and cancels work; archive preserves results; global feature disable prevents new launches |\n| Notifications | One actionable cycle alert; suppression of child duplicates; no-op quiet; new blocker revision alerts; restart deduplication; existing destinations/settings honored |\n| Browser | Setup to ready result to revision/approval; propose-only path; failure/recovery; small screens, dark/light modes, keyboard navigation and axe accessibility checks |\n| Browser connections | Project-team topic shares the existing broker with run/Team inbox/terminal topics; multiple tabs, leader loss and unmount release subscriptions; owner-to-shared identity changes cannot expose team evidence |\n\nProposed test files: `test/project-team-model.test.ts`, `test/project-team-store.test.ts`, `test/project-team-service.test.ts`, `test/project-team-discovery.test.ts`, `test/project-team-engine.test.ts`, `test/project-teams-api.test.ts`, and `test/e2e/project-teams.e2e.ts`.\n\nExtend existing `automation.test.ts`, `auto-team.test.ts`, `auto-team-model.test.ts`, `team-memory.test.ts`, `review-inbox.test.ts`, `notifications.test.ts`, `projects.test.ts`, `acp-connections.test.ts`, `acp.test.ts`, `maintenance.test.ts`, `shared-access.test.ts`, and relevant deferred/coordinated regression tests. Add focused `provider-eligibility.test.ts` coverage. Fake-provider fixtures must cover both ordinary Auto team output and the mode-specific discovery schema; use `fake-acp.mjs` to assert incompatible managed requests fail without starting a provider process.\n\nExtend `test/e2e/settings.e2e.ts` and `test/e2e/connections.e2e.ts` for the provider picker and shared-stream integration. The current connection tests already include the Team inbox topic in their subscription counts; include project-team subscriptions without regressing cross-tab sharing or removing existing topics. Preserve the streamed tool-update behavior in `ui/run.tsx` by reusing its transcript components.\n\nFor implementation validation, run focused tests for each slice, then `npm run typecheck`, `npm test`, `npm run build`, and the relevant browser tests. Run the broader end-to-end suite before release when shared routing or inbox changes warrant it. No runtime test result is claimed by this planning document.\n\nThe newly available `npm run test:acp:live` is a separately invoked, usage-consuming interactive compatibility check. It is not a CI prerequisite for standing teams and does not establish support for scheduled ACP work. Use fake protocol/process fixtures for this feature's ACP exclusion tests.\n\n## 13 Pilot and rollout\n\nAdd `scripts/evaluate-project-teams.ts` using the established evaluation approach. Fake mode proves control flow. Real mode is explicitly invoked with a repository, provider pool and hard budget, and emits immutable reports without changing feature flags or shipping changes.\n\nUse a small opt-in pilot on Code Flow and one other repository with known owner priorities. Record at least ten completed cycles across both, including unchanged input, a rejected proposal, a deferred idea, a failed check, an accepted implementation and a pause/restart. These are proposed observation requirements; they do not establish model quality by themselves.\n\nMeasure:\n\n- Proposals the owner considered worth pursuing, and implementations actually accepted/shipped.\n- Review and correction time, repeat suggestions, and rejected-work recurrence.\n- Invocations and active time per accepted improvement, with unknown provider usage shown explicitly.\n- No-op/skip frequency, selection reasons, verification failures and stale-evidence rejections.\n- Time spent blocked on missing product context, configuration or human decisions.\n\nRequire zero duplicate child launches in fault tests, zero bypasses of the explicit scope/budget/approval gates, and correct evidence for every ready result before the pilot. Keep the feature experimental until owner feedback shows that its output is worth the attention it consumes. Do not promote based on agent count, lines changed, or number of proposals generated.\n\nDisabling the feature prevents new cycles and automatic stage advancement while preserving history and review access. Stopping active work remains a distinct explicit action. Existing ordinary automations, saved runs, and Auto team workflows retain their behavior.\n\n## 14 Acceptance example\n\n1. The owner connects a repository, selects its base branch, saves a mission to improve onboarding, and enables a weekly team with the default budget.\n2. At the scheduled time, exactly one cycle pins the repository and source inputs.\n3. Discovery returns three evidence-bearing opportunities and independent review accepts the report.\n4. The service suppresses one duplicate, records why another is deferred, and selects one eligible improvement within budget.\n5. Auto team builds it in isolated worktrees and verifies the final result.\n6. The owner sees one ready card containing the problem, rationale, preview or reproduction steps, diff and real check/review evidence.\n7. The owner requests a change. A bounded whole-team continuation produces a newly verified result; the old approval state is not carried forward.\n8. The owner approves and separately merges or opens a PR through the existing controls.\n9. The next cycle remembers the decision, avoids repeating the same work, and can return no new work when nothing warrants attention.\n\nThe implementation is complete when this path works alongside the failure/recovery cases, and the owner can leave the team enabled without managing every agent turn or receiving an unbounded backlog.\n\n\n## Implementation validation record (19 October 2026)\n\n- PR preparation synchronized the implementation with `7933ecf` (Kilo/Cline ACP connections). Review corrected newest-run selection, retained full revision feedback without dropping acceptance criteria, and made archived history immutable through API decisions and recovery. All three regression tests reproduced the issues before the fixes and pass afterward.\n- TypeScript typecheck and both UI/host builds pass.\n- Full regression run after synchronization and review fixes: 1,072 tests, 1,065 passed, seven environment-gated skips, zero failures.\n- Additional focused runs after implementation review cover the new lifecycle/fault cases, notification and automation regressions, restored records, shared access boundaries and exact-SHA publication. All pass; counts overlap the full suite and are not additive.\n- Browser regression selection: 23 tests passed across project teams, Auto team, automation controls, shared stream connection limits, shared projects, filesystem policy, notifications, settings and accessibility.\n- The later full browser run recorded 95 passes, one memory-graph refresh timing failure, and one shared-project Chrome teardown failure after its six assertions passed. The memory test now waits for the published graph node; its three-test file passes. The shared-project file also passes all six tests and exits normally on an isolated retry. The full browser command is not recorded as a clean pass; CI must confirm the complete run.\n- Shipping/GitHub regression selection: 40 tests passed.\n- PR #29 CI follow-up: both browser jobs timed out after the standing-team assertion passed. Its fixture now owns the Node server directly, drains stdout, waits for shutdown, and removes temporary data after exit; the isolated browser test passes and exits normally. The PR merge path also rechecks the managed-result approval gate. Regression tests reproduced rejected-result, changed-mission and archived-team merge bypasses before the fix; all three are blocked afterward, and an unchanged approved result still merges and becomes shipped. Typecheck and the 40-test project-team/GitHub selection pass after these fixes.\n- The next complete browser run exited normally with 95 passes and one Kilo/Cline settings-search timing failure. Settings now clears the search before the section becomes interactive, and the preset test waits for the destination section before typing. All seven settings browser tests and typecheck pass after that correction; the complete browser suite and CI are rerun for the final commit.\n- Final browser validation: all 96 tests pass locally, and both GitHub browser jobs pass at `7a100f5`. One Ubuntu Node 22 job exposed the cancellation fixture reading an in-flight launch revision; the test now waits for the durable running receipt and queued reconciliation before issuing its owner decision. Fixture teardown also shuts down run managers before deleting data. The corrected cancellation test passes three consecutive runs, the complete project-team file passes all 25 tests, and typecheck passes.\n- A later CI run reproduced the shared-project Chrome teardown stall after all six tests passed. That fixture now owns Chrome through `BrowserServer`, terminates the owned browser process during teardown, and waits for bounded server shutdown before deleting its files. Shared-project and SSH browser files pass together (seven tests), and typecheck passes. An isolated SSH fixture connection failure also passes on local retry; it remains covered by both complete CI browser jobs.\n- Synthetic pilot: ten cycles across two isolated fixtures; four child launches, 14 observed fake CLI calls, two verified drafts and eight no-ops. The redacted measurement summary is [saved here](evaluations/project-teams-synthetic-2026-10-19.json).\n- Real CLI/provider quality, real product usefulness and owner acceptance have not been measured. These remain explicit rollout gates, rather than inferred from synthetic output.\n\n[Documentation library](README.md) · [Development guide](architecture.md)\n","title":"Standing project teams implementation plan"},{"group":"Engineering records","id":"performance-results","source":"docs/performance-results.md","body":"# Measured performance: 19 October 2026\n\n> Engineering record. Status, measurements and decisions below describe the recorded implementation or review. For current usage, start with the [documentation library](README.md).\n\nThe architecture changes improved latest-transcript opening by **1.9×** with\n1,000 events and **27.6×** with 5,000 events on this machine.\nThe experimental Rust reader showed no consistent end-to-end benefit and increased\ntranscript API latency, so production retains TypeScript. These numbers describe\nsynthetic history/transcript operations, not provider inference or total coding-task\nduration.\n\n## Comparison\n\nMedians of seven fresh-context Chrome trials after one unmeasured warmup per variant.\nThe endpoint is Playwright’s DOM visibility check for the latest event after opening\nShow work; timings include automation polling and are not precise paint timings.\n\n| Workload | Before | Optimized TypeScript | Optimized + Rust experiment |\n| --- | ---: | ---: | ---: |\n| 20 runs, 1,000 events: latest transcript visible | 444.45 ms | 232.94 ms | 234.59 ms |\n| 200 runs, 5,000 events: latest transcript visible | 7,124.48 ms | 257.77 ms | 320.41 ms |\n| 200 runs, 100,000 events: latest transcript visible | Timed out (30 s heading wait) | 270.34 ms | 340.54 ms |\n| 1,000 events: transcript API | 129.52 ms | 5.53 ms | 9.56 ms |\n| 5,000 events: transcript API | 6,314.58 ms | 6.58 ms | 10.46 ms |\n| 100,000 events: transcript API | Not measured after UI failure | 7.62 ms | 11.85 ms |\n| 20 runs: history search API | 2.50 ms | 1.99 ms | 1.97 ms |\n| 200 runs: history search API (5,000-event fixture) | 5.50 ms | 3.99 ms | 3.33 ms |\n\nAPI medians contain seven requests per successful variant/workload (one per browser\ntrial). The old transcript endpoint replays the entire history over SSE; the new\nendpoint returns a recent page of at most 500 events. These are deliberately the\npaths used to reach the same user-visible latest output, **not equal-size response\nbenchmarks**. Earlier events remain accessible through pagination. Reduced work,\nindexed repository membership and smaller responses explain the gain; it is not a\nlanguage-only comparison.\n\nThe baseline stress attempt failed before the run heading became visible, with no\nbrowser JavaScript exception. It was stopped before timing the other variants to\navoid background CPU interference. A timeout is not a precise timing or speedup\nratio. No baseline stress API or heap result is inferred.\n\n## Resource tradeoffs and variance\n\n| Browser metric | Before | Optimized TypeScript | Rust experiment |\n| --- | ---: | ---: | ---: |\n| 1,000 events: decoded bytes through latest output | 2.02 MiB | 1.66 MiB | 1.66 MiB |\n| 5,000 events: decoded bytes through latest output | 3.56 MiB | 2.02 MiB | 1.85 MiB |\n| 1,000 events: JS heap after GC | 4.35 MiB | 4.60 MiB | 4.61 MiB |\n| 5,000 events: JS heap after GC | 5.74 MiB | 4.73 MiB | 4.75 MiB |\n| 100,000 events: JS heap after GC | Unavailable | 4.76 MiB | 4.76 MiB |\n\nSmall-history heap increased **5.7%**; at 5,000 events it decreased\n**17.6%**. The cache and paging state have a fixed cost. All successful\nvariants rendered 200 transcript rows in this fixture; the improvement is not\nexplained by comparing different visible row counts. Received bytes include decoded\nassets and stream traffic, not compressed wire bytes. Heap is the Chrome JavaScript\nheap after forced GC, not RSS or total system memory. Startup was sampled only once\nper server and is retained in the raw data, not used for a speedup claim.\n\nBrowser latest-output ranges (min–max):\n\n| Events | Before | TypeScript | Rust |\n| --- | ---: | ---: | ---: |\n| 1,000 | 413.63–457.03 ms | 213.88–247.74 ms | 225.48–247.21 ms |\n| 5,000 | 6,456.07–8,098.10 ms | 227.98–263.78 ms | 242.07–342.26 ms |\n| 100,000 | Timeout | 260.06–351.29 ms | 262.73–344.04 ms |\n\nThis is a developer laptop, not an isolated benchmark host. Other applications and\nOS activity contribute noise; initial system load averages were\n5.62, 5.59, 6.68. Variant order rotates each round and filesystem caches are not\nflushed. Small sub-millisecond differences in history API timings should not be\ninterpreted as reliable Rust benefits. Raw samples are provided rather than a\nstatistical-significance claim.\n\n## Rust decision\n\nThe dependency-free release-mode Rust helper reads the same file backwards and\nreturns original records and byte offsets over a pipe. The same TypeScript code\nthen validates and constructs the page. Tests verify parity for Unicode, malformed\nrecords, large previews, step filters, reconnects and pagination. Benchmark timing\nincludes actual process creation, IPC and termination.\n\nAgainst optimized TypeScript, Rust transcript API medians were **73% slower**\nfor 1,000 events, **59% slower** for 5,000, and **56% slower** for 100,000.\nBrowser samples vary; a small median advantage in an individual workload does not\nestablish a consistent user-visible improvement across workloads.\nThe optimized bounded scan is already only a few milliseconds; this process boundary\nadds more cost than it removes. This does not establish that Rust itself is slower,\nor predict the performance of a persistent native worker, an in-process extension,\nor a full rewrite. The experiment remains reproducible but is not enabled in the\nproduction app and adds no Rust installation requirement for users.\n\n## Reproduction and scope\n\n- Baseline: `c300a90ea9c71f940ac9b504ad8b3513b270f257` (main at measurement time, including SQLite memory, result previews and browser scenarios).\n- Measured optimized implementation: `0081bb76c33c86c2db16ca04b862ab490cf1920e`.\n- Host: Apple M4, 10 cores, 16 GiB, macOS arm64; Node v22.14.0; Chrome 155.0.8059.80; rustc 1.90.0 (1159e78c4 2025-09-14).\n- Same synthetic fixtures, provider stubs and dependency installation for all variants.\n- No real user history, provider requests or external service latency.\n- The measured code includes the live-page race fix and the integration with the\n SQLite memory and result-preview updates from main, plus the archived-compaction\n and shutdown fix found by CI. Earlier pilot results are superseded by this run.\n\n```sh\nnpm run benchmark:compare -- --baseline c300a90ea9c71f940ac9b504ad8b3513b270f257 --trials 7 --api-repeats 1\nnpm run test:rust\n```\n\nThe harness requires Chrome and Cargo for the experimental third variant. See\n[raw samples and environment metadata](benchmarks/2026-10-19-performance.json),\n[resource limits and architecture](performance.md), and the\n[Rust experiment](../experiments/rust-transcript/README.md).\n\n[Documentation library](README.md) · [Development guide](architecture.md)\n","title":"Measured performance: 19 October 2026"},{"group":"Build and extend","id":"vscode","source":"integrations/vscode/README.md","body":"# Code Flow for VS Code\n\nUse the local VS Code integration to create tasks and inspect Code Flow results without leaving your editor. The Code Flow server must be running and authenticated before the extension can connect.\n\nConnect a local VS Code window to a running Code Flow server. Start a Safe solo task\nfrom the current saved file and selected text, inspect recent run states, open the\nfull review in Code Flow, stop a running task, or open a result worktree in a new window.\n\n1. Start Code Flow and read its local URL and token from the server startup output.\n2. Install the locally built VSIX through **Extensions → Install from VSIX**.\n3. Run **Code Flow: Connect to Local Server**, enter the loopback URL and token.\n4. Open a trusted local Git folder, then run **Code Flow: Start Task from Editor**.\n\nTokens use [VS Code SecretStorage](https://code.visualstudio.com/api/references/vscode-api#SecretStorage).\nThey are not put in workspace settings, URLs or logs. Use **Forget Server Token**\nto remove a connection. Server URL configuration is application-wide so a repository\ncannot silently redirect the extension to another service. The client refuses\nredirects, non-loopback endpoints, unsupported API versions and oversized responses.\nAn SSH tunnel can expose another Code Flow instance as a local endpoint when its\nforwarded port matches the server's Host validation.\n\nTask creation requires workspace trust and local `file:` folders. Remote VS Code\nworkspaces and virtual files are unsupported in this first integration. The task\nuses detected verification, Auto provider selection, bounded execution, no copied\nignored files and interactive tool approval requests. Current-file context excludes\ncredential filenames and paths outside the selected folder. Unsaved files require\nan explicit save before dispatch. Review code and verification in Code Flow before\nshipping a result.\n\nThe Explorer view shows the recent 100 server runs, filtered to local workspace\nfolders, and refreshes every ten seconds while visible. It displays verification\nand approval state but does not approve permissions from the editor. Open the full\nrun for those decisions. Stopping a task asks for confirmation. Creating a task can\nconsume the configured provider's quota; the extension never starts one on activation.\n\nFrom the Code Flow repository:\n\n```sh\npython3 scripts/package-vscode.py\nnode scripts/test-vscode.mjs\n```\n\nThe package is written to `output/vscode/`. Packaging uses Python 3's standard\nlibrary. Host acceptance uses an installed `code` executable (or\n`CODEFLOW_VSCODE_BIN`), an isolated temporary profile and extension directory, and a\nreal Code Flow server with fake agents. It packages and installs the VSIX first,\nthen exercises that installed artifact's activation, command registration, token\nstorage, task execution, refresh, token deletion and delayed connection responses.\nSee [VS Code extension testing](https://code.visualstudio.com/api/working-with-extensions/testing-extension).\nNo marketplace publishing or changes to the user's installed extensions are performed.\n\n[Documentation library](../../docs/README.md) · [API and CLI](../../docs/api.md) · [Getting started](../../docs/getting-started.md)\n","title":"Code Flow for VS Code"}]
|
|
1
|
+
[{"group":"Get started","id":"index","source":"docs/README.md","body":"# Your agents. A clear path forward.\n\nCode Flow brings coding CLIs into one workspace for planning, implementation, review and verification. Start with a small task, follow the evidence, and decide when the result is ready to ship.\n\n## Try the preview\n\nStart Code Flow locally and open it in your browser:\n\n```bash\nnpx --yes --package=https://github.com/midhunxavier/code-flow/releases/download/v0.1.0-preview.2/midhunxavier-code-flow-0.1.0-preview.2.tgz codeflow\n```\n\nUse **Node.js 22.13+**, **Git 2.38+** and a supported coding CLI installed and signed in. Runs on macOS, Linux and Windows through **WSL2**. Keep the terminal open while you work; **Ctrl+C** stops the app.\n\nPrefer a macOS app? Download **0.1.0-preview.2**:\n\n- [Download for Apple Silicon (M1 or later)](https://github.com/midhunxavier/code-flow/releases/download/v0.1.0-preview.2/code-flow-0.1.0-preview.2-macos-arm64-unnotarized.dmg)\n- [Download for Intel Mac](https://github.com/midhunxavier/code-flow/releases/download/v0.1.0-preview.2/code-flow-0.1.0-preview.2-macos-x64-unnotarized.dmg)\n\nRequires **macOS 13+**. The app opens its own window and keeps agents running when you close it. Node is included; Git, a signed-in coding CLI and Chrome or Edge for the shared browser are still required. The DMGs are **unnotarized preview builds** and macOS may block them; use npm if blocked.\n\n[Installation and setup](getting-started.md#install-and-run) · [Release notes and checksums](https://github.com/midhunxavier/code-flow/releases/tag/v0.1.0-preview.2)\n\n<div class=\"cards\">\n<a href=\"getting-started.md\"><strong>Start your first run ↗</strong><span>Install the app, connect a CLI and take a task from prompt to review.</span></a>\n<a href=\"patterns.md\"><strong>Choose a workflow ↗</strong><span>Plan and build, test first, compare candidates or ask a team to investigate.</span></a>\n<a href=\"runs.md\"><strong>Find your way around ↗</strong><span>Follow agent activity, ask questions and open files, changes and terminals.</span></a>\n<a href=\"verification.md\"><strong>Review with confidence ↗</strong><span>Understand checks, review evidence and when a result is ready to ship.</span></a>\n</div>\n\n## From task to reviewed result\n\n1. **Open a project.** Choose Open repository, confirm its project, then describe the outcome and add context.\n2. **Choose the team.** Assign roles yourself or review a proposed Auto-team plan.\n3. **Follow the work.** Answer questions, inspect changes and watch the checks.\n4. **Review and ship.** Read the evidence before merging or opening a pull request.\n\n<picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"images/run-dark.png\">\n <img src=\"images/run-light.png\" alt=\"The Code Flow workspace with a completed demo run and review actions.\" width=\"100%\">\n</picture>\n\n*Captured from the local application with demo agents. See [screenshot provenance](documentation.md#screenshots).*\n\n## Work with agents\n\n- [Team patterns](patterns.md) · [Auto team](auto-team.md) · [Project missions](project-teams.md)\n- [Runs and the workspace](runs.md) · [Review inbox](review-inbox.md) · [Verification](verification.md)\n- [GitHub and shipping](github.md) · [GitHub review threads](github-review-threads.md)\n- [Selective task reruns](task-reruns.md) · [Deferred starts](deferred-runs.md)\n\n## Make the workspace yours\n\n- [Configuration](configuration.md) · [ACP agents](acp-agents.md) · [Context inspection](context-inspection.md)\n- [Project conventions](project-conventions.md) · [Checkpoints and reversions](checkpoint-review.md)\n- [Service launch profiles](launch-profiles.md) · [Code intelligence](code-intelligence.md) · [Architecture explorer](architecture-explorer.md)\n- [Project memory and knowledge graph](memory-storage.md) · [Semantic claim review](memory-claims.md)\n- [Coordinated repository changes](coordinated-changes.md)\n\n## Connect and collaborate\n\n- [Team collaboration](team-collaboration.md) · [Shared project accounts](shared-projects.md)\n- [SSH projects](ssh-projects.md) · [Remote workers](remote-workers.md)\n- [GitLab and Bitbucket workflows](code-hosts.md) · [External sources](external-sources.md)\n- [Automations and remote access](automations.md) · [Notifications](notifications.md)\n- [VS Code integration](../integrations/vscode/README.md)\n\n## Operate safely\n\n- [Security and execution boundaries](security.md) · [Filesystem policies](filesystem-policy.md)\n- [Agent containers](agent-containers.md) · [Filtered Docker tasks](filtered-tasks.md)\n- [Dependency discovery](dependency-discovery.md) · [Backup and maintenance](maintenance.md)\n\n## Build and extend\n\n- [Architecture and development](architecture.md) · [API and CLI](api.md) · [Extensions](extensions.md)\n- [Provider compatibility](provider-compatibility.md) · [Accessibility](accessibility.md)\n- [Performance](performance.md) · [Run storage](run-storage.md)\n- [Documentation and GitHub Pages](documentation.md)\n\n## Engineering records\n\nThese documents record implementation decisions and evidence at a particular point in time. Use the guides above for current product instructions.\n\n- [Application improvement program](application-improvements.md)\n- [Project navigation review and implementation plan](project-first-navigation-plan.md) (proposed)\n- [Dynamic-team evaluation](dynamic-teams-evaluation.md) and [implementation plan](dynamic-teams-implementation-plan.md)\n- [Implementation review](dynamic-teams-implementation-review-1.md) and [original plan review](dynamic-teams-plan-review-0.md)\n- [Standing-team implementation plan](project-teams-implementation-plan.md)\n- [Measured performance](performance-results.md)\n\n[Project README](../README.md) · [Getting started](getting-started.md)\n\nProject collaboration lives in **More → Board** and **More → People**. Scheduled tasks, backlogs and missions live in **More → Automation**; configured Board and Automation tabs stay visible. Global Inbox and Scheduling cover the current computer.\n","title":"Your agents. A clear path forward."},{"group":"Get started","id":"installation","source":"docs/installation.md","body":"# Install Code Flow preview\n\nOpen the macOS desktop app, or start the local browser app with one command. You do not need to clone Code Flow or add it to your project. The preview uses your installed coding CLIs and their existing logins.\n\n**Desktop preview 0.1.0-preview.4.** Download the macOS app or use the exact release package below. Each published app release automatically updates the npm `latest` and `preview` channels after npm processing completes. See the [preview release notes and checksums](https://github.com/midhunxavier/code-flow/releases/tag/v0.1.0-preview.4).\n\n## Choose an installation method\n\n<div class=\"install-picker\" data-install-picker>\n<div class=\"install-tabs\" role=\"tablist\" aria-label=\"Installation method\">\n<button type=\"button\" id=\"install-tab-unix\" role=\"tab\" aria-controls=\"install-unix\" aria-selected=\"true\">macOS / Linux</button>\n<button type=\"button\" id=\"install-tab-windows\" role=\"tab\" aria-controls=\"install-windows\" aria-selected=\"false\" tabindex=\"-1\">Windows</button>\n<button type=\"button\" id=\"install-tab-npm\" role=\"tab\" aria-controls=\"install-npm\" aria-selected=\"false\" tabindex=\"-1\">npm</button>\n<button type=\"button\" id=\"install-tab-homebrew\" role=\"tab\" aria-controls=\"install-homebrew\" aria-selected=\"false\" tabindex=\"-1\">Homebrew</button>\n</div>\n<section id=\"install-unix\" role=\"tabpanel\" aria-labelledby=\"install-tab-unix\" tabindex=\"0\">\n<h3>Try it with one command</h3>\n<p>Install Node.js 22.13+ and Git 2.38+, then run:</p>\n<pre><code class=\"language-sh\">npx --yes --package=https://github.com/midhunxavier/code-flow/releases/download/v0.1.0-preview.4/midhunxavier-code-flow-0.1.0-preview.4.tgz codeflow</code></pre>\n<p>Code Flow starts locally and opens its sign-in link in your default browser. Keep the terminal open while you work. Press <strong>Ctrl+C</strong> to stop the server.</p>\n</section>\n<section id=\"install-windows\" role=\"tabpanel\" aria-labelledby=\"install-tab-windows\" tabindex=\"0\">\n<h3>Use Windows Subsystem for Linux</h3>\n<p>This preview runs inside <strong>WSL2</strong>. Install Node.js 22.13+, Git 2.38+ and your agent CLIs inside WSL, sign in there, and keep your repository in the Linux filesystem. In your WSL terminal, run:</p>\n<pre><code class=\"language-sh\">npx @midhunxavier/code-flow@preview</code></pre>\n<p>The launcher tries to open your Windows browser. If it cannot, copy the local sign-in link from the terminal. Native PowerShell and Command Prompt execution are not supported in this preview.</p>\n</section>\n<section id=\"install-npm\" role=\"tabpanel\" aria-labelledby=\"install-tab-npm\" tabindex=\"0\">\n<h3>Install for regular use</h3>\n<p>With Node.js 22.13+ and Git 2.38+ installed on macOS, Linux or inside WSL2:</p>\n<pre><code class=\"language-sh\">npm install -g @midhunxavier/code-flow@preview\ncodeflow</code></pre>\n<p>To update, run the same install command again. To remove the launcher:</p>\n<pre><code class=\"language-sh\">npm uninstall -g @midhunxavier/code-flow</code></pre>\n</section>\n<section id=\"install-homebrew\" role=\"tabpanel\" aria-labelledby=\"install-tab-homebrew\" tabindex=\"0\">\n<h3>Install with Homebrew</h3>\n<p>The preview release includes a Homebrew formula. Once the maintainer has published the <code>midhunxavier/code-flow</code> tap, install it on macOS or Linux:</p>\n<pre><code class=\"language-sh\">brew install midhunxavier/code-flow/code-flow\ncodeflow</code></pre>\n<p>Homebrew installs Node and Git as dependencies. Update with <code>brew update && brew upgrade code-flow</code>. If the tap is unavailable, use npm.</p>\n</section>\n</div>\n\n## macOS app download\n\nPrefer an app you can open from Applications? Download **0.1.0-preview.4** for your Mac:\n\n- [Download Apple Silicon DMG (M1 or later)](https://github.com/midhunxavier/code-flow/releases/download/v0.1.0-preview.4/code-flow-0.1.0-preview.4-macos-arm64-unnotarized.dmg)\n- [Download Intel DMG](https://github.com/midhunxavier/code-flow/releases/download/v0.1.0-preview.4/code-flow-0.1.0-preview.4-macos-x64-unnotarized.dmg)\n\n1. Choose **arm64** for Apple Silicon, or **x64** for an Intel Mac. macOS 13 or later is required.\n2. Open the DMG and drag **Code Flow** to **Applications**.\n3. Open Code Flow in its own desktop window. Closing or reloading the window keeps agents running.\n4. Click its Dock icon or use **Code Flow → Show Code Flow** to return. Use **Code Flow → Quit** to stop; it asks before interrupting active agents.\n\nNode is included in the app. You still need Git 2.38+, a signed-in supported agent CLI and Chrome or Edge for the shared browser. Install those before your first run. Quit Code Flow before replacing the app with a newer download. Do not run the npm and macOS launchers together against the same data directory.\n\nFiles named **unnotarized** are developer preview builds. macOS may block them. For ordinary installation, use npm until a signed and notarized DMG is available.\n\n## Connect an agent and start\n\nInstall at least one supported CLI: `claude`, `codex` or `agy`. Run it interactively and sign in before starting a task. Open **Settings → Agents & models** in Code Flow to inspect availability. Additional ACP agents can be configured in Settings.\n\nChoose a Git repository, describe your task, pick a workflow, and start your first run. Follow the [getting started guide](getting-started.md) for permissions and review.\n\n## Startup options\n\n```sh\ncodeflow --no-open # start without opening a browser\ncodeflow --port 5320 # use another local port\ncodeflow --version\ncodeflow --help\ncodeflow client --help # authenticated API client\n```\n\nIf the browser does not open, use the **Open http://localhost:…/?token=…** link printed in the terminal. Keep that link private. A restart creates a new session; use the new link if your browser asks you to sign in.\n\nIf another instance is already running, use its browser tab or stop it before starting again. A busy port can be changed with `--port`; an existing server using the same data directory must be stopped first.\n\n## Settings, updates and removal\n\nIn desktop versions with the updater, open **Settings → General → App updates** or **Code Flow → Check for Updates…**. The app also checks on launch and every six hours, and shows **Update available** in the sidebar. Preview installs receive newer previews and stable releases; stable installs receive stable releases only.\n\nClick **Download update**, then **Restart to update** when ready. If agents are working, Code Flow asks whether to keep running or save progress and restart. In-app installation requires a signed, notarized app and a release containing the matching update package. Developer previews show **Download installer** instead: download the DMG, quit Code Flow, and replace the app in Applications. Older versions without this button need one manual upgrade first.\n\nSettings and history live in `~/.codeflow`, outside the installation. Reinstalling, upgrading or removing the launcher preserves that folder. Set `CODEFLOW_HOME` to an absolute path to use a separate data directory. Avoid switching an existing data directory back to an older preview; back it up before trying new versions.\n\nThe preview does not install or sign in to agent CLIs for you. Provider access and usage limits depend on your account. See [security](security.md), [configuration](configuration.md) and [preview release preparation](releasing.md).\n\n[Documentation library](README.md) · [Getting started](getting-started.md)\n","title":"Install Code Flow preview"},{"group":"Get started","id":"getting-started","source":"docs/getting-started.md","body":"# Getting started\n\nTake a small, well-defined task from your own repository to a reviewed result. This guide covers installation, your first run and the settings that influence what the agents receive.\n\n## Requirements\n\n- Node 22.13+ for npm or source installation. The macOS app includes Node.\n- git 2.38 or newer: **Merge** relies on `git merge-tree --write-tree`, which 2.38 added. Check yours with `git --version`.\n- At least one of `claude`, `codex` and `agy` installed and signed in. Run each CLI once interactively first. Open **Settings → Agents & models** to inspect provider health and model choices.\n\n## Install and run\n\n### Try the preview\n\nOn macOS, Linux or inside Windows **WSL2**, run:\n\n```bash\nnpx --yes --package=https://github.com/midhunxavier/code-flow/releases/download/v0.1.0-preview.2/midhunxavier-code-flow-0.1.0-preview.2.tgz codeflow\n```\n\nThe launcher starts Code Flow locally and opens your default browser with its sign-in link. Keep the terminal open while you work; press **Ctrl+C** to stop. If the browser does not open, copy the sign-in link printed in the terminal. Keep that link private.\n\nOn Windows, install Node, Git and your agent CLI inside WSL2, sign in there, and keep your repository in the Linux filesystem. Native PowerShell and Command Prompt execution are not supported in this preview.\n\nFor regular use, install the launcher globally, then start it with `codeflow`:\n\n```bash\nnpm install -g @midhunxavier/code-flow@preview\ncodeflow\n```\n\nRun the same install command to update. Remove the launcher with `npm uninstall -g @midhunxavier/code-flow`.\n\n### Download the macOS app\n\nChoose **0.1.0-preview.2** for your Mac (**macOS 13+**):\n\n- [Download Apple Silicon DMG (M1 or later)](https://github.com/midhunxavier/code-flow/releases/download/v0.1.0-preview.2/code-flow-0.1.0-preview.2-macos-arm64-unnotarized.dmg)\n- [Download Intel DMG](https://github.com/midhunxavier/code-flow/releases/download/v0.1.0-preview.2/code-flow-0.1.0-preview.2-macos-x64-unnotarized.dmg)\n\nOpen the DMG, drag **Code Flow** to **Applications**, then open Code Flow. The Electron app opens its own desktop window. Closing or reloading that window keeps agents running. Click the Dock icon or **Code Flow → Show Code Flow** to return. Use **Code Flow → Quit** to stop the app; it asks before interrupting active agents. Node is bundled; install Git and sign in to a supported agent CLI before your first run. Install Chrome or Edge for the shared browser.\n\nThese are **unnotarized preview builds**. macOS may block them; use npm if blocked. Quit Code Flow before replacing it with a newer download. Run one launcher at a time. [Release notes and checksums](https://github.com/midhunxavier/code-flow/releases/tag/v0.1.0-preview.2).\n\nThe preview stores settings and history in `~/.codeflow`, outside the installation. Updating or removing the launcher preserves that folder.\n\n### Install from source\n\n```bash\ngit clone https://github.com/midhunxavier/code-flow.git\ncd code-flow\nnpm ci\nnpm start # http://localhost:4317\n```\n\n### Signing in\n\nThe first browser that opens code-flow is signed in automatically. That stays possible until a browser signs in (with or without the link) or code-flow first starts an agent, a check or an app. After that, other browsers need the link code-flow prints when it starts (`Open http://localhost:4317/?token=…`).\n\nThe session token exists only in the server's memory, so the agents code-flow runs can't use its API, even sandboxed ones with network access. A restart starts a new session. If the browser asks you to sign in, use the new owner link printed by that server.\n\n## Development\n\nFor working on Code Flow itself, use either command after `npm ci`:\n\n```bash\nnpm run dev # deterministic demo agents; no real provider work\nnpm run dev:real # installed real CLIs; sign in to each CLI first\n```\n\nBoth modes use http://localhost:4318 and `.codeflow-dev` as their home directory, rebuild the UI and restart the server on source changes. For real-agent mode, install the CLIs you intend to use and run each interactively to sign in before starting. The normal `npm start` workflow above uses http://localhost:4317.\n\n## Your first run\n\n\n\n1. Choose **Open repository**, browse to a Git repository on the displayed computer, and choose **Open this repository**. Accept or change the suggested project name, then choose **Create project**. If the repository already belongs to projects, choose its destination. This opens the composer without starting an agent; no workspace or team setup is required.\n2. Describe the task. Drag files from the **Files** panel, or from Finder, onto the task box to give the agents context. **Improve** (at the right of the task box) rewrites a rough task so agents can act on it: the same intent, with the goal first, the specifics, how to tell it's done, and any open questions. **Undo** puts your text back.\n - It's one short turn by your configured agent (Auto by default), on your subscription, using its read-only permissions in a disposable private Git directory. Improve never resets your working checkout.\n - **Settings → Agents & models → Prompt improvement** configures Auto, Claude Code, Codex or Antigravity independently of the main task. Choose separate Quick and Repo models and supported reasoning effort for each provider, plus the default mode. Settings save in this browser for this computer. Auto preserves the Claude → Codex → Antigravity preference; a pinned agent reports availability, login or quota errors without switching agents. The Improve control shows the configured agent, and the completion note names the agent that performed the rewrite. Reset restores Haiku for Quick, Sonnet for Repo and low effort for Codex/Antigravity.\n - The arrow next to it picks a mode for the current composer without changing the saved default: **Quick rewrite** (the default) works from your text alone (by default, Claude's Haiku, or low effort for Codex and Antigravity). **Use the repository** reads a snapshot of the current committed version so the rewrite can name real files and commands; it takes longer. Uncommitted changes, untracked/ignored files and executable provider configuration are not copied.\n - **Stop** cancels. Editing the task or changing its repository, context, source or execution profile discards the old request; Undo applies only while the rewritten text is still current.\n - Improve supports the **local** execution profile. SSH and container selections disable it, and filesystem restrictions pause it. Other Code Flow mutations in that repository are refused until the turn and cleanup finish. Repository snapshots allow up to 5,000 regular files, 2 MiB per file and 128 MiB in total; unsupported symlinks, submodules and larger snapshots fail explicitly.\n3. Open **Template library** and choose an optional intent starter: **Fix a bug**, **Build a feature**, **Review code**, or **Explore an idea**. Each selects an existing workflow and explains why. Existing task text is preserved; exploration disables the build/review roles for a design-only debate. You can still pick any workflow manually. Pick how the agents work together, and click an agent for each role. Under each role you can set that agent's model and effort for the role. The sentence under the team, such as *\"Claude plans → Codex builds → Claude reviews\"*, shows what will happen.\n4. Review the **computer**, **project**, repository/branch, team, checks and execution limit in the compact summary. Open **Run options** to pick the base branch, the result branch name, rounds, limits, permissions, and the default model and effort per provider. Set **Worktree setup command** (for example, `npm ci`) and **Done when this passes** for this run; save reusable commands in **Settings → Projects**. For local runs using a single check, **Repository readiness → Inspect readiness** checks the repository and installed providers. **Validate commands** executes setup and the check at the selected commit in a temporary worktree, so you can resolve setup failures before spending agent calls.\n Composer readiness checks the selected workflow and providers. Unused signed-out providers are skipped, while selected providers that are unavailable, incompatible or signed out block readiness. Auto still needs at least one usable eligible provider within its permitted pool.\n\n5. Press **Start run** (⌘↵).\n - The run reads like a conversation: your task, then the agents' work as one list of steps (open any step to see its live transcript), then the result. A reply box stays at the bottom.\n - When the run finishes, each result is a branch such as `codeflow/<run>/build`, with buttons to review it, merge it, open a PR, or copy its diff command.\n - **Needs more work** records feedback for the current commit and opens its branch review. Add an overall or line comment, then **Send comments** to start a revision. Sending waits for the original writer to load unless you explicitly choose a recipient. A failed send retains your draft. Project-mission results use their mission's revision controls.\n - **More run actions → Edit & run again** restores the task, original base commit, provider/model choices, permissions, checks, limits and execution location into an editable draft. It creates fresh result branches and retains the original run's source restrictions. Reselecting the same repository retains the draft; editing CI-log options refreshes source context while keeping the pinned commit. Reattach any original attachments you still need. A restored custom verification profile is retained; choose a preset before saving it as a task template.\n\nReview and merge results yourself if you prefer the command line:\n\n```bash\ngit diff main...codeflow/<run>/build\ngit merge codeflow/<run>/build\n```\n\n## Return to a project\n\nOpen a project in the sidebar. **Overview** shows work needing attention, running work and recent results; **Runs** searches that project's history and can include archived runs. **Repositories** opens files, verification, context, history and services for a specific repository or run worktree. Existing Workspace bookmarks remain supported. **Project settings** keeps project editing, outcome metrics and recorded scenarios accessible. **More → Board** opens shared tasks on this project’s owning computer; **More → People** shows members, repository keys and authorized access controls. **More → Automation** contains Run once later, Repeat, Process a backlog and Pursue a mission. Board and Automation become visible tabs when configured, even when the optional Board shortcut is off. Command search finds every destination.\n\n\n\n**New run** opens the project's saved draft. Text, attachments, selected files, workflow and run options are saved separately for each project on each computer. Browsing another project's page or filtering Inbox does not change the draft's destination. The composer shows one repository directly or a selector for multiple repositories. **Open another repository** asks you to choose or explicitly extend a project before launching.\n\nExisting browser drafts migrate once. Ambiguous drafts remain **Unassigned draft** in the composer project picker until you choose a destination. A removed project or repository blocks launching into it; the draft remains available. If draft storage fails, keep the tab open and use **Retry saving**.\n\n## Find help in the app\n\nOpen **Application menu → Help & documentation** for the searchable local guides. **Ask code-flow** answers questions from these guides using an installed CLI. See [the help guide](help.md) for supported providers, privacy and limits.\n\n## Giving agents context\n\nThe **Files** tab of the [side panel](runs.md#the-side-panel) (the **Files** button under the task, or <kbd>\\</kbd>) shows the selected repo: ignored files are hidden, files with uncommitted changes are marked **M**, and untracked files **U**. To add something to the task's context, drag it onto the task box, click **+**, or double-click it; clicking a file opens it to read. You can also add files from your computer, such as specs, screenshots or logs (up to 10 MB each, 25 MB in total). Drop them from Finder, or use **Attach**.\n\nEvery agent's first prompt gets a *Context from the user* section listing these items. Nothing is pasted into the prompt:\n\n- Repo files and folders that are identical in the base branch are referenced by path; every worktree already has them.\n- Your **working-copy** version of a file is copied into `.codeflow-context/repo/<path>` in each worktree when the file is uncommitted or differs from the base branch. The agent sees exactly what you see.\n- Dropped files are copied into `.codeflow-context/attachments/`.\n- `.codeflow-context/` is listed in `.git/info/exclude`, so it never ends up in an agent's commit.\n- Folders that aren't in the base branch are rejected. Commit them, or drop the individual files.\n\n## Models and effort\n\n**Settings → Agents & models** sets a model and a reasoning effort per provider. Every role that uses that provider runs with them. Leave either on *Default* to use the CLI's own configuration.\n\n| Provider | Model list | Flags passed |\n|---|---|---|\n| Claude Code | aliases `fable`, `opus`, `sonnet`, `haiku`, or any full model name via *Other…* | `--model`, `--effort` (low … max) |\n| Codex | `codex debug models`, with each model's supported efforts; the default comes from `~/.codex/config.toml` | `-m`, `-c model_reasoning_effort=…` |\n| Antigravity | `agy models` | `--model`, `--effort` (low … max) |\n\n**Per role:** under each role in the team, every agent you picked has a model picker and an effort picker. They override the provider's setting for that role only, for example Opus to plan and Sonnet to review. Left on *Default · …*, they show what the agent will use.\n\nModel lists are cached for 10 minutes. Your choices are remembered in the browser.\n\n## Worktrees and branches\n\nEvery worktree a pattern needs (`build`, `candidate-a`, `integration`, …) is created with:\n\n```bash\ngit worktree add -b <prefix>/<name> <repo>/.worktrees/<run-id>/<name> <base>\n```\n\n- **Base branch:** the starting point for every worktree in the run. By default this is the repo's current `HEAD`. The dropdown also lists local branches, remote branches, and the result branches of earlier runs, so you can build on a previous result. Uncommitted changes in your checkout aren't included.\n- **Result branch prefix:** defaults to `codeflow/<run-id>`. If you set it to, say, `feat/rate-limit`, results land on `feat/rate-limit/build`. A prefix can only be used once, because git can't have both a `feat/rate-limit` branch and `feat/rate-limit/…` branches.\n- **Submodules:** a worktree of a repo with a `.gitmodules` gets its submodules checked out (`git submodule init`, then `git submodule update --init --recursive`, for up to 10 minutes). `init` registers them in the repo's `.git/config`, as it would in your checkout. If cloning one fails (say a private submodule git would have to prompt for), the worktree is still created, without it.\n- **Location:** worktrees go in `<repo>/.worktrees/`. code-flow adds `/.worktrees/` to `.git/info/exclude`, which keeps them out of `git status` without editing your `.gitignore`. Tools that don't read git's ignore files (some test runners, linters, `tsc` globs) may still scan `.worktrees/`. Exclude it in their config, or set `CODEFLOW_WORKTREE_DIR` to an absolute path to keep worktrees outside the repo.\n- **Cleanup:** remove a run's worktrees with `git worktree remove .worktrees/<run-id>/<name>`, or delete the folder and run `git worktree prune`. The branches stay until you delete them. **Archive…** on a run, and **Clean up** in [Insights](runs.md#insights-comparisons-and-finding-runs), do this for you.\n\nNext: [Team patterns](patterns.md) · [Talking to a run](runs.md)\n","title":"Getting started"},{"group":"Get started","id":"patterns","source":"docs/patterns.md","body":"# Team patterns\n\nStart in Auto to let the coordinator build and adapt a plan. Solo is directly accessible for one agent. Open Advanced to choose reusable built-in workflows such as Plan → Build → Review, a loop, council, or debate, or to design and save a versioned template. Fixed workflows retain the execution and verification behavior described below.\n\n## Team patterns\n\n| Pattern | Roles | Flow | Reach for it when |\n|---|---|---|---|\n| **Auto team** | coordinator, generated workers, independent reviewer | Propose and approve a team → ready-task scheduling → addressed questions and bounded revisions → integrate, check, review and repair | You want code-flow to choose the team and workflow; see [Auto team](auto-team.md) for communication, memories, budgets and recovery |\n| **Plan → Build → Review** | planner, builder, reviewer | Plan (read-only) → implement → **check** (the builder fixes it first if it fails) → independent review against the task, with the plan as context → code-flow runs the reviewer's tests → the builder fixes what blocks → re-check and re-review, up to *N* rounds | Most features and bug fixes. Cross-vendor review catches what a model misses in its own work |\n| **Loop until green** | workers (1+, in hand-off order) | Baseline check → the agent works → code-flow runs the check → the failure goes back to the agent → repeat until it passes. A round that makes more tests fail is undone. After *K* rounds without progress, the next vendor takes over with a handover note | \"Done\" is a command: failing tests, type errors, a broken build. Needs a check |\n| **Best-of-N race** | contestants (2+), judge | All contestants solve the task in parallel, each in its own worktree → judge compares **anonymized** diffs (Candidate A/B…) and picks one | The solution space is wide, or the task is risky enough to want several independent attempts |\n| **Debate → Synthesize → Build** | debaters (2+), synthesizer, builder and reviewer (optional) | Independent proposals → critique rounds where each debater sees the others' proposals (**anonymized**: Proposal A/B…) and revises → synthesizer merges and records each decision → builder implements → reviewer checks it against the plan → code-flow runs the reviewer's tests → builder fixes what blocks → re-check and re-review, up to *N* rounds | Architecture, API design, big refactors. Leave the builder empty for design only |\n| **Supervisor → Workers** | supervisor, workers (1+) | Supervisor splits the task into parallel subtasks and assigns workers → workers build on separate branches → branches merge into an integration branch; the supervisor resolves conflicts, a reviewer reviews, and the supervisor fixes what blocks | Larger tasks that split cleanly (backend / frontend / tests) |\n| **Test-first (adversarial tester)** | tester, builder | Tester writes failing tests → builder makes them pass without editing them or how they run (code-flow undoes it if it does) → tester runs them, adds edge-case tests, and sends failures back | Behavior you can specify with tests. Stops an agent from grading its own homework |\n| **Review council** | reviewers (1+), chair | Reviewers review the PR's or branch's changes independently, read-only → code-flow runs the tests they show bugs with → the chair merges the findings without knowing who wrote what. Findings rank by severity, then confirmed before the rest, then by how many reviewers raised them | Reviewing a PR before merging. Nothing is changed or posted without you |\n| **Single agent** | agent | One agent works on the task in its own worktree → code-flow commits and runs the check if you set one → you reply, and each reply continues the same session on the same branch | Quick changes, questions and exploring, where a team is more than the task needs. Start a team from its result when you want a second opinion |\n\nEvery pattern works with any mix of agents. A good default is to have a **different vendor review or judge** than the one that wrote the code.\n\n**Auto** in a role picks an installed agent when the run starts. It skips plans that are at 90% or more of a usage window or recently hit a limit, until the limit resets (Claude reports its windows; the others report only when they're limited), and for 15 minutes a CLI that turned out not to be signed in. Once agents have a few finished runs in a repo, it prefers the one whose code passed the check most often there. The pick and the reason are written to the run's activity log. Quota is saved, so a restart remembers which plans are out.\n\nVerdicts, judge picks, subtask lists and test specs are **schema-constrained**: `claude --json-schema`, `codex --output-schema` and `agy --json-schema`. An answer that still doesn't fit gets one retry in the same session, and then the run **fails**: an unreadable review is never treated as approval.\n\nJudging in a race is blind: no author summaries, a shuffled order (seeded per run, so a resumed run replays identically), and an optional **panel** of judges that vote. Pick vendors that aren't competing; the UI suggests one, and the log warns when a judge also competes.\n\nThe same goes for the other roles that weigh someone else's work. Unless you pick one yourself, the composer fills each with a vendor that didn't do that work, and the log warns when a run has one agent doing both:\n- Plan → Build → Review: the reviewer is a vendor other than the planner's (and the builder's, when a third is installed). A reviewer that shares the planner's view lets a wrong plan through, so it's told to judge the change against the task, with the plan only as context.\n- Debate: the synthesizer is a vendor that isn't debating. Debaters and the synthesizer see the proposals as *Proposal A/B…*, never by vendor, so nobody sides with its own by name.\n- Supervisor: the reviewer is a vendor other than the supervisor's (see *Independent review* below).\n\nFor the same reason, reviewers are anonymous to a review council's chair (*Reviewer A/B…*).\n\n## Checks, loops and limits\n\ncode-flow treats each pattern as a loop, and what it engineers is the loop around the agents:\n\n- **Done when:** a check command such as `npm test`, `pytest` or `cargo test`. It's auto-detected from the repo and editable per repo.\n - Python projects locked with uv or Poetry get their own environment, since system Python doesn't have their packages: with `uv.lock`, setup `uv sync` (`uv sync --all-packages` in a uv workspace) and check `uv run --no-sync pytest -q`; with `poetry.lock`, setup `poetry install` and check `poetry run pytest -q`. When the pytest config lives only in `tests/` (a workspace member of its own), the check runs from there. The composer says what it detected from.\n - **code-flow runs it itself** after each change, in that worktree, and feeds the failing output back to the agent.\n - Loops end on that evidence, not on an agent saying it's done. Every check shows in the run's steps with its output.\n - Pipeline: reviewers only see code that passes.\n - Race: a candidate that fails can't win if another one passes.\n - Supervisor and Debate: the integrated or built result is checked, and the agent fixes failures.\n - Test-first: the new tests must fail first, then pass.\n - Every pattern: a passing result is proven fail-to-pass (see below).\n - When a run finishes but the check still fails, the run is shown as a warning, not a success.\n- **A check that can't run isn't a failing check.** Some failures say the environment is wrong, not the code: the command isn't installed (exit 127), Python has neither pytest nor the project's package, pytest can't load `conftest.py` or collects nothing (exit 4 or 5).\n - When the run's first check fails like that, code-flow runs it once more on the base commit, in a throwaway checkout (with your setup). If it fails the same way there, the change isn't to blame.\n - The check is then set aside for the rest of the run: no \"Fix failing check\" rounds (agents would only rewrite the test setup to get around it), and later checks are skipped.\n - The review still runs, and the reviewer is told nothing was tested. The verdict stays unknown instead of failing.\n - The log, the result card, the inbox and the phone view say *The check can't run here: \\<first error line\\>. Set `setup` and `check` in .codeflow/config.json*. *Loop until green* stops before its first round, since no round could pass.\n - If the check runs on the base commit, the change broke it, and it's a failing check like any other.\n- **A pass only counts if the tests are still the tests.** After every change, code-flow looks for edits that make a check pass without fixing anything:\n - deleted test files, and test files renamed to a name the runner doesn't pick up (`parse.test.ts` → `parse.ts`);\n - added `.skip`, `.only`, `xit`, `it.skipIf(…)`, `test.runIf(…)`, `test.skip.each(…)`, `test.fails`, `this.skip()`, `{ skip: true }`, `@pytest.mark.skip`, `pytest.importorskip`, `t.Skip()`…; in Python and JS/TS, not inside a string or comment (a test of a guard against skips may write one into a fixture);\n - test files that lost assertions;\n - a changed test config or CI workflow, including the runner's settings in a file shared with the rest of the project: pytest's `[tool.pytest…]` tables in `pyproject.toml`, and the `jest`, `vitest`, `mocha` or `ava` settings in any `package.json` (`addopts = \"--deselect …\"`, `testPathIgnorePatterns`…). The rest of those files (dependencies, other tools' settings) isn't flagged;\n - a changed `scripts.test` (or whichever script the check runs);\n - fewer tests running than before, read from the runner's summary. Each result branch keeps the counts of its check (the first and the latest, and at which commit), so a follow-up after the run compares with how many ran before its change.\n\n If git can't show the change, that is reported too (*couldn't verify test integrity*), as a serious issue: not being able to look isn't the same as finding nothing.\n\n What happens next depends on the pattern:\n - Loop until green: a pass that relies on such changes doesn't end the loop. The agent is told to undo them and fix the code. If it keeps them (the task may need it), or the rounds run out before it can answer, the result stays but is flagged, and the pass doesn't count: the run gets no verdict (as when the check can't run), its summary says *The check passes only with changes to the tests* and lists them, and no worker is credited with getting it green, so it doesn't feed Auto's picks. When the runner's summary doesn't say how many tests ran, removed assertions count as such a change too, since a drop in tests can't be seen.\n - Pipeline, Supervisor and Debate: the changes go to the reviewer, who raises each as an issue (so it blocks approval) unless the task needs them.\n - Race: a candidate that passes only that way can't beat one that passes honestly.\n - Test-first: the builder's edits to the tests are undone, and so are the tester's edits outside them. Only test files count as the tests (plus any the tester names): a stub module the tester adds for its tests to import is the builder's to fill in. The builder's changes to the test runner's own config are undone too, a new `conftest.py` or `vitest.config.*` included. What can't be put back file by file (the runner's settings in `package.json` or `pyproject.toml`, the check's script, a skip in an existing test, fewer tests running) goes back to the builder to undo, and the tests aren't probed or counted as passing until it has; if it never does, the run gets no verdict. Other changes the tests read (`vite.config.*`, a CI workflow) are passed on to the tester to look at.\n - PR follow-through: nobody reviews these fixes before they're pushed, so one that touches the tests at all isn't pushed: any of the changes above, not only the serious ones (a changed CI workflow or test config, a skip or an assertion fewer). The watch hands it to you instead.\n\n Every flagged result shows **⚠ Changes to tests to check before merging**.\n- **A pass should prove the change (fail-to-pass).** A check that passes with the change might pass without it too, and then it shows nothing about the change. So once a result's check passes, code-flow proves it:\n - it puts the change's tests onto the base code, in a checkout of its own (set up once per run): the test files, their fixtures and snapshots, and the runner's settings (`jest.config.*`, `conftest.py`, `pytest.ini`…). A test file the change deleted is deleted there too. When the change also adds or updates dependencies (`package.json`, `uv.lock`, `go.mod`…), those files come along and setup runs again, so a package only the change adds can't make the tests fail there as if they needed the change's code;\n - it runs the check there. **It should fail**: then at least one test needs the change, and the step reads *Fails without the change, as it should*. A test that imports code only the change adds fails there too, but only because it can't load (an import, collection or build error), which shows the code is there, not that it works: when that is the only way it fails, the result is a **weak proof**. A test that fails on what it checks (an assertion, an expected value) is full proof, also when another can't load;\n - if it passes there as well, the result is **not proven**: no test fails without the change. A run that never reached the tests (no test runner, nothing collected, setup failed, a timeout) proves nothing either way and says so.\n\n When a fix makes a failing check pass, the tests that failed before are the proof, and no new test is needed. When the check already passed before any change (a new feature, say), only a new test can prove it. What happens next depends on the pattern:\n - Builders are told up front that their tests will run on the original code (Pipeline, Race, Debate, Supervisor's workers, Loop until green).\n - Loop until green: an unproven pass doesn't end the loop the first time. The agent is asked for a test that fails without its change, or to say why the task can't be tested that way (docs, a pure refactor). If it adds none, the pass stands and the result is flagged. When the change touches no tests, the baseline check is the run on the base code, so the proof needs no extra run.\n - Pipeline, Debate and Supervisor: before review, the builder (the supervisor, in Supervisor) is asked once for such a test. If the check then fails and the fix attempts don't help, code-flow undoes the new test, so asking for a proof never costs a green result. The reviewer sees the proof either way, and is told to ask for a test when it's missing.\n - Race: each passing candidate is proven, and the judge sees which ones are, still blind. It's evidence for the judge, not a rule: an unproven candidate can win.\n - Test-first: the tester's tests must fail before anything is implemented. If they pass, the tester is asked once to make them fail for the missing behaviour.\n - Review council: with a check, code-flow runs it on the PR's changes and proves them against the code the PR started from, and every reviewer sees both. The proof goes into the review you can post.\n - Single agent: the result is proven and the proof reported; nobody is asked for a test.\n\n Each result shows **✓ Proven: a test fails without this change** (with how it fails), **⚠ Weak proof** (the tests fail without it only because they can't load), or **⚠ Not proven**. Reviewers and judges are told which. Insights counts proven results per pattern, out of all the proven, weak and unproven ones. A proof costs one more run of the check (plus setup once per run). Turn it off for a repo with `\"prove\": false` in `.codeflow/config.json`.\n- **Review findings are checked, and only what blocks holds approval back.** A reviewer's word is an opinion: a false positive costs a fix round, and the builder can't answer it. So in Pipeline, Debate, Supervisor and the Review council, a reviewer rates each issue high, medium or low, and shows a bug with a *repro*: a test file (its path and whole contents) that fails on the result because of the bug, a plain test-runner command that runs it, or both. A command alone can run tests that are already there.\n - code-flow writes the test into a checkout of the result's current commit, apart from the builder's worktree, and runs it with the reviewer's command, or else the run's check. That checkout is set up once per run, and again only when a file that says which dependencies to install changes (`package.json`, `uv.lock`, `go.mod`…). It's the reverse of a fail-to-pass proof: there the tests should fail without the change; here the reviewer's test should fail with it.\n - The step reads *Fails: the problem is there* or *Passes: a false alarm*. A finding is **confirmed** (its test fails), **not confirmed** (it passes: a false alarm), or **unconfirmed** (no test came with it). Sometimes code-flow can't tell: the test runner didn't start, it timed out, the check already fails on that code without the test, or the test never ran: as many tests ran with it as without (with the reviewer's own command, code-flow counts once more without the test, unless the command names it), no test ran at all, or the runner doesn't say how many ran and nothing in its output mentions the test.\n - Only a plain invocation of a known test runner runs, and only a file named like a test goes in, so a repro can't replace the code it's about. The test is never written through a symbolic link in the code under review, so a PR's tracked link can't send it outside the checkout. At most 6 run per review; low issues aren't run.\n - **Approval goes by the findings, not the verdict word.** High and medium issues block; low ones are suggestions. A false alarm never blocks. A high-severity issue claims a bug, so it blocks only when its test fails, or when code-flow couldn't run the test it came with. Without a test it's marked unconfirmed and doesn't block; reviewers are told to rate a problem no test can show medium. So a reviewer that approves with a confirmed bug doesn't approve, and one that requests changes over false alarms and suggestions does.\n - Findings that block go back to the builder (the supervisor, in Supervisor). Confirmed ones come with the reviewer's test and how it fails: the builder makes it pass and adds it (or one like it) to the change. The ones no test shows it fixes, or disputes with evidence (a test it ran, the code that handles it) instead of changing code. False alarms and suggestions are listed as needing no change. In the next review the reviewer sees the builder's answer, and keeps a disputed bug only with a repro that fails.\n - Each run of a reviewer's test is journaled, so a resumed run replays it instead of running it again, and the same test on the same commit runs once.\n - Review council: each reviewer's tests run before the chair merges the reviews, and the chair sees what each one showed and keeps a finding's repro. Findings rank by severity, then confirmed before the rest (false alarms last), then by how many reviewers raised them. The council's verdict goes by the findings too: it requests changes when a finding blocks, and approves when none does, whatever the chair said. Nor can the chair drop a finding a test confirms, or rate it lower: code-flow puts it back as its reviewer raised it, marked *Restored by code-flow* in the review, and the log says so. The review you can post says how each finding was checked.\n\n The result shows the latest review's findings, each marked **✓ Confirmed** (with how it fails, and the test), **✗ Not confirmed** (a false alarm), **⚠ Unconfirmed** or *couldn't run*, and which block. The summary counts unconfirmed high-severity claims and false alarms.\n- **Independent review:** Supervisor has a *Reviewer* role (the composer picks a different vendor than the supervisor) that checks the integrated result instead of the supervisor checking its own work. The supervisor also writes the *contracts* (interfaces, names, data shapes) that every worker gets. If you skip the reviewer, the supervisor reviews its own integration and fixes what it finds, but nobody reviews those fixes; the result says \"fixed by the supervisor, not re-reviewed\". Debate can have a reviewer check the build against the agreed plan; after the builder addresses its review, the check runs and the reviewer looks again, up to *Max review rounds*.\n- **Missing parts are reported:** in Supervisor, a worker that finishes without changing anything is listed as *no changes* with the first line of its answer, not as done. Its branch isn't merged, the supervisor and reviewer are told which parts are missing, and Insights counts it as a failed step. If the supervisor splits the task into more subtasks than *Max subtasks*, the extra ones are added to the last subtask (the log says so) rather than dropped.\n- **Parallel work stops together:** in Race, Supervisor and Debate, an agent's failed turn is part of the result (a failed candidate, a missing part), but when anything else fails in one branch (its worktree, setup, a commit or a check), the other branches are stopped too. The run ends once every branch has, so no agent keeps running after it, and Stop and the slots stay accurate.\n- **What agents see:** long diffs keep whole source files first, put lockfiles and generated files last, and list any file that didn't fit with the command to read it. A long check log keeps the lines around each failure plus the runner's summary, not just the tail. Agents also get the repo's own conventions files (`AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, …), since each CLI otherwise reads only its own. They're also told about problems reviewers raised in more than one earlier run in that repo (turn this off with `\"learn\": false`).\n- **No progress means hand off:** in *Loop until green*, a round counts as progress when it commits a change that leaves fewer tests failing, read from the runner's summary. A different failure with as many failing tests, or more, isn't progress. When the runner doesn't say how many fail, or no test fails and the check still does (a lint step after the tests, say), any different failure counts as progress. A round with no new commit, or the same failure as before, is stuck. After *K* stuck rounds, the next vendor takes over.\n- **A round that makes it worse is undone:** *Loop until green* keeps the state closest to passing so far, by the runner's count: the fewest failing tests, and a pass before a failing check with none failing (at first, the code before any change). A round that ends further from passing is undone: more tests failing, a check that fails where it passed, or no test results where there were some. code-flow puts the files back to that state in a commit of its own, runs the check again, and tells the agent why. So the branch never ends worse than the best state the loop reached, and a run that ends failing says how many tests still fail. The rounds stay in the branch's history. Without a count from the runner, nothing is undone.\n- **Limits:** every run stops at a maximum number of minutes or agent steps (60 and 40 by default). The clock doesn't run while the run waits for you (a checkpoint), or only waits for a free slot (see *One machine, many runs*). A limit that isn't a positive number is ignored (the default applies). A stopped run keeps its work: each worktree with changes is listed as \"Work so far\".\n- **Plan usage:** if a CLI says its plan is out of usage, the run stops instead of failing, so you can resume it once the plan resets. That goes for a race contestant or a Supervisor worker too: nothing is judged or merged without it. Auto skips that plan until the reset time the CLI gave (or the reset times it reported before). A CLI that isn't signed in fails the step with a message saying so, and Auto skips it for a while. A contestant or worker whose CLI isn't signed in, or stalled, fails its step without that failure being recorded as its result: a resume gives it its turn again.\n- **Retries:** a turn that fails for a passing reason (overloaded, rate-limited, a network error, a CLI that stalled) is retried after 20 s, then 60 s, with its agent slot free meanwhile. A write turn picks up where it stopped: it continues its own session, or, without one, starts over from the worktree as the step found it, so a retry never redoes the work on top of half of it. Whether a failure is passing comes from the CLI's own error type; for a CLI that gives none, from its message (an HTTP status counts only with words around it, such as \"status 503\", not a bare 500 in a test log).\n- **One machine, many runs:** at most 6 agent CLIs and 3 checks run at once across all runs (`CODEFLOW_MAX_AGENTS`, `CODEFLOW_MAX_CHECKS`). Others wait their turn, and the step says so. The wait doesn't count against the run's time limit (a sweep's runs queued behind each other don't run out of time before they start), unless another of its steps runs meanwhile.\n- **Stopping really stops:** each agent CLI and each check runs in its own process group. Cancelling, a time limit or the CLI exiting stops everything it started, such as a dev server it left in the background, and escalates to SIGKILL if needed.\n- **Setup:** an optional command, such as `npm ci` or `uv sync`, that runs once in every new worktree before the agents start. code-flow runs it itself, outside the agents' sandboxes, so dependencies are installed even though Codex has no network in Standard mode. A worktree is marked as set up (in its own git folder, not in its files) once setup finishes; one whose setup never finished, because code-flow stopped during `npm ci` say, is set up again when a resumed run reuses it.\n\nAgents may suggest their own test command (the tester in Test-first does). code-flow only runs it if it's a plain invocation of a known test runner, with no shell operators. Otherwise it falls back to your check.\n\n> **Security:** a check runs the agents' code (tests, scripts) on your machine, outside Codex's and agy's sandboxes, as your CI would. Use code-flow on repos and tasks you trust, or run checks in Docker (see [Automations](automations.md#isolation)) or code-flow itself inside a container or VM. More in [Security](security.md).\n\nAdding your own pattern: see [Architecture](architecture.md#adding-a-pattern).\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Team patterns"},{"group":"Get started","id":"acp-agents","source":"docs/acp-agents.md","body":"# ACP coding agents\n\nOpen Settings → Agents & models, select a preset and check the executable and arguments. Run Check connection before saving, then select a discovered model for an eligible run.\n\n## Connect your CLI\n\nSettings → Agents & models provides **Connect OpenCode**, **Connect Kilo** and **Connect Cline** presets through the shared Agent Client Protocol (ACP) v1 adapter. **Add compatible CLI** connects other agents implementing the same stdio protocol.\n\n## Connect a supported CLI\n\nInstall and sign in on the machine running code-flow:\n\n| CLI | Install | Sign in | Preset invocation |\n| --- | --- | --- | --- |\n| [OpenCode](https://opencode.ai/docs/acp/) | `npm install -g opencode-ai` | `opencode auth login` | `opencode acp` |\n| [Kilo](https://kilo.ai/docs/code-with-ai/platforms/cli-reference) | `npm install -g @kilocode/cli` | `kilo auth login` | `kilo acp` |\n| [Cline](https://docs.cline.bot/usage/acp) | `npm install -g cline` | `cline auth` | `cline --acp` |\n\nUse a current CLI version with native ACP support. The Kilo preset uses the current `kilo` executable; Cline uses the official CLI's `--acp` flag.\n\n1. Install and sign in with the commands above. Complete provider/model setup in the CLI.\n2. Open Settings → Agents & models and choose the CLI's **Connect** button. If code-flow cannot find it, enter its absolute executable path. Reopening a saved preset edits its existing settings, preserving custom paths and arguments.\n3. Choose **Check connection**. This initializes ACP and creates a session in a temporary directory without sending a prompt. The result shows protocol compatibility, available models and advertised session loading. It does not verify inference, account quota or access to every advertised model.\n4. Choose **Save connection**. It appears immediately in provider and model selection; no server restart is needed.\n5. Select the agent for a run and choose **Full access** in Settings → Run defaults or the composer's permissions controls.\n\nCline asks for approval before tools, including reads, edits and commands, by default. Enable **Ask me for tool permissions** in the run's permissions controls and approve requests in the approval inbox. Without it, requests are declined. The preset does not enable Cline's auto-approval flag. Cline starts new ACP sessions in Act mode; read-only code-flow turns still apply the boundaries below. Provider selection stays in the CLI's authentication setup; code-flow's model picker changes the model within that provider.\n\nThe CLI keeps its own authentication. code-flow does not read or copy its credential files. A connection check can perform the CLI's normal startup work, including loading the user's configured plugins. Use executable invocations you trust.\n\n## Other compatible CLIs\n\nEnter a stable connection ID, display name, executable, and arguments, one argument per line. For example, an agent whose documented ACP invocation is `my-agent --acp` uses executable `my-agent` and one argument `--acp`. Executables must be names on the server's PATH or absolute paths. Arguments are passed directly with no shell expansion; paths with spaces need no quoting. Do not place credentials in these fields.\n\nUse **Edit**, **Disable**, **Enable**, or **Remove** to manage connections. Disabled connections retain their settings but are unavailable for new dispatch. **Reload connections** recovers after an edit in another window. The API rejects saves based on an older settings revision.\n\nModels and effort options are discovered from ACP session configuration; legacy `models` responses are also supported. Only advertised model/effort choices can be selected. Agents without model discovery can still use their CLI default. Changes to the command or arguments invalidate cached catalogs and the connection identity pinned by existing runs. Restore the original invocation to resume those runs, or start a new run. An already-running process retains its original invocation; subsequent operations check its pin again. This pin covers the invocation, not the executable's bytes or external CLI configuration.\n\n## Execution boundaries\n\n- ACP connections require **Full access** on the current application host. ACP does not provide a standard sandbox contract. Safe, Standard and Trusted permission profiles, SSH workers, agent containers, automated triggers and locked-down external-source runs are unavailable for these connections. Auto role selection excludes ACP unless Full access is selected.\n- Read-only turns request read-only behavior and decline permission requests for file changes and execution. The engine's existing worktree change checks still apply. This is not OS-level read-only isolation; the CLI may allow actions without consulting the client.\n- With **Ask me for tool permissions** enabled for an interactive run, ACP permission requests enter the existing approval inbox. Only `allow_once` can be approved; persistent permission grants are never selected. Requests are otherwise declined. Cancellation aborts pending approvals and sends `session/cancel` before bounded process cleanup.\n- The client advertises no filesystem or terminal capabilities. The agent uses its own tools. Agents that require host-provided file or terminal operations are not supported by this first version.\n- Session loading is used only when advertised. Replayed history is excluded from new turn output. Live mid-turn steering and the scoped code-flow team-tool bridge are unavailable; normal subsequent turns and structured team outcomes remain available.\n- ACP has no standard constrained-JSON output option. The adapter adds the requested schema to the prompt and the engine validates the returned result. Context-window occupancy and cumulative session costs are not reported as per-turn token usage or cost.\n\n## Storage and API\n\nConnections are owner-managed and saved in `CODEFLOW_HOME/acp-connections.json` with private file permissions and atomic replacement. The file is excluded from portable backups because it authorizes executable invocations. Reconnect agents explicitly after restoring on another host. Corrupt settings are not overwritten; repair the file and restart.\n\nOwner-only endpoints:\n\n- `GET /api/acp-connections`: version, revision and saved connections.\n- `POST /api/acp-connections`: `{ revision, connection: { id, name, command, args, enabled } }`. IDs use `acp:<lowercase-name>`.\n- `POST /api/acp-connections/check`: `{ connection }`, checks an unsaved or saved invocation without prompting.\n- `DELETE /api/acp-connections/:id?revision=N`: removes the saved connection.\n\nCustom filesystem restrictions block connection mutation/probing and agent execution. Shared-project accounts cannot manage or probe connections.\n\nProtocol references: [ACP v1](https://agentclientprotocol.com/protocol/v1/initialization), [session configuration](https://agentclientprotocol.com/protocol/v1/session-config-options). Cline's [native ACP implementation](https://github.com/cline/cline/blob/main/apps/cli/src/acp/acpAgent.ts) advertises both provider and model selectors in the model category; the adapter selects the model control explicitly.\n\nValidation includes process fixtures for streaming, session replay, model selection, permissions, cancellation, failures and timeouts; an engine run that changes a worktree and passes its check; and browser tests covering the Settings lifecycle.\n\n## Real CLI acceptance test\n\nRun the acceptance script explicitly with an installed, authenticated CLI:\n\n```sh\nnpm run test:acp:live # OpenCode (default)\nnpm run test:acp:live -- --agent kilo\nnpm run test:acp:live -- --agent cline\n```\n\nThis is outside the default test suite and consumes normal model usage. Set `CODEFLOW_LIVE_OPENCODE`, `CODEFLOW_LIVE_KILO` or `CODEFLOW_LIVE_CLINE` to an absolute executable path if needed; `CODEFLOW_LIVE_MODEL` optionally selects an advertised model ID. Otherwise the test uses the CLI's discovered default. Missing executables fail before creating a test repository or starting code-flow. Use `--help` for options.\n\nThe script creates a temporary repository and a separate code-flow home, saves the connection through the Settings API, and starts a real coding task. It verifies failing baseline tests become passing without test edits, streamed text and tool events, and a clean commit. It then restarts code-flow and sends a follow-up that must reuse the same CLI session and remember a phrase from the first turn. Both code-flow's check and an independent test run must pass. Only reads of the task files and tests, searches for fixture names, edits to the two task files, and the exact `node --test` command can be approved automatically; unrecognized permission requests stop the test for inspection. Temporary run logs, commits, and a `receipt.json` remain at the printed evidence path; the server stops automatically. The original project and code-flow settings are unchanged.\n\nThis two-turn test passed locally on October 19, 2026 with OpenCode 1.18.30 and `opencode/big-pickle`, including session loading after restart and two committed changes. This establishes compatibility for the observed installation, not every ACP agent or OpenCode release.\n\nBoth Kilo and Cline passed the same two-turn acceptance test on October 19, 2026 using their installed CLIs and normal account authentication:\n\n| CLI | Model | Duration | One-time approvals | Result |\n| --- | --- | --- | --- | --- |\n| Kilo 7.8.12 | `kilo/kilo-auto/free` | 52.0 s | 2 | Passed both turns and session restoration |\n| Cline 3.0.73 | `anthropic/claude-sonnet-5.5` | 24.8 s | 5 | Passed both turns and session restoration |\n\nThe [sanitized acceptance receipts](evaluations/acp-live-2026-10-19.json) record run IDs, commits, streamed event counts and checked assertions. Each run changed a baseline with three failing tests into four passing tests without modifying those tests, then produced a second clean commit after restoring the same session. The Cline run exposed and verified a fix for file-edit approvals: its native editor supplies `rawInput.path` without ACP `locations`, which the adapter now carries into the approval inbox. These observations establish compatibility for the listed versions and models; model availability and task success can vary.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"ACP coding agents"},{"group":"Get started","id":"help","source":"docs/help.md","body":"# Ask code-flow\n\nGet help with setup, workflows and troubleshooting from the documentation, using a coding CLI already installed on your machine. The assistant is available in the local app's documentation.\n\n## Open the assistant\n\n1. Start Code Flow and open the owner app using the link printed in the terminal.\n2. Choose **Application menu → Help & documentation**, or search for it in the command palette.\n3. Use the **Ask code-flow** side panel, or open it from the header, and choose an installed CLI.\n4. Ask a question such as “Which workflow should I use to fix a failing test?”\n5. Continue with follow-up questions or open source guides without losing the conversation. Press **Enter** to send and **Shift + Enter** for a new line.\n\nThe companion sits beside the guide on desktop and expands to a full-width panel on a phone. Close and reopen it to switch between reading and chatting. **New chat** clears the conversation and draft.\n\nThe assistant supports **Claude Code, Codex and Antigravity**. It uses the provider's existing CLI authentication. Model and effort preferences saved in the local app's **Settings → Agents & models** are forwarded; when unset, the CLI's defaults apply. ACP and extension providers are not offered in this help flow. ACP currently requires Full access for normal runs.\n\n## What it receives\n\nEach question includes excerpts from up to five relevant bundled guides, the page you are viewing as a retrieval hint, and up to three earlier question/answer pairs from the chat. Questions are limited to 4,000 characters; answers to 12,000. Source excerpts are capped at 10,000 characters each. Engineering records are excluded from answer retrieval so historical decisions do not substitute for current instructions.\n\nThe latest 12 messages, up to three completed question/answer pairs and the current draft are stored in this tab’s session storage. They survive guide navigation and reloads. **New chat** clears them; the browser manages their lifetime when the tab closes or is restored. Code Flow does not add it to run history or project memory. The selected provider processes the prompt under its own account and logging behavior. Requests consume the same CLI usage allowance as other invocations.\n\n## Execution boundaries\n\nHelp runs with read-only, Safe adapter settings in a fresh temporary Git directory. No selected repository, run transcript, attachment, secret file or project configuration is copied into it. The prompt instructs the assistant to answer from the supplied text without tools; this instruction is not an additional OS sandbox. Provider-level settings, integrations and hooks remain subject to the normal CLI trust boundary described in [Security](security.md).\n\nHelp does not expose merge, terminal, configuration or publishing actions. Source links are chosen by the server from the retrieved guides. Answers are displayed as text, not executed as HTML. Treat generated answers as guidance and check the sources before running suggested commands.\n\nHelp currently supports macOS and Linux, with absolute, nonempty PATH entries. Only an authenticated owner on the main application listener can use help. Shared-project accounts, runner credentials and the phone listener cannot invoke it. Filesystem restrictions pause help as they do other host CLI execution. Two simultaneous help requests are allowed, with a two-minute limit per turn. **Stop**, **New chat** or leaving the page cancels the request. Closing the panel lets an active answer finish; the disposable directory is removed after the adapter exits.\n\n## Troubleshooting\n\n| What you see | Next step |\n| --- | --- |\n| No supported CLI found | Install a supported CLI on the application host and sign in through its terminal command. |\n| Not signed in | Open the owner app using its current terminal link, then reopen the docs. |\n| Provider signed out | Sign in through that provider's CLI and retry. |\n| Usage limit reached | Choose another installed provider or wait for your plan's limit to reset. |\n| CLI took too long | Ask a smaller question, or check the CLI directly in a terminal. |\n| Documentation unavailable | Run `npm run build:docs` from the Code Flow checkout. |\n| Filesystem restrictions pause execution | Read the guides directly; review policy in the main app if execution is needed. |\n\n## Hosted documentation\n\nGitHub Pages serves the same searchable guides. It cannot launch your local CLIs. The deployed site omits the companion panel and its controls. It does not connect to localhost or request credentials. To chat, open **Help & documentation** from your running local app.\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Security](security.md)\n","title":"Ask code-flow"},{"group":"Work with agents","id":"runs","source":"docs/runs.md","body":"# Runs and workspace\n\nOpen a run from the sidebar or Inbox. Read the result and verification evidence, then use the suggested next action to answer a question, request a fix or review changes.\n\n## The run view\n\nA run reads like a conversation, top to bottom:\n\n- **The header** shows execution status, the task and workflow. **More run actions** contains Edit & run again, Archive and Delete; a running task keeps Stop run visible.\n- **Evidence and next action** separate execution, automated checks, agent review and human review. A contextual action opens the pending plan/question, reviews changes, or recovers unfinished work. A finished execution can still have failed, blocked or unverified checks.\n- **Your task**, as the message that started it, with any context you gave.\n- **The agents' work**, as one list of steps (\"4 steps · 6m 2s\"). It's open while the run is going and folds away once there's a result. Open any step to see its transcript inline: the agent's messages (rendered as Markdown), its tool calls as one-line rows that expand to the full input and output, and its thinking, collapsed. <kbd>[</kbd> and <kbd>]</kbd> move between steps.\n- **The result**, with its branches and what you can do with them (see [Review and ship](github.md#review-and-ship)).\n- **The reply box** at the bottom (below).\n\n<picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"images/transcript-dark.png\">\n <img src=\"images/transcript-light.png\" alt=\"A step opened in a run: the agent’s recorded tool calls and output (demo data)\">\n</picture>\n\n## Recovering work\n\nThe next-action card explains what happened and what is retained. **Retry unfinished work**, **Resume work**, and **Extend budget & resume** reuse recorded results with a fresh allowance of the displayed calls and minutes. **Reconnect provider** opens provider health and login instructions; resume after reconnecting.\n\n**Request a fix** prepares an editable follow-up without sending it. **Fix check command** opens a continuation dialog: choose the saved result branch and correct setup/check commands. Starting it creates a child run with the same team and permissions and a single configured check. Original history and evidence remain available.\n\nIndependent agent review applies to the reviewed commit. When a follow-up changes that result, **Review outdated** keeps the earlier findings as history. Inspect the updated changes and checks before accepting or publishing them; a passing follow-up check does not renew independent approval.\n\n## Talking to a run\n\n[Auto teams](auto-team.md) add individual member targeting and structured questions. Questions addressed to you pause the workflow and appear in **Needs you**; answering saves the reply before work resumes. The team panel exposes tasks, dependencies, messages, coverage, checkpoints, context references and coordination usage. Its project-memory panel supports revision-aware edits, acceptance and deletion. A stopped Auto team with unresolved gaps resumes its existing workflow with a fresh bounded allocation.\n\nRuns are conversations, not fire-and-forget jobs:\n\n- **Approve the plan:** tick *Let me approve the plan* (the subtasks for Supervisor, the tests for Test-first). The run pauses at that checkpoint, and its time budget with it.\n - **Approve:** the agents start building.\n - **Ask for changes:** your note goes to the author, who revises in its own session and asks again.\n - **Reject:** the run ends without building anything.\n - A pending checkpoint survives restarts. Waiting runs show under *Needs you*, with a notification.\n- **Message the team** while it runs, from the reply box. **To** picks the agent, or *Next agent* gives the note to the first one that can take it. A line next to it says where the note will go.\n - **Into a running Codex turn:** Codex runs through `codex app-server`, so a note for Codex (or for anyone) reaches its running turn right away, as a new message on the live thread (`turn/steer`). Codex takes it into account from its next step, without waiting for the turn to end.\n - **At the start of the next turn:** Claude, agy, and Codex on the `exec` fallback can't take input mid-turn. Their notes are added to the start of that agent's next turn.\n - The run view shows each note as queued or delivered, and which step it reached (\"✓ delivered to codex mid-turn (Implement plan)\"). A mid-turn note also shows in that step's transcript. If the turn ended just as the note arrived, the note goes back in the queue for the next turn. If the step is retried, the retry gets the note in its prompt.\n - **When no turn comes:** a note sent during the last turn, which couldn't take it, has no turn left to go to. When the run finishes, it's marked as not delivered and the log says so, with its text: reply to send it. It never reaches a later reply's turn as if you had just sent it. (A [single agent](#a-single-agent) takes it as your next message instead; a run that stops keeps it for its resume.)\n- **Reply** after it finishes, from the same box:\n - to an agent in its worktree (**To:** e.g. *Codex · build*): it continues its own session, then code-flow commits and re-runs the check;\n - or to *Whole team · new run*: a new run that starts from this run's result branch, with your feedback added to the task.\n\n<kbd>↵</kbd> sends and <kbd>⇧</kbd><kbd>↵</kbd> adds a line.\n\n### A single agent\n\nA **Single agent** run is one conversation with one agent, as in that agent's own app:\n\n- The run reads as a chat: your messages, and each turn the agent takes. A turn streams live while the agent works. Once it's done, it folds into the agent's answer, with **Show work** for its tool calls and thinking.\n- There's no **To**: every message goes to that agent. A message you send while it works goes into its running turn if the turn takes input (Codex). Otherwise it's sent as your next message as soon as the turn ends, so nothing waits for a reply you'd have to send again.\n- A turn that fails ends that turn, not the conversation: reply to try again or to change course. Whatever it changed is kept on its branch.\n- code-flow commits after every turn and runs your check if you set one. The branch, and merging it or opening a pull request, are at the bottom of the thread.\n\nPrompts are passed on **stdin**, not as command-line arguments (`claude -p` reading stdin, Codex's JSON-RPC or `codex exec -`, and agy's stream-json input), so long tasks and diffs aren't limited by the operating system's argument size.\n\n## Projects\n\nRuns are grouped into projects. A project has a name and the repositories its runs work in.\n\n- **No setup:** the first time you start a run in a repository that no project holds, code-flow makes a project named after it. The first time you start a code-flow that has projects, every repository that already has runs gets one, so your runs are grouped from the start.\n- **The sidebar** lists every project with its runs under it, the projects with the newest runs first. Click a project's name to open its page, or the arrow next to it to fold it. Hover it for **+** (a new run in that project) and **⋯** (rename it, change its repositories, or delete it). Projects with nothing to list fold into one *more projects* line, each with how many archived runs it has. **+** next to *Projects* makes a new one.\n- **A project's page** shows what needs you, what's running, recent results with their check and pull request state, the project's automations and inbox, and the week's numbers (runs, checks passed, cost). Its task box opens the composer in the project. The side panel shows the project meanwhile.\n- **The project button** (in the composer, in Insights and Automations, or <kbd>p</kbd>) picks the project you're in: new runs start in it, and Insights and the [Automations page](automations.md#projects) show it. With *All projects*, a new run goes in its repository's project.\n - Automations says when other projects have results or automations too, with a link to show all.\n - The read-only [phone view](automations.md#phone-view) lists every project's runs, each under its project's name.\n- **Switching projects** moves the composer to the project's repository you used last, and to the pattern and team you last started a run with there from the composer (chats in the side panel don't change it). Each project remembers its own.\n- **Several repositories:** a project can hold several, and a repository can be in several projects. Each run still works in one repository. **New project** and **Edit** in the switcher name a project and add or remove its repositories.\n- **A run's repository always belongs to its project.** Starting a run in a project, or moving one there, adds the repository if it's missing. The composer says so before you start.\n- **Moving a run:** click the project chip at the top of the run. Its branches and worktrees stay where they are.\n- **A side-panel chat** starts in the project you're in, like a run from the composer.\n- **Runs started from other runs stay in that run's project:** replies to the whole team, chats started from a run's result, comparisons, sweeps and automations. An automation whose project was deleted starts its runs in its repository's project.\n- **Deleting a project** keeps its runs; they show under *No project*. Taking a repository out of a project keeps the runs already in it.\n- Projects are saved in `~/.codeflow/projects.json`. Each run records its project in its own log.\n\n## The side panel\n\nThe panel on the right (the button at the top right, or <kbd>\\</kbd>) follows what you open, like the side pane in Claude's or Codex's app. On a run (or its review), every tab is that run's: its project, its repository and its worktrees. On a [project's page](#projects), the composer and elsewhere, it's that project and repository. The line at the top of the panel says which. The pin keeps the panel on one run or project while you open others; **Follow** lets go. **↗** opens the tab in a window of its own, for that run or project, and the panel here closes. Each run and project remembers its own tab, chats, terminals, folders and browser pages, so going back to a run brings its panel back. Drag the panel's left edge to resize it; a narrow panel names only the open tab.\n\nChat, Terminal and Browser keep what's open in a strip, like browser tabs: **+** opens another, a pill shows when one is working or has news, and **×** closes it.\n\n- **Chat.** On a run, a side chat works in the run's own worktree, as a conversation of its own with the agent you pick: messages to the run's agents never reach it, and a reply to the run never continues it. **Ask** only reads, and leaves the result and its check as they were. code-flow doesn't undo what changes in the worktree while an Ask runs, since you may be working there too; a warning names the changes instead (agy can write even in an Ask: see [caveats](security.md#caveats)). **Change** edits the worktree: code-flow commits the change on the run's branch, runs the check, proves it again and updates the result, as it does for a reply; the steps it takes (the check, the proof) are the side chat's, not in the run's timeline. A message sent while the team is still working waits for it to finish, then gets a time and step budget of its own. If the run fails or is stopped before the message's turn starts, it ends with a note saying so: resume the run, then send it again. A turn the run stopped in the middle of waits instead, and resuming the run finishes it. The run's thread stays the team's: a side chat's turns aren't in it, and a change one made gets one line there, with a link back. **☰** lists every chat, closed ones too, and **Separate chat** starts a [Single agent](#a-single-agent) run on a branch of its own, from the run's result. In a project, a chat is such a run (it has no worktree to share): it shows in the sidebar, keeps its session and its branch, and **Open** shows it as a run.\n- **Changes** (on a run) lists what it changed: the whole result branch, or just its **last step** (the latest commit, a side chat's change, say), with a diff per file, kept up to date while the run works. Click a line to comment; comments go to the agent that wrote the code, and their drafts are shared with the full-width [review](github.md#review-and-ship) (**Full review**).\n- **Files** lists the folder's files (ignored files hidden; **M** changed, **U** new), with search. A file opens in the shared editor used by Repository tools → Code, with version-checked saves, three-way conflict resolution and recoverable drafts. Active run worktrees remain protected against manual saves. Open **What could this change affect?** for static import evidence. On a run, the folder picker offers the run's worktrees and starts on its result's, so you can read what the agent wrote before merging; a project with several repositories offers each. From the composer, **+** and dragging add a file to the task's context. With the keyboard, <kbd>Enter</kbd> opens a file or folder, <kbd>→</kbd>/<kbd>←</kbd> open and close a folder, and <kbd>↓</kbd>/<kbd>↑</kbd> move through the list.\n- **Terminal** is a real terminal in a run's worktree (its result's, to start with) or the repository: your login shell in a pseudo-terminal, so prompts, colours, pagers and full-screen programs (`vim`, `htop`, `less`, `git log`) work, mouse clicks and scrolling too, and it fits the panel as you resize it. The first one opens when you show the tab. **+** opens another in any of them, and **Split** shows two, one above the other (drag the line between them). An app the run started with **Run app** can be shown as a read-only log, which stays after the app exits, marked *exited*. A shell stays open between commands, so `cd`, exported variables and virtualenvs stick. Its pill names what's running in the foreground; <kbd>Ctrl</kbd>+<kbd>C</kbd> stops the command, and **Stop** ends the shell with everything it started and opens a new one in the same folder. Copy and paste with <kbd>⌘C</kbd>/<kbd>⌘V</kbd> (<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>C</kbd>/<kbd>V</kbd> elsewhere). A terminal you come back to, or reload, shows what it showed before (the last 256 KB). Up to 12 shells stay open; a new one takes the place of the oldest idle one, never one that's running something, in the foreground or in the background (`npm run dev &`, a stopped `vim`). If all 12 are, opening another says so: stop or close one first. Closing one ends everything it started, `nohup`'d and disowned jobs too, and stopping code-flow closes every shell the same way. The pseudo-terminal needs `python3` (3.6 or later, standard library only; `CODEFLOW_PYTHON` picks another). Without it a shell is fed through a pipe: commands run one at a time and each reports its exit code, there are no pagers, prompts or full-screen programs, colours are off, and <kbd>↑</kbd>/<kbd>↓</kbd> recall commands.\n- **Browser** shows pages of its own: on a run, the app it started with **Run app** first (the result card's **Show in panel** opens it too), or any address. **Phone**, **Tablet** and **Full** set the width, and **Compare** shows two pages side by side (another worktree's app, a race contestant's). An app on this computer (an `http://` address on `localhost` or `127.0.0.1`) is shown through a preview on a port of its own, which lets you annotate it:\n - **Pick** an element, or draw a **Box** over an area: each is a numbered pin with a snapshot, and you say what's wrong. **Adjust** tries style changes (size, colour, spacing) on a picked element right in the page; what you try goes with the pin. Pins stay when the page loads again, but **Adjust** can't reach them any more: their elements went with the old page.\n - **Console** lists the page's errors, failed requests and files that didn't load; they can go with the pins.\n - **Add to main chat** puts the pins and their snapshots in the run's message box, to add a line to and send; **Send to** picks a side chat that's running or done, a new side chat (once the run has a result), or a new run's task instead. Pins from different pages each say which page they're on. Snapshots sent with a reply are saved in the worktree's `.codeflow-context/` folder, which is never committed, and named in the message.\n - The preview is a different origin from code-flow (its own port), so the app's scripts can't use your code-flow session, and code-flow's cookies are taken off every request before it reaches the app. It uses the host name you opened code-flow with, so the app's own sign-in works in the frame. It passes WebSockets through (hot reload works) and takes requests only addressed to itself. It keeps the app's Content-Security-Policy, adding only what lets code-flow's page show it in a frame and run the annotations, and leaves out X-Frame-Options. Snapshots are drawn from the page's own elements: images from other sites and some fonts don't show in them, and a password you typed shows as dots.\n - Sites that refuse to be shown inside another page open in a new tab with **↗**.\n- **Snapshot** works on any page, on this computer or not: code-flow opens the address in a headless Chrome (or Chromium, Edge or Brave; `CODEFLOW_CHROME` picks one) with a fresh, empty profile, at the width you picked (Phone 390, Tablet 768, Full the page's width in the panel), and shows the picture in place of the page. **Whole page** takes the full height (up to 12,000 pixels), zoomed out as a phone would show it when the page isn't made for phones, **Retake** takes it again, and **Live page** goes back. Drag on it to draw boxes (on a touch screen, turn on **Draw** first; otherwise a finger scrolls): each is a numbered pin with its crop, sent like any other, and the whole snapshot with the boxes numbered on it goes along. A new snapshot asks before it drops boxes drawn on the old one, and going to another address goes back to the live page. It's what a signed-out visitor sees: no cookies or sign-in from your own browser, and anything that needs a click or typing first won't be in it. A snapshot takes a second or two and one Chrome while it lasts; one is taken at a time, an `alert()` on the page is answered OK, and its Chrome and profile are gone when it's done.\n- **Notes** is a scratchpad per repository (the run's, or the project's) plus a general one, saved as you type in `~/.codeflow/notes.json`, so it's the same on your phone. **→ Task** adds the selection (or the whole note) to the task in the composer; **→ Chat** sends it to a new chat.\n\n## Runs survive restarts\n\nEvery run is saved to disk as it happens (`~/.codeflow/runs/<run>.jsonl`), and the server rebuilds the run list from those files when it starts.\n\n- **Interrupted runs:** if code-flow stops while a run is going (Ctrl-C, a restart, a crash), the run comes back as **Interrupted**.\n - **Resume** re-runs the pattern from the top. Each step that already finished (agent turns, checks, commits, worktrees) is replayed instantly from a journal (`<run>.journal.jsonl`), and real work continues at the first unfinished step. Nothing finished is redone.\n - Runs that hit a time or step limit, failed, or were stopped by you can be resumed the same way, with a fresh budget.\n - A recorded check is replayed only if the code it checked is still the code at that point. A recorded commit is replayed only if it's still in the branch's history. Anything else runs again.\n - While it replays, code-flow reads what changed at the commit the replay has reached, not at the newest one already in the worktree. So the pattern decides as it did the first time: Test-first doesn't take the builder's later rounds for the tester's changes and undo them.\n - A race contestant or worker whose CLI wasn't signed in, or stalled, isn't recorded as failed: the resume gives it its turn. One that ran out of plan usage stopped the run, so its turn comes after the reset.\n - Follow-ups on a finished run don't replay the pattern at all; only the follow-ups run, each with a full time and step budget, and the run's check result stands until one of them runs the check again. A follow-up that fails doesn't undo the finished run.\n - Resume is claimed at once, so a double click or a PR watch can't start the same run twice. If the last attempt is still wrapping up (listing its work), the new one waits for it. Stop pressed meanwhile keeps it from starting: the run stays as it was. Archiving, merging, pushing and opening a pull request are refused while the run works (a follow-up or a side chat on it too, from any tab or your phone) and until a resume has started.\n - Notes delivered to a turn that never finished, or failed, go back in the queue for the next turn (the retry, or the resumed run).\n- **Sessions that are gone:** an agent continues its own conversation only from a turn that worked. If that conversation no longer exists (the CLI says so), the turn runs once more in a new conversation, given the task from the start. A conversation that couldn't be resumed is never recorded as the step's.\n- **At startup:** a step still marked as running in a run that isn't (code-flow stopped or crashed while it ran) is closed as interrupted. Folders in a worktree root that no run owns are listed in `~/.codeflow/server.log`; code-flow never deletes them. A run log without a run in it is logged there too, rather than skipped silently.\n- **Clean shutdown:** Ctrl-C or SIGTERM stops running agents and marks their runs interrupted. A second Ctrl-C exits at once.\n- **One server per data folder:** a second server on the same `CODEFLOW_HOME` waits for the first to stop (as in a restart), then refuses to start. A crash mid-write can leave a line half-written; it's closed off before the next write, so it never corrupts the next line. Failed writes, such as a full disk, are counted and reported at `/api/health`.\n- **Needs you:** finished runs you haven't opened, and interrupted runs, are listed at the top of the sidebar, whatever their project, instead of under it. The browser tab shows the count, and the bell at the bottom of the sidebar turns on desktop notifications for when a run finishes or needs you.\n\n## Insights, comparisons and finding runs\n\n- **Insights** (in the sidebar, or <kbd>i</kbd>) shows, per agent and per pattern, for the project you're in, one repo, or everything, over 7 days, 30 days or all time:\n - runs, how often the result passed its check, and how often it was proven (a test fails without the change; a weak proof, where the tests only fail to load without it, doesn't count);\n - race wins, loops each agent got green, and review findings other reviewers agreed with;\n - failed steps, tokens (input, output, cached) and time per step;\n - Claude's API-equivalent cost. Your plan isn't billed per token; it's a measure of size.\n- **Insights → Experiments → Compare setups…** starts the same task with several patterns at once, each with the team you last picked for it, optionally 2–3 times each. Insights shows them side by side: result, time, steps, tokens, cost, whether tests were touched, and whether the result was proven. The best value in each column is highlighted.\n- **Tokens and cost** also show on each step, in the run's details under the header (with a per-agent breakdown on hover) and when you hover a run in the sidebar.\n- **Disk:** every worktree is a full checkout, often with its own `node_modules`. Insights measures them and **Clean up** removes the worktrees, not the branches, of runs that finished more than *N* days ago and need nothing from you.\n - **Archive…** with its worktrees removed also clears the rest of the run's folder in `.worktrees/`: scratch checkouts an interrupted attempt left (where proofs and reviewers' tests ran), and, when the branches go too, the context staged for its agents (kept otherwise: a resume or a reply copies it into the worktree it checks out again). The folder goes once it's empty. The pull request's head code-flow fetched for a run started from one (`codeflow-pr/<n>`) is deleted with the last run still using it, unless it's checked out somewhere; it can be fetched again.\n- **Finding runs:** the sidebar lists every [project](#projects)'s runs, one line each: a status dot, the task and its age (hover for the rest). It has search (<kbd>/</kbd>) and, behind the button next to it, grouping by project or by status, filters (needs you, running, passed, failing, tests changed; pattern; agent; this repo) and **show archived runs** (also the archive button at the bottom of the sidebar). While you search or filter, matching archived runs show too. <kbd>j</kbd>/<kbd>k</kbd> move between runs. In a run, <kbd>[</kbd>/<kbd>]</kbd> move between steps. <kbd>\\</kbd> shows or hides the side panel. <kbd>?</kbd> lists every shortcut. Run status changes are announced to screen readers.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Runs and workspace"},{"group":"Work with agents","id":"auto-team","source":"docs/auto-team.md","body":"# Team\n\nStart with an Agent conversation and enable **Team** whenever the task needs collaboration. Choose a template and providers in Team setup. You can turn Team off and continue with the same agent, task, workspace and conversation.\n\n## Review the proposed team\n\nOpen **Team**, enable it, and select the template, coordinator and providers code-flow may use. A coordinator inspects the repository and proposes individual members, tasks, dependencies, file ownership, and acceptance criteria. You can approve, revise, or reject that plan before workers start. Planning itself uses a CLI invocation. Approval defaults on; an explicitly saved preference or automation policy still applies.\n\nTeam is available unless disabled with `CODEFLOW_AUTO_TEAM_ENABLED=false`. Existing saved workflow and project choices are preserved. Simple conversational questions are answered by the lead without starting another team; explicit team requests and substantial work use the configured workflow. Team progress stays collapsed under its chat message. Corrections sent while a team works stop the current execution and enter a revised request, with a visible acknowledgement and preserved partial work. Existing manual patterns are available under Advanced as built-in workflow templates. It uses the same official CLI adapters, worktrees, checks, permissions, review/shipping actions, and subscription logins as other runs.\n\n## Reusable templates in Advanced\n\nOpen **Advanced** to choose a built-in workflow, design a task graph, import/export definitions, or publish a new immutable template version. Templates can be personal or scoped to a project. Use **Import browser library** to migrate existing presets without deleting the browser originals; role/model preferences and workflow options survive, while permission grants, credentials, host bindings, and budgets do not.\n\nAuto generates plans without needing a template. It may also reuse enabled task graphs, assign their roles, combine instances, and append task-specific instructions. The runtime preserves dependencies, acceptance criteria, and shared contracts, then applies normal plan validation and independent verification. Up to six enabled definitions of 16,000 characters each are offered per run, in ID order. Fixed workflows such as test-first, races, and councils keep their existing executors in Advanced; they are not advertised as composable Auto task graphs.\n\nA manually selected template is mandatory. Its exact version and inputs are pinned to the run and survive cancellation/recovery; editing the library does not change an active run. The run menu's **Save as team template…** opens an editable preview. Review task-specific instructions and replace them with parameters before publishing. Saving does not enable Auto reuse automatically.\n\n## Run permissions across providers\n\nAll new runs use one versioned permission policy, including background jobs and collaborative launches. Every planning, worker, review, repair, retry, and follow-up launch resolves that policy against current restrictions and the selected provider. Read-only planning does not reduce implementation workers to read-only access. Templates and agent messages cannot grant permissions; only an explicit user change creates a new policy revision. Child runs inherit the parent’s current trust ceiling, approval preference, mandatory restrictions, and container or remote execution environment. A child cannot increase its parent’s permissions or remove its execution boundary. Historical saved runs without a policy retain their legacy behavior.\n\nClaude and Codex receive their native permission settings, with provider-native delegation disabled. Matching trust labels do not imply identical isolation: Codex uses its provider sandbox, while Claude's local tool rules are not an operating-system filesystem boundary. Advanced can require an agent workspace write boundary, denied agent-command network access, or disabled external tools. Unsupported combinations fail before launch or replacement dispatch. The run view records the effective settings for each turn. By default, Antigravity uses the local macOS isolated executor with supported CLI 1.4.4 or 1.4.5 and an existing native Keychain login. Interactive Agent and Team turns receive task-scoped Codeflow browser and computer tools when external tools are permitted; computer use requires Full access. It uses protected fresh settings, has no native terminal/browser/delegation or ambient MCP servers, and never resumes native sessions. Mandatory external-tool restrictions retain its file-only profile. Shared-tool live validation covers 1.4.5. Advanced also offers explicit unrestricted native Antigravity on the local host; see below. Remote/container Antigravity, ACP and extension adapters remain blocked. Claude/Codex controls are probed again at each launch, in the selected container when applicable. Unknown or missing controls stop execution.\n\nRemote workers receive and validate the complete policy, including all mandatory requirements; remote/container profiles that cannot prove a requested boundary are rejected. Required interactive approvals need the local Codex app-server and an approval handler; they cannot silently fall back to exec or combine with mandatory sandbox boundaries that approval could relax.\n\nClaude’s external-tool restriction supplies a strict MCP configuration, disables Chrome integration, and denies WebSearch/WebFetch; Code Flow’s scoped team bridge remains available. Codex managed turns override ambient approval settings, additional writable roots, and command-network settings. A mandatory workspace write boundary also removes temporary-directory write exemptions. Native-delegation controls disable provider agent tools; they are not process-level isolation from programs invoked through an authorized shell.\n\nThese requirements describe agent execution. Browser/MCP authorization is separate from terminal network access. Setup commands, checks, and previews use their separately configured execution environment. Worktrees do not restrict file reads. The built-in shared browser/computer bridge applies the same task-scoped tool checks across Claude, Codex and Antigravity. Arbitrary provider-configured integrations and filesystem reads do not share a cross-provider sandbox.\n\n## How work proceeds\n\n1. The runtime validates the proposed plan against installed providers, quota observations, configured model/effort pins, member/task limits, dependency cycles, and review independence.\n2. New runs start ready tasks as earlier tasks finish, without waiting for unrelated workers. Saved wave-based runs retain their recorded scheduling behavior. Each member has its own identity and session, even when several members use the same provider. Tasks with overlapping file ownership run separately.\n3. A worker returns a structured outcome: submitted work, a failure, or a question with a progress checkpoint. Submitted changes are integrated serially and checked before dependent work starts.\n4. When a decision is needed, the coordinator can add bounded work or workers, reassign unfinished work, retire idle workers, and add shared contracts. It cannot remove approved requirements or raise its own limits.\n5. An independent read-only member reviews the integrated result. Blocking findings can trigger bounded repairs, followed by another check and review of the current commit.\n\nAn implementation is verified only when required work is present, the configured check passes, independent review has no blocking findings, and fail-to-pass proof succeeds when enabled. Unresolved tasks, blocking review findings and failing checks are gaps that stop the team. A repository without a runnable check cannot be blocked on it: the result finishes as reviewed but unverified, with no pass/fail verdict, and nothing from it is accepted into team or project memory. An investigation can finish with evidence-bearing artifacts and independent review without a code check. The run does not merge or publish its result automatically.\n\n## Communication and blockers\n\nMembers send addressed questions and replies through their structured outcomes. The runtime attaches the sender, task, request ID, and origin; an agent message never becomes user authorization. A worker needing information ends its turn, saving its partial work and checkpoint. Its CLI slot is then available for other work.\n\nThe next scheduling boundary delivers an existing answer or runs a short read-only response turn for the addressed member. A question addressed to you appears in the run view and in **Needs you**. Your answer is saved before the waiting workflow is released. The run clock pauses while waiting for you. An unanswered peer request or failed task can prompt a bounded coordinator revision; exhausted limits produce an explicit blocker.\n\nYou can also send a note to an individual member. Codex app-server can receive user notes during a turn; other transports receive them on the next turn. Native permission requests remain denied under the existing adapter policy. Auto members use the structured question protocol for missing information.\n\nClaude and Codex members also receive scoped collaboration tools when their installed CLI supports the configuration. They can send messages, ask/reply, read their inbox, publish findings, save notes, or propose project memory during a turn. Calls acknowledge durable acceptance promptly; the scheduler applies commands at recorded boundaries. A blocked member still ends its turn to release the process slot. Each turn may make at most 256 collaboration tool calls (messages, questions, memory reads and writes together); after that, calls fail and the member finishes with its structured outcome. New controlled runs support Claude, Codex and isolated Antigravity. Explicit unrestricted Antigravity is an exception to the native-delegation restriction; other adapters remain rejected. Older saved runs retain their recorded execution behavior.\n\nQuestion and wait state are one logical journal transaction. Duplicate operation IDs do not duplicate messages. Expired deadlines, unavailable recipients and cyclic waits route to the coordinator within the existing budget. Reassigned or retired attempts cannot overwrite current work. Knowledge dependencies can use evidence-bearing replies; artifact dependencies require integrated code, and completion dependencies require the upstream task to finish.\n\n## Three memory scopes\n\n| Scope | Contents | Who receives it |\n|---|---|---|\n| Private | Observable progress, attempts, unresolved questions, and a portable checkpoint | That member; a replacement receives a separately authored shared handover and artifacts |\n| Team | Accepted contracts, task outcomes, questions, findings, and artifact references | Members whose assignment needs the information; reviewers receive independent review context |\n| Project | Accepted conventions and reusable facts with source, revision, and applicability | Future matching runs in the same project and repository |\n\nPrivate scope controls context assembly and memory APIs; it is not filesystem secrecy from a fully trusted process on the same machine. Checkpoints describe observable work, not hidden model reasoning.\n\nThe team view shows members, dependencies, messages, requirement coverage, checkpoints, and each worker turn's context references. The project-memory panel shows included and omitted entries with reasons, provenance, and branch applicability. You can create, edit, accept, delete, or rate a memory as useful, irrelevant, stale or incorrect. Saving an edit never accepts a proposal: a proposal stays a proposal until you accept it, and an accepted entry you edit is recorded as accepted by you. Edits require the displayed revision, so concurrent changes cannot silently overwrite one another.\n\nStructured worker findings become project **proposals** after result verification. Live tools can save an earlier proposal, explicitly unverified and requiring user review. They remain tied to the result branch and commit. Accepting a branch-specific proposal does not make it a convention for every branch; changing it to **Use across this repository** is an explicit user decision. Repository membership, branch/commit ancestry, expiry, and source revisions are checked when selecting memory. Retrieval uses bounded text/path/tag matching, without embeddings. User feedback adjusts ranking only for that revision; stale or incorrect revisions are excluded. Feedback never raises trust or authorizes promotion.\n\nLocked-down, untrusted, or unknown-origin material cannot automatically become accepted project knowledge, including through a derived summary. The user must approve the exact revision. Auto teams do not consume historical recurring lessons whose provenance is missing; explicit repository conventions and accepted project memory are separate inputs. Automatic learning is also disabled for untrusted sources.\n\nDeleting a memory erases its stored body, removes it from retrieval, and retains deletion metadata. Entries that list it in their provenance (it was in a member's context when they were written) keep their own text and the origins they inherited from it, so a running or resumed team that still cites it keeps working. Private checkpoints and team records are retired when their run is archived with cleanup; project knowledge is never removed that way. A run may hold at most 512 private and team records. Source run transcripts, task checkpoints in run history, provider conversations, and user backups have separate retention. Deleting memory does not erase those source histories or a prompt already supplied to a running CLI.\n\n## Budgets, restart, and resume\n\nThe run's maximum steps is also Auto team's actual CLI invocation ceiling; an explicit smaller `maxInvocations` lowers that ceiling for planning and scheduling too. Provider retries and schema repairs count. Up to one quarter of the invocation allowance may be coordinator calls, and the runtime reserves a turn for independent review when admitting work. New runs default to 200 calls over 240 minutes, allowing up to 50 coordinator calls. At least four invocations are required for a new team. These are bounds on authorized invocations, not a claim that coordination will always be 25% of actual usage.\n\nWhen a run stops, edit **Maximum calls** and **Maximum minutes** before resuming, or choose **Use extended budget** for 200 calls and 240 minutes. Resume grants a fresh allocation, reuses completed results and worktrees, and retains lifetime usage. Saved runs keep their existing limits until you explicitly change them.\n\nThe team view reports lifetime invocations, coordination share, questions, blocked turns, and revisions. Provider-reported tokens and API-equivalent cost remain unknown where the provider does not report them; cost is not a subscription bill.\n\nLaunch reservations are recorded before starting a CLI. After a crash in the interval between that write and process launch, lifetime counts conservatively retain the reservation; the runtime cannot prove whether an external process started.\n\nAccepted decisions and outcomes use the existing fsynced replay journal. A failed authoritative write stops dependent dispatch. Recovery ignores an incomplete final journal record, removes that incomplete tail before the next append, and rejects malformed complete records. Event logs remain best-effort projections. SSE reconnects across server restarts reconcile an authoritative snapshot before continuing the stream.\n\nA blocked Auto run is **stopped**, with its work available and its workflow unfinished. **Resume** replays completed allocations, including their final review and repair steps, then grants a fresh bounded allocation. Interrupted allocations continue their recorded workflow. Lifetime usage remains visible across resumes; replayed calls do not spend again. A missing provider session falls back to a fresh session with explicit task context. As with other patterns, a CLI can perform work immediately before a crash, so recovery does not promise exactly-once external execution.\n\nDuring a run, **Revise goal** stops the old execution, retains its branches and supplied context, then opens a linked team plan for your changed requirements. It uses a new explicit budget and the configured approval checkpoint. Ordinary member notes leave the accepted requirement set intact. Source trust and lockdown follow both forms of continuation.\n\nAfter completion, **Whole team · new run** plans and verifies your follow-up against the result branch. The reply box exposes its new call/time allocation; direct follow-ups can use a one-call budget and also enforce an explicit launch ceiling including retries. A provider-addressed follow-up starts outside the team's private member sessions, then can continue its own conversation. A direct agent follow-up that changes the integrated result clears its previous team approval, even if its check passes. The run displays that it needs whole-team verification again.\n\n## Adapter capabilities\n\n| Capability in code-flow | Claude Code | Codex | Antigravity |\n|---|---|---|---|\n| Structured outcomes and session continuation | Supported | Supported | Structured outcomes; fresh turns with portable context |\n| Addressed questions at turn boundaries | Supported | Supported | Supported |\n| User notes during a running turn | Next turn | App-server steering; next-turn fallback with exec | Next turn |\n| Native permission requests | Existing allowlist policy | Declined | Protected file-only policy; no interactive approvals |\n| Agent-accessible collaboration tools | Scoped per-turn MCP and allowlist; `--strict-mcp-config`, so your own and the repository's MCP servers are not loaded in these turns | Scoped per-turn MCP, app-server and exec | Structured-outcome fallback |\n\nThe capabilities panel distinguishes tested, unsupported and unknown behavior using adapter protocol fixtures and installed CLI probes. Inventory probe results are kept for ten minutes; an unknown result (a CLI missing or not signed in, or a probe that timed out) is kept for two, while managed launch controls are checked afresh before each turn. Model behavior is measured separately. Native Claude and Codex agent-spawning tools are disabled for scoped team turns so code-flow retains ownership of the roster. Default Antigravity uses a protected file-tool allowlist and macOS process/filesystem restrictions; native delegation is excluded. The explicit unrestricted option does not enforce these restrictions. Its built-in local task bookkeeping tool can remain visible. Separate vendor-native token accounting remains unknown.\n\nThe live bridge binds a random loopback port and issues an expiring credential for one active member attempt. Credentials are stored in temporary files with mode 0600 and removed when the turn exits; they are absent from prompts, command-line arguments, run records and logs. The path of the credential file is on the command line, and every CLI on this machine runs as your user: a member that can run shell commands could read another member's credential while that turn is starting. The bridge client deletes the file as soon as it has read it, which narrows the window to process start; this is isolation between members and the application's own token, not between members. The engine derives identity and provenance. A read-only reviewer can communicate without receiving source-write permissions; sandbox/network restrictions are not relaxed to enable collaboration.\n\n## Implementation and evaluation\n\nThe implementation uses `src/teams.ts`, `src/patterns/auto-team-model.ts`, and `src/patterns/auto-team.ts`, with engine metadata and replay helpers. `src/memory.ts` applies versioned memory rules over `src/memory-sqlite.ts`; `src/team-memory.ts` applies run provenance and Git/project applicability. Structured memory uses SQLite with automatic JSON migration and evidence-linked graph retrieval. Node 22.13 or newer is required; no native npm addon is needed. See [Memory storage](memory-storage.md).\n\nTests use fake vendor CLIs and isolated repositories. They exercise plan validation/approval, same-provider identities, dependency waves, peer and user questions, restart/resume, member replacement, independent verification, storage failures, memory trust/revision/deletion, and the browser workflow. They demonstrate behavior, not model quality or subscription savings. `npm run evaluate:teams -- --fake` runs an isolated comparison without model calls. Use `--real --runs 1 --providers claude,codex --max-steps 12 --max-minutes 5 --out /absolute/output` for a bounded comparison through signed-in CLIs. Reports record mode, CLI versions, actual calls, coverage, coordinator overhead, peer waiting and journal-load size/time. Estimated avoidable barrier delay is labeled separately from observed waiting.\n\nInsights shows evaluations and the recorded rollout decision. Fake or unknown runs cannot justify beta/default promotion. Promotion beyond the experimental stage requires the documented evidence thresholds; default rollout requires a larger representative sample. See [implementation evidence](dynamic-teams-evaluation.md).\n\nSet `CODEFLOW_AUTO_TEAM_ENABLED=false` to disable creation of new Auto runs. Existing teams remain inspectable and resumable; manual patterns and saved automations retain their compatibility. The feature does not require a database or new Node baseline.\n\nThe evaluation runner also accepts `--register-home /absolute/codeflow-home` to copy immutable reports into a chosen installation for Insights. This is explicit: it does not copy transcripts or change rollout settings. Reports are also available in the isolated evaluation home's `team-evaluations/` folder.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n\n## Automatic worker models\n\nNew Auto runs discover candidates through the connected CLI: Claude's stream-control initialization, Codex app-server `model/list`, and Antigravity `agy models`. The coordinator keeps the provider, model and effort selected by the user, including existing CLI defaults. Workers and reviewers use automatic selection unless their Advanced pool/model/effort settings pin or restrict them. Global coordinator defaults do not silently pin workers.\n\nAdvanced → Automatic worker selection shows cached model IDs, exact effort support, stale/unknown states and refresh. Official-document research is opt-in. Local catalog previews do not establish remote/container availability: those environments discover their own models when a run starts. Antigravity worker discovery runs inside the same macOS executor with a separate catalog identity. Its model listings do not establish per-model inference or effort support. The coordinator sees the effective file and task-tool capabilities and rejects unsupported execution environments. Every launch rechecks the CLI version and creates a clean protected profile. See [permission verification](worker-permission-verification.md#existing-login-reuse-and-managed-file-workers).\n\nCatalogs live under `CODEFLOW_HOME/model-catalogs`, partitioned by provider and execution environment. Discovery has a ten-minute cache; failed refreshes retain previous evidence. `CODEFLOW_MODEL_NETWORK=offline` disables discovery and documentation fetches and uses cached entries. This switch does not disable provider inference or the run's separately configured tools. Documentation research uses official origins, bounded downloads and a closed schema, with a seven-day attempt cache. Web text cannot grant permissions or prove account access. Unknown context, pricing, capabilities and effort support remain unknown; an explicit empty effort list means no effort control.\n\nCLI-reported availability, advertised compatibility, documented facts and measured run observations are distinct. A listed model is a candidate, not a guarantee that a paid inference call will succeed. Exact accepted worker choices, rationale, environment and catalog revision/source are saved with the step; discovery revisions have immutable snapshots. Recovery reuses recorded choices and rechecks eligibility before another execution. Failed members can be replaced within existing revision/invocation limits using portable checkpoints; native sessions stay with their provider/member.\n\nThe first version uses existing routing and budget checks, not a learned performance ranker. Costs and token limits retain their existing provider-reporting limitations; unknown pricing cannot supply an exact prospective cost guarantee. Run observations are diagnostic evidence, not benchmark scores or authority to enable a model. Public documentation only augments CLI-reported IDs; it does not make a public-only model eligible.\n\nSee [the live permission verification report](worker-permission-verification.md) for tested versions, native-control fixes and remaining boundaries.\n\nIf an isolated Antigravity worker cannot use or refresh its native login, check or unlock the login in Antigravity's normal interactive session and select **Resume**. Code Flow verifies it again through isolated model discovery before clearing the signed-out cooldown. Cached models or offline discovery cannot establish login access. Recovery keeps the recorded assignment and portable work, then starts a fresh restricted worker; it never imports a native conversation or widens permissions to refresh credentials. Native authentication diagnostics are withheld from run history.\n\n### Unrestricted Antigravity with an existing login\n\nEnable **Advanced → Antigravity execution → Use unrestricted Antigravity with my existing login** for a new local attended run. This explicitly grants Antigravity full native access using the existing login and provider configuration. Shared Codeflow tools are added through a private temporary configuration overlay. It skips the isolated profile and vendor sandbox and uses the CLI's permission-skip flag. It requires a valid saved login; it does not provide anonymous access or guarantee that the provider will never require reauthentication.\n\nTerminal, browser, MCP, hooks and native agents follow the native provider configuration. Code Flow cannot enforce workspace boundaries or read-only access in this mode. Native agent usage is outside Code Flow's worker budget. The selection is visible in Advanced, run details, coordinator inventory and each step's permission receipt (`agy-native-v1`). Claude and Codex keep the ordinary run policy.\n\nThe option is rejected with mandatory permission requirements, Safe mode, narrowed turn trust, interactive tool approvals, remote/container workers or unattended/locked-down execution. Templates and model choices cannot enable it. A continuation inherits the parent's choice and may narrow it to isolated execution, but cannot upgrade an isolated parent. Existing saved runs stay isolated; there is no automatic fallback on authentication or sandbox errors.\n\nNative model discovery uses the existing profile, with a catalog identity distinct from isolated discovery. Both modes keep portable checkpoints and start fresh managed turns without native session reuse. Recovery after an authentication failure requires a fresh successful discovery in the recorded mode; cached/offline results cannot clear the failure. Private authentication diagnostics are checked for observed persistence failures and deleted without entering run history. Actual expired-token refresh remains unverified unless observed during normal provider operation.\n\n### Steering and recovery in a conversation\n\nKeep using the main composer while Team works. Name a worker, assignment, or owned file when correcting a specific part—for example, “For api.txt, use the revised wording.” Code Flow stops that assignment and invalidates dependent work while unrelated teammates continue. Its acknowledgement means the revised instructions reached the assignment, not that the change has passed review. Ambiguous corrections affect the whole graph. Corrections during planning or final verification safely restart the delegated execution with its preserved context and remaining budget.\n\nThe Browser panel has **Take control** and **Let agents continue**. Taking control waits for the current action to settle, then pauses queued agent browser calls. You and the agents keep the same tabs, cookies, uploads and downloads. Ordinary mouse/keyboard input briefly takes control and releases it afterward. Agents can release their browser lease through `browser_control`; idle leases expire after 30 seconds, and queued calls have a bounded 90-second wait. A new owner must inspect the current page before acting.\n\nTeam coordination, workers and review can continue with another eligible provider from the configured pool after a provider failure. The replacement receives the current assignment, files and recorded tool-action history in a fresh provider session. Model pins, tool permissions, independence requirements and the remaining budget still apply. An unavailable strict independent reviewer blocks completion when no eligible replacement exists. The advanced Auto workflow option `providerFailover: false` retains manual provider recovery. Provider changes are recorded under Team activity.\n\nIf your edits overlap a team result, open **Team activity → Resolve conflicts**. Compare the starting version, your version and the team version; keep either version or edit a text result. Non-overlapping edits merge automatically. Code Flow rejects stale comparisons and preserves your branch and staging area. Applying a resolution runs the configured checks, including verification-profile gates, against the combined workspace. This check does not replace independent review of newly edited text. Binary and symbolic-link conflicts require choosing an existing version; complex repository conflicts can still be resolved in an editor.\n","title":"Team"},{"group":"Work with agents","id":"project-teams","source":"docs/project-teams.md","body":"# Project missions (preview)\n\nEnable the project-mission preview, create a mission, and review its first proposal before accepting an implementation cycle. Keep budgets and publication decisions explicit.\n\n## Start with a bounded preview\n\nA project assignment connects an agent team and owner mission to one repository. Give it tasks directly or enable recurring discovery. Existing Automations supplies the clock, and existing Auto team runs do the investigation, implementation, checks and independent review. The team brings opportunities and verified drafts back to the owner. It never merges or publishes automatically.\n\n## Reusable agent teams and project assignments\n\nOpen **Project → Project missions → Reusable agent teams** and choose **Create agent team**. Give it a name, choose its coordinator and worker/reviewer providers, and set model preferences. Optional role settings let the coordinator and workers use different models or effort for the same provider. Saving creates a versioned definition without starting agents or granting access to a repository.\n\nChoose **Assign to this project**, select a repository and configure the mission, base branch, allowed paths, checks and budgets. Reusable-team assignments initially have **Run recurring discovery** turned off. Save the draft and activate it when ready. The same reusable team can be assigned to other projects with separate authority, memory and allowances. One non-archived assignment owns each underlying Git repository, including all its linked worktrees.\n\nExisting missions are already assignments. **Edit mission → Agent team** can attach a reusable definition to one without replacing its ID, cycles, decisions, results, launch receipts or budget ledger. **Save as reusable team** copies only provider/model preferences from an existing assignment; it does not copy mission instructions, private checkpoints, sessions, project memory or permissions.\n\nEditing a reusable team creates a new version. Assignments keep their pinned configuration until you choose a version in **Edit mission** or **Use team version …**. Each cycle and child run records its own snapshot; updates never silently change other assignments or running work. Adopting changed execution settings requires fresh verification for existing ready/accepted results and retains all prior history and charged usage.\n\n## Give an assigned team a task\n\nActivate the assignment, select **Give team a task**, and enter a title, instructions, acceptance criteria and an allowed category. A required verification check must be configured. The task starts an implementation Auto team immediately, skipping discovery. It uses the assignment's existing scope, permissions, implementation allocation and rolling weekly allowance. It works even with recurring discovery disabled, or when discovery is set to proposals only.\n\nTasks appear alongside opportunities with **Owner-assigned task** attribution. They do not claim independently discovered code evidence. Checks, independent review, exact-commit approval and publication controls are the same as for discovered opportunities. A task never resets usage or inherits a previous run's sessions. Finish or stop the active cycle before assigning another task. The task form retains the exact request after an ambiguous connection failure, so retrying cannot admit the same task twice.\n\nWorkers submit completed artifacts; Code Flow commits and integrates them, runs the configured checks and obtains independent review. Pending engine gates belong in the worker handover, not in unresolved implementation requirements. The final check can reuse passing evidence only for the same unchanged commit, checkout and verification profile. Plain documentation changes are identified from the actual diff: checks and independent review remain required, while executable fail-to-pass proof is marked not applicable. Code, configuration, executable files, binary changes and mixed diffs retain the proof requirement.\n\n## Create a mission\n\n1. Open **Project → More → Automation → Pursue a mission** and click **Enable project missions**. The choice applies immediately to every project on this host and is saved across restarts; no startup flag is required. Auto team must also be permitted by the host (`CODEFLOW_AUTO_TEAM_ENABLED` defaults to enabled).\n2. Open a project that explicitly contains the repository. Select **Create a mission**.\n3. Enter its mission, intended users, priorities, exclusions and named local base branch. Select a coordinator and worker/reviewer providers. Claude, Codex and Antigravity are eligible when installed; ACP and extension providers are currently excluded from unattended project missions.\n4. Choose **Build within scope** and supply a required check command, or choose proposals only. Set allowed paths, categories and change limits. Models and reasoning use the existing provider catalog.\n5. Review the schedule and budgets, save the draft, then **Activate**. Use **Preview next cycle** before **Run cycle now**.\n\n**Disable project missions** prevents new cycles on this host. Stop active or unresolved cycles first. Individual teams keep their activation state; enabling the feature again resumes schedules for teams already active. `CODEFLOW_PROJECT_TEAMS_ENABLED` remains an optional initial default until a UI choice is saved in `CODEFLOW_HOME/project-teams/settings.json`. Separate local and SSH hosts keep their own settings.\n\nThe default schedule is Monday at 09:00 in the browser's time zone. Daily, selected weekdays and intervals use the existing Automations schedule editor and DST behavior. Code Flow must be running for its local scheduler to work. Cycles pin the current local branch commit; they do not fetch remote changes.\n\nThe default scope permits the repository except `.codeflow` and `.github/workflows`, with eight changed files and 400 added/deleted lines. Credential paths and binary changes are excluded. File bounds enforce mechanical scope; a model's risk and benefit estimates remain judgments to review.\n\nDiscovery reserves eight calls and 15 minutes. Implementation reserves 16 calls and 45 minutes. The rolling seven-day allowance is 48 calls and 120 minutes. Each launch reserves its full limit before starting. An exact, durable terminal-run receipt replaces that reservation with observed calls and elapsed time rounded up to whole minutes, including cancelled, failed and stopped runs. Original allocation fields and historical mission budgets remain saved. Unknown or interrupted launches remain fully reserved even after seven days; missing evidence never becomes zero usage. Legacy runs can gain additive usage receipts without resetting their history. Increase budgets deliberately in the mission editor when the defaults do not fit your cadence.\n\n## What a cycle does\n\nDiscovery uses read-only Auto team workers, a separate structured report turn, then an independent reviewer. It returns at most three opportunities with source quotes, affected users, a proposed change, expected benefit, risk and acceptance criteria. Empty findings are a valid result. Discovery does not run setup, checks, proof writers or code repairs.\n\nThe host verifies quotes at the pinned Git commit, suppresses previously reported problems, and ranks eligible proposals deterministically. Automatic implementation requires allowed category/paths, low risk, sufficient confidence/alignment, and a required verification profile. Hypotheses require the explicit experiment category. A cycle can build only one proposal; reports with uncertain evidence remain proposals.\n\nImplementation uses a fresh ordinary Auto team within its reserved budget. File, path and line limits are checked before commits and publication. A result becomes **ready** only when required checks and the independent review refer to the final branch commit. Existing fail-to-pass proof settings still apply. Failures and stale results remain visible with a reason.\n\nThere is one active cycle per repository, at most two unresolved implementation results, and at most 12 open opportunities. Unchanged inputs skip agent work until the rescan interval (28 days by default). Relevant owner decisions, local activity, configuration or base changes permit new discovery. An unchanged failed automatic cycle needs an explicit owner retry or changed inputs.\n\n## Review and feedback\n\nProject → Automation → Pursue a mission contains the full opportunity cards, cycle history, selection reasons, budgets and links to existing run diffs/checks. Project Overview and the global Inbox contain compact links to actionable mission opportunities. Their approval and evidence requirements stay the same.\n\n- **Build proposal** allocates a bounded whole Auto team. High-risk, unsupported or stale work requires a revised mission or an ordinary owner-directed task.\n- **Request revision** records feedback and launches a fresh bounded whole team from the pinned base, with the previous result referenced as evidence. It does not silently continue a single writer or refresh the old run's allowance.\n- **Approve this commit** accepts the exact reviewed result. Open that run to use the existing PR or merge controls. Approval alone does not ship anything. A successful local merge is recorded as shipped; opening a PR is not counted as shipped.\n- **Defer one week** retains the idea and feedback; once due it may be rediscovered. **Reject** suppresses the problem in later discovery. Owners may explicitly reconsider a saved proposal.\n- **Pause** prevents new cycles and the next implementation stage. An already running discovery may finish into proposals. **Stop cycle** cancels owned work, retaining unresolved reservations. **Archive team** stops scheduling and retains its history.\n\nChanging execution policy makes previously ready/accepted results require new verification. Changing only the schedule, recurring-discovery toggle or assignment name preserves those results and can be done during a cycle. The current schedule is stored separately; historical mission and cycle snapshots retain their original timing. Full-mission schedule updates from older clients receive the same behavior. Moving a result branch after review invalidates readiness. Generic run resumes, selective reruns, writer follow-ups, automatic PR-fix watchers and triage revision shortcuts are blocked for managed runs; use the project mission's revision controls.\n\nProject mission runs can be archived individually or in a sidebar selection. They cannot be deleted: cycle recovery, budgets and exact-result approvals retain their run history. Ordinary runs still support individual and bulk deletion. The mission editor's searchable model picker includes the same free-model filter and badges as the composer; its selections update only that team's mission.\n\nExisting notification destinations receive actionable cycle outcomes using their saved event/project scope. Child completions and no-op cycles do not produce duplicate alerts. Destinations remain opt-in.\n\n## Recovery and storage\n\nTeam aggregates are private files under `$CODEFLOW_HOME/project-teams/`. Each mutation validates and fsyncs its snapshot before acknowledgment. A cycle claim and its full budget allocation are saved before starting a run; the initial run identity is fsynced and attached to the claim before any agent dispatch.\n\nRecovery adopts only a matching team/cycle/claim/input/attempt receipt. An ambiguous launch is marked **unknown** and is never automatically replayed. Inspect its run and host process state, then stop the cycle if abandoning it; stopping does not pretend unknown usage is zero. A storage error remains visible, and corrupt team files are preserved for repair. Duplicate repository ownership blocks both records.\n\nOn first load, legacy assignments gain a shared Git metadata identity. This additive upgrade preserves their IDs, immutable mission/cycle history, operation receipts and usage. Linked worktree aliases cannot acquire separate ownership. Replacing a repository's Git metadata blocks dispatch rather than silently adopting the replacement. Reusable definitions are separate `at-*.json` files in the same directory, with at most 100 definitions, 100 versions per definition and a 2 MiB per-definition ceiling; damaged definitions are preserved and reported without preventing unrelated existing missions from loading.\n\nAutomations stores only a managed target and a display projection. Mission settings remain authoritative in the team aggregate. Managed schedules cannot be edited through generic automation controls. Schedule occurrences use stable operation IDs, so replaying a due occurrence cannot launch a second cycle. The existing one-hour missed-run grace and DST rules apply.\n\nHistory is bounded to 100 cycles, 200 opportunities, 2,000 decisions and 2,000 operation receipts, with a 16 MiB aggregate ceiling. At the limit, archive the team and retain its file before creating a replacement. History is not silently pruned; rejection feedback is never silently forgotten within a team.\n\nBackups include team evidence and missions. Restore pauses teams, cancels active cycle admission while retaining unknown reservations, and invalidates ready/accepted results. Inspect repository membership, providers and retained receipts before reactivation. ACP executable settings and other existing credential exclusions remain excluded from portable backups.\n\nOwner APIs, managed child runs and the project-scoped event topic are unavailable to shared accounts, because the child tasks include private owner mission and decision context. Custom filesystem restrictions block the feature alongside other host execution/private-data surfaces. Removing project membership stops owned work. There is no cloud scheduler or new agent runtime.\n\n## API\n\nAll mutations use a client-generated `operationId`. Team edits and opportunity decisions require the current `revision`; opportunity decisions also require `opportunityRevision`. A replay with the same operation and content is idempotent; changing its content or acting on a stale revision returns a conflict.\n\n| Method and path | Purpose |\n|---|---|\n| `GET /api/project-teams?project=ID` | Scoped assignments, reusable team summaries, storage faults and provider eligibility |\n| `GET /api/project-teams/definitions` | Reusable team summaries and definition storage faults |\n| `GET /api/project-teams/definitions/:id` | One definition and its retained versions |\n| `POST /api/project-teams/definitions` | Create from name/configuration, or name/sourceTeamId |\n| `POST /api/project-teams/definitions/:id` | Save a new version with revision, name and configuration |\n| `POST /api/project-teams` | Create a draft with projectId, repo, name, mission, optional definition `{id, revision}` and scheduleEnabled |\n| `GET /api/project-teams/:id` | Aggregate and remaining allowance |\n| `POST /api/project-teams/:id` | Update mission/name/status, schedule/scheduleEnabled or adopt a definition with revision; definition `null` detaches the reusable reference while keeping configuration |\n| `POST /api/project-teams/:id/tasks` | Admit an owner task with revision, title, instructions, acceptance array and optional category |\n| `GET /api/project-teams/:id/preview` | Readiness and schedule preview without model calls |\n| `POST /api/project-teams/:id/cycle` | Explicit cycle admission |\n| `POST /api/project-teams/:id/stop` | Stop owned cycle with revision |\n| `POST /api/project-teams/:id/opportunities/:opportunityId` | approve, build, revise, reject or defer |\n\nThe existing stream broker multiplexes `project-teams:<projectId>`. Generic run bodies cannot supply managed metadata or launch callbacks.\n\n## Validation and rollout\n\n`npm run evaluate:project-teams -- --fake --cycles 10 --out /tmp/my-standing-pilot` exercises two isolated fixture repositories and saves a report plus private run evidence. The default is synthetic. `--real --confirm` explicitly opts into signed-in CLI use, capped at 24 invocations and 60 allocated minutes per cycle. Use `--help` first. Neither mode merges or pushes.\n\nTests cover the full cycle, exact-commit approval, scope violations, failed checks, provider exclusions, budget limits, duplicate admission, durable receipt failure, restart adoption/ambiguity, cancellation, membership removal, feedback retention, storage corruption, restored authority, notifications, owner-only APIs, filesystem restrictions and shared browser transport.\n\nThe initial synthetic ten-cycle pilot produced two ready drafts and eight no-ops across two fixture repositories. It establishes mechanics only. Real-provider quality, novelty, usefulness and owner acceptance have not been measured; keep the flag opt-in until that separate pilot is assessed.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Project missions (preview)"},{"group":"Work with agents","id":"review-inbox","source":"docs/review-inbox.md","body":"# Review inbox\n\nOpen Inbox from the sidebar. Filter to a project or attention type, then open an item to answer its question or inspect its result. Sorting the inbox does not approve or publish work.\n\n## Work through attention items\n\nThe **Inbox** sidebar destination groups attention for manual and unattended runs.\nQuestions and approvals, blocked teams, unread failed/interrupted runs and pending\nautomation results appear there. Archived and handled items are excluded.\n\nRun priority comes first, followed by attention type and then the oldest waiting\nitem. Group by project, repository or attention type, or use one list. Search task\ntext, run IDs, repository paths, source titles and trigger names. Project scope\ncontinues to apply; the page reports attention items outside the current scope.\n\nOpen the run to answer questions, inspect failures or review evidence. Tool\npermissions remain in their scoped approval inbox. No result is merged or approved\nby sorting, filtering or opening this page. Grouping is a presentation choice and\ndoes not change dispatch order; the explicit run priority controls dispatch too.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Review inbox"},{"group":"Work with agents","id":"verification","source":"docs/verification.md","body":"# Verification and review\n\nOpen Project → Repository tools → Verification to inspect the repository’s checks. Select the profile in the composer, then review each required gate and criterion mapping on the final result.\n\n## Verify a result\n\nIn the composer, select a verification profile instead of the single legacy check. Lightweight, standard and thorough presets detect existing repository commands. In **Project → Repository tools → Verification**, review/edit the detected commands, add bounded custom gates, map acceptance criteria to checks, and run them at a pinned commit. A baseline comparison runs the same profile on both commits; repeated attempts distinguish consistent failures from flaky results. Export the report or profile for reuse through the API.\n\nEvery required gate must pass. Skipped gates, mixed results, missing criterion mappings and failing mapped checks cannot produce `verified`. A criterion mapping is your assertion that the chosen checks exercise its behavior; passing a command is not a semantic proof of an arbitrary requirement. Finished work without independent verification is labeled accordingly.\n\nThorough detection recognizes `test:mutation`/`mutation`, `test:visual`, and `test:a11y`/`test:accessibility` scripts when present. These are explicit mutation, visual and accessibility gates. The repository supplies its testing tools, mutation thresholds, image baselines, target URLs and accessibility rules. code-flow executes the commands and retains their exit/output evidence; it does not install those tools or invent screenshot baselines. Set optional gates to required when their failure must block verification.\n\nProfiles support at most 12 checks, 48 criteria, 30 minutes per check, three repeats and a one-hour suite deadline. Setup has a separate two-minute bound. Profile checks must preserve tracked files, the index and HEAD; changing tested code or hiding files with Git index flags blocks the result before another gate can run. Ignored or untracked build output is allowed. Temporary worktrees are removed on failure and cancellation.\n\nThe **Changes** side tab records human review of individual files at an exact commit, exposes heuristic change-risk reasons, and records time spent correcting a result. Review coverage becomes stale when the branch moves. Agent activity is not counted as human rework, and a clean test run does not mark files reviewed.\n\nThe result's merge panel can **Validate merge result** against the current local target branch. This constructs the combined tree in a disposable checkout and runs the configured setup/profile. Requiring validation pins source, target and merged tree; moving refs, rewriting checks or a different merge result invalidates it. Commit-hook changes are detected, with rollback of the application's own clean merge when possible. GitHub PR merges still use the repository's remote checks and policies; this local validation does not replace a remote merge queue.\n\nChecks execute repository code. Use a check container or explicitly trust host execution, especially with imported source text. Agent isolation and check isolation are separate settings. See [security](security.md), [agent containers](agent-containers.md), and the [API contract](api.md).\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Verification and review"},{"group":"Work with agents","id":"github","source":"docs/github.md","body":"# GitHub, review and shipping\n\nReview the result diff and its check evidence. Use the run’s publishing controls to merge locally or open a pull request; confirm the repository and target branch before applying the action.\n\n## Review and ship\n\n**Review changes** opens a workspace containing the diff, verification evidence, commit-scoped human review coverage and review comments. Mark files reviewed in the file pane. Expand **Publish changes** to use the existing merge, PR and validation controls without leaving review. **Refresh changes** reloads the diff and coverage after an external branch update; coverage is reset when the commit changes.\n\n\nWhen a run finishes, each result branch has **Review changes**, **Merge…** and **Open PR…** buttons.\n\n- **Review changes** opens a per-file diff. Click any line to comment on it, and add an overall comment if you like.\n - **Send N comments to [agent]** queues a *follow-up* in the same run: that agent continues its own session in the worktree, addresses the comments, and code-flow commits and re-runs the check.\n - Follow-ups are recorded on the run, so they survive restarts. The result header and summary reflect the latest follow-up.\n - Draft comments are kept if you navigate away.\n- **Merge…** merges the result into one of your branches, and only when that's safe:\n - If the target branch is checked out (e.g. `main` in your checkout), the merge happens there, and only when it has no uncommitted changes to tracked files (staged or not).\n - If something else stops it — untracked files the merge would overwrite, another git holding the repository's lock, a failing hook, git not knowing your name and email — you're told which (and which files), and your checkout is put back as it was.\n - That merge runs your hooks, with up to 30 minutes for them (`CODEFLOW_MERGE_TIMEOUT_SEC`), so a post-merge `npm install` or a Git LFS download has time. If git is stopped after it made the merge commit, the merge is done, and you're warned the hook may not have finished.\n - If the target branch isn't checked out anywhere, code-flow builds the merge with `git merge-tree` and moves the branch, so no checkout is touched. That needs git 2.38 or later; with an older git, check the branch out and merge there.\n - A branch that's being rebased or bisected in one of your worktrees isn't merged into: finishing or aborting the rebase would undo the merge. You're told where, and what to run there first.\n - On a real conflict you see the files and can **ask an agent to resolve them**: code-flow merges the target into the result branch and the agent fixes the markers. Then merge again.\n - By default the run is archived after merging (worktrees removed, branches kept).\n - Merging a branch that's already in the target says *Nothing to merge* instead of recording a second merge.\n- **Merge PR…** takes the place of **Merge…** once the branch has a pull request, and merges that pull request on GitHub (`gh pr merge`):\n - Pick how, from the ways the repository allows: a merge commit, squash, or rebase. You confirm before anything happens. The panel shows the PR's CI, and says so if GitHub reports a conflict.\n - It merges only while the pull request is exactly the branch you reviewed here. If the branch has commits the PR doesn't have yet (a follow-up that wasn't pushed), or someone pushed to the PR, you're told which and nothing is merged; GitHub also refuses if the PR moves in the meantime.\n - A draft or closed PR isn't merged; one already merged on GitHub is recorded as merged. No branch is deleted, and `origin/<base>` is fetched afterwards.\n - With a merge queue, or rules that hold the merge until required checks pass, GitHub accepts it and merges later.\n - **Merge into a local branch instead…** still offers the local merge above.\n- **Open PR…** pushes the branch to `origin` and opens a pull request with `gh`, using the task and summary as the description (cut to fit GitHub's 65,536-character limit). If the push works but `gh` then fails, or a pull request for the branch is already open, code-flow finds that pull request and records it, so trying again doesn't fail with \"already exists\".\n- **Archive…** removes a run's worktrees (optionally its branches too). Kept branches can still be merged or resumed later. A worktree git won't remove (a locked one, one with submodules) is tried again with `--force` twice; whatever still can't be removed is reported rather than left behind silently.\n- **Try it:** each result also has **Terminal**, **Editor** and **Folder** buttons and **▶ Run app**; see [Configuration](configuration.md#per-repo-config).\n\n## Start from where the work lives\n\nRuns can start from GitHub instead of a blank task box, using GitHub CLI (`gh`) with your own login.\n\n### Templates\n\n**Start from** above the task box sets up common jobs:\n\n| Template | Pattern | Starts from |\n|---|---|---|\n| **Fix an issue** | Plan → Build → Review | an issue; the PR you open later says `Closes #N` |\n| **Fix failing CI** | Plan → Build → Review | a PR, with its failing checks and their job logs (`gh run view --log-failed`) given to the agents |\n| **Review a PR as a council** | Review council | a PR |\n| **Upgrade a dependency** | Plan → Build → Review | the task box (replace `PACKAGE`) |\n\nThe task names the issue or PR by number. Its title isn't put in the task (whoever opened it wrote it, and agents read the task as instructions); it reaches them with the issue's or PR's text.\n\n### Sweeps: many at once\n\n**Sweep** under *Start from* lists a whole kind of work, with a checkbox per item. The ones you pick start **one run each**, with the team, check and settings you set up in the composer. Each result waits in the [inbox](automations.md#the-inbox) for you to approve, revise or reject, and nothing is merged automatically.\n\n| Sweep | Lists | Picked by default | Each run |\n|---|---|---|---|\n| **Fix open issues** | open issues | all, except ones labelled *question*, *wontfix*, *duplicate*, *invalid*… | fixes the issue and adds a test; **Approve & open PR** says `Closes #N` |\n| **Fix Dependabot PRs** | Dependabot's open PRs (`gh pr list --app dependabot`), with their CI | only the ones whose CI fails; green ones say *ready for you to merge* | gets the failing checks and their logs, adapts the code to the new version, with the PR's text and logs [fenced as untrusted](#untrusted-text); **Approve & push to PR #N** pushes the fix to Dependabot's branch (never forced) |\n| **Fix security alerts** | Dependabot alerts, code scanning alerts and leaked secrets | high and medium alerts that have a fix | Dependabot alerts: one run per package, upgrading past every alert on it. Code scanning: one run per alert, fixing the cause rather than suppressing it |\n\n- An item that already has a run (running, or finished and not rejected) starts unpicked and links to that run, so sweeping again doesn't redo work. For a package's Dependabot alerts that means a run covering every one of them: a new alert on the package is new work.\n- Lists read every page GitHub has, up to 1,000 items of each kind (200 Dependabot PRs), and say so when there were more.\n- Dependabot alerts that an open Dependabot PR already upgrades start unpicked, and so do alerts with no patched version yet (that run would have to mitigate instead).\n- **Leaked secrets are never handed to an agent.** They're listed so you can revoke and rotate them yourself.\n- At most 25 runs start per sweep; agents and checks still queue behind `CODEFLOW_MAX_AGENTS` and `CODEFLOW_MAX_CHECKS`. A run that can't start (an untrusted issue without Docker, say) is reported, and the rest go on.\n- *Also check every day at…* saves the sweep as an [automation](automations.md#sweeps) that starts runs for new items.\n- Reading security alerts needs the `security_events` scope for private repositories: `gh auth refresh -s security_events` (and admin or security manager access). The sweep says so if it's missing, or if a feature isn't turned on for the repo.\n\n### Issues and pull requests\n\n**GitHub** in the task box starts from any open issue or PR:\n\n- An **issue's** title and description go to every agent with the task.\n- A **PR's** branch is fetched (`pull/N/head`, so forks work too) and becomes the run's base. Its description goes to the agents, and its changes are compared against the PR's target branch.\n- When a run came from a PR, a result has **Push to PR #N**. It pushes to the PR's branch and is never forced, so it fails if someone else pushed in the meantime. You can't push to a PR from a fork; you can still review it.\n\n### Review council\n\nA read-only pattern: several agents review the same changes independently, and a chair merges their findings.\n\n- Reviewers are anonymous to the chair (\"Reviewer A/B\"). code-flow runs the tests reviewers show their bugs with before the chair merges, and findings rank by severity, then by whether a test confirms them, then by how many reviewers agree. The verdict follows the findings, not the chair: changes are requested when a finding blocks (see [checked review findings](patterns.md#checks-loops-and-limits)).\n- Nothing is changed. **Post to PR #N** posts the review as a comment, and only when you click it.\n\n### Follow-through\n\n**Watch PR** works on a result that has a PR. code-flow checks it every minute. A PR that stays green with no new comments is checked less often, up to every 10 minutes, and every minute again once something changes:\n\n- **Failing CI:** the failed jobs' logs go to the agent you picked, which fixes the cause. code-flow commits and pushes to the PR.\n- **New comments** from you (the account `gh` is signed in as), or from people you list under *Also act on comments from*, go to the agent in the same way. Nobody reviews these fixes before they're pushed, so anyone else's comments are logged and ignored, even an org member's. Comments that were there before you started watching are never acted on.\n - PR comments and a review's summary are read. Line comments on the diff aren't read yet; the run's log says so when watching starts.\n- These unattended fixes never run with more than *standard* permissions, and a fix that touches the tests at all isn't pushed: a deleted, skipped or weakened test, a changed test config or CI workflow, the runner's settings in `package.json` or `pyproject.toml`, or fewer tests running than before the fix. It's handed back to you instead.\n- **Stops:** after the number of fixes you allow, if someone else pushed to the PR, or when the PR is merged or closed. It then shows under *Needs you* with a notification.\n- **Can't check:** when checking the PR fails (`gh` signed out, rate-limited, offline), code-flow waits longer before each retry. After three failures in a row the watch shows under *Needs you* with the reason, e.g. *gh isn't signed in — run `gh auth login`* (it asks `gh auth status` to tell). It keeps retrying, and goes back to watching once `gh` answers again.\n- It **never merges**.\n\n### Untrusted text\n\nText from outside the repo is untrusted. Issue and PR titles and descriptions, comments and CI logs can carry instructions aimed at the agents. code-flow asks GitHub who wrote an issue or PR. Unless it's the repo's owner, a member or a collaborator, and not a fork, the run is locked down (🔒 in the run view):\n\n- agents run in Safe mode;\n- files like `.env` aren't copied in;\n- checks run in Docker without network.\n\n**Dependabot's PR text is untrusted too.** GitHub opens the PRs, but their descriptions quote the new version's release notes and commit messages word for word, and their CI logs print the new package's output. That text is fenced as data. The run itself isn't locked down: the branch is this repository's and installs what merging it would, so setup can install the new version (with network) and the check can verify it. Pushing a fix to Dependabot's branch still works.\n\n**A code scanning alert's message is fenced too.** It quotes the code it flags (names, strings), which whoever wrote that code chose. The rule's own description and help aren't fenced.\n\nWithout Docker, code-flow asks before running that person's tests on your machine; automations never do. **▶ Run app** on a locked-down run asks first too: the app is that code, running on your machine outside any container. The text itself — title, branch name and description — is quoted in a fence it can't break out of, marked as information rather than instructions. Conventions files are read from where the PR started, not from the PR.\n\nTo run these jobs on a schedule or from webhooks, see [Automations](automations.md).\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"GitHub, review and shipping"},{"group":"Work with agents","id":"github-review-threads","source":"docs/github-review-threads.md","body":"# GitHub review threads\n\nInspect the actual GitHub review thread and its current revision before replying or changing its resolved state. Refresh a stale thread before retrying an action.\n\n## Work through PR feedback\n\nRuns started from a GitHub pull request and result branches with an opened pull request show a **GitHub review threads** disclosure. It reads actual GraphQL thread IDs, diff sides, line ranges, original locations, outdated flags, resolution state, authors and replies. This complements conversation comments and review summaries; a REST root-comment ID is not used as a resolution thread ID.\n\nGitHub discussion is untrusted reference material. Comments are rendered as plain text, credential-like values are redacted and shortened bodies are labeled. Reading or resolving a thread does not send its content to an agent, approve a pull request or mark local checks and requirements complete.\n\nThe owner can explicitly press **Resolve thread on GitHub** or **Reopen thread on GitHub** when the current GitHub login has permission. There is no automatic resolution based on agent output, check results or a local review. A server-issued snapshot pins the repository, pull request node ID and current head, together with the thread location, resolution, permission state and all loaded comments and updates. Before a mutation, code-flow re-reads that exact thread and PR, and rejects changed heads, edited comments, new replies, changed locations or resolution state. Refresh and review the current discussion before acting again.\n\nGitHub’s resolve/unresolve mutation does not accept an expected head or thread revision. Therefore a remote edit can race between the final comparison and the mutation. Code-flow rechecks afterward and reports a warning if the current head/state cannot be confirmed. It does not claim that this is an atomic GitHub compare-and-swap. A dropped mutation response is explicitly ambiguous, old snapshots are invalidated, and code-flow never retries the mutation automatically. Inspect the fresh state before another action.\n\nListings use four explicit pages of 25 threads. Each initially shows up to ten comments. **Load complete thread** reads at most four pages of 25 comments; threads with more than 100 comments remain read-only here and must be handled on GitHub. Thread and comment truncation are visible. Displayed bodies are capped at 8,000 characters per comment and 2 MiB of characters for the list. Each GraphQL subprocess has a ten-second deadline and an 8 MiB response bound. Snapshots expire after 20 minutes and at most 200 are kept; restart requires a refresh.\n\nThe application uses the owner’s installed `gh` login against the explicit hostname from the run’s stored HTTPS PR URL. Arbitrary client URLs and unknown result branches are rejected. GraphQL bodies go to stdin, not a shell command, and pagination is bounded by the application rather than `gh --paginate`. The CLI owns its authentication and HTTP redirect policy; this feature does not read or export GitHub tokens or claim a separate application-enforced no-redirect transport. See the official [GitHub CLI API manual](https://cli.github.com/manual/gh_api), [review thread fields](https://docs.github.com/en/graphql/reference/pulls#pullrequestreviewthread) and [resolution mutation inputs](https://docs.github.com/en/graphql/reference/pulls#resolvereviewthreadinput).\n\n## API\n\n`GET /api/runs/:id/pr-threads?branch=<owned-result-branch>` returns a snapshot with `snapshotId`, `prId`, `head`, `state`, `threads`, truncation flags and `expiresAt`. Without a branch, the run’s source PR is used, or its sole stored result PR. An unknown branch or missing PR returns 400. No caller-supplied PR URL is used.\n\n`POST` on the same route with `{action:\"load\",snapshotId,threadId}` returns the bounded complete thread and its updated `revision`.\n\n`POST` with `{action:\"resolve\",snapshotId,threadId,expectedHead,expectedRevision,resolved:true|false}` performs one explicit resolution action. A stale/foreign/expired snapshot, changed PR/thread or incomplete comments returns 409; missing GitHub permission returns 403; malformed requests return 400. A failed or ambiguous remote mutation returns 502 with instructions to refresh. A confirmed action returns `{applied:true,refreshRequired:true,thread?,warning?}`. Any old snapshot for that PR is invalidated before mutation.\n\nAutomated tests use injected GraphQL and a fake `gh` process only. Browser acceptance covers stale-reply rejection, refresh, resolve and reopen, forged snapshot rejection and separation from local verification. No live GitHub mutations were performed during development.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"GitHub review threads"},{"group":"Work with agents","id":"task-reruns","source":"docs/task-reruns.md","body":"# Selective task reruns\n\nOpen an idle Auto-team result, select tasks to rerun and inspect the dependency preview. Allocate a fresh budget; the new child run must verify its final result independently.\n\n## Repeat only the work that changed\n\nOpen an idle Auto-team result and expand **Rerun selected tasks**. Select the accepted tasks to execute again, optionally replace instructions for those selected tasks, review the dependency preview, and allocate a fresh call/time budget. Every transitive dependent task is invalidated, including completion, artifact and knowledge dependencies. Unselected tasks with unfinished or stale work must also be selected; their old evidence cannot be silently retained.\n\nThe action creates a child run at the exact integration commit shown in the form. It does not edit, resume, or change the original run. Uncommitted worker changes are excluded and remain in the original checkouts. Failed, stopped and interrupted results can be rerun when they have an accepted plan and owned integration checkpoint. A baseline without passing parent verification is explicitly marked unverified. If the integration branch moves after the preview, refresh and review its new commit before starting.\n\nThe child executes the pinned accepted plan directly; it does not ask a coordinator to create a replacement plan. Stable task/member IDs, requirements, dependencies, acceptance, ownership paths, contracts, provider pool and model/effort settings remain fixed. Selected tasks and every dependent start pending, without prior attempts, heads, artifacts, handovers, sessions, private checkpoints or verification evidence. Other completed tasks are labeled **retained baseline inputs**, with their source run, source task commit and integration baseline. Their commits must be ancestors of that baseline. Retained public artifact references and handovers are source data, not new instructions or authorization.\n\nAll checks, reviews, fail-to-pass claims, human coverage, messages, context manifests and execution metrics start independently in the child. Every original requirement receives a fresh final independent review. A retained task count is not a new execution count; the evaluation records retained tasks and newly executed tasks separately. If the source member was retired or its pinned provider/model is no longer eligible, the rerun fails explicitly instead of silently changing the team or permissions.\n\nAn implementation rerun can legitimately produce no new diff when the accepted behavior already exists. This is measured by Git, not accepted from an agent's assertion. Such a result still requires a passing check on the child commit and independent approval of its acceptance. An entirely unchanged result does not fabricate fail-to-pass evidence: it records `proofNotRequired: \"unchanged-rerun\"` after the fresh check and review. Any new code diff, including a repair, retains the normal proof policy.\n\nPlan approval follows the source's setting. A rerun's held plan cannot be replaced through the plan editor or a coordinator revision request; reject it and create another selection to change its task set or instructions. Runtime questions and bounded repairs continue through normal Auto-team controls. An internal, checksummed seed is journaled before execution, so restart/resume restores the same selected work and pinned baseline projection. The checksum detects changed replay data; it is not an authorization token.\n\n`POST /api/runs/:id/rerun-tasks` accepts:\n\n```json\n{\n \"expectedHead\": \"EXACT_CURRENT_INTEGRATION_COMMIT_SHA\",\n \"taskIds\": [\"implement-api\"],\n \"instructions\": {\"implement-api\": \"Recheck the error path and its acceptance tests.\"},\n \"budget\": {\"maxSteps\": 12, \"maxInvocations\": 12, \"maxMinutes\": 20}\n}\n```\n\nIt returns `201 {\"id\":\"CHILD_RUN_ID\"}`. Requests need 1–24 distinct known tasks and an explicit fresh allocation of 4–200 calls and 1–720 minutes. The effective call budget must fund the invalidated tasks, independent review and one recovery allowance. Unknown fields, forged seeds and instructions for unselected tasks are rejected. Stale source state, unavailable pinned roles, or overlapping source operations return 409. Concurrent identical creation requests share one child while pending; after that creation finishes, a repeated request deliberately creates another child. A dropped response should therefore be reconciled by inspecting run history before retrying.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Selective task reruns"},{"group":"Work with agents","id":"deferred-runs","source":"docs/deferred-runs.md","body":"# Deferred runs\n\nConfigure a task in the composer and choose a time, provider capacity, or both as its start condition.\n\n## Queue a one-time task\n\nInspect and manage the queue in **Scheduling → Run once later**. Save a new deferred request from the composer after configuring its task.\n\nDeferred runs save one explicit run request for later execution. They are separate from recurring automations. A job can start at or after a chosen local time, when selected providers have capacity, or after both conditions are satisfied. The application must be running; closing it preserves the queue for the next launch.\n\nThe queue uses the application's reported provider windows and current availability. A known provider reset is a waiting condition. An unknown reset is periodically checked; it is not presented as a promised reset time, and the queue never makes a billable inference call to probe quota. Provider selection and source references are validated again when the job launches.\n\nDeferred requests use Safe mode, disable native tool and plan approval waits, and suppress ignored-file copying. Uploaded attachments cannot be saved in the queue. Repository context paths remain references to the repository and are revalidated on launch. GitHub sources are stored as issue, PR or alert references. External sources are stored as configured integration/kind/key references and fetched again; imported text and client-supplied authority are never persisted as trusted execution context. Missing parent runs must fail validation, rather than silently losing inherited source restrictions. Deferred runs never grant permission to run untrusted source checks on the host.\n\nEach launch claim is written atomically and synced before the launch callback executes. Local queue instances share a transaction lock; the application server's exclusive ownership lock is required across processes. Only one automatic claim can win for a queued revision. The launch callback must persist and sync the created run's initial snapshot before returning its run ID, and include the job ID, claim ID and attempt number in that snapshot.\n\nA crash, timeout or unexpected launch error leaves an **unknown** outcome, because a run may already exist. Unknown launches never retry automatically. The recovery UI displays the exact claim and lets you link a matching run, cancel queue recovery, or explicitly acknowledge that another attempt may duplicate work. The server must verify the linked run's persisted claim and repository/project scope. Late receipts are retained in bounded history, including receipts for older attempts. A known rejection before launch side effects is distinct from an unknown outcome and can be explicitly retried after correction.\n\nCancellation before claiming prevents execution. Cancellation during launch aborts the callback and may produce an unknown outcome; if a late receipt identifies a run, the configured cancellation callback receives it. Cancelling unknown queue recovery does not imply that an unknown existing run stopped. Started runs are stopped through the normal run controls.\n\nLimits are explicit: at most 100 active/unknown jobs, 200 retained jobs, 30 history events per job, three user-requested launch attempts, 96 KiB per request, and a 32 MiB queue file. Old terminal job records are removed first. No run is retried solely because the server restarted. Corrupt or unsupported queue files fail closed and are preserved for repair. Configuration and history are saved in `CODEFLOW_HOME/deferred-runs.json` with owner-only permissions and atomic synced replacement.\n\nThe saved request includes the exact task and command text you entered. Reference environment variable names for credentials; inline credential values would become part of that local configuration. Provider authentication files and environment values are never automatically captured. Diagnostic history is redacted, while executable configuration is preserved so redaction cannot silently change a command's behavior.\n\nThe reusable service is `DeferredRuns` in `src/deferred-runs.ts`; `ready`, `launch` and optional `cancelRun` callbacks connect it to the application. Call `load()` once, reconcile unknown claims against durable run snapshots, then `start()`. Stop the queue before shutting down the engine. Tests use fake launch callbacks and provider windows, without real provider credentials or quota.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Deferred runs"},{"group":"Your workspace","id":"configuration","source":"docs/configuration.md","body":"# Configuration\n\nUse Settings for application preferences and connections. Open a project’s Repository tools for verification, history and services. Use `.codeflow/config.json` for committed repository defaults.\n\n## Application settings\n\nOpen **Settings** at the bottom of the sidebar, press **⌘, / Ctrl+,**, or search for a settings category in the command palette. Categories have shareable addresses such as `#/settings/ssh`; browser Back and Forward preserve navigation. Search settings by a feature name such as “theme”, “permissions”, “VM”, or “Tailscale”.\n\n- **General:** light, dark, or system appearance; sidebar grouping; archived runs; keyboard shortcuts. Terminals follow the selected appearance.\n- **Agents & models:** provider health and usage, model and effort defaults. Sign in through the provider CLI on the current machine.\n- **Run defaults:** workflow, plan approval, agent permissions and run limits. These update the composer’s remembered choices and the current draft; running work is unchanged. Project defaults and per-role choices can override model preferences.\n- **Notifications:** browser notifications and outbound notification destinations.\n- **SSH connections:** save and open remote VMs, with optional port and identity-file overrides. See [SSH projects](ssh-projects.md).\n- **Remote access:** Tailscale, Wi-Fi, pairing and device access to the local workspace.\n- **Team:** connect this computer to a team server as a runner and map repository keys to local clones; on the team server, define each project's repository keys and the owner's display name, read the `https://` address teammates connect to and revoke runners. See [Team collaboration](team-collaboration.md).\n- **Integrations, Projects & access, Extensions, Data & storage:** existing source/code-host/worker integrations, project conventions, filesystem policies, shared accounts, extensions and retention controls.\n- **Advanced:** collect/export diagnostics and find the server configuration reference. Process-level settings and credentials remain environment configuration and require a restart.\n\nAppearance, run defaults and sidebar preferences save in this browser, separately for each SSH workspace. SSH definitions and the team link with its repository mappings save on the local server; project, access, notification and retention settings save on the machine hosting that workspace; team board tasks, checkpoints and runner registrations save on the team server the link points to. Controls with a **Save** action keep their existing explicit save behavior. Numeric run limits save when you leave the field; invalid values restore the previous setting.\n\n**Project → Repository tools** contains verification, code editing, dependency and context inspection, checkpoints, services, coordinated changes and filtered tasks. **Inbox** and **Automations** are sidebar destinations. Open **Insights** and the workspace from the application menu or command palette. Older Settings URLs redirect to the current destinations.\n\n## Per-repo config\n\nCommit `.codeflow/config.json` and every run in that repo (and every teammate) gets the same defaults. Anything you set in the UI wins.\n\n```json\n{\n \"check\": \"npm test\",\n \"setup\": \"npm ci\",\n \"copy\": [\".env\", \".env.local\", \"config/*.local.json\"],\n \"run\": { \"command\": \"npm run dev\", \"port\": 3000 },\n \"models\": { \"claude\": { \"model\": \"opus\", \"effort\": \"high\" } },\n \"limits\": { \"maxMinutes\": 90, \"maxSteps\": 60 },\n \"trust\": \"standard\",\n \"image\": \"node:22\",\n \"learn\": true,\n \"prove\": true\n}\n```\n\n- `check` and `setup`: the composer suggests both from the repo (see [Checks](patterns.md#checks-loops-and-limits)), but set them here when the guess is wrong. A uv workspace whose tests live in their own member might use:\n\n ```json\n { \"setup\": \"uv sync --all-packages\", \"check\": \"cd tests && uv run --no-sync pytest -q\" }\n ```\n\n Setup runs once per worktree outside the agents' sandboxes, so the check can run without network (`--no-sync`). When a run's check can't run at all (not installed, nothing collected, `conftest.py` can't import the project), the run says so and asks you to set these.\n- `copy` brings files git doesn't carry (like `.env`) from your checkout into every new worktree. Only regular files that git ignores are copied. Symlinks are refused, since a cloned repo's `.env` could point at your credentials, and so are files git would commit. Patterns can't reach outside the repo, and nothing is copied through a symlink in the worktree.\n - Copied files are never committed, even when an agent un-ignores one or runs `git add -f`. Neither are new files named like secrets (`.env`, `.env.*` other than `.env.example`, `.env.sample` and `.env.template`, `*.pem`, `id_rsa*`, `id_ed25519*`). They stay in the worktree, and a warning names what was left out. Commit such a file yourself if it belongs in the repo.\n- `image` is the Docker image for running checks in isolation (automations, untrusted sources). `learn` passes recurring review problems on to later runs. `prove` checks that each passing result has a test that fails without it ([fail-to-pass](patterns.md#checks-loops-and-limits)); set it to `false` when the check is too slow to run twice.\n- A result's worktree that was removed (archived) is set up again when you follow up on it: files copied, setup run, context restored, and the files you sent with the follow-up added. If its branch was deleted, that follow-up fails with a clear message; the run itself stays finished. A follow-up also fails, rather than delete anything, when the worktree's folder is there but holds something else: move that folder away, then send it again.\n- **Try it:** each result has **Terminal**, **Editor** (Cursor, VS Code, Zed or Sublime, whichever is installed) and **Folder** buttons that open its worktree, plus **▶ Run app**.\n - Run app starts your dev server in the worktree on a free port (passed as `$PORT`), with logs and **Stop app**. Servers stop when the run is archived or code-flow shuts down.\n\n## What agents may do while building\n\n**Settings → Run defaults → Agent permissions:**\n\n| Mode | Claude Code | Codex | Antigravity |\n|---|---|---|---|\n| Safe | edits files; no shell beyond read-only git | edits inside its sandbox | sandboxed to the worktree |\n| Standard (default) | edits + common build/test commands (npm, node, pytest, uv, poetry, cargo, go, make…) | sandboxed, no network | sandboxed to the worktree |\n| Trusted | any command | sandboxed, with network | sandboxed to the worktree |\n| Full access | any tool, no permission checks (`bypassPermissions`) | no sandbox (`danger-full-access`) | no sandbox (no `--sandbox`) |\n\n**Full access** turns every agent's sandbox off in steps that build: an agent can run anything, use the network, and change files outside its worktree with your credentials. Use it only on repos and tasks you trust completely. To keep it a choice you make:\n\n- It's picked per run in the composer, which warns while it's on, and the run shows ⚠ full access. A repo's `.codeflow/config.json` can't turn it on: `\"trust\": \"full\"` there is ignored.\n- Planning, reviewing and judging steps stay read-only, as in every mode.\n- Runs nobody watches get less: automations and sweeps run with Trusted, and fixes for watched pull requests with Standard.\n- A run started from someone outside the repo is locked down to Safe whatever you picked (see [Untrusted text](github.md#untrusted-text)).\n\nWhatever the mode, code-flow never merges or pushes on its own.\n\n## Models\n\nPer provider in **Settings → Agents & models**, and per role right under each role's agents in the team. See [Getting started](getting-started.md#models-and-effort).\n\n## Light or dark\n\ncode-flow follows your system’s appearance until you choose Light or Dark in **Settings → General**. The pick is kept in that browser, and a side panel popped out into its own window follows it.\n\n## Health\n\nClick the agent dots at the bottom of the sidebar to see, for each CLI, whether it's installed, its version, and whether it's signed in. It uses `claude auth status`, `codex login status`, and for agy whether it can list models; credentials are never read. Claude's 5-hour and 7-day usage windows are shown from its own reports, with how to fix anything that's wrong. A run won't start with an agent that's signed out, and Auto skips an agent that turned out to be signed out for 15 minutes. If agy can't reach its servers, its sign-in shows as unknown rather than signed out.\n\nHealth also checks that each installed CLI still accepts the flags and subcommands code-flow passes (from its `--help`), and warns when one is missing: after a CLI update, that's the difference between \"healthy\" and every step failing on an unknown option. It also checks for git 2.38 or newer, which Merge needs.\n\nOnce an agent CLI reports its final result, it gets 10 seconds to exit; one that's still running then (a hook or MCP server that hangs on shutdown) is stopped and its result kept. A single output line over 16 MB is skipped, not read into memory.\n\n## Environment variables\n\n| Env var | Default | Purpose |\n|---|---|---|\n| `PORT` | `4317` | Server port |\n| `CODEFLOW_HOME` | `~/.codeflow` | Run logs and code-flow's own files (see [below](#files-in-codeflow)) |\n| `CODEFLOW_WORKTREE_DIR` | `.worktrees` | Worktree folder; relative paths are inside each repo, absolute paths are used as-is |\n| `CODEFLOW_STEP_TIMEOUT_MIN` | `30` | Timeout per agent step |\n| `CODEFLOW_STEP_IDLE_MIN` | `15` | An agent CLI that prints nothing for this long is stuck (a login prompt, a stalled network, a hook that never returns) and is stopped. Keep it above your longest silent test run |\n| `CODEFLOW_GIT_TIMEOUT_SEC` | `300` | Longest a git command may run, hooks included; cloning a new worktree's submodules gets 10 minutes |\n| `CODEFLOW_MERGE_TIMEOUT_SEC` | `1800` | Longest **Merge…** may take in a checkout, your hooks included (a post-merge `npm install`, Git LFS). If git is stopped after it made the merge commit, the merge counts as done, with a warning |\n| `CODEFLOW_MAX_AGENTS` | `6` | Agent CLIs running at once, across all runs |\n| `CODEFLOW_MAX_CHECKS` | `3` | Check and setup commands running at once, across all runs |\n| `CODEFLOW_RETRY_DELAYS_MS` | `20000,60000` | Waits before retrying an agent turn that failed for a passing reason |\n| `CODEFLOW_TOKEN` | random per start | The session token (set it to keep browsers signed in across restarts; anything that can read it can use the API) |\n| `CODEFLOW_MAX_DIFF_CHARS` | `60000` | Diffs longer than this are fitted (whole files first) before being passed to another agent |\n| `CODEFLOW_CLAUDE_WRITE_TOOLS` | build/test allow-list | Comma-separated Claude tool rules for write mode, e.g. `Edit,Bash` |\n| `CODEFLOW_REMOTE_PORT` | `PORT+1` | Port for the phone view (or the full app, with phone control) and webhooks on your network, when Wi-Fi or Tailscale is selected under **Remote access** |\n| `CODEFLOW_TLS_CERT`, `CODEFLOW_TLS_KEY` | unset | Optional certificate/key file paths overriding saved HTTPS setup in Settings → Connections → Share access. Configure both together; full control requires TLS on Wi-Fi and Tailscale so browser session cookies remain Secure |\n| `CODEFLOW_ALLOW_INSECURE_PHONE_CONTROL` | unset | Explicit `1` permits full phone control over HTTP for development |\n| `CODEFLOW_INTEGRATIONS_FILE`, `CODEFLOW_INTEGRATIONS_JSON` | unset | Explicit external source definitions with server credential references; imports are read-only, while [GitLab/Bitbucket code actions](code-hosts.md) require separate explicit previews/actions |\n| `CODEFLOW_REMOTE_WORKERS_FILE`, `CODEFLOW_REMOTE_WORKERS_JSON` | unset | Explicit provisioned SSH targets and repository mapping names; see [remote workers](remote-workers.md) |\n| `CODEFLOW_EXTENSIONS_FILE`, `CODEFLOW_EXTENSIONS_JSON` | unset | Explicit trusted bundled provider/workflow plugins with API negotiation and SHA-256 pins; choose one configuration source; see [extensions](extensions.md) |\n| `CODEFLOW_NOTIFICATIONS_FILE`, `CODEFLOW_NOTIFICATIONS_JSON` | unset | Explicit outbound destinations; targets remain disabled until event/project opt-in; see [notifications](notifications.md) |\n| `CODEFLOW_SCHEDULER_TICK_MS` | `30000` | How often due automations are checked |\n| `CODEFLOW_WATCH_INTERVAL_MS` | `60000` | How often watched PRs are checked for CI results and new comments. A PR that stays green with no new comments is checked less often, up to every 10 minutes |\n| `CODEFLOW_CODEX_TRANSPORT` | `auto` | `auto`: `codex app-server` when the installed Codex has it, else `codex exec`. A Codex without app-server is re-checked every 10 minutes; a slow or failed app-server start fails only that turn. `app-server`: require it. `exec`: always `codex exec` (your notes then wait for Codex's next turn) |\n| `CODEFLOW_PYTHON` | `python3` | The Python 3 that runs the side panel Terminal's pseudo-terminal (`scripts/pty.py`). It's found once and run by its full path, so a repository that pins another Python (pyenv, asdf, mise) still gets a terminal. Without a working one, terminals are line-based (`interactiveTerminal: false` in `/api/health`) |\n| `CODEFLOW_CHROME` | first one installed | The Chrome, Chromium, Edge or Brave the side panel Browser's **Snapshot** runs headless. Without one, Snapshot says so |\n| `CODEFLOW_AGY_SKIP_PERMISSIONS` | `1` | Set to `0` to stop passing `--dangerously-skip-permissions` to agy; then configure `permissions.allow` in `~/.gemini/antigravity-cli/settings.json` |\n\nA numeric setting that isn't a valid number (`30m`, say) or is out of range falls back to its default, with a warning in the terminal.\n\nChild processes exclude the application API token, scoped team capabilities, integration/notification configuration and their registered server credential variables. They also exclude npm lifecycle variables about code-flow itself and its own dependency bin paths; your `npm_config_*` settings remain. A check's tools come from its repository. Explicit agent containers use a smaller environment and only their configured authentication volume; see [container setup](agent-containers.md).\n\n## Files in `~/.codeflow`\n\n| File | What it is |\n|---|---|\n| `runs/<run>.jsonl` | Each run's event log, appended as it happens; runs are rebuilt from it at startup. A run snapshot after the first is written as what changed; logs from older versions are compacted once, in the background. Unreadable lines (a crash mid-write) are skipped and noted in `server.log` |\n| `runs/<run>.journal.jsonl` | The result of each completed step, written to disk at once, so **Resume** replays them instead of running them again |\n| `runs/<run>.cold-index.json`, `runs/<run>.cold-detail.json` | Optional source-validated archived projections; corrupt/stale projections rebuild from the event log. Full archived detail uses a bounded cache; see [run storage](run-storage.md) |\n| `shared-accounts.json` | Private password/session hashes, expiring accounts and project roles. Never exported in backups; see [shared projects](shared-projects.md) |\n| `shared-reviews/<run>.json` | Commit-anchored human comments, separate from agent notes and prompts |\n| `team/` | The team board this instance serves: `board.json` (each project's repository keys and its tasks with attempts, checkpoint manifests, discussion, decisions, instructions, reviews, hand-overs and audit), `runners.json` (runner names and credential hashes, never credentials), `inbox.json` and `checkpoints/<checkpoint id>.bundle`. Written whole through temporary files (heartbeats and progress reports reach `board.json` with the next durable change or within 30 s); one that can't be read is moved aside to `<name>.corrupt-<time>` and logged. At startup, temporary files and bundles that no available checkpoint lists are removed from `checkpoints/` and the removal is logged. Included in backups; see [team collaboration](team-collaboration.md) |\n| `team-link.json` | This computer's link to a team server: its address, this computer's runner credential, your account, your repository mappings and defaults (mode 0600). Never exported in backups, like `shared-accounts.json`, and skipped by a restore; after restoring a backup on a linked computer, connect it again in **Settings → Connections → Share access → People** and re-enter its repository mappings and defaults |\n| `team-link/attempts/<attempt>.json` | Which local run belongs to which board attempt, and what was published; read at startup, by the recovery pass every minute and after a restore to reconcile with the team server, never to start anything again |\n| `team-link/bundles/<checkpoint id>.bundle` | A checkpoint bundle this computer created, kept until the team server has received it; a failed upload is retried with the same file, and the file of a checkpoint that was given up is deleted |\n| `filesystem-policies/<project>.json` | Revisioned read/write rules and content hashes; see [filesystem policy](filesystem-policy.md) |\n| `filtered-tasks/` | Private filtered exports, exact policy/source/result records and explicit import receipts; never a startup execution queue |\n| `remote-worker-private/` | Private SSH receipt capabilities; excluded from ordinary result projections and restore authority |\n| `code-host-operations.json` | Safe confirmed/unknown code-host mutation receipts; no automatic replay after restart or restore |\n| `extension-cache/` | Captured trusted plugin bundles; excluded from backup/restore and rebuilt only from explicit reviewed configuration |\n| `projects.json` | Your [projects](runs.md#projects): names, repositories, and the team each one last used. Written whole (through a temporary file); a change that can't be saved fails and is undone. One that can't be read is moved aside to `projects.json.corrupt-<time>` and logged; if it can't be moved either, projects are off (runs start without one) until it's fixed |\n| `server.log` | code-flow's own log, one JSON line per entry: failed requests, unhandled errors, failed writes, processes it cleaned up. Past 5 MB it moves to `server.log.1` (one old file is kept) |\n| `processes.json` | The process groups code-flow has started and not seen finish. If code-flow dies without a clean exit (a crash, `kill -9`, a closed terminal), the next start stops what's left before resuming anything, so an orphaned agent can't keep editing a worktree. Pids from before a reboot, or reused by another program, are left alone |\n| `server.lock` | One server per `CODEFLOW_HOME`. It records the server's pid, start time and the boot, so a lock left by a crash is taken over even when another program now has that pid, or after a reboot |\n\n## Dynamic teams\n\n`CODEFLOW_AUTO_TEAM_ENABLED=false` disables new Auto team runs while preserving saved-run viewing and recovery. The rollout record and evaluation evidence are shown in Insights; the composer selects Auto initially only at the `default` stage when there is no saved workflow preference. The recorded stage is informational (and a composer default when set to `default`); only `CODEFLOW_AUTO_TEAM_ENABLED` changes whether new Auto runs can start. Existing runs without the `eventDriven` option retain dependency-wave replay; newly created Auto runs default it on.\n\nThe authenticated run API accepts `loop.maxInvocations` as an additional per-allocation ceiling on actual CLI launches, including retries. Auto already caps launches with its step budget. Follow-up requests can specify `budget: { maxSteps, maxInvocations, maxMinutes }`; the UI exposes this fresh allocation and retains lifetime usage.\n\nProject memory is in `memory.sqlite`, with automatic verified import from `memory.json`, indexed keyword search and a lightweight knowledge graph. See [Memory storage](memory-storage.md) for migration, rollback, runtime requirements and backup behavior. Evaluation reports and the explicit rollout decision still use durable replacement files under the evaluation store in `CODEFLOW_HOME`. See [Auto team](auto-team.md) for scope, trust, revision and deletion semantics.\n\n## Project missions\n\nClick **Enable project missions** on a project page to enable the feature immediately and save the choice for this host across restarts. `CODEFLOW_PROJECT_TEAMS_ENABLED=true` is an optional initial default; a saved UI choice takes precedence. `CODEFLOW_AUTO_TEAM_ENABLED` must also permit Auto team execution. Each team is separately activated by its owner. Schedules, budgets, missions, recovery and provider limits are described in [Project missions](project-teams.md).\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Configuration"},{"group":"Your workspace","id":"context-inspection","source":"docs/context-inspection.md","body":"# Context and budgets\n\nOpen Context inspector before starting a run. Review file sources, omissions and size estimates; select individual modified files when you need the current working copy included.\n\n## Check what you are sending\n\nThe composer’s **Context inspector** previews explicit repository selections and attachment metadata before a run. Folder selections expand tracked files from the chosen committed base. Current modified or uncommitted descendants are not implicitly copied by a folder selection; select those individual files to stage their current copies. Explicit file previews use the current file where present and fall back to the selected base when it is missing locally. Each preview identifies its source and content hash.\n\nInspection is bounded to 50 selections, 200 result entries, 512 KiB per text file and 8 MiB of inspected text. Larger, binary, invalid UTF-8, symbolic and Git LFS material is reported as omitted. Credential names and Git internals are not previewed. Per-file sizes and approximate text-token counts are shown with aggregate inspected, omitted and attachment bytes. Unscanned file sizes and binary/image tokens remain unknown. Uploaded text estimates use byte size; uploaded contents are not sent to the inspector again.\n\nPotential credential paths such as `.env`, private-key files, credential stores and `.git` are also rejected by actual context preparation and message attachment staging. A preview omission is not the sole defense. Selecting a symbolic file, a symbolic ancestor or a base symlink/submodule is refused before copying. Preview redaction only hides recognized secret patterns from this display; it does not rewrite other repository files. Agents can still read files already in their worktree according to their existing permissions.\n\nReference files are not pasted wholesale into prompts: the assembled request identifies their locations so agents can read them. Consequently, reference-file estimates and invocation-input budgets measure different things.\n\nEnable an explicit **Invocation context budget** to choose a byte ceiling, an approximate token ceiling, or both. Its policy either records a warning or stops the run before a provider launch. Budgets default to unset and never infer a provider’s context-window size. The server measures the fully assembled per-turn prompt (including notes and selected memory already added by code-flow) plus its explicit JSON schema immediately before the adapter is invoked. A blocked attempt does not launch a CLI. Retrying with the same exceeded ceiling will block again; deliberately choose a suitable budget for a new run. Retries and fresh-session fallbacks are measured again with their actual current prompt.\n\nSaved team presets capture this budget, including previous revisions and library import/export. Applying an older preset without a budget clears a current ceiling. The Effective settings inspector identifies the browser preference or exact preset revision that supplied the current budget. Version 1 preset libraries remain compatible when the optional `contextBudget` field is absent.\n\nEvidence records exact UTF-8 prompt/schema byte counts, their aggregate, an approximate token count of one token per four bytes, a content hash, resumed-session status, exclusions and any exceeded ceilings. Prompt bodies are not duplicated into this size evidence. Earlier CLI conversations, hidden provider system instructions, protocol wrappers, tool outputs, later file reads, and image/audio tokenization are outside this measurement. An approximate guard cannot guarantee that the provider’s complete context fits its window.\n\n`POST /api/workspace/context` accepts `{repo,baseRef?,paths,attachments?:[{name,size}]}` and returns source-labeled file results, attachment metadata, byte/token aggregates, omissions and limits. Run settings carry `loop.contextBudget = {maxPromptBytes?,maxApproximateTokens?,policy:'warn'|'block'}`. Both ceilings require positive bounded integers. The warning and block policies are enforced on the server and persisted with per-step input-size evidence.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Context and budgets"},{"group":"Your workspace","id":"project-conventions","source":"docs/project-conventions.md","body":"# Versioned project conventions\n\nSave a private convention draft, inspect its version history, and preview publication to a project repository. Commit the staged conventions file if future runs should inherit it.\n\n## Keep project guidance current\n\nSettings → Projects & access keeps human-authored project guidance privately beneath `CODEFLOW_HOME/conventions/`. A saved draft does not change a repository and is never inserted into agent context automatically. External run content cannot author guidance through the team capability API.\n\nEach save requires the exact observed revision. A concurrent edit returns HTTP 409 and preserves the editor draft. Compare the latest saved text, then deliberately keep your draft against that newer base or export it. Versions retain their original text, timestamp, hash, and note. The latest 32 revisions are retained; each holds at most 64 KiB of UTF-8 text. Line endings normalize to LF and nonempty guidance ends with a newline. History files use atomic durable replacement with private file permissions. Corrupt or inaccessible histories are left untouched, and a failed operation does not poison later writes.\n\nSelect a retained version and **Preview saved revision** before restoring. **Restore as new revision** requires both the current revision and the previewed version hash. It copies the old text into a new revision; it does not rewrite history or publish anything. Save or revert unsaved editor changes before restoring.\n\nTo activate guidance, select a repository belonging to this project and inspect its current file. **Publish and stage file** explicitly writes and stages `.codeflow/conventions.md`. The repository must be clean and idle. Publication checks the saved private revision, repository HEAD, and exact source file hash while holding the private revision lock and the repository lock. Non-project repositories, symbolic `.codeflow` components, symbolic files, symbolic `.git` markers, binary files, and oversized source files are refused. Code-flow stages this single guidance path even if it is ignored; it does not commit or push.\n\nReview and commit the staged file yourself. Only `.codeflow/conventions.md` at a run's committed base ref is read into context, before existing vendor guidance files. The existing aggregate convention limit remains 6,000 characters; longer guidance is truncated with a prompt to read the repository file. Publishing a draft without committing it does not change conventions for future runs. Locked-down imported runs continue to inherit trusted parent/base guidance according to existing run creation rules.\n\nPublication attempts restore the prior target file if staging fails and the source has not changed concurrently. If another tool changes HEAD or the target during publication, those changes are preserved and the UI requires a new inspection. Review any reported Git index error before retrying.\n\nAPI contracts: `GET /api/projects/:id/conventions`, `PUT` with `{revision,text,note?}`, `POST /restore-preview` with `{version}`, and `POST /restore` with `{revision,version,hash}`. `GET /publication?repo=PATH` returns the observed `{sourceHash,head,clean,text,...}`. Explicit `POST /publish` requires `{repo,revision,sourceHash,head}`. Publication authorization is checked against the project's repository membership and current activity on the server; private drafts are not automatic execution instructions.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Versioned project conventions"},{"group":"Your workspace","id":"checkpoint-review","source":"docs/checkpoint-review.md","body":"# Checkpoints and reversions\n\nOpen Project → Repository tools → History, compare the checkpoint, then preview the inverse commit. Apply only after inspecting the patch; the inverse change remains staged for your own commit.\n\n## Review before reverting\n\nProject → Repository tools → History compares the selected checkpoint with the observed HEAD across\nall changed paths. The patch preview is bounded to 2 MiB; large comparisons are\nexplicitly marked truncated. Both comparison endpoints resolve to immutable commits.\n\nFor an owned idle run worktree, **Preview inverse commit** computes a normal Git\nrevert on a disposable checkout. Conflicting reversions do not change the user's\nworktree. Merge commits require an explicit mainline decision in Git and are refused\nby this interface. Commits outside the worktree's ancestry are also refused.\n\n**Stage reviewed inverse change** recomputes the preview, verifies its fingerprint\nand the observed HEAD, and applies the server-generated inverse patch to a clean\nindex and working copy. Tracked-file hiding flags are refused. An in-process workspace\nlease prevents another Code Flow start, resume or mutation from racing the operation.\nThe change remains staged for review and a normal user commit; history is preserved.\nExisting modifications, unknown files that would be overwritten and stale previews\nprevent application. External programs are outside Code Flow's process coordination;\nGit's index checks still guard the actual application.\n\nThe API operations are `POST /api/v1/workspace/compare-checkpoint`,\n`/preview-revert` and `/apply-revert`. Comparison accepts `repo`, `from`, `to`.\nRevert preview accepts `repo`, `runId`, `commit`, `expectedHead`; apply also requires\nthe returned `fingerprint`. Caller-authored patches are not accepted.\n\nValidation covers complete comparison, staged inverse changes, history preservation,\nconflicts, stale/forged previews, dirty worktrees, hidden tracked-file changes, and\nthe actual browser preview/application flow.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Checkpoints and reversions"},{"group":"Your workspace","id":"launch-profiles","source":"docs/launch-profiles.md","body":"# Service launch profiles\n\nOpen Project → Repository tools → Services to define a named launch profile. Check dependencies and readiness before starting the profile, then use its logs and stop controls to manage the services.\n\n## Start the services you need\n\nCreate `.codeflow/launch.json` in the repository. Project → Repository tools → Services reads the selected run's worktree configuration. It does not execute anything until you click **Start launch profile**. Review commands first: they execute repository code on your computer with your ordinary development environment. Runs from an untrusted external source require an additional explicit confirmation.\n\n```json\n{\n \"version\": 1,\n \"profiles\": [\n {\n \"id\": \"development\",\n \"name\": \"API and web app\",\n \"services\": [\n {\n \"id\": \"api\",\n \"command\": \"npm run dev\",\n \"cwd\": \"packages/api\",\n \"health\": { \"path\": \"/health\", \"timeoutMs\": 30000 }\n },\n {\n \"id\": \"web\",\n \"command\": \"npm run dev -- --host 127.0.0.1 --port $PORT\",\n \"cwd\": \"packages/web\",\n \"dependsOn\": [\"api\"],\n \"env\": { \"VITE_API_URL\": \"${API_URL}\" },\n \"health\": { \"path\": \"/\", \"timeoutMs\": 30000 }\n }\n ]\n }\n ]\n}\n```\n\nEach profile has a unique lowercase ID, a display name, and one to six services. The file supports up to twelve profiles and is limited to 128 KiB. Every service needs a unique lowercase ID and a shell command. `cwd` defaults to `.` and must be a regular directory beneath the worktree; absolute paths, `..`, `.git`, and symbolic links are rejected. Dependencies must name existing services and form an acyclic graph. Commands are launched in dependency order; each dependency must pass its readiness check before dependent commands start.\n\nBefore starting any service, code-flow allocates distinct ports and generates `PORT`, `HOST=127.0.0.1`, and `SERVICE_NAME` for each service, plus `<SERVICE_ID>_URL` and `<SERVICE_ID>_PORT` for every peer. IDs use uppercase environment names with hyphens replaced by underscores. For example `user-api` produces `USER_API_URL=http://127.0.0.1:<allocated port>`. A command must honor `PORT` to use the assigned port; code-flow does not rewrite a command or its configuration. Services should bind to `HOST` to remain on this computer.\n\nOptional `env` values can substitute these generated variables using `${NAME}`. Unknown substitutions and attempts to replace reserved variables are rejected before any command executes. Service environment entries are limited to forty bounded strings. Code-flow's server session and registered integration credentials are excluded from child environments. Ordinary credentials you exported for development remain available to the repository commands.\n\nThe default readiness check requests `/` on the service's assigned `127.0.0.1` port and waits up to 15 seconds for a 2xx response. Custom paths must be local paths; redirects are not followed. Each check is bounded to 100–120,000 ms, and the complete stack has a three-minute startup deadline. Set `health: false` for a background worker without HTTP. This only checks that its process remains running and is displayed as **HTTP readiness not confirmed**; it does not prove application health.\n\nThe panel exposes each service's status, local URL, and its last 100 bounded log lines. **Stop services** cancels pending startup and terminates all process groups in the stack. Failed health checks or process exits stop the other services and preserve recent logs for diagnosis. Only one stack per run/worktree may run at a time; stop it before choosing a different profile. Service state is ephemeral and profiles do not restart automatically after code-flow restarts.\n\nThe API uses `GET /api/runs/:id/launch-profiles?worktree=NAME` for `{profiles,found,error?,session}`, `POST` with `{worktree,profile,allowUntrusted?}` to explicitly start, and `DELETE` with the worktree query to stop. The backend resolves run-owned worktrees; callers cannot supply an arbitrary filesystem root. A start returns a `LaunchSession` after readiness succeeds. Errors retain session logs, retrievable through `GET`.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Service launch profiles"},{"group":"Your workspace","id":"code-intelligence","source":"docs/code-intelligence.md","body":"# Code intelligence\n\nOpen Project → Repository tools → Code, search for a JavaScript or TypeScript symbol, and choose Find references. Save editor changes first: the inspection reads saved files.\n\n## Find a symbol\n\nProject → Repository tools → Code offers text search, simple multi-language declaration search,\nand JavaScript/TypeScript symbols backed by TypeScript's language service.\nSemantic symbol results include a **Find references** action. Imported aliases\nare resolved; ordinary text mentions are not treated as references. Each request\nuses saved repository files, so unsaved editor drafts are excluded.\n\nLanguage-service computation runs in a separate worker with a 10-second deadline and a\n192 MiB heap ceiling, and only one inspection runs at once. The in-memory file\nhost contains at most 500 regular source files and 8 MiB of source. Results stop\nat 200 locations and explicitly report truncation. Credential selections,\nsymbolic links, external libraries, compiler plugins and generated directories\nare excluded. Repository compiler configuration is not executed or loaded; the\ninitial implementation uses a fixed safe compiler configuration and relative\nmodule resolution. Project-specific path aliases may therefore be unresolved.\n\nThe API supports `GET /api/v1/workspace/search?repo=...&mode=semantic&q=...`\nand `GET /api/v1/workspace/references?repo=...&path=...&line=...&column=...`.\nLocations are one-based and columns use UTF-16 code units, matching the language\nservice. Results include the actual language-service version and scope limits.\n\nUnit fixtures check imported aliases, string-mention exclusions, current-content\nrefresh, symlink/credential/plugin boundaries and cancellation. Browser acceptance\nexercises symbol search and cross-file reference navigation against saved JavaScript.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Code intelligence"},{"group":"Your workspace","id":"architecture-explorer","source":"docs/architecture-explorer.md","body":"# Architecture explorer\n\nUse static dependency evidence to investigate how a change may affect other files. Unresolved imports still need interpretation.\n\n## Explore repository structure\n\nOpen **What could this change affect?** while editing a file in Files or Repository tools → Code, or while reviewing a result branch. This inspects saved tracked source without executing repository code, build hooks, compilers or package managers. It resolves JavaScript/TypeScript syntax with TypeScript's parser, including literal dynamic imports and CommonJS requires. Conservative static scans also cover Python imports, Go import groups and local module packages, Rust modules and basic use declarations, and Java class imports.\n\nThe view includes file relationships, directory coupling, dependency cycles, declared npm/Python/Go/Rust/Maven packages, unresolved imports and computed runtime targets. Export the same evidence as JSON. An unresolved import may be an external or standard library package; it is not evidence that a dependency is missing. Runtime loaders, path aliases, Rust macros, Java wildcards, generated source and conditional builds still need manual inspection. No package installation or dependency-tree network lookup occurs.\n\nThe scan considers at most 600 tracked files and 8 MiB of readable source, with 4,000 relationships, 1,000 manifest dependencies, 500 unresolved imports and 200 computed targets. Truncation is visible. Existing symlink and regular-text file checks apply, and credential paths and dependency/build directories are excluded. Untracked and unsaved editor drafts are excluded. Cancellation stops the scan.\n\nThe import rules follow the [Python import system](https://docs.python.org/3/reference/import.html), [Go import declarations](https://go.dev/ref/spec#Import_declarations), and [Rust use declarations](https://doc.rust-lang.org/reference/items/use-declarations.html); these static scans do not reproduce every language's runtime resolution.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Architecture explorer"},{"group":"Your workspace","id":"memory-storage","source":"docs/memory-storage.md","body":"# Project memory\n\nOpen Project memory to search saved guidance, then use Knowledge graph to follow links to files, runs, commits and source memories. Review provenance before applying an entry to new work.\n\n## Explore project knowledge\n\nCode Flow stores private, team and project memory in `CODEFLOW_HOME/memory.sqlite` (normally `~/.codeflow/memory.sqlite`). Node **22.13 or newer** is required for the built-in `node:sqlite` driver. No database server, native npm addon, downloaded extension or extra SSH deployment asset is needed. This replaces the earlier Node 20 memory-storage baseline. SQLite is experimental in Node 22 and may print a startup warning.\n\n## Storage and migration\n\nThe first memory access imports an existing `memory.json`. Import validates the complete legacy document, creates the schema, records every entry including tombstones and provenance, and compares the imported records with the originals before committing. Unsupported/corrupt JSON or database schemas fail closed. IDs, revisions, approvals, operation receipts and feedback are preserved. Passing the old JSON filename to `MemoryStore` resolves to the corresponding SQLite file.\n\nAfter verified import, the original becomes `memory.json.pre-sqlite` with private permissions. It is a temporary rollback copy, retained until the **first mutation that changes a record**. Before writing changed records, Code Flow removes that copy and old JSON temporary snapshots so edits and deletions do not leave previous bodies in application-owned migration files. A failed database transaction can therefore leave the rollback copy removed, while the original database records remain intact. Import finalization can resume after a crash without importing duplicate records. If JSON conflicts with the import or reappears after a mutation, Code Flow reports the conflict rather than choosing a version.\n\nTo roll back before any mutation, stop Code Flow, move `memory.sqlite` aside, and rename `memory.json.pre-sqlite` to `memory.json` before using the older release. After mutations, use an explicit backup/restore workflow; the temporary import copy is no longer a current rollback source. External backups and run transcripts have their own retention.\n\nEach mutation uses `BEGIN IMMEDIATE` and updates changed memory rows, their keyword index and graph relationships in one transaction. Existing permission, exact-revision conflict, approval, provenance and publication checks still run inside that transaction. Connections close after each operation. The database uses full synchronization, DELETE rollback journaling, private file permissions and SQLite secure deletion. Successful deletion removes the entry's text, indexed terms and owned graph edges; descendants keep their own text and provenance references. This is logical/application-file redaction, not a guarantee of erasure from filesystem snapshots or storage hardware.\n\nThe existing 32 MiB logical-document limit, 5,000 live project entries and 512 private/team entries per run remain. Mutations still inspect the bounded collection to enforce provenance and collection-wide rules, but only changed rows are written. Scoped reads use a repository/project index. This change does not claim unbounded scale or measured throughput improvements.\n\nOffline encrypted backup recovers any interrupted SQLite transaction before copying the closed database. Restore retains its memories, indexes and graph. Retention uses database fingerprints and existing revision/provenance checks. A preview of an unmigrated JSON store does not itself migrate it.\n\n## Search and graph\n\nThe `memories` table holds current versioned entries. `memory_terms` is an indexed Unicode keyword table; title and tag matches carry more weight than body matches. It works on Node builds without FTS5. Search is literal token matching, with at most 32 query terms and 512 matching seeds; it does not use embeddings or an external model.\n\nThe graph uses `entities` and `relationships` tables. Every relationship identifies its supplying memory and exact revision. Entities are keyed by repository and project, so identical file names in different projects do not join their graphs.\n\n| Entity or relationship | Source |\n| --- | --- |\n| Memory → applies to → file/path | The entry's explicit `paths` |\n| Memory → learned from → run | Its run ID and `run:` source references |\n| Memory → learned from/applies to → commit | `commit:` evidence and branch applicability |\n| Memory → verified at → commit | Runtime verification metadata |\n| Memory → derived from → memory | Exact `derivedFrom` references |\n\n“Derived from” means the source was in context; it does not assert that its text was copied or its claims are logically proven. The graph does not infer imports, function calls, test coverage or arbitrary relationships from prose. Agent-supplied evidence retains the memory's existing proposal/trust status. Outdated, deleted or inaccessible source-memory revisions are not resolved to current labels.\n\nFor retrieval, Code Flow first filters to accepted, visible, current, branch-applicable and path-applicable memories. Text matches seed a traversal of at most two hops through the recorded relationships (at most 1,000 edges). Connected memories receive a small ranking boost, and the selection manifest explains it. This never broadens access or path scope and cannot use a private or stale memory as a hidden bridge. Existing character and entry budgets still apply; replay resolves the exact recorded revisions rather than running search again.\n\nExpand **Project memory → Knowledge graph** to filter connections and inspect their source revisions. The inspector includes proposals for review; it does not approve them. `GET /api/projects/:id/memory/graph?repo=<absolute-path>` exposes the same bounded, authenticated project view and reports truncation.\n\nRun journals, settings and the older recurring-review lessons under `learnings/` retain their existing formats. SQLite is used for the structured agent-memory system; this is not a migration of all application state.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Project memory"},{"group":"Your workspace","id":"memory-claims","source":"docs/memory-claims.md","body":"# Semantic claim review\n\nOpen Project memory → Semantic claim review. Compare the cited revisions and applicability before deciding whether to edit either entry; findings do not change memory automatically.\n\n## Review conflicting guidance\n\nProject memory → **Advanced memory tools** → **Semantic claim review** compares supported facts with overlapping applicability. It identifies equivalent claims and potential contradictions with the exact source entry revisions, character offsets, evidence snippets and normalized values. Findings are suggestions for human review. Analysis never selects a winner, changes status, combines text or publishes guidance automatically.\n\nThe local grammar recognizes standalone policy instructions, including a small documented action vocabulary and policy polarity: “Always permit guest requests” is equivalent to “Do not block guest requests” and conflicts with “Never allow guest requests”. Setting statements normalize numeric dimensions: “Set cache TTL to 5 minutes” equals “Cache time-to-live is 300 seconds”. Numeric constraints conflict only when their intervals are disjoint, including exclusive `<` and `>` boundaries. Overlapping ranges are not treated as contradictions. Time and byte dimensions are never compared with each other or with counts.\n\nConditional or compound prose is deliberately not flattened into unconditional advice. Statements containing conditions, alternatives, conjunctions or multiple sentences are unsupported by this grammar. Unsupported prose is counted, and a report with no findings does not prove consistency. This is a bounded deterministic semantic interpretation, not unrestricted natural-language entailment, embeddings or an external model.\n\nFor precise facts, edit a knowledge entry to add a fenced JSON array:\n\n````markdown\n```codeflow-claims\n[\n {\"subject\":\"guest requests\",\"predicate\":\"allow\",\"value\":false},\n {\"subject\":\"package manager\",\"predicate\":\"setting\",\"value\":\"pnpm\"},\n {\"subject\":\"cache ttl\",\"predicate\":\"setting\",\"min\":5,\"max\":10,\"unit\":\"minutes\"}\n]\n```\n````\n\nA fact has a subject (up to 120 characters), a lowercase predicate (up to 40 characters), and either a boolean/string/number `value`, or numeric `min`/`max` bounds. Unknown fields, invalid units and mixed scalar/range values are rejected as unsupported. `min` and `max` are inclusive. Units accept counts, milliseconds/seconds/minutes/hours, bytes, decimal KB/MB/GB and binary KiB/MiB/GiB. A missing numeric unit means count. Different explicit string settings for the same subject/predicate are a potential conflict; the analyzer does not infer application-specific aliases or which configuration is appropriate.\n\nSubjects use Unicode normalization, case/spacing normalization, a small noun normalization vocabulary, and the `time-to-live`/`TTL` alias. Policy action aliases include use/utilize/employ, allow/permit/accept, deny/block/disallow/forbid/reject, require/enforce, cache/memoize, validate/verify and log/record. These aliases remain review hints: context outside the supported statement can still matter. `enable` and `allow` remain different predicates. Structured boolean predicates can use the same aliases; other explicit predicates are compared exactly.\n\nComparisons stay inside the already authorized repository and project. Private/deleted entries are excluded. Paths must intersect by exact path or directory ancestry; repository-wide entries overlap every path. Two branch-specific entries must name the same branch, and conflicting exact commit applicability is excluded. Source revision metadata is retained. Opposite supported facts inside one entry can also be flagged; repeated equivalent facts inside one entry are not listed as duplicate entries.\n\nLimits: 500 entries; 20,000 source characters per body; 12 claims per entry; 1,000 claims total; 50,000 candidate comparisons; 100 findings; 512-character evidence snippets; 8 KiB-character structured blocks. Offsets refer to UTF-16 positions in the source body, as used by the browser editor. Every truncation and unsupported fact is reported. The feature runs locally without provider quota, network downloads, shell execution or automatic memory mutation.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Semantic claim review"},{"group":"Your workspace","id":"coordinated-changes","source":"docs/coordinated-changes.md","body":"# Coordinated changes\n\nUse coordinated changes when one repository depends on another repository’s result. Pin the dependency inputs, inspect each repository’s verification, and review publishing separately for each result.\n\n## Plan across repositories\n\nOpen **Project → Repository tools → Coordinated changes**, choose a project containing 2–10 local Git repositories and create one task per repository. Select a provider and verification command for each task, and mark its explicit dependencies. Commands left blank use the repository configuration. The graph must be acyclic and every repository must be distinct. This is local coordination; it does not clone remote repositories or supply credentials.\n\nCreation pins each authorized canonical repository, its Git metadata ownership and starting commit. Linked worktrees sharing one underlying repository cannot masquerade as separate shipping targets. Replaced repository metadata blocks dispatch and result validation. Tasks execute in separate managed workspaces with safe trust, disabled native approval waits, no inherited uploads or parent-run sessions, and no copied ignored files. The interface defaults to 20 agent invocations and 15 minutes per task. The service caps each task at 50 calls, 60 minutes, two million tokens and $100 API-equivalent usage, with defaults of 500,000 tokens and $25. These are usage limits, not subscription charges. Existing external-source trust restrictions continue to apply at launch.\n\nA downstream task starts only after all dependencies have current successful verification tied to their exact owned result branches. Its server-created launch claim records factual dependency receipts: repository, run ID, branch, immutable commit and verification identity. It does not turn sibling agent output into trusted instructions, expose sibling credentials, or mount sibling repositories. No broad external-source execution exemption is inherited.\n\nEvery launch claim is saved, synced and atomically renamed before starting an agent. The engine must save and sync the initial run's exact `{jobId,nodeId,claimId,attempt}` coordination identity before returning a receipt. A crash, timeout or ordinary callback failure after claiming produces an unknown outcome. It is never automatically retried. Link only a run whose persisted identity matches the exact claim, or acknowledge that an explicit retry can duplicate work. Each task permits at most three explicit attempts. Retrying invalidates the selected task and all downstream results and receipts; active downstream work or shipping must first be stopped or resolved.\n\nThe status panel shows dependencies, pinned bases, current run links, verified results, errors, exact unknown claims and bounded operation history. Cancelling stops pending dispatch and sends cancellation to known runs. A run receipt arriving after cancellation is stopped as well. Cancellation cannot undo commits already merged by an explicit shipping operation.\n\n## Review and ship\n\nEvery task must have a current verified immutable result before shipping review. Choose a local target branch per repository and select **Validate and preview shipping matrix**. Actual merge-target verification records source commit, current target commit and exact merged tree. The matrix expires after 30 minutes.\n\nReview each matrix entry and deliberately merge repositories one at a time. Before each action, the coordinator checks every owned result and its dependency receipts, verifies the selected source/target/tree again, and saves a durable shipping claim. The engine's merge operation must enforce the reviewed source/target/tree with compare-and-swap. A changed result, target, tree, dependency receipt or expired preview refuses shipping before the merge begins. This flow never automatically pushes or creates remote pull requests.\n\nGit repositories do not share an atomic transaction. A successful merge in one repository remains merged if another fails; the status and history show partial progress. An ambiguous shipping receipt becomes `shipping-unknown` and cannot be blindly retried. Manual recovery must prove the exact reviewed source/target/tree using the resulting local commit and engine-owned merge receipt. Do not reset unrelated commits to simulate an atomic rollback.\n\n## Persistence and recovery\n\n`CODEFLOW_HOME/coordinated-changes.json` stores revisioned jobs, sanitized task configuration, pinned commits and bounded histories in owner-only files. It can include user-entered task text and command strings; keep credentials in environment references rather than task text. The service permits ten active jobs, twenty retained jobs, ten repositories per job, one hundred history entries per job and a 32 MiB storage limit. Incomplete or invalid storage fails closed and is left untouched.\n\nNormal restart converts claimed launches and shipping operations to unknown outcomes and does not repeat them. Verified dependency facts are checked again before further work. Encrypted backup restore disables all implicit coordinated dispatch while retaining claim and shipping metadata for investigation. Review restored jobs explicitly before any retry. Private remote-worker capability receipts remain excluded from backups and restore.\n\nThe versioned authenticated HTTP interface uses `/api/coordinated-changes`: create/list jobs, cancel, explicitly retry tasks, link exact unknown runs, preview a shipping matrix, merge one repository and recover a proven unknown shipping receipt. Mutating existing jobs requires their current revision. The coordinator's callback interface is defined in `src/repo-coordination.ts`; callbacks must supply actual authorization, durable engine receipts, verification evidence and compare-and-swap merging. A callback's success is not synthetic verification evidence.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Coordinated changes"},{"group":"Connect and collaborate","id":"team-collaboration","source":"docs/team-collaboration.md","body":"# Team collaboration\n\nConnect each teammate’s computer as a runner for its local repositories and CLI logins. Use explicit host-execution or terminal grants only when teammates should work on the team server itself.\n\nProject → More → **Board** contains tasks on the project’s owning computer. Project → More → **People** contains members, roles and repository keys; the owner also gets existing account and execution/terminal grant controls. Configured boards become project tabs. The optional **Show Board in the app menu** preference only controls an unused shortcut: it never hides configured collaboration or its settings. Command search includes Board and People, with Team as a search alias.\n\nLinked servers remain available through the connection-scoped **Board** entry and its **Team connection** selector. A linked project is not attached to a local project by a matching ID, name or repository path. The URL’s `team` query pins requests, downloads and the stream to that connection. Opening a local project’s Board or People switches the document to `team=local`; previous requests and subscriptions cannot become actions against the new source. Missing projects show an unavailable state instead of another board. Shared users see Board and Results plus People from their authorized board projection, including projects with repository keys but no server clone.\n\n## Start with what you want to connect\n\nOpen **Settings → Connections → Share access** and choose:\n\n| Your goal | Choose | Next step |\n| --- | --- | --- |\n| Continue your work on a phone or tablet | **Connect my devices** | Choose Wi-Fi or Tailscale, configure HTTPS for full control, then scan your private device QR code. |\n| Join someone else’s project | **Join a team** | Open their invitation, choose your own account, then connect your own CodeFlow when you want to run tasks. |\n| Give people access to a project | **Host a team** | Choose this computer or an SSH server, configure repositories and secure access, then invite teammates. |\n\n**Manage existing connections and access** retains the account, role, device, runner and execution-grant settings. Old Settings → Team and Remote access bookmarks still open the appropriate settings.\n\n## Invite and join a project\n\nThe server owner opens **Project → People → Invite a teammate**, or the **Teammates** step in team-server setup. Choose a project and role, then **Create invitation**. Copy the link or let the recipient scan its QR code. Each link admits one person, expires after 24 hours, and can be revoked from **Pending invitations**. Only the token hash is saved; copy the link before leaving the page. Share it privately with its intended recipient.\n\nInvitation creation in the app becomes available after secure network access is configured. The link uses the HTTPS server origin, without the owner's phone-pairing capability. The recipient must be able to reach that origin over the same Wi-Fi or permitted Tailscale network.\n\nThe recipient opens the invitation, checks the project and role, then creates their own account with a password of at least 15 characters or signs in with an existing shared account. Acceptance creates the account/membership and browser session atomically. Existing accounts keep other project roles and any stronger role already held in this project. Used, expired and revoked invitations explain how to request a replacement. A removed project cannot be joined.\n\nAfter joining, **Open project board** opens the invited project immediately. Contributors and maintainers also see a credential-free **connection link**: paste it in their own CodeFlow under **Join a team**, sign in, and choose a local clone using **Choose folder**. The project checklist shows which folders still need mapping and provides a board link for each project. The shared dashboard keeps **Connect your computer to run team tasks** available later.\n\nA project invitation grants only its project role. It never grants the owner's access, a terminal, or permission to run agents on the server. Those require the existing separate grants. The browser can collaborate on tasks and reviews; a connected computer runs the work its owner starts with their own agent logins. Personal device pairing remains a separate flow.\n\n## Choose where work runs\n\nTeam collaboration lets the people who work on a project share one **team board** of tasks and hand work between their computers. One ordinary code-flow instance acts as the **team server**: teammates reach it over HTTPS through Tailscale or another network and sign in with the existing [shared project accounts](shared-projects.md). Each teammate's own code-flow installation can be linked to that server as a **runner** (\"this computer\"): it executes board tasks locally, in a Git worktree, with that person's own agent CLIs and logins; publishes bounded progress and a final **hand-over checkpoint** (a verified Git bundle plus structured context) to the board; and can hand the task over to a teammate, who continues from the checkpoint with any agent. Reviews attach to checkpoint commits.\n\nThe team server never commands another machine. In the linked-runner workflow every connection is made outbound, from a teammate's computer to the server; execution starts from that computer's own code-flow, by its owner; and nothing on the server can start, steer or stop a run elsewhere. The server holds the board, the checkpoints and who is doing what. The work happens where the agents and their logins already are.\n\nVocabulary in this document: *team* and *teammates* are people, not the agents' [Auto team](auto-team.md); a *board task* is an entry on the team board, not an Auto-team task; a *hand-over checkpoint* is a published Git bundle, not one of the [commit checkpoints](checkpoint-review.md) under **Project → Repository tools → History**; a *runner* is a teammate's own code-flow, not an [SSH worker](remote-workers.md). In the code and the tests the feature is called `collab`.\n\nDeliberately absent in this version: live pairing in the same terminal; moving a provider's live session between machines; automatic merging of combined results (fetching checkpoints into local branches and the existing merge and verification flows is the integration path); issue-tracker links; the server pushing anything to teammates' machines. Opt-in host tasks and individual interactive terminals for shared users are described below.\n\n## Set up the team server\n\nOpen **Settings → Connections → Share access → Host a team** for the guided setup. Choose **This computer** or a saved SSH server under **Server**. Setup targets that server’s own board, independently of the team selected in your browser tab. For an SSH server, choose the HTTPS port and click **Set up on this server**. Disconnected saved servers offer **Connect and set up on this server**. Then complete the five steps:\n\n1. Choose an existing project or create one, and set your name on the board. A local clone on the server is optional.\n2. Define the shared repository keys, labels, Git URLs and default branches.\n3. Choose Tailscale or another HTTPS network, enable team access, and create or import a trusted certificate using the existing secure-access controls. For another network, you manage the hostname and reachability. The wizard installs a separate team server on remote machines; the managed SSH workspace keeps its network listeners disabled.\n4. Create one-use project invitations so teammates choose their own credentials. Direct account creation and granting existing accounts access remain available. Other project roles are preserved.\n5. Review the saved configuration and copy connection instructions containing the HTTPS server origin. Passwords and phone pairing links are never included.\n\nSaved steps remain if you close and reopen setup. You can defer secure access or accounts; the review then identifies the unfinished steps instead of reporting the server as ready. Keep the host running and ask a teammate to confirm they can connect through your network’s access rules. The individual settings below remain available.\n\nFor SSH hosting, the wizard uses the saved SSH connection and the bundled Code Flow release; no manual checkout or npm install is needed. The existing SSH requirements (Node.js 20+ and Git on the login PATH) apply. Installation lives under `~/.codeflow/team-server` on the remote machine, separate from `~/.codeflow/ssh-host`. Projects, accounts, certificates and team data live in `team-server/state`; the private owner token and process identity live in `team-server/runtime.json`. Setup forwards only the project, account, repository and secure-access operations it needs, through the authenticated SSH channel. The browser never receives the remote owner token.\n\nReopening setup reuses a running server and its port, including after disconnecting and reconnecting SSH. Disconnecting SSH or closing the local app leaves the team server running. **Stop team server** in the wizard stops an idle server and keeps its data; starting setup again restarts it. A running older release must be stopped explicitly before setup installs the current release. For Linux servers with systemd, choose **Enable automatic startup** after setup. Code Flow installs `~/.config/systemd/user/codeflow-team-server.service` and verifies both service enablement and [user lingering](https://www.freedesktop.org/software/systemd/man/latest/loginctl.html), which starts the user service manager at boot without an SSH login. If server policy blocks enabling linger, setup shows the administrator command and does not claim startup is ready. **Check startup status** rechecks the actual server configuration; **Disable automatic startup** disables boot startup without stopping the current server or disabling other user services.\n\nAutomatic startup uses the installed release, saved HTTPS port and existing data. Its foreground supervisor restarts on failures under systemd, forwards shutdown to the server, and never prints the owner credential. Enabling startup does not interrupt or duplicate a server that is already running. **Stop team server** stops the current process but leaves startup enabled for the next reboot; disable startup as well to keep it off across reboots. Non-Linux servers and systems without a working systemd user manager report that automatic startup is unavailable; you can still start them from setup. DNS, firewall permissions and certificate trust still need to allow teammates to reach the chosen HTTPS port. Create an account for yourself too if you want to add this server as a team connection on your own computer.\n\nThe team server is a normal code-flow instance with nothing extra installed. Its owner is a maintainer of every project on the board and executes board tasks in-process, on that computer, like any other run.\n\n**Network.** Turn on **Settings → Connections → Share access**, choose **Tailscale · access anywhere** and choose **Set up secure remote access** to configure HTTPS, as described in [Remote access with Tailscale](automations.md#remote-access-with-tailscale). Teammates sign in and runners enrol through that network listener; both are accepted only over HTTPS or a direct loopback connection, so the HTTP read-only link is not enough, and the listener refuses connections from the server computer itself. Allow the remote port (normally 4318) for your teammates' devices in your tailnet's [access rules](https://tailscale.com/docs/features/access-control/grants), and issue the certificate for this computer's tailnet name with [Tailscale HTTPS certificates](https://tailscale.com/docs/how-to/set-up-https-certificates); the link teammates use carries that name once TLS is configured.\n\nAlternatively, choose **Other HTTPS network** in the wizard and import a trusted certificate for your server hostname. Configure DNS and network access to the remote port yourself; Tailscale is optional.\n\n**Accounts.** Create an account for each teammate under **Settings → Connections → Share access → People** and assign a role per project. Each role includes everything the roles before it may do:\n\n| Role | On the team board |\n|---|---|\n| **viewer** | reads the board, tasks, checkpoint manifests and discussion, and their own inbox |\n| **reviewer** | also comments and @mentions teammates, and records a review (approve or request changes) against a checkpoint |\n| **contributor** | also creates and edits tasks, takes an unowned task, adds decisions, requests a review from a named teammate, proposes **Send to agent** instructions, starts, stops and hands over attempts on their own tasks, accepts hand-overs offered to them, downloads checkpoint bundles and connects their computer as a runner |\n| **maintainer** | also assigns any owner, deletes tasks and checkpoints, defines a project's repositories and cancels any hand-over |\n\n**Repositories.** Under **Settings → Connections → Share access → People → Team server**, give each project its **repository keys**: a short key (up to 64 lowercase letters, digits or dashes, starting with a letter), a label and, for information only, the remote URL and default branch. A board task names its repository by key, every teammate maps the key to their own clone, and no local path ever reaches the server. The same card sets the owner's display name as teammates see it (`@owner` in a comment reaches the owner), links to the accounts page and, once remote access with HTTPS is on, shows the server's `https://` address under **Address**, without any path: that is what teammates enter when they connect their computers. Never hand out the phone link from **Settings → Connections → Share access** instead; its path is a secret. With remote access on but no TLS, the card says so and points to secure access setup in Settings.\n\n## Let teammates run tasks on the host\n\nThe host owner can enable **Settings → Connections → Share access → People → Run teammates’ tasks on this computer** per project. First define repository keys and map them to canonical repository folders belonging to that project. Choose the installed CLI (Codex, Claude Code or Antigravity), Safe or Standard agent permissions, time and step budgets, and the individual contributors or maintainers allowed to execute. A project role alone never grants host execution. Check that the provider account or billing arrangement supports your intended team usage; Code Flow launches the installed CLI with its existing login and does not transfer credentials.\n\nAn authorized teammate signs into the shared dashboard, takes a task, and chooses **Run on host computer**. The host runs a single-agent workflow in a Git worktree with the owner-selected CLI, starting from the latest checkpoint when one exists. The browser may close after queuing. Progress, requester attribution and downloadable checkpoint bundles appear on the task. Teammates can download the bundle or use their linked Code Flow installation to fetch it into their own checkout. Files and test commands execute on the host, not the teammate’s computer. Agent approval requests remain with the host owner.\n\nThe **Host queue** shows pending and active tasks in projects the viewer can access. The owner sets 1–4 concurrent host tasks (default 1); a FIFO queue admits at most 50 pending/active requests, and repeated clicks return the existing request. Requesters may remove or stop their own jobs; the host owner may stop any job. Other project members can view progress but cannot cancel someone else’s job. Existing local-runner and owner runs are outside this queue’s concurrency limit.\n\nQueued work and grants are stored privately in `CODEFLOW_HOME/host-execution/queue.json`. The queue retains up to 200 recent completed records alongside active requests. Restart recovery reconciles already-started attempts without replaying them; ambiguous starts become interrupted and may require recovery on the task. A queued request fails if its task content, owner, repository mapping or execution policy changes before launch. Account, project-role and execution-grant revocation are rechecked before dispatch and on the queue’s two-second activity pass; active affected runs are stopped. Changed policies require submitting a new request. Checkpoint publication finishes before capacity is released.\n\nHost execution uses this computer’s own board and repository mappings, independently of saved remote team connections. It is unavailable while filesystem restrictions are active. The agent-task grant does not grant an interactive terminal, a raw shell-command endpoint, arbitrary paths, CLI arguments, or owner-level run controls. However, the agents and repository checks can execute commands with the host’s existing authority. Git worktrees and project grants are not operating-system isolation: grant this feature only to people and projects trusted with that execution authority.\n\nThe closed API surface is `GET /api/host-execution`, owner-only `PUT /api/host-execution/settings {revision, policy?, concurrency?}`, `POST /api/host-execution/tasks/:taskId {revision}` and `POST /api/host-execution/jobs/:jobId/cancel {}`. Browser mutations require same-origin JSON. Runner bearer credentials cannot reach these routes. Shared queue projections omit repository paths, execution configuration and other users’ grants.\n\n## Give trusted teammates an interactive terminal\n\nUnder **Settings → Connections → Share access → People → Teammate terminals**, enable a project and select individual contributors or maintainers. This is a separate, default-off grant from agent tasks. Define the project's repository keys and map them to canonical local folders first. Python 3 PTY support is required on the host; terminals use this computer’s own board and repository mappings and are unavailable while filesystem restrictions are active.\n\nA permitted teammate opens **Board → Your host terminals**, selects a starting repository and chooses **Open terminal**. Typing, pasting, Ctrl-C, full-screen CLI programs and window resizing use a real pseudo-terminal. They can use their own browser on their own computer, but commands, files and installed CLIs run on the host computer. They can reconnect to the same session after refreshing the page. These terminals run independently of the agent-task queue and are not attached to task worktrees or checkpoints.\n\n**This grants trusted access to the host's operating-system account.** A project controls who may open a shell and its starting folder; it does not restrict what commands, files, network resources or credentials that shell can reach. Shell users can edit files, start processes and use installed CLI logins. Separate sessions and application-level ownership are not isolation between people who share an OS account. Use only for teammates trusted with that authority.\n\nEach teammate may have two terminals, with eight total on the host. Only the creator can read or type into a session through Code Flow. The owner sees session metadata and can close any session from Settings. Closing the browser or signing out disconnects the view but leaves the shell available until closed or timed out. After 30 minutes without keyboard input the shell closes, including any foreground command; receiving output or resizing does not reset that timer. Permission, account revision, role, repository mapping and host availability changes reject further input and close affected sessions on the next one-second cleanup pass. Code Flow shutdown closes sessions, and restart never restores them. Closing a terminal does not undo changes or guarantee termination of detached processes.\n\nGrants and up to 500 open/close records are saved privately in `CODEFLOW_HOME/terminal-access.json`; settings show the most recent activity. Command text and output are not written to that audit, although shell history and CLI programs can write their own files. A bounded in-memory output buffer supports reconnects. Terminal output is raw and can contain host paths and secrets; it is not a sanitized shared-review projection.\n\nThe dedicated API is `GET/POST /api/team-terminals`, owner-only `PUT /api/team-terminals/settings {revision, grant}`, `DELETE /api/team-terminals/:id {}`, `GET /api/team-terminals/:id/events`, and `POST /api/team-terminals/:id/input` or `/resize`. Mutations require same-origin JSON. Streams recheck the browser session and terminal access on every frame and once per second, without renewing an idle login. Runner bearer credentials cannot reach these routes, and generic owner shell APIs remain unavailable to shared users.\n\n## Connect your own computer\n\nOpen **Settings → Connections → Share access → People → This computer** in your own code-flow, enter the server's `https://` address (the **Address** its owner sees under **Settings → Connections → Share access → People → Team server**), your account's username and password, and a name for this machine (your hostname by default), and choose **Connect this computer**. Your code-flow sends the credentials once to the server's enrolment route, which verifies them exactly like sign-in (same rate limits, same HTTPS-or-loopback rule) without creating a browser session, requires a contributor role in at least one project, and answers with a per-machine **runner credential**, returned once and kept in the link. Plain `http://` is accepted only for loopback addresses. The credentials, and later the runner credential, go only to the address you entered: a server that redirects the request is refused with 502 \"The team server at `<url>` redirected the request; code-flow talks only to the address you entered, so enter the address the server itself answers at\", and a certificate this computer does not trust with 502 \"This computer does not trust the team server's certificate at `<url>` (…)\": give the server a Tailscale HTTPS certificate for its tailnet name, or trust its certificate on this computer with `NODE_EXTRA_CA_CERTS`. Up to 20 team servers can be saved at once. Your own board remains available even when it holds tasks. Adding the same server twice is refused; choose its saved connection instead. The team server and every linked computer must run the same code-flow version: the checkpoint protocol is not negotiated between versions, and an older team server refuses a newer computer's checkpoint manifests (400, unknown field `id`).\n\nUse **Add team connection** in **Settings → Connections → Share access → People → This computer** for another team. The **Team connection** selector in Settings and on the board switches between saved servers and **This computer’s own board**. Each server supplies its own projects and account roles; map repositories after choosing that server. Projects and repository keys that happen to have the same IDs on two servers remain separate. Switching leaves all connections and ongoing runs active. Each browser tab keeps its selection in its URL (`?team=<connection id>`), so bookmarked task links and tabs for different teams retain their destination.\n\nConnections are stored under `CODEFLOW_HOME/team-connections/<connection id>.json` (mode 0600), with separate credentials, repository mappings and defaults. Passwords are exchanged once for a runner credential and never saved. The old single connection in `team-link.json` is automatically moved to `team-connections/legacy.json`; its runner identity and existing attempt journal are preserved. An unlinked `team-link.json` still holds the local board's mappings.\n\nUnder **Settings → Connections → Share access → People → Repositories**, map each repository key of each project to a clone on this computer (**Use current repository** fills in the repository you have open). A task whose key is unmapped asks you to map it before it can start. Each saved connection has its own event stream and runner, so progress, checkpoints and recovery always go to the team that owns the task, regardless of the visible team.\n\n**Disconnect this team** removes only the selected connection and revokes its runner credential (best effort). Stop or reconcile active/unknown attempts and settle pending checkpoints first. Its old mappings and local attempt records remain on disk for inspection; a new connection gets its own mappings and runner identity. Other teams and the local board keep working.\n\nWhat never leaves this computer: your agent CLI logins, the files `copy` brings into worktrees (`.env` and the like), run transcripts and agent events, local paths and the run's private context. Of the run itself, the server receives only the progress fields and checkpoints described below. Uploaded text is filtered for recognized credentials, environment secrets and local absolute paths before it is truncated. This filtering does not inspect committed Git objects: only commit files intended to be shared with the project. Each teammate runs tasks with their own subscriptions and CLI logins on their own machine, which is the [boundary each vendor draws](security.md#how-code-flow-uses-your-subscriptions).\n\n## The team board and tasks\n\n**Team** in the sidebar (also the command palette entry **Team board**; `#/team`, or `#/team/project/<id>` for one project) shows a column per status, **Planned**, **Working**, **Blocked**, **In review** and **Done**, filtered by project, by owner (**Mine**) and by status, next to your personal inbox. Shared users see the same board under the **Team** tab of their review dashboard. Contributors can run their own tasks on their linked computer, or queue them on the host when its owner has granted execution for the project.\n\nA board task belongs to one project and one repository key and has a title (1–200 characters), a goal (up to 16 KiB) and acceptance criteria (8 KiB), an optional base branch or tag, an owner, up to 20 collaborators, a reviewer, up to 20 tasks it depends on, up to 10 labels and a `revision`. Every save carries the revision it was based on; a stale save fails with 409 \"Task changed; reload before saving\", and the page reloads the task while keeping your draft: the title editor and the blocker form stay open with your text, **Goal** and **Acceptance** keep **Save**, **Discard** and their **Unsaved changes** mark for as long as your text differs from the task (also once the reload shows a teammate's text), and a refused **Depends on** change snaps back to the task's list. A board holds at most 500 tasks, and a project at most 50 repository keys.\n\nStatus rules: any contributor may set any status, except that **Done** is refused (409) while an attempt is claimed, running or unknown or a hand-over is still offered, and **Blocked** requires a blocker text (up to 1000 characters) that is cleared on leaving Blocked. Claiming an attempt moves a Planned, Blocked or In review task to Working on its own, and is refused on a Done task (409 \"Move the task out of Done before starting an attempt\") until someone moves it back; finishing an attempt and accepting a hand-over leave the status alone, so the owner decides when the task goes to review or is done.\n\nOwnership rules: a contributor takes a task nobody owns and gives back a task they own; a maintainer assigns or unassigns anyone; nobody changes the owner while an attempt is claimed, running or unknown on the task (409, naming the runner to stop first). The other way a task changes hands is an accepted hand-over. Either way, attempts the previous owner left paused or stopped become **superseded**: the next attempt starts from a checkpoint, never from one of those worktrees.\n\nAn **attempt** is one execution of a task on one runner, at most 50 per task. It records who ran it where, the exact base commit it started from, the checkpoint it continued from, its run id on that computer, its state (claimed, running, paused, finished, failed, stopped, unknown or superseded), its last progress report and the checkpoint it produced. Only one attempt can hold execution (claimed, running or unknown) on a task at a time, and nothing can be claimed while a hand-over is offered; the same rule refuses a report that would revive a paused, stopped or unknown attempt (409 \"Stop the attempt running on `<runner>` before resuming this one\", or \"Accept, decline or cancel the offered hand-over before resuming the attempt\"); a revive is refused on a Done task as well (409 \"Move the task out of Done before resuming the attempt\"), and for a machine whose user no longer owns the task (403 \"The task now belongs to `<name>`; they continue it from its latest checkpoint, or a project maintainer reassigns it\", or \"The task has no owner now; take it to start a new attempt from its latest checkpoint\"). Once an hour the server marks a claimed or running attempt it has not heard from for 24 hours **unknown**, with a reason.\n\n## Drafting a task with an agent\n\nOn **Team**, choose **Draft with agent**, select a project and repository, describe the idea, and choose an installed agent and the local repository folder. Drafting runs remain owner-only in local history, including when the repository belongs to a shared project. **Generate draft** starts a local read-only run with Safe permissions, a five-minute budget and at most two agent calls. It uses the selected base branch (or the clone's HEAD), skips repository setup/check commands and copied files, and does not create a shared task or claim an execution attempt. The run remains available in run history; **Open drafting run** shows its details and **Stop drafting** cancels it.\n\nReview the proposed title, goal (including scope, implementation plan and questions) and acceptance criteria, then choose **Use draft in form**. Edit those fields and the human owner/reviewer before **Create task**. Saving opens the task, ready for **Build with agents**; creating a task never starts a build automatically. The ordinary **New task** form remains available without an agent. Agent drafting and execution are local-computer controls, unavailable in the shared browser dashboard.\n\n## Running a task on your computer\n\nOpen the task and choose **Build with agents**: you must own the task, and this computer must be linked or be the team server. Pick the repository folder (prefilled from your mapping), workflow, agents and permissions. Single agent suits small changes; Auto team supports a coordinator and multiple worker/reviewer providers. Models and reasoning can be adjusted in the dialog, along with the check command, time budget and agent call budget. Leave the check command blank to use the repository default (workflows requiring a check ask for an explicit command). Select your settings, and tick **Continue from the latest checkpoint** when one exists. Your code-flow then, in order:\n\n1. resolves the base commit: the latest checkpoint's commit when continuing (downloaded and imported first, as described under [Handing a task over](#handing-a-task-over)), otherwise the task's base branch, or `HEAD` of your clone, and checks that the repository can carry checkpoints: one with a submodule, a checkout filter such as Git LFS in `.gitattributes`, or a tree over 20,000 files, 256 MiB or 100 attribute files is refused before anything is claimed (400 \"This repository cannot carry team checkpoints (…); use a repository without submodules or checkout filters for board tasks\");\n2. writes a local attempt record, then **claims** the attempt on the server under a fresh attempt id: a repeated claim with the same id is idempotent, the server refuses while another attempt is claimed, running or unknown or a hand-over is offered, and a continuation must start from the latest checkpoint unless you are a maintainer; an earlier attempt of the task that is stuck on this very computer (claimed, with nothing running behind it) is settled first instead of blocking the start;\n3. starts an ordinary run in a worktree of your clone, on the engine's usual `codeflow/<run id>/<worktree>` branch (shown as the attempt's branch; only fetched checkpoints land on `team/<task id>/…` branches), with a prompt made of the task's title, goal, acceptance criteria and decisions and, when continuing, the checkpoint's context fenced as information; the prompt names the board task, and the run page shows a **Team task** link back to it;\n4. publishes **progress** while the run goes: the current stage, a summary (4 KiB), the run status, up to 100 changed file paths, the last check (its command and whether it passed), the verdict, up to 10 blockers, the branch and head commit, and the step counts. Reports are numbered, so the server ignores one that arrives late and an attempt never moves backwards; publishing is throttled to one report every two seconds and stops when the run ends (the final snapshot, with the head and the changed files, travels with the finish), and a heartbeat every 20 seconds keeps the attempt live and brings back approved instructions;\n5. when the run ends, creates a **checkpoint** if the branch moved past the base (its id is minted here, and the bundle is kept under `CODEFLOW_HOME/team-link/bundles/<checkpoint id>.bundle` until the server has it), uploads it, and finishes the attempt as *finished* (the run is done), *failed*, *stopped* (a budget or limit stop; resumable) or *paused* (cancelled, or interrupted by a restart: resume from this computer). When no checkpoint can be published for good (a repository with checkout filters or submodules, a bundle over 64 MiB, a manifest an older team server refuses), the attempt is still finished on the server, as *failed* when the run was done and otherwise in its paused or stopped state, with the reason \"No checkpoint was published: `<message>`\"; the task is released and the work stays on this computer's branch.\n\nAttempt ids are stable: a restart of your code-flow reconciles its local attempt records with the server instead of starting anything again. A run that is still going is re-attached and the reports the server missed are re-sent; a run that ended is published (an existing checkpoint and its kept bundle are reused); a run that is missing marks the attempt **unknown** with a reason and retains the reservation. Only a journal still at the pre-launch claiming step proves no run was started, allowing that claim to finish as failed automatically. Reconciliation never re-executes anything. A publication that failed is retried at once on reconnect, by a recovery pass that runs every minute with backoff (30 seconds, doubling to 30 minutes), or on demand with `POST /api/team-link/reconcile`; after 48 failed tries, about a day, the retries stop and this computer keeps the run's outcome in its local record. A refusal the server will never lift (401, the credential is gone; 403, this computer was re-linked under another runner id; 409, the attempt is already closed) ends the retries at once, and a bundle kept for a checkpoint that was given up is deleted.\n\nA board card shows a live dot while the server has heard from the runner within the last 90 seconds. After that the task shows **Connection lost; execution status unknown**: the run may still be going on that computer, or may have ended without the server hearing about it. An expired heartbeat never releases execution ownership: starting another attempt, changing the owner and handing over remain blocked. The runner can reconnect and reconcile its state. If it cannot, the task owner or a project maintainer uses **Recover attempt…**, confirms the original agent has stopped (for example, by shutting down that computer), and records how they verified this. Recovery supersedes the old attempt and records the decision in History before releasing the task. A stale recovery is refused if the runner has already reconnected. **Stop** cancels the run (it stays resumable) and records the attempt as paused; **Resume** continues the same attempt under the same id, and is offered for a paused attempt, one stopped at a budget or limit and one marked unknown, when it ran on this computer. The server has the last word: Resume is refused with 409 once the task changed hands (\"The task now belongs to `<name>`; they continue it from its latest checkpoint, or a project maintainer reassigns it\"), on a Done task (\"Move the task out of Done before resuming the attempt\"), when the attempt was superseded or already ended, and while another attempt is claimed, running or unknown or a hand-over is offered; the server reserves execution before the local agent resumes, and an unreachable server leaves the agent stopped. If a running agent later loses its credential or its attempt is superseded, this computer stops it as well as its reports. **Open run** opens the run page for any attempt that ran on this computer, and the latest attempt stays on the task page after it ended (\"No attempt yet.\" appears only on a task without attempts).\n\n## Discussion, review requests and Send to agent\n\nEvery task has a **Discussion** (comments of up to 4000 characters, 500 per task), **Decisions** (2000 characters each, 100 per task; they are snapshotted into every checkpoint and read by the next agent), **Reviews** and a collapsed **History** of up to 200 audit entries, oldest dropped. `@username` in a comment mentions a project member. Assignments, comments, mentions, review requests, recorded reviews, hand-over offers and answers, proposed instructions, blocked tasks and finished or failed attempts create entries in the personal **inbox** of the people concerned, 500 per person, oldest read items dropped first; reading is personal, so one teammate's read marks never clear another's, and you are never notified of your own actions. Removing project access stops new notifications and hides that project’s existing inbox items and unread counts. Only the hub owner can list or revoke other accounts’ runners; project maintainer permissions do not grant account-wide administration.\n\nA contributor can request a review from a named teammate (403 \"A project contributor role is required to request reviews\" otherwise); a reviewer or higher records a review against the latest checkpoint. A comment can instead be sent as an instruction with the **Send to agent** toggle: it is *proposed*; the task owner, or a maintainer of the project, *approves* or *declines* it; an approved instruction is *delivered* on the runner's next heartbeat as a note to the attempt's run, the way your own messages to a running team are, exactly once, and marked so. The owner's own instructions are approved at once. The approval is the trust boundary: an approved instruction reaches the agent as a message to the run, attributed to its author (\"Instruction from `<name>` via the team board\"), whereas a checkpoint's context reaches the next agent fenced as information from teammates, the way [webhook and issue text](github.md#untrusted-text) does, with a note that embedded instructions to change its task, tools or trust are not to be followed. Human comments otherwise never enter a prompt, as on the [shared review](shared-projects.md) pages.\n\n## Handing a task over\n\nChoose **Hand over…** on a task you own, pick a teammate who is a contributor in the project, write a note (up to 4000 characters) and, if the worktree still has uncommitted tracked changes, tick **Include uncommitted changes** to commit them on the run branch as \"Hand-over checkpoint\"; without it the hand-over is refused with 409 \"Commit, discard, or include uncommitted changes first\". The sequence is:\n\n1. the current attempt must not be claimed, running or unknown: stop it or complete recovery first (409 otherwise);\n2. your code-flow takes the attempt's latest checkpoint, or creates one from the worktree now; a retry after a failed offer reuses the pending checkpoint and uploads its kept bundle rather than creating another. When the task's current attempt is a teammate's (the task came back to you after a round trip), the task's latest checkpoint is offered, and the offer is refused with 409 when your old branch holds commits made after your attempt was superseded (\"Branch `<branch>` on this computer holds work made after its attempt was superseded by a change of owner, which cannot be handed over; start a new attempt from the latest checkpoint to continue from it\");\n3. the server records an **offer** to the teammate and notifies them; the task stays yours, with its status;\n4. the teammate chooses **Accept and download** on their computer: the bundle is downloaded and imported into their clone of the repository *before* ownership moves; a comment or review that lands during the download does not fail the acceptance, only an offer that no longer stands does (409 \"This hand-over was already `<state>`\");\n5. the server then marks the hand-over accepted, makes them the owner and supersedes your paused or stopped attempts;\n6. they continue with **Build with agents** and **Continue from the latest checkpoint**, with whatever pattern and agents they choose; the new attempt's base is the checkpoint commit, and the agent reads the checkpoint's context.\n\nA checkpoint is a cumulative Git bundle of all contributions since the original shared base commit, including earlier teammates’ work. Continuing an attempt starts at the prior checkpoint’s head, but the next bundle retains that checkpoint’s original base so a fresh teammate needs only the latest bundle, even after earlier bundles are deleted. The bundle is limited to 64 MiB and has a SHA-256 digest. Its manifest (up to 1.25 MiB) records: the checkpoint id, commit, base commit, branch, your note, the task's goal and acceptance criteria at that moment (16 KiB), the decisions, the last test result, the remaining work (8 KiB) and the changed files. Importing requires the base commit to exist in the recipient's repository; otherwise the import fails with 409 \"Fetch `<base>` into this repository first\" and the recipient fetches it from the repository's normal remote. Imports verify the file's size and digest, that the bundle's only ref names this checkpoint (otherwise 409 \"The downloaded bundle does not belong to checkpoint `<id>`; it advertises `refs/codeflow/team/<other>`. Delete this checkpoint and create a new one from the computer that holds the work\", and the download is removed), the bundle's advertised head, Git's object checks, the ancestry from the base and the tree (no submodules, no checkout filters such as Git LFS), land only in `refs/codeflow/team/<checkpoint id>`, and never touch `HEAD`, branches or the working tree. The checkpoint id is minted on the computer that creates the bundle and travels in the manifest, so the ref inside the bundle, the id on the board, the kept bundle file and the branch a fetch creates all carry the same id.\n\nA failed download or import, a declined offer or a cancelled offer changes nothing on the server: the task stays with its current owner and keeps its checkpoints. The sender or a maintainer can **Cancel hand-over** while it is offered. Repositories with submodules or checkout filters cannot carry checkpoints and so cannot be used for board tasks; **Build with agents** refuses them before anything is claimed.\n\n## Reviews and integration\n\nA review (approve or request changes, with up to 4000 characters) is bound to a checkpoint and its commit. When a newer checkpoint appears, earlier reviews are marked **stale**, and the task's review state (approved, changes requested, pending, none) counts only the latest checkpoint's current reviews, the most recent verdict winning. Requesting a review notifies the reviewer and shows on the card.\n\nTo review or integrate on any computer, choose **Fetch to this computer…** on a checkpoint (contributor or higher, on a linked instance or the team server): the bundle is imported into `refs/codeflow/team/<id>` and the local branch `team/<task id>/<first 8 characters of the checkpoint id>` is pointed at it without being checked out; a branch that is checked out somewhere, or that holds commits the checkpoint lacks, is never moved. From there the existing flows apply: compare, validate and merge the branch as you would any result branch, or open a pull request from it. Merging the results of several tasks together is a manual step in this version; nothing merges on its own.\n\n## Storage, limits and boundaries\n\nOn the team server everything lives under `CODEFLOW_HOME/team/` (mode 0700): `board.json` (each project's repository keys and its tasks with their attempts, checkpoint manifests, discussion, decisions, instructions, reviews, hand-overs and audit), `runners.json` (runner ids, names, last-seen times, the address each last connected from and the SHA-256 hashes of their credentials, never the credentials), `inbox.json` and `checkpoints/<checkpoint id>.bundle` (mode 0600). Files are written whole through a temporary file and renamed; a corrupt file is moved aside to `<name>.corrupt-<time>` and logged, and the board starts empty but writable; a file that cannot be read at all makes team storage unavailable, and every change fails with 503 until it is fixed. Every change is on disk before it is announced on the change streams, with one exception: heartbeats and progress reports on a running attempt (and a runner's last-seen time) are applied in memory and announced at once but reach `board.json` with the next durable change, the 30-second write-behind or shutdown; the first report of a claimed attempt, a resume, a finish and a checkpoint are written at once. At startup the team server removes what an interrupted upload left in `checkpoints/`: every `<id>.bundle.<hex>.tmp`, and every `<id>.bundle` that no checkpoint lists as available (not when `board.json` was set aside as corrupt, so a hand-repaired board keeps its bundles); the removal is logged. On a linked computer, `CODEFLOW_HOME/team-connections/<connection id>.json` holds each link. `CODEFLOW_HOME/team-runners/<connection id>/attempts/<attempt id>.json` ties local runs to attempts, and its sibling `bundles/` holds pending uploads. Local and migrated legacy records stay in `team-link/`, filtered by their hub origin.\n\nBounds: 500 tasks per board; per task 50 attempts, 50 checkpoints, 500 comments, 100 decisions, 50 instructions, 100 reviews, 50 hand-overs of history and 200 audit entries (oldest dropped); 50 repository keys per project; 20 collaborators, 20 dependencies and 10 labels of 40 characters per task; titles of 200 characters, goals of 16 KiB, acceptance criteria of 8 KiB, comments, notes, instructions and reviews of 4000 characters, decisions of 2000, blockers and reasons of 1000; progress summaries of 4 KiB, 100 changed-file paths of 300 characters and 10 blockers of 500 characters; checkpoint bundles of 64 MiB and 200 bundles on disk (when full, the oldest bundle of a Done task is deleted and its manifest kept; when none can be, the upload fails with 507 until checkpoints of finished tasks are deleted); 64 runners, 8 active per person; 500 inbox items per person and 5000 in all; request bodies of 64 KiB for task saves and repository keys, 192 KiB for progress reports, 1.25 MiB for checkpoint manifests (both sized so the character bounds fit in four-byte UTF-8), 64 MiB for bundles, 8 KiB for enrolment and 16 KiB otherwise; 16 MiB per board file, after which every change fails with 413 \"The team board has reached its 16 MiB storage limit; delete finished tasks first\" (deleting a checkpoint keeps its manifest, so only deleting tasks shrinks the board); change streams 4 per person and 128 in all, a stream being closed once 2 MiB of frames wait on its socket (the client reconnects with its cursor). An attempt counts as live for 90 seconds after its last heartbeat, is marked unknown after 24 hours without one, and a runner shows as online for 90 seconds after its last request.\n\nBoundaries: a runner credential is a bearer token (`tr-` and 43 characters, shown once at enrolment) bound to one account and one machine. It reaches only `/api/team/*`: every other API route, the owner's and the shared review routes included, answers 403 \"This credential only reaches the team API\", the pages treat it as an anonymous visitor (it never becomes a session), and cookies sent beside it are ignored; it stops working as soon as the account is disabled or the runner is revoked (401 \"This runner credential is not valid; connect this computer again in Settings → Connections → Share access → People\"); and the server stores only its hash. The owner's own `CODEFLOW_TOKEN` is matched first, so an owner token that happens to have that shape still signs the owner in on the local listener. Runner enrolment is accepted over HTTPS or loopback only, like shared sign-in, and a linked computer refuses to store a plain `http://` server address that is not loopback. The network listener refuses the server computer's own connections, so the server's owner executes in-process rather than through a runner. Tasks are projected per role: a teammate sees only the projects where their account holds a role, people appear as an account id and display name, and no local path, run transcript, token or hash ever leaves the server. The other way round, a linked computer reads at most 64 KiB of an error answer from the server, and its progress, summaries, blockers, test commands, checkpoint text and failure reasons use the existing secret redaction before truncation and have its repository, worktree and home folders and any other absolute path replaced by `<local path>`. Every mutation carries a revision and fails with 409 when stale; a missing role answers 403 naming the role; a task you may not see is 404. Custom [filesystem policies](filesystem-policy.md) pause starting attempts like other app-managed execution.\n\nEncrypted [backups](maintenance.md) include `team/` (the board, the runner registrations with their credential hashes, the inbox and the bundles) and the `team-link/` and `team-runners/` attempt journals, but leave out `team-connections/` and `team-link.json`, which carries this computer's runner credential, like the other credential stores; a restore of any archive skips it too and lists it under `omitted`. A restored computer therefore comes up not linked, without repository mappings or defaults: connect it again under **Settings → Connections → Share access → People** and map its repositories; its restored attempt records are read only to reconcile, never to start anything. The server side holds only credential hashes, and agent CLI logins are not in `CODEFLOW_HOME` at all. Revoke a runner on the team server (**Settings → Connections → Share access → People → Runners**) if a copy of a connection credential file leaves your control.\n\n## API\n\nPaths are absolute; `/api/v1/team/*` and `/api/v1/team-link/*` are aliases. Browser sessions (the owner and shared accounts) and runner credentials (`Authorization: Bearer tr-…`) use the same team routes; the table says who may call. Runner mutations send `content-type: application/json` (the bundle upload `application/octet-stream`) and need no Origin header; browser mutations keep the same-origin JSON requirement of shared accounts. Mutations return the updated task; a stale `revision` fails with 409, a missing role with 403 naming the role, an unknown or invisible id with 404 \"Task not found\". Unknown team routes answer 404 \"Unknown team operation\" and never fall through to owner routes. Progress reports and heartbeats do not change the task revision.\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `POST /api/team/runners/connect` | `username`, `password`, `name` | Anonymous enrolment over HTTPS or loopback: verifies the credentials like sign-in without creating a session, requires a contributor role in some project, and returns 201 with the runner, its credential (shown once), the user and the server's name. |\n| `GET /api/team/me` | none | The caller's identity, project roles and own runners. |\n| `GET /api/team/board` | optional `project` query | The board: the projects where the caller holds a role with their members and repository keys, task cards with the current attempt, latest checkpoint and hand-over, and the unread inbox count (viewer). |\n| `GET /api/team/tasks/:id` | none | The full task (viewer in its project). |\n| `POST /api/team/tasks` | `projectId`, `repository`, `title`, `goal`, `acceptance`, optional `baseRef`, `owner`, `reviewer`, `collaborators`, `dependsOn`, `labels`, `status` | Create a task (contributor); 201. |\n| `PUT /api/team/tasks/:id` | `revision` plus the changed fields | Partial save under the status and ownership rules; `null` clears an optional field. |\n| `DELETE /api/team/tasks/:id` | `revision` | Delete a task (maintainer); `{deleted: true}`. |\n| `POST /api/team/tasks/:id/comments` | `text` | Comment, with `@username` mentions (reviewer). |\n| `POST /api/team/tasks/:id/decisions` | `text` | Record a decision (contributor). |\n| `POST /api/team/tasks/:id/review-request` | `reviewer`, `revision` | Ask a teammate to review (contributor). |\n| `POST /api/team/tasks/:id/reviews` | `checkpointId`, `verdict=approve\\|request-changes`, `text`, `revision` | Record a review against a checkpoint (reviewer). |\n| `POST /api/team/tasks/:id/instructions` | `text` | Propose a **Send to agent** instruction (contributor); the task owner's own are approved at once. |\n| `POST /api/team/tasks/:id/instructions/:iid` | `action=approve\\|decline\\|delivered`, optional `attemptId` | Approve or decline (the task owner or a maintainer); `delivered` is recorded by the attempt's runner. |\n| `POST /api/team/tasks/:id/attempts` | `attemptId`, `runnerId`, `runnerName`, `baseCommit`, optional `fromCheckpointId`, `revision` | Claim an attempt (the task owner through their runner, or the server's owner in-process as runner `local`); idempotent on `attemptId`; 201. Refused (409) while another attempt is claimed, running or unknown, while a hand-over is offered, and on a Done task. |\n| `POST /api/team/attempts/:aid/progress` | `seq`, optional `runId`, the progress fields | Publish a numbered progress snapshot (the attempt's runner); a late number is ignored; 192 KiB. A report that would revive a paused, stopped or unknown attempt is refused (409) while another attempt of the task is claimed, running or unknown or a hand-over is offered, on a Done task (409 \"Move the task out of Done before resuming the attempt\"), and for a machine whose user no longer owns the task (403 \"The task now belongs to `<name>`; they continue it from its latest checkpoint, or a project maintainer reassigns it\"). |\n| `POST /api/team/attempts/:aid/heartbeat` | empty body | Keep the attempt live; returns the approved, undelivered instructions. |\n| `POST /api/team/attempts/:aid/finish` | `state`, optional `head`, `summary`, `reason` | End the attempt as finished, failed, stopped, paused or unknown. |\n| `POST /api/team/attempts/:aid/recover` | `revision`, `reason`, `confirmedStopped=true` | Recover an unknown attempt after confirming its original execution stopped (task owner or project maintainer). Supersedes it, records the decision and releases the task; stale revisions or a reconnected runner return 409. |\n| `POST /api/team/attempts/:aid/checkpoints` | optional `id`, `commit`, `baseCommit`, `branch`, `size`, `sha256`, `note`, `requirements`, `decisions`, optional `testResults`, `remaining`, `changedFiles` | Register a checkpoint manifest with `bundle: \"pending\"` (the attempt's runner); 201; 1.25 MiB. `id` (32 lowercase hexadecimal characters) becomes the checkpoint's id and ref `refs/codeflow/team/<id>`; an id already on the board is refused (409), a malformed one 400, and without one the server generates the id. |\n| `PUT /api/team/checkpoints/:cid/bundle` | `application/octet-stream` body, `x-codeflow-sha256` header | Stream the bundle to disk (the checkpoint's runner); size and digest are verified before the checkpoint becomes available. A refusal (415, 400 on a size or digest mismatch, 409 when already uploaded or deleted, 507 when storage is full, 503 when the file could not be stored) is answered at once and the declared body is then drained, so a retry reads the answer; a body without a content-length (411) or declared over 64 MiB (413) is cut off once the answer is written. |\n| `GET /api/team/checkpoints/:cid` | none | The checkpoint manifest (viewer). |\n| `GET /api/team/checkpoints/:cid/bundle` | none | Download the bundle with `content-length` and `x-codeflow-sha256` (contributor). |\n| `DELETE /api/team/checkpoints/:cid` | none | Delete a checkpoint and its bundle (maintainer); `{deleted: true}`. |\n| `POST /api/team/tasks/:id/handoff` | `to`, `checkpointId`, `note`, `revision` | Offer a hand-over from an available checkpoint of the task (the task owner or a maintainer); refused while an attempt runs. |\n| `POST /api/team/tasks/:id/handoff/:hid` | `action=accept\\|decline\\|cancel`, `revision` | Accept or decline (the recipient); cancel (the sender or a maintainer). |\n| `GET /api/team/inbox` | optional `after` query | The caller's inbox items and unread count. |\n| `POST /api/team/inbox/read` | `ids`, or `all: true` | Mark the caller's items read; returns `{unread}`. |\n| `GET /api/team/runners` | none | The caller's runners; only the hub owner sees all. |\n| `DELETE /api/team/runners/:id` | none | Revoke a runner (its user or the hub owner); `{revoked: true}`. |\n| `PUT /api/team/projects/:id/repositories` | `repositories`, `revision` | Define the project's repository keys (maintainer). |\n| `PUT /api/team/profile` | `displayName` | The owner's display name on the board (owner). |\n| `GET /api/team/events` | optional `after=<epoch:seq>` query | Server-sent events for the caller: `hello` with the epoch and sequence, then `change` events for the board and tasks in the caller's projects and `inbox` unread counts; a cursor the server no longer holds gets `resync`. Closed when the account changes or the runner is revoked, and when 2 MiB of frames wait on the socket. |\n\nThe owner-only routes below live on the local listener of each computer. On a linked computer they drive execution against the team server; on the team server itself they work in-process, and `linked` is false.\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /api/team-link` | none | The link: `linked`, the server address, user, server name, machine name, mappings and defaults, the connection state, the runner as the server reports it, the last sync and the last error. |\n| `POST /api/team-link/connect` | `url`, `username`, `password`, optional `name` | Enrol this computer: `https://` only (plain `http://` for loopback), never following a redirect and only with a trusted certificate (502 otherwise), refused (409) while already connected or while the local board has tasks; saves the link and starts the change stream and reconciliation; 201. |\n| `DELETE /api/team-link` | none | Revoke the runner on the server (best effort) and forget it here; mappings, defaults and local attempt records stay. |\n| `PUT /api/team-link/mappings` | `projectId`, `repository`, `path` | Map a repository key to an absolute folder that is a Git repository. |\n| `POST /api/team-link/draft` | `projectId`, `repository`, `idea`, `agent`, optional `repo`, `baseRef`, `agentSettings` | Start a bounded read-only task-drafting run on this computer; returns `{runId}`; 201. Requires contributor access to the project and an installed agent. Poll the ordinary run endpoint for the validated draft JSON in its completed summary. |\n| `POST /api/team-link/tasks/:id/start` | optional `repo`, `pattern`, `roles`, optional `agentSettings`, `roleSettings`, `trust`, `loop` (`check`, `setup`, `maxMinutes`, `maxSteps`), `fromCheckpointId` | Claim an attempt and start the run here; returns `{attemptId, runId}`; 201. Refused (400) before anything is claimed when the repository cannot carry checkpoints (a submodule, a checkout filter such as Git LFS, or a tree over 20,000 files, 256 MiB or 100 attribute files). |\n| `POST /api/team-link/tasks/:id/stop` | empty body | Cancel the current attempt's run (resumable) and record it as paused. |\n| `POST /api/team-link/tasks/:id/resume` | empty body | Resume this computer's paused, stopped or unknown attempt of the task under the same attempt id; refused (409) when the attempt was superseded or has ended, the task changed hands or is Done, another attempt is busy or a hand-over is offered. |\n| `POST /api/team-link/tasks/:id/handover` | `to`, `note`, optional `includeUncommitted` | Create or reuse the checkpoint (a retry uploads the kept bundle), then offer the hand-over. |\n| `POST /api/team-link/tasks/:id/accept` | `handoffId`, optional `repo` | Download and import the checkpoint, then accept the offer. |\n| `POST /api/team-link/checkpoints/:id/fetch` | `repo` | Import a checkpoint and point `team/<task id>/<first 8 characters of the checkpoint id>` at it; returns `{commit, branch, taskId, created}`. |\n| `GET /api/team-link/attempts` | none | This computer's attempt records: which run belongs to which attempt, and what has been published. |\n| `POST /api/team-link/reconcile` | empty body | Run the recovery pass now: settle claims whose answer was lost and retry unacknowledged finishes; returns `{attached, finished, removed, unknown, retried}`. |\n\nIn the owner's app the browser's own event stream carries the same board changes as a `team` topic, and the Team page also polls every 15 seconds; the shared dashboard's Board tab holds one `/api/team/events` stream instead (its runs stream is closed while the tab is shown), reloads on its frames and when the tab is shown again, and sends no periodic request of its own. Nothing in this API replays automatically: a restart reconciles local attempt records with the server and never starts a run again, and a dropped response is not proof that a mutation did not happen; reload the task before repeating one.\n\n## Validation status\n\n`npx tsx --test test/collab-model.test.ts` covers the validators and bounds, status transitions, ownership rules, mentions, board summaries and the review state. `test/collab-store.test.ts` covers persistence and atomic writes, a corrupt file moved aside, revision conflicts, role enforcement inside the store, claim idempotency and the one-busy-attempt rule, progress ordering and liveness, finish states, checkpoint manifests with bundle storage and digest verification, the hand-over state machine, personal inbox read state, hashed and revoked runner credentials and the bounds. `test/collab-api.test.ts` drives the team API with fake requests: the closed route table, role refusals, a runner that cannot report on another person's attempt, projection privacy, the change stream, the enrolment handshake (bad passwords, viewer-only accounts and insecure transports refused), a refused upload answered over a real socket, and body limits that fit the character bounds in UTF-8. `test/collab-checkpoints.test.ts` creates a bundle from a repository with a base and two commits and imports it into a clone that has the base (ref created, `HEAD` untouched), a clone without it (clear error), with tampered bytes and over the size limit, and checks that the checkpoint context fences every teammate text. `test/collab-runner.test.ts` covers the runner against both hubs: progress and checkpoints, stop and resume with delivered instructions, hand-over and acceptance, reconciliation and the recovery pass, local paths scrubbed from reasons, refused finishes and resumes, a kept bundle retried, repositories that cannot carry checkpoints, the redirect and certificate refusals, bounded error bodies and the quiet-board stream. `test/collab-link.test.ts` is the acceptance test: it spawns a team server and two linked instances with their own homes and ports over loopback HTTP, with the fake agent CLIs, and walks a task from creation through an attempt, a checkpoint, a hand-over to the second teammate, a continuation from the checkpoint, a restart that starts nothing again and a revoked runner, and checks that a viewer session reads the board but cannot create tasks. `test/e2e/team.e2e.ts` opens the board in headless Chrome, defines a repository key under Settings → Connections → Share access → People and maps it, creates a task from the board form, opens it, adds a comment, a decision and a blocker, audits the board, the task page and Settings → Connections → Share access → People with axe in light, dark and phone layouts, and signs a shared contributor in who finds the task under the Board tab and comments without seeing any execution control.\n\nNo real Tailscale network, second physical machine, TLS certificate or real provider login was exercised by these tests; the acceptance test uses loopback HTTP, which enrolment accepts in place of HTTPS. Validate the Tailscale and HTTPS setup on your own tailnet before relying on it.\n\n### Selecting connections through the owner API\n\n`GET /api/team-link/connections` lists saved connection IDs and public connection state, including `local`. `POST /api/team-link/connect` adds a server and returns its ID. Scope `/api/team/*` and `/api/team-link/*` calls with `?teamConnection=<id>` or the `X-Codeflow-Team` header; this selector is consumed locally and is never sent to another server. Downloads use the query form. `DELETE /api/team-link?teamConnection=<id>` removes that connection. An unknown explicit ID returns 404; an omitted selector with multiple remote connections returns 409 rather than choosing a team. With one remote connection, unscoped clients continue to use it; with none, they use the local board. Shared accounts and runner credentials always access the receiving server's own board and cannot select the owner's other connections. The owner event stream uses `team:<connection id>` topics.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Team collaboration"},{"group":"Connect and collaborate","id":"shared-projects","source":"docs/shared-projects.md","body":"# Shared project accounts\n\nCreate a local account and grant roles only for the projects that person should see. Review the role table before allowing contributions or maintenance, and revoke access when it is no longer needed.\n\n## Share review access\n\nThe server owner can create local accounts and assign one of four project roles; each role includes everything the roles before it may do. A **viewer** reads project run results, safe verification metadata, changed-file diffs and sanitized result exports. A **reviewer** also records human file-review coverage and comments in a separate thread anchored to the exact result commit. A **contributor** also owns tasks on the project's team board and runs them on their own computer with their own agents. A **maintainer** also assigns any task owner, defines a project's repository keys, deletes board tasks and checkpoints, revokes runners, cancels hand-overs and approves instructions to agents; accounts and roles stay with the owner. The contributor and maintainer capabilities are described in [Team collaboration](team-collaboration.md). The owner reads and replies to comments through **Changes → Human collaboration comments** on the normal run page. Comments are collaboration records: they never enter an agent's prompt or resume work.\n\nThe owner retains administration and generic run controls. Project roles alone do not allow users to start or resume arbitrary runs on this server, approve agent/tool requests, send agent notes, run checks/setup/services, change repository files, choose Docker images or authentication volumes, use terminals, merge/push/post to GitHub, edit private memory/conventions, configure integrations, manage accounts or inspect global filesystem/settings/quotas. Project assignment is not an operating-system sandbox. An explicit owner grant under Settings → Connections → Share access → People allows a contributor to queue their own team tasks on the host through the dedicated [host execution queue](team-collaboration.md#let-teammates-run-tasks-on-the-host). This delegates host execution authority to trusted teammates; it does not provide an operating-system sandbox. Generic run and shell APIs remain owner-only. A separate [interactive terminal grant](team-collaboration.md#give-trusted-teammates-an-interactive-terminal) lets a trusted contributor open their own shell on the host. That shell has the host OS user’s authority, including file changes and access outside the project; the review API restrictions do not sandbox terminal commands.\n\nThe review dashboard uses independent bootstrap requests so shared users do not load the normal composer's global health/configuration APIs. Lists, searches, cursors, attention items, run events, reconnect snapshots and exports must be filtered by current project membership on the server. Unauthorized object identities are answered as unavailable. The dashboard holds one board/review event stream for the tab it shows (`/api/events` on Results, `/api/team/events` on Board), reloads on its frames and loads the bell's unread count once with the page, keeping it live only while the Board tab is shown; it sends no periodic request of its own, so an idle dashboard does not renew its session by polling. An open interactive terminal has its own authenticated output stream. Removing a repository from a project removes shared access to its runs. An ambiguous legacy run without an explicit project is not shared automatically.\n\nShared review projections omit local paths, run commands, environment/volume configuration, raw agent tool events/logs, session identifiers, capability receipts, context/manifests, notes, memory and private conventions. Result summaries and final output are project-visible and have recognized secret patterns redacted; arbitrary sensitive prose cannot be detected reliably. Credential files and rename sources are omitted from shared diffs and coverage. Coverage describes only shared-reviewable files and reports omitted files requiring owner review. Parent/continuation references are included only when that related run is also authorized. Private conventions drafts are owner-only even when a user is assigned to their project.\n\nShared results bound task text to 24 KiB, summaries to 32 KiB and errors to 4 KiB of UTF-8. Only the latest 200 steps are included, with 8 KiB final-output and 4 KiB error previews per step. Truncation and omitted-step counts are explicit in the API and dashboard. Exports retain these same safe preview limits. Raw transcripts are not shared exports.\n\n## Authentication and storage\n\nAccounts live in private `CODEFLOW_HOME/shared-accounts.json`; the server never returns password hashes or session hashes. Passwords require at least 15 characters and at most 1024 UTF-8 bytes, without normalization or truncation. Built-in scrypt uses a fresh 16-byte salt, a 32-byte key and parameters `N=131072, r=8, p=1`. Verification compares keys in constant time and performs a dummy derivation for unknown accounts. At most two password derivations run concurrently; a full queue is refused. Login attempts have bounded global, socket-address and username rate limits.\n\nSessions use 32 random bytes and store only SHA-256 hashes. They expire after 12 hours or 30 minutes idle; each account retains at most eight sessions. Role/display-name/enabled/password changes and owner revocation invalidate every session for the account. Session activity extends idle time in memory; a restart can expire a session earlier than its last activity. Revoked identities must lose open streams immediately. Corrupt or symbolic account storage disables shared authentication without overwriting the file; the owner's printed recovery-token link remains available.\n\nNetwork sign-in and authenticated shared requests require actual HTTPS, independently of the phone development override. Cookies are HttpOnly and SameSite=Strict, Secure on HTTPS. The server validates Host/Origin and mutation request type. Shared cookies are distinct from owner and phone sessions. An explicit valid owner bearer credential on the local listener takes precedence; otherwise a valid shared cookie takes precedence. The network listener refuses local owner bearer credentials and recovery links; use its separate phone pairing or shared sign-in. Shared sign-in clears the owner browser cookie, and an explicit local owner recovery link clears the shared cookie. Account creation disables first-browser owner claiming.\n\nBackup and restore omit account/session stores, including older archives. Recreate accounts and assign project roles deliberately after restoring. Human collaboration comments can be retained with application data but convey no authentication authority.\n\n## API and enforcement contract\n\n`POST /api/auth/login {username,password}` sets the session cookie. `GET /api/auth/self` returns only the current public identity; `POST /api/auth/logout` revokes the current session. Owner administration uses `GET/POST /api/accounts`, `PUT /api/accounts/:id` and `POST /api/accounts/:id/revoke-sessions`. Every mutation carries the exact current account-state `revision`; stale updates fail with 409 while preserving the existing state. Project IDs must be validated against the current project store before assigning roles.\n\nRun collaboration uses `GET /api/runs/:id/collaboration` and, for reviewers and higher roles, `POST /api/runs/:id/collaboration {branch,expectedHead,text}`. The server authenticates the actor and resolves an owned result branch; the store rejects a changed commit. Comments are bounded to 4000 characters and 200 entries per run, with immutable author/time/commit metadata. File reviews use the existing revision-guarded `POST /api/runs/:id/review-coverage` with authenticated actor audit. User-supplied actor fields never grant identity.\n\n`canonicalSharedPath` checks the original request target before URL normalization, accepts the versioned `/api/v1` alias, and rejects encoded separators, double encoding, dot segments, empty segments and extra ambiguous suffixes. `sharedRoutePolicy` allows only explicitly enumerated method/path pairs; unknown routes default to owner-only. Global authorization must run before all API/workspace handlers, and resource authorization must precede Git/filesystem operations. Public pairing/webhook routes retain separate secret capabilities and do not inherit a shared user's project roles.\n\nThe account, access-policy and comment-store tests cover hash-only storage, modes, exact revisions, role escalation refusal, revocation, expiry, bounded login work, malformed paths/aliases, cross-project membership, projection privacy and durable commit-anchored comments. End-to-end server/browser tests are required before exposing the shared routes.\n\nThe password parameters and authorization model follow the [OWASP Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html), [Authorization Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html) and [Session Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html).\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Shared project accounts"},{"group":"Connect and collaborate","id":"ssh-projects","source":"docs/ssh-projects.md","body":"# SSH projects\n\nAdd a connection in Settings → Connections → Work on another computer, connect to the host and select a repository there. The remote workspace has its own execution environment and installed CLIs.\n\n## Open a remote workspace\n\nOpen an existing repository on a Linux or macOS server and use the complete code-flow workspace there. File browsing and editing, Git, agent sessions, approvals, terminals, worktrees, setup, builds, verification, and development servers all run on that server. Local and connected remote projects appear together in the sidebar. Click a project to work on its computer; the top bar shows the computer name. Everything stays on the main app address, with remote projects under `/computer/<saved-host-id>/`.\n\n## Connect\n\n1. Install Node.js 22.13 or newer and Git on the server. Install and sign in to the agent CLIs you want to use on that server. Python 3 enables interactive terminals; Chrome/Chromium on the server enables snapshots. The server must allow SSH TCP forwarding to its loopback interface.\n2. Configure keys, ports, jump hosts and hostnames in your normal `~/.ssh/config`, for example:\n\n ```sshconfig\n Host devbox\n HostName server.example.com\n User developer\n IdentityFile ~/.ssh/id_ed25519\n Port 22\n ```\n\n3. Run `ssh devbox` in a terminal once to verify its host key and authentication. Check that `node --version` and `git --version` work in the remote login shell. Unlock passphrase-protected keys in your local SSH agent. Password prompts are not supported in the app.\n4. Start code-flow with `npm start`. The normal `npm run build` builds both the UI and portable remote backend. For an existing development server, run `npm run build:host` after code changes.\n5. Open **Settings → Connections → Work on another computer** (or **This computer → Manage connections…** in the top bar). Select an alias or enter `user@hostname` and name it. Optionally enter an **SSH port** and an **Identity file** path on your computer, such as `~/.ssh/id_ed25519`. Leave them blank to inherit your SSH config. Choose **Save connection** to keep the definition for later or **Connect and open** to start the remote workspace.\n6. The folder picker now browses the server. Choose a Git repository and use the app normally. Agent availability and login status refer to the server.\n\nConnecting installs the application backend, without npm installation or administrator privileges, into `~/.codeflow/ssh-host/releases/<version>` on the server. Its projects, runs, settings and logs live in `~/.codeflow/ssh-host/state`. Local provider credentials, repositories, SSH agents and environment variables are not copied. Credentials and tools must be set up independently on the server.\n\n## Reconnect and stop\n\nThe remote backend is detached from SSH. Closing the page, disconnecting, removing a saved connection, or stopping the local app leaves remote work running. Reopen the saved connection to restore projects and runs. Reloading a remote project restores its connection automatically. You can also reopen it from **Settings → Connections → Work on another computer**. Reconnecting does not resubmit actions. After a connection loss during a mutation, inspect the run or files before repeating the action.\n\nSaved connections, including optional ports and key paths, survive local app restarts. Key contents are never stored by code-flow. If opening a newly saved connection fails, it stays in the list so you can retry with **Open** after resolving SSH authentication. Remove and re-add a connection to change its definition. The backend and work survive SSH interruptions, but not a server reboot or an administrator terminating the process. On backend restart, existing code-flow recovery marks interrupted work for explicit resumption. A backend that still has a live PID but fails its authenticated health check is not automatically replaced.\n\n**Disconnect** closes only the SSH channels and preview forwards. **Stop backend** stops an idle backend and is refused while runs, terminals or development services are active. Finish work and close these first. Scheduled work also stops with the backend. On the next connection, the current application build is installed. A running backend is reused during client upgrades so active work is not interrupted; stop it when idle to update it.\n\n## Remote development previews\n\nIn the side panel's Browser, enter the development server's `http://localhost:<port>` URL. Code-flow forwards that remote port over SSH and shows it on an isolated local preview origin, including hot reload WebSockets. Remote app cookies are separate from code-flow's session cookies. Arbitrary external browsing and screenshots continue to use the remote backend's existing capabilities. A remote localhost URL copied directly into a separate local browser tab is not automatically forwarded; use the app's Browser preview.\n\n## Connection boundaries\n\nThe app uses OpenSSH and your existing config, including ProxyJump. It enforces known host keys, noninteractive authentication, no agent or X11 forwarding, no configured port forwarding, and no local SSH commands. SSH command arguments never include project paths or prompts. Both the local app and remote backend bind only to loopback. Backend authentication cannot be claimed by the first browser, and its private token stays between the local gateway and the remote backend.\n\nAll projects use the local app’s owner session and trusted UI. Requests under `/computer/<saved-host-id>/api/` are routed over that computer’s SSH connection; they never fall back to the local backend. Drafts, event streams and popped-out panels are scoped to their computer. Remote API responses are sandboxed and cannot execute scripts on the app origin. Local cookies, bearer tokens and forwarding headers are removed before requests cross SSH; remote response cookies cannot overwrite local sessions. Remote previews retain the existing preview isolation and the remote filesystem-policy gate.\n\nOnly the local app owner can manage SSH connections. Shared-account and phone sessions cannot create SSH connections. Up to eight connections can be open and 32 definitions saved. Connections use the remote OS account's authority, so only connect to accounts and servers you trust.\n\n## Troubleshooting\n\n- **Host key or authentication failed:** run the exact alias with `ssh` in a terminal and resolve the error there. The app never accepts unknown fingerprints automatically.\n- **Node not found:** put Node.js 22.13+ on the remote login shell's PATH. No Node runtime is downloaded automatically.\n- **Connection refused / forwarding prohibited:** check the server's `AllowTcpForwarding`/`PermitOpen` settings and `~/.codeflow/ssh-host/host.log`.\n- **Backend still running but unavailable:** inspect the log and process before stopping it. The app avoids restarting a process that might still be doing work.\n- **Another host setup is running:** wait for it to finish. If setup crashed, verify no bootstrap process remains before removing `~/.codeflow/ssh-host/bootstrap.lock`.\n- **Remote agent unavailable:** install and sign in to its official CLI on the server. The app's health dialog shows that server's tools.\n\nThis feature differs from [SSH workers](remote-workers.md): workers transfer individual agent turns and their commits. SSH projects keep the complete project and application backend on the server.\n\n## Validation\n\n`npx tsx --test test/ssh-projects.test.ts` runs the packaged backend in a separate host home using an injected SSH transport. Browser coverage is in `test/e2e/ssh-projects.e2e.ts`. Run `CODEFLOW_TEST_LOOPBACK_SSH=1 npx tsx --test test/ssh-projects-ssh.test.ts` for the real OpenSSH acceptance test. It uses temporary keys, known hosts and an unprivileged loopback SSH daemon, and never edits personal SSH configuration.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"SSH projects"},{"group":"Connect and collaborate","id":"remote-workers","source":"docs/remote-workers.md","body":"# Remote agent workers\n\nProvision and verify a worker before assigning it work. Keep source and result receipts available so interrupted connections can be reconciled without launching duplicate work.\n\n## Choose an execution target\n\nAn explicitly registered SSH worker runs agent turns in disposable Git worktrees on another machine. The application transfers an exact commit, the turn prompt and regular files from the run’s `.codeflow-context` folder. Official agent CLIs use the worker’s own login. SSH agent forwarding, environment forwarding, port forwarding and automatic host-key acceptance are disabled.\n\nThis is an operator provisioned service: it does not discover hosts, install remote dependencies, copy local provider credentials or automatically fetch repositories from the network. Commands and checks outside agent turns still run where the application server runs. Choose the remote worker in the composer’s advanced controls.\n\n## Provision a worker\n\nUse Node 22.13 or newer, Git and the official CLI for each selected provider. Sign in on the worker itself. A dedicated OS account and disk/process limits are recommended for executable repository content. The worker account needs write access to its own state directory and provisioned repository mappings. A mapping can be a clone or a bare Git repository with an existing commit; it is not an arbitrary path supplied by a run.\n\nBuild the standalone entry point in this checkout:\n\n```sh\nnpm run build:worker\n```\n\nCopy `dist/remote-worker.mjs` to the worker through your normal administration process. Configure an existing SSH host alias with its known host key and noninteractive authentication. Code-flow runs `ssh` with strict host-key checking and never accepts a new fingerprint for you. [OpenSSH’s manual](https://man.openbsd.org/ssh) describes identity selection and forwarding options.\n\nCreate a private worker configuration, for example `/srv/codeflow/worker.json`:\n\n```json\n{\n \"version\": 1,\n \"root\": \"/srv/codeflow/state\",\n \"repos\": { \"application\": \"/srv/codeflow/repos/application.git\" },\n \"providers\": [\"claude\", \"codex\"],\n \"allowedTrust\": [\"safe\", \"standard\"]\n}\n```\n\nRegister it on the application server with `CODEFLOW_REMOTE_WORKERS_FILE=/absolute/remote-workers.json` (or the same JSON in `CODEFLOW_REMOTE_WORKERS_JSON`):\n\n```json\n{\n \"version\": 1,\n \"workers\": [{\n \"id\": \"build-linux\",\n \"name\": \"Linux build worker\",\n \"sshHost\": \"codeflow-linux\",\n \"command\": \"node /srv/codeflow/remote-worker.mjs --config /srv/codeflow/worker.json\",\n \"repoMappings\": [\"application\"]\n }]\n}\n```\n\nThe command is an explicit operator configuration; task content never becomes part of the SSH command. Keep these files outside agent workspaces. Public worker metadata contains names, IDs, mapping names and optional container profile names, without SSH commands, host details, credentials or recovery capabilities.\n\nThe worker view probes the registered channel and installed CLIs. A successful probe does not claim a provider login is valid; the official CLI reports authentication failures when a turn runs. Missing mappings, providers or container profiles fail explicitly. Remote selection never falls back to this computer.\n\n## Exact source and returned changes\n\nThe client pins the clean tracked checkout commit. If the worker already has it, no source upload is needed. Otherwise code-flow creates a Git bundle using an exclusive temporary `refs/codeflow/transfers/<requestId>` reference, deletes that reference with compare-and-swap, and uploads the bounded bundle over SSH. When the worker confirms the repository’s original base is present, the bundle excludes that history. If the bundle is too large, sync the provisioned mapping through your administration process and retry explicitly. No remote URL from task content is fetched.\n\nThe worker verifies the bundle’s sole advertised reference, enables Git object validation on import and creates a detached request worktree. It never checks out or changes the mapping’s main branch. Read turns must preserve `HEAD`, the staged tree and tracked files; changed files are discarded and the turn fails. Write turns return a worker-created commit descended from the exact accepted base. Agent attempts to rewrite checkout history fail.\n\nReturned result bundles have an exact request-owned reference and SHA-256 digest. Before importing, the client verifies the digest, advertised commit, Git object integrity and ancestry, then requires the local worktree’s original commit and clean tracked state. It fast-forwards that owned checkout only; concurrent local changes cause a visible failure. Disposable worktrees and imported temporary references are cleaned after execution. See Git’s [bundle](https://git-scm.com/docs/git-bundle) and [worktree](https://git-scm.com/docs/git-worktree) documentation for those primitives.\n\nGitlinks/submodules and repositories declaring checkout filters such as Git LFS are rejected: a parent Git bundle does not contain their materialized contents. Configured clean, smudge and process filters are suppressed during scoped checkout, staging and result import, so these steps do not secretly execute filters or fetch LFS content. Use local execution for those repositories.\n\nOnly staged reference files are copied as context. Symlinks, special files, `.env` files, private key extensions, traversal and arbitrary host paths are rejected. There are at most 100 regular files, 8 MiB per file and 16 MiB total. Host home directories, agent sessions and host environment variables are not part of the transfer.\n\n## Receipts, cancellation and ambiguous outcomes\n\nEach invocation has a random request ID, a distinct private capability and a run/step/thread scope. Before invoking a provider, the worker exclusively claims the ID, fsyncs its receipt and directories, and reports acceptance. It records heartbeats and terminal results. The private capability is stored in the server’s `remote-worker-private` directory with owner-only file permissions and pinned to the exact configured SSH host, command and container profile. Replacing a worker alias cannot send an old recovery capability to its new target. Run records contain only worker ID, request ID, fingerprint and outcome metadata.\n\nA reconnect inspects the same durable receipt. Reusing an ID never launches another provider process. A broken connection, missing receipt or expired heartbeat becomes **unknown**, even when the provider might already have performed work. There is no automatic execution retry or local fallback for that outcome. Inspect or cancel the request, and deliberately create a new invocation only after assessing its prior effects. Terminal receipts can re-download the validated result bundle without repeating execution. Open the agent step’s transcript and choose **Inspect remote receipt**. **Cancel remote execution** requires an explicit confirmation. These controls never launch a new turn; Resume is a separate owner action after reconciliation.\n\nCancellation uses the stored capability and a separate SSH control channel. The active worker checks the cancellation marker, stops the official CLI and records cancellation. A disconnected channel also aborts the scoped worker process. During a worker crash or lost host, termination cannot be confirmed until the host is inspected; an unknown receipt must not be interpreted as proof that execution stopped.\n\nFour worker execution slots and 100 request directories are retained at most. A crashed worker can leave its slot unresolved. Inspect its recorded PID, request receipt and host processes before manually archiving the terminal request directory and clearing a stale slot file. Code-flow deliberately does not reclaim an ambiguous slot and launch duplicate work. The client keeps at most 2,000 private receipts and 32 downloaded result spools pending import or discard. Metadata-only inspection does not download a result bundle. Encrypted application backups exclude private worker capabilities, so a restored server cannot cancel or recover old remote authority automatically.\n\n## Worker-local containers and boundaries\n\nOptional named `containers` profiles use the same explicit agent-container configuration as local runs. Register a `containerProfile` on the server target to select a worker-local profile. Its image and authentication volume must exist and be provisioned on the worker. Local container images, host credential volumes and home folders are never inferred or copied to the worker. Availability probes run the provider’s version command inside that exact prebuilt image with no network or credential volumes; a host CLI installation is unnecessary for a container profile. The image is never pulled automatically.\n\nRemote turns currently start fresh CLI sessions and use structured question/reply outcomes across turns. Live team tools, mid-turn steering and native interactive approval prompts are unsupported. Every result omits resumable session IDs. Final verification and independent reviews still apply to imported results. The server must enforce run/project access before exposing receipt or cancellation operations.\n\nSource and result transfers are each limited to 64 MiB, with binary Git output capped before a transfer file is written. A source tree has at most 20,000 entries and 256 MiB of file data. Protocol frames are capped at 2 MiB, streamed events at 16 MiB and provider turns at 30 minutes. Uploads have a 30-second deadline. These transfer and application bounds do not replace OS disk quotas or a container sandbox: executable repository commands and Git object decompression can consume additional worker resources. Do not give mutually untrusted users a shared worker OS account or broader SSH credentials than their work requires.\n\n## Validation status\n\nThe automated acceptance fixture launches the standalone worker in a separate process through an injected SSH transport. It exercises the official adapter against a fake CLI, source/context transfer, bundle import, read-only rejection, private capability ownership, restart recovery, dropped transport, cancellation, tampered bundles and changed local heads. No provider quota or external messages are used.\n\nAn additional opt-in acceptance test passed over real encrypted loopback OpenSSH: a temporary unprivileged daemon bound to 127.0.0.1, temporary host/client keys and isolated ssh -F configuration, the bundled worker process, a fake CLI, exact source transfer, result import and receipt recovery. Run it with `CODEFLOW_TEST_LOOPBACK_SSH=1 npx tsx --test test/remote-workers-ssh.test.ts`. It never modifies user SSH configuration or system settings, and reports a skip if unprivileged sshd is unavailable. No external SSH host or live provider login was tested. Validate those on an explicitly configured host before relying on unattended work. Container inventory uses an injected Docker fixture; an actual Docker worker image was not exercised in this acceptance run.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Remote agent workers"},{"group":"Connect and collaborate","id":"code-hosts","source":"docs/code-hosts.md","body":"# GitLab and Bitbucket code workflows\n\nConfigure the GitLab or Bitbucket connection, fetch the change into an isolated review workflow, and inspect the pinned result before commenting, pushing or opening a pull request.\n\n## Review on another code host\n\nSettings → Integrations → GitLab and Bitbucket code hosts provides explicit owner workflows for GitLab and Bitbucket Cloud. Jira, Linear and source-provider extensions remain read-only. No provider account is contacted in the fixture tests.\n\nConfigure integrations with `CODEFLOW_INTEGRATIONS_JSON` or `CODEFLOW_INTEGRATIONS_FILE`:\n\n```json\n[\n {\"id\":\"gitlab\",\"provider\":\"gitlab\",\"project\":\"group/repository\",\"credentialEnv\":\"GITLAB_TOKEN\"},\n {\"id\":\"bitbucket\",\"provider\":\"bitbucket\",\"project\":\"workspace/repository\",\"credentialEnv\":\"BITBUCKET_TOKEN\"}\n]\n```\n\nTokens remain server environment references. GitLab defaults to its v4 API and GitLab HTTPS Git root; Bitbucket defaults to its Cloud v2 API and bitbucket.org Git root. Self-hosted GitLab may set `baseUrl` and `gitBaseUrl`. A custom Bitbucket API base requires an explicit HTTPS `gitBaseUrl`; Bitbucket Data Center APIs are not supported. Neither root may contain credentials, query parameters or fragments. Fork paths must remain within the configured Git host/root; cross-host credential redirects are refused.\n\nChoose a configured host, PR number and local Git repository. Loading a preview reads bounded provider metadata, discussions and available merge policy. Fetch explicitly imports exact source/target objects and their actual merge base into random application refs, without checking out or executing incoming code. Each snapshot expires after 15 minutes and all snapshots disappear on restart. Changed heads, targets, discussion revisions and project membership require a fresh preview. Truncated discussion lists cannot be resolved or replied to here; inspect the remaining discussion in the provider UI.\n\nFetched-code review runs require an explicitly prepared agent Docker image and optional named authentication volume. Build the image with the official CLI and authenticate inside that image/volume first. No host credential directory is mounted, images are never pulled automatically, and there is no native host fallback. The container runs with the host UID, dropped capabilities and resource limits; the CLI needs its own configured network access for inference. Check/setup commands run in a separate network-disabled container. Safe mode alone is not filesystem isolation. See the existing agent-container setup in the application architecture documentation.\n\nThe run begins at the fetched source commit, reads conventions from the trusted merge base, uses Safe mode, inherits no uploads or parent run, copies no ignored files, and cannot wait for native tool approvals. It needs at least one required verification command. Raw client `externalSource` or commit-provenance objects cannot establish authority. A restored or resumed run still needs its prepared agent container.\n\nReview actions include general and new-file inline comments, replies, resolution/reopening, and GitLab exact-SHA approval/removal. Comments label the observed source and target commits. GitLab inline positions include its base/head/start diff identities. Bitbucket inline comments and approvals do not expose an atomic expected-head precondition: comments explicitly label the observed commit, postflight drift is reported, and commit-bound Bitbucket approval is deliberately unavailable here. Use Bitbucket's native UI for its approval action. Review content remains untrusted reference text.\n\nPush starts with a preview of a completed, owned, currently verified result branch. The result must descend from the observed PR source and include current required check evidence. Push sends only that exact commit to the observed source branch with `--force-with-lease=<ref>:<observed-head>`; arbitrary force pushes are not available. GitLab fork pushes require a separately configured source integration. No push occurs when merely loading the preview.\n\nNew PR creation accepts a completed owned verified result, configured destination host, title/body and target branch. The result must contain new commits descending from that exact current target. Its preview shows a unique server-generated source branch, source/target commits and outgoing text. The explicit action creates the source branch with an expected-absent Git lease, then sends the native GitLab MR or Bitbucket PR creation request. Those two remote actions are not atomic. A timeout preserves the exact branch/commit/run receipt as unknown; inspect that branch and the provider before another attempt. The application never retries creation automatically or deletes ambiguous remote branches.\n\nMerge is an explicitly labelled **pinned Git merge**, not a provider-native merge queue. A completed owned result must exactly match the current PR source and required verification/review evidence. The actual source-plus-target tree is built and checked in a disposable worktree using network-disabled check isolation and tracked-file/index/HEAD drift guards. The final commit has exactly the pinned target and source as parents. Its push uses an atomic exact-target Git lease. Source advancement cannot add unreviewed code; target advancement refuses the push. The local target branch is unchanged.\n\nProvider policy must be readable and pass at preview and immediately before the action. GitLab approval state, exact-head pipeline requirements, discussion requirements, external checks and protected-branch metadata are inspected. Native trains, merged pipelines and enforced squash/rebase workflows are blocked. Bitbucket mergeability checks and complete branch restrictions must be readable, including the admin scope required by the restrictions API. Native queues, custom on-merge checks, dependency-merge requirements, incomplete results and unknown restriction types block merging. Direct target push permission is required and the Git server still enforces its push permissions. Provider APIs do not offer an atomic approval-policy snapshot tied to the Git ref update; organizations requiring exclusively native policy enforcement should merge in the provider UI.\n\nGitLab's native merge endpoint pins the source SHA but does not pin the target SHA. Bitbucket's native merge endpoint does not expose an expected-head precondition and can queue its operation. Consequently this application does not call either native merge endpoint while claiming an exact validated target. A confirmed Git merge receipt does not imply atomic PR closure; the current provider state is reported separately and the PR may remain open. No native merge queue parity is claimed.\n\nCredentialed Git runs in a private temporary bare repository with no user/global Git config, hooks, credential helper, redirects or submodules. Authentication is a process-only URL-scoped HTTPS header. Local object imports receive no provider credential. Fetches are bounded in time, output, file count and temporary storage (256 MiB); oversized histories fail closed. Local application refs keep imported objects reachable and are normal repository data; no credential is stored in them.\n\nThe bounded `code-host-operations.json` ledger retains at most 200 explicit actions and safe identity/commit/run/branch facts. Unknown receipts are never silently evicted: if all slots are unresolved, new actions stop until the owner explicitly records manual inspection of one exact outcome. Inspection is labelled separately from provider confirmation and never replays the action. The ledger stores no tokens, comment bodies, tasks or executable callbacks. Claims are fsynced before remote mutation; interrupted or restored claims become unknown metadata, all server previews are lost, and there is no startup dispatch or automatic replay. Retention protects referenced run receipts. Encrypted restore also excludes executable extension caches and live identity/session credentials. Any custom filesystem policy blocks all code-host POST operations, and shared project sessions cannot access these owner-only routes.\n\nProvider protocol references: [GitLab merge requests](https://docs.gitlab.com/api/merge_requests/), [GitLab discussions](https://docs.gitlab.com/api/discussions/), [GitLab approvals](https://docs.gitlab.com/api/merge_request_approvals/), [GitLab external status checks](https://docs.gitlab.com/api/status_checks/), [Bitbucket Cloud pull requests](https://developer.atlassian.com/cloud/bitbucket/rest/api-group-pullrequests/), [Bitbucket branch restrictions](https://developer.atlassian.com/cloud/bitbucket/rest/api-group-branch-restrictions/), [GitLab HTTPS tokens](https://docs.gitlab.com/user/profile/personal_access_tokens/), [Bitbucket HTTPS access tokens](https://support.atlassian.com/bitbucket-cloud/docs/using-access-tokens/).\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"GitLab and Bitbucket code workflows"},{"group":"Connect and collaborate","id":"external-sources","source":"docs/external-sources.md","body":"# Read-only external sources\n\nConfigure the source integration, select an issue or pull request, and review the imported context before starting work. Imported descriptions remain untrusted task data.\n\n## Bring source context into a run\n\ncode-flow can list and import GitLab issues and merge requests, Bitbucket Cloud issues and pull requests, Linear issues, and Jira Cloud issues. Import reads the item's text and metadata. It does not fetch a review request's branch, publish comments, transition issues, push commits or merge remote work.\n\nConfigure integrations on the server with `CODEFLOW_INTEGRATIONS_FILE` pointing to a JSON file, or `CODEFLOW_INTEGRATIONS_JSON` containing the array directly. The file takes precedence. Restart code-flow after changing configuration. Credentials are read from named environment variables; the browser receives connection names and availability, never credential values or environment variable names.\n\n```json\n[\n {\n \"id\": \"gitlab-project\",\n \"name\": \"GitLab application\",\n \"provider\": \"gitlab\",\n \"project\": \"my-group/my-project\",\n \"credentialEnv\": \"MY_GITLAB_READ_TOKEN\"\n },\n {\n \"id\": \"bitbucket-project\",\n \"name\": \"Bitbucket application\",\n \"provider\": \"bitbucket\",\n \"project\": \"my-workspace/my-repository\",\n \"credentialEnv\": \"MY_BITBUCKET_READ_TOKEN\"\n },\n {\n \"id\": \"linear-team\",\n \"name\": \"Linear engineering\",\n \"provider\": \"linear\",\n \"project\": \"ENG\",\n \"credentialEnv\": \"MY_LINEAR_READ_KEY\"\n },\n {\n \"id\": \"jira-project\",\n \"name\": \"Jira application\",\n \"provider\": \"jira\",\n \"baseUrl\": \"https://my-company.atlassian.net/\",\n \"project\": \"APP\",\n \"credentialEnv\": \"MY_JIRA_READ_TOKEN\",\n \"usernameEnv\": \"MY_JIRA_EMAIL\"\n }\n]\n```\n\nUse credentials restricted to reading the configured project or team. Authentication defaults are `private-token` for GitLab, `bearer` for Bitbucket, `api-key` for Linear personal keys, and `basic` for Jira email/API-token pairs. An explicit `auth` may choose any of those modes; `basic` also requires `usernameEnv`. Linear OAuth tokens use `bearer`.\n\nGitLab's default API base is `https://gitlab.com/api/v4/`; a self-managed instance can provide its own HTTPS API base. Bitbucket uses the Cloud API at `https://api.bitbucket.org/2.0/`; Bitbucket Data Center is not supported by this adapter. Linear uses `https://api.linear.app/`; `project` is the team's key or UUID. Jira requires the site API base and a project key. Its adapter uses enhanced `/rest/api/3/search/jql`, rather than the older search endpoint being removed.\n\nThe external-sources screen lists a page of up to 50 items. Selecting **Use as task context** imports the full description, up to 20,000 characters. Server-held imports expire after 30 minutes and a restart; reimport if necessary. A run stores its accepted source snapshot for later inspection and recovery.\n\nImported content is always untrusted. The browser sends a server-issued `externalSourceId` when starting a run. The server resolves that ID to its own saved snapshot and the engine applies the same restrictions as other untrusted sources: safe agent permissions, no copied secrets, and isolation for commands unless the user explicitly permits host execution. A client-provided body, `trusted` field or text marker cannot establish source authority.\n\nConnections require HTTPS. Requests remain within the configured API origin and path, do not follow redirects, time out after 15 seconds and stop reading at 2 MiB. Credential values and remote error bodies are excluded from error messages. GitLab/Bitbucket numeric pagination stops at page 20 and reports truncation; the UI also limits displayed items to 1,000. Some Bitbucket repositories do not enable their built-in issue tracker; their issue API will report unavailable access while pull requests remain usable.\n\n## Provider extensions\n\n`ReadOnlySourceProvider` in `src/integrations.ts` defines `kinds`, `list` and `get`. Pass additional adapters through the `Integrations` constructor's `adapters` option. Extensions receive the same bounded request client. REST requests use GET; GraphQL accepts named read-only queries and variables. The request client rejects mutation operations and requests outside the configured API base. Normalize results to `ExternalSourceItem`; the class enforces output bounds and issues untrusted import identities.\n\n## API\n\n| Operation | Request |\n| --- | --- |\n| Connection availability | `GET /api/integrations` |\n| List one page | `GET /api/integrations/:id/items?kind=issue` (or `pr`) |\n| Continue a listing | Add the returned opaque `cursor` |\n| Import full source | `POST /api/integrations/:id/import` with `{ \"kind\": \"issue\", \"key\": \"123\" }` |\n| Attach imported context to a run | Include the returned `id` as `externalSourceId` in `POST /api/runs` |\n\nThe API requires the normal code-flow session. Service credentials are configured on the server and cannot be changed through this interface.\n\n## Provider references\n\nImplementation references checked on 18 October 2026:\n\n- [GitLab project issues](https://docs.gitlab.com/api/issues/) and [merge requests](https://docs.gitlab.com/api/merge_requests/).\n- [Bitbucket Cloud REST API](https://developer.atlassian.com/cloud/bitbucket/rest/), [pull requests](https://developer.atlassian.com/cloud/bitbucket/rest/api-group-pullrequests/), and [issue tracker](https://developer.atlassian.com/cloud/bitbucket/rest/api-group-issue-tracker/).\n- [Linear GraphQL authentication and queries](https://linear.app/developers/graphql).\n- [Jira Cloud enhanced issue search](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-search/) and [email/API-token authentication](https://developer.atlassian.com/cloud/jira/platform/basic-auth-for-rest-apis/).\n\nAutomated coverage uses deterministic HTTP fixtures. Live account compatibility depends on each account's current API access and credentials; tests do not spend quota or create remote content.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Read-only external sources"},{"group":"Connect and collaborate","id":"automations","source":"docs/automations.md","body":"# Automations and away mode\n\nOpen Project → More → Automation → Repeat for recurring work, Inbox for results that need attention, and Automation → Run once later for a single task that should start later. Keep the application running when a scheduled task is due.\n\n\n\n\n## Choose the right trigger\n\nSet up a run in the composer (task, team, check, settings), then choose **More run actions → Schedule… → Repeat**. It runs unattended.\n\nOpen **Scheduling** from the sidebar or press **a** to manage recurring tasks, deferred runs, backlog processing and project missions. **Inbox** is a separate sidebar destination for questions, blocked work, failures and results. Older `#/settings/automations` links open the Scheduling page.\n\n## Projects\n\nAn automation's runs start in the [project](runs.md#projects) you were in when you saved it (from *All projects*, in its repository's project). If that project is deleted later, they start in the repository's project. Turning it off and on, or making a new webhook secret, keeps its project.\n\n**Project → Automation** always lists that project’s saved work, regardless of the sidebar filter or composer draft. Choose its repository here before configuring a task or opening backlog items. Run once later and Repeat open the canonical project composer to describe the task; choosing backlog items restores that project’s saved agent workflow without leaving Automation. Multi-repository projects require a visible choice. Saved schedules for other repositories in the same project stay discoverable.\n\nGlobal **Scheduling → Repeat** uses the project filter beside its title. Inbox lists the results waiting for you. When other projects have some too, a line says how many, with **show all** to switch to *All projects*. The **Inbox** count in the sidebar counts attention items across projects, so nothing waiting for you is out of sight.\n\n## When it runs\n\n- at a time on chosen weekdays (e.g. weekdays at 07:00), or every *N* minutes (at least 5);\n- from a webhook;\n- or when you press **Run now** on the **Scheduling → Repeat** page.\n\nThe next time is saved, so restarting code-flow doesn't move it. A time missed while code-flow was closed or the computer asleep fires once when code-flow is back, if it's at most an hour late and today is still one of the schedule's days; otherwise it's skipped and the schedule moves on to its next time. A schedule never fires on a day you left out.\n\nIf the previous run is still going (or still starting), a scheduled run or **Run now** is skipped, so automations never pile up. A webhook event that arrives meanwhile waits instead, and fires once that run ends; the same event sent twice waits once.\n\n## Sweeps\n\nA [sweep](github.md#sweeps-many-at-once) saved with *Also check every day at…* is an automation too. Each time it fires it lists the open issues, Dependabot PRs or security alerts again and starts a run for each **new** item that would be picked by default, up to 5 at a time. It remembers which items it already started, so nothing is swept twice; alerts are remembered by number, so a new alert on a package that was swept before is new work. **Run now** does the same on demand.\n\nA webhook that reaches a sweep names one item, and goes through the same rules:\n\n- it has to be the sweep's kind: an issue for *Fix open issues*, a pull request for *Fix Dependabot PRs*, an alert for *Fix security alerts*;\n- the sweep's list has to pick it: not labelled *question* or *wontfix*, not a Dependabot PR whose CI passes (or is still running), not a low-severity alert or one with no fix yet, not one that already has a run;\n- webhooks start at most the sweep's limit of runs (5 by default) per hour, so someone opening fifty issues doesn't start fifty runs.\n\n## Webhooks\n\n`POST /hooks/<id>` with `Authorization: Bearer <secret>` and a JSON body. Both fields are optional:\n\n- `details` (or `task`) is added to the task as information for the agents, not instructions: it goes in a fenced block they're told not to take orders from;\n- `source` (e.g. `{\"kind\": \"pr\", \"number\": 12}`) starts from that issue or PR, or from a security alert (`{\"kind\": \"alert\", \"tool\": \"dependabot\", \"number\": 3}`, or `\"tool\": \"code-scanning\"`).\n\nAnyone with the secret can send `details`, and programs on this computer can read the secret in `~/.codeflow/triggers.json`, the agents included. So a run with `details` is marked as carrying untrusted text, like one from a Dependabot PR: its reviews aren't learned as lessons for later runs, and project memory it proposes waits for your approval. Runs from a schedule, **Run now**, or a webhook without `details` are as before.\n\nGitHub webhooks work directly through a tunnel. Use the secret as the webhook secret; code-flow checks `X-Hub-Signature-256`. These events start a run, and all others are ignored:\n\n- an opened issue starts from that issue;\n- an opened or updated pull request starts from that PR;\n- a failed workflow run starts from its PR, with the failure logs;\n- a new or reopened Dependabot alert or code scanning alert starts from that alert.\n\nLeaked-secret alerts never start a run.\n\ncode-flow answers `202` at once (GitHub gives up on a delivery after 10 seconds) and starts the run in the background. If it can't start, the reason shows on the automation (*couldn't start: …*). A delivery GitHub sends again is ignored: one with the same `X-GitHub-Delivery` id, or a signed one with the same body whatever its id says, or with none (the signature covers the body, not the id). An automation that isn't a sweep starts at most 10 runs an hour from webhooks.\n\n## The inbox\n\nAutomation and sweep results wait in **Inbox**, with an attention count in the sidebar:\n\n- **Approve:** keeps the result. **Approve & open PR** also pushes the branch and opens a pull request. For a run that started from a pull request (a Dependabot PR, say), **Approve & push to PR #N** pushes the fix to that PR instead.\n- **Revise:** your note goes to the agent that wrote the code, in its own session. The result comes back to the inbox.\n- **Reject:** deletes the run's worktrees and branches.\n- Runs that changed nothing are archived on their own, and their branches deleted.\n- Automations never pause for plan approval, and **nothing is ever merged automatically**.\n\n## Isolation\n\nTick *Run checks and setup in a Docker container* and code-flow runs those commands with `docker run`:\n\n- only the worktree is mounted read-write, plus the repo's `.git` read-only, so git works;\n- none of your environment variables or your home folder are visible inside;\n- the files it creates belong to you.\n\nCheck isolation runs the repository's scripts in the selected image. Agents normally use their own CLI sandboxes; explicit [agent container isolation](agent-containers.md) is a separate setting with its own prebuilt image and optional authentication volume. The default check image comes from `image` in [`.codeflow/config.json`](configuration.md#per-repo-config).\n\n## Phone view\n\nTurn on **Enable remote access** in **Settings → Connections → Share access**. Choose **This computer only**, **Tailscale · access anywhere**, or **Wi-Fi · local network**.\n\n- On its own it's a read-only view: what's running and what needs you, and each run's summary, review, results and steps. It refreshes itself and has no buttons.\n- Runs are listed under their project's name. Projects with runs that need you come first, then those with runs going; runs in no project come last.\n- **Wi-Fi · local network** also serves it, and webhooks, on your network at port `PORT+1`. Choose **Set up secure remote access** to import a PEM certificate and private key trusted by your phone, with the hostname or IP address covered by the certificate. Without HTTPS the listener remains read-only.\n- With network access on, the page shows a **QR code** for the selected connection: scan it with your phone's camera. (The `localhost` link only opens on the computer running code-flow, so there's no QR code until a network link exists.) If another program already has the port, the page says so instead; start code-flow with `CODEFLOW_REMOTE_PORT` set to a free one.\n- The network listener refuses connections from the computer itself, whichever of its addresses they use: programs on it, the agents included, can read `~/.codeflow`, phone secrets and all.\n- **New link** revokes the old one. So does turning off remote access or choosing **This computer only**: turning it back on gives a new link.\n\n### Remote access with Tailscale\n\n1. Install [Tailscale](https://tailscale.com/download) on the computer running code-flow and your phone, tablet or second computer. Sign in to the same tailnet and connect both devices.\n2. Open **Settings → Connections → Share access**, enable remote access and select **Tailscale · access anywhere**.\n3. Code-flow detects the local Tailscale client and shows its connection status. If it is missing, signed out, awaiting approval or disconnected, follow the displayed instructions and select **Refresh Tailscale**. Detection also refreshes automatically.\n4. Once connected and listening, copy the link or scan the QR code on the other device. The link uses this computer's Tailscale IPv4 address and `CODEFLOW_REMOTE_PORT` (normally 4318). For full control, choose **Set up secure remote access → Use Tailscale → Get Tailscale certificate**. Enable HTTPS certificates in your tailnet DNS settings first. Once setup succeeds, turn on **Full control from remote devices** and use the new pairing link. No environment variables or application restart are needed.\n\nThe Tailscale mode binds only to the detected Tailscale address, never to the Wi-Fi address or all interfaces. Your tailnet access rules and host firewall must allow the remote port. The default link is HTTP **inside Tailscale's encrypted connection**; it is not a public URL. If you already configured TLS, code-flow retains HTTPS and uses the device's Tailscale DNS name when available; that name must resolve on the other device and match your certificate. Code-flow does not run `tailscale up`, change access rules or enable Serve/Funnel.\n\nThe computer must stay awake and online with code-flow running. Disconnecting Tailscale removes the link and closes its listener on the next check (within about ten seconds plus detection time); reconnecting restores it. A changed Tailscale address updates the link and QR code. Switching between Wi-Fi and Tailscale revokes previous links and paired-device sessions. Full control requires HTTPS on either connection. Tailscale encrypts network traffic, but browsers can send HTTP cookies to other services on the same host, so Tailscale alone does not protect full-control sessions.\n\nUse the link on another device: connections from the server computer itself remain blocked to keep local agent processes from pairing. Direct access currently requires a Tailscale IPv4 address and an OS network interface; userspace-only Tailscale is not supported. Browser features that require an HTTPS secure context may be unavailable on the HTTP link. Local app previews still use separate localhost ports and are not published by this setting; this release provides remote access to Codeflow itself.\n\n### Set up HTTPS in Settings\n\nOn the computer running Codeflow, open **Settings → Connections → Share access**, enable remote access and select a network connection. Choose **Set up secure remote access**:\n\n- **Use Tailscale** requests a certificate for the connected computer's detected tailnet DNS name through `tailscale cert`. Tailscale HTTPS must already be enabled in your tailnet's DNS settings, and the current OS user must be allowed to request certificates. Codeflow never logs you in, elevates privileges or changes your tailnet configuration. The setup explains that the DNS name appears in public certificate transparency logs; the app remains private to your network. See [Tailscale HTTPS setup](https://tailscale.com/docs/how-to/set-up-https-certificates).\n- **Import certificate files** accepts a PEM certificate (including its chain) and unencrypted PEM private key. Enter the hostname or IP address your device will use. Codeflow checks the matching key, validity dates and certificate identity. The device must trust the issuer and resolve the hostname to this computer; importing does not install device trust or configure DNS.\n\nThe certificate and key are stored together in `CODEFLOW_HOME/remote-tls.json` with owner-only permissions and atomic replacement. The API never returns the private key. HTTPS setup is limited to the local owner app, and is unavailable through phone/shared sessions or SSH backends. It reloads only the network listener, leaving runs and the local app active. Certificate replacement revokes paired sessions and leaves full control off until explicitly enabled again.\n\nCodeflow automatically renews certificates obtained through Tailscale within 30 days of expiry while the app is running and Tailscale remote access is enabled. It checks once a minute and retries failures after 1 hour, doubling up to 24 hours; retry timing survives restarts. Renewal keeps full control and paired devices signed in. Settings shows the next attempt, last successful renewal and any failure. Imported certificates and environment-managed certificates remain manually maintained; use **Renew or replace certificate** for those. After time offline, Codeflow can renew an expired Tailscale certificate for the same computer name. An expired or unreadable saved certificate disables the network listener instead of falling back to HTTP. Fix it from the local app.\n\nExisting `CODEFLOW_TLS_CERT` and `CODEFLOW_TLS_KEY` overrides remain supported and take precedence. When those are set, Settings reports that HTTPS is externally managed; remove the overrides and restart to switch to saved configuration.\n\n### Full control from your phone\n\nWith HTTPS configured for **Tailscale** or **Wi-Fi**, you can turn on **Full control from remote devices**. The pairing link signs your phone in to the whole app: start runs, answer approvals, message agents, review diffs, merge results and manage automations. `CODEFLOW_ALLOW_INSECURE_PHONE_CONTROL=1` explicitly permits HTTP for development. Full control requires TLS even over Tailscale, so session cookies can be marked Secure and withheld from other HTTP services on the same host.\n\n- The QR code is a separate sign-in link that expires after ten minutes, including across server restarts. The read-only link never signs a phone in.\n- Scanning pairs one device with a separate 30-day session; the server stores only its token hash. The device list can revoke one phone immediately. **New link**, or disabling full control/network access, rotates the pairing generation and signs all devices out. TLS cookies are Secure. Legacy pairing links expire closed after upgrade.\n- *Terminal*, *Editor* and *Folder* on a result open on your computer, not the phone. *Run app* starts the app on your computer; its link uses the computer's Wi-Fi address, so it only opens if the app listens on your network too (e.g. `vite --host`).\n- Paired devices have owner control and can run commands on the computer. This pairing does not provide separate user identities or project roles.\n\nAutomations live in `~/.codeflow/triggers.json` and the phone settings in `~/.codeflow/remote.json`. Both files are readable only by you, and are written whole (through a temporary file), so a crash can't leave half of one. One that can't be read is moved aside to `triggers.json.corrupt-<time>` (or `remote.json.corrupt-<time>`) and logged in `~/.codeflow/server.log`; code-flow then starts without those automations, or with phone access off, rather than write over it. Automations in it that can be read are kept, and one whose schedule can't be read is kept but turned off.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Automations and away mode"},{"group":"Connect and collaborate","id":"notifications","source":"docs/notifications.md","body":"# Outbound notifications\n\nOpen Settings → Notifications, configure a destination and its scope, then inspect delivery history. Outbound destinations are opt-in and can disclose the event details selected for delivery.\n\n## Choose where alerts go\n\nNotifications require an explicitly configured destination and a saved opt-in for its events and project scope. New destinations start disabled. No account is connected and no destination can be created from the browser.\n\nSet `CODEFLOW_NOTIFICATIONS_FILE` to a server-side JSON file, or set `CODEFLOW_NOTIFICATIONS_JSON` to its contents:\n\n```json\n[\n {\n \"id\": \"team-status\",\n \"name\": \"Team status\",\n \"protocol\": \"webhook\",\n \"urlEnv\": \"TEAM_STATUS_WEBHOOK_URL\",\n \"secretEnv\": \"TEAM_STATUS_WEBHOOK_TOKEN\"\n },\n {\n \"id\": \"slack-status\",\n \"name\": \"Slack status\",\n \"protocol\": \"slack\",\n \"urlEnv\": \"SLACK_STATUS_WEBHOOK_URL\"\n }\n]\n```\n\nSupply those referenced environment variables to the server. URLs must use HTTPS and cannot contain user credentials or a fragment. `secretEnv` is optional and sends its value as a Bearer authorization header. The Slack protocol uses an existing [Slack incoming webhook](https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks/), with a literal text block and link previews disabled. There are no email, Teams account, or channel-management adapters; a separately configured HTTPS relay can consume the generic webhook protocol.\n\nIn Settings → Notifications, select the destination, its events, and its projects, then save. Available events are run completion, run or verification failure, and requests for attention. “Without a project” covers runs with no project; “All projects” includes future projects. A run that finishes with failed or blocked verification produces a failure event. Sending a test is a separate, explicit action and works before enabling automatic notifications.\n\nGeneric webhook requests contain JSON with `version`, `id`, `event`, `runId`, optional `project`, `pattern`, `status`, `result`, and a task `title` limited to 200 characters. A manual test additionally has `test: true`. They exclude repository paths, transcripts, command output, and imported source content. Task titles can contain sensitive business information: opt in only for an appropriate destination. Recognizable secrets and configured credential values are redacted. The browser API exposes destination hostname, scope settings, and delivery metadata; it never exposes the webhook URL, credential names, tokens, or saved payloads.\n\nThe local outbox persists an event before sending, keeps at most 200 deliveries and 2,000 recent event identities, and sends at most 20 requests per worker pass. The worker checks every ten seconds, uses a ten-second request timeout, and refuses redirects. HTTP 429, server errors, and connection failures retry up to three total attempts with bounded exponential backoff. `Retry-After` seconds can extend the delay to at most five minutes. Other HTTP errors fail once. Pending deliveries expire after 24 hours without an attempt. When the queue is full, the newest event is dropped and its count and reason appear in the UI. Delivery history exposes failures, attempt counts, and an explicit retry action.\n\nThe `Idempotency-Key` request header is the persistent delivery ID. Recipients should deduplicate it. Exactly-once delivery is not guaranteed: a timeout or crash can happen after a recipient accepts a message. A delivery interrupted while sending becomes an unknown-outcome failure on restart and requires an explicit retry. Slack incoming webhooks do not provide an application-level deduplication guarantee. After 2,000 newer event identities have displaced an old identity, replaying that old event may deliver it again.\n\nChanging the configured URL or protocol requires renewed opt-in. Disabling a destination or narrowing its scope cancels pending automatic deliveries, but cannot retract a request already in flight. Restoring an encrypted backup disables saved notification opt-ins and cancels pending or sending deliveries. Corrupt saved state leaves notifications unavailable and preserves the file for repair. Delivery state belongs to one server process; do not share its storage across multiple running servers.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Outbound notifications"},{"group":"Operate safely","id":"security","source":"docs/security.md","body":"# Security, subscriptions and caveats\n\nBefore starting work, decide whether you trust the repository, which agent permissions it needs, and where its checks should run. Read the boundaries below before enabling remote access or Full access.\n\n## How code-flow uses your subscriptions\n\ncode-flow does not read agent login files. Each adapter launches the vendor's **official, unmodified CLI** in headless mode, and that CLI uses its own login. Explicit external integrations and notification destinations use server-side environment credential references; those variables are stripped from child processes and filtered from exports:\n\n| Agent | Command used | Login | Sandbox / permissions |\n|---|---|---|---|\n| Claude Code | `claude -p --output-format stream-json` | Pro/Max (or API key) via `claude` | read: edit tools disabled, git read-only commands allowed (never with `--output`, which writes anywhere, or `--no-index`, which reads anywhere) · write: `acceptEdits` + an allow-list of build/test commands (`bypassPermissions` with Full access) |\n| Codex | `codex app-server` (JSON-RPC over stdio: `thread/start` or `thread/resume`, `turn/start`, `turn/steer`); `codex exec --json` / `codex exec resume` when app-server isn't available | ChatGPT plan (or API key) via `codex login` | Codex sandbox: `read-only` / `workspace-write` (`danger-full-access` with Full access); optional one-action approval inbox on supported app-server turns |\n| Antigravity | `agy -p --output-format stream-json` | Google AI Pro/Ultra via an interactive `agy` login | `--sandbox` (verified: blocks writes outside the worktree; dropped in write steps with Full access) + `--dangerously-skip-permissions` (see [caveats](#caveats)) |\n\n**Read-only is enforced by code-flow itself, for every agent.** Before a read-only step (plan, review, judge, debate) code-flow snapshots its worktree: the commit, uncommitted changes and untracked files. Afterwards it puts back anything the step changed, including commits, and logs a warning. A side chat's **Ask** is the exception: you may be working in that worktree at the same time, so what changed is named in a warning and left in place.\n\nThis is the boundary each vendor draws for personal use. Don't extend code-flow to read or reuse the CLIs' OAuth tokens; Anthropic and Google both prohibit that, and Google has suspended accounts for it. If you ever turn this into a product for other people, each user must sign in with their own account, and Anthropic's terms then require API keys for products.\n\n> Gemini CLI stopped serving Google AI Pro/Ultra/free personal accounts on 2026-06-18. `agy` is the CLI that serves those plans now.\n\n## The server\n\n- It listens on `127.0.0.1` only, checks the Host and Origin headers, and wants a session cookie or Bearer token on every API call (see [signing in](getting-started.md#signing-in)).\n- The first browser to open code-flow signs in without the link only when it connects directly from this machine, and only until a browser has signed in (that way, with the link, or with its cookie) or code-flow has started an agent, a check or an app. After that, a program here can't get a session by asking for the page. A tunnel (ngrok, cloudflared) forwarding to the port can rewrite the Host header to `localhost`, so a request carrying forwarding headers (`X-Forwarded-For`, `Forwarded`…) never signs in that way.\n- code-flow's pages are never shown inside another page (`X-Frame-Options: DENY`, `frame-ancestors 'none'`), so a page elsewhere can't lay them under a decoy and have you click. The side panel's Browser frames your app through its preview, on a port of its own, never code-flow itself.\n- Choosing **Wi-Fi · local network** under **Remote access** exposes the phone view and webhooks on your local network. Configure HTTPS through **Set up secure remote access**, using a Tailscale certificate or an imported certificate/key pair. Legacy `CODEFLOW_TLS_CERT` and `CODEFLOW_TLS_KEY` overrides are also supported. Saved keys have owner-only permissions; setup is local-owner-only, validates identity and dates, and revokes pairing when replacing certificates. Invalid or expired saved certificates disable the listener without an HTTP fallback. Full phone control on Wi-Fi requires HTTPS; `CODEFLOW_ALLOW_INSECURE_PHONE_CONTROL=1` is an explicit development exception. See [Automations](automations.md#phone-view).\n- **Tailscale · access anywhere** binds the remote listener only to the IPv4 address reported by the local Tailscale client. Read-only links can use HTTP inside Tailscale's encrypted connection. Full control still requires HTTPS: Tailscale does not stop browsers from sending non-Secure cookies to other HTTP services on the same IP. Non-Tailscale source addresses and this computer's own connections are refused. Missing/disconnected Tailscale and bind errors never fall back to a LAN listener. Code-flow reads Tailscale status and requests a certificate during secure-access setup. It subsequently renews that saved Tailscale certificate automatically for the same verified computer name; renewal never changes the host identity or enables full control. It does not configure the tailnet or enable public access. Switching transports rotates access tokens and invalidates paired sessions; switching to Wi-Fi without TLS disables full control. See [Tailscale setup](automations.md#remote-access-with-tailscale).\n- That network listener refuses connections from this computer itself, from loopback or any of its own addresses. Programs here, the agents code-flow runs among them, can read `~/.codeflow`, phone secrets included; through the network listener they could otherwise sign in with full control and run commands outside their sandboxes. (Binding it to the Wi-Fi addresses alone wouldn't stop that: a program here can connect to those too.)\n- **Full control from remote devices** signs in with a separate pairing link that expires after ten minutes, including across restarts. Pairing creates a separate 30-day session for each device; only token hashes are stored, TLS cookies are Secure, and individual devices can be revoked. **New link** and disabling remote/full control rotate the generation and invalidate earlier sessions. Cross-origin requests are refused. Paired devices have the owner's full control; this is not a shared-project roles system. See [Full control from your phone](automations.md#full-control-from-your-phone).\n- Webhooks: each automation has its own secret (a Bearer token, or GitHub's signature). Secrets live in `~/.codeflow` (webhook secrets in `triggers.json`), which programs running as you can read, the agents included. So text a webhook sends (`details`) reaches the agents fenced as information, not instructions, and its run's reviews aren't learned as lessons. A GitHub delivery sent again is ignored, whatever its delivery id (the signature doesn't cover the id), and webhooks start a limited number of runs an hour. See [Webhooks](automations.md#webhooks).\n- The Browser's **Snapshot** loads pages in a headless Chrome on this computer, so it refuses addresses on this computer and link-local ones (where cloud machines keep their metadata service), checking every address a name resolves to. What the page asks for as it loads, redirects included, is checked the same way. The exception is a local app at its `http://localhost:<port>` address, as the Browser shows it, never code-flow's own port. Private network addresses are fine. Not covered: WebSockets, requests from workers, and a name that resolves differently a moment later (DNS rebinding).\n\n## Running the agents' code\n\nComposer Improve uses an empty private Git repository for Quick mode and a bounded\ncommitted snapshot for repository mode. It copies no source Git history, remotes,\nhooks, working-copy drafts or executable provider configuration. Cleanup removes\nonly that private directory and never resets the source checkout. Native Safe/read\npermissions remain a CLI boundary rather than an OS filesystem sandbox; filesystem\nrestrictions block Improve entirely. See [Improve setup and limits](getting-started.md#your-first-run).\nApp-managed subprocesses use absolute PATH entries, including explicit environment\noverrides. On macOS/Linux, an absent or unusable PATH falls back to `/usr/bin:/bin`\nso repository-owned tools cannot be selected through the current directory.\n\nLocal shared-project accounts grant scoped viewer, reviewer, contributor and\nmaintainer access through closed shared review and team-board API surfaces. Roles alone do not delegate execution or the owner's phone capability. Separately, the owner can grant per-project host execution to individual contributors through the [host queue](team-collaboration.md#let-teammates-run-tasks-on-the-host). Its dedicated routes accept only task identity and revision, use owner-selected CLIs and limits, and enforce queue ownership and live grants. This is trusted host execution, not an OS sandbox; agents and repository checks retain the host's available authority. Generic owner shell APIs remain unavailable. A separate, default-off [terminal grant](team-collaboration.md#give-trusted-teammates-an-interactive-terminal) permits individual contributors to open their own interactive shell as the host OS user. Project grants determine the starting folder, not a filesystem boundary. Shell users can reach host files, credentials and other processes; only grant this to trusted people. The app enforces session ownership, bounded input/output, live revocation and a 30-minute keyboard idle timeout. Closing a session does not undo changes or reliably stop detached processes.\nBrowser sessions expire and revoke immediately. Shared-review projections omit raw transcripts, private context and\nhost paths; explicitly granted terminals return raw output to their creator and can reveal those details. See [shared projects](shared-projects.md).\n\n[Team collaboration](team-collaboration.md) adds a per-machine runner credential\nbound to a shared account. It is shown once, stored only as a hash on the team\nserver, reaches only the team routes and is revoked there in one step; the\nowner's own token is matched before a bearer is read as a runner credential, and\nthe files that hold credentials on a linked computer (`team-connections/` and legacy `team-link.json`) are\nleft out of backups like the other credential stores. The team server never\nreaches into a teammate's machine: every connection is outbound from that\nmachine, to the exact address its owner entered (a redirect or an untrusted\ncertificate is refused), and execution starts only from each computer's own\ncode-flow, with that person's own CLI logins, in a worktree there. Progress,\ncheckpoints and discussion are the only things that travel; transcripts, local\npaths and copied secret files stay where the run is, and the reasons published\nfor failed, stopped or lost attempts have local paths replaced by `<local path>`.\nTeammates' comments never enter a prompt; an instruction they send to an agent\nreaches it only after the task's owner, or a maintainer of its project (the team\nserver's owner is one everywhere), approves it, and then as a message to the run\nattributed to its author; a hand-over checkpoint's context arrives fenced as\ninformation, not instructions.\n\nCustom [filesystem policies](filesystem-policy.md) enforce application reads,\nwrites and filtered diffs, and pause ordinary code execution globally where its\nbackend cannot enforce those rules. Saved restrictions are checked before\ntrusted extension code is imported. [Extensions](extensions.md) are explicit,\nversion/hash-pinned owner-trusted server code with full process authority, not\nuntrusted sandboxed plugins.\n\n[SSH workers](remote-workers.md) use provisioned targets, strict host keys,\nworker-local credentials and durable private receipts. A disconnected accepted\nturn is uncertain; reconnecting does not execute it again. [GitLab/Bitbucket\ncode workflows](code-hosts.md) require prepared agent containers for fetched PR\ncode and use exact-ref Git leases for publication. Provider-native approval\npolicy and PR closure cannot be transacted atomically with a Git push; unknown\npolicies and unsupported queues fail closed.\n\nA check runs the agents' code (tests, scripts) on your machine, outside Codex's and agy's sandboxes, as your CI would. With **Full access** the agents themselves run without a sandbox too (see [What agents may do](configuration.md#what-agents-may-do-while-building)). Use code-flow on repos and tasks you trust, or run checks in Docker ([isolation](automations.md#isolation)), or code-flow itself inside a container or VM.\n\nText from issues, PRs, comments and CI logs is treated as untrusted, and runs started from people outside the repo are locked down. Dependabot's PR descriptions and CI logs are fenced as untrusted text too, and so are code scanning messages, which quote the code they flag. **▶ Run app** on a locked-down run asks before it starts that code on your machine. See [Untrusted text](github.md#untrusted-text).\n\nFiles copied into worktrees from your checkout (`copy`, e.g. `.env`) never go into the agents' commits, and neither do new files named like secrets. They're never copied through a symlink an agent left in the worktree. See [Per-repo config](configuration.md#per-repo-config).\n\n## Your checkout and the worktrees\n\nA broken worktree is never mistaken for your checkout. Worktrees live inside your repository (`.worktrees/<run>/<name>`), and the `.git` file that ties each one to it is a file in the worktree that any agent can change. Without it, git run in the worktree would find your checkout instead. So code-flow guards its own git:\n\n- code-flow's git in a worktree never reads the worktree's `.git`. It's told the worktree's git folder (`GIT_DIR`, `GIT_WORK_TREE`), which it finds from your repository's side: the folder in your repository's git folder that names the worktree. That holds for everything code-flow runs there, reads included: the Files panel, diffs, status, the test-integrity checks, snapshots. So an agent that points `.git` at a git folder of its own can't bring in its config, whose filter, textconv or external diff code-flow would otherwise run outside the agent's sandbox just by looking.\n- Before it changes a worktree (undoing a read-only step, putting back test files, committing, merging, resetting a scratch checkout, removing it, or starting an agent there), code-flow also checks that the worktree's `.git` is still the file git wrote: it points to the worktree's own folder in your repository's git folder, and that folder points back. Before anything that moves a branch (undoing commits, committing, merging), the worktree must be on its run's branch. If any of that fails, the step fails and the run log says what's wrong (\"its .git is missing\", \"it's on another branch…\"). Nothing in the worktree or your checkout is changed. Remove the run's worktree, or put its `.git` back, before running it again. The git that then makes the change uses the git folder that was checked, so a `.git` rewritten in between changes nothing.\n- code-flow's git never looks above the folder it runs in (`GIT_CEILING_DIRECTORIES`), so a worktree without `.git` is an error, not your checkout.\n- code-flow's git runs without `core.fsmonitor` and without hooks, whatever a config file says. Your own hooks still run where you'd expect them: when code-flow merges a result into your checkout, and when it pushes (Git LFS uploads its files in `pre-push`).\n- code-flow's diffs never run an external diff program or a textconv filter (`--no-ext-diff --no-textconv`), and a snapshot saves untracked files as they are, without clean filters.\n- Not covered: submodules. To tell whether a submodule has changes, git runs itself in the submodule's folder, and that git goes by the `.git` there, which an agent can rewrite too. In a worktree with submodules (or a git repository an agent made inside it and code-flow committed), `git status` and `git add` can still run a filter that the agent's config names. Run untrusted tasks on repositories with submodules in a container or VM.\n\n## Caveats\n\n- **agy permissions** (tested with agy 1.3.5): when headless agy hits a tool it isn't allowed to use, it doesn't work around it. The turn ends with an empty answer, which code-flow reports as a failed step that names the denied action. That's why code-flow passes `--dangerously-skip-permissions` together with `--sandbox` by default. The sandbox confines writes to the worktree, and code-flow reverts any changes made during read-only steps. Older agy versions printed nothing when piped; code-flow reports that as an error too.\n- Native permission requests are declined by default. Interactive Codex app-server runs can explicitly enable the one-action approval inbox. Requests expire, are scoped to the active run/step/thread/turn and worktree, and cannot grant session-wide permission or policy amendments. Read-only work cannot gain file writes. Unattended and locked-down source runs never wait for native approvals. Other transports keep their existing denial behavior. Team questions remain separate from tool permissions.\n- **Mid-turn notes are Codex-only, and app-server is experimental.** Mid-turn delivery needs `codex app-server`, which Codex labels experimental (written against codex-cli 0.162). If the installed Codex lacks it, or speaks a protocol this client doesn't, code-flow falls back to `codex exec` and logs that once. It then sticks with exec until the server restarts, so restart code-flow after upgrading Codex. A note is marked delivered as soon as Codex accepts it into the turn. If Codex rejects it (the turn just ended), it goes to the next turn instead. A note for \"anyone\" goes to a running Codex turn, not to whoever starts next.\n- Claude's write mode can run allow-listed commands such as `npm`, `node` and `make`, and those can run arbitrary project scripts. Only run it on repos you trust. Codex is additionally confined by its own sandbox.\n- agy needs `--dangerously-skip-permissions` for read-only steps too, so for agy the snapshot-and-restore is what enforces read-only. A side chat's **Ask** isn't restored, so what agy changes there stays, and the next **Change** commits it: ask Claude or Codex when that matters. Changes to gitignored files during a read-only step aren't detected.\n- The test-integrity checks catch the common ways to game a check, not every one. A flagged result is a prompt to look, and an unflagged one isn't proof.\n\nAuto team's [memory boundary](auto-team.md#three-memory-scopes) keeps source trust separate from execution permissions. Agent messages cannot authorize actions. Private memory is isolated in context assembly and APIs, not from fully trusted local processes. Untrusted/unknown project-memory proposals require explicit revision approval; source provenance follows derived memories. Deleting a memory removes it from retrieval; entries written with it in context keep their own text and inherit its provenance. Deletion does not touch source run transcripts, provider conversations or backups.\n\nAuto team live collaboration uses a separate loopback bridge and an expiring capability for a single run/member/attempt. The general API token is never delegated. Credentials are kept in mode-0600 temporary files, revoked after the turn and excluded from prompts, logs and arguments. Members run as the same user, so a member with a shell could read another member's credential in the brief window before the bridge client deletes it; such a capability is scoped to that member's attempt, never to the application token or to you. Claude turns receive the bridge with `--strict-mcp-config`, so your own and the repository's MCP servers are not loaded in Auto team turns. Every mutation rechecks the active assignment and uses runtime-derived provenance. Collaboration does not widen the CLI's existing filesystem or network permissions. Native nested agents are disabled for supported Claude/Codex team turns; unsupported capabilities stay explicit. See [Auto team](auto-team.md#adapter-capabilities).\n\n## Documentation assistant\n\nThe local documentation assistant uses the owner's installed built-in CLI in a disposable empty Git directory with read-only, Safe adapter settings. It receives bounded documentation excerpts and the question/conversation supplied by the browser. Code Flow does not copy a project, run transcript or credential file into the help directory. Provider-level configuration and hooks still belong to the CLI trust boundary; the “do not use tools” prompt is not an OS isolation mechanism.\n\nThe endpoint inherits owner authentication, origin validation and the global filesystem-policy pause. Shared accounts and runner credentials cannot invoke it. Answers render as text and source links are selected from known guide IDs. GitHub Pages hosts static guides only and never probes localhost for an assistant. Read [Ask code-flow](help.md) for retention, quota, cancellation and limits.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Security, subscriptions and caveats"},{"group":"Operate safely","id":"filesystem-policy","source":"docs/filesystem-policy.md","body":"# Project filesystem policy\n\nChoose explicit read and write rules for the project. Review the execution boundary before enabling restrictions: operations that cannot enforce those paths pause rather than silently bypassing the policy.\n\n## Set project path rules\n\nSettings → Projects & access stores owner-managed read and write rules per project. Each save uses the exact displayed revision; conflicting saves preserve the browser draft. Private files live at `CODEFLOW_HOME/filesystem-policies/<project>.json`, with version 1, a revision, timestamp and SHA-256 content hash. Corrupt or symbolic policy storage fails closed without replacing it with unrestricted defaults.\n\nRules use literal relative repository paths. `.` covers the whole repository; `src` covers that file or subtree. Each read/write operation must match an allow entry and no deny entry. Empty allow lists deny everything. Denies also match conservatively across case and Unicode compatibility variants. There are no globs, traversal components, absolute paths or Git internals. Credential paths, private keys and symbolic filesystem ancestors have fixed application-level denies. Policies from every matching project and the run's pinned snapshots intersect: choosing another project or weakening today's policy cannot silently weaken an older run's pinned policy.\n\nThe application enforces paths for repository file reads, tree and filename search, textual content search, file editing and filtered run diffs. Read and write checks run again before returning content or publishing a file edit. Both sides of a renamed diff must be readable. Restricted projects' raw run history, event transcripts, exports, configuration, memory and other evidence that cannot be reliably filtered are unavailable. Restricted results are omitted before list pagination, search and totals, including shared review dashboards. Existing feeds close and reconnect with a new snapshot generation when policy changes. Unscoped statistics, private notes, memory, saved automation/queue requests, diagnostics, notification payloads and maintenance data are also unavailable while any custom restriction is active. Project names, repository membership and policy metadata remain available to the owner.\n\n## Execution boundary\n\n**Any custom read or write restriction pauses normal application-managed execution across every project.** The owner-only [Filtered Docker task](filtered-tasks.md) is the dedicated enforcing exception: it runs one manual turn against an isolated fresh Git snapshot and imports permitted changes only into a separately reviewed branch. Native agents, commands, ordinary Docker mounts, SSH workers, full Git object databases and trusted JavaScript extensions do not currently provide an enforcing filtered filesystem. The application refuses these backends rather than representing prompts or a filtered checkout as a security boundary. This includes run creation, resumption, follow-ups, verification, repository readiness execution, terminals, previews, development services, code-host actions and queued dispatch. Health responses avoid Python/provider probes under a restriction. Public read-only phone views are disabled under any custom restriction.\n\nPolicy activation requires globally idle execution, closed terminals, stopped development services, no preparing/importing or unconfirmed filtered task, and no unresolved accepted/running/unknown remote receipts. Unavailable filtered-task receipt storage also blocks activation until repaired. Background PR watches, source sweeps, deferred/coordinated readiness checks and notifications pause and finish their current work before the durable policy commit. Their underlying preparation promises remain tracked even if a queue timeout has already expired. The manager holds a global execution lease during the commit. An operation still running in another repository makes activation fail instead of racing it. Policy changes never implicitly contact a remote worker to resolve uncertain receipts: inspect or cancel them deliberately first.\n\nTrusted extensions can retain host authority after importing, including bundles rejected after initialization. If extensions were attempted in the current process, restrictive activation is refused: disable extension configuration, restart, then apply the restriction. On later startup, saved restrictions are checked before importing any extension bundle; imports remain deferred until restrictions are deliberately removed and the application restarts. Automatic launches, watches and notification delivery stay paused until all current policies are unrestricted. Deliberately clearing restrictions resumes those services; it does not erase a run's pinned policy.\n\nThis is an application policy with fail-closed execution, not an OS sandbox or retroactive erasure. The owner, other local processes, existing external backups and manually started processes remain outside this boundary. Historical journal bytes are retained privately; the application blocks unsafe presentation instead of rewriting provenance. Filtered tasks provide an isolated filesystem and synthetic Git database for their narrow manual execution path. Native agents, SSH workers and normal automated/multi-agent workflows remain unsupported under restrictions.\n\n## HTTP contract\n\nOwner-only `GET /api/projects/:id/filesystem-policy` returns the committed snapshot. `PUT` accepts `{revision, read:{allow,deny}, write:{allow,deny}}`; each list has at most 100 paths, each at most 500 characters. Saves return the next committed snapshot. Revision, activity or unsupported execution conflicts return 409; denied data returns 403; invalid paths return 400; unreadable private policy storage returns 503. `/api/v1` follows the same authorization and policy. Shared accounts cannot edit policy. Parsed JSON is reused by authorization and dispatch, while a later endpoint-specific byte limit remains enforced.\n\nTests cover exact CAS/hash/private storage, failures before activation, pinned policy intersection, case/traversal/credential/symlink rejection, actual browser allowed/denied reads and writes, version aliases, global execution denial and cross-repository activation races. Startup regressions ensure saved policies prevent trusted plugin initialization, interpreter probes and public phone history, and remove restricted results from shared feeds.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Project filesystem policy"},{"group":"Operate safely","id":"agent-containers","source":"docs/agent-containers.md","body":"# Agent containers\n\nChoose an explicit image and authentication volume before selecting container execution. A Docker installation alone does not make a provider ready to run inside that image.\n\n## Prepare an isolated agent\n\nAgent isolation runs the actual vendor CLI in a prepared Docker image. It is separate from\nthe existing container option for setup and verification commands. Set `loop.agentContainer`\non a run, for example:\n\n```json\n{\n \"agentContainer\": {\n \"image\": \"my-codeflow-agents:local\",\n \"authVolume\": \"codeflow-agent-login\",\n \"network\": true,\n \"memoryMb\": 4096,\n \"cpus\": 2\n }\n}\n```\n\nThe image must already exist locally and contain the official `claude`, `codex`, or `agy`\nbinaries used by the run. Missing images fail rather than being pulled or falling back to\nhost execution. Images should also provide the language tools required by the repository.\n\nThe process runs as your host UID and GID (65532:65532 where those IDs are unavailable).\nPrepare `/home/codeflow` so that this user can read and write it. `HOME` is `/home/codeflow`\nand `CODEX_HOME` is `/home/codeflow/.codex`. For persistent authentication and conversations,\nuse a named Docker volume created and authenticated by you inside the image. For example,\non macOS or Linux:\n\n```sh\ndocker run --rm -it \\\n --user \"$(id -u):$(id -g)\" \\\n --mount type=volume,source=codeflow-agent-login,target=/home/codeflow \\\n --env HOME=/home/codeflow --env CODEX_HOME=/home/codeflow/.codex \\\n --entrypoint codex my-codeflow-agents:local login\n```\n\nUse each vendor's own login flow in that container. The image or volume must have the correct\nownership before login; Docker's default root-owned empty volumes may need initialization.\nCode-flow never mounts your host home, reads or copies your cached provider tokens, or\nforwards host API keys into the container. Credentials baked into an image are accessible to\nanyone who can access that image; a privately managed named volume is preferable. A volume\npersists agent conversations as well as login state. Without one, CLI changes inside the\ncontainer are discarded, including newly written conversations needed to resume later.\n\nOnly the selected workspace is bound into the container, writable for write turns and\nread-only for read turns. Its `.git` marker and the verified repository Git metadata are\nmounted read-only. This allows inspection while preventing agents from rewriting repository\nmetadata; code-flow performs commits on the host after the turn. Git global and system\nconfiguration are disabled inside the container. Extra files generated by code-flow, such\nas a Codex output schema, are mounted individually read-only when needed.\n\nThe container uses Docker's isolated network by default because inference requires network\naccess. Setting `network: false` passes `--network none`; inference will then be unavailable\nunless the configured image supports an offline provider. There is no host networking or\nDocker socket mount. Linux capabilities are dropped, privilege escalation is disabled,\nprocesses are limited to 256, and memory and CPU limits are enforced. The container's own\ntemporary filesystem layer remains writable for vendor CLI state and installed-tool needs;\nit is discarded when the turn ends.\n\nLive team-tool bridges are not supported inside containers. The UI and adapter status report\nthis limitation, and team members use structured outcomes and replies on subsequent turns.\nCodex app-server still supports streaming, steering, and scoped one-shot approval over stdin.\nAdditional filesystem/network permission requests and session-wide policy amendments remain\ndeclined. Selecting full CLI trust does not remove the outer Docker container boundary.\n\nCancellation, timeout, stall, and shutdown remove the daemon-owned container as well as the\nDocker client. Container names are recorded in the process recovery journal so startup can\nalso remove containers left behind by a server crash before recovering worktrees.\n\nRun `npm run test:containers` for native ordinary and filtered runtime checks.\nThe tests build and remove small fake-CLI fixture images and never\nuse provider credentials or model quota. The regular suite also tests launch arguments,\nall adapter transports, daemon/client separation, cancellation, and timeout with a fake\nDocker runtime.\n\nPer-project read/write restrictions use the separate [filtered task\nbackend](filesystem-policy.md), with a fresh allowed-file Git namespace and\nexplicit review-branch import. Ordinary agent containers mount their worktree's\nfull Git common directory and cannot enforce those restricted reads.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Agent containers"},{"group":"Operate safely","id":"filtered-tasks","source":"docs/filtered-tasks.md","body":"# Filtered Docker tasks\n\nUse a filtered task when only selected project files should reach a container. Inspect the snapshot, provision its runtime explicitly, and review the returned branch before importing changes.\n\n## Run against a bounded snapshot\n\nProject → Repository tools → Filtered tasks → Filtered Docker task runs one explicitly requested provider turn against a bounded, isolated committed snapshot. This is the enforcing execution path for custom project file policies. Normal native, SSH, automated and multi-agent runs remain refused while any custom restriction is active.\n\nSelect the project/repository, supported provider (`claude`, `codex`, `agy`), an already prepared local Docker image, instructions and a 1–30 minute limit. Network defaults off; enable it deliberately when the provider needs remote inference. The service resolves the local image to an immutable SHA-256 ID and uses that ID for dispatch. No images are pulled, no host-provider probe or login runs, and no fallback launches a host CLI. A local Docker context is required.\n\n## Filesystem and runtime boundary\n\nOnly policy-readable regular committed files are exported. Every applicable current project policy and pinned policy intersects; choosing a different project cannot widen access. The export is a fresh Git repository with one synthetic commit and no original objects, history, remotes, alternates, hooks or configuration. Ignored/uncommitted files, private application memory, notes, context attachments and host credentials are not copied. Readable source symlinks/Gitlinks, path collisions and unsupported names are refused. Export limits are 500 files, 512 KB per file and 16 MB aggregate content.\n\nThe container receives only that export, its synthetic Git metadata mounted read-only, any readable copied files denied writes mounted read-only, and an exact generated schema file when the official adapter needs one. The parent job store, original repository/common Git directory, host home, authentication volumes, environment and Docker socket are never mounted or forwarded. Container labels and extra readonly mounts come from a server-only object-identity capability; serialized client settings cannot grant it.\n\nThe image root filesystem is read-only. `/tmp` is a 256 MB tmpfs and a fresh `/home/codeflow` is a 128 MB tmpfs, owned by the runtime UID/GID. Capabilities are dropped, privilege escalation is disabled, and PID, memory and CPU limits apply. The export is watched for excessive entries, depth, logical/allocated bytes and unsupported links; excess aborts the task and requires confirmed daemon removal. Final scans have the same aggregate entry/deadline bounds. Bind-directory monitoring is best-effort polling, **not an OS disk quota**: output can overshoot between scans. Newly generated forbidden output is refused wholesale; it never reaches the original repository.\n\nAn owner-prepared image and Docker's own isolation are trusted inputs. Its intentionally provisioned provider identity can access that provider, and enabled network can reach external services; this feature is not an internet-egress allowlist. The app does not inspect image credentials. Other owner-controlled OS processes and external repository edits are outside the container boundary.\n\n## Preparing authentication without host caches\n\nA login baked under `/home/codeflow` is hidden by the fresh home mount. Ordinary `agentContainer.authVolume` profiles are intentionally unsupported for filtered tasks: a shared volume can retain conversations or repository data from another policy. Prepare a private runtime with a launcher that copies **only its deliberately provisioned credential file** from an immutable in-image seed into the fresh runtime home, then executes the official CLI. Do not copy a host home, provider session directory, Git configuration or repository into the image.\n\nFor example, install the official Codex binary in a reviewed base image at `/opt/vendor/bin/codex`, and provide this `/usr/local/bin/codex` launcher:\n\n```sh\n#!/bin/sh\nset -eu\numask 077\nmkdir -p \"$CODEX_HOME\"\ncp /opt/codeflow-credential-seed/codex/auth.json \"$CODEX_HOME/auth.json\"\nprintf 'cli_auth_credentials_store = \"file\"\\n' > \"$CODEX_HOME/config.toml\"\nexec /opt/vendor/bin/codex \"$@\"\n```\n\nProvision a separate container from that base, with no host mounts or repository, using the vendor's own login flow. For Codex, choose file credential storage, set `CODEX_HOME=/opt/codeflow-credential-seed/codex`, then run the installed binary's `login --device-auth` flow if your account permits device login. Keep only the credential file in the seed, set its ownership for the runtime UID/GID, and save the deliberately authenticated runtime as a **private local image**. The app runs containers as the host UID/GID (65532 where unavailable). Follow [OpenAI's authentication documentation](https://developers.openai.com/codex/auth/) for file storage, device-login requirements and token handling.\n\nThe seed is itself a credential: anyone who can read the image can read it. Never publish that image. Its runtime copy can refresh during a turn, but the refreshed home is discarded; when its seed expires or is invalidated, provision a fresh private image instead of copying shared session caches back into the application. Claude and Antigravity need equivalent provider-specific launchers using their own supported login/storage flow. The app never reads, exports or copies OAuth credentials itself. An offline prepared provider needs no credential seed and can keep networking disabled.\n\nThe real Docker fixture tests this exact fresh-home launcher mechanism with an in-image dummy credential and fake provider binary, plus actual namespace/readonly enforcement. It does not authenticate real providers or consume model quota.\n\n## Review, import, cancellation and cleanup\n\nAfter the turn, daemon container absence must be confirmed before inspecting output. Untrusted exported `.git` is removed; the host never asks it to execute Git commands. Bounded regular bytes, additions, deletions and executable modes are validated directly. Changed paths must be both readable and writable under the pinned/current policies. Policy/project membership and source HEAD are rechecked before dispatch, after completion, preview and import. Stored policy snapshots must have valid content hashes; enforcement uses freshly authorized canonical rules. Stored source blob identities must match their exact original committed paths.\n\nPreview shows bounded original/result content, hashes and any binary/truncation flags. **Import into new review branch** is separate and explicit. Import checks the exact job revision/result hash/source HEAD, current policy identities, original changed-file bytes/modes, original staged blob/mode and new-path absence. These checks repeat after the durable importing receipt. Host Git uses a sanitized environment, disabled replacements/lazy fetch/fsmonitor/hooks/global/system attributes, disabled automatic maintenance and object-only writes without filters. A private index starts from the original source tree; only permitted changes are replaced. An atomic Git transaction verifies original HEAD and creates a uniquely named `codeflow/filtered-…` branch. Original checkout, index and denied base-tree files stay unchanged. The permanent commit message contains only the task ID; private instructions stay in the application record.\n\nPreparing/running/importing receipts are fsynced before their effects. A restart converts unfinished phases to `unknown`, never retries or imports them. A running/preparing cancellation aborts preparation/provider work, waits for it to settle, removes owned labelled daemon containers and verifies absence before marking stopped. An interrupted import requires **Inspect interrupted import receipt**: an exact matching branch proves publication; proven absence permits a separately requested future import; a mismatched branch stays blocked. Unknown daemon receipts also block policy activation until explicitly cancelled and confirmed stopped.\n\nHistory holds at most 100 jobs. **Delete private task and export** uses the current revision and is available only for terminal, confirmed-stopped jobs without an ambiguous import. It removes the private record/export, fsyncs the parent and preserves any imported review branch. There is no implicit retention deletion, branch removal or automatic merge.\n\n## API and tests\n\nAll endpoints are owner-only, including `/api/v1` aliases. `GET /api/filtered-tasks?project=…` returns metadata; `GET /:id` returns detail only while its current policy identity still matches. `POST /api/filtered-tasks` accepts `{project,repo,task,agent,image,network?,maxMinutes?}` and returns 202. `POST /:id/preview-import` produces a bounded preview; `/import` accepts `{revision,expectedHead,resultHash}`. `/cancel` and `/receipt` accept `{revision}`. `DELETE /:id` accepts `{revision}`. Closed routes reject extra path segments. These requests participate in the filesystem activation barrier; policy activation refuses any preparation, import or unconfirmed daemon work.\n\nRun the regular filtered task, lifecycle, limits and browser suites for CAS/races/restore/UI evidence. Run `CODEFLOW_TEST_DOCKER=1 node --import tsx --test test/filtered-tasks-docker.test.ts` for real Docker namespace, original Git/denied blob absence, readonly mounts, fresh-home credential seeding, branch import and cancellation removal. The Docker fixture builds/removes its own small image and never contacts a model provider.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Filtered Docker tasks"},{"group":"Operate safely","id":"dependency-discovery","source":"docs/dependency-discovery.md","body":"# Dependency discovery\n\nExpand Getting started in the composer and select Check all dependencies. Resolve required tools first; optional Docker, terminal and snapshot capabilities can be configured when you need them.\n\n## Check readiness\n\nExpand **Getting started** and choose **Check all dependencies**. The report separates required tools from optional capabilities, explains missing authentication or compatibility, and provides a concrete next step. A successful run requires Node.js 22.13 or newer, Git 2.38 or newer, and at least one compatible provider CLI signed in through its own authentication flow. Unknown authentication or unverified compatibility is displayed as unknown rather than ready.\n\nProvider and Git checks reuse the application's doctor implementation. Optional terminal inspection checks whether the configured Python can load its pseudo-terminal modules and reports the actual shell fallback. Browser snapshot inspection uses the same Chrome-compatible browser discovery as snapshots and runs a bounded version probe. Docker inspection checks the CLI version and asks its daemon for its version; a present CLI with an unavailable daemon is reported separately. Docker availability does not prove a provider image or authenticated volume has been prepared.\n\nThese are read-only checks. They do not execute repository commands, install packages, sign into accounts, pull images, or change server configuration. Optional failures do not block normal host CLI runs. A missing Python helper still permits the line-based terminal, and a missing snapshot browser does not remove the embedded app preview. Existing `CODEFLOW_PYTHON` and `CODEFLOW_CHROME` choices are inspected without being changed.\n\n`GET /api/doctor/dependencies` returns `{checkedAt,node,git,providers,optional}`. Optional entries identify `terminal`, `snapshots`, and `docker`, each with installation/readiness state, detected version/path when available, details, and remediation text. Provider probes retain the existing doctor timeouts; optional browser/Docker probes are bounded to five seconds and pseudo-terminal discovery retains its ten-second limit. Probe commands use executable argument arrays, never a shell command assembled from browser input.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Dependency discovery"},{"group":"Operate safely","id":"maintenance","source":"docs/maintenance.md","body":"# Backup and maintenance\n\nCreate an encrypted backup before restoring or removing stored data. Read the offline requirements and inspect the retention preview before applying a maintenance operation.\n\n## Protect your local history\n\nStop the code-flow server before running maintenance. Every operation against an existing data directory takes the same exclusive `server.lock` as the server and refuses to run while another server holds it. A restore writes a new directory and never merges into existing data.\n\nThe commands use `CODEFLOW_HOME` by default. Pass `--home /absolute/data/path` to choose another data directory.\n\n## Encrypted backup and restore\n\nSupply a password through `CODEFLOW_BACKUP_PASSWORD` using your shell or secret manager. Do not put it in command arguments. Passwords must contain 12–1,024 characters. The CLI never prints the password or file contents.\n\n```sh\nnpx tsx scripts/maintenance.ts backup --output /backups/codeflow.cfb\nnpx tsx scripts/maintenance.ts restore --input /backups/codeflow.cfb --destination /new/codeflow-data\n```\n\nBackups use AES-256-GCM authenticated encryption with a fresh 16-byte salt, 12-byte nonce, and a 32-byte key derived with scrypt (`N=32768`, `r=8`, `p=1`). The compressed manifest contains each file's length and SHA-256 digest. No plaintext archive is written to disk. Output files use mode `0600`; restored directories use `0700` and files `0600` on supported platforms.\n\nBackups are limited to 256 MiB of file data and 20,000 files. Restores bound archive and decompression size and reject invalid manifests, duplicates, traversal paths, damaged data, wrong passwords, and existing destinations. The output directory must exist and the backup must be outside the data directory. Output files are created exclusively and existing backups are never overwritten.\n\nSymbolic links, special files, temporary files, server locks, process registries, phone settings/device sessions, shared account password/session stores, session credential files, team connection/capability files, saved team credentials under `team-connections/` and the legacy/local team link file `team-link.json` (this computer's runner credential for a team server, with its temporary and set-aside copies), remote-worker private capability receipts, `.env` files and private-key files are omitted. The command reports every omitted path. Restore also excludes this material from older archives. Recreate shared accounts, reconfigure phone pairing and credentials and connect a linked computer to its team server again after restoring; the team board itself (`team/`) and the local attempt records (`team-link/attempts/` and `team-runners/<connection id>/attempts/`) are included.\n\nRestored automations are disabled, their webhook tokens are rotated, and queued deliveries are cleared. Review each automation, update its webhook sender with the new token, and deliberately enable it again. This prevents old schedules or deliveries from running immediately after recovery.\n\nRestored external notification settings are also disabled. Pending or in-flight notification deliveries are cancelled; destinations must be reviewed and deliberately enabled again.\n\nRestored deferred jobs are never dispatched automatically. Pending, waiting, claimed, failed and unknown jobs become cancelled with a restore-history entry. Existing started receipts remain metadata. Review the recovered configuration and explicitly retry a cancelled job only when you intend to launch it; saved claim and run IDs remain available for investigation. Malformed deferred storage aborts the restore.\n\nRestored coordinated changes also disable all implicit dispatch. Exact launch, dependency and shipping receipts remain available for manual investigation; pending tasks become cancelled, and claimed side effects become unknown. Deliberately recover or retry only after reviewing existing runs and local commits. Invalid coordinated storage aborts the restore.\n\nGit worktrees and repositories outside `CODEFLOW_HOME` are not included. Back them up separately when they contain needed commits or uncommitted work. The password is required for recovery and cannot be recovered from code-flow.\n\n## Saved per-type retention\n\nOpen **Settings → Data & storage** to save separate ages for archived run records, attachments, private/team memory and linked workspaces. Every type starts disabled; a blank age keeps that data. You can protect specific run and memory IDs. Settings are revisioned, and concurrent saves cannot overwrite each other. Saving settings never deletes data.\n\nSelect **Preview saved retention policy**, review the exact targets and protected reasons, then download the manifest. The preview fixes the cutoff, policy revision, source references and file identities. It contains paths and IDs, without memory bodies or titles. It expires after 24 hours. Stop the server before applying that downloaded file:\n\n```sh\nnpm run maintenance -- retention-policy --plan /downloads/codeflow-retention.json --apply --home /absolute/data/path\n```\n\nFor an entirely offline workflow, save settings from a JSON file, produce a preview and then apply that exact preview. An existing output file is never overwritten:\n\n```sh\nnpm run maintenance -- retention-settings --input /path/retention-settings.json\nnpm run maintenance -- retention-policy --output /path/reviewed-plan.json\nnpm run maintenance -- retention-policy --plan /path/reviewed-plan.json --apply\n```\n\nExample settings keep attachments and workspaces indefinitely, retain private/team memory for 180 days, and retain eligible archived run records for 90 days:\n\n```json\n{\n \"days\": { \"archivedRuns\": 90, \"attachments\": null, \"memory\": 180, \"worktrees\": null },\n \"keepRunIds\": [\"important-run-id\"],\n \"keepMemoryIds\": []\n}\n```\n\nApply takes the exclusive server lock and rebuilds the manifest using its original cutoff. Any changed policy, file identity, source reference, worktree state or supplied manifest rejects the entire plan before deletion starts. A partial scan cannot be applied. The scan bounds are 2,000 runs, 50,000 live memory entries, 1,000 targets, 100,000 files and 256 MiB of read data; individual logs are limited to 64 MiB. Keep other tools from changing repositories during offline apply. If an I/O error occurs after deletion begins, the result lists applied, failed and remaining targets; stop and create a fresh preview before another attempt.\n\nRunning, paused, interrupted, stopped, unarchived, pinned and watched runs are protected. Retained child runs, selective-rerun baselines, enabled automations and pending deferred jobs preserve their parent sources. Retained coordinated-change launch, dependency and shipping receipts protect their exact run records. Project knowledge is never bulk-deleted. Pinned memory, memory used by active/resumable/kept runs, and sources referenced by retained derived memory are protected. Remaining live memory preserves its origin transcripts, attachments and workspaces.\n\nEligible old private/team memory bodies are erased atomically while their provenance tombstones remain. Attachment retention deletes only bounded regular files in known staging/generated context directories and refuses tracked repository files or symlinked ancestors. Workspace retention requires verified managed linked Git ownership, a clean index and worktree, no hidden tracked changes, no nested repository/submodule, and no Git lock. Removal uses Git without force and preserves branches. Run retention removes only the selected terminal archived logs, journals and checkpoints. These changes remove recovery evidence, so review the preview and back up data you still need.\n\nThe older `retention --days 90` command remains an age-only report and does not assess pins or provenance. Its unguarded `--apply` behavior has been removed. Existing scripts must migrate to a reviewed `retention-policy` manifest; the compatibility alias `retention --plan /path/reviewed-plan.json --apply` uses the same protected exact-plan apply.\n\n## Log compaction\n\n```sh\nnpx tsx scripts/maintenance.ts compact --id RUN_ID\nnpx tsx scripts/maintenance.ts compact --id RUN_ID --apply\n```\n\nCompaction previews savings unless `--apply` is supplied. It keeps the first and latest run metadata snapshots and every transcript, step, check, and log event. Recovery journals remain byte-for-byte unchanged. Intermediate metadata snapshots are removed; their event sequence numbers are not renumbered. The replacement is synced and renamed atomically, and its old optional checkpoint is invalidated. Only terminal run logs up to 64 MiB are supported; incomplete or corrupt logs are rejected.\n\n## Startup checkpoints\n\n`appendLine(file, event, { checkpoint: { seq, run } })` captures a complete run snapshot, including steps, at call time. The existing serialized writer appends the authoritative event, reads its exact file identity, and atomically writes `<run-id>.checkpoint.json` before the next queued append.\n\n`readRunCheckpoint<T>(id)` returns `{ run, seq }` only when the version, checksum, run ID, steps, safe sequence number and source file identity match. Source identity includes length, nanosecond modification/change times, device and inode. A subsequent append, replacement, malformed or oversized cache, or missing file falls back to journal reconstruction. Reads are bounded to 16 MiB. Cache write failures update `checkpointHealth` and never reject an authoritative append or poison the write queue.\n\nCheckpoints speed up startup but are optional projections. JSONL logs and recovery journals remain the source of truth and are preserved until an explicit retention or compaction operation.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Backup and maintenance"},{"group":"Build and extend","id":"architecture","source":"docs/architecture.md","body":"# Architecture and development\n\nUse `npm run dev` for UI work without provider quota. Read the source layout below, then run type checking and the tests relevant to your change. The documentation site shares the app’s theme and builds alongside the UI.\n\n## Layout\n\n```\nui/*.tsx UI: Preact + TypeScript, compiled against the server's own types\n (app: shell and top bar · rail: the sidebar · composer · run: the run thread · review · insights · automations · projects\n · project: a project's page · side: the side panel, its files and notes · side-chat, changes, terminal, browser: its other tabs\n · panel-bits: what the tabs share (folders, the strip of open sessions, menus))\nui/bits.tsx small shared pieces: agent tags, the Markdown renderer, QR codes\nui/collab-*.tsx the Team page: the board and its inbox (collab-board), a task with its attempt, checkpoints, reviews and discussion (collab-task), Settings → Connections → Share access → People (collab-settings); collab-state.ts holds their signals and API helpers\nui/styles.css, ui/fonts the stylesheet (the paper theme, light and dark) and its font (Inter; licence included)\nui/theme.ts light or dark: follows the system until you pick one with the top bar's theme button\nscripts/build-ui.mjs builds hashed ESM chunks and CSS, then atomically publishes public/index.html (npm start builds it)\nscripts/check-node.mjs stops npm start and npm run dev on a Node older than 20, saying why\nscripts/pty.py the Terminal's pseudo-terminal (Python 3 standard library, no native npm module): runs your login shell in a pty, relays its bytes, takes resizes and reports the shell's pid, its terminal and the foreground on fd 3; when it ends, it hangs up everything in the shell's session\nsrc/server.ts HTTP API + SSE on 127.0.0.1 (Host/Origin checked, session cookie or Bearer token)\nsrc/stream.ts one event stream per browser window (/api/stream), carrying every topic it watches: runs, run:<id>, shell:<id>\nsrc/store.ts run logs and replay journal, per-run batched write queues, the one-server lock\nsrc/work-queue.ts bounded concurrent work with ordering within each run\nsrc/transcript.ts bounded reverse byte-offset transcript pages\nsrc/sse-writer.ts byte-bounded backpressure-aware stream output\nsrc/run-history.ts indexed summaries/search and stable cursor pagination\nsrc/performance.ts bounded local runtime diagnostics\nsrc/run-log.ts what a window is sent to show a run (its latest snapshots); run snapshots on disk as patches, and compacting old logs\nsrc/teams.ts provider-independent team members, tasks, messages and context projections\nsrc/memory.ts private/team/project memory, revisions, provenance and scoped retrieval\nsrc/memory-sqlite.ts SQLite transactions, JSON migration, indexed keywords and evidence-linked graph\nsrc/team-memory.ts run/project scope and Git applicability for memory retrieval and proposals\nsrc/team-mailbox.ts validated atomic collaboration commands, requests and waits\nsrc/team-scheduler.ts recorded completion batches with concurrent worker lifetimes\nsrc/team-tools.ts expiring member capability service; stdio MCP client in team-tool-client.mjs\nsrc/team-evaluation.ts immutable evaluations and evidence-gated rollout decisions\nsrc/projects.ts projects: named groups of repos, and which project a new run goes in\nsrc/log.ts code-flow's own log, ~/.codeflow/server.log (rotated at 5 MB)\nsrc/env.ts the environment of everything code-flow starts: yours, minus its token, npm_* and its own node_modules/.bin\nsrc/integrity.ts finds changes to tests that could make a check pass without a fix\nsrc/diff.ts fits a long diff into a prompt (whole files first)\nsrc/learnings.ts the repo's conventions files, and problems reviewers keep raising\nsrc/slots.ts caps how many agents and checks run at once\nsrc/stats.ts Insights: per agent, per pattern, comparisons; ranking for Auto\nsrc/ship.ts per-file diffs, safe merges, pull requests, worktree cleanup\nsrc/github.ts gh: issues, PRs, checks, failed-job logs, comments, pushes\nsrc/watch.ts PR follow-through: polls watched PRs and sends CI failures and comments back\nsrc/routing.ts \"Auto\" roles: pick an agent whose plan has room (and that passes most in this repo)\nsrc/templates.ts starting points (fix an issue, fix failing CI, council review…) and sweeps\nsrc/sweeps.ts sweeps: list issues, Dependabot PRs and alerts; start one run per picked item\nsrc/automation.ts automations: schedules, webhooks (Bearer or GitHub signature), firing runs\nsrc/tailscale.ts bounded, read-only Tailscale detection for the remote listener\nsrc/remote.ts the phone link: read-only view (server-rendered), settings for full phone control\nsrc/collab/*.ts the team board: model (types, bounds, validators, the task state machine), store (CODEFLOW_HOME/team/, atomic writes with a write-behind for heartbeats and progress only, authorization checked in the store), api (/api/team/* for owner, shared and runner principals, with its SSE stream), checkpoints (Git bundles whose only ref is refs/codeflow/team/<checkpoint id>, the id minted by the runner before bundling), hub-client (LocalHub and RemoteHub: the same runner code against the in-process store or a team server over HTTPS), link, runner and link-api (the linked computer: team-link.json, local attempt records, hand-overs, reconciliation and the recovery pass, /api/team-link/*)\nsrc/engine.ts RunManager, TeamContext (ask / worktree / commit / diff), event log\nsrc/patterns/*.ts the team patterns, written against TeamContext\nsrc/adapters/*.ts AgentAdapter per CLI → normalized AgentEvent stream\nsrc/git.ts every git call (bounded, never prompts), worktrees with their submodules, branches, commits, diffs, merges\nsrc/repo-tree.ts file tree, search and file contents for the Files tab\nsrc/shell.ts the side panel's Terminal: shells kept open, in a pseudo-terminal through scripts/pty.py (busy while a program other than the shell is in the foreground; never closed to make room while it has jobs in the background, found with ps); line-based with a marker after each command when there's no python3\nsrc/preview.ts the side panel's Browser: a proxy per local app, on a port of its own, that adds the overlay (src/preview-overlay.js: pick, box, console, adjust), rewrites the app's Content-Security-Policy so only code-flow (and the app itself) can frame it, and keeps code-flow's cookies from the app\nsrc/snapshot.ts the Browser's Snapshot: a screenshot of any address in a headless Chrome with a fresh profile, driven over its DevTools pipe (no debugging port), one at a time\nsrc/notes.ts the side panel's Notes, ~/.codeflow/notes.json\nsrc/context.ts validates and stages task context for agents\n```\n\n- **AgentAdapter** (`src/adapters/types.ts`): `run({prompt, cwd, mode, resumeSessionId}, onEvent) → {ok, finalText, sessionId, errorKind, resetsAt}`. `sessionId` is only ever one the CLI confirmed in that turn, never a session that failed to resume. A failed turn says why in `errorKind` (`usage_limit`, `auth`, `session_not_found`, `cli_incompatible`, `stalled`, `transient`…), read from the CLI's own signals (`src/adapters/errors.ts`), and the engine retries, stops or starts a fresh session on that. Adapters convert each CLI's JSONL into `text | thinking | tool_call | tool_result | usage | status | error` events. An adapter whose turns take input while they run calls `onSteerable(steer)` once its turn can be steered. The engine then hands queued notes to `steer`. `src/adapters/codex-app-server.ts` turns app-server notifications into the events `codex exec --json` prints, so both Codex transports share one handler.\n- **TeamContext** gives patterns a few primitives. `ask()` runs one agent turn as a visible step, and `resume` continues that agent's own conversation. `worktree()`, `commit()`, `diff()` and `artifact()` handle git. `parallel()` runs branches side by side (race contestants, workers, debaters); when one throws, the others are stopped and waited for, so nothing keeps running after the run ends. Structured hand-offs (review verdicts, judge picks, subtask lists) use a fenced ```` ```json ```` block that `extractJson` parses. A pattern reads what changed through `changedFiles()` and `testChanges()`, not git directly: on a resume the worktree on disk already has the later commits, and these read at the commit the replay has reached.\n- Every event is appended to `~/.codeflow/runs/<run>.jsonl`. A run snapshot that follows another is written as what changed (`run-patch`: fields set or removed, and the new end of a string or list that kept its start), with a full snapshot first, each time the run stops running, and now and then; reading a log turns patches back into snapshots (`src/run-log.ts`). Logs from before this are compacted the same way once loaded, in the background, and only when they read back exactly as they were. Worktrees live in `<repo>/.worktrees/<run-id>/` (see [Worktrees and branches](getting-started.md#worktrees-and-branches)).\n- **Live updates**: a browser allows 6 connections per host over HTTP/1.1, shared by all its tabs, and an event stream holds one while it's open. So there's one stream (`GET /api/stream`), with topics subscribed on it (`POST /api/stream/<client>`), and the browser's windows share it (`ui/stream.ts`): the first to take a Web Lock holds it for all, the others send it what they watch and get their events over a BroadcastChannel, and when it closes the next one takes over (on the phone, over plain HTTP, there are no Web Locks: each window has its own). The topics are `runs` (the run list), `run:<id>` (the run as it is now, then its latest snapshots, every agent event and log line, then live events; its cursor is the run's own, so it holds across a restart) and `shell:<id>` (a terminal). A run shown in two places (the run view and a side chat) is one subscription (`ui/run-feed.ts`), panes that are hidden or closed unsubscribe, and a subscription that comes back (or a stream that reconnects) passes a cursor, so it gets only what it missed. The older per-feature streams (`/api/events`, `/api/runs/<id>/events`, `/api/shell/<id>/events`) still answer, for a page left open across an update.\n\n**Auto team** uses the same engine with validated plans and bounded event-driven scheduling (with saved-wave compatibility) (`src/patterns/auto-team-model.ts`, `auto-team.ts`). Stable member IDs separate identity from provider; turn metadata binds settings, session, task, usage and selected context. Questions yield a turn rather than holding a slot. Accepted plans and admission decisions replay from the journal; strict journal failures stop dependent execution. A stopped workflow resumes through completed waves and final verification before beginning another user-authorized allocation. Project memory selections journal references, not bodies; replay resolves only still-applicable exact revisions. See [Auto team](auto-team.md) for the public behavior and trust/retention boundaries.\n\n### Adding a pattern\n\nWrite a `Pattern` (roles, options, `run(ctx)`) in `src/patterns/`, then add it to `src/patterns/index.ts`. The UI builds its form from the pattern definition.\n\n`ctx.artifact()` proves a result whose check passed (fail-to-pass, see `TeamContext.prove`), so a new pattern gets proofs without doing anything. To act on one before publishing, as Pipeline does before review, use `proveOrAsk()` from `src/patterns/prompts.ts`, and `proofEvidence()` to show it to a reviewer or judge.\n\nA pattern with a reviewer asks for `REVIEW_SCHEMA` with `reproRule()` and `REVIEW_SHAPE`, then passes the verdict to `checkReview()` (all in `src/patterns/findings.ts`): it runs each finding's repro on the result (`TeamContext.reproduce`), records the review on the run, and marks what blocks approval (`blocks` in the engine). `approves()` is the gate, and `findingsForBuilder()` is what the builder gets back.\n\n### Adding an agent\n\nFor a CLI that speaks ACP v1 over stdio, use [Settings → Agents & models](acp-agents.md). The shared adapter in `src/adapters/acp.ts` handles transport and normalized events; `src/acp-connections.ts` persists owner-configured invocations and pins them to runs.\n\nImplement `AgentAdapter` in `src/adapters/`, then register it in `src/adapters/index.ts`. Add a fake for it in `test/fake-agent.mjs`.\n\nOwner-configured providers and workflows can also use [versioned extensions](extensions.md) without modifying the built-in registry. They use namespaced IDs, explicit capability/API negotiation, reviewed bundle hashes and per-run version pins. Additional provider capabilities remain unknown until separately verified.\n\nThe HTTP boundaries for shared access, the team board and its linked runners, filesystem policy, coordinated changes, code hosts and workspace operations live in separate modules. SSH framing/artifacts/receipts, durable queues, memory claim analysis and Git shipping each have independent contracts and focused tests. [Run storage](run-storage.md) separates recovery/projection from execution and bounds archived detail hydration. These boundaries retain server-owned authority and replay identities rather than moving them into client request bodies.\n\n## Development\n\n`npm start` is your server (port 4317, `~/.codeflow`). `npm run dev` is a development server (port 4318, `.codeflow-dev/`) with **fake agents**, so working on code-flow never touches your real runs or quota. It puts `test/fake-bin` on `PATH` by absolute path and refuses to start if `claude`, `codex` or `agy` would still resolve to a real CLI. `npm run dev:real` is the same dev server with the real CLIs. Both rebuild the UI and restart the server as you edit.\n\n```bash\nnpm run dev # http://localhost:4318, fake agents\nnpm run typecheck\n```\n\n## Tests\n\n```bash\nnpm test # unit + workflow tests against fake CLIs\nnpm run test:e2e # the real UI in Chrome (headless) against a server with fake agents\n```\n\nCI (`.github/workflows/ci.yml`) runs the typecheck and `npm test` on every push and pull request: Ubuntu with Node 22.13 and 24, macOS with Node 22. The required browser job exercises critical workflows and accessibility. A separate job tests the packaged VS Code extension in the actual editor host. [Provider compatibility](provider-compatibility.md) continuously probes official version/help contracts without account or inference requests.\n\nThe suite runs every pattern end to end against fake `claude` / `codex` / `agy` binaries (`test/fake-bin`). Each fake emits that vendor's real JSONL format, so tests cost no quota.\n\n- The fake `codex` also serves the app-server protocol (`test/fake-codex-app-server.mjs`). Its threads persist across processes, so resume and per-turn token counts are tested. A prompt containing `WAIT-FOR-STEER` keeps a turn open until a note is steered in. `test/codex-app-server.test.ts` covers mid-turn notes, declined approvals, cancellation, and the `exec` fallback.\n- A fake `gh` keeps issues, PRs, checks, comments and security alerts in a JSON file and pushes to a local bare repository, so the GitHub flows (fix CI → push → watch → fix again, and sweeps of issues, Dependabot PRs and alerts) are tested without touching GitHub.\n- A fake `docker` records arguments for deterministic orchestration tests. Opt-in native Docker cases separately test actual filesystem mounts, credentials and cancellation; fixtures alone do not establish container isolation.\n- The suite covers review loops, races with a failing contestant, critique rounds, merge-conflict resolution, and the guarantee that your checkout is untouched.\n- `test/ship.test.ts` covers side chats too: sessions and messages kept apart from the run's thread, a budget of their own behind the team, an Ask that keeps the verdict and your edits, and files sent to a run whose worktrees were removed.\n\n`test/hardening.test.ts` covers the guarantees about processes, runs and tests:\n\n- a background process an agent leaves behind neither hangs a step nor survives it;\n- a run can't be resumed twice at once;\n- a commit made in a read-only step is undone;\n- a loop doesn't accept a pass made by deleting the failing test;\n- race commits name no vendor;\n- the builder's test edits are undone;\n- retries, usage-limit stops and token accounting work.\n\nReliability has tests of its own:\n\n- `test/foundation.test.ts`: the child environment, numeric settings, bounded git, the repo lock, and orphaned processes reaped at the next start;\n- `test/store-reliability.test.ts`: a failed write can't corrupt the next line of a run log; the server lock is taken over from a reused pid or a previous boot, and of several servers starting at once exactly one gets it;\n- `test/stream.test.ts` and `test/run-log.test.ts`: a window's one stream (subscriptions changed while it's open, topics that can't be watched, a cursor from another server start), a run's replay with only its latest snapshots (and how many fewer bytes that is), snapshots written as patches that read back exactly, full ones where they belong, and old logs compacted on load. `test/stream-client.test.ts` is the UI's side (requests batched, a refused topic not asked for again, reconnecting with cursors), and `test/e2e/connections.e2e.ts` opens a run view, a side chat, two terminals and a second tab in one browser and checks requests and keystrokes still go through, hidden panes stop watching, and the stream passes to the other tab when its tab closes;\n- `test/git-reliability.test.ts`: worktrees get their submodules (nested, in parallel, or a warning when one can't be cloned), and the Files tab's git calls;\n- `test/side-panel.test.ts`: the side panel's server side: reading a file (and never one outside the repo), notes that persist, shells whose `cd` holds between commands and whose exit codes come back, a new shell never closing a busy one or one with a job in the background, terminals that hang up their `nohup`'d and disowned jobs when code-flow stops and that still start in a repo pinning a Python its version manager doesn't have, the list of running apps, and the browser's preview proxy (the overlay added, frames allowed, the app's own host and origin, WebSockets, and the Host check);\n- `test/preview.test.ts`: the preview proxy against a stand-in dev server: where the overlay goes in a page, the app's policy kept but frameable, the host name and code-flow's cookies, compressed pages, connections that close when the browser's do, and the overlay itself in headless Chrome (Box doesn't click through, no passwords in snapshots, console reports; skipped without Chrome);\n- `test/snapshot.test.ts`: Snapshot's checks on the address and size, the viewport and the whole page checked pixel by pixel (a desktop page at phone width zoomed out, not cut off), one Chrome at a time, a page that can't be opened, an `alert()`, a Chrome that dies (and the next snapshot still working), and no profiles left behind (skipped without Chrome);\n- `test/browser.test.ts` and `test/chat-views.test.ts`: the side panel's UI logic, bundled with esbuild: which addresses the Browser believes from the page, where pins can be sent and how they're named, comment drafts shared by the review and the Changes tab, and side chats kept out of the run's thread;\n- `test/engine-reliability.test.ts`: parallel branches that stop together, steps that always end, read-only that fails closed, worktrees set up again after an interrupted setup, retries that continue instead of redoing, dead sessions, usage limits with reset times, lockdown without network, and unique run ids;\n- `test/engine-review.test.ts`: a Test-first run stopped in its second round and resumed ends with the same tree as one that wasn't, Stop reaching a resume that's starting, a second conflict resolution committed on its own, signed-out contestants not journaled and usage limits stopping a race, notes no turn took, the council restoring confirmed findings, reviewers' commands that never ran their test, slot waits outside the time budget, and archiving (refused while busy; the run's folder and fetched PR head cleaned up);\n- `test/security-review.test.ts`: a webhook caller's text fenced and its run marked, signed deliveries sent again with a new id, the app page's framing and caching headers, the first-browser claim ending at any sign-in, and Snapshot keeping away from loopback and metadata addresses;\n- `test/adapters-review.test.ts` and `test/github-review.test.ts`: Claude's read-only git that can't write files, error kinds read from the CLI's own error line, events a CLI prints that code-flow doesn't know, Codex falling back when resume rejects a parameter, `gh` output decoded whole, code scanning messages fenced, copied secrets kept out of commits, copies that never go through a symbolic link, merges refused mid-rebase, slow post-merge hooks, and quiet PR watches polled less often;\n- `test/ui-review.test.ts`: the run's own check winning over the repo's on Edit & run again, stale Files answers dropped, `safeHref`, and the Markdown renderer keeping raw HTML and `javascript:` links as text;\n- `test/adapters-reliability.test.ts` and `test/health-routing.test.ts`: the stall watchdog, the grace after a result, crashes mid-stream, dead sessions, unknown flags, huge lines, typed errors, Health's flag checks, and Auto skipping a signed-out CLI. The fakes simulate these with `FAKE_SLEEP_VENDOR`, `FAKE_HANG_AFTER_RESULT_VENDOR`, `FAKE_CRASH_VENDOR`, `FAKE_UNKNOWN_FLAG`, `FAKE_HUGE_LINE_MB`, `FAKE_CLAUDE_RESETS_AT`, and for app-server `FAKE_CODEX_SLOW_INIT_MS`, `FAKE_CODEX_INIT_CRASH`, `FAKE_CODEX_HANG_METHOD` and `FAKE_CODEX_SERVER_REQUEST`.\n\nThe GitHub tests cover untrusted issues (lockdown, fenced text, Docker or your confirmation) and PR watches that act only on comments from you and the people you allow. The e2e test also covers sign-in, Insights and run search. Its terminals run bash with a home of their own, so your dotfiles and shell history stay out of it.\n\n## What's next\n\n- Real approvals through `codex app-server` (it already declines approval requests): ask you in the UI instead.\n- A container option for the agents themselves (today only checks and setup run in Docker).\n- Read PR line comments (review threads), not just conversation comments and review summaries.\n- Linear as a source, next to GitHub issues.\n\n## Dynamic team control and replay\n\nA live tool command and its receipt are fsynced together before acknowledgement. The scheduler records which command batch it consumes at each boundary, so replay cannot inject a future reply into an earlier task. Completion batches likewise record observed order before serial integration. One server owns the journal; no dispatch lease or outbox is needed. See the [implementation plan](dynamic-teams-implementation-plan.md) for the invariants.\n\nThe scoped loopback bridge is separate from the user API and its bearer token. Official adapters receive a temporary stdio MCP configuration for supported turns. Memory tools inherit the active member, project, task and source provenance from the engine. Native spawning controls, sandbox support and nested usage limitations are exposed through the capability inventory. Supervisor shares decomposition/integration primitives while retaining its public ID and historical replay identities.\n\nSee [Performance and resource boundaries](performance.md) for the paged UI stream, lazy assets, worker reuse, storage ordering, diagnostics and load benchmark.\n\n## Documentation system\n\n`scripts/build-docs.mjs` renders the Markdown guides into `public/docs` for the local application and `dist/docs` for static hosting. The build reads the theme/font section from `ui/styles.css`, so documentation colors and typography follow the app. `docs/site` contains the navigation groups and the small browser layer for search, appearance and help. The application menu and command palette both link to `/docs/`.\n\n`src/docs-help.ts` serves the local files and runs bounded, owner-authenticated CLI questions using the same provider adapters as the application. Source selection is deterministic local keyword retrieval; there is no embedding service or hosted AI backend. Static builds omit the local capability marker. The host packager includes the documentation's UTF-8 assets, with screenshots/fonts embedded in the generated files.\n\nSee [Documentation and GitHub Pages](documentation.md) for authoring, preview, screenshot capture and deployment. `npm run test:docs` checks guide links and the help service; `test/e2e/docs.e2e.ts` exercises local and static browser behavior.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Architecture and development"},{"group":"Build and extend","id":"api","source":"docs/api.md","body":"# Local API and command-line client\n\nUse the owner token with the local API or the bundled command-line client. Keep credentials out of repository files and shell history, and inspect an operation’s request and response contract before scripting mutations.\n\n## Use the authenticated client\n\nUse `npm run client -- --help` for the HTTP client. It requires the explicit\n`CODEFLOW_TOKEN` printed/configured by the server and accepts `CODEFLOW_URL` for\nanother configured origin. It never reads agent login files. JSON files and\nshell arguments are treated as data, not executed. Output files are created\nprivately and never overwritten.\n\n`/api/v1/*` exposes the same authenticated operations as `/api/*`.\n`GET /api/v1/capabilities` lists the new stable operation families. Existing\nunversioned clients remain compatible. Authentication, Host, Origin and phone\nsession checks apply identically to both prefixes.\n\nPaths below are relative to `/api` (or `/api/v1`). Requests with bodies use JSON.\nSuccessful mutations use the status shown where relevant; validation errors use\n`400`, missing resources `404`, and explicit concurrency conflicts `409`.\nErrors contain an `error` string. No client-side mutation retry is automatic.\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `POST /projects/:id/repositories` | `repo` absolute Git path | Owner-only explicit membership addition; canonicalizes the root and returns the updated project. Existing membership is idempotent; the 50-repository limit applies. |\n| `POST /runs` | Existing run fields; optional `projectSelection: \"explicit\"` with `project` | Explicit selection requires an existing project and current canonical repository membership. Rejected membership never silently creates or extends a project. Callers omitting the marker retain legacy placement behavior. |\n| `POST /improve-prompt` | `repo`, `text`, optional `mode=quick\\|repo`, `agent`, `context`, `source`, `execution=local` | Owner-only local prompt rewrite in a disposable private Git directory; returns `{prompt, summary, agent}` after cleanup. |\n| `POST /readiness` | `repo`, optional `baseRef`, `setup`, `check`, `execute`, `pattern`, `roles`, `providers` | Inspection by default; `execute: true` runs commands in a temporary worktree at a pinned commit. Provider scope is described below. |\n| `GET /verification/profile` | `repo`, `preset` query | Detect lightweight, standard or thorough checks. |\n| `POST /verification` | `repo`, `profile` or `preset`, optional `baseRef`, `baseline`, `setup`, `repeats` | Execute bounded required gates on isolated checkouts and return structured evidence plus Markdown. |\n| `GET /workspace/file` | `repo`, `path` query | Load a bounded regular text file and its hash. |\n| `POST /workspace/file` | `repo`, `path`, `text`, `expectedHash` | Save only the loaded version; conflicts return 409. Active run worktrees and symlinks are refused. |\n| `POST /workspace/merge-file` | `repo`, `path`, `base`, `draft` | Compute a three-way merge against the current file without writing. Returns `current`, merged `text`, `conflicts`, and `clean`; conflict markers need manual resolution before saving. |\n| `GET /workspace/search` | `repo`, `q`, `mode=text\\|symbols` query | Bounded text or declaration search with source line numbers. |\n| `GET /workspace/dependencies` | `repo` query | Bounded static architecture graph across JavaScript/TypeScript, Python, Go, Rust and Java; packages, cycles and unresolved/computed edges are explicit. |\n| `POST /workspace/context` | `repo`, `paths` | Preview explicit file context, approximate token size and sensitive-file omissions. |\n| `GET /workspace/checkpoints` | `repo`, optional `ref` query | List exact commit checkpoints. |\n| `POST /workspace/apply-checkpoint` | `repo`, `runId`, `source`, `paths`, `expectedHead` | Apply selected files to an idle, clean run worktree without committing. |\n| `POST /workspace/checkpoint-hunks` | `repo`, `runId`, `source`, `path` | Preview server-generated hunks for one existing regular text file in an idle run worktree. Returns pinned `head`, `source`, `path`, and bounded `hunks` with IDs. |\n| `POST /workspace/apply-hunks` | `repo`, `runId`, `source`, `path`, `expectedHead`, `hunks` | Apply 1–200 distinct preview IDs to a clean, unchanged run checkout. Arbitrary client patches are never accepted; files stay unstaged. |\n| `POST /runs/:id/pause` | `paused` | Hold new dispatch at operation boundaries; active work finishes. |\n| `POST /runs/:id/priority` | `priority` from -10 to 10 | Update dispatch priority, including waiting work. Equal priorities favor less-busy projects. |\n| `POST /runs/:id/provider-chat` | `agent`, optional `settings: {model, effort}`, `message` | Start a single-agent child with another provider, a portable conversation transcript, saved context files and a new worktree from the source result commit. Requires an idle source and clean worktree; retains project, computer and permission restrictions. Provider sessions and side-chat transcripts are excluded. |\n| `POST /runs/:id/fork` | exact `commit`, optional `task` | Begin a child run from an owned checkpoint, retaining source trust restrictions. |\n| `POST /runs/:id/revise-goal` | `message`, optional `budget` | Create a new Auto-team run with a user-authored goal revision and prior work references; returns 201 with its `id`. |\n| `POST /runs/:id/rerun-tasks` | `expectedHead`, `taskIds`, optional selected-task `instructions`, explicit `budget` | Execute accepted selected tasks and every dependency descendant in a child at the pinned integration commit. Retained work is labeled baseline input; final checks/review are independent. Returns 201 `{id}`. |\n| `POST /runs/:id/team-plan` | `revision`, `tasks`, `contracts`, `rationale` | Edit a pending Auto-team plan before dispatch. Keeps member identities and requirements fixed; validates owners, dependencies, paths and acceptance mappings. Returns `team`; stale revisions return 409. |\n| `GET /runs/:id/review-coverage` | `branch` query | Return current commit, changed files, reviewed files, percentage, heuristic risk, stale-review indicator and human rework history. Only run-owned branches are allowed. |\n| `POST /runs/:id/review-coverage` | `branch`, `expectedHead`, `path`, `reviewed` | Record or remove review of a changed file at that exact commit. Branch movement invalidates prior coverage; stale requests are rejected. |\n| `POST /runs/:id/human-rework` | `branch`, `expectedHead`, `minutes`, optional `note` | Append a user-entered correction-time record (1–1,440 minutes, note ≤2,000 characters, at most 100 records/run). Returns 201 with updated coverage. |\n| `POST /runs/:id/validate-merge` | `branch`, `into`, optional `allowUntrusted` | Verify the combined result with the current target in a disposable checkout. Returns pinned `source`, `target`, merge `tree`, `report` or `conflicts`. Requires a configured check/profile. |\n| `POST /runs/:id/merge` | `branch`, `into`, optional `archiveAfter`, `requireValidation` | Merge an owned result into the selected local branch. `requireValidation: true` requires passing verification for unchanged source/target and the exact merged tree. |\n| `POST /runs/:id/archive` | `archived`, optional `cleanup`, `deleteBranches` | Archive or unarchive a finished run. Use `cleanup: false` to hide it while keeping worktrees and branches. |\n| `DELETE /runs/:id` | — | Permanently remove a finished run's conversation/history and private run memory. Active runs and standing project team runs are rejected; archive managed runs to retain recovery and approval evidence. Git branches, worktrees and repository files remain. Run-list streams publish a summary with `deleted: true`; clients remove that ID. |\n| `GET /runs/:id/export` | optional `transcript=1` query | Export sanitized run metadata, checks, and requested transcript. |\n| `GET /approvals` | optional `run` query | Inspect bounded per-action permission requests. |\n| `POST /approvals/:id` | `decision=approve\\|decline` | Answer one still-active request once; expired or cancelled requests return 409. |\n| `GET /notes/history` | `key` query | Read bounded saved note revisions. |\n| `GET /notes/search` | `q` query | Search current saved notes. |\n| `POST /notes/restore` | `key`, `revision`, `expectedRevision` | Restore a prior note as a new revision. |\n| `POST /remote/tls` | `{mode: \"tailscale\"}` or `{mode: \"import\", hostname, cert, key}` with PEM strings | Local owner only. Validate and save HTTPS configuration, rotate pairing, disable full control and reload the network listener without restarting runs. Tailscale mode uses the connected computer's DNS name. Environment-managed TLS cannot be replaced here. Returns remote status and public certificate metadata, never PEM material. |\n| `GET /remote/devices` | none | List paired device metadata, never session credentials. |\n| `DELETE /remote/devices/:id` | none | Revoke one device immediately. |\n| `GET /diagnostics` | optional `repo` query | Export versions, queue/write health and sanitized recent logs. |\n\nReadiness accepts `roles` as an object of provider IDs (strings or arrays). Concrete assignments must be installed, compatible and not signed out; unused providers are skipped. `providers` alone is an explicit required-provider list. With `roles`, it limits the eligible pool for `auto` assignments; without that limit, Auto considers registered providers. An Auto-team's `roles.pool` contains alternatives and needs at least one usable member (also within `providers`, if supplied), while its coordinator is required. An explicit empty scope fails readiness. Unknown authentication remains a warning. Omitting both `roles` and `providers` preserves the original checks of all installed providers.\n\nScope IDs must be 1–128 letters, digits, underscores, colons, slashes, periods or hyphens, beginning with a letter or digit. Provider values must be registered IDs or `\"auto\"`; lists allow at most 128 entries and `roles` allows at most 64 keys. Malformed scope returns HTTP 400. The composer sends its current workflow and assignments and discards displayed or in-flight reports when that selection changes.\n\nPrompt improvement accepts up to 20,000 input/output characters and a 1,000-character\nsummary. Context is limited to 50 names of 300 characters; optional source metadata\ncontains `kind=issue|pr|alert`, integer `number` and a title of at most 200 characters.\nQuick mode supplies only the text and metadata; repository mode exports a pinned\ncommitted-HEAD snapshot with a fresh Git namespace. Source Git configuration, hooks,\nremotes, history, ignored/untracked content and executable provider configuration\nare not copied. Both use safe provider permissions and an in-process repository\nmutation lease; cleanup removes only the private directory after failure or\ncancellation, never resetting the source checkout or its index. See\n[snapshot bounds](getting-started.md#your-first-run). Snapshot preparation has a\n60-second deadline. Inherited Git routing/configuration overrides are refused so\nthe provider cannot accidentally target another checkout through its environment.\nImprove also requires absolute, nonempty PATH entries and currently supports\nmacOS/Linux; Windows is refused until executable discovery is pinned and tested.\nExplicit SSH/container profiles return\n400, shared accounts return 403, repository/policy conflicts return 409, the three-turn\nglobal concurrency limit returns 429, and provider/snapshot failures return 502.\nClosing the request cancels the turn; clients must not automatically retry it.\n\nVerification profiles contain `name`, up to 12 `checks`, optional `setup`, and\nup to 48 `requirements`. Each check has a unique `id`, `kind`, `command`, optional\n`title`, `required` (default true), and `timeoutMs` (default five minutes, maximum\n30 minutes). Kinds are `build`, `typecheck`, `lint`, `unit`, `integration`,\n`browser`, `mutation`, `visual`, `accessibility`, and `custom`. A requirement is\n`{id, description, checks: [checkId, ...]}`; an empty mapping remains uncovered.\nA failed mapped gate or uncovered criterion prevents `verified` status. Repeats\nare limited to 1–3; a mixed passing/failing result remains flaky. Baseline\nfailures are evidence, not permission to ignore a required gate.\n\nStandalone verification pins the requested commit, excludes uncommitted changes,\nand runs setup and gates on temporary worktrees. A setup or gate that changes\ntracked files, the staged tree, or HEAD is rejected with 409 before subsequent\ngates execute. Ignored and untracked generated output is permitted. Setup failure\nreturns 422. Run-attached profile verification similarly produces a blocked\nreport on candidate drift. Requirement coverage records the checks selected by\nthe user; it does not prove those checks semantically exercise the criterion.\nHuman file coverage is a separate, user-authored commit-bound record.\nSee [selective task reruns](task-reruns.md) for budget, replay, retained-evidence\nand unchanged-result semantics. Client-authored `teamRerun` seeds are never\naccepted by run creation.\n\n## Paginated history and event feeds\n\n`GET /runs` keeps the legacy full-list response when no `limit` query is supplied.\nWith `limit=1..100`, it returns `{runs, total, nextCursor?, nextBefore?}`. Pass the\nopaque `nextCursor` as `cursor` on the next request; cursors preserve ties for\nruns created in the same millisecond. Optional `q` filters all indexed run\nmetadata and `project` selects a project (including legacy repository-based membership).\n`status=all|attention|running|passed|failing|flagged`, `pattern`, `agent`, and\n`repo` filter the complete authorized index before totals and pagination. Passed\nrequires completed execution and passing verification; blocked or unverified\nresults are excluded. `section=waiting|running|results` selects a non-archived\nproject-page section. The legacy `before` timestamp is\nsupported but can omit timestamp ties; prefer `cursor`. Keep filters consistent\nbetween pages. Invalid cursors, status/section values and page sizes return 400.\n\n`GET /projects/:id/summary` returns `{live, archived, pending, week, sections}`.\n`week` contains `{runs, passed, costUsd}` for the last seven days, including\narchived history. `sections` contains cursor pages for `waiting`, `running` and\n`results`, with complete totals. Fetch additional section pages through `/runs`.\nThese totals never depend on which sidebar pages a browser has loaded.\n\nThe shared `/stream` connection accepts the `approvals` topic. It sends a\n`{type: \"snapshot\", records: [...]}` frame initially and whenever pending tool\napprovals change. Reconnects receive a fresh snapshot; approval decisions still\nuse the single-action POST endpoint. Older hosts without this topic use the\nclient's visibility-aware polling fallback.\n\n`GET /events?limit=1..100` is the bounded SSE run feed. Its initial `hello`\ncontains the recent page, every running run and at most 100 older attention\nitems, `total`, `nextCursor`, `attentionTotal`, `attentionTruncated`, and an\noptional `attentionNextCursor`. Older attention remains accessible through\nhistory; the rail displays the truncation. Without `limit`, the legacy full\ninitial list remains available. Policy changes invalidate the feed generation.\nReconnect using `Last-Event-ID`; a valid retained cursor resumes snapshots, while\na stale cursor triggers a fresh `hello`. `GET /runs/:id/events` streams that run's\nretained events with a run-bound replay cursor. These are live feeds, not durable\nclient acknowledgment queues.\n\n## Read-only external sources\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /integrations` | none | List explicitly server-configured provider metadata and availability without credential names or values. |\n| `GET /integrations/:id/items` | `kind=issue\\|pr`, optional `cursor` query | Return `{items, nextCursor?, truncated}` from a bounded provider listing. Supported kinds depend on the provider. |\n| `POST /integrations/:id/import` | `kind`, `key` | Read one item and return 201 with a server-issued source snapshot ID and untrusted marker. |\n\nRun creation accepts `externalSourceId` from the import response, or\n`externalSourceRef: {integration, kind, key}` for a server-side import. It rejects\nclient-authored `externalSource` snapshots, invalid/expired IDs, and simultaneous\nID/ref inputs. Snapshots expire after 30 minutes and the server retains at most\n200. Imported context forces Safe permissions, removes secret-file copying, and\nrequires network-disabled checks in a container or explicit host authorization.\nForks and continuations inherit the trust boundary. Source text is task context;\nPR imports do not fetch or check out remote code. See [external source setup and\nprovider limits](external-sources.md).\n\n## Outbound notifications\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /notifications` | none | List configured destination name/protocol/hostname, availability, opt-in settings, drop count and last failure. No URL paths, tokens, credential references or payloads. |\n| `POST /notifications/:id` | `enabled`, `events`, `projects` | Save explicit target opt-in. Events are `done`, `failure`, `needs-attention`; scope is `\"all\"` or project IDs (`\"unassigned\"` covers no project). Enabling requires an event and nonempty scope. |\n| `GET /notifications/deliveries` | none | List at most 200 delivery metadata records, newest first, without saved message payloads. |\n| `POST /notifications/:id/test` | empty body | Explicitly queue a test, including for a disabled destination. Returns 202; delivery proceeds in the background. |\n| `POST /notifications/deliveries/:id/retry` | empty body | Explicitly requeue a failed delivery, preserving lifetime attempts and manual-retry count. Returns 202. |\n\nDestinations are configured only through server environment JSON/file with\nenvironment-variable references for HTTPS URLs and credentials. New destinations\nare disabled. Delivery uses no redirects, a ten-second timeout and at most three\nautomatic attempts per batch. Changing the URL requires renewed opt-in. Unknown\noutcomes after restart require manual retry; recipients should deduplicate the\npersistent `Idempotency-Key`. Notification storage/configuration failure returns\n503 and leaves delivery off. See [configuration, disclosure, and delivery\nlimitations](notifications.md).\n\n## Project memory analysis and revision commands\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /projects/:id/memory` | optional `repo` query | List authorized project entries. A supplied repository must belong to the project. |\n| `POST /projects/:id/memory/commands` | `op=create\\|approve\\|update\\|delete\\|feedback` plus operation fields | Run user-authorized revision commands. All existing-entry mutations require exact `id` and `expectedRevision`; conflicts return 409. Client authority/origins are not forwarded. |\n| `GET /projects/:id/memory/analysis` | `repo` query (optional for a single-repo project) | Return bounded provenance, freshness, duplicates/semantic claims, deletion impact and curated onboarding with exact source revisions and truncation indicators. |\n| `POST /projects/:id/memory/analysis/publish` | `repo`, `expectedSources` from `analysis.onboarding.sourceRefs` | Recompute and publish eligible user-authored onboarding guidance with pinned provenance. Changed sources return 409; no eligible sources returns 400. |\n\nCreate accepts `repo`, `title`, `body`, optional `paths`, `tags`, and\n`applicability`. Update accepts editable fields and optional `accept: true`; an\nordinary edit never accepts a proposal automatically. Feedback accepts `rating`,\noptional `note` and `runId`; a run reference must have selected that exact entry\nrevision. Analysis is heuristic and bounded: lexical similarity does not infer\ncontradictions on its own. Deterministic semantic claims compare supported\npolicies and structured boolean/text/numeric intervals only across overlapping\napplicability; unsupported prose is counted. Git freshness checks do not\nestablish semantic correctness. See [supported claims](memory-claims.md).\nPublication excludes imported/private/untrusted material and prior onboarding\nsummaries. Memory APIs remain owner-only; shared project roles use the separate\nclosed review surface described below.\n\n## Multi-service launch profiles\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /runs/:id/launch-profiles` | `worktree` query | Read `.codeflow/launch.json` from the owned worktree and return `{profiles, found, error?, session}` with sanitized configuration and session logs. |\n| `POST /runs/:id/launch-profiles` | `worktree`, `profile`, optional `allowUntrusted` | Start a configured dependency-ordered group; returns 201 with session and assigned local URLs/ports. Locked-down host execution requires explicit authorization. |\n| `DELETE /runs/:id/launch-profiles` | `worktree` query | Stop the owned launch session and return `{stopped}`. |\n\nProfiles configure at most six services and bounded commands, relative working\ndirectories, dependencies, environment values and optional local HTTP health\nchecks. The server assigns ports and peer URL variables. This starts local\nprocesses; it is not remote deployment or hosting. See [launch profile\nconfiguration and lifecycle](launch-profiles.md).\n\n## Automation inspection and replay\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /automations` | none | List configured local schedules and webhook triggers. |\n| `POST /automations` | `name`, `run`, `schedule` and/or `webhook`, optional `enabled`, `isolate`, `image` | Validate saved run settings and create a trigger (201). |\n| `POST /automations/:id` | changed fields; optional `rotateToken` | Update validated settings or rotate the webhook credential. |\n| `POST /automations/schedule-preview` | `schedule`, optional `after`, `count` | Return `{times}` (at most ten UTC millisecond timestamps) without scheduling work. |\n| `POST /automations/:id/preview` | optional webhook event input | Dry-run saved settings, selected sweep items, permissions and limits without starting runs or consuming the event. |\n| `GET /automations/:id/events` | none | Inspect bounded persistent event records including queued, started, failed and dropped deliveries. |\n| `POST /automations/:id/events/:eventId/retry` | empty body | Replay an eligible saved event through the same validation, deduplication and queue limits. |\n| `POST /automations/:id/run` | empty body | Explicitly fire now; returns 201 with run IDs, or 409 if skipped. |\n| `DELETE /automations/:id` | none | Remove the trigger. |\n\nDaily schedules use `{at: \"HH:MM\", timezone?: \"IANA/Zone\", days?: [0..6]}`;\nSunday is zero and an explicit empty weekday list is invalid. Omitted timezone\nuses server local time. Interval schedules use `{every: minutes}`. During a\nrepeated local clock time, a daily schedule fires once on its first occurrence;\na missing local time shifts forward by the clock gap. Webhook event history\nkeeps at most 100 records and the pending queue at most ten. Replay preserves the\nsaved event content, while rechecking current configuration and limits. It never\nmerges results automatically.\n\nRun creation accepts `loop.verification`, `loop.maxTokens`, `loop.maxCostUsd`,\n`loop.agentContainer`, and explicit `approvals.tools`. Token/cost limits count\nonly provider-reported usage and are checked between operations. API-equivalent\ncost is not a subscription charge. Automated and locked-down runs never wait for\nnative tool permissions. See [agent containers](agent-containers.md),\n[external sources](external-sources.md), and [maintenance](maintenance.md).\n\nThe client has no automatic retry for mutations. A dropped response is not proof\nthat an operation did not execute; inspect the run before repeating a creation.\n\n## Shared identities and project review\n\n`GET /auth/self`, `POST /auth/login {username,password}` and `POST /auth/logout`\nbootstrap the local viewer/reviewer dashboard. Login sets a distinct session\ncookie; tokens and password/session hashes are never returned. Shared sessions\nrequire actual TLS or a direct loopback connection without forwarding headers.\nOwner account administration uses `GET/POST /accounts`, `PUT /accounts/:id`, and\n`POST /accounts/:id/revoke-sessions`, with the current account-state `revision`.\n\nShared routing is a closed allowlist. Project/run lists, result detail, safe\nevents, diffs, coverage and exports are scoped before counts and cursors.\nReviewers may record file coverage and `POST /runs/:id/collaboration\n{branch,expectedHead,text}`; both recheck current project membership and owned\nresult HEAD. `GET` returns the separate human discussion. These comments never\nresume an agent. All execution, administrative and private-context operations\nremain owner-only. See [roles, projections and session limits](shared-projects.md).\n\n## Team collaboration\n\nA team board served by one code-flow instance (the team server) and the linked\nrunners on teammates' computers share the routes below. Browser sessions (the\nowner and shared accounts) and per-machine runner credentials\n(`Authorization: Bearer tr-…`) use the same paths; the table says who may call.\nA runner credential reaches only `/team/*` and the enrolment route; every other\nAPI route answers 403 \"This credential only reaches the team API\", and the pages\ntreat it as an anonymous visitor. The owner token is matched before a bearer is\nread as a runner credential. Runner mutations send\n`content-type: application/json` (the bundle upload `application/octet-stream`)\nand need no Origin header; browser mutations keep the same-origin JSON\nrequirement of shared accounts. Board tasks are visible only in projects where\nthe caller holds a role; people appear as an account id and display name, never\nwith paths, transcripts, tokens or hashes. `/v1/team/*` and `/v1/team-link/*`\naliases work, and `GET /v1/capabilities` lists the family as\n`team-collaboration`.\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `POST /team/runners/connect` | `username`, `password`, `name` | Anonymous enrolment over HTTPS or loopback: verifies the credentials like sign-in without creating a session, requires a contributor role in some project, and returns 201 with the runner, its credential (shown once), the user and the server's name. |\n| `GET /team/me` | none | The caller's identity, project roles and own runners. |\n| `GET /team/board` | optional `project` query | The board: the projects where the caller holds a role with their members and repository keys, task cards with the current attempt, latest checkpoint and hand-over, and the unread inbox count (viewer). |\n| `GET /team/tasks/:id` | none | The full task (viewer in its project). |\n| `POST /team/tasks` | `projectId`, `repository`, `title`, `goal`, `acceptance`, optional `baseRef`, `owner`, `reviewer`, `collaborators`, `dependsOn`, `labels`, `status` | Create a task (contributor); 201. |\n| `PUT /team/tasks/:id` | `revision` plus the changed fields | Partial save under the status and ownership rules; `null` clears an optional field. |\n| `DELETE /team/tasks/:id` | `revision` | Delete a task (maintainer); `{deleted: true}`. |\n| `POST /team/tasks/:id/comments` | `text` | Comment, with `@username` mentions (reviewer). |\n| `POST /team/tasks/:id/decisions` | `text` | Record a decision (contributor). |\n| `POST /team/tasks/:id/review-request` | `reviewer`, `revision` | Ask a teammate to review (contributor). |\n| `POST /team/tasks/:id/reviews` | `checkpointId`, `verdict=approve\\|request-changes`, `text`, `revision` | Record a review against a checkpoint (reviewer). |\n| `POST /team/tasks/:id/instructions` | `text` | Propose a **Send to agent** instruction (contributor); the task owner's own are approved at once. |\n| `POST /team/tasks/:id/instructions/:iid` | `action=approve\\|decline\\|delivered`, optional `attemptId` | Approve or decline (the task owner or a maintainer); `delivered` is recorded by the attempt's runner. |\n| `POST /team/tasks/:id/attempts` | `attemptId`, `runnerId`, `runnerName`, `baseCommit`, optional `fromCheckpointId`, `revision` | Claim an attempt (the task owner through their runner, or the server's owner in-process as runner `local`); idempotent on `attemptId`; 201. Refused (409) while another attempt is claimed, running or unknown, while a hand-over is offered, and on a Done task. |\n| `POST /team/attempts/:aid/progress` | `seq`, optional `runId`, the progress fields | Publish a numbered progress snapshot (the attempt's runner); a late number is ignored. A report that would revive a paused, stopped or unknown attempt is refused (409) while another attempt of the task is claimed, running or unknown or a hand-over is offered, on a Done task (409 \"Move the task out of Done before resuming the attempt\"), and for a machine whose user no longer owns the task (403 \"The task now belongs to `<name>`; they continue it from its latest checkpoint, or a project maintainer reassigns it\"). |\n| `POST /team/attempts/:aid/heartbeat` | empty body | Keep the attempt live; returns the approved, undelivered instructions. |\n| `POST /team/attempts/:aid/finish` | `state`, optional `head`, `summary`, `reason` | End the attempt as finished, failed, stopped, paused or unknown. |\n| `POST /team/attempts/:aid/recover` | `revision`, `reason`, `confirmedStopped=true` | Recover an unknown attempt after confirming its original execution stopped (task owner or project maintainer). Supersedes it, records the decision and releases the task; stale revisions or a reconnected runner return 409. |\n| `POST /team/attempts/:aid/checkpoints` | optional `id`, `commit`, `baseCommit`, `branch`, `size`, `sha256`, `note`, `requirements`, `decisions`, optional `testResults`, `remaining`, `changedFiles` | Register a checkpoint manifest with `bundle: \"pending\"` (the attempt's runner); 201. `id` (32 lowercase hexadecimal characters) becomes the checkpoint's id and ref `refs/codeflow/team/<id>`; an id already on the board is refused (409), a malformed one 400, and without one the server generates the id. |\n| `PUT /team/checkpoints/:cid/bundle` | `application/octet-stream` body, `x-codeflow-sha256` header | Stream the bundle to disk (the checkpoint's runner); size and digest are verified before the checkpoint becomes available. A refusal (415, 400 on a size or digest mismatch, 409 when already uploaded or deleted, 507 when storage is full, 503 when the file could not be stored) is answered at once and the declared body is then drained, so a retry reads the answer; a body without a content-length (411) or declared over 64 MiB (413) is cut off once the answer is written. |\n| `GET /team/checkpoints/:cid` | none | The checkpoint manifest (viewer). |\n| `GET /team/checkpoints/:cid/bundle` | none | Download the bundle with `content-length` and `x-codeflow-sha256` (contributor). |\n| `DELETE /team/checkpoints/:cid` | none | Delete a checkpoint and its bundle (maintainer); `{deleted: true}`. |\n| `POST /team/tasks/:id/handoff` | `to`, `checkpointId`, `note`, `revision` | Offer a hand-over from an available checkpoint of the task (the task owner or a maintainer); refused while an attempt runs. |\n| `POST /team/tasks/:id/handoff/:hid` | `action=accept\\|decline\\|cancel`, `revision` | Accept or decline (the recipient); cancel (the sender or a maintainer). |\n| `GET /team/inbox` | optional `after` query | The caller's inbox items and unread count. |\n| `POST /team/inbox/read` | `ids`, or `all: true` | Mark the caller's items read; returns `{unread}`. |\n| `GET /team/runners` | none | The caller's runners; only the hub owner sees all. |\n| `DELETE /team/runners/:id` | none | Revoke a runner (its user or the hub owner); `{revoked: true}`. |\n| `PUT /team/projects/:id/repositories` | `repositories`, `revision` | Define the project's repository keys (maintainer). |\n| `PUT /team/profile` | `displayName` | The owner's display name on the board (owner). |\n| `GET /team/events` | optional `after=<epoch:seq>` query | Server-sent events for the caller: `hello` with the epoch and sequence, then `change` events for the board and tasks in the caller's projects and `inbox` unread counts; a cursor the server no longer holds gets `resync`. Closed when the account changes or the runner is revoked, and when 2 MiB of frames wait on the socket. |\n\nMutations return the updated task and carry the task `revision`; a stale one\nfails with 409 \"Task changed; reload before saving\". A missing role answers 403\nnaming the role, an unknown or invisible id 404 \"Task not found\", an unknown\nteam route 404 \"Unknown team operation\" (never falling through to owner routes)\nand unavailable team storage 503. Task saves and repository keys accept 64 KiB\nbodies, progress reports 192 KiB, checkpoint manifests 1.25 MiB, bundles 64 MiB,\nenrolment 8 KiB and other routes 16 KiB; a board file at its 16 MiB limit refuses\nevery change with 413. Progress reports and heartbeats do not change the task\nrevision.\n\nOn a linked computer the owner's `/team/*` requests are forwarded to the team\nserver with the runner credential, and the owner-only local routes below drive\nexecution; on the team server itself the same routes work in-process and\n`linked` is false.\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /team-link` | none | The link: `linked`, the server address, user, server name, machine name, mappings and defaults, the connection state, the runner as the server reports it, the last sync and the last error. |\n| `POST /team-link/connect` | `url`, `username`, `password`, optional `name` | Enrol this computer: `https://` only (plain `http://` for loopback), never following a redirect and only with a trusted certificate (502 otherwise), refused (409) while already connected or while the local board has tasks; saves the link and starts the change stream and reconciliation; 201. |\n| `DELETE /team-link` | none | Revoke the runner on the server (best effort) and forget it here; mappings, defaults and local attempt records stay. |\n| `PUT /team-link/mappings` | `projectId`, `repository`, `path` | Map a repository key to an absolute folder that is a Git repository. |\n| `POST /team-link/tasks/:id/start` | optional `repo`, `pattern`, `roles`, optional `agentSettings`, `roleSettings`, `trust`, `loop` (`check`, `setup`, `maxMinutes`, `maxSteps`), `fromCheckpointId` | Claim an attempt and start the run here; returns `{attemptId, runId}`; 201. Refused (400) before anything is claimed when the repository cannot carry checkpoints (a submodule, a checkout filter such as Git LFS, or a tree over 20,000 files, 256 MiB or 100 attribute files). |\n| `POST /team-link/tasks/:id/stop` | empty body | Cancel the current attempt's run (resumable) and record it as paused. |\n| `POST /team-link/tasks/:id/resume` | empty body | Resume this computer's paused, stopped or unknown attempt of the task under the same attempt id; refused (409) when the attempt was superseded or has ended, the task changed hands or is Done, another attempt is busy or a hand-over is offered. |\n| `POST /team-link/tasks/:id/handover` | `to`, `note`, optional `includeUncommitted` | Create or reuse the checkpoint (a retry uploads the kept bundle), then offer the hand-over. |\n| `POST /team-link/tasks/:id/accept` | `handoffId`, optional `repo` | Download and import the checkpoint, then accept the offer. |\n| `POST /team-link/checkpoints/:id/fetch` | `repo` | Import a checkpoint and point `team/<task id>/<first 8 characters of the checkpoint id>` at it; returns `{commit, branch, taskId, created}`. |\n| `GET /team-link/attempts` | none | This computer's attempt records: which run belongs to which attempt, and what has been published. |\n| `POST /team-link/reconcile` | empty body | Run the recovery pass now: settle claims whose answer was lost and retry unacknowledged finishes; returns `{attached, finished, removed, unknown, retried}`. |\n\nNothing here replays automatically: a restart, the recovery pass that runs every\nminute and `POST /team-link/reconcile` reconcile local attempt records with the\nteam server and never start a run again. See [team\ncollaboration](team-collaboration.md) for roles, hand-overs, checkpoints and\nlimits.\n\n## Explicit workers, coordination and extensions\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /remote-workers` | none | List explicitly configured SSH targets without private capabilities. |\n| `POST /remote-workers/:id/probe` | `repoMapping` | Probe the provisioned worker and its own provider inventory. |\n| `POST /runs/:id/remote-steps/:step/receipt` | empty body | Inspect an accepted execution receipt; never replay the turn. |\n| `POST /runs/:id/remote-steps/:step/cancel` | empty body | Explicitly request cancellation and inspect its receipt. |\n| `GET /coordinated-changes` | none | List durable multi-repository jobs and their current claims. |\n| `POST /coordinated-changes` | `project`, `name`, `nodes`, optional `operationId` | Create 2–10 explicitly configured repository tasks with a verified dependency DAG. |\n| `POST /coordinated-changes/:id/cancel` | `expectedRevision` | Cancel further dispatch; preserve accepted execution receipts. |\n| `POST /coordinated-changes/:id/shipping-preview` | `expectedRevision`, `targets` | Validate the current source/target/tree matrix without merging. |\n| `POST /coordinated-changes/:id/nodes/:node/retry` | `expectedRevision`, optional `acknowledgeUnknown` | Explicitly retry after inspecting an uncertain claim. |\n| `POST /coordinated-changes/:id/nodes/:node/attach` | `expectedRevision`, `runId` | Attach only an exact matching durable launch receipt. |\n| `POST /coordinated-changes/:id/nodes/:node/ship` | `expectedRevision`, `previewId` | Merge one unchanged verified local candidate; no automatic remote push. |\n| `POST /coordinated-changes/:id/nodes/:node/recover-shipping` | `expectedRevision`, `commit` | Confirm the exact reviewed parents/tree after an uncertain local merge. |\n| `GET /extensions` | none | Inspect host API version and explicitly configured trusted provider/workflow bundles. |\n\nRuns select SSH using `loop.remoteWorker: {workerId,repoMapping}`. Worker\ncredentials stay on the provisioned worker; accepted turns are never retried\nautomatically after a disconnected transport. See [worker setup and receipt\nrecovery](remote-workers.md). Coordination ships each repository separately;\nthere is no atomic transaction across repositories. See [coordinated\nchanges](coordinated-changes.md). Extension installation is server configuration,\nnot an HTTP operation; immutable version/hash pins are rechecked before\nexecution. See [trusted extension contracts](extensions.md).\n\n## Code-host review and publication\n\nOwner-only `GET /code-hosts` lists configured GitLab/Bitbucket code adapters and\nsafe operation receipts. `POST /code-hosts/preview {integration,key,repo,project?}`\ncreates a short-lived server snapshot. `POST /code-hosts/:id/fetch` imports exact\nsource/target Git objects without executing them; `/:id/run` accepts the prepared\nagent container and required verification configuration. `/:id/review` performs\nan explicit supported comment, reply, thread resolution or GitLab approval.\n\nPush and merge each have distinct `/:id/push-preview` or `/:id/merge-preview`\noperations taking `runId` and an owned `branch`; their actions require the\nreturned `planId`. `POST /code-hosts/create-preview` takes `integration`, `runId`,\n`branch`, `targetBranch`, `title`, and `body`; `/:id/create` explicitly creates\nthe expected-absent source branch and native PR. An unknown receipt can only be\nacknowledged explicitly at `/code-hosts/history/:id/acknowledge` with\n`{acknowledge:true}`. No startup replay occurs.\n\nGit updates use exact expected-ref leases. Merge is a pinned Git merge and may\nleave provider PR closure pending; native merge queues and atomic provider-policy\nsnapshots are not claimed. Unsupported/unknown protected-branch policies fail\nclosed. See [provider-specific operations and limits](code-hosts.md).\n\nGitHub threads use `GET /runs/:id/pr-threads?branch=<owned-branch>` and `POST` with\n`{action:\"load\",snapshotId,threadId}` or\n`{action:\"resolve\",snapshotId,threadId,expectedHead,expectedRevision,resolved}`.\nResolution rechecks fresh remote state and reports postflight uncertainty.\nSee [thread pagination, permissions and mutation races](github-review-threads.md).\n\n## Filesystem policies\n\nOwner-only `GET /projects/:id/filesystem-policy` returns the committed policy.\n`PUT` accepts `{revision,read:{allow,deny},write:{allow,deny}}` with literal\nrepository-relative paths. Policies intersect across matching projects and\npinned run snapshots. App file/tree/search/write/diff access rechecks current\nrules; raw history and other unfilterable evidence are withheld. Custom\nrestrictions pause ordinary application-managed execution globally, including\nbackground preparation. See [activation, execution boundaries and HTTP error\nsemantics](filesystem-policy.md).\n\nRestricted execution uses a separate owner-only manual task surface:\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /filtered-tasks` | optional `project` query | Return bounded lifecycle/receipt metadata; historical source/output content is not listed. |\n| `POST /filtered-tasks` | `project`, `repo`, `task`, `agent`, `image`, optional `network`, `maxMinutes` | Prepare a filtered committed snapshot and return 202 after a durable claim; only built-in providers and an explicit local immutable image are supported. |\n| `GET /filtered-tasks/:id` | none | Read task/output under unchanged current canonical policy and project membership. |\n| `POST /filtered-tasks/:id/preview-import` | empty body | Validate source/result/policy and preview bounded allowed file changes. |\n| `POST /filtered-tasks/:id/import` | `revision`, `expectedHead`, `resultHash` | Create a new review branch with an atomic exact-source ref transaction; leave checkout and index unchanged. |\n| `POST /filtered-tasks/:id/cancel` | `revision` | Cancel preparation/execution and confirm the owned daemon container is absent. |\n| `POST /filtered-tasks/:id/receipt` | `revision` | Explicitly inspect an interrupted branch-import receipt; never replay a provider turn. |\n| `DELETE /filtered-tasks/:id` | JSON `revision` | Remove a stopped terminal task's private export/record; preserve any imported review branch. |\n\nThe task container receives only permitted committed regular files and a new Git\nobject database. It mounts no original repository metadata, host login directory,\nauthentication volume or application memory. The image layer and copied files\ndenied writes are read-only. Network starts off; enabling inference access is an\nexplicit owner choice. Input/output limits are 500 files, 512 KiB per file and\n16 MiB total; live monitoring is a polling safeguard, not a filesystem quota.\nRestarted effectful phases become unknown and require explicit inspection or\ncancellation. See [runtime provisioning, recovery and limitations](filesystem-policy.md).\n\n## SSH projects\n\nOwner-only connection management is available on the local listener. SSH host\nbackends and phone/shared sessions cannot manage additional SSH connections.\n\n| Endpoint | Input | Result |\n| --- | --- | --- |\n| `GET /workspace-projects` | none | Local and connected remote project IDs and names, grouped by computer; never repository paths or credentials. |\n| `GET /ssh-hosts` | none | Saved host IDs, names, aliases and connection state, plus concrete aliases discovered from SSH config. |\n| `POST /ssh-hosts` | `sshHost`, optional `name` | Save an alias or `user@hostname`; port/key/jump configuration stays in OpenSSH. |\n| `POST /ssh-hosts/:id/connect` | none | Install or reuse the remote backend and return a stable, same-origin `/computer/:id/` workspace path. |\n| `POST /ssh-hosts/:id/disconnect` | none | Close SSH channels and preview forwards, leaving remote work running. |\n| `POST /ssh-hosts/:id/stop` | none | Stop an idle remote backend, then disconnect. Refuses active execution, terminals and development services. |\n| `DELETE /ssh-hosts/:id` | none | Disconnect and remove the saved definition; remote data remains. |\n| `GET /ssh-context` | none | Local workspace context. At `/computer/:id/api/ssh-context`, returns the saved computer ID, name and connection state. |\n\nThe returned workspace uses the same local app origin and owner session. Its\n`/computer/:id/api/` endpoints and event streams address the remote backend, so project paths, Git,\nfiles, checks, agent tools and terminals all refer to that server. Mutations are\nnever replayed automatically. See [SSH projects](ssh-projects.md) for setup,\nreconnection, remote previews, supported hosts and authentication boundaries.\n\nManaged Tailscale certificates renew automatically while Tailscale remote access is enabled. `GET /remote` includes `tls.renewal` with `automatic`, `running`, `nextAttempt`, optional `lastSuccess` and a safe `error`. Renewal preserves paired sessions and control settings. Imported/environment certificates are excluded.\n\nFor multi-agent roles in Race, Debate, Review council, Supervisor and Loop workflows, a role setting can include additional models from the same provider: `roleSettings.reviewers.claude = { model: \"first\", variants: [{ model: \"second\", effort: \"low\" }] }`. Each entry runs separately with its own settings and session. Pipeline and other single-agent roles still use one model. Auto team pools also accept variants in `roleSettings.pool`: these are allowed model/effort choices for workers and reviewers. The coordinator selects among them within the member limit; it does not launch every selected model. Coordinator settings stay fixed. Additional models use the same validation as primary models (maximum 128 additional models per provider).\n\n### Result experiences and browser scenarios\n\nThese operations are owner-only and use the existing origin, authentication,\nfilesystem-policy and host boundaries. New run requests can include `intent`\n(`bug`, `feature`, `review`, `explore`) and an existing same-repository `journeyId`.\nComparison variants can include separate `task` instructions while sharing the\nbase run configuration. Comparison launches can partially succeed; inspect the\nreturned/visible group before retrying an interrupted request.\n\n| Operation | Request | Behavior |\n| --- | --- | --- |\n| `GET /experience` | optional `repo`, `project`, `intent` query | Outcome metrics and repository/workflow sample counts, Wilson intervals and supporting run IDs. |\n| `POST /runs/:id/outcome` | `branch`, `expectedHead`, `decision=accepted\\|needs-work` | Record feedback on an idle, exact result commit; stale heads return 409. Does not publish or approve managed shipping. |\n| `POST /runs/:id/task-priority` | `taskId`, `revision`, `priority` (-10 to 10) | Prioritize undispatched tasks on the current Auto-team plan without relaxing scheduling constraints. |\n| `GET /runs/:id/worktrees/:name/serve/ready` | none | Report process state and whether its local app port accepts a connection. |\n| `GET /journeys` | absolute `repo` query | List saved immutable browser scenarios in this repository. |\n| `POST /journeys` | `repo`, `title`, local `url`, `width`, `actions`, `expected: {selector, text}` | Validate and save a scenario; returns 201 with its ID and data. Actions use `kind=click\\|fill\\|select\\|press`, `selector`, and optional `value`. |\n| `GET /journeys/:id` | none | Return scenario, generated Playwright spec, and up to four replay receipts. |\n| `POST /journeys/:id/replay` | optional local `url`, `runId`, `branch` | Replay in fresh Chrome, save screenshots and assertion status, return 201. Run-associated replays require that result's registered running app. Preserves the scenario's path/query/hash on the selected origin. |\n| `DELETE /journeys/:id` | none | Delete the scenario and retained screenshots. Existing runs keep their original attached test context. |\n\nA replay receipt's `passed` status means only that the expected visible text\nappeared after the recorded interactions. It is separate from Git verification;\nsee [result experiences](application-improvements.md#result-first-product-experiences).\n\n## Documentation and help\n\n`GET /docs/` serves the bundled documentation without requiring a session; it contains only repository documentation. Page URLs use `/docs/<guide>.html`. Static files do not expose project state.\n\n`GET /api/docs-help` lists installed built-in CLI providers for the authenticated owner. `POST /api/docs-help` invokes the selected CLI with documentation excerpts:\n\n```json\n{\n \"agent\": \"claude\",\n \"question\": \"How do I choose a workflow?\",\n \"page\": \"patterns\",\n \"settings\": {\"model\": \"sonnet\"},\n \"history\": []\n}\n```\n\nThe response contains `answer` (plain text) and `sources` (guide IDs and titles). `page` is an optional guide ID; it is never a filesystem path. `history` accepts at most three `{question,answer}` pairs. The JSON body is limited to 64 KiB, questions to 4,000 characters and answers to 12,000. Settings accept a model and effort for the selected provider.\n\nThe endpoint is owner-only on the main listener, subject to the existing origin checks and filesystem execution policy. It does not support ACP, extension providers, repository paths or remote/container execution. Closing the request aborts the turn. Two help requests can run at once; extra requests return 429. See [Ask code-flow](help.md) for execution and privacy boundaries.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n\n\n## Auto model catalog\n\n`GET /api/model-catalog` returns provider discovery catalogs, managed-provider eligibility, `allowResearch` and offline status. It uses the cached catalog and performs bounded CLI discovery when missing or stale. `POST /api/model-catalog` explicitly refreshes; its closed body accepts optional booleans `allowResearch` and `offline`. Research is disabled by default. `CODEFLOW_MODEL_NETWORK=offline` prevents new discovery/research while retaining cached evidence.\n\nEach catalog identifies its provider/environment/revision and separates reported model/effort availability, documented facts and run observations. It contains no authentication credentials. The existing `/api/models` endpoint retains its legacy behavior. Remote/container discovery happens in the selected execution environment, independently of this local preview. New Auto steps persist `modelAssignment` (provider, exact ID, effort, rationale, catalog revision/source and environment). Advanced worker `roleSettings.pool` pins are checked again at dispatch and on recovery; global coordinator defaults do not pin workers.\n\n\n### Antigravity execution authority\n\nNew run requests accept `antigravityExecution: \"isolated\" | \"native\"`. Omission defaults to isolation, or inherits the parent's recorded choice for continuations. `\"native\"` explicitly opts into unrestricted host execution using the existing login and configuration; it leaves Claude/Codex permissions unchanged. An isolated parent cannot be upgraded by a child. Native mode rejects mandatory permission requirements, Safe/narrowed trust, tool approvals, remote/container and unattended/locked-down runs. It is not a template setting and is persisted in the run policy and step receipt.\n\n`GET /api/model-catalog?antigravityExecution=native` and the POST refresh body's `antigravityExecution` choose native catalog discovery for previews; they do not grant execution authority. Default previews use isolated discovery. Both modes have distinct cache identities. Native execution records advisory read-only requests, unrestricted terminal access, provider-configured browser/MCP/hooks/native agents, and native child usage outside Code Flow's budget.\n","title":"Local API and command-line client"},{"group":"Build and extend","id":"extensions","source":"docs/extensions.md","body":"# Provider and workflow extensions\n\nInspect the extension bundle and its declared API version before enabling it. Pin the version you intend to run; an extension is trusted executable code, not a sandboxed configuration file.\n\n## Connect a trusted extension\n\nFor OpenCode or another CLI that speaks Agent Client Protocol, use [ACP connections in Settings](acp-agents.md). No JavaScript extension bundle is needed for a compatible stdio agent.\n\nThe owner can register additional provider adapters and workflow patterns with an explicit server environment configuration. There is no repository discovery, remote download, HTTP plugin installation or shared-user administration. Settings → Extensions reports loaded/rejected bundles and negotiated API versions. Restart to apply configuration changes.\n\nAn extension is trusted JavaScript server code, with the server owner's filesystem, network and process permissions. Review its source and bundle dependencies before enabling it. Hash pinning detects changed bundle bytes; it does not make malicious code safe or sandbox its behavior. Use self-contained `.mjs` bundles; relative imports resolve from the private captured-bundle cache, not the original module directory. Provider authors must honor abort signals, timeouts, trust modes and the normalized event/result contracts. Built-in compatibility probes, container isolation and SSH workers do not certify or execute additional plugin providers.\n\nSet `CODEFLOW_EXTENSIONS_FILE` to an absolute regular JSON file:\n\n```json\n{\n \"version\": 1,\n \"extensions\": [{\n \"id\": \"example\",\n \"version\": \"1.0.0\",\n \"module\": \"/absolute/path/example.mjs\",\n \"sha256\": \"replace-with-the-64-character-sha256-of-the-reviewed-bundle\",\n \"api\": { \"major\": 1, \"minimumMinor\": 0 },\n \"requires\": [\"provider-adapter\", \"workflow-pattern\"]\n }]\n}\n```\n\n`CODEFLOW_EXTENSIONS_JSON` accepts the same object; choose one source. The host supports API 1.0 and rejects a newer major/minimum minor or unknown required capability before importing the bundle. Up to 16 extensions can each supply up to eight providers and eight workflows. Configuration is bounded to 128 KiB and bundles to 512 KiB; symlink files and changed hashes are refused. Captured bytes are copied to a private hash-named cache before import, so a change to the original file between review and import cannot substitute unpinned content.\n\nThe default export has this shape:\n\n```js\nexport default {\n api: { major: 1, minor: 0 },\n providers: [/* AgentAdapter objects from src/adapters/types.ts */],\n workflows: [/* Pattern objects from src/engine.ts */]\n};\n```\n\nEach ID must be `plugin:example/name`. Provider adapters implement `available()`, `catalog()` and `run(options, onEvent)` and return normalized `AgentEvent`/`RunResult` data. Workflows declare names, descriptions, bounded role/default-provider and option schemas, then execute through `TeamContext` for worktrees, agent turns, commits, checks, artifacts and replayable remembered operations. New providers appear in agent selection and workflows in the normal composer. Built-in IDs cannot be replaced, duplicate registrations are refused and workflow default roles must refer to registered providers. Plugin capabilities remain unknown until independently established; built-in tool bridges and live approval prompts are unavailable.\n\nRuns pin every selected extension's version and SHA-256. Resume or further dispatch refuses a missing or changed pin; restore the exact reviewed bundle or create a fresh run. A restrictive filesystem policy also blocks plugin execution. The fixture acceptance test loads a real captured bundle, registers a new provider/workflow, runs it through the actual engine, commits its result and executes its real check without provider quota or external requests.\n\n`GET /api/extensions` is owner-only and returns status, hashes, negotiated versions and registered IDs, with bounded redacted error details. It does not return module source or the configuration document. Extension caches are executable code, not portable project data; restore does not enable plugins without an explicit environment configuration.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Provider and workflow extensions"},{"group":"Build and extend","id":"provider-compatibility","source":"docs/provider-compatibility.md","body":"# Provider compatibility\n\nRun the provider diagnostics when an installed CLI stops accepting an invocation. Protocol fixtures and live version/help probes provide different evidence; a fixture passing does not verify a real provider login.\n\n## Check the installed CLI\n\nThe Provider compatibility workflow runs on adapter changes, pushes to main, manual dispatch and weekly. It installs official Claude Code 2.1.300/2.1.302 and Codex 0.162.0/0.162.1 in isolated directories, plus the current official Linux x64 Antigravity build. Antigravity downloads are verified against the SHA-512 checksum in the official updater manifest. The exact observed version, platform, Node version, timestamp, required flags and missing capabilities are retained in a 30-day JSON artifact per matrix entry. A missing required flag, unknown probe or unexpected pinned version fails its job.\n\nThese real binary probes run `--version` and `--help` only with a new empty home and no account credentials. npm installs use the explicit public registry and empty user/global configuration, and probes execute the installed absolute binary path rather than falling back to a host CLI. Pinned versions compare complete semantic versions. They never sign in, list account models, ask an agent to perform work or spend inference quota. Installing an official package may run its vendor installer. Maintained process fixtures separately exercise stream normalization, error typing, structured results, sessions, cancellation and Codex app-server behavior. Help compatibility and fixture success do not prove live account/inference behavior.\n\nRun a pinned probe locally with `node --import tsx scripts/check-provider-compatibility.ts codex 0.162.1 /tmp/codex-compatibility.json`. The current Antigravity channel probe runs on Linux x64/arm64 (glibc or musl). Successful probes define evidence for those exact versions; versions outside the matrix remain untested and installed health checks still apply. Update pins explicitly after reviewing release notes and the reports. CI must run on GitHub before its scheduled evidence is available.\n\nVendor references: [Claude Code releases](https://github.com/anthropics/claude-code/releases), [Codex releases](https://github.com/openai/codex/releases), [Antigravity installation](https://antigravity.google/docs/cli/install/).\n\nLocal acceptance on October 18, 2026 passed official Claude Code 2.1.300/2.1.302 and Codex 0.162.0/0.162.1 probes on macOS arm64, plus the official current Antigravity 1.4.2 binary in an isolated Linux arm64 musl Docker container. Exact receipts are in [provider-compatibility-evidence.json](provider-compatibility-evidence.json). Linux x64 CI execution remains a separate platform check.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Provider compatibility"},{"group":"Build and extend","id":"accessibility","source":"docs/accessibility.md","body":"# Accessibility acceptance\n\nTest the relevant flow with a keyboard, at a narrow viewport and in both themes. Automated audits supplement focus, navigation and readable status checks; they do not replace them.\n\n## Check the complete interaction\n\nThe main browser suite runs local axe-core checks against WCAG 2 A/AA, 2.1 AA and 2.2 AA rules. It covers the composer, Insights, Settings (including Automations), completed runs, Workspace tools, command palette, folder picker and the Files/Notes/Chat side panel in light and dark themes, plus mobile composer/workspace layouts. The suites also check Settings categories, shared login/reviewer views, human collaboration, filtered-task imports and the Improve mode menu. Reports and screenshots are generated under `output/playwright/`.\n\nThe audit fixed helper/status/provider text contrast, named dialogs, small breadcrumb targets, keyboard access to scrolling results, focusable controls inside the hidden side panel, empty tab strips and keyboard resizing of the panel. A visible-on-focus skip link moves focus to the main content. The command palette supports arrow keys/Enter/Escape, and Escape returns focus to its trigger. The side panel supports arrow-key tab selection and keyboard resizing; closed panels are inert. Mobile Workspace uses a labelled grouped selector so its tools do not overflow into a long strip of tiny buttons. Reduced-motion preferences disable sustained animation and smooth scrolling.\n\nRun `node --import tsx --test test/e2e/accessibility.e2e.ts` with Google Chrome installed. This uses the real server and browser with deterministic fake agents; it makes no account requests. The test asserts every enabled automatic rule and writes incomplete/manual-check results rather than treating them as passing assertions. Decorative status glyphs have adjacent text and are hidden from assistive technology.\n\nAutomatic checks and scripted keyboard flows do not establish complete WCAG conformance or replace testing with assistive technology and users. The mobile screenshots were also inspected for clipped controls and unreadable content. New forms, dialogs and interactive states should extend the browser coverage. Native screen-reader/device acceptance remains an ongoing product-quality check.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Accessibility acceptance"},{"group":"Build and extend","id":"performance","source":"docs/performance.md","body":"# Performance\n\nUse the diagnostic and benchmark commands below to identify the affected boundary. Compare the same workload and record variance before changing concurrency, caching or storage.\n\n## Measure the workload first\n\nThe backend remains Node/TypeScript and the UI remains Preact. Execution journals,\nprovider receipts and repository leases retain their existing authority. The changes\nbelow reduce repeated work and bound presentation buffers without changing agent\npermissions, verification requirements or retrying ambiguous remote executions.\n\n## History and transcripts\n\n`RunIndex` maintains ordered run summaries, complete normalized search text and\nrepository/worktree membership. Ordinary updates replace the indexed record without\nsorting every run again. Unfiltered history uses a binary cursor lookup; filtered\nsearch still scans the in-memory search strings. Only the selected page is cloned.\nAuthorization filtering precedes totals and pagination.\n\nCold projections now include their full searchable text. Legacy cold indexes hydrate\nonce during asynchronous startup to obtain that text. Listing and searching no longer\nread every cold detail file or synchronously validate every archived source. Opening\na detail or using it for an operation still validates the source and rebuilds stale\nprojections. HTTP detail hydration uses asynchronous I/O; synchronous compatibility\nremains for existing internal mutation callers. Indexes grow with run count, and\nlive/resumable runs still retain their execution metadata. Legacy log compaction\nhydrates one archive and invalidates its cold summary before replacing its source;\nshutdown waits for the projection refresh to finish.\n\nNew UI subscriptions use `run-page:<id>`. The initial frame contains the latest run\nand step snapshots, a recent transcript page, and persistence status. A reconnect\nreuses a sequence cursor if the missed events fit the page; otherwise it explicitly\nresets to a recent page. The old `run:<id>` topic and per-run events endpoint remain\navailable for older clients. Those legacy endpoints and full exports still load full\nhistory; they are not the bounded UI path.\n\n`GET /api/runs/:id/transcript?step=:step&before=:cursor` reads backwards directly from\na file offset. The opaque cursor binds the offset to the run and file identity.\nAppending does not invalidate older pages; replacing/compacting a log does, returning\n409 so the viewer can reload Latest. There is no initial full scan or separate index\nfile to recover. Partial pages can be empty when a bounded scan crosses only metadata\nor events from other steps; their continuation cursor still advances.\n\nDefault limits:\n\n| Resource | Bound |\n| --- | --- |\n| Transcript response | 500 events and 1 MiB of event JSON |\n| Scan per page | 16 MiB, plus the record crossing the boundary |\n| Individual log record | 64 MiB |\n| Displayed event string | 32 Ki UTF-16 units, with a visible omission notice |\n| Browser transcript cache per run | 8 MiB estimated serialized memory / 10,000 events |\n| Idle browser run feeds | Six feeds, within a 32 MiB estimate |\n| Initial transcript rows | 200; older pages replace the step window |\n| Global run replay ring | 500 snapshots / 32 MiB serialized data |\n\nComplete event text remains in the authoritative log and full export. Historical\nstep windows do not append live text into an older page; Latest returns to current\nevents. A tool result whose call is outside the page is still displayed explicitly.\nThe estimates bound retained payloads, not total JavaScript heap or DOM allocation.\n\n## Streaming and storage\n\nSSE stops writing when Node reports backpressure, resumes in order on `drain`, and\ndisconnects a client whose queued plus writable bytes exceed the budget. The default\nis 8 MiB per connection (`CODEFLOW_SSE_BUFFER_BYTES`). The existing\n`CODEFLOW_SSE_STALL_MS` timeout also applies. An individual oversized frame is refused\nby the same bound. Slow consumers never block agent execution.\n\nDisk writes are ordered by run ID, including that run's event log and execution\njournal. Up to four unrelated runs can write concurrently. Ordinary contiguous\nappends batch up to 128 records / 256 KiB. Synced writes, rewrites and deletion form\nordering boundaries. The append backlog has a 64 MiB byte budget: overflow is a\nreported write failure, and strict journal callers fail closed.\n\nOptional startup checkpoints use a separate single-writer queue with a 32 MiB\npayload admission budget. Their captured source identity prevents a stale checkpoint\nfrom describing newer events. Durability barriers can be scoped to a run; reading\ntranscripts waits for that run's appends without fsyncing every other run. Shutdown\nstill flushes writes. The run view reports saving and persistence failures separately\nfrom workflow completion.\n\n## UI and analysis\n\nThe UI build produces hashed ESM chunks and a hashed stylesheet. Settings, Terminal,\nBrowser and Changes load on demand. Both owner and teammate terminal renderers\nuse dynamic imports; a bundle test rejects xterm in the startup import graph. Assets have immutable caching, compression,\nETags and a bounded server cache. HTML remains revalidated. Old chunks remain on the\nlocal build directory for already-open tabs; SSH release packages include only the\ncurrent manifest's files. Packaging checks that HTML and manifest belong together.\n\nCode intelligence reuses at most two isolated TypeScript workers, each with the\nexisting 192 MiB heap limit. Every request still checks and reads the current allowed\nsaved sources. Only changed source text and removals are sent to the language service.\nThis preserves freshness and filesystem checks without rebuilding the service each\ntime. Workers expire after 60 seconds idle and are terminated on cancellation,\ntimeout, policy changes and server shutdown. Only one inspection runs at a time.\n\nApprovals use snapshots on the shared SSE connection, with polling only for older\nhosts that do not expose the topic. Inventory, statistics, automation, notification,\ndeferred-run and visible diff polling use the common scheduler. Shared polling waits for completion before scheduling another request, pauses in\nhidden tabs and refreshes when visible. Background inventories explicitly opt in to\nsharing concurrent identical GETs; selection-sensitive reads always issue fresh requests.\nMutations invalidate the request-sharing map. Writes are never automatically retried.\n\n## Measurement and verification\n\n`/api/health` includes bounded HTTP timing samples, event-loop delay/utilization,\nprocess memory, disk queue depth/bytes, slow-client disconnects, storage-cache counts\nand language-worker reuse counts. Settings diagnostics include this snapshot and\nbrowser shell-ready/long-task measurements. Metrics contain no request bodies or\nquery strings. These are local diagnostic counters, not exported telemetry.\n\nRun:\n\n```sh\nnpm run build\nnpm run benchmark:performance\nnpx tsx --test test/performance-boundaries.test.ts test/transcript-pages.test.ts\nnpx tsx --test test/e2e/history-launch.e2e.ts\n```\n\nThe benchmark uses a temporary synthetic home, 10,000 indexed runs and 100,000\ntranscript events. It reports timings, scanned/response bytes and built asset sizes;\nit does not access the user's actual run history or invoke providers. Treat timings\nas machine-dependent observations. The deterministic tests enforce ordering, buffer\nbounds, cursor correctness and failure behavior instead of fragile timing thresholds.\n\nFurther optimization should follow real profiles. SQLite-backed search, Rust modules,\nadditional worker offloading and adaptive process limits remain architectural options,\nnot requirements for these bounded paths.\n\n## Before/after and native comparison\n\nSee the [measured results](performance-results.md) and committed raw samples.\n\n`npm run benchmark:compare -- --baseline <commit> --trials 7` builds the baseline\nfrom a Git archive, the current TypeScript app, and a temporary copy with the\nexperimental Rust transcript reader. It uses identical synthetic fixtures and real\nHTTP servers plus Chrome. One browser warmup precedes repeated fresh browser\ncontexts, and variant order rotates. The OS filesystem cache remains warm. Default\nfixtures have 1,000, 5,000 and 100,000 events. `--scenarios small,medium` selects a\nsubset. `--api-repeats 1` makes one API measurement per browser trial instead of\nthe default five. Raw samples, machine/tool versions, commit IDs and failures are saved to\n`output/performance/comparison.json`; `--output` overrides that destination.\n\nThe UI measurement ends when the latest event is visible after opening Show work.\nThe old app transfers/replays all events; the new app transfers a bounded latest\npage with Earlier navigation. API timing likewise compares full replay to the\nlatest page, not equal-size responses. Browser received bytes are decoded resource\nbytes, not compressed wire bytes. Heap is JavaScript heap after forced GC, not\nwhole-process memory. Timeouts are failures, never silently replaced with a timing.\nAgent execution and provider inference are excluded. See the Rust experiment's\n[README](../experiments/rust-transcript/README.md) for its scope and parity test.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Performance"},{"group":"Build and extend","id":"run-storage","source":"docs/run-storage.md","body":"# Run storage\n\nArchive completed work to reduce retained detail in memory. Open an archived run to load its full evidence; archival caches do not replace the durable event log or delete worktrees by themselves.\n\n## Understand archived history\n\nThe append-only run event log and execution journal remain the source of truth. Startup recovers at most four runs concurrently, consuming fallback logs one record at a time. Recovery retains the latest run snapshot and latest state of each step; provider transcripts are read only when that run’s history is requested. It no longer builds an array containing every historical event log. A recovery record has a 64 MiB bound; exceeding it fails recovery visibly instead of silently dropping an ID.\n\nOnly archived, completed runs with no pending notes, follow-ups, approval, watch or unresolved remote execution can use cold storage. Running, interrupted, failed, stopped and unarchived runs remain in the normal live/resumable map. No worktrees or execution journals are removed by this optimization.\n\nEach eligible run has optional `runs/<id>.cold-index.json` and `runs/<id>.cold-detail.json` projections. The compact index is at most 128 KiB; the full detail projection is at most 16 MiB. Oversized runs stay in the normal map. The projections carry checksums and exact source-file identities. A missing, damaged, changed or stale projection is rebuilt from the authoritative event log. They are caches, not a second source of truth.\n\nCold list/history entries explicitly return `cold: true` and `detailAvailable: true`. They preserve IDs, timestamps, repository/worktree ownership, parent/source/project provenance, source trust, pinned filesystem policies, permissions, usage, statistical counters and review attribution. Large task/summary previews are shortened, and heavy team graphs, context evidence, conventions and review prose are omitted. Open the run to read its complete metadata, transcript and evidence. Full-text history searches use indexed complete task and source/title text, including text beyond the preview. Legacy indexes obtain this text once during asynchronous startup; normal searches do not hydrate archived details.\n\nFull metadata is read on demand through an eight-entry, 32 MiB least-recently-used cache. Individual reads are bounded to 16 MiB. A cold read returns a detached copy; mutation and Resume promote the complete run before changing it. Weak references preserve any still-held original live run handle during promotion without retaining archived full state. Updated data stays live until its queued event snapshot has been flushed and a matching cold projection has been saved. Source changes invalidate cached detail. Cold metadata is never used to reconstruct execution claims or substitute for journal replay.\n\nHistory keeps every run ID and count. Cursor ordering uses creation time and run ID so runs created in the same millisecond are not skipped. Attention pagination includes every running run, plus at most 100 older attention items per page. Authorization filtering happens before totals and cursors. The response reports total attention, truncation and a continuation cursor; history remains available for older entries.\n\nReviewed retention manifests include both cold projection files when deleting their owner run. Their recent cache-generation time does not reset the original run’s retention age. Offline log compaction removes the derived cold projections so the next start rebuilds them. The normal backup and filesystem-policy boundaries continue to apply to these files.\n\nThis bounds retained full archived metadata and recovery batches. The compact index still grows with the number of recorded runs, and active/resumable runs remain in memory deliberately. The current UI reads bounded transcript pages directly by byte offset. Legacy stream clients and full exports can still request complete event history. Search uses an in-memory index, not a database. See [Performance and resource boundaries](performance.md) for limits, diagnostics and benchmarks.\n\n---\n\n[Documentation library](README.md) · [Getting started](getting-started.md) · [Ask code-flow](help.md)\n","title":"Run storage"},{"group":"Build and extend","id":"documentation","source":"docs/documentation.md","body":"# Documentation and GitHub Pages\n\nThe Markdown files in this repository are the source for both the local help library and a static documentation site. The site uses the same warm paper surfaces, olive controls, mint accents, Inter fonts, Georgia headings and light/dark theme tokens as the application.\n\n<picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"images/docs-dark.png\">\n <img src=\"images/docs-light.png\" alt=\"The Code Flow documentation library, using the application's colors and typography with grouped navigation, search and local help.\" width=\"100%\">\n</picture>\n\n## Read locally\n\n`npm start` builds the documentation with the app. Open **Application menu → Help & documentation** or visit `/docs/` on the application server. Search and navigation work locally; the **Ask code-flow** chat side panel uses your installed CLI after owner authentication. The conversation and draft follow you between guides in the same tab.\n\nTo preview only the static site:\n\n```bash\nnpm ci\nnpm run docs:preview\n```\n\nOpen [127.0.0.1:4319](http://127.0.0.1:4319). This preview intentionally behaves like GitHub Pages, so companion controls are omitted. Restart the preview after editing Markdown or site assets.\n\n## Publish to GitHub Pages\n\n1. Push this repository to GitHub with `main` as the publishing branch. If your default branch differs, update `.github/workflows/docs.yml` accordingly.\n2. Open the repository's **Settings → Pages** and choose **GitHub Actions** as the build source.\n3. Run the **Documentation** workflow manually, or push a documentation change to `main`.\n4. Open the deployment URL reported by the workflow's `github-pages` environment.\n\nThe upstream site is [Code Flow documentation](https://midhunxavier.github.io/code-flow/). GitHub may redirect this address to the owner’s configured Pages domain. Forks use their own owner and repository names. Adding the workflow alone does not activate or publish a site.\n\nThe workflow builds `dist/docs`, runs the documentation tests, uploads the Pages artifact and deploys it. Pull requests build and test without deploying. All site assets and links are relative, so project subpaths and custom domains work without a base-URL setting. There are no CDN fonts, analytics scripts or client framework dependencies.\n\nFor another static host, build and upload the contents of `dist/docs`:\n\n```bash\nnpm run build:docs\n```\n\n## Edit a guide\n\n1. Edit its Markdown file under `docs/`. Keep task instructions first, followed by limits and API details.\n2. Add new guides to `docs/site/navigation.json` and link them from the docs index.\n3. Link to other guides using relative `.md` paths. The builder rewrites those links to `.html` and preserves heading anchors.\n4. Run `npm run build:docs` and `npm run test:docs`.\n5. Preview desktop and mobile layouts in both themes when changing presentation.\n\nThe generated `public/docs` and `dist/docs` directories are ignored by Git. Commit the source Markdown, site assets and screenshots instead. `npm run build` packages the docs with remote host bundles too. App development builds the docs at startup; rerun `npm run build:docs` after changing guide content during a dev session.\n\n## Screenshots\n\nThe README screenshots are captured from the real application UI with deterministic fake CLIs and a disposable example repository. They are demonstration data, not live customer work or evidence of provider performance.\n\nRun the reproducible capture script with Chrome installed:\n\n```bash\nnpm run screenshots:docs\n```\n\nIt builds the app, starts a private fixture server, creates a sample run and captures the composer, run and transcript in light and dark mode. It also captures documentation in both themes. Screenshots are written to `docs/images`; transient browser artifacts belong in `output/playwright`. The fixture server and temporary repository are removed afterward. The script does not use real provider quota.\n\nKeep images focused, use descriptive alternative text, and label demo data wherever a screenshot could be mistaken for verification evidence. Re-capture when navigation or the run layout changes.\n\n## Implementation\n\n| File | Responsibility |\n| --- | --- |\n| `scripts/build-docs.mjs` | Markdown rendering, links, navigation, table of contents and search corpus |\n| `docs/site/navigation.json` | Guide groups and reading order |\n| `docs/site/style.css` | Documentation layout, using app theme tokens and fonts |\n| `docs/site/app.js` | Search, theme, code copying and local chat panel |\n| `src/docs-help.ts` | Static local serving, bounded retrieval and CLI help requests |\n| `.github/workflows/docs.yml` | Build validation and Pages deployment |\n\n[Documentation library](README.md) · [Architecture](architecture.md) · [Ask code-flow](help.md)\n","title":"Documentation and GitHub Pages"},{"group":"Build and extend","id":"releasing","source":"docs/releasing.md","body":"# Preparing a preview release\n\nThe source repository stays private to npm publishing (`private: true`). The release builder creates a separate, publishable package in `dist/npm` with compiled JavaScript, UI, local docs, workers, SSH helpers and licenses. It has no install scripts or runtime npm dependencies. The npm package and macOS app use that same runtime.\n\n## Build and validate\n\nUpdate the root package version and lockfile to a new `x.y.z-preview.N` value, then run:\n\n```sh\nnpm ci\nnpm run typecheck\nnpm test\nnpm run release:prepare\nnpm run test:package\n```\n\n`release:prepare` writes the npm tarball, SHA-256 checksum, generated Homebrew formula and draft release notes to `output/releases`. The packaging acceptance test installs that actual tarball in a temporary directory outside the checkout, starts the server, checks the browser opener and bundled docs/assets, exercises the semantic worker, then verifies graceful shutdown and headless startup. It uses fake agent CLIs and never spends provider quota.\n\nInspect the package before publishing:\n\n```sh\nnpm pack ./dist/npm --dry-run\nnode dist/npm/bin/codeflow.mjs --help\n```\n\nThe **Preview release** GitHub workflow builds and tests the tarball on macOS and Linux, builds both macOS architectures, and creates a **draft prerelease** with the exact artifacts. It accepts an existing Git ref and checks that its version matches the requested tag. Run it against a committed, reviewed release ref. Publishing that draft starts the separate **Publish npm release** workflow. Creating a draft alone does not publish npm or create a Homebrew tap.\n\n## Automatic npm publishing\n\nThe published package is scoped as `@midhunxavier/code-flow` because npm rejected the unscoped name as too similar to the existing `codeflow` package. The installed commands remain `codeflow` and `code-flow`.\n\nWhen a GitHub app release is published, `.github/workflows/npm-publish.yml` downloads its npm tarball and checksum, checks the package name and version against the release tag, and publishes those exact bytes. It updates **both `latest` and `preview`** to the new version, so the unqualified npm install command also follows the current preview. It never rebuilds the package during publication.\n\nThe publisher uses npm's [trusted publishing](https://docs.npmjs.com/trusted-publishers/) with GitHub Actions OIDC. Configure this once in the npm package's **Settings → Trusted Publisher**:\n\n- Provider: GitHub Actions\n- Organization or user: `midhunxavier`\n- Repository: `code-flow`\n- Workflow filename: `npm-publish.yml`\n- Environment name: `npm`\n- Allowed actions: **npm publish** and **npm dist-tag**\n\nThe matching GitHub environment is `npm`. The workflow grants `id-token: write` only to the publishing job and pins npm 11.21.1, which supports OIDC for both publication and tag updates. No `NPM_TOKEN` secret or interactive login is needed for each release. Do not remove existing trusted publishers or weaken account 2FA when configuring this workflow.\n\nnpm scans new uploads before they become installable. The job waits up to 20 minutes for the version, five more for the tarball, and five for tag visibility. It verifies the downloaded npm tarball against the GitHub artifact, then a separate job without publisher permissions checks fresh installs through both tags. Inspect both jobs before calling the npm release complete. The publisher disables build provenance generation because it uploads an existing artifact rather than building it.\n\nIf a run fails or needs to be retried, run **Publish npm release** manually with the existing published tag:\n\n```sh\ngh workflow run npm-publish.yml -f tag=v0.1.0-preview.4\n```\n\nA retry checks an existing npm version's integrity before repairing its tags. It refuses different bytes for the same version and skips channel changes for older releases. Runs are serialized to prevent two releases racing to update tags. If npm is still scanning an accepted upload, wait and rerun; do not increment the version or replace the release tarball merely to retry.\n\nThe automatic trigger requires a GitHub `release.published` event. Publishing the draft in GitHub's UI or with an authenticated maintainer's `gh release edit --draft=false` produces that event. If another Actions workflow publishes the draft using its `GITHUB_TOKEN`, GitHub suppresses follow-on release workflows; that workflow must explicitly dispatch **Publish npm release** instead.\n\nFor an exceptional manual publication, authenticate as the package owner and publish the tested tarball, then update both channels:\n\n```sh\nnpm whoami\nnpm publish output/releases/midhunxavier-code-flow-0.1.0-preview.4.tgz --tag preview --access public\nnpm dist-tag add @midhunxavier/code-flow@0.1.0-preview.4 latest\nnpm view @midhunxavier/code-flow@latest version\nnpm view @midhunxavier/code-flow@preview version\n```\n\nUse the actual version and filename for subsequent releases. The packaged manifest defaults to `preview`; the release workflow explicitly moves both channels. Verify `npx @midhunxavier/code-flow@latest --version` and `npx @midhunxavier/code-flow@preview --version` from a clean directory after registry publication.\n\n## macOS DMG\n\nBuild on macOS with Xcode Command Line Tools installed:\n\n```sh\nnpm run package:macos -- arm64\nnpm run package:macos -- x64\nnpm run test:macos\n```\n\nThe builder downloads an official Node runtime with a pinned SHA-256, packages a pinned Electron runtime with the prepared backend and Node license, compiles the native computer-use helper, and creates a drag-to-Applications DMG. The app requires macOS 13+. Its sandboxed renderer has no Node access, and the separate local backend survives window reloads and closing. Lifecycle tests launch the actual packaged app, verify renderer isolation and backend continuity, and check normal quit and crash cleanup. Both architectures are tested on matching GitHub runners. Git and the agent CLIs remain user-installed. The launcher reads the login shell's PATH to find common CLI and version-manager installations.\n\nWithout signing credentials the output is explicitly named `-unnotarized.dmg`. These builds are for developer testing; do not describe them as signed, notarized or Gatekeeper approved.\n\nFor distributable DMGs, install a **Developer ID Application** identity in the build keychain and save notarization credentials with `xcrun notarytool store-credentials`. Set:\n\n```sh\nexport CODEFLOW_SIGN_IDENTITY='Developer ID Application: Your Name (TEAMID)'\nexport CODEFLOW_NOTARY_PROFILE='code-flow-notary'\nnpm run package:macos -- arm64\nnpm run package:macos -- x64\n```\n\nThe builder signs Node with its JIT entitlements, signs the app and DMG, submits to Apple's notarization service, staples the ticket, and verifies the result. Never commit certificates, credentials or keychain files.\n\n### Desktop updates\n\nSigned and notarized builds also produce `code-flow-VERSION-macos-ARCH.zip`, its SHA-256 file, and `releases-ARCH.json`. The ZIP contains the notarized, stapled app; the feed uses Electron's [Squirrel JSON format](https://www.electronjs.org/docs/latest/tutorial/updates) with a version, exact asset URL, checksum and size. The release workflow attaches these automatically. Publish the DMG, ZIP and feed together under the matching `vVERSION` GitHub release, and keep the same Developer ID signing identity across updates. Unnotarized builds never produce a native update feed.\n\nThe desktop main process checks public GitHub releases on launch, every six hours, and on demand. It compares stable/preview versions numerically, skips drafts and missing architecture assets, and never selects an older version. It downloads through Electron's native updater only after the user chooses **Download update**. Restarting uses the normal active-agent confirmation and backend shutdown. Unnotarized installations offer the matching DMG instead. No GitHub token is needed, and browser/npm clients have no desktop update controls.\n\nBefore publishing the first signed updater release, install it in Applications and test updating to a second signed version on both architectures. Unit and lifecycle tests cover release selection, failure/retry states, bridge isolation and restart cancellation; only this two-version acceptance check validates Apple's signing/notarization and Squirrel's actual app replacement. Existing desktop previews without the updater need a manual upgrade to acquire it.\n\nThe GitHub workflow can import the following repository secrets into a temporary build keychain: `MACOS_CERTIFICATE_BASE64`, `MACOS_CERTIFICATE_PASSWORD`, `MACOS_SIGN_IDENTITY`, `APPLE_ID`, `APPLE_TEAM_ID` and `APPLE_APP_PASSWORD`. Supply all six for signed and notarized builds; with none set, it produces clearly labeled unnotarized developer builds. Partial credential configuration fails instead of silently dropping signing.\n\n## Homebrew\n\n`release:prepare` generates `output/releases/Formula/code-flow.rb` from the actual npm tarball's SHA-256. After the GitHub prerelease assets are published, copy this formula into `Formula/code-flow.rb` in the `midhunxavier/homebrew-code-flow` repository, then test:\n\n```sh\nbrew install --build-from-source midhunxavier/code-flow/code-flow\nbrew test midhunxavier/code-flow/code-flow\ncodeflow --version\n```\n\nThe tap must exist and contain the generated formula before advertising the Homebrew command as available. Regenerate the formula whenever the tarball changes; it references the GitHub release asset, so npm registry availability is independent.\n\n## Publish the GitHub prerelease\n\nReview the draft notes and attached checksums. Verify each DMG's signing status and label it accurately. Publish the draft as a **prerelease**, leaving GitHub's stable latest release unchanged. This starts automatic npm publication and advances both npm tags after verification. The draft notes include an exact GitHub tarball command so the preview can be tried while npm processes the upload.\n\nKeep platform claims precise: macOS and Linux are supported; Windows users use WSL2. Native Windows execution is not supported. On a real Windows machine, confirm WSL networking and the Windows browser opener before claiming end-to-end Windows validation.\n\n[Documentation library](README.md) · [Installation](installation.md)\n","title":"Preparing a preview release"},{"group":"Engineering records","id":"application-improvements","source":"docs/application-improvements.md","body":"# Application improvement program\n\n> Engineering record. Status, measurements and decisions below describe the recorded implementation or review. For current usage, start with the [documentation library](README.md).\n\nThis work continues the application review on branch `codex/application-improvements`, created from `main` at `abbb6be`. The original review contained defects, 14 broader product problems, and 73 feature ideas; those are tracked separately below.\n\nThe later `main` update at `d1677e1` is also integrated. Its composer Improve feature\nretains Quick/Repository modes with cancellation, guarded Undo, safe provider\npermissions, disposable private committed snapshots and mutation leases. Shared access, filesystem\nrestrictions and nonlocal execution profiles cannot bypass their execution rules\nthrough this endpoint.\n\nBefore publication, this branch also integrates `main` at `7a593cc`: pinned\nworktree Git namespaces, stronger test-integrity checks, compact run-log patches\nand shared live-update transport. New workspace/verification helpers retain those\nnamespace guards. Cold recovery decodes patches incrementally and refreshes source\nidentities after compaction. Owner stream sharing uses a private channel secret\nand an independent enumerable lock ID; shared accounts receive neither, and\nidentity changes close old owner connections. Targeted namespace, storage, broker,\nauthorization and same-tick repository-picker regressions cover the integration.\n\nCurrent scope: all six priority correctness packages implemented; all 14 broader\nproblems have concrete improvements; all 73 feature items have implemented\nacceptance scopes. Full-suite acceptance passed. Results are recorded\nbelow; feature boundaries remain explicit rather than claiming unrestricted\nprovider parity or complete accessibility conformance.\nAn item is complete only when its behavior is implemented, reachable from its\nintended interface, documented, and covered by appropriate validation. A service\ninterface, mock, or roadmap entry alone is not an implemented feature.\n\n## Correctness and quality gates\n\n- [x] Destination symlink containment and exclusive worktree copying.\n- [x] Repository selection and configuration responses cannot overwrite newer choices.\n- [x] Notes initialization, failed-write recovery, revision history, and conflicts.\n- [x] Invalid routes recover without blanking the application.\n- [x] Memory deletion descriptions match actual semantics; editing resolves conflicts.\n- [x] Critical browser flows are required by CI, with stable test synchronization.\n\n## Existing product problems\n\n- [x] 1. Simplify the first-run decisions.\n- [x] 2. Give the composer room instead of opening an empty side panel.\n- [x] 3. Make mobile execution controls readily accessible.\n- [x] 4. Use consistent completion and verification states.\n- [x] 5. Surface native tool approvals.\n- [x] 6. Make execution isolation explicit and enforceable.\n- [x] 7. Secure phone transport and individual sessions.\n- [x] 8. Bound history startup, storage, and browsing. Streaming recovery uses four concurrent runs; archived detail uses an eight-entry/32 MiB cache and compact source-validated projections; old attention is capped and visibly paginated. Compact IDs and active/resumable metadata still scale with history. See [run storage](run-storage.md).\n- [x] 9. Include inline GitHub review comments.\n- [x] 10. Enforce browser correctness in CI.\n- [x] 11. Make evaluation reproducible and retain experimental evidence gates.\n- [x] 12. Separate core responsibilities behind tested modules. Persistence projection/recovery/cold storage, workspace operations, shared authorization, policy enforcement, remote execution, code-host operations and repository coordination have explicit tested modules. The engine retains execution authority and the server composes the route/lifecycle boundaries; further size reduction is ongoing maintenance.\n- [x] 13. Improve keyboard, focus, touch, and screen-reader behavior. Theme/viewport browser audits, keyboard navigation and focus restoration, inert closed panels, contrast, target size, mobile tool selection and reduced motion are covered. Assistive-technology/user testing remains ongoing; no complete WCAG certification is claimed. See [accessibility acceptance](accessibility.md).\n- [x] 14. Attribute provider results with samples and uncertainty.\n\n## Feature acceptance backlog\n\n- [x] 1. Guided onboarding and dependency/authentication health, including explicit terminal/browser/Docker discovery and refresh.\n- [x] 2. Repository readiness inspection and isolated command validation.\n- [x] 3. Simple and advanced composer modes.\n- [x] 4. Named versioned team presets with import/export.\n- [x] 5. Editable acceptance criteria.\n- [x] 6. Parameterized task templates.\n- [x] 7. Effective settings and provenance inspection.\n- [x] 8. Command palette.\n- [x] 9. Scoped native tool approval inbox.\n- [x] 10. Editable validated workflow graph.\n- [x] 11. Priority queue and project fairness.\n- [x] 12. Safe checkpoint pause.\n- [x] 13. Fork from a checkpoint.\n- [x] 14. Selective task reruns and downstream evidence invalidation, with pinned retained baselines and fresh final review/checks.\n- [x] 15. Quota-aware deferred execution with durable claims, reported reset windows and explicit ambiguous-launch recovery.\n- [x] 16. Token and API-equivalent usage ceilings.\n- [x] 17. Conflict-aware task assignments.\n- [x] 18. SSH remote workers with explicit repository mappings, worker-local credentials, bounded exact source/result bundles, heartbeats, cancellation, durable claims and receipt-only recovery. Actual encrypted loopback SSH acceptance passed. See [worker setup](remote-workers.md).\n- [x] 19. Multiple verification gates.\n- [x] 20. Repository verification profiles.\n- [x] 21. Requirement-to-evidence mapping across workflows.\n- [x] 22. Review coverage reporting.\n- [x] 23. Change risk summaries.\n- [x] 24. Baseline and flaky-check diagnosis.\n- [x] 25. Optional mutation verification.\n- [x] 26. Visual and accessibility verification gates.\n- [x] 27. GitHub review threads with inline locations/replies, bounded complete-thread loading, explicit remote resolve/reopen, fresh revision/head checks and honest postflight race reporting. See [thread synchronization](github-review-threads.md).\n- [x] 28. Validate the current merge target.\n- [x] 29. Export verification reports.\n- [x] 30. Symbol-aware code search and JavaScript/TypeScript imported references through an isolated language service.\n- [x] 31. Static architecture exploration across JavaScript/TypeScript, Python, Go, Rust and Java, including manifest packages, directory boundaries, cycles, explicit unresolved/computed edges and exports. No project configuration executes. See [architecture explorer](architecture-explorer.md).\n- [x] 32. File editing with save previews and conflicts.\n- [x] 33. Selective file/hunk application.\n- [x] 34. Three-way conflict resolution.\n- [x] 35. Multi-service workspace launch profiles.\n- [x] 36. Context inspection before dispatch: bounded directory expansion, explicit assembled-input measurements and warn/block ceilings. Hidden provider history and later file/image tokens are outside this scope.\n- [x] 37. Concurrent workspace activity coordination with in-process workspace mutation leases across starts, resumes and owned worktrees. External/manual programs remain outside the lease system.\n- [x] 38. Full checkpoint comparison and guarded staged inverse commits, alongside selective file/hunk restoration. Merge-commit mainline decisions remain manual.\n- [x] 39. Memory provenance graph.\n- [x] 40. Memory edit conflict resolution.\n- [x] 41. Memory deletion impact preview.\n- [x] 42. Stale-memory detection.\n- [x] 43. Memory duplicates and supported semantic contradictions with policy synonyms/negation, structured boolean/text/numeric interval claims, unit conversion, exact source revisions and overlapping applicability. Unsupported prose is counted and no winner is selected automatically. See [semantic claims](memory-claims.md).\n- [x] 44. Dedicated versioned project conventions with immutable history, conflict-safe drafts, preview restore and guarded explicit publication.\n- [x] 45. Searchable revisioned notes.\n- [x] 46. Curated project onboarding summaries.\n- [x] 47. Explicit schedule time zones and previews.\n- [x] 48. Inspectable webhook queue.\n- [x] 49. Failed delivery replay.\n- [x] 50. Automation dry runs.\n- [x] 51. Grouped prioritized review inbox for manual and unattended questions, blocked work, failures and results.\n- [x] 52. GitLab/Bitbucket code fetch, prepared-container review, comments/replies/resolution, verified-result push, new PR creation and pinned Git merge. Durable unknown-operation receipts prevent automatic replay. Unsupported native queue/policy/atomic approval capabilities are explicit and fail closed. See [code-host workflows](code-hosts.md).\n- [x] 53. Linear and Jira sources.\n- [x] 54. Opt-in external notifications.\n- [x] 55. Documented CLI and versioned API.\n- [x] 56. Multi-repository dependency DAGs, pinned verification/source policy, durable child receipts, current accepted result evidence, reviewed shipping matrices and explicit per-repository CAS merges. Actual two-repository browser acceptance passed. See [coordinated changes](coordinated-changes.md).\n- [x] 57. Isolated agent containers.\n- [x] 58. Secure paired phone sessions.\n- [x] 59. Per-project CAS read/write allow/deny rules enforce app access and intersect current/pinned policies. Manual filtered Docker tasks use immutable prepared images, allowed committed files in a fresh Git namespace, read-only denied-write mounts, bounded outputs, confirmed cancellation and explicit source/policy/content-checked review-branch imports. Ordinary native/SSH/automated execution remains paused under restrictions. Actual Docker isolation/import/cancellation and browser acceptance passed. See [filesystem policy](filesystem-policy.md).\n- [x] 60. Secret filtering for exported content.\n- [x] 61. Saved per-type retention policies for archived runs, attachments, run memory and worktrees, with protected exact plans and explicit offline application.\n- [x] 62. Backup and restore.\n- [x] 63. History compaction and pagination.\n- [x] 64. Sanitized diagnostic bundles.\n- [x] 65. Persistent connection status.\n- [x] 66. Continuous official supported-version CLI version/help matrix, bounded isolated installs, exact executable/version checks, protocol fixtures and retained reports. Five actual official CLI probes passed locally; GitHub Actions execution awaits a pushed branch. See [compatibility evidence](provider-compatibility.md).\n- [x] 67. Task-specific provider comparisons.\n- [x] 68. Human rework tracking.\n- [x] 69. Critical-path timing analysis.\n- [x] 70. Reproducible benchmark fixtures and model identities.\n- [x] 71. Local scrypt identities, revocable expiring sessions, scoped viewer/reviewer project roles, safe results/feeds/exports and commit-anchored human review. Owner remains the sole executor. See [shared projects](shared-projects.md).\n- [x] 72. Local VS Code integration with SecretStorage connection, task creation, run inspection, cancellation and result worktrees; packaged and tested in the actual editor host.\n- [x] 73. Explicit trusted bundled provider/workflow plugins, negotiated API/capabilities, captured hash-verified cache bytes, atomic registration and immutable run version/hash pins. No repository auto-discovery or HTTP installation. See [extension contracts](extensions.md).\n\n## Implementation boundaries\n\nMutation, visual and accessibility gates execute explicitly configured repository\nscripts; they do not supply mutation engines or image baselines. External\nprovider APIs were validated with HTTP fixtures and real local Git repositories,\nnot live third-party account writes. GitLab/Bitbucket code fetching is explicit;\nordinary source-text imports remain read-only. Native containers and encrypted\nloopback SSH use fake CLIs, with no inference quota. Official compatibility\nprobes execute only version/help. Shared roles grant review access; phone pairing\ngrants owner control. Critical-path metrics use observed time, not predictions.\n\nArchitecture and semantic-memory analysis are bounded static/deterministic tools,\nnot complete runtime dependency discovery or unrestricted natural-language\nreasoning. Extensions are trusted full-authority server code. Remote workers\nmust be provisioned explicitly. Code-host native queues and unsupported policy\nsemantics require the provider UI. These are documented implementation boundaries,\nnot silently simulated success.\n\n## Validation\n\nUse fake agents for development. Never spend real provider quota, publish results,\nsend external notifications, or mutate third-party accounts as a side effect of\nthe test suite. Account-dependent features require explicit configured credentials\nfor live acceptance testing; fixture coverage is recorded separately.\n\nThe first checkpoint passed 621 unit tests with three opt-in Docker cases skipped;\nthose three native Docker cases passed separately. Its full browser suite passed\n35/35. The second batch's unit run passed 688/688 executed checks (three optional\nDocker cases skipped). Focused browser acceptance also covers conventions,\ndependency discovery, deferred restart/receipt recovery, selective reruns,\nretention, context budgets, commit reversions and language-service references.\nThe final full browser rerun passed 45/45 after the folder-picker race fix.\nThe VS Code extension passed actual host acceptance on installed VS Code 1.141.0,\nand the packaged VSIX passed the same host acceptance after isolated installation,\nincluding delayed-response connection/disconnection regressions. No real provider\nquota, external notifications or third-party account mutations were used.\nThe test runner limits unit concurrency to four files and browser concurrency to\ntwo because these suites launch real Git, server and browser processes.\n\nThe final implementation checkpoint passed 812 unit checks with six optional\nnative-runtime checks skipped. Those five real Docker checks and the encrypted\nloopback SSH check passed separately. The full browser suite passed 64/64.\nThe last filtered-task concurrency changes passed 31 focused unit checks and\n2/2 browser flows after the main suite had started. Native acceptance includes\nfresh-home credential seeding with dummy credentials, denied-file/original-Git\nabsence, read-only mounts, exact review-branch import and confirmed cancellation.\nThe packaged VSIX passed actual installed VS Code host acceptance again.\nTypecheck, UI/worker builds, 68 accessibility renderings and local documentation\nlinks passed. Provider version/help probes used actual official binaries; no\nlive inference, account mutation or notification delivery occurred.\n\nAfter integrating the newer `main` composer feature, 19 existing browser and\naccessibility checks, four new Improve browser flows and two shared/policy security\nflows passed. Eleven prompt-improvement unit checks and all 14 server-reliability\nchecks passed, including a delayed-turn lease/cancellation test that exposed and\nfixed an incorrect 400 response for concurrent run starts. A subsequent source\nreview moved Improve out of the working checkout entirely so external edits can\nnever be discarded by read-only cleanup. Final typecheck and\nUI/worker builds passed. The Improve menu adds two mobile theme audits, bringing\nthe accessibility acceptance total to 70 renderings; both mobile screenshots were\ninspected for readable controls and overflow.\n\nThe post-merge full unit run passed 828 checks with six optional native-runtime\ncases skipped (already passed separately). After private-snapshot hardening,\nall 15 Improve checks and all 15 server-reliability checks passed again, including\nexternal source edits/commits, private cleanup, executable-configuration exclusion,\nsnapshot bounds and early environment checks across both API prefixes. The four\nImprove browser flows passed with the isolated backend; the final menu-wrapping\nchange also passed its mobile/profile regression and visual inspection.\nThe final subprocess PATH fix passed 15 foundation/filesystem-policy unit checks\nand the policy browser flow. Actual malicious cwd/relative-bin executables were\nrefused for inherited and overridden PATHs; explicit absolute fixture binaries\nremain usable. The exact-route regression passed again after adding malformed\nsuffix/method checks across both API prefixes.\n\n## Result-first product experiences\n\nThe composer retains its outcome starters and now records an explicit task type\non new runs. Expand **Workflow advice from this repository** to inspect check\nsuccess, acceptance feedback, correction samples and supporting runs, then apply\na workflow deliberately. Suggestions require three completed samples per\nworkflow, retain failures in their denominator and do not infer task types from\nolder prose. They describe history, not task difficulty or a predicted winner.\n\nRunning work has a compact progress card with dispatch pause and run priority.\nAuto teams also have a **Who's doing what** map: select a task to inspect its\nowner, paths, dependency blockers and open questions. A task priority applies at\nthe next dispatch and cannot override dependencies, occupied members/paths,\nreview reservations or budgets. Existing journaled dispatches remain pinned on\nresume. Standing-team task priorities remain controlled by their mission.\n\nFinished results lead with **Try this result**, evidence, and commit-specific\n**Accept result / Needs more work** feedback. The preview waits for its allocated\nport before opening. Feedback neither publishes nor substitutes for standing-team\napproval. Project pages and Insights show historical acceptance, median elapsed\ntime to a verified result, recorded correction time and merged runs. Unrated\nruns and missing correction records are explicit; missing corrections are not\nzero-minute samples. Acceptance refers to the latest recorded decision, which\ncan belong to an earlier commit. The result screen flags that difference.\n\n**Show me what changed** captures the running app beside an optional original\nURL at the same viewport. Snapshots use fresh browser profiles and can be\ndownloaded; they are visual observations rather than automated proof. For a\nrecorded scenario, the result instead also offers a replay and a step-by-step\nscreenshot walkthrough beside the original replay.\n\n**Try two alternatives** launches two ordinary runs from the same exact result\ncommit with separate instructions and budgets. Each result has its own preview,\ncheck state and continuation link. No branch is chosen, merged or published\nautomatically. If a comparison launch fails partway through, inspect recent runs\nbefore requesting another; successful children remain visible in their group.\n\n### Record a bug and prepare its regression test\n\n1. Open a local HTTP app in **Browser** and choose **Record a bug**.\n2. Reproduce the flow. Clicks, text input, single-select changes and\n Enter/Tab/Escape are supported. The recording follows same-origin document\n navigation. Shadow DOM, nested frames, file uploads, gestures and authenticated\n fixtures require adapting the exported test.\n3. Stop recording, inspect/remove interactions, name the scenario and specify\n an expected element selector and visible text. Save the immutable scenario.\n4. **Replay original scenario** checks that assertion in fresh Chrome and saves\n a screenshot walkthrough. Password/credential fields are excluded; review\n other recorded form values before saving. Replays can mutate the app's data.\n5. **Prepare verified fix** attaches a generated Playwright regression test to a\n new pipeline draft. The agent is instructed to demonstrate failure before the\n fix and success afterward, adapting it to the repository's test setup. Review\n the draft and its check configuration before starting.\n6. On the result, start its preview and **Replay scenario on this result** to\n compare observations. A replay never changes the run's independent check or\n proof verdict. Only the specified visible-text assertion is tested. The\n recorded branch head is informational: dev servers can serve an older build\n or uncommitted files, so a browser replay does not certify a Git tree.\n\nRecordings are owner-only, stored privately under `CODEFLOW_HOME/journeys`,\nincluded by ordinary backups, and available from each project's **Recorded\nscenarios** library after restart. Export the generated test or delete an unused\nscenario and its evidence there. Limits: 100 scenarios per host, 30 interactions\nper scenario, 1,920px recording width, four retained replay receipts per scenario,\n2.5 million base64 screenshot characters per receipt, 16 MiB per evidence file,\none replay at a time, and the existing 45-second snapshot execution deadline.\nUncaptured frames are omitted, never synthesized. Replay checks local app URLs,\nrefuses code-flow's own listener ports and keeps document navigation on the\nselected origin. The existing snapshot network caveats also apply. Filesystem\nrestrictions pause these operations; shared reviewers cannot access recordings.\n\nProject pages now lead standing teams with a **Project briefing** showing up to\nthree actionable opportunities. Ready drafts come first, followed by reported\nimpact and recency. Cards expose users, benefit, risk, source quotes and the\npinned source commit. **Dismiss opportunity** records a rejection through the\nexisting team decision service, so subsequent discovery respects it. Schedules\nand standing teams remain explicitly activated by the owner.\n\n[Documentation library](README.md) · [Development guide](architecture.md)\n","title":"Application improvement program"},{"group":"Engineering records","id":"project-first-navigation-plan","source":"docs/project-first-navigation-plan.md","body":"# Project navigation review and implementation plan\n\nStatus: Phases 1–5 implemented on 21 October 2026. Phase 6 software validation is complete; the participant pilot remains an external release activity. Original review baseline: `d82a2e1`.\n\nMake **Project** the main place to start, follow and review work. Repositories, runs, shared boards, people and scheduled work should have predictable places inside that project. Show the computer that owns the project and, where relevant, the computer that executes the work.\n\nThe initial experience should be: **Open a project → describe the task → start a run → review the result.** A person using one repository on one computer should not have to configure a workspace, a team or a connection first.\n\n## Implemented navigation contract\n\nThe owner experience uses **Project** for a group of repositories, drafts and runs; **Repository** for a Git root; **Run** for an execution and its evidence; and **Computer** for the host that owns files and project records. **Repository tools** replaces Workspace in the owner navigation. Board contains shared tasks; People contains membership and access. Shared dashboards retain independent safe projections. Project Automation and global Scheduling use the existing schedule, backlog and mission operations.\n\n| Destination / visible label | Address and source identity | Access and compatibility |\n| --- | --- | --- |\n| New run / project draft | `#/project/:id/new`; current computer + project ID | Owner; legacy `#/` and `#/new` resume the last draft; `#/new/unassigned` opens the unassigned draft. |\n| Overview | `#/project/:id` | Owner projection from that computer's projects and runs. |\n| Runs | `#/project/:id/runs` | Owner; independent project query, archived option and pagination. |\n| Repositories / Repository tools | `#/project/:id/repositories/:encodedPath/:tool` | Owner; only project roots and known project run worktrees. Full path stays visible. |\n| Project settings | `#/project/:id/settings` | Owner; project editing, outcomes and scenarios. Missions live in Automation. |\n| Legacy repository tools | `#/workspace`, `#/settings/workspace`, `#/project/:id/workspace` | Global bookmarks retain composer repository context; project bookmarks render Repositories with their project context. |\n| Run / result review | `#/run/:id`, `#/review/:id/:branch` | Existing run identity, branch decoding and review permissions. |\n| Inbox / Insights | `#/inbox`, `#/insights` (including settings aliases) | Existing global filters. Filtering cannot alter a composer destination. |\n| Project Board / People | `#/project/:id/board`, `#/project/:id/people` | Owning computer’s board only (`team=local`). Switching from a linked board replaces the document before rendering collaboration controls. |\n| Linked Board / People | `?team=:connection#/team/project/:id`, `#/team/people/:id` | Connection pinned per document; server-authorized membership and repository keys. Account/grant administration remains owner-only. |\n| Project Automation | `#/project/:id/automation/:kind` | Explicit project and repository; independent of sidebar filters and the previously open composer draft. |\n| Scheduling | `#/automations/:kind` | Current computer and existing saved schedule identity. The global project filter controls the list; choose a project/repository here before configuring new work. |\n| Settings / Connections | `#/settings/:section`, connection groups | Existing owner settings. Old SSH, remote-access and team settings aliases remain supported. |\n| Board / task | `#/team`, `#/team/project/:id`, `#/team/task/:id` | Existing selected collaboration connection + board ID. No local-project identity inferred from matching names or IDs. |\n| Detached side panel | `#/side/:tab/:scope` | Existing run/project/repository pin; target remains independent of composer navigation. |\n| Another computer | `/computer/:computer/` plus these routes | Existing server request prefix and browser-storage namespace. No local fallback when disconnected. |\n| Shared-user dashboard | Existing shared-account entry and projections | Existing grants and safe APIs; owner pages are not rendered for shared accounts. |\n| Invalid/unavailable target | Malformed project route, missing project or removed repository | Explicit recovery state. Retain draft; block a new launch into an invalid destination. |\n\nScreen sketches used for this slice:\n\n```text\nFirst visit Project Overview Project composer\nComputer: This computer Project · Computer Project · repository\nOpen repository Overview | Runs | Repos Task + context\nJoin shared project Describe task / Continue Workflow + run options\nOn another computer Needs you · Running Computer / Project / Repo\n Recent results Start run\n\nMultiple repositories Shared board (retained)\nProject · Computer Collaboration connection\nRepository cards + paths Board / task details\nSelected root or worktree Existing membership/grants\nRepository tool selector No inferred local mapping\n```\n\nThere is one task editor. Overview opens the existing composer rather than maintaining a second task field. Only the visible project section mounts its specialized panels.\n\nDraft storage is versioned IndexedDB, scoped by computer and project, with a retained legacy backup and an unassigned destination for ambiguous migration. Text, attachments, selected paths, workflow roles/models, checks, limits, approvals, source, scenario, context budget and execution options travel together. The current implementation maintains one saved draft per project in this browser; simultaneous editing of the same project's draft in multiple tabs has last-writer behavior.\n\nNew UI launches send an explicit-selection marker. The server verifies that the project exists and the canonical repository remains a member before preparation and again at placement. Adding a repository is an explicit API operation. Existing API/automation callers retain their placement behavior unless they request strict selection.\n\nThe automated scenario baseline covers route aliases, computer namespaces, delayed repository responses, draft switching and launch placement. No human completion-time or wrong-turn baseline was collected in this implementation session. The participant pilot and before/after measurements remain required release validation in Phase 6; automated tests do not establish usability improvement.\n\n## Phases 4–6 implementation and validation\n\nPhase 4 reuses the existing board API, account CAS updates, repository-key editor and explicit host-execution/terminal grants. Owner projects embed only their own computer’s board. Linked boards remain connection-scoped; there is no inferred remote-to-local relationship. URLs retain `team` identity, requests/downloads and subscriptions stay pinned to that document, and a source switch reloads the document. An unavailable project does not expand to all projects. Refused board/task reads clear stale content and controls. Shared People uses only authorized board projections and never mounts owner account controls.\n\nPhase 5 adds Automation with Run once later, Repeat, Process a backlog and Pursue a mission. Project lists ignore the global filter. Repository selection is explicit; opening backlog items waits for the chosen project’s saved agent workflow to restore and retains its destination. Missions move from settings to Automation; actionable opportunities link there from Overview and Inbox. More and command search always expose specialized destinations. Saved collaboration, schedules and missions remain discoverable when the optional Board shortcut is off.\n\nNo rollout flag is needed for this implementation: it uses additive routes, existing permissions/APIs and the compatible draft format from phases 1–3. No execution records, repository keys, roles, schedules or mission history are migrated. Legacy routes and search aliases remain available. No deployment or user-data reset was performed.\n\nValidation record (21 October 2026):\n\n- Full unit suite: 1,212 tests, 1,204 passed, eight opt-in integration checks skipped, zero failures.\n- Project-navigation browser suite: all ten scenarios pass, including colliding linked project IDs, access refusal, project-specific scheduling/backlogs and mobile light/dark accessibility.\n- Shared collaboration/account browser suites: all 24 scenarios pass, including an empty shared project with repository keys and no server clone, maintainer People access without owner requests, live revocation, host execution and terminal grants.\n- Existing mission, experience and scheduling browser scenarios pass after moving mission navigation. An initialization race found by the new backlog scenario was fixed: an unsuccessful draft restore cannot open the picker with the prior repository.\n- Build, TypeScript and 12 documentation checks pass. The documentation capture script now produces project Overview and Automation screenshots with disposable demo data. Mobile screenshots were inspected; More, project tabs and scheduling controls fit at 390 pixels.\n- Final full browser suite: all 153 scenarios pass, zero failures. The earlier full run found six failures. Fixes preserve Auto team answer drafts when a question moves into the attention card, keep the mobile computer header within the viewport while reconnecting, remove duplicate global scheduling filters, and update tests to wait for explicit project selection and use the current repository/backlog controls.\n- Final full unit rerun: 1,204 passed, eight opt-in skips, zero failures. All 52 focused collaboration/API/UI checks also pass after the final visibility and terminology updates.\n- Additional integrations: VS Code extension-host acceptance passes; the opt-in Rust reader test and both real loopback SSH tests pass. After recovering Docker Desktop from stale backend processes, `npm run test:containers` passes all five real Docker checks with zero failures or skips: read/write isolation, host-secret and Git-metadata protection, cancellation, hard timeout, and filtered edit import. Together these runs cover all eight integration checks skipped by the default unit command. Live provider-usage tests were not invoked.\n- Final build, typecheck, documentation checks and whitespace validation pass. Documentation screenshots were regenerated and inspected after the UI fixes.\n\nPR integration against `main` at `0968718` preserves complete project history counts and the newer editable-retry behavior. Retry identity, custom verification and execution options now travel with each project draft, including when projects share a repository. The retry action waits for draft initialization. The full unit rerun passes 1,231 of 1,239 checks with zero failures and eight opt-in skips; all five Docker checks pass separately. The full browser run exercised 165 scenarios: 160 passed, four retry-navigation checks failed, and one reconnect test raced the expected page reload. After fixing those issues, the complete 11-scenario daily-workflow suite (including a new initialization regression) and seven-scenario Tailscale suite pass with zero failures. This verifies 166 distinct browser scenarios across the full run and focused reruns. Final build, typecheck, all 12 documentation checks and whitespace validation pass; refreshed project screenshots were visually inspected.\n\nThe automated scenario matrix is exercised by the project-navigation, team/shared-projects, SSH/computer, automation, mission, workspace and accessibility suites. Automated keyboard/axe checks support validation but do not establish human usability improvement. No participant pilot or completion-time comparison was conducted. The original five-person pilot and its wrong-destination threshold remain an external release gate; no pilot results are claimed.\n\n## Review of the idea\n\nThe direction is sound, but changing labels alone will not resolve the confusion. The app has overlapping navigation destinations, implicit project selection and several legitimate meanings of workspace and team. Those need consistent behavior as well as consistent names.\n\nThe current implementation already provides a useful foundation:\n\n| Finding | Evidence | Design consequence |\n| --- | --- | --- |\n| Projects already group repositories, runs and workflow defaults. A repository can occur in more than one project. | [Project model](../src/projects.ts) | Build on the existing project entity; avoid a new organizational layer or assuming exclusive repository ownership. |\n| Starting a run can create a project or add a repository to one. The composer can change the active project after a repository choice. | [Selection state](../ui/state.ts), [project placement](../src/projects.ts) | Make the destination understandable before launch and make membership changes explicit. |\n| The project page has a task box, metrics, scenarios, missions, results, repository details and automations together. | [Project page](../ui/project.tsx) | Give immediate work priority and move configuration and specialized workflows to predictable destinations. |\n| Human collaboration is partly called People, but its main destination is a task board. | [Application breadcrumbs](../ui/app.tsx), [board](../ui/collab-board.tsx) | Call the task board Board; reserve People for membership and access. |\n| Repository tools opens a page called Repository workspace or the project's workspace. | [Project page](../ui/project.tsx), [activity page](../ui/activity.tsx) | Use Repositories for the destination and Repository tools for actions inside it. |\n| SSH computers have separate API paths, browser state and drafts. The sidebar already lists projects by computer. | [Client routing](../ui/lib.ts), [connected projects](../ui/connected-projects.tsx), [routing regression](../test/workspace-routing.test.ts) | Keep computer identity visible and preserve those boundaries. |\n| A selected collaboration connection can address a different server from the current computer's project store. Shared repositories use keys mapped to local clones. | [Collaboration connections](../src/collab/connections.ts), [collaboration state](../ui/collab-state.ts) | A project name or ID alone cannot identify a board across connections. |\n| Connections, optional People controls and purpose-based Scheduling already exist. | [Connections](../ui/connections.tsx), [Scheduling](../ui/scheduling.tsx), [existing acceptance tests](../test/e2e/simplification.e2e.ts) | Extend the simplification already in progress and retain its useful workflows. |\n\nThree refinements are necessary to the original proposal:\n\n1. **Computer remains visible context.** Removing workspace as a navigation concept must not obscure where files, CLIs, terminals and settings live. Delegating an agent turn to a worker is a different operation from opening a project on another computer.\n2. **Runs and board tasks retain distinct meanings.** A run is an execution with results and evidence. A board task records ownership, discussion, reviews and execution attempts. A combined task system would require a separate product and data-model decision.\n3. **Project organization must feel optional for a simple start.** Opening a repository should suggest a project name automatically. Users should not complete an administrative form before describing their task.\n\nThe expected benefit is fewer decisions and fewer context switches. That remains a usability hypothesis until the proposed flows are tested with users.\n\n## Terminology contract\n\n| Term | Meaning and placement |\n| --- | --- |\n| Project | The main navigation destination for related work. Its identity includes the source that owns it. |\n| Repository | Code attached to a project. A single repository is selected automatically; multiple repositories require a visible selection. |\n| Run | One execution of a requested task. Keep New run, Start run, run history and review terminology consistent. |\n| Board | Optional shared task management within a project. Board cards open task details and their linked attempts or runs. |\n| People | Members, project roles and access. Administrative account creation remains subject to owner authority. |\n| Agents | Providers and roles performing a run. Use Agent workflow for their configuration. |\n| Computer | The location of project files and the Code Flow instance serving them. A run can additionally name an execution worker. |\n| Automation | Scheduled, deferred or triggered work within a project. Keep existing distinctions between repeat tasks, backlogs and missions. |\n| Mission | An optional ongoing goal under Automation. Retain preview eligibility and existing review controls. |\n| Workspace | Acceptable descriptive prose for the app as a whole; remove it from competing primary destinations and ambiguous context labels. |\n\nKeep established workflow names such as Auto team during this change. Explain them under Agent workflow. Renaming every public workflow, internal class, API and storage key would broaden the change without improving navigation.\n\nUse the same display terms in visible labels, accessible names, breadcrumbs, command search, empty states and documentation. Old words remain search aliases so existing users can find relocated features.\n\n## Proposed navigation\n\nThe navigation for the common local-owner case should look like this:\n\n```text\nNew run\nInbox\n\nProjects\n Code Flow\n Storefront\n Add project\n\nApplication menu\n Scheduling across projects\n Insights\n Connections\n Settings\n Help\n```\n\nProjects remain expandable to recent runs, preserving fast access from the sidebar. Show computer groups only when multiple computers are configured. Keep an unobtrusive computer label in the project header, including on a single-computer installation. On a hosted session, use the server's available name or Remote server rather than calling the browser's device This computer.\n\nWithin a project:\n\n```text\nStorefront Computer: Development VM\nOverview Runs Repositories More\n\nMore\n Board\n People\n Automation\n Project settings\n```\n\nOverview, Runs and Repositories are the owner view's primary destinations. Board becomes a visible tab when collaboration is configured; Automation can become visible when the project has active scheduled work. Keep their positions stable for that project rather than changing tabs on every transient response. People stays an access-management destination. Shared users receive destinations allowed by their existing access, such as Board and Results; they do not receive owner tools merely because the shell is shared.\n\nOverview shows the task entry point, items requiring a decision, active work and recent results. Put usage summaries, scenario libraries and detailed mission configuration behind their relevant destinations. Existing mission decisions still appear in the overview and Inbox when actionable.\n\nKeep the global Inbox and Scheduling views as cross-project tools on the current computer. Label their scope. They must not imply that they aggregate every connected computer or collaboration server. Cross-server aggregation is outside the initial release.\n\n## User flows\n\n### First project and first run\n\n1. The empty state offers **Open repository** and **Join shared project**. Remote setup is available through **On another computer** in the repository flow.\n2. Open repository validates the selected repository and suggests its folder name as the project name. Reuse an unambiguous existing project; if several contain it, show their names and require a choice.\n3. Create or open the project through the existing project API. Keep the name editable. Creating the project succeeds independently of launching an agent.\n4. Open the composer with the project and repository already set. Present a recommended installed agent/workflow using existing defaults and availability rules.\n5. The user describes the task, reviews the compact summary and selects Start run. The summary includes repository, branch, execution computer, agents, checks and limits.\n\nAdvanced run options remain available. Preserve current permission, check and approval defaults; reduced visual complexity must not silently change execution behavior.\n\n### Returning to a project\n\nOpen its Overview from the sidebar. Its task box and New run action lead to the same composer with explicit project context. A global New run action reuses the current valid project, or offers a lightweight project choice when none is selected. An existing unsent draft is restored rather than overwritten by a navigation action.\n\nUse one canonical composer for both entry points. Avoid two independent prompt forms with different defaults, attachment behavior or launch validation.\n\n### Working with multiple repositories\n\nShow the selected repository in the composer summary even when there is only one. For multiple repositories, use the last valid selection for that project and computer; show a selector before launch. Repositories displays the full list with paths and branch context sufficient to distinguish identical folder names.\n\nChoosing a repository outside the current project offers explicit **Open its project** or **Add to this project** actions. Adding uses the project's membership operation and reports success before launch. Canceling leaves the current project and draft intact. The server revalidates project membership at launch, including if another tab removed the repository.\n\n### Working on another computer\n\nOpen the remote project directly from the existing computer-grouped sidebar. Preserve the owning computer in the URL, API routing, drafts and settings. A disconnected project shows a reconnect action and its computer name; execution stays unavailable until the intended connection is restored.\n\nSwitching the computer opens that computer's project context. It does not move a project, copy repository files or reuse a local path on the remote machine. The run summary names a delegated worker separately when configured.\n\n### Collaborating with people\n\nAn owner opens Project → People to view members and roles. Existing owner administration supplies account creation and access grants. A maintainer sees only actions the existing API authorizes.\n\nProject → Board shows shared task ownership and progress. Join shared project reuses the existing connection flow, then lists authorized projects and maps shared repository keys to local clones when execution requires them. Joining must not create an unrelated local project with the same name and imply that it is the shared project.\n\nFor a linked server, a board link carries its connection identity as well as its project identity. A local project may show a linked board shortcut only when the relationship has been explicitly established; matching names, paths or IDs is insufficient. Keep the existing connection-scoped board entry available until that relationship is supported reliably.\n\n### Scheduling and ongoing missions\n\nProject → Automation opens the existing Scheduling purposes scoped to that project. Each purpose obtains its repository from the project context rather than directing users to New run just to select one. Keep Run once later, Repeat, Process a backlog and Pursue a mission as distinct choices.\n\nGlobal Scheduling can filter across projects on the current computer. Managed mission schedules remain controlled by mission settings; moving their navigation does not grant generic automation controls authority to edit them.\n\n## Context and routing design\n\n### Resolve context explicitly\n\nIntroduce a small UI context module that derives the current project, source, repository and available capabilities. Separate the project being viewed, the composer draft's destination and the filter used by global lists. Today these purposes share parts of the active-project state, which makes unrelated screens influence one another.\n\nAn owner project reference needs the computer identity and project ID. A linked shared project reference additionally needs the collaboration connection and its project ID, qualified by the computer holding that connection. Run and board-task references likewise retain their source. These are client routing identities, not new organization records or replacements for server authorization.\n\nThe context module should implement these rules:\n\n- A valid project route determines the page's project. Global filters cannot silently override it.\n- Explicit draft choices take precedence over remembered defaults. Defaults apply only to unset fields or an intentional reset.\n- An empty project has no runnable repository. It still has a useful add-repository or board-setup state.\n- Drafts and selected repositories are isolated by source and project. A repository removed from membership becomes an explicit unresolved destination; it must not silently redirect the draft elsewhere.\n- Reads, subscriptions and async selection responses are scoped and invalidated when context changes. A late response cannot overwrite a later choice.\n- The composer, side panel, repository tools and launch summary use the same resolved destination unless the side panel is explicitly pinned. A pin remains clearly labeled with its actual scope.\n- Unavailable or unauthorized sources stop resolution; they never fall back to a different computer or project for a mutation.\n\nKeep server-side identity, permission, membership and checkout validation authoritative. A project ID accepted by a UI component is not evidence of access.\n\n### Add project destinations without breaking links\n\nExtend the project route with a validated section and optional repository/tool selection. Build links centrally rather than concatenating them in every component. Persist repository/tool context needed to reopen a deep link; do not depend on whatever the composer last selected.\n\n| Existing entry | Proposed behavior |\n| --- | --- |\n| `#/project/:id` | Project Overview. |\n| New `#/project/:id/runs` | Project run history. |\n| New `#/project/:id/repositories` | Repository list and tools with explicit repository selection. |\n| `#/project/:id/workspace` | Compatibility alias for the project's Repositories destination. |\n| New `#/project/:id/automation/:kind` | Existing Scheduling purpose with an explicit project. |\n| New project Board and People routes | Resolve only against the owning server or an explicitly identified collaboration source. |\n| `#/run/:id` and `#/review/:id/:branch` | Keep working and derive project breadcrumbs from the result's recorded context. |\n| `#/team`, project and task variants | Keep connection identity and open the corresponding board/task view. Never reinterpret against an unrelated active project. |\n| `#/workspace` and `#/settings/workspace` | Open repository tools for a valid current destination; otherwise show an explicit repository/project chooser. |\n| Legacy SSH, team and remote-access settings links | Continue resolving through Connections. |\n\nRetain `/computer/:id/` prefixes, existing team query context and URL encoding. Malformed links produce a recoverable state. Unsupported project subroutes should not silently appear to be Overview. Browser back/forward must restore both the destination and its context.\n\n### Reuse existing screens\n\n| Area | Main implementation locations | Required change |\n| --- | --- | --- |\n| Routes and context | `ui/navigation.ts`, `ui/state.ts`, `ui/lib.ts`, new focused context module | Explicit view/draft/filter contexts, qualified references, aliases and consistent link building. |\n| Shell and sidebar | `ui/app.tsx`, `ui/rail.tsx`, `ui/connected-projects.tsx` | Project destinations, scope labels, breadcrumbs and discoverable global tools. |\n| Project and composer | `ui/project.tsx`, `ui/projects.tsx`, `ui/composer.tsx` | Focused overview, direct repository onboarding and one composer path. |\n| Repository tools | `ui/activity.tsx`, `ui/workbench.tsx`, side-panel scope in `ui/state.ts` | Explicit repository context, consistent labels and preserved worktree targets. |\n| Collaboration | `ui/collab-board.tsx`, `ui/collab-state.ts`, `ui/collab-settings.tsx`, `ui/shared.tsx`, `ui/team-connections.tsx` | Board versus People distinction, correct connection scope and role-appropriate views. |\n| Automation | `ui/scheduling.tsx`, `ui/automations.tsx`, `ui/project-teams.tsx` | Explicit project parameters and one configuration home for each schedule or mission. |\n| Connections and settings | `ui/connections.tsx`, `ui/ssh-projects.tsx`, `ui/team-server-wizard.tsx`, `ui/settings-sections.ts`, `ui/preferences.ts` | Computer-oriented language, shared-project setup and consistent search aliases. |\n| Server integration | `src/projects.ts`, `src/server.ts`, `src/collab/connections.ts`, existing access-policy modules | Reuse existing project operations and enforce launch membership for the new explicit UI flow. Add narrowly scoped projections only when required. |\n\nExtract reusable content from existing pages so an embedded project view does not render duplicate headers, pickers or event subscriptions. Hidden tabs should not fetch owner-only data or maintain unnecessary live feeds.\n\n## Implementation sequence\n\n### Phase 1 Establish the design contract\n\nCreate a route and label inventory covering owner, SSH, linked-server and shared-user views. Record the identity source and access requirements of each destination. Build lightweight screen sketches for the empty state, project overview, composer summary, multiple repositories and shared board. Check whether each location answers “Which project?”, “Which repository?” and “Which computer?” when those answers matter.\n\nDeliver the terminology map, route matrix and scenario checklist as the implementation contract. Capture baseline completion time and wrong turns for the usability scenarios below. This is a small design and investigation phase; it should expose any shared-project mapping gaps before route work begins.\n\n**Exit criterion:** every existing destination has a proposed home or compatibility entry, and the first-run flow requires no separate workspace or team setup.\n\n### Phase 2 Implement explicit context and route compatibility\n\nAdd the context module and project sections. Separate global list filters from composer destination state. Add project/source-qualified draft and repository preferences with a versioned migration from existing browser state. Preserve the existing per-computer namespaces.\n\nWire route resolution, late-response guards, deep links and back/forward behavior before restructuring pages. Use the existing project APIs for explicit membership changes. The new project-bound UI launch path should request strict membership validation; retain deliberate compatibility behavior for existing automation and API clients.\n\n**Exit criterion:** routing and selection tests show that a project, repository or connection switch cannot redirect a launch, leak a draft or overwrite a more recent selection. Old bookmarks continue to resolve.\n\n### Phase 3 Deliver the owner project experience\n\nImplement the focused overview, Runs and Repositories destinations. Reuse existing result rows and tools. Add Open repository onboarding and simplify the single-repository composer. Update sidebar entry points, breadcrumbs, repository changes and the compact execution summary together.\n\nKeep cross-project Inbox available. Move global Scheduling only when its project destination and command-search entry are working. Preserve keyboard shortcuts and recent-run navigation.\n\n**Exit criterion:** a new local user can open a repository, launch a fake-agent run and review its result through one project; an existing user can recover drafts, archives and repository tools without losing context.\n\nThis is the first useful release slice, dependent on Phases 1 and 2. It can ship before the collaboration restructuring.\n\n### Phase 4 Integrate collaboration with correct source identity\n\nSeparate Board from People and embed authorized project views. Reuse existing account administration, grants and board-task operations. Add explicit collaboration-source handling to navigation, requests and subscriptions. Preserve the connection-scoped board when no reliable local-project relationship exists.\n\nAdapt shared dashboards using their existing safe data projections; do not embed the owner project page with controls merely hidden. Test empty shared projects that have repository keys but no server-side local clone. Expose existing terminal or host-execution grants only to their authorized recipients.\n\n**Exit criterion:** two servers with identical project or task IDs remain distinct, permission changes remove access promptly, and selecting another board cannot redirect an existing task execution.\n\n### Phase 5 Complete project automation and discoverability\n\nScope Scheduling, backlogs and missions explicitly to the project and repository. Surface actionable outcomes in Overview and Inbox using the existing evidence and approval rules. Align settings search, command search, connection setup, errors and help text with the terminology contract.\n\nApply predictable visibility: hide unused specialized controls initially, retain access through More or search, and surface already-configured features even when an old optional-navigation preference is off. Authorization decides availability; a preference decides presentation only.\n\n**Exit criterion:** users can find and manage existing schedules, missions, shared work and connection settings from their expected project or app location. All first-run instructions match the implemented navigation.\n\n### Phase 6 Validate and roll out\n\nRun the relevant behavioral and browser suites, then the full required checks for the final code change. Conduct the usability scenarios and inspect narrow screens, light/dark themes and keyboard/screen-reader behavior. Refresh documentation and screenshots after the routes stabilize.\n\nUse a temporary navigation rollout switch if the implementation needs staged exposure. Both paths should use compatible storage so reverting navigation does not require restoring user data. Remove the temporary branch after validation rather than maintaining two permanent information architectures.\n\n**Exit criterion:** the scenario matrix passes, no unexplained source or destination switches occur, and pilot users can find previously available capabilities.\n\nPhases 2 through 5 are substantial UI and integration work. Treat collaboration as the highest-uncertainty portion. Estimate delivery after Phase 1 resolves source mapping and draft migration; a simple label-change estimate would understate the scope.\n\n## Migration and compatibility\n\nPreserve project IDs, repository membership, run logs, board tasks, shared repository keys, account roles, automation records and managed mission history. Introduce browser preference versions and additive route aliases rather than rewriting durable execution history.\n\nFor legacy browser drafts, migrate once into a valid source/project destination when unambiguous. Otherwise retain them as a recoverable unassigned draft and ask for a destination when opened. Keep the original recovery data until migration is verified. A failed migration must not create a project or start work.\n\nKeep the existing semantics for historical runs whose projects were deleted or whose membership was inferred. Show them under Unassigned and retain review/archive access allowed by current rules. New explicit selection behavior does not justify silently regrouping old runs or sharing previously private results.\n\nDo not merge projects by name, repository basename, Git remote URL or matching IDs across computers. Those can describe related work without establishing common identity or access. Repository membership changes may affect shared access, so expose their real consequences in the project settings action.\n\nThe initial scope excludes a global project database, automatic synchronization of clones, a merged run/board-task data model, cross-server Inbox federation, new permission levels and changes to execution engines. Each would need its own design and validation.\n\n## Validation plan\n\n| Scenario | Required result |\n| --- | --- |\n| New local installation, one repository | Open repository leads to a named project and ready composer with no team/workspace configuration. |\n| Returning user with an unsent draft | Navigation and reload preserve the draft, its attachments and destination. |\n| One repository in multiple projects | An explicit project selection wins; ambiguous onboarding asks the user to choose. |\n| Multiple repositories with delayed API responses | The last valid selection controls checks, defaults, tools and launch; old responses cannot win. |\n| Empty project or removed repository | Helpful setup/recovery state; no launch using a stale repository. |\n| Same project name and path on two computers | Requests, drafts, terminals and file recovery remain isolated. |\n| Disconnected SSH computer | Reconnect the intended computer; never substitute local execution. |\n| Local project with a delegated worker | The project computer and actual execution worker are both clear. |\n| Multiple collaboration servers with colliding IDs | Correct board, task, membership and inbox scope for each connection. |\n| Viewer, reviewer, contributor and maintainer | Only authorized projections and actions are available; server enforcement still rejects forged requests. |\n| Revoked or expired shared access | Stale views/subscriptions cease exposing data and present a recoverable access state. |\n| Shared board without a server clone | Board/review workflows work; local clone mapping appears when needed for execution. |\n| Existing schedules and managed missions | Same scope, budgets, controls and result links; mission readiness/approval rules remain intact. |\n| Old bookmarks, encoded branches and browser history | Correct destination and source context, or a clear recoverable invalid-link state. |\n| Pinned side panel or selected run worktree | Actual panel/tool target stays visible and cannot be mistaken for the composer repository. |\n| Deleted project and archived runs | Existing history remains discoverable with correct access and ownership semantics. |\n\nExtend behavioral coverage around `test/projects.test.ts`, `test/repo-selection-ui.test.ts`, `test/workspace-routing.test.ts`, `test/team-connections.test.ts`, `test/team-connection-ui.test.ts` and `test/shared-access.test.ts`. Add focused route/context tests where existing tests do not exercise the new behavior. Retain checkout-namespace and workspace-activity regressions when touching tool targeting.\n\nExercise browser flows in the existing simplification, connections, SSH projects, shared projects, team, project missions, workspace, settings and accessibility suites. Check visible results and request destinations rather than snapshotting implementation strings. Run `npm run typecheck`, the relevant targeted suites and documentation checks as changes land; run the full repository-required test set before release.\n\nFor usability, pilot with a small mix of new solo users, remote users and collaborators. Ask them to start a run, switch repository, identify the execution computer, find a result, open a shared board and locate access settings. Record completion without assistance, wrong turns, time to start and incorrect destination assumptions. Compare the same tasks against the current UI.\n\nProposed pilot targets are at least four of five participants completing the primary start-and-review flow without assistance, and no incorrect computer or repository assumption at the launch checkpoint. These are initial decision thresholds, not statistical proof. Any wrong-target launch or lost draft blocks release regardless of the aggregate score.\n\n## Principal risks and controls\n\n| Risk | Control |\n| --- | --- |\n| Project becomes another mandatory setup burden | Open repository suggests and creates the project through a short flow. |\n| Simplification hides execution location | Persistent computer context and a concrete pre-launch summary. |\n| Renaming People leaves the board hard to find | Dedicated Board destination, contextual links and legacy search terms. |\n| Shared and local projects look falsely unified | Qualified source identities and explicit relationships; retain connection-scoped access where needed. |\n| Useful tools disappear into menus | Stable project destinations, searchable aliases and task-based discoverability tests. |\n| Context refactoring loses drafts or misapplies defaults | Versioned migration, preserved recovery data and async-switch regression tests. |\n| Shared views expose owner data | Reuse shared projections and enforce server permissions; do not rely on hidden controls. |\n| A large rewrite delays the basic improvement | Ship the owner project flow after Phase 3, then integrate collaboration and automation. |\n\nThe recommended first implementation is the explicit context and owner project flow: one clear project destination, one composer path, predictable repository selection and visible computer scope. That creates the foundation the remaining simplification depends on.\n","title":"Project navigation review and implementation plan"},{"group":"Engineering records","id":"dynamic-teams-evaluation","source":"docs/dynamic-teams-evaluation.md","body":"# Dynamic teams: implementation evidence and rollout decision\n\n> Engineering record. Status, measurements and decisions below describe the recorded implementation or review. For current usage, start with the [documentation library](README.md).\n\nRecorded 17 October 2026 for the P0–P8 implementation on top of `main` at `8cb364143716a3feda3edd12e7fa0a310c92ecf5`. The implementation is described in the [plan](dynamic-teams-implementation-plan.md) and [Auto team guide](auto-team.md). The [machine-readable evidence](evaluations/dynamic-teams-2026-10-17.json) preserves the trial reports, limitations, assessment and explicit rollout decision.\n\n**Decision: keep Auto experimental and opt-in.** All implementation packages are delivered. The evidence supports exercising the feature with bounded budgets; it does not support replacing Supervisor by default. Earlier unsuccessful trials are retained. A successful final trial does not erase them.\n\n**Provenance of this evidence.** The JSON artifact was produced from an uncommitted working tree (`implementation.state`) by an earlier version of the runner than the committed one: its reports carry `pairId: \"pair-1\"` and, for pair A, `caseId: sum-numbers-v1`, where the committed runner writes a batch-scoped pair ID and the v2 case, and pair C predates the worker-turn attribution fix. The source runs and journals are not committed. The real numbers are therefore historical measurements of the development tree and cannot be regenerated from this repository; a fresh `--fake` run reproduces the 5/2 and 4/1 call counts, and only a new `--real --confirm` batch can produce reproducible real numbers. Per-trial model identity was not recorded (the runner passes provider defaults), and the matched Supervisor runs were pinned to two subtasks, so their 25% coordination share is by construction rather than observed.\n\n## Verification\n\n- Full automated suite: 397 tests passed at the time of the trials (416 after the post-delivery review below, 420 after [review 1](dynamic-teams-implementation-review-1.md)), including recovery, storage faults, mailbox ordering, immediate replies, identity isolation, memory trust, live capabilities, legacy compatibility and rollout controls.\n- Browser suite: 17 tests passed, including Auto approval, blockers, inspection and an in-flight goal revision that stops the old team and requires the configured approval for its child.\n- After final trust/diagnostic adjustments: 11 focused composition and rollout-engine tests passed.\n- After correcting worker-turn attribution: 8 evaluation tests passed. Worker answer and repair turns count even without task IDs; coordinator/reviewer turns do not.\n- Typecheck, UI build and `git diff --check` passed. Validation ran on Node `v22.14.0`; the existing Node 20/22 CI matrix is retained, but this report does not claim a separate local Node 20 run.\n\nThe asynchronous scheduling regression runs a fast dependency, its successor and an unrelated slow worker. It asserts that the successor starts before the slow worker ends. A separate saved-wave compatibility case asserts the old barrier, and restart tests preserve recorded completion ordering. These fixtures demonstrate scheduling behavior, not a real-provider speedup.\n\n## Real task comparisons\n\nEach pair used the same generated repository, base commit, task and verification command: implement `sumNumbers(values)` with finite-number filtering, non-array rejection, empty-input handling, input preservation and `node:test` coverage. Runs used isolated storage and repositories, official signed-in CLIs and a maximum of 12 invocations. The generated task forbids fetching packages, accessing external services or publishing. Plan approval was explicitly disabled by this unattended evaluation harness; interactive approval is covered by the browser and engine tests.\n\nThe CLIs were Claude Code `2.1.302` and Codex `0.162.1`. Pair A used Claude as coordinator; pairs B/C used Codex. These are development trials of one small task, with code fixes between them, not independent samples of the final implementation or a representative benchmark. They were not sequential: the report timestamps show pair B starting before pair A finished, and between 12:44:56 and 12:46:06 UTC three real runs (A supervisor, B supervisor, C auto) ran at the same time on one machine, so the elapsed times in the table were measured under shared CPU, network and rate-limit contention.\n\n| Pair | Pattern | Result | Calls / coordination | Worker turns | Elapsed | Selected memory | Journal bytes / load |\n|---|---|---|---:|---:|---:|---:|---:|\n| A | auto-team | stopped; unverified | 3 / 2 (67%) | 1 | 300.48 s | 0 | 38,298 / 2.95 ms |\n| A | supervisor | done; verified | 4 / 1 (25%) | 2 | 216.60 s | 0 | 23,817 / 3.53 ms |\n| B | auto-team | failed; unverified | 1 / 1 (100%) | 0 | 5.55 s | 0 | 2,538 / 0.34 ms |\n| B | supervisor | done; verified | 4 / 1 (25%) | 2 | 233.43 s | 0 | 20,345 / 3.37 ms |\n| C | auto-team | done; verified | 5 / 3 (60%) | 1 | 294.59 s | 1 | 54,147 / 0.87 ms |\n| C | supervisor | done; verified | 4 / 1 (25%) | 2 | 259.62 s | 0 | 20,789 / 1.46 ms |\n\nPair A exhausted its five-minute limit after a plan validation retry and implementation. Its initial IDs did not satisfy the lowercase identifier policy. The repaired implementation did not reach independent review before the deadline, so the runtime correctly reported an unverified stopped result. Prompts and strict schemas now communicate the identifier rules directly.\n\nPair B exposed a real Codex schema incompatibility before worker dispatch: strict object schemas must include every property in `required`, including `subplans`. Wire schemas now satisfy that restriction recursively, with an explicit regression test. A repository-path canonicalization defect also prevented the synthetic memory seed from being selected; it was fixed and tested before pair C.\n\nPair C exercised the corrected strict schemas and selected one seeded project memory. Its first plan unnecessarily assigned a task to the independent reviewer. The runtime rejected it, and a planning retry assigned the verification task to the coordinator; the runtime still performed its own independent final review. The final prompt and rejection diagnostic now tell planners to omit that redundant final-review task. Pair C therefore includes this observed extra coordination and predates the final wording improvement; no reduced overhead is claimed for that change.\n\nThe verified Auto result covered all five structured requirements, passed checks and independent review, and passed the fail-to-pass proof. Reported elapsed time includes planning and verification. Coordinator fractions count actual CLI launches, including planning repair, rather than only logical steps. Provider-reported tokens/costs can be partial; API-equivalent costs are not subscription charges and are not a complete cross-provider price comparison.\n\nNone of these real Auto runs needed peer information. They therefore supply no useful estimate of real peer-question frequency or asynchronous latency gains. The final successful Auto run used one worker; its 60% coordinator fraction is not a multi-worker overhead measurement. The 20-Auto-run, 100-worker-turn and 20-matched-pair default-rollout targets have not been met.\n\n## Live bridge evidence\n\nBoth adapters were exercised against the scoped local MCP bridge using a real CLI invocation. The server returned a unique test response only after an authenticated `read_inbox` call; the model's final response matched it.\n\n| Provider | Transport | Elapsed | Accepted callbacks | Result |\n|---|---|---:|---:|---|\n| Claude Code 2.1.302, Haiku | stream-json with MCP configuration | 8.630 s | 1 | Correct member and response |\n| Codex 0.162.1 | app-server, read-only | 13.623 s | 1 | Correct member and response |\n\nClaude first supplied an unsupported `input` field; schema validation rejected it and the model corrected the call. Codex initially showed that per-tool approval configuration was required even with a noninteractive approval policy. Its adapter now explicitly permits only the bounded collaboration/memory tool names; source-write and other tool permissions are unchanged. These successful smoke checks prove transport and identity plumbing, not complete real-provider coverage of every collaboration operation. Fake adapter and engine tests cover authentication, scope, revocation, durable writes, steering fallback and replay. The Claude summary was reconstructed from its retained successful tool output after the later Codex smoke overwrote the temporary combined report; that capture provenance is included in the JSON artifact.\n\nUnsupported or unproven capabilities remain visible in the capability inventory. Structured outcomes remain the fallback. Vendor-native nested-agent usage cannot be counted when the CLI does not expose it; the implementation does not invent that usage.\n\n## Deterministic workflow and memory evidence\n\nThe labeled fake comparison completed and verified both patterns. Auto used five calls, two coordinator calls, one blocking peer question and two worker turns. The question was answered after 796 ms; the observed post-answer delay was 226 ms. That delay is the event scheduler's own dispatch latency after an answer arrives (every trial ran event-driven, so there was no wave barrier to avoid); it is not a measured speedup. Supervisor used four calls and one coordinator call.\n\nAuto selected one synthetic accepted project memory about dependency-free `node:test`; Supervisor selected none through the new memory layer. This tests retrieval and attribution, not whether a learned memory helps a human. The real pair C selected the same kind of seed. Human usefulness/staleness feedback was zero in these trials. The application supports revision-specific useful/irrelevant/stale/incorrect feedback, exclusion of stale or incorrect revisions, and bounded relevance adjustments.\n\nSeparate memory tests verify later-run reuse of an eligible lesson, member-private isolation, exact source revisions, locked-down and inherited-source promotion restrictions, branch applicability, deletion and session-independent reconstruction. Untrusted material remains attributable and cannot become a future project instruction through an automatic summary or follow-up.\n\nThe fake Auto journal held 17,594 bytes / 58 records and loaded in 19.11 ms; Supervisor held 5,030 bytes / 13 records and loaded in 6.44 ms. Real journal measurements are in the table and JSON artifact. These are single local **journal-load** timings, not full process-restart benchmarks. Restart behavior is independently tested. The measurements provide no reason to add a database, leases or an outbox; fsynced logical envelopes and deterministic replay remain the storage decision.\n\n## Reproduction and future rollout\n\n### Implementation review after initial delivery\n\nA second review covered the complete implementation and fixed the following issues before commit:\n\n- Ready-work selection now filters unavailable and exhausted tasks before packing non-overlapping work. Live and replay scheduler waits respond to cancellation even without a deadline. Retried event-driven tasks merge the captured input commit, so a later peer commit cannot silently change their inputs.\n- Coordinator revisions validate the combined contract set against the existing bounds. A smaller explicit invocation ceiling constrains planning and review reservations; refused retries do not create false launch records. Rejected outcomes update both provider and member failure counts.\n- Provider-addressed follow-ups cannot resume a team's private member session. Direct replies retain their own subsequent conversation and can use a one-call budget; new teams still require at least four calls.\n- Live memory reads honor task paths, search text and entry limits, keeping returned entries and recorded provenance consistent. Whole-repository `*` assignments can retrieve applicable path-scoped memory. Valid namespaced operation IDs remain within storage limits.\n- Malformed JSON-RPC envelopes no longer crash the MCP client.\n- Interrupting the evaluation runner stops the whole batch, including a signal during run preparation. Separate batches use unique pair IDs. Beta eligibility requires the verified Auto result to belong to the matched comparison, and multi-worker measurements count workers that actually executed.\n- The capability panel clears stale errors when reopened after a successful retry.\n- The browser test waits for the active run's waiting status after its question appears; those are separate scheduler snapshots, which exposed a timing race on Linux CI.\n\nEvery fix has regression coverage. Final post-review validation is recorded below after the complete test and browser runs. The real measurements above remain the original immutable development-trial evidence; this review did not relabel or overwrite those trials.\n\n- `npm test`: 416 passed, zero failures or skips.\n- `npm run test:e2e`: 17 passed, including direct-follow-up and new-team budget checks.\n- `npm run typecheck` and `npm run build`: passed.\n- Final contract/model regressions: 10 passed after the last contract-bound correction.\n- `git diff --cached --check`: passed.\n\n### Commands\n\n```sh\nnpm test\nnpm run test:e2e\nnpm run typecheck\nnpm run build\nnpm run evaluate:teams -- --fake --out /tmp/codeflow-team-fixture\nnpm run evaluate:teams -- --real --confirm --runs 1 --providers codex,claude --max-steps 12 --max-minutes 8 --out /tmp/codeflow-team-real\n```\n\nUse a fresh output directory for each invocation. `--real` consumes signed-in CLI quota; ordinary tests use fakes. The runner saves immutable measurement reports and source runs under its isolated directory. Optional `--register-home DIR` copies measurement reports into another installation without copying transcripts or changing rollout. Generated fixtures and private CLI transcripts are not committed with this evidence.\n\nThe recorded decision selects all three real development comparisons, including the failures, and stays experimental. It was written into an isolated evaluation store and copied into the evidence artifact; the user's normal app configuration was not changed. The app starts at experimental in the absence of a separate explicit decision. Beta/default changes require a written rationale and qualifying real reports through the rollout API/UI; fake runs cannot qualify them, and since review 1 eligibility is judged over every real report in the store, not only the cited ones. The stage is a recorded label shown in Insights and used as a composer default; the only switch that changes whether Auto teams can start is `CODEFLOW_AUTO_TEAM_ENABLED`. More representative tasks, verified multi-worker runs, meaningful memory feedback and lower observed coordination overhead are needed before promotion.\n\n[Documentation library](README.md) · [Development guide](architecture.md)\n","title":"Dynamic teams: implementation evidence and rollout decision"},{"group":"Engineering records","id":"dynamic-teams-implementation-plan","source":"docs/dynamic-teams-implementation-plan.md","body":"# Dynamic teams, workflows, messaging, and memory\n\n> Engineering plan and record. Revision 4 below records the implementation scope and remaining extensions; the revision 3 material that follows records the delivered baseline. For current usage, start with the [documentation library](README.md).\n\nStatus: **revision 4 core implemented**, updated 21 October 2026. Dynamic Auto is the fresh-session default, Solo is directly accessible, fixed workflows and versioned templates live in Advanced, and all new runs resolve a shared permission policy at every agent launch. The revision 3 scheduler and live-tool bridge remain the execution foundation. See the [user guide](auto-team.md) and [measured evidence and rollout decision](dynamic-teams-evaluation.md).\n\n## Revision 4 — dynamic Auto, Solo, and reusable templates\n\nThis revision supersedes the earlier opt-in product/default decision and the proposal to make template selection the main Auto behavior. It extends the existing dynamic engine; it does not restart P0–P8. Historical measurements and implementation claims below remain unchanged.\n\n### Delivered scope and explicit limits\n\n- Added closed, versioned task-graph/fixed-workflow template definitions, an atomic server library with immutable history, project/personal scope, migration of browser presets, import/export, JSON editing with execution preview, and save-from-run. Auto may compose enabled task graphs; manually selected definitions are mandatory and pinned across recovery.\n- Moved fixed workflows into Advanced without replacing their executors or weakening test-first, race, council, loop, or review behavior. Typed composition of those fixed executors remains a future extension; only validated task graphs are currently Auto-compatible.\n- Added a shared per-launch policy resolver, recorded effective permissions, narrower turn access, provider eligibility checks, explicit user policy revisions, and inherited mandatory restrictions for child runs. All engine-created runs use the policy, including background and collaborative starts; historical saved runs without a policy retain their existing behavior.\n- Controlled launches currently support Claude/Codex. Unsupported provider capabilities fail closed. This is not an identical cross-provider sandbox: filesystem read isolation, general browser/MCP allowlists, and a unified sandbox for setup/check/preview processes remain outside this implementation. Real-provider enforcement acceptance is still required before claiming equivalent isolation; deterministic adapter tests only verify configuration and orchestration.\n- Auto retains its existing independent verification model. Coordinator-only investigations, typed artifact/control-flow composition, automatic template-upgrade proposals, and a visual graph designer remain future extensions. The initial designer uses validated JSON and a readable task preview.\n- The application default is decoupled from historical quality-evaluation results. Existing explicit mode choices and the Auto disable switch remain intact; no evaluation evidence is synthesized by this change.\n\nThe D0–D6 entries below describe the full design, including these remaining extensions. They should not be read as a claim that every listed extension or release acceptance probe has been completed.\n\nInitial implementation validation on 21 October 2026: typecheck and production build passed; 29 focused policy/template/adapter/composer checks and 11 browser scenarios passed. The full regression run reported 1,208 passes, eight skips, and two compatibility failures; both failures were corrected and passed in focused reruns. The full suite was not rerun after those fixes. Browser and adapter scenarios use deterministic CLIs, not live-provider sandbox enforcement probes.\n\n### Product decisions\n\n| Surface | Intended behavior |\n|---|---|\n| **Auto — default** | The coordinator investigates the goal, builds an appropriate plan, delegates useful work, and adapts to results. It can start without a template, reuse one, or adapt/combine compatible templates. |\n| **Solo — directly accessible** | One execution agent handles the task; delegation is disabled. Keep it beside Auto in the composer, without opening Advanced. |\n| **Advanced — templates and configuration** | Users choose existing patterns as reusable templates, design their own, edit versions, and configure roles, dependencies, provider/model preferences, and required checks. |\n| **Reasoning and resource controls** | Model effort, concurrency, time, invocation, and spending limits remain separate from execution mode. Do not introduce another Ultra mode in this revision. |\n\nAuto is allowed to use the smallest useful execution plan. An investigation may stay with the coordinator; a small code change may need one implementation worker and the verification required by the run. Do not create multiple implementation workers merely because Auto is selected. Required independent review remains independent; choosing Solo must never silently start a reviewer agent. If a selected policy requires another agent, explain that incompatibility before execution.\n\nCode Flow owns scheduling, workspaces, communication, recovery, permissions, budgets, and verification across providers. The coordinator proposes work and revisions through validated records. Use the existing declarative plan and event scheduler; arbitrary generated orchestration scripts and untracked provider-native teams are outside this revision.\n\n### Current foundations and gaps\n\n| Existing foundation | Revision 4 work |\n|---|---|\n| `src/patterns/auto-team.ts`, `auto-team-model.ts`, `src/teams.ts`, and `src/team-scheduler.ts` already support generated tasks, dependencies, bounded revisions, messaging, and recovery. | Extend this execution path and its contracts. Keep generated tasks usable without any template lookup or match. |\n| `src/patterns/team-strategies.ts` expands three declarative subplans. | Generalize the reusable-strategy contract to versioned templates with explicit inputs, outputs, and preserved guarantees. |\n| Fixed patterns have established execution and verification behavior. | Expose them in Advanced, then share guarded execution primitives so Auto can reuse them without losing those guarantees. |\n| `ui/composer-tools.ts` and `ui/onboarding.tsx` already save personal/project task templates and presets in browser storage. | Add a server-accessible, versioned team-template library and migrate existing entries. Current entries are mainly prompts/settings, not executable team definitions. |\n| `src/engine.ts` passes `params.trust ?? run.trust` to adapters; adapters map those labels differently. | Resolve and validate effective permissions centrally at every launch, including replacements and resumed turns. |\n| `ui/state.ts` defaults to Pipeline; `ui/composer.tsx` remembers a team workflow; evaluation metadata can change the default. | Separate execution-mode preference from template selection and evaluation evidence. Make Auto the application default while preserving explicit user choices. |\n\n### D0 — define run policy and execution contracts\n\n**Outcome:** every execution path derives authority from the run, independently of the selected provider or template.\n\n- Introduce a versioned run-policy record and a common permission resolver at the engine launch boundary. Cover filesystem reads/writes, terminal actions, network access, browser access, MCP tools, approval behavior, execution environment, and delegation where supported. These are separate capabilities: terminal network permission does not automatically grant a browser or connector.\n- Derive effective permissions from the authorized run policy, current project/host restrictions, and any narrower turn access. A coordinator's read-only planning turn must not accidentally make its implementation workers read-only. Conversely, templates, model output, messages, role settings, and provider changes cannot grant additional authority.\n- Treat tool availability separately from permission. Inherit the run's allowed tools where the provider and execution environment support them; route only to eligible providers. Record unsupported or unavailable capabilities explicitly. Do not pass credentials or provider-specific session handles between agents/providers.\n- Have each adapter report the enforcement it can provide. Use existing supported isolation when needed; otherwise reject the affected launch with a concrete explanation. Never interpret matching permission labels as proof of equivalent enforcement. Worktrees alone do not restrict reads, and prompt instructions do not constitute a sandbox.\n- Apply the resolver to planning, workers, review, integration/repair, retries, replacement members, manual templates, and Solo. Audit setup, checks, previews, remote execution, and container paths too: commands outside the native agent sandbox need their own policy enforcement or an explicit run-authorized execution boundary.\n- Persist the policy revision and effective capability summary with each launch. On recovery, retain the authorized policy and revalidate against current restrictions; a revoked permission or weaker replacement provider blocks new affected work. Any explicit user policy change creates a new revision. Reconcile an active process before continuing under changed restrictions.\n- Keep Code Flow as the delegation authority. Disable unmanaged provider-native delegation for controlled runs, including Solo; providers unable to enforce a required delegation restriction are ineligible for that mode.\n\n**Primary code:** `src/engine.ts`, `src/adapters/types.ts`, provider adapters, `src/provider-eligibility.ts`, team-tool grants, execution/container/remote-worker boundaries, and shared policy types.\n\n**Exit evidence:** mixed-provider launches receive the intended effective policy; read-only turns remain narrower; retries and replacements cannot increase access; missing enforcement is surfaced rather than silently downgraded.\n\n### D1 — strengthen dynamic Auto on the existing runtime\n\n**Outcome:** Auto succeeds without a template library and adapts its plan within the run's constraints.\n\n- Keep initial planning and bounded revisions as the core behavior. The coordinator chooses eligible members, tasks, ownership, dependencies, and sequencing from the current goal and observed evidence. Provider/model pins and runtime verification requirements remain authoritative.\n- Preserve task/member/attempt identities and journal accepted revisions before dispatch. Start ready work through the existing scheduler; ask the coordinator for decisions when evidence requires judgment, rather than on every scheduler event.\n- Support the smallest useful plan, including coordinator-only investigations where allowed. Keep independent verification for code changes and reserve its budget before admitting additional work. Make task-kind changes explicit so an investigation cannot become an unauthorized implementation task.\n- Reassign unfinished work through a checkpoint/handover, without transferring a provider's native session to another provider. Validate file ownership, serialize conflicting writers, and bind artifact dependencies to recorded commits.\n- Preserve completed work when replanning. Invalidate only evidence affected by changed inputs/contracts and require fresh checks before reporting success. Bound expansion, repair, retries, and coordination overhead using the existing run limits.\n- Expose the accepted plan, decision rationale, active workers, revisions, blockers, and remaining budget in the run view. Permission changes are runtime/user decisions, never coordinator decisions.\n\n**Primary code:** `src/patterns/auto-team.ts`, `src/patterns/auto-team-model.ts`, `src/teams.ts`, `src/team-scheduler.ts`, `src/workflow-control.ts`, and `ui/team.tsx`.\n\n**Exit evidence:** an empty template catalog does not prevent a small task or a parallel feature task from completing; a worker failure causes a bounded revision and recovery without replaying completed work unnecessarily.\n\n### D2 — versioned templates using the same execution contracts\n\n**Outcome:** users can save reusable team behavior, and the server can validate it before execution.\n\n- Define a shared schema for identity/version, scope, description/applicability, input parameters, role slots, preferred providers/models, tasks/dependencies, ownership, input/output artifacts, required checks, immutable guarantees, and permitted adaptations. Keep the existing starter-prompt catalog distinct from executable team templates.\n- Add a durable server-side store/API for built-in, personal, and project templates, with existing account/project access rules, version-conflict checks, and bounded import/export. Built-ins are immutable; users create their own versions or copies.\n- Normalize a generated plan and an instantiated template into the same validated execution records. Use typed, bounded control primitives for behavior that a simple task DAG cannot express, such as candidate selection and repair loops. Avoid a separate untracked execution engine.\n- Pin the template version, definition snapshot, resolved inputs, expansion, and adaptation patch in each run. Editing or removing a library entry must not change an existing or resumed run.\n- Templates can declare required capabilities and suggested resource limits; they cannot grant permissions, weaken required verification, or enlarge the authorized budget. Exclude credentials, auth-volume bindings, session IDs, old results, private transcripts, and machine-specific workspace paths from reusable definitions.\n- Migrate existing browser libraries idempotently and retain a recoverable copy. Preserve names, task inputs, criteria, role preferences, and supported workflow options. Stop applying saved trust/tool-approval settings or disabling current isolation. Translate unsupported settings into visible migration notes rather than pretending they are enforced.\n- Default user/imported templates to manual use until enabled for Auto. Supply the coordinator only the enabled, accessible, compatible catalog; a template's text is source material, not authority.\n\n**Primary code:** new shared template schema/store/API modules; `src/teams.ts`, `src/server.ts`, `src/shared-access.ts`, `ui/composer-tools.ts`, and `ui/onboarding.tsx`.\n\n**Exit evidence:** a template selected on another client resolves to the same immutable definition; importing or applying a template cannot change the run's permissions; existing browser libraries migrate without duplicate entries or lost originals.\n\n### D3 — convert existing patterns into reusable templates\n\n**Outcome:** familiar workflows remain available in Advanced with their original behavior.\n\nInitially register built-in template definitions that invoke the existing executors. Preserve legacy pattern IDs and replay identities for saved runs, automations, API clients, and extensions. Extract shared execution primitives incrementally; do not reimplement a pattern as a loose sequence of prompts.\n\n| Built-in template | Guarantees that must survive reuse |\n|---|---|\n| Plan / Build / Review | Plan checkpoint, implementation, required checks, independent review, and bounded fixes. |\n| Supervisor / Workers | Decomposition contracts, separate writer workspaces, integration, conflict handling, and final verification. |\n| Test first | Failing baseline evidence, protected tests, implementation, and passing verification. |\n| Iterative loop | Bounded iteration, progress/regression checks, and the existing rollback behavior. |\n| Candidate race | Isolated candidates, anonymized evaluation, deterministic recorded selection inputs, and required checks. |\n| Debate | Recorded proposals/decision, optional implementation, and verification appropriate to the selected behavior. |\n| Review council | Read-only source review, independent findings/reproductions, and evidence-preserving synthesis. |\n\nFor Auto composition, each reusable executor needs explicit input/output commits or artifacts, unique operation namespaces, and a scoped result. It shares the parent run's permissions, budgets, cancellation, and history, and cannot overwrite the parent's final verdict or reset counters. Prevent recursive template cycles and bound expansion. Mark Auto compatibility only after these guarantees are tested; Advanced availability alone does not establish composability.\n\n**Primary code:** `src/patterns/index.ts`, existing pattern modules, `src/patterns/team-strategies.ts`, and engine execution-context helpers.\n\n**Exit evidence:** parity fixtures pass for every built-in pattern; each template advertised for Auto composition preserves its guarantees under the parent run's limits and recovery behavior.\n\n### D4 — optional reuse, adaptation, and saving from Auto\n\n**Outcome:** Auto can build fresh, reuse, or combine compatible templates without being required to choose one.\n\n- Give the coordinator a compact eligible catalog with applicability, inputs/outputs, required capabilities, estimated overhead where known, and adaptation limits. Preserve direct task generation as an equally valid path.\n- Accept a versioned template reference, inputs, role assignments, and bounded patch. Resolve and expand it on the server, then validate it alongside generated tasks against the same requirements, permissions, ownership, verification rules, and budgets.\n- Show which work was generated, reused, or adapted and why. Validate composition boundaries explicitly; reject cycles, incompatible artifacts, invalid role assignments, and attempts to remove required checks. An unavailable optional template can lead to a fresh valid plan; an explicitly selected mandatory template produces a visible blocker.\n- Distinguish an in-run adaptation from a library upgrade. Adapting the current plan does not mutate its source template. An agent may propose an improved version with a comparison and supporting evidence; a user saves/publishes it explicitly. Revalidate the new version before enabling it for Auto.\n- Add “Save as template” for a completed run or selected reusable portion. Preview parameterized inputs, roles, steps, and guarantees before saving. Capture the reusable method, not a transcript, one-off findings, secret values, or a promise that future runs will pass.\n\n**Primary code:** coordinator schemas/prompts, template resolution/validation, `src/patterns/team-strategies.ts`, `ui/run.tsx`, `ui/run-actions.tsx`, and template-library UI.\n\n**Exit evidence:** the same task can use a fresh plan, a template, or an adapted template through the same runtime; saved template versions stay unchanged until explicitly published.\n\n### D5 — composer, Advanced, and default migration\n\n**Outcome:** the primary choice is Auto or Solo; reusable patterns and their design controls live in Advanced.\n\n- Replace the main pattern selector with directly accessible Auto and Solo controls. Put template selection and the template designer in Advanced. Keep the active template name visible in the composer summary when one is selected, so a constrained template run is not mistaken for unrestricted dynamic planning.\n- The Advanced designer supports parameters, roles/provider preferences, task dependencies, artifact contracts, required checks, and supported bounded control primitives. Include validation errors and an expanded-plan preview; support editing, version comparison, copying, and import/export using the same server schema.\n- Keep reasoning effort and resource controls separate. Validate each selected provider's supported settings; do not translate an effort label into additional tool authority or assume provider labels have identical meaning.\n- Default fresh/unconfigured sessions to Auto, and make the Auto control explicitly return to dynamic planning without a mandatory template. Preserve existing explicit Solo/project/workflow choices; map saved fixed-pattern choices to their Advanced template. When old storage cannot distinguish an implicit default from an explicit choice, preserve it and make Auto easy to select.\n- Decouple the application default from the historical evaluation stage. Retain measured evidence, diagnostics, and the new-run disable switch; changing the product default must not fabricate a successful quality evaluation. If Auto is disabled or cannot launch under the current policy, explain the reason and offer Solo or a compatible template without silently changing modes.\n- Preserve configured plan approvals, unattended authorization, external-source restrictions, and legacy automation/API behavior. A default change does not authorize spending, tool access, or an extra approval bypass.\n\n**Primary code:** `ui/composer.tsx`, `ui/state.ts`, `ui/onboarding.tsx`, project defaults, run views, `src/team-evaluation.ts`, and API/automation compatibility paths. Update usage documentation only when the behavior ships.\n\n**Exit evidence:** a new user starts in Auto, switches to Solo with one action, and can design/run a template in Advanced; existing saved choices and automations retain their meaning.\n\n### D6 — acceptance and delivery sequence\n\nImplement **D0 → D1 → D2 → D3 → D4 → D5**, adding focused tests with each change. D6 is the release acceptance pass. The template designer and migration work may be developed once D2's schema is stable, but changing the default follows runtime and permission verification. Do not rebuild the scheduler or storage engine merely to support this revision.\n\n| Acceptance scenario | Required evidence |\n|---|---|\n| Fresh install, no templates | Auto generates and completes an appropriate plan; template lookup is optional. |\n| Small task and Solo | Auto avoids unnecessary parallel workers; explicit Solo launches no delegated agents, including hidden native delegation. |\n| Mixed-provider feature | Eligible Claude/Codex or other workers inherit the run policy, use narrower turn access where required, and complete checks and independent review. |\n| Provider replacement | A replacement preserves the run policy; unsupported mandatory enforcement prevents dispatch instead of weakening restrictions. |\n| Template parity and composition | Advanced and Auto use the same tested template semantics; test-first protection, race isolation, loop bounds, and review-only constraints survive adaptation. |\n| Template migration and isolation | Legacy library import is idempotent; permissions/auth bindings are not restored; personal/project access boundaries hold. |\n| Recovery during execution | Recorded plan/template/policy revisions remain pinned; completed results are retained; current revocations stop affected new work. |\n| Adapt and save | An adapted run can create a new template version; the original and all previously started runs remain unchanged. |\n| Compatibility | Saved runs, explicit preferences, automations, extension patterns, remote execution, and the Auto disable switch retain documented behavior. |\n\nUse focused tests in the existing Auto/team scheduler/replay/strategy suites, adapter and approval tests, composer/library tests, and browser flows. Add bounded real-provider checks for enforcement claims; fake-agent tests verify orchestration but cannot establish sandbox equivalence. Run typecheck, relevant tests, the build, and documentation/link checks appropriate to the implementation. Report unavailable provider checks as unverified.\n\nThe complete release demonstration starts in default Auto, builds a plan without requiring a template, optionally reuses an enabled template, runs workers across providers under the run policy, recovers from interruption, and delivers verified results. The user can then save the reusable strategy, inspect it in Advanced, or start the next task in Solo.\n\nCritical review fixes (21 October 2026):\n\n- Removed the engine startup opt-out, inherited parent trust/approval ceilings and execution boundaries, and sent the full validated policy to remote workers.\n- Revalidated installed native controls on every launch; rejected unavailable approval transports and strict-boundary/approval combinations. Codex overrides ambient writable roots, temporary write exemptions, approval policy and network settings. Claude explicitly disables Chrome when external tools are forbidden.\n- Serialized follow-up permission mutations, validated recovered policy records, and preserved mandatory requirements across revisions and child runs.\n- Preserved template dependency types and global acceptance criteria; template revisions cannot replace original instructions or widen owned paths.\n- Updated built-in provider defaults to Claude/Codex. New runs using unverified Antigravity, ACP or extension adapters are rejected; legacy adapter-level compatibility remains separately tested.\n\nThese checks establish adapter behavior using deterministic CLIs and local capability probes. They do not establish equivalent operating-system enforcement across providers, or disable arbitrary subprocesses launched through an authorized shell.\n\nReview validation: the full regression run passed 1,218 tests with eight skips and no failures. All 11 targeted browser scenarios passed, along with 28 focused inheritance/recovery/template tests. The final adapter and approval refinements passed nine adapter tests and the focused approval test. Typecheck, production build, and diff whitespace checks passed. Live-provider sandbox enforcement was unverified at that checkpoint; the 2026-10-21 report below records the subsequent bounded live probes.\n\n## Revision 3 baseline and historical implementation record\n\nPrepared 17 October 2026 against repository revision `ee246c7`. Revised against [Review 0](dynamic-teams-plan-review-0.md). Phase numbers below supersede the original ordering; the review remains a record of the original proposal.\n\nImplementation is based on `main` at `8cb3641`, including its checked review findings and fail-to-pass proofs. Sections describing the original repository baseline below are historical design context, not claims about the updated code.\n\n### Implementation status\n\n| Work package | Delivered |\n|---|---|\n| P0 + Slice 0 | Opt-in Auto team, strict provider-compatible schemas, composition rules, approval, independent members, capability inventory, verification and launch budgets |\n| P1 | Fail-closed fsynced journal, incomplete-tail recovery, deterministic completion/command replay, crash recovery, SSE restart reconciliation and measured replay bytes/time |\n| P2 | Member/session/settings/usage identity, isolated writers, typed knowledge/artifact/completion dependencies, integration SHA inputs, stale-attempt rejection and bounded replacement |\n| P3 | Private checkpoints, team proposals/contracts/findings, attributable project retrieval/promotion, inspector/CAS/retention/deletion, exact-revision relevance feedback and fixture/real-run retrieval measurements |\n| P4 | Event-driven ready-work dispatch, atomic mailbox commands/receipts, request/wait/deadline state, idle answer turns, immediate/duplicate replies, cycle/unavailable escalation and replayed completion batches |\n| P5 | Bounded expansion/retirement, safe handovers, compact coordinator checkpoints, declarative reusable subplans, affected-evidence invalidation, shared Supervisor decomposition/integration primitives and saved-wave compatibility |\n| P6 | Scoped Claude/Codex MCP bridge, expiring member credentials, server-derived authority, durable inbox/memory tools, agent-origin live steering with queued fallback, explicit capability/nested-agent limits and real CLI smoke checks |\n| P7 | Composer and workflow/inbox/context/memory views, approval and blockers, phone/automation integration, bounded follow-ups, explicit in-flight goal revisions, parent-context/source-trust retention and new-run disable switch |\n| P8 | Fault/compatibility/browser tests, isolated fake and real paired evaluation runner, immutable versioned reports, approval/memory/replay/coordination measurements and a recorded rollout decision whose beta/default stages are evidence-gated (the stage is a label and a composer default; availability is controlled by `CODEFLOW_AUTO_TEAM_ENABLED`) |\n\nThe user's instruction to implement all remaining phases explicitly opened P4/P6 on 17 October 2026. Their original sampling gates remain below as historical decision criteria; the implementation does not claim that a 20-run/100-turn latency experiment has been completed. The reproducible no-barrier fixture demonstrates the scheduling order (a dependent starts before an unrelated slow worker ends); it measures no benefit. Real model quality, overhead and default rollout are assessed separately in the evidence report.\n\nThe storage decision is to retain the single-writer JSONL journal. One durable command envelope contains question/wait effects and its receipt; replayed drain batches and completion order provide the required logical atomicity. Disposable projections, rather than an outbox, database, leases or a new runtime baseline, remain sufficient. Node 20/22 CI configuration is preserved.\n\nThe rollout decision is **experimental/opt-in**. Implementing the rollout gate does not force promotion: the initial real comparisons expose bounded-time failures and coordination overhead, so there is no evidence for making Auto the default. Unknown vendor-native usage remains unknown; unsupported adapters retain their structured-outcome path.\n\n## 1. Product promise and delivery decision\n\n> Give code-flow a task; it assembles an appropriate team from your available agents, coordinates their work, and delivers a verified result with reusable project knowledge.\n\nThe user should not need to choose an orchestration pattern. A coordinator investigates the goal, proposes members and a workflow, gets plan approval according to the user's settings, and adapts the work as evidence arrives. Small tasks should produce small teams. Success means satisfying the task with evidence, not creating more agents.\n\n**Build Slice 0 before a storage migration, a new scheduler, or a mailbox service.** It is an opt-in **Auto team** pattern on the existing engine: coordinator planning, plan approval, dependency waves, addressed questions in structured outcomes, bounded revisions, and independent verification. Minimal private checkpoints and team contracts are part of that slice. Reusable project memory follows execution data in Phase 3; Slice 0 does not claim to deliver that part of the promise yet.\n\nThe long-term architecture stays the same: code-flow owns member identities, workflows, communication, and three memory scopes across official vendor CLI sessions. The delivery order changes so real runs determine how much infrastructure is needed.\n\n```mermaid\nflowchart TD\n Goal[User goal and constraints] --> Plan[Coordinator proposes team and tasks]\n Plan --> Policy[Runtime validates composition and limits]\n Policy --> Approval[Approve, revise, or reject under user settings]\n Approval --> Waves[Execute ready tasks; replay saved waves]\n Waves --> Outcomes[Artifacts, checkpoints, findings, questions]\n Outcomes --> Revise[Bounded coordinator revision when needed]\n Revise --> Policy\n Outcomes --> Verify[Integrate, check, independent review]\n Verify --> Result[Result with evidence and unresolved gaps]\n Result --> Promotion[Reviewed project-memory proposals]\n Waves <--> Journal[Existing replay journal]\n Outcomes --> View[Run snapshot and SSE team view]\n```\n\n## 2. What we reuse, and what actually needs changing\n\n| Foundation | Existing behavior | Planned extension |\n|---|---|---|\n| `src/patterns/supervisor.ts` | Read-only decomposition, contracts, approval, parallel workers, integration and review | Generate individual members, execute dependency waves, revise assignments and route questions |\n| `TeamContext.decide()` / `remember()` | Completed decisions and operations replay from the journal | Stable task/wave/attempt identities and deterministic application of recorded revisions |\n| `src/store.ts` | Ordered JSONL writes, a synced replay journal, one-server lock | Surface control-write failures; add batch records/snapshots only where required |\n| `TeamContext.ask()` and adapters | Official CLIs, sessions, structured output, retries, cancellation | Member identity and validated member-specific settings, without replacing adapters |\n| `src/routing.ts`, `src/stats.ts` | Availability/quota signals and repository outcome ranking | Apply these inputs to a validated team composition policy; allow repeated providers |\n| `src/git.ts`, engine worktree/check methods | Isolated worktrees, replay-aware commits and checks | Dependency inputs pinned to integration commits, explicit verification coverage |\n| Notes and Codex steering | User-origin notes addressed by provider; some live input | Member addressing and agent-origin messages kept separate from user instructions |\n| `src/context.ts`, `src/learnings.ts`, `src/projects.ts` | Conventions, attachments, recurring findings, project membership | Scoped checkpoints and attributable memory, with enforced promotion rules |\n| Run snapshot, SSE and composer | Existing pattern selection, approvals and live steps | Auto team choice, roster/tasks/questions and coordination metrics from Slice 0 |\n\n### Recovery correction\n\n`remember()` already journals the result of `decide()`'s underlying agent call. A deterministic pattern replays completed coordinator decisions; it does not ask the model to invent its team again. Dynamic planning therefore does not, by itself, require a new recovery model.\n\n`journalEntry()` calls `appendLine(..., { sync: true })`, and the journal write is awaited before the pattern continues. Ordinary event-log writes do not fsync each event. However, **both paths currently use `appendLine()`, which catches write errors, increments `writeHealth`, and resolves**. The journal has a durability mechanism but does not fail closed when that mechanism fails. Slice 0 should add narrowly scoped error propagation for authoritative journal writes and update the in-memory journal only after successful persistence. This is a small hardening change, not a database migration.\n\nReplay remains appropriate while execution is a bounded sequence of waves and recorded outcomes. Long-lived mailboxes, asynchronous wakeups outside those waves, or expensive replay could justify another driver later. Phase 4 has an evidence gate for that decision.\n\n### Compatibility facts\n\n- `AgentId` is a provider (`claude`, `codex`, `agy`), not an individual worker. Existing role routing deduplicates providers within a role; dynamic member IDs must bypass that deduplication without changing manual-pattern semantics.\n- Node 20 is supported and in CI. Preserve it for this plan. SQLite is not a prerequisite; any later driver proposal must explicitly address Node, packaging and platform support.\n- Resume currently gives a fresh step/time budget. Slice 0 preserves this documented behavior for an explicit user resume, while reporting lifetime usage separately. A replayed wave never resets its recorded wave/repair counters. Internal retries do not silently grant a fresh budget.\n- There are four `findPattern()` call sites in engine/server code. Register `auto-team` normally. Older versions already reject an unknown pattern on resume; no new execution-mode framework is necessary for Slice 0. Future incompatible plan schemas need an explicit version check.\n- SSE reconnect cursors currently apply within one server start. Improving cross-restart reconnect helps legacy runs too and can ship independently.\n\n## 3. Invariants to keep\n\n1. **Agents propose; the runtime validates and verifies.** A coordinator cannot delete user requirements, increase its budget, raise trust, or authorize publishing through a plan revision.\n2. **Individual identity is independent of provider.** Two Codex members have different IDs, context, sessions, assignments and usage attribution.\n3. **One active turn per member/session, one writer per worktree.** Reassignment reconciles the previous process before starting another writer. Never pass a session ID between providers.\n4. **Persist accepted control state before acting on it.** A failed authoritative write stops new dependent dispatch. UI projections are not a second source of truth.\n5. **Replay completed decisions.** Stable identities, recorded inputs and deterministic ordering reproduce accepted plans. Current clock/quota/filesystem state must not silently change an already-recorded branch during replay.\n6. **Messages are not user authorization.** Sender identity and source trust come from code-flow. Ordinary output text is never parsed as executable control commands.\n7. **Waiting releases capacity.** A member needing information ends its turn with a checkpoint; it does not occupy a CLI slot waiting for another member.\n8. **Verification applies to evidence.** Unknown or unrunnable is not a pass. A gap must never become success merely because all runnable tasks ended.\n9. **Project memory has an enforced trust boundary.** A locked-down or otherwise untrusted source cannot automatically create instructions for future runs.\n10. **No exactly-once model-execution claim.** A process can finish external work before its result is stored. Recovery reconciles artifacts and uses idempotent logical operations; transport or execution can repeat.\n\n## 4. Define “appropriate team” before implementation\n\nThese are proposed, testable defaults for the opt-in experiment. Record them in the plan and fixtures, rather than leaving them to a coordinator prompt.\n\n| Rule | Initial policy and validation |\n|---|---|\n| User constraints | Preserve provider/model pins, trust, allowed paths, check commands and budgets. Reject revisions that exceed them. |\n| Availability | Choose installed, usable providers. Reuse `exhausted()` signals, including near-full windows and recent authentication failures. Unknown quota is labeled unknown. |\n| All providers exhausted | Auto team pauses or reports a routing blocker; it does not silently spend against a known exhausted provider. This is stricter than current `resolveAuto()`, which can fall back to exhausted candidates with a warning. Explicit user choices remain visible. |\n| Provider ranking | Use existing repository pass-history ranking among eligible providers, with its existing fallback order. Treat it as a prior with limited evidence, not a universal skill score. |\n| Small teams | Start with a coordinator, one worker and an independent reviewer. Allow a second worker only for tasks whose ownership/dependencies permit useful parallel work, with a stated reason. A read-only task may use the coordinator as its investigator. |\n| Size bounds | Default maximum four active members including coordinator/reviewer, twelve tasks across accepted revisions, four execution waves, and two repair rounds. Reject expansion beyond the approved limits. These are experiment settings, not measured optima. |\n| Review independence | Reviewer is a separate member/session, read-only, and cannot approve work it authored. Prefer a different provider from the primary author; record any same-provider fallback. If the user's policy requires different providers and none is eligible, report the blocker. |\n| Same-provider workers | Allowed when useful. Member IDs, not provider strings or display names, identify tasks and communications. |\n| Models and effort | Choose from the supplied supported/configured settings or the provider default. Do not invent a model name. Explicit user settings win. |\n| Workflow validity | Unique IDs, existing owners, acyclic dependencies, bounded expansion, acceptance criteria for required tasks, and requirement coverage. Reject silent requirement removal. |\n| Ownership | Concurrent tasks need compatible declared ownership; actual changed paths are checked before integration. Shared-file work is ordered or assigned to an integration task. |\n| Task kind | Implementation requires applicable checks/review; investigation/design can succeed with an accepted evidence-bearing artifact and no code changes. |\n\nThe first plan includes stable requirement IDs, editable acceptance criteria, members and rationale, provider/model/effort settings, tasks/dependencies, ownership, contracts, verification requirements, and a turn estimate with a hard cap. Estimates must distinguish known bounds from uncertain token use.\n\n### Approval and revision policy\n\nUse the existing `ctx.approve(\"plan\", ...)` checkpoint before dispatching workers. Show the proposed team, settings, tasks, acceptance criteria, constraints and estimated turns. Approve, revise and reject must all work. Approval pauses the run clock and its recorded answer replays on resume.\n\nFor a newly selected Auto team, default plan approval on; honor explicit saved choices and existing automation authorization. The initial planning call itself consumes quota, which the composer should make clear. When approval is disabled, still record the validated plan and why no interactive checkpoint was required.\n\nRoutine reassignment or repair within the approved scope can proceed and remains visible. A user-requested goal change produces a new requirement/plan revision and goes through the same configured checkpoint before affected new work. Any proposal exceeding authorized providers, spending limits, permissions or scope requires the relevant user decision; the coordinator cannot approve that increase. Stop or finish the current turn safely before changing its inputs.\n\n### Coordination budget and measurements\n\nUse actual CLI invocations, including schema repair and retry invocations, for coordination accounting. Existing logical `agentSteps` alone may not expose every underlying retry; add attribution at the launch boundary. Report steps, input/output/cache tokens, elapsed time, and provider-reported API-equivalent cost separately. Missing token/cost reports are unknown, not zero, and API-equivalent cost is not a subscription bill.\n\nFor the initial experiment, with an approved invocation budget `B >= 4`, reserve at most `min(4, floor(B / 4))` coordinator invocations. Count initial planning, approval-driven replanning and wave revisions against this ceiling. Enforce the total execution limits too; reserve room for required review and checks before admitting more work. If the budget cannot fund a valid plan, explain that before worker dispatch.\n\nThis caps coordination at 25% of the *authorized invocation budget*, not necessarily 25% of actual spend. A tiny run may use one coordinator turn out of three total turns. Report the observed fraction honestly. Use 25% observed coordinator turns on multi-worker runs as an initial evaluation target, not an asserted result or a runtime rule that could deadlock startup.\n\nAt the coordination ceiling, run only already-authorized ready work and verification that fit the remaining budget. Do not create additional tasks or keep calling the coordinator. Return explicit gaps if another planning decision is required. User resume retains today's fresh-budget behavior; expose both the new allocation and cumulative spend. Automatic crash recovery must not be used as a budget-reset mechanism.\n\n## 5. Slice 0 — demonstrate the promise on the existing engine\n\n**Outcome:** a user chooses Auto team, describes a task, approves the generated team/plan, and sees it execute and adapt without choosing a team pattern. This is the first user-visible milestone.\n\nImplement Slice 0 alongside the small Phase 0 contract work. It does not wait for Phases 1–4.\n\n### 5.1 Plan and replay\n\nAdd `src/patterns/auto-team.ts`, register `auto-team`, and use `TeamContext.decide()` with `TEAM_PLAN_SCHEMA`. Reuse supervisor planning, approval, worktree, integration and review behavior where practical.\n\nJournal the planning inventory and relevant policy inputs before using them: provider availability/quota observations, configured settings, task limits and base commit. Validate completed decisions against their recorded input revision on replay; refresh availability at a new dispatch boundary through a new recorded observation. Do not recompute historical routing from today's quota or clock.\n\nUse stable identities such as `plan:0`, `wave:2`, and `task:api:attempt:1`. Create all task/attempt identities and a sorted wave membership before parallel dispatch. Keep unrelated operations out of those identity counters. Reconstruct the roster, sessions and questions from recorded outcomes; an `onStart` callback that does not run during replay cannot be the sole source of that state.\n\n### 5.2 Individual members and workspace handling\n\nStore a small member map within the pattern's replayable state. Add an optional `memberId` to step/ask metadata and a validated per-call settings override so two members on the same provider can actually use different configured models. Preserve existing role/provider settings as the fallback for legacy patterns.\n\nAssociate each member with its current session lineage and workspace. Resume the same session only with its compatible provider and workspace. A new task in a different worktree gets a fresh session plus a portable checkpoint; do not assume a provider can safely resume a conversation in a different directory. Keep one active assignment per member.\n\nCreate a task's worktree from the recorded integration HEAD containing its required upstream artifacts. A yielded task keeps its existing worktree and checkpoint. Reconcile any newer dependency commits only while that task is idle, then record the new input revision. A message describing code does not make that code available in another worktree.\n\n### 5.3 Execute dependency waves\n\nSelect ready tasks deterministically, respecting dependencies, ownership, available members and global slots. Use `ctx.parallel()` and `allowFailure: true` for expected worker failures; retain run-wide stopping for unsafe process/storage/workspace failures.\n\nEach turn produces a validated `TASK_OUTCOME_SCHEMA` with:\n\n- task/member/attempt identity and `submitted`, `needs_info`, or `failed` status;\n- an observable checkpoint, artifact references and unresolved requirements;\n- addressed questions/replies, contract proposals and evidence-bearing findings;\n- the input/contract revisions the result relied on.\n\nA `needs_info` result ends the CLI turn and releases its slot. Preserve partial edits for that task; do not merge them as a completed dependency. Integrate submitted artifacts serially, handle conflicts through existing controlled repair, and run applicable checks. Downstream work starts only once its required artifacts and acceptance conditions are available.\n\nAn agent saying “submitted” does not make its task verified. Code changes must pass the declared verification policy. No-change investigation tasks are evaluated against their artifact criteria.\n\n### 5.4 Questions, answers and revision between waves\n\nRepresent communication as addressed records in the structured outcomes. A question has a stable request ID, sender, recipient, task, body, source provenance, and whether it blocks progress. A reply references that request ID. The runtime supplies authenticated identities rather than trusting model-written sender fields.\n\nAfter a wave, deterministically route known replies and evaluate ready work. Call the coordinator only when a decision is needed, at most once per wave within its budget. `REVISION_SCHEMA` can propose new bounded tasks, reassignments, accepted contracts, answer tasks, evidence-backed answers, or a completion proposal. Validate the patch against the previous revision and apply it before scheduling.\n\nIf the recipient already supplied the answer, deliver it in the requester's next prompt. Otherwise schedule a short response task for that member in the next wave. The coordinator may answer from evidence or reroute the question, but cannot manufacture a missing requirement. Questions requiring user judgment become a visible input blocker. No-ready-work and cyclic waits produce a bounded escalation instead of empty waves or endless coordination.\n\nThis is cross-agent communication at turn boundaries. No separate mailbox daemon, live tool bridge, delivery table, outbox, or lease is required. The coordinator does not need to rewrite every answer: addressed contents pass through to the appropriate member.\n\nUpdate the Auto team worker framing in `src/patterns/prompts.ts` so it explains `needs_info` rather than saying nobody can answer. Update relevant Codex app-server input-denial guidance to point Auto members at the structured protocol. Keep native permission requests subject to existing policy; supporting questions does not authorize new tool permissions.\n\n### 5.5 Minimal memory and context\n\nStart with three small replayable objects:\n\n- **Private checkpoint:** current assignment, attempted approaches, observable progress, unresolved questions and relevant session/worktree references, scoped to that member.\n- **Team state:** accepted plan/contracts, shared findings, decisions, questions/replies and artifact references, selected for the current task.\n- **Context manifest:** the input commit, plan/contract revisions, message IDs and checkpoint used for each turn.\n\nKeep prompts bounded and send relevant deltas on resume. Record a compact coordinator checkpoint too: accepted plan, completed work, open decisions and remaining budget. Start a fresh coordinator session from that checkpoint when its context grows or resume fails; do not repeatedly send all worker transcripts.\n\nUse existing conventions and attributable lessons as inputs; do not implement a new project-memory index yet. Auto team must not automatically write project knowledge from locked-down runs through `learn()` or another side path. `learn()` currently checks replay and the learning setting, not lockdown; the implementation must explicitly close that bypass for automatic learning, with a regression test.\n\n### 5.6 Verify, display and measure\n\nUse an independent read-only reviewer, bounded repairs and final checks on the integrated result. A review after repairs must refer to the current commit. Start check identity with commit SHA plus command and a setup fingerprint when applicable; requirement/contract changes invalidate affected acceptance/review evidence without invalidating every unrelated check merely because the plan number changed.\n\nCompletion is a runtime predicate: every required criterion has applicable evidence, required questions are resolved, and the integrated result matches that evidence. Use existing `setVerdict()`/`setFacts()` where their meaning fits, plus an additive team outcome/coverage projection. Do not force “completed with gaps” or an unknown check into a passed verdict.\n\nAdd the opt-in composer choice and a minimal roster/task/question view now, using the existing run snapshot and SSE. Agent-origin communications must not go through the existing user-note formatter unchanged. Show coordination usage as soon as the first planning call completes, then update it throughout execution.\n\n**Likely files:** new `src/patterns/auto-team.ts` and a small focused helper if needed; changes to `src/patterns/index.ts`, `src/patterns/schemas.ts`, `src/patterns/prompts.ts`, `src/engine.ts`, `src/store.ts`, `src/routing.ts`, `src/stats.ts`, `src/server.ts`, `ui/composer.tsx`, `ui/run.tsx`, shared UI types, and fake-agent fixtures. Avoid three new subsystem directories at this stage.\n\n**Exit criteria:** the composer-to-verified-result scenario in section 10 passes with fake agents; a completed initial decision replays without another model call; two same-provider members remain distinct; a question blocks only its dependent work; coordinator usage and question frequency are recorded; review/permission settings still apply. Real opt-in runs supply the first product measurements, not a prerequisite for offline CI.\n\n## 6. Records, persistence and communication as they grow\n\nThe original record names remain useful domain concepts. They do not all need separate durable tables or independent state machines.\n\n| Concept | Slice 0 representation | Split or extend when justified |\n|---|---|---|\n| Run / workflow revision | Existing run plus versioned accepted plan and journaled revisions | Standalone workflow projection if event-driven execution needs it |\n| Member | ID, role/provider/settings, session/workspace lineage | Derive running/waiting state from attempts and blockers; persist retirement/replacement decisions |\n| Task / attempt | Accepted task graph plus recorded turn outcomes and existing steps | First-class attempt history during execution hardening |\n| Message / request | Typed addressed records with optional request/reply reference | Durable per-member inbox when asynchronous delivery is needed |\n| Delivery | Context manifest lists messages included in an attempt | Separate transport receipt only for live steering/tools |\n| Memory revision | Member checkpoints and accepted team entries | Project store, revisions, retention and retrieval in Phase 3 |\n| Artifact / verification | Existing Git/check outputs plus task/criterion references | Additional evidence types as real tasks need them |\n| Event / command receipt | Existing journal entries and stable operation IDs | Atomic batch and deduplication receipt for multi-record async commands |\n| Outbox / lease | Not required by the wave pattern | Only if a measured execution architecture requires independent dispatch ownership |\n\nTasks can be planned, ready, running, waiting, submitted, verified, failed, cancelled or superseded. Attempts distinguish interrupted execution from a failed task. Member display status is derived. Keep explicit run outcomes for succeeded, completed with gaps, awaiting input, failed, stopped and cancelled; legacy cards can project these without changing their historical meaning.\n\n### Storage requirements and default\n\nStart with the existing single-writer store and journal. Required properties are durable acceptance, stable operation IDs, ordered replay, idempotent application and recoverable snapshots. If one command must update several control records, append one versioned batch envelope containing all those changes, then fsync before acknowledging it.\n\nA filesystem append is **not guaranteed physically atomic**, even with one writer. Recovery must validate the complete framed record and apply the entire batch or none of it; an incomplete tail cannot expose partial logical changes. A damaged committed record in the middle of control history is a recovery error, not permission to skip a dependency. Test those semantics explicitly, including uncertain completion after a failed sync.\n\nUse snapshots as disposable projections tied to a journal sequence/checksum, written through a temporary file and replacement with platform-appropriate durability checks. Replay the remaining tail; do not discard authoritative history before snapshot/retention rules make that safe. Keep existing one-server ownership and process cleanup.\n\nEvaluate SQLite only if measured replay time, query volume, memory search or genuinely concurrent writers outgrow this model. Any proposal must compare complexity and durability, retain Node 20 or explicitly propose a platform change, and state install/backup/migration behavior. There is no mandatory database spike blocking Slice 0.\n\n### Delivery semantics\n\nAt turn boundaries, distinguish queued content from content recorded in an attempt's context manifest. A manifest describes intended inclusion; a completed turn provides evidence of execution. A protocol acknowledgement is not proof of comprehension. A question is resolved by an accepted reply or an explicit recorded resolution, not a delivery receipt.\n\nUse stable IDs and idempotent application across repeated turns. Live transport later adds accepted-steering receipts without changing request semantics. Never allow two concurrent resumes of one session, and never acknowledge a control operation before its authoritative write succeeds.\n\nKeep domain operations independent of HTTP/MCP: `send_message`, `ask_member`, `reply_request`, `yield_task`, `publish_finding`, `propose_contract`, `submit_task`, and `request_help`. Structured results are the first transport across all adapters. Add bounded inbox/memory reads only when those stores exist.\n\nExtend existing run snapshots and SSE before adding dedicated endpoints. Agent tools later call the same validated handlers with member-scoped credentials; user origin is derived from authenticated user requests, not a caller-supplied field.\n\n## 7. Private, team and project memory\n\n| Scope | Purpose | Visibility and lifetime |\n|---|---|---|\n| Private | Portable progress checkpoint, attempted approaches, unresolved work and session references | Only that member's assembled context; explicitly selected handover export; retained with the run under policy |\n| Team | Accepted contracts, plan, decisions, findings, questions and artifact references | Relevant members of the run; preserved across restart; archived with the run |\n| Project | Durable conventions, architecture facts, test instructions and verified lessons | Applicable future runs in the project/repository; revision, invalidation and deletion controls |\n\nPrivate means isolation in code-flow's context assembly and APIs. It is not filesystem secrecy from a fully trusted process on the same machine. Store concise observable checkpoints, not a claimed export of a model's internal reasoning. The user can inspect code-flow-owned records according to existing access policy.\n\n### Provenance and promotion rules\n\nEvery shared item carries source run/task/artifact, originating source trust/lockdown, author, evidence, scope, relevant paths/commit, revision and status. Execution permission (`safe`, `standard`, etc.) and content provenance are separate: a read-only run can still read hostile source text.\n\n- User-authored project rules can be saved directly under user authority.\n- Eligible agent-authored facts may be promoted under the configured verification policy after their evidence and target-branch applicability are established.\n- A locked-down run, or a proposal derived from untrusted content, cannot promote automatically. It stays a proposal until the user explicitly approves the specific content/revision.\n- Derived summaries, replies and handovers retain source provenance. A coordinator or independent reviewer repeating a claim cannot remove its untrusted origin. The server enforces this; agents cannot set their own trust fields.\n- Historical learnings with missing provenance are attributed observations, not automatically trusted conventions. The same promotion gate covers `learn()`, tool commands, summaries and imports.\n- Retrieval labels provenance, separates facts from suggestions, and cannot override current user constraints or tool permissions. Team messages inherit the run's restrictions and source labels.\n\nMemory about an unmerged implementation stays team-scoped unless explicitly marked and accepted as branch-specific project knowledge. Project-wide conventions must not be inferred from one branch's code. Recheck applicability when branch, repository membership or project ownership changes.\n\n### Selection, conflicts and deletion\n\nBuild each turn's context from the goal, assignment, applicable project entries, relevant team contracts, private checkpoint, pending messages and artifact/check references. Select by task, paths, tags, freshness and text search first. Store included revisions and omissions in the context manifest; introduce embeddings only when an evaluation shows a need.\n\nShared updates use expected revisions. Conflicts are visible and assigned to an owner; they do not silently overwrite accepted contracts. A changed contract identifies affected tasks and acceptance evidence.\n\nDeletion covers memory-store bodies and derived memory records, including its snapshots and retrieval data; retained memory history contains deletion metadata. Project selection journals contain references rather than bodies. Source run transcripts and task checkpoints already recorded in the run journal, prompts already delivered to a CLI, provider-managed conversations and user-managed backups have separate retention. The current implementation does not claim to redact those source histories; broader run-history erasure requires a separate retention design. Test exclusions and scope isolation with actual workflow data.\n\n## 8. Phases after Slice 0\n\nThe original default path was **P0 + Slice 0 → P1 → P2 → P3 → P5 → P7 → P8**, with conditional P4/P6 enhancements. The requested complete implementation includes all packages. Reliability, UI visibility and measurement begin in Slice 0, not at the final phase. Phase labels identify work packages, not a requirement to build every subsystem before release.\n\n### Phase 0 — contracts and composition policy, alongside Slice 0\n\n**Deliver:** plan/revision/outcome schemas, the section 4 validation rules, task-kind verification policies, fixtures and an adapter capability inventory. Record structured-output, session-resume, live-input, tool-configuration and sandbox support as tested/unsupported/unknown. Include Claude's tool allowlist and vendor-native subagent observability.\n\n**Exit:** valid/invalid plan fixtures cover duplicate members, unsupported settings, cycles, over-budget teams, reviewer independence and requirement removal. Slice 0's first PR can begin immediately; no platform migration blocks it.\n\n**Code impact:** existing pattern schemas and focused policy helpers. No broad engine extraction or new storage tree.\n\n### Phase 1 — prove recovery and harden storage where needed\n\n**Deliver:** fault-injected recovery for Slice 0 and an architecture decision based on observed requirements. Verify strict journal writes, completed-plan replay, deterministic parallel identities, crash-after-Git recovery and no concurrent writer after restart. Add batch records/snapshots only for properties the existing journal cannot provide adequately.\n\n**Exit:** restart preserves accepted teams, completed decisions, pending questions and task identities; failed writes prevent dependent dispatch; incomplete records cannot yield partial plans; Node 20/22 CI remains supported. Record replay duration and data size before choosing an optimization.\n\n**Likely files:** `src/store.ts`, `src/engine.ts`, store/recovery tests. Keep JSONL; no outbox, leases or migrations merely to match the original plan.\n\n### Phase 2 — harden dynamic execution and member identity\n\n**Deliver:** stable member/session metadata, task attempts, deterministic readiness, artifact inputs, stale-result rejection and fair admission through existing global slots. Make wave execution reusable only where another caller needs it. Keep verification and cancellation in the existing engine.\n\nDefine knowledge, artifact and completion dependencies separately. Do not dispatch a consumer merely because a message arrived when it actually needs merged code. Reassignment must stop/reconcile the old attempt and preserve portable progress. Reviewers retain scoped, independent context.\n\n**Exit:** same-provider members cannot receive one another's private context; downstream work gets the required integration SHA; stale attempts cannot overwrite replacements; cancellation terminates child processes/checks; changed evidence is reverified; missing required work cannot pass. Coordinator and worker usage remain separately visible.\n\n**Likely files:** Auto team helpers, `src/engine.ts`, `src/routing.ts`, `src/git.ts`, verification/recovery fixtures. Extract small shared helpers from supervisor where duplication is demonstrated; do not introduce an entirely separate executor pre-emptively.\n\n### Phase 3 — grow memory from observed workflow data\n\n**Deliver:** expand Slice 0's private/team records into versioned memory and add project retrieval, promotion, retention and deletion. Implement a basic inspector showing provenance and why an item was included. Use saved fixture traces and opt-in run data to tune relevance selection.\n\nApply the section 7 trust boundary at every write/promotion path. Add user approval for specific untrusted proposals, conflict resolution, branch applicability, lost-session reconstruction and project/repository scope checks.\n\n**Exit:** a later run reuses an eligible verified lesson without private-note leakage; an untrusted issue cannot inject a project convention directly or through a trusted-looking summary; stale branch facts are excluded; deletion removes content from retrieval and derived records; coordinator and worker recovery works without vendor sessions.\n\n**Likely files:** focused memory/context/promotion modules, `src/context.ts`, `src/learnings.ts`, `src/projects.ts`, a small memory UI. Introduce a directory only if the actual modules justify it.\n\n### Phase 4 — event-driven messaging\n\n**Decision gate:** evaluate at least 20 opt-in runs and 100 worker turns, alongside deliberately blocking fake-agent fixtures. These are initial sampling targets, not statistical guarantees. Open this phase if either at least 10% of worker turns yield for peer information, or at least 20% of runs lose more than 25% of active elapsed time to an avoidable wave barrier. Label counterfactual avoidable-delay estimates separately from directly measured waiting time. A reproducible correctness/liveness requirement beyond bounded waves can also justify the phase.\n\nBefore implementation, use traces or a small prototype to show that asynchronous scheduling reduces that delay within the approved coordination budget. If the sample is insufficient or waves are adequate, retain wave-based communication and record the decision.\n\n**Deliver if opened:** per-member inboxes, durable request/reply state, waiting/deadline handling, idempotent commands, answer turns for idle members, cycle/no-runnable-work detection and event coalescing. Persist a question and its wait state together so a fast reply cannot be lost. Retired/unavailable recipients route to the coordinator. An unanswered dependency never consumes a process slot.\n\nUse the existing storage interfaces and validated outcome protocol. Introduce a new driver only for execution that cannot be expressed clearly as recorded waves; bridge it through existing adapter/Git/check services.\n\n**Exit:** immediate and duplicate replies cannot duplicate work; questions survive restart; late replies after reassignment are handled explicitly; cyclic waits escalate once with a bounded resolution path; all-waiting teams release their slots. The UI exposes whom each task is waiting on.\n\n### Phase 5 — improve the coordinator already shipped in Slice 0\n\n**Deliver:** evidence-driven expansion/retirement, safe replacement handovers, compact coordinator checkpoints, completion evaluation and reusable sub-plans. Improve task sizing and routing using observed outcomes. Start with investigate, implement, independent review and bounded repair; expand into visible validated tasks.\n\nKeep user requirements distinct from implementation choices. Coalesce events if P4 is enabled; otherwise continue at wave boundaries. A session reset uses the accepted plan and checkpoint, not full transcripts. Preserve blind-comparison boundaries when using competing proposals.\n\n**Exit:** a new blocker can add an eligible specialist within limits; retired members cannot accept new assignments; invalid/stale revisions leave state unchanged; the coordinator stays within its budget; review and completion coverage hold after repairs and replans. Compare with the original supervisor on the same tasks.\n\n**Code retirement:** make supervisor's useful decomposition/integration behavior a reusable strategy. Keep its public pattern ID and saved runs working through a compatibility wrapper until parity is demonstrated; then remove duplicated helper logic. Other manual patterns migrate only when their behavior is preserved.\n\n### Phase 6 — live tools and steering\n\n**Deliver:** expose the established operations through a scoped MCP or CLI bridge where the capability matrix proves support. Derive run/member identity server-side, use per-member credentials, and retain next-turn fallback. `CODEFLOW_TOKEN` is already withheld from agents; preserve that boundary.\n\nAdd collaboration tools explicitly to Claude's per-mode/trust allowlists. A read-only reviewer may communicate without gaining source-write permission. Test sandbox access without broadening filesystem/network permissions just to make the bridge work. Supported Codex steering retains origin and message identity; rejected/unsupported steering queues the message.\n\nOnly now add transport delivery receipts where needed. A tool request returns promptly; a blocked task yields through the existing protocol. Inventory native nested-agent controls and usage visibility; unsupported accounting must remain an explicit limitation.\n\n**Exit:** invalid/cross-run credentials fail; agents cannot spoof users; live and queued delivery have the same logical result; no double session resume; no occupied-slot deadlock; unsupported adapters remain fully functional at turn boundaries.\n\n### Phase 7 — complete product integration and migration\n\n**Deliver:** richer workflow/message/memory views, member-addressed user steering, context inspection, follow-up revisions and integration with projects, automations, sweeps, inbox/phone and review/shipping flows. The composer and basic team view already exist from Slice 0.\n\nPrefer additive run snapshots. Improve persistent SSE cursors with an explicit reset/reload path, benefiting legacy runs as well. A follow-up receives an explicit execution budget and preserves applicable artifacts/memory. Existing shipping permissions remain authoritative.\n\n**Exit:** users can inspect and revise the team, answer blockers, see memory provenance, reconnect after restart and continue completed work; existing manual runs and saved automations remain usable. Disabling new Auto teams preserves compatible viewing and recovery for existing runs.\n\n### Phase 8 — evaluation and rollout gate\n\n**Deliver:** expand the measurements started in Slice 0, publish comparison results, and move from experimental to opt-in beta, then default only if quality/cost justify it. Cover missing sessions, exhausted quota, failed setup, worktree loss, storage full, stale outputs, cancellation and crash boundaries. Record CLI versions for real trials; paid calls are not required by ordinary CI.\n\n**Exit:** no silent requirement loss or false verification passes; no duplicate logical tasks/concurrent writers after restart; trust and memory tests pass; legacy compatibility holds; observed coordinator overhead and waiting rates have an explicit product decision. P4/P6 are not release prerequisites if the turn-boundary product meets its goals.\n\n## 9. Measurements and decisions\n\nCapture the following locally from Slice 0. Fake agents prove behavior; real opt-in runs measure product usefulness. Do not conflate them.\n\n| Measurement | Capture point | Decision it informs | Current result |\n|---|---|---|---|\n| Coordinator invocations, tokens, time and reported cost | First planning call, every revision and retry | Coordinator budget and wake frequency | Captured; see [evidence report](dynamic-teams-evaluation.md) for observed values and sample limits |\n| Plan approvals/revisions/rejections and reasons | Existing approval checkpoint | Composition and task-sizing policy | Partly: the code records them, but every evidence trial disabled approval, so the report holds no approval numbers |\n| Questions and blocked turns / worker turns | Every structured outcome | Need for asynchronous mailboxes | Captured; see [evidence report](dynamic-teams-evaluation.md) for observed values and sample limits |\n| Blocked duration and estimated avoidable wave delay | Yield, reply, ready and resume times; exclude user waits | P4 threshold and likely benefit | Captured; see [evidence report](dynamic-teams-evaluation.md) for observed values and sample limits |\n| Same-provider identity/addressing errors | Fixtures and actual member events | Scope of member-identity changes | Not captured: the report format has no such field; same-provider isolation is covered by tests only |\n| Verification coverage, completion quality and repair turns | Final runtime evaluation | Auto team versus supervisor/manual baselines | Captured; see [evidence report](dynamic-teams-evaluation.md) for observed values and sample limits |\n| Journal size, replay time and failed writes | Recovery fixtures and runs | Snapshot/store investment | Partly: journal bytes, records and load time are reported; failed writes are not counted |\n| Memory selected, useful, stale or corrected | Context manifest and later outcomes from P3 | Retrieval and promotion policy | Captured; see [evidence report](dynamic-teams-evaluation.md) for observed values and sample limits |\n\nA coordinator outcome proposes team changes; it does not mark its own experiment successful. Record the validated plan, actual usage and runtime evidence so decisions can be reproduced. Do not fill this table with invented targets presented as measurements.\n\nDecisions resolved for implementation: keep Node 20; default to the current journal/store; start communication at turn boundaries; enforce composition rules in code; retain configured plan approval; keep explicit-resume budget semantics visible; prevent automatic untrusted project promotion; make mailbox/live layers conditional. The numerical experiment defaults above can change only with a recorded rationale and updated fixtures.\n\n## 10. Acceptance scenarios and reviewable implementation sequence\n\n### First scenario: Slice 0\n\n1. Start Auto team from the composer on a fixture with backend, frontend and final-check requirements. No manual worker roster is supplied.\n2. A coordinator proposes the small team, contracts and graph. The user revises acceptance criteria once, then approves; a rejection variant starts no workers.\n3. Independent tasks run in isolated worktrees. A variant uses two members on the same provider with different settings and sessions.\n4. Frontend returns `needs_info` with a question to backend. Its process ends; unrelated ready work proceeds. A backend outcome or short answer task supplies evidence next wave.\n5. Frontend receives the addressed reply and accepted contract. Its checkpoint resumes in the correct workspace; missing-session recovery uses a fresh session with selected context.\n6. Integrate submitted changes. A downstream task requiring actual backend code starts from the required integration commit.\n7. Independent review requests one repair. After repair, current checks and review determine the result and list any gaps. Unknown checks never become passes.\n8. Display coordinator usage, wave/question counts and requirement evidence. No new project memory is promoted in this slice.\n9. Restart after a completed planning decision in a variant. The decision replays, preserving roster and task IDs without another planning invocation.\n\n### Later recovery, messaging and memory scenario\n\nIn P1, stop after a question outcome is journaled but before its next prompt, then resume with the same request IDs. In P4 if enabled, repeat at asynchronous delivery boundaries with an immediate/duplicate reply, expired recipient, stale attempt and cyclic wait. In P3, promote an eligible lesson and verify that a later run retrieves it without private notes; repeat from an untrusted issue and confirm that promotion remains pending user action.\n\n### Suggested first pull requests\n\n| PR | Reviewable scope | Acceptance |\n|---|---|---|\n| S0-A | Plan/outcome schemas, composition policies, fake-agent fixtures, strict journal-write error propagation | Invalid plans rejected; failed journal writes cannot advance accepted state |\n| S0-B | Auto team planning/approval, member settings, dependency waves, minimal context and final verification | Generated team executes; decisions replay; two same-provider members stay distinct |\n| S0-C | Questions/replies, bounded revisions, portable checkpoints and automatic-learning trust guard | A blocked member resumes; coordinator budget enforced; untrusted learning cannot bypass the gate |\n| S0-D | Opt-in composer, roster/tasks/questions, coordination metrics and end-to-end fixture | User-visible Slice 0 with measured counters; existing patterns still work |\n| P1+ | Choose the next narrow change from the phase gates and recorded measurements | No speculative storage or mailbox prerequisite |\n\nThe four S0 PRs make one vertical milestone; wire usage instrumentation in S0-B so the first coordinator call is measurable, then expose it in S0-D. Avoid releasing incomplete intermediate PRs as a finished Auto team feature.\n\nFor implementation, use `npm run typecheck` and focused `node:test` suites per change, the full `npm test` suite at shared engine/adapter boundaries, and `npm run test:e2e` for composer, approval, waiting and reconnect flows. Fault injection should test real failure boundaries, not merely serialize records. The revision-3 implementation is validated by the full test/build workflow and real CLI trials documented in the evidence report.\n\nThese phases are now implemented. Further model-quality tuning or a default rollout must use new, explicitly labeled evidence; it is not an unfinished infrastructure dependency.\n\n## 11. Review disposition and boundaries\n\n| Review finding | Revision 1 action |\n|---|---|\n| Promise arrives too late | Slice 0 delivers automatic team creation and basic collaboration immediately |\n| Recovery justification already met | Reuse `remember()`; distinguish later asynchronous needs from dynamic planning |\n| SQLite predetermines platform | Keep Node 20 and current store; technology change requires measured justification |\n| References use simpler persistence | Do not infer a requirement for leases/outbox from vendor examples |\n| “Appropriate team” undefined | Composition rules, budgets and fixtures are specified in section 4 |\n| Plan approval missing | Existing checkpoint is explicit before dispatch and on relevant scope revisions |\n| Project memory trust gap | Enforced provenance/promotion rule, including the existing learning path |\n| Coordination cost measured too late | Instrument the first planning call and cap coordination from Slice 0 |\n| Memory precedes useful execution data | Minimal runtime checkpoints now; full project memory follows execution |\n| Too many stored state machines | Derive member/delivery status; split records only when behavior needs it |\n\nTwo review details are refined rather than copied literally: a single append needs complete-record recovery to provide logical batch atomicity; and at the reviewed baseline, the synced journal swallowed write failures through the shared append helper. The implementation now propagates authoritative write failures and recovers incomplete tails, without making a database a prerequisite.\n\nInitial boundaries remain local official CLI execution, one coordinator per run, structured graphs, bounded expansion, isolated writers and controlled integration. Remote worker fleets, arbitrary generated workflow code, live cross-run agent messaging, vector databases and recursive autonomous teams are deferred. Manual patterns remain available while useful behavior is migrated into reusable strategies.\n\n## References\n\nRepository references: [Review 0](dynamic-teams-plan-review-0.md), [architecture](architecture.md), [patterns](patterns.md), [runs](runs.md), [configuration](configuration.md), and [security](security.md).\n\nDesign references retained from the original proposal:\n\n- [Claude dynamic workflows](https://code.claude.com/docs/en/workflows)\n- [Claude agent teams](https://code.claude.com/docs/en/agent-teams)\n- [Codex subagents](https://learn.chatgpt.com/docs/agent-configuration/subagents)\n- [LangGraph memory concepts](https://docs.langchain.com/oss/python/concepts/memory)\n\nThese are architectural references, not dependencies on a vendor's internal team implementation. code-flow owns the cross-provider protocol and validates adapter capabilities in its own environment.\n\n## Revision 3 implementation decisions\n\n- New Auto runs default to the event scheduler; saved runs lacking `eventDriven` retain their wave schemas, ordering and operation identities. Strict wire schemas satisfy Codex requirements; internal typed dependency maps are normalized from closed wire arrays.\n- Live tool calls return after durable acceptance. They do not hold a worker open waiting for a peer. A steering receipt proves protocol acceptance only; messages keep their original IDs and agent origin in live and next-turn paths.\n- A user changing the goal chooses **Revise goal**. The old execution is stopped and awaited, its branches remain available, and a linked run plans the revised requirements from the integration branch with a fresh explicit budget and configured approval. Normal member notes cannot rewrite the runtime's accepted requirement set.\n- Parent attachments, source trust and lockdown survive whole-team continuation. Reading private memory cannot silently publish it through live memory tools; provenance references are persisted separately from erasable bodies.\n- User memory feedback affects relevance for the exact revision, never trust. Synthetic seeded-memory evaluation measures retrieval plumbing; it does not claim human-rated usefulness.\n- Supervisor retains its public ID and saved-run behavior through shared decomposition/integration helpers. Other manual patterns retain their established behavior; moving them to Auto is not necessary to complete this plan.\n\n[Documentation library](README.md) · [Development guide](architecture.md)\n\n## Model discovery and worker routing — implemented 2026-10-21\n\n- New Auto executions use a persistent, validated catalog. Existing saved runs without `modelRouting` and other workflows retain legacy model selection.\n- Claude control initialization and Codex app-server model discovery resolve exact IDs, aliases and per-model efforts without an inference prompt. Antigravity `agy models` supplies reported IDs; per-model effort support remains unknown. Remote probes optionally return catalogs from that worker/profile; containers discover in their selected image. Host catalogs never establish remote compatibility.\n- Catalog records separate CLI reports, documented facts, environment identity and bounded measured observations. Immutable discovery snapshots support step provenance. Account changes discard prior observations/documentation. Concurrent cache reads coalesce; observations merge without dropping concurrent records. Invalid proposals fail closed.\n- Optional official-document research uses origin/redirect/size/time limits and a closed schema. No page text becomes an instruction. Research cannot change permissions, establish account access or promote compatibility. Offline mode keeps the last verified evidence; missing information remains unknown.\n- Coordinator identity/settings remain fixed. Worker pool and Advanced role pins constrain model/effort proposals. Every new worker execution checks the accepted model, effort, current environment, inherited run permissions and existing budgets. Recovery preserves decisions and rechecks before execution. Existing bounded member replacement supplies checkpoint-based reassignment without cross-provider session transfer.\n- UI exposes automatic selection, catalog refresh/research opt-in, stale/unknown/unsupported states and per-task/member assignment rationale/provenance. More sophisticated performance ranking is deferred.\n- Validation adds mocked catalog/discovery/research/cache/offline/effort tests, routing/provenance assertions, thread-start/resume policy tests and browser UI/API coverage. Real-provider findings are recorded separately in `docs/worker-permission-verification.md`; mock passing tests are not real-provider certification.\n\n### Native-control gaps found by live verification\n\nCodex 0.162.1 can still expose model-driven v2 delegation when only `features.multi_agent=false` and `features.multi_agent_v2=false` are set. Managed turns now also set `agents.enabled=false`, explicitly disable the structured v2 setting and hooks, and apply overrides to both process and thread start/resume configuration. Managed Codex requires at least the tested CLI version for this control. A resumed session at Ultra effort was probed after the fix.\n\nClaude managed turns explicitly disable workflows, Ultracode and hooks, deny native delegation/scheduling tools, and use strict scoped MCP plus `--no-chrome` when external tools are forbidden. Help capture uses a private regular file because the installed Claude CLI truncated piped help and Antigravity prints help on stderr. Antigravity 1.4.4 remains blocked for managed runs; discovery alone does not establish equivalent permission enforcement.\n\nThe Antigravity follow-up tested a real custom-agent tool allowlist with `gemini-3.8-flash-low`. Fixture reads succeeded and omitted terminal/write tools were not called, but this does not establish per-run control over shared permissions, hooks, plugins, browser/MCP or recovery. `init.tools` advertised tools outside the observed model allowlist and cannot attest enforcement. Managed rejection now has one shared diagnostic, and help-only sandbox evidence is marked unknown. Tests cover trust levels, Solo, fresh/resumed turns, container/remote selection and the legacy skip-permissions environment override. Enabling Antigravity requires a verified scoped policy API or a separately designed isolated executor with protected policy and mediation of every tool surface; global settings mutation, prompts and worktree reverts are not acceptable substitutes. See [the verification report](worker-permission-verification.md#antigravity-auto-feasibility-follow-up).\n\nAn isolated-execution feasibility prototype now exercises a clean home and protected policy under macOS Seatbelt. Actual fixture tests passed for workspace writes and denials of policy tampering, outside paths, ambient workspace hooks and terminal TCP access. Antigravity 1.4.4 starts under that policy; authenticated discovery/inference does not work in the prototype. A separate clean-home discovery requires sign-in, and the installed Docker daemon was unavailable after a startup attempt. Keep this code in opt-in research probes, with no production eligibility receipt or fallback. Remaining work is a separately authenticated runtime, separated inference/command networking, protected customization mounts, and provider-level forbidden-tool/recovery tests. Full Auto support remains **unimplemented and blocked**, not certified by the OS fixture checks. See [isolated execution findings](worker-permission-verification.md#isolated-execution-investigation).\n\n\n### Antigravity existing-login follow-up (2026-10-21)\n\nThe earlier isolated-execution blocker is resolved for a narrow local macOS executor with CLI 1.4.4. It reuses the native Keychain login without exporting credentials, and grants only the OS security metadata/cache-lock access needed by the official CLI/helper. Protected per-turn settings and a file-only custom agent exclude terminal, browser, MCP, hooks and native delegation. Workspace writes remain mode-dependent; authentication/runtime paths are also denied to model file tools. Fresh project records are seeded before configuration is sealed.\n\nAuto inventory exposes this tool limitation; runtime preflight is repeated before launch. Unsupported platforms, remote/container execution, interactive approvals and other CLI versions fail closed. Legacy skip-permissions flags cannot affect managed turns. The coordinator's selected provider/model remains unchanged. Isolated discovery uses a separate catalog environment; assignments retain its provenance, and steps record `macos-file-only-v1`. Native sessions are never retained or transferred; recovery uses portable context and revalidates eligibility. Native `manage_task` bookkeeping may remain available, but delegation tools do not.\n\nReal existing-login discovery returned six candidates. Real file-editing, structured-output and fresh read-only adapter turns succeeded; fixture escape/policy denials, absent hook markers and zero browser requests were observed. A real Auto run with Codex coordinating and Antigravity editing completed with a passed check/review verdict, five completed steps and no failures. Tests cover protected OS profiles, environment scrubbing, forged/unsupported launches, separate catalog caching and mocked Auto dispatch with fresh workers. Full general-purpose Antigravity execution remains outside this implementation: inference networking stays enabled and Code Flow runs checks separately. Other platforms/models/efforts and arbitrary tool-call fuzzing remain unverified. See [the latest verification report](worker-permission-verification.md#existing-login-reuse-and-managed-file-workers).\n\n### Antigravity permission/recovery review (2026-10-21)\n\nImplemented sandboxed version inspection, tracked discovery process groups, nested workspace customization protection, safe native-auth error classification/output, unexpected-tool failure detection, and guaranteed cleanup after initial callback failure. Explicit recovery rechecks login through fresh isolated discovery before clearing signed-out status; cached/offline results cannot clear it. Auto can retain an unavailable idle member without authorizing additional work, allowing bounded workflow stop and later recovery. Deterministic stop/reload/repair/resume tests preserve model pins, recorded provenance, partial checkpoints and inherited permissions. Real existing-login adapter probes passed with the strengthened profile; forced expired-token refresh remains unverified and never triggers an authority-expanding fallback. See [the review evidence and limitations](worker-permission-verification.md#antigravity-permission-and-recovery-review-2026-10-21).\n\n### Non-destructive refresh evidence (2026-10-21)\n\nAdded an opt-in native refresh observer that uses ordinary isolated discovery and reports only bounded metadata. Valid-token login reuse is distinct from observed refresh, and credential persistence remains separately unverified. No documented force-refresh interface was found in CLI 1.4.4. Real probes reported six models and an unexpired token, so expired-token renewal remains pending natural expiry. Added detection of native credential-save/fallback failures in managed turns and discovery, including failure despite a successful inference result. No permission widening, credential export, forced expiry, account mutation or logout is used. See [the investigation](worker-permission-verification.md#non-destructive-native-refresh-investigation-2026-10-21).\n\n\n### Explicit native Antigravity execution (implemented)\n\n- Add run authority `antigravityExecution: \"native\" | \"isolated\"`, defaulting to isolation when absent. Native is an explicit Advanced opt-in, never a model/template decision. Parent continuations cannot upgrade isolated authority.\n- Resolve native Antigravity to a truthful unrestricted receipt with provider-configured external tools and native delegation. Other providers retain ordinary inherited permissions. Reject incompatible mandatory requirements, Safe/narrowed trust, approvals, remote/container and unattended contexts.\n- Execute against the existing host profile and saved login, without sandbox flags or credential copies. Retain fresh portable turns, tracked process lifecycle and private authentication diagnostics. Keep native and isolated catalog identities separate and recover only with fresh discovery in the recorded mode.\n- Verified a real mixed-provider Auto run: Codex coordinator, native Antigravity file edit and terminal check, independent Codex review, configured checks passed. No new sign-in requested. Native discovery reported six models. Expired-token refresh and browser/MCP/hooks/native-child behavior were not exercised.\n- Deterministic tests cover opt-in receipts, forged/mismatched options, unchanged other-provider permissions, incompatible constraints, parent narrowing, private auth persistence failures, both catalog modes, and recovery with portable work and preserved assignment provenance.\n\n### Main integration verification\n\nAutomatic role defaults now require verified Antigravity managed-runtime availability, separately from CLI flag compatibility. Unsupported isolated reviewers are rejected before a local run starts; explicit native opt-in retains its separate eligibility. Remote execution uses the remote inventory and dispatch checks rather than host runtime evidence. Auto’s free-model picker uses the same discovered catalog as its model controls. Editable retries and project drafts preserve recorded permission requirements and the explicit Antigravity mode.\n","title":"Dynamic teams, workflows, messaging, and memory"},{"group":"Engineering records","id":"dynamic-teams-implementation-review-1","source":"docs/dynamic-teams-implementation-review-1.md","body":"# Review 1: the dynamic teams implementation\n\n> Engineering record. Status, measurements and decisions below describe the recorded implementation or review. For current usage, start with the [documentation library](README.md).\n\nReviewed: commits `2cf0736` (Add dynamic agent teams with durable collaboration and scoped memory) and `dfd752d`, on top of `8cb3641`, together with the evidence document `dynamic-teams-evaluation.md`. About 8,600 lines across 80 files: the Auto team pattern and plan model, the event scheduler, the mailbox, the memory store, the live tool bridge and its adapters, the evaluation runner and rollout store, the UI, and the documentation.\n\nMethod: four independent read-throughs (runtime and scheduler; memory store and trust boundary; live bridge, adapters and credentials; evaluation, rollout and documentation claims) plus a UI pass. Suspected defects were reproduced with scripts or tests before they were counted. The evidence JSON was checked number by number against the document. Everything marked **fixed** below was changed in this review and is covered by a test; everything marked **open** is not.\n\nBaseline before this review: typecheck clean, 416 of 416 unit tests passing, 17 browser tests passing. After: 420 of 420 unit tests, both browser suites, typecheck and the UI build pass. After merging `main` (the side panel, PR 13) and the fixes from the Codex review of PR 14: 475 of 475 unit tests, 19 browser tests.\n\n## Verdict\n\nThe implementation is more careful than most: identity, authority and provenance are derived by the server and never from model output; control state is journaled before it is acted on; schemas are closed; the test suite exercises crashes, replays and spoofing rather than object shapes. The trust boundary around project memory holds. Nothing reviewed lets an agent accept its own knowledge, borrow another member's session, or reach the application token.\n\nIts failures are of calibration, not of design: several safeguards are sized wrong. A repository without a test command could never finish a run; deleting one memory erased every record of every run that had merely seen it; one shared cap let a looping member fill the memory store for every project; every live tool call rebuilt the whole mailbox from scratch; the provider inventory re-probed the CLIs on every scheduler event. All of these are fixed here. The evidence document is honest in tone but its numbers come from an uncommitted tree, its real trials overlapped in time, and its \"rollout gate\" gates a label rather than the feature. Those are now disclosed in the documents themselves.\n\nKeep Auto team experimental and opt-in, as the evidence document already decides. Re-run the real trials from the committed runner before any beta claim.\n\n## Fixed in this review\n\n### Runtime and scheduling\n\n- **A repository without a runnable check could never finish.** The final verification treated \"no configured check\" as an unresolved gap, so the team ended blocked, the run stopped with \"unresolved work\", and every Resume spent a review invocation and blocked again. Now a complete, independently reviewed result in such a repository finishes as `completed` with `verification.check: \"unknown\"`, no pass/fail verdict, and nothing accepted into team or project memory. The UI shows \"Reviewed · no check ran\" rather than \"Verified\". `src/patterns/auto-team.ts`, `ui/team-state.ts`, `ui/team.tsx`, test \"Auto team without a runnable check finishes reviewed but unverified\".\n- **Provider inventory probed the CLIs on every scheduler event.** `teamInventory()` ran `codex debug models` and `agy models` (a network fetch, 45 s timeout) at every wave and journaled the catalogs each time. Later observations now reuse the planning catalogs and refresh only availability and quota. `src/engine.ts`, `src/patterns/auto-team.ts`.\n- **A no-op coordinator revision was asked again until the coordination budget ran out.** A revision that adds, reassigns, retries, answers or contracts nothing now ends the wave loop with the unresolved work listed. Test \"Auto team stops asking the coordinator once a revision changes nothing\".\n- **User answers and teammate messages shared one JSON block** labelled \"agent data unless origin=user\". They are now separate prompt sections, the user's answers marked authoritative for the task and teammates' messages marked as evidence. The member's own session handle and private checkpoint no longer appear in the assignment JSON. `src/patterns/auto-team.ts`.\n\n### Memory store\n\n- **Deleting one memory cascaded through context references.** `derivedFrom` records what was in a member's context, not text copied from it, so deleting a widely selected convention erased every checkpoint, contract and finding of every run that saw it, including user-accepted entries derived from them, and any run still citing it failed on its next write (\"Memory source was deleted\"). Deletion now redacts only the target; references to a tombstone inherit its origins instead of throwing. Reproduced before the fix with a scratch script. `src/memory.ts`, test \"deletion erases the entry's body; entries that saw it keep theirs\".\n- **One global cap of 5,000 entries across all scopes and repositories**, with private and team records never retired and no API to delete them. A few hundred runs, or one looping member, would make every Auto run fail at plan time with \"Memory limit reached\". The project cap now counts project entries only; private and team records are capped at 512 per run; archiving a run with cleanup retires its private and team records. The store's own load-time validation used the same global count and would have refused to open the file. `src/memory.ts`, `src/engine.ts` (`archive`).\n- **Run-wide provenance unions overflowed the 128-reference limit** and the overflow was fatal after verification had succeeded. Writes now cite the journaled selection of the task that produced them (stable across replays, which also removes a crash-idempotency hole in the pattern path), and the limit is 1,024. `src/patterns/auto-team.ts`, `src/team-memory.ts`, `src/engine.ts`.\n- **A user's edit silently accepted a proposal**, including a tags-only edit of an untrusted one, contradicting \"the user must approve the exact revision\". An edit now keeps a proposal a proposal unless the request says `accept: true`; an accepted entry the user edits is recorded as accepted by the user. `src/memory.ts`, `src/server.ts`, test \"a user's edit keeps a proposal a proposal unless they accept it\".\n- Storage failures were returned as HTTP 400; they are 500 now. The unused `memoryPrompt()` helper, which rendered bodies unescaped into Markdown headings, is removed.\n\n### Live tool bridge and adapters\n\n- **Every tool call rebuilt the mailbox overlay with one deep clone per pending command.** Measured: 1,000 pending commands cost 1.8 s per call, 4,096 cost 36 s, all synchronous on the main thread and serialized ahead of every other member's calls and the scheduler's own drains. The overlay now clones once and applies commands in place, and each member turn may make at most 256 tool calls. `src/team-mailbox.ts` (`applyMailboxCommandInPlace`), `src/engine.ts`.\n- The client answered over-limit requests with `id: null`, leaving the CLI's request waiting until its own tool timeout; it now answers with the request's id. The credential file is deleted as soon as the client has read it. `src/team-tool-client.mjs`.\n- The capability TTL was clamped to 2 hours while a step may run 24; a failed listen was cached forever; the bridge was never closed at shutdown; an inactive grant answered 400 instead of 403. `src/team-tools.ts`, `src/server.ts`.\n\n### UI\n\n- The run view's event sequence numbers (the manager's global counter) and the sidebar feed's per-start counter were gated through one map, so a run you had opened stopped updating in the sidebar and never announced that it needed you until a reload. The side panel change merged to `main` as PR 13 removed the run view's pushes into the list altogether: the list is the global feed's. The merge keeps that, so the run view's stream, including its hello snapshot, no longer touches the list. `ui/run.tsx`, `ui/state.ts`.\n- \"Waiting for you\" was shown for a team question regardless of run status, although the server accepts answers only while the run is running, and a crash left the flag set. The server clears it when it marks a run interrupted; the UI, phone view and inbox show it only for a running run, with the Resume instruction otherwise. `src/engine.ts` (`load`), `ui/team-state.ts` (`teamAwaitingAnswer`), `ui/run.tsx`, `ui/rail.tsx`, `ui/automations.tsx`, `src/remote.ts`.\n- The \"approve the plan\" preference was one shared key, so unticking it once for Plan → Build → Review dispatched Auto teams without their approval checkpoint. It is now remembered per pattern; the old shared choice still applies to manual patterns. `ui/state.ts`, `ui/composer.tsx`.\n- The open user question is now a card under the run header, like plan approval, instead of the last item in the message list. `ui/team.tsx` (`TeamQuestionCard`), `ui/run.tsx`.\n- The check-command detection could land in the composer field after the user had started typing into it (the browser test recorded `npm testnpm test` as the check); a late detection result now fills only a field nobody has touched. `ui/state.ts` (`loadCheck`).\n- Measurements polled the evaluation endpoint every 5 s while collapsed, which rebuilt the report from the whole journal each time; they load only while open. Delete in the memory panel is a two-step inline confirmation. The composer refuses an Auto team with fewer than 4 steps or an empty provider pool before the run exists. An invalid follow-up budget is explained next to the composer instead of only inside the collapsed budget disclosure; \"Revise goal\" is hidden during plan approval; the answer box has a visible label. The hello event of a run stream now always carries the snapshot.\n\n### Evaluation and runner\n\n- `repairTurns` counted any step whose title matched `/repair|fix/`, including model-authored task titles; it now counts engine-titled steps only. `reportedTokens` omitted cache-read tokens. The \"avoidable barrier\" label is \"dispatch delay after an answer\": every trial ran event-driven, so there was no barrier to avoid.\n- The rollout decision assessed only the reports a caller cited, so a caller could cherry-pick verified pairs to reach \"default\". Eligibility is now judged over every real report in the store. `src/team-evaluation.ts`.\n- The runner created an empty temporary directory on every invocation even with `--out`; `--real` now requires `--confirm` and prints the maximum invocation count; the trial settings that differ from app defaults (approval off, `maxRepairs: 1`, `maxSubtasks: 2`, provider-default models) are written to `configuration.json`. `scripts/` is now typechecked. `scripts/evaluate-teams.ts`, `tsconfig.json`.\n\n### Documents\n\n- README no longer headlines an experimental feature with one verified real run out of three as the product's primary flow; Auto team is presented as experimental and opt-in.\n- `dynamic-teams-evaluation.md` now states where its numbers come from (an uncommitted tree, an older runner, source runs not committed), that the real trials overlapped in time, what the dispatch-delay figure is, and that the rollout stage does not gate availability. Test counts are reconciled (397 at trial time, 416 after the delivery review, 420 now).\n- `dynamic-teams-implementation-plan.md`: \"validates the scheduling benefit\" is corrected to \"demonstrates the scheduling order\"; three measurement rows marked \"Captured\" are now \"Partly\" or \"Not captured\"; the rollout decision is described as a label.\n- `auto-team.md`, `security.md`, `configuration.md`: `--strict-mcp-config` disables the user's and the repository's MCP servers in Claude team turns; members run as one user and the credential file is readable in the window before the client deletes it; deletion and edit semantics as implemented; the per-turn tool budget and per-run memory cap; the stage is informational.\n\n## Open findings, ranked\n\n1. **The real evidence is not reproducible and must be regenerated.** The committed runner, a committed fixture task and recorded model identities are the minimum for a number anyone can check. Until then the evidence document supports exactly one claim: the feature runs end to end on real CLIs. Run `npm run evaluate:teams -- --real --confirm` from the committed tree, sequentially, and replace the JSON.\n2. **The rollout stage does not change behaviour.** Either make `beta`/`default` affect availability and the composer (and keep `CODEFLOW_AUTO_TEAM_ENABLED` as the kill switch), or rename the feature \"rollout record\". A gate that gates nothing invites the wrong conclusion.\n3. **Coordination budget is tight by construction.** `min(4, floor(limit / 4))` coordinator calls include initial planning and every approval revision; two \"revise\" clicks at approval leave one call for the whole run. Consider `min(8, ceil(limit / 4))` or counting approval-driven replanning separately, then measure.\n4. **Steering race.** Fixed after the Codex review of PR 14. A Codex `turn/steer` that had not answered by the 2 s deadline was journaled as not accepted, so a message Codex did take was shown again in the next context. The deadline now records an unknown outcome; the CLI's answer is journaled when it comes and settles the outcome at the next drain; the message shows as *steering* meanwhile. An answer that never comes (a crash) leaves the message available to the next context under the same ID, as an interrupted acknowledgement always did. `src/engine.ts` (`steerTeamMessages`), the late-acceptance case of the steering test.\n5. **Capability probes reran on every turn after an unknown result.** Fixed after the Codex review of PR 14. An unknown result (a CLI missing or not signed in, an older Codex refusing `codex mcp get`, a probe that timed out) was evicted from the cache at once, so every Auto turn paid the serial probe stages again. It is now kept for two minutes, against ten for a settled result. The cache key still embeds a test hook. `src/adapters/team-tools.ts`, test \"an unknown capability probe is not repeated on the next turn\".\n6. **Bridge handler errors are returned verbatim** (file paths, git errors). Whitelist domain errors as 400; everything else 500 with a generic message, logged server side. `src/team-tools.ts`.\n7. **Git fan-out per prompt**: up to 128 `merge-base --is-ancestor` calls per selection, run twice per `projectMemory()`. Memoize `(commit, head)` per run and build the query once. `src/team-memory.ts`.\n8. **Memory editor after a 409** keeps the stale revision; the only way out discards the draft. Refetch, rebase the revision, keep the typed fields. `ui/memory.tsx`.\n9. **Reconnect handling is tested only through a pure helper** with a scenario the server cannot produce. Add a server test for `Last-Event-ID` beyond `newest` and a client test for the three hello cases. `test/server-reliability.test.ts`, `test/team-ui.test.ts`.\n10. **`read_memory` results are frozen per attempt and arguments** (intentional for provenance); new team proposals are invisible to an identical later call in the same attempt. Say so in the tool description.\n11. **Answering a question after a restart requires Resume first.** The UI now says so; a better flow would accept the answer while interrupted and deliver it on Resume.\n12. **The team view is a dashboard at the top of the thread.** The question card is now under the header; the remaining roster, workflow, coverage, messages and measurements should fold into one disclosure outside plan approval, with measurements and capabilities living in Insights.\n\n## What to avoid next time\n\n- A safeguard whose blast radius is \"everything that touched it\": the deletion cascade over context references, the global memory cap across scopes and repositories, and the overlay rebuilt from scratch per call all protected one thing by endangering everything around it. Size a safeguard to the record it protects.\n- A gap nobody can close. If the team can do nothing about a condition (no test command, no reviewer quota), it must not become a blocking gap that every Resume pays to rediscover.\n- Probing external processes inside a loop. Catalog and version probes belong at plan time; a scheduler event should be a function of journaled state.\n- One JSON blob for text of different authority. User answers and teammate messages, or a member's private checkpoint and its public record, must not share a section.\n- A single preference key across patterns when one pattern makes the preference a safety control.\n- Evidence written from an uncommitted tree, trials run concurrently and called sequential, counts (\"11 focused tests\") that match no file, and estimates labelled as measurements (\"avoidable barrier\", \"repair turns\" by regex). The evidence document was candid everywhere except in these specifics, and those are what a reader checks first.\n- Calling a recorded label a gate. If a decision does not change what the software does, do not describe it as controlling rollout.\n- Headlining an experimental feature in the README before its own evidence supports it.\n\n## Suggestions\n\n- Treat the three memory scopes differently in storage: project knowledge in the curated store; private checkpoints and team records in the run's own journal directory, deleted with the run. Most of the cap, GC and cascade problems disappear with that split.\n- Replace the per-event coordinator wake with a per-wave decision unless a measurement shows per-event decisions change outcomes; the fixture data so far shows no peer questions in real runs at all.\n- Record model identity per turn in the evaluation report and pin it in the runner; otherwise two trials are not comparable even on the same day.\n- Add a `check` detection step to the Auto plan itself: when no check command exists, ask the coordinator to propose one as an investigation task, so verification becomes possible rather than unknown.\n- The plan document has become a hybrid of history and status. Move the status table and the storage decision into the evidence document, and keep the plan as the design record it was.\n\n## Verification performed\n\n| Check | Result |\n|---|---|\n| `npm run typecheck` | passes (now includes `scripts/`) |\n| `npm test` | 420 passed, 0 failed (416 before; 4 added) |\n| `npm run test:e2e` (`auto-team.e2e.ts`, `ui.e2e.ts`) | passes after the UI changes |\n| `npm run build` | passes |\n| `git diff --check` | clean |\n| Memory cascade, cap and reference-limit reproductions | executed before the fixes; covered by tests after |\n| Overlay cost benchmark | executed before the fix (36 s per call at 4,096 pending commands) |\n| Real CLI probes (`claude --help`, `codex mcp get`, `codex features list`) | executed against the installed CLIs; claims in `auto-team.md` about them hold |\n\n[Documentation library](README.md) · [Development guide](architecture.md)\n","title":"Review 1: the dynamic teams implementation"},{"group":"Engineering records","id":"worker-permission-verification","source":"docs/worker-permission-verification.md","body":"# Live worker permission verification\n\nTested locally on macOS, 2026-10-21, using temporary Git fixtures and the user's existing CLI authentication. No credentials were read/exported and no global provider settings were changed. These are bounded observations of these versions/models, not certification of every model, OS, container or remote worker.\n\n| Provider | Version/model | Observed result |\n| --- | --- | --- |\n| Codex | CLI 0.162.1, `gpt-6.1-sol`, low effort | In-workspace edit and terminal write succeeded. A sibling fixture write and localhost HTTP request from the terminal returned `EPERM`; the outside sentinel stayed unchanged and server received zero requests. |\n| Codex | Same CLI/model, app-server | Scoped `codeflow_team.read_inbox` reached only the issued local fixture capability and returned `LOCAL_MCP_FIXTURE`. |\n| Codex | Same CLI/model, browser | A new browser tab read `LOCAL_BROWSER_FIXTURE` from the local HTTP server and was closed. This worked with terminal network denied, demonstrating the separate browser permission surface. Existing tabs were not inspected. |\n| Codex | Fresh low-effort and resumed Ultra session | Before the fix, native spawn succeeded despite both feature flags being false. After adding `agents.enabled=false` and explicit v2/thread controls, native spawn was unavailable. The resumed test reused one of the earlier fixture sessions; no cross-provider session was transferred. |\n| Claude | CLI 2.1.306, `claude-sonnet-5-5`, low effort | Safe profile allowed a fixture file edit and denied the one requested Node terminal command with “requires approval.” No outside write or network request occurred. This is tool-rule enforcement, not an OS sandbox. |\n| Claude | Same CLI/model, external tools disabled | Native web/browser and external MCP were absent in the observed tool surface. After repairing truncated CLI help detection, the explicitly granted local `codeflow_team` MCP fixture remained callable and returned its marker. No external website or third-party MCP was called. |\n| Antigravity | CLI 1.4.4 | `agy models` returned six candidates. Help exposes sandbox and persistent MCP management, but no verified scoped MCP/native-delegation controls. A managed adapter call was rejected before inference. Legacy sandbox execution and external integrations were not certified. |\n\n## Fixes and boundaries\n\nCode Flow now passes Codex delegation, hook and sandbox overrides in both the CLI arguments and thread start/resume configuration. Feature-list output alone had incorrectly suggested that delegation was disabled: model-driven v2 required the additional `agents.enabled` control. Claude managed settings disable workflows, Ultracode and hooks and deny native delegation/scheduling tools. Regular-file help capture avoids truncated piped output and handles Antigravity's stderr help.\n\nMandatory workspace/terminal-network boundaries currently require a compatible local Codex environment. Claude without a container provides tool permissions, not filesystem isolation. Worktrees do not prevent reads outside their directory. With external tools left provider-configured, browser and MCP access continue to follow provider configuration. The adapter rejects mandatory external-tool isolation for Codex instead of claiming its terminal sandbox covers browser/MCP. Antigravity now has a local macOS file-only managed executor; see the latest follow-up below. Other environments remain unsupported.\n\nCLI discovery does not verify every listed model's account entitlement. Real inference observations above apply only to the named models. Remote/container controls and all other model/effort combinations remain unverified. The later Antigravity section records its separately tested local executor. Mock tests cover launch flags, policy intersection, recovery and fail-closed behavior separately.\n\nOfficial references: [Codex configuration](https://learn.chatgpt.com/docs/config-file/config-reference), [Codex app-server](https://learn.chatgpt.com/docs/app-server), [Claude workflows](https://code.claude.com/docs/en/workflows), [Antigravity CLI reference](https://www.antigravity.google/docs/cli/reference/).\n\n\nRepeatable opt-in probes are in `scripts/live-permission-probes/`; their README explains fixture creation, provider usage and the bounded checks. Runtime bookkeeping is isolated from the application server. The validated catalog service was also exercised against the installed CLIs: 12 Claude models (11 reporting effort controls), seven Codex models, and six Antigravity models (per-model effort support unknown).\n\n## Antigravity Auto feasibility follow-up\n\nOn 2026-10-21, two additional real `agy` 1.4.4 turns used `gemini-3.8-flash-low`, `--sandbox`, `--disable-slash-commands` and a workspace custom agent allowing only `view_file`, with `subagent: false` and `commandExecutionPolicy: off`. Neither turn used `--dangerously-skip-permissions`.\n\nBoth turns read the fixture successfully. In the second, the model was asked to attempt a fixture write and a harmless terminal command **only if those tools were offered**. It reported them unavailable; only `view_file` calls were observed and no write marker appeared. This is evidence that a custom-agent allowlist narrowed the observed model surface. It is not a forced-call rejection test. The `init.tools` event still listed terminal, write, browser, MCP, workflow and native delegation tools; that event is therefore not an attestation of the effective allowlist. A fixture SessionStart hook did not produce its marker; this does not establish that all hooks are disabled.\n\nThe current supported CLI interface does not provide verified per-run settings isolation, hook suppression, or scoped MCP configuration. Documented permissions live in shared settings; allowed paths, domains and `unsandboxed` rules can change the terminal sandbox's effective boundary. Hooks may come from workspace, global settings and plugins. A custom-agent file cannot establish that these independent surfaces obey the run policy. See [CLI permissions](https://www.antigravity.google/docs/permissions), [terminal sandbox](https://www.antigravity.google/docs/sandbox), [custom agents](https://www.antigravity.google/docs/subagents/) and [hooks](https://www.antigravity.google/docs/hooks/).\n\n**Decision: keep managed Antigravity unavailable.** The adapter and routing now share an actionable diagnostic, and capability reporting labels its sandbox enforcement unknown rather than tested merely because help exists. Regression tests reject every trust level, read/write turns, Solo, resumed assignments and container/remote choices. The legacy skip-permissions environment setting cannot override that rejection.\n\nEnabling Auto requires either a supported invocation-scoped policy interface or a separately implemented isolated executor. That executor must keep policy/configuration outside worker write access, exclude ambient hooks/plugins/MCP, mediate every exposed tool (including browser and delegation), separate inference connectivity from command networking, and reapply/recheck policy on resume. A container alone does not establish these properties. Temporarily editing global settings would race other sessions; a prompt instruction or post-turn revert is not enforcement. No global settings were modified and no credentials were copied for this investigation.\n\n`scripts/live-permission-probes/antigravity.ts` repeats the bounded custom-agent check. It remains an opt-in research probe, not a supported managed execution path.\n\n## Isolated execution investigation\n\nThe next investigation on 2026-10-21 implemented an **offline macOS containment prototype**, separate from the adapter. `agy-isolation-fixture.ts` creates a fresh home, restrictive settings, empty MCP/hook configuration and a file-reading custom agent. Its OS policy allows workspace and scratch writes, protects the entire home from changes, excludes workspace customizations from reads, and denies network access for the whole process tree. The child environment is constructed from a small allowlist; it does not inherit provider keys, SSH sockets or Code Flow credentials. Standard system file reads and filesystem metadata remain allowed. This is not a complete cross-platform executor.\n\nReal kernel checks passed for a local workspace write and scratch write, and rejected:\n\n- Reading a fixture outside the workspace through a symlink.\n- Reading workspace `.agents/hooks.json`.\n- Changing settings permissions, overwriting settings, or replacing their parent directory.\n- Writing an outside sentinel or connecting to a local TCP server (zero accepted connections).\n\nThe actual Antigravity 1.4.4 executable returned its version inside this boundary. `agy models` exited unsuccessfully there; no inference was attempted. A separate discovery-only check with a fresh home and normal network access returned “Please sign in.” Existing host authentication was not copied. This establishes that starting the binary and enforcing fixture containment are possible, but it does not establish authenticated model access, native tool enforcement, hook suppression by the vendor runtime, or session recovery.\n\nAntigravity's [official authentication documentation](https://www.antigravity.google/docs/cli/install/) describes interactive sign-in and alternative API-key/enterprise ADC paths. Those alternatives are not interchangeable with the user's current subscription and were not enabled as a workaround.\n\nDocker is installed on this machine, but its daemon socket was unavailable before and after a Desktop startup attempt. The ordinary container wrapper is also insufficient by itself: its writable home/auth volume could retain ambient settings, and its network permits inference and shell traffic together. No container compatibility claim was made and no Docker security settings were relaxed.\n\n**At this stage Auto remained blocked (superseded by the login-reuse follow-up below).** To proceed, the isolated executor needs separately provisioned authentication, inference connectivity separated from command networking, and real tests of browser/MCP/delegation denial and fresh/resumed execution. A production container implementation would additionally need immutable policy mounts and a clean workspace customization namespace. The experimental profile deliberately grants no inference network access and cannot be promoted into production by a successful fixture test or a user-supplied readiness flag. The current adapter and recovery gates are unchanged.\n\n\n## Existing login reuse and managed file workers\n\nThe 2026-10-21 follow-up found a working path for **macOS / Antigravity CLI 1.4.4**. A clean `HOME` can reuse the existing login through the native macOS Keychain. Code Flow creates a temporary `Library/Keychains` symlink, grants read-only access to its target, and lets the official CLI call `/usr/bin/security`. It never extracts a password, exports an OAuth token, copies a credential file, or substitutes API-key billing. The CLI also needs reads of macOS security-module metadata and write-data access to the existing `mds.lock` cache lock. Broad cache writes are not granted. Merely linking legacy OAuth files did not authenticate the CLI.\n\n`src/adapters/agy-isolation.ts` now implements the narrowly scoped executor. Each turn gets a fresh private home, an allowlisted environment, immutable provider settings/custom agent, empty MCP/hook configuration and a protected default project record. The macOS policy restricts writes to the workspace (write turns only), runtime state, scratch and the one native cache lock. Policy files and ancestors, workspace customizations, Git metadata and host login files are protected. Only the verified CLI binary and Apple's security helper can execute. File tools are separately denied access to authentication, security-cache and runtime paths, including canonicalized symlink targets. Terminal, browser, MCP, workflow and native agent tools are excluded. The vendor's local `manage_task` bookkeeping tool can still appear; it is not the native delegation tool.\n\nInference networking remains enabled. This is **file-only execution**, not a general-purpose network-denied shell sandbox. Code Flow's check/setup commands retain their separately configured execution environment. System runtime files/metadata remain readable. This is not a complete VM boundary or a claim that every provider has identical isolation.\n\nBounded observations from the real CLI and `gemini-3.8-flash-low`:\n\n- Six model candidates were discovered inside this executor with the existing login.\n- A file-reading/editing turn completed, and its JSON response was parsed through Code Flow's normal structured-result fallback.\n- A later read-only turn read the saved result with fresh provider state. Neither turn retained a native session ID.\n- A synthetic outside-file symlink read failed. A direct write and reads/writes through a symlink into protected profile state matched the provider deny rule; sentinels remained unchanged.\n- The workspace hook marker remained absent. The local browser fixture received zero requests. Terminal/browser/native delegation tools were not used; this is observed behavior plus configured controls, not arbitrary forced-tool-call fuzzing.\n- Deterministic tests exercise both read/write OS profiles, protected files, outside paths, Git metadata, child execution, environment scrubbing, forged policy rejection, unknown CLI versions, native-session rejection, catalog separation and mocked Auto dispatch/recovery.\n- A real end-to-end Auto fixture completed with verdict **passed**: `gpt-6.1-sol`/low coordinated, `gemini-3.8-flash-low` made the requested README edit, and the configured Node check plus independent review completed. Five steps completed with no failed steps. The Antigravity step recorded `os-sandbox`, `macos-file-only-v1`, isolated catalog revision 1 and no native session ID. This verifies that the adapter is reached through actual Auto dispatch, beyond standalone CLI probes.\n\nManaged eligibility now permits this local executor and rejects remote/container execution, interactive approvals and unverified CLI versions. Every launch rechecks the environment; `CODEFLOW_AGY_SKIP_PERMISSIONS` cannot bypass managed controls. Inventory describes the file-only restriction to the coordinator. The effective executor version is recorded on steps, while model assignments retain the isolated catalog revision/environment/provenance. A native session supplied during recovery is rejected as unavailable so the engine rebuilds a fresh turn from portable context. No session is transferred across providers.\n\nThe exact CLI version is deliberately pinned. Updating Antigravity requires re-verifying controls before changing that pin. Windows/Linux, container authentication reuse, other models/efforts and arbitrary forbidden-call injection are unverified. Authentication tests used a valid existing login; expired-login refresh was not tested. Read-only Keychain file rules do not claim to constrain the macOS security service's own credential lifecycle. The old offline prototype and earlier blocked decisions above remain historical investigation evidence, not the current production path.\n\nRepeat the adapter probe with `node --import tsx scripts/live-permission-probes/managed-antigravity.ts`; repeat a real mixed-provider Auto fixture with `node --import tsx scripts/live-permission-probes/managed-antigravity-auto.ts`. Both consume provider usage and touch only synthetic project files. Official control references: [custom agents](https://www.antigravity.google/docs/subagents/) and [permission rules](https://www.antigravity.google/docs/permissions).\n\n## Antigravity permission and recovery review (2026-10-21)\n\nThe review found and fixed these gaps in the managed executor:\n\n- Version inspection previously executed the candidate binary outside isolation. It now runs in a separate default-deny profile without host HOME, credentials or networking. Version and model probes use tracked process groups, so timeout and server recovery cover their children too.\n- Nested workspace `.agents`, `.gemini` and `.git` paths were not protected. Kernel rules now cover nested paths and canonical symlink targets, including workspace names containing regex metacharacters.\n- Native authentication/refresh errors could be classified as ordinary failures, and raw diagnostics could reach run history. Managed output now withholds native diagnostics and buffers response text until success; expired/revoked tokens and locked Keychains produce a safe auth failure. No automatic interactive login, global settings edits, credential export or permissive retry was added.\n- Recovery could retain the signed-out cooldown after the user repaired the login. Explicit Resume now requires fresh isolated discovery before clearing that cooldown. Failed/offline discovery and stale cached models leave it blocked. The original assignments/provenance and partial work remain recorded, and new execution rechecks model eligibility and effective permissions with a fresh provider home.\n- Workflow revisions previously rejected even an unchanged idle member whose login became unavailable. They can now retain that member and stop cleanly; adding, reassigning or retrying work still requires an eligible provider and preserves user pins.\n- Unexpected runtime tool events stop the worker and override a subsequent success result. This is drift detection, **not pre-execution mediation**; protected native policy and the OS sandbox remain the enforcement boundary. Preparation cleanup also runs if the initial event listener throws.\n\nDeterministic tests cover auth-result and diagnostic-only failures, secret-marker non-persistence, fresh-turn recovery, unchanged permission receipts, forbidden tool drift, cleanup, and a full Auto stop/reload/login-repair/resume with preserved partial files and model pins. Actual macOS kernel probes cover nested configuration, policy and authentication writes, outside symlinks and unauthorized executable launch.\n\nThe real CLI 1.4.4 passed the updated adapter read/write probes with `gemini-3.8-flash-low`: the edit and structured result succeeded, the outside read was denied, the outside file stayed unchanged, no hook marker appeared, no browser requests reached the fixture, and no native session was retained. These are bounded existing-login observations. Expired-login refresh is still **not real-provider verified**: tests simulate failures and repair without revoking, replacing or exporting the user's login. The native CLI owns refresh inside the existing boundary; if refresh needs additional access, the turn fails instead of expanding permissions. macOS security-service credential lifecycle access remains a documented trust boundary. See the provider's [locked-Keychain troubleshooting](https://www.antigravity.google/docs/cli/troubleshooting/).\n\nThe updated real mixed-provider Auto probe also completed successfully: Codex coordinated, one isolated Antigravity turn edited the fixture, and independent review plus the check passed (five completed steps, zero failed steps). The worker recorded its catalog revision and `macos-file-only-v1` permissions without retaining a native session.\n\n## Non-destructive native refresh investigation (2026-10-21)\n\nThe [official CLI command reference](https://www.antigravity.google/docs/cli/reference/) and installed 1.4.4 help expose no documented force-refresh command. The [authentication guide](https://www.antigravity.google/docs/install?tab=cli) describes silent native-Keychain reuse and a destructive `/logout`; logout was not used. Inspection of the installed executable's static messages identified native refresh and credential-persistence diagnostics. This inspected the executable, not stored credential values, and does not establish a supported refresh API.\n\nThe new opt-in `scripts/live-permission-probes/antigravity-refresh.ts` runs only a normal `agy models` request in the production read-only sandbox with a fresh provider home. It observes a private native log using bounded, symlink-rejecting reads, emits only a closed set of boolean/status fields, and removes the temporary profile/log afterward. It does not export tokens, read Keychain values directly, change the clock or expiry, revoke credentials, change account settings, intercept TLS, or redirect OAuth endpoints. Normal token renewal is owned by the official CLI and may update its stored tokens; the probe does not promise byte-for-byte unchanged native credential storage.\n\nActual isolated requests returned six model candidates. Native logging explicitly reported a **non-expired loaded token**, with no refresh event, interactive authentication attempt or credential-persistence failure observed. This verifies ordinary login reuse. **Actual expired-token renewal and successful persistence remain unverified.** Repeating valid-token requests is not proof of refresh. The script returns exit code 2 when refresh was not observed, 1 when the native request/authentication failed, and 0 only when a native refresh event and successful discovery were observed. `credentialPersistence: not-attested` remains explicit even then; `expiredTokenRefreshVerified` additionally requires a native expired-at-load observation. Evidence cannot grant runtime permissions or clear a known failure on its own.\n\nThis investigation also found native messages for refresh persistence failure and fallback to credential files that were missing from Code Flow's error handling. Managed inference and isolated discovery now inspect their private native auth logs before accepting success. Known persistence/fallback or interactive-auth diagnostics reject the operation safely, even if inference returned success; raw diagnostics and structured failure payloads are not copied into run history. These are detection checks after native execution, not interception of the macOS security service or a guarantee that the CLI never attempted a fallback write. The existing sandbox and model-tool restrictions remain unchanged. Unreadable/missing/oversized logs are explicitly inconclusive to the refresh probe, never evidence that renewal succeeded.\n\nDeterministic tests cover valid-token reuse versus refresh, persistence/fallback errors, successful-result override, secret-marker non-persistence, bounded/symlink-safe log reads and interactive authentication observation. No live credentials were deliberately expired, replaced, exported or revoked to exercise these cases. Safe completion of expired-token verification requires observing a naturally due refresh or a provider-supported test interface; Code Flow does not bypass that constraint.\n\n\n## Explicit unrestricted native mode (2026-10-21)\n\nUser-authorized `antigravityExecution: \"native\"` reuses the installed CLI's existing host login and normal configuration. It deliberately removes Code Flow's file-only isolation and vendor sandbox for Antigravity, and records `agy-native-v1`, `isolation: none`, full trust, allowed command networking, provider-configured external tools and native delegation. This is an explicit permission exception, not verified sandbox enforcement. Default and historical isolated runs retain their original behavior.\n\nReal acceptance: `node --import tsx scripts/live-permission-probes/managed-antigravity-auto.ts --native` completed with five successful steps, one Antigravity write turn, final verdict `passed`, expected README marker and no native session reuse. The recorded tools included Antigravity `run_command`; the synthetic task requested its terminal test. Codex coordinated and independently reviewed. No login/logout command, credential export, forced expiry, revocation or user configuration edit was performed. Native catalog discovery also returned six models through the existing profile.\n\nBrowser, MCP, hooks and native child agents were not exercised in the live probe. They are not restricted by this mode; native child usage cannot be bounded by Code Flow's roster/invocation budget. Native expired-token refresh remains unverified. Deterministic tests simulate refresh-persistence failures, verify private diagnostic handling and cleanup, and exercise restart/recovery for both native and isolated modes without widening authority.\n\n\n## Antigravity 1.4.5 compatibility (2026-10-22)\n\nThe installed CLI updated to 1.4.5. The explicit supported-version list now includes 1.4.4 and 1.4.5; unknown versions still stop before an isolated launch. Agent mode preserves the Antigravity executor's native-delegation setting instead of applying Claude/Codex conversation permissions to it.\n\nThe production adapter's live write/read probe passed on 1.4.5: a workspace edit and structured answer succeeded, the outside symlink read produced a tool denial, the outside sentinel stayed unchanged, no hook marker appeared, no request reached the browser fixture, and no native session was retained. Kernel fixture tests still cover policy/configuration/authentication-write denials. A real Claude → Codex → Antigravity UI fork also recovered the carried conversation token. These observations retain the existing file-only scope and authentication limitations; they do not certify arbitrary future runtime versions or unrestricted browser/MCP behavior.\n\n## Task-scoped Antigravity tools (2026-10-22, later update)\n\nInteractive Antigravity Agent and Team turns can now use the shared Codeflow browser and computer bridge. Their isolated receipt is `macos-task-tools-v2` with `externalTools: task-scoped`; required `no-external-tools` retains the existing `macos-file-only-v1` profile and exposes no bridge. This supersedes earlier statements that isolated Antigravity is always file-only. Native shell/browser, ambient customizations and provider delegation remain unavailable in the isolated profile.\n\nA fresh private HOME contains sealed settings and a custom agent exposing only the known `codeflow_agent` tool names. HTTP MCP uses a loopback endpoint and an expiring invocation token. Its transport shares the same read/write, task ownership, approval, cancellation and revocation checks as the Claude/Codex stdio bridge. MCP resources are empty; discovery does not grant access to host resources. The native-mode path uses a private configuration overlay and leaves global settings unchanged. No ambient MCP server is imported into the isolated profile.\n\nComputer tools operate outside the workspace and remain gated by Full access for Antigravity, plus macOS Screen Recording and Accessibility permissions. Every action requires a fresh screenshot token and the same foreground app; foreground identity is checked before and after capture. Pixel coordinates remain the default for existing callers. Antigravity explicitly selects the normalized 0–1000 coordinate format, which the host converts to display points. Invocation-private offloaded screenshot files are readable so the model can inspect its tool results; private settings, MCP configuration and authentication data remain protected.\n\nSchema-constrained turns permit Antigravity's built-in `finish` control tool. Only its injected `toolAction` and `toolSummary` metadata are removed when not requested by the schema; other unexpected properties remain subject to validation. Successful browser/computer effects record tool names and timestamps, without arguments, as host evidence. These receipts establish that an action succeeded at the tool boundary, not that the user's task is correct; independent review and configured checks still apply.\n\nReal Antigravity 1.4.5 validation passed the shared browser form, file/folder uploads and authenticated downloads after human/Claude/Codex use. Real Agent and Team computer turns filled and submitted a dedicated native fixture, with exact output verified by Codeflow. Earlier failed foreground/coordinate attempts were retained and corrected without removing the app guard. See `output/agent-mode/agy-browser-structured-final.log`, `agy-transfers-live.log` and `agy-computer-normalized-final.log`, and the detailed validation record in `docs/plans/agent-mode-validation.md`. These are bounded observations of the installed versions, not certification of arbitrary native tools, future versions or expired-token renewal.\n","title":"Live worker permission verification"},{"group":"Engineering records","id":"dynamic-teams-plan-review-0","source":"docs/dynamic-teams-plan-review-0.md","body":"# Review 0: Dynamic teams implementation plan\n\n> Engineering record. Status, measurements and decisions below describe the recorded implementation or review. For current usage, start with the [documentation library](README.md).\n\nReviewed: `docs/dynamic-teams-implementation-plan.md`, uncommitted, dated 17 October 2026, written against `ee246c7` (the current `main`).\n\nLens: the product promise the plan exists to serve.\n\n> Give code-flow a task; it assembles an appropriate team from your available agents, coordinates their work, and delivers a verified result with reusable project knowledge.\n\nMethod: every claim the plan makes about the current code was checked against the working tree, and the four external references were fetched. Findings cite files and functions so they can be re-checked.\n\n## Verdict\n\nThe plan is a careful systems design. Its invariants are the right ones: no silent success, unknown is not a pass, persist before dispatch, at-least-once delivery with idempotent application, agents propose and the runtime verifies. The record model is coherent and the acceptance tests are specific. None of the factual claims about the codebase are wrong.\n\nThe problem is the order. The promise becomes visible to a user in Phase 5 of 8, after a storage migration (P1), a memory subsystem (P2), a new scheduler (P3) and a messaging layer (P4) have each shipped. The plan's own stated reason for a new recovery model is already met by the existing journal, so the biggest early investment rests on a justification that does not hold. The first user-visible milestone the plan names, \"the restart-safe question-and-answer scenario at the end of Phase 4\", is a reliability demo, not the promise.\n\nRecommendation: do not approve the phase order as written. Approve the invariants, the schemas, and the adapter capability inventory. Add a thin vertical slice of the promise on the existing engine before Phase 1, and let what it measures decide the storage, scheduler and messaging investments. Details are in \"Proposed re-sequencing\" below.\n\n## What the plan gets right\n\n- **code-flow owns the team.** This is confirmed by the vendor docs the plan cites: Claude's agent teams do not spawn under `-p`, which is how code-flow runs Claude, and they are experimental with no session resumption for in-process teammates. There is nothing to delegate to.\n- **Member identity separate from provider identity.** `AgentId` is `\"claude\" | \"codex\" | \"agy\"` (`src/adapters/types.ts:9`). `resolveAuto` deduplicates within a role, and `addNote` targets an `AgentId`, so a note to \"codex\" goes to whichever Codex turn starts next (`src/engine.ts:617`). Two Codex workers really are indistinguishable today for notes, usage and Insights.\n- **Verification in the runtime, bound to evidence.** This extends what `check()`, `integrity.ts` and `fixUntilGreen` already do, and keeps the product's distinguishing rule: a result is done when the tests pass, not when an agent says so.\n- **Turn-boundary messaging first, live steering as an enhancement.** Only Codex app-server can take input mid-turn (`turn/steer`); Claude takes its prompt on stdin and agy has no input path. Making correctness independent of live transport is correct.\n- **Candour about unknowns.** The adapter matrix marks untested capabilities unknown, nested vendor subagents are called out as unaccountable, and the plan refuses to estimate dates before the spikes. That is the right posture.\n- **Explicit outcomes with gaps.** \"A gap must never become success merely because all runnable tasks ended\" is the single most important product rule in the document.\n\n## Major findings\n\n### 1. The promise arrives last\n\nPhases 1 to 4 build durable state, memory, a scheduler and mailboxes. A user sees \"describe the task, watch the team work\" only in P5, and only from the normal composer in P7. The \"first collaboration release\" after P4 still requires a hand-configured team and a hand-supplied workflow, which is a worse version of the supervisor pattern that exists today.\n\nThe existing supervisor pattern already does more than half of the promise: a model decomposes the task, writes contracts, assigns workers, workers build in parallel worktrees, the runtime integrates, checks, and gets an independent review, and the user approves the split before anything runs (`src/patterns/supervisor.ts`). The delta to the promise is: the coordinator chooses members and models rather than assigning among a user-picked roster; more than one round; and members that can ask each other questions. The first two are cheap on the existing engine. Only the third needs new machinery, and the plan has not shown how often it is needed.\n\nRecommendation: build the delta first, on the engine that exists, and measure. See \"Proposed re-sequencing\".\n\n### 2. The new runtime's recovery justification is already met\n\nSection 2, point 2 says a dynamic workflow \"must not reconstruct the team by asking the model to plan again\", and presents this as the reason pattern replay is unsuitable. But `TeamContext.remember()` journals the result of every `decide()` and `ask()` under `kind:identity#occurrence` and returns the recorded value on resume (`src/engine.ts:714`). A coordinator's plan produced through `decide()` is replayed, not re-asked. As long as the pattern's control flow is a deterministic function of journaled values, which is the existing contract, a dynamic pattern recovers without re-planning. Claude's own dynamic workflows use exactly this model: replay by prompt equality, with the first differing agent and everything after it re-run.\n\nWhere the journal genuinely falls short is different and should be stated as such: long-lived members with mailboxes that outlive any one pattern pass, and event-driven wakeups that are not expressible as a linear replay. Those are the real reasons to consider a new runtime. Whether they are worth it depends on how often members need to talk, which nobody has measured.\n\n### 3. SQLite is a platform decision presented as a spike\n\nThe repository has five runtime dependencies and no native module. `package.json` says `node >= 20`, and CI runs Node 20 on Linux. `node:sqlite` does not exist on Node 20. That leaves three options, each a real cost: a native dependency with prebuilt binaries and a node-gyp fallback; a WASM build whose durability semantics differ; or dropping Node 20. Phase 0 defers the choice, but every later phase is designed around SQLite semantics (transactions, outbox, leases, migrations), so the \"spike\" is not free to fail.\n\nWhat the plan actually needs is narrower than a database: atomic commit of a small batch of related records, crash-safe recovery, and idempotent operation application. The server is already the single writer (the one-server lock in `src/store.ts`). One fsynced append of a JSON line holding the whole batch is atomic under that constraint, torn lines are already detected and skipped (`test/store-reliability.test.ts`), and in-memory projections rebuilt at load are how runs work now. The journal is already synced and awaited before the pattern moves on (`src/store.ts:167`); only the event log append is best-effort. The plan's phrase \"the current best-effort logging behavior is not sufficient\" is true of the event log and not of the journal, and should say so.\n\nRecommendation: state the required storage properties, not the technology. Default to extending the existing append-only store with batch records and a snapshot, and keep SQLite as the fallback if a measured need appears (query volume, project memory search at scale). If SQLite is chosen anyway, make the Node 20 decision explicitly in P0 rather than letting the driver choice make it.\n\n### 4. The reference designs use simpler persistence than the plan\n\nThe plan cites Claude agent teams and Claude dynamic workflows as references. Both were checked. Agent teams use one JSON file per mailbox and file locking for task claims; dynamic workflows persist per-agent results and replay by prompt equality; neither uses a transactional store, an outbox or leases. They also document limitations the plan should expect to inherit: task status can lag, teammates stop early, the lead can declare done before tasks finish. Those are prompt and policy problems, not storage problems, and the plan's verification-in-runtime stance is the right answer to them. The reference designs therefore support a lighter P1 than the plan proposes.\n\n### 5. \"Appropriate team\" has no definition\n\nThe promise's load-bearing word is \"appropriate\". The plan says routing must stop deduplicating providers and must expose fallback reasons, but it never states the composition policy the coordinator is constrained by. The existing product encodes composition wisdom that should carry over as rules, not be rediscovered by a model:\n\n- A reviewer or judge should be a different provider from the author (\"a good default is to have a different vendor review\", `docs/patterns.md`). The supervisor pattern warns when it cannot do this.\n- Routing skips providers at 90 percent of a usage window or recently limited (`src/routing.ts`), and prefers the provider whose code passes most often in this repo (`src/stats.ts`).\n- A small task uses one worker. The plan says this but gives the coordinator no rule for it.\n\nRecommendation: write the composition policy as validation rules in P0 (minimum and maximum members by task size signal, independence constraints for review, quota and ranking inputs, user-pinned choices), and have the coordinator propose within them. The acceptance tests in P5 then test the policy, not a model's taste.\n\n### 6. The plan-approval checkpoint is missing from the promise's flow\n\n`ctx.approve(\"plan\")` exists and the supervisor pattern uses it to show the split and contracts before any worker starts (`src/engine.ts:595`, `src/patterns/supervisor.ts`). For a product that assembles a team and spends the user's subscription quota, approving the team and plan is the most important control, and it already exists. The plan mentions \"existing review/permission choices\" in P7 and an \"awaiting input\" outcome, but never names the checkpoint: show the proposed members, models, tasks, acceptance criteria and estimated turns, and let the user approve, revise, or reject before dispatch. Section 3.1's \"requirements with stable IDs\" should be the editable list the user approves here, rather than a model-extracted artifact treated as ground truth.\n\n### 7. Project memory has no trust boundary\n\nRuns started from issues, PRs or alerts by people outside the repo are locked down: Safe mode, no `.env`, checks in Docker (`src/engine.ts:2168`). The plan adds `publish_finding` and promotion to project memory, which every later run reads. An untrusted issue that persuades a worker to publish a \"convention\" would then reach every future run in that project. Section 6.3 says memory is \"not an instruction source allowed to override the current user or permissions\", but offers no mechanism.\n\nRecommendation: memory entries carry the originating run's trust level; locked-down runs cannot promote automatically and their proposals wait for the user; retrieval marks entries by provenance; the lockdown rules extend to messages between members in a locked-down run. Add this to P2's acceptance tests. The existing `UntrustedSourceError` and `lockdown` fields are the hooks.\n\n### 8. Coordinator cost is unmeasured until Phase 8\n\nThe product's premise is subscriptions, and the vendor docs the plan cites say teams \"use significantly more tokens\". Each coordinator wake is a CLI session resume with full context. The plan measures \"coordinator overhead\" in P8, after the architecture is fixed. It should be the first number produced, because it decides how event-driven the coordinator can afford to be (per event, per wave, or per phase) and therefore how much of P3 and P4 is needed. A related gap: a long-lived coordinator session grows until the CLI compacts it; the plan's portable checkpoint is the answer but is never connected to the coordinator.\n\n### 9. Memory before execution is the wrong order\n\nP2 builds relevance selection, context budgets, manifests and promotion before P3 produces any workflow data to select from. Relevance cannot be tested without tasks, contracts and findings flowing. Split P2: the parts P3 and P4 need (private checkpoints, accepted team contracts, context manifest) come first; project memory, promotion, retention and deletion come after P5, when there are real findings to promote and the lockdown rule from finding 7 can be tested against real runs.\n\n### 10. The record model is heavier than turn-boundary messaging needs\n\nTwelve records and three stored state machines. At turn boundaries, \"delivery\" is \"included in the prompt of attempt X\", which the context manifest already records; a separate Delivery record only earns its keep with live steering in P6. Member status (idle, running, waiting) is derivable from the member's active attempt and open requests, so storing it invites drift. Message, Request and Delivery can start as one record with a type. Keep the plan's names but defer the splits until a phase needs them.\n\n## Minor findings\n\n- **Budget semantics change silently.** P3 says \"resume alone does not grant an unlimited new budget\". Today resume explicitly grants \"a fresh budget\" (`src/engine.ts:2285`, `docs/runs.md:82`), and that is a documented product decision. Changing it for team runs is defensible; say that legacy and team runs differ, and why.\n- **Verification binding is over-specified.** Binding evidence to commit, contract revision, workflow revision, check definition and environment fingerprint is five dimensions where two (commit SHA and check command, plus setup fingerprint when `.codeflow/config.json` sets one) cover the cases that occur. Start there; add dimensions when a test shows they are needed.\n- **`findPattern` is a small problem.** Four call sites (`src/server.ts:406`, `:956`, `src/engine.ts:1830`, `:2291`), not a broad audit. And an unknown pattern id already makes resume fail safely with \"Unknown pattern\". Recording team runs under an id older code-flow does not know gives P8's \"older executables must not execute team-v1 runs\" for free; say so instead of designing a schema-version rejection.\n- **SSE cursors.** `Last-Event-ID` is honoured only within one server start (`src/server.ts:626`). The plan's persistent cursor requirement is real; note that it benefits legacy runs too and could ship independently.\n- **`CODEFLOW_TOKEN` is already withheld from agents** (`src/env.ts:8`). P6 presents this as a new rule; cite the existing one and extend it to the scoped credential.\n- **Claude's write mode uses an `--allowedTools` allow-list** (`src/adapters/claude.ts:81`). P6's MCP bridge tools must be added to it per trust level; the capability inventory in P0 should include this.\n- **Three new source directories** (`src/team/`, `src/workflows/`, `src/memory/`) in a codebase that is flat apart from `adapters/` and `patterns/`. Not wrong, but the architecture doc's layout table will need the same treatment, and roughly forty new files for a 22k-line codebase is a scale the plan should state plainly.\n- **Mermaid diagram.** `Runtime --> UI` hides that the UI today is driven by the run snapshot plus SSE; the plan's \"projections\" should be additions to that snapshot where possible rather than five new GET routes with their own pagination.\n- **Section 5.1 operations through \"validated turn results first\".** This is the right first transport and is under-sold: it is also the only transport that works identically for all three CLIs, which makes it the one to design the protocol around, with live tools conforming to it rather than the reverse.\n- **\"Dynamic-worker prompt\" in P4** refers to `preamble()` in `src/patterns/prompts.ts:10` (\"nobody can answer questions\"). The Codex adapter has the same assumption when declining input requests (`src/adapters/codex-app-server.ts:242`). Both change together.\n\n## Claims checked against the code\n\n| Plan claim | Status | Evidence |\n|---|---|---|\n| `AgentId` means a provider | Confirmed | `src/adapters/types.ts:9` |\n| Role resolution deduplicates providers | Confirmed | `resolveAuto` in `src/routing.ts`: `[...new Set(chosen)]` |\n| Notes can target a provider | Confirmed | `RunManager.addNote(id, text, agent?: AgentId)`, `takeNotes` |\n| Replay uses operation identities plus occurrence counts | Confirmed | `remember()` keys `kind:identity#n`, `src/engine.ts:714` |\n| Prompt framing says nobody can answer | Confirmed | `src/patterns/prompts.ts:10` |\n| JSONL history, replay journal, server lock in `store.ts` | Confirmed | `src/store.ts:13`, `:167`, `:329` |\n| Journal writes are best-effort | Partly wrong | Journal entries are fsynced and awaited; the event log is best-effort (`writeHealth`) |\n| CI matrix Node 20/22 Linux, 22 macOS | Confirmed | `.github/workflows/ci.yml` |\n| No SQLite today | Confirmed | `package.json`: five runtime deps, none native |\n| Codex live steering exists; others do not | Confirmed | `turn/steer` in `src/adapters/codex-app-server.ts`; Claude prompt on stdin |\n| APIs assume `findPattern` succeeds | Confirmed, small | Four call sites |\n| Projects hold several repos | Confirmed | `Project.repos: string[]` in `src/projects.ts` |\n| Recurring findings threshold | Confirmed | `RECURRING = 2` in `src/learnings.ts` |\n| Four external references | All resolve | Claude workflows, Claude agent teams, Codex subagents, LangGraph memory |\n\n## Proposed re-sequencing\n\n### Slice 0: the promise on the existing engine\n\nAdd one opt-in pattern, \"Auto team\", written against `TeamContext` like every other pattern. No new storage, no mailboxes.\n\n1. **Plan.** The coordinator (user's choice or routed) runs one read-only `decide()` against a `TEAM_PLAN_SCHEMA`: acceptance criteria, members (`id`, role, provider, model, effort), tasks (`id`, instructions, owner, depends-on, owned paths), contracts. The runtime validates it against the composition policy from finding 5: installed providers, quota, member cap, acyclic dependencies, reviewer independence.\n2. **Approve.** `ctx.approve(\"plan\")` shows the team and plan. Revise and reject work as they do in the supervisor pattern.\n3. **Execute in waves.** Ready tasks run with `ctx.parallel()`; each task is an `ask()` with `identity: task.id` so the journal key is stable; each member keeps a `memberId` to session map so two Codex members stay distinct; each task's worktree starts from the integration branch head, which is the plan's \"artifact dependency through integration\".\n4. **Coordinate per wave.** After integrating and checking a wave, the coordinator gets one bounded `decide()` against a `REVISION_SCHEMA`: done, new tasks, reassignments, answers to questions members raised in their structured outcomes. Cap the rounds. This is event-driven coordination at wave granularity.\n5. **Verify.** Independent review by a different provider, bounded repair, final check, `setVerdict` and `setFacts`, result with gaps listed.\n\nMembers ask questions by including them in a structured turn outcome; the owner or coordinator answers in the next wave's prompt. Coarse, but it is turn-boundary messaging, and it produces the number nobody has: how often a member needs an answer before it can continue.\n\nRecovery is the existing journal. Interrupted runs resume without re-planning (finding 2).\n\nWhat Slice 0 proves or refutes, with fake agents in CI and a few real opt-in runs:\n\n- whether a coordinator produces teams a user accepts at the approval step, and how often it is revised;\n- coordinator turns and tokens per task as a fraction of worker spend (finding 8);\n- how often a wave stalls on a question, which decides whether P4 is needed at all or whether wave-granularity is enough;\n- whether two same-provider members cause any confusion beyond notes and Insights, which sizes P1.\n\nExit criteria: fake-agent run of the section 9 scenario minus restart-under-delivery; a user can start it from the composer behind an opt-in flag; measurements recorded in the plan.\n\n### Then\n\n- **P0 in parallel with Slice 0:** schemas (Slice 0 needs them), adapter capability matrix, composition policy, verification policies by task kind.\n- **Storage decision after Slice 0,** from the required properties in finding 3. The likely outcome is batch records on the existing store plus a per-run snapshot. If SQLite wins, make the Node 20 call explicitly.\n- **P3 before P2,** and P2 split as in finding 9.\n- **P4 only if Slice 0 shows waves are not enough.** If a member rarely needs an answer mid-task, the mailbox layer can be a later enhancement rather than a prerequisite for the MVP.\n- **P5 becomes** hardening Slice 0's coordinator (reassignment with handover, member retirement, strategies as reusable sub-plans) rather than building it.\n- **P6, P7, P8** as planned, with finding 7's trust rule in P2's tests and finding 6's checkpoint in P7's composer work.\n\n## Questions to answer before approval\n\n1. What is the composition policy? Write the rules the coordinator must satisfy; they are the product.\n2. What team-messaging frequency would justify P4's mailboxes over wave-boundary answers? Pick the threshold before measuring.\n3. Is dropping Node 20 acceptable if the storage spike points at `node:sqlite`? If not, say now that native and WASM drivers are the only candidates.\n4. Who may promote to project memory from a locked-down run? The answer should be \"nobody, without the user\".\n5. What does the user see and approve before any member is dispatched, and does changing the goal mid-run go through the same checkpoint?\n6. What is the cost ceiling for coordination as a share of a run's agent turns, and what does the coordinator do when it reaches it?\n\n## Notes on the document itself\n\n- Tight, well-structured, and unusually honest about unknowns. The invariants in 3.3, 3.5, 5.2 and 5.3 should be lifted verbatim into the architecture doc when any of this ships.\n- \"Likely files\" per phase is useful. Add \"likely files removed or shrunk\": the plan adds a great deal and retires nothing, and the supervisor pattern in particular should be slated to become a strategy under the new mode rather than a parallel implementation.\n- The `docs/README.md` link added alongside this plan describes it as proposed. Keep that word until Slice 0 has numbers.\n\n[Documentation library](README.md) · [Development guide](architecture.md)\n","title":"Review 0: Dynamic teams implementation plan"},{"group":"Engineering records","id":"project-teams-implementation-plan","source":"docs/project-teams-implementation-plan.md","body":"# Standing project teams implementation plan\n\n> Engineering record. Status, measurements and decisions below describe the recorded implementation or review. For current usage, start with the [documentation library](README.md).\n\nStatus: implemented as an opt-in preview on 19 October 2026, synchronized with `origin/main` at `7933ecf` before PR review. The requirements below retain the design context. The implementation record here and [the operating guide](project-teams.md) describe the implemented interfaces and deliberate initial boundaries. Real-provider usefulness remains an unmeasured rollout gate.\n\n## Implementation record\n\n| Phase | Implemented result |\n|---|---|\n| P0 | Strict mission/report/aggregate contracts, provider eligibility, immutable mission history, private atomic store, operation IDs, repository uniqueness and rolling reservations |\n| P1 | Managed Automations targets, stable scheduled occurrence IDs, shared timer, engine dispatch barrier, durable launch receipt, exact reconciliation and fail-closed unknown launches |\n| P2 | Existing Auto team investigation workers, coordinator report synthesis, independent report review, typed discovery receipts and no-diff preservation |\n| P3 | Deterministic ranking, verified source quotes, deduplication, one implementation child, enforced scope/size/call limits, final-commit checks/review and approval-bound publishing |\n| P4 | Project setup, provider/model choices, existing schedule editor, opportunity actions, run evidence links, inbox summaries, managed automation links and scoped shared stream |\n| P5 | Owner feedback and defer expiry, bounded recent local activity/commit context, unchanged-input detection, notification reuse/deduplication and conservative restore handling |\n| P6 | Fault/restart/scope/budget/privacy/compatibility tests, browser workflow and connection checks, bounded fake/real evaluation runner, documentation and an opt-in release flag |\n\nImplementation choices relative to the proposed design:\n\n- `Trigger.target` identifies the managed team while `Trigger.run` retains a minimal display/routing projection for compatibility with existing automation screens. It contains no duplicated execution configuration. Trigger IDs are deterministic from team IDs.\n- The aggregate service is in `src/project-teams.ts`, scope guards in `src/project-team-policy.ts`, and the UI in `ui/project-teams.tsx`; separate context/discovery/state modules were unnecessary for the current bounded workflow.\n- Report synthesis is an explicit coordinator read turn followed by the existing independent reviewer. Discovery therefore requires at least eight allocated calls under Auto team's coordinator allowance. Report defects stop the cycle; an owner can start another bounded discovery rather than silently repair it.\n- Pausing between stages finishes discovery as proposals. After activation the owner can build a proposal explicitly. A changed base/configuration requires rediscovery; the initial preview does not attempt an automatic rebase or extra revalidation allocation.\n- Owner revision launches a new whole team from the pinned base, with previous-result references and feedback. It preserves scope and independent verification without relying on the generic single-writer follow-up route.\n- Initial evidence is local Git source/history, bounded owner-origin local run summaries, accepted memory through existing Auto team retrieval, and durable owner decisions. External issue/feedback ingestion and product analytics are not enabled sources in this preview.\n- Mission model choices reuse the existing model controls. Setup uses existing Safe/Standard/Trusted host execution; remote worker, agent-container, ACP and extension execution are excluded from managed cycles. Owner-chosen setup/check commands use the current host execution boundary.\n- APIs use `POST /:id` for pause/activate/edit, `GET /:id/preview`, and `POST /:id/opportunities/:opportunityId` for decisions; see the operating guide for the complete actual table.\n- Core tests and synthetic fixtures establish mechanics. The ten-cycle synthetic pilot across two fixtures produced two verified drafts and eight unchanged-input no-ops. It is not the proposed real-provider/owner-quality pilot below. The feature stays opt-in until that evidence is available.\n\n\nCode Flow will let an owner attach a standing agent team to a repository, give it a product mission, and choose a schedule. The team will discover opportunities, select worthwhile work, build bounded improvements, and bring concrete results back for review. The owner decides what becomes part of the product.\n\nBuild this as a project service above the existing Automations and Auto team systems. Automations owns the schedule. Auto team owns agent execution, worktrees, messaging, checks, and independent review. The new service owns the mission, opportunity backlog, cycle state, admission decisions, and feedback across runs.\n\n## 1 Product behavior\n\nThe owner opens a project and selects **Set up project team**. Setup captures the repository and target branch, intended users, product mission, current priorities, excluded work, agent preferences, schedule, and budget. Code Flow may draft a product description from repository evidence, but that description becomes direction only when the owner saves it.\n\nFor example:\n\n> Improve the first experience for developers using this product. Investigate confusing setup, broken flows, and small usability improvements. Work weekly. Build one small improvement that I can try, and explain why you chose it.\n\nA cycle produces up to three useful opportunities and, when permitted, one draft implementation. An opportunity contains the problem, affected users, supporting evidence, expected benefit, proposed solution, effort estimate, acceptance criteria, and uncertainties. An implementation adds a branch, verification evidence, and a way to inspect or try the result.\n\nThe project page shows **Opportunities**, **In progress**, **Ready for review**, and **History**. The existing run view remains the place to inspect individual agents, messages, tasks, checks, and changes. The existing review inbox receives actionable results and blockers.\n\nThe team remembers owner feedback. “Not relevant to our users” influences future selection; “later” defers an opportunity; “do not suggest this again” suppresses it. Feedback does not silently change the team's execution authority.\n\n### Initial defaults\n\nThese are proposed defaults to validate in a pilot, not measured optimal settings.\n\n| Setting | Initial value |\n|---|---|\n| Scope | One repository and one explicitly selected base branch per standing team |\n| Team count | One active standing team per canonical repository, including across projects |\n| Cadence | Weekly at a user-selected local time, with an explicit IANA timezone |\n| Other schedules | Daily, selected weekdays, or an interval using the existing schedule editor |\n| Autonomy | Discover and build small improvements within saved scope; proposals only is also available |\n| Visible output | At most three new opportunities and one new implementation per cycle |\n| Active work | One active cycle per team; no overlapping standing-team builds for its repository |\n| Review backlog | At most two unresolved implementations, including approved but unshipped work |\n| Opportunity backlog | At most twelve open opportunities; defer discovery when the backlog needs triage |\n| Cycle budget | At most 24 CLI invocations and 60 active run minutes |\n| Allocation | Discovery: 8 invocations and 15 minutes; implementation: 16 invocations and 45 minutes |\n| Rolling budget | At most 48 invocations and 120 active run minutes over seven days |\n| Shipping | Owner action through existing PR or merge flows |\n\nChanging cadence does not expand the rolling budget. Setup previews the next runs and explains when a chosen daily schedule can exhaust its allowance. The app must be running and its execution machine available, as with current automations.\n\n### First release boundary\n\nDeliver persistent project responsibility, scheduled discovery, bounded automatic implementation, reviewed results, and feedback in the first release. Use local repositories and the installed providers eligible for unattended Auto team runs. Start with the validated built-in CLI adapters; a provider being connected or available for an interactive run does not make it eligible for a standing team. A minimal team is sufficient; additional specialists are created only when useful within the allocation.\n\nKeep cross-repository initiatives, remote-worker execution, multiple standing teams on one repository, automatic shipping, autonomous external posting, analytics integrations, and an always-running agent process for later releases. A project containing several repositories can initially select one repository for its standing team.\n\nACP/OpenCode connections remain available through the existing interactive experience. Their current adapter requires Full access and rejects automated triggers, so the first standing-team release cannot use them for discovery, implementation, or **Run now**. Adding unattended ACP support is a separate adapter capability milestone; this feature must not bypass that restriction.\n\nThe team identity persists as mission, preferences, decisions, and knowledge. Provider sessions and private worker conversations remain scoped to individual runs. Persistent responsibility does not require a permanently running CLI process.\n\n## 2 Existing foundations and required extensions\n\n| Existing component | Current behavior | Planned use or change |\n|---|---|---|\n| [Projects](../src/projects.ts) | Groups repository paths and remembers composer/team defaults | Link a standing team to explicit project membership; keep its mission separate from last-used composer defaults |\n| [Automations](../src/automation.ts) | Schedules, timezone handling, missed-run rules, manual runs, webhook history, per-trigger busy checks | Add a managed project-team target and cycle receipts; retain the existing clock and schedule computation |\n| [Auto team](../src/patterns/auto-team.ts) | Plans work, schedules dependencies, routes questions, integrates changes, checks and independently reviews them | Run discovery and implementation as separate Auto team runs with server-owned contracts |\n| [Auto team model](../src/patterns/auto-team-model.ts) | Validates plans, revisions, ownership and outcomes | Validate discovery-only plans, typed opportunity output, and implementation scope on initial plans and revisions |\n| [Provider registry](../src/adapters/index.ts), [routing](../src/routing.ts) and [team capabilities](../src/adapters/team-tools.ts) | Registers built-in, extension and ACP provider IDs; resolves automatic choices and reports capabilities | Derive a restricted unattended inventory before resolving any team role; preserve dynamic provider IDs and validate them against that inventory |\n| [ACP connections](../src/acp-connections.ts) and [shared ACP adapter](../src/adapters/acp.ts) | Settings-managed connections, discovered models, invocation fingerprints and local Full-access execution | Reuse connection discovery/display; show why ACP is currently ineligible for standing teams without changing its execution policy |\n| [Engine](../src/engine.ts) | Runs providers, limits invocations, manages worktrees, approvals, follow-ups and result triage | Add standing-team provenance, durable admission hooks, and guarded continuation/result handling |\n| [Team memory](../src/team-memory.ts) and [memory store](../src/memory.ts) | Attributed private, team and project memory with revision and applicability checks | Supply accepted knowledge to later cycles; keep candidate ideas and product decisions in their own records |\n| [Deferred runs](../src/deferred-runs.ts) and [repository coordination](../src/repo-coordination.ts) | Durable claims, bounded execution, explicit unknown states and recovery | Reuse their storage and launch-receipt patterns without adding a competing agent queue |\n| [Coordination API](../src/coordinated-api.ts) | Validates exact owned run identity, pinned verification and shipping evidence | Extract narrow receipt/result-validation helpers where genuinely shared |\n| [Review inbox](../src/review-inbox.ts) | Prioritizes questions, blocked work, failures and pending run results | Add opportunity decisions and group child-run attention under a project-team cycle |\n| [Notifications](../src/notifications.ts) | Configured deliveries with deduplication and retries | Reuse delivery handling for meaningful cycle changes and avoid duplicate child-run notifications |\n| [Verification](../src/verification.ts) | Required gates, criterion mappings, pinned reports and merge-result checks | Require real evidence for implementation readiness and revalidate moving results |\n| [Project UI](../ui/project.tsx) and [automation UI](../ui/automations.tsx) | Project overview, schedules, review results and run navigation | Add the team workspace and expose its schedule as a managed automation |\n| [Shared browser stream](../ui/stream.ts) and [stream server](../src/stream.ts) | Multiplexes run, Team inbox and terminal subscriptions across owner tabs | Carry project-team updates through the existing broker and preserve its connection and identity boundaries |\n\n### Behaviors that cannot be reused unchanged\n\n1. `RunManager.startResolved()` clears plan approvals for triggered runs. A saved team policy must authorize autonomous work before launch; “this is scheduled” is insufficient authority to implement any idea the model invents.\n2. `Automations.busy()` checks the most recent run for a trigger. A cycle can contain discovery and implementation, so durable cycle state must remain authoritative between those runs.\n3. `Automations.save()` currently logs persistence errors without rejecting the operation. Team activation and launch admission cannot depend on a successful-looking save that failed. Add a strict committed-write path and use it for managed triggers.\n4. `toInbox()` can archive unchanged automation results and remove their worktrees. Discovery findings must be durably imported before normal cleanup, and evidence-only results must not disappear because their Git diff is empty.\n5. `triage({decision: \"revise\"})` currently routes a revision to a writer follow-up. Project-team revisions must preserve or renew whole-team verification before becoming ready again.\n6. `Projects.place()` supports lenient placement for triggered runs. Standing-team children must use `requireProjectRepo` and explicit membership checks, with no fallback into another project after deletion or removal.\n7. Current notification payloads and review grouping are run-oriented. A cycle with two child runs should present one coherent result while preserving direct access to child failures and questions.\n8. `validateRoles()` uses requested/repository trust when resolving providers, while `startResolved()` applies the unattended Full-to-Trusted cap later. Managed requests must calculate effective execution eligibility before role selection and validate explicit choices too; otherwise an ACP selection can pass preparation and fail only at engine launch.\n\n### Provider eligibility on the current baseline\n\nThe updated Auto team schema accepts an exact provider ID from the eligible inventory instead of a fixed three-provider enum. Preserve that schema behavior in the new discovery contract. Validate IDs through the registry and host policy; do not restore a hard-coded schema enum or accept arbitrary strings merely because they match an `acp:` or `plugin:` prefix.\n\n| Provider class | Initial standing-team treatment |\n|---|---|\n| Built-in Claude, Codex and Antigravity CLI adapters | Eligible when installed, selected by the owner, compatible with the effective execution profile and within observed capacity; retain current adapter-specific limits |\n| ACP/OpenCode connections | Ineligible for all managed phases because the current engine rejects automated triggers and requires Full access |\n| Owner-installed provider extensions | Keep out of the initial standing-team pool until their unattended execution and structured-outcome behavior have explicit compatibility evidence; ordinary extension runs retain their existing behavior |\n\nAdd a small shared eligibility helper, proposed as `src/provider-eligibility.ts`, to compute provider IDs and rejection reasons from the execution context. Reuse current registry, availability and adapter restrictions. Keep the standing-team compatibility allowlist as an additional policy input, so factoring the helper does not change existing interactive or ordinary-automation behavior.\n\nResolve the effective unattended trust profile before invoking `resolveAuto()`. Apply the helper to coordinator selection, worker/reviewer pools, explicit IDs, omitted/default pools, inherited project presets, planning inventory, coordinator revisions and every later dispatch. Filter defaults with a visible explanation; reject an explicitly saved ineligible selection before activation. Recheck after Settings changes. A missing or changed selected provider blocks work or uses only an already-authorized eligible fallback; it cannot silently expand the pool or enable Full access.\n\nProvider display names and model catalogs come from the existing registry/Settings flow. Preserve opaque model IDs and use the current provider-specific settings validators. A successful ACP connection probe establishes protocol/session/catalog observations, not available inference quota, sandboxing or unattended compatibility. Scoped live tools and constrained JSON are separate capabilities: structured outcomes can use prompt-and-validation fallback where supported, while an unavailable tool bridge stays unavailable.\n\nA future ACP compatibility milestone must establish an accepted unattended execution/isolation policy, report validation and retries, cancellation/recovery and accurate usage accounting. Preserve `Run.acpConnections` fingerprints and `assertAcpBindings()` if that milestone is later delivered; fingerprints pin command/arguments, not executable bytes or external CLI configuration. Two connection IDs are not evidence of independent model vendors. Until that work is complete, no managed request may omit its trigger metadata to evade the engine's ACP restriction.\n\n## 3 Architecture and execution ownership\n\nAdd a `ProjectTeams` service. It performs deterministic state transitions, accepts validated model output, and prepares normal run requests. It does not invoke provider CLIs directly or implement a second task scheduler.\n\n```mermaid\nflowchart TD\n Owner[Owner saves mission and limits] --> Team[Standing project team]\n Schedule[Existing Automations scheduler] --> Cycle[Durable cycle admission]\n Team --> Cycle\n Cycle --> Discovery[Auto team discovery run]\n Discovery --> Opportunities[Validated opportunities and evidence]\n Opportunities --> Selection[Policy and backlog checks]\n Selection --> Proposal[Proposal for owner decision]\n Selection --> Build[Auto team implementation run]\n Build --> Verification[Existing checks and independent review]\n Verification --> Inbox[Project workspace and review inbox]\n Proposal --> Inbox\n Inbox --> Decision[Owner feedback or shipping action]\n Decision --> Team\n Decision --> Opportunities\n```\n\nUse one managed automation for each team. A cycle contains a discovery child and zero or one implementation child. Completion events advance the cycle; periodic reconciliation through the existing automation tick recovers missed events. The cycle service subscribes to `RunManager.subscribeAll()` and treats events as wakeups, not as the authoritative record of completion.\n\nBefore advancing a child, reload its settled state and validate identity, evidence, and persistence. The run's `endedAt` and final bookkeeping must be present; an intermediate `status: done` event alone is insufficient.\n\nThe existing Auto team task scheduler remains responsible for members and task dependencies within each child. Existing global agent slots continue to govern capacity across manual and scheduled work.\n\nRegister the service with `startPolicyBackground()`, `pausePolicyBackground()` and shutdown in `src/server.ts`. Stop and drain in-flight preparation/launch work before filesystem-policy changes, as existing background services do. Reconciliation can use the existing tick and subscriptions; it needs no additional scheduling clock.\n\n## 4 Data model and persistence\n\nAdd versioned types in `src/project-team-model.ts`. Keep the following objects distinct so that product feedback cannot accidentally become execution permission or a verified technical fact.\n\n| Record | Required information |\n|---|---|\n| `ProjectTeam` | ID, revision, project ID, canonical repository identity/path, name, status, managed trigger ID, current mission revision, creation/update times |\n| `ProjectTeamMission` | Immutable revision, intended users, mission, priorities, exclusions, base branch, enabled evidence sources, provider/model preferences, autonomy mode, execution policy and budget settings |\n| `ProjectTeamCycle` | ID, team ID, immutable mission snapshot/hash, trigger occurrence or manual operation ID, pinned base SHA, source manifest, eligible-provider snapshot with reasons and effective trust, child claims/runs, state, selection receipt, usage reservations and timestamps |\n| `ProjectOpportunity` | ID/revision, team/repository identity, stable problem key, category, problem, affected workflow, evidence refs, hypothesis/observation distinction, proposal, benefit, effort/risk estimates, acceptance criteria, rank explanation, status and source cycle |\n| `ProjectTeamDecision` | ID, opportunity and exact revision, authenticated actor, decision, optional reason, result commit/report hash where applicable, timestamp and operation ID |\n| `ProjectTeamLaunch` | Cycle/stage/attempt, claim ID, input hash, reserved allowance, run ID if known, receipt state and reconciliation data |\n| `ProjectTeamResult` | Opportunity revision, child run ID, owned result branch and SHA, verification/report references, independent-review identity, preview references, readiness and shipping receipts |\n\n`ProjectTeamMission` includes two autonomy modes: `propose` and `build-within-scope`. Store explicit allowed work categories, included/excluded relative paths, maximum change size, pinned verification profile, and the owner's chosen existing execution/isolation settings. Semantic judgments such as “small UX improvement” need an explanation and conservative escalation; they are not completely enforceable through a file count.\n\nAdd optional `Run.projectTeam` metadata containing `teamId`, `cycleId`, `missionRevision`, `phase`, `claimId`, `attempt`, and, for implementation, `opportunityId` and `opportunityRevision`. Preserve it in run snapshots, replay, exports, and UI types. The server constructs this metadata. Generic run-creation bodies, webhook payloads, agent messages, and collaboration tools cannot supply or alter it.\n\n### Store design\n\nUse `CODEFLOW_HOME/project-teams/<team-id>.json` as one versioned aggregate per team for configuration revisions, opportunities, cycles, decisions and launch claims. Follow the strict atomic write pattern already used by deferred/coordinated services: serialize mutations, validate bounded input, write a private temporary file, sync it, rename it, and sync the directory where supported. Publish in-memory state only after the write succeeds. Use optimistic revisions and operation IDs for HTTP mutations.\n\nA single team aggregate makes opportunity selection, budget reservation and stage claiming one atomic mutation. Use the existing single-server ownership model plus an in-process canonical-repository guard for the initial one-active-team constraint. Validate all teams at startup; duplicate active ownership is an explicit configuration blocker.\n\nBound the aggregate, history and payload sizes. Start with a 16 MiB file ceiling, 100 retained cycle summaries and 200 full opportunity records. Store large evidence in existing run artifacts or bounded evidence files with immutable digests, not in the aggregate. Preserve compact rejection/deduplication records separately within a fixed cap; reaching retention limits must not silently forget “do not suggest again.” Archive or request cleanup before that cap is exceeded.\n\nInclude the new aggregates and retained evidence in the existing backup/export and maintenance inventory. Evidence cleanup follows live references from opportunities and decisions, so archiving a discovery child cannot break a pending review card. Redaction and deletion must cover copied evidence as well as run links.\n\nPersist provider references and execution-binding metadata, not copied ACP executable configuration or credentials. Keep `acp-connections.json` excluded from portable backups under the current maintenance policy. Restoring a team on another host leaves it paused until project paths, providers, referenced connections and execution settings are explicitly reconciled; portable mission data cannot authorize a new host invocation.\n\nDo not automatically delete pending results, active claims, or owner decisions to meet a size limit. Keep all claims needed to account for unresolved work. A corrupt/unsupported aggregate blocks that team's launches while leaving ordinary runs and other teams usable.\n\n## 5 Scheduling and launch recovery\n\n### Managed trigger contract\n\nExtend the trigger model into an explicit union. Existing records without a discriminator remain saved-run triggers; existing sweeps retain their behavior. A new `kind: \"project-team\"` target stores `teamId`, `projectId`, name, enabled state and schedule, without a duplicated saved run configuration. Its execution configuration comes from the current saved mission.\n\nManaged triggers initially reject webhooks. Schedule and **Run now** both call the same `ProjectTeams.requestCycle()` admission path. Add a managed-target callback to `Automations`; keep provider launching in `RunManager`. Update `FireResult`, event history and preview responses to include `cycleId`, child run IDs when available, and meaningful dispositions such as `accepted`, `skipped`, or `blocked`.\n\nUse the existing `Schedule`, `validateSchedule()`, `previewSchedule()`, `nextFire()` and missed-run behavior. Supply a stable occurrence key from the trigger ID and the scheduled UTC `nextAt` captured before advancement. Manual requests use a client operation ID; retries reuse it. Repeated ticks, duplicate HTTP delivery or restart recovery return the same cycle for that key.\n\nCreation spans two existing stores. Use a recoverable activation sequence: create a draft team, create a disabled managed trigger idempotently, save the linkage, then activate the linked pair. On restart, incomplete setup remains disabled until reconciliation verifies both records. A trigger with no valid team or a team with a missing trigger cannot launch. Managed-trigger enable, schedule changes and deletion route through the team service so Settings cannot bypass team state.\n\nUpdate all trigger readers, previews, project filters, serializers and UI types that currently assume `trigger.run` exists. Preserve old saved-run/sweep payloads and their tests. Unknown target kinds remain disabled and diagnosable; they must never be interpreted as ordinary saved prompts. The managed-trigger persistence path commits or fails explicitly without overwriting the last committed state or poisoning subsequent writes.\n\n### Admission and advancement\n\nAt cycle admission, check active status, exact project membership, canonical repository, configured branch, effective unattended provider eligibility/readiness, filesystem policy, remaining budget and backlog capacity. Pin the base commit, configuration and allowed provider choices. Never use whichever branch happens to be checked out later. Do not implicitly pull, rebase, or fetch; show the observed ref and last known freshness. Optional remote refresh belongs in a later explicit source setting.\n\nRecheck these conditions before each child launch and continuation. A later restrictive policy change or repository removal blocks further work even though a historical mission snapshot exists. An expanded mission requires a new cycle or explicit revised request; it cannot enlarge an already-admitted allocation.\n\n| Failure point | Required behavior |\n|---|---|\n| Before a durable launch claim | No run starts; a later tick can retry admission |\n| Claim saved, no dispatch attempted | Retry only when the service can prove dispatch never began |\n| Dispatch started, receipt missing | Mark `unknown`; inspect exact owned run identities and retain reservations |\n| One matching persisted child found | Attach the child idempotently and continue observing it |\n| No conclusive match or multiple matches | Block automatic replay; expose recovery with the available evidence |\n| Child finished before cycle state was updated | Import its validated result once, using run ID, report hash and input revision |\n| State write fails | Stop dependent dispatch and expose a storage blocker |\n\nExtract a narrowly scoped receipt helper from the existing coordination/deferred patterns. Add an internal admission barrier for managed child starts so the initial run identity and claim binding are synced before any CLI execution begins. Do not acknowledge an admitted launch based solely on best-effort event logging. Prove this ordering with an instrumented fake CLI.\n\nThis supports idempotent control transitions, not a promise of exactly-once external side effects. A process can act immediately before a crash. Preserve unknown states and existing execution-isolation limits.\n\n### Lifecycle rules\n\nCycle states are `requested`, `discovering`, `selecting`, `building`, and terminal `completed`, `no-op`, `skipped`, `blocked`, `failed`, `cancelled`, or `unknown`. A cycle completes when its results are durably published; an opportunity can remain awaiting human review after the active-cycle lock is released.\n\nPause prevents new child starts and new cycles; running children can finish and publish their result. **Stop current work** also invokes existing cancellation and retains partial work. **Resume** rechecks policy and capacity. Automatic restart never grants a fresh budget to a blocked or interrupted child. An explicit resume is a new bounded allocation with cumulative usage visible.\n\nA paused cycle waiting between stages keeps its last stage and a durable wait reason; it neither holds a provider slot nor silently becomes a new cycle. **Run now** requires an active team. Repeated failures with the same unresolved cause block further automatic attempts until the relevant configuration/input changes or the owner explicitly retries; a new scheduled date alone does not authorize repeating a failed build.\n\nProject deletion or repository removal blocks pending dispatch and cancels active owned children through existing cancellation. Retain history and branches for inspection. Archiving a team disables its trigger without treating its unmerged results as shipped or silently deleting them.\n\n## 6 Discovery and opportunity selection\n\n### Evidence collection\n\nBuild a bounded source manifest from the selected repository, recent changes since the previous cycle, existing run findings, open opportunity/decision records, accepted project memory, and explicitly enabled issue/PR integrations already available in Code Flow. Also include known manual work so a team can avoid proposing work already underway.\n\nRecord source IDs, revisions, timestamps, branch/commit applicability, content digests, and omissions caused by limits or unavailable connections. Resolve evidence references in the host; the model cannot assert that an invented issue, path or test result is verified. External text retains its provenance and current untrusted-source treatment through summaries and candidate reports.\n\nRepository access is sufficient for technical findings. Product feature ideas without user evidence are labeled hypotheses. Live app exploration is optional and uses an owner-configured launch profile and existing browser capability where available. Do not claim a workflow was exercised when the team only inspected source.\n\nSkip expensive discovery when the input fingerprint is unchanged: base commit, mission revision, enabled source revisions, decisions and relevant memory revisions. Add a configurable full rescan interval, initially four weeks, so unchanged code can still receive a fresh review. A skipped or empty cycle is a successful outcome and creates no artificial improvement quota.\n\n### Auto team discovery contract\n\nAdd a server-owned discovery contract to Auto team rather than registering a new agent runtime. It must:\n\n- Permit only `investigation` tasks in both the initial plan and every coordinator revision.\n- Designate one reporting task whose dependencies include all required investigations.\n- Use a discovery-specific plan/outcome schema with a versioned `project-opportunities/v1` report. Keep ordinary and legacy Auto team schemas compatible.\n- Preserve the current dynamic provider-ID schema and validate every initial or revised member against the recorded unattended inventory. Do not assume a provider supplies native constrained JSON, live steering or scoped team tools.\n- Bound candidate count, text lengths, source references and acceptance criteria; publish at most three new visible opportunities per cycle.\n- Collect the report from structured output and journal it. Never parse proposals from the final Markdown summary or scrape private memory.\n- Include the exact report and digest in independent review context; publish only the report revision that was reviewed.\n- Allow an empty report with an evidence-based reason.\n\nThe reporter can be a worker or the coordinator when the allocation permits. Initial planning and coordinator synthesis both count against Auto team's existing coordination limit. Reserve the independent review turn before admitting optional investigation tasks. With the default eight discovery invocations, the coordinator ceiling remains two.\n\nPersist a discovery review receipt binding the report digest, source manifest, run ID, reviewer step and acceptance verdict. The cycle imports exactly that report revision. A candidate is not approved simply because the same Git HEAD passed an unrelated review.\n\nDiscovery has no implementation repair turn. If review finds a report defect, bounded repairs remain read-only and may revise the report; the generic Auto team repair path must not introduce a write turn. Existing read-mode snapshot/restore, filesystem policy and adapter isolation still apply. Runtime-created worktrees and persisted report artifacts are allowed; source changes are not discovery output.\n\n### Deduplication and ranking\n\nFirst compare against active opportunities, rejected/deferred decisions, existing issues/PRs and recent manual runs. Use source keys plus normalized problem identity, affected paths/symbols and acceptance criteria. A repository commit changing must not by itself turn an old idea into a new one. Model similarity suggestions can assist grouping, but the host preserves distinct IDs and decision history.\n\nFor each eligible candidate, record mission alignment, expected impact, evidence confidence, estimated effort and risk with short explanations. Use a deterministic, versioned ranking policy over those estimates, with stable tie-breaking; do not present scores as measured user benefit. Preserve the selection receipt, including why other candidates were deferred.\n\nAutomatic implementation requires all of the following:\n\n- `build-within-scope` is enabled in the saved mission.\n- The problem and proposed work fit the allowed categories and paths.\n- Evidence is sufficient for the proposed action and has current applicability.\n- No active or suppressed matching work exists.\n- Acceptance criteria are testable with a configured verification profile.\n- The change fits budget, review capacity and size limits.\n- No unresolved product decision or excluded-risk change is needed.\n\nOtherwise keep the idea as a proposal with the reason it needs owner input. Initial exclusions should cover broad rewrites, schema migrations, authentication/permission changes and billing changes unless the owner explicitly authorizes the particular work. New feature experiments may be built when explicitly included in the mission, but their claimed benefit remains a hypothesis until evaluated.\n\n## 7 Implementation and review\n\nConvert the selected opportunity revision into a normal `auto-team` request containing the problem, evidence references, intended outcome, acceptance criteria, allowed paths, exclusions and pinned verification profile. Attach the immutable mission/cycle provenance through the server. Agent-generated report text is contextual evidence, not a new owner instruction.\n\nUse fresh implementation worktrees from the selected base. If the target branch moved after discovery, mark applicability stale and use a bounded read-only revalidation before implementation; that turn consumes the same cycle budget. If the candidate or base must materially change, record a new candidate revision and selection receipt. Do not silently build an obsolete proposal or carry unused approval across a changed scope.\n\nApply saved scope constraints to initial planning, coordinator revisions, every write/integration/repair path, and the final diff. The Auto team validator must reject ownership outside allowed paths or tasks outside the approved goal. After write turns, inspect actual changed files, including untracked files and rename destinations. Excluded paths, changed verification commands, excessive changes or unresolved scope deviations stop automatic integration/readiness and produce an explicit result to inspect.\n\nEnforce managed ownership at the engine boundary as well as in the new API. Resume, follow-up, revise-goal, whole-team continuation and selective-rerun actions on a managed run must obtain a fresh valid allocation from the team service, preserve lineage, and recheck pause/membership/scope. Generic run endpoints cannot bypass these checks. For an in-place resume, bind the new allocation in the existing run journal; keep the original launch identity intact. Linked new runs receive their own launch claim and receipt.\n\nThese are execution and result gates layered on existing sandbox policies; path checks do not create an OS security boundary. Agents do not gain wider shell/network privileges merely because they belong to a standing team. Check/setup isolation and agent isolation remain separate existing settings and are pinned for each allocation.\n\n### Readiness conditions\n\nA result becomes **Ready for review** only when the implementation exists, required acceptance evidence is present, configured required checks pass, independent review approves the same final commit, and existing fail-to-pass proof succeeds when enabled. Validate branch ownership and current HEAD against the exact recorded evidence, as the coordinated-result path does today.\n\nMissing checks, skipped required gates, stale reports, incomplete work or a failed review produce **Needs attention**, not a ready result. An investigation can finish with reviewed evidence without code checks; an implementation cannot borrow that exemption.\n\nEach review card includes:\n\n- What problem was addressed and why this opportunity was chosen.\n- A concise before/after description, with screenshots for visual changes when captured.\n- The result branch/commit and links to the diff, checks and independent review.\n- A preview action when a configured launch profile exists, otherwise concrete reproduction steps.\n- Expected benefit clearly separated from measured improvement.\n- Known limitations, usage, and the owner's available decisions.\n\nPreviews are tied to the result worktree and commit. Reuse existing service-launch controls; do not leave previews running indefinitely between cycles. Missing previews do not become fabricated demonstrations.\n\n### Owner decisions and shipping\n\n| Action | Effect |\n|---|---|\n| Build this proposal | Authorizes its exact revision and a displayed bounded allocation; uses the same admission and verification path |\n| Request changes | Saves feedback and starts a linked whole-team continuation with a new explicit allocation |\n| Approve result | Records product acceptance of the exact result commit; does not merge or publish |\n| Open PR | Uses the existing owner-authorized PR flow and records the PR link |\n| Merge | Uses existing merge validation and shipping controls against the current source and target |\n| Later | Saves a defer-until value and keeps the opportunity out of selection until then |\n| Reject | Saves the reason and suppression policy before any branch cleanup |\n\nRecord `accepted` and `shipped` separately. Approving a result or opening a PR is not evidence it reached the product. A local merge receipt or confirmed merged PR can mark it shipped. Moving the result branch invalidates its prior approval/readiness. Remote changes are considered observed only when an existing integration has actually refreshed them.\n\nThe full lifecycle is `proposed`, `selected`, `implementing`, `ready`, `accepted`, `shipped`, with explicit `deferred`, `rejected`, `blocked`, and `expired` alternatives. A new revision or failed continuation does not erase old result evidence or decisions.\n\n## 8 Budgets and attention limits\n\nEnforce cross-run budgets by reserving a child's complete maximum allocation atomically before launch, then use the existing per-run invocation/time ceilings. A reservation includes retries, schema repair and coordination; it is not only a count of tasks. This avoids adding a second per-provider dispatch counter.\n\nAt settlement, reconcile against durable invocation reservations/metrics, not model-reported usage. Retain the full reservation while a launch is unknown. Release unused allowance only when authoritative accounting proves it was unused; if reliable elapsed-active-time accounting is unavailable, retain the time reservation conservatively through its budget window.\n\nCharge settled usage to a rolling seven-day window. Outstanding reservations count even when their creation time falls outside that window, so a stuck run cannot become free by aging out. Restart, reschedule, manual **Run now**, agent-requested continuation and schema failures cannot reset allowances. An owner-requested extra allocation is explicit, recorded, and shown beside cumulative use.\n\nA weekly cycle can finish discovery without launching implementation if the remaining allowance cannot fund planning, work and independent review. Never use the remainder to build a result that cannot be reviewed. Known exhausted providers wait through existing readiness rules within a bounded window; do not silently choose providers outside the configured pool.\n\nStop new implementation while two earlier drafts await review or shipping. Accepted but unshipped work remains outstanding unless the owner explicitly parks it. Stop discovery when twelve open opportunities need triage. Suppressed and unchanged candidates do not generate repeat notifications.\n\nTrack provider-reported tokens and API-equivalent cost when available, with unknown values preserved. Invocation counts and active-run time are enforceable product limits; missing provider usage is not zero consumption or evidence that work was free.\n\n## 9 Memory and feedback\n\nAssemble each cycle's context from the saved mission, current priorities, relevant decisions, bounded recent cycle summaries, existing work, and accepted project memory selected through the existing applicability rules.\n\nKeep three kinds of information separate:\n\n| Information | Authority and lifetime |\n|---|---|\n| Product direction | Owner-authored mission and explicit revisions |\n| Opportunity and decision history | Evidence and authenticated feedback for selection, with revision-specific provenance |\n| Technical project knowledge | Existing memory proposals and acceptance rules, including branch/commit applicability |\n\nA rejected idea does not become a technical convention. A merged implementation does not automatically accept every memory proposed by its workers. Unverified ideas never become accepted project facts merely because they appeared in several cycles.\n\nPreserve source trust and allow the owner to inspect why a decision or memory influenced a proposal. Do not copy one worker's private checkpoints into future members as shared team knowledge. New child runs receive portable, attributed context and fresh member identities.\n\nFeedback can adjust selection preferences within the saved mission. “Avoid this topic” should create an explicit suppression rule visible in team settings; an inferred preference remains a suggestion until the owner accepts it. Permission, budget and source-setting changes always use the mission revision path.\n\n## 10 API and user interface\n\nAdd `src/project-teams-api.ts` as an owner-only boundary consistent with current automation/coordinated execution APIs. Shared accounts must not fall through into it. Scope all read models and notifications to their project and existing permissions; support broader shared-team management separately later.\n\nRegister the new routes in `src/filesystem-access.ts` for both execution mutations and private evidence access. Owner authentication alone is insufficient when custom filesystem restrictions pause app-managed execution or unscoped private-data access. Apply the same restriction checks to readiness previews, source discovery and evidence reads as well as actual launches.\n\n| Endpoint | Purpose |\n|---|---|\n| `GET /api/project-teams?project=<id>` | Team summaries and attention counts |\n| `POST /api/project-teams` | Create a draft with an idempotency operation ID |\n| `GET /api/project-teams/:id` | Mission, status, bounded backlog and recent cycles |\n| `PUT /api/project-teams/:id` | Save a new mission/settings revision with optimistic concurrency |\n| `POST /api/project-teams/:id/activate` | Validate and activate the linked managed automation |\n| `POST /api/project-teams/:id/pause` | Prevent new launches |\n| `POST /api/project-teams/:id/stop` | Pause and cancel current owned work |\n| `POST /api/project-teams/:id/resume` | Revalidate and re-enable the schedule |\n| `POST /api/project-teams/:id/preview` | Show readiness, schedule, budgets and current blockers without model calls |\n| `POST /api/project-teams/:id/cycles` | Run now through normal admission |\n| `GET /api/project-teams/:id/cycles/:cycleId` | Exact inputs, selection, children, claims, usage and result |\n| `POST /api/project-teams/:id/cycles/:cycleId/recover` | Reconcile a reviewed unknown claim; never blindly replay it |\n| `POST /api/project-teams/:id/opportunities/:opportunityId/decisions` | Build, revise, accept, defer or reject a specific revision |\n| `POST /api/project-teams/:id/archive` | Disable future work and preserve history |\n\nMutation bodies have closed schemas, bounded strings/collections, an expected revision and an operation ID. Stale decisions return `409` with the current version. Result acceptance also supplies the exact current commit/report identity. A model response is never a substitute for this authenticated action.\n\nAdd `ui/project-team.tsx` and `ui/project-team-state.ts`; integrate them with `ui/project.tsx`, `ui/state.ts`, `ui/types.ts` and the current route machinery. Reuse the existing schedule editor and preview, provider/model controls, check-profile selector, run cards, diff panel, and memory inspector.\n\nUse `loadAgents()` and existing catalog refresh behavior so the setup screen reflects connections added, disabled or removed in Settings. Show connected but ineligible ACP providers with the reason **Unavailable for scheduled teams** and a link to the existing Agents & models settings; do not offer connection configuration or an execution-policy override inside team setup. Saved interactive presets containing ACP require an eligible replacement before activation.\n\nThe setup screen ends with a concrete summary of what the team may build, when it will run, its budget and how results reach the owner. After activation, the project page shows its mission, next run, remaining budget, active work and review backlog. Keep protocol IDs and orchestration settings in details rather than the main product flow.\n\nGeneralize inbox presentation to `run` and `project-opportunity` items while preserving existing `prioritizedReviewRuns()` behavior for legacy callers. Group managed children under their opportunity/cycle, without counting the same blocker twice. Discovery proposals remain visible even if their child run has no diff. Opening or marking a card read does not approve it.\n\nPublish project-team state through a scoped topic on the existing `src/stream.ts` / `ui/stream.ts` broker, using `subscribe()` and cleanup on unmount. Do not open a new `EventSource` per team, card or browser tab. Preserve the current Team inbox subscription and owner/shared-account separation. A server restart reloads authoritative team state; optimistic client updates cannot revive a paused team or replay a decision.\n\n### Notifications\n\nExtend the existing notification candidate/payload model with a project-team subject, retaining old run payload compatibility. Use durable identities such as team/cycle/result-revision or blocker revision. Notify when a result is ready, an actionable proposal needs a decision, or execution fails/needs input. Suppress duplicate child completion alerts for the same published outcome.\n\nNo notification is needed for unchanged input, an empty successful cycle, normal waiting, or repeated identical blockers. Use only destinations already configured by the owner. A weekly digest and additional external integrations can follow after the core workflow works.\n\n## 11 Delivery phases\n\nAll new behavior remains behind `CODEFLOW_PROJECT_TEAMS_ENABLED` and explicit per-team activation. The existing `CODEFLOW_AUTO_TEAM_ENABLED` gate must also permit new child runs. Initial implementation proceeds in these dependent slices; implementing the plan does not require a database migration or a new Node baseline.\n\n| Phase | Deliverables and main files | Completion criteria |\n|---|---|---|\n| P0 Contracts and durable state | `project-team-model.ts`, `project-team-store.ts`, `provider-eligibility.ts`, mission revisions, lifecycle reducer, budgets and operation receipts | Concurrent edits fail clearly; invalid/corrupt state cannot launch; dynamic IDs are preserved while ACP/ineligible extensions are rejected for managed work |\n| P1 Managed scheduling and recovery | `automation.ts`, `project-teams.ts`, `server.ts`, `filesystem-access.ts`, engine admission/receipt helper | One scheduled occurrence creates one cycle; effective provider eligibility precedes role resolution; two-store activation recovers; ambiguous launch blocks replay; ordinary automations/sweeps still behave as before |\n| P2 Discovery on Auto team | `patterns/auto-team.ts`, `patterns/auto-team-model.ts`, `teams.ts`, `project-team-discovery.ts`, engine report persistence | A fake-agent cycle yields independently reviewed typed opportunities or a no-op; no discovery path dispatches a write turn; reports survive no-diff cleanup |\n| P3 Selection and bounded implementation | `project-team-policy.ts`, `project-teams.ts`, engine scope/continuation hooks | A valid opportunity creates one implementation child; scope, backlog, budget and membership gates prevent ineligible launches; exact final evidence controls readiness |\n| P4 Project workspace and decisions | `project-teams-api.ts`, `ui/project-team.tsx`, `ui/project-team-state.ts`, `project.tsx`, `automations.tsx`, `review-inbox.ts`, existing stream broker | Owner can activate, inspect, approve, revise, defer, reject, pause and recover; provider availability/reasons refresh from Settings; project-team updates share the existing stream |\n| P5 Feedback and notification integration | `project-team-context.ts`, existing memory retrieval, notifications, UI history/usage | Next cycle respects rejected/deferred work; technical memory remains separately governed; one actionable change produces one notification candidate |\n| P6 Reliability and pilot release | Fault tests, browser tests, bounded evaluation script and user docs | Restart, stale evidence, quota, cancellation, deletion, privacy and old-data compatibility pass; pilot results are recorded before expanding rollout |\n\nP0–P2 provide an internal proposal-only milestone. P3–P5 deliver the first complete user promise, including an implemented improvement before owner acceptance. P6 is the release gate. Do not present the proposal-only milestone as completion of the feature.\n\n### Concrete implementation order\n\n1. Define types, reducers, store semantics and effective provider eligibility before connecting live launches.\n2. Add managed automation previews and cycle admission using a fake launch adapter.\n3. Establish durable engine admission and exact child ownership/recovery.\n4. Add read-only discovery contracts and report capture; prove every repair path stays read-only.\n5. Implement host validation, duplicate handling, selection and allocation.\n6. Launch an ordinary Auto team implementation from one selected opportunity.\n7. Validate final results, preserve discovery evidence and support verified whole-team revision.\n8. Add project setup/workspace, inbox decisions and managed automation controls.\n9. Add feedback context, notifications and meaningful usage/outcome reporting.\n10. Complete fault/compatibility tests and run the opt-in pilot.\n\nKeep helper extraction small. Reuse deferred/coordinated persistence and receipt patterns where appropriate, but do not rewrite those services into a general workflow framework to deliver this feature.\n\n## 12 Verification strategy\n\nExtend the current fake-provider and isolated Git-repository harnesses. Core CI must not spend real provider quota or require connected external services.\n\n| Test area | Required cases |\n|---|---|\n| Model/store | Mission revision conflicts; request idempotency with changed-body rejection; one-active-team constraint; malformed state; bounded retention; atomic claim/selection/budget writes |\n| Provider eligibility | Explicit ACP selection, default pool, `auto` resolution, inherited Full-access preset and managed Run now are all rejected/filtered before launch; coordinator revisions cannot add an ineligible provider; unavailable permitted fallback blocks clearly; ordinary dynamic-ID behavior remains supported |\n| Provider settings and restore | Settings refresh updates inventory/reasons; disabled/removed/changed bindings stop dependent dispatch; opaque catalog IDs survive validation; unknown usage stays unknown; portable team backups cannot restore executable authority |\n| Scheduling | Daily/weekly/interval timezone previews; DST; missed-run policy; restart before/after occurrence advancement; concurrent manual/scheduled admission; no overlapping cycles |\n| Activation | Crash after each cross-store write; missing/orphan trigger; activation persistence failure; managed-trigger edits cannot bypass pause or settings |\n| Launch recovery | Failure before claim, before dispatch, during dispatch, before receipt, after child completion; one matching run, no matching run and conflicting matches |\n| Discovery | All tasks and revisions stay investigations; write-mode repair rejected; malformed/oversized/empty reports; invented evidence; independent report review; no-diff preservation |\n| Selection | Duplicate wording, same issue after new commit, rejected topic, defer expiry, existing manual/PR work, low evidence, stale source and insufficient budget |\n| Implementation | Allowed paths and actual changed paths agree; untracked/renamed excluded files; changed check configuration; stale base; missing acceptance work; exact final-head review/proof |\n| Continuation | Request changes uses whole-team verification; direct follow-up invalidates readiness; generic resume/follow-up cannot bypass team budget or paused state |\n| Budget | Retries counted; concurrent reservations; unknown receipts retain allowance; rolling-window boundaries; restart/resume cannot reset usage; unknown token cost remains unknown |\n| Human decisions | Stale revision/commit rejected; approval differs from merge; rejected reason retained before cleanup; accepted/unshipped work occupies review capacity |\n| Access/lifecycle | Shared identities cannot dispatch; custom filesystem restrictions block new private/execution routes; removed project/repository blocks and cancels work; archive preserves results; global feature disable prevents new launches |\n| Notifications | One actionable cycle alert; suppression of child duplicates; no-op quiet; new blocker revision alerts; restart deduplication; existing destinations/settings honored |\n| Browser | Setup to ready result to revision/approval; propose-only path; failure/recovery; small screens, dark/light modes, keyboard navigation and axe accessibility checks |\n| Browser connections | Project-team topic shares the existing broker with run/Team inbox/terminal topics; multiple tabs, leader loss and unmount release subscriptions; owner-to-shared identity changes cannot expose team evidence |\n\nProposed test files: `test/project-team-model.test.ts`, `test/project-team-store.test.ts`, `test/project-team-service.test.ts`, `test/project-team-discovery.test.ts`, `test/project-team-engine.test.ts`, `test/project-teams-api.test.ts`, and `test/e2e/project-teams.e2e.ts`.\n\nExtend existing `automation.test.ts`, `auto-team.test.ts`, `auto-team-model.test.ts`, `team-memory.test.ts`, `review-inbox.test.ts`, `notifications.test.ts`, `projects.test.ts`, `acp-connections.test.ts`, `acp.test.ts`, `maintenance.test.ts`, `shared-access.test.ts`, and relevant deferred/coordinated regression tests. Add focused `provider-eligibility.test.ts` coverage. Fake-provider fixtures must cover both ordinary Auto team output and the mode-specific discovery schema; use `fake-acp.mjs` to assert incompatible managed requests fail without starting a provider process.\n\nExtend `test/e2e/settings.e2e.ts` and `test/e2e/connections.e2e.ts` for the provider picker and shared-stream integration. The current connection tests already include the Team inbox topic in their subscription counts; include project-team subscriptions without regressing cross-tab sharing or removing existing topics. Preserve the streamed tool-update behavior in `ui/run.tsx` by reusing its transcript components.\n\nFor implementation validation, run focused tests for each slice, then `npm run typecheck`, `npm test`, `npm run build`, and the relevant browser tests. Run the broader end-to-end suite before release when shared routing or inbox changes warrant it. No runtime test result is claimed by this planning document.\n\nThe newly available `npm run test:acp:live` is a separately invoked, usage-consuming interactive compatibility check. It is not a CI prerequisite for standing teams and does not establish support for scheduled ACP work. Use fake protocol/process fixtures for this feature's ACP exclusion tests.\n\n## 13 Pilot and rollout\n\nAdd `scripts/evaluate-project-teams.ts` using the established evaluation approach. Fake mode proves control flow. Real mode is explicitly invoked with a repository, provider pool and hard budget, and emits immutable reports without changing feature flags or shipping changes.\n\nUse a small opt-in pilot on Code Flow and one other repository with known owner priorities. Record at least ten completed cycles across both, including unchanged input, a rejected proposal, a deferred idea, a failed check, an accepted implementation and a pause/restart. These are proposed observation requirements; they do not establish model quality by themselves.\n\nMeasure:\n\n- Proposals the owner considered worth pursuing, and implementations actually accepted/shipped.\n- Review and correction time, repeat suggestions, and rejected-work recurrence.\n- Invocations and active time per accepted improvement, with unknown provider usage shown explicitly.\n- No-op/skip frequency, selection reasons, verification failures and stale-evidence rejections.\n- Time spent blocked on missing product context, configuration or human decisions.\n\nRequire zero duplicate child launches in fault tests, zero bypasses of the explicit scope/budget/approval gates, and correct evidence for every ready result before the pilot. Keep the feature experimental until owner feedback shows that its output is worth the attention it consumes. Do not promote based on agent count, lines changed, or number of proposals generated.\n\nDisabling the feature prevents new cycles and automatic stage advancement while preserving history and review access. Stopping active work remains a distinct explicit action. Existing ordinary automations, saved runs, and Auto team workflows retain their behavior.\n\n## 14 Acceptance example\n\n1. The owner connects a repository, selects its base branch, saves a mission to improve onboarding, and enables a weekly team with the default budget.\n2. At the scheduled time, exactly one cycle pins the repository and source inputs.\n3. Discovery returns three evidence-bearing opportunities and independent review accepts the report.\n4. The service suppresses one duplicate, records why another is deferred, and selects one eligible improvement within budget.\n5. Auto team builds it in isolated worktrees and verifies the final result.\n6. The owner sees one ready card containing the problem, rationale, preview or reproduction steps, diff and real check/review evidence.\n7. The owner requests a change. A bounded whole-team continuation produces a newly verified result; the old approval state is not carried forward.\n8. The owner approves and separately merges or opens a PR through the existing controls.\n9. The next cycle remembers the decision, avoids repeating the same work, and can return no new work when nothing warrants attention.\n\nThe implementation is complete when this path works alongside the failure/recovery cases, and the owner can leave the team enabled without managing every agent turn or receiving an unbounded backlog.\n\n\n## Implementation validation record (19 October 2026)\n\n- PR preparation synchronized the implementation with `7933ecf` (Kilo/Cline ACP connections). Review corrected newest-run selection, retained full revision feedback without dropping acceptance criteria, and made archived history immutable through API decisions and recovery. All three regression tests reproduced the issues before the fixes and pass afterward.\n- TypeScript typecheck and both UI/host builds pass.\n- Full regression run after synchronization and review fixes: 1,072 tests, 1,065 passed, seven environment-gated skips, zero failures.\n- Additional focused runs after implementation review cover the new lifecycle/fault cases, notification and automation regressions, restored records, shared access boundaries and exact-SHA publication. All pass; counts overlap the full suite and are not additive.\n- Browser regression selection: 23 tests passed across project teams, Auto team, automation controls, shared stream connection limits, shared projects, filesystem policy, notifications, settings and accessibility.\n- The later full browser run recorded 95 passes, one memory-graph refresh timing failure, and one shared-project Chrome teardown failure after its six assertions passed. The memory test now waits for the published graph node; its three-test file passes. The shared-project file also passes all six tests and exits normally on an isolated retry. The full browser command is not recorded as a clean pass; CI must confirm the complete run.\n- Shipping/GitHub regression selection: 40 tests passed.\n- PR #29 CI follow-up: both browser jobs timed out after the standing-team assertion passed. Its fixture now owns the Node server directly, drains stdout, waits for shutdown, and removes temporary data after exit; the isolated browser test passes and exits normally. The PR merge path also rechecks the managed-result approval gate. Regression tests reproduced rejected-result, changed-mission and archived-team merge bypasses before the fix; all three are blocked afterward, and an unchanged approved result still merges and becomes shipped. Typecheck and the 40-test project-team/GitHub selection pass after these fixes.\n- The next complete browser run exited normally with 95 passes and one Kilo/Cline settings-search timing failure. Settings now clears the search before the section becomes interactive, and the preset test waits for the destination section before typing. All seven settings browser tests and typecheck pass after that correction; the complete browser suite and CI are rerun for the final commit.\n- Final browser validation: all 96 tests pass locally, and both GitHub browser jobs pass at `7a100f5`. One Ubuntu Node 22 job exposed the cancellation fixture reading an in-flight launch revision; the test now waits for the durable running receipt and queued reconciliation before issuing its owner decision. Fixture teardown also shuts down run managers before deleting data. The corrected cancellation test passes three consecutive runs, the complete project-team file passes all 25 tests, and typecheck passes.\n- A later CI run reproduced the shared-project Chrome teardown stall after all six tests passed. That fixture now owns Chrome through `BrowserServer`, terminates the owned browser process during teardown, and waits for bounded server shutdown before deleting its files. Shared-project and SSH browser files pass together (seven tests), and typecheck passes. An isolated SSH fixture connection failure also passes on local retry; it remains covered by both complete CI browser jobs.\n- Synthetic pilot: ten cycles across two isolated fixtures; four child launches, 14 observed fake CLI calls, two verified drafts and eight no-ops. The redacted measurement summary is [saved here](evaluations/project-teams-synthetic-2026-10-19.json).\n- Real CLI/provider quality, real product usefulness and owner acceptance have not been measured. These remain explicit rollout gates, rather than inferred from synthetic output.\n\n[Documentation library](README.md) · [Development guide](architecture.md)\n","title":"Standing project teams implementation plan"},{"group":"Engineering records","id":"performance-results","source":"docs/performance-results.md","body":"# Measured performance: 19 October 2026\n\n> Engineering record. Status, measurements and decisions below describe the recorded implementation or review. For current usage, start with the [documentation library](README.md).\n\nThe architecture changes improved latest-transcript opening by **1.9×** with\n1,000 events and **27.6×** with 5,000 events on this machine.\nThe experimental Rust reader showed no consistent end-to-end benefit and increased\ntranscript API latency, so production retains TypeScript. These numbers describe\nsynthetic history/transcript operations, not provider inference or total coding-task\nduration.\n\n## Comparison\n\nMedians of seven fresh-context Chrome trials after one unmeasured warmup per variant.\nThe endpoint is Playwright’s DOM visibility check for the latest event after opening\nShow work; timings include automation polling and are not precise paint timings.\n\n| Workload | Before | Optimized TypeScript | Optimized + Rust experiment |\n| --- | ---: | ---: | ---: |\n| 20 runs, 1,000 events: latest transcript visible | 444.45 ms | 232.94 ms | 234.59 ms |\n| 200 runs, 5,000 events: latest transcript visible | 7,124.48 ms | 257.77 ms | 320.41 ms |\n| 200 runs, 100,000 events: latest transcript visible | Timed out (30 s heading wait) | 270.34 ms | 340.54 ms |\n| 1,000 events: transcript API | 129.52 ms | 5.53 ms | 9.56 ms |\n| 5,000 events: transcript API | 6,314.58 ms | 6.58 ms | 10.46 ms |\n| 100,000 events: transcript API | Not measured after UI failure | 7.62 ms | 11.85 ms |\n| 20 runs: history search API | 2.50 ms | 1.99 ms | 1.97 ms |\n| 200 runs: history search API (5,000-event fixture) | 5.50 ms | 3.99 ms | 3.33 ms |\n\nAPI medians contain seven requests per successful variant/workload (one per browser\ntrial). The old transcript endpoint replays the entire history over SSE; the new\nendpoint returns a recent page of at most 500 events. These are deliberately the\npaths used to reach the same user-visible latest output, **not equal-size response\nbenchmarks**. Earlier events remain accessible through pagination. Reduced work,\nindexed repository membership and smaller responses explain the gain; it is not a\nlanguage-only comparison.\n\nThe baseline stress attempt failed before the run heading became visible, with no\nbrowser JavaScript exception. It was stopped before timing the other variants to\navoid background CPU interference. A timeout is not a precise timing or speedup\nratio. No baseline stress API or heap result is inferred.\n\n## Resource tradeoffs and variance\n\n| Browser metric | Before | Optimized TypeScript | Rust experiment |\n| --- | ---: | ---: | ---: |\n| 1,000 events: decoded bytes through latest output | 2.02 MiB | 1.66 MiB | 1.66 MiB |\n| 5,000 events: decoded bytes through latest output | 3.56 MiB | 2.02 MiB | 1.85 MiB |\n| 1,000 events: JS heap after GC | 4.35 MiB | 4.60 MiB | 4.61 MiB |\n| 5,000 events: JS heap after GC | 5.74 MiB | 4.73 MiB | 4.75 MiB |\n| 100,000 events: JS heap after GC | Unavailable | 4.76 MiB | 4.76 MiB |\n\nSmall-history heap increased **5.7%**; at 5,000 events it decreased\n**17.6%**. The cache and paging state have a fixed cost. All successful\nvariants rendered 200 transcript rows in this fixture; the improvement is not\nexplained by comparing different visible row counts. Received bytes include decoded\nassets and stream traffic, not compressed wire bytes. Heap is the Chrome JavaScript\nheap after forced GC, not RSS or total system memory. Startup was sampled only once\nper server and is retained in the raw data, not used for a speedup claim.\n\nBrowser latest-output ranges (min–max):\n\n| Events | Before | TypeScript | Rust |\n| --- | ---: | ---: | ---: |\n| 1,000 | 413.63–457.03 ms | 213.88–247.74 ms | 225.48–247.21 ms |\n| 5,000 | 6,456.07–8,098.10 ms | 227.98–263.78 ms | 242.07–342.26 ms |\n| 100,000 | Timeout | 260.06–351.29 ms | 262.73–344.04 ms |\n\nThis is a developer laptop, not an isolated benchmark host. Other applications and\nOS activity contribute noise; initial system load averages were\n5.62, 5.59, 6.68. Variant order rotates each round and filesystem caches are not\nflushed. Small sub-millisecond differences in history API timings should not be\ninterpreted as reliable Rust benefits. Raw samples are provided rather than a\nstatistical-significance claim.\n\n## Rust decision\n\nThe dependency-free release-mode Rust helper reads the same file backwards and\nreturns original records and byte offsets over a pipe. The same TypeScript code\nthen validates and constructs the page. Tests verify parity for Unicode, malformed\nrecords, large previews, step filters, reconnects and pagination. Benchmark timing\nincludes actual process creation, IPC and termination.\n\nAgainst optimized TypeScript, Rust transcript API medians were **73% slower**\nfor 1,000 events, **59% slower** for 5,000, and **56% slower** for 100,000.\nBrowser samples vary; a small median advantage in an individual workload does not\nestablish a consistent user-visible improvement across workloads.\nThe optimized bounded scan is already only a few milliseconds; this process boundary\nadds more cost than it removes. This does not establish that Rust itself is slower,\nor predict the performance of a persistent native worker, an in-process extension,\nor a full rewrite. The experiment remains reproducible but is not enabled in the\nproduction app and adds no Rust installation requirement for users.\n\n## Reproduction and scope\n\n- Baseline: `c300a90ea9c71f940ac9b504ad8b3513b270f257` (main at measurement time, including SQLite memory, result previews and browser scenarios).\n- Measured optimized implementation: `0081bb76c33c86c2db16ca04b862ab490cf1920e`.\n- Host: Apple M4, 10 cores, 16 GiB, macOS arm64; Node v22.14.0; Chrome 155.0.8059.80; rustc 1.90.0 (1159e78c4 2025-09-14).\n- Same synthetic fixtures, provider stubs and dependency installation for all variants.\n- No real user history, provider requests or external service latency.\n- The measured code includes the live-page race fix and the integration with the\n SQLite memory and result-preview updates from main, plus the archived-compaction\n and shutdown fix found by CI. Earlier pilot results are superseded by this run.\n\n```sh\nnpm run benchmark:compare -- --baseline c300a90ea9c71f940ac9b504ad8b3513b270f257 --trials 7 --api-repeats 1\nnpm run test:rust\n```\n\nThe harness requires Chrome and Cargo for the experimental third variant. See\n[raw samples and environment metadata](benchmarks/2026-10-19-performance.json),\n[resource limits and architecture](performance.md), and the\n[Rust experiment](../experiments/rust-transcript/README.md).\n\n[Documentation library](README.md) · [Development guide](architecture.md)\n","title":"Measured performance: 19 October 2026"},{"group":"Build and extend","id":"vscode","source":"integrations/vscode/README.md","body":"# Code Flow for VS Code\n\nUse the local VS Code integration to create tasks and inspect Code Flow results without leaving your editor. The Code Flow server must be running and authenticated before the extension can connect.\n\nConnect a local VS Code window to a running Code Flow server. Start a Safe solo task\nfrom the current saved file and selected text, inspect recent run states, open the\nfull review in Code Flow, stop a running task, or open a result worktree in a new window.\n\n1. Start Code Flow and read its local URL and token from the server startup output.\n2. Install the locally built VSIX through **Extensions → Install from VSIX**.\n3. Run **Code Flow: Connect to Local Server**, enter the loopback URL and token.\n4. Open a trusted local Git folder, then run **Code Flow: Start Task from Editor**.\n\nTokens use [VS Code SecretStorage](https://code.visualstudio.com/api/references/vscode-api#SecretStorage).\nThey are not put in workspace settings, URLs or logs. Use **Forget Server Token**\nto remove a connection. Server URL configuration is application-wide so a repository\ncannot silently redirect the extension to another service. The client refuses\nredirects, non-loopback endpoints, unsupported API versions and oversized responses.\nAn SSH tunnel can expose another Code Flow instance as a local endpoint when its\nforwarded port matches the server's Host validation.\n\nTask creation requires workspace trust and local `file:` folders. Remote VS Code\nworkspaces and virtual files are unsupported in this first integration. The task\nuses detected verification, Auto provider selection, bounded execution, no copied\nignored files and interactive tool approval requests. Current-file context excludes\ncredential filenames and paths outside the selected folder. Unsaved files require\nan explicit save before dispatch. Review code and verification in Code Flow before\nshipping a result.\n\nThe Explorer view shows the recent 100 server runs, filtered to local workspace\nfolders, and refreshes every ten seconds while visible. It displays verification\nand approval state but does not approve permissions from the editor. Open the full\nrun for those decisions. Stopping a task asks for confirmation. Creating a task can\nconsume the configured provider's quota; the extension never starts one on activation.\n\nFrom the Code Flow repository:\n\n```sh\npython3 scripts/package-vscode.py\nnode scripts/test-vscode.mjs\n```\n\nThe package is written to `output/vscode/`. Packaging uses Python 3's standard\nlibrary. Host acceptance uses an installed `code` executable (or\n`CODEFLOW_VSCODE_BIN`), an isolated temporary profile and extension directory, and a\nreal Code Flow server with fake agents. It packages and installs the VSIX first,\nthen exercises that installed artifact's activation, command registration, token\nstorage, task execution, refresh, token deletion and delayed connection responses.\nSee [VS Code extension testing](https://code.visualstudio.com/api/working-with-extensions/testing-extension).\nNo marketplace publishing or changes to the user's installed extensions are performed.\n\n[Documentation library](../../docs/README.md) · [API and CLI](../../docs/api.md) · [Getting started](../../docs/getting-started.md)\n","title":"Code Flow for VS Code"}]
|