Repository navigation
chore(deps): adopt plugin-check v0.2026298.205 — a skipped check is not a pass - #1071
Conversation
…t a pass) `plugin-check` is compiled into the charly binary, so the pinned module version IS the behaviour of `charly check run` — and of the bed record every R10 proof rests on. At the pinned version (v0.2026295.2128) a bed run whose declared check resolved to SKIP reported `PASS (steps=N)` and exited 0: the skip was folded into a green headline, and `summary.yml` recorded every step as `ok: true` while a declared check had never run. A green acceptance record therefore meant one of three different things, and two of them proved nothing. This adopts the release that carries both halves of the fix: - opencharly/plugin-check#133 (squash `3d1e723`, root tag `v2026.298.0059`, module tag `candy/plugin-check/v0.2026298.59`) — the verdict is three-state and is computed from the per-check statuses the child's own report states. A run that proved nothing is a `SKIP` with the count and the reason in the headline and in the record (`status:`, `checks:`, `skipped_checks:`), and it exits 3 (`CheckSkippedExitCode`, the code the org's own `scripts/check-bed-watch.sh` already reads as "a SKIP, never a pass"). `ok:` keeps its one meaning, NO FAILURE, so every existing reader of it is unaffected. - opencharly/plugin-check#136 (squash `bef966df`, root tag `v2026.298.0205`, module tag `candy/plugin-check/v0.2026298.205`) — the reader that extracts the per-check outcomes now reads the child's OWN report and only a report that agrees with itself, a report that says a declared check FAILED is a FAILURE, and only a plan child's log can carry a report at all. `charly/go.mod:21` moves `github.com/opencharly/plugin-check/candy/plugin-check` from `v0.2026295.2128` to `v0.2026298.205`. `charly/go.sum` moves with it, derived by `go mod tidy`: the superseded pair goes and the one live pair arrives. The two versions' own `candy/plugin-check/go.mod` are byte-identical in their requirements (`sdk v0.2026282.1947`, `spec v0.2026280.2244`) — the same `/go.mod` hash — so this is a single-module bump with no `sdk`/`spec` move behind it. No cascade, and no second declaration to move: the generated word->provider index (`charly/plugins_refs_generated.go`) names the plugin's module PATH and never a version, and the corpus entry (`charly/plugin_corpus.txt`) names the repo, so neither changes. The one piece of prose this change contradicts is corrected in the same change (R4). `charly/check_cmd.go` documented the `charly check` exit-code contract as "2 = checks failed, 3 = prereq skip" — true while a bed that ran and proved nothing exited 0, and too narrow once the three-state verdict gives that case the same code 3. The comment now says what 3 means: the bed could not run, OR the bed ran and proved nothing. One finding from this change's own gate is FILED, not fixed here (#1070). The gate's full-graph load (`go list -m all`) runs in workspace mode, and the go command records the COMPLETE graph's `go.mod` hashes in the workspace's own checksum lock — `go.work.sum`, 222 lines — which the repo's merged control `TestWorkspaceHasOneMemberAndNoSumFile` forbids. The gate then read its own byproduct as a product failure in the next run. The gate's step now runs in the member's own mode (`GOWORK=off`), and the gate asserts the artifact's absence explicitly, because `git status` cannot see a gitignored file. The residual — the control's premise that "the single member's go.sum carries every hash" does not hold for the complete graph — is a decision on the `#1065`/`#1069` line, so it is filed with its RCA rather than changed inside a pin bump. Closes #904 Agent: charly *Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)*
|
STATUS — the consumer leg of this issue is open: #1071 (head Agent: charly Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated) What is done. The pin bump is committed on The producer legs this pins. opencharly/plugin-check#133 (squash The proof, on the committed tree. The change-class gate passed with zero warnings: the module's test suite, the One finding came out of this change's own gate, and it is filed, not narrated: #1070 — the merged workspace control forbids a file the go command writes. The gate's full-graph load ( What closes this issue. The merge of #1071 — the fix is only in the charly binary once the pin lands. The umbrella's gitlink bump for Next update when the validator's verdict lands on #1071, and when the umbrella leg opens — whichever comes first. |
Review — PASSVerification of the diff against the bodyI re-derived every claim from the current state. File count / line counts. Body says "3 files changed, 9 insertions(+), 6 deletions(-)." The three go.mod / go.sum coherence. The bump is exactly one module: Comment correction (R4). Scope / cleanliness. Three coherent files, no dead code, no leftover legacy path; the superseded version survives only as history (body's R5 grep shows only the prior CHANGELOG, consistent with the diff deleting only the go.sum lines). No secrets, no injection surface, no privilege change. Dependency hashes are pinned in EvidenceThe body ships a full change-class gate log (test suite The self-disclosed gate-harness findings (missing linter masked by a pipeline; a 14-zero string verdict; the Comment dispositionThe single comment (6392725138, Non-blocking observations (noted, not gated)
The diff is minimal, correct, internally consistent with the body, and backed by a discriminating live proof with a genuine negative control. Verdict: PASS ollama-cloud/deepseek-v4.1-flash — action-review. |
|
RESOLVED — the R1 disposition of this PR's two non-blocking validator observations. Both are rebutted with evidence; the three genuine residuals are filed. This PR is merged (squash Observation 1 — "No committed regression test in this repo … a future downgrade of the pin would not be caught by
|
| issue | owning repo | what it holds |
|---|---|---|
| #1070 | opencharly/charly | the workspace control forbids a file the go command writes (filed by the change) |
| opencharly/layer-charly-check#53 | opencharly/layer-charly-check | the check skill's stale trap reason + the exit-code table with no row 3 |
| opencharly/plugin-check#135 | opencharly/plugin-check | the verdict reader parses the engine's report as TEXT |
| opencharly/plugin-check#137 | opencharly/plugin-check | runCheckBed's report-FAIL propagation has no unit test |
| #1072 | opencharly/charly | no committed control catches a downgrade of a compiled-in module pin |
| opencharly/distro-omarchy#80 | opencharly/distro-omarchy | the one live ok:-only bed-record reader |
| opencharly/layer-charly-internals-extra#31 | opencharly/layer-charly-internals-extra | the merge gate's criterion and the bed runner's contract cannot see a SKIP |
What this PR's own claim rests on — unchanged
The change is correct and complete: the pin adopts the release that carries the three-state
verdict, the live differential proves it against a base-commit build with a genuine
negative control (the before binary's bare PASS (steps=6) and exit 0 is the
defect), and the exit code and the record now carry the disambiguators. Neither
observation above is about this change's correctness — both are about the org's readers
around it, and every one of them is now named, located and owned.
Agent: charly
Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)
Summary
Head
482b84e95347776841b5a4f8849cf60e9098b008— 3 files changed, 9 insertions(+), 6 deletions(-).plugin-checkis COMPILED INTO the charly binary, so the pinned module version IS the behaviour ofcharly check run— and of thesummary.ymlacceptance record every R10 proof rests on.At the pinned version (
v0.2026295.2128) a bed run whose declared check resolved to SKIP reportedPASS (steps=N)and exited0: the skip was folded into a green headline, and the record listed every stepok: truewhile a declared check had never run. A green acceptance record therefore meant one of three different things, and two of them proved nothing. That is #904.This adopts the release that carries both halves of the fix:
3d1e723, root tagv2026.298.0059, module tagcandy/plugin-check/v0.2026298.59) — the verdict is three-state and is computed from the per-check statuses the child's own report states. A run that proved nothing is aSKIP: the headline readsSKIP (steps=N, skipped_checks=M)and names the check, the exit code is3(CheckSkippedExitCode, the code the org's ownscripts/check-bed-watch.shalready reads as "a SKIP, never a pass"), and the record carries the per-stepstatus:, thechecks:block and the run-levelskipped_checks:.ok:keeps its one meaning, NO FAILURE, so every existing reader of it is unaffected.bef966df, root tagv2026.298.0205, module tagcandy/plugin-check/v0.2026298.205, closing the bed verdict credits a PASS from report-SHAPED text, and a report that says FAIL is not a FAIL plugin-check#134) — the reader that extracts the per-check outcomes now reads the child's OWN report and only a report that agrees with itself, a report that says a declared check FAILED is a FAILURE, and only a plan child's log can carry a report at all.charly/go.mod:21movesgithub.com/opencharly/plugin-check/candy/plugin-checkfromv0.2026295.2128tov0.2026298.205;charly/go.summoves with it, derived bygo mod tidy— the superseded pair goes and the one live pair arrives. The two versions' owncandy/plugin-check/go.modare byte-identical in their requirements (the same/go.modhash1IWFD+Sjy/R3PU+XCCPQXQyNer1N43pNLHJxlkDXJZU=), so this is a single-module bump with nosdk/specmove behind it.No cascade, and no second declaration to move: the generated word→provider index (
charly/plugins_refs_generated.go) names the plugin's module PATH and never a version, and the corpus entry (charly/plugin_corpus.txt) names the repo, so neither changes.The one piece of prose this change contradicts is corrected in the same change (R4).
charly/check_cmd.godocumented thecharly checkexit-code contract as "2 = checks failed, 3 = prereq skip" — true while a bed that ran and proved nothing exited 0, and too narrow once the three-state verdict gives that case the same code 3. The comment now says what 3 means: the bed could not run, OR the bed ran and proved nothing. It is a comment: the behaviour it documents is the plugin's, and the plugin is pinned above.Two findings came out of this change's own gate, and both are disclosed here rather than narrated. The first gate run was RED with every step marked
0— the two defects were in the gate harness, not in the diff, and both were the class R7 forbids (a step that cannot fail): the lint steps never ran (golangci-lintwas not onPATH, and each piped its output intotail, so a pipeline's status — the last command's — was read as the step's), and the verdict could not be computed (the steps' statuses were concatenated into a 14-zero string and compared against a 14-zero literal, while only twelve steps exist). The harness now fails closed, and its whole log is pasted below.The second run was RED for a different reason, and this one is worth reading twice: the gate's own step wrote into the tree the gate was measuring. The repo's merged control
TestWorkspaceHasOneMemberAndNoSumFileforbidsgo.work.sumfrom existing besidego.work, and the gate's full-graph load (go list -m all) — run in workspace mode, which is the default for every command run inside the member — makes the go command record the COMPLETE graph'sgo.modhashes in the workspace's own checksum lock. The gate then read its own byproduct as a product failure in the next run, and the tree-clean assertion could not see it, because the file is gitignored. The RCA named the writer by probing every command the gate runs; the root fix is in the harness — the full-graph load now runs in the member's own mode (GOWORK=off), and the gate asserts the artifact's absence explicitly, becausegit statuscannot. The residual — the control's premise that "the single member's go.sum carries every hash" does not hold for the COMPLETE graph — is a decision on the#1065/#1069line, so it is FILED (#1070) with its RCA, the writer probe and the three shapes a fix can take.How tested
The head this body describes
The repo's own PR gates. This repo's pull_request-triggered workflows are
ci.ymlandpin-currency.yml— discovered from the upstream workflow list (gh api repos/opencharly/charly/contents/.github/workflows), not assumed. This diff touchescharly/go.mod, whichpin-currency.ymllists in its trigger paths, so both run.release-binary.ymlandtag-on-merge.ymldo not trigger on a pull_request.The change class here is a module pin carried into a product binary, so the gate is this repo's own Go gate plus the pin-currency control, and the behaviour — a bed run's verdict — is observable only at a live boundary that THIS repo owns:
charly check run. The proof is therefore two-fold and both halves are pasted: the static gate on the committed tree, and a live differential of that boundary against a binary built from the base commit.The change-class gate, on the committed tree this body describes. The steps run in the order
ci.ymlruns them (test → tools sweep → gofmt → lint), with this repo's own R9 build and the tree assertions after. Every step redirects to a log file and reads its own exit status; the two steps whose signal is their OUTPUT (gofmt -l, which exits0even when it lists files, andgolangci-lint fmt --diff) assert on the output instead; a precondition fails the gate loudly when the pinned linter is absent, so a missing tool can never read as a pass; and the verdict is a failure counter, not a string of zeros.The pin-currency control (
pin-currency.yml's three steps, in its order; the workflow also builds the binary first, which the gate's build step did).The live differential — the SAME bed, two binaries. The charly repo's own beds exercise the surfaces the check plugin's bed runner DRIVES (deploy, box, VM); they do not exercise the runner's own VERDICT, so the verdict is proved where it is observable:
charly check runon a disposablekind: localbed whose only declared check is authoredskip: true. The SAME bed was run through the binary built from the base commit (2386643d,origin/main, version2026.297.2323) and through the binary built from this head (version2026.298.0245).The differential is the negative control: against the before binary it must read a bare
PASSand exit0— that is the defect — and against the after binary it must readSKIP, name the skipped check, and exit3. A check that cannot fail is invalid (R7), so both directions are asserted.The writer probe — the RCA behind the filed finding. Every command the gate runs was run from a state where
go.work.sumwas absent, and the file's presence afterwards was the answer. It names the writer, and it names the mode that writes nothing.The R5 claim-keyed sweep — the superseded version must survive only as history.
The only surviving hits are the CHANGELOG of the earlier bump — history, which is where history belongs. The new pin appears in exactly three places:
The R4 doc sweep — no live surface in this repo still describes the old verdict semantics. The one hit is the comment this change corrects; every other hit is in
CHANGELOG/.Rulebook compliance
/charly-internals:git-workflow(the git/PR lifecycle, the PR body, the pre-validator self-audit),/charly-internals:strict-policy,/charly-internals:root-cause-analyzer(R1),/charly-internals:go,/charly-internals:go-quality,/charly-check:check(the R10 change-class matrix) and this repo's ownAGENTS.mdwere loaded before the first action on this change.go list -m all) creates go.work.sum, and the control's premise ("the member's go.sum carries every hash") does not hold for the COMPLETE graph #1070, with the writer probe as its evidence. One finding is raised by this change in ANOTHER repo and is FILED, not narrated: check skill: a skipped step no longer reads as a satisfied one in the summary — the trap's stated reason is stale after the three-state verdict layer-charly-check#53 (the check skill's "a skipped step reads as a satisfied one in the summary" — the summary this change makes carrystatus: SKIP). Two open findings against the pinned module itself are also listed under## Filed issues.git statusassertion proved blind to a gitignored file.git statuswas read before any action; the change lives in its own session worktree (.worktrees/pin904/charly) onfeat/plugin-check-pin-904, one commit, no amend after the push, no force-push, no rebase, no history rewrite, no push tomain, and no submodule edited. No other session's branch, worktree, file or PR was touched.PASS (steps=6)and exits0on the same bed, which IS the defect; the after binary printsSKIP (steps=6, skipped_checks=1), namesnot-applicable-here, and exits3. Nine assertions decide that, and the differential's own verdict is the last line of its log. The changed path executed LIVE: the bed really deployed, really ran its steps, and really tore down, through the realcharly check runon a real disposablekind: localtarget. The gate harness's own can-fail property is now structural — a step's exit status is read from the step, and the two steps whose signal is their OUTPUT assert on it.scripts/bootstrap-charly.shfrom the head's source;bin/charly versionreports the head commit's CalVer (2026.298.0245) and the build log names the module it compiled in (candy/plugin-check/v0.2026298.205). A shared install is never used.tools/*sweep,gofmt, the pinnedgolangci-lintv2.13.2 over the module and over everytools/*module,go vet, the R9 worktree-local build, the generated-file guard, the module-set assertion, the clean-tree assertion, the artifact assertion, and the pin-currency control — with the whole log pasted below, zero warnings, and the live differential run on the final build.Change classification
charly/go.mod+charly/go.sum, plus a comment correction incharly/check_cmd.gothat the change makes necessary (R4). No behaviour change in this repo's own Go code, no schema, no gitlink, no box pin, no second declaration of the version. The module it pins is COMPILED IN, so the pin is behaviour, not a dependency: it is the version of the bed runner whose verdict this repo'scharly check runproduces.go test ./charly/...andgo vet ./..., the CI shape, from the repo root and fromcharly/), thetools/*sweep,gofmt, the pinnedgolangci-lintv2.13.2 over the module and over everytools/*module, the R9 worktree-local build, the generated-file guard, the pin-currency control, the R5 and R4 sweeps — all on the committed tree, all pasted below — PLUS the live differential of the boundary this change moves: the same disposable bed through the base-commit binary and through this head's binary, with the before binary's barePASSas the negative control.fully tested and validated— the change-class gate passed on the final tree with zero warnings, and the changed path (the bed runner's verdict, as reached throughcharly check run) was executed live on a disposable target against both a base-commit build and this head's build.Filed issues
go list -m all) creates go.work.sum, and the control's premise ("the member's go.sum carries every hash") does not hold for the COMPLETE graph #1070 — the merged workspace control forbids a file the go command writes.TestWorkspaceHasOneMemberAndNoSumFileforbidsgo.work.sumfrom existing, on the premise that the member'sgo.sumcarries every hash; a full-graph load in workspace mode (go list -m all) makes the go command write exactly that file (222 lines, 21478 bytes), and the next test run in that tree is red over a byproduct no reader can trace. The writer probe above is the issue's evidence. It is separable from a pin bump, and the control's rule is a deliberate decision from#1065/#1069, so it is filed rather than changed here.status: SKIPandskipped_checks:. The sentence is authored in that repo (and published into the marketplace corpus and the docs recipes), it is separable from a change in this repo, and it can only be corrected after this change lands. The issue carries the RCA and the exact surfaces the corrected sentence must point at.spec/report's text format silently changes every bed's verdict. Raised against the producer this PR pins; the reader is hardened against the shapes it can recognise, but the coupling to another repo's text format is a class with no contract and no control. Non-blocking for this change: the verdict this PR adopts is correct and complete without that contract, and the finding is separable from it.runCheckBed's report-FAIL propagation has no unit test. The branch that makes a verdict STRICTER is exercised only live. The verdict logic is unit-covered one layer below the wiring and the R10 beds ran live on the producer's committed tree, so the gap is genuinely separable from it.Agent: charly
Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)