ADR 0131 — The Arquiteto routes modules to infra: it NOMINATES, Infra ELECTS
Context
The Arquiteto (Architect) already has six tools (create_module_map,
assign_story_modules, choose_project_image, create_c4_diagram,
propose_adr, emit_insight) and an image decision — choose_project_image
(ADR 0065) — that is per project: a single image, for the container of the
whole project. The real-container execution plan (ADR 0128/0130) asks for more
granularity: a project with modules of different stacks (a Node service, a
Python worker) does not fit in a single image when each module needs to come up
separately.
The design question is not "which image", it is "who decides what". The
Arquiteto already sees the architecture (it is the one who wrote the
module_map), but it does not see the fleet of images already in use, the cost
of maintaining one more variant, or the operational conventions Infra follows —
those are Infra's competences, not Architecture's. Giving the Arquiteto the
power to FIX each module's image (as choose_project_image fixes the
project's) would push an operational decision onto whoever only has the design
side.
Decision
The Arquiteto nominates; Infra elects. This delivery is only the
Arquiteto's half: a new tool, route_modules_to_infra, that produces a LIST —
one item {modulo, imagemCandidata, porque} per module of the current
module_map — and records it as artifact.module_routing, next to
artifact.module_map/artifact.project_image/artifact.c4_diagram (same
tableless design as ADR 0065, extended here: the event IS the artifact). The
half that ELECTS (the Infra Lead reading this list and deciding, with its own
proposed_action) is a separate PR — the routing exists and is auditable
before Infra has a tool to consume it, the same kind of sequencing that already
held for module_map arriving before create_c4_diagram needed it.
:direct, outside the global @registry — not a proposed_action.
Routing has no external effect: it does not bring up a container, it changes
nothing outside the event log. It is an INTERNAL architecture decision, of the
same caliber as choose_project_image and create_c4_diagram, which are also
:direct. Turning this into a proposed_action would put a draft decision
(which Infra will still review) in the same queue that approves git push.
The candidate image goes through the SAME validation as
choose_project_image, per item. validarDecisaoDeImagem
(domain/containers/project-container.ts) already knows how to refuse an image
without tag/digest, latest, and a short rationale — reimplementing that rule
per module would create two versions of it to diverge. The new domain
(domain/architecture/module-routing.ts) only adds what is SPECIFIC to the
list: it cannot be empty (it is not a decision), it cannot repeat a module
(ambiguous — which of the two images holds?), and each cited modulo must
exist in the project's current module_map (same check as
assign_story_modules/missingModules, and for the same reason: the refusal
lists the VALID names, so as not to force the model to guess).
Without a module_map, there is nothing to route — refused, not invented.
The tool only makes sense after create_module_map; the build_kickoff/1 of
ArquitetoServer (the only place from which the model learns the ORDER of the
tools) now lists route_modules_to_infra as step 4, right after the
module_map. Calling without a current module_map is refused with the same text
as create_c4_diagram — "defina o module_map antes" — instead of routing
against an empty or invented map.
Consequences
Nothing brings up a container yet. This delivery does not change the
RN-105 gate, does not touch RegistrarTransicaoDeContainerUseCase, does not
introduce a new proposed_action. What exists after it: an auditable list of
nominations, read by a human in the event log — Infra does not yet have a tool
to act on it (next PR).
The candidate image may diverge from the project image
(choose_project_image). On purpose: a module can justify an image different
from the one the Arquiteto fixed for the project as a whole — it is exactly the
granularity this delivery exists to open. Reconciling the two (or retiring
choose_project_image in favor of per-module routing) is a separate product
decision, not taken here.
EstadoDoRoteamento/GetModuleRoutingUseCase follow the same mold as
GetC4DiagramUseCase — tableless read, current by highest version with a
tie-break by seq. Existing as a separate use case (instead of inline in the
write one) is what gives correct versioning without duplicating the logic of
"which is the current one" — the same reason DecidirImagemDoProjetoUseCase
uses ObterContainerDoProjetoUseCase.