Skip to content

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

Merged
lukemaj merged 3 commits into
integration/423-web-researchfrom
fix/423-javascript-shell
Oct 19, 2026
Merged

lukemaj merged 3 commits into
integration/423-web-researchfrom
fix/423-javascript-shell

Conversation

@lukemaj

@lukemaj lukemaj commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

What changed: A JavaScript-only shell stays thin, and a short page that already states its hours or prices stays readable.
Why: Review #535 (comment) on 7d8328c544 showed the compacted heading pattern swallowed that prose and threw RangeError: Maximum call stack size exceeded on a 4 MiB plain-text heading run.
So what: The parent dispatches a fresh Claude Opus 5.5 high review of 93a5f85fd57d8d59ad8ab1e1d1e366fdce374482. This author does not merge or post a pass.

Findings

  1. Fixed. classify now reads line-structured page.text. A page is an instructional shell only when every non-empty line is a heading, a links-only line, or one whole instruction line. The hours page (visible text 123) and the shop page (visible text 140) return ok. The observed 115-character shell stays thin and still renders.
  2. Fixed. That check runs only when the existing short bound holds (visibleText.length < 4000). A text/plain body of # a b c d e f g h i repeated to 4,194,300 characters, inside the 4 MiB public body limit, returns ok and does not throw.
  3. Optional links-only listing: left as the review described. A line of bare links is still chrome. No new status, flag, or fetch path.

Status priority, the JSON-LD exclusion, the 80-character threshold, cache behavior, and the map ref are unchanged. Base remains integration/423-web-research at 8bf0a58c4fb54fea675a4c9ba445b2446b5634dd.

Candidate

  • Worktree /home/luke/.t3/worktrees/chromeria/fix-423-javascript-shell, branch fix/423-javascript-shell, clean and equal to origin/fix/423-javascript-shell.
  • Head 93a5f85fd57d8d59ad8ab1e1d1e366fdce374482, tree 8ac77a31b0484f21f3e9ff316b5ae79028951e02, pushed at 16:23 Europe/Warsaw on 2026-10-19.
  • Earlier behavior commit 1994351a4bfca7c51de9cec7cbc681ab3788ffc6. Map commit 7d8328c544acec4d961889195ed6be68467ebc75.
  • No version bump. No browser, provider, credential, or network probe. Website impact: none. This does not change a public site, URL, or user doc.

Proof

Round 1, retained. Before the first predicate, apps/server vp test run src/mcp/WebResearchRender.integration.test.ts -t "classifies an instructional" expected thin and received ok on the 115-character extract. An earlier ladder run left renders at 0. After that predicate, vp test run src/mcp/WebResearchRender.integration.test.ts src/mcp/toolkits/webResearch/handlers.test.ts -t "instructional|substantive pages|JavaScript shell|instead of returning an HTTP-200|renders thin at the registered|renders only thin|classifies rendered walls" reported 14 passed, 56 skipped, exit 0.

Round 2 red, still on 7d8328c544, after the new tests and before this predicate change. From apps/server:

../../node_modules/.bin/vp test run src/mcp/WebResearchRender.integration.test.ts -t "opening hours readable|product prices readable|paragraph after the enable-JavaScript|nine-word headings"

Exit 1. Started 16:20:03. 4 failed, 19 skipped. Decisive lines: hours expected 'thin' to be 'ok' after the text contained the Monday hours and the Springfield address; shop expected 'thin' to be 'ok' after the text contained both prices; plain text expected [Function] to not throw an error but 'RangeError: Maximum call stack size exceeded' was thrown. The first after-instruction fixture was only 70 visible characters, so it was already inside the old length rule. Replaced with the 131-character paragraph and rerun on the old predicate: vp test run src/mcp/WebResearchRender.integration.test.ts -t "paragraph after the enable-JavaScript", exit 0, 1 passed, 22 skipped, started 16:20:55.

Round 2 green, after the line check and before format-only cleanup. From apps/server:

../../node_modules/.bin/vp test run src/mcp/WebResearchRender.integration.test.ts src/mcp/toolkits/webResearch/handlers.test.ts -t "instructional|substantive pages|JavaScript shell|instead of returning an HTTP-200|renders thin at the registered|renders only thin|classifies rendered walls|opening hours readable|product prices readable|paragraph after the enable-JavaScript|nine-word headings"

Exit 0. Started 16:21:52. Duration 10.81s. 18 passed, 56 skipped. handlers.test.ts 8 passed, 43 skipped. WebResearchRender.integration.test.ts 10 passed, 13 skipped. The shell still renders once and the product text is returned. The hours, prices, and following-paragraph pages stay on the assertion ok.

