Vital Sign · Design Spec · m1nd-mcp (backend only)

Skeleton Coherence Vital Sign

A passive health signal that detects when a serving brain answers a request with a foreign skeleton — a skeleton whose slug does not belong to that brain. Born from a real incident where a brain bound to ~/m1nd served sk_keyvault_candidate for days with no alarm.

signal-only never blocks a read never blocks a write backend only · m1nd-ui/ untouched reuse, do not reinvent

1 · The one-line contract

Given the serving brain's identity (project_root basename, or a display name when present) and the store's skeleton (skeleton_id + block ids), return one of three honest answers:

Ok — slugs agree (after sanitization) Mismatch{expected_slug, found_slug} — foreign skeleton None / silent — empty store, no signal

ABSOLUTE: this is a vital sign, not a gate. It MUST NOT cause any read or write to fail, be refused, or change behavior. It only reports.

2 · The incident that made it necessary

A session bound to the root brain ~/m1nd kept answering system_blocks_snapshot with a store whose skeleton_id was sk_keyvault_candidate — a skeleton that belonged to a different project (keyvault). For days nothing surfaced the mismatch: the snapshot looked well-formed, the north packet read normally, no metric tripped.

The missing piece was a coherence check — the serving brain's identity vs. the served skeleton's slug — surfaced where operators and agents already look: the snapshot result and the orientation packet.

3 · The coherence check — reuse what already exists

The engine already exists and is already pub. The maker's job is to surface it, not to rewrite it. Verified present in the worktree:

// m1nd-mcp/src/skeleton_scan.rs:154
pub enum SkeletonCoherence {
    Ok,
    Mismatch { expected_slug: String, found_slug: String, reason: String },
}

// m1nd-mcp/src/skeleton_scan.rs:1473
pub fn skeleton_coherence(
    project_root: Option<&str>,
    display_name:  Option<&str>,
    skeleton:      Option<(&str, &[String])>,   // (skeleton_id, block_ids)
) -> Option<SkeletonCoherence>

3.1 · Rules the engine already encodes (do not re-implement)

Reuse-first (binding): there is exactly one slug sanitizer that matters — the one skeleton_scan uses to mint ids. The coherence engine already calls it. The surface code MUST call skeleton_coherence() and MUST NOT invent a parallel sanitizer; otherwise a future drift between mint-rule and check-rule silently re-opens the incident.

4 · Data flow — identity in, verdict out, surfaced twice

Serving brainproject_root
display_name?
Identitydisplay_name else basename_of(root)
sanitize_slug(reused) → expected_slug
skeleton_coherence()vs skeleton_id + sb_ ids
Noneempty store → no signal
Option<SkeletonCoherence>Ok · Mismatch · None
Surface Asystem_blocks_snapshot →
skeleton_coherence field
Surface Bnorth / orientation packet →
sickness line (on Mismatch)

5 · Surface A — system_blocks_snapshot result

Handler: handle_system_blocks_snapshot (system_blocks_handlers.rs:70). It already returns a READ-only JSON and already loads the store — the coherence field is computed from the same load, adding zero new I/O.

Before

{
  "present": true,
  "store_version": 1,
  "block_count": 3,
  "store": { … }
}

After

{
  "present": true,
  "store_version": 1,
  "block_count": 3,
  "skeleton_coherence": {
    "status": "mismatch",
    "expected_slug": "m1nd",
    "found_slug": "keyvault",
    "reason": "skeleton_id slug does not match …"
  },
  "store": { … }
}

6 · Surface B — the sickness line in the north packet

The orientation packet is composed in the HTTP routing layer (the caller of project_brains::bootstrap, per project_brains.rs:242). When coherence is a Mismatch, append one sickness line to the packet the agent reads on orientation:

{
  "north": {
    …
    "skeleton_coherence": {
      "status": "sick",
      "expected_slug": "m1nd",
      "found_slug": "keyvault",
      "advice": "this brain is serving a foreign skeleton; rebind or re-ingest before trusting block scope"
    }
  }
}

7 · RED battery (straight from the incident)

Four cases. Two are incident-specific and MUST be added as tests; two are covered by existing engine tests and are listed for completeness.

#Serving brain identityskeleton_id block idsVerdictWhy
1~/m1nd → slug m1nd sk_keyvault_candidate sb_keyvault_… Mismatch
{expected:"m1nd", found:"keyvault"}
The incident. expected m1nd; the served slug is keyvault. Both named.
2~/m1nd → slug m1nd sk_m1nd_candidate sb_m1nd_… Ok Own-root skeleton. Healthy baseline — no false alarm.
3(no store) Silent (None) Empty store = no signal. Snapshot says "no skeleton yet"; packet omits the line.
4My-Repo → slug my_repo sk_my_repo_candidate sb_my_repo_… Ok Sanitization is part of the rule: My-Repomy_repo, and sk_my_repo_candidate starts with sk_my_repo_. No false positive.

Existing engine tests already proving the spine: skeleton_coherence_has_no_signal_without_a_skeleton, skeleton_coherence_reuses_scan_slug_rules (case 4 family), skeleton_coherence_reports_identity_and_block_mismatches — all in skeleton_scan.rs test module.

8 · File / location map for the maker

FileRoleAction
m1nd-mcp/src/skeleton_scan.rs Engine: SkeletonCoherence enum (:154), skeleton_coherence() (:1473), sanitize_slug (:1512). reuse No new sanitizer. Add the two incident tests (cases 1 & 4).
m1nd-mcp/src/system_blocks_handlers.rs handle_system_blocks_snapshot (:70). wire Compute coherence from the loaded store + brain identity; emit skeleton_coherence.
m1nd-mcp/src/* (routing) Where the north/orientation packet is composed after bootstrap(). wire On Mismatch, append the sickness line. Silent otherwise.
docs/ (snapshot + north surface) Agent-facing doc describing the snapshot fields & the orientation packet. doc gate Document the new field + sickness line.
m1nd-ui/** The served web UI. FORBIDDEN Do not touch. Backend signal only.

9 · Maker checklist & proof