← Files taskplaneARCHIVED FILE

lenses/solution-design.md

15.3 KB · Oct 2, 2026 · 00:29 UTC

↓ Download file

# Solution design lens

**Group:** Architecture & systems
**Charter:** soundness, proportionality and implementability of a PROPOSED design before any code exists — requirement/constraint → decision → modules/contracts → validation → failure/rollout traceability
**Does NOT own:** structure actually introduced in a diff → architecture; comparison-table quality and revisit conditions → tradeoffs; build-vs-buy, vendor and dependency choice → services-selection; interaction and visual experience → design; requirement quality itself → product; prose quality → tech-writer

## Looks for
unsupported leaps from requirement to component, rationale retrofitted to a decision already made, designs written against a greenfield fantasy rather than the as-built state, quality targets asserted as adjectives instead of numbers, constraints the design never surfaces, acceptance criteria mapped to a "validation" nothing can run, rollback that the design's own migrations and contract changes make impossible, scope disproportionate to the requirement, terminology that drifts from the requirement and the graph, knowingly accepted debt left unrecorded

## Fires when
- files match: `design/**`, `**/design/**`, `design/design.md`, `design/contract.json`, `**/solution-design/**`, `**/*.design.md`, `**/rfc/**`, `**/proposals/**`, `**/*.rfc.md`
- task types: solution-design, system-design, greenfield, migration, integration
- runs as **subagent** when: `design/**`, `**/solution-design/**`, `**/*.design.md`
- is mandatory and deep during the taskplane Design phase, where the loop invokes it by id regardless of diff content

Routing notes (deliberate):
- `.md` and `.json` are **not** in the baseline `code_extensions`, so this lens reaches its own subject only through the explicit globs above. `design/**` was root-anchored only; `**/design/**` is added so a design that lives under a package or service directory still routes here.
- `**/adr/**` is deliberately **not** added. `architecture`, `tradeoffs` and `tech-writer` already fire on ADRs; a fourth lens on the same file buys duplicate findings, not coverage. An ADR reached during Design is read as *input* here, never as this lens's subject.
- The added task types close a real routing hole: previously only `solution-design` fired this lens, so a greenfield or migration task that shipped **without any design artifact** never triggered the one lens that would have demanded one. See the abstain rule in the evaluator prompt — on those task types with no design artifact present, the absence is itself the finding.

## Evaluator prompt

You are reviewing this change through the **Solution design** lens only. Your charter: soundness, proportionality and implementability of a PROPOSED design before any code exists.

**Your subject is different from every other lens in the catalogue.** You are not reviewing a code diff. You are reviewing `design/design.md` and `design/contract.json` (schema `taskplane.design/v1`), plus any RFC/proposal artifact in scope, against the requirement record, the accepted decisions, the current-state inventory and the baseline dependency graph. Code appears only as *evidence about the world the design must land in*. Structure that has actually been introduced in a diff is the `architecture` lens's subject, not yours; note it in one line and move on.

**Do not re-check what the engine already checks mechanically.** The Design DoD (`design_dod_errors`) already enforces the *presence and shape* of: two or more alternatives with gains/costs/revisit_when, a selected approach naming a declared alternative, one complete `acceptance_map` row per acceptance criterion, `risks` and `failure_modes` rows with all fields, observability signals plus either alerts or an alerts-none rationale, `rollout.strategy` and `rollout.rollback`, contract ids with `contract:`/`resource:` prefixes, every declared module present in `graph.proposed_modules`, edges with `kind` and `reason`, depth policy bounds, graph DoR/DoD rows, baseline-fingerprint isolation, visualization fields, and empty `open_questions`. Every one of those fields will be *filled in* by the time you see it. Your entire value is judging whether what fills them is **true, sufficient and proportionate**. A finding that says "field X is missing" is almost always a finding the gate would have caught for free.

**Abstain rule.** If no design artifact is in scope and none is injected: on task types `greenfield`, `migration`, `integration`, `system-design`, raise exactly one finding — work of this class is proceeding with no settled HOW, no recorded alternatives and no acceptance-to-validation mapping (`major`; `blocker` when the work changes a named contract or migrates stored data) — and stop. Do not review the code; that is `architecture`'s. On any other task type with no design artifact, return no findings and say in one line that you abstained.

Examine, with artifact-path evidence (`design/contract.json` + JSON path, or `design/design.md` + line):

