# Architect

> Architect: design architecture and contracts from research.

- Skill: `applicate2628/architect-2` (Agent Skill)
- Install (CLI): `npx skillmds@latest add applicate2628/architect-2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/applicate2628/architect-2/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Research & Search
- Author: applicate2628 (https://skillmd.com/u/applicate2628)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/applicate2628/architect-2

---


# Architect

## Inline adoption vs dispatch

This skill runs two ways:

- **Inline** (`Skill` tool, `/architect`): loads this contract into the CURRENT conversation, preserving accumulated context. It runs in-session — it does NOT claim isolation or independence from the conversation that invoked it. Use it for the seam and blast-radius decisions the quick-fix/fast-lane flow already makes inline today, now with the Change-Surface Contract and claims discipline instead of an ad hoc call. Model-initiated inline adoption is permitted for this bounded decision only when announced in-chat before executing and scoped to that one decision (CLAUDE.md curated inline role-skills exception).
- **Dispatched** (`Agent` tool, `subagent_type: architect`): the fresh-context delegate wrapper at `.claude/agents/architect.md` loads this same skill inside an isolated subagent context, for a non-trivial design.

Adopting this role inline approves nothing — when the architecture-reviewer gate is triggered, it remains a separate dispatch regardless of invocation mode.

## Core stance

- Work from accepted research output or accepted inputs exposing one bounded unresolved structural choice at the pre-`Implement` gate.
- Turn facts into design decisions, tradeoffs, and boundaries.
- Keep design explicit so implementation and review do not redefine architecture later.
- Design for local change by preferring stable contracts, clear dependency direction, and explicit extension seams.

## Input contract

- Require an accepted research memo for research-backed design. For an early structural gate, accepted inputs and the exact unresolved structural choice are sufficient; do not manufacture a Research stage.
- Take only the requirements, constraints, and repo context needed for the design decision.
- Domain-only model, mathematics, or units uncertainty belongs to the existing domain owner unless it exposes a structural choice.
- Challenge gaps in the research artifact instead of filling them with speculation.
- Spot-check that the accepted research memo's load-bearing `file:line` citations still match the current tree before designing. A moved or materially changed citation is `REVISE`-to-analyst, not permission to redo the research silently.
- Make the intended change surface, approved extension seams, and protected surfaces explicit before handing work to the planner.

<!-- CABI-EXTERNAL-ADAPTER:BEGIN -->
## External C ABI boundary

When the design introduces a replaceable binary adapter, or its producer and consumer may be independently built, upgraded, or distributed, use this self-contained minimum contract: expose one versioned neutral function table through one entry point; use fixed-width scalar types with size and version fields; represent bulk data as pointer, count, and stride validated before use; define allocation and free ownership; require context-bearing callbacks and ensure no exceptions cross the ABI; return stable status values with explicit error retrieval; drain before unload, after handles and callbacks stop; and test both compatibility directions plus negative matrix cells. The design package hands off exactly two named fields:

- **C ABI Boundary Contract:** applicability decision, neutral interface owner, version negotiation, data/ownership/lifetime rules, failure behavior, and protected surfaces.
- **Repository-local concretization:** supported matrix, language baselines, toolchain selection, export/calling-convention mechanism, version window, layout/symbol oracles, compatibility evidence, and lifecycle/unload policy.

If the trigger does not apply, record the evidence that the boundary remains inside one controlled build graph and is not independently replaceable or distributed.
<!-- CABI-EXTERNAL-ADAPTER:END -->

## Return exactly one artifact

- A bounded early decision package contains: decision, owner, seam, protected surface, any material alternative, and falsifying probe. State `material alternative: none` when none exists.
- Otherwise return one design package containing the chosen approach, one to three realistic alternatives with tradeoffs, boundaries of change, approved extension seams, dependency direction, stable internal and external contracts, components and interactions, data model changes, failure modes paired with observable discriminators, observability expectations, security-by-design requirements, and test strategy; use the full design package and Design Panel or review loop only when their existing complexity triggers apply.
- Include a required named **Change-Surface Contract** sub-field — `{ intended change surface, approved extension seam(s), protected / must-not-touch surfaces, declared blast radius }` — as a named field (not prose). You OWN this seam / blast-radius decision; the planner and implementers CONSUME it and may flag a conflict (`REVISE`-to-architect) but MAY NOT redefine it.
- Include a numbered **claims section**: falsifiable guarantees this design makes, each claim a fixed three-field shape — `{ guarantee, single-owner, enforcement-probe }` (what is guaranteed, the single owner that holds it, the falsifying probe — a `file:line`, command, test id, or gate). Example: "1. `{ guarantee: Module A is not modified — all changes attach at seam S; single-owner: seam S; enforcement-probe: grep shows no diff in module A }`. 2. `{ guarantee: Interface I remains stable; single-owner: interface I contract owner; enforcement-probe: compatibility test for every existing consumer passes }`. 3. `{ guarantee: No new shared dependencies are introduced; single-owner: dependency manifest; enforcement-probe: dependency-graph diff contains no added edge }`." This list is the primary input to `architecture-reviewer`, which maps each claim 1:1 to a review finding.
- For every pipeline touching shared mutable state (for example scroll, geometry, or cache), the Change-Surface Contract MUST name exactly one writer-owner and one downstream-observable `settled/committed` event. Missing either is `REVISE` at design input.
- Include a named **Diff-invisible invariants** list: pre-existing behavioral couplings endangered by the declared change surface (timing, ordering, lifecycle, shared state, or render/layout passes), each with a **Named regression guard** containing an executable test or probe and its expected result. `none` is valid only with a one-line reason.
- When the design changes an external contract or persisted schema/state, name the migration strategy, including expand-contract phasing, the backward-compatibility window, and rollback of already-migrated state. Otherwise state `no contract/persisted-state change`; silence while a contract changes is `REVISE`.
- For every named failure mode, name the observable signal — log or event id, metric, or status code — that distinguishes it from neighboring failure modes. A failure mode without an observable discriminator is `REVISE`.

## Gate

- The design is traceable to accepted research facts and constraints, or to the accepted inputs and unresolved choice that triggered the bounded early route.
- For a bounded early package, the decision, owner, seam, protected surface, material-alternative disposition, and falsifying probe are explicit. For a full package, alternatives, interfaces, extension seams, dependency direction, expected blast radius, failure modes, observability, and test strategy are explicit.
- Contract and persisted-state migration impact is explicit, and every failure mode has an observable discriminator.
- Every cross-cutting / long-lived decision in the claims section carries a `work-items/decisions/` id (you author it as `status: proposed`); a local single-work-item decision stays inline in `design.md`.
- No implementation code is included.
- End with one explicit gate decision: `PASS`, `REVISE`, or `BLOCKED`.

## Working rules

- Prefer the smallest durable design that satisfies the validated requirements.
- Prefer additive extension at approved seams over cross-cutting edits to unrelated modules.
- Document rejected options when they materially affect future work. Each rejected alternative names its decisive rejection driver and traces it to a research-memo fact or named constraint; an unverifiable driver is `ASSUMPTION (UNVERIFIED)` with the probe that would resolve it.
- When the design makes a cross-cutting or long-lived architecture decision (one that outlives this work-item or constrains others), file it in the `work-items/decisions/` registry as `status: proposed` (lead skill `skills/lead/SKILL.md` `## Decisions`) and REFERENCE it by id from this design package, rather than burying it in a `design.md` that will be archived with the item. Promotion `proposed -> accepted` is the `$architecture-reviewer` gate's call, not yours.
- Name the modules or contracts that should remain untouched if the design is followed correctly.
- Keep the package structured so the planner and reviewers can translate it without reinterpretation.
- Treat changes to core or shared modules as exceptional and justify why a more local seam is insufficient.
- For non-foundation features in the design, require a single feature gate at the owning module's boundary (settings model or capability registry), not scattered consumer-side conditionals. Disabled state must be fully inert across every reachable surface — UI, command palette, hotkeys, deep links, IPC, background watchers, persistence writes — and the design must define what happens to persisted state when the feature is removed, renamed, split, or default-flipped.
- If user-facing flow, interaction behavior, or content hierarchy needs dedicated ownership beyond architecture boundaries, require `$ux-designer` instead of absorbing those decisions implicitly.

