Episode 03Decision

Two decisions the build forced

With no requirement stage, this site's first decisions came from the build. Two estate decisions in three days, what each one settled, and what each one left open on purpose.

Published

In the lifecycle, a decision record follows a requirement. This site had no requirement, as episode 02 reported. Its first decisions came from the build instead.

There were two, both estate-wide, in three days. Each one was narrow on purpose. This episode reports what each one settled and what each one left open.

ADR-0022: the site does not run at home

At genesis on 7 September, the site’s deployment binding was a container on the home-lab host, behind a Cloudflare Tunnel. That was consistent with the estate’s deploy rules and needed no exception.

It was reviewed before any deploy code existed, and changed. The tunnel was not the problem. The decision says so: it is outbound only and routes only what a rule names.

The problem was what sits next to the site. The same host runs the estate’s source repositories and their access tokens, a CI runner with Docker socket access, the identity provider and two document stores. The decision puts it in one line:

The problem is consequence, not probability.

A flaw in a public web server would put an attacker on the same network as all of that. Hosting a folder of static files at home buys the estate nothing in return.

So the decision makes a class. A surface that is public, fully static and not regulated deploys to an external static host, never to the estate host. It was decided on 7 September. The code that delivered it merged the same evening, at 20:15.

Tested five weeks later

The decision also has a rule about configuration:

Ingress and routing configuration must live in the repository.

On 12 October the site needed www to redirect to the bare domain. The host’s redirect file matches paths, not hosts, so the redirect could not live in this repository. It is a rule in the Cloudflare dashboard.

The decision expected this. Where the host offers no configuration in the repository, the delivery brief records the value word for word. It does: the rule name, the match, the target, the status code and the owner. The deployment smoke test also fails if the redirect stops returning that exact 301.

That is the rule working as written. It is still a rule that exists in a console, and the repository holds a description of it, not the rule itself.

Cited since

ADR-0022 is cited by the site’s deploy workflow, its delivery brief, ten of its canonical artefacts and one later estate decision. It created a class with one member. Five weeks on, it still has one.

ADR-0023: a repository must say what it serves

On 9 September I read a status message and saw that this repository served a product that did not exist. The skill that seeds a repository had never asked.

The decision makes it ask. A new repository declares one of two things: the product it serves, or that it serves none, with a reason. For a product, the seeding skill checks that the brief exists first.

Silence is not a third option.

The engine enforced it two days later, on 11 September. The first repository seeded under it, on 16 September, declares its product in its own adoption file. Five later estate decisions cite ADR-0023.

What it left open, on purpose

It is not retroactive. This repository caused the decision, and it still declares nothing. Its adoption file has no product field. The decision lets a checker report that gap, but not fail it, and no checker reports it today. One older repository, the shared component library, added its declaration anyway on 11 September. This one did not.

It does not order the lifecycle. This is the part that matters for this series. The decision says:

This decision does not require that a brief has progressed to a requirement or a spec before genesis.

A brief is enough. The order that episode 02 reported, production first and no research or requirement at all, is still permitted today. The decision calls the full order “a separate question”, and no later decision settles it.

What the two have in common

Both decisions came from building, not from a requirement. Both were made within days of the problem, before much depended on the wrong answer. Both are narrow, and say what they do not cover.

That narrowness is a choice I would make again. It also means the gap that started this episode is still open. The estate now refuses a new repository with no product declaration. It does not refuse one with no requirement.

Next

Episode 04 is the hand-off from a decided product to shaped work: the point where the lifecycle gives the build its first objective.