timewarpengineering/timewarp-architecture/tw-blazor
Claude Code and agent skill by timewarpengineering/timewarp-architecture
- community
Checking that the SKILL.md file is still in the repository.
Razor file authoring — one @code at the top, markup, optional <style> last. Use when creating or editing .razor files, @code blocks, component event handlers (they only dispatch TimeWarp.State actions), or in-file <style> tags. CSS placement: tw-blazor-css-strategy. App shell: tw-blazor-layout.
Install
npx vskill@latest install timewarpengineering/timewarp-architecture/tw-blazorSKILL.md
.razor file order
- Directives (
@namespace,@inherits,@using,@inject, comments) - One
@code { … }— omit if none or code-behind only - Markup
- Optional
<style>last (Exception B —tw-blazor-css-strategy)
Never two @code blocks. Never @code after markup. Never <style> above markup.
Hand-written members live in @code. A .razor.cs exists only for attributes the C# source generators and class-level analyzers must see ([Page], [Authorize], [CrossSliceReference]). PageSourceGenerator does not run on .razor files. Do not put [Page] in @code or use @page on a page that already has [Page].
User interactions are actions
Every user interaction in the SPA is a TimeWarp.State action. A component event handler (OnClick, OnValidSubmit, @onkeydown, ValueChanged, …) only dispatches generated ActionSet methods, and at most sequences several of them (await CredentialsState.AddPasskey(); await CredentialsState.FetchCredentials();), reading state between dispatches to decide the next. The work lives in the action's handler:
- navigation —
RouteState.ChangeRoute(...)for an in-app route; a handler that injects
NavigationManager for a full-page load (forceLoad: true, e.g. a BFF challenge) or a destination only known after the work (post-sign-in return URL)
- JS interop (
IJSRuntime, JS modules, browser storage) - API calls (
IWebServerApiService/IApiServerApiService, ceremony clients) - state changes
Why: an action is the one seam that the Ctrl-K palette (every action, by derivation), the action catalog ([CatalogAction] — agent tools and palette metadata), State telemetry, and headless tests all see. A method on a page is invisible to all of them: it cannot be run from the palette, replayed, or dispatched in a test without a renderer. A button whose handler does the work itself is a feature nobody else can reach.
Exempt — purely presentational, component-local UI state: hover, focus, scroll-into-view, whether a local panel or editor is open, and the text bound to an input (including resetting a form draft from state). Lifecycle work (OnInitializedAsync loads, a render-time redirect) is held to the same rule: dispatch an action (RouteState.ChangeRoute for an already-signed-in redirect, a handler with forceLoad: true for an auth-gate redirect).
Compiler-checked — TWA0026. In SPA client code (Blazor WASM SDK), any member of a ComponentBase type — event handlers and lifecycle overrides alike — that calls one of these is reported: NavigationManager.NavigateTo / NavigateToLogin / Refresh; IJSRuntime / IJSObjectReference invokes; IApiService and its subtypes; HttpClient send/get/post/put/ delete/patch; session/local storage writes (SetItem*, RemoveItem*, Clear* — reads are fine); and any first-party type marked [SideEffectService] (ceremony clients, *JsModule helpers — mark new ones the same way). Handlers, services and static helpers are never flagged; that is where the work goes. A genuinely presentational JS call (focus, scroll, hotkey registration) opts out with [DirectComponentSideEffect("reason")] on the component (@attribute in the .razor) or member; an empty reason does not opt out and is reported as TWA0027.
JavaScript can dispatch too: a plain-markup button whose JavaScript onclick calls timeWarpState.DispatchRequest("<alias>", { … }) reaches the store with no C# handler (see the Counter demo, source/features/counter.ts). JavaScript dispatch is opt-in: allow the action in Program.AllowJavaScriptDispatch with Allow<TAction>(alias), where the alias is a const on the ActionSet (IncrementCounterActionSet.JavaScriptAlias), so the script never spells a CLR type name. Anything not allowed is rejected and nothing is sent. Use the fluent-button web component for that, not FluentButton, whose OnClick parameter claims the onclick attribute.
Every *ActionSet.Action is a Ctrl-K command by derivation: the generated ActionRoster lists it, and the palette shows it when it takes no required constructor argument and the user is signed in. The label comes from the names (AgentSurfaceState.OpenAskPanelActionSet → "Agent surface: Open ask panel"). Nothing opts an action in. Plumbing (loads, resets, ceremony steps, the palette's own actions) opts out explicitly with [CommandPaletteOptOut("reason")] on the Action class; a blank reason does not opt out, and a guard test fails on one. A label known only at run time (an admin-set assistant name) comes from a registered IActionDisplayOverride, never from a literal baked into the convention.
When an action is something an agent should run, or the palette needs better copy or a permission, tag it [CatalogAction] with a real Description, the same Permissions as the page that offers it, and Visibility (Human, Agent, Both). Its DisplayName, Description, Visibility and Permissions override the palette defaults (Human, no permissions, generated label); an Agent-only entry stays out of the palette. A derived command with no [CatalogAction(Permissions = …)] is offered to every signed-in user, so an action behind a permission must carry [CatalogAction(Permissions = …)] or opt out with [CommandPaletteOptOut("reason")]. An action that takes a URL or path re-validates it in the handler (for example LoginPage.GetSafeReturnUrl): the page is not the only caller.
Reference: features/identity/sign-in-state/ (ceremonies, challenge navigation, post-sign-in navigation) and features/identity/credentials-state/credentials-state.link-microsoft-365.cs (a cataloged full-page navigation) under source/container-apps/web/projects/web-spa/.
Server-owned availability
When whether an action applies right now depends on a server rule (last credential, already linked, state of a record), the server decides and the client reads the answer. Never copy a server rule into the client.
- The server owns availability through typed flags. Each item in a list carries a typed flag
per action (CredentialSummary.CanRevoke, CanRename); a page-level action gets a flag on the page's response (GetCredentials.Response.CanLinkMicrosoft365). The read handler sets them from the same rule code the action's handler enforces. Use typed flags, not a list of allowed action names: the compiler checks every read, and there is no string vocabulary to keep in agreement. Flags default to false, so a missing flag shows no action.
- Item actions live on the page. A row button dispatches its action directly with the row's
item (RevokeCredential(id)), shown or enabled from the row's flag, and the page then refreshes the read. The endpoint still enforces the rule (for example a 409 for a stale click), so the flag is guidance for an honest client, not the security boundary.
- Parameters the user types get a form on the page, next to the button (Rename's nickname
field). There is no generic form system.
- Ctrl-K and other global entry points get navigation and parameter-free actions only. An item
action is reached by going to its page ("revoke a passkey" means opening Passkeys). A parameter-free action whose rule may not hold (Link Microsoft 365) can stay a general command: its own flow reports when it does not apply. Actions that need an item are cataloged with Visibility = Agent.
- **Agents get the Ctrl-K commands whose visibility includes Agent, on every page, plus that
page's button actions.** Human-only commands stay out. A page-bound command is executable on its page and, off that page, returns a navigate offer instead of running. Item actions that are not palette commands stay on their page. There is no server-enumerated list of every action on every item.
Why: a client copy of a server rule drifts and races (the button shows, the server answers 409), and every surface that wants the rule needs its own copy. A flag is the server's answer for this caller and this snapshot, on the data it describes, so the list and its availability never come from different snapshots. Enumerating every action for every item instead (actions × items) floods global surfaces like Ctrl-K and needs string agreement between server and client for no benefit the page does not already have.
Use a flag only where availability needs a server-owned rule. Purely local actions (counter, theme, toggles, navigation, modals) need none, and static permission checks use [CatalogAction(Permissions = …)] plus AuthorizeView or the catalog's permission filter.
Reference: features/identity/get-credentials/ and credential-rules-application.cs (server) under source/container-apps/web/; features/application/pages/SettingsPage.razor and features/identity/components/CredentialList.razor (client) under web-spa/.
Agentic UI and WebMCP
One catalog-to-tools mapping drives both the in-app model and an external browser agent.
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewNot runA model review is scheduled for the next rescan.
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/tw-blazor/SKILL.md
Popularity
- 7-day trend score
- 5
Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/timewarpengineering/timewarp-architecture/tw-blazor)