Skip to content

Completed II

Gabri Elles edited this page Aug 21, 2026 · 7 revisions

Completed, Part II

Part 2 of the Completed list of finished ROADMAP items.

The three tier domain specialized architecture: Planner delegates to WebSearcher, AcademicSearcher, and PeerReviewer, and those in turn delegate to DocumentAnalyzer and DataAnalyzer. PeerReviewer is a Planner tier delegate for independent critique, not part of the Searcher to Analyzer chain. On 2026-07-13 a Builder Planner tier delegate was added (see the Build to Review to Fix loop entry below), and on 2026-07-14 a FindingsWriter Planner tier delegate was added the same way, one artifact earlier (see "Planner now only plans and delegates" below). Five Planner tier delegates exist now: WebSearcher, AcademicSearcher, PeerReviewer, Builder, and FindingsWriter.

The Planner now only plans and delegates, it cannot write any file. This was a 2026-07-14, user driven design question: "the planner should only plan and delegate... giving the planner the job of writing the findings will poison context." Previously the Planner wrote findings.md itself, the only artifact writing job it still had after Builder was split out for final_report.md, a real, inconsistent gap: a findings_ungrounded/missing_findings retry grew the Planner's own conversation exactly the way Builder was invented to prevent for final_report.md. Confirmed live the same day, independent of this fix: a benchmark run hit 4 consecutive findings_ungrounded retries and exhausted its budget with nothing ever written. The fix was a new FindingsWriter Planner tier delegate, dispatched exclusively by the generalized Write to Review to Fix loop when missing_findings/findings_ungrounded fires. FindingsWriter never sees the Planner's conversation, its dispatch instructions are built entirely from RunState's structured findings data, populated automatically by every Searcher and Analyzer call, plus read_workspace_file/grep_workspace_file access to go deeper into a raw fetched source if a summary isn't detailed enough. The Planner's write_workspace_file tool was removed entirely, it's now structurally incapable of writing any file, the same way it's already structurally incapable of researching.

This got live verified end to end across two real runs the same day: the architecture worked correctly, FindingsWriter and Builder both dispatched via their own independent loops, and PeerReviewer caught real issues in both, including a genuine findings.md problem that triggered a real corrective re dispatch, and separately an unsupported citation in final_report.md. Tracing one flagged citation to its root cause confirmed the layers really were independent, not just redundant: findings.md correctly recorded that python.org's landing page has no biographical content about Python's creator, a truthful negative finding, but Builder then cited python.org in support of a creator claim anyway on its own initiative, a downstream synthesis error introduced after FindingsWriter's own review, caught by a completely separate check. A new gap surfaced by that same testing, not a flaw in the dispatch mechanism itself: on a run where Builder repeatedly re committed the identical unsupported claim mistake across separate corrective attempts, never actually fixing it, the Planner, resuming each time with no signal that a Write to Review to Fix cycle just ran, kept deciding to delegate more research rather than recognizing the problem was a downstream citation error, not a research gap. Because delegate_tasks's own quota is independent of the completion check attempt limit, this let a trivially simple factual query run 25 minutes and fetch 35 URLs before finally exhausting its budget. The system's own safety net still worked correctly at the end, producing a real, honestly labeled, mostly correct report instead of a silent failure or a hang, so this was an efficiency gap, not a correctness regression. It got fixed the same day: run_completion_check now wraps its retry loop so successful Builder and FindingsWriter dispatches continue straight into the next completion check iteration instead of returning control to the Planner, so a persistently failing chain now burns its retries entirely inside one call instead of wasting Planner driven "more research" turns in between.

Also fixed around this time: a context_budget_chars blind spot for the classic inject-into-Planner path. run_stream_chars only ever counted characters from the Planner's own streamed generation, so a completion check nudge appended outside that stream loop was invisible to the budget guard and could in principle grow the Planner's context unboundedly on repeat. Fixed by measuring the character length of whatever run_completion_check actually appended and adding it to the running total right after the call.

