@matthew-cochran/crossmatrix 0.0.0-stage → 0.3.1

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/package.json CHANGED
@@ -1,6 +1,48 @@
1
1
  {
2
2
  "name": "@matthew-cochran/crossmatrix",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.3.1",
4
+ "description": "npx launcher for the crossmatrix MCP server (multidimensional House of Quality): downloads the verified release binary for your platform, runs it over stdio, and ships the crossmatrix agent skills.",
5
+ "keywords": [
6
+ "mcp",
7
+ "model-context-protocol",
8
+ "crossmatrix",
9
+ "house-of-quality",
10
+ "qfd",
11
+ "crossmatrix-mcp"
12
+ ],
13
+ "license": "Apache-2.0",
14
+ "author": "Matthew Cochran <matthew@cochranweb.com>",
15
+ "homepage": "https://github.com/praxec/crossmatrix#readme",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/praxec/crossmatrix.git",
19
+ "directory": "npm"
20
+ },
21
+ "bugs": {
22
+ "url": "https://github.com/praxec/crossmatrix/issues"
23
+ },
24
+ "bin": {
25
+ "crossmatrix-mcp": "bin/crossmatrix-mcp.js"
26
+ },
27
+ "files": [
28
+ "bin/crossmatrix-mcp.js",
29
+ "lib/install.js",
30
+ "lib/postinstall.js",
31
+ "lib/sync-skills.js",
32
+ "skills/",
33
+ "README.md",
34
+ "LICENSE",
35
+ "NOTICE"
36
+ ],
37
+ "scripts": {
38
+ "postinstall": "node lib/postinstall.js",
39
+ "prepack": "node lib/sync-skills.js",
40
+ "test": "node --test test/*.test.mjs"
41
+ },
42
+ "engines": {
43
+ "node": ">=18"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ }
48
+ }
@@ -0,0 +1,120 @@
1
+ ---
2
+ name: crossmatrix
3
+ description: "Method reference for the crossmatrix-mcp tools (crossmatrix.command and crossmatrix.query): a generic, multidimensional House of Quality / weighted cross-reference matrix. Use when you build, fill or read any crossmatrix model, or when no narrower crossmatrix-* skill fits. Covers the Configuration / Dimensions / State split, observation-only writes (tokens plus evidence, never numbers), the driver loop (gaps.next, observe, analyze), scoped slices, optimistic versions and requestIds, links, and the committed .crossmatrix/ store."
4
+ ---
5
+
6
+ # crossmatrix
7
+
8
+ crossmatrix keeps a sparse, weighted cross-reference between **dimensions** (lists of
9
+ things: needs, characteristics, jobs, tests, risks, deliverables...). You make the
10
+ judgments; the engine does every number. Domain meaning lives in your configuration and
11
+ in these skills, never in the engine.
12
+
13
+ ## Three documents, linked by id@version
14
+
15
+ | Layer | Holds | Op family |
16
+ |---|---|---|
17
+ | Configuration | scales, relation declarations, contractions | `scale.declare`, `relation.declare`, `contraction.declare` |
18
+ | Dimensions | dimensions and their members (rosters, weights) | `dimension.register`, `members.sync`, `member.propose` |
19
+ | State | cells: observations on member pairs | `observe`, `evidence.attach`, `deprecate` |
20
+
21
+ Findings are computed on every read and are never stored.
22
+
23
+ ## The store: `.crossmatrix/` (commit it)
24
+
25
+ Run `crossmatrix-mcp init` once at the repo root. It creates `.crossmatrix/` with
26
+ `config/<configId>@<version>.json`, `dimensions/<dimensionSetId>@<version>.json`,
27
+ `state/<stateId>.json`, an append-only `audit/<stateId>.jsonl` (union-merged), a README and
28
+ a `.gitattributes`. Files are canonical JSON, so diffs are small. **Commit the folder.** Run
29
+ `crossmatrix-mcp check` after merges and in CI; it exits 1 with path-specific diagnostics.
30
+
31
+ The server picks its store from `CROSSMATRIX_DIR`, else the nearest `.crossmatrix/` above
32
+ its working directory, else a per-user data directory. Confirm `root` in `model.status`
33
+ before you write.
34
+
35
+ ## Calling the tools
36
+
37
+ Every write is one `crossmatrix.command`:
38
+
39
+ ```json
40
+ {"request": {"schemaVersion": "0.2.0", "requestId": "obs-0007", "modelId": "checkout",
41
+ "expectedVersion": 6, "actor": {"actorType": "llm", "persona": "observer"},
42
+ "op": {"kind": "observe", "relationId": "rel_need_char", "from": "need_fast",
43
+ "to": "char_latency", "observation": "strong", "provenanceClass": "inferred",
44
+ "evidence": [{"sourceRef": {"sourceRef": "docs/perf.md", "locator": "L10-L24"}}]}}}
45
+ ```
46
+
47
+ - `requestId` is an idempotency key: resending it replays the first answer.
48
+ - `expectedVersion` is the version you last saw. A `version_conflict` means someone else
49
+ wrote; re-read `model.status` and retry.
50
+ - Every response, including a rejection, carries `links`: the legal next ops. A rejection
51
+ carries `error.code`; fix the cause the code names, then follow a link.
52
+
53
+ Reads are `crossmatrix.query` with `{"request": {"query": {"kind": "...", ...}}}`.
54
+
55
+ ## Observation-only
56
+
57
+ - A cell value is a **token** from the relation's scale (`none|weak|moderate|strong` or
58
+ whatever the scale declares), never a number.
59
+ - Member importance is a `weightObservation` token from the dimension's `weightScale`. A
60
+ numeric `weight` is accepted only from a deterministic source with `sourceRef.system`
61
+ (for example an FMECA criticality import).
62
+ - `provenanceClass`: `normative` (a decision), `observed` (measured or seen), `inferred`
63
+ (your judgment; **evidence is required**). A `measuredValue` needs `observed` plus a
64
+ `sourceRef`.
65
+ - Support and opposition are separate relations (`supports` vs `opposes`, `mitigates` vs
66
+ `exposes`), so strong-for and strong-against never cancel; the engine reports net and
67
+ tension.
68
+
69
+ ## Build a model
70
+
71
+ 1. `model.open` with `stateId` (creates it if absent; reopens it after a restart).
72
+ 2. `scale.declare` your relationship scale and an importance scale.
73
+ 3. `dimension.register` each axis (`dimensionKind` is any snake_case word; set
74
+ `weightScale` on weighted axes), then `members.sync` its roster.
75
+ 4. `relation.declare` each pair of axes (`from == to` is a correlation roof), with a
76
+ `coveragePolicy` (`every_from`, `every_to`, `both`) when gaps must be filled.
77
+ 5. `contraction.declare` a chain when you need a derived A→C view through B.
78
+
79
+ ## The driver loop
80
+
81
+ Do not fill cells in bulk from memory. Let the engine pick:
82
+
83
+ 1. `gaps.next` → the most important uncovered members.
84
+ 2. Open the evidence, then `observe` one cell (token + rationale + evidence).
85
+ 3. Repeat until `gaps.next` is empty or the remaining gaps are knowingly `none`.
86
+ 4. `analyze.findings`, `conflicts`, `coverage`, `analyze.marginalize` for priorities.
87
+
88
+ ## Scoped reads
89
+
90
+ Never ask for the whole model when a slice answers the question: `describe` (structure),
91
+ `slice {relationId, from?, to?}`, `trace {member}`, `explain {relationId, from, to}`,
92
+ `coverage`, `gaps.orphans`. Use `export` only to hand the model to another tool, and
93
+ `report` for a human-readable House of Quality.
94
+
95
+ ## Large rosters, hierarchies and sources
96
+
97
+ - Sync rosters straight from their source (files, tickets, tests) with a per-member
98
+ `sourceRef {"uri": "file://src/lib.rs", "hash": "sha256:..."}`. `members.sync` handles
99
+ 10k+ members; re-sending an unchanged roster is a no-op (no new version).
100
+ - Model nesting (directory contains file, epic contains story) as a `contains`
101
+ self-relation, parent → child. Add `rollup: true` to `trace`, `slice`, `coverage`,
102
+ `gaps.next` or `analyze.*` to roll leaf observations up to ancestors.
103
+ - `trace {"member": "<id or uri>"}` and `slice {"relationId", "from"|"to": "<id or uri>"}`
104
+ work in both directions and page with `limit` / `cursor` (`nextCursor`).
105
+ - `stale {"hashes": {"<uri>": "<hash>"}}` (or `"readFiles": true` for `file://` sources
106
+ inside the project) returns the changed members and every cell that rests on them.
107
+
108
+ ## Changing your mind
109
+
110
+ Never delete. `deprecate {targetId, reason, supersededBy?}` retires a member or relation;
111
+ deprecated elements leave analyses but stay in history. A re-synced roster deprecates
112
+ members that disappeared from the source.
113
+
114
+ Narrower skills: [crossmatrix-hoq](../crossmatrix-hoq/SKILL.md),
115
+ [crossmatrix-jtbd](../crossmatrix-jtbd/SKILL.md),
116
+ [crossmatrix-competitive](../crossmatrix-competitive/SKILL.md),
117
+ [crossmatrix-test-coverage](../crossmatrix-test-coverage/SKILL.md),
118
+ [crossmatrix-risk](../crossmatrix-risk/SKILL.md),
119
+ [crossmatrix-prioritize](../crossmatrix-prioritize/SKILL.md),
120
+ [crossmatrix-review](../crossmatrix-review/SKILL.md).
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: crossmatrix-competitive
3
+ description: "Benchmark customer needs against competitors (including your own product) with crossmatrix-mcp and find the gaps worth winning: high-importance needs every competitor serves weakly. Use when the user asks for competitive benchmarking, a competitor matrix, a QFD planning matrix, or where to differentiate."
4
+ ---
5
+
6
+ # Needs × competitors
7
+
8
+ Read [crossmatrix](../crossmatrix/SKILL.md) first.
9
+
10
+ ## Model
11
+
12
+ - Reuse the weighted needs from a House of Quality ([crossmatrix-hoq](../crossmatrix-hoq/SKILL.md))
13
+ or register `dim_need` with `weightScale: "importance"`.
14
+ - `dimension.register {"dimensionId": "dim_competitor", "dimensionKind": "competitor"}` and
15
+ `members.sync` the competitors **and your own product** (`attributes.self: true`). Give
16
+ each a `sourceRef` to the page, review or teardown you assessed.
17
+ - `scale.declare` a performance scale, e.g. `absent|poor|fair|good|best` (0/1/3/6/9), with
18
+ rubric descriptions a second reviewer could apply the same way.
19
+ - `relation.declare {"relationId": "rel_need_competitor", "from": "dim_need", "to": "dim_competitor", "relationType": "satisfies", "scale": "performance", "coveragePolicy": "both"}`.
20
+
21
+ ## Fill
22
+
23
+ For each need and competitor, `observe` the performance token with evidence (screenshot
24
+ path, review URL, test log) and `provenanceClass: "observed"` when you saw it yourself,
25
+ `inferred` when you deduced it. Use `gaps.next` to keep the work in importance order.
26
+
27
+ ## Read
28
+
29
+ - `slice {"relationId": "rel_need_competitor", "from": "<need>"}`: one need across all
30
+ competitors.
31
+ - `analyze.marginalize {"relationId": "rel_need_competitor", "axis": "from"}`: competitors
32
+ ranked by importance-weighted performance (your product's rank is your position).
33
+ - Gaps worth winning: needs with high importance where **no** competitor is above `fair`.
34
+ Read them from `report {"relationId": "rel_need_competitor"}` (weights and grid in one
35
+ place) and list them with their evidence. Gaps where only you are weak are catch-up work.
36
+ - `coverage` shows needs or competitors you have not assessed yet.
37
+
38
+ Feed the gaps worth winning into [crossmatrix-prioritize](../crossmatrix-prioritize/SKILL.md)
39
+ as value drivers.
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: crossmatrix-hoq
3
+ description: "Build a classic House of Quality (QFD) with crossmatrix-mcp: weighted customer needs (WHATs) against engineering characteristics (HOWs), the correlation roof between characteristics, technical-importance ranking and targets. Use when the user asks for a House of Quality, QFD, needs-to-characteristics matrix, or which engineering characteristics matter most."
4
+ ---
5
+
6
+ # House of Quality with crossmatrix
7
+
8
+ Read [crossmatrix](../crossmatrix/SKILL.md) first for the envelope, observation-only rules
9
+ and the driver loop. Every op below is a `crossmatrix.command` `op`.
10
+
11
+ ## 1. Frame
12
+
13
+ - `model.open {"stateId": "<product>-hoq", "aspect": "hoq", "title": "<product> House of Quality"}`
14
+ - `scale.declare` the relationship scale (classic 0/1/3/9):
15
+
16
+ ```json
17
+ {"kind": "scale.declare", "scale": {"id": "qfd", "levels": [
18
+ {"observation": "none", "value": 0, "description": "No relationship."},
19
+ {"observation": "weak", "value": 1, "description": "Indirect or minor effect."},
20
+ {"observation": "moderate", "value": 3, "description": "Contributes, not the main lever."},
21
+ {"observation": "strong", "value": 9, "description": "Primary mechanism."}]}}
22
+ ```
23
+
24
+ - `scale.declare` an importance scale (`low` 1, `medium` 3, `high` 9) with rubric
25
+ descriptions the customer evidence can be checked against.
26
+
27
+ ## 2. Rooms
28
+
29
+ - `dimension.register {"dimensionId": "dim_need", "dimensionKind": "customer_requirement", "weightScale": "importance"}`
30
+ - `members.sync` the needs in the customer's words, each with `weightObservation` and a
31
+ `sourceRef` to the interview, survey or ticket it came from.
32
+ - `dimension.register {"dimensionId": "dim_char", "dimensionKind": "engineering_characteristic"}`
33
+ - `members.sync` measurable characteristics. Put the target and unit in `attributes`
34
+ (`{"target": "< 200", "unit": "ms"}`); the report prints a Targets table from them.
35
+
36
+ ## 3. Relations
37
+
38
+ - `relation.declare {"relationId": "rel_need_char", "from": "dim_need", "to": "dim_char", "relationType": "satisfies", "scale": "qfd", "coveragePolicy": "every_from"}`
39
+ (every need must be served by something).
40
+ - The roof is a self-relation on the characteristics. Use `supports` for synergies and
41
+ `opposes` for trade-offs, as two separate relations:
42
+ `relation.declare {"relationId": "roof_conflict", "from": "dim_char", "to": "dim_char", "relationType": "opposes", "scale": "qfd"}`.
43
+
44
+ ## 4. Fill (driver loop)
45
+
46
+ `gaps.next` → `observe` one cell with rationale and evidence → repeat. Observe the roof
47
+ only for pairs where a real trade-off or synergy exists. Do not fill `none` everywhere.
48
+
49
+ ## 5. Read the house
50
+
51
+ - Technical importance: `analyze.marginalize {"relationId": "rel_need_char", "axis": "from"}`
52
+ rolls the needs up into a ranked list of characteristics (weight × strength).
53
+ - Trade-offs: `conflicts` lists characteristics where opposition meets or beats support.
54
+ - Gaps: `coverage` with the `every_from` policy flags needs no characteristic serves.
55
+ - The full house: `report {"relationId": "rel_need_char"}` returns Markdown with weights,
56
+ the matrix, the roof, technical importance, targets, coverage and findings.
57
+
58
+ Hand the ranked characteristics to [crossmatrix-prioritize](../crossmatrix-prioritize/SKILL.md)
59
+ or to a planner, and finish with [crossmatrix-review](../crossmatrix-review/SKILL.md).
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: crossmatrix-jtbd
3
+ description: "Map jobs to be done against user segments and journey steps with crossmatrix-mcp, then contract the axes to see which journey steps carry the most weighted job importance per segment. Use when the user wants a JTBD map, a jobs-by-segment matrix, journey coverage of jobs, or to find under-served jobs and journey steps."
4
+ ---
5
+
6
+ # Jobs to be done × segments × journeys
7
+
8
+ Read [crossmatrix](../crossmatrix/SKILL.md) first. This is a three-axis model joined by a
9
+ contraction.
10
+
11
+ ## Axes
12
+
13
+ - `dimension.register {"dimensionId": "dim_segment", "dimensionKind": "user_segment", "weightScale": "importance"}`:
14
+ segments weighted by business importance (`weightObservation`), each with a `sourceRef`
15
+ to the research that defines it.
16
+ - `dimension.register {"dimensionId": "dim_job", "dimensionKind": "jtbd_step"}`: jobs
17
+ phrased as "When <situation>, I want to <motivation>, so I can <outcome>" in the
18
+ `label`.
19
+ - `dimension.register {"dimensionId": "dim_journey", "dimensionKind": "journey_step"}`:
20
+ the product's journey steps, in order (`attributes.order`).
21
+
22
+ ## Relations
23
+
24
+ - `rel_segment_job` (`dim_segment` → `dim_job`, `relationType: "supports"`, scale
25
+ `importance-of-job`): how much this segment needs this job done. Declare a scale such as
26
+ `none|nice|important|critical` (0/1/3/9) with rubric descriptions.
27
+ - `rel_job_journey` (`dim_job` → `dim_journey`, `relationType: "satisfies"`, scale `qfd`,
28
+ `coveragePolicy: "every_from"`): which journey steps get the job done.
29
+ - `contraction.declare {"contractionId": "ctr_segment_journey", "chain": ["rel_segment_job", "rel_job_journey"], "weightCombination": "product"}`.
30
+
31
+ ## Fill
32
+
33
+ Use the driver loop: `gaps.next`, then observe from interview notes or analytics, with
34
+ evidence. Leave a cell empty rather than guessing `none`.
35
+
36
+ ## Read
37
+
38
+ - `analyze.contract`: segment → journey-step findings, each with the path through the
39
+ jobs that carries the weight. The top rows are the steps to invest in for each segment.
40
+ - `analyze.marginalize {"relationId": "rel_job_journey", "axis": "from"}`: journey steps
41
+ ranked by how much job importance they serve.
42
+ - `coverage` / `gaps.orphans`: jobs that no journey step serves are the under-served jobs.
43
+ - `report {"relationId": "rel_job_journey"}` for the Markdown matrix.
44
+
45
+ Pairs with [crossmatrix-test-coverage](../crossmatrix-test-coverage/SKILL.md), which reuses
46
+ the journey steps, and with [crossmatrix-prioritize](../crossmatrix-prioritize/SKILL.md).
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: crossmatrix-prioritize
3
+ description: "Rank deliverables against weighted value drivers with crossmatrix-mcp and hand the ranked list to a planner such as cpm-planner. Use when the user asks what to build first, wants a value-weighted backlog, or needs a priority order derived from needs, gaps, risks or competitive gaps rather than gut feel."
4
+ ---
5
+
6
+ # Deliverables × value drivers
7
+
8
+ Read [crossmatrix](../crossmatrix/SKILL.md) first.
9
+
10
+ ## Model
11
+
12
+ - `dimension.register {"dimensionId": "dim_value", "dimensionKind": "business_metric", "weightScale": "importance"}`:
13
+ the value drivers (customer needs, gaps worth winning, risks to retire, revenue or cost
14
+ goals), each with a `weightObservation` and a `sourceRef` to where its importance comes
15
+ from. Reuse members from other crossmatrix models where they exist.
16
+ - `dimension.register {"dimensionId": "dim_deliverable", "dimensionKind": "deliverable"}`
17
+ and `members.sync` the candidate deliverables (epics, features, fixes). Put effort or
18
+ duration hints in `attributes`; the engine does not use them, the planner does.
19
+ - `scale.declare` a contribution scale (`none|weak|moderate|strong`, 0/1/3/9).
20
+ - `relation.declare {"relationId": "rel_value_deliverable", "from": "dim_value", "to": "dim_deliverable", "relationType": "satisfies", "scale": "contribution", "coveragePolicy": "every_from"}`.
21
+ - If a deliverable works against a driver (it adds risk, slows a goal), record that in a
22
+ separate `opposes` relation. Never lower the support token instead.
23
+
24
+ ## Fill
25
+
26
+ Use the driver loop: `gaps.next`, then observe the contribution with rationale.
27
+
28
+ ## Rank and hand off
29
+
30
+ 1. `analyze.marginalize {"relationId": "rel_value_deliverable", "axis": "from"}`: the
31
+ ranked deliverables (value-weighted score, highest first).
32
+ 2. `conflicts`: deliverables with strong opposition; decide or split them before
33
+ planning.
34
+ 3. `coverage`: value drivers no deliverable serves (`every_from` makes them violations).
35
+ 4. Hand the ranked list to the planner. For cpm-planner, keep the order as the priority,
36
+ carry each deliverable's id, and add durations and dependencies there. Do not invent
37
+ numeric scores for the planner; pass the rank and the engine's score.
38
+
39
+ Record the hand-off in the report: `report {"relationId": "rel_value_deliverable"}`.
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: crossmatrix-review
3
+ description: "Review and sign off a crossmatrix model before it is relied on: run crossmatrix-mcp check, validate, conflicts, stale and coverage, produce the Markdown report, and walk a sign-off checklist. Use when the user asks to review, audit, validate or sign off a House of Quality or any crossmatrix model, or before committing .crossmatrix/ changes."
4
+ ---
5
+
6
+ # Review and sign-off
7
+
8
+ Read [crossmatrix](../crossmatrix/SKILL.md) first. Review is read-only: it never writes
9
+ except to record fixes the reviewer approves.
10
+
11
+ ## 1. Integrity
12
+
13
+ - `crossmatrix-mcp check` at the repo root: exit 0, no diagnostics. Fix any path it names.
14
+ - `model.open {"stateId": "<id>"}`, then `model.status`: right `root` (the repo's
15
+ `.crossmatrix/`), expected version, counts.
16
+ - `validate` returns `validated: true`.
17
+
18
+ ## 2. Evidence
19
+
20
+ - `stale {"hashes": {...}}` with the current content hashes of every source you can
21
+ re-hash: each stale entry is a cell or weight whose source changed. Re-observe or
22
+ deprecate it.
23
+ - `slice` the most important rows and confirm each `inferred` observation has evidence
24
+ that still says what the rationale claims.
25
+
26
+ ## 3. Findings
27
+
28
+ - `conflicts`: every `conflict` (opposition ≥ support) needs a decision recorded in the
29
+ report or a design change.
30
+ - `analyze.findings`: read the contributors of each finding; they name the cells that
31
+ drive it.
32
+ - `coverage` / `gaps.orphans`: violations of a `coveragePolicy` must be closed or the
33
+ policy consciously relaxed.
34
+
35
+ ## 4. Report
36
+
37
+ `report {}` (whole aspect) or `report {"relationId": "..."}`. Attach it to the PR or the
38
+ review record. `export {"format": "model"}` gives the merged JSON for another tool.
39
+
40
+ ## Sign-off checklist
41
+
42
+ - [ ] `crossmatrix-mcp check` passes and `.crossmatrix/` is committed.
43
+ - [ ] Every weighted dimension has a weight on every active member, from a token or a
44
+ deterministic import.
45
+ - [ ] No unresolved `conflict` findings, or each has a recorded decision.
46
+ - [ ] No coverage violations, or each is accepted in writing.
47
+ - [ ] No stale references.
48
+ - [ ] Every `inferred` observation has evidence.
49
+ - [ ] The top of the technical-importance or priority ranking matches the team's
50
+ understanding, or the disagreement is explained.
51
+ - [ ] The report is attached to the review.
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: crossmatrix-risk
3
+ description: "Cross failure modes against components or requirements with crossmatrix-mcp: import FMECA criticality as deterministic weights, separate exposes from mitigates, and find unmitigated critical exposure. Pairs with fmeca-mcp. Use when the user wants a risk × component matrix, to tie an FMECA to the design, or to see which requirements carry the most failure exposure."
4
+ ---
5
+
6
+ # Failure modes × components / requirements
7
+
8
+ Read [crossmatrix](../crossmatrix/SKILL.md) first. Criticality comes from an FMECA (for
9
+ example fmeca-mcp `report.export`), never from you.
10
+
11
+ ## Model
12
+
13
+ - `dimension.register {"dimensionId": "dim_fail", "dimensionKind": "failure_mode", "system": "fmeca"}`.
14
+ - `members.sync` the failure modes with their **imported** criticality as `weight` and a
15
+ deterministic `sourceRef` (`{"system": "fmeca", "sourceRef": "<session>/<fm id>", "contentHash": "..."}`).
16
+ A numeric weight without `sourceRef.system` is rejected (`observation_only`).
17
+ - The design axis: components (`implementation_asset`) or requirements
18
+ (`customer_requirement`), synced from the repo or the requirements doc.
19
+ - Two relations, never one signed scale:
20
+ - `rel_comp_exposes_fail` (`relationType: "exposes"`): the component can produce the
21
+ failure mode.
22
+ - `rel_comp_mitigates_fail` (`relationType: "mitigates"`): the component prevents or
23
+ detects it.
24
+ - Optional chain to requirements: `contraction.declare {"contractionId": "ctr_req_exposure", "chain": ["rel_req_comp", "rel_comp_exposes_fail"]}`.
25
+
26
+ ## Fill
27
+
28
+ Observe exposure and mitigation separately, with evidence (code path, design doc, test).
29
+ Re-sync `dim_fail` whenever the FMECA changes; `stale` with the new hashes shows which
30
+ cells rest on outdated failure modes.
31
+
32
+ ## Read
33
+
34
+ - `analyze.contract`: requirement → failure exposure, with the components on the path.
35
+ - `conflicts` / `analyze.findings`: failure modes where exposure meets or exceeds
36
+ mitigation (`conflict`, severity `block`), most critical first.
37
+ - `analyze.marginalize {"relationId": "rel_comp_exposes_fail", "axis": "to"}`: components
38
+ ranked by criticality-weighted exposure.
39
+ - Send unmitigated critical exposures back to fmeca-mcp as actions, and their components
40
+ to [crossmatrix-prioritize](../crossmatrix-prioritize/SKILL.md).
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: crossmatrix-test-coverage
3
+ description: "Trace requirements or journey steps to tests with crossmatrix-mcp: a requirements × tests matrix whose orphans are untested requirements and orphan tests, weighted so the most important untested requirement comes first. Use when the user asks about test coverage of requirements, a traceability matrix, untested features, or which tests to write next."
4
+ ---
5
+
6
+ # Requirements / journeys × tests
7
+
8
+ Read [crossmatrix](../crossmatrix/SKILL.md) first.
9
+
10
+ ## Model
11
+
12
+ - The WHAT axis: requirements (`dimensionKind: "customer_requirement"`) or journey steps
13
+ (`journey_step`, reused from [crossmatrix-jtbd](../crossmatrix-jtbd/SKILL.md)), weighted
14
+ with `weightObservation`.
15
+ - `dimension.register {"dimensionId": "dim_test", "dimensionKind": "test_artifact"}`, then
16
+ `members.sync` the tests from the source: one member per test id, `sourceRef` pointing to
17
+ the file and test name (`{"sourceRef": "tests/checkout.rs", "locator": "fn pays_once"}`)
18
+ plus a `contentHash` of the file. Re-sync after test changes; removed tests are
19
+ deprecated, not deleted.
20
+ - `scale.declare` a verification scale, e.g. `none|smoke|partial|full` (0/1/3/9).
21
+ - `relation.declare {"relationId": "rel_req_test", "from": "dim_req", "to": "dim_test", "relationType": "verifies", "scale": "verification", "coveragePolicy": "every_from"}`.
22
+
23
+ ## Fill
24
+
25
+ Read each test, then `observe` how fully it verifies each requirement it touches, with
26
+ the assertion lines as evidence. Do not credit a test for a requirement it only executes
27
+ without asserting on.
28
+
29
+ ## Read
30
+
31
+ - `coverage`: with `every_from`, every uncovered requirement is a **violation** (untested).
32
+ - `gaps.next`: untested requirements ordered by importance; write tests in that order.
33
+ - `gaps.orphans`: tests that verify no requirement (dead or misfiled tests) and
34
+ requirements no test touches.
35
+ - `stale {"hashes": {"tests/checkout.rs": "<current hash>"}}`: which recorded test
36
+ evidence changed since it was observed; re-observe those cells.
37
+ - `report {"relationId": "rel_req_test"}` for the traceability matrix.
38
+
39
+ Finish with [crossmatrix-review](../crossmatrix-review/SKILL.md) before a release.