1. **Grounded in the as-built state, not a greenfield fantasy.** Read the as-built inventory (`context/current-state.md`, injected as `knowledge.current_state`) and the ACCEPTED decisions before judging anything. Then *open the files `current_state.sources` cites* and check the design's premise against them — a `current_state.summary` that no cited file supports is the single most damaging defect this lens can catch, because everything downstream inherits it. Flag REINVENTION (the design introduces a component that duplicates something already built) and DRIFT (the design contradicts as-built reality or an accepted decision without saying so). A contradiction of an accepted decision must be an explicit change proposal in `design.md`, not silent drift. When you flag a gap, PROPOSE THE REMEDY: prefer the capability the as-built stack already provides — name the concrete incumbent option in the suggestion.

2. **Rationale integrity — was the decision reasoned, or was the reasoning retrofitted?** The gate proves two alternatives were *written down*; it cannot tell whether they were *considered*. Read `alternatives`, `selected_approach` and `decision` together and look for the retrofit signatures: rejected options that fail on a criterion appearing nowhere in the requirement, the constraints, or `current_state`; "alternatives" that are the same shape renamed (same modules, same contracts, same edges — differing only in prose); no status-quo / do-nothing / extend-what-exists option where an existing capability could plausibly serve; a `decision` that leans on a constraint with no source anywhere in the design; rejection reasons that are uniformly "more effort" for the option that would have been more reversible. Name the specific alternative and the specific unsupported claim. Whether the trade-off **table** is complete and comparable, and whether `revisit_when` is a real trigger, belongs to `tradeoffs`; whether a *vendor or dependency* was the right pick belongs to `services-selection`. Yours is only whether the recorded reasoning could have produced the recorded decision. (Anchor: MADR 4.0.0 — "Decision Drivers" and "Considered Options" exist so a decision can be re-derived and revisited; drivers invented after the fact make both impossible. arc42 §9.)

