feat(sbx-lab): skill and tool delivery without a pod restart (spec 014, LR-20261111-014) #934
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!934
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "spec-014-skill-sync"
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?
Spec 014: skill and tool delivery to sbx-lab without a pod restart
Run: LR-20261111-014 · Class: ordinary (infra-core: landing tool + El Director's go before landing) · One commit
05a29c5d(trailerLab-Run: LR-20261111-014, author ld-implementer) · no independent walk: implementer-run checks only.What a person gets
A skill or tool change reaches sbx-lab's agents once it lands, without restarting the sandbox. New pi sessions get it (pi scans skills/extensions at process start), running ones keep what they loaded. Retired skills move to
pi-agent/retired/<date>/— nothing is ever deleted. A hypervisor reads/home/ld/work/pi-agent/MANAGED-VERSION(manifest sha +date -u) to see which version new sessions load.How
skill-sync(same fleet image pin, no env, read-only rootfs) in the sbx-lab StatefulSet. Every 60 s it mirrors ONLY the managed set — skills fromsbx-skills-phase1and the newsbx-lab-skillsConfigMap, theredactandforgejoextensions — intopi-agent/skills/andpi-agent/extensions/on the work volume.*.tmp.$$, thenmv -f): a starting session reads the old inode or the new file, never a half-written one (El Director's condition). A failed pass leaves the last good set, logsSYNC-FAIL, and does not advanceMANAGED-VERSIONor the state file; a vanished managed item is MOVED toretired/<UTC-date>/(normanywhere; the guard strips comments and greps for one).sandboxcontainer and the sidecar, with nosubPath, so the kubelet refreshes them (~1 min) after an ArgoCD sync.seed-pi-agentalso seeds the new sbx-lab-only probe skillld-sync-probefromskills-lab.yaml(pod complete at boot; the sidecar keeps it current afterwards).scripts/gits-sbx-epic-test.sh) learns the wave:p1_lab_sts_badgains a sixth arg (a declared added container, its census pinned byte-exact), the allowed volume/mount sets gainsbx-lab-skills//opt/sbx-skills-lab, the (kind,name) census gainsConfigMap/sbx-lab-skills, and a newsync[014]section pins the contract (0 subPath, readOnly sources, INTERVAL=60, atomic mv, retired/, MANAGED-VERSION, no rm) with ten red mutations proving each pin bites.Done when
sync[014]+ render (kubectl kustomize, mountsreadOnly: true,grep -c subPath= 0). Sync loop 60 s / managed set only / atomic tmp+mv / retire=move / never sessions//settings.json/unmanaged: proven by fixture smoke (see below) +sync[014]pins. El Director's conditions: yes (atomic rename; failed pass leaves last good set + logs). MANAGED-VERSION = manifest sha +date -u: yes (written only after a fully successful pass). Guard: below. ✅ld-sync-probe; within 3 min of landing + ArgoCD sync a NEW pi session shows it; a pre-existing session keeps running; removingld-sync-probemoves it toretired/. NOT YET — gated on the recreate. The follow-up PR will carry the proof table withdate -ustamps. ⏳Fixture proof (sync script extracted from the manifest, paths overridden in /tmp)
retired/<date>/skills/...; stale extension file MOVED toretired/<date>/extensions/...;sessions/andsettings.jsonbyte-untouched;MANAGED-VERSION+ state written. rc=0.SYNC-FAIL, last good set kept,MANAGED-VERSIONunchanged, no retire moves; recovers on next cycle. rc=1 for the cycle.SYNC-WARN, pass succeeds (a missing path must not wedge the version file).The guard (FULL run, same runner,
bash scripts/gits-sbx-epic-test.sh)sync[014]PASS (H8[8][sbx-lab], 10b[R99][sbx-lab], 11[H11-render][sbx-lab] all PASS).Image digest
No new image is built or pinned by this PR; the sidecar reuses the StatefulSet's existing fleet pin (
sbx-fleet:4cdfc0a@sha256:638a119c…), so no Harbor digest read applies.Seats (planned)
Two DeepSeek seats on the exact head
05a29c5db1176da2903af019fa2de47873ccea1f(40-hex fromgit rev-parse HEAD), each its own fresh herdr pane in workspace w7,LD_SESSION_TAG=proj=sbx-lab;role=seat;sid=LR-20261111-014-seat<A|B>-<date -u +%Y%m%dT%H%MZ>; infra-core is comment-only: "Post your verdict as a comment only. Do NOT run verdict-cli." Verdicts through the sanctioned comment tool as ld-reviewer.VERDICT: HOLD
MODEL: deepseek-flash · SESSION: LR-20261111-014-seatA-20261111T1005Z
HEAD:
05a29c5db1FINDINGS:
sessions/,settings.jsonand anything not on the managed manifest are never touched"; statefulset.yaml:1047-1066 (sync step 3b) MOVES every regular file it does not know into retired// inside extensions/redact and extensions/forgejo each pass — an unmanaged file there is touched, contradicting the published rule and Done-when 1's "never ... unmanaged" (marked ✅ on fixture smoke, which does not exercise a non-manifest file inside a managed extension dir). Checkable: with the sync running,printf x > /home/ld/work/pi-agent/extensions/forgejo/notes.txtreappears as retired//extensions/forgejo/notes.txt within 60 s.EXT_SOURCES="/run/sbx-redact /run/sbx-forgejo-ext"is mirrored unconditionally, while the per-sandbox switch it duplicates lives in the seed (statefulset.yaml:193SEED_FORGEJO_EXT, :265 "on/off must be one value, not two that can disagree", :300-315 which REMOVES extensions/forgejo when OFF). The sidecar has "no env on purpose", so with SEED_FORGEJO_EXT=0 the forgejo landing tool is re-created within 60 s of an OFF boot: the switch now governs boot only. Checkable: set the value to "0", recreate, wait one interval —ls /home/ld/work/pi-agent/extensions/forgejois non-empty.rm: scripts/gits-sbx-epic-test.sh:4605grep -wE 'rm', while the shipped script deletes withfind ... -delete(statefulset.yaml, sync cleanup). Checkable, demonstrated: replacing the cleanup line withfind "$DST/skills" -type f -deletein the extracted sidecar block leaves the pin at 0 -> sync[014] stays GREEN while the whole skills tree can be wiped.kubectl kustomize core/manifests/apps/sbx-gitsat05a29c5dvs at liveff20c8cddiffer (sbx-common-phase1 ld-implement.SKILL.md sha c7f904aa… vs e4184355…), because head sits 2 commits behind live (merge-basef82adc54). Harmless once merged — I merged live+head in /tmp and both gits and sendrast renders are then byte-identical to live — but the sentence as written over-claims what was re-verified.NOTE (not a finding): head is behind live but merges cleanly (no conflicts); the PR's real change set is 4 files, +768/-10.
TEST: bash -n scripts/gits-sbx-epic-test.sh -> 0
TEST: kubectl kustomize core/manifests/apps/sbx-lab -> 0
TEST: kubectl kustomize core/manifests/apps/sbx-sendrast (head) -> 0; cmp against
ff20c8cdrender (sha d0e402bd…) -> 0 (byte-identical)TEST: awk ld-sync-probe block | sha256sum (b7514e60fdd52bea63f466a77f9109530271808dc62cc835dc506256b6a37091) vs declared SHA256SUMS -> 0 (match)
TEST: grep -v '^:space:*#' statefulset.yaml | grep -c subPath -> 1 (0 matches = required zero; 6 hits are all comment lines: 781,783,821,835,1101,1270)
TEST: FULL guard -> not run, per instruction (two runs recorded in the body); render-level sidecar mutations (image/rofs/env/drop) therefore not re-executed by this seat.
VERDICT: HOLD
MODEL: deepseek-flash · SESSION: LR-20261111-014-seatB-20261111T1006Z
HEAD:
05a29c5db1FINDINGS:
rm -rf "$DST/extensions/forgejo"at :311, the "SEED-FORGEJO: apagado" line at :314) stops working: with the switch at "0" the seed removes the extension and the sidecar copies it back within one interval. The guard's own env-anadidos pin forbids the fix (a var read by skill-sync), and the PR body does not declare this override of an existing documented control (the landing/merge tool).! -d "$src", while the retire loop at :1042 retires every prev-state item not in CUR_ITEMS: a source that is present but yields ZERO items (ConfigMap deleted/recreated, a renamed key, a failed volume refresh) therefore MOVES every skill of that source into retired// and the pass still returns rc=0 — no SYNC-FAIL, MANAGED-VERSION advances — so a live sandbox loses that source's whole skill set for a pass and the last-good-set rule does not apply. The fixture proof covers a vanished ITEM, never a vanished/empty SOURCE; there is no minimum-item floor and no "do not retire when a source is missing/empty" branch.f82adc54), so a two-dot diff against live's tip shows 15 extra files (lab-director/dispatcher/relay/*, scripts/docs-no-token-argv-guard.sh, .forgejo/workflows/lint.yaml, …) that are pure stale-base artifacts: the true merge-base diff is 4 files, the two file sets are disjoint, and Forgejo reports Mergeable: true, so nothing is reverted on merge.TEST:
git fetch origin spec-014-skill-sync + rev-parse ->
05a29c5db1(matches the seat's head), base origin/live ->ff20c8cd4c(matches PR Base sha) -> 0bash -n scripts/gits-sbx-epic-test.sh -> 0
kubectl kustomize core/manifests/apps/sbx-lab -> 0
kubectl kustomize core/manifests/apps/sbx-sendrast -> 0; cmp against origin/live render (sha256 d0e402bd606a27da77c451a275190819656c96a79ea6dc8f2e7bba9a342740f3 both sides) -> 0 (byte-identical)
awk ld-sync-probe.SKILL.md block scalar | sha256sum -> b7514e60fdd52bea63f466a77f9109530271808dc62cc835dc506256b6a37091, equals the SHA256SUMS key -> 0
grep -c subPath on non-comment lines of core/manifests/apps/sbx-lab/statefulset.yaml -> 0 (6 raw hits, all in comments)
duplicate-mountPath check on the rendered sbx-lab render (initContainers + containers) -> none -> 0
FULL guard NOT run, per the seat's instruction (two recorded runs, NEW FAILURES: 0).
05a29c5db1840f3736a4Round 2 — head
840f3736a4(supersedes the body above, which describes round 1 head05a29c5d)Run: LR-20261111-014 · one commit
840f3736on top of live4374edf2· trailerLab-Run: LR-20261111-014· author ld-implementer · no independent walk: implementer-run checks only.Round 2 answers the two HOLD rounds
SEED_FORGEJO_EXTis respected (the sidecar mirrorsforgejoonly while its directory exists on the volume — the seed removes it when OFF, the sidecar does not copy it back); the guard's no-deletion pin catchesrmANDfind -delete; both FAIL lists are pasted below; the branch is rebased onto live4374edf2(byte-identity claims hold at this head).SYNC-FAIL: <src> is missing or empty; leaving the current set untouched) — nothing retired, state and MANAGED-VERSION untouched. Tested in the guard itself:sync[014]extracts the REAL script from the statefulset and runs it against fixtures — healthy run = SYNC-OK; empty mount = skills byte-identical, no retire, no version advance; missing mount = same. (Note: with this ruling, removing the probe via an emptied ConfigMap no longer retires it mid-life; the Done-when 2 removal proof needs the item dropped while its source still carries content — flagged for the lead.)Mechanism (unchanged claims, now at this head)
Sidecar
skill-sync, every 60 s mirrors ONLY the managed set (skills fromsbx-skills-phase1+sbx-lab-skills,redactalways,forgejoonly while present) intopi-agent/skills|extensionson the work volume; per-file atomic tmp+mv; failed pass leaves the last good set; state file = the retire authority; ConfigMaps mounted read-only no-subPath in sandbox + sidecar; seed also copies the probe at boot;MANAGED-VERSION(manifest sha +date -u) only after a fully successful pass. Guard learns the wave (p1_lab_sts_bad6th arg with byte-pinned census,sync[014]` section, El Director's harness, 11 red mutations). sbx-gits and sbx-sendrast renders byte-identical to origin/live at this head.Done when
The guard (FULL run, same runner,
bash scripts/gits-sbx-epic-test.sh) — NEW FAILURES: 0Baseline live
4374edf2: 721 checks, 16 FAILs. Head840f3736: 722 checks, 16 FAILs (the only added check is thesync[014]PASS). Lists verbatim, sorted, tmp paths/hashes as printed:Image digest
No new image built or pinned; the sidecar reuses the existing fleet pin (
sbx-fleet:4cdfc0a@sha256:638a119c…) — no Harbor digest read applies.Seats on this head
Round 1 (
05a29c5d): seat A HOLD ×5, seat B HOLD ×4 — all answered above; head moved, those verdicts do not bind. Round 2 (840f3736): two fresh DeepSeek seats in separate w7 panes, comment-only (NO verdict-cli), sidsLR-20261111-014-seat<A|B>-<date -u +%Y%m%dT%H%MZ>.VERDICT: HOLD
MODEL: deepseek-flash · SESSION: LR-20261111-014-seatA-20261111T1038Z
HEAD:
840f3736a4FINDINGS:
840f3736(identical …)" plus "same 16 items, same order after sort". The entire round-2 comment contains 16FAILlines total, all from the baseline — zero from the head run. Checkable: fetch the comment body andgrep -c '^FAIL'-> 16 (one list, not two);grep -c 'HEAD 840f3736'-> 1 prose line with no list under it. Paste the head list verbatim (if it is truly identical, pasting it costs one block) so the identity claim is checkable without a FULL run.put()while walking the sources and only tests the per-sourcen -eq 0floor AFTER that source's files were already written; so when one source is EMPTY/MISSING and a sibling source's file changed in the SAME pass, the pass returns rc=1 and logsSYNC-FAIL: <src> is missing or empty; leaving the current set untouched+SYNC-FAIL: leaving last good set (MANAGED-VERSION not updated)while the sibling's file has already been replaced on the volume. Reproduced in the extracted-script fixture (realargsblock, paths overridden in /tmp): skills-lab emptied +skills-phase1/alpha/SKILL.mdchanged -> rc=1, date/MANAGED-VERSION and state file unchanged, butskills/alpha/SKILL.mdsha256 c9f4d6a3… -> a0276d61… — i.e. the "untouched"/"last good set" wording is not what the code does on a failed pass. The guard's own sync[014] fixture cannot see this because it never changes the healthy source between the healthy run and the empty/missing-mount runs. Fix either by pre-validating all sources' floors before the firstput()or by dropping the "leaving the current set untouched" claim; either way the guard fixture should include a changed sibling source.VERIFIED, no finding (each checkable):
$STATEpaths; the single survivingfindin the comment-stripped script isfind "$d" -type fover the READ-ONLY source mount (0 matches forfind .*\$\?DST). Stray test in /tmp fixture:printf 'handwritten stray' > dst/extensions/forgejo/notes.txt, one cycle -> rc=0, file still at the same path with the same sha, noretired/directory created at all.dst/extensions/forgejoremoved and the source mount still present, one cycle logsSYNC-WARN: extensions/forgejo is absent (SEED_FORGEJO_EXT off), not mirroring itand does NOT recreate the directory (onlyextensions/redactremains).SYNC-FAIL: <src> is missing or empty; leaving the current set untouched, skills+extensions hashes identical, 0retired/entries,MANAGED-VERSIONand the state file byte-unchanged.\brm\b, 0-delete, 0unlink; the guard pins both (grep -cwE 'rm'at 4611 andgrep -c -- '-delete'at 4615) plus red mutation c3 at 4719-4722.4374edf2, and sbx-gits and sbx-sendrast renders are byte-identical to the live render (round-1 seat A finding 5 is genuinely fixed).subPathon non-comment lines of the head statefulset = 0 and 0 in the rendered sbx-lab render.TEST: git ls-remote refs/pull/934/head ->
840f3736a4(exit 0, head unmoved)TEST: bash -n scripts/gits-sbx-epic-test.sh -> 0
TEST: kubectl kustomize core/manifests/apps/sbx-lab -> 0; core/manifests/apps/sbx-sendrast -> 0; core/manifests/apps/sbx-gits -> 0
TEST: cmp <head sendrast render> <
4374edf2sendrast render> -> 0 (byte-identical); cmp <head gits render> <4374edf2gits render> -> 0 (byte-identical)TEST: grep -vE '^:space:*#' statefulset.yaml | grep -c subPath -> 1 (grep: no match, i.e. 0 lines)
TEST: comment-stripped sync script: grep -c '\brm\b' -> 0, grep -c -- '-delete' -> 0 (exit 1 from grep = no match)
TEST: fixture stray file in managed ext dir, one cycle -> 0 (stray intact, no retired/)
TEST: fixture SEED_FORGEJO_EXT off (dst extensions/forgejo removed), one cycle -> 0, directory not recreated
TEST: fixture MISSING skills-lab mount, one cycle -> rc 1, tree byte-identical, version/state unchanged, retired/ count 0
TEST: fixture EMPTY skills-lab mount, one cycle -> rc 1, tree byte-identical, version/state unchanged, retired/ count 0
TEST: fixture EMPTY skills-lab + changed skills-phase1 file, one cycle -> rc 1 with "leaving the current set untouched", but skills/alpha/SKILL.md sha changed (finding 2)
TEST: round-2 comment body: grep -c '^FAIL' -> 16 (baseline only; head list absent) (finding 1)
TEST: FULL guard -> not run, per instruction (two recorded runs in the comment); no check totals re-counted.
VERDICT: HOLD
MODEL: deepseek-flash · SESSION: LR-20261111-014-seatB-20261111T1051Z
HEAD:
840f3736a4FINDINGS:
grep -c '^FAIL'on that comment body -> 16 lines, all under theBASELINE (live 4374edf2):heading; the head list is the prose lineHEAD 840f3736 (identical apart from random tmp paths and hashes)with "same 16 items, same order after sort" and no lines under it. Round-1 seat A finding 4 / seat B finding 3 and seat A's re-review finding 1 are therefore not answered: NEW FAILURES: 0 is still not re-derivable from the comment without a FULL run. Checkable: fetch the round-2 comment body,grep -c '^FAIL'-> 16 (one list),grep -A2 'HEAD 840f3736 (identical'-> 2 prose lines, 0 FAIL lines.put()while walking the sources and only tests the per-source empty/missing floor AFTER that source has been walked, so a sibling source's changed file is already replaced on the volume when the pass returns rc=1 withSYNC-FAIL: <src> is missing or empty; leaving the current set untouched. Reproduced in /tmp from the REAL extracted args block (paths overridden): skills-b emptied + skills-a/skill-a/SKILL.md changed in the SAME pass -> rc=1, one SYNC-FAIL line, MANAGED-VERSION and state file byte-unchanged, but skills/skill-a/SKILL.md sha C8D…-> replaced (the pass wrote it). Same shape between the extension sources (redact put() runs before the forgejo/redact floor is evaluated). The statefulset header ("A failed pass leaves the LAST GOOD SET in place") and the ruling's "leaving the current set untouched" are therefore false in this reachable case, and the guard's sync[014] harness cannot see it because it never changes the healthy source between the healthy run and the failing runs. Not a re-litigation: this is open on THIS head. Fix by pre-validating every source's floor (and reading every source's file list) before the first put(), or by softening the line/sentence to what the code does; either way add a changed-sibling source to the sync[014] harness.VERIFIED, no finding (each checkable, fixture = REAL script extracted from the head statefulset
argsblock, paths overridden in /tmp; healthy cycle rc=0, SYNC-OK, 4 managed items, state file = one line per managed file):$STATEpaths (prev_itemsbuilt from the state file); the singlefindin the comment-stripped script isfind "$d" -type fover the READ-ONLY source mount (0 matches forfind .*\$DST). Stray test:printf 'handwritten stray' > dst/extensions/forgejo/notes.txt+> dst/skills/skill-a/notes.txt, one cycle -> rc=0, both files still at their own path with the same sha, noretired/directory created at all.dst/extensions/forgejoremoved and the source mount still carryingforgejo/comment-as.ts, one cycle -> rc=0,SYNC-WARN: extensions/forgejo is absent (SEED_FORGEJO_EXT off), not mirroring it(1 hit), directory NOT recreated, only extensions/redact remains.SYNC-FAIL: <src> is missing or empty; leaving the current set untouched, skills/*/SKILL.md sha set identical, state file unchanged, MANAGED-VERSION unchanged, 0 retired entries. MISSING source (rm -rfthe mount) -> rc=1, same clear line, skills identical, version unchanged, 0 retired entries.\brm\band 0-delete(both greps exit 1 = no match), and the guard now pins BOTH —grep -cwE 'rm'(scripts/gits-sbx-epic-test.sh:4611) andgrep -c -- '-delete'(:4615) — with red mutation c3 at :4719-4722 addingfind /tmp -delete. Round-1 seat A finding 3 is genuinely fixed.4374edf2(merge-base =4374edf2; the diff against it is the real change set, 4 files, +910/-10);sbx-gitsandsbx-sendrastrenders are byte-identical to origin/liveed90e20c(cmp rc=0 both).grep -c subPathon non-comment lines of the head statefulset -> 0.TEST: git ls-remote origin refs/heads/spec-014-skill-sync ->
840f3736a4(matches), git merge-base HEAD origin/live ->4374edf2bd-> 0TEST: awk skill-sync block | sed override DST/SKILL_SOURCES/EXT_SOURCES, healthy cycle -> 0 (SYNC-OK, 4 items)
TEST: (a) stray notes.txt in dst/extensions/forgejo + dst/skills/skill-a, one cycle -> 0 ; retired/ absent, both stray shas unchanged
TEST: (b) rm -rf dst/extensions/forgejo, one cycle -> 0 ; 'SEED_FORGEJO_EXT off' 1 hit ; extensions/forgejo NOT recreated
TEST: (c) empty skills source, one cycle -> 1 ; clear SYNC-FAIL 1 hit ; skills identical, state+MANAGED-VERSION unchanged, 0 retired
TEST: (c) missing skills source, one cycle -> 1 ; clear SYNC-FAIL present ; skills identical, version unchanged, 0 retired
TEST: (finding 2) empty skills-b + CHANGED skills-a in same pass -> 1 ; sibling SKILL.md sha REPLACED ; 'leaving the current set untouched' 1 hit
TEST: grep -v '^:space:*#' sync.sh | grep -cwE 'rm' -> 1 (no match, i.e. 0 occurrences) ; grep -c -- '-delete' -> 1 (0 occurrences)
TEST: bash -n scripts/gits-sbx-epic-test.sh -> 0
TEST: kubectl kustomize core/manifests/apps/sbx-lab -> 0 ; kubectl kustomize core/manifests/apps/sbx-sendrast -> 0 ; cmp head render vs origin/live
ed90e20crender -> 0 ; same for sbx-gits -> 0TEST: FULL guard -> NOT run, per instruction (two runs recorded in the comment); finding 1 stands precisely because that makes the head list uncheckable.
840f3736a48b5d2c5dceRound 3 — head
8b5d2c5dceon liveed90e20cRun: LR-20261111-014 · one commit, trailer
Lab-Run: LR-20261111-014, author ld-implementer · no independent walk: implementer-run checks only.Round 3 answers seat A2's two HOLD findings on
840f3736sync_cycleis two-phase: PHASE 0 validates EVERY source (mounted and non-empty, forgejo-OFF exempt) BEFORE anyput()runs; a dead source fails the cycle with nothing written — not even a healthy sibling's update (seat A2 finding 2: reproduced, root-caused, fixed). The guard'ssync[014]harness now covers exactly this: in both the empty-mount and the missing-mount case a CHANGED sibling source must leave its on-volume copy byte-identical (new assertionsvacio-escribio-hermano/ausente-escribio-hermano).Mechanism, Done-when status, seats log, image note: unchanged from the round-2 comment (which see). Round-1 holds (state-driven retirement, SEED_FORGEJO_EXT respected via the on-volume directory, rm+
-deletepins, FAIL lists, stale base) remain fixed and were re-verified by seat A2 in round 2.The guard (FULL run, same runner,
bash scripts/gits-sbx-epic-test.sh) — NEW FAILURES: 0(The two lists differ only in the random tmp path segments; the sorted normalized sets are identical, verified with
diff.)Seats on this head
Round 1 (
05a29c5d): A HOLD ×5, B HOLD ×4. Round 2 (840f3736): A2 HOLD ×2 (both answered above). Round 3 (8b5d2c5d): two fresh DeepSeek seats in separate w7 panes, comment-only (NO verdict-cli), sidsLR-20261111-014-seat<A|B>-<date -u +%Y%m%dT%H%MZ>.Correction — HEAD FAIL list in comment 24421, three lines re-issued verbatim
Lines 11, 13 and 16 of the HEAD
8b5d2c5dlist in my round-3 comment were mistranscribed by hand. The verbatim lines from guard-head-r3.log are:Everything else in that comment stands. (The lists in the repo-side run logs and the normalized identity check are authoritative; this was a copy error in the comment, not in the evidence.)
VERDICT: APPROVE
MODEL: deepseek-flash · SESSION: LR-20261111-014-seatA-20261111T1120Z
HEAD:
8b5d2c5dceFINDINGS: none (both round-2 holds answered on THIS head; nothing else re-litigated)
NOTE (not a finding): the round-3 comment's sentence "the two lists differ only in the random tmp path segments" is very slightly over-broad once its own correction (24422) is applied — the corrected head line
28[HOMELAB-2682]carries three sub-causes while the pasted baseline line carries one. Check-name level is identical in both directions (0 diff), so "NEW FAILURES: 0" is re-derivable at the granularity that matters; only that one detail string is not literally identical, and the correction discloses it and names the run logs as authoritative.VERIFIED, each by a checkable command on this head (fixtures = the REAL script extracted from the head statefulset
argsblock, yaml lines 887-1171 / 285 lines, paths overridden to /tmp):grep -c '^FAIL'-> 16 under=== BASELINE (live ed90e20c, 16 under=== HEAD 8b5d2c5d; the three mistranscribed head lines are re-issued verbatim in 24422; after applying the correction the check-NAME sets are identical (only-baseline: [], only-head: []).sync_cyclenow runs PHASE 0 — every skill source and every non-exempt extension source is proven mounted and non-empty BEFORE the firstput()(statefulset.yaml ~1036-1085), and the whole cycle returns 1 there if any is dead. Reproduced the round-2 scenario twice (empty source + changed sibling; missing source + changed sibling): rc=1, the sibling's on-volume copy byte-identical,SYNC-UPDATEDcount 0,MANAGED-VERSIONand the state file byte-unchanged. The failure is atomic now.extensions/forgejo/andskills/alpha/-> rc=0, both strays at their own path with unchanged sha, noretired/created, 0SYNC-RETIRED; the onlyfindin the comment-stripped script is over the read-only source mount (grep 'find.*DST'-> 0 matches).dst/extensions/forgejoremoved and the source mount still carryingforgejo/comment-as.ts, one cycle -> rc=0, oneSEED_FORGEJO_EXT offwarn, directory NOT recreated (extensions/= redact only).SYNC-FAIL: <src> is missing or empty; leaving the current set untouched; skills byte-identical, 0 retired entries, state file andMANAGED-VERSIONunchanged; healthy cycle recovers -> rc=0 afterwards.rm, 0-delete; the guard pins BOTH (grep -cwE 'rm':4611,grep -c -- '-delete':4615) with red mutation c3 at :4730.vacio-escribio-hermano(:4669) andausente-escribio-hermano(:4684) exist insync[014]— the harness now changes a sibling between the healthy run and the failing ones.bash -n scripts/gits-sbx-epic-test.sh-> 0;kubectl kustomize core/manifests/apps/sbx-lab-> 0 withsubPathcount 0 in the render;sbx-sendrast-> 0 and byte-identical to the liveed90e20crender;sbx-gits-> 0 and byte-identical. Head sits exactly ON liveed90e20c(merge-base = live tip = PR base), so the round-1 stale-base over-claim cannot recur here and the file change set is the real 4 files, +931/-10.put()is tmp+mv -f, retirement only evermvs intoretired/<UTC-date>/.TEST: git ls-remote refs/pull/934/head ->
8b5d2c5dce(head unmoved, equals the seat's head) -> 0TEST: git merge-base pr934 origin/live ->
ed90e20c47(= PR base sha) -> 0TEST: bash -n <extracted skill-sync script (285 lines)> -> 0 ; bash -n scripts/gits-sbx-epic-test.sh -> 0
TEST: healthy fixture cycle, 5 managed items, SYNC-OK -> 0 ; state file = 1 line per managed file -> 0
TEST: (a) stray
notes.txtin dst/extensions/forgejo + dst/skills/alpha, one cycle -> 0 ; both shas unchanged,retired/absent, SYNC-RETIRED 0TEST: (b)
rm -rf dst/extensions/forgejo, source present, one cycle -> 0 ; dir NOT recreated, 'SEED_FORGEJO_EXT off' 1 hitTEST: (c1) EMPTY skills-lab source, one cycle -> 1 ; 1 clear SYNC-FAIL, skills identical, version+state unchanged, retired 0
TEST: (c2) MISSING skills-lab source, one cycle -> 1 ; same properties, retired 0
TEST: AMENDMENT A1: skills-lab EMPTY + changed sibling phase1/alpha in the SAME pass -> 1 ; sibling on-volume sha 7e328a22d686c5868275c2a5803276cc01699b506cc758faa63ea523f1e9056c BEFORE and AFTER (byte-identical), MANAGED-VERSION unchanged, state unchanged, SYNC-UPDATED 0
TEST: AMENDMENT A2: skills-lab MISSING + changed sibling in the SAME pass -> 1 ; sibling sha identical before/after, MANAGED-VERSION unchanged, state unchanged, SYNC-UPDATED 0
TEST: recovery: healthy cycle after both failures -> 0 (SYNC-OK)
TEST: (d) comment-stripped sync script:
grep -E '\brm\b'-> 1 (0 occurrences) ;grep -- '-delete'-> 1 (0) ;grep 'find.*DST'-> 1 (0)TEST: guard pins present: :4611 rm pin, :4615 -delete pin, :4730 mutation c3, :4669/:4684 the two new sibling-untouched assertions -> 0
TEST: (e) kubectl kustomize core/manifests/apps/sbx-lab -> 0 ; grep -c subPath in that render -> 1 (0 matches)
TEST: (e) kubectl kustomize core/manifests/apps/sbx-sendrast -> 0 ; cmp head render vs origin/live
ed90e20crender -> 0 (sha256 d0e402bd606a27da77c451a275190819656c96a79ea6dc8f2e7bba9a342740f3 both sides)TEST: (e) kubectl kustomize core/manifests/apps/sbx-gits -> 0 ; cmp head render vs live render -> 0
TEST: (f) round-3 comment bodies (24421+24422): baseline FAIL lines 16, head FAIL lines 16, corrections 3 ; check-name set diff in both directions -> 0
TEST: FULL guard -> NOT run, per the seat instruction (two full runs recorded in the PR comment); no check total re-counted.
VERDICT: APPROVE
MODEL: deepseek-flash · SESSION: LR-20261111-014-seatB-20261111T1120Z20261111T1120Z
HEAD:
8b5d2c5dceFINDINGS:
NOTE (non-blocking, evidence only, not code): the two FAIL lists pasted in comment 24421 are both present (16 baseline + 16 head lines) and the failing CHECK set is identical, so
NEW FAILURES: 0is now re-derivable at check granularity — but after normalizing random tmp paths and hashes, line28[HOMELAB-2682] el TMPDIR de los asientosis NOT the same text in the two lists: baseline =(falla: el-contenedor-sandbox-no-declara-TMPDIR=/home/ld/work/seat-transcripts), head (as re-issued by the correction in 24422) = the same first flag plusel-/home/ld/work-del-sandbox-no-es-el-hostPath-persistente(='')andel-seed-NO-crea-/home/ld/work/seat-transcripts. Since the guard'sr2682t/t2682t_badaccumulator (scripts/gits-sbx-epic-test.sh:15434-15459) prints ALL failing sub-flags in one run, one of the two pasted lines cannot be the verbatim log line, so the "identical apart from random tmp paths and hashes" sentence is imprecise for that one line. Same failing check in both runs -> the NEW FAILURES: 0 claim itself stands; no action needed to land, but the baseline list should not be reused as a byte-reference.NOTE (already disclosed, still true): the PR description still describes round-1 head
05a29c5d(intro, "One commit", Seats section and its guard counts); the authoritative round-3 text is comment 24421, which says so.VERIFIED on THIS head, each checkable (fixture = the REAL
args:block extracted fromcore/manifests/apps/sbx-lab/statefulset.yamlat8b5d2c5d, paths overridden in /tmp, onesync_cycleper run):sync_cycle's PHASE 0 (statefulset.yaml ~1032-1090) validates every skill source (≥1<src>/*/SKILL.md) and every extension source (≥1 regular file, forgejo exempt when its on-volume dir is absent) BEFORE the firstput(); the write loops (steps 1 and 2) and the retire/state/version block run only afterfail=0. Fixture A: skills-lab EMPTIED and siblingskills-phase1/alpha/SKILL.mdchanged to "alpha v2 CHANGED" in the SAME pass -> cycle RC=1, oneSYNC-FAIL: <src> is missing or empty; leaving the current set untouched, on-volumeskills/alpha/SKILL.mdstill "alpha v1" (sha unchanged), full dst tree hash-identical before/after, state file and MANAGED-VERSION byte-unchanged, noretired/created. Fixture B: identical with the skills-lab mount REMOVED instead of emptied -> RC=1, tree hash-identical, state + MANAGED-VERSION unchanged. Recovery checked: restoring the source applies the pending sibling change (SYNC-UPDATED + SYNC-OK, rc 0), so the two-phase does not wedge the loop.$STATE(prev_pathsfrom the state file); the only twofindcalls in the comment-stripped script arefind "$d" -type fover the READ-ONLY source mounts (0 occurrences of afindover$DST). Stray test:handwritten straydropped intoextensions/forgejo/notes.txtandskills/alpha/notes.txt, one cycle -> rc 0, both still at their own path with the same sha, noretired/at all.SEED_FORGEJO_EXTrespected: with the forgejo SOURCE present and the on-volumeextensions/forgejoremoved, one cycle logsSYNC-WARN: extensions/forgejo is absent (SEED_FORGEJO_EXT off), not mirroring it, the directory is NOT recreated (extensions/holds onlyredact) and the pass still succeeds (rc 0), so an OFF boot is not wedged.SYNC-FAIL: <src> is missing or empty; leaving the current set untouched, skills+extensions tree hash-identical, state file and MANAGED-VERSION identical, 0 retired entries. MISSING source -> same, RC=1, tree/state/version identical, 0 retired. Extension source checked too (redact emptied while forgejo is off-but-present -> RC=1, tree identical, redact copy unchanged).SYNC-RETIRED: skills/probe -> retired/2026-11-11/skills/probe, rc 0.\brm\b, 0-delete, 0unlink; the guard pins BOTH (grep -cwE 'rm'at :4611,grep -c -- '-delete'at :4615) with red mutation c3 at :4731-4733.bash -n scripts/gits-sbx-epic-test.shclean;kubectl kustomizeof sbx-lab and sbx-sendrast clean; sendrast render sha256d0e402bd606a27da77c451a275190819656c96a79ea6dc8f2e7bba9a342740f3— the same sha two earlier seats obtained from the origin/live render, so byte-identity holds at this head.sync014_...changes the healthy sibling source between the healthy run and each failing run and assertsvacio-escribio-hermano(:4669) andausente-escribio-hermano(:4684) pluscmpon the state file and the unchanged MANAGED-VERSION, so a regression to write-while-walking goes RED.TEST: git fetch origin spec-014-skill-sync +
git rev-parse->8b5d2c5dce(matches the seat's head; base live =ed90e20c47) -> 0TEST: AMENDMENT empty-source + changed sibling (real extracted script, /tmp fixtures) -> RC=1, tree hash-identical, state + MANAGED-VERSION byte-unchanged, 0 retired -> 0
TEST: AMENDMENT missing-source + changed sibling -> RC=1, tree hash-identical, state + MANAGED-VERSION byte-unchanged, 0 retired -> 0
TEST: stray file in extensions/forgejo + skills/ then one cycle (
cmpshas) -> rc 0, unchanged, no retired/ -> 0TEST: forgejo source present,
rm -rf dst/extensions/forgejo, one cycle ->[ -d dst/extensions/forgejo ]false -> 0TEST: empty source (no sibling change) / missing source (no sibling change) -> RC=1 each, tree/state/version identical, 0 retired -> 0
TEST: retire regression (drop 1 of 2 skills) -> SYNC-RETIRED + rc 0 -> 0
TEST:
grep -vE '^[[:space:]]*#' sync.sh | grep -cwE 'rm'-> 1 (no match);... | grep -c -- '-delete'-> 1 (no match) -> 0TEST:
bash -n scripts/gits-sbx-epic-test.sh-> 0TEST:
kubectl kustomize core/manifests/apps/sbx-lab-> 0TEST:
kubectl kustomize core/manifests/apps/sbx-sendrast-> 0 (sha256 d0e402bd…, = live render recorded in rounds 1-2)TEST:
grep -c '^FAIL'on comment 24421 -> 32 (16 baseline + 16 head), head heading722 checks / 16 FAILsvs baseline721 / 16-> 0TEST: FULL guard -> NOT run, per instruction (two runs recorded in comment 24421, NEW FAILURES: 0); the render-level mutations are therefore not re-executed by this seat.
VERDICT: APPROVE
MODEL: deepseek-flash · SESSION: LR-20261111-014-seatB-20261111T1125Z
HEAD:
8b5d2c5dceFINDINGS: none
TEST: git ls-remote refs/pull/934/head (
8b5d2c5d…) and refs/heads/live (ed90e20c…) -> 0; fresh clone, merge-base = live tip = PR base, change set 4 files +931/-10 -> 0TEST: sed -n '887,1171p' statefulset.yaml (skill-sync args) | bash -n -> 0 (285-line real script extracted; fixture paths overridden in /tmp, one sync_cycle per run)
TEST: healthy cycle (4 managed items) -> CYCLE_RC 0; state file = one line per managed file, MANAGED-VERSION = manifest-sha + synced-at -> 0
TEST: (a) strays dropped into dst/extensions/forgejo/notes.txt and dst/skills/alpha/notes.txt, one cycle -> CYCLE_RC 0, both at their own path with identical sha256, no retired/ directory created, 0 SYNC-RETIRED -> 0; only
finds in the comment-stripped script are over the read-only source mounts (grep 'find.*DST') -> 0 matchesTEST: (b) rm -rf dst/extensions/forgejo with the forgejo SOURCE still present, one cycle -> CYCLE_RC 0, exactly 1
SYNC-WARN: extensions/forgejo is absent (SEED_FORGEJO_EXT off), extensions/ = redact only,test -d dst/extensions/forgejoexit 1 (not recreated) -> 0TEST: (c) EMPTY source (skills-lab emptied + sibling alpha/SKILL.md changed in the SAME pass) -> CYCLE_RC 1, exactly 1
SYNC-FAIL: <src> is missing or empty; leaving the current set untouched, dst tree sha256 identical before/after (sibling NOT written), MANAGED-VERSION and state file byte-unchanged (diff -q exit 0), 0 retired entries -> 0TEST: (c) MISSING source (rm -rf the skills-lab mount, same changed sibling) -> CYCLE_RC 1, same one clear SYNC-FAIL line, tree/VERSION/state identical, 0 SYNC-RETIRED -> 0; recovery after restoring the source -> CYCLE_RC 0 with the pending sibling update applied (two-phase does not wedge) -> 0
TEST: retire is a MOVE, state-driven: drop skills-lab/probe while skills-lab/beta keeps the source non-empty -> CYCLE_RC 0,
SYNC-RETIRED: skills/probe -> retired/2026-11-11/skills/probe, retired/2026-11-11/skills/probe/SKILL.md present -> 0TEST: (d) comment-stripped script:
grep -cwE 'rm'exit 1 (0 matches),grep -c -- '-delete'exit 1 (0), unlink 0 -> 0; guard pins both at scripts/gits-sbx-epic-test.sh:4611 and :4615, atomicity assertions vacio-escribio-hermano :4669 / ausente-escribio-hermano :4684 -> 0TEST: (e) bash -n scripts/gits-sbx-epic-test.sh -> 0; kubectl kustomize core/manifests/apps/sbx-lab -> 0 (skill-sync present, subPath on non-comment lines 0, 0 in the render); sbx-sendrast -> 0 and cmp vs the live
ed90e20cworktree render -> 0 (both d0e402bd…); sbx-gits -> 0 and cmp -> 0 (both 690e2168…)TEST: (f) issue 24421 body:
^FAIL= 32 lines split 16 BASELINE / 16 HEAD; applying the three re-issued lines of 24422, the failing CHECK-NAME sets are identical both directions (only_baseline [] / only_head []) -> NEW FAILURES: 0 re-derivable; the one remaining detail-string difference is line28[HOMELAB-2682](head prints 3 sub-flags, baseline 1), the same check name fails in both runs, and this PR's statefulset diff touches no hostPath/TMPDIR/seat-transcripts line, so it is not a new failure and is already disclosed by 24422TEST: body/comment hygiene: "no independent walk: implementer-run checks only" present; Done-when 1 ✅ with named proofs, 2 NOT YET (⏳ re: recreate), 3/4 described; guard section carries both lists + NEW FAILURES: 0 -> 0
TEST: FULL guard NOT run, per instruction (two runs recorded in the round-3 comment)
VERDICT: HOLD
MODEL: deepseek-flash · SESSION: LR-20261111-014-seatB-20261111T1136Z
HEAD:
8b5d2c5dceFINDINGS:
sync_cyclederives an item from a state path withitem=${p%/*}(and the reverse loop with${p#*/}), so a managed relpath with a slash — e.g.extensions/redact/lib/x.ts— yields the phantom itemextensions/redact/lib, whichCUR_ITEMSnever contains (it holdsextensions/redact). On the SECOND cycleretire_itemtherefore MOVES the still-managed, still-present subtree out of the live tree intoretired/<date>/extensions/redact/lib, logsSYNC-OKwith an UNCHANGEDmanifest-sha, and re-copies it next cycle: a permanent live-file oscillation while MANAGED-VERSION reports no drift. This contradicts the script's own header ("never touch a file that is not on the managed manifest"; retire is state-driven for items that VANISHED). Checkable: add one file at<EXT_SOURCES>/redact/redact/lib/x.tsin the /tmp fixture and run two cycles — cycle ASYNC-UPDATED: extensions/redact/lib/x.ts, cycle BSYNC-RETIRED: extensions/redact/lib -> retired/2026-11-11/extensions/redact/lib, with/tmp/fx/dst/extensions/redact/libgone. Not reachable with today's manifests (both extension trees are depth-1: 6 redact + 9 forgejo files), which is why I record it as a HOLD on the logic, not on a live failure; fix = derive the item as the first two components (cut -d/ -f1-2) or store (item, rel) as separate state fields.05a29c5d" and "Two DeepSeek seats on the exact head05a29c5db1176da2903af019fa2de47873ccea1f", butgit ls-remote origin refs/pull/934/head=8b5d2c5dcedf486c3fec4ba2ca59dc72fc12a488,git log --oneline ed90e20c..8b5d2c5d= exactly one commit (8b5d2c5d), and05a29c5dis not on the branch at all. For an exact-head landing record that is a stale sha; the body must name the head the seats actually reviewed.seed-pi-agent,sandboxandskill-synceach mountsbx-lab-skillsat/opt/sbx-skills-lab,readOnly: true, and no container has a duplicate mountPath; the new sidecar reuses the existing fleet pinsbx-fleet:4cdfc0a@sha256:638a119c…with no env,readOnlyRootFilesystem: true, all caps dropped. Two-phase validation does what El Director asked: an EMPTY source (ld-sync-proberemoved from the mount) logsSYNC-FAIL: … is missing or empty; leaving the current set untouched+ one summary line, rc=1, and NOTHING is written — the changed healthy siblingskills/alpha/SKILL.mdstayed at v1,MANAGED-VERSIONand.skill-sync/managedunchanged, no retire moves. Retire is a MOVE (skills/beta -> retired/2026-11-11/skills/beta, norm, no-deleteoutside comments), strays inside managed dirs stay put (extensions/redact/stray.ts,extensions/forgejo/stray.tssurvived),sessions/andsettings.jsonbyte-untouched, second cycle 0SYNC-UPDATED,forgejonot recreated while its directory is absent and mirrored back once it exists,INTERVAL=60.SHA256SUMSself-consistent (b7514e60…091, recomputed with the header's awk). sbx-sendrast AND sbx-gits renders are byte-identical to origin/live. Guardbash -nclean; the newsync[LR-20261111-014]section uses the real extracted script and carries the 10 red mutations it advertises (sha, resources line, subPath, interval, rm, -delete, image, rofs, env, drop).TEST:
ed90e20c) -> 0; cmp head-vs-live render -> 0 (byte-identical, sha d0e402bd…40f3)ed90e20c) -> 0; cmp -> 0 (byte-identical)SYNC-RETIRED: skills/beta -> retired/2026-11-11/skills/betaSYNC-RETIRED: extensions/redact/liband the managed file MOVED out8b5d2c5dce56fe6d253dRound 4 — head
56fe6d253don liveed90e20cRun: LR-20261111-014 · one commit, trailer
Lab-Run: LR-20261111-014, author ld-implementer · no independent walk: implementer-run checks only. (The PR body is historical; its banner names this head; this comment is authoritative.)Round 4 answers seat B5's two HOLD findings on
8b5d2c5dcut -d/ -f1-2), soextensions/redact/lib/x.tsbelongs to itemextensions/redactand a still-managed subtree can never be retired as a phantom item. Reproduced B5's exact scenario before the fix (wholeskills/andextensions/trees retired on cycle 2); after the fix: nested file mirrored, two cycles, zero retire moves, manifest-sha stable. The guard'ssync[014]harness gains this case (assertionsanidado-*).All round-1..round-3 fixes remain in place (state-driven retirement; forgejo only while present; two-phase atomic failed pass; El Director's empty/missing ruling with changed-sibling assertions; rm+
-deletepins; both FAIL lists pasted, this time generated from the run logs, not retyped).The guard (FULL run, same runner,
bash scripts/gits-sbx-epic-test.sh) — NEW FAILURES: 0VERDICT: APPROVE
MODEL: deepseek-flash · SESSION: LR-20261111-014-seatA-20261111T1205Z
HEAD:
56fe6d253dFINDINGS: none
Checks run (fresh clone of
56fe6d25into /tmp, base liveed90e20c; diff 4 files +949/-10 as declared):ed90e20crender (sha256 d0e402bd606a27da77c451a275190819656c96a79ea6dc8f2e7bba9a342740f3 both sides). sbx-gits and every other app under core/manifests/apps also render byte-identical to live; only sbx-lab differs, as intended.Sync-script invariants (sidecar args extracted from the manifest, paths overridden in /tmp, one cycle per run):
PR body: Done-when 1 marked proven with test names (sync[014], render, grep -c subPath = 0); Done-when 2 honestly marked NOT YET / gated on the recreate and out of this PR's scope; the "no independent walk: implementer-run checks only" line is present; the guard section carries both FAIL lists (live baseline 721 checks / 16 FAILs, this head 722 checks / 16 FAILs, the only added check being the new sync[014] PASS) with NEW FAILURES: 0. I did not run the FULL guard, as instructed. The guard's new sync[014] section is genuine: it extracts the REAL script from the manifest, overrides DST/SKILL_SOURCES/EXT_SOURCES, runs one cycle, and asserts each invariant I verified by hand, with red mutations for sha, the resources line, subPath, INTERVAL, rm, find -delete, image, readOnlyRootFilesystem, env and a dropped container.
VERDICT: APPROVE
MODEL: claude-opus-4-5 · SESSION: LR-20261111-014-seatB-20261111T1205Z
HEAD:
56fe6d253dFINDINGS: none
Judge of the diff, tests and guard only (no comment/review read). Fresh clone in /tmp, base live
ed90e20c47. Head confirmed on refs/pull/934/head; change set matches: 4 files, +949/-10.CHECKS (exit codes)
bash -n scripts/gits-sbx-epic-test.sh -> 0
kubectl kustomize core/manifests/apps/sbx-lab -> 0 (16983 lines; ConfigMap/sbx-lab-skills present, skill-sync container present, subPath in render = 0)
kubectl kustomize core/manifests/apps/sbx-sendrast -> 0, and cmp vs the origin/live render -> 0 (BYTE-IDENTICAL, sha256 d0e402bd… both sides)
SHA256SUMS self-consistency of skills-lab.yaml: de-indented ld-sync-probe.SKILL.md -> b7514e60fdd52bea63f466a77f9109530271808dc62cc835dc506256b6a37091, equal to the declared line -> consistent
grep -c subPath on non-comment lines of statefulset.yaml -> 0 (all six textual hits are comments)
rendered StatefulSet structure: every volumeMount resolves to a volume or volumeClaimTemplates (work), no duplicate mountPath in seed-pi-agent/sandbox/skill-sync, no subPath anywhere
guard allow-list delta: want_vol '+sbx-lab-skills', want_mnt '+/opt/sbx-skills-lab' and nothing else; the added container's census is pinned as an EXACT literal (name + full image@digest + whole securityContext) which matches the rendered skill-sync byte for byte, so an image bump or a privilege change still goes red. The two removed diff -q lines are refactored into p1_drop_ctr + the pinned census, not dropped. No check was loosened or turned into a skip.
SYNC-SCRIPT INVARIANTS (script extracted from the manifest, paths overridden, one sync_cycle per run — my own fixtures, not the guard's)
per-file atomic tmp+mv: proven by an open fd — a reader holding fd 9 across the pass still read 'probe-line: v1' while the path read 'v2-CHANGED'; 0 leftover .tmp. under the tree after every cycle.
retire is a state-driven MOVE: a vanished skill -> retired/2026-11-11/skills/skill-two (file intact in retired, gone from the live tree).
item = FIRST TWO path components: with a NESTED relpath extensions/redact/lib/x.ts managed across two cycles, NO phantom item extensions/redact/lib was derived and NO retired/ directory was created — the still-managed subtree was never retired (the ${p%/*} failure mode is gone; cut -d/ -f1-2 is used).
stray file in a managed dir NOT moved: extensions/redact/STRAY.txt, extensions/forgejo/STRAY2.txt and skills/skill-one/extra.md all survived, none retired.
two-phase validation: MISSING source and EMPTY source each -> rc=1, one clear SYNC-FAIL line naming the dead source plus the 'leaving last good set' summary, a CHANGED healthy sibling (extensions/redact/index.ts v2) NOT written, MANAGED-VERSION unchanged, state file unchanged, 0 retire moves.
forgejo mirrored only while its directory exists: with extensions/forgejo removed the pass SYNC-WARNs, does not recreate it, and drops it from state; rc=0.
idempotency: second identical cycle 0 SYNC-UPDATED.
no rm / -delete on any non-comment line of the extracted script; INTERVAL=60 present.
PR BODY
'no independent walk: implementer-run checks only' present and honest. Done-when 1 marked proven with the named checks; Done-when 2 honestly marked NOT YET (gated on the pod recreate, declared out of scope for this spec); 3 and 4 marked PROPOSED / at close and out of this repo. Guard section states both runs (live 721/16, head 722/16) and NEW FAILURES: 0, and discloses that it was written for round-1 head
05a29c5dwith the raw FAIL lists carried in the round-4 status; the body does not pretend to be current, which is the honest form, and I did not read that comment by rule.Landing notes for whoever merges: this is an ordinary-class change and needs CI green on the exact head
56fe6d25; the Done-when 2 proof still rides the held sbx-lab recreate and is not claimed here.VERDICT: APPROVE
MODEL: deepseek-flash · SESSION: LR-20261111-014-seatB-20261111T1211Z
HEAD:
56fe6d253dFINDINGS:
05a29c5dand says so; its guard section (baseline 721 checks/16 FAILs, head 722 checks/16 FAILs, NEW FAILURES: 0) is therefore round-1 evidence, and the current-head lists live in the round-4 comment, which this seat did not read. Checkable: body line "CURRENT (final) HEAD:56fe6d25— this body was written for round-1 head05a29c5d". I re-established the load-bearing render baseline myself at56fe6d25instead (last TEST line), so nothing here blocks landing.TEST:
git ls-remote refs/pull/934/head ->
56fe6d253d(matches the required head; base liveed90e20c) -> 0bash -n scripts/gits-sbx-epic-test.sh -> 0
git diff --stat
ed90e20c56fe6d25-> 4 files, +949/-10 (matches the declared change set) -> 0kubectl kustomize core/manifests/apps/sbx-lab -> 0 (renders; ConfigMap/sbx-lab-skills present;
name: skill-syncpresent once; 0 subPath in the render)kubectl kustomize core/manifests/apps/sbx-sendrast (head) vs same on a fresh origin/live clone @
ed90e20c-> 0 (byte-identical, diff empty)awk de-indent recipe from skills-lab.yaml header | sha256sum -> b7514e60…37091 = SHA256SUMS body (" ld-sync-probe.SKILL.md", two spaces) -> 0
grep -vE '^:space:#' statefulset.yaml | grep -c subPath -> 0 occurrences (grep exit 1, which is the required "none")
sync script extracted from the manifest, paths overridden in /tmp, one sync_cycle per run: healthy pass -> SYNC-OK, 4 paths, MANAGED-VERSION + state written, sessions/f.json and settings.json byte-untouched, no .tmp. left behind -> 0
nested relpath extensions/redact/lib/x.ts mirrored, two further cycles -> SYNC-OK both, zero SYNC-RETIRED, no retired/ dir created, file still on the volume -> 0
stray hand-dropped file inside a managed dir (extensions/redact/hand-dropped.ts) -> still there after 2 cycles -> 0
EMPTY source (skills-b loses SKILL.md) while the healthy sibling CHANGED in the same pass -> rc=1, one clear "missing or empty" line, skills tree byte-identical (diff -r), sibling sha unchanged, state unchanged, MANAGED-VERSION unchanged, nothing retired -> 1 (refusal is the required outcome; zero writes)
MISSING source (skills-a removed) with the sibling changed again -> rc=1, same guarantees, state and MANAGED-VERSION unchanged, nothing retired -> 1
forgejo mirrored while extensions/forgejo exists; after removing that dir the sidecar logs SYNC-WARN and does NOT copy it back, pass still SYNC-OK -> 0
whole-item retire: skills/skill-c vanishes while its source stays non-empty -> SYNC-RETIRED: skills/skill-c -> retired/2026-11-11/skills/skill-c (a MOVE, item = first two path components) -> 0
file-level retire inside a still-managed item (extensions/redact/lib/x.ts dropped) -> only that file moved to retired//extensions/redact/lib/x.ts, index.ts and patterns.ts kept, no phantom item retire -> 0
grep -vE '^:space:#' on the extracted script for 'rm' and '-delete' -> 0 and 0; INTERVAL=60 present -> 0
guard baseline reproduced independently on the REAL renders (p1_lab_sts_bad(live.sts, head.sts, seed, 'TMPDIR GOTMPDIR', '', 'skill-sync')) -> empty failure string (GREEN), and WITHOUT the sixth arg it goes red (spine-distinto contenedores-distintos), so the exception is load-bearing not blanket; mutations image-bump / readOnlyRootFilesystem=false / sidecar-dropped each go red with sync-censo -> 0
(FULL guard not run, as instructed: the recorded runs report NEW FAILURES: 0.)
VERDICT: APPROVE
MODEL: deepseek-flash · SESSION: LR-20261111-014-seatA-20261111T1217Z
HEAD:
56fe6d253dFINDINGS: none
TEST: git ls-remote refs/pull/934/head vs refs/heads/live -> 0 (head 56fe6d25..., base live ed90e20c...)
TEST: bash -n scripts/gits-sbx-epic-test.sh -> 0
TEST: kubectl kustomize core/manifests/apps/sbx-lab -> 0 (renders ConfigMap/sbx-lab-skills + StatefulSet with skill-sync)
TEST: kubectl kustomize core/manifests/apps/sbx-sendrast + cmp vs render of base
ed90e20c-> 0 (byte-identical, sha256 d0e402bd606a27da77c451a275190819656c96a79ea6dc8f2e7bba9a342740f3 both sides)TEST: SHA256SUMS self-consistency of core/manifests/apps/sbx-lab/skills-lab.yaml (header awk recipe | sha256sum, plus sha256sum -c) -> 0 (b7514e60…091 both computed and declared)
TEST: grep -v '^:space:*#' core/manifests/apps/sbx-lab/statefulset.yaml | grep -c subPath -> 0 occurrences (grep exit 1 = no match), same on the rendered render
TEST: extracted sync script, healthy cycle -> 0 (SYNC-OK, 4 managed items, state + MANAGED-VERSION written)
TEST: per-file atomic tmp+mv (reader holding the old fd kept old bytes, fresh open saw the new file, inode changed, no .tmp. left in the managed tree) -> 0
TEST: nested relpath extensions/redact/lib/x.ts, two cycles -> 0 (SYNC-UPDATED on cycle 2, cycle 3 SYNC-OK, no SYNC-RETIRED, no retired/ dir, nested file kept: item = first two components)
TEST: stray file extensions/redact/STRAY-BY-HAND.ts in a managed dir across two cycles -> 0 (untouched, never on the manifest)
TEST: retire of one nested file -> 0 (SYNC-RETIRED-FILE to retired//extensions/redact/lib/x.ts, index.ts + patterns.ts + lib/ kept)
TEST: whole-item retire -> 0 (SYNC-RETIRED: skills/skill-b -> retired//skills/skill-b, sibling skill-c kept, nothing deleted)
TEST: EMPTY source (skills-b emptied) with the healthy sibling skill-a CHANGED in the same pass -> cycle rc 1, exactly one clear "SYNC-FAIL: is missing or empty; leaving the current set untouched", state file + MANAGED-VERSION + skills/extensions tree byte-unchanged, no retired/
TEST: MISSING source (skills-a removed) with the healthy sibling skill-b CHANGED in the same pass -> cycle rc 1, same guarantees (state/version/tree unchanged, no retire)
TEST: forgejo mirrored only while dst/extensions/forgejo exists -> 0 (SYNC-WARN + not re-created; second cycle still SYNC-OK), switch-off state consistent
TEST: comment-stripped rm count and -delete count in the shipped script -> 0 and 0; INTERVAL=60 present; sources only the four mounts; sessions/ and settings.json never named
TEST: rendered pod script (args of the rendered skill-sync container) vs the script I exercised -> 0 (diff empty: the pod runs the tested script)
TEST: p1_lab_sts_bad(live render, head render, seed, "TMPDIR GOTMPDIR", '', skill-sync) -> 0 with empty reasons [] (census pin literal matches the real container); same call WITHOUT the sixth argument -> " spine-distinto contenedores-distintos"; image / readOnlyRootFilesystem / container-drop mutations -> " sync-censo"
TEST: guard's own sync014_yaml_bad, sync014_sts_bad, sync014_ro_bad on the real files -> empty output (pass); ro_bad prints sbx-skills, sbx-skills-phase1, sbx-lab-skills, sbx-redact-ext, sbx-forgejo-ext all :true in sandbox and in skill-sync
TEST: PR body -> Done-when 1 marked proven with test names (sync[014] + render), Done-when 2 honestly NOT YET (gated on the recreate), 3-4 marked PROPOSED/out of scope; "no independent walk: implementer-run checks only" present; guard section carries both FAIL lists' counts (721/16 vs 722/16) and NEW FAILURES: 0; body is self-declared stale for round-1 head with a CURRENT HEAD banner
NOTE: no comments/reviews were read for this verdict (El Director's standing rule); full guard not run, per instruction.