Repository navigation
Architecture cleanup: fix live bugs, then deepen the LLM, eval-record, novelty, arms and process modules #4
Description
Why
An architecture review of dokimi found several concepts implemented in more than one place: the LLM call path, reading answer/eval records, spawning subprocesses, parsing arm specs, novelty coverage. The copies have drifted apart, and some of that drift is live bugs on main. This issue lists the fix series and the order it lands in, so reviewers can see the whole plan and contributors know which files will be busy.
Wave 1: bug fixes
Each PR carries a regression test that fails on main.
-
fix(tasks): resolve --output-dir before handing it to the agent, so a failed cell can't read as passed #8
tasks runwith the default relative--output-dircan score a failed cell as passed. The agent writes its reward relative to the task checkout, the framework reads it relative to its own cwd, finds nothing, and falls back toreturncode == 0. -
fix(panel): judge answers on their real question and context, and make parse retries real draws #5
eval panel-scorejudges every answer with an empty question and empty context: it readsquestion/content, but the answerer writesquestion_text/snippet. Separately, the panel's parse retries replay the same cached reply. -
fix(trust-gate): measure retrieval hit rate from a recorded retrieval outcome #10 The trust gate's
retrieval_hit_ratealways passes, because nothing writesretrieval_hit. Fix: the judge and panel stamp it per evaluation, and older eval files report "not measured". -
fix(cli): offer only the judge providers the Judge accepts #11 The CLI offers
openai-codexas a judge provider.Judgerejects it, but only after the answers have been paid for. -
fix(tasks): error the cell when its stale run dir can't be cleared #24 Follow-up to fix(tasks): resolve --output-dir before handing it to the agent, so a failed cell can't read as passed #8: error the cell when its stale run dir can't be cleared.
Wave 2: current model defaults
- feat(defaults)!: one current answer/judge/generator default on every surface (gpt-6.1-sol / claude-sonnet-5-5) #28 One default set on every surface:
gpt-6.1-solfor answers and generation,claude-sonnet-5-5for the judge. Dropgpt-4o-mini. Require litellm ≥ 1.105.0 for prices. Results will not be comparable with older baselines.
Wave 3: low-conflict refactors
- refactor(artifacts): write answers, eval and arms through save_artifact #27 Write answers, eval and arms through
artifacts.save_artifact(validated, atomic). - refactor(gate): merge soft_gate and hard_gate into one score_gate #22 Merge
gate/soft_gate.pyandgate/hard_gate.py. - feat(questions): write questions.v2 (question), keep reading v1 (text) #43 Add
questions.v2with a canonicalquestionfield; v1textfiles are still read. Generated sets currently failquestions.v1. - refactor(treatments): one arm-spec grammar module for split, parse and naming #26 One arm-spec grammar module covering
+and how names relate to specs. - refactor(novelty): the bundle record owns its validity #44 Novelty: the bundle record owns exclusion classes, coverage and the answer-key verdict (today the gate and the analyzer disagree on the unmeasured share). Follow-up:
RunSettingsand a thin CLI. Coverage basis: fix(novelty-gate): measure the unmeasured share over readable words, and stamp the basis #101. settings: refactor(novelty): one flag-to-setting resolver (RunSettings); thin the novelty CLI #105
Wave 4: central modules
Busy files during this wave: llm.py, utils.py, eval/*, report/*.
- One LLM-call module for provider routing, retry, draws, cache and usage, replacing
utils.get_llm. About 5 PRs. Step 1, the module with old entry points delegating: refactor(llm): one LLM module behind every call; old entry points delegate #78. - refactor(eval): give answer and eval records one reader (eval/records.py) #80
eval/records.py: a single reader for answer, eval and arms records. -
eval/compare.py: pairing, summaries and significance. This changes published numbers:report generateandreport evalcurrently disagree on the same file.
Wave 5
- Shared CLI model/provider flags, and an arms run module behind a typed run config instead of an
argparse.Namespacehand-off. - refactor(process): one process runner for git and misc spawn sites #29 (git and misc sites; harnesses follow) One process runner: kills the whole process tree on timeout, decodes UTF-8, takes an explicit env, keeps partial output. Rebuilt on the current
docker_solver. harnesses: refactor(harnesses): command, explorer, BenchFlow and repo-mode docker on the process runner #79 docker solver: refactor(harnesses): docker-* solver, sandbox and probe on the process runner #104
Wave 6
- Design issue: a harness = a profile + where the runner runs + a transcript parser.
How these land
- Each PR has one purpose. Fixes, behaviour-preserving refactors, and changes to numbers or defaults are never mixed in one PR.
- At most two open PRs per hot area. The next one is rebased after the previous one merges.
- Wave 4 lands back-to-back. Until it is through, please hold PRs that touch
llm.py,eval/*orreport/*, or mention them here.
Activity
napetrov commentedon Oct 15, 2026
Heads-up for Wave 4: #49 touches
eval/agent_runner.pyonly, in the tool-call loop and_summarize_tool_usage. It's a bug fix: arguments that fail to parse were replaced by{}and the tool still ran. It adds optionalerror/arguments_rawkeys to transcript records andtool_usage.call_errors. It doesn't toucheval/arm_summary.py,report/orllm.py.Planned follow-ups that do touch Wave 4 files, held until
eval/records.pyandeval/compare.pyland:arms run --reps Nwith a rep index, and choice consistency / pass^k in the selection summary;call_errorsaggregated insummarize_agentic_usageand the report;refactor(novelty): keep every coverage figure main prints
refactor(novelty): keep every coverage figure main prints
refactor(llm): one LLM module behind every call; old entry points del…
refactor(novelty): the bundle record owns its validity (#44)
vlad-perevezentsev commentedon Oct 28, 2026
Taking the second half of the Wave 5 item: the arms run module behind a typed run config. The shared CLI flags half stays with #94 (F1a #254 / F1b); nothing here touches flags.
Two PRs:
arms run,arms matrix-runandsubjects runhandArmRunnerfor every run-config field, both set away from its default and left out.cmd_arms_matrix_run): a frozenArmsRunConfig, built once from the parsed args, replaces theargparse.Namespacehand-off.dataclasses.replace(config, ...)instead of a copied and mutated Namespace.subjects runbuilds the config directly;FORWARDED_RUN_CONFIG_FIELDS,forwarded_run_config()and the regex test overgetattr(args, ...)go away.commands/into its own module (run_arms/run_matrix).commands/arms.pykeeps argument parsing, output paths, printing and exit codes, and_build_arms_outputleaves the growth baseline.destnames, and no flag is added, renamed or removed. F1b can then rename them to the registry's names in one place.arms.v1artifact do not change, and test(arms): pin what arms run, matrix-run and subjects run hand the arms runner #264 must pass unchanged.Found while writing #264 and left as is, since adding them changes behaviour:
subjects runhas no--temperature,--seed,--judge-temperature,--judge-panel,--stats-correction,--session-turnsor--turn-reminder, andarms matrix-runhas no--session-turns/--turn-reminder.@napetrov, does this split with F1b work for you?
test(arms): pin what arms run, matrix-run and subjects run hand the r…
refactor(arms): move the run's shared checks to dokimi/eval/arms_run.py
refactor(arms): run arms run and matrix-run from a typed ArmsRunConfig
refactor(subjects): build the arms run config directly in subjects run
refactor(arms): run the matrix cells in dokimi/eval/arms_run.py
chore(growth): lower the test_arm_runner baseline after the rebase on…
test(arms): point the output-error tests at the moved run_arms
refactor(arms): run arms from a typed ArmsRunConfig instead of the pa…