Skip to content

test(e2e): four integration surfaces nothing ever opened, and one screen already covered - #455

Open
rubenvdlinde wants to merge 6 commits into
developmentfrom
gates/decidesk-g26
Open

test(e2e): four integration surfaces nothing ever opened, and one screen already covered#455
rubenvdlinde wants to merge 6 commits into
developmentfrom
gates/decidesk-g26

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

gate-26 (visual-coverage) FAIL 5. The five screens split cleanly into two very
different problems, and only one of them was a real gap.

FOUR SCREENS GENUINELY HAD NO TEST

MeetingIntegrations, DecisionIntegrations, AgendaItemIntegrations and
MotionIntegrations are routed, manifest-declared pages whose whole job is to
mount CnDetailPage with sidebar.useRegistry: true for one object type
(ADR-019). integration-registry.spec.ts navigates to the meeting route, but
every assertion it makes is about the shared registry sidebar — the counts and
tab ids CnObjectSidebar renders. Not one touches the page component, so all four
views could render an empty shell and that spec would stay green.

tests/e2e/integration-surfaces.spec.ts drives all four and asserts their OWN
markup: the body block each view declares and the back-link it wires to its
detail route. The eight data-testids it targets were verified to exist in the
four .vue files first — a spec that selects on invented ids fails for a reason
that has nothing to do with the screen.

Read-only by construction: GETs and navigations only, nothing created or
deleted. It drives the objects ci-seed.sh already provisions, and when they are
absent it fails LOUDLY rather than skipping, because a surface that was never
opened must not report as a pass.

THE FIFTH WAS ALREADY COVERED — THE GATE JUST COULD NOT SEE IT

src/views/settings/UserSettings.vue was reported as having no e2e test. It has
one, and has had all along: user-settings.spec.ts:289 navigates to
/apps/decidesk/user-settings and asserts the component's own
user-settings-page body block mounts.

gate-26 matches a page component to its coverage on the literal token
\bUserSettings\b — file stem or manifest page id. Every mention in that file
was the kebab-case route /user-settings, so the token never appeared and a
screen with a running browser test was counted as uncovered. The fix is to write
the component name where the test already is. That is RECORDING AN EXISTING
TRUTH, not waiving a gap: no @visual exclude is used anywhere in this change,
and the note is pinned to the test so the two cannot drift apart.

MEASURED (gate package ConductionNL/.github @ b8c7eade, full scope, rebased onto
012f96b so the base is the tree that actually exists):

before 19=984 25=25 26=5 54=2 63=2 53 PASS, skips 24/29/33/47/48/61
after 19=984 25=25 54=2 63=2 54 PASS, skips 24/29/33/47/48/61

Skip set is byte-identical, so no failure was converted into a skip, and no
other gate moved.

Both directions proven: with integration-surfaces.spec.ts moved aside, gate-26
reports FAIL — 4 on the same runner — the four Integrations views come back and
UserSettings stays closed, which is what confirms the two remedies are
independent and that neither is doing the other's work.

The claim this PR makes, falsifiably

It adds four CI-executed e2e tests. The e2e baseline on development is 11 failed / 102 passed / 59 skipped. If these four pass, that becomes 11 failed / 106 passed / 59 skipped. If any of them fails, the screen it drives is broken and I would rather know — but I am not entitled to call this green until the run says so.

Skips must stay at 59. A rise there means a test declined to run and the coverage is not real.

🤖 Generated with Claude Code

…een already covered

gate-26 (visual-coverage) FAIL 5. The five screens split cleanly into two very
different problems, and only one of them was a real gap.

FOUR SCREENS GENUINELY HAD NO TEST

MeetingIntegrations, DecisionIntegrations, AgendaItemIntegrations and
MotionIntegrations are routed, manifest-declared pages whose whole job is to
mount CnDetailPage with `sidebar.useRegistry: true` for one object type
(ADR-019). `integration-registry.spec.ts` navigates to the meeting route, but
every assertion it makes is about the shared registry sidebar — the counts and
tab ids CnObjectSidebar renders. Not one touches the page component, so all four
views could render an empty shell and that spec would stay green.

tests/e2e/integration-surfaces.spec.ts drives all four and asserts their OWN
markup: the body block each view declares and the back-link it wires to its
detail route. The eight `data-testid`s it targets were verified to exist in the
four .vue files first — a spec that selects on invented ids fails for a reason
that has nothing to do with the screen.

Read-only by construction: GETs and navigations only, nothing created or
deleted. It drives the objects ci-seed.sh already provisions, and when they are
absent it fails LOUDLY rather than skipping, because a surface that was never
opened must not report as a pass.

