Skip to content

fix(e2e): the ten anchors #2165 left, and one that resolves to the wrong scenario - #2143

Merged
rubenvdlinde merged 2 commits into
developmentfrom
fix/e2e-anchors-resolve-to-scenarios
Sep 9, 2026
Merged

fix(e2e): the ten anchors #2165 left, and one that resolves to the wrong scenario#2143
rubenvdlinde merged 2 commits into
developmentfrom
fix/e2e-anchors-resolve-to-scenarios

Conversation

@rubenvdlinde

@rubenvdlinde rubenvdlinde commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Reduced after #2165 merged. Two agents were put on this and both finished.
#2165's version is the base for every anchor both changes touched. What is left
here is what it did not cover, plus one anchor I am changing rather than keeping,
argued below.

Re-measured on the merged tree, not carried

With the gate's own resolver, check_spec_anchors.py, via a probe outside the
repo (@e2e rewritten to @spec, cwd set to the app):

Both runs mutation-checked. Two anchors were planted, one naming an invented
REQ-ZAK-999 scenario and one a capability with no home; both were reported and
no real anchor was. The merged-tree run went from 10 findings to 12 with the
mutants in, so the 10 is a measurement and not a default.

This confirms #2165's ten independently. One note on its reading of that number:
the re-run "after the archive move" returning the same ten is evidence that the
2026-09-09 archive of 46 changes broke nothing new, which is worth having, but it
is not the convention's A tag survives its change being archived scenario
passing. That scenario requires the tag to resolve. These ten do not, and
documents-on-the-case is still open, so the move never touched them.

Eight of the ten cannot name a scenario, and two can

#2165 classified all ten as blocked by ConductionNL/.github#730. Eight are. Two
are not, and this is the substantive disagreement:

test canonical scenario, written and resolving today
a case without documents says so and still offers upload document-zaakdossier#scenario-req-zak-004b-empty-dossier-shows-upload-cta-with-drag-and-drop-zone
Versions on a row opens the panel, and restore is refused on a final document document-zaakdossier#scenario-req-zak-006b-restore-is-disabled-for-definitief-documents

REQ-ZAK-004b is "an empty state MUST be shown with an upload button"; the test
asserts the empty state, the upload button, and that the dossier queries stayed
under 400. REQ-ZAK-006b is "the Herstellen button MUST be visibly disabled on all
versions"; the test asserts every Restore button disabled and the stored status
is final. Neither needs documents-on-the-case.

The other eight carry @e2e exclude naming the scenario that governs them and
the change it sits in, so the sweep after documents-on-the-case archives is
mechanical rather than a re-reading. #730 is cited in the first of them.

The anchor that resolves to the wrong scenario

cases-crud.spec.ts anchored its archival-immutability test at
case-management#delete-a-case. That prefix-resolves to
Scenario CM-03a: Delete a case in initial status, which says the delete
succeeds. The test asserts a 403 because the case schema declares
x-openregister-archival, then asserts the record is still in the store. The
anchor claimed coverage of a scenario the code now contradicts, and it resolved,
so no count caught it.

Excluded with the gap named: no scenario states that a user delete of an archival
case is refused and the record survives, and REQ-CM-03 still describes the
pre-archival behaviour.

One anchor from #2165 changed rather than kept

advancing a case status persists and renders the new status was pointed at
REQ-STE-11 Scenario: A handler advances a case. Three things say that is the
wrong scenario:

  • its WHEN is "the handler presses that transition and confirms";
  • REQ-STE-11's prose is "You move the case to its next status from the case
    page
    ";
  • the scenario already carries @e2e tests/e2e/case-lifecycle-on-the-page.spec.ts,
    naming a different file as its coverage.

This test never opens the case page. It calls executeTransition(api, token, caseId, 't1') against the transition endpoint, with a comment saying why, and
then opens the board. No transition button is pressed anywhere in it.

It now cites Scenario: Successful transition with audit trail, whose THEN is
"the case status property SHALL be updated to the target StatusType UUID" —
which is the test's central expect.poll(...).toBe(sm.statusInProgress).

Where I concede, and why it is not just deference

Three of my earlier repoints were wrong on re-reading, and #2165's exclusions are
better for the same reason in each case: the test's distinctive claim has no
scenario, and citing the one its sibling already cites would hide that.

  • bezwaar committees. I cited deep-links-to-grouped-pages-still-resolve
    because that scenario literally names /settings/bezwaar-committees. But the
    test navigates by route precisely because the nav label does not work, so it
    would pass with the nav grouping undone entirely. It cannot detect the
    regression that scenario is about.
  • dashboard console errors. The grid scenario is already cited by the sibling
    test one block above. Citing it twice adds no coverage and buries the fact that
    the console-error leg has no home.
  • current status renders. Same shape: the board half is DASH-V1-006a, which
    the board test below already cites properly; the API leg asserting
    current.statusName has no scenario.

