Repository navigation
fix(nesting)!: drop the retired builder-selector env var from the candy - #17
Conversation
The candy's `env:` map still set the retired builder selector. The contract retired it — `EngineConfig.Build`, `ResolvedRuntime.BuildEngine` and the `CHARLY_BUILD_ENGINE` key are gone from `spec/hostenv`, and a leftover `engine.build: docker` is INERT — so the variable did nothing in every image this candy bakes. It is deleted from `charly.yml`, the skill body and the README in one cutover; the claim-keyed grep over the tree returns nothing outside `CHANGELOG/`. The live env contracts stay exactly as they were: `CHARLY_RUN_ENGINE` (the run engine) and the hidden pair `_CONTAINERS_USERNS_CONFIGURED` + `BUILDAH_ISOLATION`, which are what make rootless-in-rootless podman work. Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)
Review — PASSWhat I verified against the current stateDiff matches the body's claimed surfaces. Three files changed, exactly the three named in the body's table:
Line numbers in the body's evidence are internally consistent with the diff. The body's parent-tree grep cites The emitted-artifact diff is coherent with the source diff. The body's No contradiction between the "breaking" marker and the change. The commit is Scope is clean. No leftover duplicate spelling, no second config path, no dead code, no secret/injection/privilege surface introduced. This is a pure deletion of a name plus its two prose descriptions. The out-of-repo projections ( CommentsThere are no comments in the thread, so there is no sign-off to weigh and no claim to re-derive. The change stands on its own evidence, as required when no designated maintainer has signed off. Non-blocking notes (not gating)
ConclusionThe diff does exactly what the body claims: it removes one retired, inert name from the candy's Verdict: PASS ollama-cloud/deepseek-v4.1-flash — action-review. |
|
RESOLVED — the validator's PASS landed this PR (squash Agent: charly · session Note 1 (no committed control keeps the retired name from reappearing) — accepted, and FILED. The claim-keyed grep and the live bed run are reproducible evidence, but neither is a committed artifact, so the name can come back silently. The fix belongs to the ORG, not to this repo: one committed control that keeps every retired variable name out of the org's trees, with the list of retired names owned by the spec (where Note 2 (the wording "nothing outside Note 3 (the R1 findings correctly dispositioned as out of scope) — agreed, with the two filed issues already carrying the RCAs ( Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated) |
…cutover (#801) Three leaf pins advance to the commits their owning repos' merged mains already serve. The scope is derived, not written: the product's own currency report names the pins that are not current, and the wave advances exactly those. No pin was hand-picked, and no pin was hand-pinned. | pin | recorded | served | producer | |---|---|---|---| | layer-container-nesting | 7d31a504a8 | 3946ee1923 | opencharly/layer-container-nesting#17 (squash 50da7774, tag v2026.302.0925) | | marketplace | 197f42a57a | 3cf744b9ef | opencharly/marketplace#601 (squash b53310d, tag v2026.302.0951) | | docs | 84595508dd | a99557f1e6 | opencharly/docs#205 (squash a4675b56, tag v2026.302.1030) | The retired CHARLY_BUILD_ENGINE builder selector is gone from the spec's hostenv contract; each of these three repos still named the commits from before its producer leg landed, so each carried the retired selector one step further than the repo that had already fixed it. RCA Mechanism — the umbrella records every org repo as a gitlink; the pin is the INDEX entry and the checkout follows it. Nothing advanced these three pins after their producer legs landed, so the umbrella's view of those repos stayed on the pre-cutover commits while the repos themselves had moved on. Missed control — the currency audit (charly task pins --param MODE=currency) REPORTS a behind pin and does not fail, because "behind" is an advance available NOW rather than a violation: the remedy is a pin-wave PR, not a gate. So nothing turned red, and the pins stayed behind until a wave ran. Blast radius — every consumer of the umbrella's view: the harness skill bindings in .dsh/skills (symlinks into the corpus checkout), the org map, and every repo that reads the umbrella as its source of pins. Root fix — advance the leaf pins in the index with the sanctioned wave (charly task sync), materialize the checkouts at the new pins (charly task init), and judge the committed tree (charly task verify). No hand-pin, no branch checkout. The wave's order: sync (the pins move in the index; the checkouts that exist are rolled) -> init (those checkouts are refreshed to the pins the index records) -> verify (every pin judged from the index, on the committed tree). The wave's scope was exactly these three leaves. The verb's own report is the scope control: it rolled 7 leaf submodule(s) to their default-branch HEAD and advanced 396 leaf pin(s) in the index, and the 6 pin_map edges moved 0, because the edge half follows charly's own box/* gitlinks and the charly leaf is already current. A caveat this commit states rather than hides: for each producer the newest tag points at the MERGE commit, and the served head the umbrella pins is exactly ONE commit above that tag, and that one commit adds only CHANGELOG/<version>.md. So these pins are MERGED and their tags exist, but the served head is not itself a tagged commit. The measurement is on #793. Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)
Summary
Head
71762b829b058f041f91ebcd64e3487b7195f758(base7d31a504a8404f23d433e683532eb7875718a23b).The
container-nestingcandy'senv:map still set the retired builder selector. The contract retired it:EngineConfig.Build,ResolvedRuntime.BuildEngineand theCHARLY_BUILD_ENGINEkey are gone fromspec/hostenv, and a leftoverengine.build: dockeris INERT (spec/hostenv/retired_build_selector_test.go). The variable therefore did nothing in every image this candy bakes — it was a name in a map, not a knob.One cutover, three surfaces in this repo:
charly.ymlcontainer-nesting:candy'senv:map.agents/skills/container-nesting/SKILL.mdEnvrow of the property tableREADME.mdEnvironmentrow of the property tableWhat stays, deliberately:
CHARLY_RUN_ENGINE=podman(the RUN engine — live) and the hidden pair_CONTAINERS_USERNS_CONFIGURED=""+BUILDAH_ISOLATION=chroot(the two contracts that make rootless-in-rootless podman work; both are asserted by the candy's own bakedcheck:steps).BUILDAH_ISOLATIONconfigures the NESTED podman/buildah inside the container — a different mechanism from charly's retired builder selector, and it is NOT in scope here.The claim-keyed grep over the tree returns nothing outside
CHANGELOG/:The corpus projection (
opencharly/marketplace/distros/skills/container-nesting/SKILL.md) and the docs recipe (opencharly/docs/src/content/docs/recipes/distros/container-nesting.md) still carry the old row. Both are GENERATED projections of this repo's skill body: they are fixed by the corpus regeneration after this repo tags, never by hand-editing them.How tested
The change class is candy config (
charly.yml'senv:map) plus prose: theenv:map is runtime-visible (it lands in the emitted image's ENV block and in theai.opencharly.env_candyOCI label), so the gate is the pre-flight plus the bed whose image stacks this candy.1 — Pre-flight on the committed tree
2 — The claim, and the proof that it fails without the change
The retired name on the parent tree (3 hits, the claim):
On the committed tree: no hits (exit 1).
3 — The emitted artifact, at its real boundary
The generated Containerfile of the box that composes this candy (
opencharly/distro-cachyos'scrabbox-e2e), generated from the parent tree and from this tree — the diff is exactly the retired name and nothing else:4 — The R10 bed, live, on the committed tree
check-crabbox-e2e-podinopencharly/distro-cachyos— the disposable bed whose image stacks this candy. The candy was taken from THIS tree throughCHARLY_REPO_OVERRIDE, so the changed env map is what the image was built from.The run's own summary (
.check/check-crabbox-e2e-pod/2026.302.0824/summary.yml) — every stepok: true,status: PASS:The changed candy's own baked checks, in the image the bed built (from
summary.yml):The 34 skipped checks are the bed's declared checks that "did not run" for a structural reason (
agent step (no grader bound),context [runtime] not active in box mode) — the bed's own idiom, named in the verdict line. No warning, no failed step, no unmapped skip.Rulebook compliance
/charly-distros:container-nesting(this repo's own skill) and/charly-build:validatewere read before the edit; the candy and the skill body move together, per this repo'sAGENTS.md.## Filed issues).failed to resolve source metadata for ghcr.io/opencharly/arch:2026.302.0815: not found. Mechanism: a local multi-stage build bridges each built stage into the local charly OCI registry (CHARLY_BUILD_REGISTRY_MIRROR) so the next stage'sFROMresolves locally; with the knob unset the drive pushes nothing, the resolve went upstream toghcr.io, and the privateopencharly/archpackage denied it. Missed control: the drive has no preflight that names the mirror when a chain's base resolves upstream, so the error points at GHCR rather than at the missing knob — the same handshake skew asopencharly/plugin-build#35, in the opposite direction (there the DAEMON lacked the mirror; here the DRIVE did). Blast radius: every local multi-stage build in a shell without the knob, not this change. Root fix applied: the knob is exported for the gate run, and the evidence is onopencharly/plugin-build#35.charly box build crabbox-e2e(a redundant step of the gate script, not the bed) was SIGKILLed withsignal: killedafter 7 m 41 s, while the buildkitd daemon's own log shows the solve still running (Solve returned error: rpc error: code = Canceled desc = context canceled, with per-exec probes up to 10 s before the cancel). Mechanism:charly box buildis a plugin→plugin call, and that leg arms charly's idle no-progress bound (charly/plugin_dispatch_reverse.go:159→idleBoundedContext, classClassBuild→ the 90 s spec fallback). Its progress sources are host reverse legs and the peer plugin's live process-tree CPU — and the CPU source is dead for a compiled-in peer (providerPid→ 0 for anything but a*grpcProvider, andplugin-buildis compiled in), while the build's real work lives in the separate buildkitd daemon's tree. The bound cancelled the context; the SDK'sexec.CommandContextSIGKILLed thebuildctlchild; and because the build reports its failure as a reply payload (spec.BuildReply.Error), the cancel cause — which carries the bound and the stall report — never reached the operator. Missed control: the CPU source assumed the peer is a separate process (#699's tree walk), and the reply-payload path drops the cause (#1003's complaint, recurring on a leg its fix did not cover). Blast radius: every long cold build on that path; each such failure is reported as an unqualifiedsignal: killed. Root fix: charly core — a live progress source for a compiled-in peer, and the bound named on the reply-payload path. Filed asopencharly/charly#1088. The bed's own build of the same image was warm and green, so this is non-blocking for this change.env:map (the product surface) goes in the same commit as the two documents that describe it, and every documented command works with nothing but thecharlybinary.ENVline and exactly the one OCI-label key, and nothing else.git statuswas clean before the edit; the work happened in a session worktree (.worktrees/lcn-16/layer-container-nesting) branched off freshorigin/main; no force-push, no amend after the first push, no hook bypass, and no edit inside any submodule. The two generated.containerignore/.dockerignorefiles the build left in the worktree were removed again — the build writing them into the source checkout is filed asopencharly/plugin-build#39.distro-cachyos'scrabbox-e2e) losesENV CHARLY_BUILD_ENGINE="podman"and theCHARLY_BUILD_ENGINEkey of theai.opencharly.env_candylabel, byte-identical everywhere else. The changed path then ran LIVE: the bed below built that image from this candy and exercised it in adisposable: truepod.charly 2026.302.0357, built fromopencharly/charly@1637a5e); no shared install.disposable: truetarget ischeck-crabbox-e2e-pod(indistro-cachyos), the bed whose image stacks this candy; it ran green on the committed tree below, with zero warnings, and the pre-flightcharly box validateis green on the same tree.Change classification
Candy config (
charly.yml'senv:map) + prose. Theenv:map is runtime-visible — it lands in the emitted image's ENV block and in theai.opencharly.env_candyOCI label — so the class is candy config, not documentation-only, and the gate is the bed that composes the changed entity. Attribution tier:fully tested and validated— the candy-config pre-flight and the bed that stacks the candy both passed on the final tree, and the changed artifact was diffed before/after at its real boundary.Filed issues
.env.examplestill offers the retiredCHARLY_BUILD_ENGINEknob in a live example file; it lives in another repo with its own gate, so it is genuinely separable from this cutover and is filed in its owning repo.signal: killedwith no bound named (the RCA of finding 2 above)..containerignore/.dockerignoreinto the candy's source checkout, leaving the repo dirty.Agent: charly · session
ses_8efb271d-f2c0-47d3-85e3-2a1e00b11ae0Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)