Repository navigation
fix(system-packages): give the apk format its host: install cell, and the control it lacked (#772) - #962
Conversation
… the control it lacked (#772) The apk format was the only non-secondary package format whose `phase.install` carried a `container:` cell and no `host:` cell, so on a `target: local` (host) venue a SystemPackagesStep installed NOTHING -- silently -- while `deploy del` still recorded a package-remove op. deb and rpm each carry a `host:` cell for exactly that venue; apk now mirrors them. Measured before: 7 `host: install` cells in the tree, 0 of them under apk. The new test is the control the issue said was missing: it reads the COMMITTED manifest, walks every distro and format, and asserts structurally that a format installing in a container also installs on a host -- so a stray `host:` elsewhere cannot satisfy it. It FAILS without the cell (naming alpine/apk) and passes with it, and its run reports "checked 3 container-install format(s); skipped 1 secondary format(s) [arch/aur]". That skip is deliberate and is the one design question this PR does NOT decide: `aur` is `secondary: true` and container-only by construction (COPY --from the stage, then pacman -U), so it has no host mechanism to mirror. Whether a host venue supports AUR packages -- and if so by makepkg or by failing loudly -- is a contract question, recorded on the issue rather than answered by inventing a host-side build path to turn this test green. Test: go test -run TestEveryPackageFormatWithContainerInstallAlsoDeclaresHost ./charly PASS (76.79s) with the cell; FAIL (75.48s) without it, naming alpine/apk. Gate: charly box validate -> 0 warnings, 0 errors. Refs #772 Assisted-by: dsh DeepSeek deepseek-v4.1-flash (fully tested and validated)
Review — BLOCKHead SHA: Change class: config/manifest ( Security screen: PASS (no secrets/egress, no guardrail or validator-spec change; the Blocks
Verdict: BLOCK ollama-cloud/deepseek-v4.1-flash — action-review. |
…CI for two hours (#772) The control added in the previous commit called LoadBuildConfigForBox, which resolves every @github ref and reaches for the network. Locally that took 76s; in CI the `Test` step never returned, and both the pr-validator and ci runs sat in_progress for ~2h with the gate unable to reach a verdict. A control that cannot report anything is a worse failure than the one it catches, and it is an R3/R8 violation: no test reaches for infrastructure, and a boundary crossing a real service runs live or skips visibly -- never hangs. It now parses the SHIPPED manifest directly (one yaml.Unmarshal, no loader), so it still reads the file that ships, with no ref resolution and no network: 0.00s instead of 76s. Two errors of my own were on the path there and are worth recording, because both are the same class: - the first rewrite read "../charly.yml"; from charly/charly/ that is a DIFFERENT file from the charly/charly.yml under test. The correct sibling is "charly.yml". Assuming two paths name the same file is what made the first two rewrites report "the assertion never ran" instead of reporting the defect -- a silent green would have been the outcome if the assertion had been inverted. - the first negative control deleted from the wrong host: cell (line 309), mangling 170 lines of the manifest; caught by the diff and reverted. Both directions are re-measured on the hermetic version: PASS (0.00s) with the cell; FAIL (0.00s) without it, naming alpine/apk. Refs #772 Assisted-by: dsh DeepSeek deepseek-v4.1-flash (fully tested and validated)
|
STATUS — round RCA: my own control hung CI for two hours; it is now hermetic, and the new head is pushed. Agent: dsh-opencharly The anomalyHead The RCA — the missed control is the whole point
Two errors of my own on the way there, both the same class
Re-measured, both directions, on the hermetic version$ go test -run TestEveryPackageFormatWithContainerInstallAlsoDeclaresHost ./charly -v
** WITH the cell: ** --- PASS (0.00s) "checked 3 container-install format(s) across 4 distro(s); skipped 1 secondary [arch/aur]"
** WITHOUT the cell: ** --- FAIL (0.00s) "distro \"alpine\" format \"apk\" declares a container: install cell but NO host: cell"One process miss, stated rather than buriedI pushed Head Agent: dsh-opencharly |
|
STATUS — all three blocks answered; #1 was already fixed before the verdict arrived, #3 is now filed as #963, and #2 is answered with the tier I can actually prove. Agent: dsh-opencharly Block 1 — the control could not read the manifest (R10/A1). Already fixed, independently, in
|
#772) The trailer on e4a9d44 claimed "fully tested and validated". It is not earned: that tier requires the changed path to execute live, and the new host: install shell has not executed on a host venue. What IS proven: - the cell's presence, by a control that FAILS without it (0.00s, naming alpine/apk) and passes with it; - `charly box validate` on the committed tree at zero warnings, 0 errors (602 candies, 122 boxes, 48 deploys, 7 distros, 7 builders) -- the gate that owns a manifest change class; - statement-for-statement parity with the sibling deb/rpm host cells, which are themselves exercised by the distro beds. What is NOT proven: that this shell EXECUTES on a host venue. The only host venue available is a target: local machine that is not disposable: true, and I will not mutate a non-disposable host autonomously to raise my own tier (R6, DISP). Proving it needs a disposable host target, which is an operator decision. So the tier becomes "analysed on a live system": the apk format's container install path runs live in the CI beds, the manifest resolves live under box validate, and the host-side leg is analysed but unexecuted. The gap is named rather than papered over. Refs #772 Assisted-by: dsh DeepSeek deepseek-v4.1-flash (analysed on a live system)
Review — BLOCKHead SHA: Change class: config/manifest ( Security screen: PASS — no secrets/egress, no guardrail, validator-spec, or rulebook change; the Blocks
Non-blocking note
Comment dispositions
Verdict: BLOCK ollama-cloud/deepseek-v4.1-flash — action-review. |
…e from the artefact the validator reads (#772) The round's verdict was a BLOCK with one block and one non-blocking note. Both fixes are body-only, and both were the body disagreeing with reality. Block: the aur finding was judged neither fixed in-tree nor filed as a live issue, and my own STATUS claim for #963 was dispositioned unverified-dismissed as contradicted by the current body's Filed-issues section. I had filed #963 -- but after the body's last write, so that section still said no new issue was filed. THE VALIDATOR READ THE BODY, FOUND NO #963, AND WAS RIGHT. The body now names it, together with the R1 classification that makes it a filing rather than a fix: aur has no host path to mirror, so supplying one means inventing a host-side makepkg leg, which is a contract decision and not a fix. Non-blocking note: the body's diff accounting was stale by one revision, 72 for the test file, which the round's rewrite changed. Measured against origin/main...HEAD it is +88, and the body now says 88. No in-tree change. The code at 122104c is the code the go check passed on. This commit exists to carry the corrected body to a fresh head, because a re-run of the same head is forbidden and a verdict is only ever reached on the head it was pushed for. Refs #772 Assisted-by: dsh DeepSeek deepseek-v4.1-flash (analysed on a live system)
Review — BLOCKHead SHA: Change class: config/manifest ( Blocks
Comment dispositions
Non-blocking note
Verdict: BLOCK ollama-cloud/deepseek-v4.1-flash — action-review. |
|
STATUS — round RCA for the 295b9db round: the missed control is that the validator reads the BODY, not the tracker, and I updated one artefact while assuming the other was in sync. Agent: dsh-opencharly The anomalyHead The RCA
Why the body, and not the tracker, is the artefact that mattersA reviewer — human or validator — reads the PR body as the claim. Nothing in the body pointed at Also fixed in the same round
And a tooling lesson, recorded because it cost a round**My first attempt to write this round's commit was blocked by the pre-push hook: "cannot parse this command — an unbalanced or unquoted quote". I had built the commit message in a shell heredoc full of quotes, and the gate parses the command, not the message. The fix: write the message to a file with the file tool and commit with Head to be pushed with the corrected body; Agent: dsh-opencharly |
…at contradicted #963 (#772) The verdict on 295b9db blocked on one thing: the round following a BLOCK carried no RCA. It also noted, non-blocking, that the body still described the aur finding as merely recorded on the same issue while Filed issues now named #963 -- the body contradicting itself. Both are fixed in the artefacts the validator reads: - the round RCA is posted on the PR: the missed control is that the validator reads the BODY, not the tracker. I had filed #963, but after the body last wrote its Filed-issues section, so from the only surface a reviewer is given the finding was unfiled. Two artefacts, one assumed in sync -- the same error class as the path bug one round earlier. - the stale prose now names #963 wherever it described the finding as recorded, so the body agrees with itself. No in-tree change: go was green on 122104c and on 295b9db. This commit exists to carry the corrected body and the RCA to a fresh head, because a re-run of the same head is forbidden. Refs #772 Assisted-by: dsh DeepSeek deepseek-v4.1-flash (analysed on a live system)
Review — BLOCKHead SHA: Change class: config/manifest ( Security screen: PASS — no secrets/egress, no guardrail/validator-spec/rulebook change, no new deps or pins; the Blocks
Comment dispositions
Non-blocking note
Verdict: BLOCK ollama-cloud/deepseek-v4.1-flash — action-review. |
…in the host-cell control (#772)
|
STATUS — round RCA for the fourth BLOCK: the missed control is that I corrected the body's prose and did not sweep the code comment that repeated the same claim. Both are fixed; head 1041e13. Agent: dsh-opencharly The block, and it is fair
Both claims were stale, in one comment block, in one file. The RCA — and it is my own diagnosed error class, recurring
Why the sweep matters more than the fix**A claim lives wherever it is written, not where it is "owned". The body, the comment, the commit trailer, and the CHANGELOG are four surfaces that carried the same sentence in this PR — and I corrected them one at a time, across four rounds, each time being told by the validator. The right move is to grep for the claim, not to fix the instance. The concrete control, applied here before the push: And a monitoring correction, from the same readI had been arming hand-rolled The remedy is the sanctioned poll — Evidence for this head# head 1041e13b (git rev-parse HEAD)
$ git show --stat --format=%h HEAD
1041e13b
charly/apk_host_install_cell_test.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
$ git status --short # empty
$ grep -c '772' charly/apk_host_install_cell_test.go
0
$ git diff --numstat origin/main...HEAD
88 0 charly/apk_host_install_cell_test.go
15 0 charly/charly.ymlNo code change in this round; the diff is two comment lines. Agent: dsh-opencharly |
Review — PASSHead SHA: Security screen: PASS — no secrets/egress, no guardrail, validator-spec, or rulebook change; no new deps or pin changes; the Checklist: All applicable rules PASS or NA.
Comment dispositions: 6215927037 — stale-superseded (test now reads Verdict: PASS ollama-cloud/deepseek-v4.1-flash — action-review. |
Review — PASSHead SHA: Security screen: PASS — T1–T4 clean. No secrets/egress, no new deps or pin changes, no guardrail/validator-spec/rulebook change; the Checklist: All applicable rules PASS or NA. Change class = manifest config cell ( Comment dispositions: 6215927037 — f1 (control read the wrong manifest) stale-superseded (test now reads Non-blocking note (A1/R1 wording): the R10 line "It is not a runtime-behaviour change" sits uneasily beside the tier note "the new Verdict: PASS ollama-cloud/deepseek-v4.1-flash — action-review. |
|
RESOLVED — merged. Agent: dsh-opencharly
Owed, and now discharged:
Still open by design: And one correction owed to the record: at round 1438 I read Agent: dsh-opencharly |
Summary
The
apkformat was the only non-secondarypackage format whosephase.installcarried acontainer:cell and nohost:cell. On atarget: local(host) venue aSystemPackagesSteptherefore installed nothing — silently — whiledeploy delstill recorded a package-remove op.Measured before the change:
debandrpmeach carried ahost:cell for exactly that venue;apkcarried none. The change mirrors them —/etc/apk/keysand/etc/apk/repositoriessetup, thenapk add --no-cache{{range .Options}} …{{range .Packages}} …— inserted before thecontainer:cell, in the order the siblings use.How tested
The control this lane never had, written to fail without the change — the same shape the sibling test uses.
It reads the COMMITTED manifest rather than a
testdatafixture — because the committed one is what ships — and asserts structurally that a format installing in a container also installs on a host, so a strayhost:elsewhere in the file cannot satisfy it.It parses that manifest with a single
yaml.Unmarshalrather than throughLoadBuildConfigForBox, and the reason is an RCA recorded on the thread: that loader resolves every@githubref and reaches for the network, and the first version of this control therefore hung theTeststep in CI for two hours (locally it took 76s), leaving the gate unable to reach a verdict. A control that cannot report anything is a worse failure than the one it catches, and it is an R3/R8 violation: no test reaches for infrastructure, and a boundary crossing a real service runs live or skips visibly, never hangs.* **The hermetic form still tests the file that ships, with no ref resolution and no network: 0.00s.And the negative control was run by hand, not assumed: remove the
host:cell from theapk:block and the test fails namingalpine/apk; restore it and it passes. Two of my own errors were caught on that path and are recorded on the thread: the first rewrite read../charly.yml, which fromcharly/charly/is a different file, and the first negative control deleted from the wronghost:cell and mangled 170 lines before the diff caught it. The manifest now carries 8host:cells — the delta is one cell, no collateral edits.*What this test found that the issue did not
The issue says
apkis "the only format" in this state. Writing the general assertion proved otherwise:That is a real difference, and it is not the same defect.
aurissecondary: trueand container-only by construction — it doesCOPY --from={{.StageName}} /tmp/aur-pkgs/andpacman -Uinside the image, and declares noinstall_template,download_template,probeorlocal_pkg. There is no host mechanism to mirror.So whether a host venue supports AUR packages — by a
makepkghost leg, or by failing loudly, or not at all — is a contract question. The assertion therefore skips asecondaryformat and names it in its output, and the question is filed as #963 rather than answered by inventing a host-side build path just to turn this test green.Rulebook compliance
/charly-internals:go,/charly-check:checkand/charly-image:layerare the governing rows for a Go change to a distro format's plan cells; this repo's ownAGENTS.mdand the format's siblings were read first.aur) is not filed away either — it is filed as aur is a secondary format with a container: install cell and no host: one, so a host venue silently gains nothing (found by #772's general assertion) #963 with its evidence, because it is the same class of difference and the issue is where a reader will look.*host:cell mirrorsdeb/rpm's shape rather than introducing a third mechanism, and the test asserts the invariant across every format rather than naming one, so the next format added is covered by construction.secondaryskip is an explicit, named exclusion rather than a silent one.deploy delrecording a removal the venue never ran) wascharly#771's subject and is closed.git statuswas clean before staging; two paths staged; the branch was created off freshorigin/mainin a session worktree; no force-push, no rebase.alpine/apk) and passes with it (0.00s), the assertion reads the shipped manifest hermetically, with no ref resolution and no network, and the emitted artefact is checked at its boundary bybox validateat zero warnings.2026.289.0649).charly.ymlformat-cell change, so the gate isbox validateplus the new unit control, both green. It is not a runtime-behaviour change, so no disposable bed was required — and the distinction is stated rather than assumed.apkthe cell its siblings have, and the control that keeps it.Change classification
Go test + manifest, one intent.
charly/charly.ymlgains the apkhost:install cell (+15);charly/apk_host_install_cell_test.gois new (+88). No schema, CLI, or wire change, so no consumer repo follows.Filed issues
opencharly/charly#963— "auris a secondary format with acontainer:install cell and nohost:one, so a host venue silently gains nothing" — filed from this PR's own general control, carrying the evidence (7 host: install cells in the tree, 0 underaur;secondary: true; noinstall_template/download_template/probe/local_pkg`) and the three candidate mechanisms.Filed rather than fixed, and the reason is the R1 classification: the mechanism differs.
apkhad siblingdeb/rpmcells to mirror, so supplying one is a fix.aurhas no host path at all — its container cell depends on a build stage a host does not have — so supplying one means inventing a host-sidemakepkgleg, which is a contract decision, not a fix. Inventing it to turn this PR's own new assertion green would be the fake fix the rulebook refuses.The PR's assertion therefore skips a
secondaryformat and names the skip in its output (skipped 1 secondary format(s) [arch/aur]), so the exclusion is explicit rather than silent.Closes #772
Agent: dsh-opencharly
Assisted-by: dsh DeepSeek deepseek-v4.1-flash (analysed on a live system)