From 9c8c4a3ee5772d74b4920fc1be8a86428469ac66 Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 5 Sep 2026 11:15:53 +0200 Subject: [PATCH] =?UTF-8?q?Issue=20013=20=E2=80=94=20a=20module=20cannot?= =?UTF-8?q?=20run=20its=20own=20code=20at=20a=20lifecycle=20phase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Surfaced converting the catalogue: a module can declare things that exist (dir/file/network/container) but not a step that runs at a point in its lifecycle. mosquitto's dynsec admin client must be seeded before first start; the DB providers have nowhere for a migration or health-gate; it is the timing face of issue 011. Framed as a missing module capability, not a defect. Records the prior-art event hooks and their real warning — powerful but complex and flaky — so the resolution avoids rebuilding that. Ends in open questions (run-once resource vs general lifecycle hook, where the code runs, idempotency). Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF --- .../00-report.md | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 04-ISSUES/013-a-module-cannot-run-code-at-a-lifecycle-phase/00-report.md diff --git a/04-ISSUES/013-a-module-cannot-run-code-at-a-lifecycle-phase/00-report.md b/04-ISSUES/013-a-module-cannot-run-code-at-a-lifecycle-phase/00-report.md new file mode 100644 index 0000000..b160390 --- /dev/null +++ b/04-ISSUES/013-a-module-cannot-run-code-at-a-lifecycle-phase/00-report.md @@ -0,0 +1,57 @@ +--- +status: open +opened: 2026-09-05 +located-in: [] +fixed-by: +amended-design: +--- + +# 013 — A module cannot run its own code at a lifecycle phase + +## The symptom, as observed + +Found while converting the catalogue (2026-09-05), across several modules at once. A module can +declare *things that exist* — a directory, a file with fixed content, a network, a container — but +it cannot declare *a step that runs* at a defined point in its own lifecycle. Three converted +modules need exactly that and have nowhere to put it: + +- **mosquitto.** Its Dynamic Security plugin will not start unless `dynamic-security.json` already + contains an admin client *before the broker's first start* — the broker loads the plugin at + boot. Seeding it is a run-once step that must happen after the file resource exists and before + the container starts. The vocabulary has no "before first start." +- **The database providers (postgres/mongodb/mssql).** First-boot seeding works today only because + the *image* happens to do it from an env var. Anything the mesh itself must run once against the + server — a schema migration, an extension enable, a health gate before the module is announced + ready — has no home. +- The seed-then-mutate family already recorded in [011](../011-reconciling-a-seed-file-wipes-what-grew-in-it/00-report.md) + is the same shape seen from the *content* side; this is it seen from the *timing* side. + +## Why it matters beyond the instance + +This is not a defect in a module — it is a **capability the module system does not yet offer.** A +real class of modules needs to run their own code at points in the build/install/run lifecycle: +seed-before-start, migrate, post-start health-gate, pre-remove drain. The declarative resource +model deliberately describes *state*, not *steps*, and that is right for what it covers; the gap is +that some modules genuinely have a step. + +**Prior art, and its warning.** An earlier mesh had exactly this as a feature: event-driven +**hooks** that ran custom code at phases of the build/publish/deploy pipeline. It was powerful and +it was **complex to set up and flaky** — which is the real content of this record. The need is not +in question; the cost of the obvious answer is. Whatever shape this takes must not reproduce that +fragility, or it will be worse than the gap. + +## Open questions + +- Is the right unit narrow — a **run-once / init resource** ("run this once, here, in the + lifecycle") — or general — a **per-phase lifecycle hook** on a module, and if so which phases + (build / publish / install / pre-start / post-start / pre-remove)? +- Where does a hook's code run — in the module's own runtime container under its scoped account + (ADR 0048/0052), so it inherits the same isolation as its tools and events? Or is some of it the + host's, before a container exists? +- How is a step made **idempotent and reconcilable** so a re-apply does not re-run it + destructively — the same discipline the resource model gets for free and a step does not? +- What is the smallest version that unblocks the three modules above without rebuilding the old + flaky hook engine? Is "seed-before-first-start" alone enough for now, with the general case + deferred? +- A rule states how it is checked: whatever shape is chosen, what lab scenario proves a hook runs + exactly once, at the right phase, and converges on re-apply?