Files
WP-LIT-MCP/docs/superpowers/notes/2026-09-26-etappe-1a-rulings.md

36 lines
6.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Etappe 1a – Entscheidungen (Rulings) und offene Kleinigkeiten
Aus dem SDD-Ledger übernommen, 2026-09-26.
- Ruling P1 (B1): T7 PostGuard::postType() return type `: object` (WP_Post_Type) instead of `object|WP_Error` (compile error); errors via exceptions/other method as plan logic allows — cost if wrong: none.
- Ruling P2 (B2): T1 adds `<exclude name="WordPress.Security.EscapeOutput.ExceptionNotEscaped"/>` to phpcs.xml.dist (WordPress.Security rule) — our exceptions are internal and become JSON WP_Error messages, never HTML output — cost if wrong: an exception message echoed into HTML unescaped (we never do this).
- Ruling P3 (B3): T7 FieldValidator::checkScalar and T15 SettingsWhitelist::validate — parenthesize nested ternaries / extract helper so PHPCompatibility passes — cost: none.
- Ruling P4 (R1): T14 MenuWriter must accept the exact tree shape lit/menu-get returns: for object-linked items (page/post/category/term) a `url` field is ignored (not rejected); add a unit test for the round trip menu-get → menu-set-items — cost: none.
- Ruling P5 (R2): spec principle 7 (never clobber parallel edits) binds: lit/content-update REQUIRES `rev` (from lit/content-get) for all content; mismatch → lit_rev_mismatch with hint to re-read. T6 instructions/T9 accordingly — cost if wrong: one extra read per update.
- Ruling P6 (R3): all-or-nothing (spec §8.2): T9 validates everything before writing; if writeExtras() fails after insert (create) → delete the just-created draft (force, it's our own new draft); after update → restore the pre-update revision via RevisionPoint; return the error — cost: none.
- Ruling P7 (R4): T11 check wp_trash_post($copyId) result; if it fails, return ok with a warning "Kopie <id> konnte nicht in den Papierkorb gelegt werden" — cost: none.
- Ruling P8 (C4): T16 moves shared smoke helpers (fail/pass/mcp/cleanup/token) into tests/Integration/lib.sh sourced by both mcp-smoke.sh and wp-basis-smoke.sh — cost: none.
- Task 1: minor (deferred): Errors::make silent fallback on json encode failure w/o log; $details may overwrite reserved keys
- Task 2: minor (deferred): WP_Application_Passwords client-name branch untested; clientName not length-capped (sanitize when logging!); duplicated try/catch block in AbilityRegistrar
- Task 3: minor (deferred): insert format array not asserted in test; object_id/ability not truncated
- Task 4: minor (deferred): sampling ORDER BY ID vs index; array_merge numeric meta keys
- Task 5: minor (deferred): podFor call outside try/catch in write(); get_fields() null tolerance
- Task 6: Ruling P9: lit/content-types must only list/describe post types the user can edit (post type cap->edit_posts) — carried into Task 8 dispatch (small change in ContentTypesAbility) — cost if wrong: none (schema only)
- Task 6: minor (deferred): instructions "Inhaltstypen" line uncapped/untested length
- Task 7: minor (deferred): multi-value scalar fields validated as scalar; TermAssigner::assign trusts resolve(); PostGuard cap fallback to raw cap string
- Ruling P10: content rev must detect parallel edits of title/excerpt/content/status too, not only post_content: add Rev::ofPost(WP_Post) = Rev::of(post_content|post_title|post_excerpt|post_status|post_modified_gmt) used by PostPresenter (content-get) and content-update check/return (T9); Rev::of(string) stays for Divi (Etappe 2) — cost if wrong: occasional extra rev_mismatch → re-read
- Task 9: Ruling P11: rev does not cover field/term-only changes by others — accepted limitation (fields rarely edited in parallel; WP does not bump post_modified on meta change) — cost if wrong: a parallel field edit could be overwritten (revision/log still allow undo)
- Task 9: minor (deferred): assign() midway cleanup failure not reported; WP_Error after rowTouched relies on template pre-validation; Pods snapshot no "absent"; rollback via kses may alter admin markup
- Task 10: minor (deferred): permission()/execute() double load (repo-wide pattern)
- Task 11: Ruling P12: replace-with-copy loses original meta/terms (revision covers only title/content/excerpt) → before the swap, clone the original into a trashed backup post (PostCloner), return backup_id; if the backup can't be created → abort with lit_write_failed; with EMPTY_TRASH_DAYS=0 we already refuse — cost if wrong: one extra trashed post per swap
- Task 11: FINAL-FIX (important-ish, carry to final wave): PostCloner::ownMeta must check $wpdb->last_error and throw — a failed SELECT currently looks like "no meta" and replace would wipe original meta; also add create_posts guard before backup; include backup_id in WriteFailed details
- Task 11: minor (deferred): raw inserts skip meta hooks (intended); meta key with backslash edge case
- Task 14: Ruling P13: custom menu links may be /path, #anchor, http(s), mailto, tel (brief) — my 'http(s) only' was shorthand; javascript:/data: etc. must be rejected — cost if wrong: none
- Task 14: Ruling P14: private targets always refused; draft/pending/future only with allow_unpublished + correct warning; non-public taxonomy terms refused; status gate only for newly linked items (existing ones round-trip) — cost if wrong: Claude must set a flag for draft-based site setup
- Task 14: minor (deferred): description should state status gate applies only to new links; raw status string in message; no depth limit
- Task 16: minor (deferred): content-types guide "Jede Änderung liefert revision_id" also near duplicate (null there)
- Final review 1a: ready with fixes. Ruling P15: fix wave = I1 (PostCloner last_error, create_posts guard, backup_id in WriteFailed), I2 (settings isStored via sanitize_option), I3 (guard against future→publish on writes with missed schedule), I4 (edit_post_meta per key), I5 (guides/hints: codes without lit_ prefix, indirect tools via execute-ability as lit/<name>, revision_id claim), M1 (settings FollowUp/WriteFailed/partial log; menu partial log), M2 (checkRelation read_post), M3 (PostGuard refuses trashed by default), M4 (status tools WriteFailed on throwing hook), M6 (permalink regex), M7 (ChangeLog truncate object_id/ability), Task14 description, consolidation: PostChangeResult helper, Rev::matches, ChangeLog::recordPartial.
- Ruling P16: rev naming — `rev` always means the post-level Rev::ofPost for post tools; Etappe 2 Divi tree lock will use a distinct field name (e.g. `tree_rev`) — spec §5.3 to be amended in Etappe 2 plan — cost if wrong: rename later
- Ruling P17: deferred to 1b: menu rev (M5), lit_mcp_guides filter, M8 warnings (front page trash, menu unassign confirm, private schedule note, divi hint)
- Final: parked (Ruling P18): missedSchedule 60s window & zero GMT date fallback; write_failed wording mentions backup — deferred to 1b minor fixes — cost if wrong: seconds-early publish on edits within last minute before schedule