# Radius Code Quality

> Mandatory repository-wide engineering workflow for every TypeScript or JavaScript change in radius-project/ai-extensions. Use whenever an agent adds, edits, refactors, or reviews code, tests, runtime behavior, HTTP routes, page renderers, browser behavior, build logic, or generated-extension inputs. Enforces the Radius Canvas re-architecture, complete Vitest coverage with a goal of 100% coverage for changed production code, required boundary tests, and repository TypeScript/JavaScript conventions.

- Skill: `radius-project/radius-code-quality` (Agent Skill)
- Install (CLI): `npx skillmds@latest add radius-project/radius-code-quality`
- Raw SKILL.md: https://api.skillmd.com/api/skills/radius-project/radius-code-quality/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: radius-project (https://skillmd.com/u/radius-project)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/radius-project/radius-code-quality

---


# Radius code quality

Apply this skill to every production TypeScript or JavaScript change in this repository. A code change is incomplete until its behavior is covered by unit tests, any boundary tests required for the seams it touches are included, and the production design follows the repository's architecture.

The Architecture, TypeScript and JavaScript, Vitest, and Coverage rules below are standing repository policy and apply now.

The two Radius Canvas design documents are the authority for phase-specific migration detail, requirement IDs, inventories, and gates:

- `docs/design/2026-08-radius-canvas-test-architecture.md` — what Radius Canvas does, why the original structure resisted testing, the approved runtime/server/page/browser boundaries, the test layers, and the regression classes.
- `docs/design/2026-08-radius-canvas-test-plan.md` — the phase table and current status, test priorities and enforcement, per-phase acceptance and checked-in evidence, fixtures, CI gates, and the appendices holding the requirement IDs and the exact action, tool, route, page, lifecycle, journey, visual, and host inventories.

Read the relevant sections before changing a seam they define. Never guess at a requirement identifier, phase status, route owner, page state, or test level, and prefer the documents over this summary if they disagree. If the documents are not yet on the current branch, read them from the branch that introduces them or through the GitHub contents API.

Both documents track their own delivery status; the test plan's phase table is the sole source of truth for which infrastructure is delivered. Do not copy phase completion status into this skill. Determine whether delivered infrastructure is a pull-request, scheduled, or release gate from the plan and the owning workflow; phase completion alone does not make a suite a pull-request gate. Real-host qualification is a scheduled release gate and does not block pull requests.

## Required workflow

1. **Understand the change.** Read the affected production modules, their existing tests, package configuration, and relevant architecture or design documentation. Trace callers and external boundaries before editing.
2. **Classify the architecture seam.** Identify whether the change belongs to core, shared adapter, runtime, server, pages, browser, build and packaging, or plugin packaging. For Canvas re-architecture work, find the affected phase and requirement IDs in the test plan's appendices.
3. **Choose the required test levels.** Unit tests are mandatory for every production logic change. Add every boundary level required by the pull-request evidence matrix below; choosing the cheapest faithful level does not permit omitting a required wider contract.
4. **Implement through the intended seam.** Keep dependency direction, state ownership, public contracts, and artifact behavior consistent with the re-architecture. Do not preserve an architectural violation merely to make a test easy to write.
5. **Add tests in the same change.** Across the complete test set, cover successful behavior, errors and failure propagation, boundary and edge conditions, validation, cleanup, and every changed branch. Put each scenario at the lowest level that can faithfully exercise it. Add a higher-level case only when it protects a distinct boundary contract or cross-layer risk; do not duplicate lower-level business-logic coverage. Refactors move or add their tests with the production module.
6. **Measure coverage.** Target 100% line, statement, function, and branch coverage for new and changed production code. Inspect the report and add meaningful missing scenarios rather than coverage-only assertions.
7. **Run the complete applicable gate.** Run targeted tests while iterating, then the repository commands listed under Verification. A manually verified behavior does not replace a checked-in automated test.

## Architecture rules

### Package boundaries

