architecture-checks.md
# Architecture Checks
## AR-1: Direct API Client Access
**Severity:** High | **Auto-Fix:** No
UI elements should not call API services directly. Use the repository pattern.
```typescript
// BAD - Direct API call in UI element
export class MyDashboardElement extends UmbLitElement {
async connectedCallback() {
const response = await MyService.getAll();
this._items = response.data;
}
}
// GOOD - Using repository via context
export class MyDashboardElement extends UmbLitElement {
constructor() {
super();
this.consumeContext(MY_WORKSPACE_CONTEXT, (context) => {
this.#workspaceContext = context;
this.observe(context.items, (items) => { this._items = items; });
});
}
}
```
**Detection:** `*Service.get*()`, `tryExecute()`, `tryExecuteAndNotify()` in `.element.ts` files
**Reference:** `umbraco-repository-pattern` skill
---
## AR-2: No Workspace Context
**Severity:** High | **Auto-Fix:** No
Workspace elements must use workspace context for data management.
```typescript
// BAD - Data loading directly in element
export class MyWorkspaceElement extends UmbLitElement {
async connectedCallback() {
const id = this.getRouteParam('id');
this._data = await this.#repository.get(id);
}
}
// GOOD - Using workspace context
export class MyWorkspaceElement extends UmbLitElement {
constructor() {
super();
this.consumeContext(MY_WORKSPACE_CONTEXT, (context) => {
this.#workspaceContext = context;
this.observe(context.data, (data) => { this._data = data; });
});
}
}
```
**Detection:** Data loading in element `connectedCallback`, save handlers in elements
**Reference:** `umbraco-workspace` skill
---
## AR-3: Source Pattern Verification
**Severity:** Medium | **Auto-Fix:** No
Extensions should follow patterns established in Umbraco source code.
| Extension Type | Reference Pattern |
|----------------|-------------------|
| Dashboard | `src/packages/*/dashboards/` |
| Property Editor | `src/packages/*/property-editors/` |
| Workspace | `src/packages/*/workspace/` |
| Collection | `src/packages/*/collection/` |
| Tree | `src/packages/*/tree/` |
**Detection:** Compare file structure, class hierarchy, manifest structure against core
---
## AR-4: Inconsistent Persistence
**Severity:** Medium | **Auto-Fix:** No
Save/persistence patterns should follow Umbraco conventions.
```typescript
// BAD - Save in wrong location
render() {
return html`
<uui-button @click=${() => this.#repository.save(this._data)}>Save</uui-button>
`;
}
// GOOD - Workspace context handles persistence
async #handleSubmit() {
await this.#workspaceContext.submit();
}
```
**Detection:** Direct repository calls in render methods, missing error handling after save
---
## AR-5: Missing Repository Layer
**Severity:** High | **Auto-Fix:** No
Extensions with data persistence should implement the repository pattern.
```typescript
// BAD - Direct service usage
export class MyFeature {
async getData() {
return await MyDataService.get();
}
}
// GOOD - Repository layer
export class MyDataRepository extends UmbControllerBase {
async requestById(id: string) {
return this.#source.get(id);
}
}
```
**Detection:** Direct service calls without repository wrapper
**Reference:** `umbraco-repository-pattern` skill
---
## AR-6: Circular Context Dependencies
**Severity:** Critical | **Auto-Fix:** No
Context dependencies must not form cycles.
```typescript
// BAD - Circular dependency
// context-a.ts
consumeContext(CONTEXT_B, (b) => { ... });
// context-b.ts
consumeContext(CONTEXT_A, (a) => { ... });
```
**Detection:** Map context consume relationships, detect cycles
code-quality-checks.md
# Code Quality Checks
## CQ-1: Extension Type Usage
**Severity:** Critical | **Auto-Fix:** Yes
Extensions must use proper Umbraco types, not generic Lit/Web Component patterns.
```typescript
// BAD
@customElement('my-element')
export class MyElement extends LitElement { }
// GOOD
@customElement('my-element')
export class MyElement extends UmbLitElement { }
```
**Detection:** Elements extending `LitElement` instead of `UmbLitElement`
**Auto-Fix:** Replace `LitElement` with `UmbLitElement`, add import from `@umbraco-cms/backoffice/lit-element`
---
## CQ-2: Manifest Registration
**Severity:** High | **Auto-Fix:** No
Extensions must be properly registered in manifests.
```typescript
// BAD - No manifest registration
export class MyDashboard extends UmbLitElement { }
// GOOD - Registered in manifest
export const manifests: Array<ManifestDashboard> = [
{
type: 'dashboard',
alias: 'My.Dashboard',
name: 'My Dashboard',
element: () => import('./my-dashboard.element.js'),
}
];
```
**Detection:** Elements without corresponding manifest entries
**Reference:** `umbraco-dashboard`, `umbraco-workspace` skills
---
## CQ-3: Element Implementation
**Severity:** Medium | **Auto-Fix:** Partial
Elements must follow Umbraco patterns for lifecycle and rendering.
```typescript
// BAD - Direct property manipulation
this.data = newData;
// GOOD - Reactive property
@state()
private _data?: MyData;
```
**Detection:** Missing `@state()` or `@property()` decorators on reactive properties
**Auto-Fix:** Add `@state()` decorator to private reactive properties
---
## CQ-4: Context API Usage
**Severity:** High | **Auto-Fix:** No
Extensions must use Context API for shared state and services.
```typescript
// BAD - Direct service instantiation
const service = new MyService();
// GOOD - Context consumption
this.consumeContext(UMB_NOTIFICATION_CONTEXT, (context) => {
this._notificationContext = context;
});
```
**Detection:** Direct service instantiation instead of context consumption
**Reference:** `umbraco-context-api` skill
---
## CQ-5: State Management
**Severity:** Medium | **Auto-Fix:** No
State must be managed reactively using Umbraco patterns.
```typescript
// BAD - Manual state updates
this.items = await this.fetchItems();
this.requestUpdate();
// GOOD - Observable state
this.observe(
this._workspaceContext.data,
(data) => { this._data = data; }
);
```
**Detection:** Manual `requestUpdate()` calls, missing `observe()` usage
**Reference:** `umbraco-state-management` skill
---
## CQ-6: Localization
**Severity:** Medium | **Auto-Fix:** Partial
User-facing text must use localization.
```typescript
// BAD - Hardcoded strings
html`<uui-button>Save</uui-button>`
// GOOD - Localized
html`<uui-button><umb-localize key="general_save"></umb-localize></uui-button>`
```
**Detection:** Hardcoded user-facing strings in templates
**Auto-Fix:** Wrap known strings with `<umb-localize>` for common terms
**Reference:** `umbraco-localization` skill
---
## CQ-7: Naming Conventions
**Severity:** Low | **Auto-Fix:** No
Extensions must follow Umbraco naming conventions.
| Type | File Pattern | Class Pattern | Alias Pattern |
|------|--------------|---------------|---------------|
| Element | `*.element.ts` | `*Element` | - |
| Context | `*.context.ts` | `*Context` | - |
| Controller | `*.controller.ts` | `*Controller` | - |
| Repository | `*.repository.ts` | `*Repository` | - |
| Manifest | `manifests.ts` | - | `Vendor.Name` |
**Detection:** Files/classes not following naming patterns
---
## CQ-8: Conditions
**Severity:** Low | **Auto-Fix:** No
Extensions using conditions must reference valid condition types.
```typescript
// BAD - Invalid condition
conditions: [{ alias: 'Umb.Condition.DoesNotExist' }]
// GOOD - Valid condition
conditions: [{ alias: 'Umb.Condition.SectionUserPermission' }]
```
**Detection:** Unknown condition aliases in manifests
**Reference:** `umbraco-conditions` skill
---
## CQ-9: Property Editor Schema Alias
**Severity:** Critical | **Auto-Fix:** No
Property editor UIs must reference schema aliases that exist on the server.
```typescript
// BAD - Custom alias without C# implementation
meta: {
propertyEditorSchemaAlias: 'MyPackage.CustomSchema' // 404 error!
}
// GOOD - Built-in schema
meta: {
propertyEditorSchemaAlias: 'Umbraco.Plain.String'
}
```
**Detection:** `propertyEditorSchemaAlias` value not in built-in list:
- `Umbraco.Plain.String`
- `Umbraco.Integer`
- `Umbraco.Decimal`
- `Umbraco.Plain.Json`
- `Umbraco.DateTime`
- `Umbraco.TrueFalse`
- `Umbraco.TextBox`
- `Umbraco.TextArea`
If custom alias used, verify corresponding C# `DataEditor` exists in project.
**Reference:** `umbraco-property-editor-ui`, `umbraco-property-editor-schema` skills
SKILL.md
---
name: umbraco-review-checks
description: Review checks reference for validating Umbraco backoffice extensions
version: 1.1.0
location: managed
allowed-tools: Read
---
# Umbraco Extension Review Checks
Reference skill containing all review checks for the `umbraco-extension-reviewer` agent.
## Check Categories
| Category | File | Checks |
|----------|------|--------|
| Code Quality | `code-quality-checks.md` | CQ-1 to CQ-9 |
| Architecture | `architecture-checks.md` | AR-1 to AR-6 |
| UI Patterns | `ui-pattern-checks.md` | UI-1 to UI-7 |
## Quick Reference
| ID | Check | Severity | Auto-Fix |
|----|-------|----------|----------|
| **Code Quality** ||||
| CQ-1 | Extension Type Usage | Critical | Yes |
| CQ-2 | Manifest Registration | High | No |
| CQ-3 | Element Implementation | Medium | Partial |
| CQ-4 | Context API Usage | High | No |
| CQ-5 | State Management | Medium | No |
| CQ-6 | Localization | Medium | Partial |
| CQ-7 | Naming Conventions | Low | No |
| CQ-8 | Conditions | Low | No |
| CQ-9 | Property Editor Schema Alias | Critical | No |
| **Architecture** ||||
| AR-1 | Direct API Client Access | High | No |
| AR-2 | No Workspace Context | High | No |
| AR-3 | Source Pattern Verification | Medium | No |
| AR-4 | Inconsistent Persistence | Medium | No |
| AR-5 | Missing Repository Layer | High | No |
| AR-6 | Circular Context Dependencies | Critical | No |
| **UI Patterns** ||||
| UI-1 | Custom Error Handling | High | Partial |
| UI-2 | Layout Component Issues | High | No |
| UI-3 | Non-UUI Component Usage | Medium | Partial |
| UI-4 | Enum/Select Handling | Medium | Partial |
| UI-5 | Missing Loading States | Low | No |
| UI-6 | Accessibility Issues | Medium | No |
| UI-7 | Inline Styles | Low | Partial |
## Usage
Read the relevant category file(s) based on extension type:
| Extension Type | Load Files |
|---------------|------------|
| Dashboard | All three |
| Workspace | All three |
| Property Editor | code-quality, ui-pattern |
| Entity Action | code-quality only |
| Context/Repository | code-quality, architecture |
## Related Skills
| Pattern Area | Skill |
|--------------|-------|
| Repository pattern | `umbraco-repository-pattern` |
| Workspace context | `umbraco-workspace` |
| Notifications | `umbraco-notifications` |
| Context API | `umbraco-context-api` |
| Localization | `umbraco-localization` |
## Source References
### UUI Library
For UI pattern checks, refer to the UUI (Umbraco UI) library for component best practices.
**Check locally first** - The UUI source may be available in the workspace (e.g., `Umbraco.UI/packages/`). Use Glob to search for it.
**Online resources:**
- Storybook: https://uui.umbraco.com/
- GitHub: https://github.com/umbraco/Umbraco.UI
### Umbraco CMS Source
For architecture and pattern checks, compare against Umbraco CMS source implementations.
**Check locally first** - The Umbraco CMS source may be available in the workspace (e.g., `Umbraco-CMS/src/Umbraco.Web.UI.Client/`). Use Glob to search for reference implementations.
**Online resources:**
- GitHub: https://github.com/umbraco/Umbraco-CMS
When reviewing, prefer local source over web fetches for accuracy and speed.
ui-pattern-checks.md
# UI Pattern Checks
## UI-1: Custom Error Handling
**Severity:** High | **Auto-Fix:** Partial
Use Umbraco notification system, not browser alerts or custom error divs.
```typescript
// BAD
alert('Something went wrong!');
html`<div class="error">${this._errorMessage}</div>`
// GOOD
this._notificationContext?.peek('danger', {
data: { message: 'Something went wrong!' }
});
```
**Detection:** `alert()`, `window.alert()`, custom error divs/spans
**Auto-Fix:** Replace simple `alert()` calls with notification context (requires context to be consumed)
**Reference:** `umbraco-notifications` skill
---
## UI-2: Layout Component Issues
**Severity:** High | **Auto-Fix:** No
Workspace elements should use `<umb-workspace-editor>` for proper layout.
```typescript
// BAD - Save button in content area
render() {
return html`
<div>
<form>...</form>
<uui-button @click=${this.#save}>Save</uui-button>
</div>
`;
}
// GOOD - Using workspace editor
render() {
return html`
<umb-workspace-editor alias="My.Workspace">
<!-- content goes here -->
</umb-workspace-editor>
`;
}
```
**Detection:** Save buttons outside workspace editor, custom header/footer implementations
**Reference:** `umbraco-workspace` skill
---
## UI-3: Non-UUI Component Usage
**Severity:** Medium | **Auto-Fix:** Partial
Use UUI components instead of native HTML elements.
| Native HTML | UUI Replacement |
|-------------|-----------------|
| `<button>` | `<uui-button>` |
| `<input type="text">` | `<uui-input>` |
| `<input type="checkbox">` | `<uui-checkbox>` |
| `<select>` | `<uui-select>` or `<uui-combobox>` |
| `<textarea>` | `<uui-textarea>` |
| `<table>` | `<uui-table>` |
| `<dialog>` | Use Umbraco modal system |
**Detection:** Native form elements in templates
**Auto-Fix:** Suggest UUI replacements (binding syntax may differ)
---
## UI-4: Enum/Select Handling
**Severity:** Medium | **Auto-Fix:** Partial
Enums must be properly handled on both backend and frontend.
**Backend (C#):**
```csharp
// BAD - Enum serializes as integer
public MyEnum Status { get; set; }
// GOOD - Enum serializes as string
[JsonConverter(typeof(JsonStringEnumConverter))]
public MyEnum Status { get; set; }
```
**Frontend (TypeScript):**
```typescript
// BAD - Native select
html`<select><option value="draft">Draft</option></select>`
// GOOD - UUI select
html`<uui-select .options=${[{ name: 'Draft', value: 'draft' }]}></uui-select>`
```
**Detection:** Enum properties without `JsonStringEnumConverter`, native `<select>` elements
---
## UI-5: Missing Loading States
**Severity:** Low | **Auto-Fix:** No
Async operations should show loading indicators.
```typescript
// BAD - No loading state
async connectedCallback() {
this._data = await this.#loadData();
}
// GOOD - Loading state
@state() private _loading = true;
async connectedCallback() {
this._loading = true;
this._data = await this.#loadData();
this._loading = false;
}
render() {
if (this._loading) return html`<uui-loader></uui-loader>`;
return html`<div>${this._data?.name}</div>`;
}
```
**Detection:** Async data loading without loading state
---
## UI-6: Accessibility Issues
**Severity:** Medium | **Auto-Fix:** No
Extensions must be accessible.
```typescript
// BAD - Missing labels
html`<uui-input .value=${this._value}></uui-input>`
// BAD - Non-interactive element with click
html`<div @click=${this.#handleClick}>Click me</div>`
// GOOD
html`
<uui-label for="my-input">Name</uui-label>
<uui-input id="my-input" .value=${this._value}></uui-input>
`
```
**Detection:** Form inputs without labels, click handlers on non-interactive elements
---
## UI-7: Inline Styles
**Severity:** Low | **Auto-Fix:** Partial
Avoid inline styles; use CSS classes or UUI styling.
```typescript
// BAD
html`<div style="color: red; margin: 10px;">Error</div>`
// GOOD
static styles = css`
.error { color: var(--uui-color-danger); }
`;
render() {
return html`<div class="error">Error</div>`;
}
```
**Detection:** `style="..."` attributes in templates
**Auto-Fix:** Extract inline styles to static styles block