# The implement-feature algorithm

The algorithm takes a boolean flag, **`tddMode`**, and runs one of two variants — written out
separately below so each reads top-to-bottom on its own:

- **Algorithm 1 (`tddMode: true`)** — strict outside-in ATDD/TDD. Every test is written FIRST,
  executed, and observed RED before any production code exists; then the implementation is
  written and the same test observed GREEN. The acceptance test opens each slice by failing for
  the right reason.
- **Algorithm 2 (`tddMode: false`)** — basic development. Tests are written in the same step as
  the feature code and only ever asserted green. The acceptance test is authored as the slice's
  specification but never observed red; it first runs at hand-off. Accepted trade: nothing
  proves any test *can* fail. What IS proven, at slice end once the UI exists, is that the
  acceptance DRIVER executes (dry-run): a wrong selector or broken Given step is fixed by the
  algorithm, not discovered by the user.

The flag can be refined with **`tddModeOverrides`**, at two grains (a level's own key wins
over its participant's key, which wins over the global flag — `resolveTestFirst` in types.ts):

- **Per participant** (a client platform or `backend`): swaps every inner feature loop of that
  participant. Deliberately does NOT reach the acceptance level — outer discipline changes only
  when named explicitly.
- **Per test level** (`<platform>-acceptance`, `<platform>-e2e`, `use-case-unit`,
  `event-handler-unit`): swaps exactly that loop. At the acceptance level the two disciplines
  are: test-first = red-check at slice open; tests-with-code = specification now, driver
  dry-run at slice end — each platform gets whichever its resolution says, in either variant.

The verification loops (`domain-unit`, `repo-equivalence`, `<platform>-fake-vs-real-contract`)
have NO override key: writing the test is itself the deliverable there, both modes are the same
act. The canonical use: `tddMode: true` with `{ "mobile-app": false, "mobile-app-acceptance":
false }` — strict test-first everywhere except the client whose test executions are expensive,
including its per-slice acceptance red-check on the runner hardware.

Both variants share the same skeleton (orient → slices → contract → fan-out → join → real API →
smoke → pre-commit gates → commit → hand-off), integrate CONTINUOUSLY inside each slice —
a CHECKPOINT commit (gated, pushed, pipeline not awaited) lands at every point the tree is
green by construction: the freshly authored acceptance spec (marked @wip in its test titles,
which the CI acceptance jobs exclude — the specification integrates on day zero without
reddening the pipeline), the additive contract, the join barrier, and the wired real API —
and follow these standing rules:

- **Logging lives at the edges, never in the core.** Interactors and domain objects NEVER log
  — the application core stays pure. Logging happens in controllers, presenters, repositories
  and the other edges, and EVERY log line carries the x-request-id and x-visit-id tracing
  headers, so any request can be followed end to end across the layers. The ids are carried by
  the request-bound logger (or the request context), never hand-attached per log line — a
  hand-built `{ requestId, visitId }` object next to a logger that already binds them is
  duplication that drifts.
- **Request-scoped values travel in one context object, required by the compiler.** Everything
  about the request being served — the tracing ids, the user's chosen language, the observed
  device locale — is built ONCE per request at the route layer into a RequestContext and
  injected through the use case to the presenter. No production default exists anywhere on
  that path: a call site that forgets the context is a COMPILE error, and a route whose
  mandatory headers are missing fails loudly (400 from the enforcement hook, or a throw naming
  the header), never silently as `unknown-request` in the default language. Tests pass an
  explicit `fakeRequestContext(overrides)` — a test double named `fake-*` like the other fakes,
  so a double that ever leaks into a real log identifies itself at a glance. The general form
  of this rule: cross-cutting inputs are REQUIRED-BY-COMPILER, never defaulted — when a batch
  rewrite adds such a parameter, the missed call sites surface as compile errors instead of
  sleeping bugs.
- **User-facing strings live in co-located localization catalogs, never in code.** Every
  presenter and every UI component with user-facing text owns a `localization/` directory next
  to it holding a single data-only catalog (English as the reference; every other language
  carries exactly its keys, enforced by the type system; the required languages per module come
  from the global configuration). Presenters select the catalog by the request context's
  language; components read theirs through the client's catalog hook. A hardcoded user-facing
  string is a convention violation the reviewer reports. Each use case with user-facing
  messages carries one checklist item proving language selection works (resolve with a
  non-default language, assert the message equals that language's catalog entry — imported,
  never retyped). The repository's localization VERIFIER is a pre-commit gate: together with
  the whole-repo typecheck it runs after the suites and before every slice commit, so an
  incomplete catalog can never reach the trunk.
- **Every backend call goes through the client's central API client.** The api-server makes
  four headers mandatory on every business request — the two tracing ids, the chosen language,
  and the observed locale — and each client stamps them in exactly ONE place (its central API
  client). Generated client code therefore never hand-rolls a fetch: a new endpoint is reached
  by extending the central client, which is what keeps the header contract unforgeable.
- **Event-triggered work lives in `event-handlers/`, and a query owns its projection.** The
  backend has three use-case shapes. Request-driven use cases share one anatomy
  (Input/Output/Interactor/Presenter/UseCase/localization) and live under `commands/usecases/`
  or `queries/usecases/` in CQRS contexts, or a flat `usecases/` in contexts without the split.
  Work triggered by a domain event that serves NO request — a policy ("whenever X happens, do
  Y") or a notification — lives in the context's `event-handlers/` directory with its own
  anatomy: EventListener (an outbox Subscriber registered on the relay) + Handler (the
  reaction's logic, ignorant of outbox and payload parsing) + OutcomeRecorder (the event-side
  analog of a Presenter). It runs on the outbox worker, never in the request path, under
  at-least-once delivery — so a Handler is either idempotent or its non-idempotence is an
  accepted, documented trade. Third shape: a query use case whose read model is MAINTAINED as
  its own table, built from domain events. A query earns this hybrid shape for either of two
  reasons: the state exists only as an event history (a statement, an audit trail), or serving
  the read live off the write-side tables would be COMPLEX in the database — too many joins, a
  heavy projection — so a dedicated, denormalized read-model table is maintained instead. The
  hybrid keeps the read side at its top level and carries an `event-handler/` subdirectory
  holding that same three-file anatomy; `Projection.ts` at the top level is the seam between
  the halves — the interactor reads through it, and its write methods (insert for an
  append-only model, update for a maintained one) are reserved for the event handler. The
  presence of `event-handler/` inside a query use case is the marker that its read model is
  event-built; its absence means the query reads existing state directly, which is correct
  only while that read stays simple.
- **Test state is created through the front door, never through backdoors.** Every test, at
  every layer, sets up its state through the system's NORMAL input ports: use-case tests
  execute the command use cases that create the state (resolved from a whole Application
  instance) and read back through query use cases; acceptance and e2e tests establish every
  Given by driving the real UI; and third-party processes the app owns (the database, the
  cache) are populated through the app itself — never by direct inserts, raw SQL, or fixture
  loading. Two reasons: a direct insert can fabricate a state the application can never
  produce, so the test proves behavior for a world that cannot exist; and when the real
  creation path changes, a backdoored test keeps passing while the app is broken. Backdoors
  are permitted ONLY where the logic under test requires them: the repo-equivalence tests
  drive repositories directly because the repository IS the unit under test, and the
  in-memory error seams (with<Method>Error()) are deliberately built test controls, not
  backdoors. Everything else goes through the front door — a reviewer finding a test that
  bypasses the input ports reports it as a defect.
- **Snapshots are a wire format, never a query API.** `snapshot()` exists so an aggregate can
  be REPRESENTED where a representation must leave the process: persisted by a repository,
  rendered by a presenter, sent down the wire. Production logic never reads a snapshot to make
  a decision — an interactor (or another domain object) peeking into a snapshot for a fact is
  business logic that belongs ON the aggregate as an intention-revealing method: tell the
  aggregate, don't ask for its insides. Tests are the one other sanctioned consumer: asserting
  an entity's resulting state through its snapshot is exactly what the representation is for.
- **Error messages are asserted by identity, never by wording.** When a UI test verifies that
  an error (or any notice) is shown, it locates the message element by its shared selector id
  and asserts two things: the element is PRESENT, and its text is NON-EMPTY (a rendered-but-
  blank error element is a bug, not a pass). It never asserts the message's actual wording —
  that may change freely without breaking a single test: the words belong to the backend and
  the product, not to the test suite. This applies to every layer that drives a UI — client
  e2e tests, acceptance protocol drivers, smoke flows.
- **Minimal code, always.** Every step writes and commits ONLY the code the current step needs:
  the minimum that makes the present test pass, the field the present scenario reads, the
  endpoint the present slice calls. Never code for an anticipated future — no speculative
  methods, parameters, endpoints, or error branches "we'll surely need." A new aggregate starts
  with exactly the methods the current move requires and grows move by move, each demanded by
  the outer loop's current step. What counts as "needed" is defined by the artifacts, not by
  judgment: the current checklist item, the slice's scenarios, the contract draft. If a later
  slice needs more, the later slice adds it.
- **Budgets.** Every loop has a maximum number of iterations. When a budget runs out, the run
  STOPS AND FLAGS the situation to the user — it never spins forever and never papers over the
  problem. Budgets are phase-accurate (a TDD item's test REWRITES and its implementation FIXES
  are counted separately, so escalations name the actual problem) and route-aware (remote
  batched loops get a shorter per-item leash than local loops — expensive executions should
  escalate sooner, not burn runner time).
- **Every run is resumable.** Finished slices are durable in git; the working tree carries
  intra-phase progress (checklists are rebuilt by inspecting what exists); and a JOURNAL records
  each slice's completed phases with their results. On restart: completed slices are skipped,
  the in-flight slice resumes after its last recorded phase — crucially, an already-observed
  red-check is never re-run against a half-built slice (which would wrongly report a violation),
  and the fan-out resumes against the recorded contract draft.
- **Concrete exit conditions.** No loop exits on the working agent's own judgment that it is
  "done". Each checklist loop runs against an explicit CHECKLIST derived from the scenarios plus
  the mandated cases, and an INDEPENDENT REVIEWER must find no missing cases before the loop may
  close.
- **All paths come from configuration.** The algorithm takes a REPO LAYOUT object naming where
  everything lives: the api-server and its bounded contexts, the database layer (in-memory,
  Postgres, equivalence tests), the OpenAPI specs per client, each client's package with its
  fake backend, real backend, API models, integration tests, e2e tests and selectors, the
  behavior package (features, DSLs, protocol drivers, acceptance tests per client), the smoke
  flows, and the infra entry points (local-development stacks, Taskfile, CI config, global
  configuration). No role hardcodes a path — which is what makes the algorithm portable across
  projects sharing the architecture. Alongside it sits a COMMAND CATALOG: the list of known
  commands (migration diff/apply/status, query-code generation, OpenAPI generation, whole
  suites AND single-test runners for the per-item cycles, quality gates — typecheck, lint,
  format — production builds, stack up/down, database REPL and data reset, emulator boot) that
  the tool may run. The algorithm never invents shell commands — it executes
  catalog entries, and a failed catalog command stops and flags rather than being improvised
  around.
- **Models are tiered by difficulty.** Every leaf step is executed by Claude Code, but not on
  the same model: a MODEL ROSTER names a backend per tier — hard (Fable), intermediate (Opus),
  easy (Claude Code over a DeepSeek backend via a router) — and a MODEL POLICY maps each kind
  of work to a tier. Defaults: domain design, aggregate sizing, and the distillation review on
  the hard tier; use cases, tests, UI code, contract drafts, and reviews on the intermediate
  tier; repositories, the persistence ripple, controller wiring, and suite-output reading on
  the easy tier. Both roster and policy are configuration — swap models or re-tier a work kind
  without touching the algorithm. Alongside them sits a PROMPT CATALOG: one instruction text
  per work kind (test conventions, guardrails, minimal-code rules). For every model-performing
  step, the orchestrator resolves prompt + model into a WORK ORDER and passes it INTO the role
  call as its first argument — instructions are runtime values the step demonstrably received,
  not comments an implementation might ignore. The algorithm also takes as INPUT the path to an
  EXAMPLE PROJECT — a repository with the same architecture where every pattern already exists,
  done right — and every work order carries it. The prompts teach by example: they cite
  concrete exemplar files under an {example}/ placeholder (substituted by the orchestrator
  when it resolves the work order — the prompt reaches the step with absolute paths) — the deposit-money vertical is the reference slice, from acceptance spec and
  protocol driver through interactor, aggregate, repositories with error seams, SQL queries,
  and equivalence tests — and say "study these; produce the same shape". The example path is a
  REQUIRED input — the run fails without it. Working on the boilerplate itself, pass its own
  root; a client project passes its copy of the boilerplate.
- **Every step gets the narrowest context that can do the job.** Sessions PULL context by
  reading files, and every read is re-sent on every later turn of that session — so scope is
  the main token lever. Each work kind carries a CONTEXT SCOPE (context-scope.ts, derived from
  the repo layout): the workplace directory, the paths the step needs, and guidance naming
  what is out of scope. The orchestrator resolves it into the work order and the leaf executor
  appends it to the session prompt. The scale, narrowest to broadest: suite-output reading and
  failure judgment get NO repository access (the output in the task is the input); domain unit
  tests get one object plus one exemplar test; the persistence ripple gets the slice's
  aggregates plus the db/ tree; repo equivalence gets the two implementations and their
  interface; controller wiring gets the routes and the resolver; each unit-building kind gets
  its unit's directory, its bounded context, core/, and the cited exemplar; each client kind
  gets that client's subtree (client-bound scopes narrow per platform once the role knows it);
  contract design gets the spec files; acceptance authoring gets the behavior package and
  selectors; and only the kinds that are broad by nature — slice planning and classification
  (directory listings, not deep reads), the bootstrap sweep, project identity — see the whole
  tree. Two limits hold: when the compiler points outside the scope, FOLLOWING it is correct
  (surveying is not), and the exemplar citations are never trimmed — they are the cheapest
  quality lever there is.
- **Instructions travel.** The run-specific extra instructions (after guardrail filtering) are
  handed to every role at the start, not read once and dropped.
- **Gherkin tags decide which clients a slice targets.** Scenario-level tags (`@web`,
  `@mobile`, `@feature-flags-admin` — the operator console is its own client) override
  feature-level tags; run instructions may only NARROW the result, never
  widen it; no tags at either level means ALL clients — and genuine ambiguity is a stop-and-flag,
  not a guess. Every per-client phase of a slice (acceptance red-check, client fan-out, client
  join suites, real-API wiring, mobile smoke) runs ONLY for the slice's targeted clients: a
  web-only slice never touches the mobile pipeline, a mobile-only slice never busies the web
  agent.
- **Emulator-bound tests execute directly on the runner hardware — never through git.**
  Backend and browser-client tests run on the developer machine. Every test that needs an
  emulator — mobile acceptance, mobile Detox e2e, mobile smoke flows — is AUTHORED locally and
  EXECUTED remotely: the working tree is synced to the runner box and the corresponding CI job's
  own definition (same docker image, same script) runs there directly, with a warm emulator kept
  between runs and the full test output streamed back. No commit, no push, no pipeline entry —
  a deliberately-red run is just a command result. Git receives ONLY finished slices; each
  slice commit's real pipeline is the final arbiter (red there means environment drift → stop
  and flag). The mobile fake-vs-real contract tests are pure Node and stay local.
  One remote attempt is a DETERMINISTIC COMPOSITION of the command catalog's remote group —
  acquire the runner lock → pre-flight probe → sync the tree → ensure a warm device (one
  kill-and-boot recovery) → install dependencies → build the job's binary → run the job →
  fetch results → release the lock (always, per attempt: the warm device survives between
  attempts without holding the box against its real CI jobs). The vocabulary is farketari's
  own; what actually runs behind each command name is per-project configuration filled in at
  implementation time.
  Because a remote execution runs the whole suite, remote-tested clients work in BATCHES:
  author every pending item, then ONE suite run serves them all — per-item verdicts are read
  from its output, and already-covered tests re-run for free, so regressions surface
  immediately. Two suite runs per batch round replace two runs per item. (Batch test-first is
  marginally weaker than per-item test-first — tests in a batch are authored before each
  other's implementations exist — a negligible cost for independent e2e items.)
- **Green work is preserved the moment it is green.** The fan-out lanes run for hours between
  barriers, and an uncommitted tree is one reset away from losing all of it. So every checklist
  item a lane proves green becomes a work-preservation commit scoped to THAT lane's paths
  (`git add -- <the lane's context scope>`): a sibling lane's mid-TDD red state never rides
  along, which is exactly why these cannot be `git add -A`. They commit --no-verify under the
  mid-work "slice: <name> — <label>" subject (the version-bump guard and CI defer to the
  slice's final commit) and are NOT pushed — the next barrier checkpoint pushes them in one go
  and sweeps anything a lane wrote outside its scope while chasing the compiler. Lanes share
  one process and one git index, so the trunk serializes all commits behind an in-process
  mutex.
- **Interference is waited out, not prevented.** The parallel agents share one working tree, so
  a remote run's snapshot can contain another agent's half-finished edits (the shared
  OpenAPI-generated code is the classic case). When a remote run fails at BUILD time in files
  outside its own scope, that is interference, not a test result: the algorithm waits until
  every agent reaches its next stable checkpoint (the end of a green cycle, a completed
  regeneration) and re-runs — the team's own "red pipeline → wait for whoever broke it"
  discipline, applied to agents. Budgeted (interference waits); waiting cannot deadlock because
  only remote runs read the full tree — local suites are per-package, so nobody ever waits on
  the mobile agent. A remote run therefore has THREE outcomes, and only one of them is a test
  result: a genuine pass/fail (handed to the loops), interference (wait for a stable tree,
  re-run), or an INFRASTRUCTURE failure — emulator boot timeout, runner unreachable — which is
  retried as-is (budget: infrastructure retries) and, past the budget, means the runner is down:
  stop and flag. Neither non-test outcome ever reaches a repair loop or spends a test attempt
  budget. The classification is STRUCTURAL wherever it can be: WHICH step of the composed
  attempt failed decides the class — the environment steps (probe, sync, ensure-device) never
  touched the code, so their failures are infrastructure by position; only a failed CODE-BOUND
  step (install, build, the job itself) needs judgment, and that single question — broken by
  other agents' in-flight work, or a genuine failure in the job's own scope? — is the one
  remaining AI decision in remote execution (the failure-judgment prompt).
- **Shared cross-cutting files belong to the orchestrator, not the fan-out.** Waiting out
  interference has a prevention-shaped complement: files that EVERY parallel agent would touch
  — the use-case resolver, the application wiring, route index files, shared schemas — are
  never edited inside the fan-out. The proven shape is exemplar-first: the orchestrator (or a
  single agent) builds the shared infrastructure plus ONE fully-wired exemplar per area, the
  fan-out then converts the mechanical rest inside per-area boundaries, and the orchestrator
  flips the shared files itself at the join, once, against the finished parts. Agents cannot
  collide on a file none of them is allowed to edit.
- **Verifiers and seams are negative-tested before they are trusted.** Whenever a step
  produces a CHECK — a repo-wide verifier, an error seam, a contract test, a test double's
  failure mode — the step must also prove the check can FAIL: deliberately break what it
  guards (tamper a value, arm the seam, remove a required entry), observe every failure class
  fire, then restore. A guard that has only ever been seen green is not a guard; an
  always-green check is precisely the bug this rule exists to catch.

The **contract test** (each client's fake backend vs. the real backend) is authored across the
run, NOT as one late verification loop: STEP 1 writes it against the FAKE up front — it *defines*
the API the client is then built against, and is observed RED before the fake exists (TDD) or
written green alongside the fake (basic); STEP 5, after the join, EXTENDS those same tests with
the real backend. The remaining **verification loop** — B2 (fake repo vs. Postgres repo) — is
identical in both variants: its test *is* the deliverable, so "test first vs. test alongside"
does not apply to it.

---

## Algorithm 1 — `tddMode: true` (test-first)

```
ALGORITHM ImplementFeatureTdd(featureFile, extraInstructions)

  # PHASE 0 — Orient (no loop, just reading and planning)
  Read the feature file; treat each scenario as a behavioral contract.
  Read 1–2 similar existing acceptance tests (DSL, protocol drivers, specs).
  Pick the closest existing vertical slice and keep it open as a template.
  Reconcile extraInstructions against the guardrails
      (guardrail wins; say so) and BRIEF EVERY ROLE with what survives.
  Plan SCENARIO SLICES: ONE scenario per slice by default (grouping only
      scenarios that cannot pass separately), each independently
      implementable and committable; each slice's TARGET CLIENTS resolved
      from the Gherkin tags (scenario tags override feature tags;
      instructions narrow; untagged → all; ambiguous → stop and flag).

  FOR EACH slice:

      # OUTER LOOP — open it by making it fail
      Add DSL methods; write the acceptance test (every test TITLE
          carries the " @wip" marker — CI excludes it until the slice's
          final commit strips it; local runs are unfiltered); implement
          the protocol-driver steps (every Given through the real UI —
          unreachable precondition → STOP AND FLAG); shared selectors only.
      FOR EACH client platform the slice touches WHOSE ACCEPTANCE LEVEL
      IS TEST-FIRST (skipped when `<platform>-acceptance` is overridden
      to false — that platform gets the driver dry-run at slice end
      instead, see below):
          REPEAT (budget: wrong-reason fixes)
              run the slice's acceptance test for that client —
                  web: locally; mobile: run the acceptance job DIRECTLY
                  on the runner hardware and JUDGE THE OUTPUT (the red
                  run is just a command result — no commit, nothing
                  goes red anywhere)
              IF it PASSES → guardrail violation: not testing new behavior. STOP.
          UNTIL it fails FOR THE RIGHT REASON (missing behavior, not a typo)
      # The outer loop is now open. Everything below exists to close it.

      # CHECKPOINT — the executable specification integrates on day zero
      Run the pre-commit gates; commit "slice: <name> — acceptance spec
          (wip)" and push (pipeline not awaited — the @wip marker keeps
          the CI acceptance jobs off the spec).

      # CONTRACT — first draft of the seam
      Add/extend ONLY what the slice's scenarios call for in the OpenAPI
          spec (success + 400/403/404/409/500 for those endpoints — no
          speculative endpoints or fields; the renegotiation loop exists
          precisely so the contract can grow when a real need surfaces).
      Regenerate clients and server stubs.

      # CHECKPOINT — the contract change is additive: nothing references
      # the new endpoint yet, everything compiles, every suite is green
      Run the pre-commit gates; commit "slice: <name> — contract"; push.

      # RENEGOTIATION LOOP around the FAN OUT (the contract is a draft)
      REPEAT (budget: contract renegotiations)
          IN PARALLEL — backend agent plus one client agent per
          TARGETED client (from the slice's tags):

              # Each client agent: STEPS 1-2 — THE CONTRACT TEST IS THE
              # API'S DEFINITION, written against the fake FIRST, TEST-FIRST
              Build the CONTRACT CHECKLIST: one item per operation this
                  contract adds.
              STEP 1 — for each item, WRITE THE CONTRACT TEST running
                  ONLY against the FAKE (the real backend does not exist
                  yet), shaped so a second implementation can later join
                  the SAME assertions (it iterates over the
                  implementations under test — today just the fake).
              EXECUTE the contract suite — ASSERT IT FAILS (the fake is
                  not built yet; a suite green here asserts nothing → STOP
                  AND FLAG).
              STEP 2 — implement the client's fake API with three
                  doubles, switchable per operation: SLOW (response stays
                  pending), BACKEND ERROR (well-formed error response),
                  NETWORK FAILURE (no response at all — connection
                  refused, timeout, offline). This TURNS THE CONTRACT
                  TESTS GREEN; repair until the contract suite passes.
              REVIEW the contract cases (independent reviewer, arbitrated,
                  budget: reviewer rounds); the real backend joins these
                  SAME tests later (STEP 5, after the join).

              # Each client agent: STEP 3 — INNER LOOP C1, TEST-FIRST,
              # client development AGAINST THE FAKE
              Build the C1 CHECKLIST: scenario behaviors, plus FOR
                  EVERY OPERATION the four mandated cases — happy path;
                  loading indicator while pending; backend error
                  surfaced to the user; network failure surfaced to the
                  user (no error body exists — the UI must still say
                  something useful).
              Local clients — FOR EACH unchecked item (budget: attempts
              per item):
                  1. WRITE THE TEST alone (preconditions through the UI);
                     the session REPORTS THE EXACT TEST TITLE it wrote,
                     recorded on the item
                  2. EXECUTE it locally, filtered by SPEC FILE AND TITLE
                     — a test never actually written surfaces as "no
                     tests found" (a loud structural failure), never as
                     the file's other, green tests passing on its behalf
                     — ASSERT IT FAILS; a test that
                     passes immediately is JUDGED, never assumed vacuous:
                     if the item's behavior is ALREADY implemented (stale
                     review finding, resumed run, work an earlier item
                     pulled in), the fresh test is a legitimate
                     regression test — mark the item covered and move on;
                     only a test judged VACUOUS is rewritten
                  3. WRITE THE CLIENT CODE to make it pass — the
                     MINIMUM that does; nothing the item doesn't demand
                  4. EXECUTE again — ASSERT IT PASSES; while red, fix
                     the implementation and re-execute
              Remote clients (mobile) — the same discipline, BATCHED:
                  1. WRITE ALL pending tests (each session reports its
                     test's exact title, recorded on the item)
                  2. ONE suite run on the runner — ASSERT EACH RED,
                     judging any that pass (already-implemented → covered;
                     vacuous → rewritten, per-item budget); an item whose
                     test appears NOWHERE in the output did NOT pass —
                     absence is never a pass
                  3. WRITE ALL the implementations
                  4. ONE suite run — ASSERT EACH GREEN, repairing
                     stragglers per item until all pass
              REVIEW: independent reviewer lists missing cases; each is
                  ARBITRATED first (an independent judgment verifies it
                  against the current tree — a finding judged covered is
                  overruled, binding, so reviewer and writer can never
                  stall each other into escalation); adopt the confirmed
                  ones and continue (budget: reviewer rounds) until none.
              WAIT at the barrier (real API comes after the join).

              # Backend agent: CLASSIFY THE WORK (an AI decision — the
              # only one here; everything below dispatches on its answer)
              Decide the slice's backend UNITS: each unit is EXACTLY ONE
              of the four kinds, assigned to its owning bounded context
              (matching that context's existing style — CQRS split or
              flat usecases/ — never introducing the other):
                  - COMMAND: the scenario changes state → full use-case
                    anatomy, works through the domain
                  - QUERY: reads state the write side already persists,
                    and the read is SIMPLE in the database → use-case
                    anatomy over a READ-ONLY projection, no domain
                  - EVENT-BUILT QUERY: the read model must be MAINTAINED
                    as its own table — the state exists only as a
                    history of domain events, OR the live read would be
                    complex in the database (too many joins, a heavy
                    projection) → the hybrid: a query serving the
                    dedicated read model PLUS the event handler that
                    updates it from the events that change it
                  - EVENT HANDLER: a standalone reaction ("whenever X
                    happens, do Y" — a policy, a notification) → no
                    route, no presenter, no contract entry
              A slice may need several units (a command plus the event
              handler reacting to its event). Ambiguity about a kind is
              a stop-and-flag, never a guess.

              # Backend agent: FOR EACH unit, BUILD BY KIND
              SWITCH on the unit's kind:

              CASE command | query — INNER LOOP B1, TEST-FIRST
              (the kinds differ only in the teaching prompt: deposit-money
              for commands, account-overview for queries — a query's
              interactor validates, calls ONE projection read method,
              and presents)
              Find or create the use case (architecture rules apply).
              Build the B1 CHECKLIST: the slice's business rules, PLUS
                  one INFRASTRUCTURE-FAILURE case per repository or
                  projection method the interactor calls — arm that
                  method's error seam on the in-memory implementation
                  (withFindByIdError(), withSaveError(), ...) and assert
                  the use case answers 500 with the standard error body.
              FOR EACH unchecked item (budget: attempts per item):
                  1. WRITE THE UNIT TEST alone — never construct the
                     interactor by hand: create a whole APPLICATION
                     instance (fake db + cache) and RESOLVE the use case
                     from it; state via creating use cases, read back
                     via query use cases
                  2. EXECUTE — ASSERT IT FAILS (else rewrite the test)
                  3. WRITE THE CODE — the MINIMUM that makes it pass,
                     business logic in the DOMAIN OBJECTS, interactor
                     orchestrating
                  4. EXECUTE — ASSERT IT PASSES
              REVIEW as above.

              CASE command ONLY — DOMAIN DISTILLATION (refactor under
              green; commands are the only kind with domain logic, so
              the only kind that distills)
              With every use-case test green, hunt the interactor for
              imperative logic written in the DOMAIN LANGUAGE of the DSL —
              whatever speaks the ubiquitous language belongs in the
              domain (DDD). FOR EACH candidate found:
                  move it into a domain object: an intention-revealing
                  method on an existing aggregate, a new method, a value
                  object, or a NEW AGGREGATE sized by the rules in
                  aggregate-design-prompt.md (the AI prompt Claude runs
                  behind this step);
                  re-run the unit suite — a refactor must STAY GREEN
                  after every single move.
              REVIEW: independent reviewer re-reads the interactor and
                  lists domain-language logic the agent missed
                  (budget: distillation rounds) — until the interactor
                  only orchestrates.

              CASE command ONLY — DOMAIN UNIT TESTS (verification loop,
              identical in both variants: the domain object already
              exists after distillation, so the test itself is the
              deliverable)
              Build the DOMAIN CHECKLIST: for each domain object the
                  slice created or extended, the object's OWN contract —
                  validation rules, boundary values, state-transition
                  guards, events emitted — EXCLUDING behaviors the
                  use-case tests already prove. This is where the edge
                  cases no scenario names get their home.
              Per item: write the test against the object's PUBLIC API
                  (its factories and intention-revealing methods — the
                  domain's front door; no mocks, no Application) and run
                  it. A red test here has exposed a real domain defect:
                  fix the domain, never weaken the test.
              REVIEW as above.

              CASE event-built-query | event-handler — EVENT-HANDLER
              LOOP, TEST-FIRST (for an event-built query this runs AFTER
              its query half above; its Projection declares the write
              methods RESERVED for this handler)
              Create the three-file anatomy — EventListener (outbox
                  subscriber; unparseable payload = unprocessable: park,
                  never retry), Handler (the reaction's logic),
                  OutcomeRecorder (stamps the slice's identity on
                  failures) — at the location the ALGORITHM decides from
                  the kind: the query's own event-handler/ subdirectory,
                  or the context's event-handlers/ directory for a
                  standalone reaction. Register it in the application.
              Build the HANDLER CHECKLIST: the reaction's behaviors, one
                  unparseable-payload case per subscribed event, and ONE
                  AT-LEAST-ONCE case — deliver the same event twice and
                  assert the effect is not duplicated, or document the
                  accepted trade in code (an email cannot be
                  deduplicated after the fact).
              FOR EACH unchecked item: the same test-first cycle as B1
                  (tests drive the whole Application: execute the
                  command that emits the event, run the outbox delivery,
                  observe the effect). REVIEW as above.

              # Backend agent: PERSISTENCE RIPPLE — ONCE per slice, after
              # EVERY unit is built, on purpose: distillation moves can
              # reshape persisted state (new value objects, new
              # aggregates) and each unit has named its repository or
              # projection needs — so the ripple is decided against the
              # FINAL shape of everything the slice touched. Steps:
              1. READ THE SNAPSHOTS of the touched aggregates (extend
                 them minimally where the domain gained fields)
              2. DETERMINE THE REPOSITORY METHODS the use case needs
              3. DETERMINE whether the SCHEMA must change
              4. UPDATE THE SCHEMA files if needed (minimal change)
              5. RUN the migration-diff command  (task db:migrate:diff)
              6. RUN the migrate command         (task db:migrate)
              7. WRITE/UPDATE THE QUERIES in the SQL queries directory
                 (path from the repo-layout configuration)
              8. RUN the query-code generator    (task db:generate),
                 then implement the step-2 methods in BOTH repositories
                 — each new in-memory method WITH its error seam
                 (with<Method>Error()), so B1's infrastructure-failure
                 cases can arm it
              9. PROVE fake and Postgres equivalent — the B2 loop below.
              (Steps 5, 6, 8 are catalog commands; 3–6 and 7–8 run only
              when their "needed" checks say so.)

              # Backend agent: INNER LOOP B2 (verification — same in
              # both variants)
              Checklist of repository behaviors; per item: integration
              test running fake repo and Postgres repo under IDENTICAL
              conditions, failing on any difference. REVIEW as above.
              FOR EACH REQUEST-DRIVEN unit (command, query, event-built
              query): wire its endpoint to the use case in the
              controller. A standalone event handler gets NO endpoint —
              the outbox triggers it, not a route.

          IF any agent discovered a CONTRACT GAP: amend the spec,
              regenerate, notify EVERY agent, re-enter the fan-out.
              A checkmark is only valid against the contract version it
              was earned under: on absorbing the amendment, each agent
              UN-COVERS every checklist item whose verification touched
              an amended part — those are redone against the new
              contract; untouched items stay covered.
      UNTIL no agent reports a contract gap

      # JOIN — barrier with repair, not a crash
      FOR EACH suite (backend unit, backend integration, each client's
                      e2e — on its execution route):
          REPEAT (budget: join repairs)
              run it; if red, route the failure to the OWNING agent
          UNTIL green

      # CHECKPOINT — every suite green, all agents parked
      Run the pre-commit gates; commit "slice: <name> — fan-out green"; push.

      # STEP 5 — real API wired + contract tests EXTENDED to the real
      # backend, once per TARGETED client, in parallel. The contract tests
      # already exist (STEP 1, against the fake); this ADDS the real side.
      FOR EACH targeted client, IN PARALLEL:
          Implement the real API and wire it in.
          EXTEND the existing contract tests: add the real implementation
          to the set each test iterates over (the SAME fake was proved
          against in STEP 2) — never rewritten from scratch, never
          weakened. The fake-vs-real equivalence guard closes here.
          REPEAT (budget: post-wiring repairs)
              run the client's e2e suite AND its contract suite;
              if either is red: fix the ACTUAL cause — never weaken a test
          UNTIL both green in the SAME iteration

      # CHECKPOINT — real APIs wired, e2e + contract suites green together
      Run the pre-commit gates; commit "slice: <name> — real API wired"; push.

      # DRIVER DRY-RUN — only for targeted platforms whose acceptance
      # level was overridden to tests-with-code (no red-check at slice
      # open): prove their acceptance DRIVER executes, exactly as in
      # Algorithm 2. Does nothing when no platform is overridden.
      FOR EACH such client:
          REPEAT (budget: driver repairs)
              execute the acceptance test; check ONLY that the driver
              ran; fix a crashed selector / driver step
          UNTIL the driver executes cleanly

      # SMOKE — mobile only, on the runner hardware; SKIPPED entirely
      # when the slice does not target the mobile app
      List the slice's new/changed UI interactions; weave them into the
          hand-written journey flows (extend existing journeys, minimize
          steps, never duplicate coverage).
      REPEAT (budget: smoke coverage rounds)
          check the WHOLE app's interaction inventory; weave in gaps
      UNTIL every interaction appears in at least one flow
      REPEAT (budget: smoke repairs)
          run the smoke job directly on the runner (release build,
          real backend);
          a failure is a bug nothing else caught — fix the app
      UNTIL green

      # PRE-COMMIT GATES — cheap, deterministic, catalog commands
      Run the whole-repo typecheck and the localization verifier. A red
          gate is routed to a BUDGETED repair session first — gate reds
          are usually STATE problems (stale generated artifacts, orphans
          of an interrupted run, a missing registration) fixed in a turn
          or two; the repair fixes the state, NEVER the gate itself.
          Still red past the budget → stop and flag (a suite cannot
          catch a type error in a package it never compiled, or an
          incomplete catalog — and a gate that stays red found
          something structural).

      # FINAL COMMIT — the slice's arbiter commit
      Bump every deployable the slice changed (deterministic, patch by
          default — scripts/bump-changed-versions.sh; the version-bump
          guard judges the WHOLE slice, checkpoints included, and
          demands the bump with the change; a human's deliberate
          minor/major bump is left alone);
      strip the @wip markers from the acceptance specs (deterministic —
          this is the moment the spec goes live in CI, exactly when it
          turns green); run the pre-commit gates; commit the slice and
          push to main; await THIS commit's pipeline (the final arbiter
          — every suite was already green, so red here means environment
          drift: STOP AND FLAG, don't loop). The checkpoints' pipelines
          ran unawaited; anything they missed is caught here, on the
          same sha lineage.

  # HAND OFF — do NOT run the acceptance tests yourself
  Tell the user they are ready, give the commands, list the scenarios
      per slice. If the user reports failures: fix the actual cause;
      never weaken the acceptance test.

END
```

---

## Algorithm 2 — `tddMode: false` (tests with code)

```
ALGORITHM ImplementFeatureBasic(featureFile, extraInstructions)

  # PHASE 0 — identical to Algorithm 1
  Orient, brief every role, plan scenario slices (target clients
      resolved from the Gherkin tags, as in Algorithm 1).

  FOR EACH slice:

      # SPECIFICATION — authored, never observed red
      Add DSL methods; write the acceptance test (every test TITLE
          carries the " @wip" marker, exactly as in Algorithm 1);
          implement the protocol-driver steps (every Given through the
          real UI — unreachable precondition → STOP AND FLAG); shared
          selectors only.
      DO NOT run it. It first executes at hand-off, when the user closes
          the loop. (Accepted trade: nothing proves it can fail.)
      Exception: a targeted platform whose acceptance level is overridden
          to TEST-FIRST (`<platform>-acceptance`: true) opens with
          Algorithm 1's red-check here — and skips the driver dry-run
          below.

      # CHECKPOINT — commit the @wip spec, as in Algorithm 1.

      # CONTRACT — identical to Algorithm 1, checkpoint included
      Add/extend the endpoint in the OpenAPI spec; regenerate; commit
          the checkpoint.

      # RENEGOTIATION LOOP around the FAN OUT
      REPEAT (budget: contract renegotiations)
          IN PARALLEL — backend agent plus one client agent per
          TARGETED client (from the slice's tags):

              # Each client agent: STEPS 1-2 (ONE STEP in basic mode) —
              # the fake, and its contract tests (the API's definition)
              Implement the client's fake API (with the three doubles:
                  slow, backend error, network failure).
              Build the CONTRACT CHECKLIST (one item per operation) and
                  WRITE each contract test against the FAKE (no red-check:
                  the fake already exists, so the suite is asserted green).
                  These SAME tests gain the real backend after the join
                  (STEP 5). REVIEW the contract cases (arbitrated).
              # Each client agent: STEP 3 — INNER LOOP C1, TESTS WITH CODE
              Build the C1 CHECKLIST (scenario behaviors + the four
                  per-operation cases: happy path, loading indicator,
                  backend error surfaced, network failure surfaced).
              Local clients — FOR EACH unchecked item (budget: attempts
              per item):
                  1. WRITE THE TEST AND THE CLIENT CODE in the same step
                     (the session reports the test's exact title,
                     recorded on the item)
                  2. EXECUTE locally, filtered by spec file AND title (an
                     absent test reads "no tests found", never as the
                     file's other tests passing) — while red, fix
                     whichever side is wrong and re-execute
              Remote clients (mobile) — the same, BATCHED: author all
                  pending items, then ONE suite run per round on the
                  runner; repair stragglers per item until all green.
              REVIEW: independent reviewer lists missing cases; each is
                  ARBITRATED first (an independent judgment verifies it
                  against the current tree — a finding judged covered is
                  overruled, binding, so reviewer and writer can never
                  stall each other into escalation); adopt the confirmed
                  ones and continue (budget: reviewer rounds) until none.
              WAIT at the barrier.

              # Backend agent: CLASSIFY THE WORK, then BUILD each unit
              # by kind — identical structure to Algorithm 1 (the four
              # kinds: command / query / event-built query / standalone
              # event handler; the SWITCH, the per-kind prompts and
              # locations, and command-only distillation are all the
              # same); only the inner-loop discipline differs:
              FOR EACH unit's loop (B1 for commands and queries, the
              event-handler loop for event-triggered units),
              FOR EACH unchecked item (budget: attempts per item):
                  1. WRITE THE TEST AND THE CODE in the same step
                     (business logic still in the DOMAIN OBJECTS; the
                     test resolves the use case from a whole APPLICATION
                     instance — never a hand-constructed interactor; the
                     event-handler checklist still carries its
                     AT-LEAST-ONCE delivery case)
                  2. EXECUTE — repair until green
              REVIEW as above.

              # DOMAIN DISTILLATION + DOMAIN UNIT TESTS (commands only)
              # + persistence ripple + INNER LOOP B2 +
              # per-request-driven-unit controller wiring: identical to
              # Algorithm 1 (the distillation is a refactor under green
              # tests, so mode does not change it; the domain unit loop
              # and B2 are verification loops).

          IF any agent discovered a CONTRACT GAP: amend, regenerate,
              notify every agent, re-enter — un-covering, as in
              Algorithm 1, every checklist item the amendment
              invalidates.
      UNTIL no agent reports a contract gap

      # JOIN, STEP 5 (real API + extend contract tests to real) —
      # identical to Algorithm 1, checkpoints
      # included ("fan-out green" after the barrier, "real API wired"
      # after C2)
      Join barrier with routed repairs;
      real API + fake-vs-real verification per client (both suites green
          in the same iteration).

      # DRIVER DRY-RUN — this variant's substitute for the red-check,
      # placed at slice END because only now does the UI exist (at slice
      # start, a crashing selector is indistinguishable from missing
      # behavior)
      FOR EACH targeted client (except those that opened with a
      red-check per the exception above):
          REPEAT (budget: driver repairs)
              execute the slice's acceptance test (web: locally; mobile:
                  the acceptance job on the runner) and check ONLY that
                  the DRIVER ran — typecheck, every step drove the UI
                  without crashing; the verdict is IGNORED (judging
                  behavior stays with the user at hand-off)
              IF a step crashed → fix the selector / driver step
          UNTIL the driver executes cleanly

      # SMOKE, PRE-COMMIT GATES, FINAL COMMIT — identical to Algorithm 1
      # (the same checkpoints landed along the way: acceptance spec
      # @wip, contract, fan-out green, real API wired)
      Mobile smoke coverage + remote run;
      bump changed deployables' versions; strip the @wip markers;
      whole-repo typecheck + localization
          verifier (red gate: budgeted repair session first — state
          fixes, never gate edits — then stop and flag);
      commit the slice, push to main, await the slice pipeline (final
          arbiter — red means environment drift: stop and flag).

  # HAND OFF — identical to Algorithm 1, with one difference in meaning:
  # this is the first time the acceptance tests' VERDICTS are judged
  # (the drivers themselves have been proven to execute).

END
```

---

## The loop structure at a glance

| Loop | What cycles | Red → green means | Mode |
|---|---|---|---|
| Slice loop | one scenario slice through the whole pipeline | a small, committed, pushable increment | both |
| **Outer** (acceptance) | one failing acceptance test per slice | the slice exists, end to end, as the user experiences it | red-check in Algorithm 1, authored-without-running (plus slice-end driver dry-run) in Algorithm 2 — swappable per platform via `<platform>-acceptance` |
| Renegotiation loop | fan-out → contract amendment → fan-out | every agent can live with the contract | both |
| Inner C1 (client e2e — once per targeted client: web, mobile, flags-admin) | checklist item → test → client code, against that client's fake | every operation behaves on the happy path AND tells the user about slow responses, backend errors, and network failures | test-first in Algorithm 1; together in Algorithm 2 |
| Inner B1 (use-case unit — once per command or query unit the slice's classification named) | checklist item → test → interactor + domain methods (commands) or projection read (queries) | the business rules hold, expressed in the domain — and every infrastructure failure surfaces as a clean 500 | test-first in Algorithm 1; together in Algorithm 2 |
| Event-handler loop (once per event-triggered unit: standalone reaction, or an event-built query's projection builder) | checklist item → test → listener/handler code, driven by emitting the real domain event | the reaction happens, unparseable payloads park, and at-least-once delivery never duplicates the effect (or the trade is documented) | test-first in Algorithm 1; together in Algorithm 2 |
| Domain distillation (backend — command units only: the domain lives on the write side) | candidate → move into the domain → suite stays green → independent re-read | the interactor only orchestrates; everything in the DSL's language lives in the domain | identical in both |
| Domain unit loop (command units only, after distillation) | checklist item → direct test of the domain object's public API | the object's own contract holds — validation, boundaries, transition guards no scenario names | identical in both |
| Inner B2 (repo integration) | checklist item → test comparing fake vs. Postgres repos | both persistence implementations agree | identical in both |
| Contract test — STEP 1, up front (once per client) | contract item → test against the FAKE — the API's definition, written before the client | the fake honours the contract the client is built against | test-first (red before the fake) in Algorithm 1; green-alongside-fake in Algorithm 2 |
| Contract test — STEP 5, after join (once per client) | extend those same tests with the real backend | the fake the client was built against tells the truth about the real api-server | identical in both |
| Smoke loop (mobile only) | weave slice interactions into journey flows → coverage check → run on release binary | every app interaction is exercised for real, in the fewest steps | identical in both |

Ideas that make the shape work:

1. **The outer loop is opened first and closed last** *(Algorithm 1)*. Its red state is the
   license to write any code at all, and it is deliberately left for the user to close — the
   agent only certifies that all inner loops are green. Algorithm 2 gives this up: the
   acceptance test is only a specification until the user first runs it.
2. **Every seam gets an equivalence loop, not a mock.** Fake repo vs. Postgres (B2) and each
   client's fake backend vs. the real backend (C2) are held equal by tests that run both sides
   under identical conditions — in both modes. That is what lets the client and backend inner
   loops run in parallel against a contract instead of against each other.
3. **The contract is a draft with a revision path.** Any agent can report a contract gap;
   the spec is amended once for all sides instead of each side working around it, and the
   mismatch is found during the fan-out, not at C2.
4. **Clients are interchangeable in shape, not in detail.** The web frontend, the mobile app,
   and the feature-flags operator console run the same C1/C2 loops through one shared
   client-agent contract; what differs is the platform — browser + fake-in-bundle (web and
   flags-admin) vs. emulator (Detox) + FakeBackend-in-app with the deep-link test-command route
   standing in for `page.evaluate` (mobile) — and where their UI tests execute: the browser
   clients' locally, the mobile app's directly on the remote runner hardware, a property each client
   agent declares about itself. Adding another client means adding another agent of the same
   shape, not a new algorithm — the flags-admin console is exactly that.
5. **Smoke coverage trades depth for breadth, deliberately — and only where tests are slow.**
   The other loops prove behaviors in depth; the mobile smoke flows prove that every interaction
   of the app works at all, on the untouched release binary with the real backend, in as few
   steps as the journeys allow. The web client has no smoke layer because its acceptance tests
   are already fast and scale — the smoke layer exists exactly where that is false.
6. **Done is a checklist plus a second pair of eyes, never self-certification.** And every loop
   is budgeted: running out of budget means stopping and flagging the user, not looping forever
   and not weakening a test.
7. **Make it work, then make it domain-driven.** The use case is first made green with whatever
   imperative code gets there; only then does the distillation step move domain-language logic
   into aggregates and value objects, one behavior-preserving move at a time, with the green
   suite as the safety net. The DSL's ubiquitous language decides *what* moves; the rules in
   `aggregate-design-prompt.md` decide the *shape and size* of any new aggregate.
