Skip to content

pgw#995: env carries values, never a branch selection — wave 2, the BEHAVIOUR axis, and a rig that can see hub env delivery - #521

Merged
PaulFidika merged 2 commits into
masterfrom
995-env-logic-sweep
Aug 7, 2026
Merged

pgw#995: env carries values, never a branch selection — wave 2, the BEHAVIOUR axis, and a rig that can see hub env delivery#521
PaulFidika merged 2 commits into
masterfrom
995-env-logic-sweep

Conversation

@PaulFidika

Copy link
Copy Markdown
Contributor

pgw#990 deleted GEN_WORKER_PREFER_AOT — the flag that gated both the mint recipe and cell discovery, silently disarmed on a release rebuild, and cost three pod attempts before anyone noticed the whole AOT path was dark. This PR is the class, not the instance.

Why the existing census could not have caught it

pgw#929/931's config_reads_allowlist.txt is a real census — 79 classified (file, variable) pairs. But all six of its classifications (VIOLATION / BOOTSTRAP / IPC / LIBRARY / STANDALONE / TRIPWIRE) answer WHERE a read happens relative to the config pipeline. None answers WHETHER it selects behaviour. Those are orthogonal axes, and the postmortem lives entirely on the second one.

The evidence the axis was missing rather than implicit is in the allowlist itself:

  • GEN_WORKER_AOT_EXPORT_PARALLEL and _REUSE were classified LIBRARY. Torch has never heard of either name — they are first-party feature gates that happened to be filed next to TORCHINDUCTOR_CACHE_DIR.
  • GEN_WORKER_BG_YIELD, _EAGER_FIRST_BOOT, _MINT_IN_PROCESS were classified STANDALONE"a CLI/convert path that loads no app config" — while all three are read from executor.py on the serving hot path of every production pod.

A classification scheme that lets three serving-path behaviour switches be labelled "standalone CLI" isn't wrong at any single line; it's measuring the wrong quantity.

The census, on the behaviour axis

env default fleet declarations verdict
GEN_WORKER_BG_YIELD ON 0 DELETED
GEN_WORKER_EAGER_FIRST_BOOT ON 0 DELETED
GEN_WORKER_AOT_WRAPPER_SPLIT_OFF ON 0 DELETED
GEN_WORKER_MINT_IN_PROCESS ON 0 kept — burn-down, blocker named below
GEN_WORKER_AOT_RUN_IMPL_SPLIT_OFF ON 5 releases, 1 live entry KEPT — live
GEN_WORKER_AOT_EXPORT_PARALLEL OFF 0 KEPT — deleting it is a launch
GEN_WORKER_AOT_EXPORT_REUSE OFF 0 KEPT — same
GEN_WORKER_HOST_MOVE_GUARD ON 0 KEPT — ruled exception
GEN_WORKER_PROBE / _PUBLISH_ARMED OFF 0 KEPT — security boundary
PODGUARD_STATE n/a KEPT — external producer

The three deletions share one property, and it is the test rather than a hope: their default is ON, so deleting the env makes the CURRENT PRODUCTION BEHAVIOUR unconditional. No pod's behaviour changes.

"Delete every kill switch" is wrong in two separate directions, and saying so is part of the finding:

  • AOT_RUN_IMPL_SPLIT_OFF sits in the same file as its deleted sibling and looks identical — and it is LIVE: 5 SDXL releases declare it, 1 endpoint carries a non-deleted entry. Zero declarations is a fact you measure, not one you infer from the code.
  • AOT_EXPORT_PARALLEL / _REUSE are default-OFF and dark by their own docstrings. Deleting those gates makes an unproven path unconditional — a launch, not a deletion.

Tests stop using env as a red-verification seam

BG_YIELD=0 and EAGER_FIRST_BOOT=0 drove RED arms in four files. An env kept alive to be a test seam is still an env a release rebuild can flip — the mechanism doesn't care about the intent.

Those arms are replaced by absolute-bound assertions plus source-level guards proving the deleted arm is unreachable, not merely unused. That distinction is load-bearing: deleting a switch and deleting its tests looks identical to deleting a switch and leaving a second reader behind — which is exactly how PREFER_AOT kept a live gate after one was believed removed.

The guard (the bar this PR was held to)

scripts/lint_config_reads.py gains an AST pass that flags any env read feeding a conditional and fails the build unless the (file, variable) pair is in BEHAVIOUR_GATES with the threat it defends against. "It is useful" and "it is off by default" are rejected. Stale exemptions fail too — an exemption for a read that no longer exists is the second carrier this gate exists to prevent.

RED-verified — adding a plausible new switch to executor.py:

src/gen_worker/executor.py:11504 reads GEN_WORKER_SOME_NEW_SWITCH from the
environment and feeds it to a CONDITIONAL. Env vars are for CONFIG and SECRETS,
never logic or behaviour switches. GEN_WORKER_PREFER_AOT was exactly this: it
gated the mint recipe and cell discovery, silently disarmed on a release
rebuild, and cost three pod attempts before anyone noticed the AOT path was dark.

The guard also caught my own over-seeding of BEHAVIOUR_GATES — seven entries I added for reads its conservative detector correctly does not flag. Those were removed rather than the check loosened.

The one remaining defect

GEN_WORKER_MINT_IN_PROCESS is not deleted. Default-ON, zero declarations, and enable_compiled(delegate=False)caller_forced_in_process is a strictly better parameter seam that already exists, making the env redundant with it. What blocks the deletion is ten test sites across seven files that force the shape via the env and then drive the executor, which reaches the policy with delegate=None. Threading the parameter through those call sites is a real refactor of a 4k-line test file and does not belong in a sweep. Recorded in BEHAVIOUR_GATES as a burn-down, not an exception.

Deliverable 2 — the rig could not see hub env delivery

micro_mint_rig.py (pgw#978) runs the whole mint machinery locally and is why this repo can prove a change before PyPI. But it constructs its own environmentmint_process.child_env for the mint child, dict(os.environ) for the adopting process. Neither is a shape a production pod ever has, so this chain was invisible to every test in the repo:

worker function declares env  ->  release_env_declarations
operator sets a value         ->  endpoint_env_entries
pod launch                    ->  Resolve  ->  pod env  ->  Settings

That chain is what took PREFER_AOT dark. The regression class had exactly one detector: a pod.

  • tests/harness/hub_env.py models the hub's rule (an entry reaches the pod only if the release declares its name, plus the reserved-namespace defence) and reports withholdings in th#1650's typed vocabulary. Deliberately small — a big fake hub drifts from the real one and starts certifying its own behaviour, which a test asserts against.
  • micro_mint_rig.py --hub-env boots the mint child through it, and strips ambient values so a developer's own shell cannot stand in for a hub-delivered one. That substitution is what the blind spot was made of; a mode that allowed it would be decoration.
  • tests/test_hub_env_delivery_pgw995.py drives the real config.load_settings() over hub-resolved environments and reproduces the postmortem in milliseconds: release N declares the name and the value reaches Settings; release N+1 is rebuilt without the declaration, nobody touches the entry, and the value is withheld and says so.

Full HelloAck-shaped boot (Vault, applies_to version/tag matching, the mTLS resolve path) is filed on pgw#995 with an owner.

Verification

  • lint_config_reads.py OK — 77 pairs, all classified; behaviour axis green and RED-verified
  • lint_http_timeouts.py, lint_unreached_surface.py OK
  • ruff check src/gen_worker clean; every changed file clean
  • Directly affected suites green: test_mint_gate_pgw677, test_mint_reopen_pgw677, test_mint_vram_budget_pgw737, test_eager_first_boot_pgw671, test_aot_mint_unblock_pgw813_pgw815, test_hub_env_delivery_pgw995 (8 new)

Hub half: th#1650 (tensorhub #882) — the silent continue in EndpointEnvService.Resolve that did the disarming.

@PaulFidika
PaulFidika force-pushed the 995-env-logic-sweep branch from f9a7f4e to bb52303 Compare August 7, 2026 06:16
…the BEHAVIOUR axis, plus a rig that can see hub env delivery

pgw#990 deleted GEN_WORKER_PREFER_AOT, the flag that gated both the mint recipe
and cell discovery, silently disarmed on a release rebuild, and cost three pod
attempts. This is the class rather than the instance.

WHY THE EXISTING CENSUS COULD NOT HAVE CAUGHT IT. pgw#929/931 is a real census —
79 classified (file, variable) pairs — but all six of its classifications answer
WHERE a read happens relative to the config pipeline, never WHETHER it selects
behaviour. Those are orthogonal, and the postmortem lives entirely on the second
axis. The proof it was missing rather than implicit is in the allowlist itself:
GEN_WORKER_AOT_EXPORT_PARALLEL and _REUSE were filed as LIBRARY (torch has never
heard of either name — they are first-party feature gates), and BG_YIELD /
EAGER_FIRST_BOOT / MINT_IN_PROCESS as STANDALONE, "a CLI that loads no app
config", while all three were read from executor.py on the serving hot path of
every production pod.

DELETED — three switches, each default-ON with zero fleet declarations, so
deleting the env makes the shape every pod already ran unconditional and NO
pod's behaviour changes:
  GEN_WORKER_BG_YIELD               + the legacy idle-gate tree it selected
  GEN_WORKER_EAGER_FIRST_BOOT       + REFUSAL_EAGER_FIRST_DISABLED, which can no
                                      longer be returned (a refusal reason
                                      nobody can reach is a cause a reader hunts
                                      for and never finds)
  GEN_WORKER_AOT_WRAPPER_SPLIT_OFF

KEPT — and "delete every kill switch" is wrong in two separate directions:
  AOT_RUN_IMPL_SPLIT_OFF  LIVE. 5 SDXL releases declare it, 1 endpoint carries a
                          non-deleted entry. It sits in the same file as its
                          deleted sibling and looks identical — zero
                          declarations is a fact you MEASURE.
  AOT_EXPORT_PARALLEL     default-OFF and dark by their own docstrings. Deleting
  AOT_EXPORT_REUSE        those gates makes an unproven path unconditional,
                          which is a LAUNCH, not a deletion.
  HOST_MOVE_GUARD, PROBE  ruled exceptions with named threats.

TESTS STOP USING ENV AS A RED-VERIFICATION SEAM. BG_YIELD=0 and
EAGER_FIRST_BOOT=0 drove RED arms in four files. An env kept alive to be a test
seam is still an env a release rebuild can flip. Those arms are replaced by
absolute-bound assertions plus source-level guards proving the deleted arm is
UNREACHABLE, not merely unused — deleting a switch and deleting its tests looks
identical to deleting a switch and leaving a second reader behind, which is
exactly how PREFER_AOT kept a live gate after one was believed removed.

THE GUARD. lint_config_reads.py gains an AST pass that flags any env read
feeding a conditional and fails the build unless the (file, variable) pair is in
BEHAVIOUR_GATES *with the threat it defends against* — "it is useful" and "it is
off by default" are rejected, and stale exemptions fail too. Same structural bar
as the mint_recipe guard shipped in 0.93.2. RED-verified: adding a plausible new
switch to executor.py fails with a message naming the postmortem.

NOT DELETED, and it is the one remaining defect: GEN_WORKER_MINT_IN_PROCESS.
Default-ON, zero declarations, and enable_compiled(delegate=False) is a strictly
better parameter seam that already exists — blocked only on ten test sites
across seven files that force the shape via the env and drive the executor.
Recorded as a burn-down, not an exception.

DELIVERABLE 2 — the rig blind spot. micro_mint_rig.py runs the whole mint
machinery locally, which is why a change can be proven before PyPI, but it
CONSTRUCTS ITS OWN ENVIRONMENT (mint_process.child_env for the mint child,
dict(os.environ) for the adopting process) — shapes no production pod ever has.
So the chain that actually delivers env to a pod (function declares ->
release_env_declarations -> endpoint_env_entries -> Resolve -> pod env ->
Settings) was invisible to every test in this repo, and the regression class
that took PREFER_AOT dark had exactly one detector: a pod.

tests/harness/hub_env.py models the hub's rule (an entry reaches the pod only if
the release DECLARES its name, plus the reserved-namespace defence) and reports
withholdings in th#1650's typed vocabulary. `--hub-env` boots the mint child
through it and STRIPS ambient values, so a developer's shell cannot stand in for
a hub-delivered one — that substitution is what the blind spot was made of, and
a mode that allowed it would be decoration.
tests/test_hub_env_delivery_pgw995.py drives the real config.load_settings()
over hub-resolved environments and reproduces the postmortem in milliseconds:
release N declares the name and the value reaches Settings; release N+1 is
rebuilt without the declaration, nobody touches the entry, and the value is
withheld AND says so.

Full HelloAck-shaped boot (Vault, applies_to version/tag matching, mTLS resolve)
is filed on pgw#995 with an owner. Hub half: th#1650.

Gates: lint_config_reads OK (77 pairs), lint_http_timeouts OK,
lint_unreached_surface OK, ruff clean on src/gen_worker and every changed file.
CI caught what the local run had not reached: test_aot_wrapper_split_pgw793
referenced ws.DISABLE_ENV, the GEN_WORKER_AOT_WRAPPER_SPLIT_OFF constant deleted
with the switch. The test asserted install() could be killed by the env; it now
asserts the opposite — that the retired name is inert and the constant is gone.

Which is the point rather than a formality: a test that merely stopped mentioning
the switch would pass identically whether the deletion took or left a live reader
behind somewhere else, and that ambiguity is exactly how GEN_WORKER_PREFER_AOT
kept a live gate after one was believed removed.
@PaulFidika
PaulFidika force-pushed the 995-env-logic-sweep branch from bb52303 to e6804ae Compare August 7, 2026 06:34
@PaulFidika
PaulFidika merged commit a39bcf7 into master Aug 7, 2026
2 checks passed
@PaulFidika
PaulFidika deleted the 995-env-logic-sweep branch August 7, 2026 06:53
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