Skip to content

pgw#984 + pgw#985: the AOT recipe proves the endpoint's forward runs, and a deterministic arm decline is a typed refusal on both recipes - #514

Merged
PaulFidika merged 2 commits into
masterfrom
984-985-mint-recipes
Aug 6, 2026
Merged

pgw#984 + pgw#985: the AOT recipe proves the endpoint's forward runs, and a deterministic arm decline is a typed refusal on both recipes#514
PaulFidika merged 2 commits into
masterfrom
984-985-mint-recipes

Conversation

@PaulFidika

Copy link
Copy Markdown
Contributor

Two mint-recipe correctness defects the pgw#978 micro-mint rig found inside its first hour, by running the same machinery twice with only MintRequest.recipe changed. Both are fixed here, RED-proved on the rig first.

pgw#984 — a green AOT mint proved nothing about the cell it sealed

Measured. An AOT mint's phase table was {'load': 4.4, 'trace_graph': 8.1, 'seal_publish': 0.9, 'finalize': 0.0}no warmup_forward row at all. torch.export traces the declared modules directly, so mint_child._mint_aot never called the endpoint's handler. Consequences:

  • pgw#969's crash class — ctx.slots["pipeline"] unbound, 0.0 s into warmup_forward, measured twice on production L40S pods — was unreachable on this recipe;
  • an endpoint whose forward dies on its first real request minted, sealed and published an AOT cell fine.

Fix. mint_child.mint's AOT branch now drives the endpoint's OWN derived warm plan — the same _run_warm_job, the same warmup.plan over the same class-scoped sibling set the dynamo recipe uses — once, eagerly, before a byte is exported. ONE job and not the whole plan: the export derives its own class set, so a full eager pass buys minutes and proves nothing the first forward does not. Nothing is armed for it, so it specializes no graph the export then has to trace around.

execution_lane_verdict_for / _load now carry the endpoint INSTANCE out with the pipeline, because the handler is a method on it.

Behaviour change, stated: an endpoint whose class carries no @endpoint declaration, or whose derived warm plan is empty, now REFUSES an AOT mint where it used to seal one. Both were already dynamo-recipe refusals.

pgw#985 — a deterministic refusal was classified as a retryable crash, and named wrong

Measured. Same box, same pipeline, one recipe flag changed:

RuntimeError: no compile targets resolved on TinyDiffusionPipeline

...out of compile_cache.begin_fleet_mint — about a pipeline whose .unet has_compile_target had resolved one frame earlier. Two defects in one line:

