|
| 1 | +# 2026.242.0504 — fix: add a root charly.yml so the candy CI gate is real |
| 2 | + |
| 3 | +## Summary |
| 4 | + |
| 5 | +Makes this repo's candy CI gate actually gate something. |
| 6 | + |
| 7 | +Without a root `charly.yml` a plugin repo is **project-less**: charly scans **no candies at |
| 8 | +all**, so the stock `charly box validate` step in `deploy.yml` exits 0 on any manifest — |
| 9 | +including one with a malformed `plugin.providers` entry. The step has been asserting |
| 10 | +nothing. |
| 11 | + |
| 12 | +A four-line `discover:` block is the whole fix. The candy becomes scannable, the CUE schema |
| 13 | +gate applies, and the **stock** step starts working — no workflow change needed. The file |
| 14 | +declares no entities of its own; it only tells charly where to look. |
| 15 | + |
| 16 | +Refs opencharly/charly#459. |
| 17 | + |
| 18 | +## How tested |
| 19 | + |
| 20 | +The mechanism and the fix were established on `opencharly/plugin-punktfunk` |
| 21 | +(opencharly/plugin-punktfunk#4), with a plain layer candy to isolate it — `box list candies` |
| 22 | +filters plugin candies out of its own output, so a plugin candy alone cannot show this: |
| 23 | + |
| 24 | +``` |
| 25 | +# candy/plainlayer/charly.yml exists, no root charly.yml |
| 26 | +$ charly box list candies # (nothing — not scanned at all) |
| 27 | +$ charly box validate # exit 0 even with a bogus field in the candy |
| 28 | +
|
| 29 | +# with the 4-line root charly.yml |
| 30 | +$ charly box list candies |
| 31 | +plainlayer [testing] |
| 32 | +$ charly box validate |
| 33 | +... notafield: field not allowed |
| 34 | +``` |
| 35 | + |
| 36 | +And on a real plugin candy, before/after, same repo and command: |
| 37 | + |
| 38 | +``` |
| 39 | +# `- verb:punktfunk` changed to `- notaclass:punktfunk` |
| 40 | +# BEFORE: exit 0 |
| 41 | +# AFTER: |
| 42 | +charly: error: command "validate": 2 validation errors: |
| 43 | + plugin.providers.0: invalid value "notaclass:punktfunk" |
| 44 | + (out of bound =~"^(kind|deploy|verb|step|build|builder|command|loader|refs|agent-runtime|terminal):...") |
| 45 | +``` |
| 46 | + |
| 47 | +**Consumers are unaffected**, which is the real risk of adding a root manifest — a consuming |
| 48 | +project might resolve the candy twice (once via its pinned subpath ref, once via the new |
| 49 | +`discover:`). Tested by injecting the manifest into the *fetched* checkout that |
| 50 | +`opencharly/distro-cachyos` resolves for a pinned plugin candy, then validating that project |
| 51 | +both ways: identical result, exit 0 either way. |
| 52 | + |
| 53 | +**This repo's own gate is the acceptance:** the `build` check on this PR runs |
| 54 | +`charly box validate` at this repo root against the pinned charly. It now scans this repo's |
| 55 | +candy, so a green check here is the first time that step has meant anything for this repo. |
| 56 | + |
| 57 | +## Rulebook compliance |
| 58 | + |
| 59 | +- **R0 (skills)** — `/charly-internals:plugin` (plugin repo layout, the candy-ref contract) |
| 60 | + and `/charly-internals:git-workflow` loaded before the change. |
| 61 | +- **R1 (RCA)** — root-caused, and an earlier wrong hypothesis (that the legacy `candy/` walk |
| 62 | + skipped the CUE gate) was disproved by the plain-layer probe above before this fix was |
| 63 | + written. |
| 64 | +- **R2 (no split)** — this repo is fixed here, not deferred; the org-wide gap is being closed |
| 65 | + across every affected plugin repo in the same sweep, tracked by opencharly/charly#459. |
| 66 | +- **R3 (no duplication)** — one gate per repo; the stock `charly box validate` step is made |
| 67 | + to work rather than a second, parallel validation being bolted on. |
| 68 | +- **R4 (no workarounds)** — the supported mechanism (`discover:`) is used; no CI staging |
| 69 | + hack, no suppression. |
| 70 | +- **R4a (fix the product first)** — the product-side option (make `validate` treat a |
| 71 | + `candy/`-bearing dir as a project) was investigated and rejected: "returns nil for a |
| 72 | + project-less directory" is a documented contract other resolve paths rely on, so the fix |
| 73 | + belongs in the repo. |
| 74 | +- **R5 (hard cutover)** — additive; no deprecated path is left behind. |
| 75 | +- **R6 (git safety)** — feature branch, no force-push, no hook bypass. |
| 76 | +- **R7 (prove the gate)** — the before/after validate outputs are above, and this PR's own |
| 77 | + `build` check is the gate running for real. |
| 78 | +- **R9 (deployed binary)** — N/A — no binary, no runtime path; the provider is untouched. |
| 79 | +- **R10 (fresh disposable proof)** — N/A — this change cannot reach a running deployment; it |
| 80 | + changes what `charly` scans in the repo, which the validate A/B fully observes. |
| 81 | +- **RDD** — the high-risk assumption (that a root manifest might break remote consumption) |
| 82 | + was proved before landing, with the cached-checkout A/B above. |
| 83 | +- **ADE** — N/A — no install content authored; the candy's `plan:` is unchanged. |
| 84 | +- **SDD** — N/A — no schema or generated code touched. |
| 85 | +- **Concurrency mandate** — N/A — a single four-line file. |
| 86 | +- **Architecture gate / placement** — correct: the repo declares its own shape; core keeps |
| 87 | + its project-less contract unchanged. |
| 88 | + |
| 89 | +## Change classification |
| 90 | + |
| 91 | +- **Change class:** repo configuration — adds a root manifest so the existing CI gate has |
| 92 | + input. No source, no workflow, no schema change. |
| 93 | +- **Verification gate:** `charly box validate` before/after with a deliberately malformed |
| 94 | + manifest, a consumer-side resolution A/B, and this PR's own `build` check. |
| 95 | +- **Attribution tier:** `fully tested and validated` — the gate this change exists to repair |
| 96 | + was exercised in both directions, and the consumer risk was tested, with output retained |
| 97 | + above. |
| 98 | + |
| 99 | +*Assisted-by: Claude Code claude-opus-5[1m] (fully tested and validated)* |
0 commit comments