Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD across contents and paths, upstream package/repo coordinates -> @golem15/msd-core and golem15com/msd-core. Deep links into upstream history, sibling upstream packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is. Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line, package/plugin identity, regenerated lockfile, install-tree fixtures, derived registries and benchmark baseline; migration checksum baseline re-locked (MSD keeps its own install state, so no install had applied the old sums); sort-order and regex-escaped expectations in tests adjusted.
5.0 KiB
Response-language coverage
Every workflow file must instruct the model to honour response_language in the prose a user
reads. npm run lint:response-language (chained into npm run lint:ci) enforces it across the
whole catalog. This page is the how-to and the rationale; the mechanics live in
scripts/lint-response-language-coverage.cjs
and its suite,
tests/response-language-coverage.test.cjs.
The shipped requirements are REQ-LANG-01..04 in
docs/features/response-language-config.md.
The problem this gate exists for
A directive worded around questions, prompts, and explanations covers the answer and leaves the running commentary in English. The user then reads a translated answer wrapped in English status updates, progress notes, and findings — which is the defect #2529 reports, not a stylistic preference. Before this gate, 44 workflows carried exactly that wording and the older lint certified it as coverage, so the gate was legitimising the bug it was meant to catch.
Hence the discriminator: a directive counts only if it names the narration class. Naming means
the word narration or the phrase between tool calls. Enumerating members of the class — status
updates, progress notes, findings — without naming the class does not satisfy it, because the
enumeration reads as a closed list and the class is open (REQ-LANG-04).
The four coverage forms
| Form | What it looks like | When it applies |
|---|---|---|
| Shared reference | @~/.claude/msd-core/references/response-language-directive.md on its own line |
The file is loaded eagerly — a top-level workflow. Preferred: one place to maintain the wording for the 42 files that take it. |
| Own inline directive | One line carrying the directive in the file's own words | The file already has one, or its prose needs local phrasing. Must pass all four predicates (below). |
| Pinned inline directive | The canonical line, byte for byte | A lazily-loaded mode/step/template that cannot prove inheritance. |
| Inherited | nothing in the file itself | A fragment whose parent workflow dispatches it from a read/execute context and is itself covered. |
Which form to use
Answer in this order:
- Is the file loaded eagerly? Take the shared reference. An
@-line is expanded when the file is loaded, so this is the cheapest correct answer for a top-level workflow. - Is it a fragment under
<workflow>/{modes,steps,templates}/? Then check whether<workflow>.mddispatches this exact path from a read/execute context and is itself covered. The catalog writes that stub two ways — rooted atmsd-core/workflows/, or relative to the catalog — and either one counts; a path with no read/execute/run verb ahead of it on the same line is a mention, not a dispatch, and proves nothing. If both hold, the fragment inherits — add nothing. The parent's directive is already in the loaded context by the time the fragment is read, so a second copy buys no coverage and gives the wording somewhere to drift. - Otherwise the fragment carries the pinned line, and its path joins
EXACT_INLINE_DIRECTIVE_WORKFLOWSin the lint. Do not reach for the@-reference here: an@-line inside a file that is itself read later is inert — it is text at that point, not an import.
Rule 2 decides the set in rule 3, and the suite enforces the boundary in both directions: a pinned
path that would have inherited fails
a pinned workflow is one that could not have inherited instead.
The pinned line
Apply response_language to all user-facing prose — narration between tool calls, status updates, progress notes, and findings included; preserve code, paths, and identifiers.
Byte for byte, on its own line. A pinned file may also switch to the shared reference if it ever becomes eagerly loaded — that is strictly better and the lint accepts it.
What an inline directive must contain
A single line must satisfy four independent predicates:
- the token
response_language, - an action verb — apply, use, present, translate, …,
- a user-output noun — prose, output, questions, findings, …,
- the narration class —
narrationorbetween tool calls.
All four on one line. The check is textual, not semantic: it reads vocabulary, not polarity.
When the lint reds
| Message | What to do |
|---|---|
N workflow(s) have no response-language coverage |
Pick a form from the table above for each listed path. |
N shared directive reference(s) no longer carry an actionable directive |
The reference itself was weakened. Fix the reference — one edit re-covers every file that imports it. |
cannot read the workflow directory / no workflow files found under |
Discovery failed. The lint fails closed on purpose: a run that inspected zero workflows cannot establish coverage. |
Run it locally before pushing:
npm run lint:response-language