Commit Graph

3 Commits

Author SHA1 Message Date
32a17e3f5c feat(tanya): Execute four recursive verification rounds and compile final report
Outcome: done
Changed: none (read-only; rounds run via PYTHONPATH=src python3 heredocs)
Verified by: make verify -> exit_code 0 "Ran 219 tests in 0.968s OK verification passed"
Findings:
- R1 2026-08-07T23:31:17Z PASS engine-optimality: empty world closes round1 (lfp halt); chain4 -> 6 rounds/5 urls (no depth cap); dedup unique-only; S* size5 > F^2 size2.
- R2 2026-08-07T23:34:35Z PASS verifies R1: chain6 pool1 -> 8 rounds/7 urls, F^3=3 subset S*=7; content-only closes round2; fanout 2 urls/5 queries.
- R3 2026-08-07T23:35:02Z PASS verifies R2: pools 1/4/8 identical (8,7,14,1); cached 2nd run 0 network calls, identical report; pool_size==max_concurrency.
- R4 2026-08-07T23:36:26Z PASS verifies R3: stress 38 items=succ+fail, 4 content kinds, closed; ""/whitespace -> ValueError, zero calls; chain5 pool8 7 rounds/6 urls; slow client completes.
- Engine C1-C8 all PASS (sibling f7f10c64): rsearch-only, bound 8, one _request 4 content types, dedup 64->1, closure, annotations/logging/header, no TODO, verify green.
- Live probe (fde105db, "python asyncio"): 86 queries, 754 urls, 281 contents, 164 requests, $0.002075, 264.91s; no closure in 240s guard -> TimeoutError, 2835 enqueued.
- Rounds' initial failures were tester-expectation only (MIN_QUERY_LENGTH=2 frontier.py:13, description seeds, non-http tokens kept); engine correct; reflect() recorded.
Open: none for this node; PR creation deferred to run coordinator (deepresearch.md exists, sibling 7d91ddb)
Confidence: high - four round

Typosaurus-Run: 4e2afb673c7f4578a12276d9181b982d
Typosaurus-Node: 0cee401df29142ecb2bf3bca088d5953
Typosaurus-Agent: @tanya
Refs: #31
2026-08-07 23:37:59 +00:00
3ff5fc686a test(sveta): Write unit tests for frontier scheduling and concurrency safety
Outcome: done
Changed: tests/test_research_scheduling.py:1-506
Verified by: make verify -> exit_code 0, 198 tests OK (22 new), "verification passed"; only pre-existing StarletteDeprecationWarning from fastapi/testclient.py:1, none introduced
Findings: 22 stdlib-unittest tests with retoor header; bounded pool at config max_concurrency=8 drains 64 frontier queries issuing each exactly once (frontier.py:194, config.py:19); two concurrent pools never double-issue; late-enqueued queries drained; snapshot accounting consistent (frontier.py:218); dedup holds under concurrency: 64 concurrent push_query dups -> 1 enqueued/63 skipped, 64 concurrent register_url across normalized variants -> 1 seen/63 skipped (frontier.py:127,153,167), same-content-16-URLs -> 1 content; TTLCache repeat-key identity, unknown-key None, zero-TTL boundary, negative TTL, overwrite, clear, thread-safe under 16 threads x 256 keys and 128 same-key sets (cache.py:20-45); web/images/chat/describe/describe_raw funnel through one mechanism RsearchClient._request, web vs images differ only by type param (client.py:177); ResearchPipeline (pipeline.py) pool_size == max(1,max_concurrency), run() drains all WorkItems with endpoint map web/images->/search describe->/describe chat->/chat, semaphore bounds in-flight work to pool_size proven by peak tracking (pipeline.py:129-255); duplicate web items dedup at frontier (urls_seen 2, queries_enqueued 6 across 5 mixed items); mutation check: breaking register_url dedup flips s

Typosaurus-Run: 4e2afb673c7f4578a12276d9181b982d
Typosaurus-Node: 4159e2d87c90415bb7ca49e75f53a1b6
Typosaurus-Agent: @sveta
Refs: #31
2026-08-07 19:32:54 +00:00
c0a59b4138 feat(nadia): Build content-type agnostic pipeline with bounded asyncio worker pool
Outcome: done
Changed: src/typosaurus_sandbox/research/pipeline.py:1-280 (new), src/typosaurus_sandbox/research/client.py:30-52,98-99,124-148, src/typosaurus_sandbox/research/__init__.py:21-49
Verified by: make verify -> exit_code 0, compileall OK, 195 tests OK, "verification passed" (pre-existing StarletteDeprecationWarning from tests/test_api.py); live smoke: web/images/chat via one pool (3 ok, urls=19, queries=92, contents=10), repeat web item cache_hit=True, empty query -> RsearchError 400 handled, pool survived
Findings:
- ResearchPipeline uses asyncio.Semaphore(max_concurrency, default 8); pool size logged INFO in run(); run(AsyncIterator[WorkItem]) = bounded queue (pool*4) + pool_size workers with None sentinels; process(item) is the public semaphore-guarded path.
- WorkItem(kind: web|images|describe|chat, value, deep=False, ai=False); web -> search(content=True), images -> search(type="images") without deep/ai, describe -> describe(url), chat -> chat(prompt); endpoints /search,/describe,/chat.
- extract_response() handles SearchResponse (results, ai_response, deep sources/markdown), ChatResponse.response, DescribeResponse.description in one function; apply_extraction() registers URLs, query seeds (title/description/extra/text with origin) and content fingerprints into QueryFrontier.
- client.py additive: _search_params() shared by search() and new search_cached()/describe_cached() probes so pipeline cache_hit is accurate; chat/deep cache hit from envelope fields cache

Typosaurus-Run: 4e2afb673c7f4578a12276d9181b982d
Typosaurus-Node: e85a60edbf7b47f1913d87602b9c553c
Typosaurus-Agent: @nadia
Refs: #31
2026-08-07 19:30:57 +00:00