THE FIFTH WAS ALREADY COVERED — THE GATE JUST COULD NOT SEE IT

`src/views/settings/UserSettings.vue` was reported as having no e2e test. It has
one, and has had all along: `user-settings.spec.ts:289` navigates to
`/apps/decidesk/user-settings` and asserts the component's own
`user-settings-page` body block mounts.

gate-26 matches a page component to its coverage on the literal token
`\bUserSettings\b` — file stem or manifest page id. Every mention in that file
was the kebab-case route `/user-settings`, so the token never appeared and a
screen with a running browser test was counted as uncovered. The fix is to write
the component name where the test already is. That is RECORDING AN EXISTING
TRUTH, not waiving a gap: no `@visual exclude` is used anywhere in this change,
and the note is pinned to the test so the two cannot drift apart.

MEASURED (gate package ConductionNL/.github @ b8c7eade, full scope, rebased onto
012f96b so the base is the tree that actually exists):

  before  19=984 25=25 26=5 54=2 63=2   53 PASS, skips 24/29/33/47/48/61
  after   19=984 25=25      54=2 63=2   54 PASS, skips 24/29/33/47/48/61

Skip set is byte-identical, so no failure was converted into a skip, and no
other gate moved.

Both directions proven: with integration-surfaces.spec.ts moved aside, gate-26
reports `FAIL — 4` on the same runner — the four Integrations views come back and
UserSettings stays closed, which is what confirms the two remedies are
independent and that neither is doing the other's work.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/decidesk @ c7f23e1

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
test-l10n
composer ✅ 100/100
npm ✅ 548/548
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-11 09:43 UTC

Download the full PDF report from the workflow artifacts.

…, not their own view

The e2e spec added in the previous commit failed on three of the four surfaces it
drives, and it was right to. MeetingIntegrations, DecisionIntegrations and
AgendaItemIntegrations never rendered their own components at all.

  ✓ MotionIntegrations      renders its body and back-link
  ✘ MeetingIntegrations     [data-testid="meeting-integrations"] not found
  ✘ DecisionIntegrations    [data-testid="decision-integrations"] not found
  ✘ AgendaItemIntegrations  [data-testid="agenda-item-integrations"] not found

The one that passed is the only page declared `type: "custom"`. The three that
failed were `type: "detail"`, and for a detail page CnPageRenderer mounts
CnDetailPage generically — it never resolves a `component`. Two of them already
DECLARED `"component": "..."`; the key was simply ignored because of the type.
So three .vue files, each documenting itself as "routed from the manifest at
/…/integrations", were dead code, and the routes quietly served a generic detail
page instead of the intended surface.

MeetingIntegrations had a second, independent reason it could not render: it was
never imported into src/registry.js. Fixing only the type would have left it
resolving to nothing.

This is the same defect this repo already hit once. The registry comment beside
MotionIntegrations records it: "src/manifest.json page 15 names this component on
the route /motions/:id/integrations, but nothing registered it — resolution fell
through and the page rendered NOTHING." That one was found and fixed; these three
were not, because nothing opened them. gate-26 was not reporting missing
documentation — it was pointing at screens no test had ever rendered, which is
exactly the state in which this class of bug survives.

Fix: the three pages become `type: "custom"` with a page-level `component`,
matching MotionIntegrations, and MeetingIntegrations is imported and registered.
All four surfaces are now declared identically.

  npm run check:manifest -> Ajv validation: PASS (0 errors), schema 2.13.0
  eslint src/registry.js -> clean

Gates, full scope, package @ b8c7eade:

  19=984 25=25 54=2 63=2, 54 PASS, skips 24/29/33/47/48/61 — unchanged from the
  previous commit. gate-26 stays PASS; no gate moved in either direction.

FALSIFIABLE CLAIM FOR CI: integration-surfaces goes 1 passed / 3 failed to 4
passed / 0 failed, so the suite goes 14 failed / 103 passed to 11 failed / 106
passed, with skips held at 59. integration-registry must not regress — it drives
the same meeting route and asserts on the registry sidebar, which these views
mount themselves via `sidebar.useRegistry: true`. If that spec moves, this change
is wrong and I will say so.
@rubenvdlinde

Copy link
Copy Markdown
Contributor Author

My prediction was wrong, and the failure was real

I claimed 11 failed / 106 passed / 59 skipped. CI returned 14 failed / 103 passed / 59 skipped — three of the four new tests failed.

They were right to fail.

✓ MotionIntegrations      renders its body and back-link
✘ MeetingIntegrations     [data-testid="meeting-integrations"] not found
✘ DecisionIntegrations    [data-testid="decision-integrations"] not found
✘ AgendaItemIntegrations  [data-testid="agenda-item-integrations"] not found

