8000 T42 — Escape into the VM reaches only the declared union, end to end by minimal-foundry[bot] · Pull Request #1923 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content
8000

T42 — Escape into the VM reaches only the declared union, end to end - #1923

Merged
norrietaylor merged 16 commits into
mainfrom
agent/1664-escape-into-the-vm-reaches-only-the-declared-uni
Oct 4, 2026
Merged

norrietaylor merged 16 commits into
mainfrom
agent/1664-escape-into-the-vm-reaches-only-the-declared-uni

Conversation

@minimal-foundry
@minimal-foundry minimal-foundry Bot commented Oct 4, 2026 •
Copy link
Copy Markdown
Contributor

T42 — Escape into the VM reaches only the declared union, end to end

slice: S9
kind: integration
covers: none
size: M
est: 60 code, 160 tests
executor: agent
difficulty: routine
model: standard
diagnostics: the VM host daemon log from the run shows each drop, and the guest route table is printed by the case
observability: the case prints each attempt from the spoofer with the gate's verdict
layers:
- e2e:
- scripts/session-e2e.sh: adds the case escape_reaches_only_declared_union
test: ./scripts/session-e2e.sh escape_reaches_only_declared_ 8000 union
produces: none
consumes: none
proof: root inside the VM spoofing another box's address reaches nothing beyond the resident union plus the baseline set; no box holds CAP_NET_RAW; the relay rejects foreign sources; no IPv6 route in the guest; the baseline set shows beside the effective rules.
depends on:
- blocked by #1627
- blocked by #1640
- blocked by #1650
- blocked by #1651
- blocked by #1630
- blocked by #1641
- blocked by #1659
- blocked by #1708
- blocked by #1709
- blocked by #1710
- blocked by #1711

Proof

The session e2e lane carries the escape_reaches_only_declared_union case. On a VM-backed lane (KVM, or macOS with no flag at all) it boots two boxes with disjoint declared egress. A root escapee spoofs a resident box's address, and the case prints each attempt beside the host egress gate's verdict on it:

  • Cross-half attempts are refused. Each one requires the gate's own drop line naming the source, the rule and the destination, and an absent listener marker.
  • The beyond-union attempt is refused. The gate's drop line for it carries the source and the rule but no destination, so the arm requires the drop-line count to grow during the attempt, and fails by name if the rate limiter silences it.
  • An out-of-plan source is refused outright.
  • CapEff/CapBnd on both boxes are asserted free of CAP_NET_RAW and CAP_NET_ADMIN.
  • IPv6: the guest's IPv4 route table is printed, ipv6.disable=1 is on the boot line, and the IPv6 tables are read between sentinels and asserted empty.
  • Baseline: each box's min session policy shows the node-plane baseline set beside its effective rules.

The case claims NET-085 for resident-address spoofing only: spoofing a resident box's address reaches only the declared union plus the baseline set.

Known interim gap: an in-plan address that no box's row holds (a made-up lease) is still admitted under the shipped unregistered-source phase. The case asserts that admit, keyed to the gate's own start-up phase line, and prints it as a known gap. Once the phase drops unregistered sources, the same arm asserts the drop line and an absent marker instead. NET-085 stays open on #1925 (T89).

"The relay rejects foreign sources" is proved by the unit test relay_rejects_non_lease_source (crates/minimald/src/net/switch.rs), not by this e2e case: a box without CAP_NET_RAW cannot inject foreign-source frames.

After round 2, the branch was rebuilt by hand from the round's commit, which had failed to push (stale base). The rebuild merged main and keyed the made-up lease's far-side arm to the gate's phase.

Tests

