STATUS
Last updated: 2026-09-21 (collection-controls workstream batch 2 — 12 branches
(S24-S32, S34, S35, S37) merged into claude/confident-cray-5evewb,
PR #720: the control-health model + badges, a new
page-/site-wide audit panel, Bondlayer inspector parity (Field picker, Value chip), a “List
chrome” pager section, carousel fade/nav-visibility parity, applied-filter chips, Item Snippet
record navigation, an A-tier server-backed query consumer for large lists, and a real fix for
publish timeouts on large targets (parallelized R2 writes, bounded concurrency — see the plan’s
§13 for the full integration log). See In-flight below. Same day, D:load-more-fragments: an
imported Load More now carries its page size, publish writes pages 2..N of a paginating repeater
as static same-origin HTML fragments, and the load-more runtime fetches the next one when its
rows run out — first paint still carries one page. D:canvas-slot-href-guard +
D:canvas-message-fanout: slot href/src pass the same scheme gate as link-config on the
canvas; every targeted canvas message reaches every split pane. Previously 2026-09-19:
D:canvas-incremental-render: the canvas iframe patches only the subtrees whose render
signature changed instead of wiping #root on every push — a one-node edit no longer rebuilds
thousands of elements, re-mounts every embed iframe, or kills an inline edit; the full rebuild
stays as the fallback; renderNode’s child lookup is O(n) instead of O(n²); a jsdom harness now
executes the real preview.html for parity tests. Same day, D:collection-bundle: canvas CMS
data is ONE
GET /api/collection-bundle request per page instead of two requests per repeater node, cached
per collection (not per repeater) and revalidated by an xmin + denied-set ETag after the TTL —
the repeater N+1 D291 named as not done. Same day, D:editor-snapshot: the editor boots from ONE validated
snapshot request instead of a target → pages → nodes waterfall (~7–10 sequential Neon round trips
before the canvas had a tree); the snapshot is ETag-validated by an xmin-based fingerprint,
parked in KV server-side and in IndexedDB client-side, and a background whole-site prefetch makes
tab/link page switches fetch-free (the sidebar click still fetches once, not twice); the DOM-tree panel’s duplicate boot/page-switch fetch is gone; the
page read is one statement; the access middleware memoises target → project. Sibling
D:public-asset-edge-cache: published-site images no longer cost a Neon query per <img> per
visitor. Previously 2026-09-14: (D:computed-style-layer-loss ledger entry + its three residual
gaps: the editor’s detach/unlink/push merges rebuilt style_overrides from a layer allowlist and
ERASED everything outside it on a column every caller writes whole — PR #694 fixed the headline
case, the follow-up closes top-level leaf props, metadata leaking in as pseudo-layers, and
one-level-deep merging of nested max<px> layers. One data repair is open
(plans/OWNER-OPS.md row 20). Also 2026-09-14: recurring-structures wave 3, D:recurring-setup-ui: the
operator runbook’s symbol/edit-gate/collapse-pass steps got a guided CMS UI — see In-flight
below. Previously, 2026-09-13: import-fidelity wave, PR #671, D256–D266: split-canvas
registry, canvas root parity, canvas comments + To-do list, preview top bar, multi-locale
publish, custom-CSS migration, screen-root floor, text-field types, entity single-escape,
filter defaults, carousel current/rewind. Also a doc-accuracy pass, no architectural
change — importer test count, WordPress-adapter reachability, test-coverage claims,
preview.html line count, schema table count, dead archive-path citation.
recurring structures + live time plan: D:recurring-structures, waves 0–3 built, PR #682;
Previously:
canvas copy menu + element SQL/ids: D:copy-menu-inspect;
published-site link base path: D:publish-base-path;
deletion completeness: D:delete-completeness;
marketing/CRM email catalog + AI drafting: D:marketing-catalog;
symbol sync: D:symbol-sync-lineage, then D:symbol-safety-ux; Y.Text island deleted: D:ytext-deletion)
· Rewritten from a code-verified audit (four parallel
code sweeps at d2c791a; docs were not trusted). The previous STATUS (whose body had
frozen in mid-July while the banner stayed fresh) was rewritten in place during the
2026-08-22 reset, not preserved — no pre-reset copy exists under archive/.
Update procedure (docs/DOCS-POLICY.md is normative): update the section that
changed and bump the date above. No banner stacking — this file describes NOW; the
history lives in DECISIONS.md (D227 as of 2026-08-21) and git.
Launch target: go-live 2026-09-21 (owner-set, 2026-08-22).
✅ What works (code-verified)
Section titled “✅ What works (code-verified)”- End-to-end MVP loop: sign up (Clerk) → create project/target → edit → publish → view live, including custom domains with TLS and scheduled re-verification (D227).
- Editor: canvas + DOM tree + property editor (30 modules), save-v2 sink with
IndexedDB crash journal replayed on boot; design-token system (all 9 token libs
tested); component libraries (shadcn/daisyUI/html) with golden-drop tests;
behavior graph + motion; UI Lab. The canvas bottom bar’s Copy control is a MENU
(
D:copy-menu-inspect, 2026-09-07): the HTML+CSS subtree export it always had, plus the element / page / target uuids, three generateddom_nodesqueries (row, subtree, ancestor chain) and a one-paste debug block pairing those ids with a runnableSELECT— the editor previously surfaced no node id at all. Figma-UX adoption pass (D:figma-ux-adoption, 2026-08-23,plans/FIGMA-UX-ADOPTION-WORKSTREAM.md): token MATRIX (states as columns in-panel ≥440px + dialog with states|breakpoints pivot over N4 overrides), name-derived token groups, scope-filtered token pickers, FieldShell binding pill with alias-chain tooltip + non-destructive detach, Selection Colors subtree recolor/tokenizer (single undo batch), token detail popover ($description + private flag in the value blob), inline-rename everywhere (pages/groups/symbols/shared styles/canvas tabs), reviewable push-to-style diff, and the theme hygiene batch (svg-size cva guard,--destructive-texta11y token,--highlight/--accentsplit, one floating-surface recipe, 2xs/3xs/4xs type tokens). - API: ~47 route modules / ~230 endpoints; ~85 node suites run against real Postgres (PGlite + live migrations). Nodes batch save, targets/projects, pages/folders, publish (~15 suites), serve, forms (+ Turnstile + rate limit), member/visitor auth (fail-closed), CMS workflow, assets.
- Publish: dual-write live + immutable versioned builds with one-click rollback
(20 kept), sitemap/robots/feeds/redirects/OG cards, per-script sha256 CSP,
8-provider embed registry + test-locked third-party ledger, sandboxed
custom_code. The security stack is above the launch bar.
Multi-page navigation on the LEGACY
/published/:targetIddoor was broken until 2026-08-30 (D:publish-base-path): sub-pages were published and servable, but internal links were emitted root-relative and resolved against the API origin, so only the homepage loaded. Links now carry the serving base path; the host door was never affected and its bytes are unchanged. - Preview/canvas review (2026-09-13, D256–D259): split canvas panes are a
first-class registry, not a raw ref map (D256); the canvas backs every page
root the way publish does (not just a
body-shaped one), with an edit-mode wheel fallback for fixed overlays that force-forward their own pointer-events (D257); preview-canvas review pins land in a designer To-do list via a newcanvas_commentstable, not a chat thread (D258 — drizzle 0039 + its RLS sidecar applied to prod 2026-09-14,/api/commentslive); the floating draggable preview pill is deleted for a real top bar,PreviewTopBar(D259). Figma mirror of the new chrome is still open (FU-5). - Multi-locale publish (2026-09-13, D260): one document per (page ×
language), a zero-JS
language_switcherelement, and locale-gated conditionals that finally gate per locale. Known divergence carried forward: publish formats dates/numbers in the rendered locale, the canvas still formats in the default language (canvas-switcher locale threading is a tracked follow-up, not done); OG/RSS/archive-index/print-box_languagegating is out of scope for this wave./en/*documents + hreflang only appear after a republish — see Owner ops queue. - Bondlayer importer (
apps/bondlayer-worker/): end-to-end with 2,776 assertions green (58 test files); 47-kind loss registry CI-gate-guarded in tests; re-import updates in place (D215/D216). 2026-09-13 fidelity wave (D261–D266): custom-CSS migration ships Tiers 1–2 as per-nodestyle_overrides, not the derived-shared-style architecture the design called for (D261 — a fan-out rule likebgblur×33 mints 33 copies instead of one shared row; tracked as a follow-up); the imported screen root gets a min-height floor instead of a height cap (D262); Bondlayer’s one-size text field now splits by input shape then content (D263); a screen’stitle/OG/keywords are decoded once at the capture boundary instead of double-escaping (D264); an absent Bondlayer filter anchor is treated as the dropdown’s own default, andeditiscreatere-submitted (D265); imported sliders get a real current slide, rewinding arrows, styleable nav and a cloned dot template (D266). None of this is visible on a live site yet — the importer container cutover is done (IMPORT_RUNTIME="container", live since 2026-09-02); what’s still needed is RPM/Simão/12x12 re-imported (on the current importer) + republished; see Owner ops queue. - CMS: full CRUD, workflow/review, i18n, Excel import, version history, submissions inbox. Import wizard: CSV/JSON/XLSX/Webflow/Bondlayer.
- Multi-tenancy: Clerk org = project; project→target hierarchy landed in the frontend (D191–D202); five-tier roles; webhook projection heal.
🔴 Launch punchlist (blocking or near-blocking for 2026-09-21)
Section titled “🔴 Launch punchlist (blocking or near-blocking for 2026-09-21)”Security (closed 2026-08-22 by D:launch-p0-hardening unless noted):
Yjs WS upgrade has no authorizationFIXED —lib/ws-auth.tsverifies the Clerk token + target membership before the DO sees the socket (tested).FIXED — now enforces the sameassets-cdn.tsunauthenticated read-any-keymetadata.publicauthorization as/api/asset-pub(tested).- RLS partially proven: the silent BYPASSRLS fallback is gone —
getTenantDBnow throws whenNEON_AUTHENTICATED_DATABASE_URLis unset, and the chooser matrix is tested. Still open: a live cross-tenant integration test through the real tenant role (needs Neon + a pg_session_jwt-valid token). ⚠️ Deploy prerequisite: confirm the secret is set on prodskyfall-apibefore this ships. Minor sweepFIXED — backup reads admin-gated,/test-roledeleted,elementClickedorigin-checked, prod sourcemaps off.
Config that is shipped-but-dark (one value each):
5. PUBLISH_ROOT_DOMAIN="matriz.page" — set, but until D:platform-root-split
isolation was still nothing: the var only 301’d the legacy path route to a
canonical host that was itself still on matriz.design (the platform’s own
zone), because free-subdomain allocation read PLATFORM_ROOT_DOMAIN instead.
Allocation now reads PUBLISH_ROOT_DOMAIN too, so NEW targets get an
isolated <slug>.matriz.page host. Still blocked on the owner’s PSL
submission (OD-2) before the isolation is real cookie-wise, not just
origin-wise — see plans/OPEN-DECISIONS-LEDGER.md OD-2.
6. CF_ACCOUNT_ID="" — analytics reads 503 while edge writes accrue.
Verification gaps:
7. No CI runs the Python importer suite FIXED — importer-ci.yml runs all
56 test files on Actions; pytest pinned in requirements.txt.
8. CanvasPanel.tsx (1,382 lines) has zero real tests — only grep-style
source-text presence checks — and no E2E, the highest-regression surface left.
QuickToolbar.tsx (6 render-test files, ~1,040 lines, D:toolbar-identity) and
PropertyEditorShell.tsx (.isolation.test.tsx, .tabs.test.tsx) both now have
real render tests.
9. Preview↔publish is a fork: frontend/public/preview.html (5,953-line ES5
reimplementation) held to parity by ~11 spot tests. Unguarded axes drift silently.
10. Python↔TS emitter contract fixture stale FIXED — regenerated from the
live gen_sample_payload.py; regen path corrected in both citations. (Also:
deferred-features.test.ts no longer green-skips on provisioning failure.)
Product holes worth deciding before launch:
11. Property-write split-brain: 2 sites bypass the crash journal, not 18 —
PropertyCommands.updateSlotValues:969 and UndoManager UPDATE_SLOTS:571,
both slot_values-only, forced onto PATCH /api/nodes/:id by D27 because the
batch route refuses that column. The 16 persistToDB sites (attributes,
content, style_overrides x7, style_id, component ids, interactions,
link_config) DO ride the sink and the journal — none carries slot_values,
so each takes the markDirty lane at node-write.ts:285. Residual risk on
the 2 is small: that PATCH is immediate and awaited, so only a crash inside
the request loses the delta (vs. the sink’s up-to-30s window, which is what
the journal exists for).
11b. Separately, and worse: toggleActive (PropertyCommands.ts:1565) persists
NOTHING. It sends { _isactive }, which is not a column in
packages/schema and not in DOM_NODE_UPDATABLE_COLUMNS, so sanitizePatch
strips it to {} and node-write.ts:272 returns before any request, before
markDirty, before the journal. Hide/show an element and it returns on
reload; the undo entry at :1569 records a write that never happened. Needs
a decision, not just a patch: _isactive has no storage anywhere, so either
add a column or move visibility onto an existing one.
12. states.default_value/imagePresets imported but read by nobody; xlsx CMS
merge failure swallowed; duplicate detail slugs last-write-win; member
field/owner visibility rules refused at publish (render as authored).
13. Error invisibility: 21 catch { return [] } in frontend/lib/api/, no error
reporting service anywhere.
14. Decide multiplayer DELETED 2026-08-24 (D:ytext-deletion) — the Yjs
stack (~1,028 non-test lines + tests + DO + R2 binding + 7 npm deps) was
fully dormant: nothing ever constructed a text-sync session, so the CRDT
guard in front of every text write always returned false and REST always
ran. It also would have solved the wrong collision for this product — text-only
conflict resolution while the tree/styles/tokens/pages stayed single-writer —
so it is gone rather than wired. docs/design/COLLAB-YTEXT-ACTIVATION.md
has the full analysis and the activation path, kept for the record. Removing
the seam also removes D27’s original justification for excluding slot_values
from the batch route (item 11) — reverting that is a live follow-up, not done
here (it is a change to the sole write path and wants its own review).
14b. The live-crawl adapter (workers/src/import/adapters/live-crawl.ts) is
workers-only and unreachable from the wizard; WordPress is reachable —
it’s registered in packages/import/src/index.ts and ImportWizard.tsx
auto-detects .xml uploads for it.
15. Symbol sync deletes content on a front-insert FIXED 2026-08-24
(D:symbol-sync-lineage, then D:symbol-safety-ux) — sync paired
master↔instance nodes by sibling index with no back-pointer, so adding a
child at the TOP of a symbol shifted every pairing and deleted whatever fell
off the end. Clones now carry component_instance.lineageId and pair on it.
The residual first-sync hole the lineage fix recorded — an un-stamped
instance still took the positional walk once, and the backfill then froze
that guess — is closed by D:symbol-safety-ux: the fallback still runs, but
the instance is REFUSED outright (nothing written) when the guess would
repaint a node into a different element type or delete a node the master
cannot be shown to have dropped, with accept/unlink/skip as the
explicit ways out. Nodes deleted BEFORE all this are still not recoverable
from Postgres — no soft-delete, no tombstone; Neon PITR or a pre-existing
backups row are the only restores, and the owner has ruled the existing
damage a write-off (one test fixture). db/utilities/symbol-instance-shape-audit.sql
remains available for anyone who wants to look.
🔄 In-flight
Section titled “🔄 In-flight”-
Motion Studio (
D:motion-studio, 2026-10-18): three role-scoped screens (Motion / Media and content / Print) over a TimelineV1 document, a server-side Exports list (stills live), share + partner comments, hashed runtime modules, a no-GSAP guard; video rendering is OFF and the render host is not deployed, migration 0061 + RLS sidecar not applied: owner steps inplans/OWNER-OPS.md§ 2026-10-18, remainder inplans/PENDING-WORK.md(MOTION-studio). -
Section nav (
D:section-nav, 2026-10-05): native “On this page” element (heading discovery, stable ids, level map, 5 layouts × pin modes, scrollspy in the one hashed runtime) + Dev MCPinsert_section_nav/configure_section_nav/set_heading_levels; 12x12/regulamento*migration is a runbook, not yet run (plans/SECTION-NAV-12X12-MIGRATION.md).docs/architecture/SECTION-NAV.md. -
Collection controls & sliders (
D:controls-workstream, 2026-09-21): batches 1 AND 2 (S18-S32, S34-S37) merged intoclaude/confident-cray-5evewb, PR #720, ready for review.plans/COLLECTION-CONTROLS-WORKSTREAM.md. Closes the original bug (“imported filters don’t work”): the canvas repeater expansion now stamps per-itemdata-mf-<slug>markers so Preview filtering works; the importer-guard bug, dangling targets, the pager marker mismatch, empty collection-fed selects, and nested/array-backed repeater wiring are all fixed. On top of that, batch 2 added a control-health model with badges on every control panel, a page-/site-wide audit panel, Bondlayer inspector parity (a real Field picker, a Value chip for context-bound controls), a “List chrome” pager section, carousel fade/nav- visibility parity, applied-filter chips with a rendered-cap notice, Item Snippet record navigation, and an A-tier server-backed query consumer for lists above their render cap. Settings also moved off the Style tab onto the Data tab per an owner call (S36). RPM/Simão: owner said ignore this round; 12x12 was deleted by the owner ahead of re-import (S33, still open — owner action). Separately, this batch fixed a real publish-timeout bug reported on 12x12-scale targets (S37: sequential R2 writes were hitting the client’s own abort deadline, not a Cloudflare limit — now parallelized with bounded concurrency, 24.8x measured speedup at 300 puts); S38 (an incremental output-hash publish cache, complementary to S37) remains speced-only, deferred. See the plan’s §13 for the full integration log (every cross-branch conflict, two more coincidental-boundary merge hazards caught, cumulative test results). -
Canvas CMS fetches (
D:collection-bundle, 2026-09-19): shipped. The repeater expansion asks for every collection a page needs in onecollection-bundlerequest; N repeaters over one collection share one cached source; after the 30 s TTL the request is conditional and an unchanged set is a bodiless 304 whose tag also encodes the caller’s denied collections. The legacy per-collection endpoints remain as the deploy-skew fallback. Verify on prod after deploy: devtools shows onecollection-bundlerequest per canvas push at most,304on the pushes after the first 30 s, and noGET /api/content-items?…content_type_id=from the canvas. The canvas-rebuild cost that remained is closed byD:canvas-incremental-renderbelow. -
Load more (
D:load-more-fragments, 2026-09-21): shipped. Importer emitsdata-mf-page-sizeon a Bondlayerloadmore(repeaterperPage, else 6 + a finding) and maps the remaining pager parts; publish writes<page dir>/_more/<listKey>/<n>.htmlfragments (pages 2..N, cap 40) for a paginating repeater with a load-more control and stampsdata-mf-more-pages/-baseon the list root; the cumulative runtime fetches the next fragment same-origin, sanitises, appends, re-censuses. Verify on prod after a republish ofsimaomartinez: the Load More button is visible, each click appends 9 rows, devtools shows_more/<key>/2.htmlfetched once, and the document’s first paint still has 9 rows. Canvas Preview mode ends after page 1 by design. Numbered pager (slice mode) still does not fetch. -
Canvas incremental render (
D:canvas-incremental-render, 2026-09-19): shipped.setDomTreediffs the incoming tree against per-node render signatures and patches only changed subtrees through the unmodifiedrenderNode; keyed child reconciliation moves untouched siblings; anything ambiguous escalates to the full rebuild (today’s code). Targeted handlers invalidate their node’s signature so a later full push cannot skip a node whose DOM diverged. Verify on prod after deploy: devtools Elements panel keeps an embed’s<iframe>(same node, no reload) across a style edit on a sibling; the Performance panel shows no#rootwipe on a single-node edit. Still open, sender-side and pre-existing: four targeted messages reach only the primary split pane, and attribute edits throughCommandDispatcher.updatePropertiespost nothing to the canvas until the next full push. -
Editor load path (
D:editor-snapshot, 2026-09-19): shipped; the repeater N+1 it named is closed byD:collection-bundle, the canvas rebuild byD:canvas-incremental-render. Boot is oneGET /api/targets/:id/editor-snapshotrequest (target + pages + folders + the boot page’s tree, one parallel statement batch) with a strongETagcomputed from(count(*), max(xmin))over the four source tables — any write through any path moves it, so the KV-parked body and the IndexedDB client copy can never be served stale. A repeat open of an unchanged site is one fingerprint statement + a bodiless 304. The legacy sequence is kept as the fallback for deploy skew (route 404). Still open on the same path, deliberately untouched here: repeater expansion is two requests per repeater node per canvas push (cache keyed by repeater id, not collection), and every canvas push is a fullinnerHTML=''rebuild inpreview.html. Neon scale-to-zero cold starts are a plan setting, not code. Verify on prod after deploy: devtools shows oneeditor-snapshotrequest on boot,304on reload, and exactly oneGET /api/nodeson a sidebar page click (none on a canvas-tab switch once thescope=allprefetch has landed). -
Style-layer loss on detach/unlink/push (
D:computed-style-layer-loss, 2026-09-14): FIXED in code, one data repair open.computeDetachedStyles(lib/shared-styles.ts) andcomputeComponentInstanceStyles(lib/component-instances.ts) rebuilt their result from a hardcoded allowlist —base, four pseudo states, six NAMED breakpoints,responsive— while every caller (unlinkFromStyle,pushOverridesToStyle,detachFromComponent, “create style from computed”) writes that result as the WHOLEstyle_overridescolumn, so anything outside the allowlist was erased on disk: custom variant/widget states (current), exact-pixel media layers (max768/min480— the only breakpoint spelling an IMPORTED site has, so unlinking an imported element dropped its whole responsive design), container layers, nth child-rules, Radixdata-state-*. PR #694 fixed that with a generic “every other layer” pass; the follow-up closes three narrower instances of the same shape — top-level leaf props still dropped, a style bag’sstates/tokens/includescopied in as pseudo-layers (layer INVENTION, which the serializer compiles to a junk.state-statesrule), andObject.assignmerging that let an instance’smax768.hoverreplace the master’s wholehoversublayer.frontend/src/lib/computed-style-layer-loss.residual.test.tspins all three plus two guards for what #694 fixed. Still open: three 12x12 CV “Tab Link” rows already flattened to{}— repaired by the pending re-import or by hand (plans/OWNER-OPS.mdrow 20). Standing rule for anything that readsstyle_overridesand writes it back whole: enumerate to DISPLAY, never to REBUILD. -
Recurring structures + live time (
D:recurring-structures,plans/RECURRING-STRUCTURES-AND-LIVE-TIME-PLAN.md): proposal, nothing built. The 2026-09-13 forms audit (12x12 case) found twelve gaps — public-form uploads, multi-image, publish-frozen dates, no form windows/phase stamps, UUID-only submitter, no forms settings UI, importer refusals, folder double-write. The plan answers them with rollup fields, collection triggers + instantiation, site symbols, form windows/derive, and a live-time tier (data-mf-whensettled in the browser against/api/m/:targetId/time, CSP invariant untouched), in three agent waves with disjoint file ownership. Rev 2 (same day): owner answered OD-A…E — periods are a universal, UI-editedschedulefield type (D:periods), re-submission replaces per (member, edition) (D:upsert-identity), admin grids are a new exportabletableelement (D:data-tables), rows are moderated without touching the member (D:row-moderation). Wave 0 landed on branchclaude/eager-planck-6wul5f(2026-09-13, unmerged): live-time tier (D:live-time), schedule field type + editor (D:periods), form gates/derive + scoped upsert + site symbols +/time/symbols(D:form-gates,D:site-symbols,D:upsert-identity, migration 0039), submitter identity + row moderation (D:submission-identity,D:row-moderation), vocabulary + docs stubs. Wave 1 (rollups, tables, imageset upload, form settings panel, invite send, importer passes, symbols UI) not started. Wave 1 landed on the same branch (2026-09-13): rollup fields (D:rollup-fields), data tables with client CSV/XLSX export and role-gated identity columns (D:data-tables), multi-image member uploads (D:imageset-upload), form settings panel (D:form-settings-panel), member invite send (D:member-invite-send), the collapse_slots_to_array pass + single-copy satellite import (D:slots-to-array,D:folder-single-copy), Symbols pane + Preview-at. Wave 2 landed (2026-09-14): collection triggers + instantiate step (D:collection-triggers), the 12x12 end-to-end PGlite narrative + setup tool + OWNER-OPS apply runbook, product/architecture docs, umbrella entryD:recurring-structures. Branch merged with master (migration renumbered to 0040); PR #682 (https://github.com/no-tag/SKYFALL/pull/682); CI green. Wave 3 (this pass, 2026-09-14,D:recurring-setup-ui): the operator runbook’s API-only steps became UI —RecurringSetupPane.tsx(/cms/:siteId/recurring, a status-driven checklist covering symbol + edit gate + collapse pass + form/table guidance),MemberEditGateEditor.tsx(on the collection’s Schema designer), and a UI forCollapseSlotsWizard.tsx(Data tab row action, reusing D199’s census-as-consent pattern). OWNER-OPS anddocs.matriz.design’s editions page updated to lead with the UI path. Still open: the owner still has to actually run the runbook against the real 12x12 target (nothing in wave 3 executes it — seeplans/OWNER-OPS.md’s “12x12: apply the recurring-structures shape” entry), plus the pre-existing open items — the browser verification report (W2-D) handoffs, and claiming the (now more than 16) slug D-numbers at final sync. -
Marketing / CRM integration (
D:marketing-catalog,plans/MARKETING-CRM-INTEGRATION-WORKSTREAM.md): Phase 1 shipped. A 46-template predesigned catalog in@matriz/email-render(17 visitor-auth, 18 commerce, 8 lifecycle, 3 forms), each abuild(brand) => EmailTemplateTreewith an enforced merge-field contract and a test-enforced transactional/marketing unsubscribe split. ONE send seam (lib/catalog-send.ts) where the project’s own edited copy wins over the catalog default and a missing required merge field REFUSES the send. Wired: the magic link (the hardcodedmagicLinkHtmlinroutes/modules/members.tsis deleted) and the Stripe order receipt. NewPOST /api/ai/marketingdrafts subject lines and body copy on the existing native/BYOK transport, with placeholder preservation as the load-bearing guard. Fixed a silent compiler bug on the way: hrefs were sanitized before merge resolution, so every{{ … }}link collapsed to a dead#. Open, both needing an owner decision: the contact-identity fork (a person is up to three unjoined rows, so “members who ordered” is unaskable) and the order-status pageshop.order-confirmationneeds for itsorder_url. Phases 2–4 (contacts spine, project-level CRM connections, lifecycle-event triggers) are specced, unbuilt. -
AI grounding workstream (
D:live-system-context,plans/AI-GROUNDING-WORKSTREAM.md): the de facto system (customized tokens + shared styles + components + usage) replaces the formal design-system input as the AI’s steering context. All six slices have shipped: S1 system-context assembler (D234), S2 provenance diff (#557, D235), S4search_systemretrieval tool (#563, D236 — the pgvector blend is a recorded deferral), S3 explicit mutation tier with the before-value guard (#568, D239), and S5 assembly exemplars + S6 cold-start brand proposal (D:assembly-exemplars/D:brand-first-generation)./generatecontext now reads vocabulary (S1) → grammar (S5) → standing (S6);POST /api/ai/brandproposes a re-theme of preset-graded tokens ONLY — anything a human authored is dropped by grade before a payload exists. Research:docs/Research/INTEGRATED-AI-WEBFLOW-FIGMA.md. Companion:plans/AI-ELEMENT-MENU-PLAN.md(D:ai-element-menu) — E1, E2, E1.1 and E3-B4 have all shipped (#559, #566, #577, #579; D238, D244, D245): the menu is one shared item list rendered on three surfaces (QuickToolbar ✦, DOM-tree row, canvas right-click), approve writes the layer the editor’s lens targets, and “Rewrite copy” refuses CMS-bound or repeater-row text by naming its owner before spending an allowance unit. Only B5 (generate variants) remains, deliberately unbuilt — the open question is where a variant lands, recorded in the plan. Sidebar hover-escape fix shipped separately (#562, D237). -
Tier-2 site reconstruction (
plans/TIER2-RECONSTRUCTION-PIPELINE.md,D:import-tier2-pipeline): the importer-expansion workstream’s own pipeline, specced (#567) and begun. Shipped: node identity + reconciliation (#574, D241 — three-tier keys, fingerprint-as-matcher enforced by type, refuses ambiguity rather than guessing) and the loss ledger (#578, D246 — instance-scoped because Worker isolates are reused, registry-as-type, static bypass guard proven by making it fail). Zero loss kinds are emitted yet: stages 3–13 (crawl shell, style extraction, token inference, repeats, assets) do not exist. Two open decisions before any adapter ships — tier-1 keys carry no page component (a global header repeated across pages derives one row id for N pages), and the crawl-shell loss kinds live outside the guard’s scan root. Squarespace research (docs/Research/IMPORT-SQUARESPACE.md) re-classified it Tier 2, not the brief’s guessed Tier 1.5:?format=jsoncarries no page structure and every structured?format=view is robots-disallowed. No live probe was possible from this network — every claim is marked verified/corroborated/unverified, and re-probing is the adapter session’s first job. -
AI provisioning Lane B (PR #545,
D:capability-provision/D:native-ai): one sentence → a PLAN of 1..5 approvable cards; native Workers AI provider (no BYOK key, plan-metered 50/day via CONFIG KV, status readout in Settings → Capabilities). Importer-expansion hand-off brief authored (plans/IMPORTER-EXPANSION-BRIEF.md,D:importer-expansion) — the site-builder migration workstream, ready to hand to a session. -
Owner ops queue (the real blocker list,
plans/OWNER-OPS.md): ONE blocking item — Render Docker rebuild ofapps/bondlayer-worker(or finish the Container cutover), then re-import (tick “Import members” for 12x12’s 331 galleries), then republish. Plus three non-blocking cleanup actions from the 2026-08-24 deletion entry (zombie-project reconcile, orphan sweep, FK sidecar). The 2026-09-13 wave (D256–D266) adds to this queue:apply drizzle 0039 + its RLS sidecar to prod(✅ done 2026-09-14 via MCP, together with the lagging 0038/0040 +collection_accessRLS sidecar — seedb/MIGRATIONS.md); re-import RPM/Simão/12x12 and republish so the importer fixes and/en/*multi-locale documents actually appear; and a Figma sync of the new preview chrome (PreviewTopBar, To-do mode, comment pins). -
Bondlayer coverage ledger (
plans/BONDLAYER-FOLLOWUPS-WORKSTREAM.md): open rows — vendor scripts (gated on origin isolation), carousel rewind, font pipeline, three-repeater verify (blocked on the re-import). Row 2 (/detail/:slug) closed by D226 — flip the ledger. -
One live owner decision (OWNER-OPS): the
assets.metadata.publicreconcile proposal. The target- vs project-owned media question is CLOSED — see Deletion below. -
Docs reset (2026-08-22, this change): stale docs archived to
archive/2026-08-22-doc-reset/; two Starlight sites added —apps/docs-product(docs.matriz.design, user-facing) andapps/docs-dev(dev.matriz.design, synced from repo docs). Owner: create the two Pages projects’ custom domains (see OWNER-OPS entry).
🗑️ Deletion (rewritten 2026-08-24, D:delete-completeness)
Section titled “🗑️ Deletion (rewritten 2026-08-24, D:delete-completeness)”Deleting a target now removes: its rows (DB cascade), the assets it owns —
rows AND R2 objects AND their LOD variants, including member uploads attributed
only through metadata.targetId — its published/build/version/backup prefixes
and redirect file, its Yjs snapshot binary in the second R2 bucket, its
Cloudflare-for-SaaS custom hostnames, its host→target KV cache entries, and its
design system when no sibling shares it. An asset a surviving target still
renders is kept and demoted to the project library (the reference scan fails
closed — no scan, no deletion). Deleting a project does all of that for every
target plus the whole <projectId>/ media prefix.
Project deletion was previously broken outright: the projects cascade hit
design_systems while targets.design_system_id (NO ACTION) still pointed at
it, so the organization.deleted webhook 500’d and Svix retried forever — the
org vanished in Clerk and every row stayed in Neon. Targets are now deleted
first. Regression-tested in workers/test/node/delete-cascade.pglite.test.ts.
Residue from before this is cleaned with POST /api/projects/:id/orphans/sweep
(admin, dry run unless apply=1) — see the OWNER-OPS entry for how to run it and
why attribution of the 5,906 already-detached asset rows is not recoverable.
Still SET NULL by design: audit_events.target_id (an audit event outlives
the target it describes and dies with the project). Analytics Engine page views
cannot be deleted per target.
Where to start
Section titled “Where to start”Read CLAUDE.md (doc map + conventions), then this file, then
plans/OWNER-OPS.md for what’s waiting on a human, and plans/PENDING-WORK.md for the
canonical cross-cutting open-items list (D:pending-work-canonical). Full launch-readiness detail:
the 2026-08-22 deep-dive audit (summarized in decisions-logs/ — grep INDEX.md
for D:doc-reset). decisions-logs/ is the why; packages/schema/src/schema.ts
(43 tables) is the data truth.