Background
Follow-up from #28 (review comment). #28 externalized the target architecture registry so the choice grid is now fully data-driven (TARGETS.map(...)). This issue tracks the remaining follow-up and is not a regression in #28.
Problem
There's a second axis — placement (install-in-place vs export/download) — that's still inferred from architecture identity in the renderer rather than declared in the manifest. This is the same "one target, many edits" smell that #20 was about, just on the delivery dimension:
src/Library.tsx:496 — default useState<SkillArchitecture>("scout")
src/Library.tsx:966 — architecture === "cowork" ? "export" : "install"
src/Library.tsx:1143-1161 — "Added to Scout" / "Add to Scout" copy + the Scout-only Download button, gated on architecture === "scout"
Adding a new target (e.g. "Copilot Studio (skill, export-only)") today still requires touching these renderer branches.
Proposed approach
Lift placement into the existing target manifest as capabilities rather than identities:
{ id: "scout", installTargetLabel: "Scout",
targets: [
{ kind: "skill", placements: ["install", "export"] },
{ kind: "automation", placements: ["install"] },
] }
{ id: "cowork", installTargetLabel: "Microsoft 365 Copilot",
targets: [
{ kind: "skill", placements: ["export"] },
] }
Then the renderer derives:
- default = first enabled target
- placement buttons =
target.placements
- copy =
installTargetLabel
…so no architecture string literals remain in src/. Adding "Copilot Studio (skill, export-only)" becomes one manifest entry + one catalogue file, with no renderer edits.
Scope / boundary (where to stop)
Keep the same UI-safe boundary already enforced for catalogue text:
- Config = data/capabilities — labels, enabled, kinds, placements, icon key.
- Behavior stays in code —
skill vs automation genuinely branch (different builder, plan schema, IPC, render target), so those remain typed primitives; the manifest just decides whether each is offered.
- Don't let config turn into a UI DSL. For any genuinely bespoke per-architecture UX later, a component registry keyed by
SkillArchitecture beats piling more fields into config.
References
Background
Follow-up from #28 (review comment). #28 externalized the target architecture registry so the choice grid is now fully data-driven (
TARGETS.map(...)). This issue tracks the remaining follow-up and is not a regression in #28.Problem
There's a second axis — placement (install-in-place vs export/download) — that's still inferred from architecture identity in the renderer rather than declared in the manifest. This is the same "one target, many edits" smell that #20 was about, just on the delivery dimension:
src/Library.tsx:496— defaultuseState<SkillArchitecture>("scout")src/Library.tsx:966—architecture === "cowork" ? "export" : "install"src/Library.tsx:1143-1161— "Added to Scout" / "Add to Scout" copy + the Scout-only Download button, gated onarchitecture === "scout"Adding a new target (e.g. "Copilot Studio (skill, export-only)") today still requires touching these renderer branches.
Proposed approach
Lift placement into the existing target manifest as capabilities rather than identities:
Then the renderer derives:
target.placementsinstallTargetLabel…so no architecture string literals remain in
src/. Adding "Copilot Studio (skill, export-only)" becomes one manifest entry + one catalogue file, with no renderer edits.Scope / boundary (where to stop)
Keep the same UI-safe boundary already enforced for catalogue text:
skillvsautomationgenuinely branch (different builder, plan schema, IPC, render target), so those remain typed primitives; the manifest just decides whether each is offered.SkillArchitecturebeats piling more fields into config.References