fix(tiles): delete the write path under a permanently-Gone endpoint - #79
Merged
Conversation
Closes one of gate-57's two orphaned write capabilities.
TileApiController's create, update and destroy return HTTP 410 Gone
unconditionally — tile creation moved onto widget placements
(REQ-WDG-022 / REQ-TILE-PLACEMENT). The three TileService write methods
behind them had no callers at all: not the controller, not the CLI
commands, not the migrations, not the frontend. Their docblocks said they
were "preserved for legacy callers and migration tooling"; there are
neither, and the deprecation on the controller side is already complete.
Traced before choosing, because "zero callers" has two opposite fixes and
this repo has one of each. The other orphan gate-57 reports —
DashboardService::writeDashboardContent — is the WIRE case and is
deliberately left alone (see below). This one is the DELETE case, and the
discriminator is the sibling seam: there is no live path that ought to be
calling these and isn't. The 410 IS the intended behaviour, not a bug the
orphans would fix.
Deleting rather than leaving them is the point. A write path with no
caller, sitting under a permanently-410 endpoint, is a way to put rows
back into a table the app has deliberately stopped writing to — the
deprecation would be bypassed by whoever wired it up next, with nothing to
warn them. That is what gate-57 exists to catch.
getUserTiles stays: `GET /api/tiles` still reads the table, so existing
rows remain visible. Read-only was the intended end state and is now the
only state the class can express.
The three tests asserted `expects($this->never())->method('createTile')`
and friends. A mock expectation cannot name a method that does not exist,
and it would be the weaker check anyway — they now assert the methods are
absent, so re-adding the write path fails a test instead of passing
silently. Each test's real assertions (410 status + envelope + replacement
hint) are untouched.
NOT fixed here, deliberately: DashboardService::writeDashboardContent.
That one is not dead code — it is the write half of the
groupfolder-storage-backend capability (35 spec scenarios, two storage
backends, a factory and two registered CLI commands) whose read, write AND
delete accessors ALL have zero callers, while
`launchpad:storage:migrate-to-groupfolder --prune-source` already writes
to that backend and NULLs the DB column. Deleting the write half would be
wrong and wiring it means deciding where dashboard content lives — a
design decision with a spec behind it, not a gate fix. Filed separately.
Verified: gate-57 goes 2 -> 1 against this tree and still reports 2
against origin/development. php -l clean on both files.
rubenvdlinde
requested review from
WilcoLouwerse,
bbrands02 and
rjzondervan
as code owners
August 9, 2026 15:01
…leService its @SPEC Removing the three write methods left a blank line before the class closer — phpcs's 'Expected 0 blank lines after function' error, and the only ERROR in the run (everything else in that job is a pre-existing WARNING). Also adds the canonical @SPEC tag the class docblock was missing, pointing at openspec/specs/tiles/spec.md rather than a change dir, since this commit rewrote that docblock anyway.
Contributor
Quality Report — ConductionNL/launchpad @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ❌ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| composer | ✅ | ✅ 102/102 | |||
| npm | ✅ | ✅ 548/548 | |||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-09 15:08 UTC
Download the full PDF report from the workflow artifacts.
Contributor
Quality Report — ConductionNL/launchpad @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| composer | ✅ | ✅ 102/102 | |||
| npm | ✅ | ✅ 548/548 | |||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-09 15:14 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.
Closes one of gate-57's two orphaned write capabilities: 2 → 1.
What was wrong
TileApiController'screate,updateanddestroyreturn HTTP 410 Gone unconditionally — tile creation moved onto widget placements (REQ-WDG-022 / REQ-TILE-PLACEMENT). The threeTileServicewrite methods behind them had no callers at all: not the controller, not the CLI commands, not the migrations, not the frontend. Their docblocks claimed they were "preserved for legacy callers and migration tooling" — there are neither.Deleting rather than leaving them is the point. A write path with no caller, under a permanently-410 endpoint, is a way to put rows back into a table the app has deliberately stopped writing to — the deprecation would be bypassed by whoever wired it up next, with nothing to warn them.
getUserTilesstays:GET /api/tilesstill reads the table, so existing rows remain visible. Read-only was the intended end state and is now the only state the class can express.Why DELETE and not WIRE
"Zero callers" has two opposite fixes, and this repo contains one of each. The discriminator is the sibling seam: is there a live path that ought to be calling this and isn't?
DashboardService::writeDashboardContent(gate-57's other finding): yes. → left alone, see below.The other orphan is deliberately NOT fixed here
writeDashboardContentis not dead code. It is the write half of the groupfolder-storage-backend capability — 35 spec scenarios, two storage backends, a factory, two registered CLI commands — whoseread,writeanddeleteaccessors all have zero callers, whilelaunchpad:storage:migrate-to-groupfolder --prune-sourcealready writes to that backend and NULLs the DB column.Deleting the write half would be wrong. Wiring it means deciding where dashboard content lives — a design decision with a spec behind it, not a gate fix. Filed separately; gate-57 stays red at 1 with that reason.
Tests
The three tests asserted
expects($this->never())->method('createTile')and friends. A mock expectation cannot name a method that does not exist — and it is the weaker check anyway. They now assert the methods are absent, so re-adding the write path fails a test instead of passing silently. Each test's real assertions (410 status, envelope, replacement hint) are untouched.Measured
gate-57: 2 → 1 against this tree; still 2 against
origin/development.php -lclean on both files.