@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,108 @@
1
+ ---
2
+ name: review-testing-patterns
3
+ description: "Independently audit test strategy, oracles, doubles, TypeScript guarantees and TDD evidence. Detect false green, implementation coupling and environment or execution gaps; freeze expectations before records, compare every site and steelman findings. Use for a diff, existing suite, proposal or fresh transpose-testing-patterns handoff. Read-only companion to transpose-testing-patterns."
4
+ license: MIT
5
+ metadata:
6
+ author: Guillaume Mongin (@hellraisercenobit)
7
+ ---
8
+
9
+ # Review testing patterns
10
+
11
+ Run this audit yourself. Modify no files, including fixtures, records and catalogs, and
12
+ delegate no review. [Signatures](references/smell-signatures.md) locate candidates; they
13
+ are not an independent rule catalog.
14
+
15
+ ## Prerequisites
16
+
17
+ Resolve installed /transpose-testing-patterns through harness skill locations, without a
18
+ maintainer checkout. Read its `references/suite-contract.md`, `catalog.md`,
19
+ `decision-record.schema.json`, `journal-event.schema.json` and applicable guides. This
20
+ reviewer requires contract 1.1.0, catalog 1.0.0, record schema 2.0.0 and the adapter for
21
+ the detected family, compatible with 1.0.0. Apply `assessTestingProfile` from
22
+ `references/compiler-profile.mjs`. Vitest loads
23
+ `references/transpose-vitest.md`. Karma + jasmine-core + Angular TestBed loads
24
+ `references/transpose-karma-jasmine-angular.md`. Codeception loads
25
+ `references/transpose-codeception.md`. SOUND on Karma + Angular TestBed requires the
26
+ adapter and a qualification pin under `tests/testing-patterns/karma-jasmine-angular/`;
27
+ ChromeHeadless execution is optional. SOUND on Codeception requires that adapter and a
28
+ qualification pin under `tests/testing-patterns/codeception/`; Docker execution of
29
+ `codecept` is optional. Other runners still receive catalog recale, including compiler
30
+ profile and wrong-runner APIs. SOUND for a qualified Vitest transposition still needs the
31
+ Vitest adapter. Missing or conflicting references mean incomplete execution, without
32
+ verdict; do not download substitutes or invent rules.
33
+
34
+ A transposition requires a fresh context and neutral brief. Check record paths exist but
35
+ do not open them before freezing. Contamination requires a new dispatch. Standalone audits
36
+ need no historical decision record and must not manufacture a process claim.
37
+
38
+ Open the window before step 1: `ai-engineering-gate begin --dimension testing-patterns` freezes
39
+ the state the verdict binds to, captures who you are and refuses the builder of the task.
40
+
41
+ ## Blind audit and comparison
42
+
43
+ 1. **Facts.** Establish scope/base with git diff, reads and relevant untracked files, never
44
+ git log, PR descriptions or author analysis. Identify source/configuration/reference
45
+ state as well as HEAD. Derive runner, checker and compiler/runtime profile independently
46
+ from project files; do not copy the builder's record or trust constraints alone. When any
47
+ in-scope test is TypeScript or JavaScript emitted by tsc, the profile has four layers:
48
+ parser/compiler syntax (installed TypeScript version + target), lib/types, emit/runner
49
+ transform, and actual runtime. Constraints must contain the ES/compiler target; a
50
+ tsconfig path is not a substitute, and derived `compiler.target` is a second independent
51
+ layer. Apply `assessTestingProfile` to that derived profile and the actual syntax/APIs.
52
+ 2. **Derive.** Inventory behaviors and risks from the request and public contracts.
53
+ Builder-written names, assertions and helpers are not independent requirements. Defer
54
+ detailed assertion reading until freeze when possible; disclose significant exposure
55
+ to author rationale. Consider direct, state, interaction, contract, property and
56
+ characterization tests, including defensible existing choices.
57
+ 3. **Freeze.** Publish an immutable matrix in your response before the next tool call
58
+ opening records or builder evidence: site, behavior/risk, rule IDs, acceptable seams
59
+ and alternatives, runtime/type guarantees, the four compiler/runtime layers and
60
+ discriminating cases. Freeze acceptable outcomes, not exact syntax. An unknown required
61
+ layer is incomplete execution: leave it unresolved and do not issue SOUND. Changed
62
+ verified premises require a new framing/reviewer, preserving this matrix.
63
+ 4. **Compare.** Open records and evidence. Validate shape/membership separately from
64
+ semantic judgment. Compare expected / recorded / actual for every site, including
65
+ uncovered risks. Inspect actual assertions, production, helpers and configuration.
66
+ Verify scenario-to-executed-test mapping and skips/fails/retries, checker file coverage,
67
+ failure causes, oracle hashes, raw output and resolvable RED/GREEN states. Read the state
68
+ the journal derives rather than a process claim: `observed` when the builder's own events
69
+ carry the red, `replayed` when the gate ran the scenario itself, `incomplete` when a phase
70
+ or its failure class is missing, and `non-TDD` when the record never claimed the mode. A
71
+ missing red is an evidence finding whose remedy is a replay, not a judgment about intent.
72
+ Hashes alone are not inspectable evidence. A reassuring record or global green is not proof.
73
+ Recale syntax the installed compiler/`target` cannot parse, APIs not in `lib` and not proven
74
+ on the runtime, and runner APIs from a different adapter family (for example `vi.fn` on
75
+ karma-jasmine-angular). Incompatible modernization is a finding (MT-23), not taste. A
76
+ constraints versus derived target mismatch is a finding. Tests-only work with
77
+ `modern-typescript` non-applicable still recales too-modern spec TypeScript here.
78
+ 5. **Steelman.** Defend every candidate by contract, fidelity, compatibility, diagnostic
79
+ value, cost or simplicity. Drop it if that defense holds. Confirm only with an exact
80
+ applicable rule, observed evidence, impact and refutation. Report catalog gaps
81
+ separately without severity or verdict impact. Missing old TDD history is not a bug;
82
+ missing required evidence prevents completing a claimed TDD transposition.
83
+ 6. **Report.** Give scope/base/state, references/profile, coverage, unchanged matrix,
84
+ each site's comparison, findings, defended choices, gaps, checks and limits. Each
85
+ finding includes location, rule, severity, expected/recorded/actual, evidence, strongest
86
+ defense, refutation and scoped correction. Builder performs mutations/experiments and
87
+ fixes; a new fresh reviewer reassesses.
88
+
89
+ ## Completion
90
+
91
+ Use exactly SOUND (complete audit, no findings), SMELLS (findings without Blocker), or
92
+ VIOLATIONS (a Blocker). Apply the contract's severity clause for Blocker, Major and Minor;
93
+ do not restate it. Missing prerequisites or an unknown required compiler/runtime layer are
94
+ incomplete execution, not a fourth verdict. Findings from `assessTestingProfile` prevent
95
+ SOUND. A proposal verdict does not qualify runtime behavior.
96
+
97
+ File through the window you opened. Pipe a
98
+ [review envelope](../transpose-testing-patterns/references/review-envelope.schema.json) to
99
+ `ai-engineering-gate attest --dimension testing-patterns --stdin` for SOUND, and to
100
+ `ai-engineering-gate report --dimension testing-patterns --stdin` otherwise. Filing the envelope is the sole
101
+ permitted external mutation. Type each finding: `judgment` carries the correction, `evidence`
102
+ carries a remedy - `produce` an artifact, `rerun` a check, or `replay` a scenario the gate
103
+ runs itself. Put no fingerprint in the envelope; the gate computes all three. A refusal of
104
+ `state-moved` means the suite or the production code moved while you read it, so the review is
105
+ void: say so and stop. Portable reports have no automatic lock. Covered source, record or
106
+ reference changes expire the report, which the gate reports as `stale-source`,
107
+ `stale-reference` or `stale-decision`; `can-stop` is what answers whether every applicable
108
+ dimension and check agrees on one final state.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Review Testing Patterns"
3
+ short_description: "Independently audit tests and TDD evidence"
4
+ default_prompt: "Use $review-testing-patterns to audit this test strategy independently."
@@ -0,0 +1,103 @@
1
+ const INCOMPATIBLE_MODERNIZATION = { id: 'incompatible-modernization', rule: 'TP-12' };
2
+ const TARGET_MISMATCH = { id: 'target-mismatch', rule: 'TP-16' };
3
+ const WRONG_RUNNER_API = { id: 'wrong-runner-api', rule: 'TP-14' };
4
+
5
+ export function assessTestingProfile(input) {
6
+ if (needsCompilerProfile(input) && !hasResolvedProfile(input)) {
7
+ return { status: 'incomplete', findings: [] };
8
+ }
9
+ const findings = [];
10
+ if (declaredTargetMismatchesDerived(input)) {
11
+ findings.push(TARGET_MISMATCH);
12
+ }
13
+ if (syntaxExceedsCompiler(input)) {
14
+ findings.push(INCOMPATIBLE_MODERNIZATION);
15
+ }
16
+ if (usesWrongRunnerApi(input)) {
17
+ findings.push(WRONG_RUNNER_API);
18
+ }
19
+ return { status: 'complete', findings };
20
+ }
21
+
22
+ function needsCompilerProfile(input) {
23
+ return input.language === 'typescript' || input.emittedByTsc === true;
24
+ }
25
+
26
+ function hasResolvedProfile(input) {
27
+ return hasEsTarget(input)
28
+ && knownLayer(input.compiler?.version)
29
+ && hasLibLayer(input)
30
+ && knownLayer(input.emit)
31
+ && knownLayer(input.runtime);
32
+ }
33
+
34
+ function hasEsTarget(input) {
35
+ return Boolean(input.compiler?.target) && constraintHasTarget(input.constraints);
36
+ }
37
+
38
+ function hasLibLayer(input) {
39
+ return Array.isArray(input.compiler?.lib) && input.compiler.lib.length > 0;
40
+ }
41
+
42
+ function knownLayer(value) {
43
+ return Boolean(value) && String(value).toLowerCase() !== 'unknown';
44
+ }
45
+
46
+ function declaredTargetMismatchesDerived(input) {
47
+ const declared = constraintEsTarget(input.constraints);
48
+ const derived = normalizeEsTarget(input.compiler?.target);
49
+ return Boolean(declared && derived && declared !== derived);
50
+ }
51
+
52
+ function syntaxExceedsCompiler(input) {
53
+ const version = typescriptVersion(input.compiler?.version);
54
+ if (version === undefined) {
55
+ return false;
56
+ }
57
+ const syntax = input.actual?.syntax ?? [];
58
+ if (syntax.includes('satisfies') && version < 4.9) {
59
+ return true;
60
+ }
61
+ if (syntax.includes('using') && version < 5.2) {
62
+ return true;
63
+ }
64
+ return false;
65
+ }
66
+
67
+ function usesWrongRunnerApi(input) {
68
+ if (!input.adapterFamily) {
69
+ return false;
70
+ }
71
+ const usesVitestApi = input.actual?.apis?.includes('vi.fn');
72
+ return Boolean(usesVitestApi) && !input.adapterFamily.includes('vitest');
73
+ }
74
+
75
+ function constraintHasTarget(constraints = []) {
76
+ return Boolean(constraintEsTarget(constraints));
77
+ }
78
+
79
+ function constraintEsTarget(constraints = []) {
80
+ for (const item of constraints) {
81
+ const match = item.match(/\btarget\s+(ES\d+)/i);
82
+ if (match) {
83
+ return normalizeEsTarget(match[1]);
84
+ }
85
+ }
86
+ return undefined;
87
+ }
88
+
89
+ function normalizeEsTarget(value) {
90
+ if (!value) {
91
+ return undefined;
92
+ }
93
+ const match = String(value).match(/ES\d+/i);
94
+ return match ? match[0].toUpperCase() : undefined;
95
+ }
96
+
97
+ function typescriptVersion(value) {
98
+ const match = String(value ?? '').match(/(\d+)\.(\d+)/);
99
+ if (!match) {
100
+ return undefined;
101
+ }
102
+ return Number(match[1]) + Number(match[2]) / 10;
103
+ }
@@ -0,0 +1,30 @@
1
+ # Detection leads
2
+
3
+ These are search leads, not normative rules. Read the transpose-owned rule and context,
4
+ then steelman before confirming. Do not count words, mocks, classes or lines as defects.
5
+
6
+ | Candidate | Rule | Strongest plausible defense to examine |
7
+ | --- | --- | --- |
8
+ | Expected result repeats production algorithm | TP-01/05/15 | Independent specification or simpler model |
9
+ | Test reads private representation or seeds invalid state | TP-02/09/11 | Legitimate public read model or unknown boundary |
10
+ | Only verifies a business object's method was called | TP-03/08/10 | Interaction itself is an external protocol |
11
+ | Fake owns policy; only fake is exercised | TP-04/08 | Tooling test labelled honestly, product policy tested elsewhere |
12
+ | Fake/real shared tests never execute real adapter | TP-04/14 | No substitutability claim, limited stub role |
13
+ | Builder/mother hides the decisive input | TP-09/15 | Named reusable business case remains explicit |
14
+ | Every collaborator mocked or every resource real | TP-08/10 | Specific diagnostic/fidelity requirement |
15
+ | New getter/port exists only for assertion | TP-11 | Real unstable boundary or supported library consumer |
16
+ | Cast makes malformed data appear valid | TP-12 | Validated boundary with an explicit narrowing proof |
17
+ | Runtime green offered as type proof | TP-12/16 | Separate checker covers files and consumer contracts |
18
+ | Message-only error check accepts wrong error | TP-15 | Message is the whole public error contract |
19
+ | Promise assertion is unawaited; catch can skip assertions | TP-13/15 | Runner actually awaits returned operation |
20
+ | Concurrency test never forces overlap | TP-13 | Claim is eventual result, not exclusion |
21
+ | Generator cleanup/abort only tested on success | TP-13/16 | Contract has no ownership of that resource |
22
+ | Browser/provider claim backed by DOM/network simulation | TP-14 | Guarantee is within simulation fidelity |
23
+ | RED is import failure or changed oracle | TP-07 | Labelled setup or restarted cycle with preserved observations |
24
+ | TDD inferred from final files or commit messages | TP-07 | Retrospective quality audit with no chronology claim |
25
+ | Green run has missing required scenarios, skips or retries | TP-16 | Explicit non-required limitation, not claimed proof |
26
+ | Spec uses syntax the frozen compiler cannot parse (`satisfies`, `using`) | TP-12 | Compiler version actually supports that syntax |
27
+ | Spec uses a runner API from a different adapter (`vi.fn` on Karma/Jasmine) | TP-14 | Detected adapter family actually owns that API |
28
+
29
+ An experiment must preserve the audited tree. Ask the builder to run a scoped isolated
30
+ probe when evidence is unavailable. Do not inject a defect or rewrite a test yourself.
@@ -0,0 +1,23 @@
1
+ name: review-testing-patterns
2
+ type: skill
3
+ tools:
4
+ - claude-code
5
+ - cursor
6
+ - github-copilot
7
+ description: "Audit testing decisions and TDD evidence with a blind matrix and steelman gate."
8
+ label: "Review Testing 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/review-testing-patterns"
13
+ version: "1.0.0"
14
+ tags:
15
+ - software-engineering
16
+ - typescript
17
+ - testing
18
+ - vitest
19
+ content: SKILL.md
20
+ installPaths:
21
+ claude-code: .claude/skills/review-testing-patterns/SKILL.md
22
+ cursor: .cursor/skills/review-testing-patterns/SKILL.md
23
+ github-copilot: .github/instructions/review-testing-patterns.instructions.md
@@ -0,0 +1,6 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Guillaume Mongin (@hellraisercenobit)
4
+
5
+ This skill (`transpose-design-patterns`) is an original work by Guillaume Mongin.
6
+ See the repository root `LICENSE` and `NOTICE` for full terms.
@@ -0,0 +1,231 @@
1
+ ---
2
+ name: transpose-design-patterns
3
+ description: Decide the design pattern - or an explicit none - for a code change from a built-in framework-agnostic catalog, transpose it to the target framework via a bundled per-framework guide, and record the decision BEFORE writing implementation code; after implementation, hand the code to a fresh blind reviewer. USE WHEN implementing or refactoring code with a pattern-shaped decision - interchangeable behaviors, plugin/extensibility, object-creation logic, DTO/API-shape mapping, shared state, composable actions, cross-cutting concerns - or when choosing where code goes or whether a design is sound. EXAMPLES - "add a new exporter type", "wire two payment providers via DI", "where should this DTO mapping live", "make this service pluggable", "Strategy or Registry here?".
4
+ license: MIT
5
+ author: Guillaume Mongin (@hellraisercenobit)
6
+ ---
7
+
8
+ # Transpose Design Patterns
9
+
10
+ Autonomous, portable, **mandatory** workflow: detect the structural forces in the change, decide on a
11
+ catalog pattern **or on an explicit `none`**, transpose it to the target framework with a bundled guide,
12
+ and record the decision - all **before** writing implementation code. After implementation, a fresh
13
+ reviewer re-derives the design blind (`review-design-patterns`). Everything this skill needs ships inside
14
+ it; it references **no project files**, so it works in any repository.
15
+
16
+ ## Bundled sources (this skill owns them - read, do not paraphrase from memory)
17
+
18
+ - **Shared procedure:** [suite contract 1.1.0](references/suite-contract.md) - read once per execution for C01-C12, the declaration, the three fingerprints, neutral context, composition, state expiry and portable/gate limits. The shared schemas it refers to sit beside it: [declaration](references/declaration.schema.json), [decision envelope](references/decision-envelope.schema.json), [evidence append](references/evidence-append.schema.json) and [dispute](references/dispute.schema.json).
19
+ - **Catalog (framework-agnostic):** [`references/pattern-catalog.md`](references/pattern-catalog.md) -
20
+ the _Structural forces_ table (force → decision) with the _Extension-cost test_, one entry per pattern
21
+ (_Use when / Best practices / Avoid / Invariants_), the `None` entry.
22
+ - **Record shape:** [`references/design-decision-record.schema.json`](references/design-decision-record.schema.json) -
23
+ the JSON Schema of the design decision record; a gate validates against it, never against semantics.
24
+ - **Transposition (per framework):** the guide from the table below.
25
+
26
+ | Target framework | Transposition guide | Store / shared state |
27
+ | ------------------------- | ------------------------------------------------------------------- | ---------------------------- |
28
+ | Angular | [`references/transpose-angular.md`](references/transpose-angular.md) | `providedIn: 'root'` service / `@ngrx/signals` |
29
+ | React | [`references/transpose-react.md`](references/transpose-react.md) | Zustand |
30
+ | Vue | [`references/transpose-vue.md`](references/transpose-vue.md) | Pinia (setup store) |
31
+ | Vanilla TS (no framework) | [`references/transpose-vanilla.md`](references/transpose-vanilla.md) | closure observable store / signals |
32
+ | Quarkus (Java) | [`references/transpose-quarkus.md`](references/transpose-quarkus.md) | `@ApplicationScoped` CDI bean |
33
+ | PHP (Symfony) | [`references/transpose-php.md`](references/transpose-php.md) | stateless shared service; Cache / Lock for cross-request state |
34
+
35
+ > Determine the target framework from the project you are editing - `package.json` deps for the JS/TS
36
+ > frameworks (no UI framework → Vanilla TS), or a `pom.xml` / `build.gradle` declaring `io.quarkus`
37
+ > deps → Quarkus, or a `composer.json` → PHP (`symfony/framework-bundle` in its `require` confirms the Symfony
38
+ > wiring; without it the same class shapes apply at a composition root). To support another, see
39
+ > _Extending to a new framework_. **No guide for the detected stack is a valid state:** decide on the
40
+ > catalog alone and record `transposition: null`.
41
+
42
+ ## Mandatory procedure
43
+
44
+ Run **every** step before writing or changing implementation code. Do not jump straight to coding.
45
+
46
+ 0. **Declare the dimension.** Before any record, say whether design patterns apply to this change and on
47
+ which paths: pipe a [declaration](references/declaration.schema.json) to
48
+ `ai-engineering-gate declare --dimension design-patterns --stdin`. It carries the requester's own wording,
49
+ the factual constraints, the comparison base and the protected scope - never your rationale, because the
50
+ reviewer's brief is rendered from it. Declaring `non-applicable` with the reason is a complete answer:
51
+ skipping the dimension is a recorded decision, not silence. _Done when:_ the declaration is accepted.
52
+ 1. **Detect the structural forces.** Walk the eight forces of the catalog's _Structural forces_ table
53
+ against the change. Answer each one with a **value from its set** plus the site that carries it: a file,
54
+ the planned symbol, or the requirement that states it (a planned third variant lives in the ticket, not
55
+ in the code). _Done when:_ all eight forces have a value and a site - a value, never prose, because that
56
+ is what the reviewer compares.
57
+ 2. **Run the extension-cost test.** For each variation axis (the discriminator that selects a variant:
58
+ a type tag, a key, a config value), the catalog's two questions in order: _is the set closed by
59
+ declaration, and does it stay closed?_ - a closed set is a legitimate `none` even though the branch is
60
+ edited; otherwise _count the edit sites the next legitimate variant costs_ in orchestration code.
61
+ More than one orchestration edit points at a pattern. Count the next plausible variant, never the
62
+ current one, and never pick the inline branch because it is shorter.
63
+ 3. **Decide: pattern or `none`.** Take the decision the _Structural forces_ table gives for the forces
64
+ present, after step 2: a variability force whose set passes question 1 records as `none`, and the
65
+ extension force splits on the catalog's Strategy-or-Registry tie-breaker. Read the chosen entry's
66
+ _Use when / Best practices / Avoid / Invariants_. Name every alternative you considered and why it lost; the **current shape of the code is
67
+ always one of them**. `none` is a first-class result and carries the same burden: the forces, the
68
+ alternatives, the structural reason the simpler shape wins, and the trigger that reopens the decision.
69
+ Never force a pattern to satisfy a gate; never pick `none` to save ceremony.
70
+ 4. **Resolve the framework and transpose.** Look up the guide in the table above; read the section
71
+ matching your pattern (plus its _Decision Matrix_) and apply the wiring it shows - DI, signals, stores,
72
+ layering. When the section's example does not fit (injected dependencies, no entry module to wire at),
73
+ keep its layering and say in `framework.transposition` what you adapted and why. No guide:
74
+ `transposition: null`, and the catalog's _Best practices_ are the wiring. For `none` there is no guide
75
+ section either: the catalog's `None` _Best practices_ are the wiring, and `transposition` is `null`.
76
+ 5. **Record the decision.** Write one design decision record per pattern-shaped site in the change
77
+ (shape below, schema in `references/`), **before the first implementation write**, and file it:
78
+ `ai-engineering-gate record --dimension design-patterns --stdin`. The gate stores it outside the
79
+ repository, hashes every path it `cites` and refuses a citation that does not exist, so a claim about
80
+ the current code cannot outlive the code. A force absorbed by the chosen pattern keeps its own value and
81
+ says so in its `site`. What the record commits to produce goes in `plans`; the reviewer is never
82
+ dispatched to notice a file that was never written. The gate checks shape, catalog membership and
83
+ referential integrity, never semantics; a refused record is fixed, not bypassed. Without the gate, check
84
+ the record against the schema yourself (ajv, or a hand check of every constraint) and keep its path: the
85
+ reviewer compares against it as written.
86
+ 6. **Implement per the transposition.** The first write inside the declared scope is allowed only once the
87
+ record is on file - `ai-engineering-gate can-write --path <path>` answers it, and a PreToolUse hook asks
88
+ for you. In your summary state the chain _forces → pattern (or `none`) → framework section followed_,
89
+ **and for each pattern name the concrete artifact** (file + symbol) that realizes it. Verify the artifact
90
+ is **distinct from unrelated layers** - e.g. a Command/use-case is its own injectable or exported
91
+ function, **not** a method merged into a store/facade - and that every invariant in the record is
92
+ observable in the code. File each planned artifact through
93
+ `ai-engineering-gate evidence append --dimension design-patterns --stdin` as you produce it.
94
+
95
+ ## Framework transposition - step 4
96
+
97
+ Every guide uses the **same section names** (Strategy, Registry, Factory, Command, Adapter / DTO Mapping,
98
+ Composition, Singleton / shared state), so open the section matching your pattern. For
99
+ everything around the pattern (front end: reactivity, async, forms; back end: transactions, validation,
100
+ runtime model), read that guide's _Cross-cutting <framework> practice_ section; for anything else, its
101
+ _Decision Matrix_ (bottom). Store choice per framework is in the _Bundled sources_ table above.
102
+
103
+ ## Design decision record - step 5
104
+
105
+ The values below are illustrative. Every fact in your record comes from the code or from the requirement
106
+ as stated, never from this example.
107
+
108
+ ```json
109
+ {
110
+ "dimension": "design-patterns",
111
+ "schemaVersion": "1.0.0",
112
+ "catalogVersion": "1.0.0",
113
+ "contractVersions": ["1.0.0", "1.1.0"],
114
+ "need": "quote shipping rates from a second carrier; the sales team wants to add carriers by market",
115
+ "scope": ["src/shipping/"],
116
+ "base": "origin/main",
117
+ "revision": { "number": 1, "previous": null, "reason": "first decision for this change" },
118
+ "cites": [
119
+ {
120
+ "path": "src/shipping/rate.service.ts",
121
+ "checkedAt": "2026-09-18",
122
+ "claim": "RateService.quote holds one body per carrier and both callers repeat the carrier branch",
123
+ "covers": ["variability", "extension"]
124
+ }
125
+ ],
126
+ "plans": [
127
+ { "path": "src/shipping/rate.strategy.ts", "role": "implementation" },
128
+ { "path": "src/shipping/rate.strategies.ts", "role": "implementation" }
129
+ ],
130
+ "forces": [
131
+ { "force": "variability", "value": "interchangeable-open", "site": "RateService.quote holds one body per carrier" },
132
+ { "force": "extension", "value": "many-sites", "site": "the ticket names a second carrier now and carriers per market later" },
133
+ { "force": "creation-policy", "value": "absent", "site": "each carrier client is a plain constructor call with no runtime decision" },
134
+ { "force": "boundary-mismatch", "value": "shape-and-semantics-differ", "site": "absorbed by strategy: each carrier's weight unit stays inside its own strategy" },
135
+ { "force": "reusable-action", "value": "absent", "site": "quote has no caller outside RateService" },
136
+ { "force": "composition", "value": "absent", "site": "one behavior per carrier, nothing is combined" },
137
+ { "force": "shared-lifecycle", "value": "per-consumer", "site": "the service and the clients hold configuration only" },
138
+ { "force": "cross-cutting-behavior", "value": "absent", "site": "no logging, retry or metrics requested" }
139
+ ],
140
+ "alternatives": [
141
+ "none, the current if/else on the carrier code - the set is not closed by declaration and a second market reopens it",
142
+ "registry - carriers are compile-time known with typed credentials; a Map lookup loses the completeness check",
143
+ "factory - createUpsStrategy(client) holds no creation decision, it is injection by closure"
144
+ ],
145
+ "decision": {
146
+ "pattern": "strategy",
147
+ "reason": "variability and extension on one axis, the carrier; RateService must depend on a contract, not on a client class",
148
+ "extensionCost": "today: 4 edit sites in RateService per carrier (CarrierCode, import, constructor, quote branch); with the map: 1 strategy module, 1 CarrierCode literal, 1 map entry, RateService untouched",
149
+ "reconsiderWhen": "a carrier arrives from configuration or a plugin at runtime - then Registry"
150
+ },
151
+ "framework": {
152
+ "name": "vanilla",
153
+ "transposition": "Strategy → typed lookup map resolved by key; adapted: strategies are factory closures over their injected client, the map is built by createRateStrategies because the tree has no entry module"
154
+ },
155
+ "artifacts": [
156
+ { "file": "src/shipping/rate.strategy.ts", "symbol": "RateStrategy" },
157
+ { "file": "src/shipping/rate.strategies.ts", "symbol": "createRateStrategies" }
158
+ ],
159
+ "invariants": [
160
+ "RateService depends on RateStrategy and on Record<CarrierCode, RateStrategy> only; it imports no client",
161
+ "a third carrier is added without editing rate.service.ts, and the build fails until its map entry exists",
162
+ "no switch, if or ternary on the carrier code remains under src/shipping"
163
+ ]
164
+ }
165
+ ```
166
+
167
+ - Each `forces` entry answers with a value from that force's set in the catalog's _Structural forces_ table,
168
+ plus the site that carries it. All eight are present; every force at its `absent` value is a legitimate
169
+ `none`.
170
+ - `cites` are the artifacts and sources the claims rest on, each with the date you checked it and what it
171
+ `covers` - a force name, a symbol, a finding. `plans` are the artifacts this record commits to produce.
172
+ - `decision.pattern` is one of `strategy`, `registry`, `factory`, `command`, `adapter`, `composition`,
173
+ `singleton`, `none`. A facade over a store records as `singleton`; UI state (signals) is framework
174
+ wiring, not a record.
175
+ - `framework.name` is `angular`, `react`, `vue`, `vanilla`, `quarkus`, `php`, or the detected stack when no
176
+ guide covers it (then `transposition` is `null`).
177
+ - `decision.extensionCost` is the step 2 count, today and with the decision - the reviewer re-counts it.
178
+ - `decision.reconsiderWhen` is allowed for any pattern and required for `none`. For `none`,
179
+ `framework.transposition` is `null` and `artifacts` may be empty.
180
+ - `invariants` are the catalog entry's _Invariants_, made concrete for this change - the reviewer checks
181
+ each one against the code.
182
+
183
+ ## Completion protocol
184
+
185
+ After implementation and the project's deterministic checks (tests, lint, typecheck):
186
+
187
+ 1. **Ask what is left.** `ai-engineering-gate status --full` names the next action and, once every applicable
188
+ dimension has its records and its planned evidence, prints the dispatch plan: which reviewer to launch and
189
+ the neutral brief to send it, rendered from the declaration alone.
190
+ 2. **Dispatch every reviewer in the plan in one turn.** Use the `design-pattern-reviewer` agent when the
191
+ harness defines one; otherwise a fresh general subagent. Send the brief the plan printed and nothing else:
192
+ **not** the pattern name, not your rationale, not your force analysis, not the record's content. The brief
193
+ carries the record's path; the reviewer opens it only after its blind matrix is frozen. The gate refuses a
194
+ review to your own identity, so this is enforced, not only asked.
195
+ 3. **The reviewer runs `review-design-patterns`:** expected design frozen first, then the record, then the
196
+ code, compared three ways. It opens its own window and files its own envelope.
197
+ 4. **Findings: correct or dispute, then dispatch a fresh review.** Corrections are batched - address every
198
+ pending finding of every dimension, then reopen all applicable reviews together on one state. An evidence
199
+ finding is closed by its remedy; a judgment finding by a record revision that `addresses` it. A new
200
+ dispatch, never a continued conversation with the same reviewer.
201
+ 5. **A finding you contest is neither fixed nor dismissed.** File
202
+ `ai-engineering-gate dispute --dimension design-patterns --stdin` with a pointer to counter-evidence, then
203
+ hand it to the user: only a command the user types can write the arbitration.
204
+ 6. **Complete only when `ai-engineering-gate can-stop` exits 0.** Any later change to code in scope moves the
205
+ source fingerprint and voids the verdict, which the status reports as `stale-source`.
206
+
207
+ Without the gate, dispatch the reviewer yourself with a brief carrying exactly what the plan would have
208
+ carried: the skill to run, the read-only rule, the scope and base, the requester's wording, the factual
209
+ constraints, the record paths and the undeclared changes. Nothing of your rationale.
210
+
211
+ ## Enforcement rules
212
+
213
+ - Steps 0-5 are **non-negotiable** before implementation code in pattern-shaped work. A declaration is the
214
+ proof the dimension was considered, and a record - pattern or `none` - the proof the evaluation happened.
215
+ - Never invent a pattern absent from the catalog. Never use `switch` / `constructor.name` / magic strings
216
+ **as the extension mechanism** where the catalog marks them _Avoid_; the exhaustive branch over a closed
217
+ set that the catalog's `None` entry prescribes is not that.
218
+ - Treat each pattern's _Avoid_ clause as a hard rule - e.g. do **not** fold a reusable Command into a
219
+ store/facade method; resolve plugins by key via a Registry, never `switch` on a type tag.
220
+ - Keep business logic framework-agnostic - only the transposition step (4) is framework-specific.
221
+ - The bundled references are the source of truth. If a project also has its own pattern docs, follow the
222
+ project doc for project-specific wiring, but the procedure above still applies.
223
+ - The completion protocol is part of the work: an implementation without a `SOUND` fresh review is not done.
224
+
225
+ ## Extending to a new framework
226
+
227
+ 1. Author `references/transpose-<framework>.md`, mirroring `references/transpose-angular.md` (front end) or
228
+ `references/transpose-quarkus.md` (back end): a _Decision Matrix_ plus one section per catalog pattern
229
+ with idiomatic wiring for that framework.
230
+ 2. Add one row to the _Target framework_ table above, pointing at the new guide.
231
+ 3. Leave `references/pattern-catalog.md` framework-agnostic and unchanged - it is shared across all targets.
@@ -0,0 +1,3 @@
1
+ interface:
2
+ display_name: "Transpose Design Patterns"
3
+ short_description: "Pick a pattern and transpose it to the framework"
@@ -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
+ }