@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,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,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,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,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
|
+
}
|