Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions .claude/skills/design-council/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
---
name: design-council
description: "Run several independent designer passes on the same change, each from a different perspective, and choose one plan. Live mode runs the passes in isolation, shows the plans side by side and lets the architect pick or override; eval mode leaves running and picking to an external runner. Use when asked to convene a design council, compare design options or produce competing design plans."
version: 1.0.0
---

# Design council

Produce several complete, independent design plans for one change, each from a
different perspective, so the decision maker can compare real alternatives. This skill
coordinates the passes and, in live mode, the comparison. It does not implement code
and does not write an ADR.

## Tool rules for designer passes

Every designer pass follows the tool rules of the design-plan skill, before anything
else: read files with the read tool (or hashread, whichever is available), explore with
the grep, find and ls tools, write the plan with the write and edit tools, and use bash
only for design-plan's exact self-lint command, from the workspace root, with its
canonical paths and flags. No shell exploration, no shell probes (not even `echo`,
`true` or `pwd`), no wrappers and no alternate plan paths.

Setting up working copies and collecting finished plans is the coordinator's job (or
the external runner's), never something a designer does through its shell.

## Inputs

- **`.council/input.md`**, unchanged. It holds only `## Motivation`,
`## User flows to optimize for` (FLOW-n) and `## Must not break` (CANT-n). There is no
Summary; do not require or write one here.
- **The same code snapshot** for every pass.
- **The design-plan skill** (`../design-plan/SKILL.md`, resolved from this skill's
directory) with its bundled references: plan template, global invariant, linter and
input parser. Do not copy them here.
- **Perspectives** in `references/perspectives.md` (resolved from this skill's
directory): `minimal-change` plus three other lenses, one `## <id>` block each. A pass
receives exactly one block.

## Live mode (an architect is present)

### 1. Run the passes independently

1. Run one designer pass per perspective in `references/perspectives.md`, always
including `minimal-change`.
2. Give each pass a fresh context and its own working copy with identical input and
code. Supply only the perspective id, its block and the shared inputs. Do not show a
pass any other candidate, draft or conclusion until all passes have finished.
3. Each pass follows design-plan, writes its own `.council/plan.md` in its own copy and
repeats the self-lint until it exits 0.
4. Passes may run in parallel or one after another; either way each needs a fresh
context and an isolated output. If isolation is not possible, say so plainly instead
of calling the passes independent.
5. When a pass finishes, the coordinator keeps its plan under the perspective id (for
example `.council/plans/<id>.md` in the live workspace). Designers never write to a
shared output file.

### 2. Compare side by side

Only after every pass has finished, show the candidates in one table or parallel
sections with, for each:

- perspective id and plan path;
- the approach in one or two sentences;
- coverage of every FLOW-n and CANT-n, and anything left uncertain;
- added public surface (functions, storage, errors, events);
- worst-case cost on the hot paths;
- compatibility and activation impact;
- main trade-offs and open questions;
- lint status.

A clean lint run shows structure only, not that the design is correct; say which
claims you checked against the code. Report failed or incomplete passes as such; do not
drop them silently or present them as successes.

### 3. The architect decides

The architect picks one candidate or overrides. You may recommend one and say why;
`minimal-change` is the baseline for comparison, not the automatic winner. If the
architect's override changes plan content (for example combining ideas from two
candidates), produce one coherent revised plan and run the self-lint and the content
review again. An override never waives a requirement, a FLOW-n, a CANT-n or the global
invariant.

## Eval mode (no user, run by a script)

An external runner sets up the independent passes, gives each one perspective, collects
the plans and picks the plan. Inside an assigned pass:

- follow design-plan with the given perspective and finish with a lint-clean
`.council/plan.md`;
- do not start a nested council, compare candidates or pick a winner;
- never ask questions, prompt a user or wait for approval;
- record unresolved facts as uncertainties in the plan; never invent them.

## Checklist

- [ ] Every perspective in `references/perspectives.md` ran, including `minimal-change`.
- [ ] Each pass had a fresh context, an isolated copy and identical inputs.
- [ ] Every candidate is lint-clean, or its failure is reported.
- [ ] Live mode: the comparison came after all passes and the architect made the call.
- [ ] Eval mode: no questions, no nested council, no selection inside a pass.
78 changes: 78 additions & 0 deletions .claude/skills/design-council/references/perspectives.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Design perspectives

Each block below is one design lens for a single designer pass. A caller may hand a
designer just one block, so every block stands on its own.

Rules shared by every lens (repeated here so a single block is never read without them):
every lens must meet every requirement, cover every FLOW-n and CANT-n in the input, and
respect the global invariant. A lens decides which trade-offs to favour. It never adds
requirements, drops a user flow or relaxes a must-not-break item. If the evidence leads
two lenses to the same design, say so; do not invent differences.

## minimal-change

Objective: the smallest change that meets every requirement and every must-not-break
item.

- Prefer mechanisms that already exist in the code (storage, roles, errors, events,
dispatch paths) over new ones. New public surface needs a requirement that cannot be
met without it.
- Measure size as added public functions, storage slots, errors, events and touched
files, plus churn in existing tests and callers.
- Evidence to examine: what the current interfaces and storage already support, and
which gaps no existing mechanism can close.
- Trade-offs accepted: may leave some inefficiency or awkwardness in place if fixing it
is not required; must say where a slightly larger change would remove a real risk.
- Not acceptable: meeting a requirement only partly to keep the diff small.

## hot-path-efficiency

Objective: keep the recurring cost of the most frequent operations as low as possible in
the worst case.

- Count storage reads and writes, external or cross-contract calls and loops on each hot
path, before and after the change, and name the worst case (for example the deepest
nesting or the longest list a caller can create).
- Prefer bounded, constant-cost checks on the hot path; move work to rarer
administrative paths when that keeps semantics intact.
- Evidence to examine: the code of the hot paths as they are today and how often each
path runs relative to configuration paths.
- Trade-offs accepted: more implementation complexity, extra bookkeeping on write paths
or explicit limits, when the code shows they lower the recurring cost. Every cost claim
must be traced to code, not guessed.
- Not acceptable: unbounded work that any caller can trigger on a frequent path.

## compatibility-first

Objective: the least disruption to existing callers, stored data, integrations and the
way the change is activated.

- Keep existing function signatures, return values, errors, events and storage layout
unchanged where possible; add rather than alter.
- Check every existing caller, mock and test that depends on current behaviour, and
state how already-stored data is read after the change.
- Describe activation: what happens before and after the change takes effect, and
whether any caller must act.
- Evidence to examine: public interfaces, storage slot derivation, existing tests and
any documented behaviour callers rely on.
- Trade-offs accepted: extra code paths, adapters or duplicated entry points when they
keep existing users working without changes.
- Not acceptable: silent behaviour changes for existing callers or stored data.

## auditability-first

Objective: make state transitions, authority boundaries and invariant reasoning easy to
inspect and test.

- Make each state change explicit and single-purpose, with clear authorization checks
and an event for every change an observer needs to follow.
- Prefer designs where each invariant can be checked locally (one function, one slot)
over designs that need whole-system reasoning.
- Give every failure a specific error, and list the tests that would prove each
invariant and each must-not-break item.
- Evidence to examine: role and permission checks, error and event definitions, and how
existing tests express invariants.
- Trade-offs accepted: some additional code, events or validation when they reduce the
reasoning needed to trust the change.
- Not acceptable: hidden side effects, implicit authority, or state reachable only
through undocumented paths.
Loading