# Block Schema Consistency Audit

**Date:** 2026-04-29
**Plugin version at audit:** v4.7.0
**Methodology:** Programmatic extraction of every block registration across `class-blocks-v2.php`, `class-blocks-extra.php`, `class-blocks-wp7.php`, and `src/blocks/*/block.json`. Each block's attributes catalogued with name, type, default, and enum (if present). Cross-referenced for naming inconsistencies, missing enums on select-style attrs, and same-name-different-type conflicts.

> Triggered by John's call to harden AI-authoring reliability before rolling out v4.7.0 to the fleet. AI agents producing IM-Press output need a predictable, consistent attribute schema. This audit identifies where the schema currently confuses AI.

---

## Summary

| Metric | Count |
|---|---:|
| Blocks registered (across 4 registries) | **159** |
| Attribute declarations | **731** |
| Attrs with type | **694** (94%) ✓ |
| Attrs with default | **692** (94%) ✓ |
| Attrs with `enum` | **110** (15%) ⚠️ |
| Concept-level naming inconsistencies | **8** |
| Select-style attrs missing enum | **26** |
| Type conflicts (same name, different types) | **6** |

The big problem isn't the existence of attributes — it's that **AI submitting plausible-looking values to the 85% of attrs without enum gets accepted silently**, and the 8 concept inconsistencies mean AI must learn N different names for the same concept.

---

## Decisions made (2026-04-29 conversation)

Locked in for v5.0.0 cleanup release:

1. **Canonical namespace: `im-press/*`** — drop `imp/*` entirely. Brand recognition + SEO surface (CSS class names like `wp-block-im-press-heading`) + AI training-data continuity + identity coherence trump the brevity argument.
2. **No customer base yet** — free to rename aggressively without back-compat overhead.
3. **Design for long-term operability** — pick the right name, not the easy name.
4. **Avoid BB-distinctive naming** — no `fl_*` prefixes, no BB composite-field flags, no BB-specific terms in user-facing copy.
5. **Ship as v5.0.0** — semantic versioning honors the breaking changes.

---

## Inconsistency findings

### 1. Naming inconsistencies (same concept, multiple names)

Fix: pick one canonical name; deprecate the others; migrate via `Blocks_Attr_Migration` helper.