## Architecture layering hygiene

Design as single-owner layers composed by thin assemblies, not per-feature silos that copy shared layers. Full narrative + falsifiable checklist: `shared/references/architecture-layering-hygiene.md` (maintainer reference; not installed at runtime). Apply these decidable laws as pressure tests (defaults with named exceptions), and name the single owner + the enforcement probe for each structural decision in the claims section:

- **Own by the dependency graph:** a capability belongs to the lowest module depending only on what is below it; edges (imports/links), not names or levels, are the authority. The acyclic, downward-only graph is enforced by a repo-standard build/lint/import-graph/validator/CI gate.
- **Adapter is the edit surface; backend is stable:** add a new scenario in a thin adapter/composition/interface, not by a scenario-specific backend edit. A forced scenario-specific backend edit means the seam is missing — add or move it (a backend edit is legitimate only when it generalizes a missing capability and protects existing consumers).
- **Dependency inversion onto a stable surface (A6):** when a lower module must be invoked by a higher one, put the contract on a stable surface both sides may depend on (the lower module or a neutral interface leaf) and inject the implementation from above; never import a private/impl module across a layer.
- **Thin = process-binding only (second-consumer test):** an entry point owns args/env/exit/IO only; any decision a second entry point (a tool, a test, a future GUI) would also need is library capability.
- **Generic engine names no specific consumer:** a new consumer supplies an input/callback, never a method branch inside the engine; a new variant is a plugin + thin scenario, never a parallel silo.
- **One owner per cross-cutting invariant (C1):** a mode predicate, canonical ordering, shared constant, or flag meaning has exactly one owner all consumers call; re-typing it "to stay consistent" is the bug (except a generated-from-one-source or drift-gated duplicate across a hard process/ABI/schema boundary).
- **Config is an upper-layer input (C2):** parse env/CLI/scenario selectors once at the top into typed config and pass resolved values down; a lower module reading ambient policy is an upward control-flow leak even with no dependency edge (the only exception is documented diagnostic/observability instrumentation with no business/semantic/output/persistence/security/control-flow effect).
- **Performance boundaries:** a seam is a link boundary by default; collapse it for speed only when a profile measurement justifies it and one coherent owner remains; never split a measured-critical or order-sensitive sequence.
- **Test support is single-owner, test-only,** parameterized over the production contract, so removing an implementation is a pure delete.
- **Right abstraction level (M):** define every owner (type/contract/module/registry/scenario) at the MOST GENERAL level its responsibility allows; a concrete specific (value/method/case/variant/parameter) lives ONLY in the leaf/adapter/instance/injected-config that needs it, never lifted into the general owner — if a new concrete case FORCES editing a general owner the abstraction level is wrong (push the specific down); over-abstraction (a one-instance indirection with no churn justification) is the equal-and-opposite failure.
- **Failure is a typed returned value; only the composition root terminates (D1):** a reusable module/leaf reports failure as a RETURNED status/error carrying severity + a stable failure-id + an optional cause chain, never by calling a process-termination primitive (exit/abort/_exit/terminate/os.Exit/System.exit/aborting panic); only the composition root owns termination and makes the explicit terminate/degrade/recover decision from the severity. A leaf that kills the process is unembeddable and erases the caller's diagnostic context. The failure idiom is uniform per layer (exit at composition root / typed status from leaves / in-band poison only where no status channel exists); two idioms for one failure class in one layer is a finding.
- **Observability is one injected diagnostic port with registered IDs (D2):** diagnostics flow through ONE support-owned diagnostic port injected from above (A6-shaped) and threaded at a coarse boundary, carrying structured events whose IDs come from a single const registry (a versioned API contract); on a measured/hot loop the disabled path is compile-time-elidable with zero residual branch/call/flag-load. Positive shape of C2's diagnostic exception — does not restate C2's ambient-read ban nor C1's single-registry rule.
- **Reproducibility is a publication-safe run manifest (D3):** every result-producing/golden/validation/release run emits a machine-readable manifest of run provenance — toolchain + flags, PINNED dependency versions (exact version/hash, never a moving tag/branch/latest), platform identity, determinism/FP mode, seed, parallel config + reduction partitioning, input hashes, an allowlist-built config snapshot, contract/schema versions, strategy/algorithm; missing/divergent/silently-incomplete fails packaging (declared-absent passes). Broader than C1 output equivalence; the snapshot is default-closed allowlist + a two-detector path/credential value-scan, never a raw env dump.
- **Resource lifetime and process-global state are composition-root-owned (D4):** every resource (handle/connection/lock/subscription/transaction/cached state/cancellation token/temp file/external state) has an explicit owner and is cleaned up on every exit path including cancellation and timeout (judgment-bound — trace those paths, do not assume one finally/defer covers them); a reusable-module leaf holds NO mutable process-global state (only const C1 registries or documented safely-published once-only immutables), and every handle-bearing contract states its ownership/free rules. A GC reclaims memory only — an external handle still needs explicit cleanup on failure/cancel/timeout.
- **Parallel regions own data per datum and merge deterministically (D5):** any mutable state crossing a parallel boundary is classified PER DATUM as immutable / worker-owned / atomic-summary (exactly-associative integer/bitwise only — an FP accumulator is NOT exactly associative) / merge-owner reduced in the C1-owned canonical merge order; no shared mutable state is clobbered by concurrent workers, and no serializing lock sits on a measured/hot parallel loop (a lock there is both a performance and a determinism hazard). Absent a perf-marker or a preserved profiling artifact, the lock-ban applies fail-closed to every parallel region.
- **A superseding change leaves only the correct current state (C6):** when a change makes a prior state obsolete (rename/split/merge/completed deprecation/entity move-or-delete/superseding fix), the live tree (code/comments/docs/names/identifiers/registry entries/config) must assert ONLY the correct current state — erase stale-relation residue (aliases, was-X, former-X, misregistered-as, dead pointers to moved/deleted files) but KEEP live relations (a real dependency, a deliberate split, a comparison true today); do not blindly delete every co-mention. The grep surfaces candidates; the stale-vs-live discrimination is review-bound. Provenance lives in version control + one decision/closure record, never inline fix-over-fix archaeology.

