feat: lit/library-make-global – node to global module, all or nothing (E10)

- NewLibraryEntry: spec and creation of a new library entry, shared by
  lit/library-save and lit/library-make-global (NewDraft, P7, StoredNodeGuard)
- UsageMatcher::referencesAny() (ruling Q7): a global entry never names any
  entry in any spelling Divi renders (P23/P24 forms, Divi 4 shortcodes inside
  Divi 5 values, values casting to 0); used by library-save scope global and
  make-global
- make-global: entry created, node replaced by GlobalInstance::of, page through
  DiviWriter::commit; refused or rolled-back page deletes the entry, a partial
  page write keeps it (the page may show it); sources are pages and posts only

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:
2026-09-27 21:35:48 +02:00
co-authored by Claude Opus 5.5
parent 4f70886006
commit 44c1804c4c
9 changed files with 830 additions and 101 deletions
@@ -237,4 +237,26 @@ final class LibrarySaveAbilityTest extends TestCase
self::assertToolError($this->ability()->execute($this->input(['scope' => 'site'])), 'validation_failed');
self::assertSame([], $this->specs);
}
/**
* Ruling Q7: a global entry never renders another entry, in any spelling Divi renders – here a Divi 4
* shortcode inside a text value; a normal entry may hold it (it is copied, not shown as a module).
*/
public function testGlobalScopeRefusesReferencesInAnySpelling(): void
{
$quote = chr(92) . '"';
$this->page = LibraryFixtures::page(
'<!-- wp:divi/placeholder --><!-- wp:divi/section --><!-- wp:divi/row --><!-- wp:divi/column -->'
. '<!-- wp:divi/text {"content":{"innerContent":{"desktop":{"value":"[et_pb_text global_module='
. $quote . '1058' . $quote . ']x[/et_pb_text]"}}}} /-->'
. '<!-- /wp:divi/column --><!-- /wp:divi/row --><!-- /wp:divi/section --><!-- /wp:divi/placeholder -->'
);
LibraryFixtures::given([$this->page]);
$global = $this->ability()->execute($this->input(['id' => 's0', 'scope' => 'global']));
self::assertToolError($global, 'validation_failed');
self::assertSame([], $this->specs);
self::assertTrue($this->ability()->execute($this->input(['id' => 's0']))['ok']);
}
}