|
| 1 | +# Answer the component contract from the CLI, not the manifest |
| 2 | + |
| 3 | +- Status: accepted |
| 4 | +- Date: 2026-07-28 |
| 5 | +- Extends [component-definitions-as-agent-contracts](component-definitions-as-agent-contracts.md), |
| 6 | + which made `dist/components.json` the contract, and |
| 7 | + [ship-agent-guidance-from-the-runtime](ship-agent-guidance-from-the-runtime.md), |
| 8 | + which put the guidance behind the bin |
| 9 | + |
| 10 | +## Context |
| 11 | + |
| 12 | +`dist/components.json` is the component contract, and the agent-facing docs said |
| 13 | +so literally. `skill/authoring.md` and `skill/components.md` both named the |
| 14 | +jsDelivr URL as "the source of truth when the network is reachable", which is an |
| 15 | +instruction to read the whole document. |
| 16 | + |
| 17 | +Measured with `gpt-tokenizer` (`o200k_base`), that document is 13,634 tokens. |
| 18 | +The breakdown is what makes it a problem rather than a cost: |
| 19 | + |
| 20 | +| Part | Tokens | Share | |
| 21 | +| --- | ---: | --- | |
| 22 | +| JSON punctuation and indentation | 4,873 | 36% | |
| 23 | +| `example` | 4,174 | 31% | |
| 24 | +| `props` | 2,326 | 17% | |
| 25 | +| `purpose` | 1,645 | 12% | |
| 26 | +| `name`, `body`, `source` | 616 | 4% | |
| 27 | + |
| 28 | +Better than a third of it is structural syntax — quotes, braces, indentation, |
| 29 | +key names repeated 89 times. A machine parses that for free. A model pays for |
| 30 | +every token of it. |
| 31 | + |
| 32 | +The same facts already came out of the bin far cheaper. `htmdx components` |
| 33 | +lists the catalog in 1,600 tokens; `htmdx components Callout` answers in 50. |
| 34 | +Nothing needed building for the common case to cost 3% of what the docs |
| 35 | +recommended — the commands existed and the docs pointed past them. |
| 36 | + |
| 37 | +Two gaps kept the CLI from being a complete substitute. It took one name per |
| 38 | +invocation, so asking about five components meant five calls. And it had no way |
| 39 | +to answer "what does *this artifact* use", which is the question an edit starts |
| 40 | +from: an agent editing a file needs the grammar for the components in it, not |
| 41 | +for all 89. |
| 42 | + |
| 43 | +## Decision |
| 44 | + |
| 45 | +The CLI is the documented way to read the component contract. `components.json` |
| 46 | +keeps its `htmdx@2` envelope, its exact-version guarantee, and its place in the |
| 47 | +published package — it is bulk machine-readable data for tools that consume the |
| 48 | +whole catalog, and it is no longer what agent-facing documentation points at. |
| 49 | + |
| 50 | +`htmdx components` grows the two modes that make it sufficient: |
| 51 | + |
| 52 | +- `components <name...>` takes several names in one call. One unknown name |
| 53 | + fails the call rather than printing a partial answer that reads like a |
| 54 | + complete one. |
| 55 | +- `components --used <file>` prints the contract for exactly the components a |
| 56 | + file contains, taking the source from an artifact's |
| 57 | + `<script type="text/htmdx">` block or reading a bare source file directly. |
| 58 | + |
| 59 | +`--used` scans for capitalized tags instead of compiling. Compiling would be |
| 60 | +more precise, but the artifact whose contract someone needs is disproportionately |
| 61 | +the one that does not compile yet, and an answer that requires a working document |
| 62 | +is unavailable exactly when it is wanted. The same tolerance means a tag inside a |
| 63 | +code fence counts: over-reporting costs a few lines, and missing a component |
| 64 | +costs the answer. |
| 65 | + |
| 66 | +Measured across the shipped examples, `--used` costs 100 tokens (2 components) |
| 67 | +to 1,576 (16). `component-tour.html` is the outlier at 7,134, because it is a |
| 68 | +catalog demo that names 73 components — the case where reading everything is the |
| 69 | +correct answer, and where it still beats the manifest by half. |
| 70 | + |
| 71 | +`--format json` keeps emitting the bare entry for a single name. Several names |
| 72 | +and `--used` emit an array. Preserving the single-name shape keeps existing |
| 73 | +callers working, at the cost of a response shape that varies with the request. |
| 74 | + |
| 75 | +## Consequences |
| 76 | + |
| 77 | +- The realistic contract read for an edit drops from 13,634 tokens to 100-1,576. |
| 78 | +- Two documented sources of truth for one catalog is how they drift, so |
| 79 | + agent-facing docs name the CLI only. `components.json` stays documented where |
| 80 | + it is consumed as data: the package README and the integration topic. |
| 81 | +- `--used` reports what a file mentions, not what it renders. A component named |
| 82 | + only inside a code fence appears in the output. |
| 83 | +- The manifest's own size is unaddressed. Family examples are the next lever: |
| 84 | + 60 of 89 components sit in compound families whose canonical examples largely |
| 85 | + repeat each other (Avatar's three members ship one identical example, |
| 86 | + Breadcrumb's seven ship three), and 3,178 of the 4,174 example tokens belong |
| 87 | + to them. Deduplicating in a projection rather than in the manifest would keep |
| 88 | + the per-definition example validation this repo's build depends on. |
0 commit comments