Audit — P4.3.1 Three Advisory Roles (Translator / Sentinel / Guide)

1. Task framing

P4.3.1 ships the role adapter layer of the μ Integrity Monitor. It sits between P4.1.1 (the advisory envelope produced by detectors) and P4.6.1 (MCP tool surface that exposes role outputs). It is a pure presentation layer: roles consume advisories and produce three distinct output surfaces:

  • Translator → human-readable summary string
  • Sentinel → severity-gated escalation flag (SentinelFlag | null)
  • Guide → grouped corrective-action suggestions (Suggestion[])

The hard invariant is read-only: no role may mutate state, call a database, invoke π/α/κ/λ directly, or emit new advisories. Roles consume what detectors emit; they do not detect.

2. Source-of-truth references

Source Location Content
Concept doc docs/3-world/physics/enforcement/integrity.md §Three advisory roles (L117-127) The three-roles table + read-only invariant + “no mutator role” rule
Spec law docs/spec/s14-integrity-monitor.md §Advisory roles Translator/Sentinel/Guide access matrix (read input / read queue / read state)
Task prompt docs/guides/implementation/task-prompts/p4.1-mu-integrity.md §P4.3.1 (L848-1032) Detailed acceptance criteria + ready-to-paste prompt
Envelope src/domains/integrity/schema.ts Advisory, AdvisorySchema, AdvisorySeveritySchema, the 8 fields

2.1. Quotation — integrity.md L117-127

Three advisory roles

μ’s output is consumed by three roles, each with a different authority level:

Role Authority Response
Translator read-only Summarize advisory reports for a human operator; no recommendations of its own
Sentinel read-only Flag advisory reports that meet a severity threshold; may escalate to π
Guide read-only Suggest corrective actions for human review; the human decides whether to act

All three are read-only. μ does not have a “mutator” role. An advisory report that prescribes action is still a report; execution requires a separate π governance proposal or a T0-human authorization.

2.2. Quotation — s14-integrity-monitor.md

Advisory roles

Role Access Can execute
Translator Read input Suggest commands, cannot execute
Sentinel Read event queue + rule set Detect patterns, cannot veto
Guide Read state Explain state, cannot modify

The s14 phrasing is older (donor-era); the canonical contract is integrity.md L117-127 per R91 audit hierarchy. P4.3.1 honors both descriptions: every role is strictly read-only and the Sentinel emits a flag rather than calling π directly (so it satisfies both “may escalate to π” and “cannot veto”).

3. Envelope surface inventory (P4.1.1)

The fields the roles consume from Advisory:

Field Type Role consumption
role 'Translator' \| 'Sentinel' \| 'Guide' The advisory’s original role tag from the detector (NOT the consuming role); roles do not re-tag
check 'circular_logic' \| 'coercion_trap' \| 'axiom_drift' \| 'axiom_regression' Translator surfaces, Sentinel filters, Guide groups by
result 'PASS' \| 'WARN' \| 'BLOCK' Translator surfaces
severity 'LOW' \| 'MED' \| 'HIGH' Sentinel gates by threshold
evidence unknown[] Translator may surface count; Guide cites in advisory_refs indirectly via decision_hash
recommendation string Translator surfaces verbatim; Guide may aggregate into rationale
decision_hash 64-char lowercase hex Guide uses as advisory_refs[] cite key
timestamp_logical non-negative bigint Reserved for future use; not surfaced in v1 outputs

4. Output shapes — survey of similar Phase 0/1 patterns

The Phase 0 ζ Decision Trail surfaces (thought_record, thought_record_list) emit human-readable summaries via summary columns; the κ rule-engine verifies via recommendation strings; the λ Reputation surface returns typed JSON envelopes. P4.3.1 follows the typed-envelope pattern:

// Sentinel flag — analog to λ reputation_check_gates return shape
type SentinelFlag = {
  action: 'escalate_to_pi' | 'log_only';
  reason: string;
  advisory: Advisory;
};

// Guide suggestion — analog to κ rule-engine validation result groupings
type Suggestion = {
  headline: string;
  advisory_refs: string[];    // decision_hash values (64-char hex)
  rationale: string;
};

Translator emits a string directly — the simplest possible presentation surface, suitable for log lines, MCP content[].text payloads, or operator console rendering.

5. Forbidden surfaces (the “no mutation” perimeter)

The static-grep gate (acceptance criterion + CI grep) forbids the following substrings in roles.ts:

Pattern Reason
db.run better-sqlite3 mutation API
db.exec better-sqlite3 multi-statement mutation
db.prepare better-sqlite3 statement compile (could prepare an INSERT/UPDATE)
UPDATE SQL keyword (case-insensitive match in test)
INSERT SQL keyword
DELETE SQL keyword
update[A-Z] TypeScript verb naming (e.g. updateRow, updateState)
mutate TypeScript verb naming
Date.now() Wall-clock read (non-deterministic)
Math.random() Randomness (non-deterministic)
performance.now Wall-clock read

Roles may call into pure helpers from ./schema.js (e.g. computeDecisionHash is a pure SHA-256), but may NOT import:

  • ../tasks/repository.js (mutates the task store)
  • ../reputation/repository.js (mutates λ ledgers)
  • ../consensus/repository.js (mutates θ vote logs)
  • ../trail/repository.js (mutates ζ thought records)
  • ../proof/repository.js (mutates η Merkle leaves)
  • node:fs, node:crypto, better-sqlite3

Pure deterministic imports allowed: ./schema.js types only.

6. Severity-rank semantics (Sentinel gating)

The severity-rank mapping for the Sentinel threshold gate:

Severity Rank Notes
LOW 0 λ-aligned per R91 audit Q6
MED 1  
HIGH 2  

The gate is inclusive at the boundary: advisory.severity rank >= threshold rank returns a flag. Examples:

Advisory severity Threshold Flag returned?
HIGH LOW yes (2 ≥ 0)
HIGH MED yes (2 ≥ 1)
HIGH HIGH yes (2 ≥ 2)
MED LOW yes (1 ≥ 0)
MED MED yes (1 ≥ 1)
MED HIGH no (1 < 2)
LOW LOW yes (0 ≥ 0)
LOW MED no (0 < 1)
LOW HIGH no (0 < 2)

Flag action — always 'escalate_to_pi' when the flag fires (per integrity.md L124: “may escalate to π”). The 'log_only' action is reserved for downstream use (e.g. P4.4.1 escalation FSM may emit it for result: 'PASS' advisories that pass the threshold). In P4.3.1 the Sentinel always emits escalate_to_pi-tagged flags when the gate fires; the 'log_only' literal is exported in the union for forward compatibility but not produced by Sentinel.flag in P4.3.1 — it stays available for P4.4.1’s mapping table.

7. Guide grouping semantics

Guide.suggest(state, advisories) groups by advisory.check (4 possible values per AdvisoryCheckSchema). The output contract:

  • Cardinality — at most 4 Suggestions (one per distinct check).
  • Headline — derived from the check value (e.g. 'Address circular logic', 'Address coercion trap', 'Address axiom drift', 'Address axiom regression').
  • advisory_refs — array of decision_hash values from grouped advisories, preserving input order (deterministic).
  • rationale — composed from the count + grouped advisories’ recommendations.
  • state parameter — currently unused by the v1 grouping logic; reserved for future extension (e.g. system-state-aware suggestion ranking). Typed as unknown to keep the call site stable.

Empty input → empty array. Single advisory → 1 Suggestion of size 1.

8. Module layout

Path Role Note
src/domains/integrity/roles.ts Implementation Pure module; no I/O, no async
src/__tests__/domains/integrity/roles.test.ts Tests Mirrors detector test layout

Test layout matches sibling Wave-2 tests (e.g. src/__tests__/domains/integrity/detectors/drift.test.ts). The roles tests live one directory above detectors/ since roles.ts is at src/domains/integrity/roles.ts (sibling of detectors/, not nested).

9. Acceptance-criteria mapping

Each AC from the task prompt traces to a section in the contract (§7):

AC Source Contract §
AC#1 Translator class with readonly role integrity.md L123 §3.1
AC#2 Sentinel class with readonly role integrity.md L124 §3.2
AC#3 Guide class with readonly role integrity.md L125 §3.3
AC#4 Output types distinct (string / SentinelFlag|null / Suggestion[]) task prompt §3
AC#5 No “Mutator” role exported integrity.md L127 §I1
AC#6 Sentinel emits flag, never calls π integrity.md L124 §3.2 + §I2
AC#7 Guide suggests, never executes integrity.md L125 §3.3 + §I2
AC#8 Static-analysis gate passes (no mutation tokens) task prompt §I3
AC#9 npm run build && npm run lint && npm test pass task prompt §8
AC#10 No Date.now(), Math.random(), performance.now() task prompt §I4
AC#11 Severity-rank semantics (inclusive boundary) this audit §6 §3.2.2
AC#12 Guide cardinality ≤ 4 (one per check) this audit §7 §3.3.2

10. Open questions resolved

Q Resolution
Should Sentinel ever emit action: 'log_only'? No in P4.3.1 — the literal is exported for forward compat (P4.4.1 may use it); Sentinel.flag only ever emits 'escalate_to_pi' or null.
Should Guide consume the state parameter? No in v1 — reserved for future extension. Tests verify state is ignored (passing two different state values produces identical output).
Are role classes singletons? No — they are stateless adapters. Multiple instances are allowed (no class-level mutable state).
Does Translator output need to be parseable? No — it is human-facing. Tests check substring containment, not structure.
Does Sentinel need to set advisory on the flag, or just the hash? Yes — full advisory by reference. Cheap; the flag is short-lived.
Does Guide preserve input order in advisory_refs? Yes — for determinism (mirrors κ canonical iteration order).

11. Risk surface

  • Cargo-cult readonly: TypeScript readonly is compile-time only. The runtime guard is the CI grep gate (§5). The test file MUST include a static scanner that reads roles.ts and asserts none of the forbidden patterns appear. Without this test, the readonly modifier is decorative.
  • Severity ordering must match λ (R91 audit Q6 alignment). The mapping LOW=0, MED=1, HIGH=2 is canonical across μ + λ; do NOT introduce a μ-local ordering.
  • The advisory role field is informational, not behavioral. A detector emits an Advisory with role: 'Sentinel' (or similar); the consuming Sentinel adapter does NOT check this field. The role tag on the advisory encodes the origin (which detector layer produced it); the consuming role encodes the destination (how it is presented).

12. Out of scope

  • Persistencemcp_advisories table lands in P4.5.1.
  • Escalation FSM — 4-result + 3-invariant mapping lands in P4.4.1.
  • MCP tool surfacemu_* tools land in P4.6.1.
  • Fork-hook subscriber — π fork-event integration lands in P4.8.1.
  • Parity harness — 4-mock parity test lands in P4.7.1.

P4.3.1 is the pure presentation layer; everything downstream consumes Translator.summarize, Sentinel.flag, and Guide.suggest outputs.


End of audit.


Back to top

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

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