- tests/Support/FakeChangeLog extends ChangeLog: records record()/recordPartial()
calls, increasing ids, optional failure; new FakeChangeLog(id) / partialsOnly() /
any(id) / forbidden() mirror what the replaced mocks allowed. assertRecorded() /
assertPartials() check count and the named arguments. Calls a fake does not allow
throw and are also verified in TestCase::tearDown (one assertion per test that
uses a fake), so swallowed change-log calls still fail like a never() expectation.
- All ChangeLog mocks of ability/integration tests replaced (the admin tests that
stub query/purge/clear keep their mocks); expectException tests that checked the
partial log now catch and assert.
- SchemaFixture::managed(?divi) replaces the 21 isManaged-only ContentSchema mocks;
DiviFixtures::schema() uses it.
Tests 1397 -> 1404 (FakeChangeLogTest); no test has fewer assertions.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- WriteFailed::rolledBack()/partial() replace the 11-parameter constructor (now
private); the hint/state of either error are plain $hint/$state.
- Rollback::attempt(...steps) runs every undo step and collects failure messages;
used by ContentCreate::discard, ContentUpdate::rollBack, MediaUpdate::rollBack and
DiviSaver::restore.
- Errors::clientMessage/codeOf/fieldsOf replace DiviReplaceTextAbility::reason,
the JSON digging in MediaUpdateAbility::failure and ContentCreateAbility::fields.
- Errors docblock documents the exception/WP_Error split.
- ContentCreateAbility::discard takes the client reason as a parameter (both callers
still pass REASON_EXTRAS; changing the Divi text needs a ruling).
Error payloads unchanged.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Task 14 (rulings P17/P18, Q10):
- lit/menu-get returns menu_rev (Rev::ofMenu over id, parent, position,
title, url, object, object_id); lit/menu-set-items requires it, refuses
a stale one with rev_mismatch (hint: lit/menu-get) and returns the new
menu_rev. Errors::revMismatch takes an optional message/hint.
- GuideRepository: filter lit_mcp_guides adds/overrides topics; invalid
topic names and non-string content are ignored, files stay the default.
- lit/post-trash names front page, posts page and linking menus in the
confirmation and as warnings; lit/menu-assign-location needs confirm
to remove an assigned menu and names it; lit/post-schedule warns that
a private entry becomes public; Divi hints no longer point to
non-existing lit-divi-* tools.
- PostGuard::missedSchedule: 60 s publish window, zero GMT date falls
back to get_gmt_from_date(post_date); post-schedule refuses dates
within that window.
- write_failed after a clean rollback that keeps a revision/backup says
"Die Aenderung wurde nicht gespeichert (... bleibt erhalten)" via the
new state parameter (Errors::writeFailed / WriteFailed rolledBackState).
- wp-basis-smoke: menu_rev flow, unassign confirm, trash/menu and
private-schedule warnings.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
publish/unpublish/schedule/trash run their WordPress call through StatusWrite:
a throwing hook becomes WriteFailed (rollback incomplete, with post_id) and is
logged with result partial instead of surfacing as internal_error.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
after_save hook, re-read, change log and ToolResult (rev, log_id, warnings)
were repeated in nine post abilities; they now share one helper. Behaviour
and warning texts are unchanged.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
- PostGuard::editable() refuses trashed entries unless the caller opts in
(content-get, revisions-list, post-trash); publish/schedule/unpublish/update/
restore/duplicate/replace now refuse them.
- PostGuard::statusStable(): content-update, revision-restore and
replace-with-copy refuse a future post whose date has passed (any save would
publish it); the content-update rollback and PostCloner row writes re-check.
- PostCloner::ownMeta() treats a failed SELECT as an error; replace() reads the
copy before its first write.
- replace-with-copy needs create_posts (backup clone) and reports backup_id in
write_failed/partial_write.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
recordPartial() replaces the partial-log FollowUp blocks in content-create,
content-update and post-replace-with-copy; record() truncates ability,
object_type and object_id to their column sizes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7