diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 67a8a8c..3301650 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -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": [ { diff --git a/CHANGELOG.md b/CHANGELOG.md index d74571a..89ee7c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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, diff --git a/skills/dw-demo-base/SKILL.md b/skills/dw-demo-base/SKILL.md index d3dca38..7305fd1 100644 --- a/skills/dw-demo-base/SKILL.md +++ b/skills/dw-demo-base/SKILL.md @@ -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//` (each a layer with a `kind` = base | catalog | feature | theme | surface | sample-data) plus `editions/.json` (named, gate-proven compositions of layers). Version pins are annotated git tags `layers//` and `editions//`. Each demo checks out exactly the tag it targets into its own `\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//` (each a layer with a `kind` = base | catalog | feature | theme | surface | sample-data), `editions/.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 `\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.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.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//` tag pins a whole gate-proven composition at once (the usual demo pin); a bare `layers//` 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 { "/" } $dist = "\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//` 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//` 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** | `\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`) | `\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 | `\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`) | `\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`) | `\distribution\layers\theme-default\` | [`dynamicweb-swift-demo/references/styles-assets.md`](../dw-demo-swift/references/styles-assets.md) | | Feature pack | `layers/` (kind feature) | `\distribution\layers\\` | [`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.0` — the upstream Swift product still ships releases) | `\dw-swift\` | [`dynamicweb-swift-demo/references/deserialize-flow.md`](../dw-demo-swift/references/deserialize-flow.md) "Design-package deploy" | @@ -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 `\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 `\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**: @@ -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. diff --git a/skills/dw-demo-base/references/iterate-plugin.md b/skills/dw-demo-base/references/iterate-plugin.md index 85d0769..bf47ee9 100644 --- a/skills/dw-demo-base/references/iterate-plugin.md +++ b/skills/dw-demo-base/references/iterate-plugin.md @@ -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 @@ -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//references/.md` (or the @@ -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 @@ -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 diff --git a/skills/dw-demo-base/references/serializer-reference.md b/skills/dw-demo-base/references/serializer-reference.md index 5dd3678..63202f0 100644 --- a/skills/dw-demo-base/references/serializer-reference.md +++ b/skills/dw-demo-base/references/serializer-reference.md @@ -178,7 +178,7 @@ Three signals: ### How baselines roll -Baseline rolls happen out-of-band — when Dynamicweb ships a new Swift release, the `base` layer gets re-serialized from a fresh Swift install and published as a new annotated tag `layers/base/` in the Distribution repo (`justdynamics/Truvio.Commerce.Distribution`). The demo pins the exact **tag** it checked out. That tag is the stamp; cross-check it against the demo's host DW10 version when triaging schema-drift warnings (the baseline-drift self-diagnosis rule). +Baseline rolls happen out-of-band — when Dynamicweb ships a new Swift release, the `base` layer gets re-serialized from a fresh Swift install and lands on the Distribution repo's `main` (annotated tags are cut as provenance/audit history, not a consumer checkout target). The demo consumes the latest gate-proven `main` and records the resolved **commit SHA**. That SHA is the stamp; cross-check the checked-out `base` layer's `swiftVersion` against the demo's host DW10 version when triaging schema-drift warnings (the baseline-drift self-diagnosis rule). ## Cross-references diff --git a/skills/dw-demo-base/references/setup-checks.md b/skills/dw-demo-base/references/setup-checks.md index 99306ed..920c921 100644 --- a/skills/dw-demo-base/references/setup-checks.md +++ b/skills/dw-demo-base/references/setup-checks.md @@ -70,7 +70,7 @@ The demo-specific checks owned here are the TLS env var, `git` + the `gh` CLI, t ### Check: `git` + `gh` CLI present and authenticated -**Why this matters:** Demo artifacts (base, catalog, theme, feature layers) are **checked out** per-demo with `git clone` + `git checkout ` from the single Distribution repo (`justdynamics/Truvio.Commerce.Distribution`) — there are **no releases** to download (see the base SKILL "Versions prompt + Distribution clone/checkout"). `git` does the clone; `gh`, authenticated, supplies the credential helper that lets a **private** Distribution repo clone over HTTPS. If either is missing or unauthenticated, the Swift deserialize and pack-activation flows cannot fetch their sources. +**Why this matters:** Demo artifacts (base, catalog, theme, feature layers) are consumed per-demo with `git clone` + `git pull --ff-only origin main` from the single Distribution repo (URL from `$env:DW_DISTRIBUTION_REPO`) — **main IS the version**; there are **no releases** to download and no tag checkout (see the base SKILL "Versions prompt + Distribution clone/checkout"). `git` does the clone; `gh`, authenticated, supplies the credential helper that lets a **private** Distribution repo clone over HTTPS. If either is missing or unauthenticated, the Swift deserialize and pack-activation flows cannot fetch their sources. **Probe:** @@ -103,11 +103,11 @@ Set-Content -Path $probe -Value "ok"; Remove-Item $probe # throws if not writa ### Check: Versions prompt (DW10 + Swift) -**Why this matters:** The version answers select which **layer/edition tag** (`layers/base/`, `editions//`) the demo checks out of the Distribution clone, drive the Swift design-package clone tag (`v.0`), and layer compatibility checks. Ask **before** cloning anything. Note: the Distribution repo pins by **annotated tag** — the Swift version resolves to the latest-patch tag for that minor, and the reproducibility pin is the **checked-out tag**, recorded in `CUSTOMISATIONS.md`. +**Why this matters:** The version answers drive the Swift design-package clone tag (`v.0`) and layer-compatibility checks (each layer's `layer.json` declares the `swiftVersion` it targets). Ask **before** cloning anything. Note: the demo consumes the latest gate-proven `main` of the Distribution (resolved from `layers/INDEX.json`, not a tag), and the reproducibility pin is the **resolved commit SHA**, recorded in `CUSTOMISATIONS.md`. **Probe:** conversational — ask the user via `AskUserQuestion`: -> "Which **DW10 platform version** does this demo target, and which **Swift version** (e.g. `2.3`)? Both get recorded in `CUSTOMISATIONS.md` for reproducibility and select which layer/edition tag (`layers/base/`) to check out of the Distribution clone." +> "Which **DW10 platform version** does this demo target, and which **Swift version** (e.g. `2.4`)? Both get recorded in `CUSTOMISATIONS.md` for reproducibility and drive layer-compatibility checks against the Distribution's latest gate-proven `main`." **Expected:** two values captured in conversation state and written to the demo's `CUSTOMISATIONS.md`. No default — never guess a version. diff --git a/skills/dw-demo-swift/references/deserialize-flow.md b/skills/dw-demo-swift/references/deserialize-flow.md index 3bc05a1..ce89ff9 100644 --- a/skills/dw-demo-swift/references/deserialize-flow.md +++ b/skills/dw-demo-swift/references/deserialize-flow.md @@ -22,7 +22,7 @@ `dynamicweb-demo-base` setup is complete: - [`../../dw-demo-base/references/setup-checks.md`](../../dw-demo-base/references/setup-checks.md) is green (NODE_TLS_REJECT_UNAUTHORIZED, .NET SDK, ProjectTemplates, SQL Express all probed and resolved). -- The Distribution has been **checked out** (`justdynamics/Truvio.Commerce.Distribution` by default; overridable via `$env:DW_DISTRIBUTION_REPO`) at an annotated tag — usually an edition tag like `editions/swift-demo/`, which pins the `base` + `surface-swift` + `sample-data` + theme layers together — into the demo's own `distribution\` folder (see §3 — the staging snippet clones + checks out on first run). The pin is the checked-out tag, recorded in `CUSTOMISATIONS.md`. +- The Distribution has been **cloned** (repo URL from `$env:DW_DISTRIBUTION_REPO`, default below) and pulled `--ff-only` up to `origin/main` into the demo's own `distribution\` folder (see §3 — the staging snippet clones + fast-forwards on first run). **Main IS the version**: consume the latest gate-proven `main` (assert `layers/INDEX.json` `gateProven` is present) and compose the edition's layers from the live `INDEX.json` `layers` entries. The reproducibility pin is the resolved commit SHA, recorded in `CUSTOMISATIONS.md`. - [`../../dw-demo-base/references/scaffold.md`](../../dw-demo-base/references/scaffold.md) produced a running `Dynamicweb.Host.Suite` (port reachable, host responds at `/admin`). - [`../../dw-demo-base/references/mcp-setup.md`](../../dw-demo-base/references/mcp-setup.md) verification gate passed (`claude mcp list` shows `dynamicweb-commerce-mcp ✓ Connected` AND in-conversation `ToolSearch +dynamicweb` returns >200 tools). - **The DW Serializer is installed in the host** per [`../../dw-demo-base/references/serializer-reference.md`](../../dw-demo-base/references/serializer-reference.md) "Installation" section (the `Truvio.Commerce.Serializer` NuGet package added as a `PackageReference` + restored, `Files/System/Serializer/Serializer.config.json` staged, host restarted). This is a one-time-per-host step. @@ -109,7 +109,7 @@ Captured via `AskUserQuestion` in the current conversation. Format: `CLAUDE.\distribution\layers\\` folder — the per-demo checkout of the Distribution repo (`justdynamics/Truvio.Commerce.Distribution` by default; overridable via `$env:DW_DISTRIBUTION_REPO`) at the pin tag. No hardcoded machine-wide literals. +Layer path resolution: every layer path resolves under the demo's own `\distribution\layers\\` folder — the per-demo clone of the Distribution repo (URL from `$env:DW_DISTRIBUTION_REPO`, default below), pulled `--ff-only` up to `origin/main`. No hardcoded machine-wide literals. **The Serializer reads from `Dynamicweb.Host.Suite/wwwroot/Files/System/Serializer/SerializeRoot//`** (joined from `outputDirectory: "Serializer"` in `Files/System/Serializer/Serializer.config.json` + `outputSubfolder` per mode). The demo's `distribution\layers\base\` folder is the **checked-out/staging copy only** — the deserialize endpoint never reads it. The snippet below copies `layers\base\` INTO `SerializeRoot/`; that copy step is what makes the content visible. Skipping the copy (or pointing the flow at `layers\base\` directly) silently no-ops — any "121 updated" you then see comes from whatever else is already in `SerializeRoot/replace/` (typically a previous serialize roundtripping itself). Verified during a Swift2 baseline import — the original recipe pointed at the source tree and silently no-op'd. @@ -126,17 +126,17 @@ Layer path resolution: every layer path resolves under the demo's own `/" } + git clone "https://github.com/$repo" $dist # base + surface-swift + sample-data + ... + INDEX.json all present } +$index = Get-Content "$dist\layers\INDEX.json" -Raw | ConvertFrom-Json +if (-not $index.gateProven) { throw "INDEX.json has no gateProven marker — main is not gate-proven; do not deserialize." } +Write-Host "On main $(git -C $dist rev-parse --short HEAD) — record the commit SHA in CUSTOMISATIONS.md (reproducibility stamp)" $serializeRoot = "Dynamicweb.Host.Suite/wwwroot/Files/System/Serializer/SerializeRoot" # Stage BOTH layers' mode trees: base (framework-only, replace/ only) + surface-swift # (replace/ + merge/ — all content + UrlPath). The trees are disjoint, so they overlay cleanly. diff --git a/skills/dw-demo-swift/references/header-menu.md b/skills/dw-demo-swift/references/header-menu.md index 8bc5695..cea7744 100644 --- a/skills/dw-demo-swift/references/header-menu.md +++ b/skills/dw-demo-swift/references/header-menu.md @@ -61,8 +61,8 @@ that promises a menu-bar default must ship or author nav depth; the base stays f ## The shared default: `theme-default`'s `default_custom.css` The affordance CSS is not a per-demo copy step — it is a first-class Distribution default, and it -is **not a separate layer**: the former `theme-nav-polish` overlay layer is retired, and the -affordance core (carets, hover states, the reach fixes below) now ships **inside `theme-default`** +is **not a separate layer**: the header-nav affordance core (carets, hover states, the reach +fixes below) ships **inside `theme-default`** at `Templates/Designs/Swift-v2/Custom/default_custom.css`. There is no overlay concept in the Distribution anymore — `theme-default` is the ONE presentation layer every edition composes (`themes: ["default"]`), and the customer re-skin ladder starts FROM it ([`re-skin.md`](re-skin.md)). diff --git a/skills/dw-demo-swift/references/integrity-sweep.md b/skills/dw-demo-swift/references/integrity-sweep.md index 4fba38f..6146fc7 100644 --- a/skills/dw-demo-swift/references/integrity-sweep.md +++ b/skills/dw-demo-swift/references/integrity-sweep.md @@ -345,6 +345,6 @@ without errors. Run the polish gate in When all eight checks pass, deserialize is verified complete. The skill may now declare "baseline restored" to the user. -Log the result + layer name + timestamp in the per-demo `CUSTOMISATIONS.md` as a deserialize event row, and record the checked-out layer/edition tag there too. This is structural — every deserialize is reproducible by re-running this flow against the same layer name + tag recorded in `CUSTOMISATIONS.md` (the Distribution repo pins by annotated tag `layers//`). +Log the result + layer name + timestamp in the per-demo `CUSTOMISATIONS.md` as a deserialize event row, and record the resolved commit SHA there too. This is structural — every deserialize is reproducible by re-running this flow against the same layer name at the commit SHA recorded in `CUSTOMISATIONS.md` (consumers pin `origin/main`; the SHA is the forensic reproducibility stamp). diff --git a/skills/dw-demo-swift/references/pack-activation.md b/skills/dw-demo-swift/references/pack-activation.md index 561535f..be7d80b 100644 --- a/skills/dw-demo-swift/references/pack-activation.md +++ b/skills/dw-demo-swift/references/pack-activation.md @@ -26,11 +26,11 @@ ## 1. What a feature pack is -A pack is a **feature layer** — `layers//` (kind `feature`) in the Distribution repo -(`justdynamics/Truvio.Commerce.Distribution`), pinned by the annotated tag `layers//` -(or composed into an `editions/` tag). It lives in the demo's Distribution checkout at -`\distribution\layers\\`; the pin is the checked-out tag, recorded in -`CUSTOMISATIONS.md`. A pack carries three kinds of thing, each landing in a different place on the host: +A pack is a **feature layer** — `layers//` (kind `feature`) in the Distribution repo, +resolved from the live `layers/INDEX.json` on the latest gate-proven `main` (normally composed into +an `editions/.json` — the usual demo consume). It lives in the demo's Distribution clone at +`\distribution\layers\\`; the reproducibility pin is the resolved commit SHA, +recorded in `CUSTOMISATIONS.md`. A pack carries three kinds of thing, each landing in a different place on the host: - **`.cs` source** — compiles INTO the demo host's own build (source-drop, never a separate DLL). - **Disk overlays** — Razor templates and item-type XML that live on disk under `wwwroot\Files`. @@ -54,11 +54,11 @@ is a base-layer improvement, not a pack. - The Serializer installed in the host (same one-time install the deserialize flow depends on). - A Management API bearer token captured in the current conversation (format `CLAUDE.`; keep it in conversation state, never write it to a file). -- The feature layer present in the demo's Distribution checkout - (`justdynamics/Truvio.Commerce.Distribution` by default; overridable via `$env:DW_DISTRIBUTION_REPO`) - at `\distribution\layers\\`, `layer.json` at the folder root — deserialize-flow §3 - already cloned the Distribution; §4 below checks out the layer's tag if it is not in the current - snapshot. The pin is the checked-out tag. +- The feature layer present in the demo's Distribution clone + (repo URL from `$env:DW_DISTRIBUTION_REPO`) at `\distribution\layers\\`, + `layer.json` at the folder root — deserialize-flow §3 already cloned the Distribution; §4 below + fast-forwards to `origin/main` and resolves the layer from `INDEX.json`. The reproducibility pin + is the resolved commit SHA. ## 3. Pack folder anatomy @@ -93,25 +93,32 @@ behaviors have data even against the empty-catalog base. The Distribution was already cloned by [`deserialize-flow.md`](deserialize-flow.md) §3 into `\distribution\`, so the feature layer is normally already present at -`\distribution\layers\\`. Pin a specific pack version by checking out its tag -`layers//` (or pin an `editions/` tag that composes base + this pack at proven -versions — the preferred demo pin). No hardcoded machine-wide literals — everything lands under the demo root. +`\distribution\layers\\`. Fast-forward the clone to `origin/main` (**main IS the +version**) and resolve the layer from the live `layers/INDEX.json` — a feature layer is normally +consumed via an `editions/.json` that composes base + this pack at gate-proven versions. No +hardcoded machine-wide literals — everything lands under the demo root. ```powershell -$packName = "reordering-pricing" # the feature layer you are installing +$packName = "feature-pricing" # the feature layer you are installing $demoRoot = (Get-Location).Path # the demo project root -$dist = "$demoRoot\distribution" # the Distribution checkout (from deserialize-flow §3) +$dist = "$demoRoot\distribution" # the Distribution clone (from deserialize-flow §3) $packDir = "$dist\layers\$packName" -if (-not (Test-Path "$packDir\layer.json")) { - # Not in the current snapshot — clone if needed, then check out the layer's latest tag. - # (Prefer pinning an edition that composes base + this pack.) Override with $env:DW_DISTRIBUTION_REPO. - $repo = if ($env:DW_DISTRIBUTION_REPO) { $env:DW_DISTRIBUTION_REPO } else { "justdynamics/Truvio.Commerce.Distribution" } - if (-not (Test-Path "$dist\.git")) { git clone "https://github.com/$repo" $dist } - $tag = git -C $dist tag --list "layers/$packName/*" | - Sort-Object { [version]($_ -replace "^layers/$packName/",'') } -Descending | Select-Object -First 1 - git -C $dist checkout $tag - Write-Host "Checked out $tag — record it in CUSTOMISATIONS.md (the pin)" +# main IS the version — fast-forward the clone, then resolve the layer from INDEX.json. +if (Test-Path "$dist\.git") { + git -C $dist pull --ff-only origin main +} else { + $repo = if ($env:DW_DISTRIBUTION_REPO) { $env:DW_DISTRIBUTION_REPO } else { "/" } + git clone "https://github.com/$repo" $dist } +$index = Get-Content "$dist\layers\INDEX.json" -Raw | ConvertFrom-Json +$entry = $index.layers | Where-Object { $_.name -eq $packName } +if (-not $entry) { + $tomb = $index.retired | Where-Object { $_.name -eq $packName } + if ($tomb) { throw "Feature layer '$packName' is RETIRED -> use $($tomb.supersededBy)" } + else { throw "Feature layer '$packName' not in INDEX.json" } +} +if ($entry.status -eq 'deprecated') { Write-Warning "$packName is DEPRECATED -> $($entry.supersededBy); prefer the successor for new demos." } +Write-Host "Resolved $packName $($entry.version) on main $(git -C $dist rev-parse --short HEAD) — record the SHA in CUSTOMISATIONS.md" ``` ## 5. Step 2 — Read layer.json @@ -155,13 +162,13 @@ behavior. Record the choice in `CUSTOMISATIONS.md`: the compile is a **declared* line item (named in `layer.json`), not an ad-hoc controller — that is exactly the boundary the zero-custom-code base preserves while letting a feature layer offer more when a demo asks for it. -**Worked example — `reordering-pricing`.** Contract pricing (the per-customer price a buyer sees at cart +**Worked example — `feature-pricing`.** Contract pricing (the per-customer price a buyer sees at cart time) is **native default-provider behavior and needs no compile** — the zero-code headline; author the per-customer `EcomPrices` row (`PriceUserCustomerNumber`) and it resolves ([`../../dw-demo-base/references/foundational/commerce-b2b.md`](../../dw-demo-base/references/foundational/commerce-b2b.md) "Customer-scoped contract prices"). **Quantity-tier enforcement** (bulk-break pricing off `PriceQuantity > 0` rows) is what the shipped `IPriceProvider` adds — the stock cart ignores tier rows until the provider is compiled in -([`../../dw-demo-base/references/foundational/commerce-catalog.md`](../../dw-demo-base/references/foundational/commerce-catalog.md) §2.11). So: install `reordering-pricing` **data-only** to demo contract +([`../../dw-demo-base/references/foundational/commerce-catalog.md`](../../dw-demo-base/references/foundational/commerce-catalog.md) §2.11). So: install `feature-pricing` **data-only** to demo contract pricing; run the §6 opt-in compile **only** when the demo must show qty-tier enforcement. ## 6. Step 3 — Source-drop the .cs and build the host @@ -293,11 +300,11 @@ returns the host to its pre-pack state without touching base content. Pack-specific behaviors and known limitations to expect after install: -- **subscription-orders** ships its own **disabled** `Place recurring orders` scheduled task in its +- **feature-subscription-orders** ships its own **disabled** `Place recurring orders` scheduled task in its fragment. It arrives disabled deliberately — enable it only when the demo actually exercises recurring-order generation, so an idle demo host never fires it. Confirm the task exists (a `configRows` probe) and leave it disabled unless the storyline needs it. -- **reordering-pricing** ships a **compile-optional `IPriceProvider`** (see §5 "The `customCode` +- **feature-pricing** ships a **compile-optional `IPriceProvider`** (see §5 "The `customCode` declaration"): **contract pricing works zero-code** (native default provider) and **quantity-tier enforcement requires the §6 opt-in compile**. Install data-only unless the demo needs qty-tier behavior. It also carries a documented **quick-order known-limitation**: after install the diff --git a/skills/dw-demo-swift/references/styles-assets.md b/skills/dw-demo-swift/references/styles-assets.md index d46fb4b..98e93cb 100644 --- a/skills/dw-demo-swift/references/styles-assets.md +++ b/skills/dw-demo-swift/references/styles-assets.md @@ -12,29 +12,31 @@ Read that section for the asset format and wiring. This file carries the demo-in ## Reference source: `theme-default` in the Distribution -The Distribution ships **one theme layer** — `layers/theme-default/` (kind `theme`) in -`justdynamics/Truvio.Commerce.Distribution`. There is no theme choice and no overlay layers: every -edition composes `theme-default` (`themes: ["default"]`), and customer re-skins start FROM it -([`re-skin.md`](re-skin.md)). `Truvio.Commerce.DemoThemes` is **archived**; the former -`theme-nav-polish` overlay is retired — its header-nav affordance CSS is folded into -`theme-default`'s `default_custom.css`. A theme layer is pure disk-overlay (styles + CSS + assets +The Distribution ships **one theme layer** — `layers/theme-default/` (kind `theme`). There is no +theme choice and no overlay layers: every edition composes `theme-default` (`themes: ["default"]`), +and customer re-skins start FROM it ([`re-skin.md`](re-skin.md)). The former standalone demo-theme +repo is **archived**; the header-nav affordance CSS ships **inside** `theme-default`'s +`default_custom.css`. A theme layer is pure disk-overlay (styles + CSS + assets under `files/`, mirroring the host's `wwwroot\Files\` tree) with **no serialized DB content**, so the -demo's Swift version (from the versions prompt) is only a compatibility check here, not a tag selector. -The layer lives in the demo's Distribution checkout at `\distribution\layers\theme-default\`; -pin it by the tag `layers/theme-default/` (or an edition that composes it — the usual pin). +demo's Swift version (from the versions prompt) is only a compatibility check here, not a version selector. +The layer lives in the demo's Distribution clone at `\distribution\layers\theme-default\`; +it resolves from the live `layers/INDEX.json` on the latest gate-proven `main` (the usual demo consume). ```powershell $demoRoot = (Get-Location).Path -$dist = "$demoRoot\distribution" # the Distribution checkout (from deserialize-flow §3) +$dist = "$demoRoot\distribution" # the Distribution clone (from deserialize-flow §3) $theme = "$dist\layers\theme-default" -if (-not (Test-Path "$theme\layer.json")) { - $repo = if ($env:DW_DISTRIBUTION_REPO) { $env:DW_DISTRIBUTION_REPO } else { "justdynamics/Truvio.Commerce.Distribution" } - if (-not (Test-Path "$dist\.git")) { git clone "https://github.com/$repo" $dist } - $tag = git -C $dist tag --list "layers/theme-default/*" | - Sort-Object { [version]($_ -replace '^layers/theme-default/','') } -Descending | Select-Object -First 1 - git -C $dist checkout $tag - Write-Host "Checked out $tag — record it in CUSTOMISATIONS.md (the theme pin)" +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 { + $repo = if ($env:DW_DISTRIBUTION_REPO) { $env:DW_DISTRIBUTION_REPO } else { "/" } + git clone "https://github.com/$repo" $dist } +$index = Get-Content "$dist\layers\INDEX.json" -Raw | ConvertFrom-Json +if (-not ($index.layers | Where-Object { $_.name -eq 'theme-default' })) { + throw "theme-default absent from INDEX.json — check the retired tombstones for its successor." +} +Write-Host "On main $(git -C $dist rev-parse --short HEAD) — record the commit SHA in CUSTOMISATIONS.md (theme reproducibility stamp)" ``` The layer's `files/` mirrors the host overlay tree — the Style-asset areas plus the default custom