# ADR-0012 — The node owns the Hub Daemon's start, not a shell

- **Status:** **Rejected** (Operator ruling, 2026-07-25, same day it was accepted), then
  **partially revised** (Operator ruling, 2026-07-26 — see *Revision* below). Built at W20,
  withdrawn before it reached any node. Kept as a record of the reasoning and of why it was
  wrong, because the mistake is the instructive part.
- **Requirements:** none. `REQ-DAEMON-AUTOSTART` was withdrawn with it. `REQ-DAEMON-SUPERVISED`
  (W25) is where its *title* finally came true — by a different mechanism than this proposed.
- **Vindicated in title, not in shape (W25).** The node does now own the daemon's start: spt's
  own daemon supervises it as a ResidentService. That is not this ADR resurrected. What was
  rejected here was **a second start mechanism living inside alchemy** — a Scheduled Task
  alongside the shell's auto-ensure — and the Operator's objection was to the second
  mechanism, not to the node owning the start. W25 honors that objection more completely
  than the rejection did: alchemy's start-path count goes from one to **zero**, because the
  authority moves out of alchemy entirely rather than being duplicated inside it.
  Do not read this as licence to add an autostart mechanism here. There is still none.
- **Related:** `docs/KNOWN-HAZARDS.md` 1.5, Request #22, spt-core REQ-EP-8 / ADR-0023.

## What was proposed

A Scheduled Task (`"Alchemy Hub Daemon"`) running this binary's own `daemon ensure` every
five minutes while the user is logged on — so Task Scheduler, not a shell, would be the
daemon's spawn ancestor and the repeat would double as supervision.

The motivation was real and measured: spt-core's force-close is `taskkill /PID <pid> /T /F`,
a process **tree** kill, so tearing down whichever shell auto-ensured the daemon takes the
daemon with it, and nothing restarts it (pid → 0 within 0.1s of a teardown, still 0 after
15s, 2026-07-25). The blast radius is the Watch engine, so State-change delivery stops
node-wide, silently.

## Why it was rejected

`CONTEXT.md` §Hub Daemon already says the daemon "is started as a detached child by a shell
instance" and that "**its lifetime is independent of any shell instance**". That is a
mandated property. Alchemy implements it: the spawn is detached, no inherited stdio, its own
process group. A platform whose force-close reaches that process is **violating the
property**, not exposing a gap for alchemy to fill.

Three consequences of accepting the workaround, none of them acceptable:

1. **It concedes ownership of a property alchemy already holds.** Once alchemy carries a
   second start path "because the platform takes the first one away", the daemon's
   independent lifetime becomes alchemy's ongoing obligation to defend against its host —
   forever, and in a place we have already been wrong once.
2. **It hides a defect that is not ours alone.** The tree kill reaches *any* adapter that
   spawns a detached service. Patching alchemy specifically leaves that live everywhere
   else, and removes the pressure that gets it fixed where it actually is.
3. **It is a second mechanism for something that already has one.** The daemon's start
   rides whichever endpoint with a linked alchemy shell comes up first. That is the design.
   Adding a scheduler alongside it means two things can start the daemon, with two
   different lifetimes and two different failure modes, for no property the design did not
   already promise.

## Decision

**No autostart mechanism in alchemy.** The daemon is spawned detached by a shell instance,
as specced. Request #22 stays open against spt-core, where the violation is.

## Revision (Operator ruling, 2026-07-26)

**The decision below is revised: alchemy will declare `start = 'boot'`, and its
auto-ensure-at-bind retires.** This is deliberate, not a reversal by drift — and the
original reasoning is honoured exactly, because of *what* it actually banned.

The ban was on **a second start mechanism inside alchemy**, carried to compensate for a
platform that took the first one away. Under spt-core's `ResidentService` substrate
(ADR-0049, design ratified 2026-07-26; `REQ-RESIDENT-SERVICE`, inactive pending delivery)
the Hub Daemon is declared in the adapter manifest's `[service]` section and **core owns
the process from birth** — spawned job-neutrally by the daemon, never a shell's child and
never inside a launching terminal's Job Object. So `start = 'boot'` is not a second
mechanism bolted onto alchemy; it is core becoming the **single start authority**, and
ensure-at-bind is the mechanism that goes away. The count of start paths inside alchemy
falls from one to zero.

The Operator's `boot` is desired-state-running, reconciled at daemon boot, at **adapter
registration against a live daemon** (installing an adapter never requires an spt restart
to bring its service up), at hold release, and at bind as a defensive ensure.

What survives from the rejection, unchanged:

- **Alchemy still carries no start mechanism of its own.** That was the principle, and it
  is more true after this revision than before it.
- **ADR-0013's relay hop retires at adoption**, along with the remaining service-side half
  of `REQ-SHELL-CLI-SPAWN-JOB-EXPOSURE`, which closes by construction once nothing alchemy
  spawns is a shell descendant.

**Not yet true.** `REQ-RESIDENT-SERVICE` is inactive and delivery is unscheduled. Until it
lands, the Hub is still spawned by a shell instance's auto-ensure through the relay hop, and
`CONTEXT.md` §Hub Daemon deliberately still says so — the glossary records what *is*, so it
changes at **adoption**, not at ratification.

## The lesson (why this file is kept)

A question that reads as open design is frequently a property already specced and now
violated. A decision to make and a defect to report are different problems with different
owners, and getting it backwards concedes ground the project already holds. This ADR is
what that mistake looks like when it is written down convincingly: everything in the
original text was true except the premise that the behaviour was alchemy's to work around.
Check `CONTEXT.md` first — before proposing, filing, conceding, or accepting a framing,
including one offered by a peer.
