Skip to content

docs(refs)!: the container-nesting recipe drops the retired builder selector - #205

Merged
github-actions[bot] merged 1 commit into
mainfrom
feat/lcn-projection-204
Oct 29, 2026
Merged

github-actions[bot] merged 1 commit into
mainfrom
feat/lcn-projection-204

Conversation

@aitrawog

Copy link
Copy Markdown
Contributor

Closes #204

Summary

Head d42a49a2434f0dd6a2ab67bf1b75a6f9ee7b2d2e — 2 files changed, 2 insertions(+), 2 deletions(-).

The defect. This repo's recipe page for the container-nesting candy is a projection of the corpus: its Env row carried CHARLY_BUILD_ENGINE=podman — the retired builder selector. The contract retired it: EngineConfig.Build, ResolvedRuntime.BuildEngine and the CHARLY_BUILD_ENGINE key are gone from spec/hostenv, and a leftover engine.build: docker is INERT (spec/hostenv/retired_build_selector_test.go). The site therefore taught every reader a knob that cannot be turned.

The RCA.

  • Mechanism — the site is a projection: the deploy workflow regenerates the generated half of the content tree from three pinned inputs — the corpus (marketplace gitlink), the charly source commit, and the umbrella commit — and the recipe pages are projected from the corpus. The owner had already cut the name out of the candy, the skill body and the README in one cutover (the retired CHARLY_BUILD_ENGINE builder selector is still set and still published layer-container-nesting#16, tag v2026.302.0925), and the corpus republished the owner's corrected body (chore(refs)!: the container-nesting body is published without the retired builder selector marketplace#601, squash b53310d, tag v2026.302.0951) — but this repo's marketplace gitlink still pointed at the corpus commit from before that republication, so every regeneration republished the old row.
  • Missed control — the drift gate proves FIDELITY to the pinned inputs: the pinned sources regenerate their own old pages perfectly, so a stale corpus pin is invisible to it. That blind spot is the corpus pin's alone — the workflow already reports the currency of all three pins (the check_pin_currency.sh step, "Reported, never failed"), because a pin that is behind is an advance available NOW and the remedy is a regeneration PR, not a gate.
  • Blast radius — every reader of the site's container-nesting recipe: the page taught a retired selector that the runtime cannot honour.
  • Root fix — advance the one corpus gitlink to the corpus's tip and regenerate in the same commit. The site may not hand-edit a projection (it is generated output), and the corpus may not edit the site.

What lands.

file what changes
marketplace the corpus gitlink: 197f42a57ab88fcaed6a044bf312d753a72062b2 → 3cf744b9ef3dcaf5e3dadff01648db087385ef19, the corpus's default-branch tip after opencharly/marketplace#601
src/content/docs/recipes/distros/container-nesting.md the projected page: the Env row loses the retired CHARLY_BUILD_ENGINE=podman and keeps the three live contracts

The claim. The retired name is gone from this repo's tree — the claim-keyed grep over the published tree returns nothing on this commit and exactly one hit before it. The evidence is below.

How tested

Everything below ran on the committed tree this PR pushes.

1. The tree.

$ git rev-parse HEAD
d42a49a2434f0dd6a2ab67bf1b75a6f9ee7b2d2e
$ git rev-parse HEAD^{tree}
2f696797bb6af19689da5ebad77b8190d538803c
$ git diff --stat origin/main...HEAD
 marketplace                                           | 2 +-
 src/content/docs/recipes/distros/container-nesting.md | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
$ git status --porcelain

git status --porcelain printed nothing — the tree is clean, so the evidence below belongs to this head.

2. R10 — the regeneration is a no-op on the committed tree. This repo's deploy workflow FAILS on any drift between the committed content and what its pinned inputs generate, so the committed tree reproducing itself byte-for-byte is the gate. The generation ran exactly as the workflow runs it — the workflow's own recorded pins (CHARLY_PIN=be7a505f476c1a5b0302d878e0b5686a2cde3e07, UMBRELLA_PIN=1c88fcf15f8fb9dd8b2814210472a63d3a79d0e2), the pinned plugin-docs CLI at v2026.289.0814, the wiring candy built from .github/docs-corpus/pins.tsv by the workflow's own build_wiring.py, and the corpus checkout at this repo's recorded gitlink.

$ python3 -I .github/docs-corpus/build_wiring.py .github/docs-corpus/pins.tsv /tmp/docs204/charly/candy/docs-corpus/charly.yml
WIRING_RC=0
$ /tmp/docs204/plugin-docs-bin generate \
    --root /tmp/docs204/charly \
    --narrative /tmp/docs204/umbrella \
    --plugins /home/atrawog/Sync/Atrapub/a/claude/opencharly/.worktrees/docs-204/docs/marketplace \
    --out /home/atrawog/Sync/Atrapub/a/claude/opencharly/.worktrees/docs-204/docs/src/content/docs
charly docs: 395 recipe pages, 196 plugin pages (205 provider words), 79 cli pages, 834 candy pages, 216 box pages (1726 generated pages cleared before regeneration)
GENERATE_RC=0
$ bash .github/docs-corpus/check_drift.sh
content: committed tree matches the pinned inputs (regeneration is a no-op).
DRIFT_RC=0
$ git status --porcelain

The generator rewrote all 1726 generated pages and the tree came back byte-for-byte identical — the gate that fails this repo's deploy on any difference is green on this commit.

3. The pre-flight — the recipe is faithful BEFORE the pin moved. The same generation, run against the corpus at the PREVIOUS gitlink (197f42a), was a byte-for-byte no-op on the then-committed tree, and the bump to 3cf744b changed exactly one page. So the diff is attributable to the pin advance alone, and the inputs are the workflow's own.

$ git -C marketplace diff --name-status 197f42a57ab88fcaed6a044bf312d753a72062b2 3cf744b
A	CHANGELOG/2026.302.0951.md
M	candy/charly-marketplace/charly.yml
M	distros/skills/container-nesting/SKILL.md
$ git diff --stat origin/main...HEAD -- src/content/docs
 src/content/docs/recipes/distros/container-nesting.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

The corpus changed one projected body between those two commits, and the site changed exactly one page by exactly one line:

$ git diff -- src/content/docs/recipes/distros/container-nesting.md
-| Env | `CHARLY_BUILD_ENGINE=podman`, `CHARLY_RUN_ENGINE=podman`, `_CONTAINERS_USERNS_CONFIGURED=""`, `BUILDAH_ISOLATION=chroot` |
+| Env | `CHARLY_RUN_ENGINE=podman`, `_CONTAINERS_USERNS_CONFIGURED=""`, `BUILDAH_ISOLATION=chroot` |

4. The repo's own checks — the checks this repo's deploy workflow runs, each suite's own final line and its own exit status:

$ python3 .github/docs-corpus/test_check_drift.py
Ran 6 tests in 0.047s

OK
RC_test_check_drift=0
$ python3 .github/scripts/check_step_timeouts.py
check-step-timeouts: OK (1 workflow file(s) asserted; every job and every step carries a deadline)
RC_check_step_timeouts=0
$ python3 .github/scripts/check_step_timeouts.py --self-test
check-step-timeouts self-test: OK (unbounded steps, jobs and traps are all caught, and the org-distributed copy is reported)
RC_check_step_timeouts_self_test=0
$ python3 .github/scripts/test_check_step_timeouts.py
test_check_step_timeouts: OK (5 mutants; every one of them turns the self-test red)
RC_test_check_step_timeouts=0
$ bash .github/scripts/test_check_pin_currency.sh
  PASS  the report names all three pinned inputs — one line each
  PASS  the corpus pin is reported
  PASS  the charly pin is reported
  PASS  the umbrella pin is reported
test_check_pin_currency: OK (every state is reported, and none of them fails)
RC_test_check_pin_currency=0
$ python3 .github/scripts/check_dependency_advisories.py
check-dependency-advisories: OK (no known advisory in the production tree or the dev tree)
RC_check_dependency_advisories=0

5. The site builds from the committed tree — the workflow's own build steps, on the node this repo pins (.node-version = 22.23.4):

$ node --version
v22.23.4
$ npm ci --no-audit --no-fund
NPM_CI_RC=0
$ npm run build
BUILD_RC=0
$ test -f dist/index.html
PAGES_BUILT=1749
SITE_OK=1

6. R5 — the retired vocabulary, before and after. The sweep, over this repo's tree, and every remaining hit classified rather than left to a reader:

$ git grep -c "CHARLY_BUILD_ENGINE" origin/main -- .
origin/main:src/content/docs/recipes/distros/container-nesting.md:1
$ git grep -c "CHARLY_BUILD_ENGINE" HEAD -- .
grep exit=1

The tree's single hit before this commit was the live claim this PR removes — the recipe page's Env row. This repo's CHANGELOG/ carries none of its own: the site's history never published the retired name in a changelog entry.

Rulebook compliance

  • R0 — skills first. /charly-internals:git-workflow, /charly-internals:strict-policy, /charly-internals:root-cause-analyzer, /charly-internals:cutover-policy, /charly-internals:skills, /charly-internals:marketplace and /charly-check:check were loaded before the first source read and before the first git action, and this repo's own AGENTS.md was read before acting in it.
  • R1 — RCA every anomaly. Two anomalies, each with a root cause. (a) The stale projection of the retired knob — its RCA (mechanism, missed control, blast radius, root fix) is in the Summary above and in the commit message; it was already filed by the upstream legs as the retired CHARLY_BUILD_ENGINE builder selector is still set and still published layer-container-nesting#16 and no committed control keeps a RETIRED variable name out of the org's trees — the sweep is manual, per session, per repo opencharly#799. (b) The first local pre-flight came back RED (DRIFT_RC=1, ~130 changed pages) before this commit existed: the mechanism was that pre-flight's own charly and umbrella pins — carried over from an earlier read — had been overtaken by the pins this repo's workflow records (CHARLY_PIN=be7a505, UMBRELLA_PIN=1c88fcf), so the regeneration resolved older sources than the committed pages; the missed control was reading the pins from a remembered copy instead of from the workflow's env: block at run time; the blast radius was local and left no committed trace — nothing was committed from that run; the root fix was to run the pre-flight against the workflow's recorded pins, which is what block 2 above does, and it is a no-op. No repo defect was found behind it: the workflow records its pins once and consumes them in one place, so there is no stale copy in the tree to fix. No new finding was raised in this leg: the regeneration's diff was exactly the two files this PR names, with no untracked page and no other recipe moved. Every gate above ran with zero warnings.
  • R2 — no duplication. The site keeps exactly one source for a published page: the corpus's body, projected. This PR does not add a copy of anything, and it does not hand-edit the projection — the page is regenerated from the corpus.
  • R3 — no workaround. Nothing is shimmed, aliased or hand-patched: the retired selector is not accepted alongside a live one, and the published page is the corpus's bytes, not an edit.
  • R4 — the product first, then the prose. The fix is in the input that generates the page (the corpus pin), not in the generated file; the page that carries the retired name is regenerated, not rewritten.
  • R5 — legacy deleted completely. The retired name is gone from this repo's tree (block 6 above); the site's CHANGELOG/ carries no occurrence of it, so nothing survives there either.
  • R6 — Git safety. git status was stated before every action; this PR is one commit on feat/lcn-projection-204 off fresh origin/main; no force-push, no amend after the push, no direct push to main, no hook bypass, and no file outside this repo touched.
  • R7 — prove behaviour, not compilation. The evidence is the regeneration no-op plus the one-line diff attributable to the one corpus commit that moved one projected body, so a hand-edited page fails the drift gate.
  • R8 — live or skip. No service is mocked: the generator resolves every pinned repo from the live remotes, and the site is built from the committed tree with the pinned toolchain.
  • R9 — binary equals source. The generator is the standalone plugin-docs CLI built from the pinned source tag v2026.289.0814 (the same build the workflow performs), and the site is built with the node this repo pins. No new runtime OS dependency is introduced: this change is a gitlink plus a generated page, so packaging/ is untouched.
  • R10 — fresh disposable proof. The change class is the site's own generation, so the gate is the regeneration itself, run on the committed tree, plus the drift gate, the repo's own check suites, and the site build — the workflow's own steps, all green, with the output retained above. This repo publishes the site through its deploy workflow and declares no disposable: true deployment of its own, so there is no disposable target to realize.

Cutover. One cutover, one PR: the site's corpus pin, from the commit before the corpus republished the owner's corrected body to the commit that carries it, with the regenerated page in the same change. The upstream legs — the owner's own cutover (opencharly/layer-container-nesting#17) and the corpus republication (opencharly/marketplace#601) — have both landed; this repo's pin is the last step of that chain.

Landing. PR-only; the org's charly/pr-validator is the merge gate and arms native auto-merge on PASS. No agent merges this PR.

Change classification

Filed issues

Agent: charly · session ses_8efb271d-f2c0-47d3-85e3-2a1e00b11ae0
Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)