The one that passed is the only page declared type: "custom". The three that failed were type: "detail" — and for a detail page CnPageRenderer mounts CnDetailPage generically and never resolves a component. Two of the three already declared "component": "..."; the key was silently ignored because of the type. MeetingIntegrations had a second, independent reason: it was never imported into src/registry.js at all.

So three .vue files — each documenting itself as "routed from the manifest at /…/integrations" — were dead code, and those routes served a generic detail page instead of the intended surface.

This repo has hit this exact defect before. The registry comment beside MotionIntegrations records it: "nothing registered it — resolution fell through and the page rendered NOTHING." That one was caught. These three were not, because nothing ever opened them — which is precisely the state in which this bug survives. gate-26 was not asking for documentation; it was pointing at screens no test had rendered.

b186a659 fixes the app: the three pages become type: "custom" with a page-level component, and MeetingIntegrations is imported and registered. All four are now declared identically.

Falsifiable claim, second attempt: integration-surfaces goes 1 passed / 3 failed to 4 passed / 0 failed, suite 11 failed / 106 passed / 59 skipped. integration-registry must not regress — it drives the same meeting route and asserts on the registry sidebar, which these views mount themselves. If it moves, this change is wrong.

I am not merging until CI says so.

@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/decidesk @ 3f757e6

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
test-l10n
composer ✅ 100/100
npm ✅ 548/548
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-11 10:21 UTC

Download the full PDF report from the workflow artifacts.

juanclaude-conduction and others added 2 commits August 11, 2026 12:24
…ail page, not their own view"

This reverts commit b186a65.
…eir filenames suggest

Second correction on this branch, and the first one was mine.

I read gate-26's four Integrations findings as "these .vue views never render",
flipped three manifest pages from `type: "detail"` to `type: "custom"`, registered
the missing MeetingIntegrations, and predicted 4/4 green. CI returned exactly the
same 3 failures. That change is reverted in 01979f3 — it did nothing, because
the reason was wrong.

The discriminator is `config.widgets`, not `type`:

  MotionIntegrations      type: "custom"   widgets: 0   -> renders the .vue file
  MeetingIntegrations     type: "detail"   widgets: 4   -> renders the widget grid
  DecisionIntegrations    type: "detail"   widgets: 3   -> renders the widget grid
  AgendaItemIntegrations  type: "detail"   widgets: 3   -> renders the widget grid

A page that declares `config.widgets` is composed declaratively: CnPageRenderer
lays out the grid and the `.vue` file is never mounted. That holds for `custom`
too, which is why flipping the type changed nothing. Three of these screens are
not broken at all — they work, they are simply not served by the file they are
named after.

So the spec now declares, per surface, HOW it renders, and asserts that:

  - `renders: 'component'` (Motion) — the view's own body block, its explanatory
    copy, and its back-link. Unchanged.
  - `renders: 'widgets'` (the other three) — the page heading plus a non-empty
    declarative widget grid. `config.widgets` is non-empty for all three, so zero
    rendered widgets means the grid did not build, which is the real regression
    to catch here.

Both arms assert something that can fail. A component testid on a widget page is
a test that can never pass; "the route loaded" is a test that can never fail.

⚠️ SEPARATE FINDING, NOT FIXED HERE: src/views/MeetingIntegrations.vue,
DecisionIntegrations.vue and AgendaItemIntegrations.vue are dead code. Nothing
mounts them — MeetingIntegrations is not even imported into src/registry.js, and
each file's own docblock claims it is "routed from the manifest", which is no
longer true. Deleting them is the honest resolution and would make gate-26's
finding disappear for the right reason rather than because a spec names the
files. I am not deleting three components on my own read; it is filed for a
decision.

Gates, full scope, package @ b8c7eade: 19=984 25=25 54=2 63=2, 54 PASS, skips
24/29/33/47/48/61 — gate-26 PASS, nothing else moved, skip set byte-identical.

CLAIM FOR CI: all four integration-surfaces tests pass, suite 11 failed / 106
passed / 59 skipped. If a widget arm fails, that page's grid genuinely does not
build and I want to know.
@rubenvdlinde

Copy link
Copy Markdown
Contributor Author

Second correction: my fix did nothing, and the reason was wrong

CI returned 14 failed / 103 passed / 59 skipped again — the identical three failures. b186a659 (detail -> custom + registering MeetingIntegrations) is reverted in 01979f38.

The discriminator is config.widgets, not type:

page type config.widgets what renders
MotionIntegrations custom 0 the .vue file
MeetingIntegrations detail 4 the widget grid
DecisionIntegrations detail 3 the widget grid
AgendaItemIntegrations detail 3 the widget grid

A page declaring config.widgets is composed declaratively — CnPageRenderer lays out the grid and never mounts the .vue. That holds for custom too, which is exactly why flipping the type changed nothing. Those three screens are not broken. They work; they are simply not served by the file they are named after.

1d6fd728 makes the spec assert what each route actually renders: Motion keeps the component assertions; the other three assert the page heading plus a non-empty widget grid. Both arms can fail — a component testid on a widget page can never pass, and "the route loaded" can never fail.

Separate finding, deliberately not fixed here

MeetingIntegrations.vue, DecisionIntegrations.vue and AgendaItemIntegrations.vue are dead code. Nothing mounts them; MeetingIntegrations is not even imported into src/registry.js; and each file's docblock still claims it is "routed from the manifest", which is no longer true. Deleting them is the honest resolution and would clear gate-26 for the right reason rather than because a spec mentions the filenames. I am not deleting three components on my own read — filed for a decision.

Hydra Gates

[gate-63] FAIL — 2, the pre-existing ADR-079 pair, pulled into diff scope because this branch touches src/manifest.json. Same two findings as the base commit. The revert leaves the manifest untouched apart from that, and full-scope locally is 19=984 25=25 54=2 63=2, 54 PASS, skips 24/29/33/47/48/61 — gate-26 PASS, nothing else moved.

Claim for this run: all four integration-surfaces tests pass; suite 11 failed / 106 passed / 59 skipped.

@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/decidesk @ 8fba541

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
test-l10n
composer ✅ 100/100
npm ✅ 548/548
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-11 11:02 UTC

Download the full PDF report from the workflow artifacts.

juanclaude-conduction and others added 2 commits August 11, 2026 13:03
… what their filenames suggest"

This reverts commit 1d6fd72.
…e comment closing the other three

Reduces this spec from four surfaces to one, and takes gate-26 from a false PASS
to an honest FAIL 3.

WHY ONE

I was wrong about the other three routes twice, at a CI run each:

  1. "those views never render" -> flipped three manifest pages from
     `type: "detail"` to `type: "custom"` and registered a missing import.
     CI: byte-identical failures. Reverted (01979f3).
  2. "`config.widgets` is the discriminator, so assert the widget grid" -> CI:
     still failed, and a page-heading assertion added along the way ALSO broke
     the motion test, the only one that had been passing: 14 failed became 15.
     Reverted (4b18227).

What CI has proven, twice, is narrow: the motion route mounts its own component
and its body-block and back-link testids are there. The other three do not
produce their testids and I do not know what they render instead. The local dev
container cannot answer it — its bundle predates development by weeks — and
another guess costs another 28-minute run and teaches nothing.

THE COMMENT WAS CLOSING THE FINDING

Cutting the spec down to one surface left gate-26 reporting PASS, which is what
made me look. It was not the test. gate-26 matches a page component to its
coverage on the bare PascalCase token anywhere in the e2e corpus, and this file's
header listed all four paths as documentation. That was enough:

  with this file present:      [gate-26] visual-coverage: PASS
  with it moved aside:         [gate-26] FAIL — 4

Three screens no test touches, reported as covered, by a comment. The names are
now written nowhere in this file and the gate reports FAIL 3, which is true.

That is worth stating plainly because it is the same defect shape as .github#343
on gate-19 (a file-level tag counted as coverage without checking any test): when
a gate matches on a TOKEN rather than on a test, prose can satisfy it. Anything
that closes a finding has to be checked with the remedy REMOVED, or the check is
measuring the wrong thing.

WHAT LANDS

  - MotionIntegrations: a real Playwright test, read-only, failing loudly rather
    than skipping when its seed data is absent.
  - UserSettings: unchanged from the earlier commit — an existing running test
    at user-settings.spec.ts:289 that the gate could not see because every
    mention was the kebab-case route. Recording an existing truth.

  gate-26: 5 -> 3. No `@visual exclude` anywhere in this change.

WHAT DOES NOT, AND WHY IT STAYS RED

MeetingIntegrations.vue, DecisionIntegrations.vue and AgendaItemIntegrations.vue
are uncovered and gate-26 will go on saying so. Nothing appears to mount them;
MeetingIntegrations is not imported into src/registry.js at all; each docblock
still claims it is "routed from the manifest". They need a decision — delete as
dead code, or establish what actually serves those routes — not a fourth guess
dressed up as a test.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/decidesk @ 673bffa

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
test-l10n
composer ✅ 100/100
npm ✅ 548/548
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-11 11:42 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.

2 participants