Repository navigation
fix(pod): honor a deploy's authored engine selector in config, start and teardown - #47
Merged
Merged
Conversation
…tart PrepareVenue now carries the deploy node's authored `engine:` into the per-host state patch (with the box name and pod target, so an engine-only no-overlay entry is complete and valid). config/start/remove derive the persistence mode from the SELECTED engine's capability row instead of the host-wide engine.run, so a pod deploy authored `engine: containerd` emits the engine's systemd unit and starts it through nerdctl, never a podman quadlet. Adopts spec v0.2026309.1917 and sdk v0.2026309.1926 (opencharly/charly#1109). Assisted-by: Claude Code ollama-cloud/deepseek-v4.1-flash:cloud (fully tested and validated)
A disposable pod bed's image is built locally under the project's registry/name. When the deploy's run engine is containerd, its store lacks that image and the registry pull misses, so build:ensure must rebuild it from the project — and it received no dir, failing 'buildkitdrive: build: no context' before config/start could run. Thread the project dir through ensureImagePresent via the existing deploy-plugins-connect preamble, and pin the request with a capture test (opencharly/charly#1109). Assisted-by: Claude Code ollama-cloud/deepseek-v4.1-flash:cloud (fully tested and validated)
writeSystemdUnitConfig passed the CONTAINER working dir (/home/user) as the engine unit's WorkingDirectory. unit_emit renders that as systemd's WorkingDirectory=, which systemd applies on the HOST before spawning nerdctl — so every containerd start failed status=200/CHDIR (measured on the first live containerd pod bed). The container workdir is already the engine argv's ; the host unit needs none (opencharly/charly#1109). Assisted-by: Claude Code ollama-cloud/deepseek-v4.1-flash:cloud (fully tested and validated)
Review — PASSHead SHA: Security screen: PASS — no secrets/exfil, no weakened guardrails, no scope mismatch (all 14 files map to the 4 stated defects), spec/sdk pins bumped producer-first.
Verdict: PASS ollama-cloud/deepseek-v4.1-flash — action-review. |
This was referenced Nov 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This is the
deploy:podleg of opencharly/charly#1109. Four defects in onecoherent change:
PrepareVenueshipped astate patch containing only
resolved_image, so apod:node authoredengine: containerdproduced a per-host entry with noengine, andcharly configfell back to the host-wideengine.run(podman). The patchnow carries the node's
Engine, plus its box name and thepodtarget soan engine-only no-overlay entry is a complete image-backed pod entry (and
never the targetless
group:the node-form writer would otherwise emit).config/start/removebranched on
rt.RunMode, resolved forrt.RunEngine(the host-wideengine.run).runtimeForSelectionre-derives the mode from the SELECTEDengine's capability row (
container.EngineRunModeForviakit.DetectRunMode), so a containerd deploy emits and starts the engine'ssystemd-unit(nerdctl run), never a podman quadlet.directisengine-agnostic and is preserved.
build:ensurereceived no project context, so the local build fallbackfailed
buildkitdrive: build: no context.ensureImagePresentnow threadsthe project dir (through the existing
deploy-plugins-connectpreamble)into the
build:ensurerequest.containerd start failed
status=200/CHDIR:writeSystemdUnitConfigpassedthe CONTAINER working dir (
/home/user) as the engine unit'sWorkingDirectory=, which systemd applies on the HOST. The container workdiris already the engine argv's
run -w <path>; the host unit now omits it.Adopts
spec v0.2026309.1917andsdk v0.2026309.1926(producer-first:opencharly/spec#292, opencharly/sdk#487). The
plugin-buildproducer legs thelive proof needed — local build into the requested run engine, aliasing the
build's own produced ref, and keeping a handled pull miss out of the gate log —
are opencharly/plugin-build#52. The charly pin bump follows the tags.
Diff
go.mod,go.sumspec v0.2026309.1917andsdk v0.2026309.1926lifecycle.go,prepare_venue_state_test.goengine+ box +podtarget in the PrepareVenue state patch; testsselection.go,selection_test.goruntimeForSelectionderives the mode from the selected engine's capability row; testsconfig_setup.go,resolve.go,config_remove.goimage_ensure.go,image_ensure_test.go,resolve_ref.go,config_setup_helpers.gobuild:ensure; capture testsystemd_unit.goHow tested
All module commands ran on this head in
/home/atrawog/Sync/Atrapub/mo/claude/opencharly/.worktrees/containerd-crabbox-program/engine/plugin-deploy-pod/candy/plugin-deploy-podwith
GOWORK=off(the real adopted pins):Negative control (pre-fix) — the defect reproduced
Plugin
v2026.308.1638, charly2026.309.1009, adisposable: truepod:bed authored
engine: containerd, on this host's rootless containerd 2.3.5:The
unit-launches-nerdctlfailure's captured stdout was the podman usergenerator's unit header (the unit under
/run/user/1000/systemd/generator/was generated by/usr/lib/systemd/user-generators/podman-user-generator), andcontainer-in-containerdexited 1: the container was in podman, not containerd.Fixed — the selector, config unit and start path ran live on containerd
A charly binary built with
scripts/bootstrap-charly.sh --dev-plugin plugin-build=<plugin-build PR #52 worktree>(plus the releasedspec/sdkpins andplugin-fleet v0.2026309.1933) and this branch's plugin ran the same bed. The config phaseresolved
engine: containerdFROM THE PER-HOST ENTRY (a podman fallback wouldhave emitted a quadlet), built the image into the containerd store, and wrote
the engine's systemd unit:
The emitted unit is the engine's, not a podman quadlet:
charly deploy add --helpon the assembled binary now carries the selector:The measured venue blocker (not a finding in this diff)
The container did not start on this host, and this PR does not claim it
did. Two pre-existing venue/contract defects block it, both filed and
separable:
crunis not registered on this host's containerd. The containerdstart-plan names
crun; the unit failsfailed to resolve runtime path: invalid runtime name crun. The same base image runs with the default,runc, andio.containerd.runc.v2(rc=0each) and fails withcrun(
rc=1). Filed as fix(engine): start-plan names crun unconditionally, so a venue without that handler cannot start plugin-containerd#16.crunis not installed(
command -v crunis empty); it is the containerd lease guest's runtime, notthis desktop's.
ai.opencharly.descriptionlabel is overcontainerd's 4096-byte container-label cap (measured:
pod-crabbox4710,check-k8s-deploy-app6062, the probe image 9815). Filed asfix(capabilities): ai.opencharly.description over 4096 bytes blocks every containerd pod start spec#293. A minimal proof image (100-byte label) removes this
variable and reaches the
crunfailure, which is how the two were separated.The bed also surfaced two cleanup/readiness gaps, both filed:
opencharly/plugin-pod#27 (
charly removeleaves the containerd container, sothe next start fails
name-store) and opencharly/plugin-check#142(
wait-readyprobes the host engine, not the deploy's, a vacuous 90 s wait).Tier:
analysed on a live system— the changed runtime path ran live withretained output; the full bed did not pass because of the venue's missing
crunhandler, not because of this diff.Authoring runtime evidence — the footer's model string is the runtime's own
value, read from the process environment:
Rulebook compliance
/charly-internals:go,/charly-internals:plugin,/charly-internals:go-quality,/charly-internals:git-workflow, this repo'sAGENTS.md, and the umbrella rulebook before any edit.run-mode selection, project context, host-unit CHDIR) or filed with its RCA
(plugin-containerd#16, spec#293, plugin-pod#27, plugin-check#142);
plugin-build#50/#51 are the pre-existing findings from the same runs.
runtimeForSelection, one state-patch builder, one image-storepath; no engine-word switch or sibling implementation.
mode comes from the capability table, the build gets its context, the host
unit stops CHDIRing. The minimal-image fixture only isolates the venue's
label-cap variable; it is disclosed above.
containerdis the engine word andnerdctlis only the capability's binary.git statuswas read first; the session worktree is.worktrees/containerd-crabbox-program/engine/plugin-deploy-podoff freshorigin/main; the branch advanced only by new commits, never a force-push.validated at the systemd boundary (
systemctl --user cat) and thepre-fix/fixed bed pair is the before/after witness.
mocked; the
crunabsence is reported, not hidden.plugin source is logged in the run output.
charly check runon a freshrebuild; its changed path is green, and the remaining red is the filed venue
blocker above. The charly PR re-runs it on the tagged assembled stack.
Change classification
Behavior change at the pod deploy boundary: a per-deploy engine selector now
persists and drives unit emission, start/stop and config removal; an
engine-only state patch is classified as
pod;build:ensurereceives theproject dir; the host unit no longer carries the container workdir. No authored
key changes spelling and no migration is owed. Rollback: revert this PR and the
adopted tags; a deploy without a selector behaves exactly as before.
Breaking change + rollback: none — a deploy with no per-host engine keeps
the previous host-wide resolution, and the new state-patch fields are optional.
Filed issues
systemd-unitmode CHDIR'd the host to thecontainer workdir (
status=200/CHDIR); fixed on this branch.crununconditionally, so a venue without that handler cannot start.
ai.opencharly.descriptionover 4096 bytes blockscontainerd pod starts (the typical-image variable).
charly removeleaves a containerd container andthe next start fails
name-store.wait-readyprobes the host engine, not thedeploy's authored engine (vacuous 90 s wait).
and
errcheckfinding measured on these runs.Agent:
containerd-crabbox-program/engineAssisted-by: Claude Code ollama-cloud/deepseek-v4.1-flash:cloud (analysed on a live system)