SPEC-0019: qmd-Native Skills
Overviewโ
Realizes the v5.0.0 architecture (๐ ADR-0024 hard qmd dependency, ๐ ADR-0025 tracker issues as a fourth qmd collection, ๐ ADR-0026 tiered freshness strategy) by making every appropriate SDD plugin skill qmd-aware. The plugin moves from "every read-side skill scans the entire ADR + spec corpus" to "every read-side skill retrieves the top-K relevant artifacts via qmd hybrid search, then reads in full only what matters." Authoring skills (/sdd:adr, /sdd:spec) gain pre-search to suggest frontmatter edges. Sprint skills (/sdd:plan, /sdd:work, /sdd:review) gain awareness of existing code and existing tracker issues. Mutation-aware updates and a per-tier freshness model keep the index honest without per-call user friction.
This spec is the input to the v5.0.0 sprint. It defines what must be true after the implementation lands; per-skill exact algorithms live in updated SKILL.md files. Two new shared references absorb the cross-cutting concerns: references/qmd-helpers.md (retrieval pattern, MCP-vs-CLI, error handling) and references/tracker-sync.md (per-tracker fetch and normalize for the issues collection).
Requirementsโ
Requirement: qmd Preflight Enforcementโ
/sdd:init MUST refuse to operate when the qmd CLI is not available in PATH. The check happens before any CLAUDE.md write or skills-table convergence. Error output MUST include the install command (npm install -g @tobilu/qmd or bun install -g @tobilu/qmd) and a link to qmd's repository.
Scenario: qmd missing on a fresh machineโ
- WHEN the user runs
/sdd:initandcommand -v qmdreturns non-zero - THEN
/sdd:initMUST output the canonical error message naming the install command and stop without modifying CLAUDE.md,.gitignore, or any other file - AND the exit signal MUST be visible enough that downstream tooling (CI, install scripts) can detect the missing dependency
Scenario: qmd present but not yet authenticated to a model registryโ
- WHEN the user runs
/sdd:initandcommand -v qmdsucceeds butqmd statusreports the GGUF models are not yet downloaded - THEN
/sdd:initMUST proceed (model download is qmd's responsibility on first embed, not an init prerequisite) - AND
/sdd:init's final report SHOULD note that the first/sdd:index embedwill trigger a one-time ~2GB model download
Scenario: re-running /sdd:init after a successful installโ
- WHEN the user has already run
/sdd:initsuccessfully and re-runs it - THEN the qmd preflight passes (idempotent)
- AND no spurious changes are introduced to CLAUDE.md or
.gitignore
Requirement: qmd Assumption in Consumer Skillsโ
Every qmd-aware skill MAY assume qmd is installed and reachable on PATH (because /sdd:init enforced it per the Preflight requirement) and MUST NOT include conditional fallback paths gated on qmd availability. If a skill needs to handle "qmd installed but this repo not yet indexed", it MUST route to /sdd:index rather than silently degrading.
Scenario: a consumer skill encounters an unindexed repoโ
- WHEN
/sdd:checkruns in a repo whereqmd collection listshows no{repo}-*collections - THEN the skill MUST output: "No qmd collections found for {repo}. Run
/sdd:indexfirst." and stop - AND MUST NOT fall back to the pre-v5 "scan the entire corpus" behavior
Scenario: a consumer skill encounters a partially indexed repo (missing embeddings)โ
- WHEN
/sdd:checkruns in a repo where collections exist butqmd statusshows pending chunks for those collections - THEN the skill MUST proceed using BM25-only retrieval (qmd handles this gracefully by returning lex matches when vectors are missing)
- AND MUST surface a one-line note: "{N} chunks unembedded โ vector/hybrid search disabled until
/sdd:index embedruns"
Requirement: Plugin Version Bump to v5.0.0โ
The release that lands all requirements in this spec MUST bump .claude-plugin/plugin.json version from the current 4.x line to 5.0.0. The CHANGELOG MUST document the qmd dependency under "Breaking Changes" with the install command.
Scenario: v5.0.0 install on a machine without qmdโ
- WHEN a user installs
sdd@claude-plugin-sddv5.0.0 and runs/sdd:initwithout qmd installed - THEN
/sdd:initMUST emit the install command and stop, per the Preflight requirement - AND the failure mode MUST be obvious enough that a user upgrading from v4.x understands they have new install work
Requirement: Issues Collection Layoutโ
The plugin MUST sync tracker issues to .sdd/issues/{number}.md (or .sdd/issues/{tracker-id}.md for trackers using non-numeric IDs like Jira PROJ-123). Each synced file MUST carry the canonical frontmatter schema and the issue body verbatim. Synced files MUST be under .sdd/issues/, never directly under .sdd/ or elsewhere.
The frontmatter schema MUST include: id, title, status (normalized: open / closed / merged / draft), labels, assignees, author, created, updated, closed, url, tracker, and a references block parsing SPEC-XXXX and ADR-XXXX mentions plus Blocks: / Blocked by: dependency edges from the issue body.
Scenario: syncing a GitHub issue with full metadataโ
- WHEN
/sdd:index updateruns against a repo configured for GitHub and an issue exists athttps://github.com/owner/repo/issues/142with title, body, labels, assignees, and timestamps - THEN a file
.sdd/issues/142.mdMUST be written with the canonical frontmatter populated from the GitHub API response - AND the body MUST be the verbatim issue body
- AND the
trackerfrontmatter field MUST equalgithub
Scenario: syncing a Jira issue with composite IDโ
- WHEN
/sdd:index updateruns against a repo configured for Jira and an issue exists with keyPROJ-123 - THEN a file
.sdd/issues/PROJ-123.mdMUST be written (preserving the dash in the filename) - AND the
idfrontmatter field MUST equalPROJ-123
Scenario: re-syncing an issue whose body changedโ
- WHEN
/sdd:index updateruns and an issue's body has changed since the last sync - THEN the existing
.sdd/issues/{id}.mdMUST be overwritten with the current body and updatedupdatedtimestamp - AND no merge or diff MUST be attempted (the tracker is the source of truth; the local cache is replaceable)
Requirement: Tracker Sync Layerโ
The per-tracker fetch and normalize logic MUST live in a single references/tracker-sync.md reference file. Consumer skills (/sdd:index, /sdd:plan, /sdd:work, /sdd:review, /sdd:enrich, /sdd:organize) MUST consume it by section name and MUST NOT inline tracker-specific API calls in their own SKILL.md files. The reference MUST cover all seven supported trackers: GitHub, Gitea, GitLab, Jira, Linear, Beads, and tasks.md fallback.
Scenario: a new tracker is added in the futureโ
- WHEN a new tracker (e.g.,
Bitbucket) is added to the supported set - THEN the only file requiring substantive edits MUST be
references/tracker-sync.md(plus minimal entries inreferences/shared-patterns.mdยง Tracker Detection) - AND no consumer skill SKILL.md MUST need to learn the new tracker's API shape
Scenario: a tracker API rate-limits or returns an error during syncโ
- WHEN the tracker-sync layer encounters a 429 or 5xx response
- THEN it MUST retry with exponential backoff up to 3 times before reporting a sync failure
- AND a sync failure MUST surface a clear error to the consumer skill that triggered it, not a silent partial sync
Requirement: Issues Collection Sync via /sdd:indexโ
/sdd:index update MUST sync the {repo}-issues (or {repo}-{module}-issues in workspace mode) collection as part of its normal pass. Consumer skills (/sdd:plan, /sdd:work, /sdd:review, /sdd:enrich, /sdd:organize) MUST trigger an opportunistic sync at start-of-run if the issues collection has not been synced within the last 5 minutes.
Scenario: /sdd:plan starts after an idle sessionโ
- WHEN
/sdd:plan <a href="/specs/init-and-priming/spec#spec-0019" className="rfc-ref">SPEC-0019</a>runs and the last issues sync was >5 minutes ago - THEN the skill MUST trigger an issues-only sync via the tracker-sync layer before grouping requirements into stories
- AND MUST output a one-line note: "Syncing N issues from {tracker}โฆ"
Scenario: /sdd:work starts immediately after /sdd:planโ
- WHEN
/sdd:workruns within 5 minutes of a/sdd:planinvocation that already synced issues - THEN the skill MUST skip the redundant sync and proceed
- AND MUST silently note (in trace output, not user-facing) that the sync was skipped due to recency
Requirement: .sdd Gitignore Enforcementโ
/sdd:init MUST add .sdd/ to the project's .gitignore (creating .gitignore if absent). The entry MUST be appended exactly once; running /sdd:init repeatedly MUST NOT produce duplicate .sdd/ lines. Other entries in .gitignore MUST be preserved exactly.
Scenario: fresh project with no .gitignoreโ
- WHEN
/sdd:initruns in a project that has no.gitignore - THEN
/sdd:initMUST create.gitignorecontaining.sdd/ - AND MUST NOT create or modify any other entry
Scenario: project with existing .gitignore that includes .sdd/โ
- WHEN
/sdd:initruns and.gitignorealready contains.sdd/(anywhere in the file) - THEN
/sdd:initMUST leave the file unchanged - AND MUST NOT append a duplicate
Scenario: project with .gitignore that does NOT include .sdd/โ
- WHEN
/sdd:initruns and.gitignoreexists but does not contain.sdd/ - THEN
/sdd:initMUST append.sdd/to the end of the file (preceded by a single newline if the file does not end with one) - AND all existing entries MUST remain in their original positions
Requirement: qmd-helpers Referenceโ
The plugin MUST provide a references/qmd-helpers.md shared reference covering: how to call qmd (prefer the qmd MCP mcp__plugin_qmd_qmd__* tools when loaded; fall back to the qmd CLI otherwise), how to format result candidates for downstream consumption, how to handle qmd errors (timeout, no-collections, partial-embedding), and how skills should detect "this repo's collections" via exact-prefix match on collection names. Every qmd-aware consumer skill MUST consume this reference by section name.
Scenario: a skill needs to retrieve top-K candidatesโ
- WHEN
/sdd:checkneeds to find the ADRs and specs governing a target file - THEN the skill MUST use the canonical retrieval pattern from
references/qmd-helpers.mdยง Hybrid Retrieval - AND MUST NOT inline its own qmd CLI invocation
Scenario: a skill needs to identify this-repo collectionsโ
- WHEN any skill needs to filter to "collections belonging to this repo"
- THEN the skill MUST use the exact-prefix match pattern documented in
references/qmd-helpers.mdยง This-Repo Collection Identification - AND MUST NOT use substring match (which would spuriously claim e.g.,
not-myrepo-adrsfor slugmyrepo)
Requirement: Tier 1 Mutation-Aware Updatesโ
Every skill that writes to indexed content MUST trigger a narrow qmd update for the affected collection(s) before returning. The update is synchronous and silent unless it fails. Failures MUST surface as a one-line warning in the skill's normal report output.
| Skill | Writes to | Affected collection |
|---|---|---|
/sdd:adr | new ADR file | {repo}-adrs |
/sdd:spec | new spec.md / design.md | {repo}-specs |
/sdd:status | YAML or inline status field | The collection containing the artifact whose status changed |
/sdd:work | merged PR (changed code) | {repo}-code |
/sdd:plan, /sdd:enrich, /sdd:organize | tracker issues | {repo}-issues |
/sdd:review | merged PR (changed code AND closed issues) | {repo}-code AND {repo}-issues |
Scenario: /sdd:adr finishes writing a new ADRโ
- WHEN
/sdd:adrcompletes and writesdocs/adrs/ADR-NNNN-foo.md - THEN before returning, the skill MUST invoke the canonical update pattern from
references/qmd-helpers.mdto refresh the{repo}-adrscollection - AND the skill's success report MUST not mention the update (silent on success)
Scenario: a Tier 1 update failsโ
- WHEN
/sdd:adrfinishes writing the file but the qmd update fails (e.g., qmd daemon crashed) - THEN the skill's report MUST include a one-line warning: "Index refresh failed for {repo}-adrs โ run
/sdd:index updatemanually" - AND the artifact MUST still be reported as successfully written (the update is best-effort, not blocking)
Requirement: Tier 2 Session-Start Update via /sdd:primeโ
/sdd:prime MUST run qmd update (cheap file-mtime scan) on entry to catch changes from outside the current session. The update MUST be skipped when the qmd index was touched within the last 60 seconds (back-to-back primes are common). The update MUST be silent on success unless the diff is non-zero. After the update, /sdd:prime MUST surface the count of unembedded chunks for this repo's collections via a one-line note (no AskUserQuestion).
Scenario: /sdd:prime runs at the start of a fresh sessionโ
- WHEN
/sdd:primeruns and the qmd index was last touched >60s ago - THEN the skill MUST invoke
qmd updatesilently - AND MUST print a one-line note in the report header IF the update added/modified/removed any documents (otherwise omit the line entirely)
Scenario: /sdd:prime runs back-to-backโ
- WHEN
/sdd:primeruns and the qmd index was last touched <60s ago - THEN the skill MUST skip the update
- AND MUST NOT emit any update-related output
Scenario: /sdd:prime sees unembedded chunks for this repoโ
- WHEN
/sdd:primefinishes loading context andqmd statusreports unembedded chunks belonging to this repo's collections - THEN the report MUST surface a one-line note: "{N} chunks unembedded โ run
/sdd:index embed(โ{seconds}s on this machine, foreground; or wait for the next mutation skill to backfill)" - AND MUST NOT prompt the user via AskUserQuestion
Requirement: Tier 3 Staleness Threshold for Consumer Skillsโ
Read-only consumer skills (/sdd:check, /sdd:audit, /sdd:discover) MUST check the qmd index's last-modified timestamp on entry. If older than the configured staleness threshold, the skill MUST run a silent qmd update first and print a one-line note: "Index was {age} stale โ refreshed before running."
The threshold default MUST be 120 minutes (2 hours), configurable in CLAUDE.md ### SDD Configuration #### Index Freshness **Staleness Threshold** (e.g., 30m, 4h).
Scenario: /sdd:check runs after a long idle periodโ
- WHEN
/sdd:checkruns and the qmd index's last-modified timestamp is >120 minutes ago - THEN the skill MUST trigger
qmd updatesilently before performing retrieval - AND MUST emit the staleness note in its report header
Scenario: user has configured a shorter thresholdโ
- WHEN CLAUDE.md
### SDD Configuration#### Index Freshness**Staleness Threshold**is30mand/sdd:checkruns 31 minutes after the last update - THEN the skill MUST honor the configured threshold and trigger the update
Scenario: index is freshโ
- WHEN
/sdd:checkruns and the qmd index was updated within the configured threshold - THEN the skill MUST NOT trigger an update
- AND MUST proceed silently (no staleness note in the report)
Requirement: Tier 4 Always-Sync Issues for Sprint Skillsโ
/sdd:plan, /sdd:work, /sdd:review, /sdd:enrich, and /sdd:organize MUST sync the issues collection on entry, regardless of staleness threshold, subject to the 5-minute deduplication window from the Issues Collection Sync requirement.
Scenario: /sdd:work runs after a teammate closes an issue in the browserโ
- WHEN
/sdd:workruns and an issue was closed in the GitHub UI 30 seconds before - THEN the issues sync MUST run (assuming the >5min dedup window has elapsed since the last sync, which it has not in this case โ but if it had been >5min)
- AND the closed issue MUST appear with
status: closedin.sdd/issues/{N}.mdafter the sync
Scenario: a Tier 4 sync fails mid-sprintโ
- WHEN the issues sync fails for
/sdd:plan(e.g., GitHub rate limit) - THEN the skill MUST fall back to live tracker queries (the pre-qmd path) for issue context within this run
- AND MUST emit a one-line warning: "Issues sync failed โ degrading to live tracker queries for this run"
- AND MUST NOT block on the failure
Requirement: CPU-Default-Background Embed Policyโ
/sdd:index embed (and any internal qmd-embed call from another skill) MUST detect GPU availability via qmd status and apply this policy:
- GPU present โ run synchronously (foreground)
- CPU only โ run as a backgrounded Bash command, log to
/tmp/qmd-embed-{repo}.log, return the report immediately
The skill MUST accept --foreground and --skip flags as overrides. The skill MUST NOT prompt the user via AskUserQuestion to choose between modes โ the hardware-aware default is the right behavior.
Scenario: CPU-only embed defaults to backgroundโ
- WHEN
/sdd:index embedruns on a machine whereqmd statusreports "running on CPU" - THEN the embed command MUST be launched in the background with stdout/stderr redirected to
/tmp/qmd-embed-{repo}.log - AND the report MUST surface the log path and indicate that completion will be signaled by the harness's background-task notification
Scenario: GPU present runs synchronouslyโ
- WHEN
/sdd:index embedruns on a GPU-enabled machine - THEN the embed MUST run synchronously
- AND the report MUST include the embedded chunk count and elapsed time
Scenario: explicit foreground override on CPU machineโ
- WHEN
/sdd:index embed --foregroundruns on a CPU-only machine - THEN the skill MUST honor the override and run synchronously
- AND MUST NOT background despite the CPU detection
Requirement: qmd-Smart Drift Skillsโ
/sdd:check, /sdd:audit, and /sdd:discover MUST use qmd hybrid retrieval to identify the top-K candidate ADRs, specs, and (for /sdd:discover) existing decisions before reading any artifact in full. The pre-v5 "read the entire corpus" path MUST be removed from these skills (it remains in older plugin versions, not in v5.0.0+).
For /sdd:check {target} and /sdd:audit {target}, the retrieval query MUST be derived from the target's content (file path, governing comment block, code summary). For /sdd:discover, the retrieval query MUST be the candidate decision the agent is about to suggest, used to rule out duplicates.
Scenario: /sdd:check identifies governing artifacts via qmdโ
- WHEN
/sdd:check src/auth/login.goruns - THEN the skill MUST construct a hybrid query from the file's path, the governing comment block (if present), and a summary of the file's exported symbols
- AND MUST retrieve the top-8 candidates from
{repo}-adrsand{repo}-specsvia qmd - AND MUST read those candidates in full before evaluating drift
Scenario: /sdd:discover rules out duplicate decisionsโ
- WHEN
/sdd:discoveris about to suggest "Use JWT for session tokens" as an implicit decision - THEN the skill MUST first run a qmd query against
{repo}-adrsfor that suggestion - AND IF a top result has cosine similarity โฅ0.7 to the suggestion, the skill MUST NOT include the suggestion (an ADR likely already covers it)
- AND MUST log the suppressed suggestion + the matched ADR in the report's "Skipped" section
Requirement: qmd-Smart Authoring Skillsโ
/sdd:adr and /sdd:spec MUST pre-search the corresponding collection before writing the new artifact. The search query MUST be the user's description (from $ARGUMENTS) plus any context the agent has gathered. Top-K results MUST be surfaced to the user via AskUserQuestion as candidate frontmatter edges (supersedes, extends, related for ADRs; requires, extends for specs).
Scenario: /sdd:adr finds a related prior decisionโ
- WHEN
/sdd:adr "Switch from REST to gRPC"runs and the corpus contains ๐ ADR-0008 ("Use REST for the API") - THEN the skill MUST surface ๐ ADR-0008 to the user via AskUserQuestion
- AND MUST offer "supersedes ๐ ADR-0008" as one of the candidate edges to include in the new ADR's frontmatter
- AND the user MUST be able to accept, reject, or modify each suggested edge
Scenario: /sdd:spec finds a prerequisite specโ
- WHEN
/sdd:spec "Authentication"runs and the corpus contains SPEC-0014 ("Token Validation") - THEN the skill MUST surface SPEC-0014 to the user via AskUserQuestion
- AND MUST offer "requires SPEC-0014" as a candidate edge
Scenario: pre-search returns nothing relevantโ
- WHEN
/sdd:adr "Pick a CSV parsing library"runs and qmd returns no results above the relevance threshold - THEN the skill MUST proceed without surfacing edge suggestions
- AND MUST emit a one-line note: "No related artifacts found โ drafting from scratch"
Requirement: qmd-Smart Sprint Skillsโ
/sdd:plan, /sdd:work, and /sdd:review MUST use qmd retrieval to inform their sprint-level decisions:
/sdd:planMUST search{repo}-codeand{repo}-issuesto size and frame stories accurately. Stories that touch existing code MUST be framed as "extend X in path/to/file" rather than "implement from scratch"./sdd:workworkers MUST search{repo}-codefor existing patterns (helpers, conventions, sibling implementations) before writing new code. This mitigates the duplicate-implementation drift the Foundation Story Detection pattern (per ๐ ADR-0017) was designed to catch./sdd:reviewreviewers MUST search{repo}-adrsand{repo}-issuesto identify ADRs the PR should reference and prior issues the PR touches.
Scenario: /sdd:plan recognizes existing code that solves part of a storyโ
- WHEN
/sdd:plan <a href="/specs/init-and-priming/spec#spec-0019" className="rfc-ref">SPEC-0019</a>runs and one requirement involves "user session management" while{repo}-codealready contains ainternal/session/store.gowith relevant functions - THEN the corresponding story body MUST reference the existing file ("extend
internal/session/store.go") - AND MUST size the story accordingly (smaller than greenfield)
Scenario: /sdd:work avoids re-creating an existing helperโ
- WHEN a
/sdd:workworker is about to write aparseUserIDhelper and qmd retrieval surfaces an existinginternal/auth/parse.go:parseUserIDin{repo}-code - THEN the worker MUST import the existing helper rather than create a duplicate
- AND MUST broadcast
TYPE_IMPORTEDper ๐ ADR-0017's Worker Communication Protocol
Scenario: /sdd:review surfaces a missing ADR referenceโ
- WHEN
/sdd:reviewis reviewing a PR that modifies authentication code and qmd retrieval against{repo}-adrsreturns ๐ ADR-0011 (which governs auth) but the PR does not reference ๐ ADR-0011 in its body or governing comments - THEN the reviewer MUST raise this as a finding ("Should this PR reference ๐ ADR-0011?")
- AND MUST cite the relevant ADR section that suggests the connection
Requirement: qmd-Smart Context Loadingโ
/sdd:prime {topic} MUST use qmd hybrid retrieval to identify the top-K most relevant ADRs and specs to the topic, then read those in full and present them. This replaces the pre-v5 "read every ADR + every spec, then filter" path. Untargeted /sdd:prime (no topic) MUST still load all artifacts, since the user has explicitly asked for a corpus overview.
Scenario: /sdd:prime with a topic argumentโ
- WHEN
/sdd:prime authruns against a corpus of 23 ADRs and 18 specs - THEN the skill MUST construct a qmd query from the topic and retrieve the top-K candidates from
{repo}-adrsand{repo}-specs - AND MUST read only those candidates in full before producing the prime report
- AND MUST NOT read every artifact in the corpus
Scenario: /sdd:prime with no topicโ
- WHEN
/sdd:primeruns with no topic argument - THEN the skill MUST behave as today: load all ADR / spec metadata for the overview table
- AND MUST NOT use qmd retrieval (no topic = no query to construct)
Out of Scope (deferred to future specs)โ
- Tier 5 scheduled background sync (per ๐ ADR-0026 ยง "Tier 5") โ deferred to v5.1 or later, after V1 ships and produces evidence about long-tail staleness cases
- A native qmd MCP extension exposing collection-add / context-add / update / embed as MCP tools โ would let
/sdd:indexoperate via MCP only and remove the CLI dependency. Tracked as an upstream qmd feature request. - Cross-repo semantic queries โ currently each repo's collections are siloed. A future spec might define a "this repo + my other indexed repos" mode for
/sdd:checkand similar. - Issue-level frontmatter graph integration โ the
referencesblock in synced issue files listsSPEC-XXXXandADR-XXXXmentions, but these are NOT yet consumed by/sdd:graph. Integrating issues into the artifact graph (so an issue can be a node) is a separate spec.
Related Artifactsโ
Direct relationships declared in YAML frontmatter (per ADR-0023 / SPEC-0018). Run /sdd:graph chain SPEC-0019 for the transitive view.