Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/update-glob-artifact-gap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fission-ai/openspec": patch
---

Let `/opsx:update` fill a missing file under an already-satisfied glob artifact. A glob artifact is complete once one file matches it, and `/opsx:continue` only picks up `ready` artifacts, so the previous "point the user to `/opsx:continue`" handoff was unreachable and the missing file could never be created through the documented flow.
2 changes: 1 addition & 1 deletion docs-lab/reference/skills.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ other.
| Contract | Description |
|---|---|
| **Arguments** | A change proposal name, optional, plus the revision you want. With no revision stated it runs a coherence review: artifacts checked against each other for contradictions, gaps, and duplication. |
| **Creates** | Nothing new. Edits only artifact files that already exist. Missing artifacts are `openspec-continue-change`'s job. Without that skill (the core profile leaves it out), it points to `openspec status` and `openspec instructions` instead. Never code. |
| **Creates** | Edits artifact files that already exist. One exception: for an artifact written as a glob, such as `specs/**/*.md`, that already has at least one file, it can add a missing companion file once you confirm the path. An artifact with no files yet is `openspec-continue-change`'s job. Without that skill (the core profile leaves it out), it points to `openspec status` and `openspec instructions` instead. Never code. |
| **Response** | Shows each proposed revision and writes it only after you confirm, one artifact at a time. Ends with what was revised and the next step; implementation waits for `openspec-apply-change`. |

## openspec-sync-specs
Expand Down
10 changes: 8 additions & 2 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,13 @@ Revise a change's existing planning artifacts and keep them coherent with one an
- Applies your requested revision, or reviews the artifacts for contradictions if you didn't name one
- Reconciles the other existing artifacts in any direction (a design edit may ripple back to the proposal)
- Confirms every edit with you before writing, one artifact at a time
- Ends by recommending the next step: `/opsx:continue` (artifacts missing), `/opsx:apply` (carry a revised plan into code), or `/opsx:archive` (all done)
- Ends by recommending the next step: `/opsx:continue` (unstarted artifacts), `/opsx:apply` (carry a revised plan into code), or `/opsx:archive` (all done)

**Missing files:**

- For a glob artifact such as `specs/**/*.md` with at least one existing file, update can propose a missing companion file. It uses the schema's instructions and asks you to confirm the concrete path before creating it.
- Artifacts with no files yet remain with `/opsx:continue`. Intentionally skipped artifacts stay untouched.
- New files must stay inside the change directory. If a file appears at the confirmed path before creation, update stops instead of overwriting it.

**Example:**

Expand All @@ -373,7 +379,7 @@ AI: Reading add-dark-mode artifacts...

**Tips:**

