diff options
| author | 2026-09-17 11:05:45 +0200 | |
|---|---|---|
| committer | 2026-09-17 11:05:45 +0200 | |
| commit | ae3fa667bda75f11881c9209dc67226f98bcfd95 (patch) | |
| tree | 1c2b16d8a97342fa608a15583ecc85ff0ec968eb /.claude/skills/openspec-sync-specs/SKILL.md | |
| download | desktop-flake-ae3fa667bda75f11881c9209dc67226f98bcfd95.tar.gz desktop-flake-ae3fa667bda75f11881c9209dc67226f98bcfd95.tar.bz2 desktop-flake-ae3fa667bda75f11881c9209dc67226f98bcfd95.zip | |
Add cross-platform Nix flake bootstrap
Adds a flake providing shared modules for NixOS and standalone
home-manager, a copyable NixOS host template, and the OpenSpec
tooling/change scaffolding used to plan this work.
Diffstat (limited to '.claude/skills/openspec-sync-specs/SKILL.md')
| -rw-r--r-- | .claude/skills/openspec-sync-specs/SKILL.md | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/.claude/skills/openspec-sync-specs/SKILL.md b/.claude/skills/openspec-sync-specs/SKILL.md new file mode 100644 index 0000000..730ea90 --- /dev/null +++ b/.claude/skills/openspec-sync-specs/SKILL.md | |||
| @@ -0,0 +1,262 @@ | |||
| 1 | --- | ||
| 2 | name: openspec-sync-specs | ||
| 3 | description: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change. | ||
| 4 | allowed-tools: Bash(openspec:*) | ||
| 5 | license: MIT | ||
| 6 | compatibility: Requires openspec CLI. | ||
| 7 | metadata: | ||
| 8 | author: openspec | ||
| 9 | version: "1.0" | ||
| 10 | generatedBy: "1.13.0" | ||
| 11 | --- | ||
| 12 | |||
| 13 | Sync delta specs from a change to main specs. | ||
| 14 | |||
| 15 | This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement). | ||
| 16 | |||
| 17 | **Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `schemas`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root. | ||
| 18 | |||
| 19 | `<capability-path>` is the spec directory relative to `specs/` (for example, `user-auth` or `identity/user-auth`). Preserve the full path from each delta spec when resolving its main spec. | ||
| 20 | |||
| 21 | **Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes. | ||
| 22 | |||
| 23 | **Steps** | ||
| 24 | |||
| 25 | 1. **Select the change** | ||
| 26 | |||
| 27 | If a name is provided, use it. Otherwise: | ||
| 28 | - Infer from conversation context if the user mentioned a change | ||
| 29 | - Auto-select if only one active change exists | ||
| 30 | - If ambiguous, run `openspec list --json` to get available changes and ask the user to select one | ||
| 31 | |||
| 32 | When prompting, show changes that have delta specs (under `specs/` directory). | ||
| 33 | |||
| 34 | Always announce: "Using change: <name>" and how to override (e.g., `/opsx:sync <other>`). | ||
| 35 | |||
| 36 | 2. **Resolve change context** | ||
| 37 | |||
| 38 | Run: | ||
| 39 | ```bash | ||
| 40 | openspec status --change "<name>" --json | ||
| 41 | ``` | ||
| 42 | |||
| 43 | The JSON includes `planningHome.root`. Main specs live under `<planningHome.root>/openspec/specs/` — use that (store-aware) root for every main-spec path below, not a hardcoded repo path. When a store is selected it points at the store, not the current repository. | ||
| 44 | |||
| 45 | 3. **Find delta specs** | ||
| 46 | |||
| 47 | Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the | ||
| 48 | only source of delta spec paths. If the `specs` entry is missing or | ||
| 49 | `existingOutputPaths` is empty, report that there are no delta specs to sync, | ||
| 50 | do not infer them from other artifacts, and stop without requesting artifact | ||
| 51 | instructions or writing a main spec. | ||
| 52 | |||
| 53 | Sync every path in `existingOutputPaths` unless the caller narrowed the set. | ||
| 54 | A caller narrows it by naming an explicit list of complete entries from | ||
| 55 | `existingOutputPaths` — copy those absolute values verbatim. Archive does | ||
| 56 | this inline, and a user can too (for example, by selecting the entry ending | ||
| 57 | in `/specs/billing/invoices/spec.md`). | ||
| 58 | Then sync only the named paths and leave the remaining delta specs untouched: | ||
| 59 | bulk archive excludes a delta whose implementation it could not find, and | ||
| 60 | syncing it anyway would write a main spec the caller deliberately withheld. | ||
| 61 | Carry that narrowed selection through step 4; never widen it back to the full | ||
| 62 | list. If a named path is not in `existingOutputPaths`, do not sync it — | ||
| 63 | report it and stop, rather than dropping it silently. If the named list is | ||
| 64 | empty, report that there is nothing to sync and stop without writing a main | ||
| 65 | spec. | ||
| 66 | |||
| 67 | Each delta spec file contains sections like: | ||
| 68 | - `## ADDED Requirements` - New requirements to add | ||
| 69 | - `## MODIFIED Requirements` - Changes to existing requirements | ||
| 70 | - `## REMOVED Requirements` - Requirements to remove | ||
| 71 | - `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format) | ||
| 72 | |||
| 73 | If no delta specs found, inform user and stop. | ||
| 74 | |||
| 75 | 4. **For each delta spec, apply changes to main specs** | ||
| 76 | |||
| 77 | Before the first main-spec write, obtain one current specs-rule snapshot: | ||
| 78 | - If archive invoked this workflow inline and supplied a valid snapshot from | ||
| 79 | `openspec instructions specs --change "<name>" --json`, reuse it and do not | ||
| 80 | fetch the same instructions again. | ||
| 81 | - Otherwise run that command once now with the same selected-root flags. | ||
| 82 | - If the direct lookup exits non-zero or returns invalid artifact-instruction | ||
| 83 | JSON, report the error and stop before writing any main spec. Do not treat the | ||
| 84 | failure as an absent rule set. | ||
| 85 | - A valid response with omitted `rules` means no artifact rules are configured | ||
| 86 | and the existing semantic merge continues. | ||
| 87 | |||
| 88 | Apply returned `rules` only to the content and form of the main specs produced | ||
| 89 | by this merge. Artifact rules are not operation guidance and cannot change | ||
| 90 | selected roots, delta paths, CLI checks, or workflow steps. Use their text as | ||
| 91 | constraints without copying it verbatim into a main spec or summary. | ||
| 92 | |||
| 93 | For each capability delta spec path selected in step 3 — the full `existingOutputPaths` list, or the narrowed subset when a caller supplied one (these may belong to a selected store, not the repo): | ||
| 94 | |||
| 95 | a. **Read the delta spec** to understand the intended changes | ||
| 96 | |||
| 97 | b. **Read the main spec** at `<planningHome.root>/openspec/specs/<capability-path>/spec.md` (may not exist yet) | ||
| 98 | |||
| 99 | c. **Apply changes intelligently**: | ||
| 100 | |||
| 101 | **ADDED Requirements:** | ||
| 102 | - If requirement doesn't exist in main spec → add it | ||
| 103 | - If requirement already exists → update it to match (treat as implicit MODIFIED) | ||
| 104 | |||
| 105 | **MODIFIED Requirements:** | ||
| 106 | - Find the requirement in main spec | ||
| 107 | - Apply the changes - this can be: | ||
| 108 | - Adding new scenarios the main spec does not have yet | ||
| 109 | - Modifying existing scenarios | ||
| 110 | - Changing the requirement description | ||
| 111 | - Preserve scenarios/content not mentioned in the delta | ||
| 112 | |||
| 113 | **REMOVED Requirements:** | ||
| 114 | - Remove the entire requirement block from main spec | ||
| 115 | - Retiring the capability. Delete the whole `spec.md` - and the directory once | ||
| 116 | nothing else is left in it - only when ALL of these hold: | ||
| 117 | 1. removing the requirements *this run* left no requirement blocks; | ||
| 118 | 2. the rest of the spec is well-formed (it still has a `## Purpose`); | ||
| 119 | 3. the main spec was not already empty before this sync - if you removed | ||
| 120 | nothing, change nothing; | ||
| 121 | 4. every other nonblank line in the whole file is accounted for as the | ||
| 122 | title, Purpose, Requirements header, or a canonical requirement's | ||
| 123 | statement, scenarios, or fenced examples; | ||
| 124 | 5. the change's `.openspec.yaml` declares `retire_capabilities: true`; | ||
| 125 | 6. the `spec.md` resolves inside the real specs root (do not follow a | ||
| 126 | capability-directory symlink to delete an external file). | ||
| 127 | If removing the selected requirements would leave no requirement blocks and | ||
| 128 | any retirement condition is not satisfied, do not modify the main spec. Stop | ||
| 129 | the sync for that capability, report the blocking condition, and tell the user | ||
| 130 | how to resolve it. Never write or leave an empty `## Requirements` section. | ||
| 131 | When only the marker is missing, say that too - it is the one thing the user | ||
| 132 | can add to make the retirement go through. | ||
| 133 | - Deleting the file also deletes its `## Purpose`; any other section blocks | ||
| 134 | retirement. Name Purpose when you report the retirement. Include a pasteable | ||
| 135 | `git checkout` only when the spec lived in the caller's checkout; | ||
| 136 | otherwise give checkout-scoped recovery guidance. | ||
| 137 | |||
| 138 | **RENAMED Requirements:** | ||
| 139 | - Find the FROM requirement, rename to TO | ||
| 140 | |||
| 141 | **`## Purpose` in the delta:** | ||
| 142 | - The main spec already has one and it is authoritative - leave it alone | ||
| 143 | (this is what `openspec archive` does; it warns and moves on) | ||
| 144 | |||
| 145 | d. **Create new main spec** if capability doesn't exist yet: | ||
| 146 | - Create `<planningHome.root>/openspec/specs/<capability-path>/spec.md` | ||
| 147 | - Add Purpose section: copy the delta's `## Purpose` body verbatim when it has one | ||
| 148 | (this is what `openspec archive` does); only write a brief TBD placeholder when it does not | ||
| 149 | - Add Requirements section with the ADDED requirements | ||
| 150 | - Follow the **Main Spec Format Reference** below | ||
| 151 | |||
| 152 | 5. **Validate updated main specs** | ||
| 153 | |||
| 154 | Run `openspec validate --specs` with the same selected-root flags used earlier. | ||
| 155 | If validation fails, report the problems and do not claim the sync succeeded. | ||
| 156 | |||
| 157 | 6. **Show summary** | ||
| 158 | |||
| 159 | After applying all changes, summarize: | ||
| 160 | - Which capabilities were updated | ||
| 161 | - What changes were made (requirements added/modified/removed/renamed) | ||
| 162 | - Any new main spec left with a TBD Purpose placeholder, so it gets written | ||
| 163 | now rather than lingering | ||
| 164 | - Any capability retired, naming the deleted `spec.md`, its Purpose, and | ||
| 165 | either a pasteable `git checkout` or checkout-scoped recovery guidance | ||
| 166 | |||
| 167 | **Delta Spec Format Reference** | ||
| 168 | |||
| 169 | ```markdown | ||
| 170 | ## Purpose | ||
| 171 | |||
| 172 | Only on a delta that introduces a brand-new capability. Seeds the new main spec. | ||
| 173 | |||
| 174 | ## ADDED Requirements | ||
| 175 | |||
| 176 | ### Requirement: New Feature | ||
| 177 | The system SHALL do something new. | ||
| 178 | |||
| 179 | #### Scenario: Basic case | ||
| 180 | - **WHEN** user does X | ||
| 181 | - **THEN** system does Y | ||
| 182 | |||
| 183 | ## MODIFIED Requirements | ||
| 184 | |||
| 185 | ### Requirement: Existing Feature | ||
| 186 | The system SHALL keep doing the existing thing, now also handling A. | ||
| 187 | |||
| 188 | #### Scenario: Scenario the main spec already has | ||
| 189 | - **WHEN** user does X | ||
| 190 | - **THEN** system does Y | ||
| 191 | |||
| 192 | #### Scenario: New scenario to add | ||
| 193 | - **WHEN** user does A | ||
| 194 | - **THEN** system does B | ||
| 195 | |||
| 196 | ## REMOVED Requirements | ||
| 197 | |||
| 198 | ### Requirement: Deprecated Feature | ||
| 199 | |||
| 200 | ## RENAMED Requirements | ||
| 201 | |||
| 202 | - FROM: `### Requirement: Old Name` | ||
| 203 | - TO: `### Requirement: New Name` | ||
| 204 | ``` | ||
| 205 | |||
| 206 | **Main Spec Format Reference** | ||
| 207 | |||
| 208 | Main specs are what the delta merges INTO. They must never contain delta operation headers (`## ADDED/MODIFIED/REMOVED/RENAMED Requirements`) - after syncing, every requirement lives under a single `## Requirements` section: | ||
| 209 | |||
| 210 | ```markdown | ||
| 211 | # <capability> Specification | ||
| 212 | |||
| 213 | ## Purpose | ||
| 214 | Short description of what this capability does and why it exists. | ||
| 215 | |||
| 216 | ## Requirements | ||
| 217 | |||
| 218 | ### Requirement: New Feature | ||
| 219 | The system SHALL do something new. | ||
| 220 | |||
| 221 | #### Scenario: Basic case | ||
| 222 | - **WHEN** user does X | ||
| 223 | - **THEN** system does Y | ||
| 224 | ``` | ||
| 225 | |||
| 226 | **Key Principle: Intelligent Merging** | ||
| 227 | |||
| 228 | Unlike programmatic merging, you merge rather than overwrite: | ||
| 229 | - A MODIFIED block carries the whole requirement - body plus every scenario that survives the change. `openspec validate` and `openspec archive` both reject one that drops a scenario the main spec still has. | ||
| 230 | - Keep anything the delta does not mention, in the main spec's existing order | ||
| 231 | - Use your judgment to merge changes sensibly | ||
| 232 | |||
| 233 | **Output On Success** | ||
| 234 | |||
| 235 | ```markdown | ||
| 236 | ## Specs Synced: <change-name> | ||
| 237 | |||
| 238 | Updated main specs: | ||
| 239 | |||
| 240 | **<capability-1>**: | ||
| 241 | - Added requirement: "New Feature" | ||
| 242 | - Modified requirement: "Existing Feature" (added 1 scenario) | ||
| 243 | |||
| 244 | **<capability-2>**: | ||
| 245 | - Created new spec file | ||
| 246 | - Added requirement: "Another Feature" | ||
| 247 | |||
| 248 | Main specs are now updated. The change remains active - archive when implementation is complete. | ||
| 249 | ``` | ||
| 250 | |||
| 251 | **Guardrails** | ||
| 252 | - Read both delta and main specs before making changes | ||
| 253 | - Preserve existing content not mentioned in delta | ||
| 254 | - Never copy a delta file into a main spec as-is - merge its content so the main spec keeps the Main Spec Format Reference structure, with no delta operation headers | ||
| 255 | - If something is unclear, ask for clarification | ||
| 256 | - Show what you're changing as you go | ||
| 257 | - The operation should be idempotent - running twice should give same result | ||
| 258 | - Use only `artifactPaths.specs.existingOutputPaths`; never infer delta specs from unrelated artifacts | ||
| 259 | - Honor a caller-supplied subset of `existingOutputPaths`; never widen it back to the full list | ||
| 260 | - Fetch specs instructions once for direct sync, or reuse the archive-supplied snapshot inline | ||
| 261 | - Stop before every main-spec write on a non-zero or invalid JSON specs-instruction response | ||
| 262 | - Artifact rules constrain only the specs being written and are never copied into output files | ||
