The code builds, 632 unit tests and 126 real-browser journeys pass, and the hub renders in 65 ms. Meanwhile shipping has fallen from 83 commits a day to zero, 46 branches of finished work sit unmerged, and the machinery built to guarantee correctness is now the thing preventing delivery.
Reading PRODUCT.md, PLAN.md, README.md and docs/CONSTITUTION.md together, the intent is coherent and genuinely distinctive:
Chrome itself becomes the agent runtime. Not a chat window that happens to live in a browser — the new-tab page is replaced by a command center where you start a task, watch it run, and drill into what the agents did. Everything an agent needs is a browser primitive: tabs, alarms, OPFS, capture, the page's own JavaScript.
Four ideas carry the product:
memory_grep tool. A master hub agent creates, delegates to, and deletes them. Agents are durable identities, not chat sessions.agent.md, or inferred from window.* functions. The agent discovers them and invokes them with per-tool first-run approval. This is the bet that makes the whole thing interesting: the web becomes the tool catalog."permissions": []. Every capability is requested on a real user gesture at the moment of need. No debugger, no <all_urls>. This is the hardest self-imposed constraint in the project and it shapes almost every design decision downstream.Around that sits a design position — "Operate. The tool must disappear into the work" — warm-neutral paper, one petrol-teal accent, inline SVG icons, no decorative motion. And a delivery position: never accept "it serves" as "it works"; drive real behavior in a real browser; independent review by a different model before push.
The plan is good. It is not the problem.
I built and drove the current public head rather than trusting the trackers. Everything in this section is something I ran.
| Build | node build.mjs succeeds. Bundles the AI SDK + zod into a 2.8 MB service worker, strips one new Function site and two probes, runs an AST scan over 72 shipped files, syncs the component gallery. |
| Unit | 633 pass, 0 fail — but only with TMPDIR pointed at durable storage. On the host's default temp filesystem the suite dies at tests/cdp-client.test.ts with "No space left on device". Same commit, same code, two results. |
| Browser | scripts/chrome-journeys.ts: 126/126 pass against a real loaded MV3 extension — enrollment, disenroll races, management tools, owner approvals across a service-worker restart, no SW console errors, clean profile teardown. |
| Render | Hub reaches domContentLoaded in 62 ms, load in 65 ms. Well inside the 1 s budget. |
| Boot posture | Fresh profile: zero granted permissions, zero CSP violations, one warning ("storage permission not granted — changes are session-only"). |
The hub renders correctly and looks like the product brief: paper ground, teal accent, SVG icons, a composer, agent groupings, artifacts, activity. agent-do really is imported from npm and bundled — lib/agent.js wraps the library rather than reimplementing it. The security model in the code is not theatre: sender authorization derives origin from the content script's own realm, page senders hit a route allowlist, tool descriptors get zod-validated, untrusted strings go through escapeHtml/escapeAttr at all 65 innerHTML sites I checked.
This is above-average extension engineering — better reasoned than most production MV3 code I've read. The comments explain threat models and race conditions rather than restating the line below them. Whatever has gone wrong here, it is not code quality.
What it is not yet: a product anyone can pick up. On first install you get an empty hub, a red error badge, and no onboarding. Nothing works until you find Settings → Permissions and Settings → Providers yourself. The Agent Directory page ships unstyled with no navigation. The side panel's Page tab is a URL field and a status line.
You said it feels off the plan. Here is the shape of it, measured.
origin/main, per daypi sessions are running right now, seven of them continuously for more than a day. Zero Chromium processes are running, so no browser acceptance is in progress either.The collapse starts on 19 August. That is the day bbeff7b docs(project): integrate crash-recoverable task tracker landed, and it is the day after the intercom handoff lifecycle became mandatory in your journal's CLAUDE.md.
Those two artifacts are individually reasonable. Together they created a delivery process with the following properties:
The lifecycle is OPEN → IN_PROGRESS → REVIEWING → REVIEW_PASSED → READY_FOR_BROWSER → INTEGRATING → GATED → PUSHED → CONFIRMED. Nine states. CONFIRMED requires explicit product-owner confirmation — you, personally, per task.
Across 31 tracked tasks: zero are CONFIRMED. Four sit at PUSHED with "Next: obtain confirmation" — some since 18 August. Because nothing can retire, the tracker only grows, and every new task inherits a dependency graph of unretired ancestors. Six of the fifteen currently-open tasks name another open task as a blocker.
Read the tracker's own language. CAP-FB-20260819-AGENT-DIRECTORY-01: "READY_FOR_BROWSER only on old base; not deliverable… the accepted Directory delta must be recreated and independently checked on ffbdf28 before browser use." The same sentence appears on ARTIFACT-TX-01 and USAGE-RECORDING-01.
Work passes review, main advances, the review is voided by the base change, the work is recreated on the new base, and it needs review again. With serialized integration and one browser queue, a lane can never catch up. This is the mechanism that turned finished code into 46 permanently-stale branches.
The handoff ledger holds 322 records. None are closed. The distribution is the tell:
| Handoff state | Count | What it means |
|---|---|---|
| BLOCKED | 152 | Stopped, awaiting an owner or a predecessor |
| FIX_REQUESTED | 82 | Review rejected it; correction owed |
| INTEGRATING | 62 | Queued behind the serialized browser gate |
| REVIEW_PASSED | 15 | Approved, not integrated |
| PUSHED | 5 | Shipped, unconfirmed |
| REVIEWING / RECEIPT_ACKED | 6 | In flight |
72% of all handoffs are BLOCKED or FIX_REQUESTED. The fleet is spending its time producing versioned acceptance packages — v7, v8, v9, v10, v11, v14, v15, v16 of the same lane — that get statically reviewed and rejected before a browser ever runs. In your journal's project file the phrase "no browser result exists" appears repeatedly.
This is the finding I'd act on first, because it means the tracker is actively misleading you.
Fifteen tasks read Owner: unassigned, Branch: none, Candidate: —. At least nine of them have committed implementation work sitting in branches right now:
| Task (tracker says OPEN / unassigned / no branch) | Work that actually exists |
|---|---|
| CONVERSATION-RUN-STATUS-01 P0 | feat/overnight-conversation-status-nub |
| COMPOSER-AGENT-MENTIONS-01 P1 | feat/composer-agent-mentions (+3), b4a0a6f |
| DURABLE-BACKGROUND-RUNS-01 P0 | 4 branches, up to 6 commits ahead |
| PERMISSION-REMEDIATION-UX-01 P0 | docs/permission-remediation-design (+5) |
| AGENT-DELETION-LIFECYCLE-01 P1 | docs/agent-deletion-lifecycle (+3) |
| SITE-AGENT-STATUS-CLEANUP-01 | fix/site-agent-copy-status-cleanup (+2) |
| LOCAL-MODEL-MANAGEMENT-01 | feat/gemma4-catalog-preflight |
| PAGE-SCOPED-SITE-IDENTITY-01 P1 | 75e2acf (detached, /tmp only) |
| DIRECTORY-TOOL-EXPLORER-01 P1 | 3 directory branches |
The join key is broken at the root: the tracker exists so a stable CAP-FB-* ID links a task to its commits. 2 of 430 commits mention a CAP-FB ID. Every Recover: field in the tracker prescribes a git grep that will not find its own work.
Seventeen worktrees sit at exactly origin/main with zero commits. Ten of them are /tmp/cap-page-identity-directory-prep-v3 through -v13 — identical checkouts of the same tip, no work in any of them. That is a loop: create a versioned prep directory, fail a static review, increment the version, repeat. The same shape appears in the scheduled-task lane (v14, v15, v16) and the durable lane (v9, v10, v11).
That is what your dissatisfaction with the other agent is actually measuring.
df -i /tmp reports 1,043,303 of 1,048,576 inodes used. The same unit suite on the same commit is 633/0 with TMPDIR on durable storage and fails outright on the default temp filesystem — so any gate result produced on this machine right now measures its disk, not the code. It will fail any process needing a temp file, not just this project. Roughly 60 full git worktrees live in /tmp at about 7,560 inodes each, alongside 235 cap-picker-full-* and 104 cap-run-status-evidence-* retained evidence bundles.
/tmp is tmpfs — RAM-backed. A reboot destroys all 60+ worktrees and every retained evidence bundle referenced by the tracker's Gates: fields.
Seven detached worktrees in /tmp held commits not reachable from any branch. The objects live in the main repo, but the only refs pointing at them were the worktree HEADs — on reboot they become unreachable and eligible for git gc. One is b4a0a6f feat(composer): route agent mentions canonically across named, background and site agents — implementation of a task the tracker calls unstarted.
I tagged all seven as rescue/tmp-detached-* in the main repository. That is additive and reversible; nothing was deleted. Run git tag -l 'rescue/*' to see them.
The run-task route calls runTask() directly inside chrome.runtime.onMessage. There is no persisted in-flight run record; activeRuns in lib/scheduler.js is an in-memory Map. The service worker's own comment is explicit: "The port keeps the SW alive while a page is listening."
So a long agent run's survival depends on a UI page holding the agent-progress port open. Close the hub tab and the run is gone — tool side effects already committed, no result, no resume. Tool calls are journalled for replay, but the run cannot be reconstructed from them.
This is CAP-FB-20260819-DURABLE-BACKGROUND-RUNS-01, correctly rated P0, and it has four branches of work against it while the tracker lists it unassigned.
New profile, zero permissions: the hub shows an empty state and a red error-console badge reading 1. The entry is honest — "storage permission not granted — changes are session-only until enabled in Settings" — but a first-time user reads a red badge as a fault.
The deeper issue is what "session-only" means under MV3. Without the optional storage permission, lib/kv.js degrades to an in-memory Map owned by the service worker. MV3 terminates idle workers after roughly 30 seconds. So a user who enters an API key without granting storage loses it almost immediately, with no warning at the point of entry. There is no onboarding flow anywhere in the extension — I grepped for one.
In ntp/ntp.html, .panel-body { padding: 4px 0; } — no horizontal padding, so each row supplies its own. #webmcp-hub-status carries the panel-body class but has no CSS rule anywhere in the repo, and ntp.js:113 writes straight into el.textContent. Result: "Discovery has not run yet." sits flush against the panel edge, roughly 14px left of every sibling row, breaking the card boundary.
Visible in the screenshot of a clean build of current main. Not in any tracker.
ntp/ntp.js:1192 still binds document.getElementById("run-status") and sets a generic top-of-thread state. CAP-FB-20260819-CONVERSATION-RUN-STATUS-01 is P0 precisely because ffbdf28 fixed lifecycle ownership fencing and was mistaken for fixing the presentation. Worth confirming this distinction is understood before the next attempt — it is the kind of thing that gets re-closed without being fixed.
background/service-worker.js is 4,799 lines exposing 127 message routes in one flat handler object. shared/components.js is 5,193 lines with 33 custom elements. options/options.js is 1,775 lines.
This is why every change needs cross-subsystem review and why merges conflict so readily — the serialized integration queue is partly a consequence of this shape. It is also why AGENTS.md needs a rule reminding agents to grep for couplings: the couplings aren't expressible in the module structure.
The UI says "Skills". The code ships extension/recipes/, lib/recipes.js at 655 lines, and options.js importing RECIPES — while lib/skills.js is 39 lines. ntp.js:1586 reads openView("recipes/index.html", "Skills"). Your own AGENTS.md cites this rename as the example of cross-subsystem drift; it's still drifting.
mock/ and its copies in docs/. Six stale HTML design mocks, duplicated into the published site. The real extension supersedes them.docs/index.html was titled "Chrome Agent Platform — UI mocks" and linked dead mocks. RESOLVED 2026-08-22: the mocks are withdrawn (CAP-FB-20260821-DEAD-SURFACE-REMOVAL-01); the root is a CSP-safe accessible redirect to the component gallery with a visible fallback link + the real product docs.This is the genuinely novel claim and the one nothing else does. Right now it's buried behind an unstyled Directory page and a discovery status line that renders outside its card. A curated set of five real origins that expose useful WebMCP tools, reachable in two clicks from the hub, would demonstrate the entire thesis in thirty seconds. Everything else in the product is table stakes by comparison.
"permissions": [] is a real differentiator against every other agent extension, and it is currently experienced as friction. The docs/permission-remediation-design.md work already specifies the right thing: a failed run names the exact tool, capability, origin, and rationale, then offers the minimal grant and resumes the same run. Shipping that turns your hardest constraint into the thing people notice first.
CAP-FB-20260820-SEMANTIC-TOOL-SEARCH-01 is well-aimed. With 127 message routes, a management toolset, and an unbounded catalog of per-site WebMCP tools, context pressure is going to be the binding constraint long before model quality is. A local hybrid semantic + lexical top-k over the tool catalogue is genuinely load-bearing. I'd raise its priority — but only after durable runs, because it is meaningless if runs die when a tab closes.
Three steps: pick a provider and paste a key, grant storage in the same gesture, run one seeded example task that produces a visible artifact. That single path converts an empty hub with a red badge into a working demo, and it is a day of work.
The 127 routes cluster cleanly — agents, memory, provider, tools/WebMCP, artifacts, permissions, diagnostics. Extracting them into route modules with a thin dispatcher would let lanes touch disjoint files, which is the actual fix for the merge-conflict tax that the serialized queue is compensating for.
Short answer: about ten distinct lanes are live. The other ninety are residue. Counts below exclude your main checkout and the temporary worktree I made for this review, leaving 98.
| Class | Count | Disposition |
|---|---|---|
| Genuinely active HEAD commit dated 20–21 Aug |
14 | About ten distinct lanes once duplicate pairs collapse: durable product successor (+6), durable policy (+5), scheduled-task v3, Gemma 4 preflight, page-directory snapshot, provider-perm, docs. Keep. |
| Stale HEAD commit before 20 Aug |
67 | Includes real reviewed work: usage-retry-fix (REVIEW_PASSED), picker-harness-cdp (+25), integrate/provider-5e5c81e (+28), assets, artifact-tx, directory. Triage: land or abandon. Do not rebase again. |
| Zero work sitting at origin/main exactly |
17 | Ten are cap-page-identity-directory-prep-v3…v13. Delete — nothing is lost. |
| Redundant copies sharing a HEAD with another worktree |
27 | 73 distinct HEADs across 100 worktrees. e.g. cap-durable-product-successor-fa3c51c and -2998258 both sit at 2998258. Keep one each. |
| Orphan-risk detached /tmp-only refs |
7 | Now tagged rescue/tmp-detached-*. Safe to prune the worktrees. |
Your primary checkout at ~/chrome-agent-platform is on a detached HEAD at 5e5c81e, eleven commits behind origin/main. If you've been inspecting the product there, you have been looking at 19 August's build, not 20 August's. It's also missing fake-indexeddb because its node_modules is a symlink into /tmp/cap-assets-ui/ — a directory that vanishes on reboot.
The machine is degraded now. Delete the 17 zero-work worktrees and the retained evidence bundles, then move all remaining worktrees to ~/cap-worktrees/ so a reboot stops being a data-loss event. The rescue tags mean nothing is lost either way.
git checkout main && git pull && npm install. Stop testing eleven commits behind.
RUN-STATUS, WEBMCP, AGENT-ACCESS, SYSPROMPT are all shipped and waiting only on you. The 126/126 journey run in this review is the regression evidence they're blocked on. Closing them unblocks the three tasks that name them as dependencies.
Nine "unassigned" tasks have real branches. Write those branch names and commit SHAs into their entries. Then make CAP-FB-… in the commit subject a hard rule — today it's 2 commits out of 430, which means the tracker's own recovery instructions don't work.
The rebase treadmill is the single largest waste. Stop advancing main, land or abandon the ~37 stale branches against a fixed base, then reopen. Nothing else fixes the "reviewed on an old base, must be recreated" loop.
OPEN → IN_REVIEW → MERGED → DONE. Keep the two rules that are load-bearing — a different model reviews, and a real browser drives it — and delete the rest. Nine states with content-addressed gate evidence and remote attestation has produced 322 open handoffs and zero confirmations. Optimise for one shipped increment per day, not for an unfalsifiable audit trail.
Durable runs first because everything else is unreliable without it. First-run second because it converts the project from a thing you can inspect into a thing someone else can use. Both already have code or a design doc; neither needs new research.
The Chrome Agent Platform is a well-built, genuinely novel extension that passes every gate you set for it, and it stopped shipping because the process you built to protect its quality now costs more than the quality it protects — so the fix isn't more engineering, it's retiring the tracker, landing the 46 branches of finished work, and getting back to one visible increment a day.