docs(block-editor): spec for the embedded-contentlet selection guard (#36985) - #37285
Conversation
…36985) Issue-resolution spec for the reopened #36985. Clicking an embedded contentlet card does create a NodeSelection; a byte-wise change-guard then rebuilds the document and destroys it, leaving the card unselectable and the toolbar's contentlet actions disabled. The guard compares JSON.stringify of the incoming value against JSON.stringify of editor.getJSON(). TipTap normalizes on parse, so any stored document that does not round-trip byte-identically through the current schema fails it permanently -- which is why the defect looks random across a site. It is a per-row data condition, not per content type or per site. Records the measured evidence: - three confirmed triggers: the legacy chartCount typo (written until 3cd5549, #26025), missing indent on heading/paragraph/blockquote, and the string branch skipping preserveUnknownNodesInDocument for unknown node types; - the host-to-branch map explaining why only the legacy JSP contentlet editor is affected -- it is the only host where the value effect stays live after load; - AC-001..AC-008, including the deliberate node/mark split on AC-004. Also carries data-model.md (value shapes and the normalization fromJSON applies) and contracts/content-match.contract.md (the comparator's input-to-verdict table, which is the source for the Red-phase tests). Spec only -- no implementation. Per the Spec-Kit flow this needs another dev's approval before /speckit-plan output is acted on. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @rjvelazco's task in 1m 34s —— View job Spec review — #36985 selection-guardThis is a documentation-only PR (three Spec-Kit artifacts, no implementation). I reviewed it for technical accuracy against the current source and internal consistency, since a wrong claim here propagates into the implementation PR.
Verification resultsEvery load-bearing code claim I could check against the repo holds:
Notably, the existing docstring on Notes (non-blocking)
VerdictNo blocking issues. The spec is accurate against the code, internally consistent, and honest about its unverified assumptions — appropriate for a PR-1 spec gated on dev approval. The one factual nit (
· |
Proposed Changes
Spec-Kit PR 1 of 2 for the reopened #36985 — specification only, no implementation.
Clicking an embedded contentlet card in the new Block Editor does create a
NodeSelection. A byte-wise change-guard downstream then rebuilds the whole document and destroys it, so the card shows no selection ring and the toolbar's edit / delete / reorder actions stay disabled.Why it looked random
The guard compares
JSON.stringifyof the incoming value againstJSON.stringifyofeditor.getJSON(). TipTap normalizes on parse — fills schema defaults, drops undeclared attrs, reorders keys — so any stored document that does not round-trip byte-identically through the current schema fails the check permanently. It is a per-row data condition, not per content type or per site, which is exactly what authors were reporting.Three confirmed triggers, all measured
chartCounttypoSetDocAttrStep('chartCount', …)until3cd5549d35(#26025, 2023-09-11).stripDocStatsstripscharCount, notchartCount.indentIndentExtensiondeclaresindent(default0) on heading / paragraph / blockquote. Older content has no such key; the editor adds it on parse.preserveUnknownNodesInDocument, so a custom block is compared placeholder-against-original.Supplying a missing key is not enough — it has to be in the right position. That is the measured Case B in the spec, and it is what makes this a byte-comparison bug rather than a missing-attribute bug.
Why only one screen
The new Angular Edit Content screen binds with
[formControlName], so the value arrives once throughwriteValueand the effect short-circuits forever after. The legacy JSP contentlet editor assignsblockEditor.value, so its effect is the loader and stays live — the only host where a re-push can reachsetContentafter load. The spec carries the full host-to-branch map.What's in this PR
specs/36985-block-editor-selection-guard/spec.mdspecs/36985-block-editor-selection-guard/data-model.mdNode.fromJSONappliesspecs/36985-block-editor-selection-guard/contracts/content-match.contract.mdplan.md,research.md,quickstart.mdandchecklists/are Spec-Kit working artifacts and are gitignored by design (.gitignore:229-233).Reproduction
No customer data required. The spec gives two recipes: a schema round-trip that needs no server, and an API-seeded BROKEN/CONTROL pair on a local instance. The legacy JSP contentlet editor is required — the content type needs
CONTENT_EDITOR2_ENABLED=false, and the new Angular screen will not reproduce it.A convenience seeding script is attached to the QA note on the issue rather than committed; the spec's steps are the source of truth.
Still open, recorded in the spec rather than hidden
dot-contentlet-editor.service.ts:204. If CONTROL also fails locally, "Why only one screen is affected" needs rewriting before anyone implements against it.commitEditorafter remotecustomBlocksextensions is excluded for the local fixture. This does not block the fix — once the comparison is structural the verdict becomes "unchanged", so a re-push is harmless rather than merely suppressed.Checklist
specs/36985-block-editor-selection-guard/checklists/requirements.md[NEEDS CLARIFICATION]markersgetSchema(createEditorExtensions(...)), not inferredNotes for reviewers
/speckit-planand/speckit-taskskeep resolving.chartCount→charCountdata migration, no port of click-to-select to the legacy editor (contentlet-block.node.ts:23-24has none, so theFEATURE_FLAG_NEW_BLOCK_EDITOR=falseworkaround still reproduces the original symptom — separate ticket), and no apply-once memo in the effect while the re-push is unidentified.falsetoday and must betrue— expect it to fail at Red.Spec for #36985
🤖 Generated with Claude Code
This PR fixes: #36985