Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Apply a downstream brand to a PlatformPlatform fork. Edits one config file, drops in eight supplied logo assets, renames the solution and CLI to the new brand, and rotates UserSecretsId across every csproj. Skips all other source files. Use once per downstream fork after cloning, or to re-flip a brand later.
.claude/skills/platformplatform-rebrand/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-05 | ✗→✓ | ▲ Improved | 217% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 152% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 127% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 230% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 169% | 0% |
Apply a downstream brand to a PlatformPlatform fork. The rebrand is intentionally narrow: brand values live in one JSONC file, logo assets live at canonical paths, and a handful of names (solution file, CLI alias, UserSecretsId, Docker prefix) flip in lockstep. Source code is not touched.
This skill expects the upstream canonical layout — primary logos named logo-{light,dark}-88.png and square marks named logo-mark-{light,dark}-192.png. If you are on an older PlatformPlatform that still uses logo-wrap-* or -512 mark names, the upstream needs the one-time rename first.
bashgit status --porcelain
Stop if there is anything staged or unstaged. Rebrand touches many files at once and a mid-rebrand merge with unrelated changes is hard to untangle.
Use AskUserQuestion to collect all values up front. Do not write anything until every value is in hand — otherwise a partial rebrand leaves the fork in a broken state.
Required inputs:
Acme.pp. Example: acme. No spaces, no dashes inside the binary name.PlatformPlatform.slnx. Example: Acme (yielding Acme.slnx).@example.com (leading @ required). Classifies users as internal for telemetry segmentation. BackOffice access is controlled by Entra ID, not by this setting.tagline.web.<locale> — shown in the landing page footer (consumer reads the active Lingui locale).tagline.mail.<locale> — shown in the email footer (renderer reads the template's locale).The two maps must list the SAME locales (backend fails loud at startup if they diverge); the en-US value is REQUIRED. Collect en-US and da-DK for both channels — for downstream brands the web and mail copy will often differ (e.g. the email leans toward the recipient context). For PlatformPlatform itself, web == mail.
branding.primaryColor: light, lightForeground, dark, darkForeground. Drives the "button color" and its on-color text in both light and dark modes. The value flows through theme.css via an inline <style> block injected per HTML template (--brand-primary / --brand-primary-foreground CSS variables); --primary and --sidebar-primary read those. Pick foregrounds with enough contrast against the primary — that is a deliberate brand-owner decision, not auto-derived.branding.themeColor.light / branding.themeColor.dark tint the mobile browser/PWA toolbar (iOS Dynamic Island "tint band"); the frontend swaps the <meta name="theme-color"> value at runtime to match the resolved theme. branding.themeColor.light is the install-time fallback baked into manifest.json (PWA spec is single-valued). branding.backgroundColor is the PWA splash-screen background while the SPA boots.branding.emailHeaderBackground.branding.showAddToHomescreen boolean. When true the user-facing app renders the iOS "Add to Home Screen" install prompt (it self-limits to iOS Safari outside an installed PWA); false suppresses it entirely. BackOffice never shows the prompt regardless.Required logo assets (user provides absolute paths to eight files):
| Spec | Canonical path written to | | --- | --- | | Primary logo, dark ink on transparent, 88px tall | application/shared-webapp/ui/images/logo-light-88.png | | Primary logo, white ink on transparent, 88px tall | application/shared-webapp/ui/images/logo-dark-88.png | | Square icon mark for light bg, transparent, 192x192 | application/shared-webapp/ui/images/logo-mark-light-192.png | | Square icon mark for dark bg, transparent, 192x192 | application/shared-webapp/ui/images/logo-mark-dark-192.png | | Favicon, multi-res 16/32/48 | application/main/WebApp/public/favicon.ico AND application/account/BackOffice/public/favicon.ico | | Apple touch icon, light variant, 180x180, solid background | application/main/WebApp/public/apple-touch-icon.png AND application/account/BackOffice/public/apple-touch-icon.png | | Apple touch icon, dark variant, 180x180, solid background (iOS 13+ picks this when the user is in dark mode via the media="(prefers-color-scheme: dark)" link in index.html) | application/main/WebApp/public/apple-touch-icon-dark.png AND application/account/BackOffice/public/apple-touch-icon-dark.png | | Email banner, 1200x184, transparent PNG, logo centered (the logo occupies a 640x88 area with 280px transparent padding left/right and 48px top/bottom). Renders full-bleed at 600x92 and scales down on mobile; the email header background shows through the transparent areas. | application/main/WebApp/public/email/logo-1200x184.png |
Optional hero imagery (single shared pair, rendered side-by-side with the login/signup form on /login, /signup, and BackOffice login):
| Spec | Canonical path written to | | --- | --- | | Hero image, 1000x760 aspect, WebP, full quality (rendered with fetchPriority="high") | application/shared-webapp/ui/images/hero-desktop-xl.webp | | Hero image LQIP placeholder, same aspect, heavily compressed WebP (~1 KB; loaded as background-image until the XL paints) | application/shared-webapp/ui/images/hero-desktop-blur.webp |
The favicon and apple-touch inputs each fan out to two destination paths. The hero pair is single-source: both the user-facing WebApp and the BackOffice login render the same image — replace it once.
Open application/platform-settings.jsonc and replace values in identity, branding, and socialLinks with collected inputs. Keep comments on their own lines (the frontend build strips whole-line // comments only).
Copy each of the eight user-supplied files to its canonical path. Filenames never change. Favicon and apple-touch fan out to both WebApp and BackOffice paths — copy the same source file to both destinations.
The email banner is a plain drop-in: overwrite logo-1200x184.png in place. It is a fixed 1200x184 transparent slot rendered by Header.tsx — no .tsx edit, no filename change.
Verify each destination exists and is the size the user supplied:
bashls -la application/shared-webapp/ui/images/logo-*.png application/main/WebApp/public/favicon.ico application/main/WebApp/public/apple-touch-icon.png application/account/BackOffice/public/favicon.ico application/account/BackOffice/public/apple-touch-icon.png application/main/WebApp/public/email/logo-1200x184.png
bashgit mv application/PlatformPlatform.slnx application/<SolutionName>.slnx git mv application/PlatformPlatform.slnx.DotSettings application/<SolutionName>.slnx.DotSettings
Update both solution-filter files to point at the new path:
application/main/Main.slnf — "path": "..\\<SolutionName>.slnx"application/account/Account.slnf — sameEdit developer-cli/DeveloperCli.csproj:
xml<AssemblyName><cli-alias></AssemblyName>
This single rename cascades automatically via Configuration.AliasName (which reads Assembly.GetExecutingAssembly().GetName().Name). The shell alias, the per-CLI config file ({alias}.json), the git-hooks consent file, the change-detection env var ({ALIAS}_SKIP_CHANGE_DETECTION), and every user-facing install message all flip without further edits.
Every .csproj carrying a <UserSecretsId>platformplatform-<old-guid></UserSecretsId> element needs a fresh GUID and the new alias prefix.
Find them:
bashgrep -rln "<UserSecretsId>platformplatform-" application developer-cli
Generate ONE new GUID and use the SAME <UserSecretsId><cli-alias>-<new-guid></UserSecretsId> value in every match. All projects must share the identical UserSecretsId — secrets live at ~/.microsoft/usersecrets/<id>/secrets.json and every project reads from that single store. Different GUIDs would split the store per project and nothing would resolve secrets.
Existing user secret stores on disk (~/.microsoft/usersecrets/) are not migrated — developers may need to re-run any local secret setup after the rotation.
Set development.dockerVolumePrefix in platform-settings.jsonc to the product name lowercased, with spaces replaced by hyphens (e.g. productName "Acme Foo Bar" -> "acme-foo-bar"). The Aspire AppHost (which names the volumes) and the developer CLI's stop command (which removes them) both read this single value through DockerVolumeNaming.ResolveVolumePrefix, so it keeps each fork's volumes isolated on a shared machine. Use the product name, not the CLI alias — the volume identity tracks the brand.
Replace solution-file references in every workflow:
bashgrep -rln "PlatformPlatform\.slnx" .github/workflows
Files known to reference it: code-style.yml, app-gateway.yml. Replace PlatformPlatform.slnx with <SolutionName>.slnx. Also update the human-readable label in code-style.yml (e.g. full backend (PlatformPlatform.slnx) -> full backend (<SolutionName>.slnx)).
cloud-infrastructure/cluster/deploy-cluster.sh and cloud-infrastructure/modules/container-app.bicep reference ghcr.io/platformplatform/quickstart:latest as a default image. This is an upstream-provided empty hello-world container used during initial Bicep deployment, before the downstream's own container registry exists. Chicken-and-egg: Bicep creates the registry, but the first deployment needs a placeholder image. Downstream forks keep using the upstream image. Do not touch these files.
Illustrative comments in cloud-infrastructure/ and .github/workflows/ use example.com (RFC 2606 reserved domain) as placeholder sender domains. Nothing to change.
The skill assumes the codebase reaches every brand-coupled value through configuration (platform-settings.jsonc, Configuration.AliasName, the dockerVolumePrefix constant, the new UserSecretsId). If something hardcodes the old brand, that is a centralization gap upstream — surface it, do not silently patch.
Run these greps across the whole repo (excluding build artifacts):
bashgrep -rln "PlatformPlatform" . \ --exclude-dir=node_modules --exclude-dir=bin --exclude-dir=obj --exclude-dir=artifacts \ --exclude-dir=dist --exclude-dir=.turbo --exclude-dir=.git grep -rln "platformplatform" . \ --exclude-dir=node_modules --exclude-dir=bin --exclude-dir=obj --exclude-dir=artifacts \ --exclude-dir=dist --exclude-dir=.turbo --exclude-dir=.git grep -rln "\\bpp\\b" . \ --include="*.cs" --include="*.csproj" --include="*.yml" --include="*.sh" --include="*.ts" --include="*.tsx"
Expected remaining hits after the rebrand:
README.md, LICENSE, CODEOWNERS, CONTRIBUTING.md — out of scope for this skill (the rewrite is project-specific); not a brand-leak.cloud-infrastructure/cluster/deploy-cluster.sh and cloud-infrastructure/modules/container-app.bicep — the bootstrap quickstart image, left intentionally (see step 10).Anything else is a finding. For each hit:
pull-platformplatform-changes).Run in order:
build --quiet — full backend + frontend build. Any failure here means the rebrand broke something the rebrand itself was supposed to leave untouched. Investigate before continuing.aspire-restart — restart the AppHost. Should print the new product name in startup logs.e2e --smoke --quiet — confirms the SPA renders, login works, and the new product name appears where the tests assert it.Final check:
bashgit diff --stat
The diff should only touch:
application/platform-settings.jsoncapplication/shared-webapp/ui/images/logo-*.pngapplication/main/WebApp/public/favicon.icoapplication/main/WebApp/public/apple-touch-icon.pngapplication/main/WebApp/public/apple-touch-icon-dark.pngapplication/account/BackOffice/public/favicon.icoapplication/account/BackOffice/public/apple-touch-icon.pngapplication/account/BackOffice/public/apple-touch-icon-dark.pngapplication/main/WebApp/public/email/logo-1200x184.pngapplication/<SolutionName>.slnx (renamed from PlatformPlatform.slnx)application/<SolutionName>.slnx.DotSettings (renamed)application/main/Main.slnfapplication/account/Account.slnfdeveloper-cli/DeveloperCli.csproj*.csproj with a rotated UserSecretsId.cs files that read the UserSecretsId prefix.github/workflows/*.ymlSource code outside this list should be untouched. If anything else shows up in the diff, stop and investigate before committing.
The skill does not touch:
README.md, LICENSE, CODEOWNERS, CONTRIBUTING.md — these are not required for the fork to run, and the correct rewrite is project-specific (license terms, README narrative, code-owner team names). Out of scope because the skill does not know how, not because they should stay.package.json workspace scopes — already brand-neutral (@repo/*), nothing to do.PlatformPlatform.*. Nothing to do.A downstream that wants to rewrite README/LICENSE/CODEOWNERS does that in a separate pass.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 1,772 | 4,177 | +136% | 1 | 1 | 0% | 229 | 4,188 | +1729% | 0 | 0 | — |
case-02 | fail→fail | 5,222 | 4,902 | -6% | 1 | 1 | 0% | 247 | 4,149 | +1580% | 0 | 0 | — |
case-03 | fail→fail | 8,250 | 4,307 | -48% | 1 | 1 | 0% | 1,410 | 4,223 | +200% | 0 | 0 | — |
case-04 | fail→fail | 7,152 | 3,024 | -58% | 1 | 1 | 0% | 1,062 | 4,299 | +305% | 0 | 0 | — |
case-05 | fail→pass | 9,141 | 3,473 | -62% | 1 | 1 | 0% | 1,449 | 4,593 | +217% | 0 | 0 | — |
case-06 | fail→pass | 12,147 | 3,442 | -72% | 1 | 1 | 0% | 1,822 | 4,596 | +152% | 0 | 0 | — |
case-07 | fail→pass | 12,685 | 2,702 | -79% | 1 | 1 | 0% | 1,951 | 4,433 | +127% | 0 | 0 | — |
case-08 | fail→pass | 7,697 | 3,567 | -54% | 1 | 1 | 0% | 1,393 | 4,596 | +230% | 0 | 0 | — |
case-09 | fail→pass | 10,547 | 3,293 | -69% | 1 | 1 | 0% | 1,675 | 4,506 | +169% | 0 | 0 | — |
case-10 | fail→pass | 11,558 | 2,557 | -78% | 1 | 1 | 0% | 1,879 | 4,350 | +132% | 0 | 0 | — |
case-11 | pass→pass | 8,548 | 2,297 | -73% | 1 | 1 | 0% | 1,399 | 4,393 | +214% | 0 | 0 | — |
case-12 | fail→pass | 9,046 | 2,711 | -70% | 1 | 1 | 0% | 1,510 | 4,465 | +196% | 0 | 0 | — |
case-13 | fail→pass | 11,408 | 5,447 | -52% | 1 | 1 | 0% | 1,738 | 4,918 | +183% | 0 | 0 | — |
case-14 | fail→pass | 9,872 | 2,698 | -73% | 1 | 1 | 0% | 1,751 | 4,475 | +156% | 0 | 0 | — |
case-15 | fail→pass | 6,694 | 2,342 | -65% | 1 | 1 | 0% | 1,135 | 4,404 | +288% | 0 | 0 | — |
case-16 | fail→pass | 9,133 | 2,733 | -70% | 1 | 1 | 0% | 1,399 | 4,481 | +220% | 0 | 0 | — |
case-17 | fail→pass | 12,142 | 4,688 | -61% | 1 | 1 | 0% | 1,786 | 4,714 | +164% | 0 | 0 | — |
case-18 | fail→pass | 8,791 | 2,308 | -74% | 1 | 1 | 0% | 1,448 | 4,361 | +201% | 0 | 0 | — |
case-19 | fail→pass | 15,819 | 5,153 | -67% | 1 | 1 | 0% | 2,577 | 4,907 | +90% | 0 | 0 | — |
case-20 | fail→fail | 9,726 | 12,458 | +28% | 1 | 1 | 0% | 1,708 | 5,295 | +210% | 0 | 0 | — |
case-21 | fail→pass | 9,863 | 3,720 | -62% | 1 | 1 | 0% | 1,609 | 4,610 | +187% | 0 | 0 | — |
case-22 | pass→pass | 10,933 | 3,297 | -70% | 1 | 1 | 0% | 1,833 | 4,507 | +146% | 0 | 0 | — |
case-23 | fail→pass | 13,511 | 3,462 | -74% | 1 | 1 | 0% | 2,227 | 4,552 | +104% | 0 | 0 | — |
case-24 | pass→pass | 9,691 | 4,196 | -57% | 1 | 1 | 0% | 1,571 | 4,629 | +195% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 24 cases were attempted, and 23 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +67 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.