@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.
Files changed (100) hide show
  1. package/README.md +82 -0
  2. package/agents/design-pattern-reviewer.md +40 -0
  3. package/agents/modern-typescript-reviewer.md +24 -0
  4. package/agents/testing-pattern-reviewer.md +23 -0
  5. package/bin/ai-engineering-gate.mjs +6 -0
  6. package/contracts/axi/axi-standard.md +273 -0
  7. package/contracts/axi/pin.json +36 -0
  8. package/contracts/members.json +58 -0
  9. package/contracts/schemas/arbitration.schema.json +45 -0
  10. package/contracts/schemas/decision-envelope.schema.json +215 -0
  11. package/contracts/schemas/declaration.schema.json +190 -0
  12. package/contracts/schemas/dispute.schema.json +67 -0
  13. package/contracts/schemas/evidence-append.schema.json +72 -0
  14. package/contracts/schemas/gate-output.schema.json +296 -0
  15. package/contracts/schemas/marker.schema.json +75 -0
  16. package/contracts/schemas/review-envelope.schema.json +378 -0
  17. package/contracts/schemas/verdict-record.schema.json +191 -0
  18. package/contracts/suite-contract.md +270 -0
  19. package/dist/ai-engineering-gate.mjs +2762 -0
  20. package/package.json +47 -0
  21. package/skills/engineering/review-design-patterns/LICENSE +6 -0
  22. package/skills/engineering/review-design-patterns/SKILL.md +196 -0
  23. package/skills/engineering/review-design-patterns/agents/openai.yaml +3 -0
  24. package/skills/engineering/review-design-patterns/references/smell-signatures.md +117 -0
  25. package/skills/engineering/review-design-patterns/review-design-patterns.yaml +25 -0
  26. package/skills/engineering/review-modern-typescript/LICENSE +21 -0
  27. package/skills/engineering/review-modern-typescript/SKILL.md +88 -0
  28. package/skills/engineering/review-modern-typescript/agents/openai.yaml +3 -0
  29. package/skills/engineering/review-modern-typescript/references/smell-signatures.md +36 -0
  30. package/skills/engineering/review-modern-typescript/review-modern-typescript.yaml +22 -0
  31. package/skills/engineering/review-testing-patterns/SKILL.md +108 -0
  32. package/skills/engineering/review-testing-patterns/agents/openai.yaml +4 -0
  33. package/skills/engineering/review-testing-patterns/references/compiler-profile.mjs +103 -0
  34. package/skills/engineering/review-testing-patterns/references/smell-signatures.md +30 -0
  35. package/skills/engineering/review-testing-patterns/review-testing-patterns.yaml +23 -0
  36. package/skills/engineering/transpose-design-patterns/LICENSE +6 -0
  37. package/skills/engineering/transpose-design-patterns/SKILL.md +231 -0
  38. package/skills/engineering/transpose-design-patterns/agents/openai.yaml +3 -0
  39. package/skills/engineering/transpose-design-patterns/references/arbitration.schema.json +45 -0
  40. package/skills/engineering/transpose-design-patterns/references/decision-envelope.schema.json +215 -0
  41. package/skills/engineering/transpose-design-patterns/references/declaration.schema.json +190 -0
  42. package/skills/engineering/transpose-design-patterns/references/design-decision-record.schema.json +731 -0
  43. package/skills/engineering/transpose-design-patterns/references/dispute.schema.json +67 -0
  44. package/skills/engineering/transpose-design-patterns/references/evidence-append.schema.json +72 -0
  45. package/skills/engineering/transpose-design-patterns/references/marker.schema.json +75 -0
  46. package/skills/engineering/transpose-design-patterns/references/pattern-catalog.md +305 -0
  47. package/skills/engineering/transpose-design-patterns/references/record.example.json +120 -0
  48. package/skills/engineering/transpose-design-patterns/references/review-envelope.schema.json +378 -0
  49. package/skills/engineering/transpose-design-patterns/references/suite-contract.md +270 -0
  50. package/skills/engineering/transpose-design-patterns/references/transpose-angular.md +224 -0
  51. package/skills/engineering/transpose-design-patterns/references/transpose-php.md +561 -0
  52. package/skills/engineering/transpose-design-patterns/references/transpose-quarkus.md +283 -0
  53. package/skills/engineering/transpose-design-patterns/references/transpose-react.md +256 -0
  54. package/skills/engineering/transpose-design-patterns/references/transpose-vanilla.md +204 -0
  55. package/skills/engineering/transpose-design-patterns/references/transpose-vue.md +234 -0
  56. package/skills/engineering/transpose-design-patterns/transpose-design-patterns.yaml +24 -0
  57. package/skills/engineering/transpose-modern-typescript/LICENSE +21 -0
  58. package/skills/engineering/transpose-modern-typescript/SKILL.md +90 -0
  59. package/skills/engineering/transpose-modern-typescript/agents/openai.yaml +3 -0
  60. package/skills/engineering/transpose-modern-typescript/references/arbitration.schema.json +45 -0
  61. package/skills/engineering/transpose-modern-typescript/references/catalog.md +38 -0
  62. package/skills/engineering/transpose-modern-typescript/references/collections.md +50 -0
  63. package/skills/engineering/transpose-modern-typescript/references/compatibility.md +61 -0
  64. package/skills/engineering/transpose-modern-typescript/references/decision-envelope.schema.json +215 -0
  65. package/skills/engineering/transpose-modern-typescript/references/decision-record.schema.json +830 -0
  66. package/skills/engineering/transpose-modern-typescript/references/declaration.schema.json +190 -0
  67. package/skills/engineering/transpose-modern-typescript/references/dispute.schema.json +67 -0
  68. package/skills/engineering/transpose-modern-typescript/references/evidence-append.schema.json +72 -0
  69. package/skills/engineering/transpose-modern-typescript/references/idioms.md +74 -0
  70. package/skills/engineering/transpose-modern-typescript/references/marker.schema.json +75 -0
  71. package/skills/engineering/transpose-modern-typescript/references/platform.md +147 -0
  72. package/skills/engineering/transpose-modern-typescript/references/record.example.json +172 -0
  73. package/skills/engineering/transpose-modern-typescript/references/record.md +37 -0
  74. package/skills/engineering/transpose-modern-typescript/references/review-envelope.schema.json +378 -0
  75. package/skills/engineering/transpose-modern-typescript/references/suite-contract.md +270 -0
  76. package/skills/engineering/transpose-modern-typescript/references/types.md +97 -0
  77. package/skills/engineering/transpose-modern-typescript/transpose-modern-typescript.yaml +22 -0
  78. package/skills/engineering/transpose-testing-patterns/SKILL.md +96 -0
  79. package/skills/engineering/transpose-testing-patterns/agents/openai.yaml +4 -0
  80. package/skills/engineering/transpose-testing-patterns/references/arbitration.schema.json +45 -0
  81. package/skills/engineering/transpose-testing-patterns/references/catalog.md +218 -0
  82. package/skills/engineering/transpose-testing-patterns/references/decision-envelope.schema.json +215 -0
  83. package/skills/engineering/transpose-testing-patterns/references/decision-record.schema.json +857 -0
  84. package/skills/engineering/transpose-testing-patterns/references/declaration.schema.json +190 -0
  85. package/skills/engineering/transpose-testing-patterns/references/detect-adapter.mjs +98 -0
  86. package/skills/engineering/transpose-testing-patterns/references/dispute.schema.json +67 -0
  87. package/skills/engineering/transpose-testing-patterns/references/doubles-data.md +45 -0
  88. package/skills/engineering/transpose-testing-patterns/references/evidence-append.schema.json +72 -0
  89. package/skills/engineering/transpose-testing-patterns/references/journal-event.schema.json +115 -0
  90. package/skills/engineering/transpose-testing-patterns/references/marker.schema.json +75 -0
  91. package/skills/engineering/transpose-testing-patterns/references/record.example.json +230 -0
  92. package/skills/engineering/transpose-testing-patterns/references/record.md +25 -0
  93. package/skills/engineering/transpose-testing-patterns/references/review-envelope.schema.json +378 -0
  94. package/skills/engineering/transpose-testing-patterns/references/suite-contract.md +270 -0
  95. package/skills/engineering/transpose-testing-patterns/references/tdd.md +62 -0
  96. package/skills/engineering/transpose-testing-patterns/references/transpose-codeception.md +84 -0
  97. package/skills/engineering/transpose-testing-patterns/references/transpose-karma-jasmine-angular.md +73 -0
  98. package/skills/engineering/transpose-testing-patterns/references/transpose-vitest.md +111 -0
  99. package/skills/engineering/transpose-testing-patterns/references/typescript.md +42 -0
  100. 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,3 @@
1
+ interface:
2
+ display_name: "Transpose Modern TypeScript"
3
+ short_description: "Choose supported TS/JS idioms and verify the result"
@@ -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.