A batch of structural reliability fixes shipped around the same period: per attempt quota top up, artifact quarantine before nudge, a structured _run_state.json including populated findings, a real URL presence plus content level grounding check, and history scanning salvage for a narrated but never written report, fixing an old single turn lookback bug that discarded good content when the final retry produced empty text. Upstream verification also shipped: each Searcher specialist's summary now gets grounding checked before it reaches the Planner, not just the final report. A replan_action tool made the Planner's replanning decision a structured, checkable call alongside its free text reasoning, though this was later deleted in the 2026-07-11 ponytail audit as unused in practice. A persona brainstorming step had the Planner briefly reason from 2 to 3 relevant expert perspectives before planning non trivial queries, to widen slot coverage. HTML boilerplate stripping got fixed on the primary fetch path, since previously only the BeautifulSoup fallback stripped nav, footer, and script content while the primary markitdown path passed raw chrome straight through. The DDGS search client moved to a per call instance instead of a shared singleton, so concurrent specialist searches no longer share one client instance. An extract_structured_data tool gave DataAnalyzer and DocumentAnalyzer a real tool level distinction, not just a prompt driven one. A wiki index feature, a deterministic, engine maintained cross run index independent of session isolation, was also deleted in the same ponytail audit, since cross run state poisons benchmarks, the same reasoning as a rejected knowledge cache. A heavy search mode searches deeper and auto fetches more top results per call instead of fabricating fake query variant strings. A human in the loop gate reuses the existing approval widget infrastructure to gate the Planner's write_todos. An MCP tool loader gives a generic loader for the agent framework's native MCP tool types, connected per task, with nothing enabled by default and two researched, ready to uncomment servers documented in the config template. Readable run folder names replaced a bare unix timestamp with a slugified query plus timestamp. A TUI click to copy fix tries the direct system clipboard before falling back to Textual's OSC52 escape sequence, which silently no ops in terminals that don't support it. TUI paste fixes flatten a multi line paste into one line, since Textual's base input widget silently keeps only the first line and drops the rest, and also debounce a same or prefix paste redelivered within half a second, found live when a large pasted prompt showed up with a truncated repeat of its own opening appended.

Model choice got re tested against this project's actual nested delegate_tasks schema. Several models, devstral:24b, hermes3:8b, qwen2.5-coder:14b-instruct, llama3-groq-tool-use:8b, and mistral:7b-instruct-v0.3-q5_K_M, were all tried and rejected for the Planner role at the time. Devstral, despite roughly twice the parameter count, made zero real delegate_tasks tool calls across a full 8 attempt run, narrating perfectly formatted JSON in a markdown code block instead of emitting a real structured call, every single attempt, confirming bigger isn't automatically better on this schema, the failure is a structured output habit, not a reasoning or capacity limit. This was later superseded on 2026-07-11 when the 13 run Colombia B2B benchmark made deepdelve-gpt-oss the default.

A per task fetch tracking race condition got fixed: the "URLs fetched by this task" delta used to be computed via a before and after length check on the single run wide shared fetched URL list, which races under concurrent delegate_tasks dispatch, confirmed live when a 3 task run produced 9 cross attributed findings instead of 3. Fixed with a proper per task scoped contextvar, verified at 10 task concurrency with zero duplication afterward. A delegation scope relevance check then became possible on top of that fix, flagging a specialist's summary when nothing it actually fetched mentions the entity its own delegation instructions required. A no_urls problem got its own distinct completion check message with escalating language, handing back the exact fetched URL list on repeat failures instead of reusing wrong citation wording that didn't fit a report with zero citations. An explicit re delegation directive now forces delegate_tasks again when a grounding check failure repeats with no new fetches since the last completion check attempt, confirmed live to detect the exact failure it targets. delegate_tasks also started rejecting unresolved placeholder tasks and same batch cross task dependencies before dispatching, both patterns confirmed live via garbage search queries.

A wave of fixes around 2026-07-11 and 2026-07-12 tackled real fabrication and infrastructure gaps found through a large live test battery, over 15 runs across factual lookups, comparative queries, academic paper queries, current event queries outside training data, a TUI session, and several real market research queries at varying scope. A full strict code audit around the same time found two real bugs: the eval harness's find_latest_session still filtered on an old run_* prefix left over from before the folder rename above, so no current run folder had ever matched it and every eval run since had silently scored raw stdout instead of the real report artifact, and remove_workspace_file's own docstring claimed it "mandates human oversight" while the config template shipped with no permissions entry actually gating it. Both got fixed. A placeholder detector false positive also got found and fixed via a real bad output diagnosis: a live "neglected markets in Colombia" run had every single cited source turn out fabricated, root caused to the Planner's real, well formed 12 task batch getting rejected wholesale because one task's name used an ordinary numbered label, which falsely tripped a check meant for a task with no real name anywhere. Facing the rejection, the model gave up delegating those 12 sectors entirely and fabricated all of findings.md from memory instead. Fixed by checking only the instructions field for the placeholder pattern, since that's the field that actually becomes a Searcher's query. Not yet fixed at the time, same diagnosis: findings.md itself is never grounding checked at all, only final_report.md is, so a Planner that abandons real delegation partway through a run can fabricate findings.md wholesale with nothing structural catching it. Non URL pseudo citations then got caught by a new line scoped check for a "Source:" labeled or "(Org, Year)" shaped attribution with no URL on the same line, closing that gap.