- `packages/core` owns UI-agnostic product logic. It must not depend on adapters, the Copilot SDK, HTTP implementations, the DOM, or browser globals. Outside behavior is represented by typed ports.
- `packages/adapter-shared` owns reusable Node adapter behavior such as managed `rad` and Bicep execution.
- `packages/adapter-canvas` owns Copilot SDK wiring, loopback HTTP, server-rendered pages, browser behavior, and concrete external adapters.
- Adapters may depend on core; core never depends on an adapter. Shared product behavior moves into core instead of being copied between adapters.
- Use real deterministic core functions in adapter tests where practical. Do not duplicate core's unit tests in the Canvas package.

### Canvas re-architecture

- `extension.ts` is the composition root and the only module that imports the SDK's `joinSession`. Other runtime modules may invoke an injected session port, as `runtime/bootstrap.ts` does. Put canvas declarations, tools, hooks, lifecycle, and handlers behind factories in `src/runtime/` with injected dependencies, constructible without joining a real session.
- Own per-instance state, caches, callbacks, server lifecycle, and clocks in the instance-scoped server container. Do not add process-global mutable state.
- Construct one complete typed production dependency object at the composition root. Give route families and services only narrowed dependency views. Missing dependencies fail during construction; never install a silent success-shaped default.
- Keep one route table as the source of truth for method, path, matching, body policy, and handler. Every route has exactly one owner.
- Keep route handlers thin: parse HTTP input, call a use-case service, and serialize its result. Multi-stage setup, graph, environment, deployment, operation, cache, or workflow behavior belongs in independently testable services with narrow ports.
- Preserve methods, status codes, headers, payloads, stream framing, body behavior, cache scope, and fallthrough results during structural moves. Contract hardening such as a request-size limit, a `413`, a global JSON `500`, or a centralized error envelope is a separate, explicitly approved change shipped with before-and-after HTTP integration contracts, never a side effect of moving a route.
- If a server-sent event handler fails after headers are sent, emit that route's terminal error or completion frame and close exactly once.
- Keep server modules decomposed. Route family names assign ownership and do not justify one large file per family.
- Split pages by responsibility into the shared shell, graph pages, environment and credential pages, and deployment pages. Preserve stable markup, serialized state, escaping, theme tokens, URLs, operation states, and accessibility semantics.
- Put executable browser behavior in importable TypeScript under `src/browser/`. Expose explicit initialization and teardown with narrow ports for network, navigation, clocks, external opening, and DOM access. Compile those modules in memory into deterministic self-contained inline scripts; do not commit generated JavaScript, maintain behavior as source strings, duplicate it in page templates, or fetch the extension's own browser modules at runtime.
- Keep already testable helper modules in place and inject them through the composition root. Re-architecture is a surgical seam extraction, not a repository-wide file shuffle.
- Preserve one loadable generated artifact at `plugins/radius/dist/extension.mjs`, with Copilot SDK imports externalized. Never hand-edit generated output.
- Compatibility records and forwarding modules may bridge a migration but contain no independent behavior. While old and new paths coexist, record the exact residual fallback inventory, and delete the fallback only when that inventory is empty.

### Safety and compatibility

