architecture.decide
Generated by docs/scripts/generate-docs.mjs from operators/architecture-decide/operator.md and operators/architecture-decide/operator.json. Edit the source, not this page.
Binding
| Field | Value |
|---|---|
id | architecture.decide |
domain | architecture |
resources.profile | sol-fresh |
resources.requires | — |
policy.webSearch | — |
policy.grammarBound | — |
policy.imageGeneration | — |
Job
Decide one architecture with its tech stack, system boundaries, and data ownership, and prove it against the observed current state, the rejected alternatives, verified compatibility, and an independent critique.
Observe before proposing
Nothing is proposed before the current state has been observed at the frozen head of
@workspaces/be and written to response/data/current-state.json with its own fingerprint. A
proposal written before the observation describes a system simpler than the real one, and every
later comparison inherits that simplification. An observation taken at another head is worse: it
looks rigorous while describing code that no longer exists.
Incumbency is not authority
The inventory says what the system runs today; that is the most useful and the most dangerous context this operator receives. An existing framework, datastore, broker, or deployment shape enters the decision in exactly two roles: as a measurable constraint the target must satisfy, or as observed evidence about behaviour already proved. It never enters as a reason by itself. A component justified because it is already there is rejected outright.
Prove, do not assume
An alternative is counted only when it is materially different, different in ownership or mechanism, not in wording, and assessed on exactly the trade-off axes the person named. Every retained component carries a verified verdict with evidence across runtime version, deployable unit, communication failure, datastore ownership, and backup and restore; a verdict that skipped an axis is a partial check wearing a complete label. Every boundary answers the data question: it owns at least one store or states that it owns none; a store names one owning boundary that writes it, and a second writer exists only with an explicit shared-write justification.
The decision names every write it commits to
A boundary that owns a store says nothing about who writes it, when, or under which transaction, so
the decision closes that gap itself: response/data/stack-model.json carries one operations entry
per write this architecture commits to, and ## Operations of the receipt restates the same rows.
Each entry names its transport, its writer, the stores it touches, its transaction boundary, its
idempotency kind, the migrations it ships, and the coverage-matrix dimensions of the business head it
implements. That list is the frozen contract backend.source.apply fills: the implementation restates
those operations unchanged and may add none, so an operation nobody declared here cannot be written
anywhere. Declaring a write is not the same as choosing an implementation, which is why the writer is
the one file path this operator names.
The critique is a nested exchange
After the selected alternative is deepened, the branch pauses: it emits response/response.json
with status waiting and awaiting { exchange: critique, kind: independent-critique }. The
orchestrator writes critique/request/request.json with only response/data/stack-model.json as
input, never the author’s rationale, and runs a fresh agent on this operator’s own profile with no
inherited turns. That agent writes only critique/response/. When its response is done the paused
agent resumes at the confirmation step. Other branches of the same step keep running throughout.
Boundary
Context is read-only. The operator writes only response/ of its own branch: response.md,
data/current-state.json, data/stack-model.json, the alternatives page when more than one
alternative was asked for, and response.json; the critique agent writes only
critique/response/. It does not mutate routed source, publish business authority, start or
reconfigure runtime services, name implementation files in the handoff, or claim that an
implementation, a quality gate, or a UAT run has passed.
When the model Input is present it is the authority for this run and the published head is lineage
only, because a decision taken against yesterday’s promise is a decision against the wrong promise.
When it is absent the published head is the authority.
Context
| Alias | Bind | Required |
|---|---|---|
@workspaces/be | the routed backend checkout read at the frozen head; the inventory comes from its manifests and deployment files | yes |
@worktrees/businesses/<featureId> | the published business head, the promise the architecture must keep; evidence when the session carries a model Input | yes |
@knowledge/patterns | reusable shapes the scope may bind; a shape, never a selection | no |
Inputs
| Kind | From | Required |
|---|---|---|
architecture-decision | a prior run of architecture.decide on the same or an adjacent boundary; lineage that may be contradicted, never ignored | no |
model | business.decide; the head that branch modelled, when it has not been published yet | no |
Requirements
| Field | Type | Default | Ask |
|---|---|---|---|
objective | prompt | — | The objective the architecture must achieve, in the person’s words |
decisionId | id | slug of objective | The name the artifacts carry |
alternatives | number 1–4 | 1 | How many materially different designs to generate; more than one only when a comparison was asked for |
tradeoffAxes | list | cost, complexity, reversibility | The axes every alternative is scored on and the critique attacks along |
constraints | list of {id, kind, statement} | — | kind is fixed-intent, measurable, preference, assumption or unknown; at least one fixed-intent |
selectionPolicy | choice | automatic | automatic: the operator selects and records why; approval-required: the person selects |
approval | id | null | The approved alternative id; required only under approval-required, supplied on resume after CHOICE_REQUIRED |
resume | token | null | The blocked branch’s token when re-entering after a stop |
Steps
| # | Step | Params | Reads | Writes | Stops with |
|---|---|---|---|---|---|
| 1 | Validate the gate and resume | resume, approval | request/request.json, input architecture-decision when present, @workspaces/be at the frozen head | — | INVALID_INPUT, SOURCE_DRIFT, NO_PROGRESS |
| 2 | Observe the current state | — | @workspaces/be at the frozen head: manifests, configuration, deployment files, @tools/git | response/data/current-state.json | CURRENT_STATE_UNOBSERVED |
| 3 | Bind the inventory to the business promise | — | response/data/current-state.json, input model when present, otherwise @worktrees/businesses/<featureId> at its published head | — | BUSINESS_AUTHORITY_REQUIRED, EVIDENCE_MISSING |
| 4 | Frame the decision | objective, decisionId, constraints, tradeoffAxes | request/request.json requirements | — | CONSTRAINT_CONTRADICTION |
| 5 | Generate the alternatives | alternatives | response/data/current-state.json, @knowledge/patterns, @tools/websearch | response/artifacts/<decisionId>-alternatives.html only when more than one alternative was asked for, @tools/visualize | NO_VIABLE_ALTERNATIVE |
| 6 | Select | selectionPolicy, tradeoffAxes, approval | response/artifacts/<decisionId>-alternatives.html when present | — | CHOICE_REQUIRED |
| 7 | Deepen the selected alternative and declare the operations it commits to | constraints | response/data/current-state.json, the bound business head’s coverage matrix for the dimensions each operation cites | response/data/stack-model.json, including its operations | DATA_OWNERSHIP_UNASSIGNED, COMPATIBILITY_UNVERIFIED |
| 8 | Await the critique: pause, a fresh agent attacks the selection, resume when it answers | — | critique/response/critique.md once the exchange is done | response/response.json (waiting, awaiting critique) | CRITIQUE_UNRESOLVED |
| 9 | Confirm or return the selection | selectionPolicy | critique/response/critique.md, response/data/stack-model.json | — | CHOICE_REQUIRED, NO_VIABLE_ALTERNATIVE |
| 10 | Write the handoff and emit | — | everything above | response/response.md, response/response.json | — |
Under the defaults, step 5 produces one design and no comparison page, step 6 has nothing to
choose, and the decision’s quality rests on step 8. When the only alternative fails an attack, step 9
stops with NO_VIABLE_ALTERNATIVE, not CHOICE_REQUIRED. The handoff names contracts, never
implementation files, because choosing the files is the next domain’s job; the one exception is the
writer of each declared operation, which this operator does name, because the implementation may not
choose its own writer.
Outputs
| Kind | File | Type | Required |
|---|---|---|---|
architecture-decision | response/response.md | md | yes |
current-state | response/data/current-state.json | data | yes |
stack-model | response/data/stack-model.json | data | yes |
alternatives | response/artifacts/<decisionId>-alternatives.html | artifact | no |
independent-critique | critique/response/critique.md | md | yes |
response/response.md carries the ## Operations table and response/data/stack-model.json carries
the matching operations array; together they are the mutation contract backend.source.apply
consumes, and no other output of this operator crosses into that step.
Stops
| Code | Disposition |
|---|---|
INVALID_INPUT | terminate |
SOURCE_DRIFT | terminate |
NO_PROGRESS | terminate |
EVIDENCE_MISSING | terminate |
CURRENT_STATE_UNOBSERVED | terminate |
BUSINESS_AUTHORITY_REQUIRED | terminate |
CONSTRAINT_CONTRADICTION | terminate |
NO_VIABLE_ALTERNATIVE | terminate |
CHOICE_REQUIRED | fallback |
COMPATIBILITY_UNVERIFIED | fallback |
DATA_OWNERSHIP_UNASSIGNED | terminate |
CRITIQUE_UNRESOLVED | terminate |
Next
| When | Operator |
|---|---|
| the business promise must be modelled again against the decided boundaries | business.decide |
| the decision is confirmed and a backend contract changes | backend.source.apply |
| the decision is confirmed and a frontend surface changes | frontend.direction.decide |