A headless and headed browser fetch fallback got added for pages that bot wall a plain HTTP GET, motivated by a live reported bug where real, citable papers on Springer, ScienceDirect, and MDPI were all getting flagged as fake or stub sources. Root cause investigation found three distinct bot wall signatures: Springer served a stripped shell to the plain fetch, ScienceDirect returned a Cloudflare Turnstile challenge, and MDPI served an Akamai Bot Manager block. The fix retries once via a real browser before giving up, when the plain fetch looks like a stub, reusing the same boilerplate strip and markdown pipeline on the browser rendered HTML. It's a soft dependency that fails open with zero latency cost if Playwright isn't installed. Confirmed live that a headed browser beats headless: MDPI's block turned out to be a headless specific fingerprint check that fires at the network level before any JS or DOM loads, so JS side stealth tweaks made zero difference under headless mode, but a genuinely headed Chromium sailed straight through. The shipped behavior tries headed first, using a real display or an auto started virtual one on Linux, and falls back to headless only when no display is available at all. One more real bug got found and fixed along the way: the boilerplate strip regex was a substring match, so it was deleting Springer's actual 221 thousand character article body container because its CSS class happened to contain the word "sidebar," silently leaving only the cookie consent banner behind, which then passed the stub check on its own prose mass. Fixed with a size guard, since real chrome is never that large. ScienceDirect turned out not fixable this way at all, root cause pinned down precisely: it's gated by Cloudflare Turnstile, and exhaustive live testing found the challenge iframe never resolves even after patient polling with a real headed browser, and spoofing navigator.webdriver to undefined didn't help either, so the detection is deeper than any single JS flag, almost certainly fingerprinting the Chrome DevTools Protocol connection Playwright itself requires. The exact same URL, same machine, loads cleanly in the user's real, non automated Firefox, ruling out IP reputation as the cause. This was deliberately not pursued further, since defeating it would mean building and maintaining real anti detection tooling aimed specifically at circumventing a publisher's bot controls, which doesn't belong in DeepDelve's shipped default behavior, the same reasoning as declining to add CAPTCHA solving.

Three remaining structural fabrication gaps got closed in one pass on 2026-07-11, the first Windows side session. First, a findings.md wholesale fabrication gate: run_completion_check now flags a Pass 1 file with zero cited URLs or where not one cited URL matches a real fetch, quarantining it and forcing re delegation, deliberately laxer than the strict per URL final report check since Pass 1 notes legitimately mention unfetched snippet URLs. Second, structural exclusion enforcement: delegate_tasks extracts explicit exclusions from the original query and skips matching tasks individually rather than rejecting the whole batch, because the placeholder detector incident had shown wholesale rejection makes the model abandon delegation and fabricate. Third, unresolved referent rejection: a live delegated task, "Summarize its headline feature," searched the web with no idea what "its" meant and returned Microsoft Research patent statistics as Python's headline feature, so short instructions leaning on a bare pronoun with no proper noun, digit, or quote anchor are now rejected with guidance to restate the subject.

The Windows migration, a dual boot setup on the same NTFS drive, shared the Ollama model store via an explicit path, fixed a cp1252 UnicodeEncodeError in headless mode with a UTF-8 reconfigure guard, and pinned the doc extras actually used since markitdown[all] silently downgrades to a stub package on Windows with newer Python. The full pipeline got re verified live on Windows with ROCm on an RX 9060 XT 16GB.

A production batch landed on 2026-07-11, all validated live during the 13 run benchmark day: a findings.md existence gate requiring Pass 1 before final_report.md is accepted, --resume-run to reattach an interrupted run, a TUI intake clarifier that fails open, a settings.max_run_minutes run budget, --depth quick|standard|deep presets, a repeatable --seed-url, a finish line summary plus --list-runs, TUI follow up continuity for Q&A mode on an existing report, a regulation_id_check for a law number cited to a source that never actually mentions that number, and quarantined draft restore at final verdict, since earlier runs had ended with a real draft sitting in a rejected attempt file while salvage delivered meta narration instead, now the draft wins, loudly labeled.

A completion check refactor on 2026-07-12 replaced a roughly 250 line if/elif verdict chain in tui.py, which had shipped the same swallowed elif bug twice, with a data driven check list in src/engine/completion.py where the first verdict wins and there are no elif headers left to swallow, pinned by a 10 row verdict matrix in the test suite. A Windows workspace escape bug in _get_safe_path got fixed the same day: joining a base path with a drive qualified filename discards the base entirely, so drive lettered or drive relative filenames could escape the workspace, since the Planner has write_workspace_file. Drive lettered names are now rejected outright with an abspath containment check added on disk workspaces. A documentation update pass followed, flipping the config template default to deepdelve-gpt-oss and syncing the README and ROADMAP.