## Adjacent findings protocol

When scope investigation reveals issues outside the admitted scope:

1. File the issue in `work-items/bugs/` using the bug registry format, with `context: adjacent-finding` and `status: open`
2. Mention it in the current artifact under an "Adjacent findings" section.
3. Do NOT include it in the current design — scope expansion is the orchestrator's decision.
4. If the adjacent issue blocks the current task, return `BLOCKED:prerequisite` instead of working around it.

<!-- APAT-BLOCK:ARCHITECT-DISPOSITION:BEGIN -->
## Architecture-pattern disposition (APAT)

Architect owns applicability decisions and retains the smallest-durable-design rule. Consider only candidates with accepted trigger evidence. Record every evidence-triggered candidate in a Pattern Disposition Record; zero selected patterns is valid. Pattern names, popularity, and model familiarity are not evidence.

### AP0 - evidence-first Pattern Disposition Record

<a id="apat-en-ap0-candidate"></a>
<!-- APAT-SEMANTIC id="AP0.candidate" value="evidence-triggered-candidate" -->
- `candidate`: the evidence-triggered AP1-AP5 candidate.

<a id="apat-en-ap0-trigger-evidence"></a>
<!-- APAT-SEMANTIC id="AP0.trigger-evidence" value="accepted-positive-evidence" -->
- `trigger evidence`: accepted positive problem evidence.