- External errors must propagate as explicit failures; never return success-shaped fallbacks.
- Destructive environment and deployment operations fail closed whenever identity, preconditions, or external state cannot be established.
- The session repository's graph and planned views use the current worktree branch, never an implicit `main`. Graph diff uses explicit committed base and head branches.
- Reject stale source-reference context tokens, confine filesystem paths to the workspace, and preserve deployment repair attempt identity.
- Bind test HTTP servers to `127.0.0.1` on OS-assigned ports. Close servers, streams, child processes, timers, browser contexts, and temporary workspaces on success and failure.
- Rendered HTML, JavaScript strings, URLs, and serialized state must be escaped for their output context.
- Canvas page state must use `pages/page-state.ts::renderPageState(id, state)` and `browser/pages/state.ts::readPageState`. Import IDs and their matching types from the behavior-free `pages/browser-state-ids.ts`; do not hand-build hidden state elements, duplicate ID literals, or expose alternative serializers. The helper owns complete JSON serialization and hidden-element encoding; the reader uses `textContent` and `JSON.parse`, followed by existing runtime field narrowing.
- Never interpolate runtime data into executable page scripts or use quoted `escapeHtml` as JavaScript string encoding. Keep HTML text/attribute escaping, URL policy, and page-state transport separate. Trusted compiled browser scripts and static feedback code are not page state; ordinary HTTP/SSE/request/storage/build JSON is not an HTML embedding sink. Trusted markup/fragment APIs are not arbitrary-input sanitizers. Keep the parser-based `test/ci/page-state-contract.test.ts` convention check and its positive/negative fixtures passing; it complements executable HTML-parser tests rather than proving arbitrary-program safety.
- Pass command arguments as an argv array. Never interpolate user-controlled values into a shell command or enable shell execution for them.
- Tests and diagnostics must not use personal credentials, inherited tokens, live cloud resources, mutable remote repositories, or secret-shaped fixture values.

## TypeScript and JavaScript rules

- Use ESM and follow existing import conventions, including `.js` specifiers from TypeScript source where the repository does so.
- Preserve strict type safety. Prefer domain types, discriminated unions, type guards, and `unknown` narrowing over `any`, broad index signatures, non-null assertions, or chained casts.
- Do not use `as any` or `as unknown as` to bypass a contract. Improve the contract or provide a typed test fake.
- Keep public and cross-module contracts explicit. Keep types local when no other module needs them.
- Prefer small pure functions and explicit dependencies over hidden I/O, module mocking, or mutable globals.
- Represent I/O behind narrow ports and inject time, filesystem, process, network, GitHub, cloud, and persistence behavior when logic depends on it.
- Handle promises explicitly. Await work or deliberately aggregate it; do not leave floating promises or unobserved rejections.
- Validate untrusted data at the boundary. Preserve actionable errors and avoid broad catches that hide the original failure.
- Make cleanup idempotent and use `try/finally` when acquiring resources.
- Avoid speculative abstractions and unrelated refactors. Reuse existing helpers and patterns before adding another implementation.
- Follow the repository ESLint, TypeScript, and Prettier configuration. Use two-space indentation, double quotes, semicolons, no trailing commas, and LF endings as configured.
- Add comments only for non-obvious invariants, safety constraints, or architectural intent. Comments do not substitute for clear names and types.

## Vitest rules

### Unit-test placement and structure

- Collocate each unit test beside its production module as `*.test.ts`. There is no `test/unit/` directory.
- When production code moves, move its tests to the same destination in the same change.
- Put non-unit Canvas test support under `packages/adapter-canvas/test/`. Its actual layout is `ci`, `component`, `e2e`, `fixtures`, `integration/artifact`, `integration/http`, `integration/runtime`, `support`, and `visual`. Browser functional, critical journey, accessibility, and keyboard cases run through `test/e2e/canvas-chromium.test.ts` when they exercise the same workflow. Reliability reuses the applicable unit, browser, end-to-end support, and HTTP tests through `vitest.reliability.config.ts`; it is not a separate directory. Do not create a `host` suite until the test plan delivers its qualified harness.
- Put reusable deterministic data in `test/fixtures/` and shared fakes and harnesses in `test/support/`, following the existing layout. Production code must never import test support.
- A new suite directory only runs once it is added to `packages/adapter-canvas/vitest.config.ts` or a dedicated config such as `vitest.artifact.config.ts`, with a matching package script. Wire it up in the same change.

### Test design

