Skip to content

chore(deps): adopt plugin-check v0.2026298.205 — a skipped check is not a pass - #1071

Merged
github-actions[bot] merged 1 commit into
mainfrom
feat/plugin-check-pin-904
Oct 25, 2026
Merged

github-actions[bot] merged 1 commit into
mainfrom
feat/plugin-check-pin-904

Conversation

@aitrawog

Copy link
Copy Markdown
Contributor

Summary

Head 482b84e95347776841b5a4f8849cf60e9098b008 — 3 files changed, 9 insertions(+), 6 deletions(-).

plugin-check is COMPILED INTO the charly binary, so the pinned module version IS the behaviour of charly check run — and of the summary.yml acceptance 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 the record listed every step 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. That is #904.

This adopts the release that carries both halves of the fix:

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 (the same /go.mod hash 1IWFD+Sjy/R3PU+XCCPQXQyNer1N43pNLHJxlkDXJZU=), 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. 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-lint was not on PATH, and each piped its output into tail, 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 TestWorkspaceHasOneMemberAndNoSumFile forbids go.work.sum from existing beside go.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's go.mod hashes 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, because git status cannot. 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 (#1070) with its RCA, the writer probe and the three shapes a fix can take.

How tested

The head this body describes

$ git rev-parse HEAD
482b84e95347776841b5a4f8849cf60e9098b008
$ git rev-list --left-right --count origin/main...HEAD
0	1
$ git diff --shortstat origin/main HEAD
 3 files changed, 9 insertions(+), 6 deletions(-)