A context budget endgame guard shipped, since local models run at a fixed context size with no context accounting, so on overflow Ollama silently truncates from the top, eating the system prompt mid run, indistinguishable from model collapse. A new settings.context_budget_chars counts text, tool args, and results per agent stream, and on overshoot the turn gets cut with one forced wrap up turn, verified live with a tight 3000 character budget producing an honest "budget exhausted" report instead of silent truncation. A grounding layer hardening batch the same evening shipped several fixes together, all validated live: stub fetch detection recording soft 404 or paywall shells so grounding checks refuse them outright, a fix so the injected line 1 header's own URL slug no longer counts as source content, a charset fix so HTML gets decoded by its real encoding with stale meta tags scrubbed, citation format enforcement flagging a section with 3 or more figure bearing lines and no citations, a URL prefix boundary fix so a fetched URL no longer grounds a fabricated URL that merely starts the same way, the grounding check's own master switch actually being honored, and a platform independent drive letter guard.

Repo governance and CI landed the same day, triggered by an external audit's one genuinely real finding, that the repo was public with no license. A LICENSE file (MIT), a CI workflow running install, lint, and the structural test suite on every push or PR to main, and a pragmatic lint config narrowed to real issues rather than style noise all shipped together, along with floor and ceiling dependency pins and a lockfile snapshot. The rest of that audit's "critical" findings got checked directly against the code and found false or already solved.

An academic or literature review output mode shipped on 2026-07-12, triggered by a real gap: a live sales forecasting query got a properly structured literature review paper from DeepSeek while the local model collapsed on the same query through DeepDelve. A new settings.report_style option, orthogonal to depth, rewrites the Planner's report structure instructions into a literature review shape, with Abstract, Introduction, thematic sections, synthesis, benchmarking summary, challenges, and a References list, modeled on a reviewed external repo's own template, and swaps the citation format to author-year in text plus a numbered References list. It also carries that repo's Anti Leakage Protocol, preferring findings.md over parametric memory and writing "Not covered by this run's research" instead of inventing a section. The grounding layer gained a parse_academic_references function mapping author-year keys to their References entry's URL, and every line scoped check now resolves academic citations through it alongside the existing inline URL format. A real bug got caught building the test coverage: a line with two author-year citations only had its first one checked, since the regex used .search() instead of .finditer(), so a real citation earlier on a line could mask a fabricated one later on the same line. A fresh audit pass then caught a higher severity bug in the same feature before any live run: the citation detector required every token before the comma to start with an ASCII capital, so it silently failed to even detect "et al.," ampersand, or accented surname citations at all, exactly the forms the feature's own prompt tells the model to use, breaking grounding in both directions. This got fixed too, along with a related mis keying bug where a reference entry's own title could shadow its real author and year. A live validation run produced the literature review shape correctly end to end, with citations resolving with zero false positives; the run's one real failure was the pre existing hard URL presence gate correctly catching a genuine citation accuracy slip, not a defect in academic mode.

A checkmark on error TUI bug got fixed on 2026-07-12: ToolCallWidget.set_result always rendered a green checkmark regardless of the result text, so a real run showed a read_workspace_file call marked complete despite returning an error. A new error detection helper now drives both the TUI glyph and a new tool error counter. A fuzzy filename fallback for read_workspace_file/ grep_workspace_file traced the root cause of a run that gathered substantial research but never produced a report, since 16 percent of workspace read calls used a garbled or truncated filename reconstructed from memory by a sub agent one hop removed from the original fetch, each failure burning a turn and a quota unit, cascading into abort exceptions. A new fuzzy resolver using a conservative single best match threshold now auto resolves these instead of erroring. Structured _run_state.json logging expanded the same day, persisting full completion check verdict detail per attempt, a tool error counter with samples, and a subagent label disambiguator so repeat dispatches of the same task name get a readable suffix instead of colliding in post hoc analysis. A /resume-run slash command finally got added to the TUI too, since it had been CLI only for a full prior session unnoticed, and the exact scenario it exists for, a quarantined run with real work already on disk, actually happened with no TUI path to reach it. This prompted two new standing rules: mandatory TUI/CLI feature parity checks, and tracing a change's blast radius across sibling surfaces before calling it done.