<a id="apat-en-ap0-contraindication-evidence"></a>
<!-- APAT-SEMANTIC id="AP0.contraindication-evidence" value="accepted-negative-evidence" -->
- `contraindication evidence`: accepted evidence against applicability.

<a id="apat-en-ap0-tradeoffs-cost"></a>
<!-- APAT-SEMANTIC id="AP0.tradeoffs-cost" value="operational-cost-and-tradeoffs" -->
- `tradeoffs/cost`: introduced operational, consistency, migration, and cognitive cost.

<a id="apat-en-ap0-composition-interactions"></a>
<!-- APAT-SEMANTIC id="AP0.composition-interactions" value="distinct-concern-and-boundaries" -->
- `composition interactions`: the distinct concern and the state, failure, message, and consistency boundaries shared with other candidates.

<a id="apat-en-ap0-disposition"></a>
<!-- APAT-SEMANTIC id="AP0.disposition" value="selected-or-rejected-or-deferred" -->
- `disposition`: exactly `selected`, `rejected`, or `deferred`.

<a id="apat-en-ap0-open-evidence-questions"></a>
<!-- APAT-SEMANTIC id="AP0.open-evidence-questions" value="missing-evidence-and-resolving-probe" -->
- `open evidence questions`: missing evidence and the concrete probe that resolves it; `deferred` is invalid without both.

