docs(website): record slice 2, and the three defects a browser found
The Modules screen as built (website#143), and the three things standing it
up against a live server exposed. Two of them are older than this phase and
neither was reachable from a test.
- A fresh install over a row the previous boot left `startup_failed` rendered
the old failure - "module directory not present on the volume", one second
after the files were written there - and, because that branch is not
pending, suppressed the restart banner the install had just told the
operator to use. Fixed by a derivation rather than a special case: the
loader scans once at require time, so a module on the volume with no live
record arrived after that scan and anything the row says predates it.
- The boot refresh had been nulling every install's provenance. source and
sha256 exist for this screen and never survived a restart, because
lifecycle.boot() re-records with neither and the upsert assigned both
unconditionally. It could not have been found before Phase 4 wrote the first
non-null value those columns ever had - and the model's test fake
reproduced the defect faithfully, assigning exactly like the SQL.
- The restart killed the server outright on Windows. process.kill(pid,
'SIGTERM') reaches the graceful handler on Linux and is unconditional
termination where POSIX signals do not exist, so a Windows host got no
module onShutdown, no pool close and no log flush. process.emit('SIGTERM')
reaches the same listener everywhere. The test had stubbed process.kill and
asserted the call - precisely the call whose meaning differs by platform.
That last one generalises: deployment is Linux containers and would never
have shown it. A smoke that only runs where the code ships cannot find a class
of defect that only bites the people developing it.
Also documents the COALESCE in BACKEND_DESIGN's installed_modules section.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -700,7 +700,7 @@ directory on the modules volume and its URL segment.
|
||||
| `name`, `version` | the manifest's label and semver, for the admin Modules screen |
|
||||
| `state` | ENUM `installed` / `enabled` / `disabled` / `started` / `startup_failed` |
|
||||
| `failure_stage`, `failure_reason` | the stage a failure happened at (`manifest`, `core_api`, `mounts`, `extensions`, `schema`, `require`, `register`, `boot`) and its recorded reason |
|
||||
| `source`, `sha256` | the release the bundle came from and the digest verified before unpacking; both NULL for a directory placed on the volume by hand |
|
||||
| `source`, `sha256` | the release the bundle came from and the digest verified before unpacking; both NULL for a directory placed on the volume by hand. **Written only by an admin-panel install, and `COALESCE`d on upsert** — see below |
|
||||
| `installed_at`, `started_at`, `updated_at` | `started_at` is the last **successful** start |
|
||||
|
||||
**This table never decides which routes exist.** The module loader scans the filesystem at require
|
||||
@@ -722,6 +722,15 @@ on the volume (with NULL provenance for a hand-placed directory), marks any row
|
||||
overwrite the operator's decision. Every one of those writes is individually caught: a row that will
|
||||
not update is worse reporting, never a failed boot.
|
||||
|
||||
**Provenance is `COALESCE`d on upsert, and that is load-bearing.** The boot write above passes NULL
|
||||
for `source` and `sha256` — honestly, since a scan finds a directory and never where it came from —
|
||||
so a plain `source = VALUES(source)` overwrites both columns on *every* boot, and an admin-panel
|
||||
install's provenance survives only until the restart that install asks for. The statement is
|
||||
`source = COALESCE(VALUES(source), source)`: a value overwrites, a NULL leaves what is there. The cost
|
||||
is that hand-placing a different bundle over a row installed from a URL keeps the old provenance,
|
||||
which is stale rather than blank. Found in Phase 4 by installing a module and restarting; it could not
|
||||
have been found earlier, because until then no caller had ever passed a non-null value.
|
||||
|
||||
Design of record: [`MODULE_SYSTEM.md`](MODULE_SYSTEM.md) §2.4; the loader's obligations are
|
||||
[`MODULE_API.md`](MODULE_API.md) Part 4.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user