An answer mode shipped the same day too, from the dzhng/deep-research reference project: a third report_style option for a short, direct answer with no section headings and an inline citation instead of a References list. Live validated: the first attempt hit a real unsupported claim quarantine since the model's citation format deviated from spec, the completion check cycle correctly caught it and nudged a rewrite, and the second attempt passed with a clean short answer.

A TUI ProcessingWidget timer leak got fixed the same day, caught live: a run's final turn, the model's response after tool quotas were exhausted with nothing left to say, streamed zero content, so the widget's stop method, gated on the turn's first content token, never fired, and its elapsed seconds counter kept climbing indefinitely well past the point the run had already reached its final verdict, making a genuinely finished run look stuck. Fixed with unconditional cleanup once the stream is guaranteed exhausted, not just the reactive first token path.

A cross model benchmark against NVIDIA NIM ran the standing benchmark query against DeepSeek V4 Pro, a Nemotron Super 49B model, and gpt-oss-20b, all hosted. DeepSeek crashed on an uncaught 429 mid run, Nemotron made zero real delegate_tasks calls and fabricated placeholder citations, and gpt-oss-20b was the only one to reach a clean pass, though thin, with one real citation carrying a wrong paper title. None beat the local gpt-oss:20b, confirming a single general purpose LLM handling research, synthesis, and verification end to end has a real ceiling here, not just a local model weakness. This directly motivated two tracks of "specialized non LLM component instead of another LLM call" work, informed by FActScore's decompose then verify pattern and HALT-RAG's combine lexical and NLI finding. Track 1, NLI based grounding verification, added a small cross encoder entailment classifier that runs only on claim lines that already passed the cheap term overlap check, catching a citation with the right source and shared terms but a wrong specific detail, like a paper title quoted with one word swapped, exactly the NIM benchmark's failure above. This was the project's first ML or NLP dependency, and a real footprint issue got caught and fixed, since the naive install pulled the full CUDA torch build at around 6GB even though nothing here touches a GPU, switched to a roughly 200MB CPU only wheel instead. Track 2, fetch time metadata extraction, pulled title, author, and published date from the same parse the boilerplate stripper already builds, eliminating a mechanical "extract title and authors" sub agent dispatch pattern that had fired 13 times identically in one day's logs.

An uncaught crash on malformed tool call retry exhaustion got fixed the same period, plus TUI parity added: a huge write_workspace_file argument got truncated mid JSON by the model, the existing 2 retry recovery correctly retried twice, but the third consecutive occurrence hit a bare raise that killed the whole run with an uncaught error, at attempt 8 of 8, after 18 real sources already fetched and 5 report attempts already written to disk. The CLI now degrades to the same final verdict path used for other budget exhaustion instead of crashing, and the TUI gained the identical retry then degrade logic it previously had none of at all for this failure class. A tool call validation error visibility gap also got found and fixed: a full day log cross reference found a generic "Argument parsing failed" error was the single most common error signature of the day, with every one having its actual cause silently stripped, because the agent framework's detailed error config was never enabled. Turning it on also helps the model self correct on retry, not just diagnostics. Two of that error's concrete root causes got fixed the same commit: one tool was missing a required search pattern argument in many occurrences, and another was missing an optional filename argument that got made truly optional with an auto derived default.

A second live confirmed completion check stall, missing_findings, got fixed: a verification run produced literally zero content in response to this nudge for 6 consecutive attempts, then genuinely self corrected with real content on the seventh, a different failure shape from missing_artifact's, which never self corrected without help. The wording now escalates after the first occurrence and, from the second on, hands the model its own actual fetched URLs verbatim as proof real material exists, deliberately without missing_artifact's aggressive early cutoff, since that would have killed this exact run's real recovery before its genuine success at attempt 7.

The Builder sub agent and Build to Review to Fix loop, shipped 2026-07-13, was the direct fix for the context growth risk noted above. The user's own diagnosis: the Planner's own conversation only ever grows across a run, since no compaction exists in the underlying session framework, so every completion check retry historically meant appending another nudge and re showing the model its own rejected drafts, a documented failure mode called context poisoning. This maps onto the established Plan and Execute agentic pattern and reuses the existing delegate_tasks mechanism, which already gives every dispatched sub agent a genuinely fresh, isolated context, so the fix was routing report writing retries through that mechanism instead of the Planner's own conversation, not inventing a new one. A new Builder role writes and rewrites final_report.md from findings.md, and the Planner no longer writes or delegates the report at all, its own instructions end at Pass 1. Completion check problems now get classified as either Builder fixable, meaning rewriting the report from the same findings.md with no new research needed, or Planner escalated, meaning genuinely more or different research is needed, which only the Planner can decide to delegate. For Builder fixable problems, the system dispatches a Build, Review, Fix sequence directly: Builder rewrites the artifact, a fresh PeerReviewer dispatch reviews the result, with a required "REVIEW: CLEAN" or "REVIEW: ISSUES FOUND:" opening line so the caller can branch without another LLM call, and Builder gets exactly one corrective re dispatch if flagged. None of this touches the Planner's own conversation state.

