Download docs/analysis_plan.md from NicolasT08/formal-mas: direct link, hf CLI and curl.
- Browser
- Download file 22.8 kB
-
https://huggingface.co/NicolasT08/formal-mas/resolve/main/docs/analysis_plan.md
- Command line
-
hf download hf://NicolasT08/formal-mas/docs/analysis_plan.md
-
curl -L -o analysis_plan.md https://huggingface.co/NicolasT08/formal-mas/resolve/main/docs/analysis_plan.md
Analysis plan (pre-registered before any run on test)
Written 2026-11-03, during Phase 8 Part A, before any configuration was run on the test
split. It fixes the metrics, tests and handling rules so that the analysis does not depend on
the results. Any later change must be recorded at the end of this file with its date and reason,
and reported in the paper as a deviation.
Notation and equations refer to the report (docs/report/Proyecto_Inteligencia.pdf, Section IV-A).
1. Data and design
- Problems:
data/eval.jsonl, splittest: 30 problems, stratified on the 3 × 3 grid (difficulty easy/medium/hard × area algebra/number theory/combinatorics, with the documented reallocation of Phase 7). The split was fixed with seed 2026 before any prompt tuning. - Configurations: C1–C5 (Section IV-A8), all on the same LangGraph graph, profile
experiment(deepseek-flash,config/models.yaml), seed 2026, with the configuration frozen by the team's commit at the end of Part A (its hash is recorded in eachrun_config.json). - Run order on
test: C5, C3, C4, C2, C1 (team decision 2026-10-31). - One run per (configuration, problem). No configuration is re-run on
testto obtain a better result. The only exception is theprovider_errorrule of Section 5. - Paired design: every configuration sees the same 30 problems, so comparisons between configurations are paired by problem.
- The LLM response cache stays on. Identical requests across configurations (e.g. the first
attempt of C4 and C5) are answered from the cache; this keeps the paired comparison tighter and is
stated in the paper. Costs are compared with the nominal cost (
cost_usd_nominal: cache hits priced as fresh calls) so that run order does not favour later configurations.
2. Primary outcome
- Verified success of configuration m on problem x: Acc(φ_x, c*_x) of Eq. 2 for the final
assembled proof (status
solvedinresults.jsonl). Only the Lean kernel decides (RNF-1). - Success rate: SR_m = (number solved) / 30, with a Wilson 95 % interval.
- Since each problem is run once, pass@k of Eq. 15 is estimated only for k = 1 (= SR_m). This is stated as a limitation; no repeated sampling is run.
3. Planned comparisons (confirmatory)
Three comparisons, decided now, each a two-sided exact McNemar test on the paired outcomes (binomial test on the discordant pairs b, c with p = 1/2), Bonferroni-corrected for 3 tests (reject at p < 0.05 / 3 ≈ 0.0167; adjusted p-values = min(1, 3p) are also reported):
| ID | Comparison | Question |
|---|---|---|
| H1 | C5 vs C1 | Does the multi-agent pipeline beat direct proof prompting (at equal mean measured computation, b_max of C1 fixed in Part A)? |
| H2 | C5 vs C3 | RAG contribution, Δ_RAG = SR_C5 − SR_C3 (Section IV-A9e). |
| H3 | C5 vs C4 | Contribution of the Critic loop (retries and replanning). |
For each one we report: b and c (discordant counts), ΔSR, a 95 % paired bootstrap interval for ΔSR (10 000 resamples of problems, seed 2026, percentile method), the exact p-value and the Bonferroni-adjusted p-value. With n = 30 the power is low; a non-significant result is reported as "no evidence of a difference", never as "no difference".
Exploratory only (no correction, labelled exploratory, no claims): C3 vs C4, C1 vs C3, C1 vs C4, and any per-difficulty or per-area comparison.
C2 is not compared with McNemar on verified success: its success is self-assessed, not verified.
4. Secondary outcomes
- Per cell of the 3 × 3 grid: for each configuration, solved / n of each (difficulty, area) cell, with the cell's n shown. Descriptive only: no tests and no intervals per cell (n ≤ 5). Margins by difficulty and by area are reported with Wilson intervals. Empty cells (no easy combinatorics) are shown as "—".
- Baseline B (Eq. 16): claimed rate |B⁺| / 30 (Wilson), and FPR_B = |{x ∈ B⁺ : ¬Acc}| / |B⁺|
(Wilson), where every self-assessed proof is passed to the Autoformalizer–Verifier loop with the
same budget (
run_baseline_b.py --check, to be built before the C2 run ontest). FPR_B is an upper bound on the true false-positive rate; a manual review of the B⁺ problems that fail formalization (all of them if ≤ 10, otherwise 10 drawn with seed 2026) separates wrong informal proofs from formalization failures. - Efficiency (Section IV-A9c, RNF-3): per configuration, C̄_solved = Σ_x C(x) / #solved (failed problems' calls included in the numerator), and the same ratio for tokens, nominal USD cost and wall-clock time; also the means per problem. 95 % bootstrap intervals (resampling problems). RNF-3 (C̄_solved ≤ 20) is checked for C5 and reported as met or not; it is not tuned.
- Refinement robustness (Eqs. 14, IV-A9d): SR(k) for k = 1, 2, 3, where a C5 problem counts
as solved at k if its accepted proof used at most k attempts on every subgoal (read from
trace.jsonl); k = 1 is also reported from C4. Δ̂(k) = SR(k) − SR(k−1) is plotted against the independence curve of Eq. 14 with p_i estimated from first-attempt subgoal success in C5. The paper states that k = 1 from C4 has no retries or advice while k = 2, 3 come from C5. - Error taxonomy (Section IV-A9f): relative frequency of each κ class of Eq. 11 over all
failed attempts, per configuration and per area; separately, the final failure reason per
problem (
stageand final status). C1 attempts are classified with the same κ.
5. Status handling
Status in results.jsonl |
Meaning | Primary analysis |
|---|---|---|
solved |
Acc holds on the assembled proof | success |
failed (any stage) |
the method did not find an accepted proof | failure |
budget_exhausted |
b_max LLM calls reached |
failure (the budget is part of the method) |
timeout / memory limit |
Lean verification limits | failure |
error / node_error / invalid_statement |
a bug in the system or the statement | failure; counted and listed |
provider_error |
the API refused or failed after all transport retries | see below |
budget_stop |
the project or run spend cap stopped the run | see below |
run status lean_unavailable; row with lean_down: true |
Lean could not load Mathlib (start-up probes) | as provider_error: infrastructure; re-run once, see below |
provider_erroris an infrastructure failure, not a decision of the model. After a run, the problems withprovider_errorare re-run once, in a new run folder with the same configuration and cache (the original run is kept untouched). The re-run's result replaces the provider error in the analysis. If it is againprovider_error, it counts as a failure in the primary analysis, and a sensitivity analysis excludes those problems from that configuration (and, for McNemar, the pairs where either side is excluded). Counts are reported per configuration.budget_stop: the problem and the rest of the run were not attempted. The team decides whether to raise the spend cap; if raised, only the problems not attempted are run with the same frozen configuration and merged. If not, that configuration is reported as incomplete: SR on the attempted problems (with n stated), a conservative SR counting not-attempted problems as failures, and McNemar only on the problems attempted by both configurations.- Lean unavailable: a run that stops with
lean_unavailablemade no LLM call for the problems not run; those problems, and a row markedlean_down: true(Lean was lost during it, so its failure may be Lean's), are re-run once with the same configuration, exactly likeprovider_error. A success in alean_downrow stands (the kernel accepted it). - Runs marked
degraded(Lean file back-end used) are valid and are flagged in the tables.
6. Fidelity notes (manual check before reporting success)
data/curation/fidelity_review.yaml lists, under notes, problems whose formal statement differs
from the informal text in a way judged harmless at curation time. Every problem solved on test
by any configuration that has a note is reviewed by hand before it is counted as a success in the
paper (PLAN.md, Phase 9). The review records whether the accepted proof establishes what the
informal problem asks. The verified SR (Section 2) is never changed by this review: the kernel's
verdict is what it is. If a review concludes that the formal statement does not capture the
informal problem, the paper reports it next to the table (problem, configuration, reason) and gives
the SR both with and without that problem.
7. What is reported, in order
- Table: SR with Wilson 95 % CI for C1, C3, C4, C5; claimed rate and FPR_B for C2.
- Table: H1–H3 with b, c, ΔSR, bootstrap CI, exact p and Bonferroni-adjusted p.
- Table: per-cell solved/n for each configuration, with difficulty and area margins.
- Table: efficiency (calls, tokens, nominal cost, time per problem and per solved problem).
- Figure: SR(k), k = 1…3, vs the independence curve of Eq. 14.
- Figure or table: κ taxonomy per configuration and area.
- Counts of
provider_error,budget_stop,error, degraded runs and the fidelity reviews.
Analysis code (src/formal_mas/analysis/stats.py, scripts/compare.py) is written in Phase 9 to
implement exactly this plan; it is tested on dev runs before it reads any test result.
Exploratory analysis (post hoc, decided 2026-11-08)
Decided after all confirmatory test runs and their analysis were finished. It is exploratory
and changes no result, test or conclusion of §2–§7.
- Motivation. The hard problems look limited by the model rather than by the method: C1 and
C5 solve the same 4 of the 10 hard
testproblems (imo_1964_p1_2,imo_1965_p2,putnam_1964_b2,putnam_1965_b2), so the agents add nothing on that stratum with DeepSeek-Flash. - Run. C5 exactly as frozen for the main runs (same graph, prompts,
pipeline.yamlmethod settings, seed,B_max/b_max), with model profileexperiment_pro: identical toexperimentexcept that every role usesdeepseek-v4-proinstead ofdeepseek-flash(config/models.yaml). Input: the 10 hardtestproblems, all of them (not only those C5-Flash failed), as a file with exactly those lines ofdata/eval.jsonlin dataset order; its sha256 and id list go inrun_config.json. One run,--run-usd 5.00; if it ends inbudget_stopit is not resumed and the problems without a complete row are reported as not attempted. - Reported. A descriptive paired table, problem by problem, C5-Flash (the kept rows of the
main C5
testresult, §Deviations) vs C5-Pro on the same 10 problems: solved, LLM calls, cost (real and nominal), wall time in minutes; plus totals. The 2×2 table of solved/not solved may be shown as counts. - Not done. No hypothesis test, CI or p-value; nothing is added to the Bonferroni family; no claim about Pro beyond these 10 problems. Pro's results never replace C5's in any main table.
- Fidelity. Any new success gets the same manual fidelity check as §6 before it is reported.
Deviations
No deviation from the analysis itself. Infrastructure incidents during the test runs:
2026-11-03, C5 on
testinterrupted by low memory. Run20261103T174646Z_C5_experiment(commitad70ec2, clean tree) was killed at about 19:16 UTC by the host (Claude Code stops background jobs when the machine runs critically low on memory; the Lean REPL, ~3.5 GB, was sharing RAM with an idle Lean language server, ~2.7 GB). It is an infrastructure failure, not a decision of the method. 23 of 30 problems had finished and are kept unchanged; the 24th (imo_1987_p6) was cut mid-problem and left no row inresults.jsonl. No row waslean_down, and there was noprovider_errororbudget_stop. The 7 problems without a row were completed in two resume runs. Both used the same frozen configuration (the analysis is the same at either commit), the same seed and cache, and the language server was stopped first. The input of each is a file holding exactly the needed lines ofdata/eval.jsonl, unchanged and in the original order. Its sha256 and the id list are in that run'srun_config.json, and a copy is kept astest_remaining.jsonlin that run's folder.20261103T212359Z_C5_experiment_resume(commitad70ec2, clean tree; input sha25653284f8f…086fc4) ran all 7 from scratch:imo_1987_p6,imo_1979_p1,imo_1984_p6,imo_1964_p1_2,putnam_1964_b2,putnam_1995_b1,putnam_1965_b2. It reached its spend cap (--run-usd 0.50) during the third problem.imo_1987_p6andimo_1979_p1finished (bothfailed).imo_1984_p6got abudget_stoprow after 19 calls, and the last 4 were not attempted.20261103T225140Z_C5_experiment_resume2(commit93bc701, which changes only this file; clean tree; input sha256f3da0f6d…6d77f91) ranimo_1984_p6from scratch, plus the 4 problems not attempted, with--run-usd 1.00(the cap raised by team decision, §5). The requests thatimo_1984_p6repeats from step 1 are answered from the cache. Merge: the C5 result ontesthas one row per problem (30 in all), taken from the run where the problem finished:
- 23 rows from the first run;
imo_1987_p6andimo_1979_p1from resume 1;imo_1984_p6and the other 4 from resume 2. Thebudget_stoprow ofimo_1984_p6in resume 1 is discarded; only its complete row counts. The spend of the discarded attempts (imo_1987_p6in the first run andimo_1984_p6in resume 1) counts towards the project's real cost. It is not in any kept row, so each problem's efficiency figures come from its kept row only. Cache hits lower the real cost of the resume runs but not their nominal cost, and nominal cost is the figure compared (§1).
2026-11-03, run order C4 before C3. C4 was run right after C5, and C3 moved to the next off-peak morning, so the order is C5, C4, C3, C2, C1 instead of C5, C3, C4, C2, C1 (§1). The reason is schedule and cost only. C3 takes about 3 h and would have run into the provider's peak window (01:00 UTC), while C4 is short and is answered mostly from the cache of C5. This has no effect on the analysis. Each configuration is still run once on the same 30 problems with the same frozen configuration, and the comparisons are paired by problem. C4 shares its cached requests with C5, not with C3, so its cache hits are the same in either order. Costs are compared at nominal prices (§1), so run order does not favour either configuration.
2026-11-04, per-run spend cap raised from 1.0 to 2.0 USD before C3 on
test. Team decision. Onlyconfig/pipeline.yaml → spend.run_usdchanged (1.0 → 2.0);run_eval.py --run-usdcan only lower that cap, so C3 (about 3 h, no cache shared with C5 because its prompts carry no lemmas; C5 ontestcost 1.36 USD real in total) would otherwise very likely end inbudget_stopbefore the 30th problem. This is a spend-control change, not a method change: the cap is checked before each paid request and only decides whether the request is sent, so it does not change any prompt, model, budgetB_max/b_max, policy or result of a request that is sent. The project capspend.total_usdstays at 9.0 USD. C5 and C4 ran under the 1.0 USD cap (or a lower--run-usd); C3 runs with--run-usd 2.00. The offline test that pins the configured caps (tests/test_spend.py::test_real_pipeline_yaml_has_the_caps) was updated to the new value.2026-11-05, per-run spend cap raised from 2.0 to 3.5 USD before C1 on
test. Team decision. Onlyconfig/pipeline.yaml → spend.run_usdchanged (2.0 → 3.5). C1 costs more per call than C5 (the cost run20261103T161957Z_C1_experiment_c1_costspent 0.21 USD real on 3devproblems, about 0.07 USD per problem), so 30 problems would likely end inbudget_stopunder 2.0 USD. As before, this is a spend-control change, not a method change: the cap is checked before each paid request and only decides whether the request is sent, so it does not change any prompt, model, budgetB_max/b_max, policy or result of a request that is sent. The project capspend.total_usdstays at 9.0 USD. C1 runs with--run-usd 3.50. The offline test that pins the configured caps was updated to the new value.2026-11-05, C1 on
teststopped by the team. Run20261105T131443Z_C1_experiment(commit5ed7507, clean tree,--run-usd 3.50) was stopped on purpose at about 17:43 UTC, together with its whole process tree (REPL,lake), to free the machine for other work. It is an operational incident, not a decision of the method: no code, prompt, model or configuration changed. 23 of 30 problems had finished and are kept unchanged; the 24th (imo_1987_p6) was cut mid-problem (after 10 calls, 0.10 USD real) and left no row inresults.jsonl. There was noprovider_errororbudget_stop. The 7 problems without a row will be completed in a resume run, following the same procedure as for C5 (same frozen configuration, seed and cache; input file with exactly the needed lines ofdata/eval.jsonl;imo_1987_p6run from scratch, its earlier spend counted in the project's real cost but in no kept row). The resume run is20261107T200233Z_C1_experiment_resume(commit2c8eae5, clean tree; input sha256009bece56dc0fdde3c814c9e81aeda6ee7bd8dd677d0fa29bab5dd723c907b8b, the 7 problems in dataset order;--run-usd 1.65= 3.50 minus the 1.8407 USD C1 had spent, rounded down, so that C1 as a whole stays under 3.50).2026-11-07,
dirty: truein fourrun_config.jsonfiles (recording artefact, found in the analysis).run_eval.pyrewritesrun_config.jsonwhen a run ends, and the git state (andcreated_utc) were read again at that moment instead of at launch. In20261103T212359Z_C5_experiment_resume,20261103T225140Z_C5_experiment_resume2,20261103T235556Z_C4_experimentand20261107T200233Z_C1_experiment_resumethe only changed file isdocs/analysis_plan.md, edited while the run was going to log these very incidents; every tree was clean at launch (checked before each run, as the Phase 8 rules require). No code, prompt, configuration or data file changed, so the method is not affected. In those filescreated_utcis also the end time; the start is the timestamp in the run name and therun_startevent oftrace.jsonl. The two interrupted runs (20261103T174646Z_C5_experiment,20261105T131443Z_C1_experiment) never reached the final rewrite and show their launch state. Fixed after alltestruns (run_config.run_start_info):gitandcreated_utcnow describe the launch, and the state when the file is written goes togit_endandwritten_utc. Runs already made are not rewritten.2026-11-08, spend caps raised for the exploratory C5-Pro run (spend control). Team decision, after all confirmatory
testruns. Onlyconfig/pipeline.yaml → spendchanged:total_usd9.0 → 12.5 andrun_usd3.5 → 5.0. The ledger stood at 7.36 USD; re-pricing C5-Flash's tokens on the 10 hardtestproblems at Pro prices gives about 3.75 USD off-peak (7.49 USD at peak), so the old caps would stop the run. As before, the caps are checked before each paid request and only decide whether it is sent; no prompt, model of the main profiles, budgetB_max/b_max, policy or result changes, and no confirmatory run is repeated. The run uses--run-usd 5.00. The offline test that pins the configured caps (tests/test_spend.py::test_real_pipeline_yaml_has_the_caps) was updated to the new values.2026-11-08, exploratory C5-Pro run stopped by the team (operational incident). Run
20261108T130803Z_C5_experiment_pro_pro_hard(commit8f72b02, clean tree, profileexperiment_pro,--run-usd 5.00; input sha256acfd3825…26cf7a66, the 10 hardtestproblems in dataset order) was stopped on purpose at about 17:23 UTC, together with its whole process tree (REPL,lake), because the team had to leave. No code, prompt, model or configuration changed; all calls were off-peak. 5 of 10 problems finished and are kept unchanged:imo_1965_p2,imo_1993_p5,imo_1979_p1(failed),imo_1987_p6(budget_exhausted) andimo_1978_p5(provider_error: the Autoformalizer request on subgoal 1, the problem's 30th call, gotAPIConnectionError: Connection error.with no HTTP response on all 5 transport attempts, 15:34:50–15:37:28 UTC). The 6th (imo_1984_p6) was cut mid-problem after 4 calls and left no row. The last 4 (imo_1964_p1_2,putnam_1964_b2,putnam_1995_b1,putnam_1965_b2) were not attempted. The run is not resumed and theprovider_erroris not re-run: the exploratory table reports the 5 finished rows, the cut problem and the 4 not attempted as such. Real cost 1.9407 USD (151 paid calls; 0.1725 USD of it on the cut problem, in no kept row); ledger total after the run 9.3003 USD.run_config.jsonstill showsstatus: runningbecause the run never reached its final rewrite.
Clarifications written before any run on test:
- 2026-11-03, FPR_B check (§4.2), how "the Autoformalizer–Verifier loop with the same budget" is
implemented (
scripts/run_baseline_b.py --check,baselines/baseline_b.check_claim): each claimed proof (B⁺) is the sketch of the Autoformalizer on the trivial plan (the whole theorem as the only subgoal), with the Retriever's lemmas as in C5; up tocritic.k_max= 3 attempts, each failure classified by κ and followed by the Critic's deterministic feedback (no LLM advice, no Planner, no Generator, no automation pre-pass, so no fixed tactic can stand in for the claimed proof); its own budget ofB_max= 40 calls, counted apart from C2's own calls (C2's efficiency figures exclude the check; in practice the check makes at most k_max × 2 = 6 calls: one Autoformalizer call and at most one JSON repair per attempt). Acc is Eq. 2 on the assembled theorem. FPR_B = not_verified / (verified + not_verified), wherebudget_exhaustedcounts as not_verified (the budget is part of the method). A check that ended inprovider_error,budget_stoporerror, or a row markedlean_down, is left out of FPR_B, counted apart and re-run once like aprovider_error(§5); if it fails again the same way it is reported as not checked. Problems C2 did not claim are not checked.