- Test observable behavior, not implementation text. Source substring assertions may guard a narrow build contract but cannot replace executable behavior tests.
- Give tests descriptive behavior-oriented names. Keep arrange, act, and assert phases easy to identify without boilerplate comments.
- Use table-driven `it.each` or `describe.each` cases for genuine input matrices; do not obscure distinct behaviors merely to reduce line count.
- Assert meaningful outputs, state transitions, and calls through ports. Avoid assertions that only prove a mock returned its configured value.
- The complete test set must include successful behavior, errors and failure propagation, and relevant boundary and edge conditions. At minimum, consider invalid input, empty values, minimum and maximum accepted values, one-below and one-above limits, malformed or partial responses, unavailable dependencies, timeouts, cancellation, idempotency, retries where applicable, cleanup on success and failure, and repeated or concurrent operations. Assign each scenario to the lowest test level that can faithfully prove it. At higher levels, test only the errors and boundaries introduced by that boundary itself, such as serialization, transport, browser events, process startup, or multi-layer state flow; do not repeat scenarios already proven at a lower level unless the wider integration could change the result.
- Use explicit deterministic fakes that throw on unspecified operations. Do not use broad module mocks that silently succeed for behavior the scenario did not model.
- Do not manufacture testability with broad module mocks, import-time side-effect juggling, source-substring assertions standing in for behavior, private-internal reach-through, or test-only production hooks. Extract the real seam or test through the closest existing public boundary.
- Reset or restore spies, fake timers, globals, environment variables, and module state. Tests must be order-independent and safe under parallel execution.
- Use fake clocks and condition-based waits. Fixed sleeps are prohibited except when the sleep behavior itself is under test.
- Keep fixtures minimal, readable, immutable by default, platform-neutral, and secret-free. Use repository-relative synthetic paths and explicit Windows cases where path behavior matters.
- Do not reach GitHub, GHCR, cloud APIs, public CDNs, user storage, local CLI login, or the internet from pull-request tests.
- Small snapshots are acceptable for stable structural fragments. Broad snapshots must not replace semantic, state, escaping, or error assertions.
- A test that passes only on retry is a flake. Unit, runtime-integration, and HTTP-integration tests never retry; Chromium layers allow at most one diagnostic retry with the original failure retained. Never quarantine a safety, branch-selection, path-confinement, external-error, or destructive-action test.
- Keep test timeouts within the plan's defaults: 5 seconds for unit, 10 seconds for browser component and functional, 15 seconds for runtime and HTTP integration, and 30 seconds for built-extension smoke and each journey, accessibility, keyboard, or visual case.

## Required pull-request test evidence

Use the test layer names from the design documents exactly: unit, runtime integration, HTTP integration, built-extension smoke, browser component, browser functional, critical journey, accessibility, keyboard, visual, and real-host. Do not reintroduce older acronyms for these layers.

Every pull request must apply the following matrix using the test plan's current delivery and enforcement status. The rows are cumulative: when a change matches multiple pull-request rows, include the evidence from each one. Rows explicitly marked scheduled or release describe post-merge or release evidence and are not required pull-request gates. Across the applicable levels, cover the changed behavior's success case, error and failure cases, and boundary and edge conditions without duplicating the same scenario. A higher-level test complements focused lower-level tests by protecting a distinct boundary contract or cross-layer risk; it does not repeat or replace them.