Two live validation runs confirmed this end to end. A simple factual query hit missing_artifact on attempt 1, dispatched Builder, dispatched PeerReviewer (clean, no corrective pass needed), and completed cleanly in 691 seconds with the Planner's own conversation untouched. The standing heuristics algorithms sales forecasting benchmark, a genuinely hard, three way AND query with no single source satisfying all three criteria, saw the loop fire correctly 3 times on real unsupported problems, each time dispatching Builder then PeerReviewer without touching the Planner's conversation. A new finding, not previously possible to observe: on later attempts, Builder itself hit the same "narrate instead of write" failure the Planner used to be prone to, because Builder shares the run's single write_workspace_file quota pool with the Planner and every prior Builder dispatch, and by that point the pool was exhausted, with Builder's own text saying so explicitly. The pre existing quarantine restore fallback caught this correctly at the final verdict, restoring the best surviving draft with its loud unresolved check banner, an honest labeled recovery rather than a silent failure or a lost draft. The net assessment: the mechanism itself works as designed, but it doesn't rescue a query where the source material genuinely doesn't exist, and it surfaced a new, real quota sharing constraint under heavy retry load.

Phase 1 of an approved 6 phase plan: claim level grounding upgrade, combining atomic claim decomposition with per claim evidence binding. Found on 2026-07-13, informed by FActScore's decompose then verify pattern and a claim evidence provenance paper (later corrected in title on 2026-08-17 during a broader literature completeness audit, "From Fluent to Verifiable: Claim-Level Auditability for Deep Research Agents," not the earlier miscited title, the author and substance were always correct). The prior claim_grounding_problem compared a whole line's terms against the union of every source cited anywhere on that line, a real gap when a line carries two distinct claims each with its own citation, since a shared generic term between claim A and claim B's source could mark both claims "supported" even though claim B's own citation didn't actually back it. A new decompose_claim_segments splits a line into atomic segments at each citation boundary, mechanical regex token splitting with no NLP and no new dependency, and claim_grounding_problem now checks each segment only against its own bound citation's source. A residual gap in the sibling NLI and topical relevance checks, which had the same latent whole line overlap gap, got closed the next day, 2026-07-14.

check_excluded_topic, report write time enforcement of query exclusions, closed a gap where delegate_tasks already skipped dispatching a task whose own topic matched an explicit query exclusion, but did nothing to stop that topic showing up as its own section in the final report anyway, recalled from a sibling task's tangential findings. The new check reuses the exact same exclusion parser, now applied to the report's own heading sections, deliberately heading scoped rather than whole document substring matching, so a topic mentioned once in passing prose doesn't false positive.

Phase 2 of the 6 phase plan: cross source contradiction detection, FEVER style, depending on Phase 1's claim segmentation. A new find_cross_source_contradictions builds an index of every other fetched source's own claims, pairing each subject with its nearest same line figure by character distance rather than a full cross product, then for each report claim segment checks whether a different fetched source, one not cited on that segment, reports a same kind but numerically different figure for the same subject, unmentioned anywhere else in the report. Distinct from a plain unsupported claim, since the cited source really does support the claim, this instead catches the report silently picking a side of a real disagreement between two fetched sources without saying so. Isolated sanity checks during development caught and fixed a real bug before it shipped: an early version paired every subject with every number on a line regardless of kind, so a line naming both a year and an unrelated percentage spuriously "contradicted" any other source's differing percentage for a totally unrelated reason. A second real bug got found live on 2026-07-14 during Phase 6's TUI smoke test: a citation attribution appearing only inside a source line, and dozens of times across a long fetched Wikipedia article as bare attribution, image captions, or reference list entries, never as the subject of an actual claim, got treated as a genuine claim subject and paired with an unrelated nearby year, firing the same phantom contradiction after every single Builder rewrite, a structurally unfixable, non converging retry loop. This got caught only because the user pushed back on accepting the loop at face value rather than assuming it was a stream handling issue. Fixed with a new helper that treats a line as bibliographic rather than a claim if fewer than 8 letters of real text remain after stripping markdown links and a leading bullet or "Source:" marker. A fresh live re run of the identical query converged in 1 Builder cycle and about 5 minutes, versus 5 plus cycles and never converging before.