| Concept | Canonical | Deprecated aliases | Spread |
|---|---|---|---:|
| Heading text | `heading` | `title`, `text`, `label`, `headline` | 33 blocks split 5 ways |
| Background color | `backgroundColor` | `bgColor` | 20 blocks |
| Body / description | `description` | `text` (when not heading), `content` | 17 blocks |
| Text color | `textColor` | `color` (when general text) | 15 blocks |
| Image URL | `imageUrl` | `image`, `url`, `src`, `photo`, `photoSrc` | 12 blocks |
| Image attachment ID | `imageId` | `image` (when used as numeric) | (paired with above) |
| Icon color | `iconColor` | `color` (when icon-context) | 7 blocks |
| Icon name | `iconName` | `icon` (when string) | (see type-conflict #4) |
| Show/hide icon (toggle) | `showIcon` | `icon` (when boolean) | (see type-conflict #4) |
| Link URL | `linkUrl` | `url`, `link`, `href`, `buttonUrl` | 5+ blocks |

**Note on heading/title/text:** the dominant cause of confusion. Five different names for "the main text content of this block" across 33 blocks. Choosing `heading` because:
- Most-used (11 blocks already use it)
- Matches WordPress core's `core/heading` block content concept
- Self-documenting (a "title" might be a card title, page title, modal title, etc. — too generic)

### 2. Type conflicts (same attr name, different types)

Worse than naming because AI can't predict the type. Fix: rename one variant to differentiate.

| Attr | Conflicting types | Resolution |
|---|---|---|
| `columns` | `number` (count) vs `array` (config) in 8 blocks | Always integer (count). Array variants → rename to `items`. |
| `rows` | `number` vs `array` in 3 blocks | Same: integer-only; array variants → `items`. |
| `icon` | `boolean` (toggle) vs `string` (icon name) in 4 blocks | String only. Boolean variants → `showIcon`. |
| `separator` | `boolean` vs `string` in 3 blocks | Boolean → `enableSeparator` (already established by v4.7.0). String for style → `separatorStyle`. |
| `hoverColor` | `boolean` vs `string` in 3 blocks | String only (it's a color value). Boolean variants → `hoverEnabled` per-block. |
| `cardStyle` | `boolean` vs `string` in 3 blocks | String only (style name). Boolean variants → `enableCardStyle`. |

### 3. Select-style attrs missing `enum`

26 attrs across blocks that are clearly select-style by name (`align`, `position`, `layout`, `style`, `size`, `type`, `tag`, `shape`, `effect`, `direction`, `transition`) but have no `enum` array declared. AI submitting `{align: 'middle'}` (vs valid `'center'`) is accepted silently — the value passes through unverified.

Full list saved in `docs/research/data/block-schema-audit-findings.json`. Fix priority is high — these are the silent-failure path for AI.

Examples:

| Block | Attr | Default | Likely valid values |
|---|---|---|---|
| `im-press/hero` | `alignment` | `'center'` | `'left'`, `'center'`, `'right'` |
| `im-press/heading` | `tag` | `'h2'` | `'h1'`, `'h2'`, ... `'h6'` |
| `im-press/heading` | `alignment` | `'center'` | `'left'`, `'center'`, `'right'` |
| `im-press/dual-heading` | `subtextSize` | `'18px'` | (free-form CSS size — keep as text, not enum) |
| `im-press/features-grid` | `hoverEffect` | `'lift'` | universal hover-effect enum from Block_Animations |
| `im-press/post-grid` | `postType` | `'post'` | dynamic — list of registered post types at runtime |
| `im-press/star-rating` | `size` | `'24px'` | (free-form size or `'small'/'medium'/'large'`?) |

Each attr decision is small but has to be made deliberately. Some are genuinely free-form (CSS sizes), some are missing finite enums.

### 4. Coverage gaps in remaining attrs

| Gap | Count | Risk |
|---|---:|---|
| `enum` missing on `string`-typed attrs that look enum-shaped | 26 | AI submits invalid values, accepted silently |
| `default` missing | 39 | Block renders unpredictably when AI omits the attr |
| `type` missing | 37 | WP can't validate the value at all |

The 37 missing-type attrs are the most concerning because WP's REST API can't validate the input — anything passes. Worth fixing in v5.0.0.

---

## Fix list (priority-ranked)

### P0 — must fix in v5.0.0 (breaking changes already on the table)

1. **Namespace consolidation:** all blocks → `im-press/*`. Drop `imp/*` registrations.
2. **Type conflicts:** rename the ambiguous attrs (`columns→items`, `icon→showIcon` for boolean, etc.) per the table in §2.
3. **Concept canonicalization:** rename per §1 — `heading`/`backgroundColor`/`description`/`textColor`/`imageUrl`/`iconColor`/`iconName`/`showIcon`/`linkUrl`.
4. **Build `Blocks_Attr_Migration` helper class** so legacy block markup on existing pages keeps rendering during/after migration.
5. **Build one-time fleet migration script** that rewrites stored block markup on John's sites: `<!-- wp:imp/X -->` → `<!-- wp:im-press/X -->` plus attr-name canonicalization.

### P1 — high-leverage, ship in v5.0.0 alongside

6. **Add `enum` arrays to the 26 select-style attrs missing them.** Each is a 1-2 line schema patch. Without these, AI's invalid values pass silently.
7. **Add `type` declarations to the 37 attrs missing them.** Each is a 1-2 line schema patch.
8. **Add `default` declarations to the 39 attrs missing them.** Each is a 1-2 line schema patch.

### P2 — solidify in v5.x patches after v5.0.0 ships

9. **Validation feedback endpoint** (`POST /im-press/v1/validate-page`) — returns issue list when AI submits block markup. Closes the AI feedback loop.
10. **Error message audit + rewrite** for top 10 REST endpoints — ensure every error tells AI what went wrong AND how to fix it.
11. **Per-block migration metadata** — formalize a `migrate_attributes()` static method convention on every block class so future schema changes have a structured path. Pattern borrowed from BB's `filter_settings()`.

---

## What ships in v5.0.0

The first four P0 items + as many P1 items as fit a clean release window. P2 items become v5.0.x patches.

| Phase | Work | Days |
|---|---|---:|
| 1 | Audit doc (this) + raw catalog | 0.5 |
| 2 | `Blocks_Attr_Migration` helper class | 1 |
| 3 | Mass rename — all 159 blocks updated to canonical attrs | 2-3 |
| 4 | Namespace consolidation — drop `imp/*` registrations, build fleet-page migration script | 1-2 |
| 5 | Add missing enums + types + defaults (P1) | 1 |
| 6 | Skill v0.1 spec doc + REST endpoints | 1 |
| 7 | Bootstrap wiring + version bump to v5.0.0 + CHANGELOG + smoke test | 0.5 |
| **Total** | | **~7-9 focused days** |

Realistically: 1.5-2 weeks if delivered polished. Could compress if the rename pass goes clean.

---

## Out of scope for v5.0.0

The following showed up in the audit but are deferred to v5.x or beyond:

- Block-level migration metadata convention (P2 item 11)
- Validation feedback endpoint (P2 item 9)
- Error message rewrites (P2 item 10)
- Per-block edit components (the BB-style 3-tab UI from `bb-implementation-notes.md`) — opinion change later
- Composite field types (link with target+rel+download in one control) — UX polish
- Block consolidation between v1 (React-edit) and v2 (PHP-render) — separate scope decision

---

## Source data

Raw catalog + findings JSON saved at:

- `/tmp/imp-audit/block-catalog.json` — every block + every attr in machine-readable form
- `/tmp/imp-audit/findings.json` — structured findings for re-running analysis or generating other outputs
- This doc — human-readable summary with decisions

After the audit closes, raw data files move to `docs/research/data/` for repo persistence. Re-running the extractor against future plugin versions is a 30-second sanity check that schema consistency hasn't regressed.