vp lint on both files: 0 warnings, 0 errors. vp fmt then formatted extract.ts. apps/server tsc-rs --noEmit: exit 0. Pre-existing suggestions only. The same commands, exits, and decisive lines are on #440 (comment). The earlier round stays on #440 (comment).

Independent review of this head is not claimed. CI on 7d8328c544 was green and did not waive these two bugs. Live re-read of the public URL was not repeated.

Requirements and who asked: Review comment 6269336638, required fixes 1 and 2, under #440 comment 6268620365 and the approved #423 ladder. The 115-character shell must still use the existing thin/render path. Hours, prices, a following paragraph, JSON-LD, and articles that mention JavaScript must stay readable. classify must not throw on a legal plain-text body.
Deleted: A new status, flag, rung force, or fetch path. Raising the 80-character threshold. Treating any JavaScript phrase as thin. Running the shell expression over the whole 4 MiB body. SSRF, proxy, browser, session, and cache changes. A change to the optional links-only listing, which the review left as designed.
Bottleneck: The shell predicate in classify.
Checked myself: On 7d8328c544 the 123-character hours page and the 140-character shop page were thin, and the 4,194,300-character heading run threw RangeError: Maximum call stack size exceeded. On 93a5f85fd5 those three return ok, the 115-character shell still renders once, and lint, format, and server typecheck passed.

Model and harness: grok-4.7 through the Grok harness in T3 Code.

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.
PR #535 does not close #423, so the open corrective PR is a secondary map ref.
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:S labels Oct 19, 2026
@lukemaj

lukemaj commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor Author

Agent work on this PR

Estimated cost at least $0.05 · 2 responses · 26 sessions · 17.0 h wall time

Model Responses Tokens Estimated cost
grok-4.7 (grok, medium) 1 13,623,806 unknown
gpt-6.1-sol (codex, medium) 1 202,234 $0.05

Flags: 1 human correction · 11 large tool outputs · 13 repeated commands · 50 repeated reads · 57 repeated skill loads · 2 tests edited after a failure · 1 unpriced response

Details: snapshot, prices, coverage, counters
  • Task toolboxmd/chromeria#535: outcome unknown (recorded acceptance only; a finished process never implies it).
  • Proof: fix(research): classify a JavaScript-only instruction shell as thin #535
  • Snapshot 3e45eae24cce19e63a94e0e0a83dd514c3cef0545f6738f22e7553ddc1899528, records up to 2026-10-19 14:25 UTC.
  • 26 sessions on claude, codex, grok; AgentsMD 17.2.0, 17.3.1, 17.4.1.
  • The total includes work shared with 4 other tasks, counted whole in each, so sums across PRs overlap.
  • Not counted: 2,187 responses ($106.91) in sessions shared with other PRs that worked in no single PR's checkout.
  • 648 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 (1).
  • 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):

  • grok-4.7 (grok, medium), grok counters: input includes cached, output includes reasoning: input 13,556,743; cache-read 13,298,048; cache-write 0; output 67,063; reasoning 46,527; harness total 13,623,806.
  • gpt-6.1-sol (codex, medium), codex counters: input includes cached, output includes reasoning: input 200,924; cache-read 191,616; cache-write 0; output 1,310; reasoning 298; harness total 202,234.

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
grok-4.7 (grok, medium) unknown unknown unknown 0 of 1
gpt-6.1-sol (codex, medium) 298 1,012 22.7% 1 of 1

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) 1 200,924 191,616 0 1,310 298 202,234 $0.050878

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

Phase Tool calls MCP results Reads File changes Failed tool results
unclassified 5 5 0 0 0

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
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.

@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ No successful main baseline artifact is available yet. This run establishes the initial measurement.

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

Baseline: unavailable · PR result: 93a5f85 · 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: Independent review of head 7d8328c544acec4d961889195ed6be68467ebc75. Verdict: FAIL, with 2 required fixes in apps/server/src/webResearch/extract.ts.
Why: The regex runs on whitespace-compacted visibleText, so a heading token absorbs the prose after it. Short substantive pages become thin and lose their text. A 4 MiB heading run also overflows the regex stack inside classify.
So what: The author applies the small fix below and pushes a new head, which needs a fresh review. The parent still owns merge and the final cascade.

Required fixes

1. Short substantive pages are now classified thin (false positive). extract places each heading on its own line in text. visibleText is compact(rendered), which removes those line breaks. CHROME_TOKEN's heading branch then accepts up to 8 more words, so it takes in the paragraph that follows the heading. I ran the real extract/classify at base 8bf0a58c4f and at head, in a scratch copy:

HTML body visibleText length base head
<h1>Opening hours</h1><p>Monday to Friday, 9:00 to 17:00.</p><h2>Address</h2><p>12 Main Street, Springfield.</p><p>Enable JavaScript to view the map.</p> 123 ok thin
<h1>Mug</h1><p>Ceramic mug, 12 euros, ships in two days.</p><h2>Towel</h2><p>Linen towel, 18 euros, ships tomorrow.</p><p>Enable JavaScript to add items to your cart.</p> 140 ok thin

