This repository was archived by the owner on May 9, 2026. It is now read-only.
docs: v1.x editor end-state UX (prototypes + refactor plan)#150
Open
slavasolutions wants to merge 46 commits into
Open
docs: v1.x editor end-state UX (prototypes + refactor plan)#150slavasolutions wants to merge 46 commits into
slavasolutions wants to merge 46 commits into
Conversation
Lock seven new ADRs (and one supersession) from the 2026-05-06 grilling session that reshapes v1 around an opt-in site mode. - 0021 Headless / Site mode split — per-project flag, UUID + manifest block ordering (reframes ADR-0007 from definition to storage strategy) - 0022 Record schema-version persistence — every record stamps version, declarative + imperative migrations chain at read - 0023 Editor surface registry — closed core (form/article/sections/canvas) with internal contract; future-opens to third parties non-breaking - 0024 Site mode renderer = library + scaffold template (supersedes ADR-0008) - 0025 Per-bucket theme tokens — design-system foundation, schemas reference via optionsFromTokens, store token name (not resolved value) - 0026 Preview tier contract — T0/T1/T2 named tiers (T1 mostly built; site mode auto-emits markers; T2 reserved) - 0027 Markdown as first-class widget alongside richtext — two prose widgets, two storages - 0028 Block library publishing rhythm — strict semver, package-shipped migrations CONTEXT.md updated with the new vocabulary (Mode, Site, Block, Block-schema, Theme tokens, Preview tier, Schema version, Richtext extensions; Frontend / Editor mode / Widget extended). STATE.md records the architectural pivot and links the new ADRs. ADR-0007 reframed (storage strategy, not block definition). ADR-0008 superseded by ADR-0024. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0.3.0 in package.json never reached npm (broken publish workflow, fixed by #21). Rather than catch-up publish, accumulate the queued changesets + open feature PRs and ship as v0.4.0 in one wave. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
All 9 queued packages now on npm. Documents the path that got us there (six commits + per-package TP config) so future debug saves that walk. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two compounding causes: 1. Cached libSQL singleton in lib/db.ts opened lazily when a route is imported was never closed by tests. Added closeDb() export. 2. Windows occasionally needs a retry window on rm even after close(). Added safeRm() helper in test/teardown.ts (5 retries, 50ms backoff). Wired into pages-reindex, schemas/[key], preview/[token], backup, restore test suites. All 389 admin tests pass on Linux. Windows clone should clear the EBUSY blocking Eng B's full test runs. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…+ SQLite teardown + update notifier)
…/0028) (#57) Squash of 9 commits — contract trail preserved here per the PR body's "do not squash" intent (forced by repo policy: only squash-merge is allowed). The per-commit narrative below maps to the changeset entries under .changeset/ that ship with this PR. 1. f4c6f4d feat(spec): add clear.config.json mode field (ADR-0021) 2. 4e0147c feat(spec): add schemaVersion + version per ADR-0022 3. dabb8e5 feat(spec): add sections + canvas editor modes (ADR-0021/ADR-0023) 4. eb50062 feat(spec): add markdown as a first-class widget (ADR-0027) 5. 2a4c137 feat(spec): per-field extensions allowlist for richtext + markdown 6. f2bdd41 feat(spec): per-field optionsFromTokens for select widgets (ADR-0025) 7. ed0bea0 feat(spec): block-schema dialect + block-instance + blockStorage hint 8. 96a0a3b feat(spec): per-bucket tokens shape + Migration types (ADRs 0025/0022) 9. b6c6294 chore(spec): self-review polish — drop dup config re-export, symmetric tokens re-exports Phase 3 of the v1.0 release plan. Extends @clearcms/spec with the contract surface site mode and schema-version persistence depend on. Eng B track per clearcms/planning/decisions/2026-05-06-v1-architecture.md. Track tests: 219/219 (spec) — typecheck clean, build green. Self-review: APPROVE — no blockers, three LOW-severity nits addressed in commit 9. Closes the Phase 3 spec foundation milestone. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Squash of 6 commits — Eng B Phase 5/6 storage track. Trail preserved here per the no-squash intent (forced by repo policy: only squash-merge allowed). Per-commit narrative maps to .changeset/ entries. 1. 4956c11 feat(storage): write-time schemaVersion stamp (ADR-0022) 2. 40612df feat(storage): named-global writers — identity / nav / tokens 3. e8db616 feat(storage): writeItem + writePage with status routing 4. 971102c feat(storage): writeBlock + resolveBlockStorage + writePageData stamp 5. f31fe83 feat(storage): bucket-path helpers 6. b696176 feat(admin): clear-admin backfill-schema-versions CLI surface (stub) Adds the load-bearing schema-version write primitives + every named writer the v1 plan asks for, all routing through `writeStampedJson`: - `stampSchemaVersion(record, schemaVersion?)` — pure precedence chain (explicit arg → record's existing valid semver → `DEFAULT_SCHEMA_VERSION`). - `writeStampedJson(adapter, key, record, schemaVersion?)` — atomic temp+rename with stamp. - Named globals: `writeIdentity` / `writeNav` / `writeTokens` / `writeGlobal` (generic dispatch on closed-set name). - Records: `writeItem` (status-routed via `itemPath()`), `writePage` (`content/pages/<slug>/index.json`). - `writeBlock` for `blockStorage: "files"` per-instance file path. - `writePageData` augmented to stamp on object payloads. - `resolveBlockStorage(collectionSchema, collectionSlug)` strategy resolver (default `"files"` for `pages`, `"inline"` elsewhere). - Path helpers `themeBlocksDir` / `pageBlocksDir` / `pageBlockPath`. - `clear-admin backfill-schema-versions` CLI surface (stub; full bucket walk lands in Phase 6 closeout). Track tests: spec 219/219 + storage 89/89 = 308 ✅. Full implementation of bucket walk pending Phase 6. Stacked on PR #57; auto-retarget didn't fire — manually retargeted to release/v1.0 + rebased --onto to drop the now-merged spec commits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Squash of 2 commits — Stream-Migrate. Trail preserved here per no-squash intent (forced by repo policy). 1. 89441bc feat(spec): migration runner + library discovery (ADR-0022, ADR-0028) 2. ebf44a1 feat(admin): clear-admin migrate-records stub (ADR-0022) `@clearcms/spec/migrate` runtime: `migrate(record, from, to, migrations)`, `buildMigrationChain` (greedy shortest-path), `applyOp` for each declarative op kind (rename / default / remove / tiptap-to-markdown / markdown-to-tiptap), `discoverMigrations` walking both `<bucket>/migrations/` and `node_modules/@clearcms/blocks-*/migrations/` per ADR-0028. Markdown ↔ TipTap conversions are best-effort with `_migration_warnings` annotation per ADR-0027. In-package serializers (no `marked` / `remark` deps). `apps/admin/src/cli/migrate-records.ts` — argv shape (`--collection <slug>` | `--all`, `--dry-run`) locked, full bucket walk + persist deferred to Phase 6 closeout. Track tests: spec 247/247 (+25 for migration runner). Stacked on PR #57; manually retargeted + rebased --onto release/v1.0 to drop the spec commits already squashed via #57. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Squash of 2 commits — Stream-Renderer scaffold. 1. f06d7f8 feat(renderer): library scaffold (ADR-0024) 2. 43c2d70 chore(renderer): clarify FOLLOWUP(#57) marker for spec-types swap `@clearcms/renderer` library shape (NOT an Astro integration). Types + dispatch only — no real blocks yet. Exports: `renderBlocks(blocks, registry)` (pure dispatch), `UnknownBlock` (no-op fallback per ADR-0021), `usePreview()` (SSR-safe `?clear-preview=` detection + `markField` helper for ADR-0026 T1 markers). Framework-neutral — no React/Astro/Vue dep. `RendererBlockInstance` is a local mirror with a `FOLLOWUP(#57)` marker for the swap to `@clearcms/spec/blocks` once #57 lands. Now that #57 is merged, the swap is queued — see `grep -r 'FOLLOWUP(#57)' packages/`. 9 tests cover renderBlocks happy path with known + unknown blocks, `UnknownBlock` fallback, `usePreview` URL detection, `markField` output shape. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…1/0022/0025) (#75) Stream-Doctor — 4 new `clear-admin doctor` checks for the v1 site-mode + ADR-0022 schema-version contract. Each is a pure async function returning `DoctorIssue[]`; all four return zero issues on a healthy v0/v1 bucket. - `checkModeConsistency` (ADR-0021) — `clear.config.json`'s `mode` vs the bucket's site-mode artifacts. Headless + `theme/blocks/` files present → warning. Site + no `theme/blocks/` → info. - `checkDanglingBlockTypes` (ADR-0021) — page `data.json` block manifests reference unknown block types → warning each. - `checkStaleSchemaVersions` (ADR-0022) — record `schemaVersion` vs the schema's current `version`, per-collection counts. Suggests `clear-admin migrate-records` for stale collections. - `checkDanglingTokenRefs` (ADR-0025) — block-instance fields with `widget: "select" + optionsFromTokens: <category>` reference token names absent from `theme/tokens.json[<category>]`. Wired into `doctor.ts`'s main run sequence so `clear-admin doctor` surfaces the new checks alongside the 10 existing ones. Tests: 18/18 across 4 new test files. Pure fs fixtures (`mkdtemp` + JSON file writes); no SQLite DB harness — Windows EBUSY pre-existing issue (#56) doesn't apply. Local `assumeSchemaVersion` shim in `doctor-stale-schema-versions.ts` that should be swapped to `@clearcms/spec`'s now that #57 has merged. Tracked as a follow-up cleanup PR per the lead's review. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Squash of 2 commits — Stream-Docs CONTENT-PROTOCOL v1 rewrite. 1. (initial commit) docs(protocol): CONTENT-PROTOCOL.md v1 rewrite (site mode) 2. (review-nits commit) docs(protocol): address review nits — ADR-0024 scope note + SDK schemaVersion default Rewrites `docs/CONTENT-PROTOCOL.md` to cover v1 site mode. Archives the v0 file to `docs/CONTENT-PROTOCOL-v0.md` via `git mv` (history preserved). 14 sections covering: Overview (mode split) | Bucket layout | Schema versioning + migrations | Block storage strategy | Theme tokens | Markdown / richtext widgets | Item document schema | Page document schema | Block library publishing | REST API | Site mode example | Headless mode unchanged | Doctor + backfill commands | Summary TOC. Cross-references ADRs 0021 / 0022 / 0024 / 0025 / 0027 / 0028 inline. Review nits addressed in commit 2: - §1 scope note clarifying ADR-0024 (renderer architecture) lives outside this protocol doc by design — bucket-level contract only. - §3 SDK-author note on the absent-`schemaVersion` = `"1.0.0"` rule; recommends `assumeSchemaVersion` over `.default()` Zod parse so the doctor's "untracked" check stays meaningful. 1015 lines. Status line: v1, descriptive. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Establishes the rigid task list + turn-based coordination for the rest of v1. Both engineers (S1 = lead/Eng A, S2 = Eng B) read these files at session start and follow them strictly. PLAN.md is the ordered task queue from now to v1.0.0. Each task is tagged [S1] or [S2] with prereqs noted. Phase 3 already complete (all 6 PRs merged today). Phase 4-8 still queued. BATON.md is a single-line file (S1 or S2) marking whose turn it is to merge to release/v1.0. Initial value: S2 — Eng B has #102/#103 to process before lead picks up Phase 4. Hard rules in PLAN.md: - One in-flight merge at a time per session. - Strict alternation enforced by BATON. - No direct push to release/v1.0 except the two-line BATON+PLAN flip commit immediately after each merge. - Surface ownership zones to prevent file-level conflicts. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#102) Reflects the v1 wave merged on release/v1.0 (PRs #57/#72/#73/#74/#75/#76). Updates header date + tagline; new "v0.5 conceptual milestone" section listing each merged PR with SHA + scope; bucket protocol section flips to v1 layout; "in flight" section points at next gates (#52, blocks-marketing); ADR status block updated per ADR. closes T-3.7 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
T-3.9 (lead's #101 migrate-bucket-to-r2 merge) is blocked: the @clearcms/spec barrel re-exports migrate/run.ts (node:fs/promises + node:url + node:path), so Vite drags it into both client and server chunks of admin's astro build. Build fails with __vite-browser-external, or smoke tests fail with ERR_MODULE_NOT_FOUND at runtime. Confirmed with a clean lockfile + green typecheck + 426/426 tests + an astro.config external mark — the issue is fundamentally in the spec package's exports shape. Spec is S2 surface (per PLAN ownership zones). Inserting T-3.8.5 [S2] as a hot unblocker: split @clearcms/spec/migrate so the bucket-walking runner is a server-only sub-export. Detail in PLAN.md. Flipping BATON to S2 to enable the fix. T-3.9 stays open and unticked — S1 will retry it after S2's spec fix lands and lead's #101 rebases clean. T-3.10 / T-3.11 / T-3.12 reordered for cleaner Phase 3 wrap-up (pulled #103 blocks-marketing forward as T-3.11 since it's already in flight). This commit modifies only BATON.md + PLAN.md per protocol.
…rowing (#106) Bundled fix unblocking the entire v1 PR queue. Per-commit trail: - e9e9b4b fix(spec): split migrate runner — Node-only fs in /migrate/runtime sub-export - 07b3aa8 fix(deps): regenerate pnpm-lock.yaml after spec exports map change - 6370200 fix(spec,core,storage,design): publish hygiene — dist-only tarballs + spec/migrate split - eae5978 fix(admin): narrow editor mode + add markdown widget label (cherry-picked from #109 per S1's Path A) What this fixes: 1. @clearcms/spec migrate split — run.ts now pure (no node:* imports). Node-only discovery lives in new runtime.ts at @clearcms/spec/migrate/runtime (node condition only). Drops src from files whitelist. 2. Publish hygiene — @clearcms/core, @clearcms/storage, @clearcms/design flipped from raw-TS to dist-only with tsconfig.build.json + conditional exports map. 3. Admin enum narrowing — PageItemEditor + SchemaPreview narrow editorModeFor() via requiresSiteMode guard; PageContentTypeFields uses 'as const'; AddFieldModal adds 'markdown' to WIDGETS + WIDGET_DESCRIPTIONS. Per-callsite narrow until Phase 4 editor surface registry (#52). Changesets: - @clearcms/spec: minor (export shape change) - @clearcms/core: minor - @clearcms/storage: minor - @clearcms/design: minor - @clearcms/admin: patch Verification: - pnpm --filter @clearcms/admin typecheck → 0 errors - pnpm --filter @clearcms/admin build → green (no node:* in browser bundle) - pnpm pack across the four packages confirms dist-only tarballs Closes #105 Closes #107 Closes #108 Closes T-3.8.5 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(admin): clear-admin migrate-bucket-to-r2 (closes #14) One-shot fs → R2 data migration. Reads ~/clear/registry.json, creates the target bucket via the platform R2 token from R2_* env vars, PUTs every file under bucket/ at the same key, atomically rewrites registry.json with bucketProvider:'r2', and renames the local bucket dir to bucket.archived-<ISO>/ so the operator can verify parity before deleting. Implements ADR-0017 v1 happy path (steps 1, 2, 3, 5, 6, 7, 8). Status flipped from proposed to accepted. Deferred (called out in the changeset): - Step 4: per-project scoped R2 token mint per ADR-0016. - Resume-on-failure via .migration-progress.json (ADR-0017 Risks). - Streaming PUTs for large media (v1 reads each file fully into memory; fine for text-shaped sites, fails on multi-GB media libraries). Env contract: reads R2_* (the platform-token shape from ~/.r2-platform.env), distinct from the per-project CLEAR_R2_* the admin reads at boot. CreateBucket requires admin scope across all buckets, so the platform token is the right input here; ADR-0016 will replace this with a per-project token mint. Tests: vitest fixture suite injects a recording StorageOps double; covers happy path, content-type map, --bucket-name override, already-on-R2 short-circuit, missing-slug + missing-source-bucket fail-fast, env validation, and that other registry entries are preserved during rewrite. 8/8 pass. Dogfooded against ~/clear/clear-marketing/ (37 files, 172K, fs → R2 bucket "clear-marketing"). Post-migration verified: archive dir present, original bucket renamed, registry shape correct, and astro build with CLEAR_BACKEND=r2 produces the same 12 pages as the fs baseline (only diff is the homepage's local _demo/ walker, which is site-code-specific and unrelated to the migration). Discovered during testing: doctor.test.ts > "PASS for a present bucket dir" has a tight 5s timeout on a sqlite3 execSync loop that occasionally trips when running alongside other parallel test files. Pre-existing fragility (passes 397/397 with --no-file-parallelism); the new test file just adds enough load to expose it. Out of scope for this PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(admin): remove obsolete vite externals for spec/migrate Pre-T-3.8.5 astro.config.ts marked @clearcms/spec/migrate as a ssr.external + rollup external, which baked the BUILD-TIME absolute path of /home/runner/packages/spec/dist/migrate/run.js into the items chunk. That path is correct on the dev machine but doesn't exist on the CI runner (which builds from /home/runner/work/clear/ clear/...), so the smoke test failed with ERR_MODULE_NOT_FOUND. T-3.8.5 (PR #106) fixed the underlying problem structurally by splitting @clearcms/spec/migrate into a browser-safe barrel and a Node-only @clearcms/spec/migrate/runtime subpath. The vite externals in admin became obsolete and harmful — same posture as release/v1.0 HEAD, which has none. This commit removes the externals so admin's build matches HEAD's configuration. Local build passes; CI smoke test should now resolve the chunk imports through Node's normal package-exports resolution instead of an absolute file path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
First v1 block library shipping with create-clear scaffolds and renderer support. Per-commit trail: - 10542dc feat(blocks-marketing): scaffold + Hero (ADRs 0024/0025/0028) - 3ffca3c feat(blocks-marketing): Features + CTA + FAQ + Footer + Contact (5 blocks) - 43154d4 feat(blocks-marketing): Testimonials + Pricing + Logos + RichText (final 4 blocks) 10 blocks: Hero, Features, CTA, FAQ, Footer, Contact, Testimonials, Pricing, Logos, RichText. Each block ships an Astro component + Zod schema with the v1 widget dialect (ADR-0027). Theme tokens (ADR-0025) referenced via optionsFromTokens binding. Schema versioning per ADR-0022; lockstep publishing per ADR-0028. Changeset: @clearcms/blocks-marketing minor. Closes T-3.11. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements ADR-0023's closed-core surface registry. The schema's
`editor:` keyword resolves through `surfaceFor(schema)` to one of
four surfaces — form, article, sections, canvas — each implementing
the EditorSurface interface (validate + Component). PageItemEditor
and PagePageEditor dispatch through the registry instead of
branching on a narrowed 'form' | 'article' value.
- apps/admin/src/surfaces/types.ts — EditorSurface, SurfaceProps,
SurfaceContext, SurfaceValidationResult, SiteContext + ok()/failed()
helpers.
- apps/admin/src/surfaces/{form,article}/index.tsx — delegate to
SchemaForm for behavior parity. Article validator wraps
validateArticleSchema (exactly one richtext required).
- apps/admin/src/surfaces/{sections,canvas}/index.tsx — not-implemented
placeholders. Sections validator refuses outside site mode + when
no block schemas exist. Canvas validator always refuses (post-v1).
- apps/admin/src/surfaces/index.ts — surfaceFor() dispatcher + the
closed registry object. Type re-exports for consumers.
- PageItemEditor + PagePageEditor: surface dispatch + inline
SurfaceValidationFailed render when validate() returns ok: false.
Tests: 10 cases covering dispatch defaults, named-value resolution,
per-surface validators (article richtext-count, sections site-mode
+ block-schema gates, canvas reserved-for-post-v1, form always-ok),
and registry shape integrity.
Per ADR-0023 the registry is internal-only — no public registration
API in v1. Opening it to third-party surfaces is a future ADR keyed
off concrete adopter demand.
Closes T-4.1, T-4.2, T-4.3, T-4.4, T-4.5, T-4.6, T-4.7. Closes #52.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(admin): plumb project mode at boot (T-5A.1, ADR-0021) Adds the runtime resolution path for ADR-0021's mode flag. Site-mode surfaces (tokens UI, sections editor, designer extension, blocks library) consume getProjectMode() / isSiteMode() in T-5B/C/D PRs to gate sidebar entries and route guards. - project-config.ts: mode: ProjectMode now resolved from env CLEAR_MODE > file.mode > headless. Provenance tracked in sources.mode. Unknown values fall back to default rather than throwing — boot must succeed even with a slightly off config so the operator can fix the file from the running admin. - project-mode.ts: cached getProjectMode() + isSiteMode() helpers for runtime hot paths. Cache resets only via _resetProjectModeCache() (tests only). Existing v0.x installs default to headless and see no behavior change. The site-mode-only sidebar entries + route guards land in T-5B.1 (/settings/site/tokens) and T-5D.1 (sections route exposure). Tests: 6 helper cases + 4 resolver cases covering default / file / env / env-override / unknown-value fallback. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(admin): address #120 review findings — provenance + test cwd hygiene Self-review pass on T-5A.1 found two in-scope fixes: 1. project-config.ts mode resolution: provenance was reported based on which input was *populated*, not which input *resolved*. A bogus env like CLEAR_MODE=composer would record sources.mode='env' while actually using the default — misleading in doctor output. Fixed to use a validity-aware predicate so sources.mode reports the source of the *resolved* value. 2. project-mode.test.ts: process.chdir leaked across tests in the same worker, and chdir'ing to /tmp picked up stray clear.config.json files on dev machines. Fixed by capturing originalCwd in beforeEach + restoring in afterEach, and using mkdtemp for the default-case tests instead of /tmp. Follow-up filed separately for the third finding (wire doctor-mode-consistency.ts to consume the new resolver + warn on typo'd mode values). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ag + integration tests (#121) T-5F.1 — site-mode scaffold pre-wired with @clearcms/blocks-marketing v1. Per-commit trail: - 4d44e3a feat(create): template-site-marketing scaffold + --theme=marketing flag - 48f5974 test(create): integration tests for create-clear CLI (--theme, replacements, --force) What's in: - packages/create-clear/template-site-marketing/ — full scaffold (clear.config mode=site, theme tokens, theme/blocks/*.schema.json for all 10 marketing blocks, sample home page with 3 pre-populated blocks: Hero + Features + CTA) - packages/create-clear/bin/create-clear.js — THEMES map, --theme <name> and --theme=<name> forms, validation - packages/create-clear/test/create-clear.test.js — 26 integration tests subprocess-invoking the CLI: --help, default theme, --theme=marketing scaffold structure, replacements, --force, error paths - vitest infra wired (matches workspace pin 4.1.5); test/ path keeps tests out of the published tarball (verified via pnpm pack: 48 entries, zero *.test.*) Per-callsite footprint, independence-checked: zero S1-territory paths. ADRs: 0021 (mode split), 0022 (schemaVersion persistence), 0024 (block library), 0025 (theme tokens), 0027 (widget dialect), 0028 (lockstep publishing). Changeset: @clearcms/create minor. Closes T-5F.1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(admin): theme tokens UI (T-5B.1, site-mode only)
Implements PLAN T-5B.1: site-mode editor for theme/tokens.json.
- /settings/site/tokens route (gated by isSiteMode() — headless installs
302 to /settings).
- TokensPanel: 3 groups per the PLAN spec — colors, typography,
"space/radius/shadow/motion." Inputs are direct CSS strings; ColorPicker
pairs a native swatch with a text input so operators can fast-pick or
type oklch() values.
- LivePreview: sandboxed button + heading + card rendering with the live
state, transitioning per the motion.medium token.
- adminTokens.save action: isSiteMode-gated; writes via writeTokens from
@clearcms/storage. Permissive zod schema so adopters can add custom
groups (e.g., breakpoints, z-index) without a code change.
DEFAULT_TOKENS mirrors the marketing-template scaffold so a fresh
admin lands on a sensible empty state. 5/5 tests on the shape.
Closes T-5B.1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(admin): deep-merge tokens loaded from bucket so partial files keep defaults
Self-review found a real bug: tokens.astro spread { ...DEFAULT_TOKENS, ...fromBucket }
replaces the entire group when bucket has e.g. just color.primary, dropping
all other default keys the editor + LivePreview expect. Replaced with a
mergeTokens() helper that deep-merges per group + adds 5 tests covering
the merge semantics (partial group preserve, new key add, new group
verbatim, scalar override, null/undefined no-op).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…er-bucket + Hero v1→v2 (#136) T-6.1 — replaces the migration runner stub from PR #76 with the full v1 impl. Per-commit trail: - ba3b9dd feat(migrate): full migration runner — declarative + imperative + greedy-at-read + lazy-on-save (T-6.1 draft) Spec (pure runner + Node-only fs discovery): - run.ts: applyChain, inferTargetVersion + BlockTypeRegistryEntry type - runtime.ts: discoverPerBucketMigrations (ADR-0015), loadBlockMigrations (ADR-0028) Storage greedy-at-read bridge: - New migrate-bridge.ts (Node-only sub-path) — readMigratedItem/Page/PageData/Block - Lazy-on-save: caller's call. Storage stays adapter-agnostic. Admin CLIs (shared, S2-owned per task body): - migrate-records.ts full impl (--bucket --type --collection --dry-run --verbose) - backfill-schema-versions.ts full impl Blocks-marketing sample (Hero v1→v2): - subheadline → subhead rename + secondaryCta{Label,Href} - migrations/Hero/1.0.0-to-2.0.0.json declarative Changesets: - @clearcms/spec: minor - @clearcms/storage: minor - @clearcms/admin: minor - @clearcms/blocks-marketing: major (Hero schema bump) Verification: spec 264/264 pass, storage 99/99, blocks-marketing 23/23, new CLI 32/32, admin astro check clean. ADRs: 0015, 0022, 0024, 0028. Closes T-6.1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
T-5A.1 follow-up. The mode-consistency check now uses loadProjectConfig().mode as the resolver — single source of truth across env / file / default. Critical addition: when the resolver fell back to default because the raw value didn't validate, the check WARNs with the operator-supplied bad value. Without this, mode='sote' silently boots as headless and the operator can't tell their typo wasn't honored. Tests: 9 cases (was 5). Added 4: file.mode typo, env.CLEAR_MODE typo, typo prevails over headless+blocks WARN, valid env value doesn't trip the typo branch. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(admin): block-schema designer extension (T-5C.1, ADR-0024)
Site-mode-only — extends the visual SchemaDesigner to read+write
theme/blocks/<Name>.schema.json files alongside the existing
collection + layout schemas.
- lib/schemas.ts: 'block' added to SchemaKind; b:<PascalName> key
encoding with isValidBlockName guard (PascalCase only, ADR-0024);
listSchemas now scans theme/blocks/*.schema.json; readSchemaByKey
+ writeSchemaByKey convert between BlockSchema (on-disk shape:
{ name, version, fields }) and ClearSchema (designer shape:
{ type: 'object', properties }) so the existing SchemaDesigner
works without modification. Block meta (name/version/title/
description/deprecated) round-trips via a clear:block field the
designer ignores; final-form BlockSchemaSchema validation on
write.
- SchemaList.tsx: third "Block schemas" section in the left rail,
shown only when at least one block exists.
- settingsSections.ts: Settings sub-nav adds "Schemas" + "Block
schemas" entries (gated by schemaEditor).
- pages/settings/blocks/index.astro: site-mode list (302 to /settings
in headless, /schemas in site).
- pages/settings/blocks/[name].astro: site-mode designer wrapper
(302 to /schemas/b:<name>; rejects non-PascalCase names with 400).
Tests: 18 cases (was 10). New: block-key encoding (PascalCase
required, path-traversal rejected, empty rejected), dialect
conversion round-trip (fields ↔ properties + meta + deprecated +
default-version fallback).
Closes T-5C.1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(admin): address #122-style review on T-5C.1 — site-mode gate + cache + spec parity
Self-review pass on the block-designer extension found 3 in-PR fixes:
1. invalidateCachesForKey: block kind was falling through to the layout-
cache invalidation branch, calling invalidateLayoutSchema() with a
block name. Harmless today (no block-schema cache exists yet) but
wrong shape. Added explicit block branch (no-op + comment).
2. Site-mode leak on /schemas + /api/admin/schemas: the existing schema
designer routes had no isSiteMode() gate, so a headless operator who
bookmarked /schemas/b:Hero would land on a working block-designer
surface. Now: /schemas/[key] returns 404 when key is `b:` and not
site-mode; /schemas + /schemas/[key] filter blocks out of the rail;
POST /api/admin/schemas/[key] returns 403 on block writes in headless.
3. PascalCase regex was stricter than @clearcms/spec's BlockNameSchema
(which allows `[A-Za-z][A-Za-z0-9-]{0,59}` — hyphens + lowercase
leading). Mismatch meant on-disk hyphenated block schemas would be
reachable on disk but invisible in the designer. Reconciled by
matching spec's regex (max 60, leading alpha, alnum+dash). Tests
updated.
Tests: 19 (was 10 → 18 after T-5C.1 → 19 with the spec-parity case).
Filing follow-ups for: writeSchemaByKey block-path coverage, listSchemas
blocks-present coverage, redirect-route tests, title==name drop edge
case.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(admin): sections surface MVP (T-5D.1-4, ADR-0021/0024)
Replaces the Phase 4 SectionsPlaceholder with a real composition UX.
Site-mode admins with at least one block schema can now author a
sections page end-to-end: add block instances, edit each block's
fields via per-type SchemaForm, reorder via up/down buttons, delete.
Files:
- surfaces/sections/types.ts — SectionInstance, SectionsManifest,
toManifest defensive coercion, newSectionId crypto-random helper.
- surfaces/sections/SectionCard.tsx — header (type label + reorder +
delete) + body (per-block fields via SchemaForm with block-schema
converted to ClearSchema). UnknownBlockType renders inline error.
- surfaces/sections/AddSectionPicker.tsx — minimal select + add UI;
empty state when no block types exist.
- surfaces/sections/SectionsSurface.tsx — manifest stack + dispatcher.
- surfaces/sections/index.tsx — validator now returns ok() when
siteContext + at least one block schema. Component = SectionsSurface.
Storage strategy: v1 ships inline-in-pageData. Per-instance block
files (writeBlock + blockInstancePath) land as v1.x follow-up. dnd-
kit reorder, visual block picker, click-to-focus from preview also
deferred to v1.x — operator can author end-to-end with the MVP.
Tests: 11 surface-registry cases (added "sections passes when site-
mode + blocks" branch) + 7 manifest helper cases.
Closes T-5D.1 / T-5D.2 / T-5D.3 (basic) / T-5D.4. Defers T-5D.5/6/7/8.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(admin): wire siteContext to surface dispatchers (closes block-merge from review)
Self-review on T-5D found a hard blocker: PageItemEditor and
PagePageEditor passed `{}` as the surface validation context, so
sections surface's validator (which requires `ctx.siteContext`)
returned failed() unconditionally — surface never actually mounted.
Fix:
- New `lib/site-context.ts` with `loadSiteContext()` + cache. Reads
every theme/blocks/*.schema.json and theme/tokens.json into a
SiteContext shape. Returns null in headless mode.
- PageItemEditor + PagePageEditor accept `siteContext?: SiteContext |
null` prop, thread it through to surface.validate() + Component.
- .astro callers (item editor + page editor) pass `await
loadSiteContext()` as the prop.
Plus the memo fix from the same review:
- SectionsSurface uses useMemo for types + schemaForType conversion
(was rebuilt every render). Removed useCallback that was no-op
due to props-deps capturing every-render onChange.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(admin): keep storage server-only by converting block schemas in site-context
CI failure on PR #142: SectionsSurface imported blockSchemaToClearSchema
from lib/schemas.ts, which transitively imports @clearcms/storage's
readJson/writeJson — pulled the FS adapter (node:fs/promises, node:path)
into the client bundle and exploded with "join is not exported by
__vite-browser-external".
Fix: move the BlockSchema → ClearSchema dialect conversion into
loadSiteContext() (server-side .astro path). The SiteContext that
arrives at the editor island already carries ClearSchema-shaped block
schemas, so SectionsSurface no longer needs to import any conversion
helper or anything else from lib/schemas.ts. Bundle stays clean.
Local `astro build` now succeeds; CI should match.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
T-7.1 — ADR status sweep + block-library author guide + CONTENT-PROTOCOL v1 polish. ADR sweep: - 0007 reframed by 0021 (page block storage shifted to v1 dialect) - 0008 superseded by 0024 (Workers renderer dropped for self-hosted Astro) - 0021–0028 carry Implementation status (2026-05-07) sections with PR references where shipped ADR README index: - 0007 row updated to reframed - 0019 noted as skipped/reserved - 0021–0028 annotated with v1 impl status - New v1 ADR set one-line summaries New file: docs/guides/block-library-authoring.md (~325 lines) - Required exports + widget vocabulary (ADR-0027) - optionsFromTokens binding (ADR-0025) - Versioning + migrations (ADR-0022) - Publishing cadence (ADR-0028) - Worked Pricing block example - Reference: @clearcms/blocks-marketing CONTENT-PROTOCOL polish: - "What's new in v1" section - Cross-link to author guide - Renderer name fix: clear-site → @clearcms/renderer Changeset: empty frontmatter — root docs, no package version bumps. Closes T-7.1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…(T-7.3+T-7.4 partial) (#146) Pre-stages T-7.3 (README v1 rewrite) + T-7.4 (STATE.md refresh) docs in parallel with Eng B's T-7.1 docs sweep (separate paths — they touch docs/adr/ + docs/CONTENT-PROTOCOL.md + docs/guides/, this PR touches README.md + docs/migration-v0.2-to-v1.0.md + STATE.md). - README.md — adds prominent "Two modes (v1, ADR-0021)" section near the top: explains headless vs site mode + points at the migration guide. Existing scaffolder/quick-start sections unchanged. - docs/migration-v0.2-to-v1.0.md (new) — TL;DR upgrade for headless installs (just bump deps + run doctor); spec/storage table of changes with backwards-compat notes; site-mode opt-in walkthrough (config flag → scaffold → sections layout → renderer); breaking- change list (rare; only matters for internal-import consumers); rollback notes. - STATE.md — refreshed package versions to match npm reality (#110) + reflect v1 phases shipped: admin 0.4.2 with v1.0.0 queued, create 0.4.0 with --theme=marketing, blocks-marketing 0.1.0 new, renderer 0.1.0 scaffold. Header line updated to v1.0 release prep status. T-7.3 (full): full clear-admin command reference + integration guides (Astro/React/SDK) + REST docs deferred to v1.0.x — covered by issue referencing #94. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tamp per PLAN override)
Captures the editor end-state target the v1.0 stamp won't quite hit: single topbar (no duplicate preview chrome), preview overlay instead of side-panel-with-its-own-toolbar, sections outline rail, collapsed-by- default metarail groups, mobile bottom-sheet pattern. ASCII mockups for: item editor (article mode), page editor (form mode), sections surface, preview overlay, mobile compact view. Refactor plan: 7 PRs ordered by operator-impact-per-effort, starting with topbar consolidation (#149) + metarail group collapse. This is the brief for v1.0.x → v1.1.0 editor work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
End-state UX brief for the v1 admin editor — the prototypes the v1.0 stamp won't quite hit, scoped as v1.x roadmap. Filed because the live test admin (sandbox @ :1999) surfaced the duplicate-preview-chrome regression (#149) plus a few related rough edges; the doc consolidates the target for the polish arc.
What's in
What v1.0.0 ships anyway
Functionally complete (sections / tokens / blocks / mode-gating / hosted SSO / migration runner). The chrome rough edges from this doc are visible but not blocking.
Coordination
Doc-only; doesn't conflict with the v1.0.0 stamp PR (#148). Targets `main` so it's discoverable post-stamp.
🤖 Generated with Claude Code