Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions ai/skills/aidd-data/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
name: aidd-data
description: defines data modelling best practices. Use whenever we are defining data types in typescript or javascript, using `type` or `interface`.
---

# Independent Data

The information that we would use to save the current state of an application.
This data should always be modelled simply, easy to write, easy to read, without redundancy, without containing anything which could be computed and should avoid allowing invalid states but not at the expense of simplicity.

# Dependent Data

Can be deterministically derived directly or indirectly from "Independent Data".

# General Data Rules

Use `type` for data type declarations.
Do NOT use `interface` for data type declarations.

Prefer simple, readonly JSON values as they are easy to inspect, serialize and deserialize.

Use simple, descriptive, readable camelCase names for properties on objects.

Use `null` to indicate empty or default values.

Prefer types which disallow invalid states.

## Variant sets and exhaustive maps

Use one canonical union per concept. For anything that exists per variant (settings, counts, labels), prefer `Record<ThatUnion, V>` so adding a variant forces new fields or values—**exhaustive keys** / a **total map** over the variant set. Tie outcomes to that same union (e.g. `{ type: "win"; winner: PlayerMark } | { type: "draw" }`) instead of introducing a second parallel union with different spellings for the same variants (e.g. `"X" | "O"` for marks and `"xWins" | "oWins"`).

If you must model large binary information then use a Blob.

Anything other than that should never be used for either independent or dependent data.

For performance reasons we may use other classes such as typed buffers, Sets or Maps within particular algorithms, but the external inputs and outputs from those algorithms should be standard data.

Prefer arrays or tuples over objects if the indices are integers starting at 0 and have no gaps.

## Discriminated Unions

Using discriminated unions can be useful sometimes to attach different properties depending on the type. Keep in mind however that there is a balance between that and usability and concision of your type definitions. If almost all or almost all the properties are the same between all members of a discriminated union then probably just use the simpler declaration.

```
// bad because they both contain all the same properties
type Foo = { type: "a", value: string } | { type: "b", value: string }
// good
type Foo = { type: "a" | "b", value: string }
```

## Example Types

type Vec3 = readonly [number, number, number]
type Line3 = { readonly a: Vec3, readonly b: Vec3 }
type User = { readonly id: number, readonly name: string }

type UserTypes = "normal" | "premium"
type UserSettings = Record<UserTypes, { featureOne: boolean, featureTwo: boolean }>

## Example Function

// data in, data out
function removeDuplicates<T>(input: readonly T[]): readonly T[] {
// Set used internally for performance but doesn't escape function
return [...new Set(input)]
}
12 changes: 12 additions & 0 deletions ai/skills/aidd-data/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# aidd-data

This index provides an overview of the contents in this directory.

## Files

### Independent Data

**File:** `SKILL.md`

defines data modelling best practices. Use whenever we are defining data types in typescript or javascript, using `type` or `interface`.

42 changes: 32 additions & 10 deletions ai/skills/aidd-namespace/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,48 @@ name: aidd-namespace
description: Ensures types and related functions are authored and consumed in a modular, discoverable, tree-shakeable pattern. Use when creating types, refactoring type folders, defining schemas, importing types, or when the user mentions type namespaces, constants, or Schema.ToType.
---

# Type namespace pattern
Every file only exports a single public declaration though it may contain other non exported declarations.
Every file is named to match the single export.

Single import surface per type: `<type-name>/<type-name>.ts`. Each constant or function lives in `<exported-name>.ts`; namespace re-export from `public.js`. Types live in the `types/` layer per [structure](../aidd-structure/SKILL.md).
Good {
// do-foo-bar.ts
export function doFooBar() {}
}
Bad {
// foo-bar-type.ts
export type FooBar = "a" | "b"
}

# Should we use single file or namespace pattern?

if this type has no related declarations {
<type-name>.ts
DO NOT use the namespace pattern.
}
else {
USE the following namespace pattern.
}

# Namespace pattern

<type-name>/<type-name>.ts # exports the type <type-name> AND export * as <type-name> from "./public.js"
The exported type should be defined directly in this file and not re-exported.
<type-name>/public.ts # contains re-exports * from each public declaration
<type-name>/<declaration>.ts # each related declaration with a single export

If you have logical sub-types, which are never used externally to that type then you may use this namespace pattern recursively to contain them.

**Precedence:** This skill defines the canonical pattern. Existing files may not yet follow it; all new work and changes must conform. Do not copy legacy structure when adding or editing code.

## File naming (utilities and constants)

Inside a type folder, name each file after the single export it provides. The folder already identifies the type, so no type prefix in the filename.

```sudolang
NamespaceFileNaming {
typeEntry: "<type-name>.ts — type alias and namespace re-export"
utilityOrConstant: "<exported-name>.ts — one file per exported function or constant; filename = export name"
}

Constraints {
Do not use <type-name>-<exported-name>.ts for children; <exported-name>.ts is sufficient
Same name rule: File names must always match the single exported declaration.
Do not use <type-name>-<exported-name>.ts for children; <exported-name>.ts is sufficient and pre-pending would violate same name rule.
Do not use <type-name>-type.ts or anything else which would violate same name rule.
}
```

