A whitespace-bounded /name token anywhere in a claimed user message,
naming a user-invocable skill in the workspace directory, now injects that
skill's renderSkillContent as instructions context appended after every
other injection of the step — the same agent/pre-step seam the catalog,
workspace instructions, and the runtime snapshot ride. Closed-set matching
mirrors the command registry (a miss stays plain prose), only user-source
messages are scanned, the policy check runs on the loaded definition, and
this is the sole entry point for disable-model-invocation skills. The
catalog's no-reload sentence now names the gesture boundary.
The user-turn predicate (opensUserTurn) unifies the three parallel
consumers a new node kind silently missed — produced-files turn reset,
retry liveness, own-words force-scroll — so a skill invocation behaves as
the turn opener it is. The menu marker resolves through ctx.locale.bind
instead of a hand-rolled snapshot lookup; the dead legacy <skill> render
arm goes with the removal cut; command-over-skill name precedence is now
documented at the matchEnter seam; and the emptied replacement catalog
keeps the no-reload sentence, with the never-published residual recorded
in the Agent Note.
Bilingual README updates for the four touched packages (ui-skill's claim
flow and deterministic-injection model experience, the apiproxy skills
domain, the shared renderSkillContent seam export, the catalog stitch
sentence), the implemented Agent Note triplet recording the decision and
its peer-product evidence, and the regenerated catalogs/graphs.
- An empty replacement catalog is a real catalog: `renderCatalogUpdate()`
publishes zero entries when the last skill disappears, and falling back
would hide that every earlier name was retired.
- The opaque fallback keeps a `form` declaration this version cannot present.
It is the one place a newer or foreign log's declared shape would otherwise
vanish from the UI entirely, since the row marker is also absent there.
- An instruction change with an unrecognized `action` disqualifies the record.
The action decides the word the row shows, so an unknown one would be
presented as loaded or updated.
- The catalog list bounds itself and reports the withheld count. Entry count
is unbounded and the scrollport bounds height, not node count.
- A catalog message keeps content blocks this version does not know, instead
of dropping model-visible content the extensible union may carry.
- `core.md` defines `ContextFormed`, the interface actually carrying the
optional field, beside `ContextForm`.
- The superseded-in-part bullet states the affected clauses as one rule rather
than enumerating them; two rounds of enumeration each missed some, which is
the shape being fragile rather than the list being wrong.
- The note records the one migration case that does not self-heal: an
old-format catalog as the only one, with an empty current view, leaves a
stale catalog nothing replaces.
Injected context reached the transcript as one anonymous shape whose
expanded body was the whole message serialized as inline JSON, so the
escaping collapsed the only readable part — the model-facing prose —
into a single line.
`MessageSource` gains an optional producer-declared `form`, a small
semantic vocabulary of information shapes independent of `kind`: kind
says who produced the context, form says what shape it is, so several
producers may share one presentation. Two values ship.
`instructions` (workspace-context) lists the reconciled files above
their text and keeps the `<system-reminder>` framing verbatim, because
the framing is part of what the model read. `catalog` moves dsh-tool-skill
off the shared plugin kind onto a `skill-catalog` source carrying the
published name/description entries, and the body lists those instead of
re-parsing `<available_skills>` out of the prose. Catalog identity moves
with it: the republish digest now covers the durable entries, deleting
the text-slicing that recovered them from a logged message.
Everything else renders the opaque body — the model-facing text with its
real line breaks, then the remaining provenance as fields. That is the
documented default, not a leftover: a resumed, forked, or foreign log
must render whether or not its producer is mounted here, which is why
the classification lives in the durable source rather than a client-side
table keyed by producer.