Repository source: docs/plans/retraction_speed/retraction_speed_step4_implementation_summary.md
Debug mode: enabled (debug log)
Effort study: railspike-effort / v3/retraction_speed; host ledger. Standing P2 enrollment.
Extended estimates: enabled
Status: complete; merged as #119 and confirmed live by the maintainer.
Cycle estimate: 0.25 (Step 3, reaffirmed at entry)
Step 4 Entry
Branch
feat/retraction-speedfromorigin/mainat0822944(mainhas moved past the Step 1 starting revisionf046bd8; nothing in between touches retractions). Planning commit9c94d3cholds approved Steps 1–3 and the debug log in this folder, links repaired, plus the plan index entry.The Step 1–3 drafts were earlier pushed to
plans/retraction_speedat the session stop hook's request; this branch supersedes it.Whole-cycle 0.25 reaffirmed, 2026-11-09T23:40:20Z. Stage originals copied from Step 3: 0.04/0.14/0.07, non-additive. Claude, single exposed rater; copying is not a new rating.
Stage 1 - Removal context check is shared
Original estimate: 0.04 Retrospective: 0.03
Changes: new
ForumRewrite\Write\RemovalUpdateContext::forPost(), moved unchanged from the privateSourceLabelRemoval::incrementalContext(), which now calls it.Verification:
./v3 test --smoke DeleteRecordCommandTest HistoryPurgePlannerTest LocalAppSmokeTest ThreadTitleWithdrawalAndConsumersTest WriteApiSmokeTest: 666 run, 666 passed. Full suite not run.Notes: no behaviour change.
Estimate details: original scope as in Step 3; no revision.
— Claude · 2026-11-09 UTC · Model: claude-opus-5-5
Stage 2 - Retractions update one post and its pages
Original estimate: 0.14 Retrospective: 0.12
Changes:
LocalWriteService::finalizePreparedRetraction()takesRemovalUpdateContext::forPost()before the commit. After it,applyRetractionToOnePost()runsIncrementalReadModelUpdater::applyPostRemoval()with the accepted removal (AcceptedRemoval::read()),AcceptedRetraction::path()and the removal's label paths (none for a plain retraction), then clears the stale marker. Pages are cleared withinvalidateRemovedPost(); if that fails, with the full clearing; if both fail, the result ispartialwithfailed_step: artifact_invalidate, as before.With no context, or when the targeted update throws, the retraction runs today's full rebuild and full clearing. The repeat-request path (
retractionResult()for an already retracted post) is unchanged.applyPostRemoval()needed no change: an accepted retraction already maps to the same placeholder as an operator deletion.The snapshot comparison from
DeleteRecordCommandTestmoved totests/Support/ReadModelSnapshot.php; both test files use it.New tests in
WriteApiSmokeTest:testTargetedRetractionMatchesAFullRebuild(thread with no replies, thread with a reply, reply in a thread, thread with labels; each confirms the targeted path ran and equals a fresh full rebuild),testTargetedRetractionClearsOnlyThePagesThatShowedThePost,testPageClearingFailureAfterATargetedUpdateIsReportedAsPartial,testRetractionFallsBackToAFullRebuildWhenTheTargetedUpdateFails(a hidden post).Three existing tests exercised the full-rebuild path (refresh failure, page-clearing failure with retry, and cycle 1's swap test). Retractions no longer take that path by default, so each now marks the read model stale first, which routes them to it.
Verification:
Every test class that mentions retraction (47 classes, smoke set included): 858 run, 858 passed. Full suite not run.
Mutation check: with the operator-removal source path in place of the retraction's, the equality test failed on the activity row, so it covers the retraction-specific fields.
Notes: page clearing is the same set
delete-recorduses; no page was found missing in these tests.Estimate details: original scope as in Step 3; no revision.
— Claude · 2026-11-09 UTC · Model: claude-opus-5-5
Stage 3 - Timing, docs and the live check
Original estimate: 0.07 Retrospective: 0.07
Changes: concept entries post retraction and incremental source removal updated; the checklist marks this cycle's items, records the measurement and moves profile names to cycle 3.
Verification:
Timing on the 30,000-quote local copy (61 MB read model): the targeted removal took 0.24s, a full rebuild with swap 6.99s. Measured through
delete-record, which runs the same targeted update and page clearing; a signed retraction adds signature checking and its commit. Target under 1 second: passed for the shared path.Smoke set: 441 run, 441 passed. Full suite not run.
Live check: after #119 merged and deployed, the maintainer retracted a post on the live site and reported that it "worked very quickly". No response time or page-state record was taken; the planned agent check was not run, since the maintainer's own retraction covered the user path.
Estimate details: original scope as in Step 3; no revision.
— Claude · 2026-11-09 UTC · Model: claude-opus-5-5
Closeout
Cycle estimate at entry: 0.25 Retrospective scope assessment: 0.22
Technical status: all stages complete; merged as #119 (
8a189ab) and confirmed live by the maintainer.Commits: planning
9c94d3c; stages22fee09(1),91faa3f(2),1b7f0af(3).Checks: smoke set 441/441; all 47 retraction-related test classes 858/858; equality with a full rebuild for four retraction cases, with a mutation check. Full suite not run.
Retrospective slightly under the entry estimate:
applyPostRemoval()needed no change. The three test reroutes and the shared snapshot helper were unplanned but small. Stage retrospectives 0.03/0.12/0.07, non-additive.Maintainer comfortable-fit judgment and study acceptance: pending.
— Claude · 2026-11-09 UTC · Model: claude-opus-5-5