Skip to content

fix(items): align the catalog with spec #80 — renames, resourceType, and the external LLM node - #28

Merged
justinmerrell merged 3 commits into
mainfrom
fix/spec-80-respell
Sep 12, 2026
Merged

justinmerrell merged 3 commits into
mainfrom
fix/spec-80-respell

Conversation

@justinmerrell

Copy link
Copy Markdown
Contributor

Why

musher-dev/specifications#80 merged on 2026-09-12. This suite fetches the spec bundles from main unpinned, so every item has failed validation since. That PR landed three ADRs:

  • ADR 0007: renames (metadata.revision, workload.type, componentRef, required/sensitive, sizeGiB, …), no null where omission means the same, and identifier grammars.
  • ADR 0009: semanticType becomes a namespaced resourceType.
  • ADR 0019: spec.external, a node the platform does not run, with valueFrom: INPUT, size: null, and new semantic rules.

This PR is the catalog half. The platform follows in musher-dev/platform#2650–#2653.

Commits

  1. fix(items): re-spell the corpus and enforce the new rules (Re-spell every item for the ADR 0007 renames and the new identifier grammars #27)
    • All 39 documents re-spelled mechanically. No behaviour change, so no revisions bumped.
    • postgres's and redis's semanticType: POSTGRES/REDIS become dev.musher.postgresql.address and dev.musher.redis.address. Both values are host:port, and Re-spell every item for the ADR 0007 renames and the new identifier grammars #27's note that no item used the field was wrong about these two.
    • The harness gains BP-CONN-001 (ERR_INPUT_NOT_CONNECTABLE), BP-NODE-002 (ERR_CONFLICTING_NODE_COMPUTE), COMP-OUT-002/003 (ERR_UNKNOWN_INPUT_REFERENCE, ERR_INPUT_NOT_REFERENCEABLE) and §5.3's ERR_INCOMPATIBLE_PARAMETER_RESOURCE_TYPE.
    • ERR_INCOMPATIBLE_SEMANTIC_TYPE becomes ERR_INCOMPATIBLE_RESOURCE_TYPE, and a parameter carrying a platformDefault now guarantees a value.
    • rules.test.ts has a case for each rule.
  2. feat(items): llm-endpoint (A standalone llm-endpoint item: one external component, published as a building block #24)
    • A vendor-neutral external component, published as a COMPONENT item with no blueprint.
    • The catalog's layout rule narrows to "a BLUEPRINT item must hold one". A one-node blueprint around a node that runs nothing would deploy nothing.
  3. feat(items): open-webui wired to that node (Wire an LLM endpoint node into the four items that need one — open-webui first #25)
    • Two participants: models (size: null) and web. Its URL, key and model name are wired from one fromRole.
    • The Ollama workaround inputs are removed, which retires the http://ollama:11434 default that named a sibling service the deployment never contained.
    • A corpus test keeps every copy of an external component with the same resourceType on one contract.

Worth a reviewer's attention

  • One PR, not one item per PR. Items and harness interlock, and neither goes green alone. The external-node items ride along at the owner's request.
  • Open WebUI persistence, checked against the v0.10.2 source. Config.get (models/config.py) prefers a stored row over the env-seeded default. The endpoint therefore holds until an admin saves Admin Settings → Connections, and is ignored after that, including on redeploy. The listing says so.
  • Checked against the spec's own conformance trees. blueprint semantic 010/013/015/022/024–029 and component semantic 016–018 all emit exactly their diagnostics.json codes (script not committed).
  • Sequencing. The platform's catalog sync reads the old field names until musher-dev/platform#2653 lands. Merging this before that means the sync rejects every item in the window between the two.

Not in this PR

Closes #27. Closes #24. Refs #25.

Test plan

  • npm run typecheck
  • npm test against live spec main: 442 tests, 441 pass, 1 intentional skip (the blueprint check on llm-endpoint)
  • task test:item -- llm-endpoint, open-webui, and the harness run over the spec conformance trees
  • CI green on this PR

🤖 Generated with Claude Code

https://claude.ai/code/session_01DkZpdcmz7gfNrqKaA36Noa

justinmerrell and others added 3 commits September 12, 2026 16:23
musher-dev/spec #80 merged on 2026-09-12 and landed three breaking changes.
This suite fetches the spec bundles from main unpinned, so every item went red
that day. ADR 0007 renames the fields, ADR 0009 turns semanticType into a
namespaced resourceType, and ADR 0019 adds the external component node. The
items and the harness interlock, so they land together: neither half goes
green without the other.

Items, mechanical and with no behaviour change, so no revision is bumped:
- metadata.version becomes metadata.revision in all 39 documents
- workload.kind becomes workload.type, isRequired/isSensitive become
  required/sensitive, sizeGib becomes sizeGiB
- the blueprint node's component becomes componentRef
- the `value: null` on postgres's and redis's DERIVED outputs is dropped
- their semanticType POSTGRES/REDIS become dev.musher.postgresql.address and
  dev.musher.redis.address. Both values are host:port, not connection strings,
  and #27's "no item uses the field" was wrong about these two.

Harness (tests/lib/semantic.ts):
- reads componentRef and metadata.revision; ERR_VERSION_MISMATCH keeps its code
- ERR_INCOMPATIBLE_SEMANTIC_TYPE becomes ERR_INCOMPATIBLE_RESOURCE_TYPE
- BP-CONN-001: ERR_INPUT_NOT_CONNECTABLE when a wire fills a non-CONNECTION input
- BP-NODE-002: ERR_CONFLICTING_NODE_COMPUTE, where size is null iff the
  component is external
- COMP-OUT-002/003: ERR_UNKNOWN_INPUT_REFERENCE and ERR_INPUT_NOT_REFERENCEABLE
- blueprint §5.3: ERR_INCOMPATIBLE_PARAMETER_RESOURCE_TYPE, and a parameter
  carrying a platformDefault now guarantees a value

rules.test.ts moves to the new spelling and gains a case for each new rule.
Each clean case also validates structurally against the fetched bundles.
Checked against the spec's own conformance trees (blueprint semantic
010/013/015/022/024-029, component semantic 016-018): every emitted code set
matches diagnostics.json.

Refs #27.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DkZpdcmz7gfNrqKaA36Noa
The first item on spec ADR 0019's external node. It is one component declaring
spec.external with resourceType dev.musher.llm.chat-completions. Its USER inputs
are baseUrl, apiKey and an optional model, and it republishes each as a
valueFrom: INPUT output, so a consumer wiring two of them from one fromRole
takes them from one source by construction.

The item carries no vendor name anywhere, and its listing says plainly that
nothing validates the address or connects to it, which component §11 makes a
rule rather than an omission. There is no icon: ICONS.md covers upstream marks
only, and this item has no upstream.

It holds no blueprint.yaml. Listing §3.1 permits that for a COMPONENT item, and
a one-node blueprint around a node that runs nothing would deploy nothing. The
catalog's layout rule therefore narrows to "a BLUEPRINT item must hold one".
The structural blueprint check skips such an item, and the README records the
rule beside the postgres/redis wrapping it keeps.

Refs #24.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DkZpdcmz7gfNrqKaA36Noa
…ling that is not there

open-webui's ollamaBaseUrl defaulted to http://ollama:11434, a sibling service
this deployment never contained, and its own description said so. Spec ADR
0019 makes the real shape expressible, so the blueprint now has two
participants:

- models: a copy of llm-endpoint's external component, size: null
- web: Open WebUI, whose new CONNECTION inputs openaiApiBaseUrl, openaiApiKey
  and the optional defaultModels are wired from models, each carrying the
  node's resourceType

parameters stays {}, so the install form is derived: the endpoint's URL, key and
model name asked for once, together, then the generated signing key (ui.order
moves from 3 to 4 so it sorts after them).

enableOllamaApi and ollamaBaseUrl are gone. ENABLE_OLLAMA_API is pinned false
and ENABLE_OPENAI_API pinned true, and an Ollama server is reached through the
node via its OpenAI-compatible /v1.

Checked against the v0.10.2 source: OPENAI_API_BASE_URL and OPENAI_API_KEY feed
DEFAULT_CONFIG, and Config.get prefers a stored row over that default. The
endpoint therefore holds until an admin saves Connections in the UI and is
ignored after that, including on redeploy. The listing says so, rather than
implying a redeploy re-points the UI.

The copy is structural rather than chosen: a repo-local reference cannot leave
its item. A new corpus test holds every external component sharing a
resourceType to one contract, so the copies cannot drift.

Revisions: component 3 to 4, blueprint and listing 1 to 2.

Refs #25.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DkZpdcmz7gfNrqKaA36Noa
@justinmerrell
justinmerrell merged commit a4f4ba3 into main Sep 12, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant