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
29 changes: 28 additions & 1 deletion openspec/changes/spec-anchors-name-the-capability/proposal.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,31 @@ same backlog, so `git log origin/development -5 -- <path>` comes before building
not after. This branch went out CONFLICTING with 4 of 49 checks green, which is
the shape a conflicting PR always has.

## The correction this change had to make to itself

The first version of this proposal claimed 6,244 anchors and zero unresolvable.
That was measured with a REIMPLEMENTATION of the resolver rather than the
resolver, and it was wrong in both directions.

Mine ignored two things the real one does. The flat `openspec/specs/<cap>.md`
spelling, which predates the directory form: that alone made it call 13 of
planix's healthy anchors dangling. And fragment checking: the real helper
verifies that a `#fragment` names a heading somebody wrote, and mine only
checked the file.

Run properly, this repo has **28 unresolved `@e2e` anchors**, every one of them
"anchor not found". Two of the 28 were broken by this session's own #2057, which
renumbered two scenarios from `DASH-V1-006d/e` to `006f/g` to clear a collision
and left two `@e2e` citations naming the old ids. Every check passed. Those two
are repointed here; the other 26 are pre-existing and are the debt the gate fix
in "Not in this change" would surface.

The lesson is the one this whole investigation keeps teaching: every wrong
number came from a hand-rolled resolver, and every right one came from running
`check_spec_anchors.py`. `@e2e` was eventually measured by rewriting the tags to
`@spec` in a probe file OUTSIDE the repo and running the real helper with cwd set
to the app, which is exact and touches nothing.

## Not in this change

- **Gate 46 does not read `@e2e`.** Its pattern is `@spec\s+(openspec/...)`, so
Expand All @@ -100,4 +125,6 @@ the shape a conflicting PR always has.
`scripts/` are never opened.

Both live in `ConductionNL/.github`, `hydra-gates/scripts/lib/check_spec_anchors.py`,
and neither is fixable here.
and neither is fixable here. Filed as ConductionNL/.github#726 (1,964 `@e2e`
anchors fleet-wide, 194 dangling) and #727 (52 anchors under `appinfo/` and
`scripts/` never opened, 8 of openregister's 9 in `routes.php` dangling).
36 changes: 32 additions & 4 deletions openspec/changes/spec-anchors-name-the-capability/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,44 @@

## 4. Raise upstream, not here

- [ ] 4.1 Gate 46 reads only `@spec`. Its pattern is `@spec\s+(openspec/...)`,
- [x] 4.1 Filed as ConductionNL/.github#726. Gate 46 reads only `@spec`. Its pattern is `@spec\s+(openspec/...)`,
so 286 `@e2e` anchors in this repo are checked by nothing. This is why the six
were found by hand, twice, rather than by CI once. Belongs in
`ConductionNL/.github`, `hydra-gates/scripts/lib/check_spec_anchors.py`.
- [ ] 4.2 Gate 46 enumerates `lib src tests`. Two anchors under `appinfo/` and
- [x] 4.2 Filed as ConductionNL/.github#727. Gate 46 enumerates `lib src tests`. Two anchors under `appinfo/` and
`scripts/` are never opened. Same helper.

## 5. Process, recorded because it cost a branch
## 5. The claim this change first made was wrong, twice over

- [x] 5.1 Search before building. `git log origin/development -5 -- <path>` comes
- [x] 5.1 "6,244 anchors, zero unresolvable" was measured with a REIMPLEMENTATION
of the resolver, not the resolver. Mine ignored two things the real one does:
the flat `openspec/specs/<cap>.md` spelling, and fragment checking. It called
13 of planix's anchors dangling that are fine, and it called 28 of this repo's
`@e2e` anchors fine that are not.
- the real resolver, run over all 284 `@e2e` targets here, reports 28
unresolved. Every one is "anchor not found": the file resolves and the
`#fragment` names a heading nobody wrote.
- [x] 5.2 Two of the 28 were broken by this session's own #2057, and nothing
caught it.
- #2057 renumbered the kanban delta's scenarios from `DASH-V1-006d/e` to
`006f/g` to clear a collision with two scenarios the spec already had.
`tests/e2e/spec-coverage/kanban-board-keyboard-status-transition.spec.ts`
still cited `006d` and `006e`. Gate 46 did not look, because they are `@e2e`.
Repointed at the canonical `openspec/specs/dashboard/spec.md` per the rule
this change writes down.
- [ ] 5.3 The other 26 are pre-existing dossiq debt that the gate fix in section
4 would surface on the next PR touching those files. Not fixed here: each one
needs a judgement about which scenario it meant, which is the work, not the
sed.
- [x] 5.4 Stop reimplementing the instrument. Every wrong number in this
investigation came from a hand-rolled resolver; every correct one came from
running `check_spec_anchors.py`. `@e2e` was measured by rewriting the tags to
`@spec` in a probe file outside the repo and running the real helper against
it, which is how the fleet numbers in section 4 were taken.

## 6. Process, recorded because it cost a branch

- [x] 6.1 Search before building. `git log origin/development -5 -- <path>` comes
first: two PRs on this exact topic landed while this branch was being written,
and the PR went out CONFLICTING with 4 of 49 checks passing, which is the shape
a conflicting PR always has and reads green.
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ test.describe('Workflow Board keyboard status transition', () => {
return card
}

// @e2e openspec/changes/archive/2026-09-09-kanban-board-keyboard-status-transition/specs/dashboard/spec.md#scenario-dash-v1-006d-keyboard-only-status-transition-new
// @e2e openspec/specs/dashboard/spec.md#scenario-dash-v1-006f-keyboard-only-status-transition-new
test('a case card exposes a keyboard-operable "Move to…" menu', async ({
page,
}) => {
Expand Down Expand Up @@ -171,7 +171,7 @@ test.describe('Workflow Board keyboard status transition', () => {
await page.keyboard.press('Escape')
})

// @e2e openspec/changes/archive/2026-09-09-kanban-board-keyboard-status-transition/specs/dashboard/spec.md#scenario-dash-v1-006e-drag-path-unchanged-new
// @e2e openspec/specs/dashboard/spec.md#scenario-dash-v1-006g-drag-path-unchanged-new
test('case cards remain draggable for mouse/touch users', async ({ page }) => {
const card = await openBoardAndFindSeededCard(page)
await expect(card).toHaveAttribute('draggable', 'true')
Expand Down
Loading