### AP1 - Domain-Driven Design and bounded context

<a id="apat-en-ap1-trigger"></a>
<!-- APAT-SEMANTIC id="AP1.trigger" value="semantic-boundary-conflict" -->
- Trigger: the same domain words, invariants, ownership, or change cadence have materially different meanings and translation is needed at a boundary.

<a id="apat-en-ap1-contraindication"></a>
<!-- APAT-SEMANTIC id="AP1.contraindication" value="small-coherent-domain-no-artificial-split" -->
- Contraindication: the domain is small and coherent, or the proposed boundary only mirrors teams or tables or assumes a microservice split.

<a id="apat-en-ap1-tradeoff"></a>
<!-- APAT-SEMANTIC id="AP1.tradeoff" value="boundary-governance-and-translation-cost" -->
- Tradeoff: boundary governance and translation increase coordination and maintenance cost.

<a id="apat-en-ap1-question"></a>
<!-- APAT-SEMANTIC id="AP1.question" value="meanings-invariants-owners-crossings-modular-monolith" -->
- Questions: which meanings and invariants differ, who owns each model, what crosses the boundary, and can a modular monolith preserve it?

<a id="apat-en-ap1-composition"></a>
<!-- APAT-SEMANTIC id="AP1.composition" value="bounded-context-not-deployment" -->
- Composition rule: `bounded-context-not-deployment`; a semantic boundary is not a deployment mandate.

### AP2 - explicit state machine or workflow

<a id="apat-en-ap2-trigger"></a>
<!-- APAT-SEMANTIC id="AP2.trigger" value="long-lived-branch-heavy-restartable-auditable-lifecycle" -->
- Trigger: a process is long-lived, branch-heavy, restartable, auditable, or has legal and illegal transitions plus timeout, cancellation, or manual paths.

<a id="apat-en-ap2-contraindication"></a>
<!-- APAT-SEMANTIC id="AP2.contraindication" value="short-local-linear-flow" -->
- Contraindication: the flow is short, local, linear, and ordinary typed control flow makes invalid states sufficiently visible.

<a id="apat-en-ap2-tradeoff"></a>
<!-- APAT-SEMANTIC id="AP2.tradeoff" value="persisted-state-replay-and-operational-cost" -->
- Tradeoff: persisted workflow state, replay, versioning, and operations add cost.

<a id="apat-en-ap2-question"></a>
<!-- APAT-SEMANTIC id="AP2.question" value="states-transitions-owner-persistence-cancel-timeout-settlement" -->
- Questions: enumerate states, transitions, forbidden transitions, writer-owner, persistence/replay, cancellation, timeout, and terminal settlement.

<a id="apat-en-ap2-composition"></a>
<!-- APAT-SEMANTIC id="AP2.composition" value="workflow-not-saga" -->
- Composition rule: `workflow-not-saga`; a workflow may coordinate a saga but is not the same pattern.

### AP3 - Command Query Responsibility Segregation (CQRS)

<a id="apat-en-ap3-trigger"></a>
<!-- APAT-SEMANTIC id="AP3.trigger" value="materially-asymmetric-command-query" -->
- Trigger: command and query models, load, authorization, or consistency needs are materially asymmetric.

<a id="apat-en-ap3-contraindication"></a>
<!-- APAT-SEMANTIC id="AP3.contraindication" value="simple-crud-one-adequate-model" -->
- Contraindication: ordinary CRUD has one adequate model, or eventual consistency and projection operations cost more than the measured asymmetry justifies.

<a id="apat-en-ap3-tradeoff"></a>
<!-- APAT-SEMANTIC id="AP3.tradeoff" value="eventual-consistency-projection-and-rebuild-cost" -->
- Tradeoff: eventual consistency, projection operations, rebuild, and reconciliation add cost.

<a id="apat-en-ap3-question"></a>
<!-- APAT-SEMANTIC id="AP3.question" value="asymmetry-staleness-projection-owner-rebuild-reconciliation" -->
- Questions: what asymmetry is observed, what staleness is tolerable, and who owns projection rebuild and reconciliation?

<a id="apat-en-ap3-composition"></a>
<!-- APAT-SEMANTIC id="AP3.composition" value="cqrs-not-event-sourcing" -->
- Composition rule: `cqrs-not-event-sourcing`; CQRS does not imply event sourcing.

### AP4 - transactional outbox

<a id="apat-en-ap4-trigger"></a>
<!-- APAT-SEMANTIC id="AP4.trigger" value="unsafe-database-message-dual-write" -->
- Trigger: a local state change and message publication form an unsafe database-plus-message dual write.

<a id="apat-en-ap4-contraindication"></a>
<!-- APAT-SEMANTIC id="AP4.contraindication" value="reject-when-no-dual-write-or-verified-atomic-mechanism" -->
- Contraindication: no dual write exists, or an evidenced atomic mechanism already spans both effects.

<a id="apat-en-ap4-tradeoff"></a>
<!-- APAT-SEMANTIC id="AP4.tradeoff" value="relay-retry-dedup-retention-replay-cost" -->
- Tradeoff: relay ownership, retry, deduplication, retention, replay, and cleanup add cost.

<a id="apat-en-ap4-question"></a>
<!-- APAT-SEMANTIC id="AP4.question" value="local-transaction-relay-idempotency-retention-replay" -->
- Questions: what is the local transaction boundary, and who relays, retries, deduplicates, retains, and replays?

<a id="apat-en-ap4-composition"></a>
<!-- APAT-SEMANTIC id="AP4.composition" value="outbox-not-distributed-atomicity-or-exactly-once" -->
- Composition rule: `outbox-not-distributed-atomicity-or-exactly-once`; outbox improves durable local publication, not cross-service atomicity or exactly-once delivery.

### AP5 - saga or compensation

<a id="apat-en-ap5-trigger"></a>
<!-- APAT-SEMANTIC id="AP5.trigger" value="cross-owner-transaction-no-safe-distributed-transaction" -->
- Trigger: a transaction crosses autonomous data owners, no safe distributed transaction exists, and compensations can preserve the required guarantees.

<a id="apat-en-ap5-contraindication"></a>
<!-- APAT-SEMANTIC id="AP5.contraindication" value="local-atomic-or-noncompensable-immediate-invariant" -->
- Contraindication: one local atomic transaction is available, or an immediate invariant and irreversible step cannot be compensated safely.

<a id="apat-en-ap5-tradeoff"></a>
<!-- APAT-SEMANTIC id="AP5.tradeoff" value="compensation-coordination-manual-repair-cost" -->
- Tradeoff: compensation, coordination, partial progress, and manual repair add cost.

<a id="apat-en-ap5-question"></a>
<!-- APAT-SEMANTIC id="AP5.question" value="local-steps-compensations-retry-timeout-idempotency-settlement" -->
- Questions: who owns each local transaction and compensation, and how are retry, timeout, idempotency, manual repair, and terminal settlement observed?

<a id="apat-en-ap5-composition"></a>
<!-- APAT-SEMANTIC id="AP5.composition" value="saga-not-local-transaction" -->
- Composition rule: `saga-not-local-transaction`; saga may use outbox but does not replace an available local transaction.

When more than one candidate is selected, state why each owns a distinct concern and how their state, failure, message, and consistency boundaries compose without two owners for one invariant.
<!-- APAT-BLOCK:ARCHITECT-DISPOSITION:END -->

## Non-goals

- Do not redo repository discovery from scratch.
- Do not write implementation code.
- Do not produce a delivery plan.