Two anchors I had added are dropped for the same reason, and these were mine
alone:

  • #scenario-visible-action-names on the canvas test. getByRole('menuitem')
    matches whatever the accessible name comes from, so a visible menuitem does
    not prove the name is visible text rather than an aria-label. The comment
    in the test claims it does; the assertion does not.
  • #requirement-guard-evaluation-engine on the engine test. That requirement
    reads "Guards are evaluated in the frontend" and all its scenarios end in
    "the transition button SHALL be disabled". The test asserts a server-side 409.

Both would have resolved. Both would have been the failure this change is about.

Agreed independently, from opposite directions

I reached eight exclusions forced by resolve() consulting the capability index
"LAST RESORT ONLY"; #2165 reached ten anchors in one file with the same cause and
filed ConductionNL/.github#730. Same structural defect, two routes, no shared
working state. That is the strongest evidence produced on this today: an anchor
naming a capability whose canonical spec exists, for a requirement that is still
in an open delta, cannot resolve, and the convention's "the anchor is early, not
wrong" holds only while the canonical file is absent.

Verification

  • check_spec_anchors.py over every @e2e tag: 10 → 0, both ends mutation-checked.
  • eslint on all three changed files: exit 0.
  • Comment-only. No test body, locator or assertion is touched.

Every one of these cited a spec file that exists and a fragment that does
not, so the citation resolved to nothing while reading as verified
coverage. Measured with the gate's own resolver, `check_spec_anchors.py`,
by rewriting the tags to `@spec` in a probe outside the repo and running
the real helper with cwd set to the app: 26 before, 0 after, and the zero
was mutation-checked with two planted anchors before it was believed.

Seventeen are repointed at the scenario the test actually proves, because
a test proves a scenario rather than a requirement. Nine name no scenario
at all and now carry `@e2e exclude` with a reason that says which scenario
governs them and where it lives, rather than the nearest plausible
heading: an anchor that resolves to the wrong thing is worse than one that
does not resolve, because it passes the gate and hides the gap.

Eight of the nine exclusions are one cause. REQ-ZAK-011, REQ-ZAK-012,
REQ-ZAK-013, template-library REQ-005 and REQ-BES-012 are written, but
they live in the open change `documents-on-the-case`, and the resolver
consults its capability index only when the literal path is MISSING. The
canonical `document-zaakdossier/spec.md` exists, so the delta beside it is
never opened and the scenario cannot be cited yet. Archiving that change
is what makes these citable; the exclusion reasons name the scenario so
that sweep is mechanical.

Anchors now name `openspec/specs/<capability>/spec.md`, never a change
path. Three cited `openspec/changes/archive/2026-07-13-.../` and one an
open change directory; a spec path is stable, a change path moves once, on
archive, which is what broke them.
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/dossiq @ 42739ad

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
check-vue3-compile
test-l10n
format
check-schema-l10n
check-l10n-js
composer ✅ 106/106
npm ✅ 548/548
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman ⏭️
Playwright
Hydra gates

Quality workflow — 2026-09-09 14:09 UTC

Download the full PDF report from the workflow artifacts.

 left

#2165 landed the same work for sixteen of the twenty-six anchors. Its
version is the base for every anchor both changes touched. This branch now
carries only what it did not cover:

- the ten anchors in `tests/e2e/case-documents.spec.ts`, the one file
  #2165 does not touch, which it filed as ConductionNL/.github#730,
- the `#delete-a-case` anchor that resolves to a scenario asserting the
  opposite outcome,
- one changed anchor, argued in the PR description.

Three of my earlier calls are dropped as wrong on re-reading, not merely
superseded: the bezwaar committees repoint, the dashboard console-error
repoint, and the current-status repoint each cited a scenario the test
does not exercise. Two anchors I had ADDED are dropped for the same
reason: a visible `menuitem` role does not prove the "Visible action
names" scenario, and "Guard Evaluation Engine" says guards are evaluated
in the frontend while the test asserts a server-side 409.
@rubenvdlinde rubenvdlinde changed the title fix(e2e): twenty-six @e2e anchors named a heading nobody wrote fix(e2e): the ten anchors #2165 left, and one that resolves to the wrong scenario Sep 9, 2026
@rubenvdlinde
rubenvdlinde merged commit 57ede9c into development Sep 9, 2026
44 checks passed
@rubenvdlinde
rubenvdlinde deleted the fix/e2e-anchors-resolve-to-scenarios branch September 9, 2026 14:24
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/dossiq @ 7d4fb89

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
check-vue3-compile
test-l10n
format
check-schema-l10n
check-l10n-js
composer ✅ 106/106
npm ✅ 548/548
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman ⏭️
Playwright
Hydra gates

Quality workflow — 2026-09-09 14:57 UTC

Download the full PDF report from the workflow artifacts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant