clean
| plugin-clean — Served by | plugin-clean |
| plugin-clean — Placement | compiled-in (in-process) |
| plugin-clean — Version | 2026.202.1400 |
| plugin-clean — Served by | plugin-clean |
| plugin-clean — Placement | compiled-in (in-process) |
| plugin-clean — Version | 2026.202.1400 |
clean is a command word served by 2 plugin candies — plugin-clean and plugin-clean — at different points in the command tree. Both are real invocations; charly --help prints which is which.
About plugin-clean, which serves it
Section titled “About plugin-clean, which serves it”COMPILED-IN charly COMMAND-class plugin that OWNS the externalized charly clean
CLI — the build-artifact retention/prune surface. The plugin owns the command end to
end: the flag grammar (–dry-run / –images / –check / –deep / –keep / –invalidate),
the category orchestration, and the report output. No plugin-specific command LOGIC is
left in core.
–deep is the store-wide untagged/dangling-image purge category — the CLI-only-
interface gap-closing capability for issue #173: a multi-stage build’s INTERMEDIATE
stage images are only ever labeled at the FINAL stage (WriteLabels emits at the end of
the last stage), so they accumulate as unlabeled dangling images the default
charly-labeled sweep (images/DanglingIDs, no flag needed) can never see. --deep
removes EVERY untagged image in local storage, respecting the SAME InUse/live-build
backstops as the default sweep (never a tagged image, never a container-referenced
one, never mid-build — see pruneDanglingImages/selectDanglingImages in
retention.go); removing a dangling image also frees any layer blobs it alone
held (podman’s overlay storage GCs an unreferenced layer on last-reference removal),
so this is EFFECTIVELY a dangling-image-plus-unused-layer prune. --deep NEVER fires
implicitly on a plain charly clean (R5: zero default-behavior change) — it is
strictly opt-in, mirroring --images/--check’s “runs ONLY this category” semantics.
--deep --dry-run is the safe default probe: it reports the would-remove count + an
UPPER-BOUND reclaimable-bytes figure (DeepBytes, summed from each candidate’s reported
storage Size) and touches nothing. That figure is “up to”, never a firm prediction:
RDD-verified live, a –deep purge removing 68 untagged images (3,552 → 3,484) reported
~92.6 GiB via the naive per-image Size sum but freed only ~4.6 GiB of real disk (132.6
GB → 128 GB), because most of those bytes were layers SHARED with the ~3,400 remaining
(largely stale-tagged) images — removal only frees layers an image held UNIQUELY. Pair
--deep with --invalidate (which removes stale TAGS, freeing their exclusively-held
layers too) to get closer to the reported figure.
This plugin OWNS the SHARED retention ENGINE too (retention.go:
pruneImagesByRetention / pruneCheckRuns / pruneBuildCandyDirs / invalidateImageTags /
pruneDeepDanglingImages + the charly-labeled image-tag CalVer/label inventory) — K1-alpha
core-minimization relocated it here from charly/retention.go, since it has ZERO
core-only dependencies (kit.CalVer/kit.ParseCalVer/kit.ListLocalImages/kit.BuildActivityDir
are all sdk-portable). charly clean’s own CLI calls the engine LOCALLY (no wire hop,
same package). The other three callers — charly box build’s post-build prune,
charly box list tags, and candy/plugin-check’s post-run prune — reach it via
verb:retention (a spec.RetentionRequest → spec.RetentionReply Invoke), the SAME
peer/core-adapter pattern verb:credential/verb:gpu/verb:tunnel already use: core’s two
callers (already running LoadConfig in-process) resolve defaults.keep_images/
keep_check_runs themselves and pass the resolved ints in the request; plugin-check (a
peer plugin, not core) reaches verb:retention via InvokeProvider.
The project’s defaults.keep_images/keep_check_runs, for ITS OWN CLI (charly clean,
no –keep flag), resolve PLUGIN-SIDE via the shared
sdk/loaderkit.ResolveRetentionDefaultsViaExecutor (K-wave 2 cone R6 — the former
“retention-defaults” HostBuild seam charly/host_build_retention_defaults.go is
DELETED: the loader is plugin-reachable over the reverse channel, so every
verb:retention caller resolves the tunables itself; “retention” is a class-generic
action noun, not a provider word (F11).
clean is COMPILED-IN (listed in charly/charly.yml compiled_plugins) because command:clean’s Invoke(OpRun) needs the in-proc reverse channel — threaded by dispatchInProcCommand (“Seam A”) — to reach the host loader legs for the retention-defaults resolve. The out-of-process CliMain path has no reverse channel, so the categories needing a resolved keep-default (images/check/deep) error there; list/invalidate need no default and run standalone even out-of-process. There is NO hidden core-command forward — the plugin does the work directly, resolving the project config default itself; no core symbol crosses the boundary, no ad-hoc podman.
Two capabilities: command:clean dispatches through the COMPILED-IN registry path
(registerCompiledPlugin → resolve(ClassCommand,“clean”) → dispatchInProcCommand →
Invoke(OpRun) with the threaded in-proc reverse channel); verb:retention is invoked
directly by core adapters / peer plugins with no authored plugin_input, mirroring
verb:credential. NewMeta advertises both while the served CUE schema carries no
plugin_input (verb:retention’s params are the internal spec.RetentionRequest RPC, never
an authored plan step; command:clean’s args are plain CLI tokens). The R10 witness is
the disposable check-commands-local bed: charly clean --dry-run AND charly clean --deep --dry-run both exit 0 and print their would-remove reports, proving the
externalized command + the local retention engine + the loader-resolved
retention-defaults end-to-end for both the charly-labeled and the store-wide sweep.
About plugin-clean, which serves it
Section titled “About plugin-clean, which serves it”COMPILED-IN charly COMMAND-class plugin that OWNS the externalized charly clean
CLI — the build-artifact retention/prune surface. The plugin owns the command end to
end: the flag grammar (–dry-run / –images / –check / –deep / –keep / –invalidate),
the category orchestration, and the report output. No plugin-specific command LOGIC is
left in core.
–deep is the store-wide untagged/dangling-image purge category — the CLI-only-
interface gap-closing capability for issue #173: a multi-stage build’s INTERMEDIATE
stage images are only ever labeled at the FINAL stage (WriteLabels emits at the end of
the last stage), so they accumulate as unlabeled dangling images the default
charly-labeled sweep (images/DanglingIDs, no flag needed) can never see. --deep
removes EVERY untagged image in local storage, respecting the SAME InUse/live-build
backstops as the default sweep (never a tagged image, never a container-referenced
one, never mid-build — see pruneDanglingImages/selectDanglingImages in
retention.go); removing a dangling image also frees any layer blobs it alone
held (podman’s overlay storage GCs an unreferenced layer on last-reference removal),
so this is EFFECTIVELY a dangling-image-plus-unused-layer prune. --deep NEVER fires
implicitly on a plain charly clean (R5: zero default-behavior change) — it is
strictly opt-in, mirroring --images/--check’s “runs ONLY this category” semantics.
--deep --dry-run is the safe default probe: it reports the would-remove count + an
UPPER-BOUND reclaimable-bytes figure (DeepBytes, summed from each candidate’s reported
storage Size) and touches nothing. That figure is “up to”, never a firm prediction:
RDD-verified live, a –deep purge removing 68 untagged images (3,552 → 3,484) reported
~92.6 GiB via the naive per-image Size sum but freed only ~4.6 GiB of real disk (132.6
GB → 128 GB), because most of those bytes were layers SHARED with the ~3,400 remaining
(largely stale-tagged) images — removal only frees layers an image held UNIQUELY. Pair
--deep with --invalidate (which removes stale TAGS, freeing their exclusively-held
layers too) to get closer to the reported figure.
This plugin OWNS the SHARED retention ENGINE too (retention.go:
pruneImagesByRetention / pruneCheckRuns / pruneBuildCandyDirs / invalidateImageTags /
pruneDeepDanglingImages + the charly-labeled image-tag CalVer/label inventory) — K1-alpha
core-minimization relocated it here from charly/retention.go, since it has ZERO
core-only dependencies (kit.CalVer/kit.ParseCalVer/kit.ListLocalImages/kit.BuildActivityDir
are all sdk-portable). charly clean’s own CLI calls the engine LOCALLY (no wire hop,
same package). The other three callers — charly box build’s post-build prune,
charly box list tags, and candy/plugin-check’s post-run prune — reach it via
verb:retention (a spec.RetentionRequest → spec.RetentionReply Invoke), the SAME
peer/core-adapter pattern verb:credential/verb:gpu/verb:tunnel already use: core’s two
callers (already running LoadConfig in-process) resolve defaults.keep_images/
keep_check_runs themselves and pass the resolved ints in the request; plugin-check (a
peer plugin, not core) reaches verb:retention via InvokeProvider.
The project’s defaults.keep_images/keep_check_runs, for ITS OWN CLI (charly clean,
no –keep flag), resolve PLUGIN-SIDE via the shared
sdk/loaderkit.ResolveRetentionDefaultsViaExecutor (K-wave 2 cone R6 — the former
“retention-defaults” HostBuild seam charly/host_build_retention_defaults.go is
DELETED: the loader is plugin-reachable over the reverse channel, so every
verb:retention caller resolves the tunables itself; “retention” is a class-generic
action noun, not a provider word (F11).
clean is COMPILED-IN (listed in charly/charly.yml compiled_plugins) because command:clean’s Invoke(OpRun) needs the in-proc reverse channel — threaded by dispatchInProcCommand (“Seam A”) — to reach the host loader legs for the retention-defaults resolve. The out-of-process CliMain path has no reverse channel, so the categories needing a resolved keep-default (images/check/deep) error there; list/invalidate need no default and run standalone even out-of-process. There is NO hidden core-command forward — the plugin does the work directly, resolving the project config default itself; no core symbol crosses the boundary, no ad-hoc podman.
Two capabilities: command:clean dispatches through the COMPILED-IN registry path
(registerCompiledPlugin → resolve(ClassCommand,“clean”) → dispatchInProcCommand →
Invoke(OpRun) with the threaded in-proc reverse channel); verb:retention is invoked
directly by core adapters / peer plugins with no authored plugin_input, mirroring
verb:credential. NewMeta advertises both while the served CUE schema carries no
plugin_input (verb:retention’s params are the internal spec.RetentionRequest RPC, never
an authored plan step; command:clean’s args are plain CLI tokens). The R10 witness is
the disposable check-commands-local bed: charly clean --dry-run AND charly clean --deep --dry-run both exit 0 and print their would-remove reports, proving the
externalized command + the local retention engine + the loader-resolved
retention-defaults end-to-end for both the charly-labeled and the store-wide sweep.
charly --help prints the command tree, including where each clean is invoked and under which parent.