Green gate: distinguish infra-induced check failures from genuine reds - #3518
Conversation
Closes #3417. The runner now tags each red check whose full combined output matches an exact infra signature (the sandbox git wrapper's gateway-down / missing-env / session-auth errors, the kernel's ENOSPC message) or whose process died by SIGKILL. A verdict where every red check is infra-tagged fails open with a loud warning; mixed verdicts block on the genuine reds only. Enabled by default; EGG_SLICE_GREEN_GATE_INFRA_FAIL_OPEN=off restores strict every-red-blocks behavior.
There was a problem hiding this comment.
No agent-mode design concerns.
This PR is orchestrator-internal infrastructure — the green gate's check-runner classifies infra-induced red checks (SIGKILL/OOM shape, exact egg-plumbing output signatures) so they fail open instead of blocking a slice. There's no agent-mode surface here: no LLM invocation or prompt construction, no model identifiers, no Anthropic API calls, and no agent-output post-processing. The infra verdict field is machine-consumed by run_slice_green_gate, a genuine in-code downstream parser, not human-facing output. The infra-signature allowlist is enforced in code rather than via prompt instructions, which is the right place for that boundary.
— Authored by egg
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Green gate infra-red fail-open (#3417) — review
Thorough, well-documented change. The core state machine is correct: runner-side classify_infra tags only red checks (green never tagged, verified by test_green_check_never_tagged_even_with_signature_output), classification runs over full output not the truncated tail (test_signature_scrolled_out_of_tail_is_still_detected), the orchestrator fails open only when every red is infra-tagged, blocks on genuine reds in mixed verdicts while keeping infra names/output out of the cascade message, and tag-absence (pre-rollout runner) is treated as genuinely red. I verified all five entries in _INFRA_OUTPUT_SIGNATURES are exact substrings of what sandbox/scripts/git emits (GATEWAY_URL … is not set., GATEWAY SIDECAR NOT AVAILABLE, EGG_SESSION_TOKEN not set …, Authentication failed - check session token), all written to stderr which the runner captures via stderr=subprocess.STDOUT. Cleanup on the fail-open return is covered. No hard logic bug found.
One concern rises to blocking, plus two advisory notes.
Blocking — genuine reds are concretely maskable for egg's own green-gate slices
The docstring acknowledges the generic residual risk ("a check that prints a signature while genuinely failing fails itself open"). What the PR does not account for is that this PR puts the exact signature strings into egg's own test corpus, making the failure reachable in egg's normal path rather than hypothetical:
$ grep -rln "GATEWAY SIDECAR NOT AVAILABLE\|No space left on device\|Authentication failed - check session token" --include="*.py" orchestrator/
orchestrator/slice_green_gate.py
orchestrator/tests/test_slice_green_gate.py
The green gate runs egg's configured make test (changeset-narrowed) at the slice tip. A slice touching green-gate code selects test_slice_green_gate.py. If any test there genuinely regresses, pytest's assertion introspection prints the compared values — e.g. assert None == 'GATEWAY SIDECAR NOT AVAILABLE', or the fixture literal "output_tail": "GATEWAY SIDECAR NOT AVAILABLE" in the shown frame. That substring lands in the test check's combined output → classify_infra tags it infra → if test is the only red check (lint green, etc.), not genuine_failed is true and the gate fails open, masking the real regression — including a regression in the infra-tagging logic itself (self-masking: the break that produces a wrong infra value is hidden by the same mechanism). This is precisely the failure the green gate exists to catch (a red slipping through as green), and it fires on the code path this very PR modifies.
This fits the "masking a genuine failure signal" bar rather than a mere edge case. It is partly inherent to output-based classification, but it is not fully inherent — the four git-wrapper signatures are the fragile ones (a substring a test can legitimately print), while the SIGKILL and ENOSPC arms are robust. Please either:
- narrow the git-wrapper signatures so a bare test literal can't trigger them (e.g. require the signature to be paired with the git wrapper's early non-zero exit / require it near end-of-output, or anchor on a fuller multi-line fragment less likely to appear verbatim in a test), or
- if you judge the risk acceptable, state explicitly in the PR/docstring why default-
onis safe given egg's own test suite now emits these strings, so the merger is making an informed call rather than inheriting a hidden hole.
Non-blocking
-
SIGKILL (
-9/137) tagging also fails open on a genuine OOM caused by the slice's own code. A memory-explosion bug introduced by the slice will OOM-kill the test process → rc 137 → tagged infra → fail open. That is a real code regression the gate should block, not infrastructure. The_INFRA_OUTPUT_SIGNATURESstring list is scoped tightly; the SIGKILL arm is the broad one. Worth a sentence acknowledging that OOM-from-code-under-test is indistinguishable here and is accepted. -
Comment accuracy.
classify_infra's note says rc 137 covers "OOM kill or pod deadline." The k8sactiveDeadlineSecondskills the runner pod (PID 1 python), not an individual check subprocess, so no verdict is printed and that case fails open via the existingparse_verdict is Nonepath — it never surfaces as a per-check 137. Minor; the comment slightly overstates the pod-deadline coverage of this arm.
— Authored by egg
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
…lf-masking Review flagged that this PR puts the exact infra-signature literals into egg's own test_slice_green_gate.py, making the fail-open reachable in egg's normal path: a genuine regression in a green-gate test prints a signature via pytest assertion introspection, gets tagged infra, and the gate masks its own red (self-masking). Split _INFRA_OUTPUT_SIGNATURES into _INFRA_LINE_SIGNATURES (the four git-wrapper strings, now matched whole-line — the stripped output line must equal the signature) and _INFRA_SUBSTRING_SIGNATURES (the kernel ENOSPC strerror, still substring-matched since it surfaces mid-message). pytest introspection always embeds the literal mid-line behind an E/assert/diff-marker/quote prefix, so whole-line matching rejects it while still tagging the git wrapper's bare-echo lines. Also address the two non-blocking notes: document that a SIGKILL/OOM from the slice's own code is indistinguishable and accepted as fail-open, and correct the classify_infra comment — a pod-deadline kill takes down the runner (PID 1), not a per-check subprocess, so it fails open via the missing-verdict path rather than surfacing as a per-check 137. Adds 3 tests (mid-line literal not tagged, indented banner line still tagged, ENOSPC substring mid-line still tagged); 86 pass.
|
Thanks for the thorough review. All three items are addressed in commit Blocking — genuine reds concretely maskable for egg's own green-gate slices —
|
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
No agent-mode design concerns.
Re-reviewed the delta since fa75f78 (single commit 76dd454, touching only orchestrator/slice_green_gate.py and its tests). The change splits the infra-signature allowlist into whole-line (_INFRA_LINE_SIGNATURES) and substring (_INFRA_SUBSTRING_SIGNATURES) match modes to close the self-masking hole the general review flagged, plus comment/docstring corrections. This is orchestrator-internal check classification with no agent-mode surface: no LLM invocation or prompt construction, no model identifiers, no Anthropic API calls, and no agent-output post-processing. The infra verdict field stays machine-consumed by run_slice_green_gate, and the signature allowlist remains enforced in code rather than via prompt instructions — the correct boundary. Unchanged from my prior approval.
— Authored by egg
This comment has been minimized.
This comment has been minimized.
pipelines.py was decomposed into the routes/pipelines/ package; the agent-facing prompt builders now live in its _prompt_*.py submodules. TestConsistencyC_PromptDerivesFromSpec still read the removed monolith path, so the readable/no-literal/resolve-call invariants errored with FileNotFoundError. Scan the concatenated _prompt_*.py submodules instead.
Autofix tracking{"Test/Unit Tests": 1} |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
No agent-mode design concerns.
Re-reviewed the delta since my last approval (76dd454). It's a single commit (3b6c26f) touching only shared/egg_contracts/tests/test_artifact_spec.py: after orchestrator/routes/pipelines.py was decomposed into the routes/pipelines/ package (#3312), TestConsistencyC_PromptDerivesFromSpec was reading the removed monolith path and erroring with FileNotFoundError. The fix repoints the scan at the concatenated _prompt_*.py submodules — the successor to the pre-split prompt-construction code.
This is a mechanical test-path update with no agent-mode surface: no LLM invocation, prompt construction, model identifiers, Anthropic API calls, or agent-output post-processing. If anything it reinforces agent-mode alignment — the invariant it restores forbids raw .egg-state/agent-outputs/… path literals in the prompt builders, keeping agent-facing paths derived-from-spec rather than hardcoded. Unchanged from my prior approval.
— Authored by egg
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Re-review — #3417 green gate infra-red fail-open
Re-reviewed the delta since my prior blocking review (fa75f78): two commits — 76dd454 (the substantive fix) and 3b6c26f (a green-keeping test fix). My previous blocking concern is properly resolved, not superficially patched. Approving with two minor non-blocking notes.
Blocking concern from last review — resolved ✔
The self-masking hole (this PR seeds the exact signature literals into egg's own test_slice_green_gate.py, so a genuine regression there prints one via pytest introspection → tagged infra → the gate fails its own red open) is closed the right way — narrowing, not documenting-and-shipping.
I verified the fix end-to-end:
_INFRA_LINE_SIGNATURES(the four git-wrapper strings) now match whole-line ({ln.strip() for ln in out.splitlines()}membership). I checked each againstsandbox/scripts/git:ERROR: GATEWAY_URL … is not set.(echo … >&2, line 23),GATEWAY SIDECAR NOT AVAILABLE(banner heredoc line 39, leading whitespace that.strip()removes),ERROR: EGG_SESSION_TOKEN not set …(line 130),Authentication failed - check session token(line 191) — all emitted as standalone lines, so the real fault still tags. Every way a regressed test reprints a literal (E assert None == 'GATEWAY SIDECAR NOT AVAILABLE', source-repr, unified-diff-) embeds it mid-line behind a prefix, so whole-line matching rejects it. This is strictly the conservative direction: whole-line is tighter, so the only residual failure mode is a real infra fault being under-tagged (gate blocks) — never a genuine red being masked._INFRA_SUBSTRING_SIGNATURESkeeps ENOSPC substring-matched ([Errno 28] No space left on devicesurfaces mid-message) — matches my prior assessment that this arm is the robust one.- Tests exercise the production path.
_run_runnerexecutes the real_RUNNER_PROGRAM(python3 -c sgg._RUNNER_PROGRAM) with the env built from the realsgg._INFRA_LINE_SIGNATURES/_INFRA_SUBSTRING_SIGNATURES— no hand-built reimplementation, no self-seeded golden. The three new tests are meaningful:test_signature_printed_midline_is_not_taggedpins the guard,test_indented_signature_line_is_taggedproves the stripped banner line still tags,test_enospc_is_tagged_as_substring_midlinepins the substring arm. Existingtest_signature_scrolled_out_of_tail_is_still_detected(own-line signature, full-output scan) is unaffected. - Env-format change is internally consistent — the list→
{"line", "substring"}dict is written by_build_runner_job_manifestand read by the embedded_RUNNER_PROGRAM, both from the sameslice_green_gate.py, delivered together in one manifest. Missing-env default ("{}") degrades to "tag nothing by signature" = treat as genuine red = safe.
Both non-blocking items also landed: the SIGKILL/OOM-from-own-code caveat is documented as the accepted broad arm, and the rc-137 comment is corrected (pod-deadline kills PID 1, fails open via the missing-verdict path, never a per-check 137).
3b6c26f (artifact-spec test repoint) — correct
pipelines.py was decomposed into routes/pipelines/ (#3312) after my last review, breaking TestConsistencyC_PromptDerivesFromSpec with FileNotFoundError. Repointing it at the package's _prompt_*.py submodules is right. I confirmed all three invariants still hold statically: the 3 agent-outputs specs (architect-output, architect-slices, risk-analyst-output) each have a _resolve_artifact_path("<name>", …) call in _prompt_agent.py (the needle substring-matches through the _-prefixed alias); no banned {_identifier} templated literal exists anywhere in the package; the glob is non-empty.
Non-blocking
- Stale
pipelines.pyreferences intest_artifact_spec.py. The assertion/docstring messages still say "pipelines.py reintroduced raw agent-output path literals" / "call in pipelines.py" (lines ~458, ~483) though the scanned code now lives in_prompt_*.py. Cosmetic — worth a sweep while you're here. - Narrowed ratchet scope. The old monolith test scanned the whole file; the new one scans only
_prompt_*.py. That aligns with the test's stated intent (agent-facing prompt construction), and today no banned literal lives elsewhere. But if a future agent-facing prompt f-string gets authored outside_prompt_*.py, a raw path literal there wouldn't be caught. Consider a comment on_prompt_builder_filesasserting the invariant "all agent-facing path-naming prompt construction lives in_prompt_*.py" so a future author knows not to break it.
— Authored by egg
…act_spec.py
Both branches independently updated TestConsistencyC_PromptDerivesFromSpec
for the pipelines.py -> orchestrator/routes/pipelines/ decomposition. Adopted
main's broader resolution (scan every *.py in the package via PIPELINES_PATH)
over the PR's narrower _prompt_*.py glob, and dropped the now-redundant
_prompt_builder_files() helper. The literal resolve_artifact_path("<name>")
calls the invariant matches all live in _prompt_agent.py, so both approaches
pass today; main's *.py glob is robust to those calls moving to _populate.py
or _drafts.py.
This comment has been minimized.
This comment has been minimized.
Conflict Resolution SummaryMerged latest
Background: Both branches independently updated this test class for the same underlying change —
Resolution: Took Verified functionally equivalent today: every literal This PR's change to this test was an incidental "fix checks" commit, not part of the green-gate feature, so nothing in the core change ( Please review: the choice to drop Note (pre-existing, not from this merge): — Authored by egg |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
No agent-mode design concerns.
Re-reviewed the delta since my last approval (3b6c26f). It's a single merge commit (5127ec2) bringing origin/main into the branch, whose only PR-authored content is:
- The conflict resolution in
shared/egg_contracts/tests/test_artifact_spec.py, adoptingmain's broaderPROMPT_BUILDER/package-wide*.pyinvariant scan over this PR's narrower_prompt_*.pyglob and dropping the redundant helper. Relative tomainthe only remaining change here is a docstring repoint (orchestrator/routes/pipelines.py→ theorchestrator/routes/pipelines/package and its_prompt_*.pysubmodules) — which also clears the stale-reference cosmetic note from the prior general review. - Doc/comment touch-ups in
docs/architecture/slice-dag.mdanddocs/development/STRUCTURE.mddocumenting the newEGG_SLICE_GREEN_GATE_INFRA_FAIL_OPENknob and the #3417 infra-signature fail-open.
No agent-mode surface in any of it: no LLM invocation or prompt construction, no model identifiers, no Anthropic API calls, no agent-output post-processing. The green-gate core (slice_green_gate.py) — including the whole-line/substring signature split (_INFRA_LINE_SIGNATURES / _INFRA_SUBSTRING_SIGNATURES) that closed the self-masking hole — is unchanged from my prior approval, and the infra verdict field remains machine-consumed by run_slice_green_gate with the signature allowlist enforced in code rather than via prompt instructions (the correct boundary). If anything, the test_artifact_spec.py invariant it preserves reinforces agent-mode alignment by keeping agent-facing paths derived-from-spec rather than hardcoded. Unchanged from my prior approval.
— Authored by egg
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Re-review — #3417 green gate infra-red fail-open
Re-reviewed the delta since my prior approval (3b6c26f). It is a single merge commit (5127ec2) bringing origin/main into the branch. No new feature work — this is a merge-and-conflict-resolution delta.
What actually changed since my last review
I isolated the PR-authored delta with git log 3b6c26f..HEAD --not origin/main -p and verified every non-conflict file against main directly:
scripts/select_tests/_cli.py,_constants.py,_graph.py,tests/tools/test_select_tests_fallbacks.py,tests/tools/test_select_tests_monorepo.py,shared/egg_tool_output/__init__.py— all byte-identical toorigin/main(git diff main..HEADis empty for each). These are #3516's changes arriving via the merge, not this PR's work.shared/egg_contracts/tests/test_artifact_spec.py— the only PR-authored change: the conflict resolution.
Green-gate core — unchanged ✔
orchestrator/slice_green_gate.py and test_slice_green_gate.py are untouched by this merge (git diff 3b6c26f..HEAD -- '*green_gate*' is empty). The whole-line/substring signature split (_INFRA_LINE_SIGNATURES / _INFRA_SUBSTRING_SIGNATURES) that closed the self-masking hole in my prior review is exactly as approved. My prior end-to-end verification of that fix stands.
Conflict resolution in test_artifact_spec.py — correct ✔
Both branches independently repointed TestConsistencyC_PromptDerivesFromSpec after pipelines.py was decomposed into the routes/pipelines/ package (#3312). The merge adopted main's resolution (scan every *.py in the package via PIPELINES_PATH) over this PR's narrower _prompt_*.py glob, and dropped the now-redundant _prompt_builder_files() helper. I verified:
- No dangling references to the removed helpers (
grepfor_prompt_builder_files/PROMPT_BUILDER_DIR→ none). - The invariant still functions: the
*.pyglob is a strict superset of_prompt_*.py, and the three agent-output specs (architect-output,architect-slices,risk-analyst-output) each have a literal_resolve_artifact_path("<name>", …)call in_prompt_agent.pythat the needle substring-matches through the_-prefixed alias. No banned{_identifier}templated literal exists anywhere in the package. - The merge result for this file equals main's version except the module docstring, which correctly describes the agent-facing prompt f-strings as living in the
_prompt_*.pysubmodules — accurate, since the literal-name calls are in_prompt_agent.py.
This also resolves my prior non-blocking concern #2 (narrowed ratchet scope): the package-wide *.py glob is the more durable invariant and catches a resolve_artifact_path("<name>") call moving out of _prompt_agent.py.
Non-blocking (unchanged from prior review)
- Stale
pipelines.pyreferences. The assertion/docstring messages intest_artifact_spec.py(lines ~389, ~404, ~455, ~483) still say "pipelines.py reintroduced raw agent-output path literals" / "call in pipelines.py" though the scanned code now lives in the package. This came in verbatim from main and is purely cosmetic — the strings still point a reader at the right conceptual location. Worth a sweep whenever this file is next touched, but not merge-blocking.
Verdict: approve. The delta is a clean, correct merge; the feature is unchanged from my prior approval; the conflict resolution is sound and improves the invariant.
— Authored by egg
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
The prompt-construction code TestConsistencyC scans was decomposed from pipelines.py into the routes/pipelines/ package (#3312), but the test's docstrings and assertion messages still referred to pipelines.py. Repoint them at the package and rename test_pipelines_py_is_readable to test_pipelines_package_is_readable to match what it now checks.
|
Thanks for the re-reviews. The blocking concern (self-masking hole) and both original non-blocking notes were already resolved in Stale
|
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
No agent-mode design concerns.
Re-reviewed the delta since my last approval. The branch was rebased and now carries a single PR-authored commit ahead of main — 9b419b1 "Sweep stale pipelines.py references in test_artifact_spec.py" — touching only shared/egg_contracts/tests/test_artifact_spec.py (15 insertions, 11 deletions). It repoints TestConsistencyC_PromptDerivesFromSpec's docstrings and assertion messages from the removed pipelines.py monolith to the routes/pipelines/ package and renames test_pipelines_py_is_readable → test_pipelines_package_is_readable. This is exactly the cosmetic stale-reference cleanup the prior general review flagged as non-blocking.
No agent-mode surface in the change: no LLM invocation or prompt construction, no model identifiers, no Anthropic API calls, and no agent-output post-processing. If anything it reinforces agent-mode alignment — the invariant it maintains forbids raw .egg-state/agent-outputs/{_identifier}-… path literals in the prompt builders, keeping agent-facing paths derived-from-spec rather than hardcoded.
The green-gate core (slice_green_gate.py) — including the whole-line/substring signature split (_INFRA_LINE_SIGNATURES / _INFRA_SUBSTRING_SIGNATURES) that closed the self-masking hole, with the infra verdict field machine-consumed by run_slice_green_gate and the signature allowlist enforced in code rather than via prompt instructions — is unchanged from my prior approval. Unchanged verdict.
— Authored by egg
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Re-reviewed the delta since my last review at 5127ec2. The only PR-authored commit ahead is 9b419b1 "Sweep stale pipelines.py references in test_artifact_spec.py", touching only shared/egg_contracts/tests/test_artifact_spec.py (+15 / -11).
This is a pure documentation/naming cleanup with no production-code change and no behavioral change to the test logic. It resolves the one remaining open item from the prior review (stale pipelines.py references after the #3312 decomposition into the routes/pipelines/ package).
Verified:
PIPELINES_PATHalready pointed at theorchestrator/routes/pipelines/package directory (not in the diff) — the assertions were already scanning the package; only the surrounding prose was stale. The renametest_pipelines_py_is_readable→test_pipelines_package_is_readablecorrectly aligns the name with what it checks (.is_dir()+ non-empty*.pyglob).- The package exists and the old
pipelines.pymonolith is gone. - The invariant tests are not vacuous:
resolve_artifact_path(...)calls are present across_drafts.py,_populate.py, and_prompt_agent.py, sotest_every_agent_output_spec_has_resolve_callandtest_no_raw_agent_output_literals_remainexercise real package content. - The one remaining
pipelines.pymention (line 414) is an accurate historical decomposition comment, correct as written.
The earlier blocking concern (self-masking infra-signature hole) was resolved in 76dd454, which predates this review point, and the green-gate core (slice_green_gate.py, the _INFRA_LINE_SIGNATURES / _INFRA_SUBSTRING_SIGNATURES split, and the machine-consumed infra verdict) is untouched by this delta.
No blocking issues. Approving.
— Authored by egg
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
No agent-mode design concerns.
Re-reviewed the delta since my last approval at 9b419b1. The only change is the merge commit 871b562 bringing origin/main into the branch — there are no new PR-authored feature commits. git log 9b419b1..HEAD --not origin/main yields just that merge, and the merge's own conflict resolutions (combined diff) touch only two documentation files (docs/architecture/slice-dag.md, docs/development/STRUCTURE.md) — descriptive prose, no agent-mode surface.
The green-gate core is unchanged from my prior approval (git diff 9b419b1..HEAD -- orchestrator/slice_green_gate.py is empty): the whole-line/substring signature split (_INFRA_LINE_SIGNATURES / _INFRA_SUBSTRING_SIGNATURES) is still enforced in code rather than via prompt instructions, and the infra verdict remains machine-consumed by run_slice_green_gate. No LLM invocation, prompt construction, model identifiers, Anthropic API calls, or agent-output post-processing in this delta.
Unchanged verdict — approving.
— Authored by egg
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Re-reviewed the delta since my last approval at 9b419b1. The only change is the merge commit 871b562 bringing origin/main into the branch — there are no new PR-authored feature commits. git log 9b419b1..HEAD --not origin/main yields just that merge.
Verified unchanged / correct:
-
Green-gate core is untouched —
git diff 9b419b1..HEAD -- orchestrator/slice_green_gate.pyis empty. The whole-line/substring signature split (_INFRA_LINE_SIGNATURES/_INFRA_SUBSTRING_SIGNATURES) that closed the self-masking hole is still enforced in code, and theinfraverdict remains machine-consumed byrun_slice_green_gate. Both test files (test_slice_green_gate.py,test_artifact_spec.py) are also byte-identical to my last review. -
docs/architecture/slice-dag.mdconflict resolution is correct. The merge had to reconcile this PR's#3417clause with main's#3541change to the same sentence. The resolution preserved both: the fail-open clause now reads "fail-open on infra errors, including infra-signature-tagged reds inside check execution, #3417" (PR intent), and the base-resolution clause reads "root → latest completed chain tip, else the pipeline branch (#3541); child → parent's integration branch" (main intent). Neither side's meaning was lost. -
docs/development/STRUCTURE.md— the#3417line-113 edit was already present and reviewed at9b419b1; the remaining line differences against my last review point are pure offset shifts from main's additions elsewhere.
The earlier blocking concern (self-masking infra-signature hole) was resolved in 76dd454, which predates this review point. No new agent-facing surface, no code change, no behavioral change in this delta.
No blocking issues. Approving.
— Authored by egg
|
egg review completed. View run logs 20 previous review(s) hidden. |
Resolve slice_green_gate.py conflicts between #3409 (Stage A autofix) and #3417/#3518 (infra-induced red classification): - runner: keep the extracted run_cmd/fix loop and tag each first-run red with classify_infra, so the verdict carries both the fix sub-object and the infra field - job manifest: pass both EGG_GREEN_GATE_CHANGED_FILES_CAP and EGG_GREEN_GATE_INFRA_SIGNATURES - run_slice_green_gate: run the infra fail-open first, then judge autofix readiness and the failure message against the genuine (non-infra) reds; an infra red carries no fix that could clear it
Closes #3417.
What
The green gate's fail-open guarantee previously stopped at check execution: an infra fault inside a check (gateway hiccup on a git call, mid-run session-token expiry, OOM-killed worker, disk pressure) exits the check non-zero and surfaced as a definitive red that
onmode blocks on. This PR classifies a narrow set of infra signatures so those reds fail open instead, enabled by default.How
Runner-side tagging (the
_RUNNER_PROGRAMexecuted in the check-runner pod): each red check gets aninfrafield in the verdict, set when either_INFRA_OUTPUT_SIGNATURES, or-9/137), the OOM-killer shape; no test runner signals failure via SIGKILL.Tagging runs runner-side deliberately: only the runner sees the full output. The verdict carries a truncated 4000-char tail, and an early gateway error (e.g. the test selector's first git call) can scroll out of it; there's a test pinning that case. The signature list is passed via env from the orchestrator, so it stays a single orchestrator-owned constant.
Orchestrator-side decision (
run_slice_green_gate): a red verdict where every failed check is infra-tagged fails open with a loud warning. When genuine and infra-tagged reds mix, the gate blocks on the genuine reds only, and the infra-tagged check's name/output stay out of the cascade-routed failure message so nobody chases an infra ghost. A verdict whose checks carry noinfrakey at all (an in-flight pre-rollout runner) is treated as genuinely red; absence of the tag never fails open.The signature allowlist
Per the issue's security caveat, the list is a handful of exact strings emitted only by egg's own plumbing, never fuzzy patterns:
ERROR: GATEWAY_URL environment variable is not set.sandbox/scripts/git: env not wired into the runner podGATEWAY SIDECAR NOT AVAILABLEsandbox/scripts/git: wrapper's gateway health probe failedERROR: EGG_SESSION_TOKEN not set. Session required for gateway accesssandbox/scripts/git: session token missingAuthentication failed - check session tokensandbox/scripts/git: HTTP 401, mid-run token expiry/revocationNo space left on deviceKnown residual risk (called out in the module docstring and the constant's comment): the signatures are substring-matched against untrusted check output, so a check that prints one while genuinely failing fails itself open. That is inherent to any output-based classification; the mitigation is keeping the allowlist to exact egg-plumbing strings and never adding fuzzy patterns like
Killedorconnection refused.Config
EGG_SLICE_GREEN_GATE_INFRA_FAIL_OPENonoff/0/false/norestores strict every-red-blocks behavior; any other value resolves toonDefault-on per discussion: the whole point of #3417 is that
onmode should not block slices for infrastructure that is not their fault, and the classification only ever narrows what blocks; operators who want the strict gate opt out explicitly.Testing
21 new tests in
orchestrator/tests/test_slice_green_gate.py(83 total, all green):kill -9 $$) tagged, and signature-scrolled-out-of-tail still detected.Docs updated:
docs/architecture/slice-dag.mdenv table + gate description,docs/development/STRUCTURE.md.