Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
},
"metadata": {
"description": "Claude skills for Dynamicweb 10 — organized by task domain, bundled by role.",
"version": "4.7.0"
"version": "4.7.1"
},
"plugins": [
{
Expand Down
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,40 @@
All notable changes to the Dynamicweb Skills plugin are recorded here. The
`version` field in `.claude-plugin/marketplace.json` tracks these entries.

## [4.7.1]

Folds the learnings from a 2026-07 dual demo-host UI pass: two hosts had shipped
with silently-missing Style assets, and several MCP write tools were caught
reporting success without acting.

### Changed
- **Theme staging is now a mandatory deserialize step + readiness gate**
(`dw-demo-swift/references/deserialize-flow.md` "Stage the theme's Style assets",
new `integrity-sweep.md` Check 8, `dw-demo-base/references/visual-qa.md`
definition-of-done): the Swift repo ships only `ColorScheme.config` — no
`<id>.{json,css}` pairs — while serialized Area rows arrive wired to
`swift`/`buttons`/`fonts`, so `TryGet*Style` silently emits nothing and the
storefront renders in serif fallback that "looks almost right". Stage
theme-default's three pairs + rewire Areas, and gate readiness on the three
emitted style links plus a designed-looking full-page screenshot.
- **Silent no-op catalogue extended to deletes, index builds and passwords**
(`foundational/extend-mcp-tools.md` §5, `surface-priority.md`): `delete_area` /
`delete_users` / `delete_paragraphs` return `succeeded:1` and delete nothing;
`build_product_index` reports a completed build while writing only the
`LastUpdated` marker (Admin UI Repositories → Build Full is the working path;
verify by shard-file mtimes); `update_users` accepts and drops a `password`
property. Round-trip every demo-critical write.
- **PLP list layout + thumbnail lever** (`foundational/swift-building.md` §3
symptom table): the surface-serialized shop repeater ships
`GridLayoutDesktop='list'` — list is the intended layout, keep it; the
full-bleed-image failure mode is the card image item's `Width='auto'` (→
`w-100`), and a px value is the thumbnail lever. Also documents the
`ShowAlternativeImageOnHover` NRE on products without a `DefaultImage`.
- **Local demo-host bootstrap conventions** (`dw-demo-base/references/scaffold.md`
§3): admin login on local demo hosts is always `Admin`/`Admin1` (zero-lookup
during live demos; hosted installs keep real secrets), and the wizard-seeded
`Standard` area (AreaId 1) is deleted before the host counts as scaffolded
(SQL — MCP `delete_area` is a silent no-op).
## [4.7.0]

Folds the last three Truvio Distribution release cycles into the demo skills
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ and silently drops part of the input:
| MCP `save_pages` (update path) | `menuText` — the response even echoes the OLD value | DW 10.25.x | SQL `UPDATE Page SET PageMenuText` + host restart (the nav tree caches menu text) |
| MCP `save_pages` (create + update) | `urlName` — the slug you pass is **ignored**; DW derives the slug from `menuText` instead | DW 10.27.x | Set the intended `menuText` (the slug follows it), or SQL `UPDATE Page SET PageUrlName` + host restart. Don't expect `urlName` to pin the slug independently. |
| Management API `ParagraphSave` | `contentItem.groups[].fields[].value` mutations — the `ItemType_*` column never updates | DW 10.25.x | MCP `set_item_field_values` first; SQL UPDATE last resort. `ParagraphSave` IS still correct for paragraph-level scalars (Header, Sort, GridRow, Template) |
| MCP `delete_area`, `delete_users`, `delete_paragraphs` | The **entire delete** — `succeeded:1` returned, row still in the DB afterwards | DW 10.27.x + 10.28.1-Pre | SQL `DELETE` (children first: paragraphs → grid rows → pages → area), then restart for the page-tree cache. Always round-trip a delete with a `SELECT COUNT(*)` |
| MCP `build_product_index` (+ `wait_for_product_index`) | The **actual index build** — "queued" then "completed" is reported and the index's `LastUpdated` marker file is touched, but no Lucene segment files are written; queries keep serving the stale shards | DW 10.28.1-Pre | Admin UI → Settings → Repositories → *repository* → Build 'Full' — that path writes segments. Verify by shard-file mtimes under `Files/System/Indexes/<repo>/<index>/`, not by the tool's status or the marker file |
| MCP `update_users` | A `password` property — the schema has no password field, and an extra `password` property is accepted (`succeeded:1`, `updatedOn` bumped) and dropped | DW 10.28.1-Pre | There is no MCP/API password surface at all — use the plaintext escape hatch in [`users-permissions.md`](users-permissions.md) §13 |

**Rule:** after any demo-critical update through MCP / Management API, round-trip it — read the value back
through a different surface (or curl the rendered page) before declaring it done. When a silent no-op is
Expand Down
2 changes: 2 additions & 0 deletions skills/dw-demo-base/references/foundational/swift-building.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ comes from the item-type's `Title` field.
| Downloads list shows giant thumbnails | same | `HideThumbnails` | `True` for a clean filename + filetype + size table. |
| Long description has too-wide lines | `Swift-v2_ProductLongDescription` | `TextReadability` | `max-width-on` (default) constrains to a reading column; `max-width-off` fills the column. |
| Paragraph renders as empty `<div>` after SQL-direct seed | `Swift-v2_ProductGroupGrid` | `SelectedGroups` | See `SelectedGroups` below — SQL CSV/JSON inserts silently return `null`. |
| PLP renders one product per row with a giant full-bleed image | `Swift-v2_ProductListItemRepeater` + `Swift-v2_ProductDefaultImage` | `GridLayoutDesktop` / `Width` | The surface-serialized shop repeater ships `GridLayoutDesktop='list'` — **list IS the intended shipped layout**, so keep it rather than "fixing" it to a column grid. The full-bleed image is the card image item's `Width='auto'`, which the template renders as `w-100`; a **px value** (e.g. `160`) is the thumbnail lever (`style="width:<N>px"`). Column variants (`2-columns`…`6-columns` → `g-col-lg-*`) remain available when a grid is the deliberate choice. In list mode the row must earn its width — pair the thumbnail with decision-driving fields (price, and e.g. the default-unit shipping weight when the buying story is logistics-led). |
| Every PLP tile renders a Razor NRE error block | `Swift-v2_ProductDefaultImage` | `ShowAlternativeImageOnHover` | With hover-swap on, the stock template's `allAssetsImages.RemoveAll(x => x.Value == product.DefaultImage.Value)` throws when a product has no `DefaultImage` (common on data-first catalogs without imagery). Either turn the field off, or null-guard the predicate (`x?.Value == product?.DefaultImage?.Value`) as a logged template adjustment. |

For `IEnumerable<string>` fields (checkbox-list editors — `FieldDisplayGroups`, `ImageAssets`), pass a
**comma-separated string**. Bracketed-array / JSON encodings are NOT recognised by the stock editors.
Expand Down
3 changes: 2 additions & 1 deletion skills/dw-demo-base/references/scaffold.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,10 @@ The setup wizard is plain ASP.NET MVC forms and is **fully HTTP-drivable** — d
- `GET /Admin/Installation/Start` — captures the session cookie + antiforgery token.
- `POST /Admin/Installation/Step2` with `{MapFilesToExistingFolder=True, FilesPath=<wwwroot\Files>, ResetSettings=false}` — the files repository.
- `POST /Admin/Installation/Step3` with `{DatabaseType=sql, Server, Database, CreateDatabase=false, Encrypt=false, IntegratedSecurity=true}` — **this is the schema step**: ~6 s, ~260 tables migrated, and DW itself writes `GlobalSettings.Database.config`.
- **Admin user:** the schema seed creates an inactive `Administrator` (id 2, empty password). Either `POST /Admin/Installation/Step4` with `{Name, Email, Username, Password, PasswordConfirm}`, or activate it via SQL (set active + copy a password hash from a known-good host's Administrator row — keeps the secret out of logs). Capture the credentials for `references/mcp-setup.md` Step 3.
- **Admin user:** the schema seed creates an inactive `Administrator` (id 2, empty password). `POST /Admin/Installation/Step4` with `{Name, Email, Username, Password, PasswordConfirm}` — and on a **local demo host, the username/password is always `Admin` / `Admin1`**. One memorable credential across every local demo host is a presenter requirement (zero-lookup during a live demo); per-host generated secrets and the `Administrator` spelling both cost demo-time fumbling. Record it in `notes/credentials.local.md` anyway (the file is the single credential home). Hosted / customer-facing installs use real secrets — this convention is local-demo-only. If the host already has a differently-named admin, rename it to `Admin` and set the password via the plaintext escape hatch ([`foundational/users-permissions.md`](foundational/users-permissions.md) §13) — there is no MCP/API password surface.
3. **License — also plain HTTP.** Probe `/Admin/` for the `/admin/license` redirect, then `GET` + `POST /Admin/License/TrialInstallStep` (antiforgery token + the preselected trialId radio). A **Suite Trial** is fine for demos; expiry lands ~30 days out — record it in the demo's `CUSTOMISATIONS.md`. Platform-level detail: [`foundational/setup-install.md`](foundational/setup-install.md) §7.
4. **Verify:** a Management API token auth returns 200, and `/Admin/` redirects to the login screen (`/Admin/UI`), not to `/Admin/Installation/Start`.
5. **Delete the leftover `Standard` area (AreaId 1).** Every wizard bootstrap seeds a `Standard` website that no demo uses; it must not survive into a demo host (it pollutes the area list and can grab root-URL resolution). Delete its pages, grid rows, paragraphs and the `Area` row via SQL — the MCP `delete_area` tool reports success without deleting ([`foundational/extend-mcp-tools.md`](foundational/extend-mcp-tools.md) §5). Verify `SELECT COUNT(*) FROM Area WHERE AreaId=1` returns 0.

**Restore flavor — skipping the wizard entirely.** When a clean-template `.bak`/bacpac with schema + an active Administrator is available, restore it and pre-provision only: `wwwroot\Files\` (any skeleton — DW fills `System/`, `Images/`, `Files/`) plus `wwwroot\Files\GlobalSettings.Database.config` (`<Globalsettings><System><Database>`: `Type=ms_sqlserver`, `SQLServer`, `Database`, `IntegratedSecurity=True`). The restored DB is the actual wizard-skipper; `GlobalSettings.config` is optional (DW writes one on first run).

Expand Down
4 changes: 2 additions & 2 deletions skills/dw-demo-base/references/surface-priority.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ Cloning a tree (Area / Page / Paragraph / GridRow / Item) via raw SQL `INSERT IN

**The pitfall:** raw SQL clones get the visible page tree partly right, then break things you only notice 10 screens later (missing PDPs, headers appearing in the content tree, sibling-page links going to 404). Cleanup is then harder than just using MCP/API in the first place.

## Silent no-ops on UPDATE surfaces — verify by round-trip, not by status code
## Silent no-ops on write surfaces (updates, deletes, index builds) — verify by round-trip, not by status code

A `succeeded` / `status: ok` response from surfaces 1-2 does NOT guarantee the field you sent was applied. Some MCP / Management API writes report success, bump `updatedDate`, and silently drop part of the input (e.g. `save_pages` drops `menuText`; `ParagraphSave` drops item-field value mutations). The catalogue of these version-pinned no-ops and their working fallbacks lives with the tools themselves: [`foundational/extend-mcp-tools.md`](foundational/extend-mcp-tools.md) §5 (MCP/API tool behaviour) and [`foundational/content-modelling.md`](foundational/content-modelling.md) (the same two no-ops framed as paragraph/page save bookkeeping).
A `succeeded` / `status: ok` response from surfaces 1-2 does NOT guarantee the operation happened. Some MCP / Management API writes report success, bump `updatedDate`, and silently drop part of the input (e.g. `save_pages` drops `menuText`; `ParagraphSave` drops item-field value mutations) — and some delete/build tools report success while doing nothing at all (`delete_area` leaves the row; `build_product_index` touches a marker file without writing segments). The catalogue of these version-pinned no-ops and their working fallbacks lives with the tools themselves: [`foundational/extend-mcp-tools.md`](foundational/extend-mcp-tools.md) §5 (MCP/API tool behaviour) and [`foundational/content-modelling.md`](foundational/content-modelling.md) (the same two no-ops framed as paragraph/page save bookkeeping).

**The always-on demo discipline:** after any update through MCP/API where the change is demo-critical, round-trip it (read the value back through a different surface, or curl the rendered page) before declaring it done. When a silent no-op is confirmed, the SQL fallback is sanctioned — log it in the demo's `CUSTOMISATIONS.md` and note the cache that needs flushing.
1 change: 1 addition & 0 deletions skills/dw-demo-base/references/visual-qa.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ Batch at most a handful of fixes between re-checks, and never declare a page don
## Definition of done (per demo-critical page)

- Detectors: `overflowX` 0, `broken`/`stretched` empty, no unexplained gap > 120px, console free of errors, no 404 assets.
- **Theme gate:** the page `<head>` emits all three `Files/System/Styles/{ColorSchemes,Buttons,Typography}` links and the computed body font is the theme's, not the browser's serif fallback. A serif-fallback page renders "almost right" and still fails — that is the silent Style-asset empty-state (`foundational/swift-building.md` §7); the full-page screenshot must read as a *designed* page before the host counts as ready.
- Interaction pass: every visible control changes state when used.
- Eyeball checklist: pass at desktop AND mobile widths.
- Keeper screenshots (both breakpoints) saved under `<demo>\notes\qa\` (the canonical QA-evidence home — see `SKILL.md` "Artifact hygiene"; never the demo root).
17 changes: 16 additions & 1 deletion skills/dw-demo-swift/references/deserialize-flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,24 @@ From the **`surface-swift` layer** (`<demo-root>\distribution\layers\surface-swi
From the **Swift clone** (`<demo-root>\dw-swift\`) to the host's `wwwroot/`:

- `Files/Templates/Designs/Swift-v2/`
- `Files/System/Styles/`
- `Files/System/Styles/` (this lands `ColorScheme.config` ONLY — see the theme-staging step below)
- `Files/Images/Icons/` (~80 SVGs incl. `Flags/` and `LoginProviders/` — the set integrity-sweep Check 6 verifies; verified present in the Swift clone. These stock icons are also what `theme-default`'s opt-in nav icons bind to — see [`header-menu.md`](header-menu.md))

**Stage the theme's Style assets (mandatory — the Swift clone ships none).** The Swift repo's
`Files/System/Styles/` contains only `ColorSchemes/ColorScheme.config` — NO `<id>.{json,css}`
pairs — while the surface's serialized `Area` rows arrive wired to
`AreaColorSchemeGroupId='swift'` / `AreaButtonStyleId='buttons'` / `AreaTypographyId='fonts'`.
`TryGet*Style` fails silently when the file behind an id is absent (the empty-state pitfall,
[`swift-building.md`](../../dw-demo-base/references/foundational/swift-building.md) §7), so a
host built without this step renders every page in the browser's serif fallback with unstyled
buttons and a `Styles/Buttons/buttons.css` 404 — and it looks "almost right", which is exactly
why it ships unnoticed. Stage `theme-default`'s three pairs
(`ColorSchemes/Buttons/Typography` `default.{json,css}`) per
[`styles-assets.md`](styles-assets.md) and rewire the Areas to `default` (keep
`AreaColorSchemeId='light'`). Verify: the home page `<head>` emits all three
`/Files/System/Styles/{ColorSchemes,Buttons,Typography}/…css` links and each returns 200 —
integrity-sweep Check 8 owns the gate.

**Repositories skip rule.** For `Files/System/Repositories/`, copy **everything EXCEPT `ProductsBackend/` and `ProductsFrontend/`** — those two index Swift's bike-demo custom fields (`PlantHardiness`, `BikeFrameSize`, plant/bike-specific facets, etc.). Copying them into a host whose products use a different data-model causes `BuildIndex` Full to fail with "field not found in products" — the index builder validates every field reference against the live `EcomProductCategoryField` table. The other Swift-shipped indexes (`Content/`, `Files/`, `Post/`, `Secondary users/`) are demo-data-agnostic — they index Pages/Files/blog Posts/Users via standard fields plus item-type fields that DO resolve cleanly; copy those alongside. Hand-write a per-demo Products index targeting the demo's actual data-model fields instead — see [`../../dw-demo-pim/references/canonical-setup-order.md`](../../dw-demo-pim/references/canonical-setup-order.md) Step 16. (For PIM-data + Swift-frontend hybrid demos with N categories × M custom fields each, pick 5-10 demo-relevant fields per category for the index — not the full set; index size is rarely the constraint, but maintenance and admin-UI clarity are.)

**Catalog-paragraph path rewrite (run AFTER the deserialize).** The `eCom_ProductCatalog` paragraphs from the Swift baseline reference `/Files/System/Repositories/ProductsFrontend/Products.query` and `Products.facets` in their `ParagraphModuleSettings` XML. Those paths point into the bike-demo repos you skipped — the Catalog module silently renders an empty product list when the paths break. After authoring your per-demo `Products.query` + `Products.facets` (sourced from `Repository="Products"`, with parameters matching your facet fields), bulk-rewrite the paragraph references via SQL:
Expand Down
Loading
Loading