Phase 3 of the 6 phase plan: xQuAD style search result diversity reranking. DDGS already ranks by its own relevance signal, but several near duplicate results for the same angle commonly dominate the top of that ranking. A new _diversity_rerank greedily reorders web_search's results by marginal new aspect term coverage instead of raw rank, DDGS's own top result always stays first, then each subsequent pick is whichever remaining result adds the most new aspect terms. Pure reranking, no LLM call, no new dependency, applied once right after search health recording and before the auto fetch slice, improving both the auto fetch selection and the returned snippet ordering.

Phase 4 of the 6 phase plan: a topical relevance cross encoder reranker. A third stage grounding check, layered after term overlap and entailment checks, reusing the same evidence set but asking a different question: is the cited source actually about the same subject as the claim, not just lexically overlapping and non contradictory? This fixes an acronym collision case where a term overlap check passes and an unrelated sentence about a place doesn't contradict an algorithm claim, it's just unrelated, so neither upstream layer can catch it. A second cross encoder checkpoint got loaded through the already installed sentence transformers library, no new pip dependency. This got verified against the real checkpoint, not just the mocked test: the irrelevant pair scored 0.023 and the relevant pair scored 0.997, a huge margin either side of the 0.1 threshold, confirming the design assumption was correct before it ever reached a live run. A real bug got caught and fixed during this same pass too: the new check's config gate wasn't included in the test suite's existing mocking guards, so the first full suite run after wiring it in silently loaded the real, unmocked reranker model, the exact anti pattern that guard was built to prevent.

Phase 5 of the 6 phase plan: coverage accounting, or ResearchMap. Distinct from every other completion check, since those all verify content that already exists is properly grounded, this instead asks whether the Planner's own top level delegated research plan actually paid off, since a report can be perfectly grounded yet still be thin because most of the Planner's own delegated angles came back with nothing usable and got silently dropped rather than surfaced or retried. This was deliberately built entirely from already reliable, model independent structural data instead of a new Planner authored schema, after investigating and explicitly ruling out relying on the todos file, since that's free text with only a prompted, zero code validated convention. A new RunState.coverage() reuses two already existing, engine populated primitives to compute total, covered, ratio, and uncovered task names over distinct top level task names. The new check_thin_coverage isn't Builder or FindingsWriter fixable, since fixing thin coverage needs new delegation, which only the Planner can decide, so it falls through to the classic inject-into-Planner path by design, and it's conservative by construction, firing only when a majority of top level tasks came back with no real source and there are enough of them for that ratio to mean anything.

Live verification the same day found 2 more real bugs, both fixed and live confirmed. First, the sub agent timeout deadline fix from an earlier phase was never actually live, it existed in the config template but nothing back filled an existing user's real config file, so it was silently disabled the whole time, and every earlier "live verified" confirmation of that fix had only been true because the key had been temporarily test added to the config and reverted afterward. This prompted a new standing rule: new settings keys must be grepped in the live config, not just the template, before a dependent fix counts as verified. Second, _dispatch_writer_review_fix's corrective Fix pass had no evidence base of its own, since its second dispatch is a fresh sub agent with zero memory of the first Write dispatch, and the fix instructions said to use "the real source material you were given" but never actually included it, harmless for Builder, whose source is a real re readable file, but fatal for FindingsWriter, whose source material only ever existed as a string in the first dispatch's prompt. Confirmed live: a corrective FindingsWriter dispatch burned its entire turn hunting for guessed, nonexistent filenames instead of writing a fix. Fixed by re appending the original write instructions to the fix instructions. Third, check_thin_coverage itself false positived on the project's own internal Write to Review to Fix dispatches, since Builder, FindingsWriter, and PeerReviewer are dispatched directly from the Planner's own top level context rather than through delegate_tasks, landing them at the same depth as a genuine top level research task, structurally indistinguishable by depth alone. Fixed with a new constant naming those three roles explicitly and skipping finding recording for them.

Phase 6, also called item B4: unify run_cli/run_agent's stream iteration and retry logic. Done. The two genuinely duplicated pieces between headless and TUI mode got extracted into shared helpers: an async generator racing each update against an optional wall clock deadline, replacing the CLI's inline manual loop, and a pure decision function for the malformed tool call retry pattern, previously copy pasted between both call sites and once found missing from the TUI entirely. This was the last open phase of the 2026-07-14 six phase plan, all six phases are now done.

