diff --git a/ai/skills/aidd-data/SKILL.md b/ai/skills/aidd-data/SKILL.md new file mode 100644 index 00000000..dbf4ca04 --- /dev/null +++ b/ai/skills/aidd-data/SKILL.md @@ -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` 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 + +## Example Function + +// data in, data out +function removeDuplicates(input: readonly T[]): readonly T[] { + // Set used internally for performance but doesn't escape function + return [...new Set(input)] +} \ No newline at end of file diff --git a/ai/skills/aidd-data/index.md b/ai/skills/aidd-data/index.md new file mode 100644 index 00000000..23383837 --- /dev/null +++ b/ai/skills/aidd-data/index.md @@ -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`. + diff --git a/ai/skills/aidd-namespace/SKILL.md b/ai/skills/aidd-namespace/SKILL.md index ff7ad681..00b8fc9e 100644 --- a/ai/skills/aidd-namespace/SKILL.md +++ b/ai/skills/aidd-namespace/SKILL.md @@ -3,9 +3,36 @@ 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: `/.ts`. Each constant or function lives in `.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 { + .ts + DO NOT use the namespace pattern. +} +else { + USE the following namespace pattern. +} + +# Namespace pattern + +/.ts # exports the type AND export * as from "./public.js" +The exported type should be defined directly in this file and not re-exported. +/public.ts # contains re-exports * from each public 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. @@ -13,16 +40,11 @@ Single import surface per type: `/.ts`. Each constant or f 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: ".ts — type alias and namespace re-export" - utilityOrConstant: ".ts — one file per exported function or constant; filename = export name" -} - Constraints { - Do not use -.ts for children; .ts is sufficient + Same name rule: File names must always match the single exported declaration. + Do not use -.ts for children; .ts is sufficient and pre-pending would violate same name rule. + Do not use -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. diff --git a/ai/skills/aidd-structure/README.md b/ai/skills/aidd-structure/README.md index 949465c3..d494d004 100644 --- a/ai/skills/aidd-structure/README.md +++ b/ai/skills/aidd-structure/README.md @@ -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. diff --git a/ai/skills/aidd-structure/SKILL.md b/ai/skills/aidd-structure/SKILL.md index 8e5c48b3..cd5ef970 100644 --- a/ai/skills/aidd-structure/SKILL.md +++ b/ai/skills/aidd-structure/SKILL.md @@ -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, 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, 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 } ``` @@ -40,11 +42,11 @@ Constraints { UI components or elements (also called "elements"). -**From plugins:** only Observe for reactive re-renders and void-returning action functions. +**From state:** only Observe 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 @@ -65,7 +67,7 @@ When a component has implementation-specific sub-parts, mirror the root structur ``` components/my-component/ components/ - plugins/ + state/ types/ ``` @@ -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) } diff --git a/ai/skills/index.md b/ai/skills/index.md index b6604198..34065481 100644 --- a/ai/skills/index.md +++ b/ai/skills/index.md @@ -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.