$ git diff --name-only origin/main HEAD
charly/check_cmd.go
charly/go.mod
charly/go.sum
$ git diff origin/main HEAD
diff --git a/charly/check_cmd.go b/charly/check_cmd.go
index e14d4488..25c096e8 100644
--- a/charly/check_cmd.go
+++ b/charly/check_cmd.go
@@ -21,10 +21,13 @@ import (
 // external `target: local` deploy's own --verify path RELOCATED to candy/plugin-fleet's
 // verify_local.go (#55 W3 B3 remainder) — it no longer lives here.
 
-// The `charly check` exit-code contract (2 = checks failed, 3 = prereq skip) lives in
+// The `charly check` exit-code contract (2 = checks failed, 3 = not a verdict) lives in
 // the sdk (exitcode.CheckFailExitCode / exitcode.CheckSkippedExitCode); the plugin/main signal it
-// across the module boundary via *exitcode.ExitCodeError. The `charly check` CLI + its
-// exit-code plumbing live in command:check (candy/plugin-check).
+// across the module boundary via *exitcode.ExitCodeError. 3 covers BOTH "the bed could not run"
+// (a required host prerequisite is absent) AND "the bed RAN and proved NOTHING" (a declared
+// check resolved to SKIP): the plugin's three-state verdict gives the second case the same
+// code, so a reader that stops at "prereq skip" reads exit 3 as narrower than it is.
+// The `charly check` CLI + its exit-code plumbing live in command:check (candy/plugin-check).
 
 // resolveCheckRunnerContext computes the committed-APK anchoring + loads the OUT-OF-TREE plugin
 // candies a live baked-plan runner needs, so `charly check live` and `charly check feature run`
diff --git a/charly/go.mod b/charly/go.mod
index 637aa5d4..c81d1a85 100644
--- a/charly/go.mod
+++ b/charly/go.mod
@@ -18,7 +18,7 @@ require (
 	github.com/opencharly/plugin-cache/candy/plugin-cache v0.2026271.2314
 	github.com/opencharly/plugin-candy-kind/candy/plugin-candy-kind v0.2026271.2319
 	github.com/opencharly/plugin-candy/candy/plugin-candy v0.2026281.948
-	github.com/opencharly/plugin-check/candy/plugin-check v0.2026295.2128
+	github.com/opencharly/plugin-check/candy/plugin-check v0.2026298.205
 	github.com/opencharly/plugin-clean/candy/plugin-clean v0.2026287.2341
 	github.com/opencharly/plugin-cmd/candy/plugin-cmd v0.2026272.2039
 	github.com/opencharly/plugin-command/candy/plugin-command v0.2026277.738
diff --git a/charly/go.sum b/charly/go.sum
index 186a915e..7cc2b253 100644
--- a/charly/go.sum
+++ b/charly/go.sum
@@ -169,8 +169,8 @@ github.com/opencharly/plugin-candy-kind/candy/plugin-candy-kind v0.2026271.2319
 github.com/opencharly/plugin-candy-kind/candy/plugin-candy-kind v0.2026271.2319/go.mod h1:sJZfp6ovCsj1LzQAzq/RFw5FmtzBbnWZk8IPvyK/v+c=
 github.com/opencharly/plugin-candy/candy/plugin-candy v0.2026281.948 h1:becOqUxBQxNBTMwazoqmF24b4ApCfDHAQmzOfVwUTWg=
 github.com/opencharly/plugin-candy/candy/plugin-candy v0.2026281.948/go.mod h1:UC/PfR3DaB6yXAeqWPUwpqn/hpvOh3YhWjiay5zJW+U=
-github.com/opencharly/plugin-check/candy/plugin-check v0.2026295.2128 h1:k5ZAE19NQLushp7FxqIzGz+8DyJ2oQslsK87ZSCwxuM=
-github.com/opencharly/plugin-check/candy/plugin-check v0.2026295.2128/go.mod h1:1IWFD+Sjy/R3PU+XCCPQXQyNer1N43pNLHJxlkDXJZU=
+github.com/opencharly/plugin-check/candy/plugin-check v0.2026298.205 h1:leWRuk9/n0+GSJEaqFrkmX8QTdQ2kyefxXLjiBswZDg=
+github.com/opencharly/plugin-check/candy/plugin-check v0.2026298.205/go.mod h1:1IWFD+Sjy/R3PU+XCCPQXQyNer1N43pNLHJxlkDXJZU=
 github.com/opencharly/plugin-clean/candy/plugin-clean v0.2026287.2341 h1:7HvWdgnWjmKl6XdU7TbUrAlGju+XGRgG3ub72Vc4mpw=
 github.com/opencharly/plugin-clean/candy/plugin-clean v0.2026287.2341/go.mod h1:JvMqAl8jecSWVscrP51vWx2aeDxP9ui1fcXecR3HkwA=
 github.com/opencharly/plugin-cmd/candy/plugin-cmd v0.2026272.2039 h1:gVsGunNwjAjPTX/rTXFzqFwwaEs5FKKZRhTFeO+RR0I=

The repo's own PR gates. This repo's pull_request-triggered workflows are ci.yml and pin-currency.yml — discovered from the upstream workflow list (gh api repos/opencharly/charly/contents/.github/workflows), not assumed. This diff touches charly/go.mod, which pin-currency.yml lists in its trigger paths, so both run. release-binary.yml and tag-on-merge.yml do 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.yml runs 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 exits 0 even when it lists files, and golangci-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.


=== 0. head, toolchain, and the pinned linter ===
482b84e95347776841b5a4f8849cf60e9098b008
482b84e9 chore(deps): adopt plugin-check v0.2026298.205 (a skipped check is not a pass)
go version go1.26.8 linux/amd64
golangci-lint has version 2.13.2 built with go1.27.3-X:nodwarf5 from (unknown, modified: ?, mod sum: "h1:bCyq3E4vo9qwzifjpzJqYndEt7Ncva80qkk8G2B4TXU=") on (unknown)
PRECONDITION_RC=0

=== 1. the module's test suite (CI shape: go test ./charly/... from the repo root) ===
== test (rc=0) ==
ok  	github.com/opencharly/charly/charly	180.342s
ok  	github.com/opencharly/charly/charly/internal/pluginsgen	128.265s
test_RC=0

=== 2. the tools/* sweep (CI shape: GOWORK=off, -count=1) ===
== tools (rc=0) ==
== tools/generate-packages/ ==
?   	github.com/opencharly/charly/candy/generate-packages/cmd/serve	[no test files]
== tools/golden-cloudinit/ ==
?   	github.com/opencharly/charly/tools/golden-cloudinit	[no test files]
== tools/golden-compile/ ==
?   	github.com/opencharly/charly/tools/golden-compile	[no test files]
== tools/gomod-canonical/ ==
ok  	github.com/opencharly/charly/tools/gomod-canonical	0.001s
== tools/ledger-validate/ ==
ok  	github.com/opencharly/charly/tools/ledger-validate	0.002s
tools_RC=0

=== 3. gofmt (CI shape: the OUTPUT must be empty, gofmt -l exits 0 either way) ===
GOFMT_RC=0 (gofmt -l rc=0)

=== 4. golangci-lint fmt --diff (charly/) — must print nothing ===
FMT_RC=0

=== 5. golangci-lint v2.13.2 (charly/, CI shape) ===
== lint (rc=0) ==
0 issues.
lint_RC=0

=== 6. golangci-lint v2.13.2 (each tools module, CI shape) ===
== toolslint (rc=0) ==
== lint tools/generate-packages/ ==
0 issues.
== lint tools/golden-cloudinit/ ==
0 issues.
== lint tools/golden-compile/ ==
0 issues.
== lint tools/gomod-canonical/ ==
0 issues.
== lint tools/ledger-validate/ ==
0 issues.
toolslint_RC=0

=== 7. go vet ./... (from charly/) ===
== vet (rc=0) ==
vet_RC=0

=== 8. the R9 worktree-local build (scripts/bootstrap-charly.sh) ===
== build (rc=0) ==
bootstrap-charly: regenerating compiled-in plugin wiring
bootstrap-charly: building bin/charly
bootstrap-charly: built ./bin/charly (2026.298.0245)
build_RC=0

=== 9. the generated wiring is clean (scripts/check-generated-clean.sh) ===
== generated (rc=0) ==
generated_RC=0

=== 10. the binary is the worktree-local build ===
== version (rc=0) ==
2026.298.0245
version_RC=0

=== 11. the binary's module set carries the new plugin-check (the member's own mode: GOWORK=off, so the step does not write into the tree it measures) ===
github.com/opencharly/plugin-check/candy/plugin-check v0.2026298.205 h1:leWRuk9/n0+GSJEaqFrkmX8QTdQ2kyefxXLjiBswZDg=
MODS_RC=0

=== 12. the tree is clean, and the workspace carries NO checksum lock of its own ===
STATUS_RC=0
ARTIFACT_RC=0 — go.work.sum is absent
ARTIFACT_RC=0 — go.work.dev.sum is absent

GATE_DONE: all steps passed
GATE_EXIT=0

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).

=== 1. the control's own proof (it must be able to fail) ===
charly: fetching git metadata for 171 repo(s) (first run — may take a moment)...
charly: git metadata cached.
plugin github.com/opencharly/plugin-example-structkind/candy/plugin-example-structkind: served from /home/atrawog/.cache/charly/plugin-build/views/plugin-example-structkind-1a105a6ca5951472 (build sha256:f537fb4f0312282bf8674da8b7a94d5b7c27adc56913a0a881b4a7a61053cab2); source /home/atrawog/.cache/charly/repos/github.com/opencharly/plugin-example-structkind@v2026.272.0147.view.619cda42315d3d56/candy/plugin-example-structkind
plugin plugin-example-kind: served from /home/atrawog/.cache/charly/plugin-build/views/plugin-example-kind-0db2a3f3d238ae08 (build sha256:35d94e3a68de44a3bba5beb411491fa494118c0dcba4857bbc17201739e9db2e); source /home/atrawog/.cache/charly/repos/github.com/opencharly/plugin-example-kind@v2026.272.0055.view.619cda42315d3d56/candy/plugin-example-kind
charly: fetching git metadata for 8 repo(s) (first run — may take a moment)...
charly: git metadata cached.
charly: fetching git metadata for 25 repo(s) (first run — may take a moment)...
charly: git metadata cached.
charly: fetching git metadata for 1 repo(s) (first run — may take a moment)...
charly: git metadata cached.
charly: fetching git metadata for 15 repo(s) (first run — may take a moment)...
charly: git metadata cached.
charly: fetching git metadata for 6 repo(s) (first run — may take a moment)...
charly: git metadata cached.
plugin github.com/opencharly/plugin-jetkvm/candy/plugin-jetkvm: served from /home/atrawog/.cache/charly/plugin-build/views/plugin-jetkvm-bdabfd88da61a691 (build sha256:d3d97ac10f06ca44d840927958b62908c552503a361422f07850ed2e2d9d09f9); source /home/atrawog/.cache/charly/repos/github.com/opencharly/plugin-jetkvm@v2026.272.0236.view.619cda42315d3d56/candy/plugin-jetkvm
task pin-currency-proof: 1 step(s), 0 failed, 0 skipped
  [pass] check — the control bites on a neglected pin and stands down on a churned one
      exit=0
PROOF_RC=0

=== 2. the classifier's self-test ===
the classifier, on a scratch twin:
  ok    cur                       (the pin IS the served ref)
  ok    behind                    (the served ref descends from the recorded pin)
  ok    not-on-the-served-line    (a squash-merge: neither descends from the other)
  ok    unmerged                  (the recorded pin is AHEAD of the served ref)
  ok    unjudgeable               (the recorded commit is not in the twin's object store)
PIN-CADENCE-SELF-TEST-OK
SELFTEST_RC=0

=== 3. the control itself ===
plugin github.com/opencharly/plugin-example-structkind/candy/plugin-example-structkind: served from /home/atrawog/.cache/charly/plugin-build/views/plugin-example-structkind-1a105a6ca5951472 (build sha256:f537fb4f0312282bf8674da8b7a94d5b7c27adc56913a0a881b4a7a61053cab2); source /home/atrawog/.cache/charly/repos/github.com/opencharly/plugin-example-structkind@v2026.272.0147.view.619cda42315d3d56/candy/plugin-example-structkind
plugin plugin-example-kind: served from /home/atrawog/.cache/charly/plugin-build/views/plugin-example-kind-0db2a3f3d238ae08 (build sha256:35d94e3a68de44a3bba5beb411491fa494118c0dcba4857bbc17201739e9db2e); source /home/atrawog/.cache/charly/repos/github.com/opencharly/plugin-example-kind@v2026.272.0055.view.619cda42315d3d56/candy/plugin-example-kind
plugin plugin-jetkvm: served from /home/atrawog/.cache/charly/plugin-build/views/plugin-jetkvm-bdabfd88da61a691 (build sha256:d3d97ac10f06ca44d840927958b62908c552503a361422f07850ed2e2d9d09f9); source /home/atrawog/.cache/charly/repos/github.com/opencharly/plugin-jetkvm@v2026.272.0236.view.619cda42315d3d56/candy/plugin-jetkvm
task pin-current: 1 step(s), 0 failed, 0 skipped
  [pass] run — ask whether every box/* pin is current, within this repo's own bound on neglect
      currency: 0 of 6 pin(s) NOT current [6 cur]; intents: 6 upstream leaf/leaves (6 implicit), 0 derived-from edge(s); producer-first legs: none — every pin is at the ref its intent names
CURRENT_RC=0

PIN_GATE_DONE: all steps passed
PIN_GATE_EXIT=0

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 run on a disposable kind: local bed whose only declared check is authored skip: true. The SAME bed was run through the binary built from the base commit (2386643d, origin/main, version 2026.297.2323) and through the binary built from this head (version 2026.298.0245).

The differential is the negative control: against the before binary it must read a bare PASS and exit 0 — that is the defect — and against the after binary it must read SKIP, name the skipped check, and exit 3. A check that cannot fail is invalid (R7), so both directions are asserted.

=== the BEFORE binary (origin/main) ===
2026.297.2323
--- stdout ---
--- stderr ---
Resolved @github.com/opencharly/plugin-vm -> main (default branch)
Resolved @github.com/opencharly/plugin-deploy-local -> main (default branch)
charly: fetching git metadata for 1 repo(s) (first run — may take a moment)...
charly: git metadata cached.
plugin github.com/opencharly/plugin-deploy-local/candy/plugin-deploy-local: served from /home/atrawog/.cache/charly/plugin-build/views/plugin-deploy-local-b6b62bf9e2bf954d (build sha256:a24f3dfefb34b8c4d4fd598557168394762c51e74c67fc0abd6c0bd733480957); source /home/atrawog/.cache/charly/repos/github.com/opencharly/plugin-deploy-local@main.view.619cda42315d3d56/candy/plugin-deploy-local
charly check run check-pin-verdict-witness: [deploy-add] START (charly deploy add; log: .check/check-pin-verdict-witness/2026.298.0253/deploy-add.log)
charly check run check-pin-verdict-witness: [deploy-add] PASS after 1.256s (log: .check/check-pin-verdict-witness/2026.298.0253/deploy-add.log)
charly check run check-pin-verdict-witness: [bring-up-members] START
charly check run check-pin-verdict-witness: [bring-up-members] PASS after 0s
charly check run check-pin-verdict-witness: [check-live] START (charly check live; log: .check/check-pin-verdict-witness/2026.298.0253/check-live.log)
charly check run check-pin-verdict-witness: [check-live] PASS after 2.999s (log: .check/check-pin-verdict-witness/2026.298.0253/check-live.log)
charly check run check-pin-verdict-witness: [update] START (charly update; log: .check/check-pin-verdict-witness/2026.298.0253/update.log)
charly check run check-pin-verdict-witness: [update] PASS after 1.227s (log: .check/check-pin-verdict-witness/2026.298.0253/update.log)
charly check run check-pin-verdict-witness: [cleanup] START (charly deploy del; log: .check/check-pin-verdict-witness/2026.298.0253/cleanup.log)
charly check run check-pin-verdict-witness: [cleanup] PASS after 1.236s (log: .check/check-pin-verdict-witness/2026.298.0253/cleanup.log)
charly check run check-pin-verdict-witness: [cleanup-members] START
charly check run check-pin-verdict-witness: [cleanup-members] PASS after 0s
charly check run check-pin-verdict-witness: PASS (steps=6)
BEFORE_RC=0

=== the AFTER binary (this branch) ===
2026.298.0245
--- stdout ---
--- stderr ---
Resolved @github.com/opencharly/plugin-vm -> main (default branch)
Resolved @github.com/opencharly/plugin-deploy-local -> main (default branch)
charly: fetching git metadata for 1 repo(s) (first run — may take a moment)...
charly: git metadata cached.
plugin github.com/opencharly/plugin-deploy-local/candy/plugin-deploy-local: served from /home/atrawog/.cache/charly/plugin-build/views/plugin-deploy-local-b6b62bf9e2bf954d (build sha256:a24f3dfefb34b8c4d4fd598557168394762c51e74c67fc0abd6c0bd733480957); source /home/atrawog/.cache/charly/repos/github.com/opencharly/plugin-deploy-local@main.view.619cda42315d3d56/candy/plugin-deploy-local
charly check run check-pin-verdict-witness: [deploy-add] START (charly deploy add; log: .check/check-pin-verdict-witness/2026.298.0253/deploy-add.log)
charly check run check-pin-verdict-witness: [deploy-add] PASS after 1.273s (log: .check/check-pin-verdict-witness/2026.298.0253/deploy-add.log)
charly check run check-pin-verdict-witness: [bring-up-members] START
charly check run check-pin-verdict-witness: [bring-up-members] PASS after 0s
charly check run check-pin-verdict-witness: [check-live] START (charly check live; log: .check/check-pin-verdict-witness/2026.298.0253/check-live.log)
charly check run check-pin-verdict-witness: [check-live] SKIP after 2.48s: declared check not-applicable-here did not run: skip: true (log: .check/check-pin-verdict-witness/2026.298.0253/check-live.log)
charly check run check-pin-verdict-witness: [update] START (charly update; log: .check/check-pin-verdict-witness/2026.298.0253/update.log)
charly check run check-pin-verdict-witness: [update] PASS after 1.285s (log: .check/check-pin-verdict-witness/2026.298.0253/update.log)
charly check run check-pin-verdict-witness: [cleanup] START (charly deploy del; log: .check/check-pin-verdict-witness/2026.298.0253/cleanup.log)
charly check run check-pin-verdict-witness: [cleanup] PASS after 1.208s (log: .check/check-pin-verdict-witness/2026.298.0253/cleanup.log)
charly check run check-pin-verdict-witness: [cleanup-members] START
charly check run check-pin-verdict-witness: [cleanup-members] PASS after 0s
charly check run check-pin-verdict-witness: SKIP (steps=6, skipped_checks=1) — check-live: declared check not-applicable-here did not run: skip: true
command "check": charly check run check-pin-verdict-witness: skipped (check-live: declared check not-applicable-here did not run: skip: true)
AFTER_RC=3

=== the records ===
--- before: .check tree ---
.check/check-pin-verdict-witness/2026.298.0253/summary.yml
bed: check-pin-verdict-witness
calver: 2026.298.0253
driver: /home/atrawog/Sync/Atrapub/a/claude/opencharly/.worktrees/pin904-before/charly/bin/charly
driver_version: 2026.297.2323
driver_size: 52296588
driver_mtime: 2026-10-25T02:15:29Z
runner_stderr: runner.stderr.log
steps:
  - name: deploy-add
    duration_seconds: 1
    ok: true
    skipped: 0
  - name: bring-up-members
    duration_seconds: 0
    ok: true
    skipped: 0
  - name: check-live
    duration_seconds: 3
    ok: true
    skipped: 1
  - name: update
    duration_seconds: 1
    ok: true
    skipped: 0
  - name: cleanup
    duration_seconds: 1
    ok: true
    skipped: 0
  - name: cleanup-members
    duration_seconds: 0
    ok: true
    skipped: 0
total_seconds: 7
diagnostics:
  errors: 0
  warnings: 0
  advisories: 0
  allowlisted: 0
  errors_fatal: true
  warnings_fatal: false
  advisories_fatal: false
  cache_ratio: not-applicable-no-build-steps
ok: true
--- after: .check tree ---
.check/check-pin-verdict-witness/2026.298.0253/summary.yml
bed: check-pin-verdict-witness
calver: 2026.298.0253
driver: /home/atrawog/Sync/Atrapub/a/claude/opencharly/.worktrees/pin904/charly/bin/charly
driver_version: 2026.298.0245
driver_size: 52322778
driver_mtime: 2026-10-25T02:50:43Z
runner_stderr: runner.stderr.log
steps:
  - name: deploy-add
    duration_seconds: 1
    ok: true
    status: PASS
    skipped: 0
  - name: bring-up-members
    duration_seconds: 0
    ok: true
    status: PASS
    skipped: 0
  - name: check-live
    duration_seconds: 2
    ok: true
    status: SKIP
    skipped: 1
    skip_reason: "declared check not-applicable-here did not run: skip: true"
    checks:
      - id: not-applicable-here
        status: SKIP
        text: "a declared check that does not apply to this run"
        reason: "skip: true"
  - name: update
    duration_seconds: 1
    ok: true
    status: PASS
    skipped: 0
  - name: cleanup
    duration_seconds: 1
    ok: true
    status: PASS
    skipped: 0
  - name: cleanup-members
    duration_seconds: 0
    ok: true
    status: PASS
    skipped: 0
total_seconds: 6
diagnostics:
  errors: 0
  warnings: 0
  advisories: 0
  allowlisted: 0
  errors_fatal: true
  warnings_fatal: false
  advisories_fatal: false
  cache_ratio: not-applicable-no-build-steps
skipped_checks: 1
ok: true
status: SKIP

=== the assertions ===
BEFORE_ASSERT: the before binary printed a bare PASS (the defect)
BEFORE_ASSERT: the before binary exited 0 (the defect)
AFTER_ASSERT: the after binary printed SKIP
AFTER_ASSERT: the after binary named the skipped count
AFTER_ASSERT: the after binary named the check that did not run
AFTER_ASSERT: the after binary exited 3 (CheckSkippedExitCode)
AFTER_ASSERT: the record carries status: SKIP
AFTER_ASSERT: the record carries skipped_checks: 1
BEFORE_ASSERT: the before record carries no skipped_checks key (the false-green record)

DIFF_DONE: the differential discriminates
DIFF_EXIT=0

The writer probe — the RCA behind the filed finding. Every command the gate runs was run from a state where go.work.sum was absent, and the file's presence afterwards was the answer. It names the writer, and it names the mode that writes nothing.

$ cd charly && go list -m -f '{{.Path}} {{.Version}}' all
>>> WROTE go.work.sum (222 lines, 21478 bytes)
$ cd charly && GOWORK=off go list -m -f '{{.Path}} {{.Version}}' all
>>> no go.work.sum
$ cd charly && go mod tidy
>>> no go.work.sum
$ cd charly && GOWORK=off go mod tidy
>>> no go.work.sum

The R5 claim-keyed sweep — the superseded version must survive only as history.

$ git grep -n "2026295\.2128" -- .
CHANGELOG/2026.295.2309.md:1:# 2026.295.2309 — chore(deps): adopt plugin-check v0.2026295.2128 (kind:local bed teardown via deploy del)
CHANGELOG/2026.295.2309.md:7:This change is the pin bump to `v0.2026295.2128` (tag `v2026.295.2128`, squash `37f4fe6`) and its two `go.sum` hash lines. Two files, no Go source change. One bump closes #128 and #130 for this pin; in the new module `bedRootTeardownArgs` (`bed_run.go:335`) is the one predicate behind both cleanup switches (`bed_run.go:760`, `bed_run.go:988`).
CHANGELOG/2026.295.2309.md:74:Before = a binary rebuilt from `origin/main` `9fd74793` (pin `v0.2026293.1223`, version 2026.295.1709), tree `be0eb882e33a481f2ed1f660abfdfea2d41439e5`. After = the worktree binary above (pin `v0.2026295.2128`, version 2026.295.2200), tree `ca520dc07e75460cee69723299c85ebf8706e162`.
CHANGELOG/2026.295.2309.md:83:The module source agrees: `grep -rl bedRootTeardownArgs` returns 0 files under `plugin-check@v0.2026293.1223` and 3 files under `plugin-check@v0.2026295.2128`.

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:

$ git grep -n "2026298\.205" -- .
charly/go.mod:21:	github.com/opencharly/plugin-check/candy/plugin-check v0.2026298.205
charly/go.sum:172:github.com/opencharly/plugin-check/candy/plugin-check v0.2026298.205 h1:leWRuk9/n0+GSJEaqFrkmX8QTdQ2kyefxXLjiBswZDg=
charly/go.sum:173:github.com/opencharly/plugin-check/candy/plugin-check v0.2026298.205/go.mod h1:1IWFD+Sjy/R3PU+XCCPQXQyNer1N43pNLHJxlkDXJZU=

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/.

$ grep -rniE "skipped_checks|CheckSkippedExitCode|PASS \(steps" --include="*.md" --include="*.cue" --include="*.mjs" --include="*.go" . | grep -v "CHANGELOG/"
./charly/check_cmd.go:25:// the sdk (exitcode.CheckFailExitCode / exitcode.CheckSkippedExitCode); the plugin/main signal it

Rulebook compliance

  • R0 — skills first. /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 own AGENTS.md were loaded before the first action on this change.
  • R1 — every finding is fixed or filed. The finding this change exists for is the false-green verdict, and it is FIXED in the product by the pin bump. The first gate run raised two more findings — in the GATE, not the diff — and both are FIXED in the harness, with the root fix described in the Summary and the re-run pasted below. The second gate run raised a third, also in the harness: the gate's own step wrote into the tree it measured, and the repo's own control then read the byproduct as a product failure. The harness's step now runs in the member's own mode and the gate asserts the artifact's absence; the residual — the control's premise about the toolchain — is FILED as the merged workspace control forbids a file the go command writes: a full-graph load in workspace mode (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 carry status: SKIP). Two open findings against the pinned module itself are also listed under ## Filed issues.
  • R2 — no duplication. One canonical implementation per behaviour: the verdict lives in the module, this repo carries the VERSION, and no second declaration of that version exists to move (the generated provider index names the module PATH, the corpus entry names the repo). The gate harness has one implementation of a step's verdict — its own exit status — after the two masks in it are gone, and one implementation of the tree's cleanliness — the artifact assertion — after the git status assertion proved blind to a gitignored file.
  • R3 — no workaround. No sleep, no retry, no suppression, no fallback. The missing linter is not worked around: the gate now FAILS LOUDLY when the pinned linter is absent, and the mask that let "command not found" read as a pass is removed. The forbidden artifact is not hidden: the gate asserts it is absent, and the finding behind it is filed with the command that writes it.
  • R4 — product before prose. The product changes first — the binary's compiled-in behaviour — and the prose sweep shows the only live surface that still described the old semantics is the comment this change corrects. The one guidance sentence in the org that this change makes stale lives in another repo and is FILED (layer-charly-check#53), not narrated.
  • R5 — legacy deleted. The claim-keyed grep above shows the superseded version survives only in the CHANGELOG of the earlier bump, and the new pin appears in exactly the three places it must.
  • R6 — user work preserved. git status was read before any action; the change lives in its own session worktree (.worktrees/pin904/charly) on feat/plugin-check-pin-904, one commit, no amend after the push, no force-push, no rebase, no history rewrite, no push to main, and no submodule edited. No other session's branch, worktree, file or PR was touched.
  • R7 — behaviour proven, and the proof can fail. The coverage FAILS without the change: the differential's before binary — a build of the base commit — prints a bare PASS (steps=6) and exits 0 on the same bed, which IS the defect; the after binary prints SKIP (steps=6, skipped_checks=1), names not-applicable-here, and exits 3. 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 real charly check run on a real disposable kind: local target. 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.
  • R8 — live or skip, never a mock. Both binaries are real builds of two real trees, and the bed is a real deployment: the records in the pasted log carry the driver's own path, size and mtime, and the run's own step timings. Nothing is faked or stubbed.
  • R9 — binary equals source. The binary is this worktree's own, built by scripts/bootstrap-charly.sh from the head's source; bin/charly version reports 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.
  • R10 — fresh disposable proof. The change-class gate ran on the committed tree this PR pushes — the module's test suite, the tools/* sweep, gofmt, the pinned golangci-lint v2.13.2 over the module and over every tools/* 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

  • Change class: a module pin carried into a product binary — charly/go.mod + charly/go.sum, plus a comment correction in charly/check_cmd.go that 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's charly check run produces.
  • Verification gate: this repo's own Go gate (go test ./charly/... and go vet ./..., the CI shape, from the repo root and from charly/), the tools/* sweep, gofmt, the pinned golangci-lint v2.13.2 over the module and over every tools/* 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 bare PASS as the negative control.
  • Attribution tier: 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 through charly check run) was executed live on a disposable target against both a base-commit build and this head's build.

Filed issues

Agent: charly
Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)

…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)*
@aitrawog

Copy link
Copy Markdown
Contributor Author

STATUS — the consumer leg of this issue is open: #1071 (head 482b84e9).

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 feat/plugin-check-pin-904: 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), and the one piece of prose the change contradicts is corrected in the same change (R4) — charly/check_cmd.go documented the exit-code contract as "2 = checks failed, 3 = prereq skip", and 3 now covers BOTH "the bed could not run" AND "the bed ran and proved nothing".

The producer legs this pins. opencharly/plugin-check#133 (squash 3d1e723, root tag v2026.298.0059, module tag candy/plugin-check/v0.2026298.59) — the three-state verdict, computed from the per-check statuses the child's own report states. opencharly/plugin-check#136 (squash bef966df, root tag v2026.298.0205, module tag candy/plugin-check/v0.2026298.205, closing #134) — the reader that extracts the per-check outcomes reads the child's OWN report, believes only a self-consistent one, and treats a report that says a declared check FAILED as a FAILURE.

The proof, on the committed tree. The change-class gate passed with zero warnings: the module's test suite, the tools/* sweep, gofmt, the pinned golangci-lint v2.13.2 over the module and every tools/* module, go vet, the R9 worktree-local build, the generated-file guard, the module-set assertion, the clean-tree assertion, the workspace-artifact assertion, and the pin-currency control. The behaviour itself is proven by a live differential: the SAME disposable kind: local bed, whose only declared check is authored skip: true, run through the binary built from origin/main and through the binary built from this head. The before binary printed a bare PASS (steps=6) and exited 0 — that IS the defect. The after binary printed SKIP (steps=6, skipped_checks=1), named not-applicable-here, and exited 3 (CheckSkippedExitCode). Nine assertions decide that, and the differential's own verdict is the last line of its log.

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 (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 TestWorkspaceHasOneMemberAndNoSumFile forbids. The gate's step now runs in the member's own mode (GOWORK=off) and asserts the artifact's absence explicitly, because git status cannot see a gitignored file; the residual — the control's premise about the toolchain — is a decision on the #1065/#1069 line, so it is filed with its RCA and the writer probe. Two findings in other repos are filed on the same footing: opencharly/layer-charly-check#53 (the check skill's stated reason for the uppercase-token trap is stale after this change) and opencharly/plugin-check#135 / #137.

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 charly and plugin-check follows, with charly task verify as the gate.

Next update when the validator's verdict lands on #1071, and when the umbrella leg opens — whichever comes first.

@github-actions

Copy link
Copy Markdown
Contributor

Review — PASS

Verification of the diff against the body

I re-derived every claim from the current state.

File count / line counts. Body says "3 files changed, 9 insertions(+), 6 deletions(-)." The three ### FILE: diffs are check_cmd.go (+6/−3), go.mod (+1/−1), go.sum (+2/−2) → 9 insertions, 6 deletions. Exact match. The pasted git diff in the body is byte-for-byte identical to the three provided file diffs (check_cmd.go hunk, the go.mod require line, and the two go.sum line swaps). No contradiction.

go.mod / go.sum coherence. The bump is exactly one module: plugin-check v0.2026295.2128 → v0.2026298.205. The old h1: and /go.mod pair is removed and the new pair added; no other go.sum line moves. Critically, the /go.mod hash is identical on both versions (1IWFD+Sjy/R3PU+XCCPQXQyNer1N43pNLHJxlkDXJZU=), which independently confirms the body's claim that the two versions' go.mod requirements are identical — i.e., no transitive sdk/spec movement, so no other go.sum churn is expected. This is an internal consistency check that passes.

Comment correction (R4). charly/check_cmd.go rewrites "3 = prereq skip" to "3 = not a verdict" and explains that exit 3 now covers both "the bed could not run" and "the bed ran and proved nothing." This matches the described three-state plugin behavior (headline SKIP, CheckSkippedExitCode = 3, record gains status:/skipped_checks:). The change is a comment only; no behavioral risk in this repo's Go code.

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 go.sum.

Evidence

The body ships a full change-class gate log (test suite ok … 180s, tools sweep, gofmt, pinned golangci-lint over module and every tools/* module with 0 issues, vet, R9 worktree build at 2026.298.0245, generated-file guard, module-set assertion, artifact assertions go.work.sum/go.work.dev.sum absent, pin-currency control) plus a live differential with a negative control: the same kind: local bed through the base-commit binary prints a bare PASS (steps=6) and exits 0 (the defect), while this head's binary prints SKIP (steps=6, skipped_checks=1), names not-applicable-here, and exits 3. The after record adds status: SKIP, skipped_checks: 1, per-step status:, and the checks: block. This is coverage that fails without the change, which is the R7 property that matters here.

The self-disclosed gate-harness findings (missing linter masked by a pipeline; a 14-zero string verdict; the go list -m all writer of go.work.sum) are about how the gate was run, not committed code, and are resolved in the harness and/or filed as #1070. They do not affect the product diff.

Comment disposition

The single comment (6392725138, @aitrawog, [issue]) is a status update: it restates the same head SHA (482b84e9), the same 3-file bump, the same differential result (before PASS/0, after SKIP/3), and the same filed issues (#1070, layer-charly-check#53, plugin-check#135/#137). Every claim in it matches the current body and diff — no stale assertions, no contradiction. It does not claim approval, and no designated maintainers are configured for this run, so it is not a sign-off; the change is assessed solely on its own evidence, which stands.

Non-blocking observations (noted, not gated)

  • No committed regression test in this repo. The behavioral proof is a one-off live differential, not CI coverage; a future downgrade of the pin would not be caught by charly/... tests. This is disclosed and partly filed (feat(core)!: K3 ready-now opener — dissolve alias residue + calamares_types + dead code #137 covers the report-FAIL branch), and a committed test of a compiled-in module's bed-runner verdict is impractical in this repo. Disclosed, so not a block.
  • Run-level ok: true persists on a SKIP. The after record keeps ok: true on every step (including the skipped one) and adds status: SKIP. The body frames ok: as "NO FAILURE," not "PASS," so a naive reader of ok: alone still sees green; the disambiguating signal is the new status:/skipped_checks: fields and the CLI headline/exit code. This is a disclosed producer-side API decision (not in this repo's diff), so it is not a block on this PR — but a consumer that reads only ok: would still be misled, and that reader-side change belongs with the umbrella/R10 machinery.

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.

View action run

@aitrawog

Copy link
Copy Markdown
Contributor Author

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 32c5972, tag v2026.298.0310), so nothing here can be fixed inside it. R1 does not allow a finding raised on a PR to be filed away: it is fixed, or rebutted on the thread with evidence, and a genuinely separable residual is filed in the owning repo. That is what follows.


Observation 1 — "No committed regression test in this repo … a future downgrade of the pin would not be caught by charly/... tests."

The behavioral proof is a one-off live differential, not CI coverage; a future downgrade of the pin would not be caught by charly/... tests. This is disclosed and partly filed (#137 covers the report-FAIL branch), and a committed test of a compiled-in module's bed-runner verdict is impractical in this repo. Disclosed, so not a block.

REBUTTED on the placement ground (R2) for the "no test in this repo" half. The observation is right that there is no committed test in this repo, and the reason is stronger than "impractical": a test in this repo could not distinguish the two versions even in principle, because the surface this repo compiles against is identical.

$ OLD=.../plugin-check@v0.2026295.2128; NEW=.../plugin-check@v0.2026298.205
$ (cd $OLD && go doc -all . > api-old.txt); (cd $NEW && go doc -all . > api-new.txt)
$ wc -l api-old.txt api-new.txt
     999 api-old.txt
     999 api-new.txt
$ diff api-old.txt api-new.txt; echo "API_DIFF_RC=$?"
API_DIFF_RC=0

The full exported API is byte-identical across the two versions. The new behaviour is entirely unexported: bed_status.go exists only at the new version, and every symbol in it is lowercase (checkOutcome, checkReport, stepVerdictFromReport, runStatus, runSkippedChecks, …). Nothing this repo imports changed shape, so nothing this repo could assert on differs.

The coverage therefore lives in the module that owns the verdict, and it is real:

$ grep -c "^func Test" $NEW/bed_status_test.go
20
$ ls $OLD/bed_status_test.go
ls: cannot access '.../plugin-check@v0.2026295.2128/bed_status_test.go': No such file or directory

Twenty verdict tests at the adopted version; that file does not exist at all at the version it replaced. No test in this repo reads a bed record, and this repo is the host, not the owner: this PR's own body states the boundary — "plugin-check is COMPILED INTO the charly binary, so the pinned module version IS the behaviour of charly check run" — so the version lives here and the behaviour (and its coverage) lives in the module.

The downgrade half is real, and it is FILED: #1072. The observation disclosed it and did not file it. The RCA, with its measurements, is on that issue; in short: plugin-check is compiled into the binary, so the pin is behaviour, and while a gitlink pin is audited from the index by the pin contract, a module pin is audited by nothing. The org's own verb:module-pins (mode: check) is the capable control and it is dormant — no repo in the tree runs it — and it asserts equality with a source go.mod, not a floor, so even a coordinated downgrade would pass it. The one literal version assertion in the org (check-wave-pins, charly.yml:4998-5065) is referenced by no plan and its expectations are a wave stale — that bed's unusability is already #958, and the two are cross-referenced.


Observation 2 — "Run-level ok: true persists on a SKIP … a consumer that reads only ok: would still be misled, and that reader-side change belongs with the umbrella/R10 machinery."

The after record keeps ok: true on every step (including the skipped one) and adds status: SKIP. The body frames ok: as "NO FAILURE," not "PASS," so a naive reader of ok: alone still sees green …

REBUTTED for the umbrella half — the umbrella's R10 machinery is not the misled reader; it is the reference implementation. It classifies by exit code, with ok: report-only:

$ grep -n "classify_exit" -A 9 scripts/check-bed-watch.sh
classify_exit() {
  case "${1:-}" in
    0) printf 'pass' ;;
    3) printf 'skip' ;;
    124) printf 'timeout' ;;
    *) printf 'fail' ;;
  esac
}

Its own header says it: "summary.yml's ok: alone does NOT distinguish a pass from a prereq skip", and its line 40 says the authoritative signal is rc. The org's own beds-and-r10 guidance says the same thing from the writer's side — "never count a bed as passed from summary.yml alone" and "capture ${PIPESTATUS[0]} (the charly exit) and corroborate the on-disk summary.yml". Both are in place today, before this change. So the reader-side change this observation asks for is already made where the observation says it belongs.

FILED for the reader half — because the org does have real ok:-only readers, and they are now measured, named, and located. The observation says the change "belongs with the umbrella/R10 machinery"; the measurement says the readers that would be misled are elsewhere, and there are three distinct ones:


Why the merged body is not edited

CHANGELOG/2026.298.0310.md was created from this PR's body at merge time, and the org's tag-on-merge creates that file only if it does not exist — so the committed changelog is a frozen copy of the pre-edit body. Editing a merged body would make the PR body and the repository's own changelog disagree: a doc divergence in the org's history, produced by the act of recording the disposition. No org workflow compares the two, and no validator re-runs on a merged PR.

The disposition therefore lives where a post-merge finding belongs: on this thread, and on the issues. The body's ## Filed issues stays as the honest record of what the change itself filed at merge time — #1070, opencharly/layer-charly-check#53, opencharly/plugin-check#135, opencharly/plugin-check#137 — and the three residuals this disposition files are recorded here.

The complete set of issues this work now carries:

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant