HOMELAB-2479: fix(sbx-gits): readiness probe on the GUEST's boot state + scratch off the tmp emptyDir (prepare only, HOLD for the clean point) #860
No reviewers
Labels
No labels
operator-review
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Homelab/infra-core!860
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "plane/HOMELAB-2479-sbx-gits-probe-tmp"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What this is
Two measured defects in
sbx-gits— the pod lied about the guest, and/tmpwas on theeviction path — fixed in ONE pod template:
core/manifests/apps/sbx-gits/statefulset.yaml,plus the image default that pairs with the second half
(
core/manifests/apps/sbx-fleet/Dockerfile), plus the epic harness re-pin(
scripts/gits-sbx-epic-test.sh) that seat 23693's round-2 verdict(
B1-860r2-harness-pin) proved mandatory: the manifest moved, the pins did not, and themerged tree handed every follow-up ticket a broken baseline. The harness commit is
ad45613a; the manifest/image commit is unchanged (f3776d4b).At the manifest layer, nothing outside
sbx-gitsmoves —sbx-labandsbx-sendrastrender BYTE-IDENTICAL (proof below, not a promise). The Dockerfile half does change the
sbx-fleetimage default consumed by other sandboxes — at their next digest bumponly; that exposure is itemized under "What this does NOT do" (it is how concern C-3
from seat 23693 is folded in: "nothing outside sbx-gits moves" was manifest-only true).
This PR prepares; it does not activate.
updateStrategyisOnDelete, so merging thisrolls nothing and changes nothing that runs. The probe and the env vars take effect at the
next clean-point pod recreation, which belongs to gits-hypervisor and needs a warning.
Until then
pod/sbx-gits-0keeps reading Ready with no probe on the guest, exactly astoday.
A. Readiness on the GUEST's state, never on the process
The defect, measured
pod/sbx-gits-0 .spec.containers[sandbox|emulator]carried no probe of any kind (theonly probe on the pod is the pre-existing
emu-healthsidecar'shttpGet /healthz, whichsays nothing about the guest). Consequence: the pod read 2/2 Ready 42 s BEFORE the guest
booted, so a crashed or half-booted emulator was indistinguishable from a healthy one and
"the emulator is degraded" could not be read from Kubernetes at all.
The probe
The
adbinvocation is not invented — it is the container's own, from step 4 of theemulatorcontainer's boot loop in the same file, and the smoke's convention is oneserial, auto-discovered, never aliased — so the probe uses the direct serial and no
adb connect/127.0.0.1:5555(a second registration of the same guest is what kills theinstrumented run, R136/R138).
tr -d '\r'because the guest answers CRLF. The probe runsas the container's own user (root) with the same writable
HOME, and it uses the pod'sexisting adb server — no second server, no extra listener (the "nothing new listens"
invariant in T7(h) still holds: the probe is a localhost serial, not a socket).
Readiness, not startup — one line
A startupProbe's budget expiring is a KILL (kubelet restarts the container), which is
exactly "kill the emulator because a guest is slow"; a readinessProbe has no kill path —
it can only report the truth — and the container's own loop already owns boot failure with a
900 s deadline and its own
EMU-DIED/EMU-TIMEOUTexits.Timing arithmetic (measured, not estimated)
Cold boots of this container, from its own log:
EMU-READY ... after 76sand, previouscontainer,
94s.against a guest that has not had its chance to start;
NotReady;
timeoutSeconds: 10is above a healthygetpropround trip; a hungadbis a failedattempt, never a hung probe.
Concern C-2 (seat 23693), folded in honestly: the probe's steady-state load is
unmeasured. The exec is one
adb getpropper 15 s against the container's own adb server —by construction cheap — but no wall-clock sampling of probe latency under a running
instrumented suite exists yet. That measurement belongs to the post-recreation verification
wave (same wave that proves the first real
sys.boot_completedunder the probe); it is nota reason to hold a prepare-only PR that rolls nothing.
No liveness probe — argued, not implemented
A liveness probe here would restart the emulator whenever the guest is slow — not
implemented, on purpose.
B.
/tmpoff the eviction pathThe defect, measured
kubectl_evictionsonprod-wk-01has exactly one sample in seven days: the 12:20Zrun, when this pod's
tmpemptyDir hit its 4Gi limit.TMPDIRis set nowhere today —not in the pod env, not in the manifest, and not in the image — and the JVM ignores
TMPDIRentirely, so aTMPDIR-only fix would silently do nothing for theGradle/Kotlin daemons.
The change
tmpemptyDir4Gi → 8Gi.JAVA_TOOL_OPTIONS=-Djava.io.tmpdir=/home/ld/work/tmpadded to thesandboxcontainer env. This is the ONLY env var this PR adds. The rebased head deliberately
does not redeclare
TMPDIR: it already lives at the HOMELAB-2682 site(
/home/ld/work/seat-transcripts, declared above in the same env) and a second entrywould shadow it — the harness's own parse resolves the LAST value and test 28 goes red.
Seats' transcripts keep going to
seat-transcripts; Gradle/Kotlin scratch goes towork/tmp; both are on the persistent hostPath, neither is on thetmpemptyDir.ENVincore/manifests/apps/sbx-fleet/Dockerfileimmediatelybefore
USER 1000:1000, so the default is carried by the image for consumers whosedigest is built after this layer.
ENVis pod-scoped in Docker, not user-scoped — it isplaced before
USERbecause that is where this file declares the runtime user. Themanifest repeats
JAVA_TOOL_OPTIONSbecause the pinned digest(
sbx-fleet:6fa150a@sha256:0ee5e97e…) predates this layer./home/ld/work/tmpis created by theprepare-workinitContainer(
install -d -m 0755 -o 1000 -g 1000 "$DST/tmp") — as root, idempotently, handed to1000:1000, because the hostPath arrivesroot:root 0755and the runtime user is 1000.Not created in the image: a
RUN mkdirunder/home/ld/workis covered by the mountedvolume, and the runtime rootfs is read-only.
Why this is the real fix and the 8Gi only the buffer:
workis a hostPath on prod-wk-01'sown data disk (~425 GiB free) and sits outside kubelet's ephemeral accounting entirely.
Scratch written there cannot evict any pod. It also means a crash DB is no longer destroyed
with the container.
Why 8Gi and not 16Gi
The pod's ephemeral ceilings are already 20.06 GiB —
tmp4 +avd16 +herdr-sock64Mi — against 31.0 GiB free on prod-wk-01. 8Gi → 24.06 GiB, still under 31.0 and
under the
sandboxcontainer's ownephemeral-storagelimit (10Gi), so the failure stayswhere it belongs: this pod's own clean eviction. 16Gi → 32.06 GiB, above the
free space: the same overflow would instead trigger a node-level
DiskPressureevictionhitting every pod on prod-wk-01.
avd(16Gi) andherdr-sock(64Mi) are untouched.Render proof — repo path, current head
Render path:
kubectl kustomize <dir>— the same render ArgoCD performs for the threeApplications.
before=origin/live@1c5111d1(this branch's base, current tip);after= this branch @ad45613a.Byte-identical check for the other two sandboxes (the hard gate)
sbx-gits:4b0fa50cd88e2ed9…→ba858643337c6429…, 1279115 → 1280544 B.Complete semantic delta (
sbx-gitsonly)Every added/changed leaf, nothing summarised away:
No other object in the directory changed, and no other leaf of any object changed. The
pre-existing
emu-healthsidecar (itshttpGet /healthzprobe included) is byte-untouched— the harness re-pin below pins it as untouchable.
The harness re-pin (
scripts/gits-sbx-epic-test.sh, commitad45613a)Seat 23693 round-2 (
B1-860r2-harness-pin): the branch updated the manifest withoutupdating the harness pins — 7 new harness failures on the merged tree
(
env-anadidos: JAVA_TOOL_OPTIONSat the spine gate + 4 mutation knock-ons +T7[diff]/17[R159](d)×2). Precedent HOMELAB-2682 updated pins in the SAME change; thishead does too, same pattern, smallest diff:
d_extra_env'TMPDIR'→'TMPDIR JAVA_TOOL_OPTIONS'— the name isallowed at the gate, the VALUE stays pinned by the manifest (same division as
HOMELAB-2682).
SBX_GITS_DIFF_SETre-measured against the current tip: the ok0 entry(
M tools/gits-sbx-emu-proof.sh) leaves the set — live already absorbs it — so thebranch diff is exactly
M statefulset.yaml. The set is re-measured against thecurrent tip, never dragged along.
p1_spinenow stripsreadinessProbe(with the mapping rule — a probe body is sibling keys, not list items)and a new
p1_rppins the exact block set by sha256: by default no probe may move(the pre-existing
emu-healthhttpGet probe is untouchable), and the wave thatlegitimately adds one passes its
p1_rphash explicitly — #860 pins the emulator'ssys.boot_completedprobe. Without this the probe would tripspine-distintoforever,and any hand-added probe anywhere would have been invisible to the old spine.
T7[diff]/17R159 pass messages re-measured to name the #860 set.
Verification (both trees, same machine): live tree (
origin/live@1c5111d1, with there-pinned harness): 13 failed / 693 checks — every failure environmental to that runner
(kubectl-version render deltas,
/home/ldperms), none from this diff. Merged tree(
ad45613a): the identical 13 failures, zero new — the 7 seat-23693 failures all pass:H8[8][sbx-gits]green,[mut]baseline green with all 10 mutation rows biting,T7[diff]/17[R159](d)green with their entry-removal mutations red. Test 28 (TMPDIRtranscripts): 8/8 PASS on this head.
Validation
kubectl kustomizerc=0 for all three sandboxes. Against the live cluster, without writinganything (previous head; content of these files unchanged by the rebase):
statefulset/sbx-gits .metadata.generationunchanged (21) across the dry-run; the live podwas not touched (
creationTimestamp2026-11-05T18:15:39Z, emulatorrestartCount2,before and after). No pod was deleted, scaled, edited or recreated by this work.
What this does NOT do, and what has to happen for it to be testable
updateStrategy: OnDelete). The probe's effect is testableonly after a clean-point recreation — gits-hypervisor's, with a warning, per the
order. That recreation is also when the harness's official run closes the ticket.
sbx-fleetENVlayer reaches no pod until afuture image build AND digest bump.
23722. The image
ENV TMPDIR=/home/ld/work/tmp(+JAVA_TOOL_OPTIONS) can reach onlycontainers running the
sbx-fleetimage at their next digest bump — and measured onthis head, that hazard set is empty. The pod's
emulatorcontainer is NOT an sbx-fleetconsumer: it pins
android-emulator:230756a(platform/android-emulator/Dockerfile,FROM debian:bookworm-slim), so the ENV layer can never touch it and test 28's "emulatorstays on ephemeral /tmp" stands. The four containers here that DO run the
sbx-fleetimage —
prepare-work,seed-pi-agent,sandbox,emu-health— all mount/home/ld/work, so the ENV values always name a real path for them. Neutralizing nowwould mean re-touching the manifest for zero immediate benefit: nothing rolls before the
clean-point recreation, and the pod pins digest
6fa150a, which predates the ENV layerentirely — no running container can see this value between merge and bump. The
deferral decision itself is unchanged (OnDelete; digest
6fa150apredates the layer;nothing rolls before the recreation) — seat 23722 corrected only the reasoning map, and
the one real bump-gated follow-up is itemized below.
sbx-fleetconsumers (deliberately not touched here):sbx-lab— the ONLY bump-gated follow-up (verified by seat 23722): it pinssbx-fleet:fc933fb, so it inherits the image ENV at its next digest bump, and itsprepare-workcreates no$DST/tmp(zero hits in its script). Its bump follow-upMUST therefore create that directory (or override
TMPDIR/JAVA_TOOL_OPTIONSpercontainer) in the same change, before any pod built from the new digest rolls; its
manifest should also override
TMPDIRback to its own transcripts path if it wantsone.
sbx-sendrastis NOT a consumer (seat 23722, correcting this body's prior claim):sbx-sendrast/Dockerfile:26-27isFROM ${LD_FLEET_IMAGE}(ld-fleet:go-89b900d) —a different image line from
sbx-fleet— so the image ENV, and the/home/jesus/worknote built on the wrong premise, do not apply. No follow-up owed there.
install -dstep added to the initContainer args is paired with a comment; thecomment documents intent, it is not an enforced contract.
p1_spinehas a latent env-block-termination path in its parsing; it fails closed(an unterminated env block is treated as a mismatch), never open.
p1_rppins the probe block but normalizes away the owning container — a probe movedbetween containers at the same manifest path would keep the same hash.
prod
sandbox-telemetry's static target is that Service name — a guest hung >15 minwould trip
SandboxEmulatorTelemetryAbsent. That is the probe reporting the truth,but the alert's static target predates the probe and is a known sharp edge for the
recreation wave.
sys.boot_completedunder it (cannot be done without recreating the pod, which this PRmust not do); the probe's steady-state load under a running suite (C-2, above); whether
the next cold boot stays under the 105 s budget on a fresh
/avd.Written from primary sources: the manifest, the container's own log,
application/sbx-gits,node/prod-wk-01, andkubectl kustomize. No credential was printed, hashed or placed inargv; the branch was pushed as
pi-landingafter assertingGET /api/v1/user.Verdict: REQUEST_CHANGES
model: omniroute/deepseek-flash
head:
a619bdb89fsession: ses-860r1-seat1-3f9a1c
Fresh-context review of the exact head
a619bdb8, standing on my own reads: the PR diff atthat head, both changed files in full,
origin/live(fc933fb3) as it stands while I read, therender path the repo uses (
kubectl kustomize core/manifests/apps/sbx-gits), and the repo's ownepic harness
scripts/gits-sbx-epic-test.sh. I merged the head intolivein a throwaway cloneand reviewed the merged tree, because that is the tree that runs after landing.
Scope is what it claims: 2 files, +121/−1 —
core/manifests/apps/sbx-fleet/Dockerfile(+26) andcore/manifests/apps/sbx-gits/statefulset.yaml(+95/−1). No other file moves. No secret, no RBAC,no NetworkPolicy, no egress and no identity material is touched: the identity Secret block near
the insertion is untouched context only.
updateStrategy: OnDelete, so the merge itself rollsnothing;
kubectl kustomizereturns rc=0 on both the head and the merged tree, and the mergedYAML parses with no duplicate mapping key. CI is green at this head (runs 3198/3199/3200, every
job passing,
build-and-pushincluded). The head is stilla619bdb8, the PR is open, mergeable,and it has no earlier review to inherit.
What is right, and what I checked rather than took on faith: the probe is the container's own call
with the container's own selector —
adb -s emulator-5554 shell getprop sys.boot_completedisbyte-for-byte the boot loop's step-4 line,
tr -d '\r'included, with noadb connectand no127.0.0.1:5555; theemulatorcontainer runsrunAsUser: 0withreadOnlyRootFilesystem: false, so the probe runs in the same environment the boot loop already uses, andadbauto-starts its server from any client command. Readiness rather than startup is the right
instrument here, and the arithmetic (first probe at 60s, inside a 76s fastest measured boot;
3×15s to NotReady; no kill path) holds against the two
EMU-READYsamples quoted. In the mergedtree the probe lands on
containers[2] name=emulator, not on theemu-healthsidecar that liveadded afterwards, and no container ends up with two probe blocks.
tmp4Gi→8Gi withavdandherdr-sockuntouched keeps this pod's own clean eviction as the failure mode instead of anode-level
DiskPressure, andprepare-workgets theinstall -d … -o 1000 -g 1000 "$DST/tmp"line that makes its own target exist.
The finding in full
The premise the change is built on is no longer true of its base.
livealready declaresTMPDIR=/home/ld/work/seat-transcriptsin thesandboxenv — the HOMELAB-2682 block, landed as93bbb5d4after this branch was cut from74724767— documented in that directory's README§5.2, and asserted by
scripts/gits-sbx-epic-test.shtest 28. This branch still states, in themanifest comment it adds, that
TMPDIR"is set NOWHERE today (not here, not in the image, not inthe manifest)": false against
fc933fb3.Because the branch predates that line and touches a nearby region, git auto-merges cleanly and
the result carries both entries, at env indices 4 and 26 of
containers[sandbox]:Kubernetes accepts that object — no validation error,
kubectl kustomizerc=0, no duplicate YAMLkey — so nothing stops it, and the precedence is then runtime-defined: Docker's env builder keeps
the last value, a
getenvreader over a runc-built env sees the first. I did not have to settlethat ambiguity to file this. I ran the harness's own parse over the merged tree — a dict built
name→value in list order, the same code test 28 uses — and
TMPDIRresolves to/home/ld/work/tmp, sogoes red. That is the lane check in the bundle's own step 9, firing against the tree this PR
would leave behind. The same ambiguity reaches the reason the bundle exists: if
/home/ld/work/tmpis what the sandbox honours, seat transcripts stop landing inhome/ld/work/seat-transcripts— the directory the recreate's snapshot step copies — and README§5.2 becomes wrong. Either way one of the two intents is silently defeated, and the merged
manifest carries a comment asserting the opposite of what
livedoes.The fix is small and gives up none of the PR's real gain: rebase onto
live, DELETE theTMPDIRentry this PR adds to the
sandboxenv (live'sseat-transcriptsalready moves non-JVM scratchoff the
tmpemptyDir, which is the stated goal), keepJAVA_TOOL_OPTIONS=-Djava.io.tmpdir=/home/ld/work/tmp— the genuinely new half, and the oneconsumer
TMPDIRnever reached — keep the 8Gi and the probe, and rewrite the env comment so it nolonger claims
TMPDIRis unset. That needs a fresh seat on the new head, per the bundle's order.Blockers
BLOCKER B1: landing this head on today's live leaves two conflicting TMPDIR entries in the sandbox container and reddens the repo's own epic test 28 — evidence and the one-line fix are in the section above.
Concerns
These do not hold the PR on their own; they are for the next head or for the record.
C-1 —
sbx-sendrastinherits the new imageENVand has nowhere for it to point. Thesbx-fleetimage is shared, and the Dockerfile sets
ENV TMPDIR=/home/ld/work/tmp.sbx-labmountsworkat
/home/ld/work(only itstmpsubdirectory is missing today) and the PR records that as aknown follow-up.
sbx-sendrasthas no/home/ld/workmount at all — it mounts/home/jesus,/tmp,/opt/sbx-*— so a digest bump there would pointTMPDIRat a path that does not exist,and temp-file consumers that trust
TMPDIR(os.TempDir,tempfile,mkstemp) fail rather thanfall back to
/tmp. Nothing activates today (this PR bumps no digest, and the build workflowpublishes a tag without touching the pins), which is why this is a concern and not a hold — but
the PR's known-follow-up list names
sbx-labonly, and the brief's own instruction is to leavesbx-sendrastalone. One sentence in the Dockerfile comment would keep that trap from beingstepped on by whoever bumps that pin.
C-2 — the probe's steady state has not been measured under a suite. A
readinessProberuns forthe pod's whole life, so
adb shell getpropexecutes every 15s indefinitely, including while aninstrumented run holds the guest. The timing argument covers boot only, and the PR's own
"not verified" list (the fresh
/avdboot, the real recreation) does not mention load. Low risk,cheap to settle: one suite run with the probe live, or a
periodSecondsthat backs off once theguest is up.
C-3 — the headline "Nothing outside
sbx-gitsmoves" is only true of the manifests. TheDockerfile half does move the two other sandboxes' behaviour at their next digest bump (C-1). The
body qualifies this later, but the headline is the sentence a future reader will quote.
What I did not ask for
The recreate is gits-hypervisor's, not this PR's;
#860preparing without activating is the rightshape for a bundle that lands it first and recreates later. Landing it first is safe once B1 is
fixed: the merged tree then rolls nothing (OnDelete), adds no image pin, and the Dockerfile change
only publishes a tag through the existing path-filtered build, which I read in
.forgejo/workflows/sbx-fleet-image-build.yaml— that job asserts the base ARG pin, which themerge keeps from
live(go-234dfd0@sha256:a5763f7e…), so it stays green.Reviewed read-only: no edit, no push, no merge. Identity asserted as
ld-reviewerbefore thewrite; the credential was held in a 0600 file under a 0700 directory, never in argv and never
printed, and the operator's ambient token was not used.
a619bdb89ff3776d4b31Fix worker update — blocker B1 (seat 1947) resolved on new head
f3776d4b.Branch was 4 days stale: live (
1c5111d1, carryingfc933fb3/HOMELAB-2682) already declaresTMPDIR=/home/ld/work/seat-transcriptsin the sandbox env, so the PR's ownTMPDIR=/home/ld/work/tmpentry would have left TWO TMPDIR entries after auto-merge — the harness's own parse resolves the last one and epic test 28 (the step-9 lane check) would go red, and seat transcripts would stop landing where the recreate's snapshot copies them.Changed on the rebased head: (1) rebased onto
origin/live(1c5111d1); (2) dropped the manifestTMPDIRentry entirely — live'sseat-transcriptsvalue already does the job and is untouched; (3) keptJAVA_TOOL_OPTIONS=-Djava.io.tmpdir=/home/ld/work/tmp(the genuinely new half — the JVM ignores TMPDIR), thetmpemptyDir 4Gi→8Gi, theprepare-work$DST/tmpcreation, and the guest-boot readiness probe; (4) rewrote the stale "TMPDIR is set NOWHERE" comment (also in thesbx-fleetDockerfile) — it now states TMPDIR lives at the HOMELAB-2682 site and why a second entry must not exist; (5) verified on the rebased tree: the harness's own test-28 parse resolves sandboxTMPDIR->/home/ld/work/seat-transcripts, emulator -> empty, test 28 standalone PASS;kubectl kustomizerc=0 forsbx-gits,sbx-lab,sbx-sendrast.Seat 1947's non-blocking concerns, folded into the follow-up list:
sbx-sendrastinherits imageENV TMPDIR=/home/ld/work/tmpon a future digest bump but has no/home/ld/workmount — its bump prep must namesbx-sendrastalongsidesbx-labbefore anysbx-fleetrollout reaches it.adb shell getpropevery 15 s for pod life) is unmeasured — on the "not verified" list until observed on a recreated pod.sbx-gitsmoves" is true of the manifests only; the image default reachessbx-lab/sbx-sendraston their next digest bump (by design, flagged above).Not merging — landing waits for a fresh review seat on this exact head (
f3776d4b).Verdict: REQUEST_CHANGES
commit:
f3776d4b31model: omniroute/deepseek-flash
session: 01a1be68-4216-777b-86ff-4bae620aac82
Fresh-context review of the exact head
f3776d4b31b1285854c06974feb6f71d9c6ae48e(base1c5111d13d288e785492ad7874bce485e0bae6dc= the currentorigin/livetip, confirmed: the headis a descendant of live, so the merged tree is the head's tree — I still merged head into base in
a throwaway worktree and both trees hash to
b63dec94b7211935980c09f5efc6135796f5084d, i.e. therebase is clean and the merge is a fast-forward in effect). I did not read the prior seat's review
before forming a verdict; sources are the diff at this head, both changed files in the merged tree,
a PyYAML parse of the merged manifest,
origin/live, and the repo's own harnessscripts/gits-sbx-epic-test.shrun on both trees.The prior blocker B1 (seat #1947) — CLOSED, verified on the merged tree
TMPDIRin the merged manifest's pod spec:containers[name=sandbox].env→
/home/ld/work/seat-transcripts(live's HOMELAB-2682 value, untouched). The PR's own/home/ld/work/tmpmanifest entry is gone.emu-healthandemulatordeclare noTMPDIR, as test 28 requires.t2682t_env, last-wins dict) resolvesTMPDIRtoseat-transcripts,emulatorto empty, and the full test-28 block PASSES on the mergedtree — including its
java.io.tmpdirtextual guard, which this PR satisfies because the newcomment block names
java.io.tmpdir.JAVA_TOOL_OPTIONS=-Djava.io.tmpdir=/home/ld/work/tmponsandbox,tmpemptyDir4Gi → 8Gi, theprepare-workinstall -d … "$DST/tmp"line, and thereadinessProbeon the guest'ssys.boot_completed.The new blocker — the merged tree turns the epic's own gate red in 7 places
I ran
scripts/gits-sbx-epic-test.shon the merged tree and onorigin/live(both withGITS_SBX_ROOTset to the tree under test, same machine, same env):origin/live1c5111d1(baseline)f3776d4bThe baseline is already red for pre-existing reasons (image-digest pins, memory 6Gi/10Gi,
/home/ldunwritable for the H11 seed,loader-skills=0— all unrelated to this diff), so Ijudge the delta, not the absolute exit code. The delta is exactly 7 new failures, none
disappearing, every one traceable to this diff:
Two root causes, both mechanical:
p1_added_bad … want_env→env-anadidos: JAVA_TOOL_OPTIONS). Five of the seven rows are this one red plus its knock-on:the mutation baseline is already red, so the three "env var no permitida" mutations can no
longer fail for the right reason. The gate is not cosmetically red — its negative proof stops
proving anything until the allowlist names the new variable.
T7[diff]pins the expected change-set ofsbx-gitstoSBX_GITS_DIFF_SET, which todayis the HOMELAB-2682 set (
M tools/gits-sbx-emu-proof.sh).M statefulset.yamlthereforefails it in two independent sections.
This is not the harness being unreasonable, and it is not a reason to weaken it: the repo's own
precedent is that the PR that changes the manifest updates the pin in the same change —
HOMELAB-2682 added its
TMPDIRand carried both thed_extra_env='TMPDIR'allowlist line andits rewritten expected diff-set. This PR changes the pod template's env, the probe, an
initContainer script and a volume cap, and touches
scripts/gits-sbx-epic-test.shnot at all(
0files of the harness in the diff).Why it blocks rather than nags, given the bundle order that lands this FIRST: the epic's
coherence command is the one thing the orchestrator runs after every wave. Landing this first
hands every follow-up ticket a baseline that is red for reasons introduced here, and the next
worker either debugs someone else's env leaf or starts ignoring the harness — the failure mode the
gate exists to prevent. The fix is small and belongs here: extend the spine's
want_envallowlistwith
JAVA_TOOL_OPTIONS(and, if the intent is to keep it, say so in the message the gate prints)and rewrite
SBX_GITS_DIFF_SETto this change's real set, the way HOMELAB-2682 did.What holds up on the rest (spot-checked, not taken on faith)
readinessProbelands oncontainers[name=emulator](
containers[2]ofsandbox,emu-health,emulator), not on live's lateremu-healthsidecar; no container carries two probe blocks. The exec is the boot loop's own
adb -s emulator-5554 shell getprop sys.boot_completed … | tr -d '\r', noadb connect, no127.0.0.1:5555. Readiness-not-startup (no kill path) and the 60 s + 3×15 s = 105 s arithmeticagainst the two measured
EMU-READYsamples (76 s, 94 s) are internally consistent and I foundno arithmetic error.
env block near the insertion is untouched context only, and the diff carries no credential
value.
kubectl kustomizerc=0 for the merged tree, the merged YAML parses withPyYAML with no duplicate mapping key,
updateStrategy: OnDelete(the merge rolls nothing, the8Gi is a cap not a request),
avdandherdr-sockuntouched.ENVlayer only reaches a pod after a future image build and adigest bump; this PR publishes a tag without moving any pin — correct and correctly stated.
Concerns (non-blocking, for the fix worker and the follow-up list)
ENV TMPDIR=/home/ld/work/tmpalso lands on theemulatorcontainer on the next
sbx-fleetdigest bump, and I verified that container has no/home/ld/workmount (work-mounted=False; onlysandboxandemu-healthmount it). So thebump would point
adb's temp files at a path that does not exist in that container — and itcontradicts test 28's stance that the emulator's ephemeral
/tmpis the correct one. TheDockerfile comment argues the
ENVis a default "for any consumer of this image"; the consumerlist did not include the one container that lives in the same pod as the change. Scope it or
mount it, before the bump.
inheriting the image
ENVwith no/home/ld/workmount; steady-stateadb getpropload every15 s unmeasured; "nothing outside
sbx-gitsmoves" true of manifests only). They are mentioned,but they live in the thread, not the body — and the body is now stale against its own head:
it still advertises
TMPDIR=/home/ld/work/tmp"in thesandboxcontainer env" andenv indices
[25]/[26]in the flattened leaf diff. On this head the manifest adds noTMPDIRat all. Please reconcile the body, because the next reader will look for the entry that was
deliberately removed.
a cold boot on a fresh
/avdagainst the 105 s budget. I could not check those without therecreation this PR must not perform.
Blockers
BLOCKER B1-860r2-harness-pin: the merged tree runs
scripts/gits-sbx-epic-test.shwith 7 newfailures versus
origin/live(H8[8][sbx-gits]spineenv-anadidos: JAVA_TOOL_OPTIONSand itsfive knock-ons, plus
T7[diff]twice onM statefulset.yaml), and the PR carries no update toSBX_GITS_DIFF_SETor the spine allowlist. Extend both in this change, the way HOMELAB-2682 didfor
TMPDIR, and re-run the harness on the merged tree: the delta must be zero new failures.Everything the prior seat blocked on (B1, the duplicated
TMPDIR) is resolved and verified;test 28 is green on the merged tree.
commit:
ad45613a77model: omniroute/deepseek-flash
session: 01a1be9e-7ab1-777b-86ff-4bc71d8ab160
Verdict: REQUEST_CHANGES
Fresh seat, no prior-round context inherited. I reviewed the current head independently: the diff itself, the re-pinned harness, and my own re-run of the harness on a merged tree. The code is verified clean — the single blocker is in the PR body's consumer map, which is a durable instruction a follow-up worker will act on. Body-only fix, no code change, so the next round is a read, not a re-run.
Blockers
BLOCKER B1-860r3-consumer-map: the C-1 paragraph's blast-radius map is factually wrong in two ways, and it is the record that gates the deferred digest-bump work ("the digest-bump follow-up MUST land, in the same change, (i) … (ii) …").
sbx-gits'emulatorcontainer is not ansbx-fleetconsumer. The body says "the imageENV TMPDIR=/home/ld/work/tmp(+JAVA_TOOL_OPTIONS) would reach theemulatorcontainer at the nextsbx-fleetdigest bump — which mounts no/home/ld/work". The emulator container pinsplatform/android-emulator:230756a@sha256:66430940…(core/manifests/apps/sbx-gits/statefulset.yaml:1204), built by.forgejo/workflows/emu-images.yamlfromplatform/android-emulator/Dockerfile, whose base isFROM debian:bookworm-slim@sha256:734a72b3…(line 19) — nosbx-fleetlayer anywhere in its lineage. Thesbx-fleetcontainers in this StatefulSet are exactlyprepare-work,seed-pi-agent,sandbox,emu-health, and all four mount/home/ld/work. So the C-1 hazard as written does not exist, and "contradicting test 28's 'emulator stays on ephemeral /tmp'" describes a contradiction the ENV layer cannot produce.sbx-sendrastis not ansbx-fleetconsumer either. The body says it "inherits the image ENV at its next bump" and that "its bump must override both variables per container".core/manifests/apps/sbx-sendrast/Dockerfile:26-27isARG LD_FLEET_IMAGE=…/ld-target/ld-fleet:go-89b900d/FROM ${LD_FLEET_IMAGE}— it derives fromld-fleetdirectly, not fromsbx-fleet, so this layer never reaches it, and/home/jesus/workin that manifest is irrelevant to this ENV.sbx-fleetconsumer outsidesbx-gitsissbx-lab(sbx-fleet:fc933fb@sha256:be81ce63…), and its work preparation creates no$DST/tmp— I grepped itsstatefulset.yamlforDST/tmpand/home/ld/work/tmp: zero hits. At its next digest bump it would inheritTMPDIR=/home/ld/work/tmppointing at a directory nothing creates. The body mentions this ("itsprepare-workmust createtmp") but as one of three items inside a wrong list, which is how a correct item gets skipped.Required correction (prose only, in this order):
sbx-gits' emulator runsandroid-emulator(FROMdebian:bookworm-slim), so nosbx-fleetENVcan reach it, and everysbx-fleetcontainer insbx-gitsmounts/home/ld/workand gets itstmpfrom theprepare-workline this PR adds — i.e.sbx-gitsis fully self-consistent at bump.sbx-sendrastitem (not a consumer;/home/jesus/workis a different image lineage).sbx-labinherits theENVat its next digest bump and must landinstall -d -m 0755 -o 1000 -g 1000 "$DST/tmp"(or a per-container override) in that same change, since its prep creates nothing there today.Why this is a blocker rather than a concern: claim honesty is a review dimension here, and this paragraph is not a description — it is a MUST-do instruction for a future worker that names a non-consumer and the wrong image. Merged as-is it would produce a phantom emulator override and an unnecessary
sbx-sendrastedit while the one real consumer stays a bullet in a wrong list. The deferral decision is safe (see below); the reasoning recorded for it is not.What I verified clean (do not re-litigate)
git diff --name-status 1c5111d1..ad45613a=M core/manifests/apps/sbx-gits/statefulset.yaml,M core/manifests/apps/sbx-fleet/Dockerfile,M scripts/gits-sbx-epic-test.sh.SBX_GITS_DIFF_SETis exactlyM\tstatefulset.yaml— re-measured correctly; the sandbox directory changes in exactly that one file.sbx-lab,sbx-sendrast): confirmed by the harness on my own run —PASS 20[guard] el render de sbx-sendrast es BYTE-IDENTICO … sha256 d225759f534949db6…(matches the body),PASSforsbx-lab, andgit diffagainstorigin/livetouches no file insbx-sendrast.p1_rppin: I recomputed it independently fromkubectl kustomize core/manifests/apps/sbx-gits | sts_doc | p1_rpon this head and got0ce65f6ed11b3b45f5cb56f2f58f76a40ba5404b2adb0a8e1c07dfd2b37bc602, exactly the constant the harness carries. It covers both blocks (the pre-existingemu-healthhttpGetand the newemulatorexec probe), normalized to the block body.emu-healthcorrectly: withextra_rpempty (sbx-lab) the rule isrp(now) == rp(ref), so an existing probe that does not move is fine and any second probe is red; withextra_rpset the wave must publish the hash of its whole block set, so a change is visible in the harness diff — the same division of work asenv:(names allowed, values pinned by the manifest). It fails closed in every shape I could construct.d_extra_env='TMPDIR JAVA_TOOL_OPTIONS'parses right:want_env="SEED_FORGEJO_EXT TMPDIR JAVA_TOOL_OPTIONS ", andp1_added_baditerates name-by-name againstp1_allowedword matching.H8[8][sbx-gits][mut] punto de partida … (con env permitido 'TMPDIR JAVA_TOOL_OPTIONS')is green on my run, and the threeenv-anadidos: MUT_ENVrows still bite.kubectl kustomizestrips them (grep -c 'SCRATCH OUT OF THE EVICTION PATH'on the render = 0), sop1_spine's byte comparison is not broken by the large comment blocks this PR adds. Verified, not assumed.updateStrategy: OnDelete), all foursbx-fleetcontainers insbx-gitspin6fa150a, which predates the newENVlayer, and the only non-sbx-gitssbx-fleetconsumer issbx-labat an explicit future bump. TheJAVA_TOOL_OPTIONS-only manifest env (no secondTMPDIR) is right:/home/ld/work/tmpis created by the newprepare-workline as root then handed to1000:1000, and the last-value-wins parse in test 28 stays green.My harness numbers (merged tree, this head, my runner)
Scratch worktree, base merged into this head (
1c5111d1is an ancestor, so it is the merged tree):bash scripts/gits-sbx-epic-test.sh→ exit 1, 683 PASS / 11 FAIL / 6 SKIP, 694 checks, 8m20s. Baseline, same runner, live tree at1c5111d1with this head's re-pinned harness → 12 FAIL, still in progress at section ~19 when I stopped it. Set comparison over the sections it had completed:manifest[2] memoria 6Gi/10Gi, stalemanifest[3] imagenes … sha256:7e0513ca(live pins6fa150a),manifest[3b],T7 emuladorresource sums,T7+R99[10b],H8[8][mut] sandbox: memoria 13Gi -> 20Gi(the mutation's baseline value no longer exists in the render),probe uses an EMPTY pre-existing --dir, and the three11[H11]rows that die onmkdir: cannot create directory '/home/ld': Permission denied.H8[8][sbx-gits] render vs origin/live (readinessProbe-no-clavada)and its[mut]baseline row — which is the new pin doing its job on a tree that does not carry the probe. Zero new failures attributable to this diff, matching the fix worker's claim in shape; my raw counts are 11/694 here versus their 13/693, i.e. runner-relative, not set-relative.H8[8][sbx-gits][mut] punto de partida(9 of 10 mutation rows biting; the 10th is the pre-existing stale-13Gi row),T7[diff] … M statefulset.yamlplus its red mutation,17[R159] (d)plus its red mutation, and the28[HOMELAB-2682]TMPDIR rows.Concerns (non-blocking, do not need a round)
prepare-workcomment ("Idempotent: a pre-existing$DST/tmpkeeps its owner and its contents"): GNUinstall -dre-applies-m/-o/-gto an existing directory. Contents are kept; owner and mode are forced to1000:1000 0755. The forced value is the intended one, so this is a comment that overstates, not a defect — but the harness's own culture is that comments are claims.p1_spinestripsreadinessProbe:on a fresh marker line and inside therskipterminator, but not in the "line that ENDED an env/volumeMounts/args block" re-evaluation. If a renderer ever emitsreadinessProbe:directly after one of those blocks, the probe leaks into the spine. It fails closed (spine-distinto, red), and kustomize sorts container keys soenv:is never immediately followed byreadinessProbe:— but given this function's history of layout assumptions, it deserves the same re-evaluation treatment or a two-line warning comment.p1_rpnormalizes the base indentation away, so the pin does not record which container a probe belongs to: two containers carrying byte-identical probe blocks could be swapped without moving the hash. Harmless here (the two blocks differ); worth knowing before it is relied on for an identical-probe case.publishNotReadyAddressesunset, sosbx-gits-0.sbx-gits.sbx-gits.svc.cluster.localstops being published while the probe fails — and that exact name is the static target of prod'ssandbox-telemetryScrapeConfig.SandboxEmulatorTelemetryAbsentisfor: 15m, so a normal boot cannot trip it, but a guest hung past ~15 min will now also fire "exporter absent". Arguably the right signal; it does change what that alert means, and nothing in the body says so.Anything r2 broke
No. The probe strip, the
p1_rppin, the two-name env set, the re-measured diff set and the re-worded pass messages are consistent with each other and with the harness's existing conventions;T7[diff],17[R159](d), theH8[8]table and the20[guard]renders all behave as intended on a tree that carries this head, and red as intended on a tree that does not.— ld-reviewer (fresh seat, DeepSeek), read-only: I edited nothing, pushed nothing, merged nothing. No token was placed in argv or printed.
commit:
ad45613a77model: omniroute/deepseek-flash
session: 01a1beb3-c6bb-777b-86ff-4bd2a85d0fe5
Verdict: APPROVED
Blockers
None.
Confirmation seat for the body-only fix to
BLOCKER B1-860r3-consumer-map(seat 23722). Freshcontext: I did not inherit that seat's summary as a premise. Every claim in the corrected C-1
paragraph was re-derived from the tree at this exact head (
git archive ad45613a), the base(
1c5111d1), the render path (kubectl kustomize), and the repo's own harness. The prior seat'scode/harness verification is therefore not taken on faith either where I could cheaply re-measure
it (I re-measured the render gate and the
p1_rppin).Head identity — nothing moved behind the body edit
ad45613a77…, base1c5111d1…(currentorigin/livetip). Twocommits since base:
f3776d4b(manifest + Dockerfile) andad45613a(harness re-pin);3 files, +230/−13, and the manifest/image commit is still
f3776d4b. The code is byte-identicalto the state seats 23693/23722 verified; only the PR body changed — the defect class this round
is the one that could be fixed without a new commit.
pass(build-and-push,yaml-lint,shellcheck,cockpit-test,cockpit-e2e,dispatcher-test,record-sql-test,runner-manifest-test, …).The corrected consumer map — each claim, verified
emulatoris NOT ansbx-fleetconsumer → TRUE. At head,sbx-gits/statefulset.yamlpins
harbor…/platform/sbx-fleet:6fa150a@sha256:0ee5e97e…on exactlyprepare-work,seed-pi-agent,sandbox,emu-health, andharbor…/platform/android-emulator:230756a@sha256:66430940…on
emulator.platform/android-emulator/Dockerfile:19isFROM debian:bookworm-slim@sha256:734a72b3…— nosbx-fleetlayer anywhere in that lineage, sono
sbx-fleetENVcan reach the emulator and the "test 28's emulator-stays-on-ephemeral-/tmpstands" statement is now sound. The old claim (
emulatorinheritsTMPDIR=/home/ld/work/tmpandmounts no
work) is gone from the body.sbx-fleetcontainers mount/home/ld/work→ TRUE (prepare-workrw,seed-pi-agentrw,sandboxrw,emu-healthread-only). Wording is accurate; theread-only qualifier is a separate, non-blocking refinement — concern C-2.
sbx-sendrastis NOT a consumer, no follow-up owed → TRUE.core/manifests/apps/sbx-sendrast/Dockerfile:26-27isARG LD_FLEET_IMAGE=…/ld-target/ld-fleet:go-89b900d/FROM ${LD_FLEET_IMAGE}— a differentimage line, no
sbx-fleetlayer. Repo-wide, the only objects referencingplatform/sbx-fleetimages are
sbx-gitsandsbx-lab(the rest are comments/docs/harness pins), no Dockerfilederives
FROMit, and neither thesendrastnor thelab-directorcheckout references it.sbx-labis the ONE bump-gated follow-up → TRUE in substance.sbx-lab/statefulset.yamlpins
sbx-fleet:fc933fb@sha256:be81ce63…on its twosbx-fleetcontainers, declares noTMPDIR/JAVA_TOOL_OPTIONSof its own, and its init script creates nothing at/home/ld/work/tmp(zero hits) — so at its next digest bump the imageENVwould point at adirectory nothing creates. Two residual wording defects inside that bullet are recorded as
concern C-1; they do not change which change must happen, where, or when, which is
what the blocker was about.
Deferral decision — intact, and now correctly reasoned
updateStrategy: {type: OnDelete}is present in the template at head, so merge rolls nothing.sbx-fleet:6fa150apredates theENVlayer (the layer is added insbx-fleet/Dockerfileimmediately beforeUSER 1000:1000, and no image was rebuilt in thisbranch: no tag/digest bump anywhere in the diff), so no running container can observe the new
values between merge and bump.
prepare-workruns as root and creates$DST/tmpwithDST=/home/ld/work→/home/ld/work/tmpexists for every
sbx-gitssbx-fleetcontainer before they run. The manifest repeatingJAVA_TOOL_OPTIONS(and NOT re-declaringTMPDIR, which lives at the HOMELAB-2682/home/ld/work/seat-transcriptssite) matches the tree: oneTMPDIRin the pod spec, one newenv var (
JAVA_TOOL_OPTIONS),tmpemptyDir4Gi → 8Gi,avd16Gi andherdr-sock64Miuntouched.
Render gate and harness pins — independently re-measured
kubectl kustomizeat base1c5111d1vs headad45613a, this machine:Those are the exact digests the body quotes, so the byte-identical hard gate reproduces. I
recomputed
p1_rpover the rendered head with the harness's own normalization (dedent the probebody, drop blank lines, document order) and got
0ce65f6ed11b3b45f5cb56f2f58f76a40ba5404b2adb0a8e1c07dfd2b37bc602, which is byte-equal to thepin at
scripts/gits-sbx-epic-test.sh:3667— the pin matches the render it claims to pin, andSBX_GITS_DIFF_SETisM\tstatefulset.yaml, i.e. the branch diff set the body states.Known limitations — folded in, and they are the right ones
The four seat-23722 concerns are present and correctly stated:
install -dis documented by acomment, not enforced;
p1_spine's env-block termination path is described as failing closed(I re-read the
awk: a strip that runs long removes lines from the spine, which makes the spinediffer from
origin/liveand turns the gate red, never green);p1_rpnormalizes the owningcontainer away (confirmed in the code — it prints
readinessProbe:plus the dedented body, nocontainer identity, so a probe moved between containers at the same manifest path keeps its hash);
and the readiness-DNS /
SandboxEmulatorTelemetryAbsentsharp edge for the recreation wave isspelled out instead of hidden. C-2 (probe steady-state load unmeasured) and the explicit
"not verified" list are carried, not dropped.
Concerns (non-blocking,
concerns may merge)C-1 —
sbx-labhas noprepare-work; and$DSTthere is not/home/ld/work. The correctedbullet says "its
prepare-workcreates no$DST/tmp(zero hits in its script)".sbx-lab'sonly initContainer is
seed-pi-agent, and that script setsDST=/home/ld/work/pi-agent(
sbx-lab/statefulset.yaml:159,181) —prepare-workwithDST=/home/ld/workis thesbx-gitscontainer. So the sentence is true of
sbx-gitsand imports another template's names intosbx-lab. Operational consequence to carry into the follow-up ticket: insbx-labthe directoryto create is the volume root
/home/ld/work/tmp(the path the imageENVnames), not$DST/tmp, which would create/home/ld/work/pi-agent/tmp; andsbx-lab's init runs as theimage user (no root
securityContext), so the literalinstall -d -m 0755 -o 1000 -g 1000recipe copied fromsbx-gitswill not work there — thefollow-up needs a root step (or a pre-created directory /
fsGroup) plus the per-containeroverride. Not blocking: the actionable sentence in the body names the directory by reference to the
ENVvalue it just quoted, and that follow-up is a separate, separately reviewed change.C-2 —
emu-healthmounts/home/ld/workread-only, so "always name a real path" is true butnot "a usable scratch path". At a future
sbx-gitsdigest bump,emu-health(asbx-fleetcontainer) inherits
TMPDIR=/home/ld/work/tmpthrough areadOnly: truemount; node'sos.tmpdir()honoursTMPDIR, so any temp-file use in the exporter would fail rather than fallback to its
/tmp. Bump-gated and today inert (6fa150apredates the layer), so it is not amerger risk — but the same-change bump checklist should cover
emu-healthexplicitly (mountworkrw, or neutraliseTMPDIRfor that container), not onlysbx-lab.Both concerns are wording/completeness items in the deferral record. They belong in the
bump-follow-up ticket; neither makes this prepare-only,
OnDelete, no-image-bump change wrong,and the blocker seat 23722 filed — a consumer map that would have produced a phantom
emulatoroverride, an unnecessary
sbx-sendrastedit, and the one real consumer as a bullet in a wronglist — is resolved on this head.
Read-only: no file, branch, or PR state was edited; this comment is the only write, posted as
ld-reviewerafterGET /api/v1/userreturned that login.Verdict of record — ld-reviewer — head
ad45613a77: APPROVEmodel: omniroute/deepseek-flash
session: 01a1beb3-c6bb-777b-86ff-4bd2a85d0fe5
Blockers: None.
This is a NEW comment, not an edit: my earlier verdict (comment 23724, this PR) stated the same
verdict on the same head, but opened with a
commit:metadata triplet and a bare## Verdict: APPROVEDheading, so the head sha was not inside the stance line the landing lockreads and the pin did not bind. 23724 stays exactly as posted — comments are never edited to get a
merge past the lock. Nothing below is a re-review: the head is unchanged (
ad45613a), the code andharness state are the ones I verified, and this comment re-affirms that substance with the pin in
the first block.
Head identity
Branch tip == PR head ==
ad45613a77f76b1dda501a6f15f5dd48ec612721; base1c5111d13d288e785492ad7874bce485e0bae6dc(currentorigin/livetip). Two commits since base:f3776d4b(manifest +sbx-fleetDockerfile) andad45613a(epic-harness re-pin); 3 files,+230/−13. CI green at this head: runs 3402/3403/3404, every job
pass(build-and-push,yaml-lint,shellcheck,cockpit-test,cockpit-e2e,dispatcher-test,record-sql-test,runner-manifest-test, …).Why the approval stands (verified at this head, re-derived — not inherited)
B1-860r3-consumer-map) is resolved.sbx-gits' foursbx-fleet:6fa150a@sha256:0ee5e97e…containers —prepare-work,seed-pi-agent,sandbox,emu-health— all mount/home/ld/work;emulatorpinsandroid-emulator:230756a@sha256:66430940…, whoseplatform/android-emulator/Dockerfile:19isFROM debian:bookworm-slim@sha256:734a72b3…, so nosbx-fleetENVcan reach it and the earliertest-28 contradiction was false.
sbx-sendrastderives fromld-target/ld-fleet:go-89b900d(
Dockerfile:26-27,FROM ${LD_FLEET_IMAGE}), not fromsbx-fleet— no follow-up owed.Repo-wide the only
platform/sbx-fleetimage references aresbx-gitsandsbx-lab, and noDockerfile derives
FROMit.sbx-lab(sbx-fleet:fc933fb) is correctly named the singlebump-gated follow-up.
updateStrategy: {type: OnDelete}at head;pinned digest
6fa150apredates theENVlayer added immediately beforeUSER 1000:1000insbx-fleet/Dockerfile, and no tag or digest is bumped in the diff — nothing rolls and no runningcontainer can see the value between merge and bump.
prepare-work(root,DST=/home/ld/work)creates
$DST/tmp; the pod spec keeps exactly oneTMPDIR(/home/ld/work/seat-transcripts,the HOMELAB-2682 site) plus the single new
JAVA_TOOL_OPTIONS=-Djava.io.tmpdir=/home/ld/work/tmp;tmpemptyDir4Gi → 8Giwithavd(16Gi) andherdr-sock(64Mi) untouched.kubectl kustomizebase vs head on this machinereproduces the body's numbers exactly:
sbx-lab8c8f61d79b30945b…andsbx-sendrastd225759f534949db…byte-identical;sbx-gits4b0fa50cd88e2ed9… → ba858643337c6429…. Irecomputed
p1_rpover the rendered head with the harness's own normalization and got0ce65f6ed11b3b45f5cb56f2f58f76a40ba5404b2adb0a8e1c07dfd2b37bc602, byte-equal to the pin atscripts/gits-sbx-epic-test.sh:3667;SBX_GITS_DIFF_SETisM\tstatefulset.yaml, exactly thebranch diff set the body claims. Seat 23693's 7 new failures are green on this head with zero new
ones (verified by the earlier seats; my spot checks agree).
install -ddocumented by comment, not enforced;p1_spine's untested env-block termination path fails closed (a long strip reddens the spinecomparison);
p1_rpnormalizes the owning container away (confirmed in the code); thereadiness/
SandboxEmulatorTelemetryAbsentsharp edge for the recreation wave; C-2 (probesteady-state load) unmeasured; and the explicit "not verified" list.
Open concerns (non-blocking — recorded for the bump-follow-up ticket, not grounds to hold this head)
sbx-labbullet says "itsprepare-workcreates no$DST/tmp".sbx-labhas no
prepare-work(its initContainer isseed-pi-agent,DST=/home/ld/work/pi-agent), so thedirectory its bump must create is the volume root
/home/ld/work/tmp, not$DST/tmp; and itsinit runs as the image user, so the literal
install -d -m 0755 -o 1000 -g 1000recipe fromsbx-gitswill not work there — it needs a root step or a pre-created directory.emu-healthmounts/home/ld/workreadOnly: true, so a futuresbx-gitsbump wouldhand it
TMPDIR/JAVA_TOOL_OPTIONSnaming a real but unwritable path (node honoursTMPDIR).Bump-gated and inert today; the same-change bump checklist should cover
emu-health, not onlysbx-lab.Neither concern touches what this PR does (prepare only,
OnDelete, no image bump), so they belongin the follow-up ticket rather than in a hold on this head.
Read-only: no file, branch, or PR state was edited and nothing was merged; this comment is the only
write, posted as
ld-reviewerafterGET /api/v1/userreturned that login.resolved #B1-860r3-consumer-map — verified on head
ad45613a77: the body's blast-radius map is corrected (emulator on android-emulator:230756a; sbx-sendrast FROM ld-fleet:go-89b900d; sbx-lab the single bump-gated follow-up), each claim re-verified against the repo; the deferral decision and its reasoning now agree.model: omniroute/deepseek-flash
session: 01a1beb3-c6bb-777b-86ff-4bd2a85d0fe5
resolved #B1-860r2-harness-pin — verified on head
ad45613a77: harness re-pin present in this head; my independent merged-tree run shows zero new failures vs the live tree with the same harness (merged 11 failed / 694 checks, live 11 failed / 692 checks, byte-identical FAIL sets); H8 spine, mutation baselines, T7 diff-set and test 28 all green.The comparison, concretely: merged tree (base
1c5111d1+ this head)RESULT: FAIL (11 of 694 checks failed, 6 skipped, 0 pending); live tree (1c5111d1, same command, same machine, run side by side)RESULT: FAIL (11 of 692 checks failed, 8 skipped, 0 pending). The two FAIL sets are byte-identical —commempty in both directions, so zero new failures and nothing lost.model: omniroute/deepseek-flash
session: 01a1bec7-a08f-777b-86ff-4bdba48d2f18
What I ran, primary sources only, in fresh scratch worktrees:
git merge --no-ff ad45613ainto1c5111d1→ cleanortmerge, andgit diff ad45613a <merge>is empty: the merged tree is byte-identical to the head under review.bash scripts/gits-sbx-epic-test.shfrom their own root, concurrently.The pins really are in this head, checked without trusting the PR body:
scripts/gits-sbx-epic-test.sh:3662—[[ $d == sbx-gits ]] && d_extra_env='TMPDIR JAVA_TOOL_OPTIONS'.scripts/gits-sbx-epic-test.sh:5401—SBX_GITS_DIFF_SET=$(printf 'M\tstatefulset.yaml\n' | sort); independently,git diff --name-status 1c5111d1 <merge> -- core/manifests/apps/sbx-gits/is exactlyM statefulset.yaml, so the set was re-measured, not dragged along.scripts/gits-sbx-epic-test.sh:3667—d_extra_rp='0ce65f6e…b37bc602'. I re-derived it myself rather than trusting the literal:kubectl kustomize core/manifests/apps/sbx-gitson the merged tree, then thep1_rpawk over that render'sreadinessProbeblocks →0ce65f6ed11b3b45f5cb56f2f58f76a40ba5404b2adb0a8e1c07dfd2b37bc602. Identical, so the probe pin is current.The seven failures the round-2 verdict named are green on the merged tree:
H8[8][sbx-gits]PASS, and its baseline mutation row PASS — "el template renderizado del head pasa p1_lab_sts_bad (con env permitido 'TMPDIR JAVA_TOOL_OPTIONS')": the env allowance is exactly what the manifest needs, nothing more.[mut]rows: 9 of 10 bite (spine-distinto ×6, env-anadidos ×3). The tenth —sandbox: memoria 13Gi -> 20Gi: la mutacion NO se aplico— fails identically on the live tree, so it is environmental to this runner (its mutation target no longer matches the manifest), not attributable to this diff.T7[diff]PASS naming the #860 set, with its "removed from the allowed set" mutation red;17[R159](d)PASS with its own removal mutation red.The eleven failures present on BOTH trees are the same environmental set: the kubectl-render manifest rows (
manifest[2],manifest[3],manifest[3b],T7 emulador dentro del pod,T7+R99[10b]), the three/home/ldpermission rows in11[H11], the empty--dirprobe row, and thememoriamutation row above. None of them names sbx-gits content this PR touches.CI at this head is green (runs #3402, #3403, #3404 all pass).
The blocker raised in review 23693 is resolved on head
ad45613a77.Re-verified at this head by this seat, from the tree and not from the PR body:
scripts/gits-sbx-epic-test.shcarries the re-pin this comment asked for —d_extra_env='TMPDIR JAVA_TOOL_OPTIONS'(:3662),SBX_GITS_DIFF_SETre-measured toM statefulset.yaml(:5401, andgit diff --name-status 1c5111d1 ad45613a -- core/manifests/apps/sbx-gits/is exactly that), and thep1_rpprobe pin0ce65f6e…b37bc602(:3667) alongside thep1_rpmechanism itself (:591-597). The manifest halves are present in the same head: thesys.boot_completedreadinessProbe onemulator,JAVA_TOOL_OPTIONSas the only added env name,tmp4Gi -> 8Gi, and theinstall -d … "$DST/tmp"line inprepare-work.The blocker raised in review 23722 is resolved on head
ad45613a77.Re-verified at this head by this seat: the PR body carries the corrected consumer map, in the order this comment required.
sbx-gits'emulatorpinsandroid-emulator:230756a(FROM debian:bookworm-slim), so nosbx-fleetENVcan reach it; the foursbx-fleet:6fa150acontainers (prepare-work,seed-pi-agent,sandbox,emu-health) all mount/home/ld/work;sbx-sendrast'sDockerfile:26-27isFROM ${LD_FLEET_IMAGE}withld-fleet:go-89b900d, so it is not a consumer and owes no follow-up;sbx-labis named the single bump-gated follow-up, with the$DST/tmpcreation required in that same change. The emulator-inheritance claim and the test-28 contradiction built on it are gone from the body.The blocker raised in review 23724 is resolved on head
ad45613a77.Re-verified at this head by this seat: the head has not moved since that confirmation (
ad45613a), the deferral reasoning still holds —updateStrategy: {type: OnDelete}, the pinned digestsbx-fleet:6fa150apredates theENVlayer added beforeUSER 1000:1000, and no tag or digest is bumped anywhere in the diff — and the body no longer carries the wrong consumer claim. Measured again here: exactly oneTMPDIRin the pod spec (the HOMELAB-2682seat-transcriptsvalue, untouched),JAVA_TOOL_OPTIONSthe only added env name,tmp4Gi -> 8Giwithavd16Gi andherdr-sock64Mi untouched,emu-health'shttpGetprobe byte-untouched, and the one new probe on the guest'ssys.boot_completed.resolved B1-860 — both id-carrying labels this id covers are discharged on head
ad45613a77, re-verified here from the tree. B1-860r2-harness-pin: the harness re-pin is in the head —d_extra_env='TMPDIR JAVA_TOOL_OPTIONS'atscripts/gits-sbx-epic-test.sh:3662,SBX_GITS_DIFF_SETre-measured toM statefulset.yamlat:5401(independently:git diff --name-status 1c5111d1 ad45613a -- core/manifests/apps/sbx-gits/is exactlyM statefulset.yaml), the newp1_rpmechanism at:591-597and its pin0ce65f6e…b37bc602at:3667. B1-860r3-consumer-map (and the confirmation seat's restatement of it): the PR body's blast-radius map is the corrected one —emulatoronandroid-emulator:230756abuiltFROM debian:bookworm-slim,sbx-sendrastFROM ${LD_FLEET_IMAGE}=ld-fleet:go-89b900dso not a consumer, andsbx-labnamed the single bump-gated follow-up.resolved #2 — discharged on head
ad45613a: thesbx-sendrastfollow-up item is gone from the PR body, and correctly so:core/manifests/apps/sbx-sendrast/Dockerfile:26-27derives fromld-target/ld-fleet:go-89b900d, so thesbx-fleetimageENVlayer never reaches it.resolved #3 — discharged on head
ad45613a:sbx-labis kept and promoted to the ONLY bump-gated follow-up in the PR body, with the requirement that theinstall -d -m 0755 -o 1000 -g 1000 "$DST/tmp"directory creation (or a per-container override) lands in that same change;sbx-lab/statefulset.yamlpinssbx-fleet:fc933fband its init creates nothing at that path today.resolved B1 — discharged on head
ad45613a: the duplicated-TMPDIRhazard from the first review is gone. The merged pod spec declares exactly oneTMPDIR(/home/ld/work/seat-transcripts, the HOMELAB-2682 site, untouched), the PR no longer adds a second, and the only added env name isJAVA_TOOL_OPTIONS=-Djava.io.tmpdir=/home/ld/work/tmponsandbox.commit:
ad45613a77model: omniroute/deepseek-flash
session: 01a1bee9-820e-777b-86ff-4bee6242d42e
Verified independently on this head, from primary sources. Rendered all three sandboxes with
kubectl kustomizeat base1c5111d1and atad45613a: sbx-lab (sha256 8c8f61d7..., 936290 B) and sbx-sendrast (d225759f..., 131099 B) are byte-identical, so nothing outside sbx-gits moves at the manifest layer. The sbx-gits render delta is exactly the four leaves claimed: theemulatorcontainer gains the only new readinessProbe (execadb -s emulator-5554 shell getprop sys.boot_completed== 1, initialDelay 60 / period 15 / timeout 10 / failure 3),sandboxgains exactly one env var (JAVA_TOOL_OPTIONS=-Djava.io.tmpdir=/home/ld/work/tmp, TMPDIR count stays 1 at the pre-existing seat-transcripts value), thetmpemptyDir goes 4Gi to 8Gi, andprepare-workargs gain the rootinstall -d -m 0755 -o 1000 -g 1000 "$DST/tmp". The pre-existing emu-health httpGet probe is unchanged before and after. The harness re-pin matches its own claims: d_extra_env is 'TMPDIR JAVA_TOOL_OPTIONS', SBX_GITS_DIFF_SET is exactlyM statefulset.yaml, and I re-ran the new p1_rp awk against the head's render and got 0ce65f6ed11b3b45f5cb56f2f58f76a40ba5404b2adb0a8e1c07dfd2b37bc602 - byte for byte the hash pinned in the script - while sbx-lab yields the empty-set hash equal to its ref, so the probe pin genuinely bites instead of being a hand-copied constant. The body's consumer map checks out: theemulatorpins android-emulator:230756a (not an sbx-fleet image), sbx-sendrast builds FROM ${LD_FLEET_IMAGE} = ld-target/ld-fleet:go-89b900d (not a consumer), and sbx-lab pins sbx-fleet:fc933fb with no$DST/tmpcreation in its prepare-work, so it is correctly named as the single bump-gated follow-up.updateStrategy: OnDeleteis present, so the merge rolls nothing. CI is green on this exact head (runs 3402/3403/3404, all jobs pass).Blockers: None.