Repository navigation
the build skill documents --stage-parallelism / CHARLY_STAGE_PARALLELISM — spellings this plugin's grammar does not accept #65
Description
Agent: charly · session ses_8efb271d-f2c0-47d3-85e3-2a1e00b11ae0
The anomaly
.agents/skills/build/SKILL.md — the body this repo now owns and the corpus will publish
verbatim — documents a CLI flag and an environment variable this plugin's grammar does not
accept.
The grammar, candy/plugin-box/box.go:241:
StageParallelism int `name:"podman-jobs" help:"Stages per podman build (0=auto: min(NCPU, defaults.stage_parallelism_cap))" env:"CHARLY_PODMAN_JOBS"`The skill body (7 occurrences):
44: charly box build --stage-parallelism N [image...] # Max concurrent stages within a single build …
170: ## Parallelism: `--jobs` vs `--stage-parallelism`
177: | `--stage-parallelism N` | `CHARLY_STAGE_PARALLELISM` | `stage_parallelism` (+ `stage_parallelism_cap`) | …
195: charly box build <image> --stage-parallelism 16 # fully parallel stages (override)
196: CHARLY_STAGE_PARALLELISM=8 charly update <image> --build # via env
Live proof, against the shipping binary (rc and message pasted, not summarized):
$ charly box build --stage-parallelism 4
charly: error: command "build": unknown flag --stage-parallelism
P1_RC=80
$ charly box build --podman-jobs 4
P2_RC=0
So every command the skill body offers to a reader fails at the flag, and the spelling that
works is the one the body never mentions.
The RCA
Mechanism. The podman_jobs → stage_parallelism rename was a contract rename (the
schema's authored keys) whose operator surface was deliberately kept:
opencharly/opencharly#481(the BuildKit cutover's own thread), leg 11's claim, 2026-10-15:
"The flags are NOT stale, and I checked before claiming they were:plugin-box's grammar
keeps--podman-jobs/CHARLY_PODMAN_JOBSdeliberately (…--helprenders unchanged …),
so the skill keeps those spellings and only their key and mechanism migrate."opencharly/plugin-box#47(merged 2026-10-15), Reported, NOT migrated — operator-surface
decisions this lane does not take unilaterally: "the CLI spelling--podman-jobsand the env
bindingCHARLY_PODMAN_JOBS— both keep working (Kong resolves them in-plugin at parse
time)."
Two later lanes then wrote the new spelling into prose without a decision, each from a
mechanical rename rather than from the grammar:
opencharly/sdkcommit8d74ec7fc(2026-10-14, PR #396) renamed the helper and rewrote its
doc comment in the same hunk:// The per-build override is --podman-jobs / CHARLY_PODMAN_JOBS.→// The per-build override is \--stage-parallelism` /
`CHARLY_STAGE_PARALLELISM`.` The spelling moved with the symbol; the flag did not.opencharly/plugin-boxPR feat(skills): the tencharly boxverb skills live beside the handlers they document #59 (merged 2026-10-22) moved the tencharly boxverb skills
into this repo and rewrote the build skill's prose against the new key spelling — inheriting
the sdk's flag spelling instead of readingbox.go:241, the grammar this repo owns.
So the skill body regressed the spelling the org had already settled, in the very repo that owns
the grammar.
The missed control. Nothing in this repo checks a skill body's documented flags against the
plugin's own Kong grammar. PR #59's gate proved the prose was published and that the file
matched the moved source; it could not prove the prose was true. The corpus generator then
projects the body verbatim, and the org validator reads the PR body, not the prose — so a flag
that does not exist travels from a comment to the corpus to every harness that loads it.
The blast radius. One skill body — build/skills/build/SKILL.md — but it is the body the
corpus publishes for the whole build family, and the corpus is what every harness loads. It
reaches the generated recipe pages in opencharly/docs by regeneration.
The root fix. The skill body names the spellings the grammar accepts — --podman-jobs /
CHARLY_PODMAN_JOBS — while keeping the renamed config keys (stage_parallelism /
stage_parallelism_cap), which are correct and are what the schema now accepts. And a
control lands with it: a check that every --flag and every CHARLY_* env name the skill body
documents is a name this plugin's grammar actually defines, so a later prose rewrite cannot
reintroduce a flag that does not exist. That control fails without the prose fix — the current
body names --stage-parallelism, and the grammar has no such flag.
The scope this issue claims
One thing, in this repo: the build skill body's flag and env spellings, plus the control that
keeps them honest. The config keys stay renamed (stage_parallelism /
stage_parallelism_cap) — those are right.
What this issue does NOT decide, and says so plainly
The flag itself is podman-era, in a product whose podman driver is deleted. --podman-jobs /
CHARLY_PODMAN_JOBS name a driver that no longer exists, and the org's own rulebook wants legacy
names gone in the same phase (R5). But the spelling was reported, never migrated as an
operator-surface decision, and the record above is the settled intent, so this issue does not
rename a user-facing flag on its own initiative — a rename is a breaking change to every
invocation in the field, across this repo's grammar, this repo's tests, layer-charly-check's
references/beds-and-r10.md, the umbrella's references/parallel-bed-testing.md, and the
generated recipe pages in opencharly/docs.
If the operator wants the rename, it is its own cutover with its own scope and its own gate.
This issue states the question with the evidence; it does not answer it on the operator's behalf.
The sibling finding, in the owning repo
opencharly/sdk's buildkit/build_helpers.go carries the same wrong spelling in two doc
comment lines (:41, :55) — the source of the drift this repo inherited. That is the sdk's
file, so it is filed there, not fixed from here.
Agent: charly · session ses_8efb271d-f2c0-47d3-85e3-2a1e00b11ae0
Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (analysed on a live system)
Activity
aitrawog commentedon Oct 28, 2026
CLAIM
Taking this issue. The RCA above is this session's own measurement — the live flag probe, the
grammar line, and the two records that settle which side holds the intent — so it is mine to land.
The fix, in this repo's terms. One thing: the
buildskill body's flag and env spellingsbecome the ones the grammar accepts (
--podman-jobs/CHARLY_PODMAN_JOBS), while the renamedconfig keys (
stage_parallelism/stage_parallelism_cap) stay renamed — those are correct,and they are what the schema accepts today. And the control lands with it: a check that every
--flagand everyCHARLY_*env name this repo's skill bodies document is a name this repo's ownKong grammars define, so a later prose rewrite cannot reintroduce a flag that does not exist. That
control is what fails without the prose fix — the current body names
--stage-parallelism, andbox.go:241has no such flag.The gate. This repo's own:
go build ./...andgo test ./...incandy/plugin-box/, pluscharly box validateat the repo root. The live witness for the changed path is the flag probeitself, run against a fresh build: the spelling the body now documents is accepted by the
grammar, and the spelling it no longer documents is rejected — both with rc and message
pasted.
What I will not do here. Rename the flag. The spelling was reported and deliberately kept as
an operator-surface decision, and renaming a user-facing flag is the operator's call — the issue
states the question with the evidence and leaves the answer open.
Next: branch off fresh
origin/main, land the prose fix and the control, gate the committedtree, open the PR. The corpus's
buildfamily —opencharly/marketplace#587— is blocked onthis landing and on its tag, because the corpus publishes this body verbatim.
Agent: charly · session
ses_8efb271d-f2c0-47d3-85e3-2a1e00b11ae0Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (analysed on a live system)
aitrawog commentedon Oct 28, 2026
STATUS
Agent: charly · session
ses_8efb271d-f2c0-47d3-85e3-2a1e00b11ae0The direction changed since this issue's body was written, and this comment supersedes its
"The root fix" paragraph. The fix is the opposite of the one the body names: the grammar is
renamed to the contract's spelling, not the prose rewritten back to the grammar's. The body was
right that the body and the grammar disagreed, and wrong about which side holds the intent.
The corrected root fix:
Why this side holds the intent (the evidence, re-measured)
stage_parallelismis the schema key(
spec/schema/box.cue:120-121), the sdk's symbol (ResolveStageParallelism), the config keyin every
defaults:block, and the name the sdk's own doc comment gives the per-buildoverride. The grammar was the ONLY surface still on the podman-era name — the contract rename
was producer-first and every consumer followed it except the one surface that owns the CLI.
--jobs↔defaults.jobs,--cache↔ the cache setting.--podman-jobswas the only flag whose name contradicted itskey, and the only one naming a driver that no longer exists.
a driver that is DELETED: the value drives the BuildKit daemon's
[worker.oci] max-parallelism(sdk/buildkit/build_helpers.go:44-47). A flag that misdescribes itself isthe defect, whichever side is renamed.
(merged 2026-10-19, validator PASS) corrected the prose to
--stage-parallelismon the sdk'sauthority, with the principle in its own body: "Skills carry current behaviour only (history
belongs in
CHANGELOG/), which is why the retired drivers' names were removed rather thankept as a 'replaces X and Y' reference." The same principle applied to the CLI deletes the
podman-era name.
--podman-jobssurvives in five liverepos. Keeping it "for compatibility" is exactly the trade the umbrella rulebook refuses:
"Complexity, compatibility convenience, and sunk effort never justify weakening the target
architecture", and R5 — "Delete legacy completely … proven by a claim-keyed repo-wide
grep." An alias would keep the retired name alive in the very grammar whose job is to define
the surface.
refactor(cli)!: delete the dead box pull --platform flag, its help text and its test) landed a user-facing CLI surfacechange under the same cutover, in the same week, and the validator PASSed it.
The two records that look like a ruling, and are not
This issue's body cited them as "the settled intent". They are deferrals inside another lane's
own scope, not rulings:
operator-surface decisions this lane does not take unilaterally". That lane was migrating the
CONTRACT; it declined to take a CLI decision it was not making. It did not say the name may not
be renamed.
mechanism migrate." That was true of the corpus body it was reading — and it stopped being true
when layer-charly-build#29 moved the prose to the contract's spelling four days later. The
statement "the flags are NOT stale" described the corpus at that moment, not a standing policy.
What this changes for users, stated plainly: an invocation that passes
--podman-jobsorCHARLY_PODMAN_JOBSstops being accepted. That is the cutover's point. No alias is kept.The correction to this issue's RCA, and to its sibling note
settle on the grammar's side. The contract settled; the CLI did not follow. The mechanism is
unchanged — a one-line Kong struct tag that no compile, no test and no schema check could
contradict — but the drift direction is: the CONTRACT moved, and the CLI was left behind.
buildkit/build_helpers.go:41and:55are no longer "the same wrong spelling".With the grammar renamed, those two lines become TRUE. The sibling finding shrinks to line 18's
stale phrase ("box-arg normalization, podman-jobs resolution, host platform detection"), which
is the sdk's file and is filed on the sdk's leg.
Where the fan-out is tracked
opencharly/opencharly#785 — "stage_parallelism consumer legs: the retired --podman-jobs flag
name remains in consumer repos after the BuildKit cutover". It carries the measured referrers
(umbrella
charly/charly/go.mod:15+charly/charly.yml:2530; marketplacecandy/charly-marketplace/charly.yml:125; layer-charly-checkcharly.yml:709; the umbrella's.agents/skills/agents/references/parallel-bed-testing.md:596,601; the sdk'sbuildkit/build_helpers.go:18), the RCA, and the producer-first order. This issue is theproducer leg; #785 is the fan-out. Claimed on #785 by this session.
What is done, what is next, when
(
cdc2a95, five files: the grammar, its tests, the pull skill's--platformrows, the newcontrol
candy/plugin-box/skill_doc_flags_test.go, and the bed fixture incharly.yml); proventhree ways — the red arms (the control fails on the pre-change tree, and on a body that
documents a flag the grammar lacks), the green arm (
gofmt,go build,go vet,go test -count=1, all rc=0), the live probes against the sanctioned dev binary (the new flagaccepted rc=0; the retired flag rejected rc=80
unknown flag --podman-jobs), and the R10 bedcheck-box-local(PASS, steps=6).!hard-cutover marker in the title, and the## Filed issuessection carrying #785.Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (analysed on a live system)
aitrawog commentedon Oct 28, 2026
STATUS
Agent: charly · session
ses_8efb271d-f2c0-47d3-85e3-2a1e00b11ae0The producer leg is open: #66 — head
cdc2a9577f2a86ef698dc6400eb0d4c17d2886a9, branchfeat/build-skill-flag-spelling-65, 5 fileschanged, 345 insertions(+), 27 deletions(-). It does exactly what the STATUS above superseded the
body's "root fix" paragraph with: the grammar's flag and env names become
--stage-parallelism/CHARLY_STAGE_PARALLELISM, the retired spelling is deleted with no alias and no shim, and thecontrol lands with it.
Its gate, on the committed tree:
gofmtclean;go build/go vet/go test -count=1rc=0;charly box validate→ "0 warnings, 0 errors"; the R10 bedcheck-box-local→ "PASS (steps=6)";and the changed path executed live against the worktree-local DEV build — the new flag is
accepted, the retired one is rejected with rc=80 "unknown flag --podman-jobs".
The cross-repo fan-out is tracked in opencharly/opencharly#785, and the corpus's
buildfamilyis re-keyed and regenerated in opencharly/marketplace#587 (blocked on this landing and tag).
Next update: the validator's verdict on #66, and the end of each review round.
Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)
refactor(cli)!: the build concurrency flag is --stage-parallelism, an…
aitrawog commentedon Oct 28, 2026
RESOLVED
Agent: charly · session
ses_8efb271d-f2c0-47d3-85e3-2a1e00b11ae0Landed: #66 — squash
7a8c8b2ccdd3bac44fe6bf86d42b174b75feab42, tagv2026.301.1542(candy tagcandy/plugin-box/v0.2026301.1542), validator verdict PASS, merged2026-10-28T15:42Z.
What it fixed — at the source, not in the prose. This issue's subject was that the build/pull
skill bodies name a flag this plugin's grammar does not accept. The grammar now speaks the
contract's spelling, and the bodies are held to it:
--stage-parallelism/CHARLY_STAGE_PARALLELISM— the contract's name for the knob (
spec/schema/box.cue:120-121); the retired--podman-jobs/CHARLY_PODMAN_JOBSis deleted with no alias and no shim;--platformrows are deleted (that flag left the pull grammar inplugin-box#40 while the body still documented it);
daemon's
[worker.oci] max-parallelism, not a per-build podman flag;repo is read against the Kong grammar its own command parses into, and the flag names and env
bindings come from the grammar structs — so the published surface cannot drift from the parser.
The gate, on the committed tree:
gofmtclean;go build/go vet/go test -count=1rc=0;charly box validate→ "0 warnings, 0 errors"; the R10 bedcheck-box-local→ "PASS (steps=6)";the changed path executed live against the worktree-local DEV build — the new flag is accepted, the
retired one is rejected with rc=80 "unknown flag --podman-jobs"; and the control's two red arms
(M1: 5 findings, M2: 2 findings) prove it can fail.
The rest of this cutover is tracked in opencharly/opencharly#785 — the SHIPPED binary's
compiled-in plugin-box pin (opencharly/charly), the corpus regeneration (opencharly/marketplace#587),
and the prose in layer-charly-check, the umbrella's
parallel-bed-testing.md, and the sdk. Thisissue is the producer leg and it is done.
Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)
aitrawog commentedon Oct 28, 2026
Closed by #66 (squash 7a8c8b2, tag v2026.301.1542).