Packet — R82.H docs/2-plugin/health.md rewrite plan
Scope. Exact Edit/Write operations to execute in Step 4. One file touched:
docs/2-plugin/health.md. The current file carries enough fabrication that a full rewrite viaWriteis cleaner than a sequence ofEditops — the rewrite still preserves the Jekyll frontmatter and the three load-bearing non-payload paragraphs (stdio rationale, never-throws claim, logging table).
Audit: docs/audits/r82-h-health-audit.md (792be44a)
Contract: docs/contracts/r82-h-health-contract.md (ce52979b)
Target file: docs/2-plugin/health.md (current: 121 lines, 7 kB)
Operation: Full rewrite via Write (content plan in §3).
Tests: docs-only slice — no Jest invocation; sweep-based verification in Step 5.
1. Change summary
| Area | Before (current file) | After (rewrite) |
|---|---|---|
| Frontmatter | 7 lines with updated: 2026-04-16 |
7 lines with updated: 2026-04-19 (other keys identical) |
| Implementation path citation | src/domains/system/tools.ts (L10) |
src/tools/health.ts |
| Tool-surface count | “19-tool surface” (L10) | “14-tool surface” |
| Input schema | detail=summary\|full Zod object (L16-27) |
Empty object z.object({}) — zero arguments |
| Output shape | 12-field JSON blob with database.*, memory.*, checks.* sub-objects (L29-54) |
6-field flat payload: status, version, uptime_ms, db_tables, phase, mode |
| Failure-modes table | SAFE_MODE, DIAGNOSE, heap thresholds (L56-65) |
Single paragraph: “handler never throws; db_tables falls back to 0 on any DB error” |
| Periodic-checks section | 30-second setInterval + 5 checks (L69-82) |
Removed entirely. Replaced by a short “Deferred to Phase 1+” callout citing the manifest. |
| Peer tools | unified_init, unified_vitals (L88-94) |
Replaced by server_ping (the actual Phase 0 peer per src/server.ts:538) |
| Logging section | Correct (L98-111) | Kept verbatim |
| Cross-references | Correct (L115-121) | Kept, one link added to src/tools/health.ts for reader traceability |
Net: file shrinks from 121 lines to roughly 95 lines.
2. Risks
- R-1: Link breakage — the current file is linked from several downstream pages. Mitigation: the file path and frontmatter-level
titledo not change, so existing[label](health.md)or[label](/AMS/2-plugin/health.html)links remain valid. Anchor IDs within the file change (section slugs differ after a rewrite); however, no other page links to a#anchorinside health.md per thegrep -rn "health.md#" docs/sweep (expected zero). - R-2: Frontmatter drift — the rewrite must preserve all existing keys. Mitigation: the rewrite plan in §3 shows the exact frontmatter block verbatim;
updatedis the only key that changes. - R-3: Phase 1+ callout misreading — readers could read the “Deferred to Phase 1+” section as a Phase 0 capability. Mitigation: use a
Note:admonition-style opening and an explicit “not in the Phase 0 response” qualifier. Every contract §1.2 strike term must be absent from the rewritten body even inside the Phase 1+ callout if naming it would risk confusion — when in doubt, cite the manifest row instead of the feature name. - R-4: Word-boundary grep false-positives — e.g. “mode” appears in “server modes” as well as the
mode:payload field. This is a positive sweep (must return ≥ 1) so false-positives are harmless.
3. Rewrite plan — full file content
Use a single Write call with the following content. The file fully replaces
the current version; the worktree is clean at origin/main so no merge is
needed.
3.1 Frontmatter (lines 1-7 of new file)
---
title: Health — the `server_health` MCP tool
tags: [plugin, health, observability, phase-0]
updated: 2026-04-19
parent: "2 — Plugin: The Colibri Server"
nav_order: 30
---
(Matches the current frontmatter with only the updated date bumped.)
3.2 Body outline
# Header + stdio rationale (kept from current file)
# Tool contract
- Name: server_health
- Inputs: no arguments; empty object schema (cite src/tools/health.ts:57)
- Output shape (6 fields, flat): status, version, uptime_ms, db_tables, phase, mode
- Example JSON payload
- Failure posture: never throws; db_tables → 0 on any DB error
# Related system tools (Phase 0)
- server_ping — the only other system-tier tool in Phase 0
# Deferred to Phase 1+
- Short callout: periodic checks, SAFE_MODE/DIAGNOSE reporting, memory/event-loop probes are aspirational surfaces targeted for Phase 1+; not in the Phase 0 response.
# Logging (kept verbatim)
# Cross-references (kept, with src/tools/health.ts added)
3.3 Exact final file content
The full content to write in Step 4 is:
---
title: Health — the `server_health` MCP tool
tags: [plugin, health, observability, phase-0]
updated: 2026-04-19
parent: "2 — Plugin: The Colibri Server"
nav_order: 30
---
# Health — the `server_health` MCP tool
Phase 0 does **not** expose an HTTP `GET /api/health` endpoint. Health is surfaced the same way every other capability is surfaced: as an **MCP tool** in the 14-tool surface defined by [ADR-004](/AMS/architecture/decisions/ADR-004-tool-surface.html). The tool name is `server_health` and it lives in the α System Core, registered from `src/tools/health.ts` by `bootstrap()` at `src/server.ts:555`.
> **Why an MCP tool, not an HTTP endpoint.** Phase 0 transport is **stdio only**. There is no HTTP server, no dashboard, no port to bind. The AMS donor's `GET /api/health` and `npm run dashboard` both belonged to a web-UI layer that Phase 0 does not ship; both are cited in the donor reference ([`../reference/extractions/`](../reference/extractions/)) but not carried forward. When the Phase 2+ web surface lands, a health HTTP handler may wrap the same underlying probe — but `server_health` stays the tool-surface entry point.
---
## Tool contract
**Name.** `server_health`
**Inputs.** None. The input schema is `z.object({})` (empty object) — the tool accepts **zero arguments**. Non-object arguments are rejected by stage 2 of the α middleware chain with the standard `INVALID_PARAMS` envelope. See [s17 — MCP Surface](../spec/s17-environment.md) and [middleware](middleware.md) §Stage 2 for the schema registration pattern.
**Output shape.** A flat 6-field payload, exactly as returned by `buildHealthPayload(ctx)` in `src/tools/health.ts`:
```json
{
"status": "ok",
"version": "0.1.0",
"uptime_ms": 1283,
"db_tables": 12,
"phase": "phase2",
"mode": "FULL"
}
```
| Field | Type | Meaning |
|-------|------|---------|
| `status` | literal `"ok"` | The handler never throws and never returns a non-`"ok"` status in Phase 0. See "Failure posture" below. |
| `version` | string | Server version; populated from `package.json` at `createServer` time. |
| `uptime_ms` | integer ≥ 0 | Wall-clock milliseconds since `bootStartMs`, floored to an integer. Uses `performance.now()` so clock adjustments cannot make it negative. |
| `db_tables` | integer ≥ 0 | Count of user tables in the Colibri SQLite DB (excludes `sqlite_*` internals). Falls back to `0` when `ctx.db` is undefined or the query throws (closed handle, locked WAL, migration-in-progress). |
| `phase` | `"phase1"` \| `"phase2"` | `"phase1"` during transport-only boot; `"phase2"` once `startup.ts` Phase 2 has opened the database. |
| `mode` | `"FULL"` \| `"READONLY"` \| `"TEST"` \| `"MINIMAL"` | The runtime mode fixed at `createServer` time. Mirrors `RUNTIME_MODES` in `src/modes.ts`. |
The payload is assembled synchronously; tests assert a response time well under the 100 ms SLA (`src/__tests__/tools/health.test.ts` describe 3).
**Failure posture.** The handler never throws. The only branch with a defensive fallback is `db_tables`, which becomes `0` when `ctx.db` is undefined (Phase 1 boot) or when the SQLite probe throws. There is no `SAFE_MODE` or `DIAGNOSE` mode-transition side-effect from this tool — `mode` is a pure read-through of `ctx.mode`.
---
## Related system tools (Phase 0)
`server_health` has one sibling in the α System Core:
| Tool | Purpose | When to call |
|------|---------|--------------|
| `server_ping` | Minimal liveness probe — returns `version`, `mode`, `uptime_ms` | Smoke tests, handshake confirmation |
These are the only two system-tier tools in the Phase 0 14-tool surface. See [`../reference/mcp-tools-phase-0.md`](/AMS/reference/mcp-tools-phase-0.html) for the full 14-tool inventory.
---
## Deferred to Phase 1+
> **Note.** The following are aspirational features targeted for Phase 1+ and are **not** part of the Phase 0 response: a 30-second periodic-check loop, a ring buffer of historical check results, structured `database` / `memory` sub-objects with heap and RSS readings, and a degraded-status reporting surface. The Phase 0 response is exactly the 6 flat fields listed above; anything richer is scheduled for a later milestone once the monitoring contract is ratified. The R82 manifest (`r82-phase-0-1-stabilization/manifest.md` row H) is the record of this decision.
---
## Logging
All server output goes to stderr. Stdout is reserved exclusively for MCP JSON-RPC frames — see [s18 — Stdio Invariant](../spec/s18-stdio.md). Overriding `process.stdout.write` or emitting any non-JSON byte on stdout corrupts the MCP transport and is the kind of bug that was caught the hard way in the AMS donor (documented in [`../reference/extractions/`](../reference/extractions/)).
Log verbosity is controlled by `COLIBRI_LOG_LEVEL`:
| Level | Shows |
|-------|-------|
| `error` | Errors only |
| `warn` | Errors + warnings |
| `info` | Normal operation (default) |
| `debug` | Verbose; every α-chain dispatch |
The donor `AMS_LOG_LEVEL` variable is **not read** by Phase 0 code. Any environment variable in the `AMS_*` namespace is heritage — Phase 0 reads `COLIBRI_*` only per [s17 — Environment](../spec/s17-environment.md).
---
## Cross-references
- [`src/tools/health.ts`](https://github.com/LastEld/AMS/blob/main/src/tools/health.ts) — the live handler (registered via `registerHealthTool(ctx)` at `src/server.ts:555`)
- [ADR-004 — Tool Surface](/AMS/architecture/decisions/ADR-004-tool-surface.html) — the 14-tool list that includes `server_health`
- [Boot](boot.md) — two-phase startup and when tools register
- [Database](database.md) — how `ctx.db` is opened in Phase 2
- [Middleware](middleware.md) — the α chain `server_health` flows through like any other tool
- [s18 — Stdio Invariant](../spec/s18-stdio.md), [s17 — Environment](../spec/s17-environment.md)
(end of file)
3.4 Line-count check
New file length estimate: ~95 non-blank lines within the backticked content block. Final written file will be one full markdown document (the outer `````markdown` fencing in §3.3 is just for this packet’s display — the written file is the fenced content with the outermost triple-backtick fence stripped).
4. Test strategy
Docs-only slice. No Jest. No npm invocation.
Step 5 verification runs the §2 sweeps from the contract:
- 8 negative sweeps → each must return
0. - 6 positive sweeps → each must return ≥ 1.
- Frontmatter preservation (3 header lines present).
- Never-throws preservation (≥ 1 occurrence).
The verification file captures the literal command + output for each sweep.
5. Rollback plan
If the Step-4 Write produces a file that fails any Step-5 sweep:
git restore docs/2-plugin/health.md- Re-read this packet §3 for the rewrite content.
- Fix the specific sweep miss via an
Editdelta. - If the fix requires altering the overall structure, revise this packet first and amend the commit.
6. Commit
packet(r82-h-health): execution plan