- It won't create missing artifacts - that's `/opsx:continue`
- It won't start an artifact with no existing files. Enable `/opsx:continue` for that, or use `openspec status` and `openspec instructions` if that optional workflow isn't installed.
- If the change was already implemented, follow up with `/opsx:apply` so the code matches the revised plan
- If your revision changes the *intent* of the change, start fresh with a new change instead (see [When to Update vs. Start Fresh](opsx.md#when-to-update-vs-start-fresh))

Expand Down
4 changes: 3 additions & 1 deletion docs/opsx.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,9 @@ Works through tasks, checking them off as you go. If you're juggling multiple ch
```
/opsx:update add-dark-mode - we're storing the theme in a cookie now
```
Revises the change's existing planning artifacts and keeps them coherent - in any direction (a design edit may ripple back to the proposal). Planning artifacts only: it never edits code, and it never creates missing artifacts (that's `/opsx:continue`). Every edit is confirmed with you first. If the change was already implemented, it recommends `/opsx:apply` so the code catches up with the revised plan. If your revision changes the change's *intent*, start fresh instead - see [When to Update vs. Start Fresh](#when-to-update-vs-start-fresh).
Revises the change's existing planning artifacts and keeps them coherent in any direction (a design edit may ripple back to the proposal). It never edits code. Every edit is confirmed with you first. See [the update reference](commands.md#opsxupdate) for how it handles missing files without starting a new artifact.

If the change was already implemented, it recommends `/opsx:apply` so the code catches up with the revised plan. If your revision changes the change's *intent*, start fresh instead. See [When to Update vs. Start Fresh](#when-to-update-vs-start-fresh).

### Sync delta specs
```text
Expand Down
6 changes: 6 additions & 0 deletions openspec/changes/add-update-workflow/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ Review feedback flagged that "update" alone is generic — could it apply to any
### 6. Next-step guidance, especially for already-implemented changes
A change can be revised after it was built — tasks checked off, `/opsx:apply` already run. The update itself behaves identically (planning artifacts only), but stopping silently would strand the user: the code and the revised plan now disagree. So the skill ends by reporting where the change stands (from the status JSON and the tasks checklist) and recommending the next command — `/opsx:continue` if artifacts are missing, `/opsx:apply` to carry a revised plan into code, `/opsx:archive` when everything is done. Guidance only: the skill never implements, mirroring the "All artifacts created! You can now implement this change with `/opsx:apply`" hand-off that `continue-change.ts` already uses.

### 7. Companion-file correction (#1733)

The original glob-file deferral was unreachable: one matching file marks an artifact `done`, while continue selects only `ready` artifacts. Update can therefore propose a missing companion file within an already populated glob. This corrects the unarchived spec's former blanket deferral without changing the graph's completion rule or starting another artifact.

The exception uses existing status and instructions output, requires current dependency context and user confirmation, and preserves the change-only planning scope. Immediately before creation, it rechecks scope and the concrete path and uses an operation that refuses an existing target. Delegated creators must obey the same limits. No new CLI command, metadata, graph state, or automatic artifact writer is introduced.

## Risks / Trade-offs

- **No deterministic staleness signal.** With no digest/ledger, the skill relies on the agent reading the artifacts to spot incoherence. Trade-off accepted: an agent that rewrites prose must read it anyway, and a content-blind signal earns its cost only for use cases this change excludes (Decision 3).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The system SHALL provide a `/opsx:update` workflow skill that revises a change's

#### Scenario: Missing artifacts are deferred to continue

- **WHEN** keeping the change coherent would require an artifact that has not been created yet
- **WHEN** keeping the change coherent would require an artifact with no existing output files and status `ready` or `blocked`
- **THEN** the skill revises only the artifacts that currently exist
- **AND** it notes the not-yet-created artifacts and points the user to `/opsx:continue` to create them

Expand Down Expand Up @@ -53,7 +53,7 @@ The `/opsx:update` skill SHALL learn which artifacts exist and where they live b

#### Scenario: Resolve artifact paths cross-platform

- **WHEN** the skill reads or writes an artifact on macOS, Linux, or Windows
- **WHEN** the skill reads or revises an existing artifact file on macOS, Linux, or Windows
- **THEN** it uses the `existingOutputPaths` provided by the CLI status output
- **AND** it does not assume forward-slash separators

Expand All @@ -63,11 +63,30 @@ The `/opsx:update` skill SHALL learn which artifacts exist and where they live b
- **THEN** the skill edits the concrete files reported in that artifact's `existingOutputPaths`
- **AND** it does not write to `resolvedOutputPath`, which for a glob artifact remains the glob pattern rather than a real file

#### Scenario: A new file under a glob artifact is deferred to continue
#### Scenario: A missing companion file under a populated glob artifact

- **WHEN** keeping the change coherent would require a new file under a glob artifact that does not exist yet (for example a spec for a not-yet-captured capability)
- **THEN** the skill revises only the files already present in `existingOutputPaths`
- **AND** it points the user to `/opsx:continue`/`/opsx:propose` to create the new file rather than inventing a path from the glob
- **WHEN** reconciliation identifies a missing companion file for a glob artifact with non-empty `existingOutputPaths`
- **THEN** the skill MAY propose creating that file using the artifact's instructions, template, project context, rules, and current dependency files
- **AND** it selects an unused concrete path matching the artifact's `outputPath` inside `changeRoot`, including after resolving linked parent directories
- **AND** it creates the file only after user confirmation, refreshing status, instructions, and path checks immediately before creation
- **AND** creation SHALL fail rather than overwrite a file that appeared in the meantime
- **AND** it SHALL NOT start another artifact, write main specs, or edit implementation code

#### Scenario: Required inputs are no longer available

- **WHEN** a populated glob artifact remains `done` but a required non-skipped dependency is missing
- **THEN** the skill SHALL stop new companion creation and ask the user to restore the dependency first

#### Scenario: Schema delegates companion creation

- **WHEN** the artifact instruction delegates creation to another skill or command
- **THEN** the skill SHALL invoke it only if it can honor the confirmed concrete path and the update guardrails
- **AND** otherwise it SHALL stop rather than invoke broader generation

#### Scenario: Intentionally skipped artifact

- **WHEN** status or instructions mark an artifact as skipped
- **THEN** the skill SHALL leave it untouched and SHALL NOT treat its empty outputs as missing or send it to continue

### Requirement: Bidirectional Coherence Review

Expand Down Expand Up @@ -109,7 +128,7 @@ After applying confirmed revisions (or finding none needed), the `/opsx:update`

#### Scenario: Next step when artifacts are incomplete

- **WHEN** the update finishes and the change still has not-yet-created artifacts
- **WHEN** the update finishes and the change still has artifacts with no outputs and status `ready` or `blocked`
- **THEN** the skill recommends `/opsx:continue` to create them

#### Scenario: Next step when the change is fully done
Expand Down
15 changes: 11 additions & 4 deletions skills/openspec-update-change/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,13 @@ This workflow revises artifacts that already exist; `/openspec-continue-change`
- Read the artifact(s) the request touches and the change's other existing artifacts.
- Draft the requested edit in the conversation, not in files. Work out exactly what it changes; step 5 owns every write. Then check every other existing artifact against the drafted edit - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.
- Note everything that is now inconsistent, missing, or contradictory.
- Propose revisions only to files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/openspec-continue-change` to create them.
- Propose revisions to files that already exist (`existingOutputPaths`). If an artifact has no existing output files and status `ready` or `blocked`, note it and point the user to `/openspec-continue-change` to create them. Leave `skipped` artifacts untouched; do not treat them as missing or defer them to the continue workflow.
- A glob artifact (e.g. `specs/**/*.md`) is marked `done` after at least one file matches, and the continue workflow only handles `ready` artifacts. When reconciliation identifies a missing file for a glob artifact whose `existingOutputPaths` is non-empty:
1. Run `openspec instructions "<artifact-id>" --change "<name>" --json` and use its `instruction` and `template`. Treat `context` and `rules` as constraints; do not copy them into the file. If instructions report `skipped: true`, do not create the file. Read current dependency files from disk; if a required non-skipped dependency is missing, stop and ask the user to restore it first.
2. Choose a concrete path inside `changeRoot` that matches `artifactPaths.<id>.outputPath` and does not already exist. Verify it remains inside `changeRoot` after resolving any symlinked parent directories. The glob `resolvedOutputPath` is not a valid target.
3. Include the new file in step 5's proposed revisions and create it only after the user confirms.
4. After confirmation, immediately before creation, refresh status and instructions. Verify the artifact is still in scope, not skipped, and partially populated; repeat the concrete-path checks above.
5. Use a create operation that fails if the target already exists. If `instruction` delegates creation to another skill or command, invoke it only if it can honor the confirmed path and these guardrails; otherwise stop. If any check fails or the confirmed draft is no longer valid, stop and reconcile with the user rather than replacing existing content or choosing a different path.
- If the change is already coherent, say so and propose no revisions.

5. **Confirm and apply, one artifact at a time**
Expand All @@ -82,21 +88,22 @@ This workflow revises artifacts that already exist; `/openspec-continue-change`
```

6. **Point to the next step (guidance only - NEVER act on it)**
- Artifacts still missing -> suggest `/openspec-continue-change` to create them.
- Artifacts with empty `existingOutputPaths` and status `ready` or `blocked` -> suggest `/openspec-continue-change` to create them.
- Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/openspec-apply-change` to carry the delta into code.
- Everything done and implemented -> suggest `/openspec-archive-change`.

**Output**

After each invocation, show:
- Which artifacts were revised (and which proposed revisions were rejected)
- Anything deferred to `/openspec-continue-change` (not-yet-created artifacts or files)
- Any file created under a glob artifact that was already partially populated
- Anything deferred to `/openspec-continue-change` (artifacts with no files yet and status `ready` or `blocked`, never `skipped` artifacts)
- Where the change stands and the recommended next command

**Guardrails**
- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/openspec-apply-change`.
- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.
- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.
- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/openspec-continue-change`'s job.
- Do not advance the build frontier: if an artifact has empty `existingOutputPaths` and status `ready` or `blocked`, that is `/openspec-continue-change`'s job. Leave `skipped` artifacts untouched. The only new-file scope is a confirmed concrete path under a glob artifact whose `existingOutputPaths` is non-empty.
- Confirm every edit with the user before writing.
- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/openspec-new-change` (the "Update vs. Start Fresh" heuristic).
Loading
Loading