Impact: for any non-ok status, WebResearch.ts returns text: "" (the fetch return after the render block). The page goes to render. If no preview browser is installed, PublicRender.ts:239-242 returns provider_error, and TinyFish is the only fallback. Before this PR the same page returned ok with its text. #440 comment 6268620365 requires that valid short content stays readable. The PR's near-miss test only puts prose after the instruction in the same <p>, so it never covers a heading followed by a paragraph.

2. classify can throw RangeError: Maximum call stack size exceeded. A visibleText made of about 197k nine-word headings ("# a b c d e f g h i " repeated, about 3.95M characters or more) overflows V8's regex backtrack stack. Reachability: a text/plain response passes its raw body, up to PublicHttp MAX_BYTES = 4 MiB, directly as visibleText (WebResearch.ts, the text/plain branch). A body that is mostly unclosed <h1> runs reaches the same range. classify at WebResearch.ts:484/513/614 and SessionRead.ts:267 sits outside Effect.try, so the throw becomes a defect, not a typed status. Base classify cannot throw. CPU cost is fine: every adversarial 4 MiB input I tried finished in 2 to 65 ms. The failure is stack depth, not time.

Minimal fix for both: check the shell only when short is true, and check line-structured page.text instead of visibleText. The page qualifies only when every non-empty trimmed line is a heading line (^#{1,6} .*$), a links-only line, or the single anchored instruction line. That makes the 8-word cap unnecessary. Pages with JSON-LD are already excluded by the existing !page.text.includes("JSON-LD:"). I prototyped this in a scratch copy. The observed scrapingcourse shell stays thin, both pages above return ok, and the 4 MiB adversarial inputs return in about 1-2 ms without throwing. Please add both pages above as ok controls, plus one large heading-run regression.

Optional

  • A links-only line counts as chrome. A product listing made of bare <a> text plus "Enable JavaScript to see more." is still thin, even though its link labels may carry facts. <li> items are not affected, because the - prefix breaks the match. This is acceptable as designed, but worth knowing.

Cleared

  • Status priority holds. The check runs after the challenge, status >= 400, origin, login and consent branches, so a 500 is still origin_error and a challenge title still wins.
  • The JSON-LD exception is preserved, and the 80-character threshold is unchanged.
  • Cache TTLs and retention are unchanged. A false thin is cached for 300 s.
  • The MCP seam test goes through the registered web_read callTool with the real render ladder fixture.
  • The map edit adds only PR #535 to WR. No SSRF, transport, session, frozen-input, or Preview browser: look like a regular Chrome browser (WhatsApp Web blocks the Electron user agent) #460 change.

Evidence and limits

  • Exact head is unchanged at verdict time. CI on the exact head passed, including Build and Typecheck, Lint, Test, and Test Server 1-8.
  • My checks: scratch copies of extract.ts from base and head (git show), run under Node v26.8.2 with parse5 8.0.1. I did not run the PR's vitest locally because there was no approved test workspace. I relied on CI for that and did not inspect the author's local RED/GREEN logs.
  • This is not Live Verification. A post-fix native-URL proof of the scrapingcourse page is still owed through the parent sequence.

Reviewer: Claude Opus 5.5 (high) through Claude Code in T3 Code.

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 commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

What: Independent round-2 review of head 93a5f85fd57d8d59ad8ab1e1d1e366fdce374482 (tree 8ac77a31b0) against base 8bf0a58c4f. Verdict: PASS, no required fixes.
Why: The line-structured check on page.text, gated by the existing short bound, fixes both round-1 failures. The 115-character shell stays thin and renders, and status priority and JSON-LD behavior are unchanged.
So what: The parent owns merge and the post-fix live native-URL proof of the scrapingcourse page, which is still owed.

Round-1 dispositions

  1. Short substantive pages forced thin: fixed. Hours page (visible text 123) and shop page (140) return ok at head and thin at 7d8328c544. A paragraph after the instruction (131) returns ok. So do prose before or after the instruction on the same line, a <br>-separated price, a list item, a table row, and two instruction lines.
  2. RangeError on a 4 MiB plain-text heading run: fixed. The run of "# a b c d e f g h i " (4,194,299 characters) throws Maximum call stack size exceeded at 7d8328c544 and returns ok in 0.0 ms at head. The same holds with newline-separated headings. short is false there, so the line check never runs. The HTML heading runs (3.1M to 3.6M visible characters) also return ok.
  3. Optional links-only listing: unchanged, as the PR says. Bare <a> price labels plus "Enable JavaScript to see more." are still thin. I do not promote this.

Required

None.

Optional

  • Headings are chrome by design, so a page whose only facts sit in headings becomes thin when an instruction line follows. Example: <h1>Open Mon-Fri 9:00-17:00</h1><h2>12 Main Street, Springfield</h2><p>Enable JavaScript to view the map.</p>, visible text 91: base ok, head thin. This follows the approved shape ("navigation/headings" plus the instruction), and the page still goes to render. I see no need to change it now.

Cleared

  • Status priority: with the shell body, status 500 gives origin_error, 401 login, 403 and 429 challenge. A "Just a moment" title gives challenge, a "Sign in" title login, a cookie-consent title consent, and a password input login. All match base.
  • JSON-LD: the shell plus JSON-LD stays ok. The JSON-LD check still applies to both thin branches.
  • Bounded input, CPU, and stack: the line check runs only under visibleText.length < 4000. On HTML, text can still be long when visible text is short: compaction removes runs of spaces from inline siblings, and the JSON-LD tail adds more. I fed classify texts of up to 4.0M characters with short visible text. The cases were 4M spaces inside the instruction, inside the to … tail, inside a heading, and inside a links line, plus 3,000 [ characters on a ]( line. Each finished in 2 to 7 ms with no throw. All three regexes are anchored or bounded ({0,5} tail words, {1,256} link labels), have no nested unbounded quantifier, and are not stateful (MARKDOWN_LINK is global but used only through replace). A 4M-newline text took 140 ms, but extract collapses newlines, and a text/plain body that size fails short.
  • Line anchoring and instruction count: the instruction must fill a whole trimmed line, and a second instruction line returns ok. HEADING_LINE matches the # … lines that extract emits. An empty heading (#) or a link URL containing ) counts as prose, which only errs toward ok.
  • Callers: WebResearch.ts:484/513/614 (HTTP, render, TinyFish) and SessionRead.ts:267 all pass extract() output. The exception is the text/plain branch, whose visibleText is the trimmed raw body, so short bounds it directly. A short text/plain body of headings plus the instruction is thin and goes to render, as an HTML shell does.
  • Tests and CI: the seven new tests run in CI on this exact head. CI run 39268477069 (headSha 93a5f85) shows WebResearchRender.integration.test.ts with 23 tests, 5 skipped. The 5 skips are the pre-existing pinned-headless-shell tests at lines 538, 676, 742, 791, and 839, so none are new. handlers.test.ts passed 51 tests. Lint, Build and Typecheck, Test, and Test Server 1-8 are all green. The ladder test goes through the registered web_read MCP toolkit fixture and asserts one render and ok HTTP for the article.
  • Scope: three files. docs/map.md only appends PR #535 to the WR ref. There is no new status, flag, fetch path, SSRF, cache, session, or Preview browser: look like a regular Chrome browser (WhatsApp Web blocks the Electron user agent) #460 change, and no version bump.
  • Author's invalid 70-character fixture: I make no claim about it. The corrected 131-character fixture is ok on both 7d8328c544 and head, as the PR says.

Outside this PR

extract itself has a pre-existing quadratic whitespace regex. .replace(/[\t ]+\n/g, "\n") at extract.ts:116 is the same on base and main. It stalls the event loop for minutes on a 4 MiB body of single-space inline elements. Filed as #538. This PR does not touch it and it does not block this verdict.

Evidence and limits

  • I verified the head and tree with git fetch + rev-parse, and verified the base as the merge-base.
  • I copied extract.ts from base, 7d8328c544, and head with git show into a scratch directory outside every worktree. I ran it under Node v26.8.2 with parse5 8.0.1, the same version the author worktree resolves.
  • I did not run vitest locally: the author worktree is frozen read-only, and this review had no approved test workspace. CI on the exact head covers that.
  • This is not Live Verification. I made no browser, provider, credential, or network probe.

Reviewer: Claude Opus 5.5 (high) through Claude Code in T3 Code.

@lukemaj
lukemaj merged commit 06cd67f into integration/423-web-research Oct 19, 2026
28 checks passed
lukemaj added a commit that referenced this pull request Oct 19, 2026
…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 added a commit that referenced this pull request Oct 19, 2026
…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 added a commit that referenced this pull request Oct 19, 2026
…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 added a commit that referenced this pull request Oct 19, 2026
…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 added a commit that referenced this pull request Oct 19, 2026
…560)

* feat(research): share page reads through a linked environment (#455)

* 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 (#502)

* 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 (#505)

* 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 (#513)

* feat(research): read selected existing browser sessions privately

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

* fix(fork): register session reader upstream edits

* fix(research): classify a JavaScript-only instruction shell as thin (#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.

* docs: link final research train PR in feature map

* fix(research): reconcile shared browser feature ownership

* test(server): give the RemoteDelegation box mock readRunRecovery

#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>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant