You are running Phase 3.2 — Audit-and-Verdict — of IdeaSpark.

Step 3.2 produces a corpus-anchored audit on the Phase 2.2 candidate. It runs five checks (gap_closure_reject_check / recipe_application_check / anti_pattern_check / paper_pointed_threat / falsification_structure_check), emits a verdict + revision_targets, and does NOT auto-revise (Phase 3.3 applies revisions in a separate call).

Inputs (explicit paths):
- The CANONICAL candidate: `$RUN_DIR/phase2_coherence/refined_candidate.json` when the Phase 2.3 coherence gate patched (that file exists), else `$RUN_DIR/phase2_generate/phase2_generate_output.json`. Judge the candidate as-is; do NOT read the 2.3 trace report — this audit stays blind to it.
- `$RUN_DIR/phase2_coherence/blocking_findings.json` — WHEN IT EXISTS: the 2.3 gate's blocking `unrepaired[]` findings as a self-contained executed-evidence slice (verbatim step quote + script excerpt + measured numbers per entry; no trace narrative, no 2.3 verdict — blindness otherwise preserved). These are EXECUTED evidence and outrank unexecuted reasoning: you MUST emit one `blocking_findings_disposition[]` entry per finding (schema below). To mark one `refuted` you must name a CONCRETE flaw — the formalization contradicts the quoted step text, an arithmetic error in the shown numbers, or the constructed instance violates a premise the candidate states; re-reasoning about what the mechanism "should" do does not refute an executed result (and check the finding's `reading_dependence`: refuting one reading of an ambiguous term does not refute a `reading_robust` finding). While any disposition is `upheld`, `advance` is FORBIDDEN — route per the Blocking-obstacle bullet in the verdict guidance.
- `$RUN_DIR/phase2_select/phase2_select_output.json` — the Phase 2.1 spec (selected_gaps with chosen_pattern_id).
- **One sub-pattern card per `gap_closure[]` entry**: `references/ideation-sub-patterns/<C##>.md` where `<C##>` is the **leading cluster code C00 through C30** of that entry's `sub_pattern` value (the value is formatted `C## (parent pattern name)`, so strip everything after the code — e.g. `C12 (Substitute the Operator or Representation)` → open `C12.md`). NOT a parent-pattern-named file. Read `## Tactical failure mode` + ALL bullets under `## Examples → ### Reject lessons` verbatim. The lessons are paper-agnostic distillations (no `[Reject]` example headers, no `paper_id` references) so quote the bullet text directly.
- `$RUN_DIR/phase0/lit_table.md` — used for paper-pointed threat search.
- `$RUN_DIR/phase3_collision/collision_hits.json` — mechanism-specific retrieval, TWO channels per hit's `collision_channel` field: `signature` (candidate's own vocabulary, recent window — contemporaneous scoop risk) and `alias` (other communities' names for the same mechanism, multi-year window — renamed-ancestor risk). Alias-channel hits are older by design; do NOT discount a threat for being 2-3 years old — a same-mechanism ancestor subsumes the candidate regardless of age. The file is pre-truncated by the orchestrator (per-channel relevance ranking, ≤120 hits/channel, zero-relevance noise dropped, `relevance_score` on each hit) so it is safe to read whole in chunks; the untruncated pool sits in the sibling `collision_hits.full.json` if a deeper forensic sweep is ever warranted.
- `references/anti-patterns.md` — 3 reject-favored compositions with required mitigations.

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
▸ MARKDOWN SAFETY — applies to EVERY prose field below

The renderer hands `$...$` to KaTeX. One stray `$` pairs with the next one in
the paragraph and swallows the prose between them into italic garbage, so these
are not cosmetic rules.

1. An identifier keeps its underscore. `foo_bar` is code, NOT `foo_{bar}`, and
   never goes inside `$`. Backtick it whole. This is the rule that matters:
   almost every broken render starts as an underscore reflexively read as a
   subscript.
     WRONG  gradients disabled $(torch.no_{grad},$ model in eval mode)
     RIGHT  gradients disabled (`torch.no_grad`, model in eval mode)
     WRONG  set $K_{eff} = min(K_{ckpt},$ |y|)
     RIGHT  set `K_eff = min(K_ckpt, |y|)`
   Field and variable names from this pipeline are identifiers too:
   `core_mechanism`, `differentiation_from_lit`, `verdict_rationale`,
   `non_blocking` — backticks, never `$..._{...}$`.

2. A literal dollar sign is `\$`. Currency in prose opens math otherwise.
     WRONG  API line ~ $1500 vs $10000 (15%) -> feasible
     RIGHT  API line ~ \$1500 vs \$10000 (15%) -> feasible

3. Inside `$...$`: math tokens only. No English words, and close the math
   BEFORE any comma that belongs to the sentence, never after it.
     WRONG  ... at weight $lambda_{v},$ and take the step
     RIGHT  ... at weight $\lambda_v$, and take the step

4. To WRITE ABOUT LaTeX delimiters, backtick them. Never type a bare `$$`
   inside a sentence.

5. Display math stands alone in its own paragraph, at column 0, balanced.
   Never mid-sentence.

6. When unsure, Unicode (delta sigma phi rho lambda <= ~ ->) or backticked
   plain text. Correct plain text always beats a red KaTeX error.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Output (JSON):

{
  "gap_closure_reject_check": {
    "entries": [
      {
        "gap": "<verbatim from candidate.gap_closure[i].gap>",
        "main_pattern": "<from candidate.gap_closure[i].main_pattern>",
        "sub_pattern": "<from candidate.gap_closure[i].sub_pattern>",
        "tactical_failure_mode_quoted": "<quote the sub-pattern card's ## Tactical failure mode in ≤ 2 sentences>",
        "reject_lessons_evaluated": [
          {
            "lesson_quoted": "<the bullet verbatim from ### Reject lessons>",
            "candidate_match": "no | yes | borderline",
            "reasoning": "<one sentence: which candidate field would trigger this lesson, or why it doesn't>"
          }
        ],
        "verdict": "clear | triggered | borderline"
      }
    ],
    "verdict": "<aggregate: 'triggered' if ANY entry triggered; 'borderline' if ANY borderline and none triggered; 'clear' only when ALL clear>",
    "reasoning": "<2-3 sentences naming which gap_closure entry (by gap + main_pattern) drove the aggregate verdict and which specific lesson_quoted carried weight>"
  },

  "recipe_application_check": {
    "entries": [
      {
        "gap": "<verbatim from candidate.gap_closure[i].gap>",
        "sub_pattern": "<from candidate.gap_closure[i].sub_pattern>",
        "tactical_pattern_quoted": "<quote the C##.md `## Tactical pattern` signature move in ≤ 2 sentences — the specific verb/operation that defines THIS cluster, NOT the parent pattern's generic gist (note: in this taxonomy the sub_pattern string only carries the PARENT display name, so the citation string alone does NOT prove the cluster card was opened — you must judge the mechanism against the card text)>",
        "instantiation_in_core_mechanism": "<cite the SPECIFIC step in core_mechanism that performs that signature operation. If core_mechanism only enacts the parent pattern's generic idea without the cluster's distinctive move, say so explicitly. THIN-CORPUS EXCEPTION: when this entry's how_closed declares thin corpus support for its domain, judge ONLY whether core_mechanism performs the cluster's abstract Step-by-Step move — do NOT mark bypassed for lacking surface resemblance to the card's exemplars (they come from distant domains); borderline is the ceiling for domain-appearance concerns.>",
        "verdict": "applied | bypassed | borderline"
      }
    ],
    "verdict": "<aggregate: 'bypassed' if ANY entry bypassed; 'borderline' if ANY borderline and none bypassed; 'applied' only when ALL applied>",
    "reasoning": "<one-two sentences naming which entry drove the aggregate and which tactical move was or was not found in core_mechanism>"
  },

  "anti_pattern_check": {
    "composition_set": ["<list of DISTINCT pattern ids across all gap_closure[] entries: every main_pattern AND every non-null companion_pattern (a Step 2a chain or Step 3b pair is part of the composition — omitting companions lets a reject-favored pairing hide inside one gap entry)>"],
    "matched_pattern_id": "<audit_decomp_supervisor | audit_decomp_prior | audit_decomp_operator | null>",
    "required_mitigation_quoted": "<from anti-patterns.md if matched; null otherwise>",
    "mitigation_substantively_delivered": "<true | false | n/a>",
    "reasoning": "<one sentence: not just keyword presence but mechanism-level — did the candidate's core_mechanism actually produce the artifact the mitigation requires?>"
  },

  "paper_pointed_threat": {
    "threat_paper_id": "<paper_id from lit_table or collision_hits, or 'no_threat_found'>",
    "threat_source": "lit_table | collision_hits | n/a",
    "threat_channel": "<for collision_hits threats: the hit's collision_channel value (signature | alias); null otherwise>",
    "subsumption_argument": "<one paragraph: what specific result of threat_paper_id makes the candidate's claim stand or fall. null if no_threat_found.>",
    "addressable_via": "<one of: a one-sentence description of which candidate field would need to change to dodge this threat; the literal string 'unaddressable' for exact-mechanism overlap (this also fires the hard floor → abandon); the literal string 'not_needed' when a threat paper exists but the candidate already stands against it (no change required); or null ONLY when threat_paper_id = no_threat_found. Do NOT use null to mean 'unaddressable' or 'not needed' — downstream severity labeling reads this field.>",
    "parametric_family_concern": "<SOFT SIGNAL, from your own knowledge, NOT from the retrieved pool: if the candidate's core mechanism plainly resembles an older, named research family the pool did not surface (e.g. 'goal-conditioned success detectors', 'learned value-function reward models'), NAME THE FAMILY and 1-2 vocabulary phrases a scoop-check should query — do NOT cite specific papers from memory (titles/authors/years hallucinate; family names don't). null when the pool covered the mechanism's obvious relatives. This field NEVER feeds the hard floor or the verdict — it flows to Phase 4's reviewer_concerns_and_responses as a 'run a scoop-check on X before investing' flag.>"
  },

  "falsification_structure_check": {
    "minimal_experiment_named": "<yes | no — does the paragraph name a concrete minimal experiment (setup + comparison), not just 'we will evaluate'>",
    "outcome_metric_named": "<yes | no — does it name the downstream task-outcome metric AND its qualitative direction if the candidate works>",
    "load_bearing_variable": "<quote the ONE named quantity the mechanism claim pivots on (a gradient norm, an information-gain term, a logit divergence, a learned threshold, a representational direction, ...), or 'absent' if no single variable is named>",
    "negative_control_target": "<'outcome_metric' when the negative-control intervention on the load-bearing variable predicts the DOWNSTREAM outcome metric returns to baseline; 'tautological' when the predicted effect is the load-bearing variable's own value or a quantity analytically derived from it (a control of the form \"intervene on X → X becomes 0\" tests a definition, not a mechanism); 'absent' when no negative control is stated>",
    "numeric_bar_provenance": "<'none' when the paragraph carries NO numeric outcome bar — the preferred shape, since metric + direction + control is what falsifies; also the value to record when the only numeric content is exempt (see below). 'derived' when every bar follows analytically from the mechanism or setup AND the derivation is INSTANTIATED — it names what the bar follows from and supplies the inputs, so a reader can recompute it; asserting 'this follows from the evidence floor' without giving that floor a value does NOT qualify. 'measured' when every bar is a value reported in a cited paper AND names the paper_id. 'asserted' when a bar CLAIMS a derivation or source but leaves it uncheckable — a concern, not a fabrication. 'invented' when ANY outcome bar carries no provenance at all (a bare 'ρ ≥ 0.6', 'recovers ≥ 80%', 'accuracy stays ≥ 0.9'). Scope rules, all of which have bitten real runs: (i) OUTCOME bars include RESOURCE and COST predictions ('at < 5% of its executions', 'under 2 GPU-days'), not just performance metrics — a cost bar is invented exactly as easily; (ii) QUALITATIVE magnitude claims ('drops by an order of magnitude', 'substantially above chance') are NOT bars — they are direction statements, which is the shape this field wants; never push a candidate toward vaguer wording; (iii) replication and scale counts ('≥ 3 seeds', '~200 skills', '≤ 12-skill libraries', corpus sizes) are experiment design — exempt; (iv) CONDITIONING thresholds that scope WHERE a claim is asserted rather than stating its level ('on edits whose CLIP similarity ≥ 0.85') are setup — exempt, provided the scope is stated as a condition; (v) thresholds that follow analytically (a sign test's 'Δ ≤ 0', 'the ratio falls below 1', 'near chance ≤ 0.6' against a 0.5 baseline) are derived, and when they are the ONLY numeric content record 'none'.>",
    "verdict": "sound | deficient | borderline",
    "reasoning": "<1-2 sentences: which sub-answer drove the verdict. 'sound' requires ALL of: minimal_experiment_named=yes, outcome_metric_named=yes, load_bearing_variable quoted (not absent), negative_control_target=outcome_metric, numeric_bar_provenance ∈ {none, derived, measured}. Any 'no'/'absent'/'tautological'/'invented' → deficient. borderline covers a named-but-ambiguous variable, a control whose target metric is arguably-but-not-explicitly the outcome, and numeric_bar_provenance='asserted' — an uncheckable derivation is surfaced as a concern, NOT forced through the rewrite door, because a false 'invented' would compel an edit to a kill-switch field on a judgment call.>"
  },

  "blocking_findings_disposition": [
    {
      "finding_ref": "<first ~15 words of the blocking finding, enough to identify it>",
      "status": "upheld | refuted",
      "basis": "<for refuted: the CONCRETE flaw (formalization-vs-quoted-text mismatch / arithmetic error / illegal instance), stated checkably. For upheld: one sentence on what it implies for the verdict.>"
    }
  ],

  "verdict": "advance | revise | abandon",
  "verdict_layer": "<'hard_floor' if a hard-floor rule triggered abandon; 'soft_judgment' otherwise>",
  "verdict_rationale": "<2-3 sentences citing specific check findings (gap_closure_reject_check.entries[X].reject_lessons_evaluated[N].lesson_quoted / recipe_application_check.entries[X].verdict / anti_pattern_check.mitigation_substantively_delivered / paper_pointed_threat.subsumption_argument). 'Looks fine overall' is a process error.>",

  "revision_targets": [
    {
      "scope": "tactical | sub_pattern | falsification",
      "field": "<for tactical: which Phase 2.2 candidate field; for sub_pattern: which gap_closure[] entry's sub_pattern needs swapping (cite gap verbatim); for falsification: always 'falsification_prediction'>",
      "issue": "<which check / lesson triggered>",
      "fix_direction": "<concrete: what Phase 3.3 should change. For tactical: field-level edit. For sub_pattern: which specific sibling sub-pattern under the same parent fits better and why. For falsification: which structural element is missing/tautological (per falsification_structure_check) — the rewrite must keep the SAME experiment and claim, only repairing the structure.>"
    }
  ]
}

Verdict logic (two-layer: hard floor + soft judgment):

### Layer 1 — Hard floor (mechanical, NOT overridable by LLM)

If ANY of these fires, `verdict = abandon`. Set `verdict_layer = "hard_floor"` and cite the triggering check finding:

- `gap_closure_reject_check.verdict = triggered` — candidate falls into a documented Reject pattern (any gap_closure entry's Reject lesson marked `candidate_match: yes`).
- `anti_pattern_check.matched_pattern_id != null` AND `mitigation_substantively_delivered = false` AND the mitigation cannot be inserted by revision.
- `paper_pointed_threat.threat_paper_id != "no_threat_found"` AND it is exact-mechanism overlap.

When abandon fires: orchestrator emits `phase_3_failed.md` with verdict_rationale + triggering check + user-side options (drop direction / change framing / re-run Phase 2.1+2.2 with different gap selection from a fresh random sibling sample).

### Layer 2 — Soft judgment (LLM weighs checks within the safe zone)

If Layer 1 did not fire, the LLM picks `advance` or `revise`. Set `verdict_layer = "soft_judgment"`.

Constraints:
- LLM cannot contradict per-check facts (a borderline stays borderline).
- LLM CAN weigh severity: trivial borderline in non-load-bearing field → may justify advance with concern surfaced for Phase 4 to fold into reviewer_concerns_and_responses. Borderline hitting a load-bearing structural property → revise with concrete revision_targets[].
- LLM CAN identify a concern across-checks no single check flagged.

Heuristics:
- **Default to advance** when only one check is borderline AND it's in a non-load-bearing aspect.
- **Choose revise** when at least one borderline / partial finding hits a load-bearing structural property (core_mechanism's central argument, anti_pattern mitigation strength, addressable threat via specific candidate field).
- **Choose revise** when `recipe_application_check.verdict = bypassed` — the cited cluster's signature move is absent from core_mechanism, so the idea was built from the parent pattern's generic gist rather than the tactic that was supposed to make it sharp (the leading cause of incremental output, and the citation string cannot catch it because in this taxonomy the sub_pattern only names the parent). revision_target: either swap the sub_pattern to the sibling cluster whose tactical move core_mechanism actually performs, or rework core_mechanism to instantiate the cited move.
- **Choose revise** when paper_pointed_threat exists AND `addressable_via` names a specific candidate field.
- **Choose revise** when `falsification_structure_check.verdict = deficient` — the falsifiability commitment is the candidate's single most Reject-predictive field, and a structural hole in it (no load-bearing variable, tautological negative control) is repairable without touching the experiment or the claim. Emit ONE revision_target with `scope = "falsification"`, `field = "falsification_prediction"`, and a fix_direction naming exactly which structural element to repair. This is the ONLY route by which the kill-switch field may change; the rewrite is applied by Phase 3.3's dedicated `rewrite_falsification` op, gated by the merger on this audit's authorization, and MUST be re-audited (separate bounded call, self-contained prompt: `falsification_reaudit.txt`) before Phase 4.

  A `scope=falsification` target MAY ALSO be emitted when `falsification_structure_check.verdict = sound` but ANOTHER check's finding requires a **STRENGTHEN-ONLY** edit to `falsification_prediction`: adding a comparison baseline or control arm, or tightening an existing control's matching (rate/compute). Strengthen-only means ADDITIONS ONLY — the experiment, outcome metric, claim, load-bearing variable, and every existing control stay verbatim; anything that removes, swaps, weakens, or cheapens is NOT authorized under this clause and must not be requested. Name in fix_direction exactly what to ADD and which check finding requires it. Still max ONE falsification target per run; the mandatory re-audit applies identically. Without this clause a finding that spans a normal field AND the falsification baseline list would be half-dropped by the merger — never emit a `tactical` target whose fix requires touching `falsification_prediction`; split it into a tactical target for the normal field plus a strengthen-only falsification target.
- **Blocking obstacle findings** (the `blocking_findings.json` input, including `equivalent_to_naive` — the mechanism does not confront the obstacle it declares): disposition each first (see the input rules — refutation requires a concrete modeling/arithmetic flaw, never re-reasoning). Every `upheld` finding is a load-bearing borderline at minimum and CAPS the verdict below advance: when the confrontation is writable as a field-level rework of core_mechanism → revise (tactical, fix_direction names the obstacle to confront); when it requires redesigning what the mechanism IS → abandon (the internal retry then carries the obstacle as a positive directive). `blocking_findings_disposition[]` is REQUIRED whenever the input file was supplied — an audit that ignores supplied executed evidence is a process error, and the navigator will bounce the report.
- **Single-pattern candidates** (candidate.composition_note non-empty): weigh the note's defense as a soft signal. A substantive defense (names why the one move is complete and what a second pattern would fail to add) → no action. A generic defense ("the move is deep") → non-blocking concern for Phase 4's reviewer_concerns_and_responses; it does NOT gate advance by itself.
- **Choose advance** when all checks are clear / holds / null / no_threat_found / sound.

Hard rules:

1. **Phase 3.2 emits judgment only — no `final_candidate`, no auto-revision**. When a fix is needed, emit `verdict = revise` + `revision_targets[]`; Phase 3.3 (separate LLM call) applies them. Auto-revise within 3.2 triggers self-answering bias.

2. **anti_pattern_check evaluates substantive delivery, not keyword presence**. Does the candidate's core_mechanism actually produce the artifact the mitigation names? Keyword present but artifact absent → `mitigation_substantively_delivered = false`.

3. **Verdict must cite specific check findings**. `verdict_rationale` cites lesson_quoted / mitigation field / subsumption_argument. Generic "all checks pass" is a process error.

4. **No `composition` revision scope**. Phase 2.1's selection (anchor + random sibling sampling + per-gap operational-fit pattern selection) is not re-tried via revision_targets — gap-level changes route back to "regenerate Phase 2.1+2.2." Phase 3.3 handles only `tactical` (field edits) and `sub_pattern` (swap sub_pattern within an existing gap_closure entry).

5. **recipe_application_check is semantic, not a citation check**. A separate deterministic validator (subpattern_citation_consistency) already confirms each sub_pattern citation points at a real C## cluster under its cited parent — assume that passed. In this taxonomy the sub_pattern string carries only `C## (parent display name)`, so a clean citation does NOT prove the cluster's own card was opened. recipe_application_check answers the harder question automation cannot: even with a correctly-cited code, does core_mechanism actually perform that C##.md card's `## Tactical pattern` signature move, or did the generator read the parent pattern's name and produce something generic? Judge the MECHANISM against the card text, not the citation string.

6. **`scope = "falsification"` covers exactly two cases**: (a) a falsification_structure_check deficiency — the rewrite repairs STRUCTURE only (name the load-bearing variable, fix a tautological negative control, state the missing minimal experiment / metric direction, or — when `numeric_bar_provenance = invented` (never merely `asserted`, which routes to borderline instead) — STRIKE the unsourced numeric bar while keeping the metric, its direction, the load-bearing variable and every control verbatim; striking a bar that was never evidence is a FABRICATION REPAIR, explicitly NOT the "weaken the claim" anti-substitution violation named below, because falsifiability lives in the direction + control, which survive untouched); (b) a STRENGTHEN-ONLY addition required by another check's finding (add a baseline/control arm, tighten matching — see the verdict guidance above). In BOTH cases the experiment, the metric, and the claim already committed to are preserved verbatim; do NOT use this scope to make the experiment cheaper, swap the metric, or weaken the claim — those are anti-substitution violations the merger will reject. `compute_budget` has NO revision route under any scope.

Output path: `$RUN_DIR/phase3_critique/phase3_critique_output.json`.

---

