feat(nadia): Implement query-variant frontier and URL/content deduplication
Outcome: done
Changed: src/typosaurus_sandbox/research/frontier.py:1-234, src/typosaurus_sandbox/research/__init__.py:14-20,34-36,44-48
Verified by: make verify -> exit_code 0, compileall OK, 104 tests OK, "verification passed"; module smoke test passed (URL normalization, query/URL/content dedup, async get_query, snapshot accounting)
Findings:
- QueryFrontier API: seed, push_query(query, origin), push_variants_from_result (count of new queries from title/description/string extra; does not register URLs), register_url, register_content, register_result (URL+content dedup), get_query/pop_query (count queries_issued), snapshot() frozen DedupStats for closure deltas, origin_of.
- Query dedup key = whitespace-collapsed casefold; URL dedup via normalize_url (lowercase scheme/host, IDNA, strip default port/userinfo/fragment, collapse slashes); content dedup via sha256 of whitespace-normalized text.
- One threading.Lock guards all seen-sets/counters; pending queries in asyncio.Queue usable sync via pop_query and async via get_query.
- Query variant length window 2-200 chars; out-of-window dropped without touching counters.
- frontier.py imports only envelopes.SearchResult from foundation; no new dependency; retoor header, no comments/docstrings.
Open: engine leaf wires QueryFrontier into worker pool and computes per-round snapshot() deltas for closure; testwriter leaf covers frontier API.
Confidence: high - acceptance criteria exercised by smoke assertions; full suite passes via mak
Typosaurus-Run: 4e2afb673c7f4578a12276d9181b982d
Typosaurus-Node: 5e16725c0f944ca0b4373eb640430f7d
Typosaurus-Agent: @nadia
Refs: #31