
Fusion Code Conventions
- 1.6k installs
- 1 repo stars
- Updated August 4, 2026
- equinor/fusion-skills
fusion-code-conventions provides documented workflows for Applies and explains code conventions across TypeScript, React, C#, and Markdown. Enforces naming rules, file naming patterns, TSDoc and XML doc standards, inli
About
The fusion-code-conventions skill applies and explains code conventions across TypeScript, React, C#, and Markdown. Enforces naming rules, file naming patterns, TSDoc and XML doc standards, inline comment intent (the *why*, not the *what*), code structure, error handling, async patterns, and dead code policy. Also enforces ADR and contributor doc decisions, and flags decisions that appear stale or misaligned with current tooling. USE FOR: convention questions, code review against project standards, applying naming rules, auditin # Code Conventions ## When to use Use when code needs review, writing, or explanation against project conventions. Applies at two layers: - **Language conventions** - naming, file naming, type system, TSDoc/XML doc standards, code structure, error handling, async patterns, dead code policy. Each language has a dedicated agent and authoritative reference doc. - **Cross-cutting conventions** - applied on every review: intent capture (every non-obvious decision must be documented well enough that code could be regenerated from comments alone) and constitution enforcement (ADRs and contributor docs are law; deviations require a new decision record; stale d.
- "what are the naming conventions for this project?"
- "how should I write TSDoc for this function?"
- "does this file follow our code style?"
- "review this for convention violations"
- "what comment style should I use here?"
Fusion Code Conventions by the numbers
- 1,558 all-time installs (skills.sh)
- +72 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #86 of 1,352 Code Review & Quality skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
fusion-code-conventions capabilities & compatibility
- Capabilities
- "what are the naming conventions for this projec · "how should i write tsdoc for this function?" · "does this file follow our code style?" · "review this for convention violations" · "what comment style should i use here?"
- Use cases
- documentation
What fusion-code-conventions says it does
# Code Conventions ## When to use Use when code needs review, writing, or explanation against project conventions.
npx skills add https://github.com/equinor/fusion-skills --skill fusion-code-conventionsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.6k |
|---|---|
| repo stars | ★ 1 |
| Security audit | 3 / 3 scanners passed |
| Last updated | August 4, 2026 |
| Repository | equinor/fusion-skills ↗ |
How do I use fusion-code-conventions for the task described in its SKILL.md triggers?
Applies and explains code conventions across TypeScript, React, C#, and Markdown. Enforces naming rules, file naming patterns, TSDoc and XML doc standards, inline comment intent (the *why*, not the *.
Who is it for?
Teams invoking fusion-code-conventions when the user request matches documented triggers and prerequisites.
Skip if: Skip when cached docs are missing, the request is a negative trigger, or another sibling skill owns the workflow.
When should I use this skill?
Applies and explains code conventions across TypeScript, React, C#, and Markdown. Enforces naming rules, file naming patterns, TSDoc and XML doc standards, inline comment intent (the *why*, not the *what*), code structur
What you get
Step-by-step guidance grounded in fusion-code-conventions documentation and reference files.
- ADR violation reports
- Stale decision flags
By the numbers
- Enforces ADRs and contribute/ contributor documentation
Files
Code Conventions
When to use
Use when code needs review, writing, or explanation against project conventions. Applies at two layers:
- Language conventions — naming, file naming, type system, TSDoc/XML doc standards, code structure, error handling, async patterns, dead code policy. Each language has a dedicated agent and authoritative reference doc.
- Cross-cutting conventions — applied on every review: intent capture (every non-obvious decision must be documented well enough that code could be regenerated from comments alone) and constitution enforcement (ADRs and contributor docs are law; deviations require a new decision record; stale decisions flagged).
Typical triggers:
- "what are the naming conventions for this project?"
- "how should I write TSDoc for this function?"
- "does this file follow our code style?"
- "review this for convention violations"
- "what comment style should I use here?"
- "is this idiomatic TypeScript / React / C# / Markdown?"
- "apply code conventions to this file"
- "are these inline comments good enough?"
- "does this violate any ADR?"
- "is this ADR still current?"
- "we have a CONTRIBUTING.md — check if this change follows it"
When not to use
- Security vulnerability scanning (use a dedicated security review skill)
- Performance profiling or benchmarking
- High-level architecture or system design decisions
- Generating net-new code without a review target
- Mutating files without explicit user confirmation
Precedence and applicability
This skill provides org-wide baseline conventions. When installed in a repository with its own conventions, precedence (highest wins):
1. Repository-level policy — CONTRIBUTING.md, contributor guides (contribute/), ADRs, .github/copilot-instructions.md, AGENTS.md, or equivalent 2. Tooling configuration — biome.json, tsconfig.json, .editorconfig, linter configs 3. This skill — all rules in references/*.conventions.md and agent modes
When a repository explicitly narrows, relaxes, or contradicts a rule from this skill, the repository policy wins. Don't flag code that conforms to repo's documented conventions, even if it deviates from the skill baseline.
If conflict is undocumented (no ADR, no contributor note, no config), treat the skill rule as default and recommend the team record their intent.
For maintainers: record convention overrides in CONTRIBUTING.md, a contributor guide, or an ADR so both humans and agents discover them consistently.Agent modes
| Agent | Activated for |
|---|---|
agents/typescript.agent.md | TypeScript naming, TSDoc, type system, code style, error handling |
agents/react.agent.md | React component structure, hooks rules, accessibility, keys |
agents/csharp.agent.md | C# naming, CQRS patterns, async/await, null safety, testing |
agents/markdown.agent.md | Markdown structure, frontmatter, links, callouts, GitHub-specific syntax |
agents/intent.agent.md | Intent capture — applied in parallel on every code review |
agents/constitution.agent.md | ADR and contributor doc enforcement — applied in parallel when project has decision records |
Convention references (authoritative rules per language):
references/typescript.conventions.mdreferences/react.conventions.mdreferences/csharp.conventions.mdreferences/markdown.conventions.md
Required inputs
If required inputs are missing or ambiguous, ask before proceeding.
- Target code (inline snippet or file path) or a specific convention question
- Language or file type (inferred from content if not provided)
- For constitution checks: path to ADR directory and/or contributor docs (inferred from common locations —
docs/adr/,CONTRIBUTING.md,contribute/— if not provided)
When the developer's context or persona is known, consult the matching follow-up file in assets/ for targeted clarifying questions. Ask only unanswered questions.
assets/framework-core-developer.follow-up.md— Fusion Framework internals, shared libraries, framework APIsassets/react-app.follow-up.md— Fusion React app developmentassets/core-service.follow-up.md— Fusion Core backend services (C# / .NET)
Instructions
Step 1 — Classify and route
Detect the primary language, then activate the matching language agent:
- TypeScript (
.ts, non-component.tsx) →agents/typescript.agent.md - React (
.tsxwith JSX or hooks, component files) →agents/react.agent.md - Mixed
.tsx(TypeScript type concerns + React component concerns) → both agents in parallel - C# (
.cs,.csproj) →agents/csharp.agent.md - Markdown (
.md,.mdx) →agents/markdown.agent.md
Always activate in parallel on any code review:
agents/intent.agent.md— every review, regardless of languageagents/constitution.agent.md— when project has ADRs (docs/adr/,adr/) or contributor docs (CONTRIBUTING.md,contribute/,.github/copilot-instructions.md)
If language cannot be determined, ask before proceeding.
Step 2 — Apply or explain conventions
Each language agent reads its authoritative reference file first, then:
- For convention questions: answers with rule explanation and corrected code example
- For code review: identifies deviations, states the rule, shows corrected version
- Does not flag patterns the project has explicitly configured in
biome.jsonor.editorconfig
The intent and constitution agents run in parallel and contribute findings to the combined report.
Step 3 — Present findings
Organise all findings from all agents into a unified report:
- Required — must fix: naming violations, missing TSDoc on exports,
anytypes, constitutional violations - Recommended — should fix: weak intent comments, undocumented magic values, unjustified suppressions
- Advisory — consider: missing decision records, stale ADRs, implicit exceptions to formalise
For each finding: state the rule, affected code, and corrected version or recommended action.
Step 4 — Apply corrections
Apply only corrections the user explicitly approves. Edit files using workspace tools. Don't rewrite entire files unless under 50 lines.
Expected output
- For convention questions: a rule explanation with a corrected code or markup example
- For code review: a unified findings report (Required / Recommended / Advisory) covering language conventions, intent quality, and constitutional compliance
- Offered corrections with the user's explicit approval before any file is mutated
Safety & constraints
- Never mutate files without explicit user confirmation.
- Don't flag style choices the project has opted into via
biome.jsonor.editorconfig. - Don't invent ADR content — only enforce and challenge what is actually documented.
Constitution Agent
When to use
Use this agent mode whenever reviewing or implementing code changes in a project that has ADRs or contributor documentation. Its job is to act as the project's constitutional layer: decisions recorded in ADRs and `contribute/` docs are law — but the agent also flags when those laws appear stale, contradicted by current tooling, or misaligned with actual practice.
Activate in parallel with language agents on any code review or implementation task where the project has ADRs or contributor docs.
When not to use
Do not use for projects with no ADRs and no contributor documentation — there is no constitution to enforce.
The constitution principle
Recorded decisions represent the reasoning at a point in time. They are authoritative until superseded. Code that violates them without a corresponding update to the decision record is a silent drift — and silent drift is the most dangerous kind.
The agent's job is twofold: 1. Enforce: flag code that violates a recorded decision without a new ADR or doc update authorising the deviation. 2. Challenge: flag decisions that appear outdated, contradicted by current tooling, or obviously misaligned with modern practice — and recommend they be revisited rather than silently ignored or mindlessly followed.
---
Step 1 — Discover the constitution
Before reviewing anything, locate all project decision documents:
1. ADRs — look in docs/adr/, adr/, docs/decisions/, or any directory named after a common ADR convention (0001-*.md, YYYYMMDD-*.md). 2. Contributor docs — look in CONTRIBUTING.md, contribute/, .github/CONTRIBUTING.md, .github/copilot-instructions.md, AGENTS.md. 3. Inline policy files — SECURITY.md, CODEOWNERS, .editorconfig, biome.json, tsconfig.json.
Read all discovered documents. Build a mental model of:
- What patterns and technologies are mandated
- What patterns are explicitly prohibited
- What rationale was given for each decision
- When the decision was made (date, PR, or version reference if present)
---
Step 2 — Enforce active decisions
For each ADR or contributor rule that is not marked as superseded or deprecated:
The code must conform
Flag any code change that deviates from a recorded decision as a constitutional violation:
⛔ Constitutional violation — ADR-007: Service-to-service calls must use the internal HTTP client, not raw fetch.
Found: `await fetch('/api/items')`
Required: `await httpClient.get('/api/items')`
ADR: docs/adr/0007-http-client.mdInclude:
- The ADR or doc reference
- The exact rule violated
- The affected code
- The compliant alternative
Deviations require a new decision record
A deviation is only acceptable when:
- A new ADR explicitly supersedes the old one, or
- The contributor docs have been updated to reflect the new direction, and
- The change is accompanied by a note referencing the updated record
If neither condition is met, recommend the developer either comply with the existing decision or open a new ADR before merging.
---
Step 3 — Challenge stale or misaligned decisions
Not all recorded decisions age well. Flag a decision as potentially stale when any of the following apply:
The tooling has moved on
The decision mandates or prohibits a technology that has since been superseded by the project's own toolchain.
⚠️ Stale decision — ADR-003 mandates Webpack for bundling, but the project now uses Vite
(detected: vite.config.ts, @vitejs/plugin-react in package.json).
Recommend: review and supersede ADR-003, or confirm Vite is an undocumented exception.The decision contradicts current practice everywhere
The decision is consistently violated across the entire codebase with no apparent friction — suggesting it was never really adopted.
⚠️ Unenforced decision — CONTRIBUTING.md requires conventional commits, but the last 20
commits in git log use freeform messages. Either enforce or formally remove the requirement.The rationale no longer applies
The decision's stated rationale (e.g. "we avoid X because it lacks Y") is invalidated by a platform update, library upgrade, or ecosystem change.
⚠️ Rationale superseded — ADR-012 prohibits optional chaining because the TypeScript
target was ES5. tsconfig.json now targets ES2022. The prohibition may no longer apply.
Recommend: revisit ADR-012.The decision is internally contradictory
The ADR or contributor doc contradicts another recorded decision without acknowledging the conflict.
⚠️ Conflicting decisions — ADR-005 mandates class-based services; ADR-014 mandates
functional modules with no classes. These conflict. Recommend: resolve and supersede one.---
Step 4 — Distinguish enforcement severity
Not all deviations are equal. Use these levels:
| Level | Meaning | Action |
|---|---|---|
⛔ Constitutional violation | Active decision is breached, no authorising record exists | Block — fix or open a new ADR |
⚠️ Stale decision | Recorded decision appears outdated or misaligned | Warn — recommend revisiting the ADR |
ℹ️ Missing decision record | A significant pattern or technology choice has no ADR | Inform — suggest writing one |
💡 Implicit exception | The codebase already deviates consistently — the exception may be intentional | Inform — suggest formalising the exception |
---
Instructions
1. Discover all ADRs and contributor docs in the project (Step 1). 2. For each active recorded decision, check whether the code under review complies (Step 2). 3. For each recorded decision, assess whether it is still current and internally consistent (Step 3). Use git log -n 20 --oneline to verify whether recent commit history aligns with contributor guidelines (e.g. conventional commits, branching strategy). 4. Report findings grouped by severity level (Step 4). 5. For constitutional violations: recommend either compliance or a new ADR before merge. 6. For stale decisions: recommend a specific action (supersede, update, or confirm as-is). 7. Return all findings to the orchestrator.
Expected output
Findings grouped as:
⛔ Constitutional violations— active decisions breached, require fix or new ADR⚠️ Stale decisions— recorded decisions that appear outdated or misalignedℹ️ Missing decision records— significant undocumented choices💡 Implicit exceptions— consistent deviations that should be formalised
Each finding: the document reference, the rule, the affected code or pattern, and a recommended action.
Safety & constraints
Do not mutate files directly; mutations are handled by the orchestrator confirmation flow. Do not invent ADR content or fabricate decisions — only enforce and challenge what is actually documented. If no ADRs or contributor docs exist, report that and skip enforcement.
C# Code Conventions Agent
When to use
Use this agent mode for C# convention questions and applying C# code standards to .cs files.
When not to use
Do not use this agent mode for infrastructure-as-code, build pipeline YAML, or non-C# project files.
Required inputs
- Target C# code (snippet or file path) or a specific convention question
- Target .NET version if known (infer from project file when available)
- Nullable reference types enabled status (infer from
.csprojorDirectory.Build.props; assumeenableif not found)
Convention reference
references/csharp.conventions.md is the authoritative source for all C# rules. Read this file before answering any convention question or reviewing any code.
The reference covers: project structure, naming, compiler settings, null safety, async/await, disposables, architecture patterns (MediatR CQRS, base controllers, API versioning), API response models, EF Core, error handling, code style, XML doc comments, and testing.
Project discovery
Before applying conventions to a specific codebase, inspect the project to understand its actual settings:
1. Read Directory.Build.props (if present) for global settings (Nullable, TreatWarningsAsErrors, ImplicitUsings). 2. Read the target .csproj for TargetFramework, per-project overrides, and package references. 3. Read .editorconfig for enforced style rules (var usage, namespace style, indentation, naming rules). 4. Note any explicit deviations from the defaults in references/csharp.conventions.md — treat those project-specific settings as the ground truth for that codebase.
Instructions
1. Confirm target is C# source. 2. Read references/csharp.conventions.md to load the full convention set. 3. Run project discovery (above) to identify any project-specific overrides. 4. For convention questions: answer with the rule from the reference, a rationale, and an inline code example. 5. For code review: compare the target code against the reference conventions (plus any project overrides), identify deviations, state the rule violated, and show the corrected version. 6. Return findings and corrections to the orchestrator.
Expected output
- Convention explanation with code examples, or
- List of deviations with inline corrections
Safety & constraints
Do not mutate files directly; mutations are handled by the orchestrator confirmation flow. Do not flag a deviation if the project's own .editorconfig or project file explicitly opts into the differing style.
Intent Comments Agent
When to use
Use this agent mode whenever reviewing or writing code — regardless of language. Its sole focus is intent capture: ensuring that every non-trivial decision, exposed surface, and reasoning path is documented clearly enough that a developer could regenerate the code from the comments and docs alone, without reading the implementation.
Activate in parallel with every language-specific agent during code review.
When not to use
This agent does not review syntax, formatting, or naming — those are covered by the language agents.
The regenerability principle
If you deleted the implementation and kept only the intent comments, TSDoc, and interface definitions, a competent developer should be able to write the code back.
This is the bar every file should meet. If comments only say what the code does rather than why it does it that way, the code fails this test.
---
What must be documented
Exported interfaces and public APIs
Every exported function, type, class, or interface must carry documentation that explains:
- What problem it solves and why it exists
- The constraints or invariants it operates under
- Why the shape is what it is (field choices, optional vs required, union members)
- Anything a caller must know to use it correctly
// ❌ Undocumented shape — caller has no idea why `reason` is optional
export interface CancellationResult {
cancelled: boolean;
reason?: string;
}
// ✅ Intent captured — caller understands the invariant
/**
* Result of a cancellation attempt.
*
* `reason` is present only when `cancelled` is false — it explains
* why cancellation was rejected (e.g. the item is already processing).
* When `cancelled` is true, `reason` is always undefined.
*/
export interface CancellationResult {
cancelled: boolean;
reason?: string;
}Decision gates
Every if, else, switch branch, and non-trivial ternary must be explained when the condition is not self-evident from the variable names alone.
Capture:
- Why this branch exists
- What business rule or constraint it encodes
- Why the alternative was rejected
// ❌ Condition reads clearly but the reason is opaque
if (item.lockedBy !== null && item.lockedBy !== currentUser) {
return false;
}
// ✅ Business rule captured
// Items locked by another user must not be edited — concurrent edits corrupt
// the workflow state. The current user's own lock is always safe to proceed.
if (item.lockedBy !== null && item.lockedBy !== currentUser) {
return false;
}Iterator transforms and pipelines
Every .filter(), .map(), .reduce(), for…of loop, and RxJS pipe() chain with a non-obvious purpose needs a comment that explains the business invariant being enforced.
// ❌ What is filtered is clear; why is not
const billable = entries.filter((e) => e.type !== 'overhead' && e.approved);
// ✅ Financial rule captured
// Only approved, non-overhead entries are billable — overhead is absorbed
// at department level and must never appear on client invoices
const billable = entries.filter((e) => e.type !== 'overhead' && e.approved);Architecture and pattern choices
When a non-obvious pattern is chosen over a simpler one, explain it:
- Why MediatR/CQRS over a direct call
- Why a discriminated union over a class hierarchy
- Why polling over a websocket
- Why denormalization in this query result
// ❌ Pattern is used with no explanation
const state: LoadState = { status: 'idle' };
// ✅ Explains why this pattern was chosen
// Discriminated union over a class hierarchy — this state machine is consumed
// by React render functions which need exhaustive switch narrowing without
// instanceof guards. Adding a new state forces all consumers to handle it.
type LoadState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: Item[] }
| { status: 'error'; error: Error };Magic values
Every hard-coded number, string, timeout, or threshold must be a named constant with a comment explaining where the value comes from.
// ❌ Unexplained literal
setTimeout(flush, 250);
// ✅ Origin and rationale captured
// 250 ms debounce matches the minimum human perception gap for keypress feedback
// (below 100 ms feels instant; above 300 ms feels laggy — Nielsen 1993)
const DEBOUNCE_MS = 250;
setTimeout(flush, DEBOUNCE_MS);Workarounds and escape hatches
Any suppression (// biome-ignore, @ts-ignore, #pragma warning disable, as, !) must justify:
- Why the safe path was insufficient
- What makes this usage acceptable
- A ticket if the workaround is temporary
// ❌ Silent suppression
// biome-ignore lint/suspicious/noExplicitAny
const raw = response as any;
// ✅ Justified
// The vendor SDK types `response` as `any`; a typed wrapper is tracked in #892.
// Safe here because we validate the shape immediately below via `isApiResponse`.
// biome-ignore lint/suspicious/noExplicitAny: vendor SDK gap, see #892
const raw = response as any;Async sequencing decisions
When async operations are sequential rather than parallel — or vice versa — the reason must be documented.
// ❌ Sequential awaits — is this intentional or a performance bug?
const user = await fetchUser(id);
const permissions = await fetchPermissions(id);
// ✅ Intent captured
// Sequential by design — permissions fetch depends on user.tenantId
// which is only known after fetchUser resolves
const user = await fetchUser(id);
const permissions = await fetchPermissions(user.tenantId);Error handling intent
Explain why an error is caught, swallowed, rethrown, or transformed:
// ❌ Swallowed silently
try { ... } catch { return null; }
// ✅ Reasoning captured
// 404 is expected when the feature flag is not yet provisioned for this tenant;
// return null so the caller renders the default (disabled) state
try { ... } catch (err) {
if (err instanceof ApiError && err.statusCode === 404) return null;
throw err;
}---
What counts as a dead comment
Flag and remove any comment that:
- Restates the next line of code in different words
- Repeats the symbol name
- Describes the syntax rather than the intent
// ❌ Dead — says the same thing as the code
// Filter items
const active = items.filter(...);
// ❌ Dead — restates the name
// UserService class
class UserService { ... }---
Instructions
1. For each exported interface, type, function, class, and module: verify intent is documented. Flag missing or insufficient documentation as missing API intent. 2. For each decision gate with a non-obvious condition: verify a why comment is present. Flag missing explanations as missing decision intent. 3. For each iterator or pipeline with a non-obvious business purpose: flag missing explanations as missing transform intent. 4. For each hard-coded literal: flag unnamed or unexplained values as unexplained literal. 5. For each suppression or escape hatch: flag ones without justification as unjustified override. 6. For each pattern or architecture choice that is non-obvious: flag undocumented choices as missing pattern rationale. 7. For each async sequencing decision (serial vs parallel): flag undocumented choices as missing async intent. 8. For each comment that restates syntax: flag as dead comment — recommend removal or replacement. 9. Apply the regenerability test: "Could a developer reconstruct this code from only the intent comments and interface documentation?" If not, identify what is missing. 10. Return all findings to the orchestrator grouped by category.
Expected output
Findings grouped as:
- Missing API intent — exported surfaces without sufficient documentation
- Missing decision intent — non-obvious branches without why
- Missing transform intent — iterator/pipeline business rules undocumented
- Missing pattern rationale — non-obvious pattern choices unexplained
- Missing async intent — serial vs parallel choices undocumented
- Unexplained literals — magic values without name or origin
- Unjustified overrides — suppressions and escape hatches without rationale
- Dead comments — comments that restate syntax
Each finding: affected line(s), rule violated, suggested comment draft.
Safety & constraints
- Do not guess or hallucinate intent. If the why is not evident from the code, naming, or existing documentation, flag it as missing intent — do not invent business logic or rationale to satisfy the regenerability test.
- Do not mutate files directly; mutations are handled by the orchestrator confirmation flow.
Markdown Code Conventions Agent
When to use
Use this agent mode for Markdown convention questions and applying Markdown standards to .md and .mdx files.
When not to use
Do not use this agent mode for .mdx files containing substantial JSX/component logic — route those to agents/react.agent.md as well.
Required inputs
- Target Markdown content (snippet or file path) or a specific convention question
- Document purpose if known (README, skill file, documentation article, changelog)
Convention reference
All rules live in references/markdown.conventions.md. Read that file before answering.
The reference covers:
- Frontmatter (YAML validity, quoting rules)
- Document structure (heading hierarchy, blank lines)
- Links (relative vs absolute, anchor validity, bare URLs)
- Code blocks (language identifiers, inline code)
- Lists, Emphasis, Images, Callouts, Formatting
- GitHub-specific: Alerts, Strikethrough, Task lists, Mentions and references
Instructions
1. Read references/markdown.conventions.md to load the authoritative rules. 2. Check whether the target file is rendered on GitHub or another renderer — apply the ## GitHub-specific rules only when the render target is GitHub. 3. For convention questions: answer with a rule explanation and a corrected Markdown example. 4. For document review: identify deviations, cite the rule, show the corrected version. 5. Do not flag deviations that are intentionally sanctioned by a .markdownlint* or prettier config in the repository. 6. Return findings and corrections to the orchestrator.
Expected output
- Convention explanation with Markdown examples, or
- List of deviations with inline corrections
Safety & constraints
Do not mutate files directly; mutations are handled by the orchestrator confirmation flow.
React Code Conventions Agent
When to use
Use this agent mode for React convention questions and applying React code standards to component and hook files (.tsx, .jsx).
When not to use
Do not use this agent mode for pure TypeScript logic with no React surface (no JSX, no hooks, no components). Route those to agents/typescript.agent.md instead.
Required inputs
- Target React component or hook code (snippet or file path) or a specific convention question
- React version context if available (React 18+ assumed by default)
Convention reference
All React rules live in references/react.conventions.md. Read that file before answering. TypeScript rules (TSDoc, types, code style) live in references/typescript.conventions.md — read it too when reviewing .tsx files.
The React reference covers:
- Naming (components, hooks, event handlers, boolean props)
- Component structure (one per file, co-located props, no nested component definitions)
- Hooks rules (conditional calls, dependency arrays,
useEffectmisuse, state mutation) - Keys in lists
- Accessibility (semantic HTML, aria labels, keyboard support)
- TSDoc for exported components and hooks
Instructions
1. Read references/react.conventions.md and references/typescript.conventions.md to load the authoritative rules. 2. Discover project-specific overrides before flagging anything:
- Read
biome.json— do not flag patterns the project has explicitly configured or suppressed.
3. For convention questions: answer with a rule explanation and an inline code example. 4. For code review: identify deviations, cite the specific rule, show the corrected version. 5. For .tsx files with substantial non-React TypeScript, delegate type-system and code-style concerns to agents/typescript.agent.md in parallel. 6. Return findings and corrections to the orchestrator.
Expected output
- Convention explanation with code examples, or
- List of deviations with inline corrections
Safety & constraints
Do not mutate files directly; mutations are handled by the orchestrator confirmation flow.
TypeScript Code Conventions Agent
When to use
Use this agent mode for TypeScript convention questions and applying TypeScript code standards to .ts and .tsx files.
When not to use
Do not use this agent mode for React-specific component concerns — delegate those to agents/react.agent.md in parallel when a .tsx file contains component definitions.
Required inputs
- Target TypeScript code (snippet or file path) or a specific convention question
- tsconfig path if available (to confirm strict mode, target, path aliases)
Convention reference
All rules live in references/typescript.conventions.md. Read that file before answering.
The reference covers:
- TSDoc (required tags, good examples, anti-patterns)
- Naming conventions (file naming, interfaces, enums, constants, generics)
- Type system (strict mode,
any, type assertions, non-null assertions, discriminated unions, generics, utility types, explicit return types) - Code style (variable declarations, immutable patterns, single responsibility, file organisation, import style, readability)
- Inline comments
- Async / await
- Error handling
- Dead code policy
Instructions
1. Read references/typescript.conventions.md to load the authoritative rules. 2. Discover project-specific overrides before flagging anything:
- Read
tsconfig.jsonto confirm strict mode settings and compiler flags. - Read
biome.json— do not flag style choices the project has explicitly configured.
3. For convention questions: answer with a rule explanation and an inline code example. 4. For code review: identify deviations, cite the specific rule, show the corrected version. 5. When a .tsx file also contains React component definitions, delegate React-specific concerns to agents/react.agent.md in parallel. 6. Return findings and corrections to the orchestrator.
Expected output
- Convention explanation with code examples, or
- List of deviations with inline corrections
Safety & constraints
Do not mutate files directly; mutations are handled by the orchestrator confirmation flow.
Follow-Up Questions — Core Service Developer
Clarifying questions to ask before reviewing or applying conventions to Fusion Core backend services (C# / .NET APIs). Pick the relevant section based on the code under review. Skip questions already answered.
API Design & Versioning
- Does the controller use
[ApiVersion]and[MapToApiVersion]attributes, and does the route template include the version segment (e.g.,/api/v{version:apiVersion}/orders)? - Are response models prefixed with
Apiand versioned with a suffix (e.g.,ApiOrderV2) to distinguish them from domain and persistence models? - Is the controller using policy-based authorisation (
[Authorize(Policy = "...")]) rather than inline role string checks? - Are new endpoints documented with XML doc comments (
<summary>,<param>,<response>) including HTTP status codes? - Does the endpoint accept
CancellationTokenas the last parameter and pass it through to all async calls? - Are breaking changes to existing endpoints (renamed fields, removed properties, changed status codes) accompanied by a new API version or migration path?
CQRS & MediatR Patterns
- Are commands named as verb phrases (
CreateOrder,CancelBooking) and queries asGet*phrases (GetOrderById,GetActiveOrders)? - Does each command/query contain a nested
Handlerclass, or is the handler in a separate file? Which pattern does this project use consistently? - Is the handler's
Handlemethodasync Task<TResponse>(neverasync void), and does it accept and propagateCancellationToken? - Are commands that mutate state kept separate from queries that only read? Are there any commands that return read-model data (mixing concerns)?
- Does a FluentValidation validator exist for the command/query, and does it follow the project's naming convention (e.g.,
CreateOrderValidator)? - Are MediatR pipeline behaviours (logging, validation, transaction) registered centrally, or are there per-handler cross-cutting concerns that should be lifted?
Entity & Data Model Conventions
- Are EF Core entity classes prefixed with
Db(e.g.,DbOrder,DbLineItem) to distinguish them from domain and API models? - Are domain query result models prefixed with
Query(e.g.,QueryOrder) or do they reuse entity or API types across layers? - Does the
DbContextuse explicit Fluent API configuration (OnModelCreating/IEntityTypeConfiguration<T>) rather than relying solely on attribute conventions? - Are navigation properties and foreign keys explicitly configured, or are there shadow properties that could cause unexpected migration behaviour?
- Does the mapping between
Db*entities andApi*/Query*models use a consistent strategy across the project (extension methods, hand-written, AutoMapper)?
Error Handling & Null Safety
- Is nullable reference types (
<Nullable>enable</Nullable>) active in the project file? Are there#nullable disabledirectives that suppress it without justification? - Are null checks using pattern matching (
is null,is not null) rather than== null/!= null? - Does the code avoid
.Resultand.Wait()on tasks, usingawaitconsistently throughout? - Are exceptions typed (e.g.,
NotFoundException,ConflictException) with structured properties, or are bareExceptionorInvalidOperationExceptionthrown? - Are
FirstOrDefault()/SingleOrDefault()results null-checked before use, or is the code assuming non-null without validation? - Does global exception-handling middleware map domain exceptions to appropriate HTTP status codes, or does each controller handle this locally?
XML Documentation & Intent Comments
- Do all public classes, methods, and properties have XML doc comments (
<summary>,<param>,<returns>,<exception>)? - Do intent comments on non-obvious decisions explain the why (e.g., "soft-delete instead of hard-delete because audit trail retention policy")?
- Are suppression pragmas (
#pragma warning disable,[SuppressMessage]) justified with a comment explaining why the warning is inapplicable? - Are magic numbers, timeout values, retry counts, and claim/scope name strings extracted to named constants with origin comments?
- Can complex LINQ queries or EF Core projections pass the regenerability test — if deleted, could a developer rewrite them from the intent comments and type signatures alone?
Testing Conventions
- Do test classes use the
*Testssuffix (e.g.,OrderApiTests,CreateOrderHandlerTests)? - Do test methods follow the
Subject_Context_ShouldOutcomenaming pattern (e.g.,CreateOrder_WithInvalidPayload_ShouldReturnBadRequest)? - Are integration tests using
WebApplicationFactoryorTestcontainers, and is database isolation handled per test class or per test? - Are assertions using FluentAssertions (
.Should().Be...) consistent with the project's assertion style? - Do tests cover the documented error paths (validation failures, not-found, conflict, unauthorised), not just the happy path?
ADR & Architectural Decisions
- Does this service have an ADR directory (
docs/adr/,adr/)? Does this change comply with recorded decisions on data access patterns, authentication, or API design? - If a new technology, library, or architectural pattern is introduced, has a new ADR been drafted?
- Does the
CONTRIBUTING.mdor contributor docs specify service-specific conventions (branching strategy, database migration process) that apply? - Are there existing ADRs referencing deprecated patterns or tooling that this change could trigger for revision?
- Does this change cross a policy boundary where conventions require requirement-based authorization rather than inline role or scope checks?
Follow-Up Questions — Framework Core Developer
Clarifying questions to ask before reviewing or applying conventions to Fusion Framework internals, shared packages, or framework APIs. Pick the relevant section based on the code under review. Skip questions already answered.
Public API Surface
- Which exports are part of the public API versus internal implementation? Is there a barrel file (
index.ts) that explicitly defines the public boundary? - Are any existing public exports being renamed, removed, or having their signature changed? If so, has a
@deprecatedtag with a replacement been added to the old export? - Do all public exports have TSDoc explaining the problem solved and constraints, not just the parameter types?
- Are generic type parameters on public APIs documented with
@typeParamexplaining the expected constraint (e.g.,TConfig extends BaseModuleConfig)? - Is this an additive API, a rename, or a behavioral change to an existing export that could affect downstream consumers?
- Are re-exported types from dependencies wrapped in the package's own type alias, or are consumers being exposed to transitive dependency types?
Module & Package Structure
- Is this a new Fusion module, an extension to an existing module, or a standalone utility library?
- Does the module follow the provider/configurator/module pattern (
*Provider,*Configurator,*Module)? Are these named consistently with existing modules? - Are internal helpers kept in files not re-exported from the barrel, or are they leaking into the public surface?
- Does this module define both a configuration-time API (used in
configure()callbacks) and a runtime API (used in components/hooks), and are these boundaries clear? - If this package has peer dependencies on other framework packages, are the version ranges correct and documented?
- Is there a clear separation between the module's configuration-time API and its runtime API?
Performance & Bundle Impact
- Is the code structured to allow unused parts to be tree-shaken (e.g., side-effect-free modules, no top-level mutations)?
- Does this feature introduce a large third-party dependency (e.g.,
lodash,moment) that could bloat the bundle? Is there a smaller alternative? - Should any part of this feature be lazy-loaded only when needed, rather than included in the main bundle?
TSDoc & Intent Comments
- Can every exported function, type, hook, and class pass the regenerability test — if the implementation were deleted, could a developer rewrite it from the TSDoc and intent comments alone?
- Do intent comments on non-obvious decisions explain why this approach was chosen over alternatives (e.g., "polling instead of WebSocket because the upstream API has no push support")?
- Are magic values (timeouts, retry counts, cache TTLs, buffer sizes) extracted to named constants with origin comments explaining the chosen value?
- Do
@exampleblocks in TSDoc show the consumer's perspective (how an app developer would use this API), not just internal test usage? - Are
@ts-ignore,ascasts, and!assertions each justified with a comment explaining why the safe path is insufficient?
Type System & Generics
- Are generics constrained (
T extends SomeBase) rather than left unbounded where the implementation assumes structure? - Does the package avoid
anyentirely? Whereunknownis used, is there a type guard or narrowing function nearby? - Are discriminated unions preferred over type assertions for branching logic? Is the discriminant field documented?
- Do conditional types and mapped types have an accompanying intent comment explaining the transformation in plain language?
Error Handling & Async Patterns
- Does the module define typed error subclasses (extending a framework base error) with structured context properties, or does it throw generic
Error? - Are async public methods that accept
AbortSignalor cancellation tokens documented with@throwslisting the cancellation error type? - Where operations are sequential, is there a comment explaining why they cannot be parallelised (data dependency, ordering constraint)?
- Does the module clean up subscriptions, timers, and event listeners in its
dispose()path? Are there resources that could leak ifdispose()is not called?
ADR & Decision Records
- Does this change introduce or modify a pattern significant enough to warrant a new ADR (e.g., new module lifecycle phase, new configuration mechanism, new error hierarchy)?
- Is there an existing ADR that governs this area (module structure, provider patterns, configuration API shape)? Does this code comply or deviate?
- If an existing ADR is being deviated from, has a superseding ADR been drafted, or is this silent drift?
- Does the
CONTRIBUTING.mdorcontribute/directory specify framework-specific conventions (versioning policy, breaking change process) that apply to this change? - Should backward compatibility be treated as mandatory for this surface, or is the package still experimental/internal?
Follow-Up Questions — React App Developer
Clarifying questions to ask before reviewing or applying conventions to a Fusion React application. Pick the relevant section based on the code under review. Skip questions already answered.
Component Structure & Naming
- Does each component live in its own file named in
PascalCase(e.g.,DataGrid.tsx), or are multiple components defined in one file? - Are there nested component definitions inside a parent component (which cause full unmounts on re-render)?
- Does the component's props interface have TSDoc explaining the component's purpose and each non-obvious prop?
- Are event handler props named with
onprefix (e.g.,onSelect,onClose) and internal handlers withhandleprefix (e.g.,handleClick)? - Is the component in the correct layer directory (
src/components/,src/pages/, or a feature folder), consistent with how the rest of the app is organised? - Are presentational components kept free of data-fetching logic, or is a hook extraction needed?
Hooks & State Management
- Does each custom hook live in its own file named in
camelCase(e.g.,useItems.ts) with theuseprefix? - Are all hooks called unconditionally at the top level — no hooks inside
if, loops, or early returns? - Are dependency arrays complete? Are there object or array literals in deps that would cause infinite re-renders?
- Is
useEffectbeing used for data transformation that should beuseMemoor a derived value instead? - Does the hook depend on Fusion context (
useCurrentContext) and need to refetch when context changes? - Does the hook's TSDoc explain what triggers it, what it returns, and what callers should expect on error?
Styling & EDS Usage
- Are styles implemented with
styled-componentsusing theStyledobject pattern, or has an alternative approach been introduced? - Are EDS design tokens used for colors, spacing, and typography instead of hard-coded values?
- Where EDS components are customised, is
styled()wrapping used rather than overriding internal class names? - Are there magic pixel values or colour hex codes that should reference an EDS token instead?
- Does the component need density support (
EdsProvidercompact/comfortable), and is it handled correctly for both modes?
Data Fetching & API Layer
- Is the API endpoint registered in
app.config.tsorconfig.ts, and is the HTTP client accessed viauseHttpClientfrom@equinor/fusion-framework-react-app/http? - Are there direct
fetch()oraxioscalls that bypass the framework HTTP client (and its auth/interceptor chain)? - If using React Query, does the query key follow the project convention (API path + parameters)?
- Are loading, error, and empty-data states handled in the component, or are they silently swallowed?
- Does the response type have a corresponding interface, and is it documented with TSDoc?
TSDoc & Inline Comments
- Do all exported components, hooks, types, and utility functions have TSDoc explaining why they exist and what problem they solve?
- Are non-obvious conditional branches annotated with intent comments explaining the business rule?
- Are
.filter(),.map(),.reduce()chains annotated with the business invariant they enforce (e.g., "exclude draft items because the API returns all statuses")? - Are
// biome-ignore,@ts-ignore, orascasts justified with a comment explaining why the safe approach is insufficient? - Are hard-coded strings, numbers, or timeouts extracted to named constants with origin comments?
Routing, Manifest & App Integration
- Does this feature add new routes? Do route segments follow kebab-case naming?
- Are route params and search params typed and documented in the component or loader TSDoc?
- Should this view be registered in the App Manifest (
app.manifest.ts) for deep linking? - Are navigation calls using
useNavigatefromreact-router-domrather than directwindow.locationmanipulation?
ADR & Project Conventions
- Does the app have an
adr/ordocs/adr/directory? Does this change comply with recorded decisions, or introduce a pattern without a corresponding ADR? - Does the app's
CONTRIBUTING.md,contribute/, or.github/copilot-instructions.mdspecify project-specific conventions that override the defaults? - Has
biome.jsonor.editorconfigbeen checked for project-specific overrides that should not be flagged? - Does this change introduce a new library or architectural choice significant enough to warrant a new ADR?
Changelog
0.1.3 - 2026-05-07
patch
- Drop articles, filler, hedging from SKILL.md activation body
- Compress typescript, react, csharp, markdown convention references
0.1.2 - 2026-03-23
patch
- Separate Controllers/ and Endpoints/ into distinct lines in the project layout to avoid ambiguity
- Clarify Startup.cs guidance to distinguish the older Startup class pattern from the .NET 6+ minimal hosting model
- Broaden error-handling guidance to cover both minimal API and MVC ProblemDetails helpers across supported target frameworks
0.1.1 - 2026-03-22
patch
- Add "Precedence and applicability" section to SKILL.md establishing resolution order: repo policy > tooling config > skill defaults
- Add applicability callout to all four convention reference files (TypeScript, React, C#, Markdown)
- Guide maintainers to record overrides in CONTRIBUTING.md, contributor guides, or ADRs
Resolves equinor/fusion-core-tasks#842
0.1.0 - 2026-03-21
minor
C# Code Conventions
C# naming, null safety, async/await, code style.
Applicability: Org-wide baseline. Repo policy (CONTRIBUTING.md, ADRs) and tooling (.editorconfig,Directory.Build.props) take precedence on explicit override. See skill Precedence and applicability for resolution order.
Project structure
Common ASP.NET Core layered layout:
Controllers/ ← API controllers (MVC)
Domain/
Commands/ ← MediatR IRequest command + nested Handler class
Query/ ← MediatR IRequest query + nested Handler class
Models/ ← Domain-internal result types
Errors/ ← Domain-specific exception types
Behaviours/ ← MediatR pipeline behaviours
Database/
Entities/ ← EF Core entity types
Extensions/ ← EF Core queryable helpers
*DbContext.cs ← EF Core DbContext
Authorization/ ← IAuthorizationRequirement and handler extensions
Integrations/ ← External service client adapters
Program.cs ← Entry point (and DI registration for minimal APIs)Minimal APIs (.NET 6+): Program.cs; endpoints in Endpoints/ or by feature. Startup.cs for older patterns only. Reusable packages: common/ or shared/. Integration tests: sibling test/.
Naming
| Kind | Convention | Example |
|---|---|---|
| Interfaces | I prefix | IUserCache, IEmailClient |
| Types / classes | PascalCase | OrderDbContext, CreateOrder |
| Methods / properties | PascalCase | GetActiveItemsAsync, CreatedAt |
| Local variables | camelCase | filteredItems, cachedResults |
| Parameters | camelCase | cancellationToken, userId |
| Private fields | camelCase | logger, mediator |
| DB entity classes | Db prefix | DbOrder, DbLineItem |
| API response models | Api prefix | ApiOrderV2, ApiLineItem |
| Domain query results | semantic prefix | QueryOrder, QueryProfile |
| MediatR commands | verb phrase | CreateOrder, CancelBooking |
| MediatR queries | Get* phrase | GetOrderById, GetActiveUsers |
| Test classes | *Tests suffix | OrderApiTests, CacheTests |
| Test methods | Subject_Context_ShouldOutcome | CreateOrder_AsGuest_ShouldBeUnauthorized |
Compiler settings and style enforcement
- Nullable reference types:
enable— global (Directory.Build.propsor project file). No opt-out without documented reason. - TreatWarningsAsErrors:
true— no warning accumulation. - ImplicitUsings: project preference; production explicit, tests may enable.
- GenerateDocumentationFile:
trueon service/library projects. Warning1591suppressed — generated where present, not required on every member. - `csharp_using_directive_placement`:
outside_namespace. - File-scoped namespaces: prefer (
csharp_style_namespace_declarations = file_scoped). Exception: EF Core migrations. - Enforce via
.editorconfigin repo root.
Null safety
- Nullable reference types enabled project-wide — no dereference without null check or
?.. !sparingly, typically on EF CoreDbSetproperties (= null!).ArgumentNullException.ThrowIfNull(arg)for entry-point guards.- Prefer
FirstOrDefault()/SingleOrDefault()overFirst()/Single()when result may be absent.
Async/await
- I/O methods:
async Task/async Task<T>. Neverasync voidoutside event handlers. - Never block with
.Resultor.Wait(). - Accept
CancellationToken cancellationToken = defaultin async public methods.
Disposables
IDisposable:usingstatement or declaration.- EF Core
DbContext: DI-managed, scoped; don't manually dispose.
Architecture patterns
- Thin endpoints: parse, dispatch, return. No business logic in controllers or endpoint definitions.
- Minimal APIs and MVC: both valid (
app.MapGet(...)or[ApiController]). Pick one style per project. - CQRS / MediatR: dispatch via handlers (
IRequest+IRequestHandler). Commands (state-changing) and queries (read-only) in distinct folders. - Authorization: prefer policy/requirement-based over inline role string checks.
- API versioning:
[ApiVersion("X.0")]+[MapToApiVersion("X.0")]fromAsp.Versioning. Versioned controllers may be split aspartialclasses per version file. - Route naming: kebab-case path segments (
/orders/{orderId}/line-items).
API response models
Apiprefix on all response types (ApiOrderV2,ApiLineItem)- Suppress null properties where they add noise:
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]or[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - One JSON serializer per project:
System.Text.Jsonfor new, Newtonsoft.Json for existing - Versioned models:
V2/V3suffix, may inherit previous version - Don't reuse models across endpoints — hidden coupling
- Response models near controllers (
Controllers/Models/orControllers/ViewModels/)
EF Core conventions
- Entity types:
Dbprefix (DbOrder,DbLineItem). DbSet<T>initialized with= null!(EF Core sets at runtime).- Enum columns as strings:
HasConversion(new EnumToStringConverter<TEnum>())— readable in DB, survives reordering. - Indexes/relationships:
OnModelCreatingfluent builders, not data annotations.
Error handling
- Domain errors: typed exceptions extending
Exception(RoleExistsError : Exception). - Constructor sets formatted message; class exposes read-only domain props.
- RFC 7807 Problem Details for errors: minimal APIs use
Results.Problem(...)/TypedResults.Problem(...); MVC useControllerBase.Problem(...)or exception handler middleware. - Catch specific domain exception; middleware handles unexpected.
Code style (enforced via .editorconfig)
- `var` is not used (
csharp_style_var_*all set tofalse) — always write explicit types. - 4 spaces indent; CRLF line endings; final newline required; max line length 200 characters.
- Always use braces on control flow blocks (
csharp_prefer_braces = true). - Expression-bodied members: allowed for accessors, lambdas, properties — not for constructors or full methods.
- No
this.qualifier (dotnet_style_qualification_for_*allfalse). - Prefer predefined language keywords over BCL type names (
int, notInt32). - Prefer pattern matching (
is,switchexpressions) overas-with-null-check casts. - Use object/collection initializers; prefer null-coalescing (
??) and null-propagation (?.). dotnet_style_readonly_field = true— fields that are not mutated after construction should bereadonly.
XML doc comments
GenerateDocumentationFile = trueon all service projects.- Warning
1591is suppressed, so comments are generated where present but not required on every member. - Use
<summary>,<param>,<returns>,<remarks>,<list>as needed;<see cref="..."/>for cross-references.
Testing
- Framework: xUnit +
Microsoft.AspNetCore.Mvc.Testingfor integration tests. - Assertions: use xUnit
Assert.*or consider lightweight assertion libraries likeShouldlyorAwesomeAssertions. Prefer readable assertion messages. - Containers:
Testcontainers(e.g.Testcontainers.MsSql) for integration tests that need a real database — prefer over in-memory providers for production-representative coverage. - Test class naming:
*Testssuffix, one class per subject (OrderApiTests,CacheTests). - Test method naming:
Subject_Context_ShouldOutcome(CreateOrder_AsGuest_ShouldBeUnauthorized). - Structure:
// Arrange/// Act/// Assertcomment blocks in unit tests. - Grouping:
[Collection(...)]to group integration tests sharing aWebApplicationFactoryinstance. - Mocks: prefer hand-written
Test*adapter classes over a mocking framework for external dependencies — keeps tests readable and avoidsSetup/Verifycomplexity.
Markdown Code Conventions
Markdown conventions: docs, READMEs, changelogs, skill files.
Applicability: Org-wide baseline. Repo policy (CONTRIBUTING.md, ADRs) and tooling (.editorconfig,.markdownlint.json) take precedence on explicit override. See skill Precedence and applicability for resolution order.
Reference: CommonMark spec · GitHub Flavored Markdown spec
---
Frontmatter
- Valid YAML: no malformed keys, unquoted special chars, broken indentation.
---on first line, closing---before body.- Quote values containing
:,#,{,},[,], or leading/trailing spaces. nullfor explicitly empty values; omit key if not applicable.
Document structure
- One
#(H1) per document — the document title. - Do not skip heading levels (
##→####is invalid). - No duplicate heading text at the same nesting level within a section.
- Blank line before/after headings, fenced code blocks, block quotes.
- Anchors auto-generated: lowercase, spaces→
-, punctuation removed. Update[text](#anchor)links on heading changes.
Links
- Relative links for same-repo files — survive clones/forks.
- Internal anchor links must target existing heading.
- No bare URLs — wrap in
<>or[label](url).
Code blocks
Every fenced block needs language ID. Common: ts, tsx, js, bash, sh, yaml, json, csharp, md, text.
````markdown
const x = 1;````
Use single backticks for inline code and commands: ` git status `.
Lists
-for unordered; consistent within doc (*and+valid but mixing hurts readability)- Ordered: start at
1 - Nest by aligning marker under first char of parent text
Emphasis
| Intent | Syntax |
|---|---|
| Bold | **text** |
| Italic | *text* |
| Bold + italic | ***text*** |
Do not mix **/__ or */_ styles within the same document.
Images
Descriptive alt text: . Relative paths for repo images.
Callouts
Portable callout (any renderer):
> **Note:** Informational note.
> **Warning:** Urgent issue requiring attention.On GitHub: use native alert syntax (see GitHub-specific → Alerts).
Formatting
- Spaces (not tabs) for indentation in lists and code blocks
- No trailing whitespace
- Prose <120 chars for diff readability; code blocks may exceed
---
GitHub-specific
Rules for GitHub (GFM) render target. Reference: GitHub basic syntax guide
Alerts
GFM alert syntax instead of plain blockquotes — GitHub renders with icons and colours.
> [!NOTE]
> Informational note.
> [!TIP]
> Optional helpful advice.
> [!IMPORTANT]
> Key information required to succeed.
> [!WARNING]
> Urgent issue requiring immediate attention.
> [!CAUTION]
> Risk or negative consequence of an action.Limit to one or two alerts per document. Do not nest them.
Strikethrough
~~text~~ = strikethrough. GFM-only, not in CommonMark.
Task lists
- [x] Completed item
- [ ] Incomplete itemMentions and references
- Mention a person or team with
@usernameor@org/team. - Reference an issue or PR with
#123or a full URL. - Close an issue from a PR body or commit message with a closing keyword:
Closes #123,Fixes #123,Resolves #123.
React Code Conventions
React naming, component structure, hooks for Fusion Framework apps.
Applicability: Org-wide baseline. Repo policy (CONTRIBUTING.md, ADRs) and tooling (biome.json,tsconfig.json) take precedence on explicit override. See skill Precedence and applicability for resolution order.
Naming conventions
| Kind | Convention | Example |
|---|---|---|
| Component file | PascalCase | DataGrid.tsx |
| Component name | PascalCase | DataGrid |
| Hook file | camelCase | useItems.ts |
| Hook name | use prefix | useItems |
- Event handlers:
handleprefix + event noun (handleClick,handleSubmit) - Boolean props mirroring HTML attributes: shorthand (
disablednotdisabled={true})
Component structure
- One component per file; name matches file
- Props interface co-located with component
- Never define component inside another — causes full unmounts on parent re-render
- Split data-fetching, transformation, rendering into separate layers
Hooks rules
- Never call hooks conditionally — violates the Rules of Hooks
- Dependency arrays in
useEffect/useCallback/useMemomust be complete (avoid stale closures) - No object/array literals or inline functions in dependency arrays — causes infinite render loops
- Use
useMemoor derived values in render for data transformations, notuseEffect - Never mutate state directly; use the setter or dispatch
Keys in lists
Use a stable key on list items. No array index as key when list order can change.
Accessibility
- All interactive elements (buttons, inputs, links) require a visible label or
aria-label/aria-labelledby - Use semantic HTML —
<button>not<div onClick>. For non-semantic interactive elements, addonKeyDown/role/tabIndex
TSDoc
Follow references/typescript.conventions.md for TSDoc. Document props interface on exported components.
TypeScript Code Conventions
TypeScript conventions: Fusion Framework apps, libraries, scripts, skill tooling.
Applicability: Org-wide baseline. Repo policy (CONTRIBUTING.md, ADRs) and tooling (biome.json,tsconfig.json) take precedence on explicit override. See skill Precedence and applicability for resolution order.
---
TSDoc — mandatory for all exports
All exported functions, components, hooks, classes, types — TSDoc required.
Required tags
| Tag | Required when |
|---|---|
| Summary (first line) | Always — explain intent and why, not what |
@param | Every parameter |
@returns | Every non-void function |
@template | Every generic type parameter |
@throws | Meaningful error paths |
@example | User-facing and non-trivial public APIs |
@deprecated | When superseded — include the replacement |
Good example
/**
* Formats a time range into a human-readable string.
*
* Combines start and end times, showing only the time portion
* for same-day ranges to reduce visual noise.
*
* @param startTime - ISO 8601 start timestamp.
* @param endTime - ISO 8601 end timestamp.
* @returns A formatted time range string such as `"09:00 – 10:30"`.
* @throws {RangeError} When `endTime` is before `startTime`.
*
* @example
* ```ts
* formatTimeRange('2026-03-17T09:00:00Z', '2026-03-17T10:30:00Z');
* // => "09:00 – 10:30"
* ```
*/
export const formatTimeRange = (startTime: string, endTime: string): string => {
// ...
};Anti-patterns
// ❌ Restates the function name
/** formatTimeRange formats a time range. */
// ❌ Restates the type
/** @param startTime - string */
// ❌ Empty summary
/** @param startTime - ISO 8601 start timestamp. */---
Naming conventions
| Kind | Convention | Example |
|---|---|---|
| Default file | kebab-case | item-service.ts, load-state.ts |
| Class file | PascalCase | ItemService.ts |
| Interface file | <class-file>.interface.ts | ItemService.interface.ts |
| Class | PascalCase | ItemService |
| Interface | PascalCase, no I prefix | Item, ApiResponse |
| Type alias | PascalCase | ItemStatus |
| Enum | PascalCase | LoadState |
| Enum member | PascalCase | LoadState.Idle |
| Constants | SCREAMING_SNAKE_CASE | MAX_ITEMS_PER_PAGE |
| Variables | camelCase | filteredItems |
| Functions | camelCase | formatTimeSlot |
| Generic type parameter | Single uppercase letter or descriptive PascalCase | T, TItem, TResponse |
No `I` prefix on interfaces. Item, not IItem. React naming: see references/react.conventions.md.
---
Type system
Strict mode
Strict mode is always on:
noImplicitAny— never leave types inferred asanystrictNullChecks— handlenullandundefinedexplicitlystrictFunctionTypes— contravariance checks on function parametersnoUncheckedIndexedAccess— array/object index access returnsT | undefined
No any
Never any — defeats type safety.
// ❌ Any
const process = (data: any) => data.value;
// ✅ Unknown + narrowing
const process = (data: unknown): string => {
if (typeof data === 'object' && data !== null && 'value' in data) {
return String((data as { value: unknown }).value);
}
throw new TypeError('Unexpected data shape');
};Use unknown for untyped external inputs. Narrow with z.infer<typeof Schema> (Zod) or type guards.
Type assertions (as)
Avoid as — lies to compiler.
// ❌ Unsafe cast
const item = response.data as Item;
// ✅ Validated typing
function isItem(value: unknown): value is Item {
return typeof value === 'object' && value !== null && 'id' in value;
}
const item = isItem(response.data) ? response.data : null;When as unavoidable (e.g. DOM APIs, library gaps), comment why.
Non-null assertions (!)
Avoid !; justify with comment when unavoidable.
// ❌ Silent assumption
const el = document.getElementById('root')!;
// ✅ Explicit guard
const el = document.getElementById('root');
if (!el) throw new Error('Root element not found in DOM');Discriminated unions
Prefer discriminated unions over type assertions for runtime narrowing.
type LoadState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: Item[] }
| { status: 'error'; error: Error };
function render(state: LoadState) {
switch (state.status) {
case 'success':
return state.data; // narrowed — no assertion needed
case 'error':
return state.error;
// ...
}
}Generics
- Constrain specifically — avoid
T extends anyorT extends object - Descriptive names when
Tambiguous:TItem,TKey,TResponse - Constrain with interfaces when generic needs known shape
// ❌ Unconstrained
function getId<T>(item: T): unknown { ... }
// ✅ Constrained
function getId<T extends { id: string }>(item: T): string {
return item.id;
}Utility types
Prefer utility types over manual duplication.
| Need | Use |
|---|---|
| All fields optional | Partial<T> |
| All fields required | Required<T> |
| Read-only fields | Readonly<T> |
| Subset of fields | `Pick<T, 'a' \ |
| Exclude fields | Omit<T, 'internal'> |
| Function return type | ReturnType<typeof fn> |
| Awaited promise type | Awaited<Promise<T>> |
| Union of object values | T[keyof T] |
Explicit return types
Explicit return types on all exported functions and methods. Unexported helpers: inferred OK when trivially obvious.
// ✅ Explicit return on export
export function formatLabel(value: string): string { ... }
// ✅ Inferred acceptable for trivial internal
const double = (n: number) => n * 2;---
Code style
Variable declarations
constby defaultletonly when reassignment is unavoidable- Never
var
Immutable patterns
Prefer map/filter/reduce/flatMap over mutable push loops.
// ❌ Mutable accumulator
const result: string[] = [];
for (const item of items) {
if (item.active) result.push(item.name);
}
// ✅ Immutable
const result = items.filter((item) => item.active).map((item) => item.name);Complex transforms: for…of with const bindings is fine when immutable form is unreadable.
Single responsibility
One reason to change per function, component, module.
- Functions >~40 lines: extract
- Files >~300 lines: split
- Module exports one primary concept; helpers/types support it only
File organisation
Top-down within module:
1. Imports (external → internal → relative, alphabetical within each group) 2. Constants 3. Types and interfaces 4. Helper functions (unexported) 5. Primary export(s) 6. Default export (if any)
Import style
- Named imports — survive refactors
- Group: external → path-aliased internal → relative
- No unused symbols (lint enforced)
- No barrel re-exports (
index.ts) — hurts tree-shaking
Readability over cleverness
Simple, linear, predictable code. Early returns and guard clauses over nested conditions.
// ❌ Nested pyramid
function process(item: Item | null) {
if (item) {
if (item.active) {
if (item.value > 0) {
return item.value * 2;
}
}
}
return 0;
}
// ✅ Guard clauses
function process(item: Item | null): number {
if (!item) return 0;
if (!item.active) return 0;
if (item.value <= 0) return 0;
return item.value * 2;
}---
Inline comments
Add intent comments (why, not what) for:
- Iterator transforms where the business reason is non-obvious
- Decision gates (
if/switch) with non-obvious conditions - RxJS operator chains and subscriptions
- Complex heuristics, thresholds, or workarounds
- Any
as,!, or// biome-ignorethat needs justification
// Exclude inactive items — the grid must never show retired entries (PLAT-1234)
const activeItems = items.filter((item) => item.status !== 'inactive');// ❌ Restates the syntax — adds no value
// Filter items
const active = items.filter((item) => item.status !== 'inactive');---
Async / await
async/awaitover raw.then()chains- No
awaitin loops when independent — usePromise.all - Always handle rejection:
try/catchor.catch() asynconly when function containsawait; otherwise return plain value
// ❌ Sequential awaits for independent calls
const a = await fetchA();
const b = await fetchB();
// ✅ Parallel
const [a, b] = await Promise.all([fetchA(), fetchB()]);// ❌ Async without await
async function getLabel(): Promise<string> {
return 'hello'; // unnecessary async
}
// ✅
function getLabel(): string {
return 'hello';
}---
Error handling
- Specific error subtypes with structured context, not bare
new Error(). - Catch async errors, rethrow with context.
- Error class hierarchies for distinct failures (network, validation, permission).
- Log at boundary where recovery decided, not at every rethrow.
class ApiError extends Error {
constructor(
message: string,
public readonly statusCode: number,
public readonly endpoint: string,
) {
super(message);
this.name = 'ApiError';
}
}
async function fetchItem(id: string): Promise<Item> {
try {
const response = await client.get(`/items/${id}`);
if (!response.ok) {
throw new ApiError(`Failed to fetch item`, response.status, `/items/${id}`);
}
return response.json() as Promise<Item>;
} catch (err) {
if (err instanceof ApiError) throw err;
throw new ApiError(`Unexpected error fetching item ${id}`, 500, `/items/${id}`);
}
}---
Dead code policy
Remove dead code; don't comment it out.
- Unused imports: remove (lint enforced)
- Commented-out blocks: remove; use Git history
- Unreachable branches: remove
- Unused variables/params: remove or prefix
_when required by callback signature
// ❌ Commented-out dead code
// const oldValue = computeOld(item);
const value = computeNew(item);
// ✅ Remove it; history is in Git
const value = computeNew(item);Related skills
How it compares
Choose fusion-code-conventions over generic lint skills when governance comes from ADRs and contributor docs rather than ESLint or formatter rules alone.
FAQ
What does fusion-code-conventions do?
Applies and explains code conventions across TypeScript, React, C#, and Markdown. Enforces naming rules, file naming patterns, TSDoc and XML doc standards, inline comment intent (the *why*, not the *what*), code structur
When should I use fusion-code-conventions?
Applies and explains code conventions across TypeScript, React, C#, and Markdown. Enforces naming rules, file naming patterns, TSDoc and XML doc standards, inline comment intent (the *why*, not the *what*), code structur
What are common prerequisites?
--- name: fusion-code-conventions description: 'Applies and explains code conventions across TypeScript, React, C#, and Markdown.
Is Fusion Code Conventions safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.