Skip to content

feat(research): share page reads and web search across environments - #560

Merged
lukemaj merged 8 commits into
mainfrom
final/423-web-research
Oct 19, 2026
Merged

lukemaj merged 8 commits into
mainfrom
final/423-web-research

Conversation

@lukemaj

@lukemaj lukemaj commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

What changed: Agents can share page reads through a linked research host, continue full-text snapshots, search by locale/site, and explicitly opt into an existing browser session.
Why: Separate harness fetches repeated work and lost shared cache, provenance, continuation and provider-health information.
So what: This cumulative train targets main; merge is preauthorized after current full CI and independent Claude Opus 5.5 HIGH review, followed by separate staging with no Chromeria restart tonight.

Fixes #423
Fixes #444
Fixes #445
Fixes #446
Fixes #447
Fixes #562
Part of #443; retention/pruning stays OPEN before activation under #486.

Result and scope

web_read routes to the configured linked host and returns immutable cache identity, provenance, bounded output and continuation. An unavailable configured host returns a typed error without silently fetching locally. Thin public pages use isolated rendering then a pinned zero-charge TinyFish fallback. Hosted continuation is memory-only for 15 minutes, 32 entries and 64 MiB; hosted bodies never enter persistent PageCache. web_search merges providers with locale/site controls and visible engine/provider health. Session reads require explicit opt-in and a selected persistent profile/tab; their text never enters public PageCache.

Browser identity #460, extraction performance #538, retention policy #486 and Observer Muse support toolboxmd/agent-observer#127 stay separate. No tool-denial/pacing policy, service change or deployment is included.

Reviewed components

Ticket PR Exact reviewed head Opus HIGH verdict Integration merge
#443 #455 9bc9608 R5 PASS 818c4e1
#444 #502 183dfc5 R3 PASS 399ba5a
#445 #505 3393e88 R2 PASS d5b7163
#446 #513 ad38c77 R2 PASS 8bf0a58
Thin-JS correction #535 93a5f85 R2 PASS 06cd67f

#449/#440 core, #454/#441 subpages and #453/#442 rendering already merged into main (c1d3960/def07e92/f27d8ee1 respectively). Five remaining components are replayed linearly onto current main e760e6a, including #556 durable Prism recovery, #558 scoped outside-agent grants and #559 thread tool allowlists. Reconciliation preserves browserCookieMirror and webResearch exports and both fork inventories; range-diff otherwise shows identical patches. No merge commit or version bump.

Combined acceptance and limits

Canonical combined proof includes the original 20-case table, fixed 25-query outcomes, exact source identities, counts and failure history. Old live runs retain 8bf/900a identities, not this rebased final commit.

  • 20 URLs: 14 root ok, four robots-blocked, two origin errors; default traversal, partial subpages and output bounds retained. Continuation/TTL repeat preserved original hash/fetchedAt.
  • 25 sequential query/locale calls: SearXNG health observed, DuckDuckGo errors on q12/q22; isolated TinyFish key absent, so search explicitly degraded. No judged semantic quality or live TinyFish-search success claimed.
  • Actual pinned hosted fetch has independent reserve 0/settle 0/charged 0 ledger, although content stayed thin. Separate corrected anonymous render returned 12 named products/prices; conservative helper rejected residual instruction text while actual content inspection accepted useful render. No helper edit or repeat hid that distinction.
  • Codex/bestia, Grok/rocky, Claude/bestia and Muse/bestia share one representation. Grok continuation returned the remaining text; a stopped-host call returned research_host_unavailable. Captured processes stopped and ports were free.
  • Accepted TinyFish boundary: documented private-target/redirect protections plus local checks; provider internal DNS/rebinding/subresources remain UNVERIFIED. Caller/native histories are outside PageCache retention guarantees.
  • User accepts actual Muse native/MCP proof instead of unavailable Observer counts; a separate agent owns Observer feat(server): continue idle threads in Wight mode #127. Muse is tested, not deferred; raw native/MCP results supplement its lossy product projection.
  • Logged-in browser live proof is explicitly user-deferred; no logged-in profile used. Live cancellation remains unproven supplemental coverage. No exhaustive helper-descendant or packet-level zero-network claim. feat(research): share bestia research host across cavallo and rocky #443/fix(research): bound page-cache retention before shared-host activation #486 remain pre-activation retention gates; freshness is not deletion.

Current proof and remaining gates

