Skip to content

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).

  • 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 generated dom_nodes queries (row, subtree, ancestor chain) and a one-paste debug block pairing those ids with a runnable SELECT — 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-text a11y token, --highlight/--accent split, 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/:targetId door 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 new canvas_comments table, not a chat thread (D258 — drizzle 0039 + its RLS sidecar applied to prod 2026-09-14, /api/comments live); 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_switcher element, 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 _language gating 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-node style_overrides, not the derived-shared-style architecture the design called for (D261 — a fan-out rule like bgblur ×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’s title/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, and edit is create re-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):

  1. Yjs WS upgrade has no authorization FIXED — lib/ws-auth.ts verifies the Clerk token + target membership before the DO sees the socket (tested).
  2. assets-cdn.ts unauthenticated read-any-key FIXED — now enforces the same metadata.public authorization as /api/asset-pub (tested).
  3. RLS partially proven: the silent BYPASSRLS fallback is gone — getTenantDB now throws when NEON_AUTHENTICATED_DATABASE_URL is 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 prod skyfall-api before this ships.
  4. Minor sweep FIXED — backup reads admin-gated, /test-role deleted, elementClicked origin-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.

  • 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 in plans/OWNER-OPS.md § 2026-10-18, remainder in plans/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 MCP insert_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 into claude/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-item data-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 one collection-bundle request; 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 one collection-bundle request per canvas push at most, 304 on the pushes after the first 30 s, and no GET /api/content-items?…content_type_id= from the canvas. The canvas-rebuild cost that remained is closed by D:canvas-incremental-render below.

  • Load more (D:load-more-fragments, 2026-09-21): shipped. Importer emits data-mf-page-size on a Bondlayer loadmore (repeater perPage, else 6 + a finding) and maps the remaining pager parts; publish writes <page dir>/_more/<listKey>/<n>.html fragments (pages 2..N, cap 40) for a paginating repeater with a load-more control and stamps data-mf-more-pages/-base on the list root; the cumulative runtime fetches the next fragment same-origin, sanitises, appends, re-censuses. Verify on prod after a republish of simaomartinez: the Load More button is visible, each click appends 9 rows, devtools shows _more/<key>/2.html fetched 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. setDomTree diffs the incoming tree against per-node render signatures and patches only changed subtrees through the unmodified renderNode; 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 #root wipe on a single-node edit. Still open, sender-side and pre-existing: four targeted messages reach only the primary split pane, and attribute edits through CommandDispatcher.updateProperties post 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 by D:collection-bundle, the canvas rebuild by D:canvas-incremental-render. Boot is one GET /api/targets/:id/editor-snapshot request (target + pages + folders + the boot page’s tree, one parallel statement batch) with a strong ETag computed 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 full innerHTML='' rebuild in preview.html. Neon scale-to-zero cold starts are a plan setting, not code. Verify on prod after deploy: devtools shows one editor-snapshot request on boot, 304 on reload, and exactly one GET /api/nodes on a sidebar page click (none on a canvas-tab switch once the scope=all prefetch 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) and computeComponentInstanceStyles (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 WHOLE style_overrides column, 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, Radix data-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’s states/tokens/includes copied in as pseudo-layers (layer INVENTION, which the serializer compiles to a junk .state-states rule), and Object.assign merging that let an instance’s max768.hover replace the master’s whole hover sublayer. frontend/src/lib/computed-style-layer-loss.residual.test.ts pins 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.md row 20). Standing rule for anything that reads style_overrides and 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-when settled 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-edited schedule field type (D:periods), re-submission replaces per (member, edition) (D:upsert-identity), admin grids are a new exportable table element (D:data-tables), rows are moderated without touching the member (D:row-moderation). Wave 0 landed on branch claude/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 entry D: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 for CollapseSlotsWizard.tsx (Data tab row action, reusing D199’s census-as-consent pattern). OWNER-OPS and docs.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 — see plans/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 a build(brand) => EmailTemplateTree with 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 hardcoded magicLinkHtml in routes/modules/members.ts is deleted) and the Stripe order receipt. New POST /api/ai/marketing drafts 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 page shop.order-confirmation needs for its order_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), S4 search_system retrieval 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). /generate context now reads vocabulary (S1) → grammar (S5) → standing (S6); POST /api/ai/brand proposes 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=json carries 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 of apps/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_access RLS sidecar — see db/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.public reconcile 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) and apps/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.

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.