diff --git a/internal/daemon/ARCHITECTURE.md b/internal/daemon/ARCHITECTURE.md index 7fafabe..709ad65 100644 --- a/internal/daemon/ARCHITECTURE.md +++ b/internal/daemon/ARCHITECTURE.md @@ -41,15 +41,20 @@ consumer-defined seams: declaring a function-typed interface for every call would balloon the surface for no win — services are unexported, so package-external code can never reach them. -- Capability hooks still take `*Daemon` as their receiver argument, - but `VMService` calls into them through a `capabilityHooks` struct - (function-typed bag) populated at construction. The service has no - `*Daemon` pointer. +- Capability hooks do not take `*Daemon`. Each capability is a struct + with explicit service-pointer fields (`workDiskCapability{vm, ws, + store, defaultImageName}`, `dnsCapability{net}`, `natCapability{vm, + net, logger}`) populated at wiring time. `VMService` invokes them + through a `capabilityHooks` struct (function-typed bag) populated at + construction; neither the service nor any capability has a `*Daemon` + pointer. -Lazy-init getters (`d.hostNet()`, `d.imageSvc()`, `d.workspaceSvc()`, -`d.vmSvc()`) let existing test literals (`&Daemon{store: db, runner: r}`) -keep working — the getter constructs the service from whatever is on -the `Daemon` if nothing was pre-wired. +Services + capabilities are built eagerly by `wireServices(d)`, called +once from `Daemon.Open` after the composition root's infrastructure is +populated, and once per test that constructs a `&Daemon{...}` literal. +Tests that want to stub a particular service or the capability list +assign the field before calling `wireServices` — the helper is +idempotent and skips anything already set. ## Service state