Current head656b4c620c759df006bf1b57d11f0f52cb3cf046, tree10ad15a3cca5f0c2fee51d9bd54087c36092a6e9, base main e760e6a (#558/#559). One additive allowlist conflict preserved both new main auth paths and research's PreviewAutomation path; the existing Browser ownership correction remains. Range-diff changes otherwise concern map/allowlist context, with research runtime patches unchanged. Current Node24 proof:10files/141testsPASS covering research, session reads, outside grants, OAuth, tool allowlist and recovery/contracts; scoped server types exit0; actual complete fork model PASS55features. Historical Opus5.5 HIGH R4 PASS on122687 is followed by exact-current Opus HIGH R5 PASS, no required findings, authenticated review/independent success56737660886. CI39332329579 remains pending. A one-line test-fixture completion for #562 is the only change since R4; details below.

R1 cumulative review found only duplicate Browser inventory ownership. Fixed: main's Chrome-login feature remains the single owner; research uses shared dependencies, duplicate allowlist entries removed. Optional closing-PR560 map ref removed. R2 confirmed the fix/no new required finding, but posted error because main advanced during review; no stale PASS is reused. R3 passed on15ae/base3c2, then main advanced again with #558/#559. That PASS is historical; fresh R4 reviews the new current head and both newer base interactions.

Reused unchanged research feedback:16files/317passed/6pinned-browser-prerequisite skips,19fork-maintenance fixture tests and server types on e227; Mermaid3PASS onfc9. The19fixtures did NOT cover the actual registry failure on e227/d531; the direct complete fork model is now separately executed and green. Live observations retain8bf/900a source identities; current focused integration checks and full CI cover later base changes. No complete-current merge-gate claim from affected checks alone.

Website impact: none outside shipped user documentation; affected guidance is updated in docs/user/remote-access.md and fork feature docs. No visible UI change requiring screenshots. Package versions stay upstream-owned under AGENTS.md. Private transcripts, credentials and raw page bodies are not committed/uploaded.

CI stall repair (#562)

The main #556 thread-read change introduced a required readRunRecovery call, but the RemoteDelegation test's box mock lacked it. Effect's missing-method failure was retried by the follower, so multiple tests each exhausted120seconds and the server shard exceeded10minutes. Both final-PR attempts and subsequent main runs showed the same file stall; the last pre-#556 main run passed. The repair supplies { state: "none" } in that test mock, matching the normal no-recovery fixture. No runtime, assertion or timeout change.

Faithful unchanged-copy RED timed out; the single-line variant passed39/39 in56.09s. Final656b exact source passes39/39 in57.52s, server typecheck exit0, targeted format PASS. Targeted lint did not execute because local worktree cannot resolve @oxlint/plugins; full current CI owns lint, with no exception claimed. Canonical repair handoff. R5 passed; complete CI, including the transfer artifact, is still required; no third blind retry or timeout increase.

Elon record

Requirements and who asked: Maintainer #423/#440–#447 outcome, reviewed components, latest final Opus 5.5 HIGH instruction, preauthorized merge after gates, no app restart tonight.
Deleted: Repeated per-harness fetches, silent local fallback, unauthorized persistent hosted bodies, forced built-in denial, new-login work and unrelated Observer/identity/GC scope.
Bottleneck: Full CI after the diagnosed one-line test-fixture repair and passing exact-head R5; agreed live observations and boundary decisions remain recorded with explicit limits.
Checked myself: Remote/GitHub merge targets and heads, component reviews, range-diff/reconciliation, focused Node24 feedback, immutable manifests and actual native/MCP outputs, captured process cleanup, user decisions and separately owned follow-ups.

Implementation: GPT-6.1-Sol/Codex. Component reviews: Claude Opus 5.5/Claude Code. Coordinator: GPT-6.1-Sol/Codex. Final cumulative Claude Opus 5.5 HIGH R5 PASS on656b4c620c759df006bf1b57d11f0f52cb3cf046; historical R4 PASS is not reused as the current gate.

@lukemaj

lukemaj commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

What: Current Fork Stack Model failure is reproduced: ServerBrowser.test.ts and ServerBrowser.ts each have two exclusive feature owners after final-main reconciliation.
Why: Research #446 and main's Chrome-login mirror #520 both list those files under upstreamFiles; the fork model requires one owner, with other consumers recorded under sharedFiles.
So what: Coordinator owns the correction: keep main's existing Chrome-login owner and move the research references to sharedFiles, then obtain fresh exact-head review/CI. The current cumulative review is still reading frozen d531; a four-line metadata-only patch is prepared outside the worktree for application with any findings once that round returns.

Actual command on d531: task Node24 node scripts/fork-features.mjs check exits1 with fork-features: FAIL: apps/server/src/preview/ServerBrowser.test.ts: expected exactly one feature owner, found 2, matching CIjob118305379889. Independent source registry scan finds exactly the two duplicate paths, each agent-web-research/chrome-login-mirror. No runtime/source behavior, test assertion or fork policy change is proposed. The19 passing fork-maintenance unit tests exercise fixtures and did not validate the actual full registry; current-head real registry check remains failed, not waived.

Requirements and who asked: User requires complete current fork/CI gate before final-main merge.
Deleted: CI retry without a change, policy weakening, removing browser behavior or editing the reviewed source during its active frozen round.
Bottleneck: Integration feature metadata introduced by reconciliation, plus current cumulative review findings before one corrective push.
Checked myself: Full failed-job log, actual owner-validation implementation, both registry entries, direct failing command and exact prospective patch. Current source/head remains d531; no PASS or main merge claimed.

@lukemaj

lukemaj commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Independent cumulative review: FAIL (1 required fix)

What: The exact head d531826dcfbffdf2445ff7e4e95a8286b60d64fa (tree a5c39bb4, base c8b69c64) has one blocking defect: the fork inventory now gives two owners to ServerBrowser.ts/ServerBrowser.test.ts, so the required Fork Stack Model check fails. I found no other supported blocker in the cumulative 40-file diff.
Why: The rebase auto-merged #446's upstreamFiles claim on those two files with #520's BrowserCookieMirror claim, which main already owns. fork-features.mjs check requires exactly one owner per allowlisted path.
So what: The parent applies the two-line inventory fix below and pushes. The new head then needs complete CI and a fresh exact-head review. Merge stays with the parent under the existing authority.

Reviewer: Claude Opus 5.5 (Claude Code), high effort. Read-only: no source writes, no live provider/URL calls, no installed-app or config changes.

Required

R1. Duplicate feature owner breaks the fork stack gate (docs/fork-features.md:2450-2451, scripts/fork-upstream-edits.txt:569-571)

  • Trigger: any run of scripts/fork-check.sh or node scripts/fork-features.mjs check on this head.
  • Effect: fork-features: FAIL: apps/server/src/preview/ServerBrowser.test.ts: expected exactly one feature owner, found 2. CI Fork Stack Model fails on d531 (run 39324111754) and also failed on e227 (run 39324083151). Main c8b passes this check.
  • Cause: agent-web-research.upstreamFiles adds ServerBrowser.test.ts and ServerBrowser.ts. The BrowserCookieMirror entry (spec: mirror Mac browser logins into Chromeria and out to bestia and Rocky #520, docs/fork-features.md:2729-2730) already lists both as upstreamFiles. git merge-tree from base f27d8ee onto main reproduces this exact fork-features.md text with no conflict, so the manual conflict resolution in index.ts/settings.ts/fork-upstream-edits.txt did not cause it. The rebase did.
  • Fix (validated in a scratch copy, fork-features: OK (54 features)): in agent-web-research, move apps/server/src/preview/ServerBrowser.test.ts and apps/server/src/preview/ServerBrowser.ts from upstreamFiles to sharedFiles. Keep packages/contracts/src/previewAutomation.ts in upstreamFiles. Delete the two now-redundant ServerBrowser* lines under the #446 heading in scripts/fork-upstream-edits.txt; spec: mirror Mac browser logins into Chromeria and out to bestia and Rocky #520's lines 544-545 keep them allowlisted.
  • Proof correction: the PR body says "19 fork checks passed" for e227. That set evidently did not include fork-features.mjs check, which fails locally on both e227 and d531.

Optional (not blocking)

  • docs/map.md:96 adds PR #560 as a secondary WR ref. AGENTS.md says secondary PR refs are only for an open PR that does not close the primary Issue, and this PR has Fixes #423. This is harmless once merged.

Cleared in the cumulative scope

Unverified at review time

CI on d531 was still running: Test, Test Server 1/3/4/6 and Test Web pending. Lint, Build and Typecheck, Release Smoke, and Test Server 2/5/7/8 passed. Live runs keep their 8bf/900a identities; none was rerun here. The R1 fix touches inventory metadata only, so no live proof needs to be redone.

Elon record

Requirements and who asked: User/parent: final cumulative Opus 5.5 HIGH review of exact PR560 head against #423/#440-#447 acceptance, accepted boundaries, and main's #520/#551 integration.
Deleted: Re-reviewing component internals already passed at their exact heads beyond integration seams, reopening accepted boundaries, and new live or proof gates.
Bottleneck: The required Fork Stack Model CI check, which fails on duplicate inventory ownership. Remaining CI jobs come next.
Checked myself: Head/base/mergeability via gh pr view; the full cumulative diff of the source, contract and MCP files; git merge-tree reconciliation comparison; the CI failure log; local node scripts/fork-features.mjs check (FAIL) and the scratch-copy fix (OK); canonical proof 6277188172 and the #423/#445 SearXNG decisions.

@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ The exact PR base did not have a successful artifact. Baseline uses the latest successful main measurement shown below.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 20.9 KiB 20.9 KiB 0 B (0.0%) 29.3 KiB ✅
Codex Live turn messages 2 2 0 (0.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.2 KiB 21.2 KiB 0 B (0.0%) 29.3 KiB ✅
Claude Live turn messages 2 2 0 (0.0%) 8 ✅

Baseline: c8b69c6 · PR result: 656b4c6 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@lukemaj

lukemaj commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

What: The sole required final-review finding is fixed on PR #560 headfc9e3f7d7d4cff023fb3524d8d5f0dc2d05dcc8f; the actual complete fork-model command now passes locally.
Why: Research and main's Chrome-login feature both claimed exclusive ownership of ServerBrowser.ts/test.ts after the rebase. The research references are now shared dependencies, main retains one owner, and duplicate allowlist entries are removed.
So what: Fresh independent Claude Opus5.5 HIGH R2 is running on exactfc9e3f, with fresh GitHub CI required. No runtime implementation changed, no acceptance rerun or final merge/activation is claimed.

R1 full findings: #560 (comment) . R1 found no other supported blocker in the cumulative diff and explicitly accepted reusing unchanged live proof for this metadata-only repair. Delta is exactly docs/fork-features.md,docs/map.md,scripts/fork-upstream-edits.txt; apps/packages diff fromd531 exits0. Optional mapPR560ref removed under current repository rule for a final PR closing its primary Issue. Tree6024e3592eb10ec2fcf0c3d1b1793a6a71688360, base mainc8b69c643bfe2fb031ffcd096376a6cd35a9dac8.

Faithful RED: actual node scripts/fork-features.mjs check ond531 exit1 expected exactly one feature owner,found2. GREEN onfc9: taskNode24 bash scripts/fork-check.sh --upstream upstream exit0, including fork-features OK(54features), fork-check OK; current pinnedMermaid3/3PASS. Earlier19fork-maintenance tests were fixture checks and did not cover the real registry; that distinction is corrected in PRbody. Runtime317PASS/6browser-prerequisite skips and scoped types are reused unchanged-source feedback, not complete CI. R2 reviewpendingstatus56733317647 authenticatedlukemaj; taskchromeria423-final560-opus-high-r2-fc9e3f7d7d. No package version bump per repo.

Requirements and who asked: User requires final Opus HIGH/current CI before preauthorized merge; R1 requires metadata reconciliation.
Deleted: Runtime patch, test assertion edits, policy weakening, stale review success and pretending fixture tests were the full registry gate.
Bottleneck: Fresh exact-head cumulative review and complete GitHub CI.
Checked myself: Full R1 report, actual fork-check implementation/feature owners, direct RED/GREEN commands, exact3-file diff/runtime byte equivalence, new remote PRhead/base, current Mermaid and pending authenticated review status.

@lukemaj

lukemaj commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Independent cumulative review R2: no verdict, base moved (no blocker found on the reviewed candidate)

What: On head fc9e3f7d7d4cff023fb3524d8d5f0dc2d05dcc8f (tree 6024e359) over base c8b69c64, the R1 fix is correct and I found no new required finding, but main moved to 3c2ea2a65a16f25c96875f2afed2cbc89aec1ee2 (#556) during review, so I post error, not success.
Why: The brief requires a fresh head and base before a verdict. #556 touches packages/contracts/src/orchestratorMcp.ts, ThreadManagementService.ts and docs/map.md, which this PR also uses or edits, and CI on fc9e3f7 also still runs against the old base.
So what: The parent rebases (or otherwise re-forms) the candidate on current main, runs complete CI on that head, and requests a fresh exact-head review. Merge stays with the parent under the existing authority.

Reviewer: Claude Opus 5.5 (Claude Code), high effort. Read-only: no source writes, fetch, checkout or worktree; no provider, URL, browser or installed-state calls.

Required

None on the reviewed candidate.

R1 fix (confirmed)

  • git diff d531826dcf fc9e3f7d7d: only docs/fork-features.md, docs/map.md and scripts/fork-upstream-edits.txt (3+/5-). git diff --exit-code d531826dcf fc9e3f7d7d -- apps packages exits 0, so the source R1 reviewed is byte-identical.
  • agent-web-research now lists ServerBrowser.ts/ServerBrowser.test.ts in sharedFiles. spec: mirror Mac browser logins into Chromeria and out to bestia and Rocky #520's Chrome login mirror (docs/fork-features.md:2729-2730, allowlist lines 544-545) is their single upstreamFiles owner. previewAutomation.ts and ServerBrowserContexts.ts keep one research owner each.
  • scripts/fork-features.mjs checks single ownership only over upstreamFiles and allowlist membership. sharedFiles is only used in the overlap report, so it still watches both files for research.
  • Local node scripts/fork-features.mjs check on fc (Node 26.8.2): fork-features: OK (54 features). CI Fork Stack Model on fc: success.
  • Removing the PR #560 map ref matches AGENTS.md, since this PR closes feat(research): web_read tool with a shared cache and free-first fetch ladder #423.

Cumulative seams (rechecked on fc against c8b)

I reread these seams myself rather than taking over the R1 verdict. They match R1's cleared findings:

  • McpHttpServer.registerWebResearch maps OrchestratorMcpFailure to typed isError results.
  • McpRouting.invoke refuses environmentId for both research tools.
  • readsAsCaller(..., {linkedCallers:"allowed"}) rejects read-only links and still requires a thread caller otherwise.
  • ResearchHost:
    • refuses a second hop (forwarding_loop) and read-only links;
    • pins expectedLinkIdentity;
    • validates cacheEnvironmentId, subpage hosts and researchEnvironmentId;
    • session forwarding requires contextThreadId, the preview capability, and response checks on profile, tab, rung and the session: prefix.
  • ServerBrowser readPage:
    • requires an explicit tab and the same persistent, non-incognito profile, and refuses isolated contexts;
    • rechecks control and generation after navigation;
    • has a 4 MiB cap.
  • The additive index.ts/settings.ts/orchestratorMcp.ts reconciliations are intact.

Reused from R1 without re-execution (assumptions unchanged because the bytes are identical): the TinyFish zero-cost and URL revalidation, the hosted memory-only continuation limits, the session snapshot privacy, the search quota, in-flight and health handling, and auth settings write scope.

Base movement (why no verdict)

  • gh api compare c8b69c64...3c2ea2a6: one commit, 3c2ea2a65a feat(mcp): expose durable Prism recovery in thread reads (#556), 12 files, including packages/contracts/src/orchestratorMcp.ts, apps/server/src/orchestration-v2/ThreadManagementService.ts (a service registerWebResearch provides) and docs/map.md.
  • git merge-tree --write-tree 3c2ea2a6 fc9e3f7d exits 0 (no textual conflict). Semantic compatibility and CI on the combined tree are unverified, and GitHub still reports baseRefOid c8b69c64.

Optional (not blocking)

Unverified

Elon record

Requirements and who asked: User/parent: final cumulative Opus 5.5 HIGH R2 of the exact PR560 head, confirming the R1 fix and checking for new integration risk before the preauthorized merge.
Deleted: A component re-review beyond the integration seams, a rerun of live proof for a metadata-only change, and any reopening of accepted boundaries.
Bottleneck: A candidate on current main (3c2ea2a) with complete CI. The remaining test jobs on fc come after it.
Checked myself: git ls-remote showed main moved, plus gh pr view and gh api compare; the d531..fc diff and the apps/packages exit 0; the fork-features.mjs logic and a local check (OK, 54); the cumulative seam source; merge-tree onto 3c2ea2a; the check-runs on fc; the R1 report, the PR body, canonical proof 6277188172 and decision 6277148762.

@lukemaj
lukemaj force-pushed the final/423-web-research branch from fc9e3f7 to 15ae7ff Compare October 19, 2026 21:15
@lukemaj

lukemaj commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Independent cumulative review R3: PASS (no required finding)

What: Head 15ae7ff6169f75d03658eef3c71e587392419e8d (tree 1f7d894d) on current base 3c2ea2a65a16f25c96875f2afed2cbc89aec1ee2 has no supported blocking finding. The 7 rebased patches are identical to the reviewed fc9e3f7d, and main's #556 integrates without semantic conflict.
Why: R2 posted error only because main moved to #556 during review. The rebase introduces no new authored source, and #556's changed owners never reach the research paths.
So what: The parent merges only after complete CI on 15ae7ff6 is green. CI was still running at review time. Merge stays with the parent under the existing authority.

Reviewer: Claude Opus 5.5 (Claude Code), high effort. Read-only: no source writes, checkout or fetch; no provider, URL, browser or installed-state calls.

Required

None.

Rebase identity (checked)

  • Live state: git ls-remote gives main = 3c2ea2a6 and the branch = 15ae7ff6. gh pr view shows the same base and head, and the PR is MERGEABLE. compare shows 7 commits ahead and 0 behind.
  • git range-diff c8b69c64..fc9e3f7d origin/main..15ae7ff6: all 7 patches are = (identical).
  • git merge-tree --write-tree 3c2ea2a6 fc9e3f7d returns 1f7d894d, which is this head's exact tree. The candidate is therefore the clean three-way merge of the R2-reviewed tree onto current main, with no hand edits.

#556 semantic integration (checked)

#556 changes 12 files. This PR touches only two of them: packages/contracts/src/orchestratorMcp.ts and docs/map.md.

  • orchestratorMcp.ts: feat(mcp): expose durable Prism recovery in thread reads #556 adds the OrchestratorMcpRunRecovery union and an optional recovery field on OrchestratorMcpThreadRun (around line 489). This PR adds six research failure codes to the OrchestratorMcpFailure literal list (around line 848). The hunks are disjoint and the schemas are independent. The research tools do not encode or decode OrchestratorMcpThreadRun.
  • ThreadManagementServiceShape: feat(mcp): expose durable Prism recovery in thread reads #556 adds a required readRunRecovery, supplied in runtimeLayer through layerPrismRecoveryHooksProvided. The research code uses the service only for caller checks: SessionRead goes through threadAccess.loadCaller/assertLiveCaller, which use getThreadShell/getProjectThreadRecords, and registerWebResearch/tools.ts provide or require the whole service. The only readRunRecovery caller outside tests is OrchestratorMcpService.readThread (line 3416), which no research path calls.
  • Test mocks: the new research tests build ThreadManagementService with partial Layer.mock(...)({...}), so the new required member causes no type error, and none of those paths invoke it. The parent's server-scoped typecheck log for 15ae7f has 0 error TS lines (only existing Effect suggestions).
  • OrchestratorMcpService/RecoveryHooks/RecoveryHistory: this PR does not edit them. feat(mcp): expose durable Prism recovery in thread reads #556's read-only projection and its defaultValue fallback are unaffected.
  • Cross-environment compatibility: recovery is optional and absence means unknown, so research host forwarding between a linked environment on this build and an older peer is unaffected. Research forwarding does not carry thread runs.
  • docs/map.md: feat(mcp): expose durable Prism recovery in thread reads #556 edits the PZ comment line, and this PR edits the WR block. The hunks are disjoint, and feat(mcp): expose durable Prism recovery in thread reads #556 adds no WR ref.

Cumulative seams (reused from R1/R2 because the bytes are identical)

Reused without re-execution: the registerWebResearch typed error mapping, the environmentId refusal in McpRouting, readsAsCaller linked-caller admission, and the ResearchHost forwarding pins (loop, read-only link, expectedLinkIdentity, response identity checks). Also reused: ServerBrowser.readPage (explicit tab, persistent non-incognito profile, generation recheck, 4 MiB cap), TinyFish zero-cost plus URL revalidation, the hosted memory-only continuation limits, the session snapshot privacy, search quota/in-flight/health handling, and the settings write scope. The R1 single-owner fix is still present. Fork Stack Model passes on 15ae7ff6 in CI, and the parent's local fork-check reports fork-features: OK (54 features).

Optional (not blocking)

Unverified

Elon record

Requirements and who asked: User/parent: a fresh final cumulative Opus 5.5 HIGH R3 of exact head 15ae7ff6 on current main 3c2ea2a6, covering #556 integration, before the preauthorized merge.
Deleted: A re-review of the identical component patches beyond the seams, a live-proof rerun for a rebase with no research-owner change, and any reopening of the R2 optional or accepted boundaries.
Bottleneck: Complete GitHub CI on 15ae7ff6, which was still pending.
Checked myself: git ls-remote, gh pr view and compare (head/base/ahead-behind); range-diff (7 identical); merge-tree equal to the head tree; the full #556 source diff and the overlapping hunks; readRunRecovery callers; the research use of the thread service; the parent's typecheck, test and fork-model logs; the check states; and that no new PR comments arrived since R2.

lukemaj and others added 7 commits October 19, 2026 23:22
* feat(research): share page reads through a linked environment

* docs(research): link the shared-host pull request

* fix(research): preserve shared host contracts across MCP clients

* test(research): cover bounded queues through the shared host

* fix(research): preserve shared subpage and render contracts

* test(research): prove forwarded subpage count and pending limits
* feat(research): add zero-charge TinyFish fallback

* fix(research): close TinyFish response scopes

* docs(research): link TinyFish component PR

* fix(research): preserve pages and bound hosted fallback

* fix(research): avoid caching hosted deadline refusals
* feat(research): add shared locale-aware web_search

* docs(research): link web_search component PR

* test(research): await queued search completion before checking time

* fix(research): map locales to SearXNG language syntax
* feat(research): read selected existing browser sessions privately

* docs(research): link the session reader component PR

* fix(fork): register session reader upstream edits
…535)

* fix(research): classify a JavaScript-only instruction shell as thin

Navigation and headings pushed the public enable-JavaScript page past 80 characters, so web_read reported it ok and never rendered. The existing thin status now covers that instruction when it is the whole remainder.

* docs(map): point agent web research at the JavaScript-shell fix

PR #535 does not close #423, so the open corrective PR is a secondary map ref.

* fix(research): judge a JavaScript shell from its lines

Compacted text let a heading pattern swallow the hours and price paragraphs, and the same pattern threw on a 4 MiB plain-text heading run. The shell check now accepts only short pages whose lines are headings, links, or one instruction.
@lukemaj
lukemaj force-pushed the final/423-web-research branch from 15ae7ff to 1226879 Compare October 19, 2026 21:23
@lukemaj

lukemaj commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Independent cumulative review R4: PASS (no required finding)

What: Head 12268798ba8e942e383c33e6f6bba0f54fe7591d (tree 42d74bba73218ef9a55432bb35f9660aa61b7cb8) on base e760e6af8be7762948211a8f39306fda3ab1cc89 (#558 and #559) has no supported blocking finding.
Why: The rebase changes only docs/map.md context and the one append conflict in scripts/fork-upstream-edits.txt. #558's grant check runs ahead of both research tools and fails closed for limited outside grants, and #559 does not touch any research path.
So what: The parent merges only after complete CI on 12268798 is green; most jobs were still pending at review time. The PR body still describes 15ae7ff6 on 3c2ea2a6, so the parent should update it.

Reviewer: Claude Opus 5.5 (Claude Code), high effort. Read-only: no source writes, checkout or worktree; no provider, URL, browser or installed-state calls.

Required

None.

Rebase identity (checked)

#558 outside grants (checked against research)

  • McpToolAccess.declare now runs checkOutsideGrant(CurrentTool.name, params) before withContext and the handler. Research is registered through the custom registerWebResearch, but it calls built.handle from HandlersLayer.layer(WebResearchHandlers.layer). That layer is McpToolAccess.toLayer, whose routedHandlers provides CurrentTool (McpToolAccess.ts:329). So the grant check runs for web_read and web_search too. The custom registration does not bypass it.
  • A limited client: web_read and web_search are not in the operations map, so they are refused with capability_denied before any routing, ResearchHost forwarding, SessionRead or fetch. outsideGrant.test.ts covers this unknown-tool refusal, even with every operation granted. The custom registration returns the refusal as an isError result. This fail-closed behavior is main's policy, and broadening it is not required.
  • An unlimited client (limits undefined) returns early, as before feat(auth): scope renewable outside-agent OAuth grants #558. Internal thread and provider callers have client undefined, so requireThreadCaller still applies. Linked peers come in through layerMcpClientAuthenticator, which sets linked: client.peerLink with limits only when the session carries mcpLimits, so the readsAsCaller(..., { linkedCallers: "allowed" }) admission and the read-only refusal are unchanged. If a peer-link approval ever carried limits, the linked host would refuse research (fail closed), not widen access.
  • Renewal (refresh_token) re-signs the same SessionClaims, including mcp, so a renewed grant cannot drop its limits and reach research.

#559 thread tool allowlists (checked)

  • McpHttpServer.layer registers ThreadMcpToolkit (t3_thread_mcp_configure only) alongside layerWebResearch, so no tool names collide. contracts/index.ts exports both threadTools.ts and webResearch.ts.
  • ThreadToolsRequest and tools are optional on delegate_task only, and toolAllowlist is optional on the capabilities result. Research inputs carry no tools key, so the new PeerForwarding carriesTools probe never fires for web_read or web_search. Research forwarding through ResearchHost is unaffected.
  • Claude allowlists map mcp:t3-code:web_read to native allowedTools and drop unlisted MCP servers. Research is governed by the thread's opt-in allowlist like any other tool. The PR's orchestration-instruction text says "when available", so a disallowed tool is simply denied natively. Allowlists on other providers are rejected by toolPolicyRejection, which does not touch research.
  • docs/map.md: main's OAG node and the research WR children sit in disjoint lines. orchestratorMcp.ts: the failure-code additions and feat(orchestration): add opt-in thread tool allowlists #559's fields are in independent schemas.

Reused (identical bytes, not re-inspected beyond the seams above)

The R1 to R3 conclusions on: the registerWebResearch error mapping, the environmentId refusal in McpRouting, ResearchHost forwarding pins, ServerBrowser.readPage session constraints, TinyFish zero-cost and URL revalidation, the memory-only hosted continuation limits (15 min, 32 entries, 64 MiB), session snapshot privacy, search quota and health, and the settings write scope. Component reviews #455, #502, #505, #513 and #535 are as linked in the PR body.

Parent proof (inspected, not re-executed)

  • final-e760-research-auth-tests.log: 10 files, 141 passed. The log does not list the files, so their identity rests on the parent's brief.
  • final-e760-server-types.log: 0 error TS lines; only existing Effect suggestions.
  • Fork Stack Model passes in CI on 12268798, and the parent reports a local fork-check result of 55 features.

Unverified

Optional

Elon record

Requirements and who asked: User/parent: a fresh final cumulative Opus 5.5 HIGH R4 of exact head 12268798 on main e760e6af, covering the #558 and #559 seams, before the preauthorized merge.
Deleted: A re-review of the unchanged research patches, a live-proof rerun for a rebase that changes no research owner, and any push to broaden outside grants.
Bottleneck: Complete GitHub CI on 12268798, still pending.
Checked myself: live refs and tree; range-diff and merge-tree against 15ae7ff6; the full #558/#559 diffs in the overlapping MCP, auth, peer and contract files; the CurrentTool provisioning on the research path; checkOutsideGrant and its unknown-tool test; how limits and linked are set on MCP clients; the merged layer composition and contracts exports; the parent's test and typecheck logs; and the check states.

#556 added ThreadManagementService.readRunRecovery to every thread read,
but this file's box Layer.mock lacked it. The unimplemented method died on
each read there, the laptop follower retried it as transient, and each
remote-follow test waited out the 120 s test timeout, hanging the Test
Server shard that runs the file.

Fixes #562

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lukemaj

lukemaj commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Independent cumulative review R5: PASS (no required finding)

What: Head 656b4c620c759df006bf1b57d11f0f52cb3cf046 (tree 10ad15a3cca5f0c2fee51d9bd54087c36092a6e9) on base e760e6af8be7762948211a8f39306fda3ab1cc89 has no supported blocking finding.
Why: Since R4 (12268798), the only change is one line in a test fixture, RemoteDelegation.test.ts. It returns the same { state: "none" } that the production RecoveryHooks default returns, and it does not hide the recovery-successor behavior.
So what: The parent merges only after complete CI run 39332329579 on 656b4c6 is green, including the transfer artifact. At review time, Lint, Build and Typecheck, Test, Test Web and Test Server 1 to 8 were still running.

Reviewer: Claude Opus 5.5 (Claude Code), high effort. This is a new task, independent of the repair author. It was read-only: no checkout, source edit, worktree, provider, URL, browser or installed-state calls.

Required

None.

Identity (checked live)

  • gh pr view 560: head 656b4c62, base e760e6af, MERGEABLE. git ls-remote: main = e760e6af, refs/pull/560/head = 656b4c62. The commit's tree is 10ad15a3, its parent is 12268798 (the R4 PASS head), and merge-base = base.
  • git diff 12268798 656b4c62: 1 file, 1 insertion (apps/server/src/peer/RemoteDelegation.test.ts:260). No product runtime file changed. The cumulative diff is therefore the R4-reviewed diff plus this line.

New: the #562 fixture line (inspected)

  • Mechanism holds. Layer.mock in .repos/effect-smol/packages/effect/src/Layer.ts:2306-2328 turns a missing method into UnimplementedError. OrchestratorMcpService.ts:3451 calls threadManagement.readRunRecovery(run) for every recent run in a thread read. Its Effect.mapError does not catch a defect, so before this line the box's thread read died.
  • Faithful value. RecoveryHooks.ts:40's default readRunRecovery is () => Effect.succeed({ state: "none" }), the same value. RecoveryHistory.readRunRecovery also returns none for any run that is not failed, and not an interrupted, cancelled or rolled-back continuation. OrchestratorMcpService.activity.test.ts already stubs it the same way four times. The return type is NonNullable<OrchestratorMcpThreadRun["recovery"]>, and as const keeps the literal.
  • Does not mask recovery. The only production consumer of readRunRecovery in this path is the recentRuns[].recovery field of thread reads (git grep finds no peer or follower use). The follower's successor logic sits in the box's waitForThread (recoveryWait.ts, and here the mock's thread.successor flag). The test the remote follower reports the recovery successor reply returned by wait (:2261) still drives thread.successor and still asserts result === "Recovery succeeded." and status === "completed". No expectation or timeout changed. The recovery.test.ts suite keeps the real History.readRunRecovery against SQL, so real recovery-read behavior keeps its own coverage.
  • Scope. The one-line repair is the smallest change that restores the gate. It matches test(server): RemoteDelegation.test.ts hangs on main since #556, timing out Test Server shards #562's non-goals: no timeout change and no change to follower retry. The other partial mocks were left alone, which was correct, because their shards pass.

Repair proof (inspected, not re-executed)

  • repair-test.log: Tests 39 passed (39), Duration 57.52s, rc=0, run from the frozen worktree whose HEAD is 656b4c62 and has a clean status.
  • repair-typecheck.log: 0 error TS lines (Effect suggestions only). repair-fmt.log: formatting passes.
  • repair-lint.log: lint did not run (module resolution failure for @oxlint/plugins in the worktree). CI Lint owns that gate.
  • test(server): RemoteDelegation.test.ts hangs on main since #556, timing out Test Server shards #562's diagnosis: a timeout at 12268798 before the change, 39/39 after. Its shard-order reasoning matches the R4 CI evidence. Not rerun here.

Reused from R1 to R4 (identical bytes, not re-inspected)

R4's rebase identity, the #558 outside-grant check (it runs before registerWebResearch's handler and fails closed for limited grants), internal, provider and linked-caller admission, renewal limits, #559 allowlists (no layer collision, no ResearchHost forwarding change), the R1 single-owner Browser inventory fix, and the R1 to R3 research seams. Component reviews #455, #502, #505, #513 and #535 are as mapped in the PR body.

Unverified

Optional

Elon record

Requirements and who asked: User/parent: a fresh final cumulative Opus 5.5 HIGH review of exact 656b4c6 on e760e6a, judging whether the #562 fixture line completes the mock without masking recovery behavior.
Deleted: A re-review of the unchanged R4 seams, a local rerun of the parent's passing tests, and a live-proof rerun for a test-only change.
Bottleneck: Complete CI run 39332329579 on 656b4c6.
Checked myself: live refs and tree; the exact incremental diff; Layer.mock's unimplemented behavior; the readRunRecovery caller, its production default and the history semantics; the follower's successor path and its test; the repair test, typecheck, fmt and lint logs; and the CI job states.

@lukemaj

lukemaj commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Agent work on this PR

Estimated cost at least $8.09 · 165 responses · 33 sessions · 19.6 h wall time

Model Responses Tokens Estimated cost
gpt-6.1-sol (codex, medium) 67 17,952,724 $2.74
grok-4.7 (grok, medium) 2 16,500,316 unknown
claude-opus-5-5 (claude, high) 96 13,245,040 $5.35

Flags: 1 human correction · 12 large tool outputs · 15 repeated commands · 53 repeated reads · 62 repeated skill loads · 2 tests edited after a failure · 2 unpriced responses

Details: snapshot, prices, coverage, counters
  • Task toolboxmd/chromeria#560: outcome unknown (recorded acceptance only; a finished process never implies it).
  • Proof: feat(research): share page reads and web search across environments #560
  • Snapshot c1ce63823fed3d5a30e8f2b3f93375ec7311d15a46bebcd3e6aaf9305c1df7f2, records up to 2026-10-19 16:59 UTC.
  • 33 sessions on claude, codex, grok; AgentsMD 17.2.0, 17.3.1, 17.4.1.
  • The total includes work shared with 6 other tasks, counted whole in each, so sums across PRs overlap.
  • Not counted: 2,859 responses ($141.76) in sessions shared with other PRs that worked in no single PR's checkout.
  • 654 responses in these sessions worked on other PRs and are counted there.
  • Totals reconcile with the measured sessions: yes. Evidence complete: yes.
  • Prices: list-price estimate from T3 local rate table (path withheld) as of 2026-10-18, schedule 414551dda4c47258113dcfa46a5dfa5aa512406e3dfcadcca3381421595c5588. Unknown prices stay unknown, never zero.
    • T3 LiteLLM rate table when present; bundled schedule covers the rest.
    • Model price sources: T3 local rate table (path withheld).
    • Unpriced: unknown model (2).
  • Harness-reported cost: none reported.
  • Usage totals are not billing. Subscription spending is separate and is never posted as spend.
  • Crashed runs are counted separately: 0.

Token counters by model (native counter semantics; never added across semantics):

  • gpt-6.1-sol (codex, medium), codex counters: input includes cached, output includes reasoning: input 17,893,334; cache-read 17,707,520; cache-write 0; output 59,390; reasoning 25,283; harness total 17,952,724.
  • grok-4.7 (grok, medium), grok counters: input includes cached, output includes reasoning: input 16,406,629; cache-read 16,025,216; cache-write 0; output 93,687; reasoning 64,831; harness total 16,500,316.
  • claude-opus-5-5 (claude, high), claude counters: input excludes cache, output includes thinking: input 192; cache-read 12,979,475; cache-write 212,902; output 52,471; reasoning 29,225; harness total 13,245,040.

Generated tokens (reasoning and other output are disjoint; other output includes code, tool calls and replies):

Model Reasoning Other output Reasoning share Split known for
gpt-6.1-sol (codex, medium) 25,283 34,107 42.6% 67 of 67
grok-4.7 (grok, medium) unknown unknown unknown 0 of 2
claude-opus-5-5 (claude, high) 29,225 23,246 55.7% 96 of 96

Usage by work phase (this task's own responses):

Phase Model Responses Input Cache read Cache write Output Reasoning Total Priced part
unclassified gpt-6.1-sol (codex, medium) 67 17,893,334 17,707,520 0 59,390 25,283 17,952,724 $2.736280
unclassified claude-opus-5-5 (claude, high) 96 192 12,979,475 212,902 52,471 29,225 13,245,040 $5.349299

Recorded activity by phase (observations, not separate token bills):

Phase Tool calls MCP results Reads File changes Failed tool results
unclassified 277 28 119 2 23

Selected rates (USD per million tokens). These rates value the report at the selected schedule date; they do not establish historical prices or subscription spending.

Model Input tier Input Cache read Cache write Other output Reasoning
claude-opus-5-5 all 4 0.2 5m: 5; 1h: 8 20 20
gpt-6.1-sol all 2 0.1 2.5 10 10
grok-4.7 all unknown unknown unknown unknown unknown

Other output and reasoning are priced without double counting inclusive native output. Missing rates remain unknown.

Local measurement from native records; usage totals are not billing. Updated in place by agent-observer publish.

@lukemaj

lukemaj commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

What: Cost reporting for #560 remains explicitly PARTIAL after the verified Cavallo/Rocky rollout.
Why: The existing supported Observer report is an earlier source-work snapshot; current cross-machine activation/recovery usage is not completely attributed, and the user has held all Observer work on bestia until its hardware is stable.
So what: Keep the existing report as scoped historical evidence and defer its supported refresh; neither its “complete” wording nor its priced subtotal establishes full #560/code-plus-deployment cost.

The published snapshot c1ce63823fed3d5a30e8f2b3f93375ec7311d15a46bebcd3e6aaf9305c1df7f2 cuts off19October18:59Warsaw, before the final integration and this Mac rollout. Its priced API list-equivalent subtotal is not native-reported cost or actual subscription spending. Newer private diagnostics show unplaced/unbound callback usage on Cavallo and an unknown PR560 task on Rocky after supported sync; they are excluded, never silently counted as zero or manually assigned. Shared/unplaced work is not substituted from an entire project/conversation. Native-reported fleet cost, actual subscription spend and a complete cross-machine total remain unknown. Measurement gaps remain tracked in Observer58.

No post-rollout Observer sync, install, publish command or other Observer tool ran on bestia after the user's hold. This aggregate-only qualification is beside the original generated report, not a rewritten renderer or an invented combined cost. Private JSON/native records stay private. The Mac operation itself is live-verified on exact5898397, with Cavallo receipts and Rocky receipts; bestia deployment and measurement remain held.

Requirements and who asked: User requires an honest readable PR-scoped report, explicitly accepts partialcost for now, and holds all bestia Observer work pending stable hardware.
Deleted: A zero/full-total claim, mixing unrelated sessions, manual token bookkeeping and further bestia measurement/publication commands.
Bottleneck: User's hardware-stability hold plus actual supported cross-machine attribution/usage coverage.
Checked myself: Existing published report snapshot/cutoff, private supported sync/query evidence before hold, user instruction, exact-source Mac live receipts and absence of any further bestia Observer operation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

1 participant