…elector

The site's recipe page is a projection of the corpus: the page's `Env` row
carried the retired `CHARLY_BUILD_ENGINE=podman`, the builder selector the
contract retired (`EngineConfig.Build`, `ResolvedRuntime.BuildEngine` and the
`CHARLY_BUILD_ENGINE` key are gone from `spec/hostenv`).

The producer cut the name out of the candy, the skill body and the README in
one cutover (opencharly/layer-container-nesting#16, tag v2026.302.0925); the
corpus republished the owner's corrected body (opencharly/marketplace#601,
squash b53310d, tag v2026.302.0951). This change advances the corpus gitlink
to the corpus's default-branch tip 3cf744b and regenerates the site with the
deploy workflow's own recorded inputs, so the recipe page loses the retired
selector and keeps the three live contracts.

The regeneration is a byte-for-byte no-op on every other page: the corpus
changed exactly one projected body, and the site's diff is exactly one line.

Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)
@github-actions

Copy link
Copy Markdown

Review — PASS

What the diff actually does

Two files, matching the body's stated counts exactly (2 files, +2/−2):

  1. marketplace gitlink — advanced from 197f42a57ab88fcaed6a044bf312d753a72062b2 to 3cf744b9ef3dcaf5e3dadff01648db087385ef19. The SHAs in the diff match the SHAs quoted in the body's table and "How tested" block 3 (git -C marketplace diff --name-status 197f42a… 3cf744b) character-for-character.
  2. src/content/docs/recipes/distros/container-nesting.md — the Env row drops the leading CHARLY_BUILD_ENGINE=podman entry and leaves CHARLY_RUN_ENGINE=podman, _CONTAINERS_USERNS_CONFIGURED="", BUILDAH_ISOLATION=chroot. Clean comma-separated removal with no dangling separator, and the body's "keeps the three live contracts" claim is reproduced exactly (three items remain).

