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:

  1. The sentence was true of the PATH and false of the machine. npx --yes @anthropic-ai/claude-code fetches the CLI — 184 KB packed, no account, no credential, no spend.
  2. 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 containsresolves tocontents
plugin.json + marketplace.jsonmarketplace.json[]
plugin.json onlyplugin.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.

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

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.