fix(gate-57): drop the caller-less cascade-delete wrapper - #128
Conversation
gate-57 orphaned-write-capability reported 1 finding over 82 lib/Service files: DashboardVersionService::deleteVersionsForDashboard(). It was a literal pass-through to DashboardVersionMapper::deleteByDashboardUuid() with zero callers. The live cascade path is Listener\VersionsListener, which is registered against DashboardDeletedEvent in Application.php per dashboard-cascade-events REQ-CSC-002/REQ-CSC-003 and calls the mapper directly, inside the log-and-continue envelope REQ-CSC-006 requires. Routing the listener through a second wrapper would add a hop and nothing else, so the wrapper is removed rather than wired. The dashboard-versioning spec claimed the wrapper was 'wired and ready for the sibling cascade-events listener; the dashboard delete path itself does not yet invoke them'. That was stale — the listener has shipped. The spec now records the real path and states that there is deliberately no service-layer wrapper. Before: 1 finding over 82 files. After: 0 findings over 82 files.
Quality Report — ConductionNL/launchpad @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 104/104 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-16 10:47 UTC
Download the full PDF report from the workflow artifacts.
S27 — the PHPUnit red is the coverage ratchet, and no test can honestly clear itFirst, the question that was open: is this PHPUnit red the ratchet or a broken test? It is the ratchet, and the same job proves it: Zero failing tests. The PR removes 3 statements, all 3 covered. Why no test fixes thisThe guard is one integer cross-multiplication with no tolerance, so deleting And the file-scoping fix does not rescue this one
Worse than the whole-project verdict, because the deleted wrapper was 100% covered inside a 72% file. So this PR needs a ratchet decision, not a code change. The narrow fix that would clear it honestly is to have the guard compare The |
…nchpad # Conflicts: # lib/Service/DashboardVersionService.php # tests/Unit/Service/DashboardVersionServiceTest.php
Quality Report — ConductionNL/launchpad @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 104/104 | |||
| npm | ✅ | ✅ 525/525 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-16 19:54 UTC
Download the full PDF report from the workflow artifacts.
gate-57 orphaned-write-capability — launchpad
Workstream S8 of the fleet debt programme. Baseline run
31929375066(headcc6858e8, unchanged at the time of writing — this worktree's base is currentorigin/development).Gate evidence (L1 / L3 / L4)
File set reproduced from
run-hydra-gates.sh:git ls-files -- lib/Service,.php, minustests/. Output captured to a file, never piped.Helper identity line:
app_id=launchpad source=appinfo/info.xml foundation=no.Per-finding judgement — 1 finding, 1 delete, 0 wires
lib/Service/DashboardVersionService.php:542 deleteVersionsForDashboarddeleteVersionsForDashboard()was one statement:return $this->versionMapper->deleteByDashboardUuid(...). It had zero callers. The live cascade path isListener\VersionsListener, registered againstDashboardDeletedEventinApplication.php(dashboard-cascade-eventsREQ-CSC-002 names it explicitly in the required-listeners list) — and that listener callsdeleteByDashboardUuid()directly, inside the try/catch log-and-continue envelope REQ-CSC-006 requires. Its six sibling listeners (LocksListener,TranslationsListener,ReactionsListener,WidgetPlacementsListener, …) all use the same shape: listener → mapper, no service hop.So this is the gate's "already live under a different class" case, not a missing wire. Routing the listener through a wrapper would add a hop and change nothing; adding any other caller would mean a second delete path outside the cascade envelope.
The
dashboard-versioningspec said the wrapper was "wired and ready for the sibling cascade-events listener; the dashboard delete path itself does not yet invoke them (future work alongsidedashboard-cascade-events)". That sentence is stale — the listener has shipped. It now records the real path and states there is deliberately no service-layer wrapper. The method's entry intools/spec-annotations-allowlist.txtis removed with it.No gate-exclusion comment was used (programme law L8).
Quality (L10)
Run under PHP 8.3 in a
php:8.3-clicontainer against this worktree (host PHP is 8.2):lint— cleanphpcs(scope islib/perphpcs.xml) — 0 errors inlib/Service/DashboardVersionService.php; repo-wide 2 errors / 470 warnings, all in untouched filesphpmd— clean, exit 0phpstan—[OK] No errorspsalm— 28 errors, every oneUndefinedClass: ZipArchiveinlib/Service/ImportService.php(the barephp:8.3-cliimage has nozipextension); none in a file this PR touchesphpunit— 1572 tests, 3971 assertions, 16 errors, all 16Error: Class "ZipArchive" not foundinImportServiceTest; same missing extension, none in a file this PR touchesScope
One method deleted, its unit test deleted with it, one allowlist line and one stale spec sentence corrected. No behaviour change on any live path — the cascade delete still runs, from the listener, exactly as before.