| Pull-request change                                                                      | Required checked-in tests and evidence                                                                                                                                                                                                                                                                                  |
|------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Production logic, bug fix, validation, state transition, parser, or renderer helper      | Collocated unit tests for every changed behavior and branch, including success, explicit error or failure behavior, and boundary and edge values                                                                                                                                                                        |
| Runtime factory, declaration, action, tool, hook, branch context, open, or close         | Unit tests plus runtime integration built from the real runtime composition with a fake SDK session; include registration/lifecycle success, SDK or callback failure, duplicate or stale events, and open/close/reconnect boundaries                                                                                    |
| Server container, request parsing, dispatch, route, stream, cache, or page route         | Unit tests plus HTTP integration against a real loopback server on an OS-assigned port with controlled external fakes; include successful contracts, malformed and limit-boundary requests, dependency failures, cleanup, and fail-closed behavior                                                                      |
| Page renderer or serialized browser state                                                | Unit tests plus HTTP integration for meaningful markup, serialized state, escaping, stable IDs, required markers, empty/minimum/maximum content, malformed data, and error states                                                                                                                                       |
| Browser entry, helper, generated inline script, or DOM interaction                       | Importable unit tests plus generated-script and renderer-wiring checks, browser component tests where a component owns the behavior, and browser functional tests in real Chromium for user-visible interaction; cover network errors and DOM boundaries                                                                |
| Supported multi-page or browser-and-server workflow                                      | All owning lower-level tests plus a critical journey in Playwright with controlled server data; cover successful completion, surfaced server or browser failure, and navigation, cancellation, stale-result, retry, and resume boundaries                                                                               |
| Interactive control, focus behavior, announcement, or material page state                | Owning unit/component/functional tests plus automated WCAG 2.2 A/AA and keyboard coverage for success, validation/error states, disabled and loading states, focus order, focus restoration, and keyboard-only boundaries                                                                                               |
| Selected stable visual state                                                             | **Scheduled evidence, not a pull-request gate:** owning behavioral tests land in the pull request. Regenerate canonical light- and dark-theme baselines only through the `Canvas Functional Tests` workflow dispatch with `update_snapshots`; download and review its Ubuntu artifact before committing the baselines   |
| Async work, external call, subprocess, polling, cancellation, timeout, or cleanup        | Owning unit and boundary tests are pull-request evidence. **Scheduled evidence:** add the case to the reliability run when it protects races, repetition, multiple instances, platform paths, timeout limits, or cleanup under failure; if its test path is not already included, update `vitest.reliability.config.ts` |
| GitHub identity, token, account, scope, package credential, or `gh` behavior             | Owning unit and boundary tests plus applicable Chromium workflow coverage; include precedence, missing/insufficient credentials, wrong identity, redaction, unavailable commands, and fail-closed boundaries without real secrets                                                                                       |
| Destructive environment or deployment operation, path, branch, or source reference       | Owning unit, HTTP/runtime, and applicable critical-journey tests; include authorization/precondition failure, path traversal and confinement, stale references, wrong branch/identity, repeated requests, and fail-closed boundaries                                                                                    |
| Build, packaging, exports, dependencies, runtime/page/browser source, or generated input | Built-extension smoke against the real production build in a subprocess with an SDK registration stub, in addition to the owning tests above; verify startup, registration, failure propagation, shutdown, and the single-artifact contract                                                                             |
| Test, fixture, fake, harness, or test configuration                                      | Direct tests for non-trivial harness behavior plus every suite the change supports; prove deterministic success, intentional failure reporting, boundary handling, isolation, and cleanup                                                                                                                               |
| Generated output such as `plugins/radius/dist/`                                          | Rebuild from source, run built-extension smoke, and confirm the artifact is in sync; never hand-edit generated output                                                                                                                                                                                                   |
| Real-host installation, discovery, or panel lifecycle                                    | Real-host qualification is a scheduled release gate, not a pull-request test. Never claim loopback HTTP, browser emulation, or built-extension smoke as real-host coverage                                                                                                                                              |

Documentation-only changes do not require product tests unless they change executable examples, generated inputs, test configuration, or claimed behavior. For all other pull requests, omitting required pull-request evidence from a matching row requires a written explanation that the evidence is structurally inapplicable to the change; inconvenience, implementation size, coverage at another layer, or a scheduled row is not sufficient.

## Coverage policy

