Open
opened 2026-11-04 09:49:09 +00:00 by kayg
·
113 comments
No Branch/Tag specified
main
j470b-server-mesh-rejoin
mesh-compat-382
j444b-d381-key-wins-flood
j461b-macos-utun-reader-thread
j475b-remove-host-motion
fix-reconnect-new-link
j481-d380
dev
j479-mesh-soak
fix/j476-host-facts
j475-motion-pill-split
j470-mac-node-join
root-upgrade-d376
j474-review-merge
latency-targets-120hz
nightly-suite-shards
gotatun-pin-j469
macos-utun-reader-thread
fix-flood-echo-stall
wip/j416
motion-fast-ease-out
mesh-addhost-j259
takeover-close-terminals-d368
kbd-instant-followups
fix-trust-key-double-click
nightly-suite-partial-report
appwin-icons-j207
rusqlite-slice1
swipe-content-first-d374
mesh-mtu-381
addhost-instant-open
glass-contrast-d365-mock
glass-contrast-d365
signout-followups-442
fix-startup-toast-red
research-gotatun-matrix-j317
themes-sync-j304
fix-load-flakes
fix-nix-zig-patch
fix-compute01-browser-baseline
ratchet-size-baseline-419
predict-auto-d130
revoke-coverage-d367
nightly-suite-queue-timeout
nightly-browser-suite
fix-rustsec-2026-0459
fonts-ioskeley-obel
log-retention-d375
fix-kbd-no-anim-427
fix-dev-red-specs-2
takeover-proof-305-j205
wip/j410-server-pump
ui-consistency-d372
fix-osc52-clipboard
libghostty-vt-1.0
forget-retired-host-d369
j404-merge-426
fix-setup-lock-race
fix-touch-select-426
revoke-access-report-ui
fix-predictive-echo-specs
fix-selinux-agent-exec
wip/j395-checkpoint2
wip/j395
j391-review-scratch
ui-consistency-j195-deferred-rebased
fix-rustc-paths-421
decisions-wording-341
fix-install-suite-red
fix-dev-rtt-specs
fix-pending-removal-align
fix-privileged-skip
fix-install-suite-404
review-j368
mesh-dns-j254
revoke-access-report-d356b
revoke-privilege-round5
fix-removal-confirm
review-j360
gotatun-rotation-367
deps-refresh-340
review-j354
review-J351
review-j350
addhost-takeover-d359
revoke-report-items-347
revoke-end-sessions-d362
review-j348
size-mesh-ssh-346
size-trim-337
revoke-access-diff-d356
j259-pre-rebase-b06c5a6
j305-ended-at-start-spec
addhost-sudo-consent
fix-layout-real-specs-411
fix-shell-open-411
fix-console-hosts-373
fix-shell-exit
fuzz-host-name-353
fix-host-details-410
macos-filter-j197
research-gotatun-tricks
revoke-p3-j216l
stale-state-pill
revoke-followups-j216k
revoke-progress-j216e
backup/macos01-20261106/lume/j197
backup/macos01-20261106/lume/appwin-icons-j207-stash0
backup/macos01-20261106/claude/stash-0
backup/macos01-20261106/claude/stash-1
backup/macos01-20261106/claude/J80-macos-s1
backup/macos01-20261106/claude/J80
backup/macos01-20261106/claude/J80-slice1-20261102
backup/macos01-20261106/claude/J16b-appwin-on-dev
backup/macos01-20261106/claude/appwin-launch-idle-146
backup/macos01-20261106/claude/J80-s1-run
wip/j259
trust-285-p3c
runtime-lock-j216f
mesh-ssh-p3-j175
tun-proof-p3b
trust-285-p3
terminal-polish-j271
tun-proof-p3
ssh-install-flakes-j216i
fuzz-fixes-j264
trust-285-a
trust-285-b
restart-oversized-fixture-j198
backup/macos01-20261106/lume/appwin-icons-j207
restart-bound-j216h
up-preflight-p3-j198
backup/macos01-20261106/lume/j251
up-preflight-j198
wip/up-preflight-j198-j245-p2
wip/up-preflight-j198-j245-p1
agent-fsync-offloop-j196
wip/up-preflight-j198-j245
settings-offscreen-j231
wip/up-preflight-j198-j226-inspection
wip/j103f
themes-j206
wip/up-preflight-j198-j226
flakes-j216c
backup/macos01-20261106/claude/j197
flakes-j216b
backup/macos01-20261106/claude/j197-red
wip/up-preflight-j198-review
flakes-j216
backup/macos01-20261106/claude/appwin-icons-j207
engineering-j208b
appwin-mock-j209
toast-stack-j165
engineering-j208
decisions-d351
decisions-d350
decisions-d349
appearance-presets-mock-j129
decisions-d348
docs-audit-spec
doc-coverage-baseline-raise
decisions-d347
decisions-d346b
decisions-d346
j88h-profile
ui-consistency-j195-deferred
ui-consistency-j195
engineering-practices-j194
preferred-login-ui-j166
signin-pools-263-j174
macos-node-265-s1
decisions-d345
decisions-d344
proxy-cgroup-j164
verify-quick-fuzz-329
proxy-start-io-j168
timing-flakes-j163
up-263-s1
mcp-events-j147
verify-quick-fuzz-j158
proxy-p3-j159
agent-resource-j157-detect
wip/j103d-final
remote-cancel-hang-j155
proxy-traefik-281-j62d
j149-verify-quick
proxy-traefik-281-j62b
j150-drop-unnamed
wip/j79d-red-r4
wip/j103d-d332
rootless-toast-j142
wip/j103d-head
wip/j103d
j125c-mcp
macos-build-j136
backup/macos01-20261106/claude/j136
wip/j79d-red-cli
wip/j79d-red-server
j121-link-liveness
j118c-tracer-failures
wip/j79c-head
release-desktop-j131
j124c-signed-macos
wip/j79c-red-p3
wip/j79c-red-p23
wip/j79c-red-p22
merge-j128-check
perf-deferred-j128
sidebar-toggle-260
j127-shortcut-changed-dot
j125-mcp
j118-tracer-failures
j126-account-summary
settings-sections-mock-j92
j120-pinch-lock
sidebar-292
backup/macos01-20261106/claude/j120
taste-2026-11-03
row-actions-242-215
backup/macos01-20261106/claude/stash-0-untracked
backup/macos01-20261106/claude/J80-265-s1-run
backup/macos01-20261106/claude/J80-macos-s1-20261102
j112-canvas-commands
ui-no-overshoot-scroll
fix-recovery-restart
backup/macos01-20261106/claude/stash-1-untracked
taste-2026-11-02
size-baseline-ratchet
machine-cards-structure-j108
j109-boot-copy-mockups
fix-dev-red
wip/up-263-s1
j95b-settings-rows
proxy-traefik-281
j95-option-rows
docs-human-j91
docs-keys-palette
fix-flood-1013-270-j38d
mesh-ssh-s1
mesh-ssh-s0
addhost-d291-tunnel
test-stability-load
mesh-relay-220
repro-296
shim-thp-off-24
mesh-pins-258-v2
mesh-dns
signin-d311-merged
docs-pages
ui-size-283
fix-ui-batch-specs
wip/j12b-edge-update
gate-on-arm
fix-rename-sync-293
trust-285-j60b
fix-focus-ring-clip
integ-ui-1101
edge-update-v2
signin-d311
taste-2026-11-01
layout-per-tab-250
ui-approved-splitopt-224
ui-approved-splitopt-224-on-dev
icon-text-centre-282
ui-approved-189-212-216
research-reverse-proxy
transfer-budget-320
review-mesh-pins-258-r2
sidebar-pill-259
review-mesh-relay-220
wip/j12-edge-update-v2
nix-package
status-crash-loop-237
fix-dev-test-failures
fix-fixture-socket-path
wip/mesh-relay-220-pre-dev-advance-rebase
fix-canvas-side-clip
research-head-to-head
frosted-real-256
review-mesh-pins-258
wip/mesh-relay-220-pre-final-rebase
review-layout-per-tab-250-r2
broad-suite-triage
nix-devshell
review-status-crash-loop-237-r3
wip/nix-devshell-pre-p3
taste-frosted-real
wip/code-serve-demo-2026-10-30
wip/code-serve-glass-2026-10-30
wip/code-serve-clusters-2026-10-30
wip/code-merge-release94-dev-2026-10-30
wip/rproxy-delta-scratch-2026-10-30
wip/stale-note-257-staged-2026-10-30
wip/d241-size-staged-2026-10-30
review-broad-suite-207
wip/mesh-pins-258-pre-split
wip/mesh-relay-220-pre-rebase
mesh-measure-220
plan-211-update
review-edge-autoupdate-r3
perf24-reconnect
review-stale-note-r2
release-prod-live
d241-shell-marker
review-mesh-pins-server-258
review-revocation-r3
review-setup-token-r2
review-sync-journal-r3
wip/review-mesh-pins-258-prune-review-1995fee46
frosted-scroll-256
sync-journal-r3
revocation-per-account-wip
review-tokio-asyncfd-0386
review-sync-p3s
wip/stash-8by64-stash0-2026-10-30
mesh-drag-r2
setup-token-data-wip
release-9-4
auth-busy-1013
mesh-view-overlap
sync-p3s
review-auth-busy-wake
review-readd-r8-wip
mesh-drag-bounds
audit-auth-93
app-windows-stream-start
review-authority-cap-r3
app-windows-r7-measure
readd-minimal-r6
readd-minimal-r5
app-windows-r7-soak
readd-minimal-r4
app-windows-r7
app-windows-r6
readd-minimal-r3
readd-minimal-r2
readd-minimal
merge-release8-dev
review-busy-page-liveness-r2
review-busy-page-liveness
mesh-view-trim
review-authority-read-retry-r2
review-remove-uninstall-r2
release-retry-reattach
release-retry
review-remove-uninstall
review-authority-read-retry
app-windows-ship
mesh-autoswitch-p2
mesh-ssh
mesh-relay
mesh-macos-node
screen-and-control
session-labels
review-real-logins-r5
review-real-logins-r4
review-real-logins-r3
shim-memory-r2
review-real-logins-r2
shim-memory
shadow-addhost-instant
shadow-real-logins
shadow-same-session-flood
shadow-prod-perf-fix
hosts-from-server
app-windows-real
app-windows-transport
release-prod-6w
mesh-policy-verify
quiet-close-toast
dev-merge-r4
review-mesh-agent-transport
review-app-windows-transport
settings-roundtrip
review-release-pipeline-r4
mesh-agent-transport
ended-strip-fix
review-audit-ca-update-d244
settings-rows
phone-width
signout-clear
fix-restart-tests
dev-merge-rp
signer-hardening
review-audit-ca-update
terminal-fidelity
review-audit-ca-update-wip
mesh-integrate
review-mesh-agent-transport-515d3712
review-mesh-dataplane-feec1988
mesh-dataplane
signout
research-canvas-node
wip/review-renderer-cost
wip/review-terminal-fidelity
padlock-glyph
review-release-pipeline-2e95b689
phone-polish
sidebar-polish
inbox-generic
review-mesh-dataplane-378d4055
prod-batch
ui-inbox
ui-ca-unlock
ui-machine-sections
review-mesh-dataplane-a5f744fb
tls-session-prod
session-bounds-prod
fix-dead-session
session-bounds
tls-session
palette-polish
shortcuts-d222
research-appmeta
phone-gutter
ui-centre-button
wip/ui-centre-button
wip/tls-session
review-addhost-breakit
ui-platform
renderer-cost
remote-agents
mesh-api
ssh-bootstrap
prod-hotfix2
prod-fixes
review-remote-agents
review-mesh-dataplane
session-life
research-browser
review-mesh-api
research-capture
review-mesh-api-round3
review-mesh-api-round1
gate-stability
review-gate-stability
review-relay-direct
review-mesh-api-findings
proto-sessions
font-picker
proto-addhost
proto-windows
mesh-core
latency-gate
research-bsd
research-discovery
keytray-mods
mock-core
ui-tweaks
fix-motion-sel
fix-motion
integrate2
fix-robust
t-web
t-server
vt-ghostty
fix-snap-echo
t-web-auth
review-vt2
t-agent
lane-protocol
lane-shim
lane-auth
s1-base
s1-contracts
s1-workspace
No results found.
No labels
launch-qa
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set
Reference
kayg/infraternal#338
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Job J178: pre-deploy review of origin/dev (
5d33f7cdd) against production's revisione0633114b.Scope: the combination of the merges since
e0633114b(#263infraternal updf3e69727, J157 #324 terminal launch timeout81c432ef7, J159 proxy P3sf14e4309a, J168 proxy start IO4fd5adf0c, decisions-only merges D336–D344):cli/actions.rsin #263);e0633114bwith live sessions;Plan (~2.5 h): read diff and merges (30 min) → build both revisions on a VM (45 min, parallel) → migration run on a DB copy, upgrade run with live sessions, budgets,
verify fast(60 min) → verdict.No branch is changed and nothing is deployed. Verdict (GO / NO-GO) will be posted here.
Start. Reviewing
origin/dev=5d33f7cdd5ab0ae7527befde92835f4b342556b5against productione0633114b. No branch of my own, no code changes.Work on infraternal-dev-01-compute01, in
~/j178(logs) and~/worktrees/j178(a detached checkout of dev). It is queued through build-slot:deploy-e0633114b-result;post.sh: (A) the production binary on an untouched 0038 DB copy, as a control; (B) the dev binary migrating a copy of/var/lib/infraternal-test.bak-20261104T004406(exactly migrations 1–38, like production) with a seeded 15-minute invite row; (B2) a restart on the migrated copy; (C) a rollback probe, the production binary on the migrated DB; (D)transfer-budget.tsagainst both shipped web roots; (E) the real restart specs (restart.spec.ts,restart-three-session.spec.ts), where a wrapperINFRATERNAL_BINstartse0633114bplus its web, and the restart brings up the dev binary plus dev web against the same live PTYs;scripts/verify faston dev.Note on the test server: its host key was in no known_hosts I could reach. I pinned ED25519
SHA256:9ecLFuTqog/XaV6EfGQU/wkrrhkxblLf1bNlW0AMRlkin a per-job known_hosts. Only the two backup sqlite files were copied off it (notremote-ca). Nothing on the server was changed.Progress (code reading done; the runs are still queued on compute01 at position 3, since every slot is held by long browser runs).
Read so far, to be confirmed by the runs:
git show --remerge-diffis empty fordf3e69727,81c432ef7,f14e4309aand4fd5adf0c, so none had a conflict on dev. The onlycli/actions.rschange in range is844ff8ed7, which replaces the oldupplaceholder (.not_available_yet()) and drops nothing else.remote_join_tokens; the table has no indexes, triggers or inbound FKs to lose (git grep remote_join_tokens -- migrations/).bak-20261104T004406is at exactly 1–38 (integrity ok), the same shape as production.bak-20261104T081403and the live test DB carry 43–47 from21d4f71c9(branchapikey-actions-303, not on dev). → P3: dev cannot be deployed to the test server as-is (sqlx "previously applied but missing"); this is not a production issue. To be proven in the run.?device_code=links from installed CLIs still parse, anddevice-start {device,scopes}is still accepted./auth/device-approveis removed; the service worker is network-first, so only a tab left open across the deploy fails one approval until it reloads (P3, informational)./var/lib/infraternalis the server's root:root 0700--datadir.sudo infraternal upthere runsprepare_system_layout("/")(up.rs:367: chmod 0711, addsbin/andagent/) and enrolls, and only then refuses the existing/usr/local/bin/infraternal → /opt/infraternal/e0633114b/infraternallink, leaving a half-installed machine inside the server's state dir.Verdict: GO: deploy
origin/dev=5d33f7cdd5ab0ae7527befde92835f4b342556b5to production (replacinge0633114b)No P1 or P2. Four P3s, none of which blocks the deploy. Each claim below was checked with a run on infraternal-dev-01-compute01 (VM clock CET; IST = +4:30). Logs are in
~claude/j178/.Evidence
1. Same build recipe as production.
nix build git+file:///home/claude/infraternal?rev=5d33f7cdd…uses the same derivation as~/deploy-e0633114b-result:exit=0,SOURCE=5d33f7cdd5ab0ae7527befde92835f4b342556b5.e0633114bshare/infraternal/infraternal(server)infraternal-x86_64-musl2. Merges and conflict resolutions.
git show --remerge-diffis empty fordf3e69727(#263),81c432ef7(J157),f14e4309a(J159) and4fd5adf0c(J168): none had a conflict on dev.cli/actions.rsis touched in this range only by844ff8ed7, which replaces the oldupplaceholder (.not_available_yet()); no other action is removed. The two proxy merges fit together: J168 creates the custody dir lazily on firstput, and J159 widens orphan matching to sibling<digest>/bin/traefikreleases. J157 is local to the infraternal agent (30 s launch bound, launcher exit = failure) with no wire change.3. Migrations on a production-shaped DB (
post2.log). Source: a copy of/var/lib/infraternal-test.bak-20261104T004406, which is at migrations 1–38 exactly like production (integrity ok). I added one 15-minuteremote_join_tokensrow so the 0040 copy path runs on real data.healthz=yes.migrations [(36,1),(37,1),(38,1),(39,1),(40,1)],healthz=yes after 126 ms,integrity ok,fk_violations []. The seeded invite survives (900s, key digest kept). The 3 existing approvals become('sign_in', 0, 3). Counts are unchanged (users 1, sessions 12, approvals 3).healthz=yes.healthz=yes. Going back toe0633114bafter the deploy works.4. Upgrade with live sessions (
upgrade-run.log,upgrade-stalled.log). I used the real restart specs (real/restart.spec.ts,real/restart-three-session.spec.ts; the harness is unchanged sincee0633114b).INFRATERNAL_BINwas a wrapper that startse0633114b+ its web and, on the restart, swaps in the dev binary + dev web on the same data dir and live PTYs. The browser keeps the old JS across the swap, as a real open tab would.All 4 tests that cross the
e0633114b→ dev swap pass. "retained route, CA locked/unlocked" fails before the restart, and fails identically for production→production and dev→dev (ctrl-old.log,ctrl-dev.log:expect.poll … data-status === "connecting"at restart-fixture.ts:243). That's an existing fixture problem on compute01, not this deploy.5. JS budgets (dev's
transfer-budget.tson both shipped web roots;budget_exit=0for both):e0633114bCritical has 8.8 KB gzip / 7.2 KB brotli of headroom left (2.7 %).
6. Gate:
scripts/verify faston5d33f7cdd(compute01,fast.log), verbatim:Static rows (vs the D243 baseline, report only): x86_64
23373616 bytes; baseline 22418256; delta +955360, aarch6421244048 bytes; baseline 20453280; delta +790768.7. Things I checked and found fine:
?device_code=links printed by installed CLIs still parse (approval-link.ts), anddevice-start {device, scopes}from old CLIs is still accepted. Per-address auth budgets for IPv4 are unchanged; behind the proxy, the source is still the trustedX-Forwarded-For. The newframe-ancestors 'none'/X-Frame-Options: DENYapplies only to the static web router, and the app frames none of its own pages. No decision in D336–D344 sets a deploy condition.Findings
P3-1: dev cannot go on the test server as it stands. The live test DB and
bak-20261104T081403carry migrations 43–47 from21d4f71c9(apikey-actions-303, not on dev). Run: dev binary on a copy →The server stopped after an error. Why auth initialization failed, migrations[…38, 43, 44, 45, 46, 47]. Production is unaffected (it's at 38). To try dev on the test server, deploy a dev that already has #303 merged, or reset it frombak-20261104T004406.P3-2:
infraternal upon the server's own host takes over the server's data dir. On 10.69.69.89, production runs as root with--data /var/lib/infraternal(root:root 0700,StateDirectory=infraternal), and/usr/local/bin/infraternal → /opt/infraternal/e0633114b/infraternal. That is the baseupuses for its own install.run_helper_innercallsprepare_system_layout("/")(up.rs:367: chmod 0711, addsbin/andagent/) and enrolls into/var/lib/infraternal/agentbeforeinstall_system_servicerefuses the foreign PATH link. The result is a half-installed machine inside the server's state dir, and a host row for an infraternal agent that never starts. This only happens if someone runssudo infraternal upon the server host, so it doesn't block the deploy. Fix direction: refuse when the base holds a server database, before any change. A run-based proof (scratch test calling the real functions on a production-shaped root) is queued as root on compute01; I'll add its result below.P3-3: a tab left open across the deploy fails one CLI sign-in approval.
/auth/device-approvewas removed (94071631b, nowdevice-approve-start/finish). The service worker is network-first, so a reload or the link itself gets the new page. Only an already-open old tab hitting Approve fails, once.P3-4 (test infrastructure): the restart fixture's remote listener is a fixed port (
TRACER_TEST_PORT+1000, 5299 by default). A fixture whose stop fails leaves a server holding it, and every later restart on the VM fails withserver listener failed. My first wrapper run did exactly this (my mistake: the wrapper neededrsync, which the dev shell doesn't have). Those 8 orphaned servers and their shims held 5299 from 12:31 to 12:38 CET, until I killed them by PID. A restart spec from another job on compute01 in that window may have failed for this reason.Not done / limits
nice -n19outside build-slot, after checking load, rather than queue each one for about an hour. The nix build,post.shandverify fastwent through build-slot.SHA256:9ecLFuTqog/XaV6EfGQU/wkrrhkxblLf1bNlW0AMRlkper job.P3-2 proven by a run (root, throwaway tree
/root/j178-scratch=5d33f7cdd+ one uncommitted scratch test, now deleted). The test calls the realprepare_system_layoutand theninstall_system_service, inrun_helper_inner's order, on a temporary root shaped like production's host:var/lib/infraternal0700 holdinginfraternal.sqlite, andusr/local/bin/infraternal → /opt/infraternal/e0633114b/infraternal.So
upwidens the server's data dir to 0711 and addsagent/andbin/inside it before refusing (in production the enrollment intoagent/happens between these two calls). Still P3: it needs someone to runsudo infraternal upon the server host. The verdict stays GO.Cleanup: the
/root/j178-scratchtree and target, the gate target (~/worktrees/j178/target, 45 G) and every DB copy are deleted, and my orphaned fixture processes are killed. Logs stay in~claude/j178/. No branch was changed and nothing was deployed.J198 starting: branch
up-preflight-j198, base2198148d7(current origin/dev). Plan (~3 hours): reproduce read-only preflight failure and restart PermissionDenied on compute01; test-first transactional root install fix, atomic commit; isolate restart fixture directories/ephemeral listeners and child cleanup, parallel proof, atomic commit; quick then fast gate, root throwaway-clone proof, cleanup targets. All waits foreground; no merge/deploy. Existing #338 records this request.J198 evidence so far: current dev base
2198148d7.up.rsstill mutates layout beforeinstall_path_linkrefuses; regression tests now cover server data mode/content preservation, foreign CLI read-only refusal, existing machine refusal, and rollback after service failure. Baselinerestart::reproduction is queued foreground on compute01 (queue position 3; all four slots busy; dev02 also full). Logs:~/j198-evidence/baseline-restart.log; one target:~/worktrees/j198/target. Shared HOME restart fixture directories are claude-owned; no foreign-owned descendants found. No port-5299 processes seen; two unrelated j93fixture processes belong to that job and were left alone. Root SSH works for the requested throwaway-clone proof. Honcho retrieval failed with HTTP 400 (no valid session ID); issue #338 remains the record.J198 checkpoint: pushed WIP
88f061094onup-preflight-j198(regression tests only, will fold into fix 1). Baseline compile caught a test-onlyMetadataExtimport mismatch, corrected before pushing; rerun is foreground queued on compute01 after the cold dependency build. Production code remains unchanged. J195's~/j195-base.shsetsINFRATERNAL_TEST_STATE_ROOT=/tmp/j195s; restartDirectory::newcurrently places replacement executables there, andOpened::open_withrejects world-writable ancestors (/tmp1777). Shared HOME paths were claude-owned, with no root-owned descendants, and /tmp/j195s has already been removed. Next run records HOME versus /tmp baseline plus failing preflight/rollback tests, then implementation. Root clone prepared at/root/j198-proof, target remains only~/worktrees/j198/target. Logs in~/j198-evidence/.J198 red/green evidence: the server preservation test failed on original production code (
left 457= 0711,right 448= 0700); all four new preflight cases failed, and rollback failed with leftover["etc", "keep", "source", "usr", "var"]instead of["keep", "source"]. Logsred-server.log,red-preflight.log,red-rollback.login~/j198-evidence/. Implemented read-only preflight before approval and repeated in root helper, plus ownership of created inodes until confirmed service activation. The server preservation test now passes (green-server.log). Added unwind coverage preserving an existing empty base and owned CLI link. Full baseline restart comparison is foreground queued using the preserved original-code executable. No merge/deploy.J198 fixture root cause confirmed on original-code executable: with a candidate under
/tmp,Signed::openfailsKind(PermissionDenied)at restart_tests.rs:380 (red-restart-cause.log). With private executable ancestry separated from/tmpsocket state, the same real fd-exec test now passes (green-restart-one.log). This is distinct from port 5299: the Rust tests use Unix control sockets. No root-owned shared state or port-5299 orphan was found in this reproduction. Fixture changes also reuse per-test directories for recovery/signature tests and own subprocess groups through panic. Browser fixture requests127.0.0.1:0, reads back a private port, retains it across restart, and has exit/failure cleanup. Full baseline, parallel proof and gates remain pending behind build-slot queues. One target:~/worktrees/j198/target; no merge/deploy.J198 checkpoint: branch
up-preflight-j198, head8b6b9ae97(atomic commits70a5703e0for install safety and8b6b9ae97for fixture isolation). Full baselinerestart::ran on original production code: HOME = 19 passed;/tmp/j198s= 14 passed, 5 failed. That namespace excludes the two positiverestart_binary::audit_testscases behind the same PermissionDenied cause; the originalrestartfilter also includes those. The queued final foreground run repeats the full filter, then quick → fast, root clone proof, two parallel full restart suites, two simultaneous real fixture restarts, and two parallel browser restart runs. It also measures static size versus base2198148d7using the same single target. Full runner log~/j198-evidence/final-run.log, target~/worktrees/j198/target. Root clone/root/j198-proofis throwaway and will be deleted with targets. No merge/deploy.J198 checkpoint: both atomic fixes are pushed, head
7dcdcbeba5ff9f19862dce628e5bc9ac28a5b6econup-preflight-j198(commits59b10eaa9,7dcdcbeba). A final review foundinfraternal --json serverneeded process detection too; its focused regression failed before and passed after the parser fix, folded into fix 1.The foreground build-slot run on compute01 has been queued about 20 minutes, currently behind two jobs. Both VMs are busy. It will repeat the broader original
restartfilter to capture all seven PermissionDenied failures, then quick, fast, UID0 fake-server preservation/rollback proof under/root/j198-proof, two concurrent Rust restart suites, two concurrent real fixtures, two concurrent browser restart tests, and static-size comparison. Logs:/home/claude/j198-evidence, target:/home/claude/worktrees/j198/target. No merge or deploy. Continuing foreground polls until it finishes.J198 exact J195 reproduction, now confirmed with the broader
restartfilter against the frozen baseline production/test code from start-of-job dev2198148d7:28 passed; 0 failed; 1 ignoredINFRATERNAL_TEST_STATE_ROOT=/tmp/j198s:21 passed; 7 failed; 1 ignoredAll seven match the report: recovery listener/CA preservation, closed-stdin cold exec, real fd exec/rollback, reload after inode replacement, rootless marked replacement, admitted-inode immutability audit, and pathname-replacement audit. Logs show
Kind(PermissionDenied)during candidate admission. Executable fixtures inherited the socket-state root beneath world-writable/tmp; admission correctly refuses that ancestry. Existing home scratch directories inspected were claude-owned, with no root/foreign descendants. No port-5299 orphan was present during reproduction. The fixed fixture separates trusted per-user executable directories from short private socket directories; the independent browser fixture now selects an ephemeral remote port.Build-slot acquired at 18:56 IST after ~40 minutes queued. Quick is running on source identical to pushed head
7dcdcbeba5ff9f19862dce628e5bc9ac28a5b6ec; a non-fast-forward tracking-ref fetch refusal was corrected and the VM HEAD is aligned with a clean diff. Fast and real/parallel proofs follow. Full evidence logs:/home/claude/j198-evidence/baseline-all-{home,tmp}.log.J198 gate checkpoint: quick caught one
clippy::collapsible_ifin the new CLI-link preflight. Collapsed the condition and removed a stale function doc/allow attribute, folded into fix 1. Still exactly two logical commits, pushed:f8c0ff063transactional/read-only up installation54acc252899a0da66e169254c3d3712289986c97isolated restart fixtures (current head)First quick output, verbatim:
Evidence is retained in
/home/claude/j198-evidence/quick-clippy-red.log. The rerun is queued through build-slot on compute01; both VMs were full when selected. Continuing foreground polls. Final fast/root/parallel evidence is still pending, not claimed passed.J198 additional steer from J103f: make restart fixtures independent of debug executable size, keeping the production 512 MiB running-image and 128 MiB replacement bounds unchanged, as a third atomic commit.
Finding:
Signed::imageand several exec tests already strip replacement copies, but recovery, deleted/replaced install-path, closed-stdin and log fixtures still launch the original Cargo executable; several in-process control tests also snapshot that executable. Once it grows past 512 MiB, production correctly refuses it.Plan: add a runnable sparsely padded >512 MiB regression first, confirm the real failure, reuse the existing stripped-image helper for every self-exec/recovery launch and affected in-process test, then rerun quick/fast plus all root/parallel proofs on the three-commit head. Also explicitly check that production still refuses the oversized running image. Estimated implementation/proof setup ~30 minutes plus shared build-slot waits.
The prior pending rerun was stopped (foreground session exited 143), to avoid gates on a superseded head. The failing-first size run is now queued on compute01, logging to
/home/claude/j198-evidence/red-size.logandfinal-size-run.log. It will remain foreground through final verification.J198 crash-safety checkpoint: pushed WIP
765dbe557onup-preflight-j198, containing only the >512 MiB failing-first regression and access to the existing bounded child-output helper. The test is still queued, so this checkpoint is not a passing fix and is not the final third commit. Fixture implementation will follow the confirmed failure; the WIP will be amended into the requested atomic debug-size fix.Foreground run remains
/home/claude/j198-evidence/final-size-run.log; target remains/home/claude/worktrees/j198/target. No merge/deploy.J198 debug-size fix red/green confirmed. Three atomic commits are now pushed, final implementation head
586daae23049e57088552dc51dd69283a33577fa:f8c0ff063read-only up preflight and transactional installation1082599d9private restart paths, ephemeral ports, and child cleanup586daae23compact self-executed restart test images, without changing production boundsThe new regression extends a runnable ELF sparsely to 536,870,913 bytes. Before the fix it failed with
InvalidDatafrom snapshot admission and the resulting control-request hang bounds (RED_SIZE_EXIT=101). After routing every self-exec path and the affected in-process control tests through the existing compact-image cache, the same padded executable ran all 19 otherrestart::tests successfully (GREEN_SIZE_EXIT=0). Its separate direct call to productionOpened::running()still refused the oversized image:production oversized running-image refusal: preserved.No production admission bound changed. Evidence:
/home/claude/j198-evidence/red-size.log,green-size.log. Continuing quick, fast, root preservation/rollback, concurrent suites/fixtures/browser restarts, and static delta measurement in the held foreground slot.J198 current head:
730b3c4137648ecca9263f9f47a8e5daa4eb8606(three atomic commits). The additional regression's format lint is fixed; fmt/doc coverage/workspace clippy now pass.verify quickthen hit an independent runner defect:scripts/verify.d/quick.shinvokescargo test -p infraternal --lib --bins --locked, but infraternal is a bin-only package. Output, verbatim:I have not edited the gate. The foreground follow-up will reproduce the same Cargo target-selection failure on unmodified origin/dev, restore the branch, run
cargo test --locked -p infraternal --binsand tracer typecheck directly, then executescripts/verify fastverbatim and all real/parallel proofs. The runner now explicitly supplies claude's own XDG runtime/user bus, verifiedactive, rather than inheriting root's bus through runuser.Evidence target and log directory unchanged; no merge/deploy. Recommendation outside J198's implementation scope: quick should select the target kinds each changed package actually has, or omit the nonexistent
--libtarget for bin-only packages.J198 foreground gate checkpoint, unchanged pushed head
730b3c4137648ecca9263f9f47a8e5daa4eb8606.e8e8d2ff79b328a71faa62e7f764d705a133eeb4:error: no library targets found in packageinfraternal``. Branch restored cleanly.165 passed; 0 failed; 3 ignored; tracer check exit 0. All seven originally reported restart tests pass.scripts/verify fastis running verbatim. CLI compile-out, clippy, SSH helper, fixture-path, release component checks, and the complete workspace test row have passed. Workspace test row took 1407s on the shared VM; no failed-test lines. Inode-reuse probe reported SKIP because this filesystem did not reuse an inode in 256 attempts. Now running the separate shim process-per-user row; static builds and TypeScript remain.Root/parallel proofs and static delta comparison still follow in the same foreground slot. Logs
/home/claude/j198-evidence/fast.logandfinal-gates-with-quick-proof.log; target/home/claude/worktrees/j198/target. No merge or deploy.J198 finished. Branch
up-preflight-j198, head730b3c4137648ecca9263f9f47a8e5daa4eb8606, based on start-of-job origin/dev2198148d788c32b39d9ac604fc24c9cee6e79968. Three atomic commits are pushed:f8c0ff063: refuse running/installed server, existing infraternal agent, foreign CLI link, and unsafe destinations before install writes; record new inode ownership and roll it back on enrollment, copy, activation, or unwind failure. Preserve pre-existing directories, modes, files, and an owned CLI link.1082599d9: separate private trusted executable fixtures from short socket directories, choose ephemeral remote ports, and own child process groups/service generations through disposal, panic, and worker exit.730b3c413: execute snapshot-dependent restart tests from compact images instead of admitting the original debug image. Production's 512 MiB running-image and 128 MiB replacement limits are unchanged.Final gate:
nix develop -c scripts/verify fast, compute01, same job target as quick, exit 0. Full output is attached verbatim asfast.log. Rust summary and final output, verbatim:Quick limitation, proven on unmodified dev: quick's
cargo test -p infraternal --lib --bins --lockedfails because the package has no library. The same command fails on unmodified origin/deve8e8d2ff79b328a71faa62e7f764d705a133eeb4. No gate script changed. Quick passed scope/fmt/docs/clippy; directcargo test --locked -p infraternal --binsthen passed165 passed; 0 failed; 3 ignored, and direct tracer typecheck exited 0 before fast.Failing-first and root proof: the old install code failed the new fake-server regression by widening mode 0700 to 0711, and failed read-only/rollback regressions. Current
up::tests::ran as UID 0 in/root/j198-proofat this head:27 passed; 0 failed; 2 ignored. The real transaction functions reported:The root run also covered foreign CLI refusal, custom installed server detection, existing enrollment preservation, failed activation rollback, and unwind preserving pre-existing paths. The unwind panic in the log is deliberately caught by its regression.
PermissionDenied reproduction and all seven fixed: the frozen original restart code from base passed
28 passed; 0 failed; 1 ignoredwith home-based state. WithINFRATERNAL_TEST_STATE_ROOT=/tmp/j198s, it exactly reproduced21 passed; 7 failed; 1 ignored. Candidate executables had inherited the socket state root under world-writable/tmp; production correctly refused that ancestry. Home scratch paths were claude-owned with no foreign/root descendants, and no port-5299 orphan was present during reproduction. All seven now pass in the direct bin suite and in BOTH concurrent broadrestartsuites:restart::recovery_tests::verified_exec_and_late_refusal_preserve_listener_and_carestart::tests::cold_exec_with_closed_stdin_keeps_the_image_pinned_and_maps_nullrestart::tests::real_fd_exec_pins_replacement_and_cold_rollback_uses_null_stdinrestart::tests::reload_reopens_install_path_when_the_running_inode_was_replacedrestart::tests::rootless_even_marked_replacement_is_cold_and_open_fd_pins_the_inoderestart_binary::audit_tests::admitted_inode_cannot_change_in_place_before_executionrestart_binary::audit_tests::pathname_replacement_keeps_the_admitted_inodeDebug-size regression: a runnable ELF padded sparsely to 536,870,913 bytes first failed with snapshot
InvalidDataand resulting control timeouts (RED_SIZE_EXIT=101). After the fixture fix, all 19 otherrestart::tests ran successfully from that same oversized executable (GREEN_SIZE_EXIT=0). A separate direct production snapshot assertion still reportsproduction oversized running-image refusal: preserved.Parallel real-environment proofs:
restart --test-threads=2runs concurrently: each30 passed; 0 failed; 1 ignored. The new process-group guard regression verifies both drop and panic cleanup.127.0.0.1:3219and127.0.0.1:32641; both restarted and returned health 200, then disposed successfully.real/restart.spec.ts --grep "CA unlocked" --workers=1runs: each1 passed, with separate output directories.Static D243 delta, identical release/static build commands against the branch base
2198148d7:Both head binaries have no PT_INTERP. The gate's older configured size baselines are report-only; the table above is the freshly measured change against this branch's actual base.
Cleanup completed: source restored cleanly to the pushed head, no live processes attributed to this job,
/home/claude/worktrees/j198/targetdeleted,/root/j198-proofdeleted, and private temporary state/remote bundle and runner copies removed. Evidence logs remain under/home/claude/j198-evidenceand are attached inj198-proofs.txtandj198-sizes.txt. No merge, deploy, or issue closure.Remaining recommendation outside this fix: correct quick's target selection for bin-only packages (#329 runner); fast is green and the skipped quick actions were run directly.
J198 follow-up: merging origin/dev
e8e8d2ff79into up-preflight-j198 (starting head730b3c4137). Resolve J204/D349 alias integration inside the transaction, write and observe failing rollback regression, then green/root proof and foreground quick/fast. Estimate 2 hours plus shared build-slot queue; one target at /home/claude/worktrees/j198/target, removed at finish. Both hosts busy; compute01 has shorter queue and root proof must run there. No merge to dev or deployment.J198 follow-up progress: merge conflict resolved around the transaction; regression covers absent infra plus foreign file/directory/symlink and pre-existing owned alias. Red version intentionally uses J204's untracked alias helper; production fix remains unapplied pending observed failure. compute01 queue has taken 18 minutes, now second; dev-02 was busier. Foreground SSH run remains active, no detached pollers. Evidence will be alias-{red,green,root-proof,fast}.log under /home/claude/j198-evidence. No target exists yet.
Recovery checkpoint pushed:
84ce790a4on up-preflight-j198 is the WIP merge of origin/deve8e8d2ff7. It deliberately retains untracked J204 alias creation so the new rollback test can fail first. Production fix not yet applied; red run is first in compute01's queue after 27 minutes. Will amend this merge to the final integration after red/green and qualify the final head. All waiting remains foreground; one target only.Failing-first proof observed on the naive J204 integration: rollback_after_alias_creation_preserves_preexisting_aliases failed with
new infra survived rollback, 0 passed / 1 failed, exit 101. The service-manager fixture checked the alias existed before failing activation. Log: /home/claude/j198-evidence/alias-red.log.Applied the minimal fix: create the relative infra symlink through the transaction's exclusive link helper and record its new inode; an occupied alias is inspected/skipped and never tracked. This preserves foreign paths and pre-existing owned aliases. Green quick + up suite now run in the same foreground slot. Initial quick correctly flagged test formatting; applied rustfmt before red.
J198 follow-up checkpoint now
0b485af66e(WIP merge, own branch). Red was observed as documented. The exclusive alias creation/tracking fix is now present. Quick caught two clippy semicolon findings in the regression; fixed both, and remote cargo fmt --all -- --check is now exit 0. Final foreground runner queued on compute01: quick + up suite, then finalize merge metadata, repeat on final head, UID-0 throwaway-clone proof, scripts/verify fast. Target /home/claude/worktrees/j198/target retained only until done. Queue has grown again; no result claimed yet.J198 follow-up queue update: final qualification has waited 28 minutes, now fourth on compute01 (both hosts still occupied). Working tree is clean and checkpoint
0b485af66remains pushed; no code changes during the wait. Foreground SSH/build-slot run continues. The final merge's parents are reviewed J198 head730b3c413and deve8e8d2ff7. One target remains /home/claude/worktrees/j198/target and will be removed after qualification.J198 queue update: remaining qualification runner has waited 46 minutes and is first in compute01's queue; all four slots remain occupied. The fix is unchanged, clean and pushed at WIP
0b485af66. Red proof is retained; green/root/fast results remain pending. Foreground waiting continues; no work is detached and no production paths are touched.J198 integration finalized and pushed:
338b432029on up-preflight-j198, merging deve8e8d2ff7. Alias is created only in activation after preflight; exclusive creation records its inode. Rollback removes that inode only; every occupied alias stays untracked.Failing-first test:
new infra survived rollback, exit 101. After the fix,cargo test --locked -p infraternal --bins up:: -- --nocapture: 29 passed, 0 failed, 2 ignored, including the five alias scenarios, fresh 0711 layout and fake-server preservation. Quick's fmt, doc coverage and workspace clippy PASS; its test row reaches the already-knownno library targets found in package infraternalerror being fixed separately by the orchestrator.Same foreground slot now qualifies final SHA
338b432029: repeat quick/up, root clone under /root, then scripts/verify fast. No merge to dev or deployment.UID-0 proof PASS at final head
338b432029. A throwaway clone under /root/j198-proof executed the real installer functions via its copied unit harness, inside the same foreground build slot (root never builds into claude's target):Final-head quick repeats fmt/doc coverage/workspace clippy PASS, then the known bin-only --lib test-selection failure. Direct final-head up suite passes. scripts/verify fast is now running in the foreground. Full logs will be attached with the final gate result.
Fast gate finding at
338b432029: the SSH install integration row has 100 passed / 1 failed / 2 ignored. Failure isframed_upload_keeps_invitation_after_verified_raw_and_compressed_payloads, tests/install.rs:388, oninstall.rollback().await.unwrap(): Failure { kind: SetupRunning, exit_status: Some(73) }. The alias/up suite and restart tests passed, including the oversized debug fixture regression. Gate continues through static builds.The complete diff against merged dev
e8e8d2ff7contains only the J198 CLI up/restart code and real restart fixtures; SSH production/test sources and dependencies are unchanged. I will check the failure against unmodified dev after the gate completes before attributing it to this merge. Root proof remains PASS, 29/0/2.First final-head fast run completed, exit 1. Every Rust row passed except test (101), due solely to
framed_upload_keeps_invitation_after_verified_raw_and_compressed_payloadsrollback returning SetupRunning/73; static builds PASS. All seven previously failing restart tests passed, as did oversized_debug_image_does_not_limit_restart_fixtures. Static artifacts: x86_64 23,474,672 bytes; aarch64 21,338,192 bytes, both static/stripped with no PT_INTERP.Baseline check: clean dev
e8e8d2ff7, unchanged SSH test and runtime-helper executables (verified relevant source/manifests/lockfile diff is empty), 40 isolated runs of that specific test all PASS. Therefore I have NOT reproduced its failure on dev and do not claim that yet. Branch restored cleanly to338b432029.Queued a foreground follow-up in the same target: full unchanged SSH install suite on clean dev, then scripts/verify fast again on exact final head
338b4320. This addresses the new failure without changing unrelated code. Logs: alias-ssh-baseline{,-full}.log and alias-fast-rerun.log. Root proof is already PASS at this same final head. No target cleanup until this foreground run settles.SSH failure investigation: on clean dev
e8e8d2ff7, all 40 isolated attempts passed, and all ten full install-fixture runs passed (each 101 passed / 0 failed / 2 ignored). The unchanged SSH harness and recovery helper were reused after proving the relevant crate sources, manifests and lockfile are identical between dev and J198. No second target or unrelated code change was introduced. This did NOT reproduce the initial SetupRunning/73 failure, so its cause remains unconfirmed.Source is restored cleanly to final head
338b432029. Full scripts/verify fast now repeats on that exact unchanged head, in the foreground. Root alias/layout/server proof remains PASS.Repeated fast progress at unchanged
338b432029: workspace test and shim process-per-user rows passed, including the previously failing SSH rollback fixture. Static CLI images are rebuilding after the clean-dev baseline checkout. No code changes. Foreground runner remains active; root proof already passed at this exact head. Will attach both gate runs and the baseline records, then remove target/root clone and attributed processes.J198 follow-up finished. Branch
up-preflight-j198, pushed head338b432029a1433712c1d0d2b4388159273e7e6b. Merged origin/deve8e8d2ff79b328a71faa62e7f764d705a133eeb4into reviewed J198 head730b3c4137648ecca9263f9f47a8e5daa4eb8606.D349's relative
infra -> infraternalalias now belongs to the installer transaction. It is created after read-only preflight and publication of the canonical CLI. Exclusive symlink creation records an inode only for a new alias; rollback removes that inode only if unchanged. Foreign files/directories/symlinks and pre-existing owned aliases remain untouched. Kept the transaction's fresh 0711 base / 0755 bin / 0700 enrollment layout instead of restoring unconditional chmod of an existing server directory.Failing-first regression and root proof: naive untracked alias creation failed
rollback_after_alias_creation_preserves_preexisting_aliaseswithnew infra survived rollback, exit 101, after the service-manager fixture confirmed the alias existed. Fixed code passes all five cases: new alias, foreign file, foreign directory, foreign symlink, earlier owned symlink. Final-head unit suite and UID-0 proof in/root/j198-proofboth passed29 passed; 0 failed; 2 ignored. The root proof used the real transaction functions and also passed the fresh 0711 layout test:Final gate:
nix develop -c scripts/verify fast, compute01, foreground through build-slot, exact head above, exit 0. Complete verbatim output attached asalias-fast-rerun.log. Summary and ending, verbatim:All seven restart tests identified in the original J195 steer passed after this integration, along with
oversized_debug_image_does_not_limit_restart_fixtures. Production binary-size limits remain unchanged.Initial gate failure, retained in evidence: the first fast run had
100 passed; 1 failed; 2 ignoredin SSH's install suite:framed_upload_keeps_invitation_after_verified_raw_and_compressed_payloads, line 388, rollback returned SetupRunning/73. It passed in the final full gate without code changes. I did not reproduce that failure on clean deve8e8d2ff7: 40 isolated runs and ten full SSH suite runs all passed (each full run 101/0/2). Baseline runs used the same SSH test/recovery-helper images after verifying those crate sources, manifests and lockfile are identical to dev. Cause remains unconfirmed; no unrelated SSH changes were made. The first gate log and both baseline logs are attached.Quick limitation: final-head quick passed fmt, doc coverage and workspace clippy, then hit the known bin-only
--libselection error (no library targets found in package infraternal). The orchestrator is fixing that separately. Direct up tests and full fast passed; this branch changes no gate scripts.Static measurements (same static recipe/host; delta includes the merged dev changes):
730b3c413338b43202Both stripped/static images have no PT_INTERP. Gate configured-baseline deltas are +1,056,416 / +884,912 bytes, report-only.
Cleanup: no live processes attributed to J198; target and
/root/j198-proofdeleted; temporary source bundle and remote runner copies removed. VM and control worktrees are clean at the pushed head. Small evidence logs remain under/home/claude/j198-evidence. No merge to dev, deployment or issue closure. No remaining blocker for this branch; the unreproduced SSH test failure is recorded above.J218 independent review started: up-preflight-j198 at
338b432029, comparing git diff origin/dev...338b43202 (local origin/dev36c971a650; merge-basee8e8d2ff79). Read contract, D345/D349, process, and compute01 common brief. Plan: inspect preflight/transaction and restart isolation, run changed CLI crate tests and targeted failure probes, post verdict with evidence. Estimate 60 minutes plus build-slot queue. No merge/push/deploy/closure. scripts/fj is absent; using the installed fj CLI as the common brief directs.J218 progress: code reading complete; no new foreign-file overwrite or untrusted-directory write found in the alias transaction. Prepared regression probes for D349 alias server detection and SIGTERM during the actual Install enrollment transaction, plus writable/symlink ancestor refusal. The independent quick/crate-tests/probes/fast runner is waiting in the foreground through build-slot on compute01 (first in queue after about 13 minutes). Only target planned is /home/claude/worktrees/j218/target; UID-0 probes will use a throwaway /root/j218-review clone and a copied harness inside the same held slot. No result or verdict claimed yet.
J218 independent review findings, confirmed against
338b432029a1433712c1d0d2b4388159273e7e6bwith normal-user and UID-0 execution. Four failing assertions cover three P2 findings:P2: rollback deletes a pre-existing unit alias outside its inode journal.
crates/infraternal/src/up_install.rs:406. Preflight accepts an earlier danglingetc/systemd/system/control.service -> infraternal-agent.service. After publication, a failedis-activecheck callsdisable --now; disabling removes all aliases to the unit, including that earlier entry. The real offline unit-file operation (systemctl --root <private-fixture> disable infraternal-agent.service) reportedRemoved '.../etc/systemd/system/control.service'.j218_rollback_preserves_preexisting_unit_aliasthen fails withrollback removed a pre-existing unit alias. Service-state calls are fixture-controlled; the host service manager is untouched. Preserve earlier unit entries by limiting rollback to owned entries, or refuse them before writing.P2: SIGTERM strands enrollment and prevents retry.
crates/infraternal/src/up_install.rs:399(transaction creation atup.rs:386). The root helper relies solely onDrop. A normal termination signal during enrollment does not unwind it. The probe holds the realInstall::begintransaction, writes synthetic enrollment state, waits for a ready event, signals that exact child, and reaps it. Both users reportSIGTERM: enrollment_survived=true retry_preflight_refused=true;j218_sigterm_rolls_back_enrollmentfails. The existing signal test covers only the invoking user's pending key copy. Add cancellation/recovery for the root transaction and a regression covering its installed state.P2: the supported
infraname bypasses server-host detection.crates/infraternal/src/up_install.rs:35and:109. The process parser requires aninfraternalprefix, and unit inspection requires that string inExecStart./usr/local/bin/infra\0server\0...returnsNone, and an installedcontrol.servicewithExecStart=/opt/infra server --data /srv/custompasses preflight. With custom data/binary locations, the other default-path checks need not refuse either.j218_running_server_alias_is_detectedandj218_installed_server_alias_is_detectedboth fail. Apply D349 consistently to process and unit detection.Independent original-head crate run:
nix develop -c cargo test --locked -p infraternal --bins --tests, exit 0: 166 unit tests and 124 integration tests passed, 4 ignored across all targets. The added probes report1 passed; 4 failed; 1 ignored; the UID-0 up suite in/root/j218-reviewreports30 passed; 4 failed; 3 ignored. Original up tests and the new writable/symlink ancestry control pass as root. The root clone has been removed.Failing regressions are committed locally on
review-j218:196a9a9a5(alias preflight),e16099051(termination),1dbf20080(unit alias rollback). They add tests only and intentionally fail on the reviewed head. Patch and logs will be attached with the final verdict. Nothing is pushed.scripts/verify quickpassed scope/fmt/doc coverage/workspace clippy, then hit the documented bin-only--libselection error. The VM source is restored cleanly to the original reviewed SHA;scripts/verify fastis running in the same foreground build slot. Final verdict and gate output follow when it ends.J218 final verdict: REQUEST CHANGES.
Reviewed
up-preflight-j198at338b432029a1433712c1d0d2b4388159273e7e6businggit diff origin/dev...338b43202(merge-basee8e8d2ff79b328a71faa62e7f764d705a133eeb4). Read CLAUDE.md, architecture/process, D345/D349, compute01's common brief, #338 and comment 79108. Three P2 findings are independently reproduced, including as UID 0:P2: rollback deletes a pre-existing unit alias outside its inode journal.
crates/infraternal/src/up_install.rs:406. Preflight accepts an earlier danglingetc/systemd/system/control.service -> infraternal-agent.service. After publication, a failedis-activecheck callsdisable --now; disabling removes all aliases to the unit, including that earlier entry. The real offline unit-file operation (systemctl --root <private-fixture> disable infraternal-agent.service) reportedRemoved '.../etc/systemd/system/control.service'.j218_rollback_preserves_preexisting_unit_aliasthen fails withrollback removed a pre-existing unit alias. Service-state calls are fixture-controlled; the host service manager is untouched. Preserve earlier unit entries by limiting rollback to owned entries, or refuse them before writing.P2: SIGTERM strands enrollment and prevents retry.
crates/infraternal/src/up_install.rs:399(transaction creation atup.rs:386). The root helper relies solely onDrop. A normal termination signal during enrollment does not unwind it. The probe holds the realInstall::begintransaction, writes synthetic enrollment state, waits for a ready event, signals that exact child, and reaps it. Both users reportSIGTERM: enrollment_survived=true retry_preflight_refused=true;j218_sigterm_rolls_back_enrollmentfails. The existing signal test covers only the invoking user's pending key copy. Add cancellation/recovery for the root transaction and a regression covering its installed state.P2: the supported
infraname bypasses server-host detection.crates/infraternal/src/up_install.rs:35and:109. The process parser requires aninfraternalprefix, and unit inspection requires that string inExecStart./usr/local/bin/infra\0server\0...returnsNone, and an installedcontrol.servicewithExecStart=/opt/infra server --data /srv/custompasses preflight. With custom data/binary locations, the other default-path checks need not refuse either.j218_running_server_alias_is_detectedandj218_installed_server_alias_is_detectedboth fail. Apply D349 consistently to process and unit detection.Independent original-head crate run:
nix develop -c cargo test --locked -p infraternal --bins --tests, exit 0: 166 unit tests and 124 integration tests passed, 4 ignored across all targets. The added probes report1 passed; 4 failed; 1 ignored; the UID-0 up suite in/root/j218-reviewreports30 passed; 4 failed; 3 ignored. Original up tests and the new writable/symlink ancestry control pass as root. The root clone has been removed.Failing regressions are committed locally on
review-j218:196a9a9a5(alias preflight),e16099051(termination),1dbf20080(unit alias rollback). They add tests only and intentionally fail on the reviewed head. The patch and complete normal-user/root/crate/quick logs are attached to finding comment 79132. Local review head is1dbf200806a22aba1cf904ae1aa19e05bcdd8340. Nothing is pushed.The existing five CLI-alias rollback scenarios, read-only server data/mode preservation, and the new writable/symlink ancestor control pass as root. No foreign
infrafile overwrite, additional unprivileged destination replacement race, new secret in argv/logs, D345 omission, or prohibited product-name addition was found in the reviewed diff. Tracked-path cleanup runs in reverse creation order; the broad manager disable in finding 1 bypasses that protection. Reviewer execution covered Rust unit/integration tests and the root transaction probes; no separate browser E2E run was performed.scripts/verify quickpassed scope/fmt/doc coverage/workspace clippy, then hit the documented bin-only--libselection error. Direct crate tests passed as reported above.Final verification attempt:
nix develop -c scripts/verify fast, compute01, original clean head338b432029a1433712c1d0d2b4388159273e7e6b, foreground through build-slot,CARGO_BUILD_JOBS=4,INFRATERNAL_TEST_STATE_ROOT=/tmp/j218s. Exit 1: output storage exhausted during workspace test compilation. This run did not reach the final gate table or static builds; it is not a green independent full-gate receipt. The storage failure is recorded as a verification limit, not an additional source-code finding. Error excerpt, verbatim:Foreground runner ending, verbatim:
Complete
fast.logis attached to this comment. J198's earlier green gate remains its supplied evidence in comment 79108; this review does not claim to have independently repeated it successfully.Cleanup: the sole VM target directory (42 GB) and
/root/j218-revieware removed; the source worktree is clean at the reviewed head; no processes carrying this review's state-root marker or root proof executable remain. Small receipts are retained outside the checkout and attached to #338. The control checkout is untouched; the localreview-j218worktree retains the three failing test commits. No merge, push, deployment or issue closure.REQUEST CHANGES: three P2 findings.
J198 review fixes started on up-preflight-j198 at reviewed head
338b432029. Read J218 comments 79148 and 79132 and applied the attached three-commit test patch without production changes. Plan: (1) reproduce all four failing assertions, including UID 0; (2) fix supported-name server detection, journal-only unit rollback, and root-helper termination handling in separate atomic commits with their tests; (3) repeat root proof and scripts/verify fast on final head, push own branch, remove target/root clone. Estimate 3 hours active work plus shared queue/rebuild time. One target: /home/claude/worktrees/j198/target. Both hosts occupied; compute01 queue is shorter and root proof must run there. No merge/deployment.J198 review-fix progress: exact J218 patch applied first on
up-preflight-j198at338b43202. Foreground reproduction runner is queued on compute01 (both VM slot pools occupied; dev02 has the longer queue). No production edits yet. Runner will capture normal-user and UID-0 red assertions before each atomic fix. Target:/home/claude/worktrees/j198/target; logs:/home/claude/j198-evidence/j218-{quick-red,red,root-red}.log.Rollback choice: stop the failed service and let the inode journal remove our enablement link. Rejecting administrator-owned aliases is unnecessary when rollback can preserve them. The commit will explain this choice.
J198 checkpoint: test-only snapshot pushed to own
wip/up-preflight-j198-reviewatefc515904323f3ac78f5bd9fc38f9f74ec0e30f3so the applied reviewer tests and added published-state termination probe survive a session loss. Final implementation branch remainsup-preflight-j198at338b43202; no production change yet.Foreground red rebuild on compute01:
scripts/verify quickpassed scope/fmt/doc coverage/workspace clippy, then hit the already-reportederror: no library targets found in package infraternal. Direct bin regression compilation continues in the same slot/target. Exact reviewer probes run first as claude and UID 0; then the additional installed-state signal probe will be run red.J198 confirmed the exact J218 patch red on unchanged production head
338b432029a1433712c1d0d2b4388159273e7e6b: normal user and UID 0 both report1 passed; 4 failed; 1 ignored. Failures:j218_running_server_alias_is_detected,j218_installed_server_alias_is_detected,j218_sigterm_rolls_back_enrollment, andj218_rollback_preserves_preexisting_unit_alias. The ancestry control passes.The SIGTERM probe reports
enrollment_survived=true retry_preflight_refused=true; real offlinesystemctl --rootreports removing the earliercontrol.servicealias. Host manager untouched. Root execution was from/root/j198-review-proof. Logs attached below.Next foreground runner first reproduces the additional published-install SIGTERM test, then checks the command-parser and journal-only rollback fixes. Original signal test is deliberately still red until its own fix.
J198 checkpoint: draft fixes are preserved at
a59e7560e48457ac58af43279616fc0367bf2695on ownwip/up-preflight-j198-review; final branch remains338b43202pending green evidence and atomic commits. Exact original J218 failures are attached to comment 79185.The second foreground runner has waited ~14 minutes in compute01 queue position 2. Both VM pools are occupied; compute01 is required for the UID-0 proof and retains the sole target. It will reproduce the added post-publication SIGTERM case red before applying the parser and journal-only rollback candidates. Signal candidate registers SIGTERM/SIGINT/SIGHUP before writes, cancels enrollment through Drop, and observes cancellation at synchronous activation boundaries. No new dependencies, unsafe signal cleanup, or persistent recovery format.
All work is still within the posted active-work estimate plus shared queue/rebuild time. Source: control worktree
/home/kayg/Developer/Worktrees/infraternal-j198; target/home/claude/worktrees/j198/target; logs/home/claude/j198-evidence/j218-*. No merge or deployment.J198 three atomic fixes are pushed on
up-preflight-j198:557ccbb8ddc0f4a9c786c42e89470e5a9430b4d7: recognize infra in process and quote-aware ExecStart parsing (D349).b57265a1ae50760951a5e55c3ed696ffdbc58d0c: stop instead of disable during rollback; the inode journal removes only our enablement entry, preserving administrator aliases.fa5501eb304018a4415f790a3522b36b2586e43b: root termination scope and interruption checks during activation.Exact original J218 probes failed first as both users (comment 79185). The added post-publication SIGTERM probe also failed first as claude and UID 0 with
SIGTERM stranded the installed binary. Parser/alias rollback slice then passed34 passed; 0 failed; 4 ignoredas both users, excluding only the pending signal parents.Full candidate
cargo test --locked -p infraternal --bins --testsexited 0. UID-0 up suite in/root/j198-review-proofwith fixtures under its private TMPDIR:36 passed; 0 failed; 4 ignored. Each of SIGTERM/SIGINT/SIGHUP reportsenrollment_survived=false retry_preflight_refused=false. The installed-state signal test, earlier unit-alias inode assertion, and five CLI-alias scenarios pass. Fake server output remainsbefore: mode=700 entries=1/after: mode=700 entries=1 contents=unchanged cli_link=unchanged.One quick run hit
ExecutableFileBusyat the unchangedrestart_deleted_path_review_tests.rs:68spawn. The subsequent full crate run passed that test and all unit tests. Cause is unconfirmed; this is recorded rather than called a proven baseline failure. Scope/fmt/doc coverage/workspace clippy passed.The foreground runner now repeats the root proof with final git head
fa5501eb304018a4415f790a3522b36b2586e43b, then runsnix develop -c scripts/verify fastverbatim in the same slot and target. Final gate output and cleanup follow. No merge or deployment.J198 final verification status, unchanged head
fa5501eb304018a4415f790a3522b36b2586e43b: the native candidate full crate run and UID-0 suite are green as reported in 79224. The combined runner then stopped before fast because a root-owned metadata step tried to read the claude-owned Git worktree, triggering Git's ownership check. No source or test failure caused that stop. The metadata step now runs as claude; no global safe.directory setting was added.Both VM pools were checked again: compute01 had eight jobs queued, ARM had three. Per the common brief, the final quick/fast gate is moved to the less busy ARM VM. The compute target has been deleted before creating the ARM target, retaining only
/root/j198-review-proof/bin/root-testsfor the native proof. Root clone is reset to the exact final SHA and has no tracked diff. Native root proof remains queued on compute01 through build-slot; ARM quick/fast is queued separately in the foreground.Sole target is now
/home/claude/worktrees/j198/targeton dev02. Evidence:/home/claude/j198-evidence/j218-*on each host, copied to/home/kayg/.cache/codex-scratch/j198/evidenceon control. No merge or deployment. Final head/gate table, size measurements, and cleanup still follow.J198 final-head native root proof passed on compute01: UID
0, git headfa5501eb304018a4415f790a3522b36b2586e43b, no tracked diff in the throwaway/rootclone.SIGTERM/SIGINT/SIGHUP each show
enrollment_survived=false retry_preflight_refused=false. The post-publication SIGTERM probe passes. Fake server output:mode=700 entries=1 contents=unchanged cli_link=unchanged. Prior unit alias inode and foreign infra preservation pass.Compute cleanup is complete:
owned live survivors: []; native target,/tmp/j198s, and/root/j198-review-proofare absent. Source worktree and small evidence logs retained. ARM final quick/fast is running in the sole remaining target at the same pushed head. No merge or deployment.J198 final gate update at
fa5501eb304018a4415f790a3522b36b2586e43b: ARM quick passed. The CLI compile-out test pass reported 172 passed / 1 failed / 5 ignored; failure wasrestart::tests::cold_exec_with_closed_stdin_keeps_the_image_pinned_and_maps_null, withexceeded 0ns; status=signal: 9 (SIGKILL). Its shared 120-second deadline had expired before the child wait. No baseline attribution is established. The gate continues its remaining rows in the foreground; I will rerun the failed test and full gate after this run exits. Final-head root proof remains green (36 passed / 0 failed / 4 ignored), attached to 79269. Sole target remains dev02/home/claude/worktrees/j198/target.J198 checkpoint: head remains pushed and clean at
fa5501eb304018a4415f790a3522b36b2586e43b. Full gate remains running in the foreground on ARM, now in static builds for the second target. Workspace tests also hit restart fixture hang bounds (strip test image exceeded 60s, cold-stdinexceeded 0ns, deleted-path childexceeded 60s); I am preserving these failures and will rerun the failed tests and gate after completion, without attributing them to dev or changing bounds. ARM has been slow even for lightweight SSH/log reads. Final-head UID0 proof is complete and green; compute target/proof clone are removed. Sole retained target: dev02/home/claude/worktrees/j198/target. No merge/deploy.J198 first final gate completed: every summary row PASS except
testFAIL(101). Main bin result: 169 passed, 4 failed, 5 ignored. Full log + quick log attached to 79380. Static sizes: x86 23,483,472 bytes (+8,800 vs338b43202); ARM 21,355,280 bytes (+17,088 vs338b43202). No source changes. The rerun (restart tests for both feature sets, thenscripts/verify fastverbatim) is first in ARM build-slot queue and has waited over 20 minutes. Both VMs were checked before queuing; compute had four busy slots plus three queued jobs. Continuing foreground polling. Headfa5501eb304018a4415f790a3522b36b2586e43b, sole target dev02/home/claude/worktrees/j198/target; completed green root proof/compute cleanup in 79269. No baseline-failure attribution, no merge/deploy.J198 review fixes pushed on
up-preflight-j198, headfa5501eb304018a4415f790a3522b36b2586e43b. No merge or deployment.Atomic commits:
557ccbb8ddc0f4a9c786c42e89470e5a9430b4d7: recognize both CLI names in process argv and quote-aware ExecStart parsing, including custom paths.b57265a1ae50760951a5e55c3ed696ffdbc58d0c: stop on rollback instead of disable. The journal removes our enablement link and preserves earlier administrator aliases; refusing otherwise valid pre-existing aliases is unnecessary.fa5501eb304018a4415f790a3522b36b2586e43b: register SIGTERM/SIGINT/SIGHUP before writes, unwind enrollment on cancellation, and check cancellation before committing installed state. Service-manager calls finish before rollback.J218's original patch was applied unchanged before production edits: claude and UID0 each reproduced 1 passed / 4 failed / 1 ignored. The extra installed-state SIGTERM test also failed first. Red receipts are attached to 79185 and 79224.
Final-head real root proof on compute01 used a clean throwaway clone under
/root, private fake paths, and UID0. Receipt (full log attached to 79269):SIGTERM, SIGINT and SIGHUP each reported
enrollment_survived=false retry_preflight_refused=false. Installed-state cancellation, existing unit-alias inode preservation and CLI-alias rollback scenarios passed.Verification qualification: final ARM
scripts/verify quickexited 0. Firstscripts/verify fastexited 1 with four restart fixture timeout failures in the workspace bin (169 passed / 4 failed / 5 ignored). Both restart-only reruns then passed all 20 tests, with default features and withplaceholders, without a source change. The second fullscripts/verify fastexited 0; its workspace bin passed 173 / 0 / 5 ignored. This is not an unqualified green run: the CLI plugin compile-out row masks earlier embedded command failures. Its raw log still contains two parent restart failures (deleted-path recovery and oversized-image nested suite; the nested suite reported reload/recovery timeouts). These failures are preserved in the attached log, not attributed to unmodified dev. No hang bounds or gate scripts were changed.Second full gate output, verbatim:
Final TypeScript output:
Static release sizes with the pinned gate recipe: x86 23,483,472 bytes (+8,800 versus prior J198 head
338b43202, 23,474,672); ARM 21,355,280 bytes (+17,088 versus 21,338,192). Production admission bounds are unchanged.Control and remote branch heads match and the control worktree is clean. Compute target, private state and root proof clone are removed. ARM cleanup also exited 0:
owned live survivors: [], target and/tmp/j198aremoved,target exists: False. All verification runs have ended. Full logs are retained under control/home/kayg/.cache/codex-scratch/j198/evidenceand attached here.J226 focused re-review started: branch
up-preflight-j198, headfa5501eb304018a4415f790a3522b36b2586e43b, delta from J218's338b432029a1433712c1d0d2b4388159273e7e6b. Local review branchreview-j226at the head. Plan (~45 minutes): inspect ExecStart/server refusal, inode rollback and signal cancellation; reproduce J218's four regressions and installed-state SIGTERM red/green as UID0 on compute01 via meshtest/build-slot; post evidence and verdict. No push, merge, deployment or closure.scripts/fjis absent in both the control checkout and target tree; using the common brief's documentedfjCLI fallback.J226 interim finding (P2),
crates/infraternal/src/up_install.rs:115-123/:40-48: valid systemd command forms bypass installed-server refusal.ExecStart=@/opt/infra harmless server --data /srv/privateis parsed as executable/opt/infrawith argv[0]harmlessand commandserver; the new check treatsharmlessas the host command./procinspection also missesharmless\0server\0..., so this form can evade refusal while the server runs. The systemd parser on compute01 independently confirmedCommand Line: harmless server --data /srv/privateusing a private executable fixture. It also confirms hexadecimal escapes (\x73erver) and backslash/newline continuation resolve toserver, while the new line-by-line tokenizer does neither. Root failing assertions are prepared and queued; this comment does not claim they have executed yet.All four compute01 build slots are occupied; J226 is queued behind five jobs. Foreground SSH runner remains active. Root proof source is
/root/j226/{base,head}, sole target/root/j226/target, evidence/root/j226/evidence. No production service manager operations are used by the regression fixtures. No push/merge/deploy/closure.J226 checkpoint: the root runner acquired a compute01 build slot after roughly 20 minutes in the queue and confirmed UID 0 plus the requested base/head SHAs. Baseline compilation is running in the foreground via the meshtest account. Evidence stays under
/root/j226/evidence, sole target/root/j226/target; no background run or push. Read-only checks and review are complete. The remaining time is baseline/head compilation, five required red/green assertions, the parser/SIGKILL and inode controls, and the requested gate. The original ~45-minute estimate is now likely to be exceeded because of the queue; source findings are already recorded above and no implementation work is being added.J226 root evidence now confirms two remaining P2 findings on
fa5501eb304018a4415f790a3522b36b2586e43b:Systemd syntax can evade installed/running server refusal.
crates/infraternal/src/up_install.rs:117and:120(shared tokenizercrates/infraternal-status/src/lib.rs:548). The root probe reportsrefused=falsefor@/opt/infraternal harmless server --data /srv/private,\x73erver,${MODE}withEnvironment=MODE=server,-infraand a continued command. The plaininfra servercontrol correctly refuses. Systemd independently accepts these forms;@supplies argv[0]harmless, which also defeatsprocess_command.j226_systemd_command_forms_refuse_serverfails at the actual preflight assertion. Handle manager semantics or conservatively refuse unresolved commands; argv[0] alone cannot establish the running executable's identity.SIGKILL has no next-run recovery.
crates/infraternal/src/up_install.rs:207and:141(cleanup depends onDropat:440). The journal is only in memory.j226_sigkill_retry_recovers_enrollmentstops the helper after the real transaction has synthetic enrollment state and reportsSIGKILL: enrollment_survived=true retry_preflight_refused=true. Next preflight refuses this leftover directory; there is no recovery step. This is an incomplete-install retry failure, not a request to catch SIGKILL in a handler.Required red/green proof, UID 0 via meshtest on compute01 through build-slot:
338b43202+ J218's original probes and the installed-state probe: 1 passed, 5 failed, 2 ignored. All four original regressions and installed-state SIGTERM fail; the ancestry control passes.fa5501eb3: 7 passed, 0 failed, 2 ignored. SIGTERM/SIGINT/SIGHUP each reportenrollment_survived=false retry_preflight_refused=false.34:5997252before/after) and rollback removes it while preserving the earlier alias. A replacement enablement inode survives rollback as required.Verification qualification: the initial head attempt reused the baseline Cargo binary in the shared target, detected from its test inventory. That attempt is discarded. All five changed Rust files were then touched, head was recompiled, and the above corrected results were obtained. The first quick attempt also encountered that stale status library; it is not a source finding or final gate receipt. The actual
fastgate now runs after restoring original test and gate source. No success claim for it yet.Failing tests and passing inode controls are committed locally on
review-j226:339f1a2a8,7d0e0ffad,ef0263849(headef02638492aef69420452cc6344eb5e56d46138b). Nothing pushed. Patch/logs and final verdict follow after the gate and cleanup.J226 verification checkpoint: required UID0 red/green proof and both remaining findings are confirmed (79541). The full unchanged-source
scripts/verify fasthas reached static builds; the first release build finished and the second architecture is still compiling in the same foreground build slot. No background wait, push, merge or deployment.Gate qualification discovered in the raw scan: workspace
setup_outputreported 4 pass / 2 fail under full UID0 capabilities. Both failures rely on denied access to mode-000 directories/sockets. Running the exact same unmodified test image with onlyCAP_DAC_OVERRIDE/CAP_DAC_READ_SEARCHremoved passes all 6 tests (exit 0). This is an invocation-environment limitation, not an additional source finding; final gate will be reported as failed rather than green. No restart flake is being treated as a finding.Extra ordinary-signal proof also passes: SIGTERM during activation, then SIGINT + SIGHUP while rollback is blocked in its private fake manager;
SECOND_SIGNAL_EXIT 1 SURVIVORS []. Root source and gate files are restored to target head. Sole target remains/root/j226/target, logs/root/j226/evidence. Cleanup and the final verdict/attachments follow the gate's exit.J198 resumes REQUEST CHANGES from J226 (79510/79541/79564) on
up-preflight-j198, basefa5501eb304018a4415f790a3522b36b2586e43b. Copied J226's exact test source first; existing J218 source is unchanged and four J226 probes are appended. Plan: reproduce the two requested failures as UID0 from a private/rootclone; merge current origin/dev; use manager-resolved commands and executable inode identity for conservative refusal; persist only verifiable transaction ownership for next-run recovery, with lock and foreign-path controls; atomic commits, quick then fast and final root proof, foreground only. Estimate 3 hours implementation/validation plus shared build queues. No merge into dev or deployment. No target currently retained; creating sole target on compute01 for native/root proofs. Both slot queues checked: compute 4 busy/5 queued, ARM 2 busy/3 queued; compute selected for mandatory native root proof. J226's full-capability mode-000 setup_output limitation will be reported separately.J198 checkpoint: original J226 tests copied unchanged onto production base
fa5501eb3; test-only snapshot pushed aswip/up-preflight-j198-j226. The root red runner acquired compute slot 3 after ~25 minutes and is now executing quick/build as claude before UID0 probes in/root/j198-j226-proof. Production code has not changed. Sole target/home/claude/worktrees/j198/targeton compute01. Planned controls include next-run recovery after publication, committed-state preservation, live transaction locking and invalid-journal refusal. Merge preview shows only restart_tests.rs conflict with current origin/dev. Foreground session remains active; no merge/deploy.J198 checkpoint: exact J226 tests copied first. On
fa5501eb3, with umask 022, both behavioral probes fail as claude: command forms report refused=false and failvalid @ unit bypassed preflight; SIGKILL reports enrollment_survived=true retry_preflight_refused=true. Immutable native test image and raw logs are in compute01:/home/claude/j198-evidence/j226-*. Root replay plus merge-only quick is foreground queued (all compute slots occupied; dev-02 also occupied).Implementation: ask the manager for resolved ExecStart paths and use kernel executable/inode identity; unresolved services refuse conservatively. Durable recovery will retain inode ownership across abrupt stops, with a private locked 0600 journal. Main branch remains
fa5501eb3; exact test-only snapshot is pushed at wip/up-preflight-j198-j226. No deploy or integration merge.J198 checkpoint: native/root runner is still foreground queued (compute01 four slots busy, two earlier jobs ahead; ARM also has both slots busy and three queued). The runner now chains exact unchanged root red probes, merge-only quick, manager-inspection quick, then durable-recovery quick, to avoid repeated queue entries. Main branch/index still preserves the merge-only tree for its separate commit.
Current WIP snapshot is pushed at wip/up-preflight-j198-j226-inspection, including the scoped journal prototype and tests. Journal: private 0600, retained inode lock, synced inode records before publication, read-only recovery preflight, next privileged attempt rolls back only recorded unchanged inodes. Added controls for enrollment and published-state abrupt stops, committed installs, live transaction locking, and invalid/private metadata. The original SIGKILL regression will be adapted after its unchanged red replay to assert next-run recovery, since read-only preflight itself must not perform cleanup. All raw logs remain compute01:/home/claude/j198-evidence; sole target is /home/claude/worktrees/j198/target. Validation is pending, not claimed green.
J198 UID0 red proof completed unchanged at
fa5501eb30in /root/j198-j226-proof, using J226's exact tests and umask 022. Both requested regressions fail:j226_systemd_command_forms_refuse_server(five forms refused=false, plain infra refused=true) andj226_sigkill_retry_recovers_enrollment(SIGKILL: enrollment_survived=true retry_preflight_refused=true). Both controls pass: offline enable removes our own link while preserving the earlier alias, and a replacement enablement inode survives rollback.Verbatim result:
test result: FAILED. 2 passed; 2 failed; 0 ignored; 0 measured; 178 filtered out; finished in 0.10s, exit 101. Root log attached below. The foreground runner now checks merge-only tree9260f37a81before each fix's quick check.J198 checkpoint: origin/dev
a8b4ee720is merged into up-preflight-j198 and pushed asba156cb2d9. Merge-only tree9260f37a81passed quick, including 174 CLI tests / 0 failed / 5 ignored. No integration merge or deployment.Manager inspection quick has passed workspace clippy and is in unit tests. Durable recovery quick follows in the same foreground slot. WIP backup
aed052a1e5on wip/up-preflight-j198-j226-inspection includes the scoped journal and regression controls; it is not a validated final head. Sole target remains compute01:/home/claude/worktrees/j198/target. Root exact red evidence is attached to 79644.The final root proof and scripts/verify fast remain required and pending.
J198 validation correction: the first manager-inspection quick receipt had the old 174-test count and no new J226/inspection test names. Discarding that receipt as stale. The subsequent durable-recovery check stopped at two compile errors (mutable directory iterator and a missing fixture path borrow), now corrected. No fix commit was made from either result; main branch remains the validated merge
ba156cb2d.Foreground rerun explicitly touches transferred sources before Cargo and requires the new test names to appear as passing. It checks the manager fix separately, then recovery with enrollment/published abrupt stops, committed-state, live-lock, invalid-journal, foreign replacement and failed-stop controls. Stop failure retains the journal and owned files so the next healthy attempt can recover. Latest WIP
ed0994b1d8is pushed on wip/up-preflight-j198-j226-inspection. Root exact red proof remains valid and attached to 79644. Sole target unchanged; no deploy/integration merge.J198 fresh-source quick now compiles and discovers the manager-inspection tests. All three J226 controls plus both kernel/manager identity tests pass. The CLI row is red for one existing restart regression:
restart::recovery_tests::oversized_debug_image_does_not_limit_restart_fixtures,ExecutableFileBusy/ code 26 at its exec spawn. Verbatim:test result: FAILED. 178 passed; 1 failed; 5 ignored; 0 measured; 0 filtered out; finished in 12.51s.Cause: this test opened the same oversized image for writing before executing it. Another harness thread can fork during that writer window and retain the descriptor until exec, even after our thread closes it. Dev's executable publisher already solves this by making the final inode in a separate process. The pending fix pads a staging image and publishes a separate final executable through that helper; the production 512 MiB bound is unchanged. This will be its own atomic test-fixture commit. Corrected quick checks are foreground queued, with explicit timestamp refresh and test-name checks. Main branch remains
ba156cb2d; no deploy or integration merge.J198 checkpoint: manager/kernel inspection is pushed at
6599bf3d2228056512eeb72f9d3b0f86b4ba1fc1; its refreshed quick check passed 179 tests, 0 failed, 5 ignored. Durable recovery candidate is preserved onwip/up-preflight-j198-j226-inspectionat2303444216018e37874772a65acc164bf61d7e6c. Recovery quick V4 is queued in the foreground on compute01 (all four slots occupied); no recovery pass is claimed yet. After quick, the same foreground runner will validate the committed SHA in a private root clone, run both restart suites in parallel, then runscripts/verify fast. Sole target remains/home/claude/worktrees/j198/target; it will be deleted after evidence collection.J198 validation checkpoint at pushed head
f0cf215bab3670da3d343f8cb65bd5749416d56a(up-preflight-j198).Refreshed
scripts/verify quick: CLI 186 passed, 0 failed, 5 ignored. All seven durable recovery controls explicitly passed.Root proof ran through build-slot in
/root/j198-j226-final-proof, UID 0, CapEff000001ffffffffff: 48 passed, 0 failed, 4 ignored. All six J226 systemd command forms reportedrefused=true; SIGKILL left enrollment as expected, read-only retry preflight accepted it, and the next transaction recovered it. SIGTERM/SIGINT/SIGHUP immediate rollback still passed. Fake server proof printed:Both parallel restart suites passed: 19 passed, 0 failed each;
PARALLEL_RESTART_EXIT=0,0. Fullscripts/verify fastis running in the same foreground slot on this committed SHA. This is not a final gate receipt yet.J198 gate checkpoint, unchanged pushed head
f0cf215bab3670da3d343f8cb65bd5749416d56a: foregroundscripts/verify fasthas completed CLI compile-out, workspace clippy/tests, process-per-user shim tests, cargo-deny, architecture and documentation checks. No nested failed-test or compiler-error entries have appeared. Static builds are active. All sixsetup_outputintegration tests passed as unprivilegedclaude; the two mode-000 tests under full root DAC override are an environment limitation, as J226 reported. The separate SSH inode-reuse fixture reported its documented skip after 256 attempts without filesystem inode reuse. No final gate pass is claimed yet.J198 finding at
f0cf215bab3670da3d343f8cb65bd5749416d56a: full foregroundscripts/verify fastfinished with every preceding row PASS andstatic builds FAIL (101). The native GNU tests compiled nixrenameat2, but that wrapper is GNU-gated and absent on musl (E0433 RenameFlags,E0425 renameat2). This is our build defect, not a baseline failure. Replacing only the entry point with libc preserves kernel RENAME_NOREPLACE semantics on both shipping targets. Will run quick and both static builds before committing, then repeat root proof and the full foreground gate on the corrected SHA. Prior complete log remains/home/claude/j198-evidence/j226-final-fast.log.J198 portability checkpoint: native quick rejected direct libc in the CLI because workspace policy forbids unsafe code. The same narrow renameat2 operation now lives in the shim's existing audited OS boundary, beside its startup-file metadata operation; the CLI calls its safe wrapper. No new dependency, framework, or policy exception. Rechecking quick and both static targets, then folding this correction into the durable-recovery commit and rerunning the root/full gate at the resulting SHA. The earlier native-only quick failure is saved as
j226-portable-quick.log.J198 static-link finding: safe-wrapper quick passed (186 CLI tests, 0 failed, 5 ignored), but x86_64 musl link failed on
undefined symbol: renameat2. libc 0.2.190 declares the function, while the bundled static musl does not export it. The correction now follows the shim's existing pidfd/close_range pattern:libc::syscall(SYS_renameat2, ..., RENAME_NOREPLACE), inside the same audited boundary. No dependency or CLI unsafe exception. Will recheck quick and both shipping targets before amending the recovery commit. Link evidence is/home/claude/j198-evidence/j226-portable-v2-x86_64-unknown-linux-musl.log.J238 verdict: GO: deploy dev
8d3207cc8to production (replacing5d33f7cdd)This is a production-risk review only, not a code review. No P1 or P2. I found two things the owner must know, not blockers: the old binary refuses to start on the migrated database, so take the backup first, and CLI keys from before the upgrade end within 24 h. The runs were on compute01 (VM clock CET). Logs are in
compute01:~claude/j238/logs/and the harness is in~claude/j238/. I did not touch production, the test server or any branch. I did not runverify fast, per the reviewer rule.Disclosure: production and the test server are the same host (
ssh= 10.69.69.89). While reading the test server's unit I also ransystemctl cat infraternal-serverandls /var/lib/infraternal/there. That was read-only, and those two commands were all. Production's flags below come from it.Artifacts are the deployed builds
sha256(infraternal):/opt/infraternal/8d3207cc8/infraternalon the host =compute01:~/deploy-8d3207cc8-result=9e57c31b…809dce4(SOURCE8d3207cc8485…)./opt/infraternal/5d33f7cdd/infraternal=~/deploy-5d33f7cdd-result=4a8706a6…66312c6. These are the binaries I tested.1. Migrations on a populated DB (
logs/upgrade-run.log)Production's DB is at 0001–0040 (5d33).
8d3207cc8adds 0043, 0044, 0045, 0046, 0047, 0048, 0049 and nothing else. There is no 0041/0042 and no timestamped migration in this range. 0001–0040 are unchanged./auth/device-start→ approval page (passkey) →/auth/device-redeem(OWNER_SESSION true,API_KEY_MINTED true). Then a host row shaped likecodewas added: rootless uid 1000,remote_host_bootstrap.os='Linux', and a placeholder mesh nodeunnamedplus ahandnode. I added that row with SQL against 5d33's schema, because 5d33's Add host needs a reachable SSH target and an unlocked CA.[38, 39, 40, 43, 44, 45, 46, 47, 48, 49], allsuccess=1,healthz=200 after 1080 ms,integrity ok,fk []. Counts are unchanged: users 1, sessions 1, api_keys 1, remote_hosts 1, bootstrap 1, mesh_nodes 2.unnamedtolinux-3b1f6cand wrote onemesh_auditrow. Thehandname was kept.infraternal-test.dev-5d33-20261104T141340, 1–40, 13 sessions, 3 approvals): upgrade → same version list,healthz=200 after 754 ms,integrity ok,fk [], all 13 sessions kept.healthz=200 after 360 ms). Each migration is recorded by SQLx and runs once in its own transaction.CREATE TABLE/INDEXorALTER TABLE ADD COLUMN(O(1) in SQLite), plus two updates over small tables: 0047 overapi_keysand 0049 over placeholdermesh_nodes. Nothing is dropped and no row is deleted. The total migration stall is about 1 s, during a restart that is down anyway.2. Upgrade behaviour
/on8d3207cc8with no sign-in screen (SIGNIN_SCREEN false). Screenshot attached:new-upgrade-reopen.png. Existing sessions gettap_spent=0, tap_action=NULL(0045/0048), so the first per-action change (D336) asks for a passkey tap, as designed.hard_expires_at= min(current deadline, upgrade + 24 h). Measured:1793965153 − 1793878753 = 86400. The key still authenticates after the upgrade:remote.hostgives 404 with the real key and 403 with a bogus key (logs/keycheck.txt). Its old scopes (hosts:read terminal:connect actions:dry-run) don't include the new CLI'sstatus:read, soserver.statusis 403 for it. 5d33 had no/v1/actionsat all (405), so no existing automation breaks. The owner should sign the CLI in again after the deploy.codenever started its agent. If it has others, they take the normal restart path (unlock above). The embedded infraternal agent'sinfraternal agent failed: unexpected end of fileat shutdown appears with 5d33 too, and in the test server's journal (10:57 and 11:03 IST runs). It isn't new.auth/http.rsare identical between the two revisions, so an open old tab doesn't hit a removed endpoint. A reload is still the cleanest path.3. Re-adding
code(#305 part 2)The real lock on
code, read-only (nothing changed; field repro kept):~/.local/share/infraternal/(kayg, uid 30033, rootless) =.bootstrap-owner(33 B) +.bootstrap-lock/0700 =transaction,kind=fresh,lease=3273114 …(pid not running),payload(link count 2, same inode as~/.local/share/infraternal.bootstrap-cache/payload),cache/owner(INFRATERNAL_PAYLOAD_CACHE_V1),state/join-key.pk8. There's noactivating, nobin/, nosessions/and no user unit.The shipped regression
test_fresh_attempt_stopped_at_connect_is_recoverable(9872e8975) differs from this shape: itscache/payloadlink sits inside the lock, it has asessions/dir and a.derkey. So I rebuiltcode's exact shape with dummy files and ran it through 8d3207cc8's oracle (previous_attempt_shell.py, realinspect_previous+FRESH_CLEANUP+ROLLBACK_TRANSACTION) (logs/code-shape.log):The whole shipped oracle passes as well:
Ran 86 tests in 84.451s OK(logs/shipped-oracle-full.log). An earlier red run in my notes came from using the wrong runtime helper (the session binary instead of therecovery-helperexample); it was not a code issue.So this shape is recovered, not refused. The server parses it as a fresh stale attempt and shows a cleanup confirmation ("Earlier setup ownership marker / Fresh setup ownership journal / No live setup found"). Confirming it removes the lock and the add-host continues.
Rootless vs root: the leftover is in kayg's home. Add
coderootless, askayg@code, so that this base is the one inspected and cleaned. A root add-host uses/var/lib/infraternaland never looks at~kayg/.local/share/infraternal, so the leftover would stay behind.4. Config
Production's ExecStart (drop-in):
server --data /var/lib/infraternal --listen 10.69.0.89:7011 --public-origin https://ssh.kayg.org --web-root /opt/infraternal/5d33f7cdd/web --remote-listen 10.69.69.89:7443 --remote-ca-dir /var/lib/infraternal/remote-ca --behind-proxy --trusted-proxy 10.69.0.99. The test unit runs this same flag set on8d3207cc8since 16:27 IST, and locally8d3207cc8started with it on the migrated DB A (START new-proxy: healthz=200 after 339 ms). The new non-test code reads no new env var and needs no new flag. Only the two paths change (5d33f7cdd→8d3207cc8).5. Rollback
START old-on-migrated: EXITED … auth initialization failed(SQLx sees applied versions 43–49 that it doesn't know). Rolling back the binary alone does not work.START old-on-backup: healthz=200 after 384 ms, the session reopens (SIGNIN_SCREEN false), migrations[38, 39, 40], integrity ok./.Owner steps (IST)
systemctl stop infraternal-server, thencp -a /var/lib/infraternal /var/lib/infraternal.bak-$(date -u +%Y%m%dT%H%M%S). Take the backup with the server stopped, so the WAL is included./opt/infraternal/5d33f7cdd/to/opt/infraternal/8d3207cc8/(binary and--web-root; the files are already there and the checksum matches). Thensystemctl daemon-reload && systemctl start infraternal-server, and checkcurl -s 10.69.0.89:7011/healthz.kayg@code(rootless). When it lists the earlier setup, confirm the cleanup, then finish the add-host.infra sign-in https://ssh.kayg.org). Old keys end within 24 h and lackstatus:read./var/lib/infraternal, point the drop-in back to5d33f7cdd, start. Never start 5d33 on the migrated DB.Cleanup: every DB copy, the test-snapshot copy, the private passkey and key state and the self-signed cert are deleted. No processes are left. Logs and harness stay in
~claude/j238/.Verdict: GO
J198 validation checkpoint: the direct syscall version passed refreshed
scripts/verify quick(CLI186 passed, 0 failed, 5 ignored) and the x86_64 shipping musl build (PORTABLE_STATIC_x86_64-unknown-linux-musl=0). This resolves both the GNU-only nix API and the bundled-musl missing-symbol failure. Aarch64 musl is compiling in the same foreground slot. Exact candidate is saved onwip/up-preflight-j198-j226-inspectionate4e7f3c2fd4ef2a1a35c4b97df7106ac9c2ae659. After aarch64 succeeds I will fold the portability correction into the recovery commit, push the resulting head, then rerun root proof, parallel restarts and the full gate at that committed SHA.J198 head update:
up-preflight-j198now points toa2beb5a0b9bc283adff887100346847e6d99ae33. The durable-recovery commit was amended to include the musl-compatible syscall boundary, with an exact force-with-lease againstf0cf215bab3670da3d343f8cb65bd5749416d56a; that earlier SHA and failure evidence remain preserved on the WIP ref. Refreshed quick passed (CLI186/0/5), and both x86_64 and aarch64 shipping musl builds passed. The foreground runner accepted the new SHA and is now running its private root proof, parallel restarts, andscripts/verify fast. No merge or deployment.J198 final validation checkpoint at unchanged
a2beb5a0b9bc283adff887100346847e6d99ae33: fresh UID0/full-capability root proof passed 48 tests, 0 failed, 4 ignored in/root/j198-j226-final-v2-proof-v2. Fake server remained0700 with unchanged contents/link; SIGKILL recovery, SIGTERM/SIGINT/SIGHUP, foreign aliases and replaced inodes passed. Both parallel restart suites passed19/0 (PARALLEL_RESTART_EXIT=0,0). Full foregroundscripts/verify fasthas passed workspace tests and every non-static row, including cargo-deny and doc coverage. All six setup_output tests passed as claude. SSH inode-reuse fixture reported its documented skip after256 attempts without inode reuse. The final static images are rebuilding under the committed head; no final gate pass claimed yet. Log/home/claude/j198-evidence/j226-final-v2-fast.log; sole target remains/home/claude/worktrees/j198/targetand will be deleted at completion.J198: J226 request changes addressed, pushed on
up-preflight-j198, heada2beb5a0b9bc283adff887100346847e6d99ae33. Merged origin/deva8b4ee7206d3d72f4e04aeb432d86869a3ae532c. No merge or deploy performed.The exact J226 tests were copied first and confirmed red on
fa5501eb3: 2 failed, 2 passed, exit 101 (comment 79644 and its attachedj226-root-red.log). Both now pass./proc/<pid>/exeidentity, including inode checks. Unresolved commands refuse conservatively. It no longer trusts argv[0] or parses raw unit command syntax. All six reviewed command forms refuse as root.Install::begintakes the journal lock, repeats preflight and recovers only proven owned inodes. SIGKILL recovery covers enrollment and published installation. Committed installs, foreign aliases, replaced inodes, and a live transaction are preserved. Failed stop retains recovery authority. The directory publication syscall remains inside the existing audited shim boundary and builds for both shipping musl targets.Validation at the final head, all foreground on compute01:
scripts/verify quick: CLI 186 passed, 0 failed, 5 ignored (j226-portable-v3-quick.log).j226-final-v2-root.log). SIGTERM/SIGINT/SIGHUP rollback and inode-preserving controls pass. Fake server before: mode=700 entries=1; after: mode=700 entries=1 contents=unchanged cli_link=unchanged.j226-parallel-v2-a.log,j226-parallel-v2-b.log).nix develop -c scripts/verify fast: exit 0; full output attached asj226-final-v2-fast.log. Receipt below is verbatim. No nested failure/error matches were found. The full log also records the frontend/TypeScript checks.The static deltas above are against the gate's configured D243 baselines, not isolated size contributions from this patch. The inode-reuse probe did not obtain reuse in 256 attempts; the foreign-inode rollback controls did pass. Native aarch64 key-memory execution is skipped on this x86 host.
As J226 reported, two setup_output mode-000 tests fail with UID-0 CAP_DAC_OVERRIDE because root bypasses their permissions; that is an environment limitation. All six setup_output tests passed unprivileged as claude in this gate.
Cleanup complete: no surviving processes matched this job's executable/cwd paths; deleted the sole VM target, all three private root proof clones, old bulk test executable, bundles and private temporary test state. Small evidence logs remain under
/home/claude/j198-evidenceand/home/kayg/.cache/codex-scratch/j198/evidence. Both worktrees are clean and the pushed branch head matches the validated SHA.J245 security re-review of
up-preflight-j198ata2beb5a0b9bc283adff887100346847e6d99ae33, delta sincefa5501eb3(J198 commitsfe5bc3f28,6599bf3d2,a2beb5a0b). I read CLAUDE.md, PROCESS.md, BRIEF-COMMON, D349 and comments 79148, 79510, 79541, 79564 and 79912. As the reviewer rules say, I did not rerun the gate. I only ran the two J226 regressions and two probes of my own.Verdict: REQUEST CHANGES. 1 P1, 1 P2, 3 P3.
J226 regressions, red/green as UID 0 on compute01
I made a private clone in
/root/j245and ran it through build-slot. Base isreview-j226(ef0263849=fa5501eb3+ J226's tests). Head isa2beb5a0bplus my two probes.j226_systemd_command_forms_refuse_serverFAILED (j218_review_tests.rs:357),j226_sigkill_retry_recovers_enrollmentFAILED (:158).2 failed,base_EXIT=101. Artifact:j245-base-run.log. Ignore the head section of that same log: it reused the base test binary from the shared target (the stale-binary trap J226 described).ok,2 passed; 0 failed,head_EXIT=0. Artifact:j245-head-run.log.On the real systemd, run as the
claudeuser manager on compute01: all six J226 forms resolve topath=/opt/infraternal,/opt/infraor a bareinfra. Head refuses each of them, by name or because the path is not absolute or does not exist. Artifact:j245-systemd-forms.log.Findings
P1: preflight now refuses every stock systemd host.
crates/infraternal/src/up_inspect.rs:148-173,:186-197,:205-213,:283-295. Any unit for whichsystemctl showfails, or which is notLoadState=loaded, has noExecStart=, or has a non-absolutepath=, counts as "unresolved". That covers:systemctl show getty@.serviceexits 1;maskedandnot-foundentries thatlist-units --allalways returns;path=python3andpath=systemctlunresolved. They are only searched at exec time, soshowdoes not give the resolved path that6599bf3d2assumes.Evidence:
j245_real_host_manager_accepts_stock_unitscallsinspect::installed_services("/", /usr/bin/systemctl, empty)as root on compute01, a host with no infraternal unit. Result:Err(... "Could not resolve the command for service NetworkManager.service through systemd ..."),probe_EXIT=101(j245-head-run.log). A shell walk over the same 201 units (Debian 13, systemd 257.13) found more than 40 refusals:autovt@,getty@,sudo.service(masked),sshd.service(not-found),apt-listchanges.service(path=python3) and others. The fixture tests cannot catch this. The unprivileged check atup.rs:275hits it too, so the documentedinfraternal upstops before sudo.Suggested direction, all fail-closed for infraternal itself:
maskedandnot-foundunits as having no command;path=, refuseinfraandinfraternal*by name, and otherwise resolve it through systemd's fixed exec search path;P2: the retained root-only journal blocks every later unprivileged
infraternal up.crates/infraternal/src/up_journal.rs:212-219, called fromup.rs:927viaup.rs:275.Journal::opencreates/.infraternal-up.journalas 0600 root and deliberately keeps it after rollback (up.rsfailed_install_rolls_back_new_pathsasserts that it stays). The documented flow runsinstall_preflight()as the invoking user before sudo.inspectmaps every open error exceptNotFoundtoinvalid(), so EACCES turns into "cannot be read or trusted". So after any failed, rolled-back or SIGKILLed attempt (the exact case this fix targets), the normal retry is refused with a misleading message. That holds even when the journal is empty.Evidence:
j245_unprivileged_preflight_reads_retained_journal, run asclaude(euid 1500) against a root-owned empty 0600 journal:Err(... "The existing /.infraternal-up.journal cannot be read or trusted ..."),journal_EXIT=101(j245-head-run.log).Suggested fix: when not root, treat EACCES on a root-owned file as "defer to the privileged repeat".
Install::beginalready reruns the full check under the lock.P3-1: wrapper and indirect forms still get past the installed-unit check.
crates/infraternal/src/up_inspect.rs:194-217.ExecStart=is read, soExecStartPre=andExecStartPost=are not checked.path=is classified./usr/bin/env infra serverand/bin/sh -c "exec /opt/infra server"resolve toenvandshand pass (j245-systemd-forms.log, forms 7 and 8).ExecStart=/usr/local/sbin/ctl serverwithctl -> /opt/infraternal-old/infraternal(a different build, so a different inode) passes.fs::canonicalizebeforeinstall_imagewould close this cheaply.These are not regressions from
fa5501eb3, and a running server is still caught by the/proccheck. A stopped server unit in one of these forms is not.P3-2: renamed copies of another build get past both checks.
crates/infraternal/src/up_inspect.rs:17-24. Identity is the name (infra,infraternal*) or the current image's inode. A server binary from a different build, copied to/opt/x/control, is matched neither in/procnor in a unit. Otherwise the/proc/<pid>/exehandling is sound:openandfstat, then runs a secondstatto catch an exec race;NotFoundis skipped, for exited processes and kernel threads.An embedded marker, such as an ELF note checked on the pinned descriptor, would identify foreign builds. Record this as a known limit if you don't fix it.
P3-3: renameat2 has no fallback.
crates/infraternal-shim/src/pty.rs:155-175. The wrapper is properly confined:pty.rsboundary;deny(unsafe_code)stays in place;AT_FDCWD,RENAME_NOREPLACE;But
ENOSYS(kernels before 3.15) andEINVAL(filesystems withoutRENAME_NOREPLACE, for example OpenZFS older than 2.2, which ships with Ubuntu 22.04 and Proxmox 7 ZFS roots) just fail the install. That is fail-closed and rolls back cleanly, butupcannot complete on those hosts. A safe fallback for those two errors would be the previous exclusivemkdirwith record-after-create. Its crash window leaves an unrecorded directory, which preflight then refuses: still closed, just needs manual cleanup. The error message should at least name the cause.Test note (no severity):
j226_systemd_command_forms_refuse_server(j218_review_tests.rs:358) passes on head for a reason that has nothing to do with the command forms. The fixture has nosystemctl, somanager()fails to spawn and every unit is "unresolved". There is no accepting control in that test. The real-manager probe above is what actually exercises the change.Journal: what passes
O_NOFOLLOW,create_new0600 followed by an explicit chmod, andchecked()requires uid 0, mode exactly 0600, nlink 1, the open inode matching the name, and a size of 1 MiB or less. These checks run before and after the lock. A journal owned by another user, hardlinked or symlinked is refused. It lives at/, which only root can write.step()andrecord()fsync before the operation they cover. A destination is recorded beforehard_linkorrenameat2.Registeredcomes beforedaemon-reload,Committedcomes after the is-active check, and the directory is synced on open.disableis never called. A failed stop keeps the records.Committedrefuses recovery. A live installer causesWouldBlockand a refusal. A corrupt line, an unknown version, a secondBeginor a stray record makes the journal untrusted and is refused (fail-closed). A partial trailing line is ignored and truncated byclear()before the nextBegin.Cleanup
/root/j245, including its only target dir,/tmp/j245*and the state root are deleted. No J245 processes are left. The probe tests exist only in a local patch (attached,j245-probes.patch); nothing was pushed, merged, deployed or closed.REQUEST CHANGES: P1 (stock-host manager refusal), P2 (retained journal blocks unprivileged retry), 3 × P3.
J198 starts J245 request changes on branch
up-preflight-j198, basea2beb5a0b9bc283adff887100346847e6d99ae33. Read comment 79938 and copied the attached probes before implementation. Plan: reproduce the two probes; merge origin/dev32a2f7046(D351); fix stock-manager classification, privileged retry handoff, indirect executable identification, and unsupported no-replace publication in separate commits with red-first tests; run real user-manager and root proofs, quick, then fast in the foreground; clean the target. Estimate: 3 hours including compilation/slot queues.One technical correction to the suggested fallback: Linux link(2) cannot hard-link directories (EPERM). Files and symlinks already publish via exclusive link/unlink. I will preserve fail-closed directory ownership and report the narrow compatible directory fallback choice before implementing it, rather than pretend link(2) can publish directories.
J245 red probes are queued in the foreground on compute01 (queue position 5, own wrapper PID 703838). Both VMs were busy when checked; compute01 is required for the real systemd/root proof. No production fix has been applied before the red run. The merged dev head is
15cc9fee7, pushed. Pending test sources are synced in/home/claude/worktrees/j198; sole target will be that worktree's target. Red artifacts will be/home/claude/j198-evidence/j245-own-{root,user}-red.logandj245-red-build.{jsonl,log}.For the unsupported directory syscall, the safe compatible alternative is exclusive mkdir followed immediately by inode journaling (and ordinary rollback on failure). A stop in the mkdir-to-record interval must refuse that unproven inode rather than delete it; link(2) cannot publish directories. Files and symlinks already use exclusive link/unlink. I will explicitly document this narrow fallback crash limit and exercise forced ENOSYS/EINVAL plus occupied-path controls.
J198 own red receipts, before applying a production fix on the VM: the copied
j245_unprivileged_preflight_reads_retained_journalfails as uid 1500 with the existing root-owned 0600 journal classified as unreadable/untrusted;RETAINED_EXIT=101. The new test against the real systemd user manager fails on its bareExecStart=trueunit;USER_MANAGER_EXIT=101. Artifact attached:j245-own-user-red.log. The VM code is15cc9fee7(a2beb5a0bmerged with dev32a2f7046) plus the red tests and a behavior-preserving publication seam. The native build exited 0; no stale test artifact was reused. Root probes are still in the foreground slot queue.J198 root red proof completed through build-slot in the private
/root/j198-j245-red-proofclone. Each of the six focused probes failed as expected (eachPROBE_EXIT=101): unsupported directory publication (ENOSYS), real system manager (NetworkManager), inert stock-unit states, stopped wrappers, renamed copy of the current build, and SIGKILL followed by ordinary-user retry. The last fails exactly at the ordinary-user read of root's 0600 journal. Full root artifact attached:j245-own-root-red.log. Both original J245 probes have now been independently confirmed red; the ordinary-user and real-user-manager logs are already attached at 79982. P1 quick/root/user-manager green run is starting on the same sole target.The first P1 quick run stopped in the D351 shape-lint row before compilation. I reproduced that row on a clean, unchanged dev
32a2f70463bfaf04ffdf39aa7ea3ce34cb60feb1snapshot: 4 tests, 1 failure, due solely to the retainedallow(clippy::too_many_lines)ingotatun_persistence.rs. The attachedj245-d351-baseline-shape.logrecords the failure, exact base SHA and empty status. Separate maintenance commitd5b35d1eeremoves that now-redundant attribute and documents the scenario; it changes no runtime behavior. P1 quick now passes the shape, diff-doc and sleep rows and is compiling workspace clippy. I did not weaken or bypass the gate.J198 J245 repair progress: P1 committed and pushed as
ae0c0a8be. The exact real-host probe failed on the prior code with NetworkManager.service and passes now; the real user-manager probe also failed before and passes now.scripts/verify quickpassed including workspace clippy and affected unit tests. Evidence:/home/claude/j198-evidence/j245-own-root-red.log,j245-own-user-red.log,j245-p1-root.log,j245-p1-user-manager.log,j245-p1-quick.logon compute01. P2 root/user retry check is queued in a foreground SSH session. Remaining P3 changes will stay separate atomic commits. Sole target:/home/claude/worktrees/j198/target.J198 foreground wait update: the P2 quick/root retry run has waited about 16 minutes for a mandatory compute01 build slot; it is first in line and all four slots remain busy. No run was detached. P1 is pushed at
ae0c0a8bewith its real-manager receipts attached at 80017. The remaining P3 patches and final root/fast runner are prepared; their validation will continue in the same foreground session once the slot opens. Sole target remains/home/claude/worktrees/j198/target.J198 crash-safety checkpoint while first in the foreground queue (~30 minutes): unvalidated P2 source/tests are preserved on my separate WIP ref
wip/up-preflight-j198-j245-p2, snapshot1cb3f9b533e6664ce0ef5ef0e3ab48d06553633d. The implementation branch stays at validated P1ae0c0a8be. No validation success is claimed for P2 yet. Resume instructions:/home/kayg/.cache/codex-scratch/j198/resume-current.md; active foreground session 85934; sole target/home/claude/worktrees/j198/target. Remaining slice runner retains its build slot between validations.P2 first quick run: workspace clippy and all other quick rows passed; bin tests were 203 passed, 1 failed, 12 ignored. The failing existing test is
up::install::j218_review::j226_sigkill_retry_recovers_enrollment: read-only retry accepted recovery, but the subsequent exclusive lock transiently reported another installer. Receipt attached (j245-p2-quick.log).The multithreaded suite forks unrelated children while these tests open and close flock descriptors. Linux retains a flock in a forked child until that inherited descriptor closes: a small event-driven reproducer confirms the parent can close its shared descriptor and still fail the next exclusive lock until the child exits (
j245-flock-fork-repro.log, attached). The enrollment signal/retry proofs now execute in a dedicated filtered test process so other suite threads cannot inherit their journal descriptors; production locking is unchanged. The P2 quick/root/user retry rerun is queued in the foreground. Its runner now retains the slot on errors too, allowing repairs without restarting the queue.J198 P2 committed and pushed as
c8e015b7e. A root-owned 0600 journal that ordinary-user preflight cannot read is now pending privileged validation, not corruption. No unprivileged result authorizes writes: root still reopens, verifies and locks the journal before recovery. The signal/retry fixtures run in a dedicated filtered process to avoid unrelated suite forks retaining their flock descriptors.Evidence on compute01:
j245-p2-v2-quick.log(all quick rows PASS),j245-p2-v2-root.log(50 passed, 0 failed, 9 ignored),j245-p2-v2-user-retry.log(SIGKILL → UID1500 read-only preflight → root recovery, passed; journal bytes and enrollment unchanged by UID1500 stage). These logs are copied to control scratch and attached. Wrapper and secondary Exec command repair is now validating in the same foreground slot. Final full fast gate remains pending.J198 P3 wrapper/canonical/secondary-command repair committed and pushed as
7f60e7dab. The manager remains responsible for parsing unit commands. The inspector now includes ExecStartPre/Post/Condition, conservatively checks literal infraternal references in env and shell wrappers, and resolves symlink targets before testing image names/inodes. Dynamic shell construction and external scripts remain documented runtime-inspection limits.Red:
j245-own-wrapper-red.logreproduced the real-manager env-wrapper bypass before the repair. Green: real user-manager probej245-wrappers-real_user_manager_refuses_stopped_installs.logrefuses forms 1–10 (argv0, escapes, environment, bare/prefixed commands, continuation, env/shell wrappers, Pre/Post); forms 0 and 11 remain accepted.j245-wrappers-real_user_manager_accepts_stock_and_bare_commands.logalso passes.j245-wrappers-quick.log: all rows PASS.j245-wrappers-root.log: 51 passed, 0 failed, 10 ignored. The exact normal-user retry proof remains green. Logs are in/home/claude/j198-evidence/and copied/attached here. The known-copy repair is now validating in the same foreground slot; full fast remains pending.J198 P3 known-copy repair committed and pushed as
b02a2ff95. Executable identity now compares the current image and at most three usual installed builds by inode, then lazily cached SHA-256 for size-matched unknown regular files. Descriptors are pinned once per scan; candidate paths open nonblocking so a FIFO cannot stall preflight. No dependency was added. Renamed foreign builds with no known local reference remain documented as a detection limit.The original root red is the
renamed_copy_of_known_build_is_detectedfailure in the already attachedj245-own-root-red.log. It now passes for both a process and a manager command.j245-hashes-quick.log: all quick rows PASS.j245-hashes-root.log: 52 passed, 0 failed, 10 ignored.j245-hashes-user-retry.log: exact SIGKILL/ordinary-user/root recovery sequence still passes. Logs are in/home/claude/j198-evidence/and copied to control scratch. The directory fallback is next; its first quick run caught a missing test import, which is fixed. An added SIGKILL test will validate durable replay of the fallback's newly allocated inode, with a red run against the unsupported-publication seam first. Full fast remains pending.J198 P3 directory fallback committed/pushed as
d2687f08a; merged currentorigin/dev(53e41d1ca) into my branch and pushed final candidatec5bf1dcb9a. No merge into dev or deployment was performed.Directory publication now falls back on ENOSYS/EINVAL/EOPNOTSUPP to exclusive mkdir, immediately journaling its new inode. Linux cannot hard-link directories; regular files already use link/unlink. A synced Unpublished record revokes the staging inode's old destination claim before mkdir. Recovery acts only on the new recorded inode. A stop between exclusive mkdir and its record leaves an unproven path for inspection, never deletion by assumption.
Red receipts: original forced fallback fails ENOSYS (
j245-own-fallback-red.log); added root SIGKILL replay test fails the same unsupported-publication seam (j245-fallback-crash-red-root.log, exit101). Green receipts:j245-fallback-v2-quick.logall rows PASS;j245-fallback-v2-root.log54 passed/0 failed/11 ignored, including forced fallback with occupied-path preservation and SIGKILL next-run replay;j245-fallback-v2-user-retry.logexact normal-user retry remains green. All logs under/home/claude/j198-evidence/, copied/attached here.The foreground slot now runs final quick, private-root proof, both real user-manager probes, two parallel restart suites and scripts/verify fast verbatim at
c5bf1dcb9. Sole target/home/claude/worktrees/j198/target; final full-gate result remains pending.Final candidate
c5bf1dcb9a8b2121ce35d10bd3d37bb6ae04cd44: quick passed. Real UID0 proof in/root/j198-j245-final-proofpassed 54 tests (0 failed, 11 helpers/host-specific tests ignored), then explicitly passed the SIGKILL → UID1500 → root recovery proof and real system-manager acceptance.Exact root receipt (
j245-final-root.log):SIGTERM/SIGINT/SIGHUP still roll back immediately; the SIGKILL next-run and alias/inode-preservation regressions pass. Both real user-manager probes pass: stock states/bare commands accepted, forms 1–10 refused, unrelated controls 0/11 accepted.
Two restart suites ran in parallel: 19 passed, 0 failed each; PARALLEL_RESTART_EXIT=0,0. Every one of the seven reproduced size-dependent failures passes in both runs; names and source receipts are recorded in
j245-final-seven-restart-tests.txt. These final proof and parallel logs are attached. Production's 512MiB binary admission bound remains unchanged.scripts/verify fastis running in the foreground in the same slot, log/home/claude/j198-evidence/j245-final-fast.log; it is building CLI feature variants. This is a progress report, not the final gate result. Sole target/home/claude/worktrees/j198/target; cleanup follows the completed gate.Foreground final-gate progress at unchanged head
c5bf1dcb9a8b2121ce35d10bd3d37bb6ae04cd44: the full workspace test row passed ((test: 1495s)). The SSH inode-reuse fixture reportedINODE_REUSE_SKIPPED: the filesystem did not reuse a released inode during 256 initial attempts; it has 0 failed and 1 ignored. The gate is now building the shim process-per-user variant. Final full-gate result is still pending; log remains/home/claude/j198-evidence/j245-final-fast.log. Root54/0, real-manager probes and both parallel restart19/0 receipts are attached at 80109. Sole target/home/claude/worktrees/j198/target; source is committed/pushed and no further code changes are pending. Continuing the foreground wait through the remaining rows.J198 final gate update at head
c5bf1dcb9a8b2121ce35d10bd3d37bb6ae04cd44: workspace test row passed (1495s); shim variants, cargo-deny, architecture and D351 source checks passed. Fullscripts/verify fastis still running in the foreground through static builds (first release build finished; second target compiling).One failure in the privileged row's 81 runs:
infraternal-plugin-mesh test:gotatun_tun_owner an_unprivileged_user_cannot_attach_the_persisted_tun, line 75, EINVAL. Its source requires real root and switches to UID/GID65534; the sandbox maps only UID0. This appears to be a sandbox test-selection limitation, but baseline confirmation is pending. I will replay the exact test on unmodified merged dev53e41d1caafter this gate releases the slot. No off-scope gate or mesh changes.Artifact:
/home/claude/j198-evidence/j245-final-fast.logon compute01. Final root/manager/parallel-restart receipts are attached to comment80109. No merge or deploy.J198 final
scripts/verify fastcompleted atc5bf1dcb9a8b2121ce35d10bd3d37bb6ae04cd44, exit 1. All rows pass except one privileged integration test. Baseline replay is queued in the foreground (compute01 position5); classification is pending. Full log attached.Static report-only sizes: x86_64 24,865,136 bytes, gate baseline22,418,256, delta+2,446,880; arm64 22,545,864 bytes, gate baseline20,453,280, delta+2,092,584. Both statically linked, stripped, no PT_INTERP.
J198 J245 repair round complete on
up-preflight-j198, pushed headc5bf1dcb9a8b2121ce35d10bd3d37bb6ae04cd44. Merged dev53e41d1cainto the branch. No merge into dev, deploy, or issue closure.Atomic fixes (each has failing-first evidence already attached in this issue):
ae0c0a8be: accept normal systemd units; skip template/masked/not-found/no-command units; resolve bare executables using the manager/unit search path; limit conservative refusal to identified binaries and unresolvable infra references. Real manager acceptance/refusal coverage.c8e015b7e: a root-owned private journal unreadable by the normal user is pending root recovery, never corruption. Root revalidates the journal before recovery. Exact SIGKILL → UID1500 preflight → root recovery passes.7f60e7dab: stopped units inspect pre/post/condition commands, literal env/shell wrappers and canonical symlinks.b02a2ff95: inode identity plus lazy cached SHA256 against locally known executable builds catches renamed copies; nonblocking regular-file checks avoid FIFO stalls. A renamed foreign build without any known local reference, and dynamic/external shell scripts, remain documented detection limits.d2687f08a: unsupported directory rename-no-replace uses exclusive mkdir and a synced journal revocation/new-inode record. Linux cannot hard-link directories; regular files already use atomic link/unlink. A stop before a new directory inode is journaled leaves an unproven path for manual inspection, never guessed deletion. Forced fallback, foreign occupied destination and post-record SIGKILL replay are covered.Final proofs at this exact head, attached in comment80109:
/root/j198-j245-final-proofrootup::tests: 54 passed, 0 failed, 11 ignored. Separate normal-user retry and real-host manager tests each passed.j245-final-seven-restart-tests.txt). Production 512MiB restart admission bound unchanged.Environment notes: as requested by J226, the two mode000
setup_outputtests fail with full UID0 CAP_DAC_OVERRIDE and pass without it; that is a test-environment limitation, not this repair. The SSH inode-reuse fixture skipped because this filesystem did not reuse an inode in 256 attempts.Final
scripts/verify fastresult: exit1, solelyprivileged integration FAIL(1). Baseline-confirmed on clean dev53e41d1ca5946dbe8703571b731fa8e07161d4cd: exact mesh testan_unprivileged_user_cannot_attach_the_persisted_tunfails at line75 with EINVAL, sandbox exit101. The test explicitly requires real root and switches to UID/GID65534; the gate sandbox maps only UID/GID0. A separate namespace probe records uid/gid maps0 1500 1andsetgid(65534): OSError(22, Invalid argument). This is an unrelated privileged test-selection limitation, requiring an orchestrator gate follow-up; no gate or mesh behavior changed here. VM restored to final head and clean after baseline replay.Artifacts attached:
j245-final-fast.log,j245-baseline-sandbox.log,j245-sandbox-uid-map.log,j245-final-cleanup.log. Canonical control copies:/home/kayg/.cache/codex-scratch/j198/evidence/; VM:/home/claude/j198-evidence/.Verbatim final gate table:
Static sizes (gate-recorded baselines, not a separate origin/dev release rebuild): x86_64 24,865,136 bytes, baseline22,418,256, delta+2,446,880; arm64 22,545,864 bytes, baseline20,453,280, delta+2,092,584. Both static, stripped, no PT_INTERP. Static rows PASS; arm64 native key-memory probe SKIP on x86.
Cleanup complete: sole target removed; all eight private root-proof clones removed; own
/tmp/j198*bundles/images/state/scripts removed; no job executables remain running. Source worktree and small evidence logs retained.git ls-remoteconfirms pushed headc5bf1dcb9a8b2121ce35d10bd3d37bb6ae04cd44.J250 re-review of
up-preflight-j198atc5bf1dcb9a8b2121ce35d10bd3d37bb6ae04cd44, delta sincea2beb5a0b(J198'sae0c0a8be,c8e015b7e,7f60e7dab,b02a2ff95,d2687f08aplus D351 housekeepingcb7163365,d5b35d1ee,301fc79fe). I read CLAUDE.md, D349/D351, BRIEF-COMMON, J245's 79938 with its probe patch, and 79939–80109. As the reviewer rules say, I did not runverify fastorverify quick. I built the test binary once from the pushed SHA through build-slot on compute01 (clean tree,cargo test --locked -p infraternal --bins --no-run). I ran the root probes from a private clone/root/j250.Verdict: APPROVE. P1 and P2 are fixed and proven on the real host. 3 × P3 remain. They don't block, but each fix is a few lines, so I recommend landing them before merge or recording them as known limits.
P1: stock systemd hosts. Fixed.
These ran as root against compute01's real system manager (Debian 13, systemd 257.13). Each planted unit sat in
/run/systemd/system, was never started, and was removed after its probe. Artifact:j250-root-run.log.j245_real_host_manager_accepts_stock_unitson the unmodified host:Ok(()),stock-host PROBE_EXIT=0. NetworkManager,getty@, maskedsudo, not-foundsshdandpath=python3all pass now.ExecStart=/opt/j250/infra serveris refused ("invokes infraternal"),PROBE_EXIT=101.up_inspect.rs:529).masked/not-foundare skipped (:402). A unit with no Exec command is accepted. A bare name is searched (:344-359). Refusal of an unresolved command needs a mention of infra (:430-451).P2: retained root journal. Fixed.
journal::inspect(root, 0), returnsOk(())(J245 euid=1500 journal inspect: Ok(()),USER_EXIT=0).preflight, then rootInstall::begin:J245_USER_RETRY_ACCEPTED,sigkill-retry EXIT=0. The test asserts that the user stage changed neither the journal bytes nor the enrollment, and that the root stage recovered both.up_journal.rs:221-241,up_install.rs:58-62,:202-206): the handoff applies only to EACCES, owner 0, a non-root euid, and a file that is a regular file with uid 0, mode exactly 0600, nlink 1 and size within the limit. It returnsneeds_rootwith an empty snapshot. That carries no authority to delete anything.Install::beginrefuses a non-owner euid and then repeats the full check under the lock. The only cost: the pre-sudo stage skips the service and path checks while a journal is pending, so the root stage reports those refusals instead. That is acceptable.P3 targets from J245
/usr/bin/env infra server, a renamed copy/opt/j250/control(different inode, same bytes), and a barej250ctlsymlinked in/usr/local/sbinare all refused (PROBE_EXIT=101). With J198's harness corrected for my environment, both real user-manager probes pass: stock commands are accepted, forms 1–10 are refused, and 0/11 are accepted (j250-user-manager-rerun.log). My first in-script run showed 101 because myrunuserinherited a root-owned test state root; that was my harness, not the branch.systemd-run -p SystemCallFilter=~renameat2 -p SystemCallErrorNumber=ENOSYS|EINVALaround the wholeup::installfilter. A ctypes control in the same unit confirms the filter bites:renameat2 probe: -1 Function not implementedand-1 Invalid argument. Result:26 passed; 0 failedfor both ENOSYS and EINVAL, the same as the unfiltered control. That includesj245_sigkill_recovers_fallback_publicationanddirectory_publication_fallback_is_exclusive_and_owned. The code (up_install.rs:316-350) is sound. The staging directory is always empty at publication. TheUnpublishedrecord syncs before the exclusivemkdir, and the new inode is recorded before the chmod. A crash betweenmkdirand its record leaves a 0700 directory, which preflight refuses for the data, bin and agent paths (up_install.rs:86-109).Findings
P3-1: bare command names are searched on the unit's
Environment=PATH, which systemd does not use.crates/infraternal/src/up_inspect.rs:320-341(search_path, used at:413). The unit testunit_environment_path_exposes_an_inode_alias(:745-761) encodes this precedence. systemd resolves a bareExecStart=name onExecSearchPath=or its compiled default, and ignores the unit'sPATH. Proof on compute01 (j250-systemd-path-probe.log): a unit withEnvironment=PATH=/nonexistentandExecStart=j250probereportspath=j250probeand ran/usr/local/sbin/j250probe. Bypass (j250-root-run.log): the same unit withExecStart=j250ctl server, where/usr/local/sbin/j250ctlpoints to our image, is refused without theEnvironment=line but accepted with it (bare-alias+unit-PATH PROBE_EXIT=0). Fix: searchExecSearchPath=(or the default) as systemd does. To stay conservative, also check the unit's PATH, and refuse when either finds our image.P3-2: the wrapper allow-list misses common exec wrappers.
up_inspect.rs:376-387only looks insideenvand shells.ExecStart=/usr/bin/nice -n5 /opt/j250/infraternal serverand/usr/bin/setpriv --reuid=0 /opt/j250/control serverare both accepted on the real manager (PROBE_EXIT=0). The same applies totimeout,stdbuf,chrt,ionice,taskset,runuser,sudoandnohup. Matchingimage_nameon every argv word regardless of the program, plus an inode check on absolute argv words, would close this class without a list. The second named form is a renamed copy, which only an inode or hash check can catch. As in J245's P3-1, only stopped units are affected;/proccatches a running server.P3-3: an unresolvable unrelated command is refused when any of its text contains "infra".
up_inspect.rs:261-263is a case-insensitive substring match over the wholesystemctl showoutput, including arguments and environment values. That makesrelevant(:397) true, so:438-451refuses. A unit withExecStart=/opt/infrastructure-tools/missing --xis refused ("Could not resolve the command ...",PROBE_EXIT=101). This is fail-closed and stock Debian passes, but it goes beyond "refuse only an unresolvable command that names infra or infraternal". Fix: testimage_nameon the program and argv words instead of a substring.Info, no severity:
ExecReload=andExecStop=are not inspected (:417-422). That is fine, because they run only while the unit is active, which/proccovers.D351 rows
These ran on compute01 against
origin/dev(merge base53e41d1ca), inj250-d351-rows.log:test-shape-lints.py: OK,SHAPE_EXIT=0.test-diff-practices.py:DP_SELFTEST_EXIT=0.diff-practices.py docs origin/dev:DOCS_vs_dev_EXIT=0.diff-practices.py sleeps origin/dev:diff sleeps: ok.doc-coverage --check:doc coverage ratchet: ok.The only
too_many_*allows left are invendor/russh. Measured againsta2beb5a0b, these rows also count dev's merged code. Even then, nothing inup_inspect.rs,up_install.rs,up_journal.rs,j218_review_tests.rsorpty.rsis flagged. Every new item in J198's commits carries a doc comment.Cleanup
/root/j250(clone and binary), the planted units,/usr/local/sbin/j250ctl,/opt/j250,/tmp/j250*and~/worktrees/j250(target included) are removed, and the manager is reloaded. No J250 processes remain;/has 371G free. Note: the eight/root/j198-j245-*proof clones (about 842M each) were still present at 23:00 IST. Nothing was pushed, merged, deployed or closed.APPROVE: P1 and P2 verified fixed on the real host. 3 × P3 open (unit-PATH search precedence, wrapper allow-list, "infra" substring false refusal).
J198 follow-up started on
up-preflight-p3-j198, baseorigin/dev=5ea05cc2e1f174ce47cc430bd13e9457ae91f29c. Read J250 comment80234. Scope: three P3 repairs only, one failing-first regression and atomic commit per repair: (1) systemd executable search ignores unit Environment PATH; (2) documented explicit exec-wrapper list with literal known-image inspection; (3) whole-name/path-component references instead of infra substrings. Then quick, full fast, and true-root real-systemd proof on compute01; push/report head, no merge/deploy, delete target.Estimate: 45–60 min implementation/red-green; approximately 90 min gates and root proof plus build-slot queue. Sole target remains
/home/claude/worktrees/j198/target. Foreground waits only.J198 follow-up finding: initial
scripts/verify quickstops before clippy/tests at D351 shape-lints. It flags#[allow(clippy::too_many_lines)]incrates/infraternal-server/src/mesh_ssh.rs:349, already present in base5ea05cc2e1f174ce47cc430bd13e9457ae91f29cand untouched by this job. Reproducedpython3 scripts/test-shape-lints.pyagainst a cleangit archive 5ea05cc2esource snapshot (indexed so the check'sgit ls-filesscan works): same one failure, exit1. Artifacts on compute01:/home/claude/j198-evidence/p3/initial-quick-red.log,/home/claude/j198-evidence/p3/shape-dev-red.log. I am leaving that off-scope server file unchanged and continuing the three regression red runs from the native binary build. Foreground build active through build-slot.J198's three J250 P3s are committed and pushed on
up-preflight-p3-j198, head64669d72737d3aedc0411c7f2a5c5a49ede9faf6, base5ea05cc2e(still current dev at final fetch).438cbb502: useExecSearchPathor systemd's compiled default for bare command names, independent of Environment PATH. Replaces the old precedence regression and removes the unused environment decoder/property request.3c4325929: explicitly documents and adds nice, setpriv, ionice, chrt, taskset and stdbuf to the literal wrapper list; inspect absolute arguments against existing inode/hash identities as well as distributed names. Does not evaluate shell programs or wrapper options.64669d727: wholeinfra/infraternalword/path components replace substring matching; punctuation stays inside basenames so infrastructure-tools and my-infra-helper are unrelated.Each regression first failed on unchanged production code with exit101 (all three red logs attached). After each repair, inspection suite: fix1
6 passed, 0 failed, 2 ignored; fix27 passed, 0 failed, 2 ignored; fix38 passed, 0 failed, 2 ignored. Workspace clippycargo clippy --workspace --all-targets --locked -- -D warningsPASS (workspace-clippy.log).The final quick, true-root proof in
/root/j198-p3-proofagainst compute01's real systemd, and verbatim full fast are now running in the foreground at this head. Baseline D351 shape-lint failure recorded in comment80285 remains unchanged. Sole target/home/claude/worktrees/j198/target, will delete at finish. No merge/deploy.J198 broader root validation found two legacy boundary regressions before fast started (
boundary-root-red.log, retained). Rootup::: 54passed/2failed/11ignored at candidate64669d727.j218_alias_commands_respect_word_boundariesstill expected/opt/infra-otherto refuse. That expectation conflicts with this follow-up's requested whole-name semantics; changing it to accept.j226_systemd_command_forms_refuse_serverexposed the raw unresolved-infraspelling. Preserve the single leading systemd ignore-failure sigil as an identity hint, without treating/opt/-infraas aninfrapath component. Added both boundary controls to the P3 regression. This is literal hint matching, not parsing systemd commands.Fix3 will be amended after the broader suite and workspace clippy pass; first two commits remain unchanged. Requeued foreground on compute01 to reuse the sole target needed for the required root proof. Both build-slot queues were checked; dev02 has fewer queued jobs but would require a second target and a separate native compute01 build. No full fast has started yet. Evidence source of all failures remains on compute01 and control.
J198 boundary rerun PASS: full
up::as ordinary build user 56passed/0failed/11ignored (boundary-green.log), workspace clippy PASS (workspace-clippy-v2.log). Amended fix3 to9388f5cd0with the raw -infra hint and updated infra-other expectation; first two fixes remain438cbb502,3c4325929.Merged current dev
f9f8ced33(launch lifeline work plus the shape-lint repair), pushed final candidate7087c58de3b1f530b585618f594af5292bee6ac1onup-preflight-p3-j198. Control worktree clean. The earlier baseline shape blocker is now resolved by that dev merge. Finalscripts/verify quick, true-root proof against the real manager, thenscripts/verify fastare running in the foreground at this head. Logs/home/claude/j198-evidence/p3/final-{quick,root,root-manager,fast}.log; sole target unchanged. No merge into dev or deploy.J198 final validation at7087c58de: root
up::56passed/0failed/11ignored, and explicit SIGKILL→normal-user retry proof PASS. Two environment/baseline findings stopped this first final attempt before fast:restart::recovery_tests::oversized_debug_image_does_not_limit_restart_fixturespads to512MiB+1 but dev commitd2acd9847raisedRUNNING_IMAGE_LIMITto768MiB (#221). Its child now correctly accepts that image, contradicting the fixture assertion. Neither that production constant nor fixture is changed in this follow-up. Exact clean-dev reproduction is queued. Artifactmerged-quick-oversize-red.logattached. The earlier D351 shape failure is resolved by merged devf9f8ced33.infraternal-login-f508ff2f2545485eb31222b845b7c020-0.servicebelonging to another job. It runs a login worker from/var/cache/infraternal-login-bin; we did not stop, change or remove it. The proof's own stopped unit and alias were removed in its finally guard. Artifactshared-manager-host-refusal.logattached.I am adding one ignored test-only proof entry that classifies a uniquely prefixed owned unit via actual
systemctl showproperties. This lets the real-root proof check all three repairs on a shared machine without changing production whole-host preflight or assuming no other infraternal work exists. The independent root transaction suite already passes. This test-only proof addition, clippy/up:: verification and the exact dev baseline replay are queued in the foreground on compute01, reusing the sole target. No production-bound changes or off-scope restart fix; no merge/deploy.J198 final head
37f3e7349d5c2b082298fed762d7f55203451714pushed onup-preflight-p3-j198(includes current merged devf9f8ced33). The three production fix commits remain438cbb502,3c4325929,9388f5cd0;37f3e7349is a separate test-only shared-manager proof entry.True UID0 proof at this exact head: root
up::56passed/0failed/12ignored; explicit SIGKILL→ordinary-user retry→root recovery PASS. Fake server directory remains0700 with unchanged contents/link. Real system-manager proof: all19 cases PASS, including bare alias with Environment PATH=/nonexistent, each of six wrappers with a named CLI and a renamed inode alias, accepted infrastructure path/environment controls, literal infra component refusal and missing/removed-unit controls. Only uniquely named stopped units were planted; none started. The finally guard removed the owned unit and executable alias. Unrelated active login worker untouched. Receiptsfinal-root.log,final-root-manager.log; external proof harness attached for reproduction.Quick passes all rows except its one baseline oversized-image regression: 209passed/1failed/15ignored. Exact replay on clean dev
f9f8ced338278d924803a691f9636a9519babcb2fails identically, exit101 (oversize-dev-red.log). Dev's production bound768MiB and the fixture's padding512MiB+1 are both untouched here. This supersedes the earlier shape-lint blocker, fixed in merged dev.Full
scripts/verify fastis still running in the FOREGROUND, currently CLI compile-out variants, log/home/claude/j198-evidence/p3/final-fast.log. No merge into dev or deploy. Final verbatim table and cleanup to follow.J198 foreground fast progress at
37f3e7349d5c2b082298fed762d7f55203451714: privileged integration PASS (7/7), shim process-per-user and the D351/source checks completed, release-exclusion and nightly-fuzz rows PASS. Static builds now running.Log scan finds the same
oversized_debug_image_does_not_limit_restart_fixturesbaseline failure in BOTH CLI-plugin-compile-out and workspace-test rows. This is the unchanged fixture's512MiB+1 padding versus dev's768MiB running-image bound, already reproduced on clean devf9 in the attachedoversize-dev-red.log; no other failure found so far. Final gate table will be quoted after the static rows finish. Root56/0 and all19 real-manager cases at this head are attached to comment80360. One target only, cleanup after final gate. No merge/deploy.J198 follow-up implemented and pushed on
up-preflight-p3-j198, head37f3e7349d5c2b082298fed762d7f55203451714, including merged devf9f8ced33. No merge into dev or deploy.Three atomic production fixes, each with a regression that failed before implementation:
438cbb502: bare executable lookup uses systemd's ExecSearchPath or compiled default, independent of child Environment PATH; replace the old precedence test and remove the unused environment decoder.3c4325929: documented literal wrapper list now includes nice, setpriv, ionice, chrt, taskset and stdbuf. Absolute argument paths use existing inode/hash identity checks, covering renamed images without evaluating shell programs or wrapper options.9388f5cd0: unresolved identity hints use whole infra/infraternal names or path components, excluding infrastructure-tools, infrasonic, my-infra-helper and infra-other. Preserve raw systemd -infra as a hint while excluding /opt/-infra; update the legacy substring expectation.37f3e7349adds only an ignored real-manager proof entry for a uniquely named stopped review unit. It leaves production whole-host refusal intact. The initial whole-host run correctly refused another job's active infraternal login worker; that unit was never touched. The targeted proof queries actual systemctl show properties and calls the production classifier, allowing repeatable verification on the shared host.Evidence at the final head:
final-root.log).final-root-manager.log,root-manager-proof.py.txt).Quick and fast are RED due the same unchanged baseline fixture,
restart::recovery_tests::oversized_debug_image_does_not_limit_restart_fixtures. Dev commitd2acd9847 raised RUNNING_IMAGE_LIMIT to768MiB, but the fixture still pads to512MiB+1 and expects refusal. Exact clean-devf9 replay fails identically with exit101 (oversize-dev-red.log). This branch changes neither the production bound nor that fixture. Quick's other rows PASS;209 tests pass,1 fails,15 ignored. Full fast records the same fixture failure in CLI plugin compile-out and test rows. The workspace test run stops at this failed target, so this is not a claim that all workspace tests ran. The earlier baseline D351 shape failure was repaired by merged dev.Final
scripts/verify fastexit1, all remaining rows PASS, including privileged integration7/7 and static builds. Verbatim table:Static sizes (gate-recorded baselines, not an isolated origin/dev release rebuild): x86_64 27,175,472 bytes, baseline22,418,256, delta+4,757,216; arm64 24,501,456 bytes, baseline20,453,280, delta+4,048,176. Both static/stripped with no PT_INTERP. Arm64 native key-memory probe SKIP on this x86 host. The SSH inode-reuse fixture also reported SKIP because this filesystem did not reuse an inode in256 attempts; its wrapper row is PASS.
Cleanup verified (
final-cleanup.log): targetFalse, private root cloneFalse, job temp paths[], owned units[], owned CLI aliases[], owned executable/cwd processes[]. Native gate driver exited. Source worktree retained clean at37f3e7349, andgit ls-remoteconfirms the pushed head. No merge into dev, deployment or issue closure.Final receipts attached:
final-fast.log,final-quick.log,final-root.log,final-root-manager.log,oversize-dev-red.log,final-cleanup.log. Canonical control artifacts/home/kayg/.cache/codex-scratch/j198/evidence/p3/; VM/home/claude/j198-evidence/p3/. Root proof harness and prior per-fix red/green logs are attached in comments80300/80334/80360. The remaining gate blocker is the unchanged baseline oversized-image fixture.J198 starts the high-priority oversized restart fixture repair on
restart-oversized-fixture-j198, base3e659eb78bee41921af9585a59aa9e404c8a0d2b. Plan: reproduce the existing regression on this clean dev, share the unchanged production running-image bound with its padding fixture, prove red/green, then quick and fast in the foreground. Estimate: 20–30 minutes implementation/reproduction plus queued build/gate time. One target: compute01/home/claude/worktrees/j198/target; compute01 has a free slot, dev02 is queued.Scope addition from the orchestrator: also isolate
j226_failed_stop_keeps_recovery_authorityand prove parallel execution, in a second atomic commit. Inspection showsFixture::newalready gives every test a private UUID root and journal. The suspected race is inherited flock descriptors across sibling test forks, which the existingisolated_enrollment_proofhelper already prevents for several recovery tests. I will reproduce before choosing the smallest fix. J216b candidate2a07151b0matches the intended shared-bound repair; its unexecuted proof does not replace this run. Clean-base quick passed; fresh exact regression build still running, logs at/home/claude/j198-evidence/oversized/on compute01.Reproduced on clean
3e659eb78: oversized-image regression exit 101, becauseOpened::running().is_err()was false. Shared-bound repair9763829a9is committed/pushed: the exact test now passes, production oversized running-image refusal is preserved, and all 19 nested restart tests pass. Production 768 MiB running-image and 128 MiB replacement bounds are unchanged.Also reproduced the additional install failure on the FIRST run with
--test-threads=16: 17 passed, 1 failed, 7 ignored;j226_failed_stop_keeps_recovery_authorityfailed with “Another infraternal up installation is still running”. Each fixture already has a UUID-specific root and private journal. The cross-test leak is a journal lock inherited by concurrent sibling forks, not a shared root. The smallest repair uses the existing isolated enrollment proof process for this two-attempt test, keeping both its UUID root and lock private from sibling forks. Running quick and 20 parallel review-suite repetitions before the second commit and final gate.Evidence retained on compute01 under
/home/claude/j198-evidence/oversized/{red,green,up-parallel-base}.log, and copied to control/home/kayg/.cache/codex-scratch/j198/evidence/oversized/.Both atomic fixes are pushed:
9763829a9shares the unchanged running-image bound;d17bcbf99ee7c20bde89e732a7ce5072c0c1a2f3isolates failed-stop recovery from sibling forks.verify quickpassed, including workspace clippy and 210 CLI tests (0 failed, 15 ignored). All 20 review-suite runs with--test-threads=16passed (18 passed, 7 ignored each); the failed-stop test passed in every repetition. Before isolation, the first identical parallel run failed with the live-install lock refusal.Fetched origin/dev before final verification: still
3e659eb78.scripts/verify fastis queued in the foreground on compute01 using the same sole target as quick. Both VMs are occupied; reusing the warmed native target avoids another full build. Logs:/home/claude/j198-evidence/oversized/{quick,up-parallel-green,fast}.log(fast log is created when the slot starts). Small completed logs are also retained on the control host under/home/kayg/.cache/codex-scratch/j198/evidence/oversized/. No merge or deploy.J265 review start: reviewing restart-oversized-fixture-j198 at
d17bcbf99(base origin/dev3e659eb78). Plan: inspect both commits and J175 compatibility, reproduce plain-dev oversized failure, then run both named tests 10× each on a dev+branch merge with --test-threads=16 using one VM scratch target; delete target and post verdict. Estimate: 30 minutes plus build-slot queue. Reviewer-only targeted checks; no full gate.J265 finding: latest fetched dev is
159edcedc9; reviewer dev+branch merge is 22cf6c9be262a5400ca289cc5d23ff44faabc367. Both requested repairs merge cleanly onto dev. However, merge-tree against J17573d6180b2reports a real content conflict in restart_binary.rs: this branch changes visibility on the 768 MiB declaration while J175 restores 512 MiB and its documentation. Required combined resolution: retain J175 documentation andpub(crate) const RUNNING_IMAGE_LIMIT: u64 = 512 * 1024 * 1024;, with this branch’s shared padding reference. Do not resolve by keeping 768 MiB if integrating the revert. Evidence: compute01 /home/claude/j265-evidence/diff-compatibility.log. Targeted red/green + 10 parallel repetitions are queued, using only /home/claude/worktrees/j265/target (removed by runner on exit).J198 checkpoint: head remains pushed
d17bcbf99ee7c20bde89e732a7ce5072c0c1a2f3; no source edits pending. Finalscripts/verify fastacquired compute01 slot 2 after the queue and is still running in the foreground. CLI plugin compile-out completed (282s); the full workspace test row is progressing through server bootstrap tests, with noFAILEDentries so far. Do not treat this as a completed gate. Full current output:/home/claude/j198-evidence/oversized/fast.log. Earlier quick and red/green/20 parallel runs are complete and retained in that evidence directory and the control-host copy. Implementation/reproduction is complete; remaining elapsed time is the shared-machine queue and full gate, then cleanup of/home/claude/worktrees/j198/target.J265 verdict: REQUEST CHANGES (integration compatibility).
Reviewed
restart-oversized-fixture-j198atd17bcbf99ee7c20bde89e732a7ce5072c0c1a2f3(commits9763829a9,d17bcbf99), authored from3e659eb78. Fresh origin/dev was159edcedc9978f0370ea8cb02a937082d631c9b6; targeted green tests used its clean scratch merge with the branch,22cf6c9be262a5400ca289cc5d23ff44faabc367.Finding: the requested consistency with J175
73d6180b2is not yet achieved. That commit restores the running-image bound to 512 MiB; this branch still has 768 MiB.git merge-tree --write-tree 22cf6c9be262a5400ca289cc5d23ff44faabc367 73d6180b2exits 1 with a content conflict incrates/infraternal/src/restart_binary.rs. Reconcile the two changes before integration: preserve J175's restored-bound documentation andpub(crate) const RUNNING_IMAGE_LIMIT: u64 = 512 * 1024 * 1024;, while retaining this branch's shared padding reference. Choosing the branch's 768 MiB line would discard the restoration; choosing J175's private constant would break the new reference. Evidence: compute01/home/claude/j265-evidence/diff-compatibility.logandsource-review.log. This is a demonstrated integration conflict, not a claim that this branch raises its current dev bound.The two repairs otherwise retain their purpose. The oversized fixture still executes production refusal first and then the restart suite from the padded executable. No replacement-size bound, running-image bound, timeout or assertion is weakened by the reviewed diff. The failed-stop change is process isolation, not a retry: the existing helper executes one exact child test, propagates child failure, and leaves both install attempts and every durable-ownership assertion intact. Its private child prevents sibling test forks from inheriting its journal lock.
Independent checks on
claude@infraternal-dev-01-compute01, through build-slot, nice -n10, -j4, one target:cargo test -j4 -p infraternal restart::recovery_tests::oversized_debug_image_does_not_limit_restart_fixtures -- --exact --test-threads=16 --nocapture: exit 101, failingOpened::running().is_err(). Artifact:/home/claude/j265-evidence/dev-red.log.--test-threads=16. Every run printsproduction oversized running-image refusal: preservedand passes all 19 nested restart tests. Artifacts:/home/claude/j265-evidence/oversized-{1..10}.log.cargo test -j4 -p infraternal up::install::j218_review:: -- --test-threads=16 --nocapture, 10/10 passes. The namedj226_failed_stop_keeps_recovery_authoritypasses every time alongside sibling tests; each suite reports18 passed; 0 failed; 7 ignored. Artifacts:/home/claude/j265-evidence/recovery-suite-{1..10}.log.Verbatim reviewer summary:
Artifact:
/home/claude/j265-evidence/summary.log; independently checked test markers and counts invalidated-report.txt. Sole scratch target/home/claude/worktrees/j265/targetis deleted (cleanup.log); reviewer runner has exited. Evidence also copied to control/home/kayg/.cache/codex-scratch/j265/evidence/.No repository code changed; reviewer quick/fast gate not run, as required by BRIEF-COMMON Reviewers. Read the author's quick/parallel evidence in #80545 and full fast output at
/home/claude/j198-evidence/oversized/fast.log; the author's fast gate was still in progress at the last read, so this review does not claim it passed (snapshotauthor-fast-incomplete-tail.log). No push, merge into dev, or deploy.Orchestrator: merged
restart-oversized-fixture-j198(9763829a9,d17bcbf99) to dev as9b0c731e8. merge.sh gates passed. J265's content review was clean: purpose kept, 10/10 green on both tests, plain dev reproduced exit 101.J265's only finding is a conflict with the unmerged
mesh-ssh-p3-j175(73d6180b2, 512 MiB). J175 will reconcile it on its own merge: keeppub(crate)and the shared padding reference, take 512 MiB if the real debug binary fits. I merged before J198's author fast run finished, because this test was red on dev and blocking other gates; the next dev gate run covers it.J198 closed out after merge
9b0c731e8: both fixes are included (install-lock isolationd17bcbf99already proved by 20 parallel passes); stopped only my fast run at your request (exit 143 during static builds), freed its slot, deleted the sole 48G target, and retained evidence in/home/kayg/.cache/codex-scratch/j198/evidence/oversized/and compute01/home/claude/j198-evidence/oversized/; no remaining J198 work.J299 pre-deploy review of
74b9c4d80vs prod8d3207cc8: GO, details in #305 (comment 82162).