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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion docs/decisions/0074-a-side-effect-crosses-one-typed-seam.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: A side effect crosses one typed seam
status: proposed
status: accepted
date: 2026-09-21
pattern: Command–query separation at the runner boundary — a child asks for an effect with a typed command; the runner validates current facts, performs the effect, and returns a typed receipt or refusal
---
Expand Down Expand Up @@ -214,3 +214,14 @@ This pull request deliberately leaves `status: proposed`. Its cold-reader gate r
## Public hygiene

This record keeps only public issue and pull-request numbers, repository-relative paths, one public-tree sha and the date required to audit the hard-case trace. It carries no Slack channel, user or message ids, no private URL, no customer or company name, no credential and no unpublished account detail.

## Amended 2026-09-22 — accepted after the cold-reader gate and one review round

*Acceptance re-evaluation.* The independent cold-reader gate recorded on pull request #2194 passed, and one review round found no remaining contradiction in the architectural bet. The bet is still two-sided and neither half is optional: the child has only narrow typed commands and no write route through its shell; the runner reads fresh facts, owns each operation's specific gates, performs the effect and records its typed receipt. The earlier sentences that deliberately kept this record proposed are historical gate statements and are superseded by this amendment. Acceptance says the boundary is decided, not that its four-unit rollout has shipped.

Two fixtures arrived after the proposal and strengthen the same boundary:

1. **Issue #2197 — a push the runner did not perform could not make the unit ready.** A ship child pushed its clean completed head, submitted its pull-request description and handed off, but its own push never entered the runner's `pushed[]`; only a later salvage entry did. The runner therefore judged completed work “not ready” and opened no pull request. Reading the remote ref would only replace one inference with another: under this record the runner performs the requested push, records `by: runner` against the exact gated tree, and ship-round readiness consumes that receipt.
2. **Issue #2196 — a number the runner did not reserve could not be coordinated.** Two record-writing children independently scanned for the next free decision number and both chose 0074. Each checkout was locally reasonable, but no runner-owned effect serialized the shared allocation, so neither child could know about the other's concurrent choice. Record-number reservation is another instance of the rule, not a fifth rollout unit hidden here: when that write is designed, allocation must cross a narrow typed command whose runner reads fresh facts, performs the reservation and returns the receipt; a shell or directory scan cannot be its authority.

Both failures have the same shape as the accepted hard case: an external effect the runner did not perform is an effect it cannot authoritatively know. The implementation plan therefore treats runner receipts, never shell success text or a later remote guess, as transition evidence.
2 changes: 1 addition & 1 deletion docs/explanation/design-decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Statuses: **proposed** (written, not yet agreed), **accepted** (agreed, being bu
| 0071 | [A ship unit owns its pull request until the pull request is merged — merge-ready waits on facts, a DIRTY head buys a rebase round, and anyone's merge ends the unit](../decisions/0071-a-ship-unit-owns-its-pull-request-until-it-is-merged-merge-ready-waits-on-facts-and-a-dirty-head-buys-a-rebase-round.md) | Three mechanisms in place of one always-on loop — every push is preceded by a rebase onto the fetched base; a sweep command a person runs rebases the pipeline's open pull requests with a two-rung resolver (git alone with the repository's own merge drivers and rerere, then one bounded model round reading the repository's AGENTS.md); and watch-until-merge is a per-repository setting, off by default, reacting to push-to-base webhooks under a rebase-in-flight cap and a spend limit | accepted | 2026-09-20 |
| 0072 | [A run has one live state, owned by the server — a closed set from admitted to ended, one event the moment it changes, one projection and one wording function every surface reads](../decisions/0072-a-run-has-one-live-state-owned-by-the-server-a-closed-set-one-event-one-wording-function-and-every-surface-reads-the-one-field.md) | State machine as the one source of truth — the server assigns each run's live state from a closed union, appends a state event to the run's log when it changes, projects {state, since, bound, detail} onto the run summary, and one wording function beside the ending words renders it everywhere; the seven rival derivations are deleted | accepted | 2026-09-20 |
| 0073 | [The ship pipeline dissolves into the orchestrator — the unit machine is the deterministic atom, judgement composes units, and the pipeline graph retires](../decisions/0073-the-ship-pipeline-dissolves-into-the-orchestrator-the-unit-machine-is-the-deterministic-atom-and-judgement-composes-units.md) | Agentic orchestration over deterministic unit workflows — coding → review → fix → checks → merge remains one leased, single-owner state machine; an LLM loop reads each durable ending and chooses the next bounded act, while DAG planning, recovery directives and person-managed workflow columns retire | accepted | 2026-09-21 |
| 0074 | [A side effect crosses one typed seam](../decisions/0074-a-side-effect-crosses-one-typed-seam.md) | Command–query separation at the runner boundary — a child asks for an effect with a typed command; the runner validates current facts, performs the effect, and returns a typed receipt or refusal | proposed | 2026-09-21 |
| 0074 | [A side effect crosses one typed seam](../decisions/0074-a-side-effect-crosses-one-typed-seam.md) | Command–query separation at the runner boundary — a child asks for an effect with a typed command; the runner validates current facts, performs the effect, and returns a typed receipt or refusal | accepted | 2026-09-21 |

<!-- /generated:decision-records -->

Expand Down
Loading