- The goal is **100% line, statement, function, and branch coverage for every new or changed production-code path that is reachable through a real seam**.
- Treat 100% as a quality goal, not a mandate to force coverage through unnatural testing techniques. Do not expose production internals solely for tests, add test-only branches or hooks, over-mock implementation details, invoke unreachable states artificially, or write assertions whose only purpose is to execute a line. Prefer behavior-focused tests through natural seams; when a legitimate path cannot be exercised naturally, document the limitation as described below.
- Repository aggregate and per-package coverage must never decrease. The accepted baseline lives in `coverage-baseline.json` and is enforced as Vitest thresholds in `vitest.config.ts`; treat it as a hard floor. Raise it alongside tested production changes, and never lower it without explicit justification and review approval.
- The 80% line, 80% function, and 70% branch thresholds that applied to newly extracted runtime, route, and renderer modules were minimum migration gates, not the target for new work under this skill. `packages/adapter-canvas/src/browser/**` is pinned at 99.5% statements, branches, functions, and lines in `coverage-baseline.json`; that measured threshold is a real gate rather than a migration floor.
- The browser scope has only 0.5 percentage points of threshold headroom, so its escape valve is explicit rather than silent. When a changed browser path is genuinely unreachable — a defensive branch that only a violated invariant could reach, or a platform capability the test substrate cannot withhold — mark exactly that path with a `/* v8 ignore next */` comment whose adjacent line states why it is unreachable and what would have to change to make it testable, and call it out in the pull request for review. Never widen the ignore beyond the unreachable path, never ignore a path merely because covering it is awkward, and never lower the pinned threshold instead. An ignore without a justification comment, or one a reviewer has not accepted, is coverage gaming under the rule above.
- Coverage percentages do not replace scenario gates. Explicitly test worktree branch selection, stale source-reference rejection, external-error propagation, path confinement, resumable operation identity, destructive fail-closed behavior, and retained action, tool, lifecycle, route, and artifact contracts whenever affected.
- Do not game coverage with ignored executable lines, uncovered allowlists, trivial assertions, or tests coupled to implementation details.
- Exclude only generated bundle text, vendored libraries, and test fixtures as defined by the test architecture.
- If 100% changed-code coverage is genuinely infeasible, document the exact uncovered lines or branches and why they are structurally unreachable, rather than merely inconvenient, and add the strongest behavior and boundary tests available. Effort, time pressure, and test awkwardness do not qualify, and a repository threshold is never lowered automatically.
- Do not expand an unrelated change solely to cover pre-existing untouched code, but do not leave newly introduced branches or changed behavior untested.

## Verification

Run the smallest targeted Vitest command during implementation, then run the baseline pull-request check from the workspace root:

```text
pnpm install --frozen-lockfile
pnpm run typecheck
pnpm run lint
pnpm run format:check
pnpm run coverage
pnpm run build
pnpm run test:integration:windows-process
pnpm run test:integration:artifact
pnpm run test:component
pnpm run test:chromium
```

These are the required pull-request gates. `pnpm run coverage` already includes runtime and HTTP integration, so do not rerun those directories separately. The Windows process integration command runs in the Windows CI job; component and Chromium commands run in the Playwright CI container. Run them locally when the required environment is available, and otherwise require their CI jobs to pass without claiming they ran locally. Visual and reliability are scheduled gates, not pull-request gates. Regenerate or validate canonical visual baselines through the Ubuntu `Canvas Functional Tests` workflow rather than a non-Linux local run. Use the `Canvas Reliability` workflow for the scheduled cross-platform resilience sweep, and update its Vitest allowlist when a new test path must participate. Do not replace any required gate with manual validation or imply real-host coverage from another suite.

Before finishing, confirm:

- Every production behavior change has meaningful unit coverage.
- Those changed production paths target 100% line, statement, function, and branch coverage.
- Every applicable matrix row is satisfied with checked-in tests at each required level.
- Success, error and failure propagation, and relevant boundary and edge conditions are covered at the lowest faithful level, with higher-level tests limited to distinct boundary contracts and cross-layer risks.
- Architecture, package boundaries, state ownership, branch behavior, safety, and artifact contracts are preserved.
- Tests are deterministic, isolated, secret-free, and clean up all resources.
- Typecheck, lint, format, applicable tests, coverage, and build pass.

