Skip to content

fix(gate-57): drop the caller-less cascade-delete wrapper - #128

Merged
rubenvdlinde merged 2 commits into
developmentfrom
S8/gate-57-launchpad
Aug 16, 2026
Merged

fix(gate-57): drop the caller-less cascade-delete wrapper#128
rubenvdlinde merged 2 commits into
developmentfrom
S8/gate-57-launchpad

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

gate-57 orphaned-write-capability — launchpad

Workstream S8 of the fleet debt programme. Baseline run 31929375066 (head cc6858e8, unchanged at the time of writing — this worktree's base is current origin/development).

Gate evidence (L1 / L3 / L4)

python3 vendor/conduction/hydra-gates/hydra-gates/scripts/lib/check_orphaned_write_capability.py <82 files> > out.txt

File set reproduced from run-hydra-gates.sh: git ls-files -- lib/Service, .php, minus tests/. Output captured to a file, never piped.

findings files measured helper exit
before 1 82 0
after 0 82 0

Helper identity line: app_id=launchpad source=appinfo/info.xml foundation=no.

Per-finding judgement — 1 finding, 1 delete, 0 wires

# finding judgement reasoning
1 lib/Service/DashboardVersionService.php:542 deleteVersionsForDashboard DELETE Literal pass-through to a mapper method the live cascade listener already calls directly.

deleteVersionsForDashboard() was one statement: return $this->versionMapper->deleteByDashboardUuid(...). It had zero callers. The live cascade path is Listener\VersionsListener, registered against DashboardDeletedEvent in Application.php (dashboard-cascade-events REQ-CSC-002 names it explicitly in the required-listeners list) — and that listener calls deleteByDashboardUuid() 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-versioning spec 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 alongside dashboard-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 in tools/spec-annotations-allowlist.txt is removed with it.

No gate-exclusion comment was used (programme law L8).

Quality (L10)

Run under PHP 8.3 in a php:8.3-cli container against this worktree (host PHP is 8.2):

  • lint — clean
  • phpcs (scope is lib/ per phpcs.xml) — 0 errors in lib/Service/DashboardVersionService.php; repo-wide 2 errors / 470 warnings, all in untouched files
  • phpmd — clean, exit 0
  • phpstan[OK] No errors
  • psalm — 28 errors, every one UndefinedClass: ZipArchive in lib/Service/ImportService.php (the bare php:8.3-cli image has no zip extension); none in a file this PR touches
  • phpunit — 1572 tests, 3971 assertions, 16 errors, all 16 Error: Class "ZipArchive" not found in ImportServiceTest; same missing extension, none in a file this PR touches

Scope

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.

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.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/launchpad @ 82f62ad

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.

@rubenvdlinde

Copy link
Copy Markdown
Contributor Author

S27 — the PHPUnit red is the coverage ratchet, and no test can honestly clear it

First, 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:

OK (1573 tests, 4048 assertions)

Coverage current:    51.70%  (11647/22530 statements)
Coverage merge base: 51.70%  (11650/22533 statements)
FAIL: coverage dropped against the merge base by less than 0.01% —
      too little to show in the percentage, but a real loss in the counts below.
      merge base 11650/22533 -> head 11647/22530 statements.

Zero failing tests. The PR removes 3 statements, all 3 covered.

Why no test fixes this

The guard is one integer cross-multiplication with no tolerance, so deleting d statements of which c were covered lowers the ratio whenever c/d exceeds the scope's own ratio. Here that is 100% against 51.70%. Every statement this PR removes is a statement it cannot cover, so nothing written inside it can move the number — the only moves are to delete less (i.e. not do the work), to add covered code elsewhere as filler, or to delete additional uncovered statements until the arithmetic balances. That last one is the honest measure of what the gate is asking for: 3 more uncovered statements anywhere in the repo, or 2 more from this same file under file scoping. I have not done any of them.

And the file-scoping fix does not rescue this one

ConductionNL/.github#473 scopes the ratchet to changed files, and launchpad#126 is the matching one-file adoption of the canonical guard (still open). That fix is worth merging on its own account — it removes a whole class of false failures — but measured against this PR it does not help. Running the canonical guard on the measured per-file numbers for lib/Service/DashboardVersionService.php:

Scoped to 1 changed PHP file(s).
Changed files, head:    72.05%  (183/254 statements)
Changed files, base:    72.37%  (186/257 statements)
FAIL: coverage of the files this change touches dropped by 0.32%.
exit=1

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 covered/statements over the statements present in both reports, so removing a line neither credits nor penalises — full strength retained on everything that remains. Recorded on the fleet board under DECISION-1.

The Branch Policy Check failure is the S8/ branch-naming artefact and is out of scope here.

…nchpad

# Conflicts:
#	lib/Service/DashboardVersionService.php
#	tests/Unit/Service/DashboardVersionServiceTest.php
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/launchpad @ bc342f6

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.

@rubenvdlinde
rubenvdlinde merged commit 2bf1a6b into development Aug 16, 2026
37 of 38 checks passed
@rubenvdlinde
rubenvdlinde deleted the S8/gate-57-launchpad branch August 16, 2026 19:56
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