fix(e2e): the ten anchors #2165 left, and one that resolves to the wrong scenario - #2143
Merged
Merged
Conversation
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.
Contributor
Quality Report — ConductionNL/dossiq @
|
| 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.
Contributor
Quality Report — ConductionNL/dossiq @
|
| 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Re-measured on the merged tree, not carried
With the gate's own resolver,
check_spec_anchors.py, via a probe outside therepo (
@e2erewritten to@spec, cwd set to the app):developmentat 7ef65d5, every one of them intests/e2e/case-documents.spec.ts, the single file chore(openspec): finish the anchor convention and re-verify the 21 open changes #2165 does not touch.Both runs mutation-checked. Two anchors were planted, one naming an invented
REQ-ZAK-999scenario and one a capability with no home; both were reported andno 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 archivedscenariopassing. That scenario requires the tag to resolve. These ten do not, and
documents-on-the-caseis 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:
a case without documents says so and still offers uploaddocument-zaakdossier#scenario-req-zak-004b-empty-dossier-shows-upload-cta-with-drag-and-drop-zoneVersions on a row opens the panel, and restore is refused on a final documentdocument-zaakdossier#scenario-req-zak-006b-restore-is-disabled-for-definitief-documentsREQ-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 needsdocuments-on-the-case.The other eight carry
@e2e excludenaming the scenario that governs them andthe change it sits in, so the sweep after
documents-on-the-casearchives ismechanical rather than a re-reading. #730 is cited in the first of them.
The anchor that resolves to the wrong scenario
cases-crud.spec.tsanchored its archival-immutability test atcase-management#delete-a-case. That prefix-resolves toScenario CM-03a: Delete a case in initial status, which says the deletesucceeds. The test asserts a 403 because the
caseschema declaresx-openregister-archival, then asserts the record is still in the store. Theanchor 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 statuswas pointed atREQ-STE-11
Scenario: A handler advances a case. Three things say that is thewrong scenario:
page";
@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, andthen 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
statusproperty 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.
deep-links-to-grouped-pages-still-resolvebecause that scenario literally names
/settings/bezwaar-committees. But thetest 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.
test one block above. Citing it twice adds no coverage and buries the fact that
the console-error leg has no home.
the board test below already cites properly; the API leg asserting
current.statusNamehas no scenario.Two anchors I had added are dropped for the same reason, and these were mine
alone:
#scenario-visible-action-nameson the canvas test.getByRole('menuitem')matches whatever the accessible name comes from, so a visible
menuitemdoesnot prove the name is visible text rather than an
aria-label. The commentin the test claims it does; the assertion does not.
#requirement-guard-evaluation-engineon the engine test. That requirementreads "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.pyover every@e2etag: 10 → 0, both ends mutation-checked.eslinton all three changed files: exit 0.