You are running Phase 2.3 — Coherence Gate (dry-run trace) — of IdeaSpark.

You are a FRESH context that did NOT author the candidate (never run this inside the Phase 2.1+2.2 context — the context that wrote a logic bug is the one context guaranteed to rubber-stamp it). Your single question: **does this algorithm, executed exactly as written, survive on paper?** Logic bugs read fluently — "resample R=16 times until the 24-bit parity matches" sounds fine until you compute the acceptance probability (~4^-24) and discover the loop is vacuous. They surface only under EXECUTION, so this gate executes; it does not review.

This gate verifies INTERNAL PROCEDURAL VALIDITY plus one consistency question about depth (T5: does the mechanism actually confront the obstacle it declares — judged against the candidate's OWN declaration, not against taste). It does NOT judge novelty (Phase 3.2 audit), falsifiability structure (audit check 5), implementability detail (Phase 4.1.5), or whether the method will actually work on real data (that is what the falsification experiment exists for). A candidate can pass this gate and still be wrong empirically; it cannot pass this gate and be incoherent.

Inputs (explicit paths):
- `$RUN_DIR/phase2_generate/phase2_generate_output.json` — the candidate. Load-bearing fields: `core_mechanism`, `core_mechanism_steps`, `core_mechanism_reasoning`, `gap_closure[].how_closed`.
- `$RUN_DIR/phase2_select/phase2_select_output.json` — context only (what each gap demands).

## Sufficiency — running the five actions is not the same as having tested the candidate

Measured: the same candidate, through this same prompt, produced a 0-finding report and a 4-finding report on two runs that differed only in how hard the gate pushed. Depth is therefore part of the contract, not a matter of taste. Two obligations set the floor:

**Pick the likely-fatal step yourself, first.** Before checking anything, read `core_mechanism` and `core_mechanism_steps` and write into the report which single step or claim you judge most likely to be structurally broken, and why you picked it. Attack that one first, with executed code. `core_mechanism_reasoning` is the candidate's argument for itself and is under test — do not adopt its account of where the hard part lies. (In the measured case the fatal step was a preprocessing stage the candidate never flagged: it performed, at observation time, exactly the lossy commitment the mechanism claimed to relocate.)

**Try to break every stated guarantee.** For each claim of the form "exactly zero", "cannot happen", "by construction", "strictly dominates", "recoverable", "no free parameter": construct an instance that would VIOLATE it and run that instance. A guarantee you did not try to break is a guarantee you did not check.

## The five trace actions (run ALL, in order)

**T1 — Formalize the procedure.** Four duties; run all four.

*(a) Dataflow.* Rewrite `core_mechanism_steps` as an explicit dataflow: inputs → numbered steps (each: what it CONSUMES, what it PRODUCES, with types/dimensions) → outputs. Every failure to formalize is a finding: a step consuming an artifact no prior step produces; a symbol/threshold used before it is defined (and not declared a hyperparameter with a selection rule); a circular dependency (step k needs step k+j's output); a key term used with two meanings.

*(b) Unbound parameters.* An UNBOUND PARAMETER — a number the procedure reads as a parameter (threshold, budget, window, interval, count) that is not bound to a named symbol with a default and a selection rule (generation hard rule 5a), which is the form a magic constant takes to evade every symbol-keyed check; the test is whether the number carries a NAME, so `k = 5`, `epsilon in [0.02, 0.05]`, `W ~ 1e5` all PASS, while `gate on >= 95% agreement`, `<= 0.1 * |F|`, `the top 5`, `every 2e3 tokens` are findings — and numbers that are not parameters never count (artifact versions, algebraic/structural constants inside a formula, interval endpoints, definitional settings, experiment-campaign scale). The parameter-vs-scale line is BEHAVIOURAL: if changing the number changes what the ALGORITHM DOES — a budget it spends, a window it fits over, the point at which it freezes or switches regime — it is a parameter (so a calibration window '~20 admissions' that decides when the gate's weights freeze IS one), whereas a number that only sizes the evaluation campaign — corpus size, checkpoint count, the library size a curve is run out to — is scale and exempt. A name bound ANYWHERE in the method fields counts, since they are read jointly: a quantity introduced as `B_exec = 5` in core_mechanism passes where a later step writes a bare `<= 5`. This check covers the METHOD fields only (`core_mechanism`, `core_mechanism_steps`) — numbers inside `falsification_prediction` are audit check 5's territory and MUST NOT be raised here, since this gate is forbidden from repairing that field.

*(c) Dual-reading protocol* (formalization is itself interpretation — control for it): before formalizing each step, QUOTE the step text verbatim. When an operative term admits more than one defensible reading (e.g. "unchanged": strictly-equal vs does-not-decrease; "similar", "stable", "near"), formalize EVERY defensible reading and run T2/T3/T5 under each. Tag every downstream finding `reading_robust` (holds under all defensible readings) or `reading_dependent` (name the reading it needs). An ambiguous operative term is ALWAYS itself a wording finding — whichever reading the author intended, the text must be edited to say it precisely, so emit a wording patch (or, if the step is only sound under one reading, patch the text to that reading and say so). Never silently pick one reading and report its findings as unconditional.

*(d) Estimand match* (conditional): if the candidate declared an ESTIMAND in its PREMISES (BLOCK 1), check that the estimator the steps construct actually targets that estimand — same quantity, same population/process, same conditioning. An estimator that targets a different quantity than the declared estimand (e.g. declares per-step acceptance rate but estimates per-episode) is a finding.

**T2 — Numeric dry-run.** Construct ONE minimal concrete instance with REAL small numbers (e.g., 3 objects, 24 bits, R=16, n=100 samples) and walk the full procedure by hand, actually COMPUTING every intermediate quantity. This is where magnitude absurdities fall out mechanically: acceptance probabilities that make loops vacuous, counts that exceed their containers, dimension mismatches, quantities that are constant when the mechanism needs them to vary. EXECUTE, don't estimate: when a code-execution tool is available in your context, write a short stdlib-only Python script for the instance, RUN it, and paste both the script and its output into the report — hand-arithmetic is permitted only when no execution tool exists, and every hand-computed quantity must then be marked `unexecuted`. Show the arithmetic (or executed output) in the report — an anomaly you assert without the computed number is not a finding.
WHAT THE NUMBERS CAN AND CANNOT ESTABLISH. You invented this instance, so its magnitudes are yours, not the candidate's. Only two kinds of result survive that:
- STRUCTURAL — the finding holds for ANY instance because it follows from the procedure's algebra or shape: a probability that makes a loop vacuous, an estimator whose two cases collapse to the same value, a correction that is constant across the items it must separate, a dimension that cannot match, a quantity the mechanism needs to vary that provably cannot. Here the numbers are a vehicle; any numbers expose it. This is what the dry run is FOR.
- INSTANCE-CONTINGENT — the finding is a property of the instance you built (a percentage gap between mechanism and baseline, an R², a win rate over synthetic settings). It is a hypothesis about the real system, not a measurement of it, and the instance may be unfavourable by construction.
Tag every dry-run anomaly and every T5 divergence `structural` or `instance_contingent`, and say what makes it so. An `instance_contingent` result may motivate a finding but must NOT be the sole support for a `blocking` one — blocking means the mechanism is broken as written, which no single invented instance can show. Do not launder a contingent number into the report as though it were evidence about the real system.
FORCED RESULTS ARE NOT EVIDENCE. When a computed value lands exactly where the candidate predicts, ask whether it COULD have come out otherwise. A quantity fixed by construction — an algebraic identity restated as a check, a control arm that compares a thing with itself, a threshold implied by how you built the instance — confirms nothing, and banking it as confirmation manufactures assurance the trace never earned. Say so in the report, and where it is cheap, re-run under a perturbation that would have moved a genuinely contingent quantity.

**T3 — Degenerate probes.** Push the formalized procedure through edge inputs: empty set, k=0, all-identical elements, ties at every threshold, single-element input, the maximum the spec allows. For each probe: does the procedure halt, divide by zero, emit a meaningless output, or silently skip a branch that other steps depend on?

**T4 — Claim→step mapping AND grading.** For every property the candidate asserts about its own mechanism (inside core_mechanism / how_closed — e.g. "unbiased", "zero-residual", "generator-free readout", "provably order-invariant"), do two things. FIRST, map: name the step of the T1 formalization that ESTABLISHES it — a property no step establishes is an assertion, not a construction (finding). SECOND, grade its strength honestly:
- `established` — the mapped step constructs/derives the property with no unstated assumptions.
- `conditional` — the property holds only under assumptions the candidate does not state where the claim appears (list them in `assumptions_missing`). Patch: state the assumptions next to the claim, keep the claim.
- `overclaim` — the claimed strength is wrong as stated (e.g. "finite-sample unbiased" for an estimator that is only consistent/asymptotically unbiased; "exact" for an approximation). Patch: downgrade the WORDING to the defensible grade — this is a wording repair, not a redesign.
- `empirical` — the property is an empirical prediction, not a constructed guarantee; legitimate as long as it is not phrased as a guarantee.
Arbitration (record which was used per claim). STATISTICAL claims (unbiasedness, variance, coverage, calibration) are settled by execution ONLY when the executed result is structural in the T2 sense — the bias is present for any input distribution, the estimator is provably targeting the wrong quantity, the variance cannot shrink because the sampler is degenerate. Then extend the T2 script (e.g. 10k replications), report the measured number, and the number decides. When the result would instead depend on the distribution YOU chose for the instance — "bias measured 0.03 on my synthetic draw" — a Monte Carlo cannot settle it: more replications only sample your own fiction more finely, and the honest grade is `empirical`, deferred to the falsification experiment. Do not run a Monte Carlo whose outcome you could have moved by choosing the instance differently and then record it as arbitration. THEOREM-SHAPED claims (provable, order-invariant, converges) cannot be settled by a dry run at all — record the assumptions the proof would need and the key proof obligations as a note; such a claim is at best `conditional` until a proof exists. Everything else is settled by the T1 mapping argument.
ANTI-CLAIM-AVOIDANCE: this grading must never reward vagueness. A candidate making strong claims with honest stated assumptions outranks one making only hedged weak claims; do NOT emit findings that push claims to become vaguer, and if EVERY claim is already hedged/unfalsifiable-soft, record that itself as a weakness finding (the mechanism asserts nothing checkable).

**T5 — Naive-baseline comparison (the depth check).** Construct the most naive/obvious version of the anchor mechanism INDEPENDENTLY, from the anchor gap and the mechanism's inputs — do NOT adopt the candidate's own stated naive version (a self-served naive can be a strawman).
FAIRNESS OF THE NAIVE (this decides whether the verdict means anything). Your naive must be SIMPLER, not merely DIFFERENT, and it must run under the SAME constraints the candidate operates under — the same budget (tokens, memory, latency, compute), the same information available at the same moment, the same deployment restrictions. A "naive" that wins by relaxing one of those is not a baseline; it is a second design, and beating the candidate with it says nothing about the candidate. Measured case: against a mechanism whose whole problem was a FIXED retention budget, an independently-built "naive" that kept an unbounded full-precision log scored higher — on a cost axis that ignored the very constraint under test. State, in one clause, which constraints your naive respects and how it is simpler (fewer components, no learned part, no extra pass, a rule instead of an optimisation).
If the only naive you can build that beats the mechanism must RELAX a constraint, that is a different and still-valuable finding — record it as a note that the candidate's constraint may be unnecessary (its own framing is what is in question), NOT as `equivalent_to_naive`, which asserts the machinery earns nothing under the constraints the candidate accepted. Run the naive version on the SAME T2 instance and numerically compare its outputs/decisions with the full mechanism's (extend the T2 script and paste its output when execution is available; otherwise hand-compute and mark `unexecuted`). Then judge against the branch the candidate's NAIVE-BASELINE AUDIT (core_mechanism_reasoning BLOCK 2) declared:
- `confronts_obstacle` — the naive version demonstrably fails on the instance for the reason the candidate names (show the number), and the full mechanism's extra machinery is what fixes it. Positive confirmation.
- `equivalent_to_naive` — the mechanism's behavior on the instance is the naive version's plus cosmetic differences, while the candidate declared branch (i). FINDING: the mechanism does not confront its own declared obstacle. This is an OBSTACLE HOLE (below), not a patchable defect.
- `n_a` — no meaningful naive execution exists for this shape (e.g. a pure equivalence theorem): degrade to tracing where the STANDARD argument breaks on the instance; if neither is expressible, record n_a with the reason — never fabricate a comparison.
Candidates declaring branch (ii)/(iii) are judged for consistency instead: (ii) equivalence is expected — verify the increment was surfaced, not hidden; (iii) verify the naive version indeed works on the instance (that IS the claim).

## Repair contract (patch-only, single pass)

Fixes must make the WRITTEN procedure sound; they must not redesign the idea. Allowed field targets: `core_mechanism`, `core_mechanism_steps`, `core_mechanism_reasoning`, `gap_closure[i].how_closed` (narrative alignment only), `hook`/`title` (ONLY if a repair makes them factually wrong), and `signature_terms`/`alias_terms` (ONLY if a repair changed what the mechanism IS — Phase 3.1 must query the repaired mechanism, not the broken one).

FORBIDDEN (out of scope, merger-enforced or audit-owned): `falsification_prediction` + `compute_budget` (kill-switch; falsification quality is audit check 5's job with its own gated door), `gap_closure[].gap/main_pattern/sub_pattern` (pattern bindings are 2.1/3.3 territory), `differentiation_from_lit` / `almost_prior_paper_id` / `what_step_was_missed` (novelty surface — audit territory), and ANY "improvement" not required by a specific T1-T5 finding. Sanding edges off the idea is a failure mode of refine loops; this is a gate, not a polish. ONE pass — no iterate-until-clean; residual doubts surface at Phase 3.2 / 4.1.5.

OBSTACLE HOLES — the one class of finding you must NOT patch around. A finding is an obstacle hole when its honest fix would change what the mechanism IS (a redesign, not a repair), or when it coincides with the obstacle the candidate's own PREMISES / NAIVE-BASELINE AUDIT declared it exists to solve (including T5's `equivalent_to_naive`). For obstacle holes, avoidance-style patches — abstain on the affected cells, clamp the affected range, skip the affected regime — are FORBIDDEN when their effect is to sidestep the declared obstacle rather than solve it (such patches remain legitimate for genuine engineering edges unrelated to the declared obstacle). Record obstacle holes under `unrepaired[]` with severity `blocking`, verbatim and unsoftened.

For any other finding genuinely unfixable without redesigning the mechanism, likewise do NOT attempt the redesign: record it under `unrepaired[]` with severity `blocking` and leave the candidate unchanged on that point — the Phase 3.2 audit and its abandon/retry machinery own that decision.

Output (JSON):

{
  "trace_report": {
    "formalized_procedure": [ {"step": "S1", "consumes": ["..."], "produces": ["..."], "note": "..."} ],
    "dry_run": {"instance": "<the concrete minimal instance, with numbers>",
                 "execution": {"mode": "executed | unexecuted", "script": "<the stdlib Python script, when executed>", "output": "<its stdout, when executed>"},
                 "computed_quantities": [ {"quantity": "...", "value": "...", "arithmetic": "..."} ],
                 "anomalies": [ {"anomaly": "<with the computed number that exposes it>", "kind": "structural | instance_contingent", "why": "<what makes it hold for any instance, or what ties it to this one>"} ]},
    "degenerate_probes": [ {"probe": "...", "behavior": "...", "finding": "<or null>"} ],
    "claim_step_map": [ {"claim": "...", "established_by": "<step id or 'UNESTABLISHED'>", "strength_grade": "established | conditional | overclaim | empirical", "assumptions_missing": ["<only for conditional>"], "arbitration": "executed-mc | argument | proof-obligations", "measured": "<the MC number for executed-mc, else null>"} ],
    "naive_comparison": {"declared_branch": "<(i) false-premise | (ii) incremental | (iii) minimalism — as declared in the candidate's BLOCK 2, or 'undeclared'>",
                          "naive_version": "<the independently-constructed naive mechanism, one sentence>",
                          "naive_fairness": "<which of the candidate's constraints this naive respects (budget / information / deployment) and in what way it is SIMPLER — required, because a naive that relaxes a constraint invalidates the verdict>",
                          "instance_behavior": {"naive": "<computed result on the T2 instance>", "mechanism": "<computed result>", "divergence": "<the number(s) that separate them, or 'none'>", "kind": "structural | instance_contingent"},
                          "verdict": "confronts_obstacle | equivalent_to_naive | n_a",
                          "reasoning": "<1-2 sentences; for n_a, why no comparison is expressible>"}
  },
  "verdict": "pass | patched",
  "unrepaired": [ {"finding": "...", "severity": "blocking | note", "why_not_repaired": "...",
                    "verbatim_step_quote": "<the exact candidate text the finding is about>",
                    "executed_evidence": "<the script excerpt + measured numbers that establish it, self-contained — a reader with ONLY this entry must be able to check the modeling and the arithmetic>",
                    "reading_dependence": "reading_robust | reading_dependent: <which reading>",
                    "structural_requirement": "<BLOCKING entries only (null for notes). What it would TAKE to get past this obstacle — written for the next generation, which keeps working on this same bottleneck and would otherwise rediscover the wall by hitting it. Three parts: (a) the identifying condition — the specific capability, signal, or contrast ANY mechanism here must possess, never a restatement of the defect; (b) sources of it that are AVAILABLE vs EXCLUDED in this setting, with the reason (the gap's own premises, cost, corpus already occupied) — and price the excluded ones honestly rather than inheriting the candidate's own cost claim, which may be wrong; (c) SALVAGE: what in the failed candidate is worth carrying forward (an instrument, a control, a measurement) versus discarded. State this ONLY as far as your executed evidence supports, and say plainly when it supports nothing concrete — a vague requirement is worse than none because it spends the next attempt's budget. Never cite a number that is the normalizer of its own comparison: a ratio in which one arm defines the scale is 1.0 by construction and is not evidence. Stating what would be required is DIAGNOSIS, which is this gate's job; it is not the redesign the repair contract forbids — do not write the replacement mechanism.>"} ],
  "applied_revisions": [
    {"scope": "coherence",
     "op": "replace | append_sentence | append_items",
     "field": "<JSON-path within the allowed targets>",
     "value": "<new value>",
     "outcome": "applied",
     "delta_summary": "<one sentence: which T1-T5 finding this repairs>"}
  ]
}

`verdict = pass` requires `applied_revisions` empty. Each patch entry must cite its motivating finding in delta_summary — a patch with no finding is scope creep. The merger then runs (host invokes; do not run it yourself):

  python3 "$SKILL_DIR/scripts/run.py" phase3_merge_revisions \
    --phase2 $RUN_DIR/phase2_generate/phase2_generate_output.json \
    --revisions $RUN_DIR/phase2_coherence/phase2_coherence_output.json \
    --out $RUN_DIR/phase2_coherence/ --out-name refined_candidate.json

`refined_candidate.json` becomes the canonical candidate for every later phase (3.1 collision, 3.2 audit, 3.3, Phase 4, validators). On `verdict = pass` the merger is skipped and the Phase 2.2 file stays canonical. The Phase 3.2 audit does NOT read this report's trace/verdict/patches — it judges the (possibly repaired) candidate blind, so "already checked" cannot bias it — with ONE exception: blocking `unrepaired[]` entries are executed evidence, not narrative, and they DO reach the audit via the separate file below.

BLOCKING-FINDINGS HANDOFF: when `unrepaired[]` contains any entry with severity `blocking`, ALSO Write `$RUN_DIR/phase2_coherence/blocking_findings.json` — a JSON array holding exactly those entries, verbatim and self-contained (each must carry its `verbatim_step_quote` + `executed_evidence` + `reading_dependence` + `structural_requirement`). This file — never the full report — is what the 3.2 audit receives; `next` lists it as an audit input, and the audit must disposition each entry (uphold, or refute by naming a concrete modeling/arithmetic flaw). Skipping this file when blocking findings exist silently un-plugs the gate's strongest output.

Output path: `$RUN_DIR/phase2_coherence/phase2_coherence_output.json`.
