docs(arch): tighten door-type framing — reversal cost, not deferral

ADR-009, open-questions.md, and the architect agent spec all had the same
conflation: 'two-way door' was phrased as 'can be decided during
implementation,' which reads as 'defer the decision.' That's not what it
means. A two-way door is a decision you make now and can revert later if
wrong — it's about reversal cost, not urgency.

ADR-009: add §'What this framework is NOT' — explicitly separates door
type (reversal cost) from deferral (scope management). State that
architecture decisions are the architect's regardless of door type.
Reword the two-way-door process from 'can be decided during
implementation' to 'pick the simplest option that works, implement it,
revert if needed.'

open-questions.md: reword the header to clarify door type describes
reversal cost, not urgency. Add 'Door type is separate from whether a
decision is made.'

architect.md: add Key Principle #8 (decisions are made, not deferred),
a new 'Door Types and Decision Urgency' section, and two new anti-patterns
(#8: door type as deferral, #9: hedging language in resolved decisions).
This commit is contained in:
2026-06-28 09:19:10 +00:00
parent 7d812af8f4
commit 3f011cbb82
3 changed files with 74 additions and 13 deletions

View File

@@ -244,6 +244,46 @@ last_updated: 2026-05-29
and migration notes belong in commit messages or separate migration docs. and migration notes belong in commit messages or separate migration docs.
7. **Lifecycle states**: Every doc has a status. Draft → reviewed → stable → 7. **Lifecycle states**: Every doc has a status. Draft → reviewed → stable →
deprecated. Stale `draft` docs are a sign of unfinished work. deprecated. Stale `draft` docs are a sign of unfinished work.
8. **Decisions are made, not deferred**: An open question that has a clear
answer is resolved, not left "open" with hedging language like "v1 default"
or "can be revisited later." If the decision is made, mark it resolved. If
the decision genuinely can't be made yet (the use case isn't concrete,
the options aren't clear), leave it open — but say *why* it can't be made,
not "we'll decide later." The architect's job is to make architecture
decisions, not to defer them to the implementation agent.
## Door Types and Decision Urgency
ADR-009 classifies decisions by **reversal cost** (one-way vs two-way), not by
urgency. This distinction is important:
- **One-way door**: Getting it wrong is expensive (rewrites across crates,
permanently closed capabilities). Requires an ADR before implementation.
Gets the deliberation it deserves.
- **Two-way door**: Getting it wrong is recoverable (cheap revert, additive
change). Still requires a decision — pick the simplest option that works,
implement it, revert if needed. The decision is made; what's cheap is the
reversal, not the decision.
**Door type ≠ deferral.** A two-way door is not a license to leave a decision
unmade. Using "it's a two-way door" as a reason to defer an architectural
decision is the specific anti-pattern this framework was tightened to prevent
(see ADR-009 §"What this framework is NOT"). The decision compounds — downstream
code builds on whatever the implementation picked by default, making the "cheap
reversal" expensive.
**Architecture decisions are the architect's, regardless of door type.** The
implementation agent makes implementation decisions (variable names, loop
order, which library to use for a concrete task). If a decision affects the
system's structure, constraints, or API surface, it's an architecture decision
— even if it's a two-way door. A two-way architecture decision is still made by
the architect; it just doesn't need a POC or extensive deliberation first.
**Deferral is separate.** Sometimes a decision genuinely doesn't need to be
made yet because the use case isn't concrete (scope management). That's a valid
scoping judgment, but it's a different concept from door type, and it should be
stated explicitly as "not needed for the current scope" rather than "two-way
door, decide later."
## Anti-Patterns to Avoid ## Anti-Patterns to Avoid
@@ -258,6 +298,17 @@ last_updated: 2026-05-29
6. **Missing ADR for a visible choice**: If a reader would ask "why X over Y?", 6. **Missing ADR for a visible choice**: If a reader would ask "why X over Y?",
write an ADR write an ADR
7. **No README index**: Without the index table, ADRs and docs are unfindable 7. **No README index**: Without the index table, ADRs and docs are unfindable
8. **Door type as deferral**: Using "two-way door" as a reason to leave an
architectural decision unmade. Door type classifies reversal cost, not
urgency. A two-way door is a decision you make now and can revert later —
not a decision to defer. If the decision is made, mark the OQ resolved. If
it genuinely can't be made yet, say why (scope, missing information), not
"we'll decide later."
9. **Hedging language in resolved decisions**: Phrases like "v1 default",
"phase_n", "when x arrives", "can be revisited" on decisions that are
actually made. If the decision is made, state it cleanly. Reserve temporal
language for decisions that are genuinely deferred by scope — and even
then, say "not needed for the current scope" rather than "v1."
## When to Redirect ## When to Redirect

View File

@@ -16,23 +16,31 @@ Without an explicit framework, one-way doors can be treated as casually as two-w
### Classification ### Classification
Every architectural decision is classified as one of: Every architectural decision is classified by **reversal cost** — how expensive it is to undo if you got it wrong:
**One-way door** — Reversing this decision requires rewriting significant code across multiple crates or permanently closes a capability door. Examples: **One-way door** — Reversing this decision requires rewriting significant code across multiple crates or permanently closes a capability door. Getting it wrong is expensive. Examples:
- BiStream as a concrete quinn type (closes WASM door permanently) - BiStream as a concrete quinn type (closes WASM door permanently)
- alknet-vault pulled into alknet-core as a dependency (loses standalone property permanently) - alknet-vault pulled into alknet-core as a dependency (loses standalone property permanently)
- ProtocolHandler signature changes (every handler must be rewritten) - ProtocolHandler signature changes (every handler must be rewritten)
**Two-way door** — Reversing this decision is cheap or additive. Examples: **Two-way door** — Reversing this decision is cheap or additive. Getting it wrong is recoverable. Examples:
- Static vs dynamic handler registration (can add ArcSwap later) - Static vs dynamic handler registration (can add ArcSwap later)
- Single transport vs multi-transport endpoint (can add transport trait later) - Single transport vs multi-transport endpoint (can add transport trait later)
- Call protocol stream model (can add multiplexing later) - Call protocol stream model (can add multiplexing later)
### Process ### Process
- **One-way doors** require an ADR before implementation. If the right choice is unclear, validate with a POC before writing the ADR. If a POC can't resolve the uncertainty within a reasonable timebox, default to the option that keeps more doors open. - **One-way doors** require an ADR before implementation. If the right choice is unclear, validate with a POC before writing the ADR. If a POC can't resolve the uncertainty within a reasonable timebox, default to the option that keeps more doors open. One-way doors get the deliberation they deserve because getting them wrong is expensive.
- **Two-way doors** can be decided during implementation. Start with the simplest option and add complexity when needed. Note the decision in a commit message or a brief ADR if the context is worth capturing, but don't block on it. - **Two-way doors** still require a decision — pick the simplest option that works, implement it, and move on. If it turns out wrong, revert and try the alternative. The decision is made; what's cheap is the reversal. Note the decision in a commit message or a brief ADR if the context is worth capturing, but don't block on it.
- When in doubt, classify up. If it's unclear whether a door is one-way or two-way, treat it as one-way until proven otherwise. - When in doubt about which classification applies, classify up. If it's unclear whether a door is one-way or two-way, treat it as one-way until proven otherwise.
### What this framework is NOT
This framework classifies decisions by **reversal cost**, not by **urgency**. It does not say "two-way doors can be deferred." A two-way door is a decision you make now and can revert later if needed — it's not a license to leave the decision unmade.
- **Deferral** is a separate concept: sometimes a decision genuinely doesn't need to be made yet because the use case isn't concrete (scope management). That's valid, but it's a scoping judgment, not a door-type classification.
- **Conflating the two** — using "it's a two-way door" as a reason to defer an architectural decision — leads to decisions that compound into a mess. The decision gets made by default (the implementation picks something), and downstream code builds on it, making the "cheap reversal" expensive.
- **The architect's role**: architecture decisions (one-way OR two-way) are for the architect to make, not the implementation agent. The implementation agent makes implementation decisions (variable names, loop order, which library to use for a parsed task). If a decision affects the system's structure, constraints, or API surface, it's an architecture decision regardless of its door type.
### WASM as a design constraint ### WASM as a design constraint
@@ -46,10 +54,10 @@ This is not "WASM support now." It's "don't close the WASM door accidentally."
## Consequences ## Consequences
**Positive:** **Positive:**
- One-way doors get the deliberation they deserve — ADRs, POCs, explicit justification - One-way doors get the deliberation they deserve — ADRs, POCs, explicit justification — because getting them wrong is expensive
- Two-way doors don't block progress — start simple, add complexity when needed - Two-way doors don't block progress — decide, implement, revert if needed — because getting them wrong is recoverable
- WASM compatibility is preserved as a constraint, not treated as an active deliverable - WASM compatibility is preserved as a constraint, not treated as an active deliverable
- The framework creates a shared vocabulary for discussing decision urgency ("is this a one-way door?") - The framework creates a shared vocabulary for discussing reversal cost ("is this a one-way door?")
**Negative:** **Negative:**
- Classification requires judgment — some decisions are genuinely ambiguous (mitigated: classify up when in doubt) - Classification requires judgment — some decisions are genuinely ambiguous (mitigated: classify up when in doubt)

View File

@@ -7,9 +7,11 @@ last_updated: 2026-06-27
Questions are organized by theme. Each question has a stable OQ-ID for cross-referencing from spec documents. Questions are organized by theme. Each question has a stable OQ-ID for cross-referencing from spec documents.
Door type classifications follow ADR-009: Door type classifications follow ADR-009 — they describe **reversal cost** (how expensive it is to undo), not urgency:
- **One-way door**: Reversal requires rewriting significant code or permanently closes a capability. Requires ADR before implementation. - **One-way door**: Reversal requires rewriting significant code or permanently closes a capability. Getting it wrong is expensive — requires ADR before implementation.
- **Two-way door**: Reversal is cheap or additive. Can be decided during implementation. - **Two-way door**: Reversal is cheap or additive. Getting it wrong is recoverable — decide, implement, revert if needed.
Door type is separate from whether a decision is made. A two-way door is a decision you make now and can revert later, not a decision to defer. See ADR-009 §"What this framework is NOT."
## Theme: Core Types ## Theme: Core Types