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.11.1"
"version": "4.11.2"
},
"plugins": [
{
Expand Down
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,41 @@
All notable changes to the Dynamicweb Skills plugin are recorded here. The
`version` field in `.claude-plugin/marketplace.json` tracks these entries.

## [4.11.2]

Makes the demo consumption contract mechanical about version currency: the demo skills now pin the
Distribution's latest gate-proven `main` and resolve layers from its machine-readable layer index,
instead of resolving and checking out the newest git tag. A stale or retired layer reference now
fails loudly or auto-corrects to its successor rather than silently materializing a removed layer —
the failure class behind a demo that consumed a layer removed several releases earlier.

### Changed
- **Consumption contract: pin `origin/main` + read `layers/INDEX.json`, never resolve a git tag**
(`dw-demo-base/SKILL.md` "Versions prompt + Distribution clone/checkout";
`dw-demo-swift/references/{deserialize-flow,pack-activation,styles-assets}.md`;
`dw-demo-base/references/{setup-checks,serializer-reference}.md`;
`dw-demo-swift/references/integrity-sweep.md`): the clone/checkout flow drops the
`git tag --list … | Sort [version]` newest-tag resolver. Consumers clone once,
`git pull --ff-only origin main`, assert the index's `gateProven` marker is present, and resolve
each layer from the live `layers` entries — a name absent from `layers` is looked up under
`retired` and resolved to its `supersededBy` successor (loud, never silent). Reproducibility is the
resolved commit SHA recorded in `CUSTOMISATIONS.md`, not a tag.
- **Retired-layer names purged from build instructions** (`dw-demo-swift/references/pack-activation.md`,
`styles-assets.md`, `header-menu.md`; `dw-demo-base/SKILL.md`): the pack-activation worked example
and per-pack notes move off the retired `reordering-pricing` / `subscription-orders` bundle names
onto the live `feature-pricing` / `feature-subscription-orders` layers; the header-nav affordance is
described as shipping inside `theme-default` without latching onto the retired `theme-nav-polish` /
overlay names. Retirement notes stay only where they help the reader; instructions no longer build
from a dead name.

### Added
- **Dead-layer-name sweep in the fold-back content-hygiene gate**
(`dw-demo-base/references/iterate-plugin.md` Step 1b §5 + verification gate + anti-patterns): every
fold that names a Distribution layer sweeps the name against `INDEX.json` (the source of truth) and
rewrites a retired name to its `supersededBy` successor. Deliberately kept out of
`scripts/validate-skills.py` so the index stays the single source of truth rather than forking a
retired-name blocklist that drifts on the next rename.

## [4.11.1]

Folds a demo-build session's design-quality-gate learnings into the demo skills' visual-QA,
Expand Down
43 changes: 25 additions & 18 deletions skills/dw-demo-base/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,37 +60,44 @@ The Serializer install steps live in base so any sister skill can pull them; the

### Versions prompt + Distribution clone/checkout

Demo artifacts are **not** kept in a shared machine-wide vault. All of them now live in ONE consolidated repo — `justdynamics/Truvio.Commerce.Distribution` — consumed by **`git clone` + checkout of an annotated tag**, never a release zip. The clone holds `layers/<name>/` (each a layer with a `kind` = base | catalog | feature | theme | surface | sample-data) plus `editions/<name>.json` (named, gate-proven compositions of layers). Version pins are annotated git tags `layers/<name>/<semver>` and `editions/<name>/<semver>`. Each demo checks out exactly the tag it targets into its own `<demo-root>\distribution\` folder, so two demos on the same machine can pin different versions without collision. Before any artifact is fetched, ask the user two things (record both in the demo's `CUSTOMISATIONS.md` for reproducibility):
Demo artifacts are **not** kept in a shared machine-wide vault. All of them live in ONE consolidated Distribution repo (its URL is per-machine — a default plus the `$env:DW_DISTRIBUTION_REPO` override, below), consumed by **`git clone` + `git pull --ff-only` on `origin/main`** — never a release zip, never a tag checkout. The clone holds `layers/<name>/` (each a layer with a `kind` = base | catalog | feature | theme | surface | sample-data), `editions/<name>.json` (named, gate-proven compositions of layers), and **`layers/INDEX.json`** — the distribution's machine-readable **layer index**, the source of truth for what layers exist, their kind/version/status, and what any retired name was superseded by. **Main IS the version:** consumers pin the latest gate-proven `main`, not a frozen tag. The Distribution supports the current latest Swift release only and rolls forward with it, so there is no re-consumable old state to pin — annotated tags survive as provenance/audit history, never a consumer checkout target. Each demo clones into its own `<demo-root>\distribution\` folder, so two demos on the same machine stay isolated. Reproducibility is the resolved **commit SHA** recorded in `CUSTOMISATIONS.md` (forensics), not a tag. Before any artifact is fetched, ask the user two things (record both in the demo's `CUSTOMISATIONS.md`):

1. **DW10 version** — the platform version the demo host runs (drives layer compatibility checks).
2. **Swift version** — e.g. `2.4` (drives the layer/edition tags and the Swift design-package clone tag `v<version>.0`). The Distribution supports the **current latest Swift release only** and rolls forward with it; the current cycle is **Swift 2.4 on DW 10.28.1-PreRelease** (editions attested there; stable re-prove pending).
2. **Swift version** — e.g. `2.4` (drives the Swift design-package clone tag `v<version>.0`). The Distribution supports the **current latest Swift release only** and rolls forward with it; the current cycle is **Swift 2.4 on DW 10.28.1-PreRelease** (editions attested there; stable re-prove pending).

**Tag resolution — layer/edition versions are their own semver, not the Swift minor.** Actual tags are full semver (`layers/base/3.0.0`, `editions/swift-demo/3.0.0`); each layer/edition declares the Swift release it targets via `swiftVersion` in its `layer.json` (the Distribution tracks one Swift release at a time — latest-only). Never guess a tag from the Swift minor — clone the Distribution once, resolve the **latest tag for the ref**, check it out, and verify the checked-out `layer.json`'s `swiftVersion` matches the versions prompt. An `editions/<name>/<semver>` tag pins a whole gate-proven composition at once (the usual demo pin); a bare `layers/<name>/<semver>` tag pins a single layer:
**Layer resolution — read `INDEX.json`, never resolve a git tag.** Clone the Distribution once, or `git pull --ff-only` an existing clone up to `origin/main`; then read `layers/INDEX.json`. Assert its `gateProven` block is present — that marker is what says `main` is at a gate-proven tip, not mid-release — and resolve each layer/edition you need from the **live `layers` entries** (`status: active` or `deprecated`). Verify the checked-out `layer.json`'s `swiftVersion` matches the versions prompt (each layer declares the Swift release it targets — the Distribution tracks one at a time, latest-only). If a name you reach for is absent from `layers`, look it up under `retired`: a retired entry names its **`supersededBy`** successor — resolve to that successor, never the dead name (a retired reference must resolve loudly to its successor, never to silence):

```powershell
$repo = if ($env:DW_DISTRIBUTION_REPO) { $env:DW_DISTRIBUTION_REPO } else { "justdynamics/Truvio.Commerce.Distribution" }
$repo = if ($env:DW_DISTRIBUTION_REPO) { $env:DW_DISTRIBUTION_REPO } else { "<owner>/<distribution-repo>" }
$dist = "<demo-root>\distribution"
git clone "https://github.com/$repo" $dist # one repo — all layers + editions live here
$ref = "editions/swift-demo" # the edition (or `layers/base`) to pin
$tag = git -C $dist tag --list "$ref/*" |
Sort-Object { [version]($_ -replace "^$ref/",'') } -Descending |
Select-Object -First 1
if (-not $tag) { throw "No $ref/* tag found — check the Distribution repo's tags." }
git -C $dist checkout $tag # the whole snapshot; consume the layer dirs you need
# Verify the Swift target: (Get-Content "$dist\layers\base\layer.json" | ConvertFrom-Json).swiftVersion
# must equal the versions-prompt answer (e.g. 2.4.0).
if (Test-Path "$dist\.git") {
git -C $dist pull --ff-only origin main # main IS the version — fast-forward to the gate-proven tip
} else {
git clone "https://github.com/$repo" $dist # one repo — all layers + editions + INDEX.json live here
}
$index = Get-Content "$dist\layers\INDEX.json" -Raw | ConvertFrom-Json
if (-not $index.gateProven) { throw "INDEX.json has no gateProven marker — main is not at a gate-proven tip; do not consume." }
# Resolve a layer from the live index (retired -> follow supersededBy, never the dead name):
$name = "surface-swift" # the layer (or edition) the demo composes
$entry = $index.layers | Where-Object { $_.name -eq $name }
if (-not $entry) {
$tomb = $index.retired | Where-Object { $_.name -eq $name }
if ($tomb) { throw "Layer '$name' is RETIRED -> use $($tomb.supersededBy)" } else { throw "Layer '$name' not in INDEX.json" }
}
# Consume $dist\layers\$name\ ; verify (Get-Content "$dist\layers\$name\layer.json" | ConvertFrom-Json).swiftVersion
# equals the versions-prompt answer (e.g. 2.4.0).
```

Record the RESOLVED tag (not just the minor) in `CUSTOMISATIONS.md` — that exact tag is the demo's reproducibility pin (a later rebuild clones the same repo and `git checkout`s that tag). The checked-out snapshot contains every layer at that commit; read whichever `layers/<name>/` dirs the edition composes.
Record the resolved **commit SHA** (`git -C $dist rev-parse HEAD`) in `CUSTOMISATIONS.md` — that SHA is the demo's forensic reproducibility stamp (a later rebuild pulls `main` and reads the current `INDEX.json`; the SHA says which gate-proven tip this demo was built against). The clone contains every live layer at that commit; read whichever `layers/<name>/` dirs the edition composes.

With those answers, artifacts resolve from the demo's Distribution checkout. `Truvio.Commerce.DemoThemes` and `Truvio.Commerce.FeaturePacks` are **archived** — their themes and packs are now theme/feature layers in the Distribution:
With those answers, artifacts resolve from the demo's Distribution checkout. The former standalone demo-theme and feature-pack repos are **archived** — their themes and packs are now theme/feature layers in the Distribution:

| Artifact | Source (in the Distribution clone) | Working tree | Consumed by |
|---|---|---|---|
| Serialized base | `layers/base` (kind base) — **framework-only**: 16 framework SQL sets in `replace/_sql/` (countries, currencies, languages, shops, payments, shippings, VAT, order flow/states, AccessUser), **zero content, zero pages, empty catalog by design** | `<demo-root>\distribution\layers\base\` | [`dynamicweb-swift-demo/references/deserialize-flow.md`](../dw-demo-swift/references/deserialize-flow.md) §3 |
| Swift content surface | `layers/surface-swift` (kind surface) — ALL Swift content: both areas (`Swift 2` + `Swift 2 Nederlands`) in `replace/_content/` + `merge/_content/`, `UrlPath` in `replace/_sql/`, and its **own item-type XMLs** (`itemtypes/`, 128 `ItemType_Swift-v2_*.xml`) | `<demo-root>\distribution\layers\surface-swift\` | [`dynamicweb-swift-demo/references/deserialize-flow.md`](../dw-demo-swift/references/deserialize-flow.md) §3 |
| Demo catalog + identities *(optional)* | `layers/sample-data` (kind sample-data) — ships ALL demo content as SQL files (`merge/_sql/catalog.sql`: products / groups / prices; `merge/_sql/identities.sql`: buyer + CSR); editions activate it via `sampleData: true` (e.g. `swift-demo`); otherwise author per-demo via the [`dw-demo-pim`](../dw-demo-pim/SKILL.md) recipes | `<demo-root>\distribution\layers\sample-data\` | [`dynamicweb-swift-demo/references/deserialize-flow.md`](../dw-demo-swift/references/deserialize-flow.md) §3 |
| Demo theme / style assets | `layers/theme-default` (kind theme — pure disk-overlay `files/`, no serialized DB content). **The ONE presentation layer** — every Swift demo starts from `theme-default` and re-skins on top of it; there is no theme choice and no separate overlay layers (the former `theme-nav-polish` affordance CSS is folded into `theme-default`'s `default_custom.css`) | `<demo-root>\distribution\layers\theme-default\` | [`dynamicweb-swift-demo/references/styles-assets.md`](../dw-demo-swift/references/styles-assets.md) |
| Demo theme / style assets | `layers/theme-default` (kind theme — pure disk-overlay `files/`, no serialized DB content). **The ONE presentation layer** — every Swift demo starts from `theme-default` and re-skins on top of it; there is no theme choice and no separate overlay layers (the header-nav affordance CSS ships inside `theme-default`'s `default_custom.css`) | `<demo-root>\distribution\layers\theme-default\` | [`dynamicweb-swift-demo/references/styles-assets.md`](../dw-demo-swift/references/styles-assets.md) |
| Feature pack | `layers/<name>` (kind feature) | `<demo-root>\distribution\layers\<name>\` | [`dynamicweb-swift-demo/references/pack-activation.md`](../dw-demo-swift/references/pack-activation.md) |
| Swift design package | local clone of `https://github.com/dynamicweb/Swift` (release tag `v<version>.0` — the upstream Swift product still ships releases) | `<demo-root>\dw-swift\` | [`dynamicweb-swift-demo/references/deserialize-flow.md`](../dw-demo-swift/references/deserialize-flow.md) "Design-package deploy" |

Expand Down Expand Up @@ -266,7 +273,7 @@ A sibling skill that runs without `dynamicweb-demo-base`'s outputs (no `.mcp.jso

## Reference-content layout

Demo artifacts (base, catalog, theme, and feature layers) are checked out per-demo from the Distribution clone into the demo's own `<demo-root>\distribution\` folder — see "Versions prompt + Distribution clone/checkout" above. There is no shared machine-wide vault; each demo owns the exact tag it checked out.
Demo artifacts (base, catalog, theme, and feature layers) are checked out per-demo from the Distribution clone into the demo's own `<demo-root>\distribution\` folder — see "Versions prompt + Distribution clone/checkout" above. There is no shared machine-wide vault; each demo consumes the latest gate-proven `main` and records the resolved commit SHA as its reproducibility stamp.

Two read-only reference sources are **local clones**, not downloads, and their location is per-machine — **ask or discover it, never hardcode**:

Expand All @@ -290,7 +297,7 @@ Port, DB name, and Management API bearer token vary per project. Read them from

## Baseline-drift self-diagnosis rule

When grep results in skill text contradict the live baseline, consider "the baseline has rolled since this skill was authored" as a candidate cause. Cross-check the checked-out `base` layer's tag against the demo's Swift version before assuming the skill is correct. Reality wins; the skill is the second source of truth.
When grep results in skill text contradict the live baseline, consider "the baseline has rolled since this skill was authored" as a candidate cause. Cross-check the checked-out `base` layer's `swiftVersion` (from its `layer.json`) against the demo's Swift version before assuming the skill is correct. Reality wins; the skill is the second source of truth.



Expand Down
32 changes: 30 additions & 2 deletions skills/dw-demo-base/references/iterate-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ value is the role + date, not the person.
Sanitization protects the customer; this step protects the *corpus*. Fold-backs that skip it
produced, historically: a pivot that left the retracted model live in five other files, a
retracted API claim that survived three releases next to the file that retracted it, and the
same lesson recorded four times in different words. Run all three checks BEFORE drafting the
same lesson recorded four times in different words. Run these checks BEFORE drafting the
edit:

### 1. Supersede sweep — when the learning corrects, retracts, or pivots existing guidance
Expand Down Expand Up @@ -239,6 +239,29 @@ append a new dated subsection below it. Specifically:
- If the target reference would exceed ~20KB after the edit, stop and propose a split (or a
different home) to the user instead of appending.

### 5. Dead-layer-name sweep — when the fold references a Distribution layer

Demo-skill folds routinely name Distribution layers (`base`, `surface-swift`, `feature-*`,
`theme-default`, editions). A layer name that was renamed or retired since the skill was authored
becomes a **latch-on target** — an instruction that tells a future demo to build from a layer that
no longer exists (the staleness this discipline exists to prevent). Before folding, sweep every
Distribution layer name the edit mentions against the **distribution's layer index**
(`layers/INDEX.json` on the latest gate-proven `main` — the source of truth for what layers exist and
what a retired name was superseded by):

- **Live (`status: active`/`deprecated`)** — use it; prefer the successor over a `deprecated` layer
for new instructions.
- **Retired (an `INDEX.json` `retired` entry)** — rewrite the instruction to the entry's
`supersededBy` successor. A retirement *note* ("X was retired, use Y") may stay only where it
genuinely helps the reader; an *instruction* that builds from the dead name goes.
- **Absent entirely** — stop; the name is wrong. Resolve it against `INDEX.json` before folding.

Keep the retired-name list in `INDEX.json`, never hardcoded into `scripts/validate-skills.py` — a
blocklist in the skills repo forks a second source of truth from the index and drifts the moment a
layer is renamed. The index is authoritative; this sweep is the release-time check that consults it.
(The Distribution side enforces the mirror rule in its own CI — its validator rejects a retired layer
name in its living docs.)

## Step 2 — Make the edit

Edit the file at `$DYNAMICWEB_SKILLS_REPO/skills/<skill>/references/<topic>.md` (or the
Expand Down Expand Up @@ -417,7 +440,9 @@ genuine first install on a fresh machine.
source notes, the staged diff, AND the commit message / PR body.
6. **Content-hygiene gate passed (Step 1b)** — supersede sweep run over `skills/` if the
learning corrects anything; no second copy of an existing lesson; owning SKILL.md routing
row updated if scope changed.
row updated if scope changed; every Distribution layer name the fold mentions checked against
`INDEX.json` (a retired name rewritten to its `supersededBy` successor, never left as an
instruction).
7. The branch is pushed and a **PR is open** against the integration branch (not pushed
directly).
8. After merge: the marketplace clone is at the new commit, and the user has the
Expand Down Expand Up @@ -495,6 +520,9 @@ The cost of this dance is high enough that the Step 1a + final pre-commit grep p
all of `skills/` (Step 1b §1).
- **Recording the same lesson in a second home.** If a grep finds the lesson already exists,
sharpen the canonical copy and pointer to it (Step 1b §2).
- **Leaving a retired layer name as a build instruction.** A renamed/retired Distribution layer
name in an instruction sends a future demo to a layer that no longer exists. Sweep layer names
against `INDEX.json` and rewrite to the `supersededBy` successor (Step 1b §5).

## Reference: file layout

Expand Down
Loading
Loading