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 via Write is cleaner than a sequence of Edit ops — 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

  1. R-1: Link breakage — the current file is linked from several downstream pages. Mitigation: the file path and frontmatter-level title do 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 #anchor inside health.md per the grep -rn "health.md#" docs/ sweep (expected zero).
  2. R-2: Frontmatter drift — the rewrite must preserve all existing keys. Mitigation: the rewrite plan in §3 shows the exact frontmatter block verbatim; updated is the only key that changes.
  3. 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.
  4. 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:

  1. 8 negative sweeps → each must return 0.
  2. 6 positive sweeps → each must return ≥ 1.
  3. Frontmatter preservation (3 header lines present).
  4. 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:

  1. git restore docs/2-plugin/health.md
  2. Re-read this packet §3 for the rewrite content.
  3. Fix the specific sweep miss via an Edit delta.
  4. If the fix requires altering the overall structure, revise this packet first and amend the commit.

6. Commit

packet(r82-h-health): execution plan

Back to top

Colibri — documentation-first MCP runtime. Apache 2.0 + Commons Clause.

This site uses Just the Docs, a documentation theme for Jekyll.