Files
Javier Calvarro Nelson 760ff44ffc Add dotnet-blazor plugin with activation fixes from PR #357
Integrates the 9 Blazor skills from PR #357 (javiercn/author-component) with
fixes for the systemic activation failure (all skills showing NOT ACTIVATED):

Key fixes:
- Improve skill descriptions with explicit USE FOR/DO NOT USE FOR patterns that
  match the activation keyword style used by working skills in the repo
- Add license: MIT to all skill frontmatter per repo conventions
- Add marketplace entries (.github/plugin, .claude-plugin, .cursor-plugin)
- Add CODEOWNERS entry for @dotnet/aspnet
- Add README.md plugin entry
- Add both playwright and microsoft-docs MCP servers to allowed-external-deps.txt

MCP server analysis:
- The eval framework (AgentRunner.cs:302-307) already skips non-stdio MCP servers
- playwright (type: 'local') and microsoft-docs (type: 'http') are both filtered
  out during evaluation, so they don't affect eval results
- Root cause of NOT ACTIVATED is likely description quality, not MCP servers

Skills included:
- author-component: Blazor component authoring patterns
- collect-user-input: Forms, validation, EditForm patterns
- configure-auth: Authentication/authorization across render modes
- coordinate-components: Cross-component state sharing
- create-blazor-project: New project scaffolding with correct render modes
- fetch-and-send-data: API calls and async data lifecycle
- plan-ui-change: Component decomposition planning
- support-prerendering: Prerender-to-interactive handoff
- use-js-interop: IJSRuntime, IJSObjectReference, .razor.js modules

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-22 13:22:43 +02:00

101 lines
5.2 KiB
YAML

scenarios:
- name: "Equipment inventory loaded once"
prompt: |
I have an existing Blazor Web App with Interactive Server (per-page).
I need an interactive /inventory page that shows a table of equipment items
(Id, Name, Location, LastInspected date). The data comes from an EquipmentService
you should create (just return fake data, 5-10 items).
The page should show "Loading inventory..." until data is ready, then display
the table. The important thing is that when the page first loads I see the HTML
instantly (fast initial paint), but the equipment data should NOT be fetched
twice — once during the initial server render and again when the page becomes
interactive. It should only call the service once and carry that data across.
assertions:
- type: "file_exists"
path: "**/Inventory.razor"
- type: "file_contains"
path: "**/Inventory.razor"
value: "PersistentState"
- type: "file_contains"
path: "**/Inventory.razor"
value: "InteractiveServer"
- type: "file_contains"
path: "**/Inventory.razor"
value: "??="
rubric:
- "Uses [PersistentState] attribute on the data property and the ??= pattern in OnInitializedAsync to avoid duplicate data fetches during prerender-to-interactive handoff"
- "Component correctly shows loading state when data is null, then displays the equipment table when loaded"
- "Does NOT disable prerendering — preserves the fast initial HTML response while avoiding duplicate loads"
- "Creates a proper EquipmentService registered in DI, not inline data generation"
timeout: 600
- name: "Privacy page that reads cookies in a global interactive app"
prompt: |
I have an existing Blazor Web App with Interactive Server (global interactivity).
I need a /privacy page that displays the privacy policy and reads a "consent"
cookie from the incoming HTTP request to show whether the user has already
accepted cookies.
The challenge is that the app uses global interactivity, but this page needs
to read HttpContext (for the cookie). HttpContext is only available during
static server-side rendering, not in interactive components. So this specific
page needs to opt out of interactive rendering and stay fully static, while
the rest of the app remains interactive.
Make sure App.razor is updated so that the routing knows to skip the interactive
render mode for this page.
assertions:
- type: "file_contains"
path: "**/Privacy.razor"
value: "ExcludeFromInteractiveRouting"
- type: "file_contains"
path: "**/Privacy.razor"
value: "HttpContext"
- type: "file_contains"
path: "**/App.razor"
value: "AcceptsInteractiveRouting"
rubric:
- "Privacy page uses @attribute [ExcludeFromInteractiveRouting] to opt out of interactive routing and force static SSR"
- "App.razor conditionally applies the render mode using HttpContext.AcceptsInteractiveRouting() — returns null for excluded pages so they render statically"
- "Privacy page accesses HttpContext to read cookies — this only works because the page is excluded from interactive routing"
- "The pattern preserves global interactivity for all other pages while allowing this specific page to be static"
timeout: 600
- name: "Notifications page with live polling"
prompt: |
I have an existing Blazor Web App with Interactive Server (per-page).
I need an interactive /notifications page that:
- Loads an initial set of notifications from a NotificationService (create it with fake data)
- After the page becomes interactive, starts a background polling loop that
checks for new notifications every 5 seconds and updates the UI
- Shows a notification count badge and a list of notification messages
Important constraints:
- The initial notifications should appear in the fast server-rendered HTML
and NOT be fetched again when the page becomes interactive
- The polling loop must not run during the initial server render — only after
the page is live and interactive in the browser
- When the user navigates away, the polling should stop cleanly (no leaked tasks)
assertions:
- type: "file_exists"
path: "**/Notifications.razor"
- type: "file_contains"
path: "**/Notifications.razor"
value: "RendererInfo.IsInteractive"
- type: "file_contains"
path: "**/Notifications.razor"
value: "PersistentState"
- type: "file_contains"
path: "**/Notifications.razor"
value: "InteractiveServer"
rubric:
- "Uses RendererInfo.IsInteractive to guard the polling loop startup — polling only begins after the component is interactive, not during prerender"
- "Uses [PersistentState] with ??= pattern to persist initial notification data across the prerender-to-interactive handoff"
- "Polling uses Task.Delay in an async loop with CancellationTokenSource, cancelled in DisposeAsync — not System.Threading.Timer"
- "Component implements IAsyncDisposable and cancels the polling token in DisposeAsync to prevent resource leaks"
timeout: 600