Example: under `types/point/`, files `length.ts`, `add.ts`, and `normalize.ts` each export the like-named function.

Expand Down
4 changes: 2 additions & 2 deletions ai/skills/aidd-structure/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ maintainable and predictable.
## Why

Unchecked dependencies create circular imports and tangled modules. A strict
layer hierarchy — `types ← services ← plugins ← components` — ensures each
layer hierarchy — `types ← services ← state ← components` — ensures each
layer depends only on the layers below it.

## Usage

Invoke `/aidd-structure` when creating folders, moving files, or adding
imports. Components may depend on plugins (Observe, void actions) and types but
imports. Components may depend on **state** (Observe, void actions) and types but
never on services directly. Services depend on other services and types.
Types depend only on other types.

Expand Down
28 changes: 15 additions & 13 deletions ai/skills/aidd-structure/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,32 @@ description: Enforces source code structuring and interdependency best practices
# Standard folder structure

```
types ← services ← plugins ← components
↑ ↑
└─────────┴────────┘ (types only depend on types)
types ← services ← state ← components
↑ ↑ ↑
└─────────┴────────┘ (types only depend on types)
```

Use a top-level folder named **`state`** (not `plugins`) for application state and ECS wiring. The name reads clearly in game and product codebases.

## Dependency rules

```sudolang
LayerDependency {
layer: "components" | "plugins" | "services" | "types"
layer: "components" | "state" | "services" | "types"
mayDependOn: String[]
mustNotDependOn: String[]
}

DependencyRules [
{ layer: "components", mayDependOn: ["plugins (Observe<Data>, void actions)", "types"], mustNotDependOn: ["services"] },
{ layer: "plugins", mayDependOn: ["services", "types", "other plugins"], mustNotDependOn: [] },
{ layer: "services", mayDependOn: ["other services", "types"], mustNotDependOn: ["components", "plugins"] },
{ layer: "components", mayDependOn: ["state (Observe<Data>, void actions)", "types"], mustNotDependOn: ["services"] },
{ layer: "state", mayDependOn: ["services", "types", "other state modules"], mustNotDependOn: [] },
{ layer: "services", mayDependOn: ["other services", "types"], mustNotDependOn: ["components", "state"] },
{ layer: "types", mayDependOn: ["other types"], mustNotDependOn: ["everything else"] }
]

Constraints {
Never: components → services
Never: services → components or plugins
Never: services → components or state
Never: types → anything except types
}
```
Expand All @@ -40,11 +42,11 @@ Constraints {

UI components or elements (also called "elements").

**From plugins:** only Observe<Data> for reactive re-renders and void-returning action functions.
**From state:** only Observe<Data> for reactive re-renders and void-returning action functions.

## plugins (if using @adobe/data/ecs for state)
## state (if using @adobe/data/ecs for state)

ECS Database.Plugin declarations. Usually depend on services, types, and other plugins.
ECS `Database.Plugin` declarations and related state composition live under a **`state`** folder. Usually depend on services, types, and other state modules.

## services

Expand All @@ -65,7 +67,7 @@ When a component has implementation-specific sub-parts, mirror the root structur
```
components/my-component/
components/
plugins/
state/
types/
```

Expand All @@ -76,7 +78,7 @@ components/my-component/
```sudolang
fn whenAddingOrMovingCode() {
Constraints {
Place code in the correct layer (components, plugins, services, types)
Place code in the correct layer (components, state, services, types)
Check dependencies against the dependency rules
Fix any violations (e.g. components importing services)
}
Expand Down
1 change: 1 addition & 0 deletions ai/skills/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- aidd-agent-orchestrator - Agent orchestrator that coordinates specialized agents for software development tasks. Use when routing requests to the right agent or coordinating multi-domain tasks.
- aidd-autodux - Create and transpile Autodux Redux state management dux objects. Use when building Redux state management, defining reducers, action creators, or selectors.
- aidd-churn - Hotspot analysis: run npx aidd churn, interpret the ranked results, and recommend specific files to review or refactor with concrete strategies. Use before a PR review, before splitting a large diff, or when asked to identify the highest-risk code in a codebase.
- aidd-data - defines data modelling best practices. Use whenever we are defining data types in typescript or javascript, using `type` or `interface`.
- aidd-ecs - Enforces @adobe/data/ecs best practices. Use this whenever @adobe/data/ecs is imported, when creating or modifying Database.Plugin definitions, or when working with ECS components, resources, transactions, actions, systems, or services.
- aidd-error-causes - Use the error-causes library for structured error handling in JavaScript/TypeScript. Use when throwing errors, catching errors, defining error types, or implementing error routing.
- aidd-fix - Fix a bug or implement review feedback following the AIDD fix process. Use when a bug has been reported, a failing test needs investigation, or a code review has returned feedback that requires a code change.
Expand Down
Loading