1. Two computations of one fact (§1.29, th#1616). has_compile_target scanned cfg.targets; apply scanned them again in its own loop; begin_fleet_mint called both and reported whichever declined under the first one's sentence. What had actually declined was apply, because the process had no CUDA — so the message ruled out the only thing it was not.

  • compile_cache.resolve_targets is now the ONE target authority. has_compile_target, apply and begin_fleet_mint all read it.
  • compile_cache.arming_block is the ONE precondition authority — side-effect free, returns the named reason apply logs and begin_fleet_mint refuses with. apply still DECIDES (one evaluator); arming_block only names.
  • The wiring fact ("this pipeline owns no declared target") and the environment fact ("this process cannot arm the targets it owns") are now separate sentences. The same misnaming is gone from build and _compile_and_warm.

2. The classification, not the message, decides the second pod. A bare RuntimeError exits 1 → CRASHED → the retryable class, for a fact no retry can change. begin_fleet_mint now raises the typed CompileArmRefused, which the mint child turns into MintChildRefusedEXIT_REFUSED → terminal on attempt one — exactly what the AOT recipe has always done with the identical condition. Warm-forward failures are classified the same way on BOTH recipes now (the hub's self_mint_abort event already booked phase=warmup_forward as deterministic while the worker still called it crashed); a resource shortfall re-raises untouched, so EXIT_RESOURCE and pgw#848's re-budgeted retry are unaffected.

Also: a refused or crashed mint's report carried phase="" in every case — _close_phases() closes the open phase and MintReport.phase was read after it in the same call.

The rig proof

task rig:mint, ~20 s a cycle, --stage mint:

recipe before after
dynamo crashed, exit 1, RuntimeError: no compile targets resolved on TinyDiffusionPipeline, phase="" refused, exit 2 (EXIT_REFUSED), not retryable, mint family='microrig' … fn='rig-generate' recipe='dynamo': TinyDiffusionPipeline owns the declared compile target(s) ['unet'] for family 'microrig', but this process cannot arm them: torch reports no CUDA device in this process, phase="load"
aot phases finalize/load/seal_publish/trace_graph phases finalize/load/seal_publish/trace_graph/warmup_forward (0.26 s)

Tests

tests/test_mint_recipe_parity_pgw984_pgw985.py — 9 rows at the real seams, no card and no compile required:

  • resolve_targets is one relation: move it and has_compile_target, apply and begin_fleet_mint all move with it;
  • a cardless process is named as the environment fact it is, and the wiring sentence must NOT stand in for it;
  • the refusal leaves TORCHINDUCTOR_CACHE_DIR where it was (gw#608's invariant, kept);
  • the dynamo recipe, in a REAL child on a REAL request file, exits EXIT_REFUSED, is not retryable, names family/function/recipe/target/why, writes no artifact, and reports the phase it died in;
  • both recipes refuse a missing target in the same vocabulary;
  • the AOT recipe runs the endpoint's own forward (asserted through ctx.slots) before it exports, and warmup_forward lands in the phase table;
  • an AOT mint cannot seal for a handler that raises — nothing is exported;
  • a MemoryError in the warm plan is still a resource shortfall, not a refusal.

8 of the 9 fail on a clean detached checkout of origin/master. The one that passes is the missing-target parity row — that condition was already typed on both recipes; it is the invariant, not the defect.

tests/test_kernel_lane_pgw947.py doubles updated for the 3-tuple _load.

Verification

  • tests/ 3299 passed, 37 skipped, 1 xfailed (-n 4 --dist loadfile); tests_v2/ 21 passed.
  • mypy clean (231 files), ruff clean, HTTP-timeout guard, unreached-surface guard, config-read guard all pass.
  • No new skips (37 before and after).

Filed, not fixed here: pgw#988 (P0)

The same rig cycle found that origin/master cannot adopt any AOT cell it publishes. th#1645/pgw#987 (PR #507) added entries to _UNBOUNDED_ENVELOPE_BLOCKS, but aot_cells._discover_inner runs aot_serve.verify against the DECLARE metadata, pre-download, and that verify requires the entries map:

aot-cells: ck5-… filtered: malformed declared contract: metadata declares no entries map
aot_cell_discovery miss: rejected by class: verify:malformed declared contract: metadata declares no entries map=1

Reproduced on a clean detached checkout of c2e52f5f; the rig's adopt leg is green at f029593e (before PR #507) and red at c2e52f5f and on this branch identically. Not caused by and not fixed by this PR — restoring entries re-creates th#1645's 413, so the fix is a contract decision for that lane. Filed as pgw#988 with the measurement and three candidate fixes.


Supersedes #510 — same branch, same commits. #510 stopped receiving pull_request events during the 2026-08-06 Actions outage (4 close/reopen cycles and 2 pushes produced zero runs), so its status rollup stayed empty and it could not register required checks. CI is green on this branch: run 31128545355 on a9103f9ctests success (12 steps, 15m48s) and fast gates success (14 steps, 1m34s).

… and a deterministic arm decline is a typed refusal on both recipes

Both defects were MEASURED by the pgw#978 micro-mint rig inside its first
hour, by running the same machinery twice with only `MintRequest.recipe`
changed.

pgw#984 — the AOT recipe never entered the endpoint. Its measured phase table
was `load / trace_graph / seal_publish / finalize` with NO `warmup_forward`
row: `torch.export` traces the declared modules directly, so the handler was
never called. pgw#969's crash class (`ctx.slots["pipeline"]` unbound, 0.0 s
into `warmup_forward`, twice on production L40S pods) was therefore
unreachable on that recipe, and an endpoint whose forward dies on its first
request minted, sealed and published a cell fine. `mint_child.mint` now drives
the endpoint's OWN derived warm plan once, eagerly, before a byte is exported
— the same `_run_warm_job` over the same class-scoped sibling set the dynamo
recipe uses. One job, not the whole plan: the export derives its own class set.

pgw#985 — `compile_cache.begin_fleet_mint` raised
`RuntimeError: no compile targets resolved on TinyDiffusionPipeline` about a
pipeline whose `.unet` had resolved a frame earlier. Two defects in one line:

  * Two computations of one fact (§1.29, th#1616). `has_compile_target` and
    `apply` each scanned `cfg.targets`, and the raise reported whichever
    declined under the first one's sentence — the actual decline was `apply`,
    for want of CUDA. `resolve_targets` is now the one target authority and
    `arming_block` the one precondition authority; the wiring fact and the
    environment fact are separate sentences. Same misnaming removed from
    `build` and `_compile_and_warm`.
  * A bare `RuntimeError` exits 1 -> `CRASHED` -> the retryable class, for a
    fact no retry can change (a second billed pod). It is now the typed
    `CompileArmRefused`, which the child turns into `MintChildRefused` ->
    `EXIT_REFUSED` -> terminal on attempt one, exactly as the AOT recipe has
    always classified the identical condition. Warm-forward failures are
    classified the same way on both recipes; resource shortfalls still
    re-raise untouched so `EXIT_RESOURCE` and pgw#848's re-budget are intact.

Also: a failed mint's report carried `phase=""` in every case — `_close_phases()`
closes the open phase and the field was read after it in the same call.

RED-proved on the rig (dynamo: `crashed`/exit 1 -> `refused`/exit 2, typed;
AOT: `warmup_forward` now in the phase table) and by 8 of the 9 new rows
failing on a clean `origin/master` checkout.
…d which one arming_block owns

Docstring only — the two authorities are separate functions already; this
stops the target authority's own docstring from explaining the ARM fact,
which is the confusion the split exists to end.
@PaulFidika
PaulFidika force-pushed the 984-985-mint-recipes branch from fd4f557 to a9103f9 Compare August 6, 2026 22:43
@PaulFidika

Copy link
Copy Markdown
Contributor Author

CI is green on this head; the PR gate cannot see it — GitHub's status rollup is still down.

Run 31128545355 executed for real on merge head a9103f9c (not a queue-expired shell — note the step counts and wall times):

tests       success  12 steps  22:01:41Z -> 22:17:29Z  (15m48s)
fast gates  success  14 steps  22:22:37Z -> 22:24:11Z  (1m34s)

Both required contexts are present and green as check-runs on the PR head:

$ gh api .../commits/a9103f9c/check-runs
fast gates  success
tests       success
$ gh api .../rules/branches/master   # required contexts
fast gates
tests

But statusCheckRollup is empty and mergeStateStatus is BLOCKED, because the rollup is built from the event pipeline, which is not delivering. Repo-wide, exactly ONE pull_request run has been produced since 14:46 UTC (973-limits-wave2 at 21:18, itself queue-killed). This PR has been given every legitimate trigger and produced zero runs: 4 close/reopen cycles on #510, 2 pushes, and a brand-new PR (opened) — which is why #510 was retired for this one.

Head was restored to a9103f9c (force-with-lease, tree byte-identical) precisely because that SHA already carries both green required check-runs; the synchronize marker commit that briefly moved it is gone.

Not merging. Required-status is a real gate and the runs behind it are genuinely green — but making the PR mergeable right now would mean bypassing the gate rather than satisfying it, and the difference between "the checks passed" and "the gate observed them pass" is exactly what the gate is for. Polling for the rollup to recover.

Local verification on this same head, for the record: tests/ 3299 passed, 37 skipped, 1 xfailed, mypy clean (231 files), ruff clean, HTTP-timeout / unreached-surface / config-read guards, uv build.

@PaulFidika
PaulFidika merged commit 17329eb into master Aug 6, 2026
4 checks passed
@PaulFidika
PaulFidika deleted the 984-985-mint-recipes branch August 6, 2026 23:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant