Repository navigation
refactor(cli)!: the build concurrency flag is --stage-parallelism, and a control holds every skill body to the grammar - #66
Conversation
…d a control holds every skill body to the grammar The CONTRACT renamed the inner-concurrency knob `podman_jobs` -> `stage_parallelism` (spec/schema/buildwire.cue, sdk/buildkit.ResolveStageParallelism) and the builder is BuildKit — the value drives the daemon's `[worker.oci] max-parallelism`, not `podman build --jobs N`. The Kong grammar kept the retired driver's spelling, so the flag's own help read "Stages per podman build" for a driver that is deleted, and the published build skill documented a spelling the grammar rejected — a command that fails AT THE FLAG (#65). This renames the flag and its env binding to `--stage-parallelism` / `CHARLY_STAGE_PARALLELISM`, deletes the pull skill's `--platform` rows (the flag was deleted from the grammar by #40 while the body still documented it), and adds the control that reads each body from the path the corpus reads it from and asserts that the flags and env bindings it presents for its own command are ones the grammar defines. Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)
|
STATUS Agent: charly · session PR open: #66 — head What is done. The flag rename ( What is next. The org's When the next update is due. As soon as the validator's first verdict lands — and again at Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated) |
Review — PASSI re-derived every claim against the current body, diffs, and comment. The change is coherent, the arithmetic checks out, and the new control's logic matches the red arms it reports. Details below, file by file.
|
…s producers (#791) The umbrella's currency report on this wave's base named SIX pins that were not current, and every one of them had a producer that had already landed. This is the consumer leg of that wave: the umbrella's tree changes only in its gitlinks, and what those gitlinks now carry is the `stage_parallelism` rename and the guidance it republished. currency: 6 of 409 pin(s) NOT current [2 behind, 403 cur, 4 differs (no checkout to judge ancestry)]; intents: 403 upstream leaf/leaves (403 implicit), 6 derived-from edge(s); NOT CURRENT: charly: behind; intent upstream:main (implicit: .gitmodules branch=); recorded 874a8b113e; served be7a505f47 (branch); layer-charly-check: differs (no checkout to judge ancestry); intent upstream:main (implicit: .gitmodules branch=); recorded cbd44f223d; served c77ecbb77a (branch); marketplace: behind; intent upstream:main (implicit: .gitmodules branch=); recorded 99a1bb4a6f; served 5feb9c8b83 (branch); plugin-box: differs (no checkout to judge ancestry); intent upstream:main (implicit: .gitmodules branch=); recorded b9ed1216c8; served 803541c15d (branch); plugin-marketplace: differs (no checkout to judge ancestry); intent upstream:main (implicit: .gitmodules branch=); recorded ba254d57ee; served 12242956b6 (branch); sdk: differs (no checkout to judge ancestry); intent upstream:main (implicit: .gitmodules branch=); recorded d71b9abb4b; served 352755df2e (branch); producer-first legs: charly (874a8b113e -> be7a505f47) -> marketplace (99a1bb4a6f -> 5feb9c8b83) WHAT EACH ADVANCE CARRIES. charly 874a8b113e -> be7a505f47 (4 commits, 6 files) opencharly/charly#1082 compiles in the plugin-box that spells the knob `--stage-parallelism`, and #1084 adopts the landed `stage_parallelism` vocabulary in the project manifest. The umbrella's effective binary now ACCEPTS `--stage-parallelism` and REJECTS `--podman-jobs` (rc=80). layer-charly-check cbd44f223d -> c77ecbb77a (2 commits, 2 files) opencharly/layer-charly-check#55 republishes the authoring-gotchas reference so it names the knob that exists: `--stage-parallelism`, and the outer `--jobs` beside it. marketplace 99a1bb4a6f -> 5feb9c8b83 (6 commits, 26 files) opencharly/marketplace#591 republishes the umbrella's own guidance from its post-fix tag, so the corpus teaches `--stage-parallelism` and the pin-advance verb's true scope. #586 and #590 ride along. plugin-box b9ed1216c8 -> 803541c15d (8 commits, 9 files) opencharly/plugin-box#66 is the rename's producer; #68, #70 and #72 are the follow-up that makes the skill-body control read the TREE (so a newly added body is covered without a hand-maintained slice) and gives the vacuity guard its own red arm. plugin-marketplace ba254d57ee -> 12242956b6 (2 commits, 5 files) opencharly/plugin-marketplace#55 replaces the false family->owner table with the mechanism that cannot go stale. sdk d71b9abb4b -> 352755df2e (1 commit) opencharly/sdk#476 names the knob that exists in the buildkit file header. THE SCOPE IS DERIVED, not hand-listed: the product's own currency report on this wave's base names the pins that are not current, and its own producer-first line is the wave's work list. The wave ran in the sanctioned order on a light tree: `charly task sync` (the pin moves in the index; the checkouts that exist are rolled), then `charly task init` to materialize the advanced paths AT the pins the index records, then the gate. The verb's own report is the scope control: 6 pin(s) moved — 6 pin_map entries moved 0, 396 index-only advances moved 4, and 2 of the 7 rolled leaves moved. The six `derived-from:charly` EDGES stay `cur`: the two `charly` trees record identical `box/*` twins, so advancing the leaf changes what the edges are judged against, not what they are worth. Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)
…erate the site on --stage-parallelism (#198) This repo's `marketplace` pin moves from 99a1bb4a6f10112f67e3ddd73d3e86b3f6bc3417 to 5feb9c8b8350849fd2d46d252b486844c18bd0a4 — the corpus's merged `main` tip, advanced through the submodule's own remote-tracking path, never a hand-set gitlink. The corpus is what decides which projection of the guidance corpus the site serves, and at this tip it names the `build`-family owners DIRECTLY — plugin-box at v2026.301.1542 among them — so the recipe pages it projects teach the renamed knob. The wiring candy's own `plugin-build` pin moves from v2026.242.0525 to v2026.296.1104 — the newest plugin-build tag, and the first one whose `candy/plugin-build/schema/build.cue` carries `stage_parallelism?: int`. That is what the site's `reference/plugin/**` and `reference/candy/**` sections project, so the site's last `podman_jobs?: int` goes with the old pin. THE REGRESSION THIS REPAIRS, AND ITS ROOT CAUSE. The published site taught the retired flag before, then stopped, then taught it again — measured on this repo's own history: $ for c in 81da73e c0e7db9 98a5f6c; do git show $c:src/content/docs/recipes/build/build.md \ | grep -cE 'stage-parallelism|podman-jobs'; done 7 0 # 81da73e — PR #167, the corpus pin at layer-charly-build:v2026.292.2017 0 7 # c0e7db9 — PR #176, the corpus pin at layer-charly-build:v2026.296.0919 0 7 # 98a5f6c — PR #197, the corpus pin at the corpus's merged main of Oct 28 The pin moved FORWARD in time every time, and the CONTENT moved BACKWARD. The mechanism, measured on the corpus's own refs list and on the owner's tree at the tag the corpus named: $ for c in 602324e 1c15651 99a1bb4a 5feb9c8; do git -C marketplace show $c:candy/charly-marketplace/charly.yml \ | grep -oE '@github.com/opencharly/layer-charly-build:v[0-9.]+'; done 602324e layer-charly-build:v2026.292.2017 # the body teaches --stage-parallelism 1c15651 layer-charly-build:v2026.296.0919 # the body teaches --podman-jobs 99a1bb4a layer-charly-build:v2026.296.0919 # the body teaches --podman-jobs 5feb9c8 (no ref — the build family is named from plugin-box:v2026.301.1542) `layer-charly-build` was DRAINED by the build-family split, and at its tag v2026.296.0919 it has no `.agents/` tree at all — its manifest says it "owns NO `skill:` entity any more". So the corpus's `build/skills/build/SKILL.md` at 1c15651 and 99a1bb4a COULD NOT HAVE BEEN SOURCED from the tag the corpus's own refs list names: it was an ORPHAN body, carried in the corpus's tree from before the drain. The corpus published a body that no tag it named could supply, and the site published that body faithfully. The MISSED CONTROL is exactly there. A provenance check that compares a projected body against the owner's file AT THE NAMED TAG has nothing to compare an orphan against, so it is silent precisely where the defect lived. The site's drift gate is downstream of the same blindness: it compares the committed tree to the pinned inputs, and a faithful projection of a regressed input IS "no drift". The corpus currency report asks a third question — is the PIN behind the corpus's merged main — and answered `cur` throughout. The BLAST RADIUS: the published `build` family and every page naming the flag. The site told its readers to pass `--podman-jobs`, a flag the shipped binary REJECTS with rc=80 since opencharly/charly#1082 landed (opencharly/plugin-box#66 renamed it). THE NEW PIN HAS NO ORPHAN. The corpus's body at 5feb9c8 is plugin-box's file at v2026.301.1542 plus the generator's own header, and nothing else: $ git -C marketplace show 5feb9c8:build/skills/build/SKILL.md > corpus.md $ git --git-dir=$GD/plugin-box show v2026.301.1542:.agents/skills/build/SKILL.md > owner.md $ diff owner.md corpus.md 6a7,8 > <!-- GENERATED by `charly marketplace generate` — DO NOT EDIT. … --> THE CLAIM, MEASURED. The retired name is gone from the published tree, and the new one is carried: $ grep -rnE "podman-jobs|podman_jobs|PODMAN_JOBS" src/content/docs (no output; exit 1) $ git grep --cached -nE "podman-jobs|podman_jobs|PODMAN_JOBS" -- src/content/docs (no output; exit 1) Four carriers carried it at HEAD: the `build` recipe (the flag line, the `Parallelism: --jobs vs --podman-jobs` section, the `defaults.podman_jobs` / `podman_jobs_cap` example), the `beds-and-r10` recipe, the `parallel-bed-testing` recipe, and the plugin-build reference page's schema block. The last is the pin advance; the first three are the corpus advance. No page is hand-edited. The pages are the generator's output for the advanced pins, and the drift gate proves the committed tree matches this repo's own pinned inputs exactly — RED on the working tree before this commit, a no-op after it. Refs #196 Refs opencharly/opencharly#785 Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)
Summary
Head
cdc2a9577f2a86ef698dc6400eb0d4c17d2886a9— the commit this PR pushes: 5 files changed,345 insertions(+), 27 deletions(-).
The inner-concurrency flag of
charly box buildis renamed to the contract's spelling —--stage-parallelism/CHARLY_STAGE_PARALLELISM.--podman-jobs/CHARLY_PODMAN_JOBSareremoved: no alias, no shim, no deprecation window. This is a breaking change to the CLI
surface, which is why the title carries the
!marker.The BuildKit cutover (opencharly/opencharly#481) renamed the knob
podman_jobs→stage_parallelismin the CONTRACT — the schema key (spec/schema/box.cue:120-121), the sdksymbol (
sdk/buildkit.ResolveStageParallelism), the config key in everydefaults:block, andthe prose (opencharly/layer-charly-build#29, merged 2026-10-19, validator PASS). The Kong
grammar in THIS repo was the one surface that did not follow it, and it was false about its own
mechanism: the flag's help read "Stages per podman build" for a driver that is deleted — the
value drives the BuildKit daemon's
[worker.oci] max-parallelism(
sdk/buildkit/build_helpers.go:44-47).That left two live defects, and this PR fixes both:
.agents/skills/build/SKILL.mdis published VERBATIM into the opencharly/marketplace corpus,so every harness that loads the corpus was handed a command that fails AT THE FLAG:
charly box build --stage-parallelism N→charly: error: command "build": unknown flag --stage-parallelism(rc=80, measured against the shipping binary — the live proof is pastedunder How tested).
--platform, a flag deleted from the pull grammar by bug:charly box pull --platformis a documented no-op — the flag is parsed, never threaded, and any value silently pulls the host platform #40 whilethe body kept its alias-table row and its flag bullet.
Both are the same class, so both get the same control:
candy/plugin-box/skill_doc_flags_test.goreads each of this repo's ten skill bodies from thepath the corpus reads it from, and asserts that every flag and every environment binding each
body presents for its OWN command is one that command's Kong grammar actually defines. The flag
names and env bindings come from Kong's own parser model, so the surface cannot drift; it is a Go
test and not a prose review because a prose review cannot fail (R7). It reads three claim
surfaces — usage claims (a fenced line or inline span that invokes the command), alias rows (a
table row whose first cell is a
--flag), and flag-section bullets — and it deliberately does NOTread prose that merely mentions the command, because that prose names the HOST's surface (
-C,--dir,CHARLY_PROJECT_DIR), not this plugin's. Both exclusions are stated RULES about whichSURFACE is read, not an allowlist of names, so a later prose rewrite cannot smuggle a false flag
in through a sentence.
The files, and what each one does:
candy/plugin-box/box.go--stage-parallelism(CHARLY_STAGE_PARALLELISM), and its help now states the mechanism it actually drives. The comment above the struct records WHY the spelling changed, since a rename without a reason is a mystery to the next reader.candy/plugin-box/box_test.goCHARLY_STAGE_PARALLELISM— the flag's env alias is covered by the same test that covered the old one.candy/plugin-box/skill_doc_flags_test.go.agents/skills/pull/SKILL.md--platformalias row and its flag bullet are deleted, with no replacement prose: the flag does not exist, so the body says nothing about it.charly.ymlcheck-box-localbed's skill-owner fixture is corrected: the control's skill body now lives at the OWNING CANDY'S REPO ROOT, which is the layout every real skill owner uses (the candy undercandy/, its.agents/skills/at the project root) and the layout the sdk's body lookup actually resolves (sdk/deploykit/skills_collect.go→candyRepoRoot). The fixture had written the body BESIDE the candy, so it proved a shape that no real owner uses.The corpus's
buildfamily is re-keyed and regenerated in opencharly/marketplace#587 (alreadyclaimed by this session) — this PR is its PRODUCER leg, and that leg is blocked on this PR's
landing and tag. The flag rename's remaining consumer legs — opencharly/charly's compiled-in pin
(
charly/charly/go.mod:15), opencharly/layer-charly-check's prose (charly.yml:709), theumbrella's
parallel-bed-testing.md, and the sdk's stale phrase — are tracked, with their order,in opencharly/opencharly#785.
How tested
Everything below ran on the committed tree this PR pushes.
1. The repo's own gate —
go build/go vet/go test -count=1incandy/plugin-box/, plusgofmt -l.2. The R10 bed —
check-box-local, this repo's live witness (adisposable: truekind:localdeploy driven by
charly check run: build → artifact checks → realize → deployment checks →rebuild fresh with
charly update→ checks again → remove).3. The structural check at the repo root.
4. The changed path, executed LIVE against the sanctioned worktree-local DEV build
(
bash scripts/bootstrap-charly.sh --dev-plugin plugin-box=<this checkout>→built ./bin/charly (2026.301.1224) — DEV BUILD, local plugin source; a DEV build is never installed and neverreplaces the released binary). The discriminator is the exit code: rc=80 is Kong rejecting the
flag before anything else runs; rc=1 is the command running on past the parse and failing on the
box name — so the new flag was accepted and the retired one was not.
The same DEV binary's help renders the renamed flag with its corrected mechanism:
5. The control fails without the change (R7 — a coverage that cannot fail is invalid). Two red
arms, each produced by restoring the pre-change content, running the control, and restoring the
tree with the file's SHA-256 verified identical before and after the restore.
Arm M1 — the pre-rename tree (the control against the grammar as it was):
Arm M2 — the pull body before the
--platformrows were deleted:Both arms are the control's own findings, not an assertion about the diff: the arm restores the
file, the control reads the restored body against the grammar, and it names the exact line and
the exact flag.
6. R5's claim-keyed sweep — the retired spelling. The claim: the retired flag name and the
retired env name are not a flag, not an env binding, and not a published flag table anywhere in
this repo.
Every surviving hit is classified, and none of them is a surface:
charly.yml:349-398— pre-existing, unchanged by this PR (the diff touchescharly.ymlonly at 454+). This is the bed's NEGATIVE arm for the retired CONFIG key: it asserts that
defaults.podman_jobsno longer fires, so the retired key must be named there.candy/plugin-box/box.go:234-235— the comment that records WHY the flag was renamed. It isprose about the retired name, not a flag, an env binding or a published row.
The pull body's
--platformis gone, and the build body now matches the grammar:Rulebook compliance
/charly-build:build,/charly-build:validate,/charly-image:image,/charly-internals:pluginand/charly-internals:git-workflow(the five this repo's ownAGENTS.md names) were loaded before the first source read and before the first git action.
the grammar's; its RCA (mechanism, missed control, blast radius, root fix) is in
the build skill documents
--stage-parallelism/CHARLY_STAGE_PARALLELISM— spellings this plugin's grammar does not accept #65 and, for the cross-repo fan-out, in stage_parallelism consumer legs: the retired --podman-jobs flag name remains in consumer repos after the BuildKit cutover opencharly#785. Thefindings that are not this repo's file are filed, not fixed from here (see Filed issues).
The red arms in How tested are the proof that the new coverage fails without the change.
Zero warnings in every gate above:
0 warnings, 0 errorsfromcharly box validate, nogofmtoutput, nogo vetoutput,go testrc=0, the bed'sPASS (steps=6).grammar lacks", and it reads the grammar through Kong's own model rather than a hand-written
list of names. The corpus's
buildfamily is re-keyed to this repo's owners underthe
buildfamily is published from a DRAINED candy's INLINE manifest: the six repos that now own the fifteen build-family skills are not pinned DIRECTLY, so the corpus keeps the retiredengine.buildprose marketplace#587 rather than gaining a second copy of a body.The retired flag is removed, not tolerated.
changed; the prose change is the deletion of rows for a flag that does not exist. Every command
this body documents works with the
charlybinary alone.surviving hit is classified there.
git statuswas clean before every action; this PR is one commit onfeat/build-skill-flag-spelling-65offorigin/main(b9ed121); no force-push, no amendafter the push, no direct push to
main, no hook bypass.independent arms; the changed path was executed live against the DEV build (§4 of How
tested), and the emitted artifact — the skill bodies the corpus publishes — was verified above
(at its boundary: the control reads each body from the corpus's own path, and the corpus
regeneration publishes that same path's content).
disposable: truelocal target; noservice was mocked.
from THIS checkout (
bootstrap-charly.sh --dev-plugin plugin-box=<this checkout>, DEV BUILD2026.301.1224), never against a shared install.
repo's full assembled bed:
charly check run check-box-localPASS (steps=6) on the committedtree, including the fresh rebuild through
charly updateand the removal afterwards. The bed'scharly.ymlfixture change is in the same tree that was gated.Cutover. One cutover, one PR: the flag, its env binding, its help text, its test, the pull
body's dead rows, the control that holds the bodies, and the bed fixture that the control's
correctness depends on. No deferred phase and no TODO is left inside the scope. The cross-repo
legs are separate PRs in their own repos, tracked by number in opencharly/opencharly#785.
Landing. PR-only; the org's
charly/pr-validatoris the merge gate and arms nativeauto-merge on PASS. No agent merges this PR.
Change classification
binding renamed) plus the control that keeps the repo's published bodies true of that surface,
plus a fixture correction in the repo's own bed.
fully tested and validated. The change-class gate passed on the finaltree (the repo's Go gate,
charly box validate, and thecheck-box-localR10 bed, allrc=0), and the changed path was executed live against a build made from this source.
buildfamily (thebuildfamily is published from a DRAINED candy's INLINE manifest: the six repos that now own the fifteen build-family skills are not pinned DIRECTLY, so the corpus keeps the retiredengine.buildprose marketplace#587,blocked on this landing and tag) and for the flag's remaining consumer legs
(stage_parallelism consumer legs: the retired --podman-jobs flag name remains in consumer repos after the BuildKit cutover opencharly#785).
!marker in thetitle records that, and this body is the changelog entry.
Filed issues
name remains in consumer repos after the BuildKit cutover". Carries the RCA for the whole
fan-out and the measured referrers:
opencharly/charly's compiled-in pin(
charly/charly/go.mod:15,charly/charly.yml:2530), the marketplace's build-family owner pin(
candy/charly-marketplace/charly.yml:125),opencharly/layer-charly-check's prose(
charly.yml:709), the umbrella's.agents/skills/agents/references/parallel-bed-testing.md:596,601,and the sdk's stale phrase (
sdk/buildkit/build_helpers.go:18). Non-blocking for this PR(each site is another repo's file); the order is producer-first, and this PR is the producer.
still set and still published". Found in the same pass over the corpus's published bodies: the
contract retired
engine.build/CHARLY_BUILD_ENGINE(the removal is pinned inopencharly/spec'shostenv/retired_build_selector_test.goand inopencharly/plugin-settings'candy/plugin-settings/retired_settings_test.go), yet that repo still ASSIGNS the retiredvariable (
charly.yml:143) and still publishes its name in two prose surfaces that reach thecorpus and the generated docs. Non-blocking for this PR — it is that repo's file, and the
finding is filed there with the RCA.
Agent: charly · session
ses_8efb271d-f2c0-47d3-85e3-2a1e00b11ae0Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)