feat: deferred 1a items - menu_rev lock, lit_mcp_guides filter, warnings, schedule guard precision
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
This commit is contained in:
@@ -99,7 +99,15 @@ PAST=$(ability lit/post-schedule "{\"post_id\":$PAGE_ID,\"date\":\"2020-01-01 10
|
||||
[ "$(error_code "$PAST")" = "validation_failed" ] || fail "past schedule accepted: $PAST"
|
||||
tool lit-post-publish "{\"post_id\":$PAGE_ID}" >/dev/null
|
||||
[ "$(wp post get "$PAGE_ID" --field=post_status)" = "publish" ] || fail "scheduled page did not publish now"
|
||||
pass "publish/unpublish/schedule, past dates rejected"
|
||||
PRIVATE=$(tool lit-content-create '{"post_type":"page","title":"LIT Smoke Privat"}')
|
||||
PRIVATE_ID=$(echo "$PRIVATE" | jq -r '.changed[0].id // empty')
|
||||
[ -n "$PRIVATE_ID" ] || fail "content-create (private): $PRIVATE"
|
||||
CREATED_POSTS+=("$PRIVATE_ID")
|
||||
wp post update "$PRIVATE_ID" --post_status=private >/dev/null
|
||||
SCHEDULED_PRIVATE=$(ability lit/post-schedule "{\"post_id\":$PRIVATE_ID,\"date\":\"$FUTURE\"}")
|
||||
echo "$SCHEDULED_PRIVATE" | jq -e '.ok == true and (.warnings | any(contains("ÖFFENTLICH")))' >/dev/null \
|
||||
|| fail "scheduling a private page does not warn: $SCHEDULED_PRIVATE"
|
||||
pass "publish/unpublish/schedule, past dates rejected, private schedule warns"
|
||||
|
||||
# 6. Copy and replace (lit-content-update, lit/post-replace-with-copy: rev required)
|
||||
COPY=$(ability lit/post-duplicate "{\"post_id\":$PAGE_ID}")
|
||||
@@ -153,29 +161,44 @@ REFUSED=$(tool lit-content-update "{\"post_id\":$MISSED_ID,\"rev\":\"$MISSED_REV
|
||||
[ "$(wp post get "$MISSED_ID" --field=post_status)" = "future" ] || fail "missed schedule got published"
|
||||
pass "content-update refuses a missed schedule instead of publishing it"
|
||||
|
||||
# 9. Menus
|
||||
# 9. Menus (lit/menu-set-items: menu_rev from lit/menu-get required)
|
||||
MENU=$(ability lit/menu-create '{"name":"LIT Smoke Menü"}')
|
||||
MENU_ID=$(echo "$MENU" | jq -r '.menu_id // empty')
|
||||
[ -n "$MENU_ID" ] || fail "menu-create: $MENU"
|
||||
ability lit/menu-set-items "{\"menu_id\":$MENU_ID,\"items\":[{\"type\":\"post\",\"object_id\":$PAGE_ID,\"children\":[{\"type\":\"custom\",\"url\":\"/kontakt\",\"title\":\"Kontakt\"}]}]}" \
|
||||
| jq -e '.ok == true' >/dev/null || fail "menu-set-items (tree)"
|
||||
MENU_REV=$(ability lit/menu-get "{\"menu_id\":$MENU_ID}" | jq -r '.menu_rev // empty')
|
||||
[ -n "$MENU_REV" ] || fail "menu-get lacks menu_rev"
|
||||
NOREV=$(ability lit/menu-set-items "{\"menu_id\":$MENU_ID,\"menu_rev\":\"000000000000\",\"items\":[]}")
|
||||
[ "$(error_code "$NOREV")" = "rev_mismatch" ] || fail "stale menu_rev accepted: $NOREV"
|
||||
SET=$(ability lit/menu-set-items "{\"menu_id\":$MENU_ID,\"menu_rev\":\"$MENU_REV\",\"items\":[{\"type\":\"post\",\"object_id\":$PAGE_ID,\"children\":[{\"type\":\"custom\",\"url\":\"/kontakt\",\"title\":\"Kontakt\"}]}]}")
|
||||
echo "$SET" | jq -e '.ok == true and (.menu_rev | type) == "string"' >/dev/null || fail "menu-set-items (tree): $SET"
|
||||
STALE_MENU=$(ability lit/menu-set-items "{\"menu_id\":$MENU_ID,\"menu_rev\":\"$MENU_REV\",\"items\":[]}")
|
||||
[ "$(error_code "$STALE_MENU")" = "rev_mismatch" ] || fail "outdated menu_rev accepted: $STALE_MENU"
|
||||
TREE=$(ability lit/menu-get "{\"menu_id\":$MENU_ID}")
|
||||
[ "$(echo "$TREE" | jq -r '.menu_rev')" = "$(echo "$SET" | jq -r '.menu_rev')" ] || fail "menu_rev of set-items differs from menu-get"
|
||||
CHILD_ID=$(echo "$TREE" | jq -r '.items[0].children[0].id // empty')
|
||||
[ "$(echo "$TREE" | jq -r '.items[0].children[0].title')" = "Kontakt" ] || fail "menu tree: $TREE"
|
||||
UNCHANGED_ARGS=$(jq -cn --argjson id "$MENU_ID" --argjson items "$(echo "$TREE" | jq -c '.items')" '{menu_id: $id, items: $items}')
|
||||
UNCHANGED_ARGS=$(echo "$TREE" | jq -c --argjson id "$MENU_ID" '{menu_id: $id, menu_rev: .menu_rev, items: .items}')
|
||||
UNCHANGED=$(ability lit/menu-set-items "$UNCHANGED_ARGS")
|
||||
echo "$UNCHANGED" | jq -e '.summary | contains("keine Änderung")' >/dev/null || fail "menu no-op: $UNCHANGED"
|
||||
REMOVE=$(ability lit/menu-set-items "{\"menu_id\":$MENU_ID,\"operations\":[{\"op\":\"remove\",\"id\":$CHILD_ID}]}")
|
||||
MENU_REV=$(echo "$UNCHANGED" | jq -r '.menu_rev')
|
||||
REMOVE=$(ability lit/menu-set-items "{\"menu_id\":$MENU_ID,\"menu_rev\":\"$MENU_REV\",\"operations\":[{\"op\":\"remove\",\"id\":$CHILD_ID}]}")
|
||||
[ "$(error_code "$REMOVE")" = "confirm_required" ] || fail "menu removal without confirm: $REMOVE"
|
||||
ability lit/menu-set-items "{\"menu_id\":$MENU_ID,\"operations\":[{\"op\":\"remove\",\"id\":$CHILD_ID}],\"confirm\":true}" \
|
||||
ability lit/menu-set-items "{\"menu_id\":$MENU_ID,\"menu_rev\":\"$MENU_REV\",\"operations\":[{\"op\":\"remove\",\"id\":$CHILD_ID}],\"confirm\":true}" \
|
||||
| jq -e '(.previous_items[0].children | length) == 1 and (.items[0].children | length) == 0' >/dev/null \
|
||||
|| fail "menu removal with confirm"
|
||||
LOCATION=$(ability lit/menu-list '{}' | jq -r '.locations[0].location // empty')
|
||||
if [ -n "$LOCATION" ]; then
|
||||
ability lit/menu-assign-location "{\"location\":\"$LOCATION\",\"menu_id\":$MENU_ID}" | jq -e '.ok == true' >/dev/null \
|
||||
|| fail "menu-assign-location"
|
||||
UNASSIGN=$(ability lit/menu-assign-location "{\"location\":\"$LOCATION\",\"menu_id\":0}")
|
||||
[ "$(error_code "$UNASSIGN")" = "confirm_required" ] || fail "unassign without confirm: $UNASSIGN"
|
||||
ability lit/menu-assign-location "{\"location\":\"$LOCATION\",\"menu_id\":0,\"confirm\":true}" \
|
||||
| jq -e '.summary | contains("LIT Smoke Menü")' >/dev/null || fail "unassign with confirm does not name the menu"
|
||||
fi
|
||||
pass "menu create, tree, no-op resend, operations with confirm, location"
|
||||
TRASH_LINKED=$(ability lit/post-trash "{\"post_id\":$PAGE_ID,\"confirm\":true}")
|
||||
echo "$TRASH_LINKED" | jq -e '.changed[0].action == "trashed" and (.warnings | any(contains("LIT Smoke Menü")))' >/dev/null \
|
||||
|| fail "trashing a menu-linked page does not name the menu: $TRASH_LINKED"
|
||||
pass "menu create, menu_rev lock, tree, no-op resend, operations with confirm, location (unassign with confirm), trash warns about menu link"
|
||||
|
||||
# 10. WordPress settings (whitelist)
|
||||
ability lit/wp-settings-get '{"group":"general"}' | jq -e '.settings[] | select(.key == "blogname")' >/dev/null \
|
||||
|
||||
Reference in New Issue
Block a user