From e8f2d0b79887fee9ef9c4803f40cbeca29869cb0 Mon Sep 17 00:00:00 2001 From: Oriol Torrent Florensa Date: Fri, 3 Jul 2026 11:45:49 +0200 Subject: [PATCH 1/5] feat(unic-archon-dlc): add /improve-architecture skill (redesign step 10) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the off-line arch-health half out of the inert legacy unic-dlc-cleanup workflow into an interactive command/skill (two-axis, ADR-0017): its core is a design grilling loop that needs the live conversation, so it composes Matt Pocock's improve-codebase-architecture verbatim (+ /codebase-design, /grilling, /domain-modeling) rather than reimplementing it. Why it earns its place (ADR-0021): it adds three DLC layers the raw skill lacks — an intent-drift pass (PRD stories/ACs vs shipped), a durable arch-review.md artifact under / (ADR-0015), and an ADR-consolidation gate with superseding across both ADR homes (plugin-local + repo-root; never deletes an ADR, updates the matching README.md index). Two modes: `/improve-architecture ` is intent-grounded against a build session; no-arg is a repo-wide sweep (dated artifact, intent-drift skipped). Config load is lenient — off-line, touches no tracker — so no new config key and no lib/ change (thin, ADR-0018). Does not touch the legacy cleanup workflow (step 11's scope). - commands/improve-architecture.md — the 7-step command - docs/adr/0027 + plugin ADR index row - AGENTS.md doctrine bullet; CHANGELOG [Unreleased] entry - plugin.json description adds improve-architecture - redesign progress table: step 10 done Co-Authored-By: Claude Opus 4.8 --- .../.claude-plugin/plugin.json | 2 +- apps/claude-code/unic-archon-dlc/AGENTS.md | 1 + apps/claude-code/unic-archon-dlc/CHANGELOG.md | 2 +- .../commands/improve-architecture.md | 249 ++++++++++++++++++ ...-improve-architecture-skill-superseding.md | 95 +++++++ .../unic-archon-dlc/docs/adr/README.md | 1 + .../unic-archon-dlc/docs/redesign/README.md | 32 +-- 7 files changed, 364 insertions(+), 18 deletions(-) create mode 100644 apps/claude-code/unic-archon-dlc/commands/improve-architecture.md create mode 100644 apps/claude-code/unic-archon-dlc/docs/adr/0027-improve-architecture-skill-superseding.md diff --git a/apps/claude-code/unic-archon-dlc/.claude-plugin/plugin.json b/apps/claude-code/unic-archon-dlc/.claude-plugin/plugin.json index 200f48d6..ed40492e 100644 --- a/apps/claude-code/unic-archon-dlc/.claude-plugin/plugin.json +++ b/apps/claude-code/unic-archon-dlc/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "unic-archon-dlc", "version": "0.8.0", - "description": "A complete Archon-powered AI development lifecycle as an installable DLC pack — explore, specs, tickets, build, pr-review, qa, triage, and cleanup workflows.", + "description": "A complete Archon-powered AI development lifecycle as an installable DLC pack — explore, specs, tickets, build, pr-review, qa, triage, improve-architecture, and cleanup workflows.", "author": { "name": "Unic AG", "url": "https://www.unic.com" diff --git a/apps/claude-code/unic-archon-dlc/AGENTS.md b/apps/claude-code/unic-archon-dlc/AGENTS.md index 965e4c7d..c7bdf7f9 100644 --- a/apps/claude-code/unic-archon-dlc/AGENTS.md +++ b/apps/claude-code/unic-archon-dlc/AGENTS.md @@ -64,6 +64,7 @@ Load-bearing invariants. These either originate in a Plugin ADR or are policy de - **Nyquist map gates the build.** Every issue in Issues JSON must carry a `test_command` (or `test_command_planned`) before `/build` consumes it — the gate runs in `/tickets` ([ADR-0022](docs/adr/0022-tickets-slice-to-build.md)); `/build` reads the build-ready `issues.json` directly ([ADR-0023](docs/adr/0023-build-generic-red-green-refactor-loop.md)). - **`/qa` gates on `gates.qa` and fail-closes the merge.** The QA Archon pipeline (e2e → coverage → UAT → verify-pr-base → merge) has two `approval:` gates (UAT + merge), both HITL by default and skipped in AFK; downstream nodes use `trigger_rule: all_done` so AFK auto-merges a clean build, while the merge node's fail-closed `when` blocks auto-merging a red e2e/coverage or a wrong PR base. `/qa` is also an **issue-producing on-ramp**: a UAT rejection files each defect directly as a `ready-for-agent` tracker issue (composing the configured tracker + `classification.labels`, Matt's `qa` brief shape, AI disclaimer) that feeds `/tickets` — it does not just halt. Never `lib/tracker-adapter.mjs` (dissolved). See [ADR-0025](docs/adr/0025-qa-pipeline-onramp.md). - **`/pr-review` is a self-contained fan-out Archon review that harvests, never depends.** The PR-review Archon pipeline (bootstrap → guard → prep → seven parallel aspect nodes → synthesize → reconcile → review-gate → post) reviews the open PR and posts a summary comment + inline comments via the configured tracker (MCP-first, CLI-fallback). It **harvests `unic-pr-review`'s review learnings** (confidence→severity rubric, structured summary, hidden-marker idempotency, conditional spawn table, two-surface posting) with **no ADO code and no runtime dependency** on that plugin. `prep` composes **one Intent Brief** (linked work items + Confluence/MD docs + PR description + `PRD.md`) that is **injected into every aspect** (the quality lever) and surfaces cross-source contradictions; intent-check is skipped only when no source resolves. Re-review is first-class — a dedicated `reconcile` node keyed on the `` marker (never author identity) classifies findings new / still-present / fixed / regressed. The `review-gate` confirms before posting `when gates.pr-review == hitl` (AFK posts directly); posting is advisory — the real merge gate is `/qa`. Never `lib/tracker-adapter.mjs` (dissolved). See [ADR-0026](docs/adr/0026-pr-review-generic-archon-harvest.md). +- **`/improve-architecture` is a skill that composes Matt's method and owns ADR superseding.** The off-line, on-demand arch-health box is a **command/skill** (its core is a design grilling loop that needs the live conversation — [ADR-0017](docs/adr/0017-container-follows-structural-need.md)), not an Archon workflow. It **composes `improve-codebase-architecture` verbatim** (technical drift + deepening HTML report + `/grilling` loop, with `/codebase-design` vocabulary + `/domain-modeling`) and **earns its place** ([ADR-0021](docs/adr/0021-earns-its-place-compose-verbatim.md)) by adding three DLC layers the raw skill lacks: an **intent-drift** pass (PRD stories/ACs vs shipped), a **durable `arch-review.md`** artifact under `/` ([ADR-0015](docs/adr/0015-workflows-slug-artifact-home.md)), and an **ADR-consolidation gate with superseding**. Two modes: `` → intent-grounded against that build session; no-arg → repo-wide sweep (dated artifact, intent-drift skipped). Superseding works across **both ADR homes** (plugin-local + repo-root), never deletes an ADR (old status → `Superseded by ADR-NNNN`, index updated in the matching `README.md`). Config load is **lenient** (off-line, touches no tracker); **no new config key**, no `lib/` change, no auto-hook (PLAN #8). It does **not** touch the legacy `unic-dlc-cleanup.yaml` — that is `/cleanup`'s (step 11) scope. See [ADR-0027](docs/adr/0027-improve-architecture-skill-superseding.md). - **Dogfooding note.** This monorepo has had Setup run against it; the generated artefacts live under [`docs/agents/`](../../../docs/agents/) at the repo root and are managed by the marker-delimited block in the root [`AGENTS.md`](../../../AGENTS.md). Those files describe the target two-axis architecture (see [ADR-0016](docs/adr/0016-dlc-thin-process-layer.md)–[ADR-0018](docs/adr/0018-generic-core-config-compose.md)) with the tracker as the single source of truth, and should be treated as the canonical agent guidance for this repo, not as a current-practice snapshot. ## External dependencies diff --git a/apps/claude-code/unic-archon-dlc/CHANGELOG.md b/apps/claude-code/unic-archon-dlc/CHANGELOG.md index 435d1379..c31950d5 100644 --- a/apps/claude-code/unic-archon-dlc/CHANGELOG.md +++ b/apps/claude-code/unic-archon-dlc/CHANGELOG.md @@ -6,7 +6,7 @@ - (none) ### Added -- (none) +- **`/improve-architecture` off-line arch-health command/skill** (ADR-0027) — surfaces technical + intent drift and deepening opportunities, writes a durable `arch-review.md`, and consolidates ADRs **including superseding**. It **composes Matt Pocock's `improve-codebase-architecture` verbatim** (technical drift + deepening HTML report + `/grilling` loop, with `/codebase-design` vocabulary + `/domain-modeling`) and **earns its place** (ADR-0021) by adding three DLC layers the raw skill lacks: an **intent-drift** pass (PRD stories/ACs vs shipped), a **durable artifact** under `/` (ADR-0015), and an **ADR-consolidation gate with superseding**. Two modes: `/improve-architecture ` → intent-grounded against that build session; `/improve-architecture` (no arg) → repo-wide sweep (dated artifact, intent-drift skipped). Superseding spans **both ADR homes** (plugin-local + repo-root), never deletes an ADR (old status → `Superseded by ADR-NNNN`, matching `README.md` index updated). Config load is **lenient** (off-line, touches no tracker); no new config key, no `lib/` change, no auto-hook. Harvests the legacy `unic-dlc-cleanup` `arch-review` + `adr-consolidation` content; does **not** touch that workflow (step 11's scope). ### Fixed - (none) diff --git a/apps/claude-code/unic-archon-dlc/commands/improve-architecture.md b/apps/claude-code/unic-archon-dlc/commands/improve-architecture.md new file mode 100644 index 00000000..fadc3b9a --- /dev/null +++ b/apps/claude-code/unic-archon-dlc/commands/improve-architecture.md @@ -0,0 +1,249 @@ +--- +argument-hint: '[ | (empty = repo-wide sweep)]' +description: "Off-line architecture health: surface technical + intent drift and deepening opportunities, write a durable arch-review.md, and consolidate ADRs (including superseding). Composes Matt Pocock's improve-codebase-architecture verbatim and adds the DLC intent/artifact/ADR layers." +--- + +# unic-archon-dlc:improve-architecture + +> Design rationale: [ADR-0027 — `/improve-architecture` is a skill that composes Matt's method and owns ADR superseding](docs/adr/0027-improve-architecture-skill-superseding.md) (container per [ADR-0017](docs/adr/0017-container-follows-structural-need.md); compose-don't-reimplement per [ADR-0016](docs/adr/0016-dlc-thin-process-layer.md); earns-its-place per [ADR-0021](docs/adr/0021-earns-its-place-compose-verbatim.md); artifact home per [ADR-0015](docs/adr/0015-workflows-slug-artifact-home.md); tracker/ADRs are the source of truth per [ADR-0013](docs/adr/0013-tracker-single-source-of-truth.md)). + +**Arguments:** "$ARGUMENTS" + +`/improve-architecture` is an **off-line, on-demand** box: it surfaces architectural drift + +deepening opportunities and **consolidates ADRs (including superseding older ones)**. It runs off the +main line — periodically or when you sense drift, **not** per-feature and **not** as an end-of-cycle +auto-hook. It is an **in-session command/skill** because design grilling needs the live conversation +(inherently HITL — [ADR-0017](docs/adr/0017-container-follows-structural-need.md)). + +It is a **thin composing wrapper**: it **owns the _what_** — the DLC config binding, the durable +`arch-review.md` artifact, the intent-drift pass against the PRD, and the ADR-superseding gate — and +**delegates the _method_** for technical drift + deepening to Matt Pocock's +`improve-codebase-architecture` skill **verbatim**, composing `/codebase-design` for the architecture +vocabulary, `/grilling` for the design walk, and `/domain-modeling` to keep `CONTEXT.md` current. +Compose those skills by name — never reimplement or vendor them. + +**What the DLC adds over the raw skill (why this box earns its place — [ADR-0021](docs/adr/0021-earns-its-place-compose-verbatim.md)):** +Matt's skill produces an ephemeral HTML deepening report + a design grill for **technical** drift. On +top of that this box adds (a) an **intent-drift** pass comparing the PRD's stories/acceptance +criteria against what shipped, (b) a **durable `arch-review.md`** committed under the artifacts dir, +and (c) an **ADR-consolidation gate with superseding** across both ADR homes. + +Follow these steps in order. Do not skip any step. + +> **Shell requirement**: Step 1 uses `<<'EOJS'` heredoc syntax, which requires a POSIX-compatible +> shell. On Windows, run inside WSL2 or Git Bash; cmd.exe and PowerShell do not support heredocs. All +> filesystem work uses Node's `node:fs`/`node:path`, so paths are cross-platform. + +## Step 1 — Load config (lenient) + +`/improve-architecture` reads (never writes) `.archon/unic-dlc.config.yaml`. Unlike the tracker-bound +boxes, it is **off-line and touches no tracker**, so a missing or incomplete config is +**non-blocking** — it degrades to defaults and continues. Run: + +```bash +node --input-type=module <<'EOJS' +let output +try { + const { pathToFileURL } = await import('node:url') + const mod = await import(pathToFileURL(`${process.env.CLAUDE_PLUGIN_ROOT}/lib/config-schema.mjs`).href) + const { existsSync } = await import('node:fs') + const { join } = await import('node:path') + const cwd = process.cwd() + + const yamlPath = join(cwd, '.archon', 'unic-dlc.config.yaml') + if (!existsSync(yamlPath)) { + // Off-line box: no config is fine — fall back to defaults and continue. + const config = mod.mergeConfig() + const g = (p) => p.split('.').reduce((o, k) => (o == null ? undefined : o[k]), config) + output = { ok: true, degraded: true, reason: 'no-config', artifacts_dir: config.artifacts_dir, docs: config.docs, matt_suite: g('skills.matt_suite') } + } else { + const r = mod.loadConfig(yamlPath) + if ('error' in r) { + const config = mod.mergeConfig() + const g = (p) => p.split('.').reduce((o, k) => (o == null ? undefined : o[k]), config) + output = { ok: true, degraded: true, reason: `config-unreadable: ${r.message}`, artifacts_dir: config.artifacts_dir, docs: config.docs, matt_suite: g('skills.matt_suite') } + } else { + const config = mod.mergeConfig(r.config) + const g = (p) => p.split('.').reduce((o, k) => (o == null ? undefined : o[k]), config) + output = { + ok: true, + degraded: false, + artifacts_dir: config.artifacts_dir, + docs: config.docs, + matt_suite: g('skills.matt_suite'), + } + } + } +} catch (err) { + // Even a plugin load error should not stop an off-line review — default and warn. + output = { ok: true, degraded: true, reason: `plugin-load: ${err?.message ?? String(err)}`, artifacts_dir: 'workflows', docs: null, matt_suite: null } +} +process.stdout.write(JSON.stringify(output) + '\n') +EOJS +``` + +Parse the JSON. Keep `ARTIFACTS_DIR` (default `workflows`), `DOCS`, and `MATT_SUITE`. If `degraded` +is `true`, print a one-line warning naming `reason` and note that `ARTIFACTS_DIR` fell back to +`workflows`, then continue. If `MATT_SUITE` is present and `MATT_SUITE.present` is `false`, warn that +`improve-codebase-architecture` + `/codebase-design` + `/grilling` + `/domain-modeling` are declared +dependencies and review quality will degrade, then continue (non-blocking). + +## Step 2 — Determine mode + +Read `$ARGUMENTS`: + +- **Non-empty → per-slug mode.** Treat the argument as a Slug. Resolve the session dir + `//` and read, if present: `PRD.md` (intent), `report.md` (technical + outcome + "Decisions Made"), and `issues.json` (breakdown). A missing file is **not** fatal — warn + that the source is unavailable and degrade the passes that rely on it (e.g. no `PRD.md` → the + intent-drift pass in Step 4 downgrades to "no PRD anchor available for ``"). +- **Empty → repo-wide sweep mode.** Review the whole codebase with no PRD anchor. The intent-drift + pass (Step 4) is **skipped** and recorded as `n/a — repo-wide sweep`. + +State the resolved mode to the user before continuing. + +## Step 3 — Technical drift + deepening (delegate to Matt's method) + +Run the `improve-codebase-architecture` skill (`.agents/skills/improve-codebase-architecture/SKILL.md`) +**verbatim** — do not restate or reimplement its steps here. In summary it: reads `CONTEXT.md` + +relevant `docs/adr/` for grounding; uses an `Explore` agent to walk the codebase noting friction +(shallow modules, tight coupling, leaky abstractions, poor **locality**, hard-to-test seams); applies +the **deletion test**; presents candidates as a self-contained **HTML report** in the OS temp dir with +before/after diagrams and a top recommendation; then runs a **grilling loop** (via `/grilling`) on the +candidate the user picks, keeping the domain model current inline (via `/domain-modeling`) and offering +ADRs for load-bearing rejections. + +Bind that method to the DLC: + +- Use `CONTEXT.md`/`CONTEXT-MAP.md` for the **domain** vocabulary and the `/codebase-design` skill for + the **architecture** vocabulary (deep modules, interface, depth, seam, adapter, leverage, locality; + the deletion test; "the interface is the test surface"). Use these terms exactly. +- **Per-slug mode:** focus the Explore walk on the slug's changed surface — derive it from + `report.md` and/or the build's diff (e.g. `git diff --name-only ..HEAD`, adapting the range) — + but keep Matt's method and still surface repo-wide friction the change touches. +- **Repo-wide mode:** the full-codebase walk Matt's skill already performs. +- Note any candidate the grill loop marks "accept as ADR" or any load-bearing rejection worth + recording — these feed Step 6. + +## Step 4 — Intent drift (DLC value-add — per-slug mode only) + +**Skip in repo-wide mode** (record `Intent Drift: n/a — repo-wide sweep, no PRD anchor` in Step 5). + +In per-slug mode, compare the PRD's user stories + acceptance criteria against what shipped +(`report.md` + the diff). This is behaviour drift, distinct from the technical/structural drift of +Step 3: + +- Delivered behaviour that **diverges** from what the PRD describes (wrong behaviour, not merely + imperfect code). +- Acceptance criteria **silently dropped or narrowed** during build. +- Behaviour **added** during build that was not in the PRD (scope creep). + +For each finding, note: the PRD section referenced, what was described vs what was delivered, and a +recommended action — **fix now**, **create a follow-up issue** (flows into `/tickets`), or **accept as +an ADR** (feeds Step 6). If no `PRD.md` was found for the slug, record `Intent Drift: no PRD anchor +available for ` and continue. + +## Step 5 — Write the durable arch-review report + +Complement Matt's ephemeral HTML (which lands in the OS temp dir) with a durable markdown artifact +committed to the repo ([ADR-0015](docs/adr/0015-workflows-slug-artifact-home.md)). All file IO via +Node `node:fs`/`node:path`. + +- **Per-slug mode** → `//arch-review.md` +- **Repo-wide sweep** → `/arch-review-YYYY-MM-DD.md` (today's date; non-slug) + +Write these sections: + +```markdown +## Architecture Review — + +### Technical Drift + + + +### Intent Drift + + + +### Deepening Opportunities + + + +### Summary + +CLEAN | ISSUES FOUND () +``` + +Print the absolute path written. + +## Step 6 — ADR consolidation with superseding (per-ADR A/R/E gate) + +Get explicit human approval before writing or amending any ADR. + +### Collect candidates + +Gather decisions from: + +1. `report.md` "Decisions Made" (per-slug mode). +2. Step 3 / Step 4 findings marked "accept as ADR" and any load-bearing rejection Matt's grill loop + surfaced (a decision a future explorer would need in order not to re-suggest the same thing). + +If there are no candidates, say so and skip to Step 7. + +### Gate each candidate individually + +Present each candidate and wait for **A / R / E**: + +``` +--- +ADR Candidate N of M +Proposed file: /docs/adr/NNNN-.md (home = plugin-local | repo-root) +Status: Proposed +Supersedes: ADR-NNNN (omit if it supersedes nothing) +Context: +Decision: +Consequences: +--- +Accept (A) / Reject (R) / Edit (E)? +``` + +- **Choose the ADR home.** Infer from what the decision concerns and **confirm with the user**: a + decision about one plugin → that plugin's `docs/adr/` (e.g. + `apps/claude-code//docs/adr/`); a cross-cutting / monorepo-wide decision → the repo-root + `docs/adr/`. Both homes use MADR-lite (`# NNNN. Title` · `**Status:** Accepted (YYYY-MM)` · + `## Context` / `## Decision` / `## Consequences`) and per-directory zero-padded numbering — see the + root `docs/adr/README.md`. +- **A (Accept):** compute the next `NNNN` = highest existing number in the **chosen home** + 1 (scan + that dir's files). Write the ADR. Print `ADR written: /docs/adr/NNNN-.md`. +- **R (Reject):** skip. Print `ADR skipped: `. +- **E (Edit):** re-show the draft, let the user revise Context / Decision / Consequences / home / + Supersedes, then re-present for A/R. + +### Superseding mechanics (never delete an ADR) + +When an accepted ADR supersedes an existing one: + +1. In the **old** ADR file, change its status line to `**Status:** Superseded by ADR-NNNN`. +2. In the **new** ADR, add a `Supersedes ADR-NNNN` reference (in the status area or Context). +3. Update the **matching home's** `README.md` index: set the **old** row's Status cell to + `Superseded by ADR-NNNN`, and add a **new** row for `NNNN`. (Each home has its own index — + plugin-local `docs/adr/README.md` and repo-root `docs/adr/README.md`.) + +An ADR in one home may supersede an ADR in the other; amend whichever home(s) the two files live in. + +## Step 7 — Summary + +Print a concise summary: + +``` +/improve-architecture complete — mode: <per-slug <slug> | repo-wide sweep> + report: <path to arch-review.md> + summary: <CLEAN | ISSUES FOUND (count)> + html: <Matt's temp HTML path, if produced> + ADRs: accepted N | rejected M | edited-then-accepted K + written: <NNNN-*.md paths, or none> + superseded: <ADR-NNNN → by ADR-MMMM, or none> + next: run /tickets to file follow-up issues · /build for a fix slice + cadence: off-line / on-demand — run every few build cycles or when you sense drift; + there is no end-of-cycle auto-hook. +``` diff --git a/apps/claude-code/unic-archon-dlc/docs/adr/0027-improve-architecture-skill-superseding.md b/apps/claude-code/unic-archon-dlc/docs/adr/0027-improve-architecture-skill-superseding.md new file mode 100644 index 00000000..4fdbdd6a --- /dev/null +++ b/apps/claude-code/unic-archon-dlc/docs/adr/0027-improve-architecture-skill-superseding.md @@ -0,0 +1,95 @@ +# 0027. `/improve-architecture` is a skill that composes Matt's method and owns ADR superseding + +**Status:** Accepted (2026-07-03) + +## Context + +`/improve-architecture` is the off-line arch-health box ([ADR-0014](0014-workflow-per-box-decomposition.md)): +it surfaces architectural drift + deepening opportunities and **consolidates ADRs, including +superseding** older ones. It runs periodically or on demand, not per-feature and not as an +end-of-cycle auto-hook (PLAN #8). + +The shipped `unic-dlc-cleanup` Archon workflow bundled two off-line concerns behind the inert +`type:`-style schema: an `arch-review` node (technical + intent drift, deepening) and an +`adr-consolidation` node (per-ADR approval gate). The two-axis pivot ([ADR-0017](0017-container-follows-structural-need.md)) +splits arch-health out: because its core is a **design grilling loop** that needs the live +conversation, it is a **Claude Code command/skill**, not an Archon workflow — the same litmus that +kept `/triage` and `/specs` interactive. + +There is already a strong reference implementation of the technical half: Matt Pocock's +`improve-codebase-architecture` skill, which walks the codebase for shallow modules / tight coupling / +leaky abstractions via the deletion test, presents deepening candidates as a self-contained HTML +report, and grills the picked candidate ([ADR-0021](0021-earns-its-place-compose-verbatim.md) says: +reference such a skill verbatim, don't reimplement). Three questions had to be resolved: + +1. **How much to delegate to Matt's skill vs. reproduce** — does this box add enough over the raw skill + to earn its place? +2. **Run scope** — per-slug (intent-grounded against a build session's PRD) or a repo-wide periodic + sweep? +3. **Superseding mechanics** — which ADR homes, and how the index stays consistent. + +These were grilled with the maintainer (2026-07-03); the decisions below are that outcome. + +## Decision + +### 1. Delegate the technical half verbatim; add the DLC layers + +`/improve-architecture` **composes `improve-codebase-architecture` verbatim** for technical drift + +deepening (its `Explore` walk, deletion test, HTML report, before/after diagrams, and `/grilling` loop), +plus `/codebase-design` for the architecture vocabulary and `/domain-modeling` to keep `CONTEXT.md` +current — none of them reimplemented ([ADR-0016](0016-dlc-thin-process-layer.md)/ +[ADR-0021](0021-earns-its-place-compose-verbatim.md)). It **earns its place** ([ADR-0021](0021-earns-its-place-compose-verbatim.md)) +by adding three things Matt's skill lacks: (a) an **intent-drift** pass comparing the PRD's stories + +acceptance criteria against what shipped (harvested from the legacy `arch-review` node's Step 3), (b) a +**durable `arch-review.md`** artifact committed to the repo (Matt's HTML is ephemeral, in the OS temp +dir), and (c) an **ADR-consolidation gate with superseding** (harvested from the legacy +`adr-consolidation` node). It composes team skills for the _how_ and owns the _what_ — the same shape +as `/triage` ([ADR-0024](0024-triage-intake-on-ramp.md)). + +### 2. Two modes, argument-driven + +`/improve-architecture <slug>` runs **per-slug**: it resolves `<artifacts_dir>/<slug>/`, reads that +build session's `PRD.md` / `report.md` / `issues.json`, focuses the technical walk on the slug's changed +surface, and runs the intent-drift pass against the PRD. `/improve-architecture` with **no argument** +runs a **repo-wide sweep** over the whole codebase; with no PRD anchor the intent-drift pass is skipped +and recorded as `n/a`. A missing session file (or a missing config) is non-blocking — the box is +off-line and degrades rather than halting. + +### 3. Durable artifact under the artifacts dir + +Per-slug writes `<artifacts_dir>/<slug>/arch-review.md`; the sweep writes a dated +`<artifacts_dir>/arch-review-YYYY-MM-DD.md` (non-slug) ([ADR-0015](0015-workflows-slug-artifact-home.md)). +Sections: Technical Drift, Intent Drift (or `n/a`), Deepening Opportunities, Summary +(`CLEAN | ISSUES FOUND (count)`). This complements — not replaces — Matt's temp HTML. + +### 4. ADR superseding across both homes, index-aware + +The per-ADR gate (`Accept` / `Reject` / `Edit`) can write to **either ADR home** — the plugin-local +`apps/claude-code/<plugin>/docs/adr/` or the repo-root `docs/adr/` — inferred from what the decision +concerns and confirmed with the user; numbering is the next `NNNN` in the chosen home. Superseding +**never deletes** an ADR: it sets the old file's status to `**Status:** Superseded by ADR-NNNN`, adds a +`Supersedes ADR-NNNN` reference to the new one, and updates the **matching home's** `README.md` index +(old row Status cell → `Superseded by ADR-NNNN`; new row added). This follows the repo-root +`docs/adr/README.md` "Amending records" rule and keeps each home's index self-consistent. + +### 5. No new config block, off-line, no auto-hook + +The box reads `artifacts_dir` + `docs` + `skills.matt_suite` from `.archon/unic-dlc.config.yaml` but +adds **no new config keys** — it is thin ([ADR-0018](0018-generic-core-config-compose.md)) and needs no +tunable knobs. It is inherently HITL (interactive skill box) so it needs no `gates.*` entry. It stays +off the main line with **no end-of-cycle auto-hook** (PLAN #8); consumers run it on a cadence (every few +build cycles / when drift is felt). + +## Consequences + +- **Arch-health is now interactive and intent-grounded.** The technical review reuses Matt's proven + method; the intent-drift pass and durable artifact are DLC additions the raw skill did not provide. +- **No `lib/` or `config-schema.mjs` change**, so no new tests; the existing suite stays green. Behaviour + (lenient config load, per-slug vs sweep routing, superseding across both homes) is validated by + reading, not by CI. +- **The legacy `unic-dlc-cleanup.yaml` still holds inert `arch-review` + `adr-consolidation` content.** + This step **does not touch it** — retiring/rebuilding `/cleanup` as the repo-global operational janitor + is **step 11's** scope. Until then the legacy workflow is dormant (inert schema), not a second live + arch-review. +- **Manual follow-up:** a full end-to-end run against a real slug (and a real superseding of an ADR) is + not asserted by CI; it is exercised on demand by consumers. diff --git a/apps/claude-code/unic-archon-dlc/docs/adr/README.md b/apps/claude-code/unic-archon-dlc/docs/adr/README.md index bf81cf9d..20174864 100644 --- a/apps/claude-code/unic-archon-dlc/docs/adr/README.md +++ b/apps/claude-code/unic-archon-dlc/docs/adr/README.md @@ -32,3 +32,4 @@ Plugin-scoped architectural decisions. Repo-wide decisions live in `docs/adr/` a | 0024 | `/triage` is the intake on-ramp; thin wrapper binds Matt's method to DLC config | Accepted | | 0025 | `/qa` is an Archon pipeline with two config-gated approvals + an issue-producing on-ramp | Accepted | | 0026 | `/pr-review` is a generic fan-out Archon workflow harvesting unic-pr-review's learnings | Accepted | +| 0027 | `/improve-architecture` is a skill composing Matt's method + owns ADR superseding | Accepted | diff --git a/apps/claude-code/unic-archon-dlc/docs/redesign/README.md b/apps/claude-code/unic-archon-dlc/docs/redesign/README.md index a3e48024..7a0f038e 100644 --- a/apps/claude-code/unic-archon-dlc/docs/redesign/README.md +++ b/apps/claude-code/unic-archon-dlc/docs/redesign/README.md @@ -10,22 +10,22 @@ This directory drives the refactor of `unic-archon-dlc` into a **thin, Matt-alig > > Legend: ✅ done · ⏭️ skip (not a build) · ▶️ next · ⬜ todo -| # | Step | Container | Status | PR / notes | -| --- | ----------------------- | ----------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 00 | Archon schema pre-work | — | ✅ done | ADR-0011 (#262) | -| 01 | Foundations | — | ✅ done | ADRs 0012–0021 + AGENTS/CONTEXT sweep (#262) | -| 02 | `/handoff` | Matt's, ref | ⏭️ skip | referenced verbatim, not shipped; dependency declared in `/setup` | -| 03 | `/setup` | skill | ✅ done | conversational skill + `config-schema` lib; rich YAML config (#263) | -| 04 | `/specs` | skill | ✅ done | branch-on-input command; template → `templates.prd`; `specs.{discuss_mode,gate}` (#264) | -| 05 | `/tickets` | skill | ✅ done | command; stops at build-ready `issues.json` (no DAG gen); `tickets` config; ADR-0022 (#265) | -| 06 | `/build` | Archon | ✅ done | keystone — ported to key-discriminated schema as one generic red/green/refactor loop over `issues.json`; dag-builder dissolved; ADR-0023 (PR #266) | -| 07 | `/triage` | skill | ✅ done | thin wrapper over Matt's `triage` method; single-source label binding (DLC config); retires old triage workflow; ADR-0024 (PR #267) | -| 08 | `/qa` | Archon | ✅ done | ported to key-discriminated schema; e2e → coverage → UAT + merge gates (`gates.qa`, `all_done` AFK auto-merge, fail-closed merge); UAT-reject files agent-ready issues; `qa` config block; ADR-0025 (PR #268) | -| 09 | `/pr-review` | Archon | ✅ done | new generic fan-out workflow (7 intent-grounded aspects → synthesize → reconcile → gate → post); harvests unic-pr-review learnings, no dependency; iteration-aware re-review; `pr-review` config; ADR-0026 (PR #269) | -| 10 | `/improve-architecture` | skill | ▶️ next | + ADR superseding | -| 11 | `/cleanup` | command | ⬜ todo | new — repo-global janitor | -| 12 | `/explore` | Archon | ⬜ todo | `/prototype` NOT built (Matt's referenced skill) | -| 13 | Finalize | — | ⬜ todo | reduced — `Unic-dlc.mmd` diagram + dogfood dry-run only (sweep done) | +| # | Step | Container | Status | PR / notes | +| --- | ----------------------- | ----------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 00 | Archon schema pre-work | — | ✅ done | ADR-0011 (#262) | +| 01 | Foundations | — | ✅ done | ADRs 0012–0021 + AGENTS/CONTEXT sweep (#262) | +| 02 | `/handoff` | Matt's, ref | ⏭️ skip | referenced verbatim, not shipped; dependency declared in `/setup` | +| 03 | `/setup` | skill | ✅ done | conversational skill + `config-schema` lib; rich YAML config (#263) | +| 04 | `/specs` | skill | ✅ done | branch-on-input command; template → `templates.prd`; `specs.{discuss_mode,gate}` (#264) | +| 05 | `/tickets` | skill | ✅ done | command; stops at build-ready `issues.json` (no DAG gen); `tickets` config; ADR-0022 (#265) | +| 06 | `/build` | Archon | ✅ done | keystone — ported to key-discriminated schema as one generic red/green/refactor loop over `issues.json`; dag-builder dissolved; ADR-0023 (PR #266) | +| 07 | `/triage` | skill | ✅ done | thin wrapper over Matt's `triage` method; single-source label binding (DLC config); retires old triage workflow; ADR-0024 (PR #267) | +| 08 | `/qa` | Archon | ✅ done | ported to key-discriminated schema; e2e → coverage → UAT + merge gates (`gates.qa`, `all_done` AFK auto-merge, fail-closed merge); UAT-reject files agent-ready issues; `qa` config block; ADR-0025 (PR #268) | +| 09 | `/pr-review` | Archon | ✅ done | new generic fan-out workflow (7 intent-grounded aspects → synthesize → reconcile → gate → post); harvests unic-pr-review learnings, no dependency; iteration-aware re-review; `pr-review` config; ADR-0026 (PR #269) | +| 10 | `/improve-architecture` | skill | ✅ done | command/skill composing Matt's `improve-codebase-architecture` verbatim + DLC layers (intent-drift, durable `arch-review.md`, ADR-superseding gate across both homes); two modes (per-slug / repo-wide sweep); ADR-0027 (PR #270) | +| 11 | `/cleanup` | command | ▶️ next | new — repo-global janitor | +| 12 | `/explore` | Archon | ⬜ todo | `/prototype` NOT built (Matt's referenced skill) | +| 13 | Finalize | — | ⬜ todo | reduced — `Unic-dlc.mmd` diagram + dogfood dry-run only (sweep done) | ## How to run a step From b2e0284abc8beb6ba0eb509f4040dd6ceeef24f8 Mon Sep 17 00:00:00 2001 From: Oriol Torrent Florensa <oriol.torrent@unic.com> Date: Fri, 3 Jul 2026 11:46:13 +0200 Subject: [PATCH 2/5] chore(unic-archon-dlc): bump to 0.9.0 for /improve-architecture Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --- .../unic-archon-dlc/.claude-plugin/marketplace.json | 2 +- .../unic-archon-dlc/.claude-plugin/plugin.json | 2 +- apps/claude-code/unic-archon-dlc/CHANGELOG.md | 11 +++++++++++ apps/claude-code/unic-archon-dlc/package.json | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/claude-code/unic-archon-dlc/.claude-plugin/marketplace.json b/apps/claude-code/unic-archon-dlc/.claude-plugin/marketplace.json index 2134fa87..fca5779f 100644 --- a/apps/claude-code/unic-archon-dlc/.claude-plugin/marketplace.json +++ b/apps/claude-code/unic-archon-dlc/.claude-plugin/marketplace.json @@ -21,7 +21,7 @@ "name": "unic-archon-dlc", "source": "./", "tags": ["productivity", "workflow", "ai-development"], - "version": "0.8.0" + "version": "0.9.0" } ] } diff --git a/apps/claude-code/unic-archon-dlc/.claude-plugin/plugin.json b/apps/claude-code/unic-archon-dlc/.claude-plugin/plugin.json index ed40492e..9d14e3d5 100644 --- a/apps/claude-code/unic-archon-dlc/.claude-plugin/plugin.json +++ b/apps/claude-code/unic-archon-dlc/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "unic-archon-dlc", - "version": "0.8.0", + "version": "0.9.0", "description": "A complete Archon-powered AI development lifecycle as an installable DLC pack — explore, specs, tickets, build, pr-review, qa, triage, improve-architecture, and cleanup workflows.", "author": { "name": "Unic AG", diff --git a/apps/claude-code/unic-archon-dlc/CHANGELOG.md b/apps/claude-code/unic-archon-dlc/CHANGELOG.md index c31950d5..034a42aa 100644 --- a/apps/claude-code/unic-archon-dlc/CHANGELOG.md +++ b/apps/claude-code/unic-archon-dlc/CHANGELOG.md @@ -5,6 +5,17 @@ ### Breaking - (none) +### Added +- (none) + +### Fixed +- (none) + +## [0.9.0] — 2026-07-03 + +### Breaking +- (none) + ### Added - **`/improve-architecture` off-line arch-health command/skill** (ADR-0027) — surfaces technical + intent drift and deepening opportunities, writes a durable `arch-review.md`, and consolidates ADRs **including superseding**. It **composes Matt Pocock's `improve-codebase-architecture` verbatim** (technical drift + deepening HTML report + `/grilling` loop, with `/codebase-design` vocabulary + `/domain-modeling`) and **earns its place** (ADR-0021) by adding three DLC layers the raw skill lacks: an **intent-drift** pass (PRD stories/ACs vs shipped), a **durable artifact** under `<artifacts_dir>/` (ADR-0015), and an **ADR-consolidation gate with superseding**. Two modes: `/improve-architecture <slug>` → intent-grounded against that build session; `/improve-architecture` (no arg) → repo-wide sweep (dated artifact, intent-drift skipped). Superseding spans **both ADR homes** (plugin-local + repo-root), never deletes an ADR (old status → `Superseded by ADR-NNNN`, matching `README.md` index updated). Config load is **lenient** (off-line, touches no tracker); no new config key, no `lib/` change, no auto-hook. Harvests the legacy `unic-dlc-cleanup` `arch-review` + `adr-consolidation` content; does **not** touch that workflow (step 11's scope). diff --git a/apps/claude-code/unic-archon-dlc/package.json b/apps/claude-code/unic-archon-dlc/package.json index c10abc23..102b618f 100644 --- a/apps/claude-code/unic-archon-dlc/package.json +++ b/apps/claude-code/unic-archon-dlc/package.json @@ -1,6 +1,6 @@ { "name": "unic-archon-dlc", - "version": "0.8.0", + "version": "0.9.0", "private": true, "license": "LGPL-3.0-or-later", "type": "module", From 4d2524e17e9aecfcaee2f5db19819dd41090bec0 Mon Sep 17 00:00:00 2001 From: Oriol Torrent Florensa <oriol.torrent@unic.com> Date: Fri, 3 Jul 2026 11:54:29 +0200 Subject: [PATCH 3/5] chore(unic-archon-dlc): vendor composed skills for /improve-architecture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /improve-architecture composes Matt Pocock's codebase-design, grilling, and domain-modeling skills by name, but they were never vendored into .agents/skills/ (absent from develop). Add them so the command's compositions resolve. This also backfills a latent gap for /triage, which already composes /grilling + /domain-modeling. improve-codebase-architecture was already committed, so it is untouched here. Scope is deliberately limited to these three needed skills — the broader skills-lock.json sync + other skill updates remain a separate concern. .agents/skills/ is prettier-ignored, so this is CI-neutral. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --- .agents/skills/codebase-design/DEEPENING.md | 37 ++++++ .../skills/codebase-design/DESIGN-IT-TWICE.md | 44 +++++++ .agents/skills/codebase-design/SKILL.md | 114 ++++++++++++++++++ .agents/skills/domain-modeling/ADR-FORMAT.md | 47 ++++++++ .../skills/domain-modeling/CONTEXT-FORMAT.md | 60 +++++++++ .agents/skills/domain-modeling/SKILL.md | 74 ++++++++++++ .agents/skills/grilling/SKILL.md | 10 ++ 7 files changed, 386 insertions(+) create mode 100644 .agents/skills/codebase-design/DEEPENING.md create mode 100644 .agents/skills/codebase-design/DESIGN-IT-TWICE.md create mode 100644 .agents/skills/codebase-design/SKILL.md create mode 100644 .agents/skills/domain-modeling/ADR-FORMAT.md create mode 100644 .agents/skills/domain-modeling/CONTEXT-FORMAT.md create mode 100644 .agents/skills/domain-modeling/SKILL.md create mode 100644 .agents/skills/grilling/SKILL.md diff --git a/.agents/skills/codebase-design/DEEPENING.md b/.agents/skills/codebase-design/DEEPENING.md new file mode 100644 index 00000000..3938457b --- /dev/null +++ b/.agents/skills/codebase-design/DEEPENING.md @@ -0,0 +1,37 @@ +# Deepening + +How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in [SKILL.md](SKILL.md) — **module**, **interface**, **seam**, **adapter**. + +## Dependency categories + +When assessing a candidate for deepening, classify its dependencies. The category determines how the deepened module is tested across its seam. + +### 1. In-process + +Pure computation, in-memory state, no I/O. Always deepenable — merge the modules and test through the new interface directly. No adapter needed. + +### 2. Local-substitutable + +Dependencies that have local test stand-ins (PGLite for Postgres, in-memory filesystem). Deepenable if the stand-in exists. The deepened module is tested with the stand-in running in the test suite. The seam is internal; no port at the module's external interface. + +### 3. Remote but owned (Ports & Adapters) + +Your own services across a network boundary (microservices, internal APIs). Define a **port** (interface) at the seam. The deep module owns the logic; the transport is injected as an **adapter**. Tests use an in-memory adapter. Production uses an HTTP/gRPC/queue adapter. + +Recommendation shape: *"Define a port at the seam, implement an HTTP adapter for production and an in-memory adapter for testing, so the logic sits in one deep module even though it's deployed across a network."* + +### 4. True external (Mock) + +Third-party services (Stripe, Twilio, etc.) you don't control. The deepened module takes the external dependency as an injected port; tests provide a mock adapter. + +## Seam discipline + +- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a port unless at least two adapters are justified (typically production + test). A single-adapter seam is just indirection. +- **Internal seams vs external seams.** A deep module can have internal seams (private to its implementation, used by its own tests) as well as the external seam at its interface. Don't expose internal seams through the interface just because tests use them. + +## Testing strategy: replace, don't layer + +- Old unit tests on shallow modules become waste once tests at the deepened module's interface exist — delete them. +- Write new tests at the deepened module's interface. The **interface is the test surface**. +- Tests assert on observable outcomes through the interface, not internal state. +- Tests should survive internal refactors — they describe behaviour, not implementation. If a test has to change when the implementation changes, it's testing past the interface. diff --git a/.agents/skills/codebase-design/DESIGN-IT-TWICE.md b/.agents/skills/codebase-design/DESIGN-IT-TWICE.md new file mode 100644 index 00000000..49a7c42a --- /dev/null +++ b/.agents/skills/codebase-design/DESIGN-IT-TWICE.md @@ -0,0 +1,44 @@ +# Design It Twice + +When the user wants to explore alternative interfaces for a chosen deepening candidate, use this parallel sub-agent pattern. Based on "Design It Twice" (Ousterhout) — your first idea is unlikely to be the best. + +Uses the vocabulary in [SKILL.md](SKILL.md) — **module**, **interface**, **seam**, **adapter**, **leverage**. + +## Process + +### 1. Frame the problem space + +Before spawning sub-agents, write a user-facing explanation of the problem space for the chosen candidate: + +- The constraints any new interface would need to satisfy +- The dependencies it would rely on, and which category they fall into (see [DEEPENING.md](DEEPENING.md)) +- A rough illustrative code sketch to ground the constraints — not a proposal, just a way to make the constraints concrete + +Show this to the user, then immediately proceed to Step 2. The user reads and thinks while the sub-agents work in parallel. + +### 2. Spawn sub-agents + +Spawn 3+ sub-agents in parallel using the Agent tool. Each must produce a **radically different** interface for the deepened module. + +Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [DEEPENING.md](DEEPENING.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint: + +- Agent 1: "Minimize the interface — aim for 1–3 entry points max. Maximise leverage per entry point." +- Agent 2: "Maximise flexibility — support many use cases and extension." +- Agent 3: "Optimise for the most common caller — make the default case trivial." +- Agent 4 (if applicable): "Design around ports & adapters for cross-seam dependencies." + +Include both [SKILL.md](SKILL.md) vocabulary and CONTEXT.md vocabulary in the brief so each sub-agent names things consistently with the architecture language and the project's domain language. + +Each sub-agent outputs: + +1. Interface (types, methods, params — plus invariants, ordering, error modes) +2. Usage example showing how callers use it +3. What the implementation hides behind the seam +4. Dependency strategy and adapters (see [DEEPENING.md](DEEPENING.md)) +5. Trade-offs — where leverage is high, where it's thin + +### 3. Present and compare + +Present designs sequentially so the user can absorb each one, then compare them in prose. Contrast by **depth** (leverage at the interface), **locality** (where change concentrates), and **seam placement**. + +After comparing, give your own recommendation: which design you think is strongest and why. If elements from different designs would combine well, propose a hybrid. Be opinionated — the user wants a strong read, not a menu. diff --git a/.agents/skills/codebase-design/SKILL.md b/.agents/skills/codebase-design/SKILL.md new file mode 100644 index 00000000..16620c24 --- /dev/null +++ b/.agents/skills/codebase-design/SKILL.md @@ -0,0 +1,114 @@ +--- +name: codebase-design +description: Shared vocabulary for designing deep modules. Use when the user wants to design or improve a module's interface, find deepening opportunities, decide where a seam goes, make code more testable or AI-navigable, or when another skill needs the deep-module vocabulary. +--- + +# Codebase Design + +Design **deep modules**: a lot of behaviour behind a small interface, placed at a clean seam, testable through that interface. Use this language and these principles wherever code is being designed or restructured. The aim is leverage for callers, locality for maintainers, and testability for everyone. + +## Glossary + +Use these terms exactly — don't substitute "component," "service," "API," or "boundary." Consistent language is the whole point. + +**Module** — anything with an interface and an implementation. Deliberately scale-agnostic: a function, class, package, or tier-spanning slice. _Avoid_: unit, component, service. + +**Interface** — everything a caller must know to use the module correctly: the type signature, but also invariants, ordering constraints, error modes, required configuration, and performance characteristics. _Avoid_: API, signature (too narrow — they refer only to the type-level surface). + +**Implementation** — what's inside a module, its body of code. Distinct from **Adapter**: a thing can be a small adapter with a large implementation (a Postgres repo) or a large adapter with a small implementation (an in-memory fake). Reach for "adapter" when the seam is the topic; "implementation" otherwise. + +**Depth** — leverage at the interface: the amount of behaviour a caller (or test) can exercise per unit of interface they have to learn. A module is **deep** when a large amount of behaviour sits behind a small interface, **shallow** when the interface is nearly as complex as the implementation. + +**Seam** _(Michael Feathers)_ — a place where you can alter behaviour without editing in that place; the *location* at which a module's interface lives. Where to put the seam is its own design decision, distinct from what goes behind it. _Avoid_: boundary (overloaded with DDD's bounded context). + +**Adapter** — a concrete thing that satisfies an interface at a seam. Describes *role* (what slot it fills), not substance (what's inside). + +**Leverage** — what callers get from depth: more capability per unit of interface they learn. One implementation pays back across N call sites and M tests. + +**Locality** — what maintainers get from depth: change, bugs, knowledge, and verification concentrate in one place rather than spreading across callers. Fix once, fixed everywhere. + +## Deep vs shallow + +**Deep module** = small interface + lots of implementation: + +``` +┌─────────────────────┐ +│ Small Interface │ ← Few methods, simple params +├─────────────────────┤ +│ │ +│ Deep Implementation│ ← Complex logic hidden +│ │ +└─────────────────────┘ +``` + +**Shallow module** = large interface + little implementation (avoid): + +``` +┌─────────────────────────────────┐ +│ Large Interface │ ← Many methods, complex params +├─────────────────────────────────┤ +│ Thin Implementation │ ← Just passes through +└─────────────────────────────────┘ +``` + +When designing an interface, ask: + +- Can I reduce the number of methods? +- Can I simplify the parameters? +- Can I hide more complexity inside? + +## Principles + +- **Depth is a property of the interface, not the implementation.** A deep module can be internally composed of small, mockable, swappable parts — they just aren't part of the interface. A module can have **internal seams** (private to its implementation, used by its own tests) as well as the **external seam** at its interface. +- **The deletion test.** Imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep. +- **The interface is the test surface.** Callers and tests cross the same seam. If you want to test *past* the interface, the module is probably the wrong shape. +- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a seam unless something actually varies across it. + +## Designing for testability + +Good interfaces make testing natural: + +1. **Accept dependencies, don't create them.** + + ```typescript + // Testable + function processOrder(order, paymentGateway) {} + + // Hard to test + function processOrder(order) { + const gateway = new StripeGateway(); + } + ``` + +2. **Return results, don't produce side effects.** + + ```typescript + // Testable + function calculateDiscount(cart): Discount {} + + // Hard to test + function applyDiscount(cart): void { + cart.total -= discount; + } + ``` + +3. **Small surface area.** Fewer methods = fewer tests needed. Fewer params = simpler test setup. + +## Relationships + +- A **Module** has exactly one **Interface** (the surface it presents to callers and tests). +- **Depth** is a property of a **Module**, measured against its **Interface**. +- A **Seam** is where a **Module**'s **Interface** lives. +- An **Adapter** sits at a **Seam** and satisfies the **Interface**. +- **Depth** produces **Leverage** for callers and **Locality** for maintainers. + +## Rejected framings + +- **Depth as ratio of implementation-lines to interface-lines** (Ousterhout): rewards padding the implementation. We use depth-as-leverage instead. +- **"Interface" as the TypeScript `interface` keyword or a class's public methods**: too narrow — interface here includes every fact a caller must know. +- **"Boundary"**: overloaded with DDD's bounded context. Say **seam** or **interface**. + +## Going deeper + +- **Deepening a cluster given its dependencies** — see [DEEPENING.md](DEEPENING.md): dependency categories, seam discipline, and replace-don't-layer testing. +- **Exploring alternative interfaces** — see [DESIGN-IT-TWICE.md](DESIGN-IT-TWICE.md): spin up parallel sub-agents to design the interface several radically different ways, then compare on depth, locality, and seam placement. diff --git a/.agents/skills/domain-modeling/ADR-FORMAT.md b/.agents/skills/domain-modeling/ADR-FORMAT.md new file mode 100644 index 00000000..da7e78ec --- /dev/null +++ b/.agents/skills/domain-modeling/ADR-FORMAT.md @@ -0,0 +1,47 @@ +# ADR Format + +ADRs live in `docs/adr/` and use sequential numbering: `0001-slug.md`, `0002-slug.md`, etc. + +Create the `docs/adr/` directory lazily — only when the first ADR is needed. + +## Template + +```md +# {Short title of the decision} + +{1-3 sentences: what's the context, what did we decide, and why.} +``` + +That's it. An ADR can be a single paragraph. The value is in recording *that* a decision was made and *why* — not in filling out sections. + +## Optional sections + +Only include these when they add genuine value. Most ADRs won't need them. + +- **Status** frontmatter (`proposed | accepted | deprecated | superseded by ADR-NNNN`) — useful when decisions are revisited +- **Considered Options** — only when the rejected alternatives are worth remembering +- **Consequences** — only when non-obvious downstream effects need to be called out + +## Numbering + +Scan `docs/adr/` for the highest existing number and increment by one. + +## When to offer an ADR + +All three of these must be true: + +1. **Hard to reverse** — the cost of changing your mind later is meaningful +2. **Surprising without context** — a future reader will look at the code and wonder "why on earth did they do it this way?" +3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons + +If a decision is easy to reverse, skip it — you'll just reverse it. If it's not surprising, nobody will wonder why. If there was no real alternative, there's nothing to record beyond "we did the obvious thing." + +### What qualifies + +- **Architectural shape.** "We're using a monorepo." "The write model is event-sourced, the read model is projected into Postgres." +- **Integration patterns between contexts.** "Ordering and Billing communicate via domain events, not synchronous HTTP." +- **Technology choices that carry lock-in.** Database, message bus, auth provider, deployment target. Not every library — just the ones that would take a quarter to swap out. +- **Boundary and scope decisions.** "Customer data is owned by the Customer context; other contexts reference it by ID only." The explicit no-s are as valuable as the yes-s. +- **Deliberate deviations from the obvious path.** "We're using manual SQL instead of an ORM because X." Anything where a reasonable reader would assume the opposite. These stop the next engineer from "fixing" something that was deliberate. +- **Constraints not visible in the code.** "We can't use AWS because of compliance requirements." "Response times must be under 200ms because of the partner API contract." +- **Rejected alternatives when the rejection is non-obvious.** If you considered GraphQL and picked REST for subtle reasons, record it — otherwise someone will suggest GraphQL again in six months. diff --git a/.agents/skills/domain-modeling/CONTEXT-FORMAT.md b/.agents/skills/domain-modeling/CONTEXT-FORMAT.md new file mode 100644 index 00000000..eaf2a185 --- /dev/null +++ b/.agents/skills/domain-modeling/CONTEXT-FORMAT.md @@ -0,0 +1,60 @@ +# CONTEXT.md Format + +## Structure + +```md +# {Context Name} + +{One or two sentence description of what this context is and why it exists.} + +## Language + +**Order**: +{A one or two sentence description of the term} +_Avoid_: Purchase, transaction + +**Invoice**: +A request for payment sent to a customer after delivery. +_Avoid_: Bill, payment request + +**Customer**: +A person or organization that places orders. +_Avoid_: Client, buyer, account +``` + +## Rules + +- **Be opinionated.** When multiple words exist for the same concept, pick the best one and list the others under `_Avoid_`. +- **Keep definitions tight.** One or two sentences max. Define what it IS, not what it does. +- **Only include terms specific to this project's context.** General programming concepts (timeouts, error types, utility patterns) don't belong even if the project uses them extensively. Before adding a term, ask: is this a concept unique to this context, or a general programming concept? Only the former belongs. +- **Group terms under subheadings** when natural clusters emerge. If all terms belong to a single cohesive area, a flat list is fine. + +## Single vs multi-context repos + +**Single context (most repos):** One `CONTEXT.md` at the repo root. + +**Multiple contexts:** A `CONTEXT-MAP.md` at the repo root lists the contexts, where they live, and how they relate to each other: + +```md +# Context Map + +## Contexts + +- [Ordering](./src/ordering/CONTEXT.md) — receives and tracks customer orders +- [Billing](./src/billing/CONTEXT.md) — generates invoices and processes payments +- [Fulfillment](./src/fulfillment/CONTEXT.md) — manages warehouse picking and shipping + +## Relationships + +- **Ordering → Fulfillment**: Ordering emits `OrderPlaced` events; Fulfillment consumes them to start picking +- **Fulfillment → Billing**: Fulfillment emits `ShipmentDispatched` events; Billing consumes them to generate invoices +- **Ordering ↔ Billing**: Shared types for `CustomerId` and `Money` +``` + +The skill infers which structure applies: + +- If `CONTEXT-MAP.md` exists, read it to find contexts +- If only a root `CONTEXT.md` exists, single context +- If neither exists, create a root `CONTEXT.md` lazily when the first term is resolved + +When multiple contexts exist, infer which one the current topic relates to. If unclear, ask. diff --git a/.agents/skills/domain-modeling/SKILL.md b/.agents/skills/domain-modeling/SKILL.md new file mode 100644 index 00000000..d0f7e1a5 --- /dev/null +++ b/.agents/skills/domain-modeling/SKILL.md @@ -0,0 +1,74 @@ +--- +name: domain-modeling +description: Build and sharpen a project's domain model. Use when the user wants to pin down domain terminology or a ubiquitous language, record an architectural decision, or when another skill needs to maintain the domain model. +--- + +# Domain Modeling + +Actively build and sharpen the project's domain model as you design. This is the *active* discipline — challenging terms, inventing edge-case scenarios, and writing the glossary and decisions down the moment they crystallise. (Merely *reading* `CONTEXT.md` for vocabulary is not this skill — that's a one-line habit any skill can do. This skill is for when you're changing the model, not just consuming it.) + +## File structure + +Most repos have a single context: + +``` +/ +├── CONTEXT.md +├── docs/ +│ └── adr/ +│ ├── 0001-event-sourced-orders.md +│ └── 0002-postgres-for-write-model.md +└── src/ +``` + +If a `CONTEXT-MAP.md` exists at the root, the repo has multiple contexts. The map points to where each one lives: + +``` +/ +├── CONTEXT-MAP.md +├── docs/ +│ └── adr/ ← system-wide decisions +├── src/ +│ ├── ordering/ +│ │ ├── CONTEXT.md +│ │ └── docs/adr/ ← context-specific decisions +│ └── billing/ +│ ├── CONTEXT.md +│ └── docs/adr/ +``` + +Create files lazily — only when you have something to write. If no `CONTEXT.md` exists, create one when the first term is resolved. If no `docs/adr/` exists, create it when the first ADR is needed. + +## During the session + +### Challenge against the glossary + +When the user uses a term that conflicts with the existing language in `CONTEXT.md`, call it out immediately. "Your glossary defines 'cancellation' as X, but you seem to mean Y — which is it?" + +### Sharpen fuzzy language + +When the user uses vague or overloaded terms, propose a precise canonical term. "You're saying 'account' — do you mean the Customer or the User? Those are different things." + +### Discuss concrete scenarios + +When domain relationships are being discussed, stress-test them with specific scenarios. Invent scenarios that probe edge cases and force the user to be precise about the boundaries between concepts. + +### Cross-reference with code + +When the user states how something works, check whether the code agrees. If you find a contradiction, surface it: "Your code cancels entire Orders, but you just said partial cancellation is possible — which is right?" + +### Update CONTEXT.md inline + +When a term is resolved, update `CONTEXT.md` right there. Don't batch these up — capture them as they happen. Use the format in [CONTEXT-FORMAT.md](./CONTEXT-FORMAT.md). + +`CONTEXT.md` should be totally devoid of implementation details. Do not treat `CONTEXT.md` as a spec, a scratch pad, or a repository for implementation decisions. It is a glossary and nothing else. + +### Offer ADRs sparingly + +Only offer to create an ADR when all three are true: + +1. **Hard to reverse** — the cost of changing your mind later is meaningful +2. **Surprising without context** — a future reader will wonder "why did they do it this way?" +3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons + +If any of the three is missing, skip the ADR. Use the format in [ADR-FORMAT.md](./ADR-FORMAT.md). diff --git a/.agents/skills/grilling/SKILL.md b/.agents/skills/grilling/SKILL.md new file mode 100644 index 00000000..fe3569db --- /dev/null +++ b/.agents/skills/grilling/SKILL.md @@ -0,0 +1,10 @@ +--- +name: grilling +description: Interview the user relentlessly about a plan or design. Use when the user wants to stress-test a plan before building, or uses any 'grill' trigger phrases. +--- + +Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer. + +Ask the questions one at a time, waiting for feedback on each question before continuing. Asking multiple questions at once is bewildering. + +If a question can be answered by exploring the codebase, explore the codebase instead. From 6c80c39a25aa50ddb51ccf2a5b95bc35f648d5fa Mon Sep 17 00:00:00 2001 From: Oriol Torrent Florensa <oriol.torrent@unic.com> Date: Fri, 3 Jul 2026 12:03:24 +0200 Subject: [PATCH 4/5] chore(unic-archon-dlc): lock the 3 vendored skills in skills-lock.json Restore the repo's lock+vendor pairing (every skill is both locked and vendored, per caveman/diagnose) for the codebase-design/grilling/domain-modeling dirs added for /improve-architecture. Entries are copied verbatim (source + path + computedHash) from the tool-generated worktree lock, so the later full skills sync merges cleanly (identical lines). Only these 3 entries are added; the rest of the pending sync stays uncommitted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --- skills-lock.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/skills-lock.json b/skills-lock.json index d9c16b61..39650c38 100644 --- a/skills-lock.json +++ b/skills-lock.json @@ -7,12 +7,24 @@ "skillPath": "skills/productivity/caveman/SKILL.md", "computedHash": "934433479903febc585bf6deb5f0cebc63137e3f86b7babe0aab1ecb94d6d7a4" }, + "codebase-design": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/codebase-design/SKILL.md", + "computedHash": "2426dc4accf1a85dedfb560edb3a877ec8828b7375ea08c970df2b6c900a2a22" + }, "diagnose": { "source": "mattpocock/skills", "sourceType": "github", "skillPath": "skills/engineering/diagnose/SKILL.md", "computedHash": "15939a26f86edec2d4862042b8564e5a062cb81d04e047a0cea6305c8830b5f5" }, + "domain-modeling": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/domain-modeling/SKILL.md", + "computedHash": "67343881f5def98487d56243155716110afbcbf22ec92421c882d532b941cb17" + }, "grill-me": { "source": "mattpocock/skills", "sourceType": "github", @@ -25,6 +37,12 @@ "skillPath": "skills/engineering/grill-with-docs/SKILL.md", "computedHash": "1adf321072f53cce3dcaf5357d91b8230d4aa647bb8a51756745337a6ee567b8" }, + "grilling": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/productivity/grilling/SKILL.md", + "computedHash": "ef685a6fa0bbe73b05bbd8dc1ec97258191587f07c6aa7dbc9006675ceb8f90f" + }, "handoff": { "source": "mattpocock/skills", "sourceType": "github", From 5c38c72f88a0a9be909bdab758e249a221e1b289 Mon Sep 17 00:00:00 2001 From: Oriol Torrent Florensa <oriol.torrent@unic.com> Date: Fri, 3 Jul 2026 12:16:07 +0200 Subject: [PATCH 5/5] docs(unic-archon-dlc): defer all ADR writing in /improve-architecture to Step 6 (Copilot review) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step 3 delegates to Matt's improve-codebase-architecture, which offers to write ADRs inline for load-bearing rejections. Make that offer capture-only: no ADR is written/numbered in Step 3, and the delegated skill must not write one either — proposed decisions are captured as candidates for the Step 6 gate. Step 6 is the single place ADRs are written, so the A/R/E gate, MADR-lite format, both-homes numbering, and superseding/index mechanics always apply (prevents format/process drift and gate bypass). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --- .../unic-archon-dlc/commands/improve-architecture.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/claude-code/unic-archon-dlc/commands/improve-architecture.md b/apps/claude-code/unic-archon-dlc/commands/improve-architecture.md index fadc3b9a..27505e9c 100644 --- a/apps/claude-code/unic-archon-dlc/commands/improve-architecture.md +++ b/apps/claude-code/unic-archon-dlc/commands/improve-architecture.md @@ -122,8 +122,13 @@ Bind that method to the DLC: `report.md` and/or the build's diff (e.g. `git diff --name-only <base>..HEAD`, adapting the range) — but keep Matt's method and still surface repo-wide friction the change touches. - **Repo-wide mode:** the full-codebase walk Matt's skill already performs. -- Note any candidate the grill loop marks "accept as ADR" or any load-bearing rejection worth - recording — these feed Step 6. +- **Defer every ADR to Step 6 — do not write one here.** Matt's method offers to record a + load-bearing rejection as an ADR inline (using its own template); in the DLC that offer is + **capture-only**. Do **not** accept, number, or write any ADR during Step 3, and do not let the + delegated skill write one — instead record each proposed decision (the "accept as ADR" candidates + and load-bearing rejections) as a candidate and hand it to Step 6. Step 6 is the **single place** + ADRs are written, so the A/R/E gate, MADR-lite format, both-homes numbering, and superseding/index + mechanics always apply and ADRs can never drift into an inconsistent format or bypass the gate. ## Step 4 — Intent drift (DLC value-add — per-slug mode only)