Multilingual Spec Skill
Manages specifications inside a project's spec/ folder. Layout: one folder per spec, one file per language. Specs may optionally be grouped under a one-level topic folder.
Why this is a skill, not an agent
- Mid-flow interactivity is the contract — duplicate-check confirmation ("extend existing, supersede, or proceed as new?"), translation review, slug-rename gates, and drift-resolution choices are all per-step user dialogues; an agent's fire-and-forget contract would lose them.
- Persistent on-disk artifact is the deliverable — every operation writes spec files (canonical + translations) under
spec/[<topic>/]<slug>/<lang>.md in pairs and may also regenerate spec/README.md; skills own persistent state.
- Multilingual sync requires conversational context — when a translation drifts from the canonical, the skill negotiates the resolution with the user (lift the change, discard the edit) rather than picking silently; that gating only fits in a skill.
- Counter-dimension considered: translation itself benefits from a narrow specialised prompt, but the load-bearing dimension is the canonical-vs-translation flow control, not the per-string translation quality — skill wins; if translation quality ever needs sharpening, dispatch a translation agent from inside this skill rather than reshaping it.
spec/
├── .spec-config.yml
├── README.md # autogenerated index
└── [<topic>/]<slug>/
├── en.md # canonical
└── de.md # translation
Topic folders group related specs (for example spec/api/, spec/claude/). Only one level of topic nesting is allowed. Without a topic, specs live directly under spec/<slug>/.
Defaults
- Canonical language:
en
- Translations:
de
- Spec root:
spec/ (relative to project root)
Overridable via spec/.spec-config.yml (validated against schemas/spec-config-v1.0.schema.yaml by scripts/validate_schemas.py):
canonical_language: en
languages: [en, de]
spec_root: spec
inherits: # optional — portfolio-inherited spec layer
- source: nolte-shared # the hub plugin whose portfolio-scope specs are inherited
ref: v0.1.8 # tag-pinned hub release (never a floating branch)
overrides: # optional declared deviations from inherited specs
- spec: project/branching-model
section: "§Branch roles"
reason: "trunk-based repo; the long-lived develop branch role does not apply"
local: spec/project/branching-model/override.md
If the file is missing, use the defaults. Create it on the first create operation so downstream projects have a visible extension point.
Portfolio-inherited spec layer (the inherits: key)
Per spec/project/portfolio-inherited-spec-layer/, a consumer repo may reference portfolio-wide specs that live canonically in a hub (claude-shared) instead of copying them. The contract this skill must respect:
inherits: is optional and absent on the hub itself (the hub is the root; resolution terminates there). A consumer's effective spec set is local ∪ inherited — the local spec/ tree plus every hub spec whose canonical file carries a Portfolio-Scope: portfolio header, resolved at each source's pinned ref from ${CLAUDE_PLUGIN_ROOT}/spec/.
- A spec is inheritable only if its canonical file carries a
Portfolio-Scope: header line (sibling to Status:), value portfolio or local. A spec with no such line defaults to local. When creating or editing a spec, preserve any Portfolio-Scope: line; do not add portfolio scope unprompted (promoting a spec to portfolio-wide is an explicit maintainer act).
- Inherited specs are authoritative by default. A consumer deviates only through a declared
overrides: record (with a non-empty reason:) plus a local override file containing only the replaced sections — never by copying the inherited spec into the local tree, and never by an undeclared same-key local spec.
- A requirement marked
[locked] immediately after its RFC 2119 keyword (- **MUST** [locked] …) is non-overridable downstream; never author an override against a locked section.
User-language policy
- Detect the user's language from their message and reply in that language.
- Spec files on disk always exist in every configured language. The canonical file is authoritative; translations must mirror it semantically.
- If the user describes a feature in a non-canonical language, still produce the canonical version first, then translate.
Slug and topic rules
- Slug: ASCII kebab-case, derived from the canonical EN title. Example: user says "Benutzer-Authentifizierung" → canonical title "User Authentication" → slug
user-authentication.
- Topic (optional): ASCII kebab-case, chosen to group related specs. Use an existing topic if one fits; otherwise propose a new one to the user before creating it.
- Slugs and topics are stable. Renaming is a conscious operation—confirm with the user before moving folders.
Operations
1. Create
Take the user's description (any language).
Duplicate check first (see operation 5). If clear overlap exists, stop and ask: extend existing, supersede, or proceed as new. Show candidate paths.
Draft the canonical spec from templates/spec.template.md, filling every section with content from the user's description. Mark unknowns explicitly instead of inventing.
Ground every load-bearing claim the draft makes about the working copy — a cause, a state ("this component is unreachable"), an existence ("the helper is already there"), an absence ("no caller remains"). Per spec/claude/claim-provenance/, write each such claim as established, naming the observation that established it (a command with its output, or a file:line), or as unestablished, naming the observation that would settle it and stating it wasn't made. When that observation is cheap with the means at hand — a Grep, a Read, one command — make it rather than publish the claim unestablished; the unestablished exit obliges the spec to say what makes the observation expensive. The same rule governs prose that operation 2 lifts into the canonical, and translations mirror the canonical's provenance markers unchanged.
Translate into each other configured language. Translation preserves structure (headings, requirement order, checklist items). RFC 2119 keywords stay in English and are glossed in-language, for example MUSS [MUST], SOLLTE [SHOULD], KANN [MAY].
Write spec/[<topic>/]<slug>/<lang>.md for every configured language in a single operation—never leave a spec partially written.
Regenerate spec/README.md (operation 4).
Confirm in the user's language with relative paths.
2. Update / prevent drift
- The canonical version is the only source of truth.
- If the user edits the canonical version: regenerate every translation from it.
- If the user edits a translation directly: warn that changes must flow through the canonical. Offer:
- (a) lift the semantic change into the canonical, then regenerate all translations, or
- (b) discard the translation edit and regenerate from the canonical.
- After any update operation, all language files for that spec must match structurally and semantically.
- Every changed requirement is followed to what restates it, in the same pass. A spec restates its own rules in several places, and an edit that reaches the rule but not its restatements leaves the corpus asserting the old and the new version at once. Before the update is finished, walk each changed requirement through:
- the acceptance criteria that roll it up — a criterion carrying an exception the requirement no longer has is the single most common form of this defect;
- any stated count the change invalidates ("seven dimensions", "the three grounding specs", "these four decisions");
- every cross-reference to the changed rule, including
§X anchors that may now point at the wrong section;
- the artifacts outside the spec that restate it — a skill's
references/ digest, a checklist, a template — since they go stale silently and no spec-level check sees them.
- This walk is not optional tidying:
spec/project/spec-readiness/ §Dimension 3 flags a stale restatement as a Warning, because a downstream consumer that reads the acceptance criterion as the gate will implement the abandoned rule. For a skill's own supporting files, spec/claude/skill-management/ §"Progressive disclosure & file references" carries the matching obligation and spec/claude/skill-review/ verifies it against the skill's SKILL.md. Keeping a skill in step with a spec this edit changed has no automated gate at all — that is why it belongs in this walk, done by the author, in the same pass.
3. Drift check
A. Translation-vs-canonical drift (always):
- For every spec folder, compare each translation to the canonical:
- Same headings in the same order
- Same count of requirement bullets and acceptance-criteria checkboxes
- Same requirement IDs / ordering
- Report mismatches. Offer to regenerate the affected translations from the canonical.
B. Local-vs-inherited drift (only when spec/.spec-config.yml carries an inherits: key; per spec/project/portfolio-inherited-spec-layer/ §"Drift detection"):
- Run against each source's pinned
ref, not against the hub's current develop, so conformance is judged against the exact upstream the consumer pinned. Resolve the inherited corpus from ${CLAUDE_PLUGIN_ROOT}/spec/ at that release.
- Run the comparisons canonical-to-canonical (in the hub's canonical language); never compare against a translation view, which is derived and never authoritative.
- For each
inherits: source, report:
- Critical — a tracked verbatim copy of an inherited spec in the local
spec/ tree; a local spec sharing a logical key <topic>/<slug> with an inherited portfolio-scope spec without a declared override (undeclared divergence); an overrides: record whose section contains a [locked] requirement; a cross-reference that is a ghost reference or a duplicate-key collision in the combined namespace; a cyclic inheritance declaration.
- Warning — an
overrides: spec that does not resolve to a portfolio-scope spec at the pinned ref (broken reference); an overridden section that no longer exists upstream (stale override); an empty or missing reason; a floating or absent ref.
- Severities are the canonical scale from
spec/claude/review-plan/ §"Severity scale"; surface findings for spec-drift-audit to reconcile. Treat any open Critical as a gate failure (the silent fork must not reach develop).
4. Regenerate index
- Write
spec/README.md with a table:
- Topic | Slug | Title (per language) | Status | Scope | Last updated
Status is read from the Status: line in the canonical file's header. If absent, use draft.
Scope is read from the Portfolio-Scope: line in the canonical file's header (portfolio | local). If absent, use local (the default per spec/project/portfolio-inherited-spec-layer/). This column makes the inheritable subset visible at a glance.
Last updated uses git log -1 --format=%cs -- <canonical file>. If the file is untracked, use unversioned.
- Don't invent values—if something can't be read, mark it
unknown.
5. Coverage / duplicate check
Can be invoked standalone ("is X already covered?") or as step 2 of create.
- Extract salient nouns/verbs from the user's requirement.
Grep the canonical files under spec/ for those terms.
Read every canonical file with more than one keyword hit and assess semantic overlap against the user's requirement—not just keyword overlap.
- Respond in the user's language with:
- which existing specs fully cover the requirement,
- which partially cover it and the specific gap,
- none, if no overlap.
Template maintenance
The starter template at templates/spec.template.md is deliberately minimal. Every time this skill is used for a non-trivial spec, briefly assess fitness:
- If real specs repeatedly need a section the template lacks → propose adding it.
- If template sections stay empty across most specs → propose trimming them.
Don't silently modify the template. Surface the proposal to the user and let them decide.
Examples
- Read
examples/01-create-new-spec-multilingual.md when creating a new spec from scratch in all configured languages simultaneously.
- Read
examples/02-translate-existing-spec.md when translating an existing canonical spec into a second language.
- Read
examples/03-drift-check-translations.md when checking whether a translation has drifted from the canonical version.
Gotchas
spec/.spec-config.yml is the authoritative language source: the canonical language and the full language list come from this file; if it is missing, fall back to en / [en, de] defaults but create the config on the first create operation — guessing language settings from the conversation language will produce wrong canonical paths in multilingual repos.
- Canonical file must be written first, translations second: if a spec is only partially written (canonical exists, translations absent), the repo is in a broken state per the "always together" rule; ensure the write step covers every configured language in a single operation or rolls back entirely.
git log -1 for Last updated requires the file to be tracked: untracked spec files produce no git log output; the index regeneration step must handle this case by marking Last updated as unversioned rather than leaving the cell empty or erroring out.
Resumability
Per spec/claude/resumable-work/, this skill is resumable: true. State is persisted to .resume/spec/<run-id>.yml after every successful user-approval gate and after each named phase boundary. On re-invocation, scan that directory for files with status: in_progress whose inputs: snapshot matches the current invocation; if one matches, prompt the operator with Resume run <run_id> from phase <phase> (last checkpoint <last_checkpoint_at>)? [resume / start-new / discard]. The state-file envelope (schema_version, run_id, inputs, phase, decisions[], status, ...) and the fail-closed semantics on schema or YAML errors are load-bearing in the spec; don't duplicate those rules here.
Hard rules
- Canonical and translation files are always created and updated together. Never leave a spec with only one language file on disk.
- After any operation that touches spec content, translations must be in sync with the canonical—no drift, ever.
- Slugs are ASCII kebab-case and never change silently.
- Never modify
spec/.spec-config.yml without telling the user.
- Never invent
Status, dates, or requirement content. Read from source or mark unknown.
1---2name: spec3description: Creates, translates, indexes, deduplicates, and drift-checks multilingual specifications stored under the project's spec/ folder. Invoke when the user wants to write a new spec, update or translate an existing one, check whether a requirement is already covered, regenerate the spec index, or verify that translations are still in sync with the canonical version. Supports writing the request in any configured language; specs on disk always exist in all configured languages, with one canonical source and the rest as translations kept strictly in sync. Don't use for readiness audit (contradiction detection, audience fit, AC coverage) — use spec-readiness-reviewer. Supports resume on re-invocation per `spec/claude/resumable-work/`.4---56# Multilingual Spec Skill78Manages specifications inside a project's `spec/` folder. Layout: one folder per spec, one file per language. Specs may optionally be grouped under a one-level topic folder.910## Why this is a skill, not an agent1112- **Mid-flow interactivity is the contract** — duplicate-check confirmation ("extend existing, supersede, or proceed as new?"), translation review, slug-rename gates, and drift-resolution choices are all per-step user dialogues; an agent's fire-and-forget contract would lose them.13- **Persistent on-disk artifact is the deliverable** — every operation writes spec files (canonical + translations) under `spec/[<topic>/]<slug>/<lang>.md` in pairs and may also regenerate `spec/README.md`; skills own persistent state.14- **Multilingual sync requires conversational context** — when a translation drifts from the canonical, the skill negotiates the resolution with the user (lift the change, discard the edit) rather than picking silently; that gating only fits in a skill.15- Counter-dimension considered: translation itself benefits from a narrow specialised prompt, but the load-bearing dimension is the canonical-vs-translation flow control, not the per-string translation quality — skill wins; if translation quality ever needs sharpening, dispatch a translation agent from inside this skill rather than reshaping it.1617```18spec/19├── .spec-config.yml20├── README.md # autogenerated index21└── [<topic>/]<slug>/22 ├── en.md # canonical23 └── de.md # translation24```2526Topic folders group related specs (for example `spec/api/`, `spec/claude/`). Only one level of topic nesting is allowed. Without a topic, specs live directly under `spec/<slug>/`.2728## Defaults2930- Canonical language: `en`31- Translations: `de`32- Spec root: `spec/` (relative to project root)3334Overridable via `spec/.spec-config.yml` (validated against `schemas/spec-config-v1.0.schema.yaml` by `scripts/validate_schemas.py`):3536```yaml37canonical_language: en38languages: [en, de]39spec_root: spec40inherits: # optional — portfolio-inherited spec layer41 - source: nolte-shared # the hub plugin whose portfolio-scope specs are inherited42 ref: v0.1.8 # tag-pinned hub release (never a floating branch)43 overrides: # optional declared deviations from inherited specs44 - spec: project/branching-model45 section: "§Branch roles"46 reason: "trunk-based repo; the long-lived develop branch role does not apply"47 local: spec/project/branching-model/override.md48```4950If the file is missing, use the defaults. Create it on the first `create` operation so downstream projects have a visible extension point.5152### Portfolio-inherited spec layer (the `inherits:` key)5354Per `spec/project/portfolio-inherited-spec-layer/`, a consumer repo may **reference** portfolio-wide specs that live canonically in a hub (claude-shared) instead of copying them. The contract this skill must respect:5556- `inherits:` is **optional** and absent on the hub itself (the hub is the root; resolution terminates there). A consumer's effective spec set is `local ∪ inherited` — the local `spec/` tree plus every hub spec whose canonical file carries a `Portfolio-Scope: portfolio` header, resolved at each source's pinned `ref` from `${CLAUDE_PLUGIN_ROOT}/spec/`.57- A spec is inheritable only if its **canonical** file carries a `Portfolio-Scope:` header line (sibling to `Status:`), value `portfolio` or `local`. A spec with no such line defaults to `local`. When creating or editing a spec, preserve any `Portfolio-Scope:` line; do not add `portfolio` scope unprompted (promoting a spec to portfolio-wide is an explicit maintainer act).58- Inherited specs are **authoritative by default**. A consumer deviates only through a declared `overrides:` record (with a non-empty `reason:`) plus a `local` override file containing only the replaced sections — never by copying the inherited spec into the local tree, and never by an undeclared same-key local spec.59- A requirement marked `[locked]` immediately after its RFC 2119 keyword (`- **MUST** [locked] …`) is **non-overridable downstream**; never author an override against a locked section.6061## User-language policy6263- Detect the user's language from their message and reply in that language.64- Spec files on disk always exist in every configured language. The canonical file is authoritative; translations must mirror it semantically.65- If the user describes a feature in a non-canonical language, still produce the canonical version first, then translate.6667## Slug and topic rules6869- **Slug**: ASCII kebab-case, derived from the canonical EN title. Example: user says "Benutzer-Authentifizierung" → canonical title "User Authentication" → slug `user-authentication`.70- **Topic** (optional): ASCII kebab-case, chosen to group related specs. Use an existing topic if one fits; otherwise propose a new one to the user before creating it.71- Slugs and topics are stable. Renaming is a conscious operation—confirm with the user before moving folders.7273## Operations7475### 1. Create76771. Take the user's description (any language).782. **Duplicate check first** (see operation 5). If clear overlap exists, stop and ask: extend existing, supersede, or proceed as new. Show candidate paths.793. Draft the canonical spec from `templates/spec.template.md`, filling every section with content from the user's description. Mark unknowns explicitly instead of inventing.8081 Ground every load-bearing claim the draft makes about the working copy — a cause, a state ("this component is unreachable"), an existence ("the helper is already there"), an absence ("no caller remains"). Per `spec/claude/claim-provenance/`, write each such claim as **established**, naming the observation that established it (a command with its output, or a `file:line`), or as **unestablished**, naming the observation that would settle it and stating it wasn't made. When that observation is cheap with the means at hand — a `Grep`, a `Read`, one command — make it rather than publish the claim unestablished; the unestablished exit obliges the spec to say what makes the observation expensive. The same rule governs prose that operation 2 lifts into the canonical, and translations mirror the canonical's provenance markers unchanged.824. Translate into each other configured language. Translation preserves structure (headings, requirement order, checklist items). RFC 2119 keywords stay in English and are glossed in-language, for example `MUSS [MUST]`, `SOLLTE [SHOULD]`, `KANN [MAY]`.835. Write `spec/[<topic>/]<slug>/<lang>.md` for every configured language in a single operation—never leave a spec partially written.846. Regenerate `spec/README.md` (operation 4).857. Confirm in the user's language with relative paths.8687### 2. Update / prevent drift8889- The canonical version is the only source of truth.90- If the user edits the canonical version: regenerate every translation from it.91- If the user edits a translation directly: warn that changes must flow through the canonical. Offer:92 - (a) lift the semantic change into the canonical, then regenerate all translations, or93 - (b) discard the translation edit and regenerate from the canonical.94- After any update operation, all language files for that spec must match structurally and semantically.95- **Every changed requirement is followed to what restates it, in the same pass.** A spec restates its own rules in several places, and an edit that reaches the rule but not its restatements leaves the corpus asserting the old and the new version at once. Before the update is finished, walk each changed requirement through:96 - the **acceptance criteria** that roll it up — a criterion carrying an exception the requirement no longer has is the single most common form of this defect;97 - any **stated count** the change invalidates ("seven dimensions", "the three grounding specs", "these four decisions");98 - every **cross-reference** to the changed rule, including `§X` anchors that may now point at the wrong section;99 - the **artifacts outside the spec** that restate it — a skill's `references/` digest, a checklist, a template — since they go stale silently and no spec-level check sees them.100- This walk is not optional tidying: `spec/project/spec-readiness/` §Dimension 3 flags a stale restatement as a `Warning`, because a downstream consumer that reads the acceptance criterion as the gate will implement the abandoned rule. For a skill's own supporting files, `spec/claude/skill-management/` §"Progressive disclosure & file references" carries the matching obligation and `spec/claude/skill-review/` verifies it against the skill's `SKILL.md`. Keeping a skill in step with a spec *this* edit changed has no automated gate at all — that is why it belongs in this walk, done by the author, in the same pass.101102### 3. Drift check103104**A. Translation-vs-canonical drift** (always):105106- For every spec folder, compare each translation to the canonical:107 - Same headings in the same order108 - Same count of requirement bullets and acceptance-criteria checkboxes109 - Same requirement IDs / ordering110- Report mismatches. Offer to regenerate the affected translations from the canonical.111112**B. Local-vs-inherited drift** (only when `spec/.spec-config.yml` carries an `inherits:` key; per `spec/project/portfolio-inherited-spec-layer/` §"Drift detection"):113114- Run **against each source's pinned `ref`**, not against the hub's current `develop`, so conformance is judged against the exact upstream the consumer pinned. Resolve the inherited corpus from `${CLAUDE_PLUGIN_ROOT}/spec/` at that release.115- Run the comparisons **canonical-to-canonical** (in the hub's canonical language); never compare against a translation view, which is derived and never authoritative.116- For each `inherits:` source, report:117 - **Critical** — a tracked verbatim copy of an inherited spec in the local `spec/` tree; a local spec sharing a logical key `<topic>/<slug>` with an inherited `portfolio`-scope spec **without** a declared override (undeclared divergence); an `overrides:` record whose `section` contains a `[locked]` requirement; a cross-reference that is a ghost reference or a duplicate-key collision in the combined namespace; a cyclic inheritance declaration.118 - **Warning** — an `overrides:` `spec` that does not resolve to a `portfolio`-scope spec at the pinned `ref` (broken reference); an overridden `section` that no longer exists upstream (stale override); an empty or missing `reason`; a floating or absent `ref`.119- Severities are the canonical scale from `spec/claude/review-plan/` §"Severity scale"; surface findings for `spec-drift-audit` to reconcile. Treat any open Critical as a gate failure (the silent fork must not reach `develop`).120121### 4. Regenerate index122123- Write `spec/README.md` with a table:124 - Topic | Slug | Title (per language) | Status | Scope | Last updated125- `Status` is read from the `Status:` line in the canonical file's header. If absent, use `draft`.126- `Scope` is read from the `Portfolio-Scope:` line in the canonical file's header (`portfolio` | `local`). If absent, use `local` (the default per `spec/project/portfolio-inherited-spec-layer/`). This column makes the inheritable subset visible at a glance.127- `Last updated` uses `git log -1 --format=%cs -- <canonical file>`. If the file is untracked, use `unversioned`.128- Don't invent values—if something can't be read, mark it `unknown`.129130### 5. Coverage / duplicate check131132Can be invoked standalone ("is X already covered?") or as step 2 of create.1331341. Extract salient nouns/verbs from the user's requirement.1352. `Grep` the canonical files under `spec/` for those terms.1363. `Read` every canonical file with more than one keyword hit and assess semantic overlap against the user's requirement—not just keyword overlap.1374. Respond in the user's language with:138 - which existing specs fully cover the requirement,139 - which partially cover it and the specific gap,140 - none, if no overlap.141142## Template maintenance143144The starter template at `templates/spec.template.md` is deliberately minimal. Every time this skill is used for a non-trivial spec, briefly assess fitness:145146- If real specs repeatedly need a section the template lacks → propose adding it.147- If template sections stay empty across most specs → propose trimming them.148149Don't silently modify the template. Surface the proposal to the user and let them decide.150151## Examples152153- Read `examples/01-create-new-spec-multilingual.md` when creating a new spec from scratch in all configured languages simultaneously.154- Read `examples/02-translate-existing-spec.md` when translating an existing canonical spec into a second language.155- Read `examples/03-drift-check-translations.md` when checking whether a translation has drifted from the canonical version.156157## Gotchas158159- **`spec/.spec-config.yml` is the authoritative language source**: the canonical language and the full language list come from this file; if it is missing, fall back to `en` / `[en, de]` defaults but create the config on the first `create` operation — guessing language settings from the conversation language will produce wrong canonical paths in multilingual repos.160- **Canonical file must be written first, translations second**: if a spec is only partially written (canonical exists, translations absent), the repo is in a broken state per the "always together" rule; ensure the write step covers every configured language in a single operation or rolls back entirely.161- **`git log -1` for `Last updated` requires the file to be tracked**: untracked spec files produce no git log output; the index regeneration step must handle this case by marking `Last updated` as `unversioned` rather than leaving the cell empty or erroring out.162163## Resumability164165Per `spec/claude/resumable-work/`, this skill is `resumable: true`. State is persisted to `.resume/spec/<run-id>.yml` after every successful user-approval gate and after each named phase boundary. On re-invocation, scan that directory for files with `status: in_progress` whose `inputs:` snapshot matches the current invocation; if one matches, prompt the operator with `Resume run <run_id> from phase <phase> (last checkpoint <last_checkpoint_at>)? [resume / start-new / discard]`. The state-file envelope (`schema_version`, `run_id`, `inputs`, `phase`, `decisions[]`, `status`, ...) and the fail-closed semantics on schema or YAML errors are load-bearing in the spec; don't duplicate those rules here.166167## Hard rules168169- Canonical and translation files are always created and updated **together**. Never leave a spec with only one language file on disk.170- After any operation that touches spec content, translations must be in sync with the canonical—no drift, ever.171- Slugs are ASCII kebab-case and never change silently.172- Never modify `spec/.spec-config.yml` without telling the user.173- Never invent `Status`, dates, or requirement content. Read from source or mark unknown.