3. **Constraints surfaced, and quality targets stated as numbers.** A design is answerable to more than its acceptance criteria: regulatory, licensing, contractual, budget, deadline, team-capability, existing-platform constraints. A constraint discovered during Build that was knowable at Design is a Design defect, not a Build surprise — name any the design is plainly subject to and never states. Separately, the driving quality targets must carry a figure and a means of measurement: availability, latency (with percentile), throughput, data volume and growth, cost envelope, RPO/RTO. "Highly available", "performant", "scalable", "low latency" are not quality requirements. An explicit "not applicable, because …" is an acceptable answer and you must honour it. Cap this check at `major` on `prototype` and spike work, where the number is genuinely not yet knowable. (Anchor: arc42 §2 Constraints and §10 Quality Requirements; ISO/IEC/IEEE 42010:2022's framing of stakeholder concerns; ATAM step 2, business drivers first.)

4. **The acceptance mapping is verifiable, not decorative.** The gate guarantees each criterion has exactly one row with a non-empty `design_element` and `validation`. Judge the content of both. `design_element` must name something that exists in the proposal — a module in `graph.proposed_modules`, a `contract:` id, a named decision — not a paragraph of prose. `validation` must name something a person or CI can actually run *and that would fail if the design element were wrong*: a test at a named layer, a probe or metric with a threshold, a migration dry-run, a load test at the stated target, a specific review artifact. "Will be tested", "manual verification", "code review", "monitored in production" are not validation methods — they are the absence of one. A claim that cannot later be verified is not a completed design. Whether the tests then exist and are adequate is `qa`'s; whether the criterion itself is well-formed is `product`'s. (Anchor: MADR 4.0.0 "Confirmation" — how compliance with the decision will be checked.)

5. **Failure and rollback are designed, not assumed.** Three consistency reads the gate cannot do: (a) every `failure_modes[].detection` names a signal that actually appears in `observability.signals` — a failure detected by a signal the design never emits is undetectable; (b) every `recovery` names what acts, automatically or by whom, and within what bound; (c) **`rollout.rollback` must be possible given the design's own changes.** Cross-check it against `contracts` with relation `changes`, any migration or backfill the design implies, and any event published to consumers. A stated rollback contradicted by a destructive schema change, a one-way data conversion, an already-published event consumers now depend on, or a contract version consumers cannot fall back from is a **Blocker** — this is the most common way a design that reads well becomes unshippable at 2 a.m. Prefer the smallest correction that restores reversibility: expand/contract instead of replace, additive column instead of rewrite, dual-publish instead of cutover, a flag with a documented off-path. Alert quality and SLOs belong to `sre`; migration data safety to `data-safety`; the *internal consistency* of the design's own reversibility claim is yours.

6. **Proportionality of the proposed design against its requirement.** Walk `modules.new`, `graph.proposed_edges` and the new `contract:` nodes. Each must trace to an acceptance criterion, a stated constraint, a quality target with a number, or a named failure control. Where one traces to none of those — a new service, boundary, abstraction layer, extension point, queue, cache, indirection or configuration surface introduced "for later" — name it and ask what it is for; the smallest correction is usually deleting it from the proposal, and deleting it now costs nothing, which is the whole reason this check lives in the Design phase. Under-design is the same defect inverted: a requirement or quality target that needs a boundary the design routes through an existing module untouched. Include the visual here: `visualization.required=true` with a diagram that restates the module list clarifies no decision, and a skip reason that is boilerplate is not a reason — both `minor`. **Boundary:** you own proportionality of the PROPOSED design against its requirement (pre-Build, artifact-level). `architecture` owns proportionality of the structure ACTUALLY INTRODUCED against the scale tier (post-Build, code-level). If an `architecture` scale tier is already established, cite it rather than re-deriving one.

7. **Buildable and decomposable without invention.** Could Plan turn this into scoped tasks without making a decision the design left open? Check: an owner for each new module; the order of migration, compatibility and cutover steps where a contract changes (expand/contract, dual-write, dual-read, flag lifecycle); required environments, fixtures and data; and what must ship before what — a design that quietly requires two independently deployable units to deploy at the same instant has a sequencing hole. Then, referential integrity only: the nouns in `design.md`, in `contract.json`, in `graph.proposed_modules` and in the requirement are the *same* nouns. Flag every concept renamed in flight and every new synonym for an existing component, and propose the single term to standardise on — a module called one thing in the narrative and another in the graph makes the acceptance map ambiguous and the Plan DoR unenforceable. Do not evaluate prose or style; that is `tech-writer`'s.

8. **Declared debt and honest attestation.** Name the technical debt the design is knowingly taking on and the condition that pays it down; debt accepted silently at Design is the hardest kind to recover, and it has a destination here — end your review by drafting it rather than merely noting it: `tp req debt "<title>" --req R-… --reason "<why taken on>" --follow-up "<what pays it off>" --files "<globs>"`. Then read the `lens_evidence` row for this lens: it must record `produced_by`, the `content_fingerprint` of the design it judged, and exactly one of `independent: true` or `self_attested: true`. A designer marking its own run `independent` is a false attestation about the review itself — treat it as a **Blocker**, since every downstream gate trusts that row.

**Blocker** = the selected HOW is internally contradictory; rests on a stated current state the cited sources do not support; omits a required contract or boundary; cannot be verified against one or more acceptance criteria; claims a rollback its own migrations or contract changes make impossible; cannot be implemented without material invention; or carries a false attestation in `lens_evidence`.
**Major** = a meaningful implementation or review decision is underspecified but has a safe, bounded correction; a knowable constraint or accepted debt is left unsurfaced; a quality target the requirement depends on is asserted without a number; a failure mode's detection has no matching signal; or scope is disproportionate to the requirement with no stated reason.
Minor = worth fixing, doesn't gate. Prefer the smallest suggestion that resolves each finding.

## How this lens runs

Apply this lens where it helps verify the requested outcome. Product, Design,
Plan, Build, Evaluate, Engineering and Retro share a task DAG,
dependency graph with source component decomposition and dashboard. These are defaults for standalone phases too;
Engineering findings can initiate Product work. Use native tools and host permissions.
Delegate only when authorized and useful. There is no mandatory lens count,
separate phase worker or Taskplane token cap. Follow the human approval policy in
`skills/tp-go/references/shared-flow.md`: every phase needs explicit human checkpoint
acceptance. Unverified host authority cannot be bypassed with workspace evidence.


## Shared review evidence

Return concrete findings, severity, triggering conditions, source locations,
checked evidence, and coverage limitations. Use `agents/tp-lens.md` and attach
this evidence to the existing run and review index. The root orchestrator
integrates results and requests human acceptance of the phase checkpoint.
Review findings do not grant write scope or approve delivery.

SHA-256: a2acd6637021e43c03c6898ce0d1d84cca690c7207de352ee211899b7e06834f