Files
OpenSpec/openspec
Clay GoodandClaude Opus 5.5 3fea2a96b2 fix(workflows): check removed requirements against their baseline, unblock the glob new-file flow (#2083)
* fix(verify): read a removed requirement's baseline before searching

Since #1962, verify inverts the check for `## REMOVED Requirements`: it
searches the codebase for the removed behavior and passes when nothing is
found. But a REMOVED block carries only the requirement name plus its
`**Reason**` and `**Migration**` notes - the parser takes names and nothing
else - so the search started from the heading alone. An implementation that
used different vocabulary from the requirement title read as a clean
removal, and because absence is the passing result here, the report showed
a silent pass.

The RENAMED branch immediately below already loaded its baseline from the
main spec first. Give REMOVED the same treatment: read the requirement's
body and scenarios from the main spec before searching, and search for that
behavior.

A removal needs one step a rename does not. Archive deletes a REMOVED
requirement outright, while a rename is retitled in place, so after a sync
there is no baseline left in the main spec to fall back on. Recover it from
repository history instead, and when it cannot be recovered at all, report
Spec Coverage as not verified for that entry rather than counting it as
passing.

Closes #2067

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(update): state the glob new-file exception on the scope guardrail

The guardrails of /opsx:update contradicted step 4. The first scope bullet
said to edit only the concrete files in `existingOutputPaths`, and a file
step 4 creates to fill a gap under a partly written glob artifact is by
definition not in that list. The exception sat two bullets below, so an
agent applying the stricter blanket rule refused the very operation the
glob-gap flow exists to perform, and the missing spec went unfilled.

State the exception on the bullet carrying the stricter rule, where an
agent reading it will see it, and leave the confirmation and path checks
where they already live in step 4.

Closes #2068

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(workflows): harden the removed-baseline and glob-scope guidance

Adversarial review of the two preceding commits found one real bug, one
half-applied fix, one stale spec, and a test guard with no teeth.

The history fallback did not work. `git log -p -- <capability-path>/spec.md`
omits the `openspec/specs/` prefix that `<capability-path>` is defined
relative to, and `git log` reports an unmatched pathspec as success with no
output. An agent following it literally saw silence, concluded the baseline
was unrecoverable, and marked every already-synced removal not verified -
so the recovery path the fix advertised could never once succeed. The
command is now `git -C <planningHome.root> log -p --
openspec/specs/<capability-path>/spec.md`, anchored on the planning home
because the capability may live in a store rather than the current
checkout, and the text now warns that empty output means the pathspec
matched nothing.

Three further gaps in the same branch. A baseline that only restates its
own title gives an agent no more vocabulary than the heading did, so it is
now unusable evidence like a missing one. The not-verified bail-out moved
ahead of the search, because it previously sat two bullets after the rule
saying that finding nothing is expected. And a miss in the main spec is no
longer attributed to an earlier sync alone, since a differently-worded
baseline misses a literal title lookup.

Step 6 claimed each REMOVED entry "is checked there against its baseline
behavior" in the same breath as the branch that may not check it; it now
excepts an entry marked not verified. The scorecard models the
not-verified wording too, having offered only confirming phrasing.

The #2068 fix was applied to the guardrail but not to step 2, which states
the same scope rule two steps before the exception appears - the same
reading order that caused the bug. Both sites now carry it, and the
guardrail says "any" confirmed path rather than "the one", matching a step
4 written per gap.

`openspec/specs/opsx-verify-skill/spec.md` still described the old
behavior, its Removed-requirement scenario carrying none of the clauses its
Renamed sibling has, so the accepted spec sanctioned the silent pass. It
now mirrors the template, as #1962 did for the preceding fix.

Tests: the ordering assertion compared two unguarded `indexOf` results, so
a reworded sentence would have made it pass on -1 and retired the only
structural guard in the change. Indices are now checked present first, the
fallback is pinned between the primary read and the search, the negative
guards match reworded re-introductions rather than one exact sentence, the
scope-rule check sweeps the whole body instead of a tail slice that could
not see step 2, and the git command and the "(step 4)" cross-reference are
pinned. All 20 of the hardened assertions fail against the two preceding
commits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(verify): quote the planning root in the history fallback command

An unquoted `git -C <planningHome.root>` splits on spaces, so the
fallback failed for any planning root with a space in its path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(verify): read history from the planning root, not the repo root

`git show <rev>:<path>` resolves the path from the repository root,
not from `-C`, so the fallback failed whenever the planning root is a
subdirectory (a monorepo package or a store inside a larger repo). A
leading `./` keeps the path relative to the planning root.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-14 22:37:04 +00:00
..