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.
90 lines
5.0 KiB
Markdown
90 lines
5.0 KiB
Markdown
# 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`](../../scripts/lint-response-language-coverage.cjs)
|
|
and its suite,
|
|
[`tests/response-language-coverage.test.cjs`](../../tests/response-language-coverage.test.cjs).
|
|
The shipped requirements are REQ-LANG-01..04 in
|
|
[`docs/features/response-language-config.md`](../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:
|
|
|
|
1. **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.
|
|
2. **Is it a fragment under `<workflow>/{modes,steps,templates}/`?** Then check whether
|
|
`<workflow>.md` dispatches this exact path from a read/execute context *and* is itself covered.
|
|
The catalog writes that stub two ways — rooted at `msd-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.
|
|
3. **Otherwise the fragment carries the pinned line**, and its path joins
|
|
`EXACT_INLINE_DIRECTIVE_WORKFLOWS` in 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:
|
|
|
|
1. the token `response_language`,
|
|
2. an action verb — apply, use, present, translate, …,
|
|
3. a user-output noun — prose, output, questions, findings, …,
|
|
4. the narration class — `narration` or `between 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
|
|
```
|