-
Notifications
You must be signed in to change notification settings - Fork 0
Completed I
Part 1 of the Completed list of finished ROADMAP items.
Builder duplicate section bug: an edit_workspace_file call that retypes existing content while
adding new material. Root caused from the live session transcript, implemented and live
validated on 2026-08-17. We traced final_report.md's two near identical "Mexico City" and
"Mexico City, Central Districts" sections to the exact edit_workspace_file call that caused them
(BuilderFix_attempt3_reviewed, the run's last action): old_string="### Mexico City" (the bare
heading), and new_string was that heading plus the heading's own pre existing bullet retyped
verbatim plus a new subsection. Since the original bullet was never part of old_string, it stayed
where it was, and the retyped copy landed directly in front of it. Fixed at two layers. First,
BUILDER_INSTRUCTIONS/FINDINGS_WRITER_INSTRUCTIONS (src/prompts.py) now explicitly say to
anchor an adding edit on the boundary of existing content, never retype it into new_string.
Second, find_duplicate_report_sections/check_duplicate_report_sections
(src/engine/completion.py) is a new structural, self consistency completion check comparing every
h3 plus subsection of a report against every other via _content_word_overlap (threshold 0.6),
registered in GROUNDING_CHECKS/_QUARANTINE_PROBLEMS/_BUILDER_FIXABLE_PROBLEMS, gated by new
settings.duplicate_section_check.enabled. Live validated by running the new function directly
against the real incident's final_report.md: it correctly flags exactly the one real duplicate
section and nothing else, no false positives on the genuinely distinct Lisbon/Portugal sections.
Citation misattribution: a real, correctly fetched claim gets attached to the wrong cited URL.
Implemented and live verified against the real incident data on 2026-08-17. Found via a manual
claim by claim audit of the ablation smoke test's own final_report.md, after the user asked "did
you evaluate the report properly?" since the automated structural score had missed it entirely.
Four real, accurately worded Mexico visa figures (the "300 days" income formula, the "$53" fee, a
sworn translator requirement, and the MiConsulado interview) were all traced verbatim to
themexicohandbook.com's fetched content but cited to esimcard.com instead. Both URLs were
genuinely fetched this run, so the hard grounding gate passed, and claim_grounding_problem's term
overlap gate passed too, on nothing more than a coincidentally shared bare year, since both sources
were 2026 dated visa guides. extract_salient_terms's own number regex is narrow enough (period
grouped decimals, bare 4 digit years, percentages only) that it never extracted "300" or "53" as a
checkable term at all. This is the same failure shape find_unsupported_regulation_ids already
catches for regulation identifiers, generalized to the broader class of small numeric claims via a
new sibling function, find_unsupported_specific_figures (src/utils/grounding.py): line scoped,
matches dollar or currency figures and day or month count claims, and flags one that's verbatim
absent from its own cited source. Wired into cheap_grounding_problems (new
settings.grounding_check.specific_figure_check, default true) and a new
check_specific_figure_unsupported completion check. A real implementation bug got caught and
fixed during live validation, not just synthetic tests: the first version stripped commas from the
claim's digits but not from the source content before searching, so a genuinely supported figure
whose source also formats it with a thousands separator comma (source says "$1,200," search target
was bare "1200") false positived as unsupported. Fixed by normalizing thousands separator commas
out of the source content too, then re validated against the same real report: the false positives
resolved, and the true incident plus two more previously unnoticed real grounding failures in the
same report (a fabricated Lisbon rent range cited to a property purchase price page with no rental
data at all, and the model's own unstated USD approximation absent from every fetched source) were
all correctly caught.
Output and export feature gaps, plus an optional HTTP API and web UI. Implemented and live
verified on 2026-08-02. Scoped from a purpose versus built feature gap check rather than a code
audit: research inputs were web only, output was markdown only, no programmatic API existed, and
research_output/ folder names collided for long queries sharing a prefix. Several things shipped
together. Folder name collisions got fixed: _slugify_run_dir_name (engine/tui.py) now appends a
6 hex sha1 hash of the full query, not just its truncated slug. A references.bib output got
added, built from what final_report.md actually cites, not the raw fetched URL list, since a
bibliography from "everything fetched" would list sources the report never used, a gap caught and
corrected before implementation. --seed-doc//seed-doc added local document ingestion (PDF,
DOCX, XLSX, PPTX, txt, md) mirroring the existing --seed-url pattern, deliberately exposing no new
tool to the model, since the engine just loads the file into the workspace and read_workspace_file
already covers it. settings.pdf_engine added an optional final_report.pdf via pandoc, weasyprint,
or system LaTeX. A real bug got found and fixed here too: pandoc's --pdf-engine=weasyprint shells
out to the weasyprint CLI, which lives in the venv's bin/, not the system PATH when invoked as
~/.venvs/deepdelve/bin/python directly, fixed by prepending the executable's directory to the
subprocess env's PATH.
The bigger piece was src/api.py plus src/static/index.html, an optional FastAPI HTTP API and
web UI. A key finding before writing any code: orchestrator.py's _session and tui.py's
session log state are module level globals, not contextvars, so two truly concurrent runs in one
process would corrupt each other's state. Fixed by design with one in process FIFO job queue and
one worker coroutine, always single flight, deliberately a third copy of run_cli/run_agent's
orchestration shape rather than a fourth shared abstraction. New endpoints included POST /research, GET /research/{id}/status|stream|report|bib|pdf, POST /research/{id}/resume|followup|cancel, GET /runs, and GET/POST /settings. A follow up design
correction got caught before implementation: the original plan leaned on orchestrator_module._session
surviving in memory between a run and its follow up, but the shared queue can run an unrelated job
in between and silently clobber it. Fixed by persisting session.to_dict() to
<run_dir>/_agent_session.json per run instead of trusting the global. A real bug found live, not
just written and assumed correct: /followup's first version ran the full completion check and
artifact rewrite pipeline against a follow up question, the same as a fresh run, which rewrote
findings.md repeatedly and never touched final_report.md. Root cause was missing that
tui.py's own run_agent/is_followup branch deliberately sets skip_completion_check = True
once the required artifact already exists, fixed by mirroring that condition in
api.py::_run_research. Two more real bugs surfaced during the frontend pass: _artifact_path
gated report, bib, and pdf routes on run_id in _jobs, which is in memory and this process
lifetime only, so any run from a prior server process 404'd with a misleading "Unknown run_id"
despite having real artifacts on disk, fixed to check the workspace directory directly. And FastAPI
doesn't auto add HEAD support to a @app.get route, so every "does this artifact exist" check in
the frontend, which used fetch(..., {method:"HEAD"}) specifically to avoid downloading full
content just to check presence, silently 405'd, meaning no Report, Bibliography, or PDF button ever
appeared, fixed via @app.api_route(..., methods=["GET","HEAD"]). A new
settings.api_password option, unset by default to match the loopback only posture, got added
specifically because full settings CRUD (API keys included) plus user requested LAN or phone
access together raised the stakes enough to warrant it, layered on top of the existing
--i-understand-the-risk non loopback bind guard. The web UI itself has no CDN or build step, uses
a small dependency free markdown to HTML renderer for the report viewer rather than a general
CommonMark library, since this project's own report dialect is narrow and predictable, renders
settings as a real generated form from the config's own shape instead of a raw JSON textarea, and
uses a signal lamp status indicator as the one deliberate animated element, with reduced motion
respected. Status text shows the user's actual query, never the internal run id slug, caught live
after a resume plus cancel left that slug as the only visible status. The mobile responsive layout
was built to spec but not visually verified this session, no browser automation tool was available,
so it needs a check on an actual phone before being treated as done.
Also folded in the same session, all 5 findings from an earlier 3 agent audit covering security,
correctness, and production readiness: an SSRF guard on outbound fetches (tools/web.py, an
httpx event hook checking every request's real resolved target, including mid redirect chain
hops, against loopback, link local, private, and reserved ranges), a no_urls_count resume
carryover gap, traceback leakage trimmed across tools/core.py/web.py/fs.py (exception type
and message only, no local filesystem paths handed to the model), enable_session_persistence's
_DEFAULTS fallback fixed to match config_template.yaml's real default, and a new test_tools.py
(same flat assert based convention as test_structural_checks.py, wired into CI) pinning the SSRF
guard plus several previously untested pure logic functions.
Completion check starvation bug class: 8 real instances found and fixed, then replaced with one
shared structural mechanism. Implemented and live verified on 2026-07-31. This started as an
Ornith 1.0 9B re test (see the Model Bake off page), but the same night's
investigation surfaced the identical bug shape repeatedly on gpt-oss, the project's own default,
too: a COMPLETION_CHECKS/GROUNDING_CHECKS check that isn't Builder or FindingsWriter fixable
and never caps its own firing wins first match on every attempt for as long as its condition holds,
permanently starving every check below it. Six live incident patches went in one night, covering
consecutive counter duplication across check_task_verification_flagged and
run_completion_check's own force_whole_rebuild, final verdict salvage's hardcoded problem name
tuple widened three times, check_task_verification_flagged/check_thin_coverage left uncapped,
and a hand written lambda c: A(c) or B(c) starvation guard for
report_underuses_findings/_evidence that was live confirmed dead code, since or's short
circuit meant the already winning check got tried first. At that point the user explicitly asked to
stop patching incident by incident: "we're having too much issues with starvation we need to
create a defined structure, we're guessing."
The structural fix, in src/engine/completion.py: _consecutive_occurrences gained an optional
skip_problems set, becoming the one canonical counting definition, previously duplicated three
times with drifting copies of the same fix. CONSECUTIVE_SAME_PROBLEM_ESCALATION_THRESHOLD got
hoisted to module level, since two separate copies of "3" had already silently disagreed once
mid session, caught by the test suite. A new _capped(ctx, problem, verdict, skip_problems)
became the required call for any non self resolving check. A new _STARVATION_YIELD_TARGETS dict
plus _apply_starvation_yield replaced the buggy lambda with a declarative mechanism that can't
repeat the ordering bug by construction. Final verdict salvage's hardcoded tuple got replaced with
an unconditional call, since the function's own 200 character gate was always the real safety
check. A systematic audit, not another live incident, found 2 more previously unknown real
instances, check_propagated_ungrounded_content and check_report_underuses_evidence, both
missing a cap, both found by checking every entry in both lists against the invariant before they
ever caused a failure. A new standing test in test_structural_checks.py asserts every non self
resolving check in COMPLETION_CHECKS + GROUNDING_CHECKS calls _capped, which is the actual
"defined structure" payoff, a future check that skips it fails the suite immediately. The design is
grounded in real prior art: OS scheduler aging, the circuit breaker pattern (Nygard, Release It!),
Chain of Responsibility (GoF), and a 2026-07-14 industry piece applying the same
retry/circuit-breaker/fallback-chain shape to production LLM agent systems, confirming this is a
known missing layer, not a codebase specific invention. Live verified post refactor: a fresh
gpt-oss run against the standing sales forecasting benchmark converged cleanly through the full
check chain, no starvation, no stuck pattern, and produced a real, honestly caveated, correctly
grounded final_report.md. Full detail is in ARCHITECTURE.md section 1. One honest caveat this
fix does NOT resolve: that same live verification run's report, while structurally sound, didn't
actually meet the query's own expectations, no "top 5 heuristic algorithms" list, no Colombia
cultural pattern integration, despite check_report_underuses_findings/_evidence correctly
flagging both gaps every attempt. The pipeline is now working as designed, real convergence, no
fabrication, but the model's own tendency to abandon the harder half of a multi facet query under
repeated correction is a genuine, separate, still open capability gap. See the
Model Bake off page's gpt-oss:20b entry.
check_task_verification_flagged escalation streak fragmentation plus a static warning text
bug. Implemented and live verified on 2026-07-29, found while live smoke testing the two fixes
above. Two live runs of the standing two facet benchmark query, one fresh, one --resume-run, both
ended Report: NOT WRITTEN, with task_verification_flagged cycling for the entire retry budget
and never reaching findings.md/final_report.md at all. Root caused via _run_state.json
directly, not assumed: check_task_verification_flagged's escalation from "delegate tasks again"
to "acknowledge the gap," and eventually to force_whole_rebuild, counts a consecutive streak of
the same problem, but check_untracked_delegation firing once in between, itself a direct symptom
of the model failing to comply with this check's own "stop redelegating, reuse the exact
task_name" directive, confirmed live, reset that streak to zero. So a run stuck on the identical
underlying problem for its whole budget kept getting the same "redo" directive instead of ever
escalating. Separately, the check's own .warning field, the human or log facing message distinct
from .inject, the model facing directive, was a static string always claiming "Pushing agent to
redo them specifically" regardless of which of the three branches (fresh redo, acknowledge after
repeat, quota exhausted stop) actually fired, which made diagnosing this exact incident from
_run_state.json alone harder than it should have been. Fixed:
check_task_verification_flagged's own prior_same counting now treats untracked_delegation as
a continuation, not a break, scoped specifically to that one symptom, since a genuinely different
interrupting problem still correctly resets it. .warning now has three distinct messages matching
the three directives. Live reverified directly against both failed runs' actual saved
_run_state.json sequences: replaying the exact attempt history now correctly produces
"acknowledge the gap" instead of "delegate tasks again" at the point the real run got stuck.
check_report_underuses_evidence, a Builder stage per task coverage check. Implemented and
live verified on 2026-07-29. check_findings_underuses_evidence (2026-07-26) already guarantees
every covered top level task has at least one real URL surviving into findings.md, but nothing
then guaranteed Builder's own selection from findings.md represented every task either.
check_report_underuses_findings's flat citation ratio can pass while every surviving citation
comes from a single task, confirmed live on 2026-07-28 (part 4 of the Literature Review): gpt-oss:20b
dropped a heuristic algorithms task entirely for an off topic citation, but still cleared the 50
percent ratio threshold on Colombia's larger raw source count. This one is literature grounded (the
NAACL 2025 "Coverage based Fairness in Multi document Summarization" paper, arXiv:2412.08795,
validates a binary per cluster presence check as a legitimate, cheap proxy for their more expensive
NLI based "Equal Coverage" metric). It's a new sibling check with the same per task binary
presence design as its upstream sibling, wired into GROUNDING_CHECKS and the existing starvation
guard. Live smoke tested (real gpt-oss:20b, a 48 minute run, the standing two facet benchmark
query): it correctly stayed silent on a report that unevenly but genuinely covered both tasks, no
false positive.
Check priority shadowing, or cross fix regression blindness. Implemented and live verified on
2026-07-29. The same live smoke test above surfaced a second, independent, more serious bug once
the user insisted the actual report content be read, not just checked for crashes:
check_uncited_claims never got a turn across 3 completion check attempts because
check_stub_source kept winning. Traced to real_grounding_problem's own internal ordered if
chain (utils/grounding.py) returning only its first hit, so every GROUNDING_CHECKS function
keyed off the single shared ctx.grounding_problem string can be permanently shadowed by a
persistently recurring higher priority one, and re running a sibling check against the same ctx
can never reveal a second problem, since the fact was never computed. The terminal "retry budget
exhausted" message reported only stub_source, even though uncited_claims, 6 figure bearing
lines with no citation, confirmed by calling find_uncited_claim_lines directly against the run's
actual saved output, was independently, simultaneously true the whole time. This one is also
literature grounded ("Regression Accumulation in Multi Turn LLM Programming Conversations,"
arXiv:2607.01855, finds 55.7 percent of multi turn regressions are a later fix breaking an earlier
requirement through incompatibility, not forgetting, and their validated fix, "Verification Gate,"
is full re verification every turn with every failing constraint made visible). Fixed at the
correct layer: utils/grounding.py::cheap_grounding_problems extracts the pure string and regex
sub checks (stub_source, non_url_citation, regulation_unsupported, quote_paraphrased,
claim_unsupported, uncited_claims, deliberately excluding NLI and reranker model inference checks
to avoid multiplying that cost every attempt) into a function that returns every hit, not just the
first. real_grounding_problem itself is unchanged in behavior, verified against 18 other call
sites. completion.py gained _other_grounding_problems/_with_other_grounding_addendum for the
correct layer plus _collect_other_active_problems/_with_other_problems_addendum for
COMPLETION_CHECKS, whose checks are genuinely independent of each other, unlike most of
GROUNDING_CHECKS, confirmed via direct testing after the first implementation attempt targeted
GROUNDING_CHECKS itself and was proven dead code by feeding it this session's own real incident
data. This deliberately does not change which single Verdict is "the" recorded problem, the
escalation or bonus counters, or the verdict matrix tests, only the addendum text shown to the
model and the terminal user facing message gain the extra visibility, preserving the
one primary directive per turn design. Live reverified directly against this session's own captured
incident: feeding the real saved final_report.md into the fixed code now correctly surfaces
uncited_claims alongside stub_source.
FindingsWriter evidence base quality, two fixes. Implemented and live verified on 2026-07-21,
confirmed working at their own layer, though the deeper root behavior they were meant to help with
stayed open (see the Pending section of ROADMAP.md). Root caused via the actual session
transcript, not guessed: a live run's FindingsWriter dispatch abandoned a complete, in budget, 30
real finding evidence base ("we don't see them here," its own recorded reasoning) and went to re
read raw source files by hand instead, writing findings.md from only 1 of 33 real findings. This
was researched against real prior art before building (RAG noise robustness literature,
DeepResearch Slice arXiv:2601.03261, read in full, whose diagnosis of "distracted by spurious
passages" as one root cause of exactly this retrieval utilization gap directly supported fix 1
below; LLMxMapReduce arXiv:2410.09342, read in full, informing a not yet built chunked dispatch
alternative). Fix 1 filtered relevance flagged findings out of the citable list
(src/engine/completion.py::_is_citable_finding): a finding confirmed off topic by
orchestrator.py's scope relevance check was still rendered as an ordinary citable entry,
indistinguishable from genuinely useful findings around it, live case was a Colombia holidays task
that fetched a New Zealand page. Scoped specifically to the relevance marker, not the two
verification warning variants, since those are a narrower citation mismatch that may coexist with
real content. Fix 2 threaded the already extracted page title through to the evidence base: the
evidence base rendered ### Source: {url} with no title while FINDINGS_WRITER_INSTRUCTIONS
requires the model's own output to be ### [Title](url), a real format mismatch forcing the model
to invent a title for every entry before it could start. A real title is already extracted at
fetch time but was only ever written into the saved file's own header, never threaded to
run_state.data["fetched_urls"]. Now it is. A live re test confirmed both fixes fire correctly,
the one relevance flagged finding this run was correctly excluded, and most fetched URLs got a
real title in the correct format, but FindingsWriter's first action was again reading raw source
files directly, bypassing the now cleaner, better formatted evidence base entirely, only 2 of 44
real findings made it into findings.md. Two independent live runs now confirm the model's raw
file exploration habit is independent of evidence base noise or format, real progress on evidence
base quality, but not the fix for the actual behavior.
FindingsWriter evidence abandonment, root cause closed on 2026-07-22, moved here from Pending
on 2026-07-24 after a documentation sync gap, since the fix shipped the day after the entry above
but Pending was never updated to reflect it. The two contributing factor fixes above held at their
own layer but didn't change the headline behavior. Two further changes, same session (commit
f1562f7, "Fix FindingsWriter evidence-abandonment: structural write-first gate + dedupe multi-URL
fan-out"), actually closed it. First, writer_gate_ctx (src/tools/core.py, a new contextvar plus
check_writer_gate, wired into with_quota's sync and async wrappers) blocks
read_workspace_file/grep_workspace_file until the armed gate's write_workspace_file call has
happened, armed only for FindingsWriter, never Builder, whose own instructions correctly require
reading findings.md first. A prompt only reorder of FINDINGS_WRITER_INSTRUCTIONS' workflow was
tried first and live disconfirmed, the model still called read_workspace_file first, before
escalating to this structural gate, which is what actually held. Second,
_collapse_multi_url_task_findings (completion.py) was found while investigating why even a gate
forced first write stayed thin. orchestrator.py::_run_single_task's add_finding call fires once
per fetched URL but attaches the same task level summary every time, so a task fetching N URLs
produced N near identical "findings," inflating the raw count and letting whichever task fetched
most URLs dominate the position bias reorder's favorable edges by fetch count alone. This groups
citable findings by task name and summary before rendering, keeping the body text once per group
while every real URL is still individually named for citation. Live confirmed closed on a third
smoke test run: the evidence base collapsed to 5 real distinct clusters from a raw 15 citable
findings, and findings.md wrote all 5 of 5, with the first FindingsWriter dispatch calling
write_workspace_file directly with zero blocked reads, the gate never even needed to fire. Only 2
completion check retries total this time, versus 4 and 6 in the two prior live tested runs, the
fastest, cleanest convergence of the three. final_report.md ended up thin but honest, 3 of 5
sources genuinely had nothing usable, and Builder said so instead of fabricating filler. Two
smaller items surfaced during this investigation but weren't covered by this fix and got tracked
separately: one Searcher sub agent fired around 14 near duplicate web_search calls on one angle
without diversifying into a sibling angle its own task line implied, and a cosmetic mojibake
filename showed up in one findings.md heading, diagnosed as the model degenerating into token
repetition on a genuinely empty source rather than a code bug.
Shared quota starvation, angles (a) and (c). Implemented and live verified on 2026-07-21,
found via a live benchmark run, the standing sales forecasting and heuristic algorithms query,
right after this session's 4 synthesis fixes shipped, that failed outright: Report: NOT WRITTEN,
thin_coverage 4 times consecutively, retry budget exhausted. Root caused with hard evidence, not
assumption: one sibling task produced 14 finding entries and 26 tool call log lines, while its
four siblings got exactly 1 log line each and the model's own narration said outright "could not
be retrieved due to exhausted search quota." Angle (a), in src/tools/core.py::check_quota: the
existing per task rescue required task_fetched_urls_ctx to be non empty as proof a task had
already fetched something, but web_search never populates that context var, only
fetch_url_to_workspace does, so a task blocked on its own first web_search call could never
qualify, exactly backwards from what a starved task needs. Dropped the requirement, every task now
gets one guaranteed grace top up the first time it hits the wall, proven progress or not, still
bounded to once per task_id. Angle (c), in src/engine/orchestrator.py, added a new
_reserve_batch_quota_headroom at delegate_tasks's call site: with max_concurrent_tasks small,
often 1, tasks in one delegate_tasks batch run roughly in listed order, sequentially draining the
same shared pool, so a heavily active sibling can structurally starve later listed ones even
across completion check topups. This pre reserves enough total headroom for the whole batch before
any task in it starts, removing the structural first mover advantage, though it's not true round
robin scheduling, which would need cooperative mid turn interleaving, a much bigger change, it just
bounds the damage instead of eliminating dispatch order entirely. A live re test on the same query
confirmed the fix: zero thin_coverage attempts versus 4 before, 25 findings recorded across 9
balanced task angles versus one task dominating 14 of 28, and a real report converged in 4
attempts. Quota starvation itself is closed.
A second, distinct bug turned up in that same re test and also got fixed: findings.md carried an
AUTO-RECOVERED DRAFT banner, FindingsWriter had narrated instead of calling
write_workspace_file, and the narration itself got cut short by its own generation budget
partway, capturing only 3 of 25 real findings. Root cause: SUBAGENT_BUDGET_NUDGE
(orchestrator.py) is a single, role blind nudge shared across every dispatched role, and its
text, "do NOT call any more tools, return as your final message," is correct for Searcher and
Analyzer, whose final message is their findings, but directly instructs a writer role, whose entire
success criterion is calling write_workspace_file, to do the exact opposite of its job the moment
it hits budget pressure. A new _select_budget_nudge (pure, testable) plus
SUBAGENT_BUDGET_NUDGE_WRITER route Builder and FindingsWriter to a write the file now nudge
instead. This was the user's own catch, not found via self audit, findable by reading the code
without needing a live failure, folded into the standing "check all surfaces" lesson. A follow up
live re test confirmed this specific bug was fixed, but surfaced a deeper, still open one: no
AUTO-RECOVERED banner this time, findings.md written via a real write_workspace_file call,
faster convergence, but findings.md contained exactly 1 of 33 real findings recorded in
_run_state.json, since the model chose to write a single well formatted, fully cited entry rather
than all 33, and no existing check verifies "findings.md reflects everything found," only
"findings.md exists and is formatted correctly." The final report was grounded, zero fabrication,
but entirely about an unrelated topic, missing every Colombia and heuristic algorithm angle despite
most fetched sources containing real Colombia content. Net effect: this converted a total loss
failure into a partial loss failure, real progress, not a full fix. The underlying pressure, that
context_budget_chars's single roughly 50,000 character ceiling is too tight for FindingsWriter to
consolidate a large finding set in one dispatch, narrating or writing, stayed open and tracked as
its own item.
Four literature backed synthesis and reliability candidates. Implemented on 2026-07-22,
planned and executed the same session. All four extend src/engine/completion.py's
GROUNDING_CHECKS/_dispatch_writer_review_fix/run_completion_check. The first,
findings ordering, drew on "Lost in the Middle" (arXiv:2307.03172) and PING's "Anchor Effect"
(arXiv:2601.22984): _reorder_findings_for_position_bias, right before
_build_findings_source_material, is a pure positional zigzag or sandwich reorder, since no
value or importance signal exists anywhere in the data model to rank by, splitting chronological
entries in half and interleaving front forward with back reversed so every finding lands within
one hop of either edge of the assembled context instead of drifting toward the middle as a run
accumulates more findings. The second, a propagation aware hallucination check drawing on the PING
taxonomy, got narrowed from the paper's full DAG plus NLI entailment design, since DeepDelve has no
claim dependency structure and the paper's own released code doesn't actually implement that
mechanism either, down to the specific, already documented split brain pattern: a citable finding
whose content substantially term overlaps an uncited or cutoff sibling for the same task name. A
new _find_propagated_bad_content plus check_propagated_ungrounded_content only fires if the
flagged content also appears inside the report itself, not merely in findings.md. The third, forcing
reasoning at synthesis time, drew on PIVOT (arXiv:2605.11225), and corrected the original
candidate's premise: BUILDER_INSTRUCTIONS/FINDINGS_WRITER_INSTRUCTIONS already instruct
think_tool use before finalizing, confirmed by reading both directly during planning. So this
shipped as verification rather than a new prompt: _dispatch_writer_review_fix now snapshots
think_tool's quota usage before and after the Write dispatch, reusing the same reads before and
after pattern already proven there for PeerReviewer's read_workspace_file check, deliberately not
a hard gate since a writer skipping think_tool makes no false claim, unlike a fabricated "REVIEW:
CLEAN." The fourth, full artifact rebuild repetition escalation, drew on an ACM CAIS '26
planning horizon paper and also corrected the original candidate's premise: the real current
behavior of CONSECUTIVE_SAME_PROBLEM_ESCALATION_THRESHOLD was "give up early" on 3 consecutive
identical problems, not a "narrower nudge" as originally described, so there was no existing gentler
nudge to sharpen. The user decided on "the more complete one," a full rebuild rather than a
wording only reframe, matching the paper's actual full horizon replan mechanism: on the third
consecutive occurrence, the system grants exactly one extra attempt, bounded to once per problem
type, that dispatches a genuine full rebuild instead of the usual targeted fix instructions. Three
more literature leads found via fresh search, plus three RAG repos the user asked about, LightRAG,
RAG-Anything, and GraphRAG, were also researched this session but none were adopted, either
reviewed and not applicable or too weak an evidence base on a full read.
Engine driven iterative deepening, from ROADMAP item 10 and the dzhng/deep-research
candidate. Implemented on 2026-07-19, found still mis listed as an untried candidate during the
2026-07-21 status audit. This is a structural refine loop: each round's Searcher findings carry a
FOLLOW-UP DIRECTIONS section, stored per finding. When check_thin_coverage fires,
_select_deepening_tasks picks real, unconsumed directions and geometrically narrows them, using
the exact newBreadth = ceil(breadth/2) formula from the source project, deduplicated against
already consumed directions so a retry never redispatches the same lead twice.
_dispatch_deepening_round then dispatches them directly, bypassing the Planner entirely, the same
mechanism _dispatch_writer_review_fix already used for Builder and FindingsWriter retries, so the
engine drives the extra research round rather than hoping the Planner model chooses to loop.
A non generative routing classifier for delegate_tasks's agent_id. Implemented and live
verified on 2026-07-20, merged from the SOTA literature review: small and mid sized models fail
disproportionately at structured serialization, schema valid output but wrong content, not
semantic understanding, in a way a 6,000 sample SFT run can't fix, per the constraint tax papers,
confirmed live in this project's own data at roughly 4.9 percent of real delegate_tasks calls
using a hallucinated but well formed agent_id. Routing got pulled out of free generation into a
frozen all-MiniLM-L6-v2 embedding plus a LogisticRegression(class_weight="balanced"), with a
decided policy of reject and nudge, not silent override, not advisory only. A new extraction
script, run against 101 real session logs, found 1,096 valid pairs and 57 hallucinated, about 4.9
percent, matching an earlier ad hoc count almost exactly, now from a reproducible script. After near
duplicate removal, 814 valid pairs remained, with a class distribution of DocumentAnalyzer 331,
WebSearcher 330, DataAnalyzer 99, and AcademicSearcher 54, split 651 train and 163 held out. A new
training script got real held out per class results: DocumentAnalyzer at 0.89 precision and 0.88
recall, WebSearcher at 0.89/0.85, DataAnalyzer at 0.68/0.65, and AcademicSearcher, the weakest
since it's the smallest class, at 0.44/0.64. Overall accuracy came out to 0.82. A new
src/utils/agent_routing.py module is a lazy singleton that fails open, and a real design gap got
found and fixed before shipping: the classifier's 4 known classes span two different delegation
levels, so predict_agent_id takes a candidate_classes param restricting prediction to the
intersection of the caller's own real roster and the classifier's known classes, never a blind
global argmax. New logic in src/engine/orchestrator.py rejects a delegate_tasks call when the
declared agent_id isn't real for this caller, or when the classifier disagrees above
min_confidence, silently no opping on the common case. The config default shipped as enabled: false, a conservative rollout matching this project's own standing caution about untested
features.
The first live end to end test, same day, found a real regression and reverted the config back to
enabled: false. With the feature flipped on, a real headless query exercising both delegation
levels worked correctly at the nested level, but the Planner level AcademicSearcher dispatch was
wrongly rejected 8 consecutive times, every single retry of "find peer reviewed papers on
borrow-checker soundness" got rejected as "looks like a WebSearcher task" at 0.67 to 0.75
confidence, above the 0.6 threshold, across roughly 2 real minutes and 8 wasted Planner turns,
until the Planner gave up on the angle entirely. The concrete, measured harm: the final report
contained zero content on the academic angle, half the user's actual question never got
researched, a real content coverage failure directly caused by this feature. Root cause:
AcademicSearcher is the classifier's weakest, smallest class, and a flat min_confidence=0.6
threshold treats a confidence number as equally trustworthy regardless of which class produced it,
but this run showed the model can be confidently wrong specifically for the class it's worst at.
This was a structural policy flaw, not just a data problem: reject and nudge conflated two
different risk profiles under one policy, rejecting a declared role that isn't real for this caller
at all versus rejecting a declared role that is valid but the classifier disagrees with.
A same day data quality fix found a real, root cause bug in the training data itself. Auditing the
54 AcademicSearcher examples by eye surfaced 6 that were obviously market sizing or business
research tasks, not literature searches, tracing further to 13 total from one session, a near
identical task template repeated with a different sector noun each time, sitting alongside a
differently phrased sibling task in the same session correctly labeled WebSearcher. Two
general purpose automated detectors were tried and rejected here, worth keeping the reasoning for:
plain text similarity never caught this since different sector nouns keep character overlap below
the dedup threshold, and embedding cosine similarity couldn't cleanly separate "same underlying
ask, inconsistently routed" from "different ask, same topic domain, correctly routed differently,"
since this project's own real historical benchmark queries cluster so heavily on one topic that a
loose enough threshold also flagged hundreds of genuinely correct pairs. The team landed on a
direct, documented exclusion of the one manually verified session instead of forcing an unreliable
general heuristic. After retraining on the cleaned data, the AcademicSearcher class shrank from 54
to 50 examples, but held out recall rose from 0.64 to 0.80 at the same 0.44 precision, fewer false
negatives, directly the failure direction that caused the live regression. All 3 real instruction
variants that failed live now correctly predict AcademicSearcher. A second, same day live re test
of the exact query that failed before confirmed the fix held in the real pipeline: zero classifier
rejections anywhere in the run, and the AcademicSearcher task was accepted on the first try. A
second, unrelated bug surfaced in that same run and got recorded separately: final_report.md
dropped the entire academic section despite findings.md having it correctly, root caused to the
run hitting 3 completion check remediation cycles that each burned read_workspace_file quota,
exhausting it by the third BuilderFix pass, which then said outright it couldn't re read the
source file and cited only 2 of roughly 7 real sources. This wasn't fixed in this session, just
newly found.
Sub agent dispatches had no wall clock deadline at all. A real, live confirmed gap, fixed and
live verified. engine/tui.py's run_cli already raced each stream update against
settings.max_run_minutes via asyncio.wait_for instead of a plain async for (a 2026-07-12 fix,
because a plain async for only checks a deadline once it actually receives an update, invisible to
a stream that goes silent for a long time). That fix never propagated to
engine/orchestrator.py::_run_single_task, the code path every Searcher, Analyzer, Builder,
FindingsWriter, and PeerReviewer dispatch goes through, which used a bare async for update in stream: with zero deadline, relying entirely on the raw OpenAI SDK HTTP client's blunt roughly 600
second default connection timeout, which then discards the whole in progress response and raises a
generic error instead of degrading gracefully. Root caused live on 2026-07-14 during Phase 4 smoke
testing, after the user pushed back on accepting repeated live run timeouts as "just model
slowness" without further investigation, correctly, since the real cause turned out to be
structural: cross referencing Ollama's own logs against the exact failure window found two requests
that returned HTTP 500 after hanging exactly 10m0s and 6m24s. The 10 minute one wasn't actually
stuck, Ollama's own timing log showed it continuously, validly decoding tokens the entire time up
to nearly 20,000 tokens when the connection was force closed, matching almost exactly what 600
seconds at that model's real token rate would produce. A single sub agent turn had run away into a
very long generation with no early cutoff mechanism watching it at all. A first fix attempt was
itself incomplete, caught by live verification, not just the unit suite: it gave _run_single_task
the same max_run_minutes deadline as run_cli's own top level guard, anchored to the same run
start clock. Live testing with a tight 2 minute budget showed a cutoff did fire, but the persisted
session log showed the new inner marker text never actually appeared, since the outer guard's
cancellation had propagated down and pre empted the inner one before its own deadline check ever
got a chance to run on its own terms, meaning in realistic configs the new code was effectively
dead. The real fix was a new, independent settings.sub_agent_timeout_minutes (default 10), a
fresh per dispatch deadline computed at the start of each _run_single_task call, not shared with
the run wide clock. The client's own SDK timeout also got bumped to run comfortably past both
budgets, otherwise the SDK's own blunt default kept winning the race against either graceful
cutoff. Live verified directly: with a generous 45 minute run budget and a tight 1 minute
sub agent budget, a sub agent dispatch was cut short within a minute, and the Planner's own next
turn literally said "The task timed out, but the capital of Italy is a well-known fact (Rome)... I
stop delegation immediately," proving the graceful marker text reached the Planner and it
correctly adapted instead of hanging, retrying blindly, or crashing. This also retroactively
explained several earlier "timeout" observations from that same day's smoke tests that had been
attributed to model slowness or complexity, since this structural gap was the common thread making
any of those failure modes catastrophic, total loss of the turn, no graceful degrade, instead of
just slow.
History
Model Research
Reviews & Audits
Reference