18 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, estimate, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | estimate | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| quick-261005-qvk | 01 | execute | 1 |
|
true |
|
|
|
Purpose: the documented Winter icon-* API on pact.NavigationItem.Icon and SettingsItem.Icon is the contract ported plugins already use; the framework SPA must honour it. Output: updated icons.ts and icons.test.ts, a rebuilt modules/boardwalk/dist, one code commit.
<execution_context>
@/.codex/gsd-core/workflows/execute-plan.md
@/.codex/gsd-core/templates/summary.md
</execution_context>
Phase 10 D-11: navigation icons are lucide names in a curated registry; a small Winter icon-* map lets ported plugins work unchanged; unknown names get a neutral fallback. The map today is only icon-archive, icon-circle, icon-list-ul, icon-tags, icon-user, icon-search, icon-cog, icon-users. iconFor trims the name, looks up icons, then winterIcons then icons again, else Square. own() uses Object.hasOwn so constructor/toString never count.
Call sites already wired (do not change them): PluginRail.vue, SectionPanel.vue, SectionFlyout.vue, SettingsIndexView.vue all pass the server icon string through iconFor().
Package: @lucide/vue 1.17.0 (not lucide-vue-next). Named imports only. Confirmed exports in admin/node_modules/@lucide/vue/dist/lucide-vue.d.ts: Book, Bookmark, Calendar, Clock, Flag, FileText, GraduationCap, Pencil, CircleQuestionMark, File, Folder, Copy, Files, Globe, Mail, House, Database, Info, Leaf, Lock, Key, Link, Star, Heart, Bell, Inbox, Download, Upload, Funnel, Wrench, WandSparkles, LifeBuoy, Network, MessageCircle, TriangleAlert, Box, Briefcase, Building, Languages, Gauge, MapPin, Phone, Camera, Play, Pause, ShoppingCart, CreditCard, Table, LayoutGrid, RefreshCw, ExternalLink, CircleCheck, ListOrdered, ClipboardList, Trophy, Paperclip, Code, Cloud, ChartBar. CircleHelp and HelpCircle are absent in this pin; icon-question-circle maps to CircleQuestionMark (key circle-question-mark). Filter is absent; icon-filter maps to Funnel (key funnel). List, Tags, Image, Plus, Minus, Check, X, Settings, User, Users, Archive, Circle, Puzzle, Trash2, LogOut already sit in icons.
Docs (admin-controllers.md, users-and-permissions.md, settings.md, porting-a-plugin.md, docs/examples/blog/plugin.go) show Icon: "icon-pencil" as Winter examples. They do not claim those names already render and they do not load Font Awesome. Leave those pages unchanged; mapping icon-pencil makes the examples true. Do not add an icon catalogue.
BM Studies names from the task: group icon-graduation-cap, Courses icon-book, Readings icon-bookmark, Meetings icon-calendar, Sessions icon-clock-o, Deadlines icon-flag, Materials icon-file-text-o, Tags icon-tags. Quizzes: icon-question-circle, icon-list.
Closed Winter alias table (Winter name -> lucide key already in, or added to, icons). Keep every alias already present.
BM and documented: icon-graduation-cap -> graduation-cap icon-book -> book icon-bookmark -> bookmark icon-calendar -> calendar icon-clock-o -> clock icon-clock -> clock icon-flag -> flag icon-file-text-o -> file-text icon-file-text -> file-text icon-tags -> tags (exists) icon-pencil -> pencil icon-edit -> pencil icon-question-circle -> circle-question-mark icon-question -> circle-question-mark icon-list -> list (exists)
Already mapped, keep: icon-archive, icon-circle, icon-list-ul, icon-user, icon-search, icon-cog, icon-users.
Common Winter backend (core / RainLab / typical plugin nav and settings): icon-home -> house icon-leaf -> leaf icon-magic -> wand-sparkles icon-life-ring -> life-buoy icon-sitemap -> network icon-copy -> copy icon-files-o -> files icon-envelope -> mail icon-envelope-o -> mail icon-globe -> globe icon-database -> database icon-info-circle -> info icon-exclamation-triangle -> triangle-alert icon-warning -> triangle-alert icon-lock -> lock icon-key -> key icon-star -> star icon-heart -> heart icon-comment -> message-circle icon-comments -> message-circle icon-comments-o -> message-circle icon-folder -> folder icon-folder-o -> folder icon-file -> file icon-file-o -> file icon-picture-o -> image icon-photo -> image icon-plus -> plus icon-minus -> minus icon-check -> check icon-check-circle -> circle-check icon-times -> x icon-trash-o -> trash-2 icon-trash -> trash-2 icon-sign-out -> log-out icon-cogs -> settings icon-group -> users icon-list-ol -> list-ordered icon-list-alt -> clipboard-list icon-dashboard -> gauge icon-tachometer -> gauge icon-th -> layout-grid icon-th-large -> layout-grid icon-table -> table icon-bar-chart -> chart-bar icon-wrench -> wrench icon-cube -> box icon-building -> building icon-briefcase -> briefcase icon-language -> languages icon-download -> download icon-upload -> upload icon-refresh -> refresh-cw icon-filter -> funnel icon-link -> link icon-code -> code icon-cloud -> cloud icon-bell -> bell icon-inbox -> inbox icon-paperclip -> paperclip icon-external-link -> external-link icon-shopping-cart -> shopping-cart icon-credit-card -> credit-card icon-map-marker -> map-pin icon-phone -> phone icon-camera -> camera icon-play -> play icon-pause -> pause icon-trophy -> trophy
Do not add Font Awesome, lucide-vue-next, or any new npm package. Do not change plugin navigation in other repos. Do not commit .planning artifacts. Rebuild dist: hashed files under modules/boardwalk/dist/assets/ will change; commit the whole dist tree with the source.
Existing tests in admin/tests/app/icons.test.ts: lucide names disc-3/tags, aliases icon-archive/icon-cog, Square fallback including constructor/toString/proto, and a loop that every winterIcons value exists in icons. Extend that file; keep those cases.
Verify command that already works in this repo: npm --prefix admin test (vitest run) and npm --prefix admin run typecheck. After a SPA source change, npm --prefix admin run build then scripts/check-admin-dist.sh.
Task 1: Tracer - iconFor maps BM Winter names and icon-pencil onto lucide components admin/src/app/icons.ts, admin/tests/app/icons.test.ts admin/src/app/icons.ts, admin/tests/app/icons.test.ts, admin/node_modules/@lucide/vue/dist/lucide-vue.d.ts (confirm Book, Bookmark, Calendar, Clock, Flag, FileText, GraduationCap, Pencil exist as declare const) - iconFor('icon-graduation-cap') returns GraduationCap, not Square - iconFor('icon-book') returns Book; iconFor('icon-bookmark') Bookmark; iconFor('icon-calendar') Calendar; iconFor('icon-clock-o') Clock; iconFor('icon-flag') Flag; iconFor('icon-file-text-o') FileText; iconFor('icon-tags') Tags; iconFor('icon-pencil') Pencil - lucide keys work too: iconFor('book') returns Book, iconFor(' graduation-cap ') returns GraduationCap - Existing cases still pass: icon-archive, icon-cog, unknown-icon-name, icon-unknown, empty/null/undefined, constructor, toString, __proto__ all unchanged - Every winterIcons value is a key in icons In admin/tests/app/icons.test.ts, extend the Winter-alias case (and add lucide-name cases as needed) so the BM names and icon-pencil assert the matching @lucide/vue components. Import those components from @lucide/vue the same way the file already imports Archive, Disc3, Settings, Square, Tags. Run the file and confirm the new assertions fail because the aliases and registry keys are missing.Then in admin/src/app/icons.ts add named imports Book, Bookmark, Calendar, Clock, Flag, FileText, GraduationCap, Pencil from @lucide/vue (keep the existing named-import list; do not switch to a namespace import). Register kebab-case keys book, bookmark, calendar, clock, flag, file-text, graduation-cap, pencil on icons. Add winterIcons aliases from the BM-and-documented block in context (icon-graduation-cap, icon-book, icon-bookmark, icon-calendar, icon-clock-o, icon-clock, icon-flag, icon-file-text-o, icon-file-text, icon-pencil, icon-edit; icon-tags already exists). Do not change iconFor, own, or fallbackIcon. Do not add a new dependency. Do not edit Vue shell components; they already call iconFor.
Leave Quizzes and the rest of the common Winter table for Task 2. Do not rebuild dist yet. Do not commit yet. cd /media/nvme/dev/golem15/summercms.io/summercms/summercms.go && npm --prefix admin test -- tests/app/icons.test.ts iconFor resolves every BM Studies Winter name and icon-pencil to the matching lucide component; unknown names still return Square; the existing constructor/toString fallback cases still pass; nothing committed.
Task 2: Map Quizzes and the closed Winter backend alias table, rebuild the embedded SPA, commit once admin/src/app/icons.ts, admin/tests/app/icons.test.ts, modules/boardwalk/dist/ admin/src/app/icons.ts, admin/tests/app/icons.test.ts, scripts/check-admin-dist.sh, docs/backend/admin-spa.md (confirm it does not claim icon names already render; do not edit docs) - iconFor('icon-question-circle') returns CircleQuestionMark; iconFor('icon-list') returns List - iconFor('icon-home') returns House; iconFor('icon-magic') WandSparkles; iconFor('icon-life-ring') LifeBuoy; iconFor('icon-sitemap') Network; iconFor('icon-envelope-o') Mail; iconFor('icon-copy') Copy - Every alias in the closed table in context is present in winterIcons and resolves through iconFor to a defined lucide component - iconFor('icon-unknown') and iconFor('constructor') still return Square - typecheck passes; a fresh Vite build matches modules/boardwalk/dist Add the remaining named @lucide/vue imports from the closed table in context (CircleQuestionMark, File, Folder, Copy, Files, Globe, Mail, House, Database, Info, Leaf, Lock, Key, Link, Star, Heart, Bell, Inbox, Download, Upload, Funnel, Wrench, WandSparkles, LifeBuoy, Network, MessageCircle, TriangleAlert, Box, Briefcase, Building, Languages, Gauge, MapPin, Phone, Camera, Play, Pause, ShoppingCart, CreditCard, Table, LayoutGrid, RefreshCw, ExternalLink, CircleCheck, ListOrdered, ClipboardList, Trophy, Paperclip, Code, Cloud, ChartBar). Register each as its lucide kebab-case key on icons (circle-question-mark, file, folder, copy, files, globe, mail, house, database, info, leaf, lock, key, link, star, heart, bell, inbox, download, upload, funnel, wrench, wand-sparkles, life-buoy, network, message-circle, triangle-alert, box, briefcase, building, languages, gauge, map-pin, phone, camera, play, pause, shopping-cart, credit-card, table, layout-grid, refresh-cw, external-link, circle-check, list-ordered, clipboard-list, trophy, paperclip, code, cloud, chart-bar). Skip keys already in the map (list, image, plus, minus, check, x, settings, users, puzzle, trash-2, log-out, user-plus, circle-alert, tags, archive, circle, search, user).Add every remaining winterIcons alias from the closed table. Do not invent names outside that table. Do not map a Winter name onto a lucide export that is not in the declare-const list above (in particular not CircleHelp). Keep Square as the fallback. Keep named imports only.
Extend admin/tests/app/icons.test.ts: keep the Task 1 BM/pencil cases; add Quizzes icon-question-circle and icon-list; add at least icon-home, icon-magic, icon-life-ring, icon-sitemap, icon-envelope-o, icon-copy, icon-leaf, icon-cogs, icon-edit; keep the loop over Object.entries(winterIcons); keep the unknown/empty/inherited fallback cases.
Do not edit docs/ or plugin navigation in other repos. Do not add Font Awesome or any package to admin/package.json.
Then from the repository root run npm --prefix admin run typecheck, npm --prefix admin test -- tests/app/icons.test.ts, npm --prefix admin run build (writes modules/boardwalk/dist), and scripts/check-admin-dist.sh. If dist hashes change, stage the new assets and any removed old hashed files.
Make ONE commit containing only admin/src/app/icons.ts, admin/tests/app/icons.test.ts, and modules/boardwalk/dist/ (index.html plus assets). Message: fix(admin): map Winter icon-* names onto lucide for plugin navigation. No co-author line and no session attribution trailer. Do not stage .planning files or other dirty files. cd /media/nvme/dev/golem15/summercms.io/summercms/summercms.go && npm --prefix admin run typecheck && npm --prefix admin test -- tests/app/icons.test.ts && scripts/check-admin-dist.sh && bash -c 'set -e; msg=$(git log -1 --format=%B); files=$(git show --name-only --format= HEAD); ! grep -qi -e co-authored-by -e claude-session <<< "$msg"; echo "$files" | grep -q "admin/src/app/icons.ts"; echo "$files" | grep -q "admin/tests/app/icons.test.ts"; echo "$files" | grep -q "modules/boardwalk/dist/"' Quizzes and the closed Winter table resolve through iconFor to lucide components; unknown names remain Square; typecheck, icons tests and check-admin-dist.sh pass; HEAD is a single code commit of icons source, tests and the dist tree, with no co-author or session trailer and no planning files.
<threat_model>
Trust Boundaries
| Boundary | Description |
|---|---|
| admin API navigation/settings JSON -> SPA iconFor | Plugin-supplied icon strings are untrusted labels; they must never become arbitrary JS object members or extra network loads |
STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|---|---|---|---|---|---|
| T-qvk-01 | Tampering / elevation via prototype keys | iconFor / own() | medium | mitigate | Keep Object.hasOwn lookup; tests continue to assert constructor, toString and proto return Square |
| T-qvk-02 | Information disclosure | lucide named imports | low | mitigate | Named imports only so the bundle does not include the entire @lucide/vue set; no Font Awesome webfont fetch |
| T-qvk-SC | Tampering | npm/pip/cargo/go installs | low | accept | No new package; only additional named exports from the already-pinned @lucide/vue 1.17.0 |
| </threat_model> |
<success_criteria>
- BM Studies side-menu Winter names and Quizzes icon-question-circle / icon-list render lucide glyphs instead of empty squares once the binary embeds the new dist.
- Documented icon-pencil examples become true without editing docs.
- Unknown Winter or lucide names still show Square.
- One framework commit; no plugin-repo changes. </success_criteria>