Documentation source
View/Block System Cleanup (v3)
Unified workspace editor, response-integrated field blocks, container removal, block config schemas, and consistent editing experience across admin and entity detail pages.
## Problem
The view/block system has grown organically and is now scattered across multiple disconnected subsystems:
1. **The default entity detail view is hardcoded** — bento layout, notes, responses, container children, sidebar are all hardcoded JSX, not driven by the view/block system. Custom views exist as separate tabs alongside this hardcoded default, creating two completely separate codepaths.
2. **The editor is broken and inconsistent** — admin has a basic flat-block editor that doesn't support workspace layouts. The detail page has a ViewEditorSheet that overflows, doesn't scroll, and can't configure blocks. Script-created views can't be edited because the config shape isn't validated. Two different editor UIs, neither works well.
3. **Responses are not integrated with views** — the ResponsesTab is hardcoded on the default view. The response-form block exists but is standalone. There's no "view as form" pattern where field blocks become editable inputs that create response submissions with provenance tracking.
4. **Container is a confusing separate concept** — a toggle on entity types that hardcodes a ContainerChildrenSection on the detail page. Containers are just entities with connections — this should be emergent from adding connection-list/child-entity-list blocks to views, not an explicit setting.
5. **Block types lack configuration** — most blocks have no config UI. Clicking settings does nothing. No typed schemas for block configs, so validation and migration are impossible.
## Solution
### Mental Model
Three layers, clearly separated:
| Layer | What it is | Editable | Stored |
|-------|-----------|----------|--------|
| **Default view** | Auto-generated bento showing all entity data | No | Not in views table — rendered from schema |
| **Workspace views** | Block-driven layouts designed per entity type | Yes, via workspace editor | `views` table, `page_type: "workspace"` |
| **Entity overrides** | Fork of a workspace view customized for one entity | Yes, via same editor | `views` table with `entity_id` set |
The default view is the safety net — always available, shows everything. When workspace views exist, they're shown by default. The default view is accessible via "View All Properties" in the entity dropdown or as a fallback when no custom views exist.
### Two Edit Modes
The workspace editor supports two distinct modes with clear visual separation:
**Layout mode** — rearranging, adding, removing, resizing, and configuring blocks. This changes the view structure. Triggered by "Customize Layout" in the dropdown. Saves affect the view record.
**Response mode** — filling in field values using the view as a form. This creates response submissions. Triggered by "Edit Values" or a pencil icon on the view. Saves create an `entity_response` record following the OG amble pattern.
These are separate toolbar states. Layout mode shows block handles and the config panel. Response mode shows editable inputs on field blocks and a "Save Response" button. A user cannot be in both modes simultaneously.
### Response Mode Details
When a user enters response mode on a workspace view:
1. Field-displaying blocks (field-card, data-table) switch to editable inputs
2. Inputs show the current value pre-filled, with response history indicators (who set this, when, confidence)
3. User edits values across the view — it looks like a form but follows the view's designed layout
4. **"Save Response" creates an `entity_response` submission** — not a direct entity update
5. The submission uses the entity type's **default criteria set** (auto-synced from schema via `syncDefaultCriteriaSet()`). If no default criteria set exists, one is created on-demand from the entity type schema
6. The response record is populated as:
- `criteria_set_id`: the default criteria set ID
- `criteria_snapshot`: snapshot of the criteria set dimensions at submission time
- `values`: `{ dimensionKey: newValue, ... }` for all edited fields (dimension keys map to field names via `CriteriaSetDimension.fieldName`)
- `field_meta`: `{ fieldName: { confidence: "high" } }` for each edited field (user-provided values default to high confidence)
- `source`: `"manual"`
- `submitted_by_user`: current user ID
- `weighted_score` / `normalized_score`: computed from numeric dimension values and weights (same as existing `submitEntityResponse()` logic)
- `status`: `"submitted"` (or `"promoted"` if auto-promotion is enabled)
7. Auto-promotion is controlled by a new `promotion_mode` field on `criteria_sets` (see Migration section). When `promotion_mode = "auto"` (the column default), response values are immediately promoted to `entity.content` via the existing `promote_field_value` RPC. When `promotion_mode = "manual"`, values appear as pending responses needing explicit promotion
8. View returns to display mode showing updated current values
This integrates with the existing response system — `FieldResponseIndicator`, `promote_field_value` RPC, `entity.metadata.field_sources` all work as-is.
## Design
### Architecture
#### 1. Workspace Editor Component
One `WorkspaceEditor` component replaces both the existing `visual-editor/` subsystem and the old `ViewEditor`/`ViewEditorSheet`. It is used in both admin and entity detail pages.
The existing `features/views/components/visual-editor/` directory (EditModeProvider, EditableBlockGrid, SortableBlockWrapper, BlockPalette, BlockConfigPopover, EditModeToolbar) is **replaced** by the new workspace-editor. Useful patterns from the existing code (dnd-kit integration, edit mode context) are carried forward, but the components are rebuilt for the new architecture.
```
features/views/components/workspace-editor/
index.tsx — main editor component (layout mode)
editor-canvas.tsx — block grid with dnd-kit drag/drop (replaces EditableBlockGrid)
block-palette.tsx — categorized, scrollable block picker (replaces visual-editor/block-palette.tsx)
block-config-panel.tsx — right sidebar for selected block config (replaces BlockConfigPopover)
editor-toolbar.tsx — layout preset, add block, save/cancel (replaces EditModeToolbar)
sortable-block.tsx — draggable block wrapper with resize handles (replaces SortableBlockWrapper)
response-mode.tsx — response editing overlay (new)
```
**Canvas**: shows blocks in their layout positions (matching actual view rendering). Blocks are draggable, resizable (full/half/third), removable. Click to select and open config panel.
**Block palette**: opens from "Add Block" button. Categorized and scrollable (fixing the current overflow bug):
- **Data**: field-card, data-table, entity-filter, stat-cards
- **Collections**: connection-list, child-entity-list
- **Charts**: chart, radar, bubble-chart, ranking
- **Content**: rich-text, summary, status-banner, activity, notes (new block type)
- **Evaluation**: response-form
- **Advanced**: kanban, form-flow, canvas, entity-graph
Block types not shown in the palette (used programmatically or rarely): entity-card, entity-feed, custom, image, video, excalidraw, react-flow, menu, pdf-viewer, table. These still have config schemas but aren't offered in the visual palette.
**Config panel**: slides open on the right when a block is selected. Renders a typed config form based on block type. Every block has at minimum: label, size. Block-specific fields are driven by Zod schemas.
**Save behavior**:
- Admin: saves directly to view record (entity-type template)
- Detail page (no entity override): "Save" = fork-on-write, "Save as Default" = update template
- Detail page (entity override exists): "Save" = update override, "Save as Default" = update template
#### 2. New Block Types
Two new block types are added to `BLOCK_TYPES` in `features/blocks/types.ts`:
| New Type | Purpose | Config |
|----------|---------|--------|
| `notes` | Rich text notes editor for entity.description | `{ placeholder?: string }` |
| `documents` | Document list with upload capability | `{ showUpload?: boolean, filterType?: string }` |
These are new entries in the `BLOCK_TYPES` array and require corresponding renderer components in `features/blocks/components/`.
Existing block types that already cover needed functionality (no new types needed):
- `connection-list` — already exists, gets config for relation type filter and display mode
- `child-entity-list` — already exists, gets config for child type filter and display mode
- `response-form` — already exists, gets config for criteria set selector
- `field-card` — already exists, gets config for field selector and display mode
- `stat-cards` — already exists, can show entity metadata
#### 3. Block Config Schemas
Every block type gets a typed Zod config schema in `features/blocks/config-schemas.ts`. Config schemas are defined for all 29 existing block types plus the 2 new ones.
```typescript
// BlockConfig shape (unchanged, but config is now validated per type)
interface BlockConfig {
id: string // auto-generated if missing
type: BlockType // required
label?: string // display name
size?: "full" | "half" | "third"
config: Record<string, unknown> // validated per block type via Zod
}
```
**Priority block configs** (these get full config panel UIs):
| Block Type | Config Fields |
|-----------|--------------|
| `field-card` | `fields: string[]`, `displayMode: "value" \| "card" \| "inline"` |
| `connection-list` | `relationTypes?: string[]`, `entityTypes?: string[]`, `display: "list" \| "grid" \| "compact"` |
| `child-entity-list` | `childTypes?: string[]`, `display: "list" \| "table" \| "grid"`, `sortBy?: string`, `sortOrder?: "asc" \| "desc"` |
| `response-form` | `criteriaSetId?: string` (defaults to entity type's default) |
| `notes` | `placeholder?: string` — bound to `entity.description`, direct entity update (intentional exception to response model; notes are freeform, not structured data) |
| `documents` | `showUpload?: boolean`, `filterType?: string` |
| `chart` | `chartType: "bar" \| "pie" \| "line" \| "area"`, `dataSourceField?: string`, `colorThresholds?: object` |
| `data-table` | `columns?: string[]`, `sortConfig?: object` |
| `rich-text` | `content?: string` |
| `stat-cards` | `stats?: StatConfig[]` |
| `radar` | `dimensions?: string[]`, `showSliders?: boolean` |
| `ranking` | `field?: string`, `limit?: number` |
**Remaining block types** (kanban, summary, entity-card, entity-feed, status-banner, activity, custom, image, video, excalidraw, react-flow, bubble-chart, entity-graph, form-flow, canvas, menu, pdf-viewer, entity-filter, table) get a **passthrough schema** — `z.record(z.string(), z.unknown())` — so they validate without blocking. Config panel UIs for these are deferred; the panel shows a JSON editor fallback.
The editor validates and normalizes on load — missing IDs are generated, configs are validated against their type's schema, and a warning badge is shown on blocks that had to be normalized. This fixes the current bug where script-created views can't be edited.
#### 4. Block Data Sources
Each block config explicitly declares where its content comes from:
| Data Source | Blocks | Reads from |
|------------|--------|-----------|
| Entity fields | field-card, data-table | `entity.content[field]` |
| Entity connections | connection-list, child-entity-list | `entity_relations` table |
| Entity responses | response-form, field-card (response overlay) | `entity_responses` table |
| Entity metadata | stat-cards, status-banner | `entity.metadata` |
| Static/configured | rich-text, notes | `config.content` or `entity.description` |
| Computed | chart, radar, ranking | Aggregation query |
#### 5. Removals
| What | Action | Reason |
|------|--------|--------|
| Container tab (admin) | Remove from entity-type-admin-client.tsx | Container is emergent from connections |
| `ContainerConfig` type | Remove from features/entities/types.ts | No longer needed |
| `isContainerType()` helper | Remove from features/entities/types.ts | No longer needed |
| `getAllowedChildTypes()` helper | Remove from features/entities/types.ts | No longer needed |
| `ContainerChildrenSection` | Delete features/entities/components/container-children-section.tsx | Replaced by child-entity-list and connection-list blocks |
| `ViewEditorSheet` | Delete features/views/components/view-editor-sheet.tsx | Replaced by workspace editor |
| Old `ViewEditor` | Delete features/views/components/view-editor.tsx | Replaced by workspace editor |
| `WorkspaceViewEditor` | Delete features/views/components/workspace-view-editor.tsx | Replaced by workspace editor |
| `visual-editor/` directory | Delete features/views/components/visual-editor/ (all files) | Replaced by workspace-editor/ |
| Container admin tool | Remove `manageContainerConfig` from features/tools/admin-tools.ts | No longer needed |
| CLAUDE.md reference | Remove manageContainerConfig from admin tools list, reconcile tool count with actual tools in admin-tools.ts | Keep docs accurate |
#### 6. Admin Entity Type Tabs
Simplified from 5 tabs to 4:
| Tab | Content |
|-----|---------|
| **Overview** | Name, slug, description, icon, color |
| **Fields** | Schema editor + field config (extraction, display, connection settings) |
| **Criteria Sets** | CriteriaSetManager (unchanged) |
| **Views** | Rebuilt — uses WorkspaceEditor for full workspace view design |
#### 7. Entity Detail Page Flow
**No custom views exist:**
- Default bento view shown (unchanged)
- "Customize Layout" in dropdown opens workspace editor to create first view
- Save scope based on permissions
**Custom views exist:**
- First workspace view shown by default
- View tabs if multiple views exist
- "View All Properties" in dropdown shows default bento
- "Customize Layout" enters layout mode on current view (rearrange blocks)
- "Edit Values" enters response mode on current view (fill in field values as a response)
**Entity detail hero/dropdown changes:**
- `more-actions-dropdown.tsx`: "Customize Layout" always available (creates first view or enters layout mode). "Edit Values" available when a workspace view is active. "View All Properties" shows default bento.
- `entity-detail-hero.tsx`: shows mode indicator when in layout or response mode
#### 8. Migration
A new migration adds `promotion_mode` to `criteria_sets`:
```sql
ALTER TABLE criteria_sets
ADD COLUMN promotion_mode text NOT NULL DEFAULT 'auto'
CHECK (promotion_mode IN ('auto', 'manual'));
COMMENT ON COLUMN criteria_sets.promotion_mode IS
'Controls whether response values are auto-promoted to entity.content on submission';
```
Default is `"auto"` so that the existing behavior (edit → save → immediately reflected) works out of the box. Teams that want review workflows can set individual criteria sets to `"manual"`.
**Container config data migration**: No data migration needed. Existing entity types with `config.container.enabled = true` simply lose the container UI toggle. Their actual connections remain in the `entity_relations` table and are unaffected. The existing `child-entity-list` blocks in any views they have continue to work. For entity types that relied on the auto-generated `ContainerChildrenSection` on the default bento view, the bento already shows connections — no data is lost.
## Trade-offs
### Chosen: Separate default view from workspace views
- Pro: Safety net always shows all data; custom views can be specialized without data loss risk
- Con: Two rendering paths to maintain
- Alternative considered: Make default view a stored view record. Rejected because the default must always reflect the current schema, and stored views can diverge.
### Chosen: Response-based editing (not direct entity update)
- Pro: Full provenance, append-only history, multi-actor collaboration, matches OG amble design
- Con: Extra step for simple edits (submit response then promote)
- Mitigation: Auto-promotion mode on criteria sets (`promotion_mode: "auto"` default) eliminates the extra step for simple cases
### Chosen: Remove Container as explicit concept
- Pro: Simpler admin, fewer concepts to learn, connections are already the mechanism
- Con: Loses the explicit "this type is a container" signal for non-view contexts
- Mitigation: Any entity type that has connections with child-entity-list or connection-list blocks in its views implicitly acts as a container
### Chosen: Single editor component for both admin and detail
- Pro: Consistent experience, one codebase to maintain
- Con: Editor must handle both contexts (type-level vs entity-level saves)
- Mitigation: Save scope is handled by the parent page passing appropriate callbacks
### Chosen: Replace existing visual-editor rather than extend
- Pro: Clean architecture, existing visual-editor has debt (broken config popover, overflow issues)
- Con: Throwaway existing code
- Mitigation: Carry forward useful patterns (dnd-kit integration, edit mode context concept)
## Acceptance Criteria
### Editor
- [ ] Workspace editor renders in admin Views tab with full block editing
- [ ] Same workspace editor renders on entity detail pages
- [ ] Block palette scrolls properly and doesn't overflow
- [ ] Priority block types (12 listed above) have typed config panels
- [ ] Remaining block types show JSON editor fallback in config panel
- [ ] Blocks can be dragged to reorder, resized (full/half/third), and removed
- [ ] Script-created and tool-created views are editable (config normalization on load)
- [ ] Save on detail page: "Save" = entity override, "Save as Default" = type template
- [ ] Save in admin: saves to type-level view
### Response Integration
- [ ] Response mode on a workspace view turns field blocks into editable inputs
- [ ] Saving in response mode creates an entity_response submission using default criteria set
- [ ] Response record includes criteria_set_id, criteria_snapshot, field_meta, computed scores
- [ ] Field blocks show provenance badges (who set the value, when)
- [ ] Auto-promotion works when criteria set promotion_mode is "auto"
- [ ] Manual promotion flow works via existing FieldResponseIndicator
- [ ] Response history visible on field blocks via existing indicators
### New Block Types
- [ ] `notes` block renders NotesEditor with configurable placeholder
- [ ] `documents` block renders document list with optional upload
- [ ] Both new types available in block palette
### Removals
- [ ] Container tab removed from entity type admin
- [ ] ContainerConfig, isContainerType, getAllowedChildTypes deleted from types
- [ ] ContainerChildrenSection deleted
- [ ] manageContainerConfig admin tool removed
- [ ] CLAUDE.md updated (admin tool count, manageContainerConfig reference removed)
- [ ] ViewEditorSheet, old ViewEditor, WorkspaceViewEditor deleted
- [ ] visual-editor/ directory deleted (replaced by workspace-editor/)
- [ ] Related test files updated (entities/types.test.ts, admin-tools.test.ts)
### Block Configs
- [ ] Zod schemas exist for every block type's config (typed for priority, passthrough for rest)
- [ ] Config panels render typed forms for priority block types
- [ ] Config validation catches invalid shapes and normalizes on load
- [ ] connection-list block has relation type filter and display mode config
- [ ] child-entity-list block has child type filter and display mode config
- [ ] field-card block has field selector and display mode config
- [ ] response-form block has criteria set selector
### Entity Detail
- [ ] When no custom views: default bento shown, "Customize Layout" creates first view
- [ ] When custom views exist: first view shown by default
- [ ] "View All Properties" shows default bento
- [ ] View tabs shown when multiple views exist
- [ ] Layout mode and response mode are visually distinct and mutually exclusive
### Migration
- [ ] `promotion_mode` column added to `criteria_sets` with default "auto"
- [ ] Existing criteria sets get promotion_mode = "auto"
## Files
### New files
- `features/views/components/workspace-editor/index.tsx`
- `features/views/components/workspace-editor/editor-canvas.tsx`
- `features/views/components/workspace-editor/block-palette.tsx`
- `features/views/components/workspace-editor/block-config-panel.tsx`
- `features/views/components/workspace-editor/editor-toolbar.tsx`
- `features/views/components/workspace-editor/sortable-block.tsx`
- `features/views/components/workspace-editor/response-mode.tsx`
- `features/blocks/config-schemas.ts`
- `features/blocks/config-schemas.test.ts`
- `features/blocks/components/notes-block.tsx` (new block type)
- `features/blocks/components/documents-block.tsx` (new block type)
- `features/blocks/components/block-config-panels/` (per-block-type config UIs)
- `supabase/migrations/YYYYMMDD_NNN_criteria_sets_promotion_mode.sql`
### Modified files
- `features/blocks/types.ts` — add `notes` and `documents` to BLOCK_TYPES, typed config interfaces
- `features/views/types.ts` — editor-related types
- `features/views/hooks/use-view-editor.ts` — response-mode save logic, integration with submitEntityResponse
- `features/views/server/actions.ts` — normalize/validate on load
- `features/blocks/components/field-card-block.tsx` — edit mode for response input
- `features/blocks/components/block-renderer.tsx` — config normalization, new block type rendering
- `features/responses/types.ts` — add promotion_mode to CriteriaSetRecord
- `features/entities/types.ts` — remove ContainerConfig, isContainerType, getAllowedChildTypes
- `features/entities/types.test.ts` — remove container-related tests
- `features/entities/components/more-actions-dropdown.tsx` — add "Edit Values", update "Customize Layout" behavior, add "View All Properties"
- `features/entities/components/entity-detail-hero.tsx` — mode indicator for layout/response editing
- `features/tools/admin-tools.ts` — remove manageContainerConfig tool
- `features/tools/admin-tools.test.ts` — remove manageContainerConfig tests
- `app/(app)/admin/entity-types/[slug]/entity-type-admin-client.tsx` — remove Container tab
- `app/(app)/admin/entity-types/[slug]/views-tab.tsx` — use workspace editor
- `app/(app)/[typeSlug]/[id]/entity-detail-client.tsx` — clean up view switching, remove container logic, add response mode
- `app/(app)/[typeSlug]/[id]/page.tsx` — pass additional props for response mode
- `CLAUDE.md` — update admin tool count, remove manageContainerConfig reference
### Deleted files
- `features/views/components/view-editor-sheet.tsx`
- `features/views/components/view-editor.tsx`
- `features/views/components/workspace-view-editor.tsx`
- `features/views/components/visual-editor/` (entire directory)
- `features/entities/components/container-children-section.tsx`
## Relationship to v2 Spec
This spec supersedes `view-block-system-v2.mdx`. Status of v2 items:
| v2 Item | Status | v3 Treatment |
|---------|--------|-------------|
| UnifiedViewRenderer | Done | Kept, modified for response mode |
| Standalone `/view/[id]` route | Done | Kept as-is |
| ViewTabBar | Done | Kept, minor updates |
| useViewRealtime hook | Done | Kept as-is |
| Per-block Suspense | Done | Kept as-is |
| View templates | Not started | Deferred — not in v3 scope |
| Copy/fork API routes | Partially done | fork-on-write is done, copy deferred |
| parent_view_id migration | Done | Kept as-is |
| ViewEditor refactor | Not started | Replaced by workspace-editor in v3 |
| Typed block configs | Not started | Core deliverable of v3 |
The v2 spec should be updated to `status: completed` for done items and `status: superseded` overall, with a note pointing to v3.