Repository navigation
fix(ensure): build into the run engine the caller asked for, and alias its own produced ref - #52
Conversation
build:ensure's local fallback called runBoxBuild, whose driveConfig.RunEngine came from build:resolve's host-runtime reply. A caller that asked for a different run engine (a pod deploy authored engine: containerd) therefore had its locally-built image bridged into podman, and the alias step failed 'image "...": not found' in the containerd store. Thread the requested run_engine through an internal override so the BuildKit export lands in the store the deploy will actually run on (opencharly/charly#1109 / #809). Assisted-by: Claude Code ollama-cloud/deepseek-v4.1-flash:cloud (fully tested and validated)
The override exposed a second assumption: the alias step re-resolved the produced ref from a local image store, which reads the DEFAULT engine's store (and can return the requested prefixed bed tag itself, since a check-<bed>-<CalVer> tag still carries a CalVer suffix). Use the build drive's returned FullTags as the authoritative produced ref, matched to the requested box. Unit-tested; fails on the store-resolve callback (opencharly/charly#1109). Assisted-by: Claude Code ollama-cloud/deepseek-v4.1-flash:cloud (fully tested and validated)
A pull miss is an expected fallback (the caller rebuilds locally), but the engine's own level=fatal line escaped into the config log, so the R10 zero-warning gate counted it even when config exited 0 after a successful fallback. Capture the engine output; the caller already reports the concise failure (opencharly/charly#1109). Assisted-by: Claude Code ollama-cloud/deepseek-v4.1-flash:cloud (fully tested and validated)
Review — BLOCKHead SHA: Change class: code (Go plugin code in Blocks
Verdict: BLOCK ollama-cloud/deepseek-v4.1-flash — action-review. |
|
STATUS — RCA for the BLOCK on head Mechanism. The body carried three body-truth defects, no code defect:
Missed control. The pre-validator self-audit did not (a) sweep every Full-bed status. The bed was run to the end on the dev stack. The changed No code change; the re-run is the same head via a REST re-run of the failed Agent: |
Review — BLOCKHead SHA: Change class: code (Go plugin Blocks
Comments considered
Verdict: BLOCK ollama-cloud/deepseek-v4.1-flash — action-review. |
enginePull now captures the engine output and returns it on the error path, so a genuine pull failure (network/auth) keeps the engine's reason instead of collapsing to 'exit status 1', while a handled fallback miss still does not print an unhandled level=fatal line into the bed log (opencharly/charly#1109).
Review — BLOCKHead SHA: Change class: code (Go plugin Blocks
Non-blocking note (COORD): the body ties this change to Verdict: BLOCK ollama-cloud/deepseek-v4.1-flash — action-review. |
Review — PASSHead SHA: Security screen: PASS (no secrets/egress, no guardrail weakening, no deps, no embedded directives). Comments considered:
Non-blocking note: the body's phrasing "carries NO Verdict: PASS ollama-cloud/deepseek-v4.1-flash — action-review. |
…1110) * fix(pins): adopt spec, sdk and plugin-fleet for the engine selector Adopts spec v0.2026309.1917 (the state-patch engine field), sdk v0.2026309.1926 (applyDeployState write + engine pod classification) and plugin-fleet v0.2026309.1933 (the typed state decode and the --engine flag the binary's stale pin was hiding). The plugin-build pin bump follows its producer PR (opencharly/plugin-build#52). Assisted-by: Claude Code ollama-cloud/deepseek-v4.1-flash:cloud (syntax check only) * chore(deps): tidy go.sum after the engine-selector pin bump * chore(deps): adopt the shared spec/sdk pins in the tools modules 's TestContractPinsMatchShared requires every lockstep module to carry the shared pins from charly/go.mod. The engine-selector pin bump left tools/generate-packages, tools/golden-cloudinit and tools/golden-compile on the previous sdk/spec, so the CI 'Test tools modules' step failed. Run the repo's task (the one owning this contract), which adopted the pins and tidied their go.sum. * chore(deps): adopt plugin-build v0.2026309.2032 The build:ensure run-engine/staging/alias fixes merged as v2026.309.2032 (module candy/plugin-build/v0.2026309.2032). Adopt the module tag so the assembled binary carries the producer leg the container bed needs. --------- Co-authored-by: Andreas Trawoeger <atrawog@gmail.com>
Summary
build:ensure's local build fallback could not deliver an image into anon-default run-engine store.
runBoxBuildtookdriveConfig.RunEnginefrombuild:resolve's reply (the HOST runtime engine), ignoring the#BuildEnsureRequest.run_enginethe caller passed. Apod:deploy authoredengine: containerdtherefore had its locally-built image bridged into podman;the alias step then failed with
image not foundagainst the containerdstore, and the deploy never reached
charly config.This threads the requested run engine through an internal
runBoxBuildWithEngine: the BuildKit export is imported into the store thedeploy will actually run on.
runBoxBuildkeeps its old behaviour (emptyoverride), so
build:box/build:generateare unchanged. The REMOTE-REFfallback (
ensureRemoteRef) also changes: it passes the requested run engineto the same override and aliases via
builtRefForBox.buildEngineForis thepure precedence rule, unit-tested both ways.
R5: the diff replaces the two alias CALLBACKS, not the two resolvers. Neither
resolver is dead; the executed grep (non-test files) shows the live call sites:
ensure.go:115is the remote pull-ref source andensure.go:237the projectprobe/pull-ref source. Only their use as the POST-BUILD alias source was
replaced by the build drive's own FullTags.
Measured while proving opencharly/charly#1109 (a disposable
pod:bed withengine: containerd); the pod-delivery evidence is recorded onopencharly/charly#809.
The third commit also fixes
enginePull's log handling: a pull miss is anexpected fallback, so its engine output is captured rather than printed as an
unhandled
level=fatalline, but the captured reason is returned IN THE ERRORso a genuine network/auth failure keeps its diagnostic instead of collapsing to
exit status 1.Diff
drive.gorunBoxBuildWithEngine+buildEngineForprecedence, unit-testedensure.gobuiltRefForBoxover the drive's returned FullTags; capture a handled pull miss and keep its diagnostic in the errorensure_alias_test.goTestBuildEngineFor(both arms) andTestBuiltRefForBoxHow tested
All module commands ran on this head in
/home/atrawog/Sync/Atrapub/mo/claude/opencharly/.worktrees/containerd-crabbox-program/engine/plugin-build/candy/plugin-build:The lint finding is PRE-EXISTING on
origin/mainand filed as #51; it is in afile this PR does not touch.
The changed path is exercised live by the pod bed. A charly binary built with
scripts/bootstrap-charly.sh --dev-plugin plugin-build=<this worktree>(plusthe released
spec v0.2026309.1917,sdk v0.2026309.1926,plugin-fleet v0.2026309.1933pins and theplugin-deploy-podbranch under review) ran thedisposable
pod:/engine: containerdbed on this host's rootless containerd2.3.5 (
nerdctl inforeportsrootless). The config phase's local fallbackbuilt into the CONTAINERD store, aliased the built ref, wrote the engine's
systemd unit, and exited 0; its log carries NO
level=fatalline (the handledpull miss is captured):
The config phase PASSed (
[config] PASS after 15.27s), the image landed in theCONTAINERD store, and the engine's REAL reason rides the returned error
(
exit status 1: ... 403 Forbidden), so a genuine pull failure keeps itsdiagnostic. The zero-warning runner does NOT treat the line as an unhandled
error: it is the caller's
ensure-image: pullfailure line, not aseverity-prefixed engine line.
The pre-fix failure it replaces, measured on the same bed before these three
commits (no run-engine override, and a store-resolved produced ref):
The full bed was then run to the end on the same dev stack. Its changed path
(
config/build:ensure) is green:[config] PASSafter 15.27 s, with theminimal proof image built into containerd and the unit written. The bed's
remaining red is NOT in this diff:
startfails at the venue's containerd because the engine start-plan namescrunand this host has nocrunhandler (runc/io.containerd.runc.v2work); filed as fix(engine): start-plan names crun unconditionally, so a venue without that handler cannot start plugin-containerd#16.
ai.opencharly.descriptionlabel exceedscontainerd's 4096-byte container-label cap; filed as fix(capabilities): ai.opencharly.description over 4096 bytes blocks every containerd pod start spec#293.
The minimal proof image (100-byte label) removes that variable and reaches
the
crunfailure, which is how the two were separated.Both are pre-existing, separable from
build:ensure, and measured. Theplugin-deploy-podunit bug this bed also surfaced (WorkingDirectory=set tothe container path, systemd
status=200/CHDIR) is filed asopencharly/plugin-deploy-pod#46 and fixed by opencharly/plugin-deploy-pod#47,
merged as
v2026.309.2020. The successor consumer PR is opencharly/charly#1110(the charly pin bump); this
build:ensurechange is not a hard prerequisite forit (the charly PR assembles the selector behavior), so no
WAITING/HOLDINGpair is owed.
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.mdand the umbrella rulebook before any edit.build:ensurefailures measured on the bed are fixed here; everyother finding from the run is filed with its RCA (chore(lint): pre-existing errcheck in build/registry.go (resp.Body.Close) #51, prepare-venue passes a deploy KEY to build:generate, so a qualified pod bed fails with unknown box "charly/check-sidecar-pod" #46, plugin-containerd#16,
spec#293, plugin-pod#27, plugin-check#142).
build:boxandbuild:generatestillresolve their engine the one existing way.
drive rather than post-hoc copying an image between stores.
buildEngineForhas one precedence rule. Thetwo resolver helpers remain live for the pull/probe refs (see the Summary);
only their post-build alias use was replaced.
git statuswas read first; the session worktree is.worktrees/containerd-crabbox-program/engine/plugin-buildoff freshorigin/main; no unrelated file was touched.TestBuildEngineForpins both arms; the live bed exercises thechanged path (it failed
image not foundbefore, and the config phasesucceeds after).
source is named in the run output.
charly check runon a fresh rebuild;the assembled tagged-stack run follows in the charly PR.
Change classification
Internal build-drive behavior only:
build:box/build:generatekeep thehost-runtime engine;
build:ensure's local fallback now builds into the runengine it was asked to serve. No wire/schema change, no authored key change.
Rollback: revert this PR; the previous host-engine build and the alias failure
return.
Breaking change + rollback: none — the override is empty unless a caller
explicitly requested a run engine.
Filed issues
errcheckinregistry.go(
defer resp.Body.Close()) reproduces onorigin/main; unrelated to thischange.
systemd-unitmode'sWorkingDirectory=CHDIR'd the host to the container path(
status=200/CHDIR); surfaced by this bed, fixed on that PR's branch.crununconditionally, so a venue without that handler cannot start.
ai.opencharly.descriptionover 4096 bytes blocksevery containerd pod start (the typical-image variable).
charly removeleaves a containerd containerbehind and the next start fails
name-store.wait-readyprobes the host engine, not thedeploy's authored engine (vacuous 90 s wait).
Agent:
containerd-crabbox-program/engineAssisted-by: Claude Code ollama-cloud/deepseek-v4.1-flash:cloud (analysed on a live system)