A claim_grounding_problem false positive on citation only sub bullets got fixed on 2026-07-14. Root caused a live Eiffel Tower smoke test failure that burned its entire 8 attempt retry budget on unsupported claims, even though both flagged claims were verbatim in the fetched source, a genuine false positive, not model fabrication. This project's own Builder output shape puts a claim on one line and its citation on a separate "Source:" sub bullet, and that bare sub bullet was being processed as its own claim segment, with the salient term extractor pulling an editorialized anchor phrase out of the citation's own link text as if it were a checkable fact, then failing it because that exact phrase, the writer's own paraphrase, doesn't appear verbatim in the source. A helper that already existed for exactly this line shape, built earlier the same day for the cross source contradiction work, just hadn't been applied here yet. Now it is, in both places. A live end to end re verification of the exact same query produced zero false positive occurrences, converging cleanly in under 500 seconds versus over 1000 seconds of wasted grinding before.

Immediate narration salvage inside _dispatch_writer_review_fix. Implemented on 2026-07-18, targeting the "writer role finishes its turn without ever calling write_workspace_file" failure class at its root. The project already had a salvage function for a model that narrates a complete report as chat text instead of calling the tool, but it only ran as a last resort at final verdict time, and only for a missing final report, not for a missing findings file, which was the case that actually burned one candidate model's full 8 attempt budget. This is now checked immediately after every Write dispatch: if the required artifact is still missing but the dispatch returned at least 200 characters of real text, that text gets persisted as the artifact right away, clearly flagged as an auto recovered draft, and flows into the same review and grounding cycle a genuine write would, instead of looping blind on a file that will never appear on its own. A live re test against the exact case that motivated it surfaced a more precise root cause than assumed: that particular model's dispatches returned a genuinely empty response, confirmed via the persisted session log, zero events, no tool call, no text at all, not a narrated report, the same symptom already documented for a different candidate. Salvage correctly declined to act, since there was nothing above the 200 character floor to recover, rather than fabricating content from nothing. A full re run confirmed the diagnosis further: still missing_findings, all 8 attempts, and the final corrective pass decoded over 45,000 tokens continuously, blew past its own context window once, and was still running when checked, a second, independent confirmation of the runaway generation failure class this project had already fixed the missing guard for elsewhere. The final verdict for that specific candidate: genuinely no recoverable content to give in the FindingsWriter role, empty responses and runaway non answers alike, a harder failure than "narrates instead of writing," and no structural salvage can rescue a dispatch that produces nothing at all.

Shared quota pool starvation, fixed on 2026-07-18. This ring fences a task's remaining quota once it's shown real fetch activity this dispatch: the first time a tool call would exceed the shared cumulative limit for a task that's already fetched something real, it grants one small one time top up instead of hard blocking, bounded so it can only fire once per tool per run. This directly targeted a documented failure where a dispatch had fetched 2 real sources, then hit a bare "Quota reached" wall before ever synthesizing them.

A Brave Search MCP country parameter rejecting real countries, including Colombia, got fixed the same day. The MCP server's country param turned out to be a fixed 37 code enum that doesn't include Colombia's code at all, confirmed by reading the installed package's own schema source, which broke every Colombia targeted search outright. A wrapper strips an out of enum country value before it reaches the subprocess, falling back to an unscoped search instead of a hard rejection, scoped only to specs whose server name contains "brave" so it can't affect any other MCP server.

The completion check remediation loop exhausting read_workspace_file's quota before the final Builder pass gets to read what it needs, found live on 2026-07-20 during the routing classifier's second re test run. A run hitting multiple completion check remediation cycles dispatches a corrective sub agent per cycle, each burning read quota against the same shared pool as normal first pass work, and 3 remediation cycles in one run exhausted the quota, so the final corrective Builder pass silently dropped an entire correctly researched section rather than erroring loudly. Fixed on 2026-07-21 with a new helper mirroring the existing writer side quota top up, applied at both Builder and FindingsWriter remediation dispatch sites.

Two shipped TUI quality of life improvements, moved from Pending during the 2026-07-21 status audit, the rest of that backlog stayed open. AgentMessageWidget click to copy landed on 2026-07-14, mirroring the existing user message widget's copy pattern exactly, one click copy on the agent's actual answers and reports, not just the user's own prompt. Right click paste landed the same day, a read side mirror of the existing copy helper, using the system clipboard tools with no OSC52 equivalent since that escape sequence is write only, wired into a right click on the prompt input, inserting at cursor or replacing the current selection. Live verified: a real copy then paste round trip returned the exact original text. This required installing the actual clipboard tools on the dev machine, since neither was present beforehand, meaning copy had been silently falling back to an unverified OSC52 path and paste had had no fallback at all, worth checking for on any fresh setup, since without one of these two tools paste always shows a "clipboard paste failed" warning instead of pasting.

Clone this wiki locally