feat(themes): let installed packs select loader symbols - #7650
Conversation
Design Review (Fable 5, fork) — ✅ PASSDesign-level review of Design-Verdict: PASS Closed name-allowlist at the right layer — install-time validation, descriptor surfacing, fail-closed frontend mapping — preserves the installed-pack trust boundary while reusing the carousel. The shape holds up under scrutiny: the optional manifest field is backward compatible, precedence keeps the compiled [DESIGN-REVIEWED] 1bf22c2 |
UX Review (Fable 5, fork) — ✅ PASSUX-level review of The base error-message style matches ( UX-Verdict: PASS Author-facing errors name field, rule, and fix in sibling phrasing; end users just see their theme's symbols, with fail-closed ghost-pose fallback. [UX-REVIEWED] 1bf22c2 |
First Principles Review (Fable 5, fork) — 🟡 CONCERNSPremise-level review of Base-tree evidence is now complete. Writing the review. First-Principles-Verdict: CONCERNS One undeclared layer: the read path now 500s a disk-edited pack that the PR's own descriptor filter and frontend fallback already degrade gracefully. What this change shipsIntent: let installed theme packs pick the chat-loader symbols without granting them code execution — an ADDITION (claims "Fixes #7632").
Watch
Subtractions
[FIRST-PRINCIPLES-REVIEWED] 1bf22c2 |
Opus 4.8 Review (fork) — ✅ no blocking findingsReviewed Review detailsThe change is well-guarded end to end: the backend validates on install and re-filters on the read path, No findings. [OPUS-REVIEWED] 1bf22c2 |
GPT 5.6 Review (fork) — ✅ no blocking findingsReviewed Review detailsNo findings. |
9d92c4c to
2e4e6e0
Compare
The parametrized installer regression now includes
|
2e4e6e0 to
098831a
Compare
098831a to
bac707c
Compare
cdb2a7c to
1bf22c2
Compare
1bf22c2 to
e83a4e2
Compare
…xed loader Installed theme packs could only pick loader icons from a fixed 8-symbol allowlist (PR #7650); custom art or motion needed compiled-in code. Extend the installed-pack surface: loader/*.png|webp (4-8 raster images, Level 1) feed the stock carousel, and loader/loader.html (Level 2) is a fully custom loader served under the overlay CSP in a sandboxed, click-through iframe. resolveLoader precedence: compiled loader -> pack loader.html -> pack images -> loaderIcons -> default poses.
…xed loader Installed theme packs could only pick loader icons from a fixed 8-symbol allowlist (PR #7650); custom art or motion needed compiled-in code. Extend the installed-pack surface: loader/*.png|webp (4-8 raster images, Level 1) feed the stock carousel, and loader/loader.html (Level 2) is a fully custom loader served under the overlay CSP in a sandboxed, click-through iframe. resolveLoader precedence: compiled loader -> pack loader.html -> pack images -> loaderIcons -> default poses.
…xed loader Installed theme packs could only pick loader icons from a fixed 8-symbol allowlist (PR #7650); custom art or motion needed compiled-in code. Extend the installed-pack surface: loader/*.png|webp (4-8 raster images, Level 1) feed the stock carousel, and loader/loader.html (Level 2) is a fully custom loader served under the overlay CSP in a sandboxed, click-through iframe. resolveLoader precedence: compiled loader -> pack loader.html -> pack images -> loaderIcons -> default poses.
…xed loader Installed theme packs could only pick loader icons from a fixed 8-symbol allowlist (PR #7650); custom art or motion needed compiled-in code. Extend the installed-pack surface: loader/*.png|webp (4-8 raster images, Level 1) feed the stock carousel, and loader/loader.html (Level 2) is a fully custom loader served under the overlay CSP in a sandboxed, click-through iframe. resolveLoader precedence: compiled loader -> pack loader.html -> pack images -> loaderIcons -> default poses.
…xed loader Installed theme packs could only pick loader icons from a fixed 8-symbol allowlist (PR #7650); custom art or motion needed compiled-in code. Extend the installed-pack surface: loader/*.png|webp (4-8 raster images, Level 1) feed the stock carousel, and loader/loader.html (Level 2) is a fully custom loader served under the overlay CSP in a sandboxed, click-through iframe. resolveLoader precedence: compiled loader -> pack loader.html -> pack images -> loaderIcons -> default poses.
…xed loader Installed theme packs could only pick loader icons from a fixed 8-symbol allowlist (PR #7650); custom art or motion needed compiled-in code. Extend the installed-pack surface: loader/*.png|webp (4-8 raster images, Level 1) feed the stock carousel, and loader/loader.html (Level 2) is a fully custom loader served under the overlay CSP in a sandboxed, click-through iframe. resolveLoader precedence: compiled loader -> pack loader.html -> pack images -> loaderIcons -> default poses.
Problem / Motivation
Compiled themes can customize the chat loading indicator through
registerThemeBranding(), but installedtheme.jsonpacks cannot use that TypeScript registration seam.An installed theme can configure most of the dashboard experience while still being forced to inherit the default Kiro ghost poses. Theme authors currently have no safe, portable way to keep the existing carousel and change only its symbols.
Why it matters
Installed themes are the user-facing distribution format. They can be installed from a local folder or GitHub without rebuilding Kiro Crew.
Loader customization should work in that format without allowing theme packs to inject React components, SVG markup, or executable code.
What changed (motivation → approach → change)
The existing carousel already handles animation, icon sizing, distinct sampling, pool changes, and reduced motion. This change keeps that implementation and adds a declarative installed-pack input.
Level 1 and Level 2 manifests may now declare:
The backend:
The frontend:
loadercomponents at the highest precedence;ChatFooterusable outsideThemeProviderfor isolated tests.The branch now builds on upstream #7722, which owns the
ArtifactsPagememoization cleanup that restored the frontend lint warning budget, and #7761, which repaired the inherited redactor-census baseline.The authoring skill, frontend contract, and themes system specification document the new field and trust boundary.
Tests
Backend coverage verifies valid Level 1 declarations, Level 0 rejection, type and size bounds, unknown-name and duplicate rejection, backend/frontend allowlist parity, and propagation through
GET /api/themes/{slug}.Frontend coverage verifies stock-symbol resolution, malformed-descriptor fallback, compiled custom-loader precedence, and existing carousel/turn-state behavior.
Validation completed locally:
/local/homeownership, sensitive-path classification, long Unix-socket paths, and unrelated existing testsgit range-diffconfirms current commite83a4e24a92bfaa4eb6351e3204d150cd042b0ecis feature-patch-equivalent to1bf22c2132bbc1c272a9fd9974cd3666ad7bf4eeand builds on current mainManual verification
Verified through the real Settings flow in an isolated Kiro Crew pod:
Screenshots / video
Watch the real-turn loader recording
Evidence was captured from the isolated worktree pod with a real server and agent turn, then published on an immutable evidence-only ref. No fixture or binary evidence is included in the feature commit.
Related Issues
Related prior work: #894, #952, #5549.
Fixes #7632
Checklist
feat|fix|docs|refactor|perf|test|chore|ci|build|revert: ...)