@hellraisercenobit/ai-engineering-gate 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -0
- package/agents/design-pattern-reviewer.md +40 -0
- package/agents/modern-typescript-reviewer.md +24 -0
- package/agents/testing-pattern-reviewer.md +23 -0
- package/bin/ai-engineering-gate.mjs +6 -0
- package/contracts/axi/axi-standard.md +273 -0
- package/contracts/axi/pin.json +36 -0
- package/contracts/members.json +58 -0
- package/contracts/schemas/arbitration.schema.json +45 -0
- package/contracts/schemas/decision-envelope.schema.json +215 -0
- package/contracts/schemas/declaration.schema.json +190 -0
- package/contracts/schemas/dispute.schema.json +67 -0
- package/contracts/schemas/evidence-append.schema.json +72 -0
- package/contracts/schemas/gate-output.schema.json +296 -0
- package/contracts/schemas/marker.schema.json +75 -0
- package/contracts/schemas/review-envelope.schema.json +378 -0
- package/contracts/schemas/verdict-record.schema.json +191 -0
- package/contracts/suite-contract.md +270 -0
- package/dist/ai-engineering-gate.mjs +2762 -0
- package/package.json +47 -0
- package/skills/engineering/review-design-patterns/LICENSE +6 -0
- package/skills/engineering/review-design-patterns/SKILL.md +196 -0
- package/skills/engineering/review-design-patterns/agents/openai.yaml +3 -0
- package/skills/engineering/review-design-patterns/references/smell-signatures.md +117 -0
- package/skills/engineering/review-design-patterns/review-design-patterns.yaml +25 -0
- package/skills/engineering/review-modern-typescript/LICENSE +21 -0
- package/skills/engineering/review-modern-typescript/SKILL.md +88 -0
- package/skills/engineering/review-modern-typescript/agents/openai.yaml +3 -0
- package/skills/engineering/review-modern-typescript/references/smell-signatures.md +36 -0
- package/skills/engineering/review-modern-typescript/review-modern-typescript.yaml +22 -0
- package/skills/engineering/review-testing-patterns/SKILL.md +108 -0
- package/skills/engineering/review-testing-patterns/agents/openai.yaml +4 -0
- package/skills/engineering/review-testing-patterns/references/compiler-profile.mjs +103 -0
- package/skills/engineering/review-testing-patterns/references/smell-signatures.md +30 -0
- package/skills/engineering/review-testing-patterns/review-testing-patterns.yaml +23 -0
- package/skills/engineering/transpose-design-patterns/LICENSE +6 -0
- package/skills/engineering/transpose-design-patterns/SKILL.md +231 -0
- package/skills/engineering/transpose-design-patterns/agents/openai.yaml +3 -0
- package/skills/engineering/transpose-design-patterns/references/arbitration.schema.json +45 -0
- package/skills/engineering/transpose-design-patterns/references/decision-envelope.schema.json +215 -0
- package/skills/engineering/transpose-design-patterns/references/declaration.schema.json +190 -0
- package/skills/engineering/transpose-design-patterns/references/design-decision-record.schema.json +731 -0
- package/skills/engineering/transpose-design-patterns/references/dispute.schema.json +67 -0
- package/skills/engineering/transpose-design-patterns/references/evidence-append.schema.json +72 -0
- package/skills/engineering/transpose-design-patterns/references/marker.schema.json +75 -0
- package/skills/engineering/transpose-design-patterns/references/pattern-catalog.md +305 -0
- package/skills/engineering/transpose-design-patterns/references/record.example.json +120 -0
- package/skills/engineering/transpose-design-patterns/references/review-envelope.schema.json +378 -0
- package/skills/engineering/transpose-design-patterns/references/suite-contract.md +270 -0
- package/skills/engineering/transpose-design-patterns/references/transpose-angular.md +224 -0
- package/skills/engineering/transpose-design-patterns/references/transpose-php.md +561 -0
- package/skills/engineering/transpose-design-patterns/references/transpose-quarkus.md +283 -0
- package/skills/engineering/transpose-design-patterns/references/transpose-react.md +256 -0
- package/skills/engineering/transpose-design-patterns/references/transpose-vanilla.md +204 -0
- package/skills/engineering/transpose-design-patterns/references/transpose-vue.md +234 -0
- package/skills/engineering/transpose-design-patterns/transpose-design-patterns.yaml +24 -0
- package/skills/engineering/transpose-modern-typescript/LICENSE +21 -0
- package/skills/engineering/transpose-modern-typescript/SKILL.md +90 -0
- package/skills/engineering/transpose-modern-typescript/agents/openai.yaml +3 -0
- package/skills/engineering/transpose-modern-typescript/references/arbitration.schema.json +45 -0
- package/skills/engineering/transpose-modern-typescript/references/catalog.md +38 -0
- package/skills/engineering/transpose-modern-typescript/references/collections.md +50 -0
- package/skills/engineering/transpose-modern-typescript/references/compatibility.md +61 -0
- package/skills/engineering/transpose-modern-typescript/references/decision-envelope.schema.json +215 -0
- package/skills/engineering/transpose-modern-typescript/references/decision-record.schema.json +830 -0
- package/skills/engineering/transpose-modern-typescript/references/declaration.schema.json +190 -0
- package/skills/engineering/transpose-modern-typescript/references/dispute.schema.json +67 -0
- package/skills/engineering/transpose-modern-typescript/references/evidence-append.schema.json +72 -0
- package/skills/engineering/transpose-modern-typescript/references/idioms.md +74 -0
- package/skills/engineering/transpose-modern-typescript/references/marker.schema.json +75 -0
- package/skills/engineering/transpose-modern-typescript/references/platform.md +147 -0
- package/skills/engineering/transpose-modern-typescript/references/record.example.json +172 -0
- package/skills/engineering/transpose-modern-typescript/references/record.md +37 -0
- package/skills/engineering/transpose-modern-typescript/references/review-envelope.schema.json +378 -0
- package/skills/engineering/transpose-modern-typescript/references/suite-contract.md +270 -0
- package/skills/engineering/transpose-modern-typescript/references/types.md +97 -0
- package/skills/engineering/transpose-modern-typescript/transpose-modern-typescript.yaml +22 -0
- package/skills/engineering/transpose-testing-patterns/SKILL.md +96 -0
- package/skills/engineering/transpose-testing-patterns/agents/openai.yaml +4 -0
- package/skills/engineering/transpose-testing-patterns/references/arbitration.schema.json +45 -0
- package/skills/engineering/transpose-testing-patterns/references/catalog.md +218 -0
- package/skills/engineering/transpose-testing-patterns/references/decision-envelope.schema.json +215 -0
- package/skills/engineering/transpose-testing-patterns/references/decision-record.schema.json +857 -0
- package/skills/engineering/transpose-testing-patterns/references/declaration.schema.json +190 -0
- package/skills/engineering/transpose-testing-patterns/references/detect-adapter.mjs +98 -0
- package/skills/engineering/transpose-testing-patterns/references/dispute.schema.json +67 -0
- package/skills/engineering/transpose-testing-patterns/references/doubles-data.md +45 -0
- package/skills/engineering/transpose-testing-patterns/references/evidence-append.schema.json +72 -0
- package/skills/engineering/transpose-testing-patterns/references/journal-event.schema.json +115 -0
- package/skills/engineering/transpose-testing-patterns/references/marker.schema.json +75 -0
- package/skills/engineering/transpose-testing-patterns/references/record.example.json +230 -0
- package/skills/engineering/transpose-testing-patterns/references/record.md +25 -0
- package/skills/engineering/transpose-testing-patterns/references/review-envelope.schema.json +378 -0
- package/skills/engineering/transpose-testing-patterns/references/suite-contract.md +270 -0
- package/skills/engineering/transpose-testing-patterns/references/tdd.md +62 -0
- package/skills/engineering/transpose-testing-patterns/references/transpose-codeception.md +84 -0
- package/skills/engineering/transpose-testing-patterns/references/transpose-karma-jasmine-angular.md +73 -0
- package/skills/engineering/transpose-testing-patterns/references/transpose-vitest.md +111 -0
- package/skills/engineering/transpose-testing-patterns/references/typescript.md +42 -0
- package/skills/engineering/transpose-testing-patterns/transpose-testing-patterns.yaml +23 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Vanilla TypeScript Transposition — Design Patterns (TypeScript 2026)
|
|
2
|
+
|
|
3
|
+
Generic, portable wiring for the patterns in [`pattern-catalog.md`](pattern-catalog.md) in a
|
|
4
|
+
**framework-less** TypeScript project (libraries, CLIs, workers, Web Components, plain DOM apps). No DI
|
|
5
|
+
container, no component framework — just ES modules, functions, closures, and explicit wiring. Examples
|
|
6
|
+
use throwaway domains (user, cart, payment, order); substitute your own types.
|
|
7
|
+
|
|
8
|
+
## Vanilla building blocks
|
|
9
|
+
|
|
10
|
+
The transpositions below lean on a few framework-less defaults:
|
|
11
|
+
|
|
12
|
+
- **ES modules with named exports** — tree-shakeable; one concern per module. No default-export grab-bags.
|
|
13
|
+
- **Functions and closures over classes.** Use a `class` only for genuine identity/lifecycle; never build
|
|
14
|
+
inheritance hierarchies — compose factory functions instead.
|
|
15
|
+
- **Explicit dependency injection**: pass collaborators as function arguments, or close over them in a
|
|
16
|
+
factory. There is no magic container — a single **composition root** (your entry module) wires the
|
|
17
|
+
concrete implementations once.
|
|
18
|
+
- **One reactive primitive, deliberately chosen**: a tiny closure-based observable store (below) for
|
|
19
|
+
app state, or **signals / atom-based stores** (e.g. `@preact/signals-core`, `nanostores`'
|
|
20
|
+
atoms/computed, or a TC39 Signals polyfill) when you want fine-grained derivation. Pick one and keep
|
|
21
|
+
state out of scattered module-level `let`s.
|
|
22
|
+
- **Strong typing**: `as const satisfies` for lookup maps, discriminated unions, branded ids, `readonly`.
|
|
23
|
+
- **Runtime validation** (zod/valibot) at every external boundary — network, storage, plugins, env.
|
|
24
|
+
|
|
25
|
+
## Pattern → Vanilla wiring
|
|
26
|
+
|
|
27
|
+
### Strategy → typed lookup map, resolved by key
|
|
28
|
+
|
|
29
|
+
Stateless strategies in an `as const satisfies` record; pick by key — no `switch`.
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
export interface PaymentStrategy {
|
|
33
|
+
pay(amount: number): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const paymentStrategies = {
|
|
37
|
+
stripe: stripeStrategy,
|
|
38
|
+
paypal: paypalStrategy,
|
|
39
|
+
} as const satisfies Record<string, PaymentStrategy>;
|
|
40
|
+
|
|
41
|
+
export type PaymentMethod = keyof typeof paymentStrategies;
|
|
42
|
+
|
|
43
|
+
const strategy = paymentStrategies[method]; // typed, exhaustive, no switch
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Registry → self-registering map (validated when dynamic)
|
|
47
|
+
|
|
48
|
+
Plugins self-register into a module-level map; no central switch. Validate runtime/external plugins.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
const registry = new Map<string, Plugin>();
|
|
52
|
+
export const registerPlugin = (p: Plugin): void => void registry.set(p.key, p);
|
|
53
|
+
export const getPlugin = (key: string): Plugin | undefined => registry.get(key);
|
|
54
|
+
// dynamic plugins: validate the shape with zod/valibot before registering
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Factory → factory function closing over dependencies
|
|
58
|
+
|
|
59
|
+
Isolate creation logic; return an interface, not a concrete class. Closures replace constructor DI.
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
export const createLogger = (env: Env): Logger => (env.production ? remoteLogger() : consoleLogger());
|
|
63
|
+
|
|
64
|
+
export const createOrderApi = (http: HttpClient): OrderApi => ({
|
|
65
|
+
place: (cart: Cart): Promise<Order> => http.post('/orders', cart),
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Command → framework-agnostic use-case function
|
|
70
|
+
|
|
71
|
+
Encapsulate one action as a plain (async) function. Invoke it from an event handler, a CLI command, or
|
|
72
|
+
a store action — keep it independent of any caller.
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
// application/use-case — depends only on a port, not on the DOM/transport
|
|
76
|
+
export const checkoutCart = (orders: OrderApi, cart: Cart): Promise<Order> => orders.place(cart);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
A store action is **not automatically** the Command — keep it a distinct use-case function the store action **delegates** to. Full rule + layering: catalog → _Command_.
|
|
80
|
+
|
|
81
|
+
### Adapter / DTO Mapping → pure `from`/`to` at the data boundary
|
|
82
|
+
|
|
83
|
+
Map DTO ↔ domain inside the api module; never let DTOs reach UI/render code.
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
// UserDto / User / userFromDto: see catalog -> Adapter / DTO Mapping
|
|
87
|
+
export const getUser = async (http: HttpClient, id: string): Promise<User> =>
|
|
88
|
+
userFromDto(await http.get<UserDto>(`/api/users/${id}`));
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Composition → factory functions + closures + a composition root
|
|
92
|
+
|
|
93
|
+
Compose behavior by combining small functions; wire concrete dependencies once at the entry module.
|
|
94
|
+
No inheritance, no service locator.
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
// main.ts — composition root: build the graph explicitly, top-down
|
|
98
|
+
const env = loadEnv();
|
|
99
|
+
const http = createHttpClient(env.apiUrl);
|
|
100
|
+
const orders = createOrderApi(http);
|
|
101
|
+
const checkout = (cart: Cart) => checkoutCart(orders, cart);
|
|
102
|
+
|
|
103
|
+
mountApp({ checkout, cartStore });
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Singleton / shared state → a closure-based observable store
|
|
107
|
+
|
|
108
|
+
The common case for app-wide state without a framework: a single store instance exposing an immutable
|
|
109
|
+
snapshot, a `subscribe`, and actions. Keep the shape minimal; update immutably inside actions.
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
type Listener<S> = (state: S) => void;
|
|
113
|
+
|
|
114
|
+
export interface Store<S> {
|
|
115
|
+
get(): S;
|
|
116
|
+
subscribe(fn: Listener<S>): () => void;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const createStore = <S>(initial: S) => {
|
|
120
|
+
let state = initial;
|
|
121
|
+
const listeners = new Set<Listener<S>>();
|
|
122
|
+
const set = (next: (s: S) => S): void => {
|
|
123
|
+
state = next(state);
|
|
124
|
+
for (const fn of listeners) fn(state);
|
|
125
|
+
};
|
|
126
|
+
const store: Store<S> = {
|
|
127
|
+
get: () => state,
|
|
128
|
+
subscribe: (fn) => (listeners.add(fn), () => void listeners.delete(fn)),
|
|
129
|
+
};
|
|
130
|
+
return { store, set };
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// a concrete store: expose the readonly Store + named actions, hide `set`
|
|
134
|
+
export const createCartStore = () => {
|
|
135
|
+
const { store, set } = createStore<{ items: readonly CartItem[] }>({ items: [] });
|
|
136
|
+
const add = (item: CartItem): void => set((s) => ({ items: [...s.items, item] }));
|
|
137
|
+
const total = (): number => store.get().items.reduce((sum, i) => sum + i.price, 0);
|
|
138
|
+
return { ...store, add, total };
|
|
139
|
+
};
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Prefer **signals / atom-based stores** (`@preact/signals-core`, `nanostores`' atoms/computed, a TC39
|
|
143
|
+
Signals polyfill) when you need fine-grained derived values and effects instead of re-deriving on each
|
|
144
|
+
`subscribe` notification.
|
|
145
|
+
|
|
146
|
+
## Cross-cutting vanilla practice
|
|
147
|
+
|
|
148
|
+
### Wire dependencies in a composition root
|
|
149
|
+
|
|
150
|
+
Construct the dependency graph once, at the entry module, and pass it down. Modules receive collaborators
|
|
151
|
+
as arguments — they never import concrete singletons or reach into a global container.
|
|
152
|
+
|
|
153
|
+
### Derive, don't duplicate
|
|
154
|
+
|
|
155
|
+
Compute derived values with pure functions (or `computed` signals). Never store the same fact twice and
|
|
156
|
+
keep it in sync by hand.
|
|
157
|
+
|
|
158
|
+
### Render from state
|
|
159
|
+
|
|
160
|
+
Drive the DOM from state: `subscribe` to the store (or run a signal effect) and re-render the affected
|
|
161
|
+
region. Keep render functions pure-of-logic — they read state and produce DOM, nothing else.
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
const unsubscribe = cartStore.subscribe((s) => renderCart(root, s));
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Async at the boundary
|
|
168
|
+
|
|
169
|
+
Use native `fetch` with an `AbortController` for cancellation; map DTO→domain in the api module. Surface
|
|
170
|
+
errors as typed results (`{ ok: true; value } | { ok: false; error }`) rather than throwing across layers.
|
|
171
|
+
|
|
172
|
+
### Keep modules cohesive
|
|
173
|
+
|
|
174
|
+
UI/DOM code renders and dispatches; api modules do IO and mapping; use-cases hold logic; the store holds
|
|
175
|
+
state. Don't mix transport, mapping, and business rules in one function.
|
|
176
|
+
|
|
177
|
+
## Vanilla Decision Matrix
|
|
178
|
+
|
|
179
|
+
| Problem | Recommended Solution |
|
|
180
|
+
| ----------------------------- | ------------------------------------------------------------ |
|
|
181
|
+
| Shared app state | closure observable store, or signals / atoms (@preact/signals-core, nanostores) |
|
|
182
|
+
| Derived value | pure function, or `computed` signal |
|
|
183
|
+
| Dependency injection | function arguments / factory closures + a composition root |
|
|
184
|
+
| Interchangeable behaviors | Strategy (`as const satisfies` lookup map) |
|
|
185
|
+
| Plugin / extension system | Registry (self-registering Map, zod for dynamic) |
|
|
186
|
+
| Object creation with logic | Factory (function closing over dependencies) |
|
|
187
|
+
| Reusable action | Command (use-case function, store action delegates) |
|
|
188
|
+
| API ↔ domain shape mismatch | Adapter — fromDto / toDto in the api module |
|
|
189
|
+
| Cross-cutting behavior | Composition (factories + closures + composition root) |
|
|
190
|
+
| Async read / mutation | native `fetch` + `AbortController`, map at the boundary |
|
|
191
|
+
| DOM update | subscribe to store / signal effect → render the region |
|
|
192
|
+
| Runtime data validation | zod / valibot at the boundary |
|
|
193
|
+
|
|
194
|
+
## Anti-Patterns to Avoid
|
|
195
|
+
|
|
196
|
+
- inheritance hierarchies / abstract base classes — compose factory functions instead
|
|
197
|
+
- a service locator or hidden global container instead of an explicit composition root
|
|
198
|
+
- module-level mutable `let` state scattered across files instead of one store / signal
|
|
199
|
+
- mutating state in place instead of immutable updates inside actions
|
|
200
|
+
- business logic in DOM event handlers instead of use-case functions; DTOs leaking into render/DOM code
|
|
201
|
+
- string magic / `switch` on a type tag instead of an `as const satisfies` lookup map
|
|
202
|
+
- a Command merged into a store action when it must be reusable — keep it a distinct use-case the action delegates to
|
|
203
|
+
- god modules mixing transport + mapping + business rules; calling `fetch` from UI code — go through an api module
|
|
204
|
+
- `any`; magic strings; skipping runtime validation at external boundaries
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# Vue Transposition — Design Patterns (TypeScript 2026)
|
|
2
|
+
|
|
3
|
+
Generic, portable Vue wiring for the patterns in [`pattern-catalog.md`](pattern-catalog.md).
|
|
4
|
+
Framework-specific only — keep business logic framework-agnostic in the catalog. Examples use
|
|
5
|
+
throwaway domains (user, cart, payment, order); substitute your own types. This guide states **common**
|
|
6
|
+
Vue practice, not any single project's house rules.
|
|
7
|
+
|
|
8
|
+
## Vue building blocks
|
|
9
|
+
|
|
10
|
+
The transpositions below lean on a few modern-Vue defaults (Vue 3.4+/3.5+):
|
|
11
|
+
|
|
12
|
+
- **`<script setup lang="ts">` + Composition API** only — no Options API, no mixins in new code.
|
|
13
|
+
- **`ref` / `computed`** for reactive state; prefer `ref` for primitives and `computed` for derived
|
|
14
|
+
values. Reach for `reactive` only for cohesive objects, and read it without destructuring.
|
|
15
|
+
- **`defineModel()`** for two-way binding instead of a manual `modelValue` prop + `update:modelValue`
|
|
16
|
+
emit; **`useTemplateRef()`** for template refs; **reactive props destructure** (3.5, with defaults).
|
|
17
|
+
- **Composables (`useXxx`)** for reusable, composable logic — the Vue unit of composition (VueUse for
|
|
18
|
+
common ones). Keep them small and focused.
|
|
19
|
+
- **Typed DI** via `provide` / `inject` with an `InjectionKey<T>`, consumed through a custom composable.
|
|
20
|
+
- **Strong typing**: `defineProps`/`defineEmits` generics, discriminated unions for variants,
|
|
21
|
+
`as const satisfies` for lookup maps, branded ids.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// di.ts
|
|
25
|
+
export const ANALYTICS = Symbol() as InjectionKey<Analytics>;
|
|
26
|
+
|
|
27
|
+
// useAnalytics.ts
|
|
28
|
+
export function useAnalytics(): Analytics {
|
|
29
|
+
const a = inject(ANALYTICS);
|
|
30
|
+
if (!a) throw new Error('ANALYTICS not provided');
|
|
31
|
+
return a;
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Pattern → Vue wiring
|
|
36
|
+
|
|
37
|
+
### Strategy → typed lookup map, resolved by key
|
|
38
|
+
|
|
39
|
+
Stateless strategies in an `as const satisfies` record; pick by key — no `switch`.
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
export interface PaymentStrategy {
|
|
43
|
+
pay(amount: number): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const paymentStrategies = {
|
|
47
|
+
stripe: stripeStrategy,
|
|
48
|
+
paypal: paypalStrategy,
|
|
49
|
+
} as const satisfies Record<string, PaymentStrategy>;
|
|
50
|
+
|
|
51
|
+
export type PaymentMethod = keyof typeof paymentStrategies;
|
|
52
|
+
|
|
53
|
+
const strategy = paymentStrategies[method]; // typed, exhaustive, no switch
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Registry → self-registering map (validated when dynamic)
|
|
57
|
+
|
|
58
|
+
Plugins self-register into a module-level map; no central switch. Validate runtime/external plugins with
|
|
59
|
+
zod/valibot. When the registry is request/tenant-scoped, `provide` it and read it through a composable
|
|
60
|
+
instead of importing the module singleton.
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
const registry = new Map<string, Plugin>();
|
|
64
|
+
export const registerPlugin = (p: Plugin): void => void registry.set(p.key, p);
|
|
65
|
+
export const getPlugin = (key: string): Plugin | undefined => registry.get(key);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Factory → factory function returning an abstraction
|
|
69
|
+
|
|
70
|
+
Isolate creation logic; return an interface, not a concrete class. A composable can be a factory of
|
|
71
|
+
reactive-bound values.
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
export const createLogger = (env: Env): Logger => (env.production ? new RemoteLogger() : new ConsoleLogger());
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Command → framework-agnostic use-case, invoked from a handler or store action
|
|
78
|
+
|
|
79
|
+
Encapsulate one action as a plain (async) function in a framework-agnostic module. Invoke it from an
|
|
80
|
+
event handler or a Pinia action.
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
// application/use-case — no Vue imports
|
|
84
|
+
export const checkoutCart = (cart: Cart): Promise<Order> => orderApi.place(cart);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
A Pinia action is **not automatically** the Command — keep the action a distinct framework-agnostic use-case (function) the store action **delegates** to. Full rule + layering: catalog → _Command_.
|
|
88
|
+
|
|
89
|
+
### Adapter / DTO Mapping → pure `from`/`to` at the data boundary
|
|
90
|
+
|
|
91
|
+
Map DTO ↔ domain inside the api client / composable; never leak DTOs into components or templates.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
// UserDto / User / userFromDto: see catalog -> Adapter / DTO Mapping
|
|
95
|
+
export const getUser = async (id: string): Promise<User> =>
|
|
96
|
+
userFromDto(await api.get<UserDto>(`/api/users/${id}`));
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Composition → composables + slots + provide/inject
|
|
100
|
+
|
|
101
|
+
Compose behavior through composables and component composition (slots, `provide`/`inject`) — not
|
|
102
|
+
inheritance, not mixins.
|
|
103
|
+
|
|
104
|
+
```vue
|
|
105
|
+
<script setup lang="ts">
|
|
106
|
+
const { visible } = useTooltip(); // behavior via composable
|
|
107
|
+
</script>
|
|
108
|
+
|
|
109
|
+
<template>
|
|
110
|
+
<section>
|
|
111
|
+
<slot name="media" />
|
|
112
|
+
<slot />
|
|
113
|
+
</section>
|
|
114
|
+
</template>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Singleton / shared state → a Pinia setup store
|
|
118
|
+
|
|
119
|
+
The common case for app-wide state. Use a **setup store** (function form): `ref` for state, `computed`
|
|
120
|
+
for getters, functions for actions; mutate only through actions. Consume with `storeToRefs` so the
|
|
121
|
+
destructured state/getters stay reactive.
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
import { defineStore } from 'pinia';
|
|
125
|
+
|
|
126
|
+
export const useCartStore = defineStore('cart', () => {
|
|
127
|
+
const items = ref<CartItem[]>([]);
|
|
128
|
+
const total = computed(() => items.value.reduce((sum, i) => sum + i.price, 0));
|
|
129
|
+
const add = (item: CartItem): void => void items.value.push(item);
|
|
130
|
+
return { items, total, add };
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// consume
|
|
134
|
+
const cart = useCartStore();
|
|
135
|
+
const { items, total } = storeToRefs(cart); // reactive
|
|
136
|
+
cart.add(item); // actions destructure directly
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
For app-wide _services_ (not reactive state), inject via `provide` / `inject` + a typed composable.
|
|
140
|
+
|
|
141
|
+
## Cross-cutting Vue practice
|
|
142
|
+
|
|
143
|
+
### computed for derived state, not watchers
|
|
144
|
+
|
|
145
|
+
Derive with `computed`. Do **not** use a `watch` that writes into another `ref` to compute a value.
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
// Bad: watch([first, last], () => (full.value = `${first.value} ${last.value}`))
|
|
149
|
+
const fullName = computed(() => `${first.value} ${last.value}`);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Watchers only at external boundaries
|
|
153
|
+
|
|
154
|
+
`watch` / `watchEffect` are for side effects that sync with systems outside Vue (persistence, analytics,
|
|
155
|
+
non-Vue widgets). Register teardown with `onWatcherCleanup` (3.5). Event-response logic belongs in the
|
|
156
|
+
handler, not a watcher.
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
import { onWatcherCleanup } from 'vue';
|
|
160
|
+
|
|
161
|
+
watchEffect(() => {
|
|
162
|
+
const sub = socket.subscribe(onMessage);
|
|
163
|
+
onWatcherCleanup(() => sub.unsubscribe()); // 3.5 — also available as the callback's onCleanup arg
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Async at the edge
|
|
168
|
+
|
|
169
|
+
Default to a composable (TanStack Query Vue, or a framework helper like Nuxt `useAsyncData`); or —
|
|
170
|
+
accepting that `<Suspense>` is still an **experimental** API (it may change before stabilizing) —
|
|
171
|
+
`<Suspense>` + top-level `await` in `<script setup>`. Map DTO→domain in the fetcher; route API calls
|
|
172
|
+
through a composable/store rather than calling the HTTP client from a component.
|
|
173
|
+
|
|
174
|
+
### Two-way binding with defineModel
|
|
175
|
+
|
|
176
|
+
For a value mirroring a parent binding, use `defineModel()` — not a manual `modelValue` prop plus an
|
|
177
|
+
`update:modelValue` emit, and not a `watch` to sync.
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
const value = defineModel<string>();
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Keep components thin
|
|
184
|
+
|
|
185
|
+
Components render UI, bind state, dispatch actions. Push logic and mapping into composables, the store,
|
|
186
|
+
and pure framework-agnostic functions.
|
|
187
|
+
|
|
188
|
+
### Template SRP
|
|
189
|
+
|
|
190
|
+
No business logic in templates. Move ternaries, multi-condition branches, and data-transforming calls
|
|
191
|
+
into `computed`.
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
// Bad (template): {{ user.isActive ? 'Active' : 'Inactive' }}
|
|
195
|
+
const statusLabel = computed(() => (user.value.isActive ? 'Active' : 'Inactive'));
|
|
196
|
+
// template: {{ statusLabel }}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Forms
|
|
200
|
+
|
|
201
|
+
Bind with `v-model` / `defineModel`. Keep validation rules in pure functions (zod/valibot) the form just
|
|
202
|
+
calls; a library (VeeValidate) wires them to fields when forms get large.
|
|
203
|
+
|
|
204
|
+
## Vue Decision Matrix
|
|
205
|
+
|
|
206
|
+
| Problem | Recommended Solution |
|
|
207
|
+
| ----------------------------- | ------------------------------------------------------------ |
|
|
208
|
+
| Local component state | ref / reactive (objects) |
|
|
209
|
+
| Derived value | computed |
|
|
210
|
+
| Shared app state | Pinia setup store + storeToRefs |
|
|
211
|
+
| Dependency injection | provide / inject + InjectionKey + a typed composable |
|
|
212
|
+
| Interchangeable behaviors | Strategy (`as const satisfies` lookup map) |
|
|
213
|
+
| Plugin / extension system | Registry (self-registering Map, zod for dynamic) |
|
|
214
|
+
| Object creation with logic | Factory (factory function / composable) |
|
|
215
|
+
| Reusable action | Command (framework-agnostic use-case, store action delegates) |
|
|
216
|
+
| API ↔ domain shape mismatch | Adapter — fromDto / toDto in the fetcher / composable |
|
|
217
|
+
| Cross-cutting behavior | Composition (composables + slots + provide/inject) |
|
|
218
|
+
| Async read / mutation | composable (TanStack Query / useAsyncData) — or `<Suspense>` (experimental) + await |
|
|
219
|
+
| Two-way binding | defineModel() |
|
|
220
|
+
| Template ref | useTemplateRef() |
|
|
221
|
+
| External system sync | watch / watchEffect with onWatcherCleanup |
|
|
222
|
+
| Form | v-model / defineModel + pure validators (zod/valibot) |
|
|
223
|
+
|
|
224
|
+
## Anti-Patterns to Avoid
|
|
225
|
+
|
|
226
|
+
- Options API or mixins in new code (use `<script setup>` + composables)
|
|
227
|
+
- deriving state in a `watch` that writes a `ref` instead of using `computed`
|
|
228
|
+
- destructuring a `reactive` object (loses reactivity) — use `ref` + `computed`, or `toRefs` / `storeToRefs`
|
|
229
|
+
- mutating props; mutating store state outside actions
|
|
230
|
+
- business logic in templates; DTOs leaking into templates; string magic / `switch` on a type tag instead of an `as const satisfies` lookup map
|
|
231
|
+
- god composables/components; calling the HTTP client from a component — go through a composable/store
|
|
232
|
+
- manual `modelValue` prop + `update:modelValue` emit instead of `defineModel`
|
|
233
|
+
- a Command merged into a Pinia action when it must be reusable — keep it a distinct use-case the action delegates to
|
|
234
|
+
- inheritance-heavy code; `any`; magic strings; global mutable singletons outside Pinia
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: transpose-design-patterns
|
|
2
|
+
type: skill
|
|
3
|
+
tools:
|
|
4
|
+
- claude-code
|
|
5
|
+
- cursor
|
|
6
|
+
- github-copilot
|
|
7
|
+
description: "Decide a design pattern - or an explicit none - from a framework-agnostic catalog, transpose it to the target framework (Angular, React, Vue, Vanilla TS, Quarkus, PHP/Symfony), and record the decision before writing implementation code; then hand the code to a fresh blind reviewer. Use when implementing or refactoring a pattern-shaped decision - interchangeable behaviors, plugin extensibility, object creation, DTO mapping, shared state, or cross-cutting concerns."
|
|
8
|
+
label: "Transpose Design Patterns"
|
|
9
|
+
author: "Guillaume Mongin (@hellraisercenobit)"
|
|
10
|
+
license: "MIT"
|
|
11
|
+
copyright: "Copyright (c) 2026 Guillaume Mongin (@hellraisercenobit)"
|
|
12
|
+
homepage: "https://github.com/hellraisercenobit/skills/tree/main/skills/engineering/transpose-design-patterns"
|
|
13
|
+
version: "1.2.0"
|
|
14
|
+
icon: "https://img.icons8.com/color/48/blueprint.png"
|
|
15
|
+
tags:
|
|
16
|
+
- software-engineering
|
|
17
|
+
- architecture
|
|
18
|
+
- implementation
|
|
19
|
+
- conventions
|
|
20
|
+
content: SKILL.md
|
|
21
|
+
installPaths:
|
|
22
|
+
claude-code: .claude/skills/transpose-design-patterns/SKILL.md
|
|
23
|
+
cursor: .cursor/skills/transpose-design-patterns/SKILL.md
|
|
24
|
+
github-copilot: .github/instructions/transpose-design-patterns.instructions.md
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Guillaume Mongin (@hellraisercenobit)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: transpose-modern-typescript
|
|
3
|
+
description: "Translate implementation intent into modern idiomatic TS/JS: everyday syntax, types, collections, generators, resource lifetime and native browser APIs. Compare native, library, custom and existing choices; record before editing, then obtain a fresh blind review. Use when writing or refactoring TS/JS with verbose extraction, nullish guards, redundant helpers, repeated lookups, eager production, unsafe casts or manual platform mechanisms. Companion to review-modern-typescript. Excludes prose-only and formatting-only edits."
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: Guillaume Mongin (@hellraisercenobit)
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Transpose modern TypeScript
|
|
10
|
+
|
|
11
|
+
Choose the simplest supported implementation that expresses intent and protects invariants.
|
|
12
|
+
Actively seek useful language and platform capabilities: working code can still contain
|
|
13
|
+
avoidable complexity. Justify readability, maintenance, safety, work avoided or lifetime.
|
|
14
|
+
Keep an existing choice when it wins.
|
|
15
|
+
|
|
16
|
+
## Read first, then disclose by need
|
|
17
|
+
|
|
18
|
+
- [Shared contract 1.1.0](references/suite-contract.md): C01-C12, the declaration, the three
|
|
19
|
+
fingerprints, neutral brief, independence, composition, verdicts and gate/portable execution.
|
|
20
|
+
Read once per execution.
|
|
21
|
+
- [Catalog 1.1.0](references/catalog.md): inventory axes and rule routing; load applicable families only.
|
|
22
|
+
- [Compatibility](references/compatibility.md): compiler, declarations, build and runtime are separate.
|
|
23
|
+
- [Record schema 2.0.0](references/decision-record.schema.json) and
|
|
24
|
+
[record guidance](references/record.md): validate before the first affected write.
|
|
25
|
+
- Shared shapes beside the contract: [declaration](references/declaration.schema.json),
|
|
26
|
+
[decision envelope](references/decision-envelope.schema.json),
|
|
27
|
+
[evidence append](references/evidence-append.schema.json) and [dispute](references/dispute.schema.json).
|
|
28
|
+
|
|
29
|
+
Resolve /review-modern-typescript before claiming completion. Both companions require contract
|
|
30
|
+
1.1.0, catalog 1.1.0 and record schema 2.0.0. Missing or conflicting references make execution
|
|
31
|
+
incomplete. No framework guide is required by this catalog.
|
|
32
|
+
|
|
33
|
+
## Procedure
|
|
34
|
+
|
|
35
|
+
0. **Declare.** Pipe a [declaration](references/declaration.schema.json) to
|
|
36
|
+
`ai-engineering-gate declare --dimension modern-typescript --stdin` before any record: whether the
|
|
37
|
+
dimension applies, the reason, the requester's own wording, the factual constraints, the comparison
|
|
38
|
+
base and the protected paths. `non-applicable` with its reason is a complete answer. The reviewer's
|
|
39
|
+
brief is rendered from this document, so it carries no chosen idiom and no rationale.
|
|
40
|
+
1. **Frame.** Identify raw need, exact scope/base and actual compiler/lockfile, tsconfig,
|
|
41
|
+
build, runtimes and browsers. Put the ES/compiler target in declaration `constraints`
|
|
42
|
+
when this dimension applies; a tsconfig path is not a substitute. Do not pin projects to
|
|
43
|
+
the test compiler. In JS, use runtime rules and existing JSDoc/checkJs without requiring
|
|
44
|
+
a TS migration. Respect
|
|
45
|
+
framework reactivity, DOM ownership and cleanup. Invoke /transpose-design-patterns only
|
|
46
|
+
for architectural decisions; a local idiom does not automatically need a pattern.
|
|
47
|
+
2. **Inventory.** Walk every catalog axis across relevant sites, including retained code.
|
|
48
|
+
Record applicable axes and reasoned exclusions at scope level. Searches suggest
|
|
49
|
+
candidates; read context. Examine access, amount consumed, lifetime, asynchrony,
|
|
50
|
+
platform services, transformations and type guarantees.
|
|
51
|
+
3. **Compare.** Read applicable rules. Compare current code, native, library and custom
|
|
52
|
+
constructions wherever plausible. Prefer a supported native capability satisfying the
|
|
53
|
+
whole contract. Explain semantics, complexity, maintenance and compatibility. Preserve
|
|
54
|
+
business meaning in helpers. No blanket bans on loops, reduce, classes, enums,
|
|
55
|
+
libraries or assertions. Distinguish algorithmic benefits from measured timings;
|
|
56
|
+
benchmark only when the decision depends on timing.
|
|
57
|
+
4. **Record before editing.** File it: `ai-engineering-gate record --dimension modern-typescript --stdin`,
|
|
58
|
+
which stores it outside the repository, validates it against the envelope and this catalog's schema, and
|
|
59
|
+
refuses a `cites` path that does not exist. Name in `plans` what the record commits to produce. State
|
|
60
|
+
each site's `semanticDelta` - what the change is expected to alter, what it must preserve - and, where a
|
|
61
|
+
type guarantee moves, its `consumption`, so MT-24 is answered rather than assumed. Group coherent choices
|
|
62
|
+
only if all sites remain identifiable. `none` means no specialized transposition helps; simple code can
|
|
63
|
+
still be written. `retain` is a separate action and can retain a named native idiom. A revision names the
|
|
64
|
+
cited paths it `changed`, and the gate refuses one whose citations are byte-identical. Without the gate,
|
|
65
|
+
validate with a JSON Schema 2020-12 validator plus catalog membership checks yourself.
|
|
66
|
+
5. **Implement and check.** The first write inside the declared scope is allowed once the record is on file;
|
|
67
|
+
`ai-engineering-gate can-write --path <path>` answers it. Follow recorded invariants. Run proportional
|
|
68
|
+
existing checks and small behavioral/type checks where needed. Types do not prove runtime support. Verify
|
|
69
|
+
touched browser behavior in a real browser with the project's required tooling. Report commands, outcomes
|
|
70
|
+
and remaining limits separately, and file each planned artifact and check output through
|
|
71
|
+
`ai-engineering-gate evidence append --dimension modern-typescript --stdin`.
|
|
72
|
+
6. **Dispatch fresh review.** Run `ai-engineering-gate status --full`: once the records and the planned
|
|
73
|
+
evidence are on file it prints the dispatch plan and the neutral brief for each reviewer. Use
|
|
74
|
+
`modern-typescript-reviewer`, otherwise a fresh general subagent without inherited conversation. Send the
|
|
75
|
+
brief as printed - scope and base, raw request, factual constraints, record paths, check evidence - and no
|
|
76
|
+
chosen idiom, rationale, expected verdict or test oracle. The reviewer runs /review-modern-typescript
|
|
77
|
+
itself. Without an independent context, report incomplete execution.
|
|
78
|
+
7. **Close the loop.** Corrections are batched: address every pending finding of every dimension, then
|
|
79
|
+
dispatch all applicable reviews together on one state. An evidence finding is closed by its remedy, a
|
|
80
|
+
judgment finding by a revision that `addresses` it. A finding you contest goes to
|
|
81
|
+
`ai-engineering-gate dispute --dimension modern-typescript --stdin` with counter-evidence, and only the
|
|
82
|
+
user arbitrates it. Edits invalidate overlapping verdicts. Finish when `ai-engineering-gate can-stop`
|
|
83
|
+
exits 0.
|
|
84
|
+
|
|
85
|
+
## Deliverable
|
|
86
|
+
|
|
87
|
+
Summarize scope/profile, decisions, artifacts, benefits/trade-offs, records/revisions,
|
|
88
|
+
checks, independent report and examined state. State portable mode honestly. Do not
|
|
89
|
+
implicitly install polyfills, upgrade TS or extend a gate. Catalog gaps are separate
|
|
90
|
+
from findings. Missing evidence is not successful completion.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/hellraisercenobit/skills/blob/main/contracts/schemas/arbitration.schema.json",
|
|
4
|
+
"title": "Arbitration 1.0.0",
|
|
5
|
+
"description": "A dispute's only exit, and only a human hand writes it. `arbitrate` is refused whenever an identity handoff exists for the call, which is the case for every agent tool call a hook saw.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": [
|
|
9
|
+
"document",
|
|
10
|
+
"documentVersion",
|
|
11
|
+
"dimension",
|
|
12
|
+
"dispute",
|
|
13
|
+
"decision",
|
|
14
|
+
"words"
|
|
15
|
+
],
|
|
16
|
+
"properties": {
|
|
17
|
+
"document": {
|
|
18
|
+
"const": "arbitration"
|
|
19
|
+
},
|
|
20
|
+
"documentVersion": {
|
|
21
|
+
"const": "1.0.0"
|
|
22
|
+
},
|
|
23
|
+
"dimension": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"minLength": 1
|
|
26
|
+
},
|
|
27
|
+
"dispute": {
|
|
28
|
+
"description": "The identifier of the dispute being arbitrated.",
|
|
29
|
+
"type": "string",
|
|
30
|
+
"minLength": 1
|
|
31
|
+
},
|
|
32
|
+
"decision": {
|
|
33
|
+
"description": "`uphold` closes the finding by arbitration and allows one fresh review on the same state; `reject` puts its remedy or correction back to pending.",
|
|
34
|
+
"enum": [
|
|
35
|
+
"uphold",
|
|
36
|
+
"reject"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
"words": {
|
|
40
|
+
"description": "The user's own words.",
|
|
41
|
+
"type": "string",
|
|
42
|
+
"minLength": 1
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Modern TypeScript catalog 1.1.0
|
|
2
|
+
|
|
3
|
+
One normative source per rule; the files below are owned by transpose-modern-typescript.
|
|
4
|
+
The record schema enumerates these stable IDs. Review signatures only point here.
|
|
5
|
+
Rules apply to JS as well as TS unless explicitly about static types.
|
|
6
|
+
|
|
7
|
+
## Inventory axes
|
|
8
|
+
|
|
9
|
+
For every scope, mark each axis applicable with sites, or excluded with a reason:
|
|
10
|
+
`idioms`, `data-access`, `consumption`, `lifetime`, `async`, `platform`,
|
|
11
|
+
`types`, `modules`. Examine all relevant sites, not only easy replacements.
|
|
12
|
+
Prefer supported language/platform capabilities when they remove avoidable work or
|
|
13
|
+
mechanism while preserving the complete contract. Current code is always an alternative.
|
|
14
|
+
|
|
15
|
+
| Need or signal | Read |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| Repeated extraction, guards, array access, accumulation | [Everyday idioms](idioms.md), MT-01..05 |
|
|
18
|
+
| Repeated lookup, membership, object metadata, partial consumption | [Collections and lifetime](collections.md), MT-06..08 |
|
|
19
|
+
| Async coordination, network flow, parsing, copying | [Platform](platform.md), MT-09..12 |
|
|
20
|
+
| DOM observation, rendering, workers, storage, security | [Platform](platform.md), MT-13..16 |
|
|
21
|
+
| Inference, states, helpers, boundaries, mutability | [Types and abstractions](types.md), MT-17..21 |
|
|
22
|
+
| Imports, runtime targets or any uncertain feature | [Compatibility](compatibility.md), MT-22..23 |
|
|
23
|
+
| A replacement that may widen or erase a static guarantee | [Types and abstractions](types.md), MT-24 |
|
|
24
|
+
| A helper that forwards to a platform or language capability | [Platform](platform.md), MT-25 |
|
|
25
|
+
|
|
26
|
+
## Decision discipline
|
|
27
|
+
|
|
28
|
+
Every rule specifies intent, use conditions, alternatives/trade-offs, avoid cases,
|
|
29
|
+
observable invariants and compatibility sources. Read those clauses together. No rule
|
|
30
|
+
requires syntax for its own sake or guarantees native code is faster. A Minor finding can
|
|
31
|
+
identify concrete avoidable code, even without a bug, after its strongest defense fails.
|
|
32
|
+
Claim measured speed only with a relevant measurement. Account for index construction,
|
|
33
|
+
allocation, retained memory, serialization, cleanup, editor cost and public compatibility.
|
|
34
|
+
|
|
35
|
+
`none` is a valid decision when no specialized transposition improves the scoped contract.
|
|
36
|
+
Explain why plausible candidates do not help and what would reopen the choice. It can mean
|
|
37
|
+
writing simple code, retaining a domain helper or using an ordinary loop. It is not the
|
|
38
|
+
same as `retain` (an action), or an entirely non-applicable dimension.
|