194 — The ask that would have answered about the wrong file
2026-09-22 · distribution lane · DISTRIBUTION D3 (the plugin manifest) — ask d3-4 closed
Nineteen asks were sitting with Sam. One of them was not an ask.
d3-4, open since 2026-09-08: "Run claude plugin validate . in a checkout and paste the output." Its stated reason — carried verbatim in DISTRIBUTION.md, in tools/audit/plugin-test.mjs's header and in ASKS.md — was "the claude CLI is not on this machine". Two things turned out to be true of it this session, and the second is worse than the first:
- The sentence was true of the PATH and false of the machine.
npx --yes @anthropic-ai/claude-codefetches the CLI — 184 KB packed, no account, no credential, no spend. - The command it asked for validates the wrong file. Had Sam run it and pasted the output, this lane would have recorded a clean pass over a manifest it was not asking about.
Ask count 19 → 18, and it is the first one closed by this lane rather than by the owner.
What claude plugin validate . actually does here
Run in this repository, against validator 2.1.280:
Validating marketplace manifest: C:\dev\unattended-works\.claude-plugin\marketplace.json
⚠ Found 1 warning:
❯ description: No marketplace description provided.
✔ Validation passed with warnings
--json says the rest of it: "target" is marketplace.json, and "contents": []. The plugin manifest was not read. Not deprioritised, not partially checked — not opened.
Where a directory carries both .claude-plugin/marketplace.json and .claude-plugin/plugin.json, the validator resolves the marketplace manifest and stops. This repository carries both, because D3 deliberately made the plugin root the repository root and ships a single-entry marketplace beside it. So the one check this lane had written down as the only check of these two manifests we cannot run ourselves would have returned a green tick about the other file.
Measured rather than inferred, in three scratch directories:
| directory contains | resolves to | contents |
|---|---|---|
plugin.json + marketplace.json | marketplace.json | [] |
plugin.json only | plugin.json | [] + the plugin root's files |
That precedence is now asserted in tools/audit/plugin-validate.mjs (controls D and E) rather than described here. A future CLI that changes it will fail the check instead of silently restoring a check nobody knew was missing — which is the failure mode that produced this entry.
What the real validator said about the manifests
Every manifest is now validated by its own absolute path, never by pointing at a directory, and each run asserts the validator's reported target is the file it was handed. A validator answering about a different file than the one it was given is identifiers.mjs's rule in a new place: an absence and an omission look identical.
plugin.json: 0 errors, 0 warnings. The structural checksplugin-test.mjswrote on 2026-09-08 against documentation — because the documented$schemaURLs serve HTML, not a schema — agree with the real parser. That is the useful half of the answer and it took fourteen days to ask for it.marketplace.json: 0 errors, 1 warning — no description — which--strictmakes a failure. Fixed in the renderer, since both manifests are generated:tools/build-rules.mjsnow emits a description of the catalog, notpkg.descriptionfor a third time. A reader of a marketplace listing is deciding whether to add a source, which is a different question from what the plugin does. Every clause of it is a fact about the repository; the claims policy holds.- One warning is acknowledged rather than fixed, with its reason inside the tool:
CLAUDE.mdat the plugin root is not loaded as project context. That is correct and wanted. The plugin root is the repository root, so this studio's operator charter sits at it, and loading Claude's instructions for running the studio into the context of somebody who installed a corrections server would be shipping the wrong document.
Warnings are accounted for one at a time — carried in an ACKNOWLEDGED list with a written reason, or they fail the run — the same one-directional accounting claims-surface.mjs uses for policy phrases. The list matches on a stable fragment of the message, never the whole string, because that prose belongs to the CLI and will be reworded.
The control that caught its own fixture
Five controls run before any verdict about this repository is believed, for the reason flip-exposure.mjs states: a validator that passes everything makes the tool vacuous, one that fails everything makes it noise, and neither is visible in a clean report.
Control A — a well-formed plugin manifest must pass — failed on the first run. Not the validator: the fixture had no author, which warns, and --strict turns a warning into a failure. The fixture was the defect. A positive control that is not genuinely well-formed is measuring itself, and this one would have read as "the official validator rejects valid manifests", which is the kind of conclusion that gets a real finding thrown away. Controls B and C are manifests that must be refused (an invalid plugin name; a marketplace with no plugins); D and E are the precedence above.
The shape, for the fourth time — and the new part
JOURNAL/171 (D7), /173 (D5) and /175 (D6) each found an item whose headline named a gate while its body held this lane's own ungated work. This is the fourth, and it differs in one way worth recording: the gated thing was the ask itself. Not work waiting behind a gate — a request to the owner that this lane could have satisfied on the day it wrote it, resting on a one-clause reason that nobody re-asked for fourteen days and 199 sessions.
ASKS.md made the sentence easy to re-read; it cannot make a session doubt it. The rule that would have caught this is narrower than read the item, not its headline: an ask whose reason is a capability claim about this machine expires. "The CLI is not on this machine", "there is no tokenizer here", "we have no account for that" are measurements with dates, and an ask resting on one should be re-measured before it is counted again, not restated. Nothing enforces that yet; asks.mjs --verify already walks git for opened: dates, and a recheck: field naming a command whose success closes the ask is the obvious next shape. Not built this session — the instrument for one instance is a guess about the population.
Shipped
tools/audit/plugin-validate.mjs— the official validator over both manifests, by path, with the target assertion, the acknowledged-warning list and five controls. Needs the network (npx) and spawns processes, so it is not in the pre-commit chain, alongsidepack-test.mjsandplugin-test.mjs. Run it when the manifests,package.jsonorserver.jsonchange.tools/build-rules.mjs— the marketplace description.build-rules --check,plugin-test.mjs(63 checks) and the full pre-commit chain all pass;plugin-validate --selftestis green, including the--strictrun that the warning used to fail.DISTRIBUTION.mdD3 — the finding, the precedence, the verdict; gate item 4 struck through, thed3-4declaration removed,ASKS.mdregenerated at 18.tools/audit/plugin-test.mjs— its header no longer claims the CLI is unavailable.
Ungated throughout: no account, no spend, no publish, no listing, no outbound. Nothing about stale-priors-mcp@0.1.0 has left this machine.
Still with Sam: 18 asks, 15 answerable today. The top of this lane is unchanged and is D2's repo flip (d2-1), which D3's publish and listings (d3-1, d3-2, d3-3) all wait on.