AI adapter — designs ONE candidate Agreed Solution Description (architecture + UX folded) from a clarified `# Discovery` section. Called N-parallel by `/solution-design` orchestrator; also invokable by `/plan` Slice I fallback or standalone CLI. Stateless — never writes `_thought`.
Resources
2Install
npx skillscat add dmitrydf/q/solution-designer Install via the SkillsCat registry.
/solution-designer — Solution Design Adapter
Design one candidate Agreed Solution Description (architecture + UX, folded) from a clarified # Discovery section. The orchestrator (B3) calls N parallel instances of this skill, ranks the candidates with an independent Opus selector, and the user picks one. This skill produces a single candidate — it does not select, compare, or persist.
This skill is an AI adapter — "Judgment only, behind a port" (per ~/.claude/rules/code_first_architecture.md §AI Adapter, lines 81–102). It receives a structured input, returns a structured output, and has no state of its own.
Design self-checks use the Cockburn Six Design Tests (<KL>/Development/Sources/Books/simplifying-software-design-cockburn/simplifying-software-design-cockburn.md §0.3, lines 69–80). The Abstraction Test and Responsibility Alignment Test are hard-gate booleans in the output schema; the other four are advisory notes (simplifying-software-design-cockburn.md:79 — "experienced designers invent decently robust designs by paying close attention to the first two tests").
When to Use
Activate when:
- Called by
/solution-design(Phase 2) orchestrator (B3) as one of N parallel candidate producers — the primary invocation surface. - Called by
/plan(Phase 3, Slice I) as the inline fallback when Phase 2 was skipped — same I/O contract, different caller. - Called directly via the standalone CLI surface
/solution-designer --discovery <path-or-section>for one-off iteration on a fixture or a single Discovery.
In all three surfaces the skill consumes the same JSON-shaped input and emits the same JSON-shaped output. The skill does not decide whether Phase 2 should run at all (that decision lives in B3) — when invoked, it always executes.
The skill is pure judgment. No tools are required besides Read (for the Cockburn citation source and the optional Discovery file).
Invariants
The skill MUST obey the following invariants. They are non-negotiable and any caller that needs different behavior must wrap this skill, not modify it.
Stateless. The skill MUST NOT write
_thoughtfiles. It MUST NOT mutate phase state. It MUST NOT callpre_plan_gates.pywrite-side commands (phase-start,phase-stop,register-session-todo, etc.). The orchestrator (B3) owns all persistence. (C5 of S-H-v2-A;~/.claude/rules/code_first_architecture.md:81–102AI-adapter pattern.)Producer-never-verifies (external). This skill does not fact-check its own output. The Cockburn-gate self-check inside Step 4 of Execution is a self-report, not a verification — the booleans are emitted in the output, and the orchestrator's independent Opus selector + the user's pick are the actual quality gate. (
~/.claude/rules/code_first_architecture.md:112"Producer never verifies its own output.")UX + architecture folded for v1. A single invocation produces one candidate that covers both architecture and UX decisions. Do not split into separate UX-only or architecture-only runs. (Cockburn
simplifying-software-design-cockburn.md:206— "start fat, split only as needed.")Discovery is the only grounding surface. Every decision in the candidate MUST cite which locked
# Discoveryfield excerpt grounded it. The skill MUST NOT invent decisions that have no anchor in Discovery; it MUST NOT consult the codebase, prior plans, or external sources beyond what is passed in as input.One candidate per invocation. Do not emit multiple alternatives in a single run. The orchestrator achieves N alternatives by running N parallel invocations (each with different
model/design_context_override).
Input Contract
Schema
{
"guiding_policy": "<markdown body of the locked `## Guiding Policy` field from `# Discovery`>",
"desired_outcome": "<markdown body of the locked `## Desired Outcome` field from `# Discovery`>",
"desired_solution": "<markdown body of the locked `## Desired Solution` field from `# Discovery`>",
"metrics": "<markdown body of the locked `## Metrics` field, including the OMTM line>",
"discovery_src_hash": "<12-hex SHA-256 prefix of the four locked field bodies — the `_discovery_locked_fields_hash` from pre_plan_gates.py>",
"design_context_override": "<optional free-text string injected by the orchestrator to vary candidates (e.g. 'explore a synchronous design', 'optimize for fewest moving parts')>",
"model": "sonnet" | "opus"
}Field rules
| Field | Required | Source / Semantics |
|---|---|---|
guiding_policy |
yes | Locked ## Guiding Policy body verbatim from # Discovery. The list of locked fields is DISCOVERY_LOCKED_FIELDS in ${KIT_HOOKS_DIR}/pre_plan_gates.py:153-158. |
desired_outcome |
yes | Locked ## Desired Outcome body verbatim. |
desired_solution |
yes | Locked ## Desired Solution body verbatim. |
metrics |
yes | Locked ## Metrics body verbatim. The OMTM sub-field inside ## Metrics is what pre_plan_gates.py actually locks (see the inline comment at pre_plan_gates.py:157). |
discovery_src_hash |
yes | The 12-hex SHA-256 prefix of the concatenated bodies of the four locked fields, computed by _discovery_locked_fields_hash in pre_plan_gates.py:178-189. Used by the caller / orchestrator to detect stale invocations; this skill echoes it back unchanged in the output as provenance. |
design_context_override |
no | Free-text string the orchestrator passes to vary one candidate against another. The skill incorporates it as additional steering but it does NOT override locked Discovery — locked fields always take precedence. |
model |
no (default "sonnet") |
Enum "sonnet" | "opus". Selects which Claude model the skill runs under for this one candidate. The orchestrator's per-run model-mix decision (e.g. --number-of-sonnet-agents N --number-of-opus-agents N) passes through to per-candidate model selection. Default is sonnet. |
Deterministic errors
If any required locked field is missing, empty, or whitespace-only, the skill MUST return an error result without producing a candidate. Errors are deterministic — the skill does not try to fill in the missing field.
{
"error": "<one of: guiding_policy required, desired_outcome required, desired_solution required, metrics required, discovery_src_hash required>",
"agreed_solution_description": null,
"architecture_decisions": [],
"ux_decisions": [],
"cockburn_gate_results": {},
"confidence": null,
"assumptions": [],
"discovery_anchors": [],
"model_used": "<echo of input.model or default>"
}If metrics is present but contains no OMTM line, return error: "metrics field has no OMTM line — run /clarification step 8 first". The OMTM line is the locked sub-field per pre_plan_gates.py:157.
If a locked field is present but malformed in a way the skill can detect (e.g. it is empty markdown, or a placeholder like TODO), return error: "<field> is empty or placeholder; re-run /clarification --from <thought-path>".
Output Contract
Schema
{
"agreed_solution_description": "<markdown body, plain business words first, technical detail in a follow-up paragraph>",
"architecture_decisions": [
{
"area": "<short noun phrase, e.g. 'persistence', 'concurrency model', 'message format'>",
"choice": "<the chosen approach, in plain business words first>",
"rationale": "<one to three sentences: why this choice fits the Discovery>",
"discovery_anchor": "<verbatim excerpt from one of the 4 locked Discovery fields that grounds this choice>"
}
],
"ux_decisions": [
{
"area": "<short noun phrase, e.g. 'entry surface', 'feedback timing', 'failure-recovery affordance'>",
"choice": "<the chosen UX approach>",
"rationale": "<why this choice fits the Discovery>",
"discovery_anchor": "<verbatim excerpt from a locked Discovery field>"
}
],
"cockburn_gate_results": {
"abstraction_test": {
"passed": true,
"reasoning": "<one to three sentences explaining the test against this candidate>"
},
"responsibility_alignment_test": {
"passed": true,
"reasoning": "<one to three sentences>"
},
"evolution_test": {
"notes": "<advisory: what changes are likely, and how many components each would touch>"
},
"communications_patterns_test": {
"notes": "<advisory: do communication paths form cycles or back-edges>"
},
"data_connectedness_test": {
"notes": "<advisory: does each component have the data it needs from input>"
},
"data_variations_test": {
"notes": "<advisory: handling of edge data shapes, first-run, empty input>"
}
},
"confidence": "low" | "medium" | "high",
"assumptions": [
"<one assumption made about the Discovery or its context, in plain words>"
],
"discovery_anchors": [
{
"field": "guiding_policy" | "desired_outcome" | "desired_solution" | "metrics",
"excerpt": "<verbatim excerpt that was used as grounding somewhere in the candidate>"
}
],
"model_used": "sonnet" | "opus"
}Field rules
agreed_solution_description: Markdown body. Plain business words first; technical detail follows in a separate paragraph (~/.claude/rules/plan-gates.mdGate 0a OQ19). A reader without codebase context must understand the operational change the candidate proposes from the first paragraph alone.architecture_decisions/ux_decisions: Each is a list. Each item has all four sub-fields. If the candidate has no UX-relevant choices (e.g. a backend-only slice), emit"ux_decisions": []— do NOT fabricate decisions. Same rule applies toarchitecture_decisionsfor a UX-only candidate.cockburn_gate_results.abstraction_test.passedandcockburn_gate_results.responsibility_alignment_test.passedare hard-gate booleans. A candidate with either set tofalseis emitted anyway (the orchestrator's selector may rank it low or surface it as a degraded candidate; an all-FAIL emission is informative because it suggests the Discovery itself is malformed).confidence:"low"|"medium"|"high". Low if either hard gate failed, or if more than 2 assumptions were required, or if more than half of decisions lack a verbatim Discovery anchor.assumptions: Each entry names one judgment call the skill made that was not directly grounded in Discovery. Empty list is acceptable. Do not hide assumptions.discovery_anchors: Aggregate list of the verbatim excerpts the skill leaned on. EachexcerptMUST be a verbatim substring of the corresponding input field — no paraphrase, no synthesis.model_used: Echoes backinput.model(or the default"sonnet"). Provenance for the orchestrator's selector when ranking candidates that came from different models.
All required fields must be non-empty on a successful return. On error, the skill returns the error envelope from the Input Contract section.
Execution
Five internal steps. The skill runs them in order; no step is skipped.
Step 1 — Load and validate
Read the input fields. If any required field is missing, empty, or whitespace-only, return the deterministic error envelope (see Input Contract → Deterministic errors). If metrics lacks an OMTM line, return the corresponding error. If a field is a placeholder (TODO, empty body), return the corresponding error. The skill MUST NOT attempt to design over a missing or malformed anchor.
If a design_context_override is present, retain it as steering. Locked Discovery fields always take precedence over the override — the override may narrow the design space but it MUST NOT contradict a locked field.
Step 2 — Restate the locked fields as anchors
Read the four locked-field bodies verbatim. Keep them in working memory as the only grounding surface for the rest of the run. This is the equivalent of the Cagan-style "quote relevant parts before answering" pattern for long-context tasks — the skill anchors itself before drafting.
Step 3 — Draft the candidate
Produce a single candidate that covers both architecture and UX (folded per Cockburn "start fat"). For every decision in the candidate:
- Name the area (short noun phrase).
- State the choice in plain business words first.
- Write the rationale in one to three sentences.
- Pick a verbatim excerpt from one of the four locked Discovery fields that grounds this choice. If no verbatim excerpt fits, the decision is ungrounded — either revise the choice so it traces to Discovery, or record it as an explicit assumption (in
assumptions[]) instead of as a grounded decision.
Draft agreed_solution_description in plain business words first: a reader without codebase context must grasp the operational change from the first paragraph alone (~/.claude/rules/plan-gates.md Gate 0a OQ19). Technical detail (file paths, function names, schema details) belongs in a follow-up paragraph or in the per-decision rationale fields, not in the opening paragraph.
If the input represents a backend-only slice, emit ux_decisions: []. If it represents a UX-only slice, emit architecture_decisions: []. Do NOT fabricate decisions to fill the empty list.
Step 4 — Self-apply the Cockburn Six Design Tests
Apply each test against the drafted candidate. The first two are hard gates (booleans + reasoning); the other four are advisory notes.
| Test | Apply by asking |
|---|---|
Abstraction Test (Cockburn :72) |
Do the names of the components / areas in the candidate convey what each does? Are they recognizable to experts in the domain? |
Responsibility Alignment Test (Cockburn :73) |
For each named component / area, do the name, the responsibility, and the data + functions it owns align? |
Evolution Test (Cockburn :74) |
If a likely future change happens (orchestrator changes, Discovery format changes, UX is split off later), how many components in this candidate have to change? Single-locus is good. |
Communications Patterns Test (Cockburn :75) |
Do the components talk to each other in cycles or back-edges? One-way flow with a leaf node is good. |
Data Connectedness Test (Cockburn :76) |
Does each component have all the data it needs from its inputs, or does it have to reach out to fetch state? |
Data Variations Test (Cockburn :77) |
How does the candidate handle edge data shapes — empty inputs, first run, malformed but valid-looking data? |
Refine-once-on-FAIL rule. If either hard gate (abstraction_test or responsibility_alignment_test) fails, attempt one focused refinement of the candidate that targets the failing gate (e.g., rename a misleading component, or split a multi-responsibility node into two). Re-apply the failing test. Then emit with the resulting results regardless of pass/fail. Do not loop. (Editorial — chosen to prevent in-skill iteration loops while still giving the skill a chance to fix obvious self-detected misalignment.)
Step 5 — Emit structured output
Assemble the output JSON per the Output Contract schema. Populate discovery_anchors[] by aggregating the verbatim excerpts used across architecture_decisions[].discovery_anchor and ux_decisions[].discovery_anchor. Set confidence:
low— either hard gate failed (even after the one refinement), ORassumptionshas more than 2 entries, OR more than half the decisions lack a verbatimdiscovery_anchor.medium— both hard gates passed AND assumptions ≤ 2 AND at least half the decisions have a verbatim anchor.high— both hard gates passed AND assumptions ≤ 1 AND every decision has a verbatim anchor.
Echo model_used from input.model (or the default). Return.
Eval Harness
This skill is the first in the platform to ship a Python eval companion (eval.py). The harness is hybrid by design:
| Layer | Mechanism | Lives in |
|---|---|---|
| Deterministic checks | Python script — schema validation, anchor-coverage ratio, SHA-256 change-impact diff | ~/.claude/skills/solution-designer/eval.py |
| Judgment-quality eval | /double-check invocations against fixtures |
Caller's responsibility (not bundled into eval.py) |
The separation reflects Cockburn responsibility alignment — schema validation and judgment evaluation are distinct responsibilities and live in distinct components. It also preserves producer-never-verifies: AI-based eval inside the skill would be self-verification; AI-based eval lives in a separate context via /double-check.
Fixtures
~/.claude/skills/solution-designer/fixtures/ holds 3–5 canonical input fixtures drawn from real shipped slices (or hypothetical-but-realistic UX-heavy inputs). Each fixture is a JSON file conforming to the Input Contract schema.
Starter fixtures (shipped with v0.1):
| File | Source slice / shape |
|---|---|
sample-slice-A-walking-skeleton.json |
Backend-heavy phase-state authority; UX surface is the CLI. |
sample-slice-C-todo-mgmt.json |
Code-enforcement slice; UX surface is the todo.py CLI + TODO.md format. |
sample-ux-heavy-hypothetical.json |
Hypothetical UX-heavy slice (e.g. a wizard with explicit user choices). Used to exercise the ux_decisions[] path. |
Running the deterministic eval
# Validate one fixture + a captured skill output against the schema, and diff against a baseline:
python3 ~/.claude/skills/solution-designer/eval.py \
--fixture ~/.claude/skills/solution-designer/fixtures/sample-slice-A-walking-skeleton.json \
--skill-output /tmp/run-2026-05-22.json \
--baseline /tmp/run-2026-05-21.jsonThe script emits a JSON report to stdout with:
schema_valid: true | falseper fixture (deterministic).anchor_coverage_ratio:(decisions with non-empty discovery_anchor) / (total decisions)— a regex-based metric over the captured output.change_impact:{sha256: "<hex>", diff: "<unified-diff body>" | null}. On first run with no baseline, emitsbaseline_absent: trueand no diff.
Re-running with an unchanged fixture and an unchanged prompt body should yield identical SHA-256 and an empty diff. Non-zero diff on an unchanged run is itself a regression signal (the skill is non-deterministic).
Running judgment-quality eval
Judgment-quality evaluation runs through /double-check, separately from eval.py. Suggested form:
/double-check --class gate --rounds 1 \
--against "<solution-designer output for fixture X>; discovery-grounding + Cockburn-gate validity per ~/.claude/skills/solution-designer/SKILL.md §Execution Step 4"The checker receives the fixture + the skill output as the claim package and verifies discovery-grounding (every decision cites a verbatim anchor) and Cockburn-gate validity (the hard-gate booleans match the candidate's actual abstraction / responsibility shape).
Invocation Surfaces
The skill is invokable from three surfaces. All three pass the same Input Contract JSON and consume the same Output Contract JSON. The surface layer is the only changing locus when adding a new caller (Cockburn Evolution Test — single-locus change).
Surface 1 — /solution-design orchestrator (B3, primary)
The Phase 2 orchestrator (B3) invokes this skill via the Agent tool, N times in parallel, with different model and/or design_context_override per invocation. The locked Discovery is inlined into the prompt by the orchestrator (B3 is the only writer; B1 stays stateless). The choice of subagent_type (e.g. general-purpose vs. a dedicated type) is decided at B3 Planning — B1 does not depend on it.
Surface 2 — /plan Slice I fallback
When the user runs /plan directly and Phase 2 was skipped, Slice I (the /plan enhancement) invokes this skill inline as a fallback to fill the Coherent Actions input. Slice I (or Slice E) tracks time + tokens to Stats.md — that instrumentation belongs to Slice I / E, NOT to this skill.
Surface 3 — Standalone CLI
/solution-designer --discovery <path-to-_thought-or-section> [--context <text>] [--model sonnet|opus]For one-off iteration outside the orchestrator — e.g., debugging a fixture, exercising the Discovery → candidate pipeline against a hand-written Discovery, or regression-testing prompt changes. Stateless; the standalone surface runs the skill and prints the JSON output to stdout.
Optionality Boundary
This skill does NOT decide whether Phase 2 (/solution-design) should be invoked at all. That decision — the "optionality trigger" question — lives in /solution-design (B3 orchestrator).
When this skill is called, it always executes (subject to the deterministic input-validation errors in the Input Contract). The boundary keeps this skill a clean adapter: it produces a candidate when asked. The orchestrator owns the upstream "should we even run Phase 2" question.
Edge Cases
| # | Condition | Handling |
|---|---|---|
| E1 | # Discovery section absent in the source _thought |
Caller's responsibility to detect upstream. If the skill is called with empty locked-field inputs, return the deterministic error envelope per Input Contract. |
| E2 | design_context_override empty or missing |
Run without it (optional field). |
| E3 | All 6 Cockburn tests fail | Emit anyway with cockburn_gate_results.*.passed=false for the two hard gates + confidence: low. Do NOT refuse to emit — an all-FAIL candidate is informative (the orchestrator's selector may treat it as a signal that the Discovery itself is malformed). |
| E4 | Standalone CLI invocation without orchestrator | Skill runs (stateless); eval.py is the verification path for one-off runs. |
| E5 | /plan (Slice I) fallback path |
Same skill, same I/O contract; orchestrator records time + tokens to Stats.md. The time/token instrumentation belongs to Slice I or Slice E, not to this skill. |
| E6 | UX-only or arch-only candidate (one is N/A) | Emit empty array for the N/A type. Do NOT fabricate decisions. Hard rule. |
| E7 | Re-run with identical fixture and unchanged prompt | change-impact diff in eval.py should yield zero diff and identical SHA-256. Non-zero diff = the skill is non-deterministic and that itself is a regression signal — caller's call. |
| E8 | First-run baseline (no prior output for eval.py diff) |
eval.py reports baseline_absent: true, no diff metric (degraded mode, not failure). |
| E9 | Locked Discovery fields present but malformed (e.g. ## Metrics body has no OMTM line) |
Reject at Step 1 with a specific pointer to the malformed field. Do not silently attempt a design over a missing-anchor field. |
Source
~/.claude/rules/code_first_architecture.mdlines 81–102 — AI Adapter / judgment-port pattern.~/.claude/rules/code_first_architecture.md:112— Producer-never-verifies invariant.${KIT_HOOKS_DIR}/pre_plan_gates.py:153-158—DISCOVERY_LOCKED_FIELDS(the four locked field names).${KIT_HOOKS_DIR}/pre_plan_gates.py:178-189—_discovery_locked_fields_hash(12-hex SHA-256 prefix of the four locked field bodies).<KL>/Development/Sources/Books/simplifying-software-design-cockburn/simplifying-software-design-cockburn.mdlines 69–80 — Six Design Tests.<KL>/Development/Sources/Books/simplifying-software-design-cockburn/simplifying-software-design-cockburn.md:79— "first two tests" emphasis (Abstraction + Responsibility Alignment).<KL>/Development/Sources/Books/simplifying-software-design-cockburn/simplifying-software-design-cockburn.md:206— "start fat, split only as needed" (UX + architecture folded for v1).~/.claude/rules/plan-gates.mdGate 0a OQ19 — Plain business words first.Thoughts/workflow-phases-redesign_THOUGHT.mdS-H-v2-A entry — Slice H v2 architecture (3-skill split, C1–C5).Thoughts/workflow-phases-redesign_H_v2_B1_PLAN.md— Locked plan this skill implements.
Non-goals
- This skill does not select among candidates. The orchestrator's independent Opus selector + the user gate own selection.
- It does not decide whether Phase 2 runs at all (Optionality Boundary, above).
- It does not write or mutate any file. Stateless adapter — only the orchestrator writes.
- It does not invoke
/double-checkon its own output. Producer-never-verifies is preserved by the orchestrator's N-parallel + selector + user gate pattern, not by self-fact-check inside the skill. - It does not produce multiple alternatives in a single run. N alternatives come from N parallel invocations.
- It does not consult the codebase, prior plans, or external sources. Discovery is the only grounding surface.