Skip to main content

test-analyzer

analyzes test files and generates slice.json specifications for drift detection

Informations de source

Dépôt
dilgerma/embuilder-node
Dernière activité de la source
28 février 2026 à 06:49
Langue détectée de SKILL.md
anglais
Étoiles
5
Forks
1

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Explorateur de fichiers
4 fichiers

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
test-analyzer
description
analyzes test files and generates slice.json specifications for drift detection
## Overview Analyzes test files (`*.test.ts`) to generate `code-slice.json` specification files. These capture behavioral contracts in given/when/then form, enabling drift detection between tests and `slice.json` design documentation. **CRITICAL:** This skill NEVER touches existing `slice.json` files. It only creates/deletes `code-slice.json` files. ## Purpose - Extract test specifications from existing test code - Generate `code-slice.json` files matching the format of `slice.json` in `.slices/` directory - Enable drift detection by comparing `code-slice.json` (what tests say) with `slice.json` (design documentation) - **NEVER modify or delete existing `slice.json` files** - only work with `code-slice.json` ## Input - Path to a test file (e.g., `ActivateShift.test.ts` or `OnlineReservationStatus.test.ts`) - Test file must follow given/when/then pattern using: - `DeciderSpecification.for()` for state-change slices - `PostgreSQLProjectionSpec.for()` for state-view slices ## Output Format **File:** `.slices/<Context>/<folder>/code-slice.json` (same directory as `slice.json`) Contains: ```json { "id": "<slice-id-from-slice.json>", "title": "<slice-title-from-slice.json>", "specifications": [...] } ``` - MUST include `id` and `title` fields copied from the corresponding `slice.json` file - NO commands, events, readmodels, or other slice metadata (only id, title, and specifications) - Each specification has complete nested objects with fields, NOT simple strings - See [specification-format.md](examples/specification-format.md) for detailed structure ### Specifications Array Content Include ALL test cases that are: - NOT already in the `specifications` array of existing `slice.json` - All given/when/then scenarios from tests (happy path, edge cases, error scenarios) - Behavioral contracts being tested - NO implementation details, only specifications **IMPORTANT:** Even if a command/event is defined in slice.json's commands/events arrays, if the test specification is NOT in the specifications array, it MUST be included in code-slice.json ### Specification Structure Each specification includes: - `vertical`: false - `id`: unique generated ID - `sliceName`: slice title - `type`: COMMAND, EVENT, READMODEL or SPEC_ERROR' - `title`: spec description from test - `given`: array of event objects (preconditions) - STATE_CHANGE: events that happened before - STATE_VIEW: events to be projected - id is the same as the original elements id - `when`: command object OR empty array - STATE_CHANGE: single command object (the action being tested) - STATE_VIEW: empty array `[]` (events are in `given` for projections) - id is the same as the original elements id - `then`: array of event/error/readmodel objects - Can be events (what happened as a result, type SPEC_EVENT) - Can be errors (SPEC_ERROR type) - Can be readmodel assertions (SPEC_READMODEL type for projections) - id is the same as the original elements id - `comments`: empty array - `linkedId`: MUST be the same as the specification's `id` field (self-reference) ## Supported Slice Types ### 1. STATE_CHANGE Slices Uses `DeciderSpecification.for()` with decide/evolve pattern. ```typescript const given = DeciderSpecification.for({ decide, evolve, initialState: ... }); it('spec: description', () => { given([...events]) .when(command) .then([...events]) }); ``` **See:** [state-change-example.md](examples/state-change-example.md) ### 2. STATE_VIEW Slices Uses `PostgreSQLProjectionSpec.for()` for projections/read models. ```typescript given = PostgreSQLProjectionSpec.for({ projection: SomeProjection, connectionString, }); it('spec: description', () => { await given([...events]) // Events are in given() for projections .when([]) // when() is always empty for projections .then(assertReadModel); }); ``` **IMPORTANT for STATE_VIEW:** - Events are placed in `given` array (the events to project) - `when` is always empty array (no command in projections) - `then` contains readmodel assertions **See:** [state-view-example.md](examples/state-view-example.md) ## Analysis Process ### Step 1: Identify Slice Type 1. Read the test file 2. Look for `DeciderSpecification.for()` → STATE_CHANGE 3. Look for `PostgreSQLProjectionSpec.for()` → STATE_VIEW 4. Extract slice name from describe block or file name ### Step 2: Extract Test Specifications For each `it('spec: ...', ...)` test: 1. Extract specification description from test name 2. Identify given events (preconditions) 3. Identify when command/events (action) 4. Identify then events/assertions (postconditions) 5. Map to given/when/then structure 6. **Compare with existing slice.json:** - Check if specification already exists in `slice.json` specifications array - If exact match found in specifications array → SKIP - If NOT in specifications array → INCLUDE (even if command/event element exists) ### Step 3: Extract Element Definitions #### For STATE_CHANGE: - **Commands:** Extract from `when()` calls with fields and types - **Events:** Extract from `given()` and `then()` calls with fields and types - **Dependencies:** Commands → Events (OUTBOUND), Events → Commands (INBOUND) #### For STATE_VIEW: - **Events:** Extract from `given()` calls (events to be projected) - **IMPORTANT:** `when()` is always empty for STATE_VIEW slices - **Read Model:** Extract from projection name and assertion logic in `then()` - **Dependencies:** Events → Read Model (OUTBOUND) ### Step 4: Build Specifications Array - Include ALL test cases NOT in `slice.json` specifications array - This includes happy path scenarios, error scenarios, edge cases, and alternative flows - Format as given/when/then specifications with full field definitions See [specification-format.md](examples/specification-format.md) for complete format details. ## Slice Name and Context Detection **Slice Name:** - Extract from test file name: `<SliceName>.test.ts` → `<SliceName>` - Example: `ActivateShift.test.ts` → "ActivateShift" **Context:** - If metadata contains `restaurantId` or `locationId` → "Restaurant Management" - Otherwise → extract from aggregate name or default to "General" **Using `.slices/index.json` for lookup:** 1. Read `.slices/index.json` to find correct folder 2. Match on slice title (case-insensitive) 3. Extract `folder` and `context` from matching entry 4. Construct path: `.slices/<context>/<folder>/slice.json` ## Implementation Steps When invoked with a test file path: 1. **Read and parse the test file** - Use Read tool to load file - Identify testing framework pattern 2. **Extract slice metadata** - Parse describe block for slice name - Determine slice type (DeciderSpecification = STATE_CHANGE, PostgreSQLProjectionSpec = STATE_VIEW) - Extract context from metadata fields - Infer slice name from file path 3. **Check for existing files** - Lookup `slice.json` location via `.slices/index.json` - Read `slice.json` if found for comparison - Extract `id` and `title` fields from `slice.json` (required for code-slice.json) - Track which elements already exist in design documentation 4. **Analyze test cases** - Find all `it('spec: ...')` test cases - Extract given/when/then structures - Compare with existing `slice.json` 5. **Build specifications array** - Include ALL test specifications NOT already in `slice.json` specifications array - Format with complete field structures (see [specification-format.md](examples/specification-format.md)) - **CRITICAL: Look up real IDs from slice.json for linkedId fields:** - For each command in `when`: Find matching command in `slice.json` → `commands` array by title - For each event in `given`/`then`: - First try `slice.json` → `events` array by title - For STATE_VIEW: If not found, search in `readmodels` → `dependencies` array (INBOUND events) - Use the `id` field from the matched element/dependency as the `linkedId` - For each readmodel assertion in `then`: Find matching readmodel in `slice.json` → `readmodels` array by title - If no match found, use `null` (indicates new element not in design documentation) - For errors (SPEC_ERROR): Do NOT include `linkedId` field (errors don't exist in slice.json) 6. **Decision: Generate, Update, or Remove code-slice.json** - **Compare test specifications with `slice.json` specifications array** - **All test specs in `slice.json` specifications:** Remove/don't create `code-slice.json` (no drift) - **Test specs NOT in `slice.json` specifications:** Generate/update `code-slice.json` with: - Write to `.slices/<Context>/<folder>/code-slice.json` (same directory as `slice.json`) - `id` field copied from `slice.json` - `title` field copied from `slice.json` - `specifications` array with missing specifications - **NEVER touch `slice.json`** - it's read-only design documentation 7. **Validate output** - Ensure JSON is valid - Verify all required fields present - Check structure matches reference format ## Field Type Mapping Infer TypeScript/JSON types from test data: - String values → `"String"` - UUID format → `"UUID"` - Date strings (ISO format) → `"Date"` or `"DateTime"` - Numbers with decimals → `"Decimal"` - Whole numbers → `"Integer"` - Boolean values → `"Boolean"` - Arrays → `"List"` with element type ## ID Generation **CRITICAL: LinkedId Fields Must Use Real IDs from slice.json** - **linkedId in given/when/then elements**: MUST use the actual element IDs from slice.json - Look up command IDs in `slice.json` → `commands` array → `id` field - Look up event IDs in `slice.json` → `events` array → `id` field - **For STATE_VIEW slices**: Events may not be in the `events` array if they're from other slices - Instead, look in `readmodels` → `dependencies` array for entries with `type: "INBOUND"` and `elementType: "EVENT"` - Use the `id` field from the dependency entry - Look up readmodel IDs in `slice.json` → `readmodels` array → `id` field - If element not found in slice.json, use `null` (will be identified as drift) - **Other ID fields**: Generate unique IDs for specification containers - Specification `id`: Generate unique ID (e.g., `"GENERATED-SPEC-<counter>"`) - Element wrapper `id`: Generate unique ID for each given/when/then element - These are just structural IDs, not the element's identity **Example from slice.json:** ```json { "commands": [ { "id": "3458764657124632223", // ← Use THIS for linkedId "title": "Activate Shift", ... } ], "events": [ { "id": "3458764657005669318", // ← Use THIS for linkedId "title": "Shift activated", ... } ] } ``` **Example in code-slice.json:** ```json { "id": "GENERATED-SPEC-001", // ← Specification ID "linkedId": "GENERATED-SPEC-001", // ← MUST match specification id "when": [ { "title": "Activate Shift", "id": "GENERATED-SPEC-CMD-1", // ← Generated wrapper ID "linkedId": "3458764657124632223" // ← MUST be real command ID from slice.json } ] } ``` **Summary of linkedId usage:** 1. **Specification level**: `specification.linkedId` = `specification.id` (self-reference) 2. **Element level (given/when/then)**: - Commands: `element.linkedId` = real ID from `slice.json` → `commands` array - Events: `element.linkedId` = real ID from `slice.json` → `events` array OR `readmodels` → `dependencies` (for STATE_VIEW) - Readmodels: `element.linkedId` = real ID from `slice.json` → `readmodels` array 3. **Error elements (SPEC_ERROR)**: No `linkedId` field (errors don't exist in slice.json) ## Usage Invoke with any test file path: **State-change slices:** ``` /test-analyzer src/slices/ActivateShift/ActivateShift.test.ts /test-analyzer src/slices/PlanVacation/PlanVacation.test.ts ``` **State-view slices (projections):** ``` /test-analyzer src/slices/ActivatedOnlineReservations/OnlineReservationStatus.test.ts /test-analyzer src/slices/ActiveShifts/ActiveShifts.test.ts ``` The skill automatically: - Detects slice name from file path - Determines if STATE_CHANGE or STATE_VIEW - Finds corresponding `.slices/` documentation - Generates `code-slice.json` in same directory as test file ## Drift Detection Strategy 1. **Compare test specifications with `slice.json` specifications array** 2. **Extract specifications NOT in `slice.json` specifications array:** - Happy path scenarios not in specifications - Error scenarios (thenThrows) not in specifications - Edge cases not in specifications - Alternative flows not in specifications - **Note:** Even if command/event elements exist in slice.json, if the specification (given/when/then) is not in the specifications array, include it 3. **Actions on `code-slice.json`:** - All test specs in `slice.json` specifications → Remove/don't create `code-slice.json` (no drift) - Test specs NOT in `slice.json` specifications → Generate/update `code-slice.json` - No `slice.json` found → Generate `code-slice.json` with all test specifications **Key Principle:** - `slice.json` = design documentation (source of truth, READ ONLY) - `code-slice.json` = test-derived specifications (generated/deleted by this skill) - Drift = test specifications that are NOT in slice.json specifications array ## File Locations - **Test file:** `src/slices/<SliceName>/<SliceName>.test.ts` - **Output:** `.slices/<Context>/<folder>/code-slice.json` (same directory as slice.json)
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub