Uncomplect rat-stack

Uncomplect means separate responsibilities that currently change together. Preserve behavior proved by tests or runtime observations. Replace structure that combines unrelated responsibilities. Make the separation hold under the next change.

Rich Hickey’s Simple Made Easy (2011) distinguishes simplicity from familiarity.

Greg Young’s The Art of Destroying Software argues for replaceable programs. Optimise for deletion.

Read first

Read AGENTS.md, VISION.md, and the relevant .brain/ decisions. Inspect the design’s source, tests, and recorded runtime results. Check library APIs against matching pinned sources before changing Effect, XState, or Alchemy code. Every recommendation must name its source file, preserved behavior, deleted complexity, and enforced check.

Terms used here

TermMeaning
CapabilityOne named action with a contract and a server-side handler.
ContractThe shared name, input, output, failures, metadata, and approval setting.
ProjectionAn interface built from capabilities: a command, API, or agent tool.
CartridgeA package that owns one job and its resources. It must pass the add-and-remove test below.
FenceTypes, lint rules, tests, and hooks that reject prohibited changes. Project rules cover remaining gaps.
EffectThe library for describing work with typed failures and required services.
ServiceA named interface for one job. A service tag identifies the service requested by a caller.
SchemaA definition of accepted values that provides TypeScript types and runtime checks.
LayerA description of how to construct services and supply their dependencies.
Alchemy StackThe infrastructure program that declares cloud resources and their connections.
BindingA connection that gives runtime code access to a resource.
MachineNamed states, events, and legal transitions for one lifecycle, defined with XState.
ActorA running unit of work used by a machine.
FeatureA thin route and view that read client state and call named commands.
ClientThe module that owns queries, commands, and a local copy of server data.
PortAn interface for an application job, independent of a provider.
AdapterAn implementation of a port for a particular provider.

CLI means command-line interface. MCP lets agents discover and call tools. RPC lets a client call named server operations.

Atoms hold observable client values. A property test18 checks a rule across generated inputs. A model test checks stateful rules across generated command sequences.

Find combined responsibilities

Behavior in a projection

Network calls and retries in a feature

Provider code in a caller

Resources separate from the code that uses them

Infrastructure setup in runtime code

Approval and retry rules inside handlers

Lifecycle encoded in scattered flags

Two places approving writes

Service wrapper that hides nothing

Unchecked external input

Crash folded into an outcome

PR #9 (37b8ba9) removed onError: { target: "settled" }. A first-attempt crash had stored refused permanently. The fix propagates the crash and leaves contact delivery pending.

Duplicated API definitions

Deployment configuration or live flag

Undeclared deployment inputs

On 2026-10-04, missing EVENTS_* deployment configuration produced three analytics deletes without a code change. Schema validation alone cannot prove the intended set of resources. Read the plan before applying it.

Hide implementation details behind the interface

A capability hides schemas, behavior, approval, and projections. A cartridge hides its provider implementation and resources.

For each proposed service tag, Layer, or state, name what it removes. For consequential changes, compare two function signatures: their inputs, outputs, failures, and required services. Choose the one requiring less caller knowledge while preserving failures and behavior.

Run the cartridge test

Apply VISION.md’s cartridge test:

  1. In an isolated checkout, remove the package and the line that provides its Layer or adds its resources to the Alchemy Stack. Run pnpm turbo run check test build.
  2. Check that callers use its public interface, not internal implementation details.
  3. Ask whether its schemas, tests, and recorded runtime results support replacement within about one week.

Use the week to pressure-test replacement. Keep each service responsible for its named job.

Enforce the separation

Use the strongest available check: types and APIs first, then lint and behavior tests, then project rules.

  1. Follow gardener: add a rule before cleaning up the pattern. Plant a bug before trusting a new property.
  2. Verify existing names in oxlint.config.ts, scripts/oxlint-plugin-*.ts, and packages/*/test/.
  3. Mark missing coverage No fence yet. Label candidates; do not present them as shipped rules.

Output

Lead with More complicated locally, simpler overall or More machinery, no net simplification. Give each combined responsibility’s source, preserved behavior, smallest interface, deleted complexity, and existing check or proposed check. Include the signature, any machine, cartridge-test result, deletion list, and one material question. State what becomes impossible, what becomes obvious, and what disappears.


Last changed 2026-10-07 in f1a00f2. Source on GitHub. Change log.

Unlinked mentions (30)