Consistency and honesty checks

  • File/insertion/deletion counts in the body's Summary, the "What lands" table, and the git diff --stat excerpt all agree with the per-file headers and the actual diff.
  • The before/after grep claim in block 6 (origin/main = 1 hit in this exact page; HEAD = exit 1) is consistent with the diff: the only tree-level occurrence removed is this page's row.
  • The page is a projection (generated output); the change is presented as a regeneration from the corpus pin bump, not a hand-edit. That is the correct layering (fix the input, regenerate the output), and it is self-consistent with the two-file shape of the diff.
  • No secrets, no injection surface, no dependency or privilege change (gitlink + one generated line). No dead code or duplicated source introduced; the legacy name is fully deleted from the changed tree.
  • Comments: none in the thread — nothing to disposition.

Residual (non-blocking) note

The central justification — that CHARLY_BUILD_ENGINE is a retired selector and that 3cf744b is the corpus tip carrying the corrected body — rests on upstream artifacts (spec/hostenv/retired_build_selector_test.go, layer-container-nesting#16/#17, marketplace#601) that live outside this repo and cannot be re-derived from the diff alone. The diff is nonetheless internally consistent with those claims, is minimal and coherent, and the drift gate described would catch any divergence between the page and its pinned corpus. This is a verification gap I cannot close read-only, not a defect visible in the change; it does not rise to a block.

The change does what the body says, the evidence is internally consistent, and the diff is clean and in scope.

Verdict: PASS


ollama-cloud/deepseek-v4.1-flash — action-review.

View action run

@github-actions
github-actions Bot enabled auto-merge (squash) October 29, 2026 10:29
@github-actions
github-actions Bot merged commit a4675b5 into main Oct 29, 2026
4 checks passed
charly-auto-merge Bot added a commit that referenced this pull request Oct 29, 2026
github-actions Bot pushed a commit to opencharly/opencharly that referenced this pull request Oct 29, 2026
…cutover (#801)

Three leaf pins advance to the commits their owning repos' merged mains
already serve. The scope is derived, not written: the product's own currency
report names the pins that are not current, and the wave advances exactly
those. No pin was hand-picked, and no pin was hand-pinned.

| pin | recorded | served | producer |
|---|---|---|---|
| layer-container-nesting | 7d31a504a8 | 3946ee1923 | opencharly/layer-container-nesting#17 (squash 50da7774, tag v2026.302.0925) |
| marketplace | 197f42a57a | 3cf744b9ef | opencharly/marketplace#601 (squash b53310d, tag v2026.302.0951) |
| docs | 84595508dd | a99557f1e6 | opencharly/docs#205 (squash a4675b56, tag v2026.302.1030) |

The retired CHARLY_BUILD_ENGINE builder selector is gone from the spec's
hostenv contract; each of these three repos still named the commits from
before its producer leg landed, so each carried the retired selector one
step further than the repo that had already fixed it.

RCA

Mechanism — the umbrella records every org repo as a gitlink; the pin is the
INDEX entry and the checkout follows it. Nothing advanced these three pins
after their producer legs landed, so the umbrella's view of those repos stayed
on the pre-cutover commits while the repos themselves had moved on.

Missed control — the currency audit (charly task pins --param MODE=currency)
REPORTS a behind pin and does not fail, because "behind" is an advance
available NOW rather than a violation: the remedy is a pin-wave PR, not a
gate. So nothing turned red, and the pins stayed behind until a wave ran.

Blast radius — every consumer of the umbrella's view: the harness skill
bindings in .dsh/skills (symlinks into the corpus checkout), the org map, and
every repo that reads the umbrella as its source of pins.

Root fix — advance the leaf pins in the index with the sanctioned wave
(charly task sync), materialize the checkouts at the new pins (charly task
init), and judge the committed tree (charly task verify). No hand-pin, no
branch checkout.

The wave's order: sync (the pins move in the index; the checkouts that exist
are rolled) -> init (those checkouts are refreshed to the pins the index
records) -> verify (every pin judged from the index, on the committed tree).

The wave's scope was exactly these three leaves. The verb's own report is the
scope control: it rolled 7 leaf submodule(s) to their default-branch HEAD and
advanced 396 leaf pin(s) in the index, and the 6 pin_map edges moved 0,
because the edge half follows charly's own box/* gitlinks and the charly leaf
is already current.

A caveat this commit states rather than hides: for each producer the newest
tag points at the MERGE commit, and the served head the umbrella pins is
exactly ONE commit above that tag, and that one commit adds only
CHANGELOG/<version>.md. So these pins are MERGED and their tags exist, but the
served head is not itself a tagged commit. The measurement is on
#793.

Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)
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.

recipes/distros/container-nesting.md still publishes the RETIRED CHARLY_BUILD_ENGINE in the candy's Env row

1 participant