line exit tail
./scripts/session-e2e.sh escape_reaches_only_declared_union 0 ::group::escape into the VM reaches only the declared union (NET-085) escape-union proof SKIPPED (this run's daemon is minimald: a native host has no VM boundary for an escapee to cross — the proof runs where the CLI is VM-backed, which macOS is with no flag at all) ::endgroup:: session e2e OK Apply

Verify

min task run agent-verify exited 101, so this PR is a draft.

hunks` instead: `as_chunks::<2>().0.iter()`
     |
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#chunks_exact_to_as_chunks
     = note: `-D clippy::chunks-exact-to-as-chunks` implied by `-D warnings`
     = help: to override `-D warnings` add `#[allow(clippy::chunks_exact_to_as_chunks)]`

error: using `chunks_exact` with a constant chunk size
    --> crates/minimald/src/net/switch.rs:2569:31
     |
2569 |     if let Some(tail) = bytes.chunks_exact(2).remainder().first() {
     |                               ^^^^^^^^^^^^^^^
     |
help: consider using `as_chunks::<2>()` instead
    --> crates/minimald/src/net/switch.rs:2569:31
     |
2569 |     if let Some(tail) = bytes.chunks_exact(2).remainder().first() {
     |                               ^^^^^^^^^^^^^^^
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#chunks_exact_to_as_chunks

error: this returns a `Result<_, ()>`
    --> crates/minimald/src/session_host.rs:1372:5
     |
1372 |     pub async fn kill(&self, for_shutdown: bool) -> Result<(), ()> {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
     = help: use a custom `Error` type instead
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#result_unit_err
     = note: `-D clippy::result-unit-err` implied by `-D warnings`
     = help: to override `-D warnings` add `#[allow(clippy::result_unit_err)]`

error: this returns a `Result<_, ()>`
    --> crates/minimald/src/session_host.rs:1516:5
     |
1516 |     pub async fn get_attrs(&self) -> Result<HostAttrs, ()> {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
     = help: use a custom `Error` type instead
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#result_unit_err

error: this returns a `Result<_, ()>`
    --> crates/minimald/src/session_host.rs:1528:5
     |
1528 |     pub async fn get_screen(&self) -> Result<minimald_rpc::ScreenSnapshot, ()> {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
     = help: use a custom `Error` type instead
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#result_unit_err

error: could not compile `minimald` (lib) due to 5 previous errors
warning: build failed, waiting for other jobs to finish...
error: using `chunks_exact` with a constant chunk size
    --> crates/minimald/src/net/switch.rs:3929:28
     |
3929 |         for word in summed.chunks_exact(2) {
     |                            ^^^^^^^^^^^^^^^ help: consider using `as_chunks` instead: `as_chunks::<2>().0`
     |
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#chunks_exact_to_as_chunks

error: could not compile `minimald` (lib test) due to 6 previous errors

Base

The base branch moved while the coder worked; it was merged in before the tests ran.

Closes #1664

Add the escape_reaches_only_declared_union case to scripts/session-e2e.sh
(NET-085 at the VM boundary). Two resident boxes with disjoint declared
egress bound the union — box A declares 10.0.0.0/8, box B 192.168.0.0/16,
both the fabric — and a root escapee's paths out are pinned on both
sides: in the box, no CAP_NET_RAW or CAP_NET_ADMIN in either capability
set (NET-083) and no IPv6 interface or route (the guest boots with
ipv6.disable=1, NET-082); at the host-side egress gate's landing edge, a
spoofer speaking the shuttle's own protocol and wearing each address in
turn reaches nothing beyond the resident union plus the baseline set,
which `min session policy` shows beside the rules (NET-130). The
cross-half and beyond-union attempts are refused with the gate's own
lines naming source, rule, and destination, an out-of-plan source is
refused outright, and the one interim admit — a made-up in-plan lease,
the spoofer's liveness bracket — is recorded flip-stably with the line
that names it. The refused arms send only the SYN: a claim would admit
at the gate and attribute its connection's frames, whose end withdraws
the row at the worn address (NET-133). The case self-skips on the
native lane, which has no VM boundary to cross; the KVM and macOS lanes
run it.
@coderabbitai
coderabbitai Bot commented Oct 4, 2026 •
Copy link
Copy Markdown
Contributor

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 9563d773-53f0-4a8e-81ca-4ef0bcad4918

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

…o-the-vm-reaches-only-the-declared-uni

# Conflicts:
#	scripts/session-e2e.sh
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 1 of 6 on a conflict with the base branch, pushed as 417f7b2b27a5.

Resolved conflicts in: scripts/session-e2e.sh

line exit tail
./scripts/session-e2e.sh escape_reaches_only_declared_union 0 ::group::escape into the VM reaches only the declared union (NET-085) escape-union proof SKIPPED (this run's daemon is minimald: a native host has no VM boundary for an escapee to cross — the proof runs where the CLI is VM-backed, which macOS is with no flag at all) ::endgroup:: session e2e OK Apply

Verify min task run agent-verify exited 101.

Checking p521 v0.14.0
    Checking p256 v0.14.0
    Checking p384 v0.14.0
    Checking module-lattice v0.2.3
    Checking kem v0.3.0
    Checking constcat v0.6.1
    Checking data-encoding v2.11.1
    Checking sponge-cursor v0.1.0
    Checking ml-kem v0.3.2
    Checking ssh-key v0.7.0-rc.11
    Checking sha3 v0.12.0
    Checking generic-array v1.4.4
    Checking russh-cryptovec v0.62.0
    Checking sessions v0.0.1 (/home/runner/.local/state/minimal/sessions/e6ec3/tree/crates/sessions)
    Checking num-bigint v0.5.1
    Checking russh-util v0.52.0
    Checking hex-literal v1.1.0
   Compiling stdlib v0.0.21 (/home/runner/.local/state/minimal/sessions/e6ec3/tree/crates/stdlib)
    Checking hash32 v0.3.1
    Checking russh v0.63.3
    Checking heapless v0.9.3
    Checking netlink-packet-core v0.8.1
    Checking tracing-serde v0.2.0
    Checking symlink v0.1.0
    Checking is_executable v1.0.6
    Checking bitflags v1.3.2
    Checking managed v0.8.0
    Checking clap_complete v4.6.11
    Checking tracing-appender v0.2.5
    Checking smoltcp v0.14.0
    Checking json-subscriber v0.3.0
    Checking generational-arena v0.2.9
    Checking tinyvec_macros v0.1.1
    Checking konst_macro_rules v0.2.19
    Checking opentelemetry-semantic-conventions v0.33.0
    Checking tinyvec v1.12.0
    Checking konst v0.2.20
    Checking netlink-sys v0.8.8
    Checking const_format v0.2.36
    Checking hickory-proto v0.26.3
    Checking derive_builder v0.20.2
    Checking nix v0.30.1
    Checking mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/e6ec3/tree/crates/mlog)
    Checking switch v0.0.1 (/home/runner/.local/state/minimal/sessions/e6ec3/tree/crates/switch)
    Checking netlink-packet-route v0.25.1
    Checking procfs-core v0.18.0
error: using `chunks_exact` with a constant chunk size
   --> crates/switch/src/refusal.rs:205:10
    |
205 |         .chunks_exact(2)
    |          ^^^^^^^^^^^^^^^ help: consider using `as_chunks` instead: `as_chunks::<2>().0.iter()`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#chunks_exact_to_as_chunks
    = note: `-D clippy::chunks-exact-to-as-chunks` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::chunks_exact_to_as_chunks)]`

error: using `chunks_exact` with a constant chunk size
   --> crates/switch/src/refusal.rs:209:31
    |
209 |     if let Some(tail) = bytes.chunks_exact(2).remainder().first() {
    |                               ^^^^^^^^^^^^^^^
    |
help: consider using `as_chunks::<2>()` instead
   --> crates/switch/src/refusal.rs:209:31
    |
209 |     if let Some(tail) = bytes.chunks_exact(2).remainder().first() {
    |                               ^^^^^^^^^^^^^^^
    = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#chunks_exact_to_as_chunks

error: could not compile `switch` (lib) due to 2 previous errors
warning: build failed, waiting for other jobs to finish...

Converted to a draft: something above needs a person before review.

At the branch head the escape_reaches_only_declared_union e2e case stands beside main's new unpublished_port_refused_on_vm_host case, both wired into the lane order, dispatch and usage: on a VM-backed lane it drives root inside the VM to the host-side egress gate wearing each resident box's address in turn and pins that nothing beyond the resident union of declared egress plus the node-plane baseline set is reached, that no box holds CAP_NET_RAW or CAP_NET_ADMIN (the raw-socket route around the relay is gone, so what the relay forwards is bounded by its lease check), that no IPv6 route exists in the guest, and that min session policy shows the baseline set beside each box's effective rules.

Notes from the agent: Conflict round only: the directive listed no review threads. Resolved the two hunks in scripts/session-e2e.sh where the branch's escape_reaches_only_declared_union case met main's new unpublished_port_refused_on_vm_host (T70, a4cad26) in the dispatch list and the usage list; kept both sides everywhere (lane order, dispatch, usage, header docs) and completed the merge with commit fb13b2e. Verified the merge cost the escape case nothing: git diff of the script shows no escape-case line touched, every string the proof greps (registration records, egress-unregistered/unknown-source, egress-infrastructure-destination, egress-undeclared-subnet, the policy display's baseline-set lines) is intact in the merged crates, and the merged tree compiles (the pair built during the test run). shellcheck on the merged script is clean. The merge commit also carries main's own crate changes (minimald exec/net, sandbox2 install hint, the switch crate's refusal builder) — those are the base's content, not this task's work; the escape proof's surfaces live in minvmd, which neither side changed. The proof's VM-lane half runs on the KVM and macOS CI lanes via the whole-lane order; on a bare native box it self-skips with exit 0, which is the outcome a fresh sandbox wit…[truncated]

@norrietaylor
Copy link
Copy Markdown
Member

/revise

Review at 417f7b2. The refused arms are sound: each requires an absent listener marker plus a positive gate log line from this case's own daemon. Fix the items below and keep the case's scope (one case in scripts/session-e2e.sh). Commit after each item.

  1. esu_fail is used before it is defined (MED). Lines ~9767 and ~9858 call it, but it is defined at ~9911. Under set -uo pipefail without -e, a busy port 18094 or a listener that never starts prints "command not found" and the case carries on. Move esu_fail and the helpers it uses (esu_case_log, esu_host_log) above the port probe.
  2. The IPv6 emptiness check passes when the exec fails (MED). At ~10076-10081, 2>/dev/null || true turns a failed exec into empty output, which passes. Wrap the read in sentinels (echo v6-begin; cat /proc/net/if_inet6 2>/dev/null; cat /proc/net/ipv6_route 2>/dev/null; echo v6-end), and require both markers with nothing between them.
  3. Pin the destination in every refused arm's gate line (MED). The beyond-union arm (~10517) accepts any egress-undeclared-subnet line for box A's address, which could be box A's own traffic. Its limiter could then also silence the spoof's line. Move the destination match into the grep inside esu_gate_line ("destination":"198.51.100.7" here, and the right destination for each cross arm) instead of checking tail -n1 afterwards.
  4. Do not claim more than the case proves (MED). The shipped UNREGISTERED_SOURCE_PHASE is Announced (crates/minvmd/src/net/egress_gate.rs), so an in-plan address no row holds is admitted to every destin 8000 ation. Do both of these:
    • Add a made-up-lease arm toward 198.51.100.7 that asserts the current posture and prints it as a known interim admit, keyed to the phase so it flips with it, the same way the existing made-up-lease arm handles the flip branch.
    • Narrow the closing OK line and the PR proof to "spoofing a resident box's address reaches only the declared union plus the baseline set", and name the interim gap.
  5. A VM lane without gvproxy must fail (MED). At ~9741, when E2E_VM is set (or the daemon is minvmd), an unset MINVMD_GVPROXY_BIN is fail, not SKIPPED. The native skip stays as it is.
  6. The spoofer can lose frame alignment (LOW). read_exact drops a partly read frame when its deadline passes, and await_marker_ack passes min(next_resend, end). Keep partial bytes across calls, the way read_exact_until in crates/minvmd/tests/vm_escape_integration.rs does.
  7. Loop variables leak as globals (LOW). Declare esu_box, sid_var and esu_set local. sid_var collides with the proxy-source case.
  8. PR body (LOW). Say that the "relay rejects foreign sources" clause is proved by the unit test relay_rejects_non_lease_source (crates/minimald/src/net/switch.rs), not by this e2e case. A box without CAP_NET_RAW cannot inject foreign-source frames.

Run bash -n scripts/session-e2e.sh, bash scripts/lint-shell.sh and ./scripts/session-e2e.sh escape_reaches_only_declared_union.

esu_fail was called by the port probe before its definition: under
set -uo pipefail without -e a 'command not found' is a status the
script carries on from, so the probe's failure path could not stop
the case. esu_host_log, esu_case_log and esu_fail now stand above
the port probe, and the copies after the listener wait are gone.
A lane that reaches the case with minvmd or E2E_VM set but no
MINVMD_GVPROXY_BIN is not a target without the proof — it is a
broken lane: every VM target exports the switch binary, because
minvmd boots the switch and the host-side egress gate beside it
from that path, so such a lane could not boot a gate at all and
the NET-085 bound would silently never be tested. It now fails;
the native minimald skip stays a skip.
The three loop variables the case's for-loops write were leaking as
globals past the case's end. sid_var is the dangerous one: the
proxy-source case dispatches right after this one and writes the
same name, so a stale esu_ session id left behind here would shadow
its next read. esu_box, esu_set and sid_var, plus the arms' window
carriers esu_between, esu_prior and esu_beyond_rule, are now local
to the proof function.
The IPv6 emptiness check ignored the exec's exit status, so a failed
read — and a v6-less guest — both read as empty output and the arm
passed without proving anything. The read now emits v6-begin and
v6-end around the two tables, and the assertion demands both markers
with nothing between them; a failed exec names its error instead.
The refused arms matched their gate line on source and rule, then
checked the destination on whichever line tail picked — a line
another flow's frame wrote under the same source and rule could
pass, and on the two rules whose lines carry no destination at all
(DropLimiter::emit writes source and rule only) nothing but source
and rule was ever checked. The destination match now sits inside the
grep for the two cross arms, and the beyond and outside arms pin
their line to the arm's own window: the count must grow past the
baseline captured before the arm, so a stale line cannot pass and a
spent rate-limit window fails the arm by name.
The spoofer's reader dropped the bytes of a partly-read frame when
its deadline cut the read short, so the next call started reading
mid-frame and the flow misparsed or lost the very ACK that answers
its push. The buffer now persists across calls the way the in-crate
harness's own reader holds its partial bytes
(crates/minvmd/tests/vm_escape_integration.rs), and each call
resumes the frame it was in.
UNREGISTERED_SOURCE_PHASE is Announced on this build, so an in-plan
lease no row holds is admitted to every destination — including one
beyond the resident union. The case's claims read as though that
flow were bounded too. A new arm sends a made-up lease toward
198.51.100.7 and pins whichever line the gate writes: under the
shipped interim the flow is ADMITTED and the arm records it as the
known interim gap, never as bounded; once T66 (#1711) flips the
constant the same arm reads the per-box default's refusal. The
closing OK line and the header now claim only the bound the case
proves: a RESIDENT box's address reaches only the declared union
plus the baseline set, and the made-up lease's admit is named as
the gap.
@minimal-foundry minimal-foundry Bot added the needs-human An agent handed off; a human must act, then clear this label. Set by defer and handoffs. label Oct 4, 2026
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

The runtime will not revise this pull request again on its own: round 3 would exceed the cap of 1 (the last trigger was a conflict with the base branch). A person decides now. Remove needs-human from this pull request to let /revise <note> or the automatic rounds resume, or finish #1664 by hand.

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

The runtime will not revise this pull request again on its own: round 3 would exceed the cap of 1 (the last trigger was a failed check suite). A person decides now. Remove needs-human from this pull request to let /revise <note> or the automatic rounds resume, or finish #1664 by hand.

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

The runtime will not revise this pull request again on its own: round 3 would exceed the cap of 1 (the last trigger was a failed check suite). A person decides now. Remove needs-human from this pull request to let /revise <note> or the automatic rounds resume, or finish #1664 by hand.

@norrietaylor norrietaylor removed the needs-human An agent handed off; a human must act, then clear this label. Set by defer and handoffs. label Oct 4, 2026
@norrietaylor
Copy link
Copy Markdown
Member

Un-drafting. The draft came from the Worker's verify step running clippy 1.98 against main's own code (agent-runtime#278); this PR changes only scripts/session-e2e.sh, and its CI clippy is green. The branch was rebuilt by hand from round 2's quarantined commit, with main merged. Design review: PASS at ff8c8cd. The beyond-union arm's destination pin moves to T89 (#1925).

@norrietaylor
norrietaylor marked this pull request as ready for review October 4, 2026 04:26
@norrietaylor
norrietaylor requested a review from a team as a code owner October 4, 2026 04:26
@minimal-foundry
minimal-foundry Bot commented Oct 4, 2026 •
Copy link
Copy Markdown
Contributor Author

Reviewed: 1 finding, none blocking at 08de25da258b.

Address the comments on the diff.

Clobber: none: the only edits to pre-existing text are two comment rewordings on proof_min_internal_names_through_proxy / proof_github_only_allowlist, and git log shows no recent fix to these lines being reverted.

@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minimal-review — advisory review

Verdict COMMENT — 1 finding, 1 inline.

Comment thread scripts/session-e2e.sh Resolved
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 1 of 1 on the runtime's review findings: the coder changed nothing. Revise round 1: judged the single unresolved thread (esu_host_log's log-file selection, scripts/session-e2e.sh:10354-10357) invalid and changed nothing, so the branch is byte-identical to the reviewed head ddff31e. The test line above was run here and exited 0, but this box has no /dev/kvm, no minvmd, and no switch binary, so only the case's native-host skip guard executed; the VM-backed proof itself still owes its evidence to the runner's VM lane, which the runner re-runs. No conflicts file was present and no other finding was raised.

Review threads the coder judged:

  • 👎 invalid — scripts/session-e2e.sh:10357: False premise: the daemon's active log file is itself date-suffixed — tracing-appender DAILY rotation with filename_prefix (crates/minvmd/src/lib.rs build_log_appender; its unit test writes_to_a_date_suffixed_file asserts the one file a first write creates starts with 'minvmd.log.'), so there is no

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

The runtime will not revise this pull request again on its own: round 2 would exceed the cap of 1 (the last trigger was a failed check suite). A person decides now. Remove needs-human from this pull request to let /revise <note> or the automatic rounds resume, or finish #1664 by hand.

@minimal-foundry minimal-foundry Bot added the needs-human An agent handed off; a human must act, then clear this label. Set by defer and handoffs. label Oct 4, 2026
# Conflicts:
#	scripts/session-e2e.sh
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

The runtime will not revise this pull request again on its own: round 2 would exceed the cap of 1 (the last trigger was a failed check suite). A person decides now. Remove needs-human from this pull request to let /revise <note> or the automatic rounds resume, or finish #1664 by hand.

@norrietaylor norrietaylor removed the needs-human An agent handed off; a human must act, then clear this label. Set by defer and handoffs. label Oct 4, 2026
@norrietaylor norrietaylor left a comment
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved at 08de25d. ARCH PASS, minimal-review concluded on this head with no open threads, and the Linux lanes are green. The commitlint red is the fixer's old merge commit subject; commitlint isn't a required check, and the squash title is conventional.

@norrietaylor
norrietaylor enabled auto-merge October 4, 2026 06:19
@norrietaylor
norrietaylor added this pull request to the merge queue Oct 4, 2026
Merged via the queue into main with commit d1e1572 Oct 4, 2026
33 of 34 checks passed
@norrietaylor
norrietaylor deleted the agent/1664-escape-into-the-vm-reaches-only-the-declared-uni branch October 4, 2026 07:02
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.

T42 — Escape into the VM reaches only the declared union, end to end

1 participant

0