{"id":10443,"plugin_id":"plugin_asdk_app_69fe16bb7a048191af54574d5986aab2","kind":"skill","collection_source":null,"comparison_source":null,"observed_at":"2026-09-30T22:57:11.958Z","digest":"4f8d9f6b32b12f26d6ff80190114b57719401200e55acbdc7ac20ff233b7ed3d","against":null,"payload":{"name":"visual-editor-audit","description":"Audit an existing BrightSite site for visual-editor editability — walk every page, component, and layout and flag what a non-technical user cannot edit (no data-bs-edit markers, components with empty props_schema, bare loops for repeating content, hardcoded internal links), then optionally fix it. Use when the user says \"check what's editable,\" \"the client can't edit X in the editor,\" \"why can't I edit this,\" \"audit the site for the visual editor,\" \"make the existing site editable,\" \"fix editability,\" or wants a pre-handoff QA pass on whether the site is editable. For authoring new content correctly the first time, use the visual-editor-authoring skill instead.","included_files":[{"relative_path":"editability-contract.md","size_in_bytes":34226}],"skill_md_contents":"---\nname: visual-editor-audit\ndescription: Audit an existing BrightSite site for visual-editor editability — walk every page, component, and layout and flag what a non-technical user cannot edit (no data-bs-edit markers, components with empty props_schema, bare loops for repeating content, hardcoded internal links), then optionally fix it. Use when the user says \"check what's editable,\" \"the client can't edit X in the editor,\" \"why can't I edit this,\" \"audit the site for the visual editor,\" \"make the existing site editable,\" \"fix editability,\" or wants a pre-handoff QA pass on whether the site is editable. For authoring new content correctly the first time, use the visual-editor-authoring skill instead.\n---\n\n# Visual Editor Editability Audit\n\nWalk every page, component, and layout on a BrightSite site and report what is **not\neditable in the visual editor** — the things a non-technical client can't click and change\n— then optionally fix them. This is the reactive counterpart to `visual-editor-authoring`\n(which prevents the problem at create time).\n\nThe rules this audit enforces live in\n**[editability-contract.md](editability-contract.md)** (in this skill's directory).\nRead that file first; it is the source of truth for every check below.\n\n## When to use this\n\n- A client says they can't edit something in the visual editor.\n- Pre-handoff QA: confirm the site is actually self-editable before giving it to the client.\n- After a site was built quickly (or by another tool) and you suspect it's full of raw HTML.\n- After running `visual-editor-authoring` on a large build, as a verification pass.\n\nIf you're creating new content, don't audit after — author it right the first time with the\n`visual-editor-authoring` skill.\n\n## Inputs you need from the user\n\n1. **Account ID** — the BrightSite account to audit.\n2. **Scope** — pages, components, layouts, or all. Default: all.\n3. **Fix mode** — report only, or report then offer to fix? Default: report, then offer.\n\n## The contract in one sentence\n\n> The editor sees exactly two editable things: elements tagged `data-bs-edit=\"field\"`, and\n> `component()` instances whose component has a **non-empty `props_schema`**. Everything\n> else is invisible.\n\nEvery check below is a way the authored content fails that rule.\n\n## Workflow\n\n### Step 1: Pull the inventory\n\n- `mcp__brightsite__list_pages`\n- `mcp__brightsite__list_components`\n- `mcp__brightsite__list_layouts`\n\nReport the counts before you start. If it's a large site (>150 entities), audit in batches\n(≤5 concurrent) and tell the user you're throttling.\n\n### Step 2: Fetch and evaluate each entity\n\nFor each entity, fetch with `get_page` / `get_component` / `get_layout`. Pages and layouts\nreturn both live (`heex`) and staged (`heex_staged`) bodies — **audit both**, because a\nstaged body goes live on the next publish. Components return their `heex` and `props_schema`.\n\nEvaluate against these checks:\n\n**Critical** (the user genuinely cannot edit this content)\n\n- **Page/layout has zero editable nodes** — no `data-bs-edit` anywhere AND no embedded\n  `component(...)`. The whole body is one opaque \"Page Content\" block.\n- **Component has an empty `props_schema` (`{}`)** but its `heex` contains content/props.\n  The editor shows \"no editable properties.\" (Caused by `create_component` without the\n  follow-up `update_component`.)\n- **Repeating content rendered with a bare `:for`** (a `:for` with no surrounding\n  `data-bs-collection`/`data-bs-item` and not driven by a component `item_schema`). Items\n  can't be added, removed, reordered, or individually edited.\n- **A collection prop's rendered loop has no `data-bs-collection`/`data-bs-item`/\n  `data-bs-edit` markers** — even when the component's `props_schema` has a populated\n  `item_schema`. Symptom: the panel shows empty placeholder fields for each item, and the\n  cards on the canvas don't hover-highlight or click-select. The `item_schema` makes the prop\n  appear; the rendered markers make it editable. Fix: add the three markers to the loop in the\n  component HEEx (`data-bs-collection=\"<prop>\"`, `data-bs-item={idx}`, `data-bs-edit=\"<field>\"`).\n- **An editable prop passed as an inline literal** in a `component(...)` call — e.g.\n  `component(\"related\", %{cards: [%{title: …, href: \"/team\"}]})`. The props panel reads the\n  component instance, not the literal, so those fields show as **empty defaults** and the\n  user can't edit the real per-page values. Fix: move the data into the page's `params_schema`\n  as a collection and bind it (`%{cards: @params.related_cards}`).\n\n**Warning** (editable, but the editing experience is broken or fragile)\n\n- **Internal links hardcoded** as `href=\"/slug\"` instead of `href={page_url(\"id\")}` — no\n  page picker, breaks on slug change. Flag missing `data-bs-edit-type=\"page\"` on internal\n  `<a>` tags too.\n- **`data-bs-edit-type=\"text\"` on an element containing `<br>` or child tags** — the first\n  edit will flatten it. Should be `richtext`.\n- **Tailwind classes inside a richtext field** (`class=\"…\"` on a `<span>`/child within a\n  `data-bs-edit-type=\"richtext\"` element) — dropped on save; should be inline `style=\"…\"`.\n- **`<a>` with an icon/child markup but no `data-bs-edit-target`** on the text child —\n  editing the label destroys the icon.\n- **Component props with no `order` key** — fields list in arbitrary order in the panel.\n- **`type:\"page\"` component prop whose default is a path** (`/contact`) not a page ID — the\n  picker shows \"— Select a page —\".\n- **A reusable component takes a page-link/CTA prop but renders `href={page_url(@x)}`\n  directly** (no `resolve` helper) — breaks if the prop holds a literal path/anchor/`tel:`\n  instead of a page ID. Suggest the smart `resolve` helper (page ID → `page_url`, else raw).\n- **`:if` guard or `||` fallback that relies on Elixir truthiness with possibly-empty\n  strings** (`:if={@label}`, `\"\" || @fallback`) — `\"\"` is truthy, so an empty-label CTA still\n  renders and the fallback never fires. Should test `@label && @label != \"\"`.\n\n**Info** (nice to improve)\n\n- Long page with many `data-bs-edit` fields but no `data-bs-section` grouping — the element\n  tree is a flat wall; suggest wrapping logical sections in `data-bs-section=\"Label\"`.\n- A section's markup is duplicated across multiple pages as raw HTML — candidate to extract\n  into a reusable component.\n- **Inline `data-bs-collection` without `data-bs-item-schema`** — it works, but only gets the\n  basic one-item-at-a-time editor. If the items have a clear shape, suggest adding\n  `data-bs-item-schema='{…}'` for the rich (collapsible / drag-reorder / typed) editor.\n- **Ordered-list numbers stored as an editable field** — an item field like `num`/`number`\n  (\"01\", \"02\") that's really just the position. Suggest rendering it with `bs_index(idx)` (or\n  `bs_index(idx, pad: 2)`) and dropping it from the data, so it auto-renumbers on reorder.\n- **A CTA authored as two separate fields** (a `text` field + a `page` link side by side, or\n  a `type:\"page\"` link whose label is a separate prop) — suggest a single\n  `data-bs-edit-type=\"button\"` (page) or a `type:\"button\"` component prop so label + link\n  edit as one grouped button.\n\n### Step 3: Output the report\n\nMarkdown table grouped by severity, then by entity type. For each finding include:\n\n- Severity marker — `[!]` critical, `[~]` warning, `[i]` info (no emoji unless asked).\n- Entity type + title, and whether the issue is in the live or staged body.\n- The specific defect and the contract rule it violates.\n- The concrete fix (the marker/attribute to add, or the two-call sequence to run).\n- The entity ID, so the user can jump to it.\n\nEnd with a summary block:\n\n```\nEditability audit\n-----------------\nPages audited:      14   (3 not editable)\nComponents audited:  9   (2 with empty props_schema)\nLayouts audited:     2\n\nCritical: 5\nWarnings: 11\nInfo: 4\n\nTop fixes by impact:\n1. 3 pages have zero editable nodes — add data-bs-edit / componentize: [list]\n2. 2 components have empty props_schema — run update_component: [list]\n3. Gallery page uses a bare :for — convert to a data-bs-collection: [id]\n```\n\n### Step 4: Offer to fix\n\nAsk before changing anything:\n\n> Want me to fix the critical issues? I'll add `data-bs-edit` markers, set `props_schema`\n> on the empty components, and convert bare loops to editable collections. I'll show each\n> change as a diff and confirm before saving.\n\nIf yes, apply fixes with `update_page` / `update_component` / `update_layout`, following the\nauthoring contract. Show each change as a diff (`old → new`) and get approval per entity.\nKey reminders while fixing:\n\n- Setting a component's `props_schema` is an `update_component` call — `create_component`\n  can't do it.\n- Content edits to pages/layouts go to the **staged** body; tell the user they must publish\n  (`publish_page` / `publish_layout`) to make the now-editable version live.\n- When you add `data-bs-edit` markers, preserve the existing rendered output — markers\n  change editability, not appearance.\n\n## Anti-patterns to avoid\n\n- **Don't auto-fix without per-entity approval.** Rewriting a page's HEEx to add markers\n  can subtly change rendering if done carelessly. Show the diff.\n- **Don't only audit the live body.** A clean live page with a non-editable staged body\n  ships the problem on the next publish. Check both.\n- **Don't flag styled-but-static decorative elements as \"not editable.\"** A background\n  flourish the client never needs to edit isn't a defect. Focus on content: headings,\n  body copy, images, links, CTAs, repeating items.\n- **Don't report a component \"fixed\" after `create_component`-style edits alone** — verify\n  the `props_schema` is non-empty after the `update_component`.\n- **Don't fetch hundreds of entities in parallel.** Throttle to ~5 concurrent.\n\n## Example invocation\n\n> Audit account `YOUR_ACCOUNT_ID` for visual-editor editability — pages, components, and\n> layouts. Tell me what the client can't edit, then fix the critical stuff.\n\n## Tools used\n\n- `mcp__brightsite__list_pages` / `mcp__brightsite__list_components` / `mcp__brightsite__list_layouts`\n- `mcp__brightsite__get_page` / `mcp__brightsite__get_component` / `mcp__brightsite__get_layout`\n- `mcp__brightsite__update_page` / `mcp__brightsite__update_component` / `mcp__brightsite__update_layout` (only with explicit per-entity approval)\n- `mcp__brightsite__publish_page` / `mcp__brightsite__publish_layout` (to make staged fixes live, with approval)\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}