How you actually wire this thing

The technical configuration.

Everything above describes behaviour. This page is the build sheet: where the briefs and skills live on disk, what the database really is, the unusual write-path that makes it work, the MCP servers behind it, which models the agents run, and the rules that keep rotation safe.

Where everything lives

Filesystem layout

All paths are under the user's Clawpilot home, ~/.copilot (i.e. C:\Users\smccormick\.copilot).

Role briefs
m-role-briefs/CHIEFOFSTAFF.md (CoS), SOLUTIONARCHITECT.md (SA), STEWARD.md. Plus m-role-briefs/scouts/ for the 4 K-scout briefs: BACKLOG-SENTINEL.md, PIPELINE-WATCHER.md, REUSE-SCOUT.md, INTAKE-TRIAGE.md. (Signal Scout's contract lives inline in ring-tick/SKILL.md Job B.)
Delivery + ops skills
m-skills/ — the engagement skills: daily-brief, decision-capture, ado-bulk-triage, ado-scribe, ring-tick, outbound-voice, bootstrap-roles, rotate-role, plus the utility cowork and the deprecated forwarding shims bootstrap-clawpilot-roles / rotate-clawpilot-role. Five further folders (signal-scout, backlog-sentinel, pipeline-watcher, reuse-scout, intake-triage) are empty scaffolding — the scout contracts live in the role briefs.
Workflow configs
m-workflows/<workflow-name>/workflow.json — the authoritative config that bootstrap-roles and rotate-role read. haleon/workflow.json declares CoS/SA/Steward/Operator + DAB audit mode (haleon-aiac-db) + phase boundaries (2026-09-14 Pilot→Scale, 2027-02-08 Scale→Transform). loom/workflow.json declares Architect/Operator/Steward + plan_md_only audit.
General skills
skills/ — Clawpilot-wide skills available to any session, including ui-designer and visual-storyteller (used to build this very site), plus pptx, docx, xlsx, loop, web-artifacts-builder, etc.
MCP servers
m-mcp-servers.json — the MCP topology (see below).
Automations
m-automations/automations.json — scheduled-task definitions (currently a Loom watchdog template; the Haleon ring-tick is built and staged, but its recurring schedule is intentionally off pending a Charlie posture decision — see Roadmap).
DAB runtime
A files/dab/ folder under a session-state directory — holds dab-config.json (52 entities post Batch 6) and dab-mcp-shim.js (the MCP wrapper).

Two rings, one ceremony library. Both the Haleon delivery ring (CoS / SA / Steward) and the Loom illustrator stack (Architect / Operator / Steward) are launched and rotated by the same generic skills bootstrap-roles + rotate-role, dispatched on workflow-name. All workflow-specific surface (role names, brief paths, models, audit entities, ledger location, escalation channel, phase calendar) lives in workflow.json — no skill hardcodes a ring. The old loom-specific bootstrap-clawpilot-roles / rotate-clawpilot-role remain as deprecated forwarding shims for back-compat.

The single brain

The database

Engine
PostgreSQL, running locally on Charlie's laptop (localhost:5432) — not Azure Database for PostgreSQL or any managed service.
Database
haleon_aiac
Schema
haleon
Base tables
~29 — 22 originals (listed below) plus Batch 3–7 substrate (rotation_log, agent_snapshots, tick_lease, scout_lease, drainer_lease, scout_watermark, scout_enable_flags)
DAB entities
52 — base reads + *In shim views + action-views (e.g. CostRollupDayIn, CostBreakerRollingIn, ScoutEnabledCheckIn, OpenAgentRun)
Access
via the haleon-aiac-db MCP server → Data API Builder → Postgres. No application talks to Postgres directly.

The 22 base tables

Table (snake_case plural)DAB entity (PascalCase singular)Holds
signalsSignalraw inbound pings to triage
briefsBriefthe 5-3-2-1 briefs
pod_intelPodIntelper-pod status intelligence
customer_healthCustomerHealthcustomer sentiment / health signals
comms_draftsCommsDraftoutbound drafts awaiting sign-off
quiet_watch_stateQuietWatchStatethe Quiet Watch list
recipient_registryRecipientRegistryrecipient voice profiles
meetingsMeetingmeeting records
meeting_prepsMeetingPrepmeeting prep docs (+ loop_page_url)
steerco_prepsSteercoPrepSteerCo prep docs
pd_sync_prepsPdSyncPrepPD-sync prep docs
risks_issues_blockersRiskIssueBlockerthe RIB register
ado_writeback_queueAdoWritebackQueuepending ADO write-back ops (drained by ado-scribe; CAS-guarded via sp_update_ado_writeback)
decisionsDecisionengagement + technical decision narrative (now with decision_class, owning_peer, adr_id, narrative routing columns)
agent_runsAgentRunper-turn audit spine (multi-writer; 7-member class enum; gap-scan key (session_id, session_turn_seq))
adrsAdrarchitecture decision records
compliance_stateComplianceStateper-use-case compliance posture
cost_telemetryCostTelemetryidempotent whole-UTC-day cost cells (derived rollup of agent_runs)
reuse_catalogReuseCatalogreusable pattern catalog
schema_versionSchemaVersionschema migration marker
skill_versionsSkillVersioninstalled skill versions
transcriptsTranscriptmeeting-transcript archive (rotation snapshots now live in agent_snapshots)

Plus 7 Batch 3–7 substrate tables not listed above: rotation_log + agent_snapshots (Steward audit, D4); tick_lease, scout_lease, drainer_lease (three independent single-flight mutexes); scout_watermark (per-source scan cursors); scout_enable_flags (the 3-greens DB gate, read by sp_check_scout_enabled()).

Naming footgun. The DB tables are snake_case plural; DAB exposes them as PascalCase singular. A mapping table is patched into CHIEFOFSTAFF.md (~lines 29–53). Use the entity name (left column of your MCP calls) — not the table name — when calling DAB.

The clever bit

The write-path shim — and why it exists

The schema uses 15 PostgreSQL ENUM types (e.g. agent_tier, brief_slot, comms_draft_status, decision_status, rib_severity/rib_status/rib_type, pod_id, tone) plus jsonb columns. Data API Builder v1.7.93 cannot bind text → enum or text → jsonb over its Postgres path — a direct create_record on those columns fails with SQLSTATE 42804.

The fix that is actually in place

text in via MCP <Entity>In view INSTEAD OF trigger sp_* function casts base table
  • 17+ write-shaped views named v_<entity>_in, exposed as DAB entities: SignalIn, BriefIn, CommsDraftIn, QuietWatchIn, PdSyncPrepIn, MeetingPrepIn, SteercoPrepIn, AgentRunIn, DecisionIn, AdoWritebackIn, RibIn, RecipientIn (the original 12), plus Batch 2–7 additions: AdrIn, ComplianceStateIn, DrainerLeaseIn, AdoWritebackResetIn, RotationLogIn, AgentSnapshotsIn, TickLeaseIn, ScoutLeaseIn, ScoutWatermarkIn, ScoutEnableFlagsIn, SchemaMigrationIn, plus action-views CostRollupIn / CostRollupDayIn / CostBreakerIn / CostBreakerRollingIn / ScoutEnabledCheckIn and the read-view OpenAgentRun.
  • Each shim view has at least an INSTEAD OF INSERT trigger that casts text → enum/jsonb and delegates to one of the sp_* PL/pgSQL functions (well over 25 now — covering create, CAS-guarded UPDATE, reset, rollups, breakers, lease acquire/release, watermark advance, ADR stage advance, session-seq allocation, etc.). INSTEAD OF UPDATE triggers exist on v_comms_draft_in, v_agent_run_in, v_ado_writeback_in (CAS-guarded per ISS-03), v_adr_in (stage-CAS via sp_advance_adr_stage, Fix-G) and v_brief_in (Unit H); v_signal_in carries an explicit deny on UPDATE (signals are append-only); the rest do their update semantics by repeating the INSERT (upsert inside the SP).
  • Writes go through the *In entities; reads use the base entities. jsonb columns are passed as JSON-encoded strings at the MCP boundary.

Why not just call the functions? An earlier attempt exposed the sp_* functions directly as DAB stored-procedure entities. That failed — DAB does not support procedure/function entities on PostgreSQL. The views-plus-triggers approach replaced it.

The data-API layer

Data API Builder surfaces

Version
Data API Builder v1.7.93, database-type: postgresql
REST
exposed under /api
GraphQL
exposed under /graphql
MCP
dab.exe --mcp-stdio exposes tools read_records, create_record, update_record, delete_record, execute_entity, describe_entities
Auth provider
Simulator (dev); the MCP runs as the anonymous role (now read-only on all base tables — ISS-22)
Permissions
per-entity, per-role. Per ISS-22 Option A (resolved 30 May, Unit H closed the residual), the anonymous role is tightened to least-privilege: every base table is read-only, so the only sanctioned write path is the validating *In views (which carry the INSTEAD OF triggers + sp_* functions). Six base tables were flipped to read-only in Unit H (Brief, MeetingPrep, PdSyncPrep, QuietWatchState, Signal, SteercoPrep); the original 6 (ado_writeback_queue, agent_runs, comms_drafts, decisions, recipient_registry, risks_issues_blockers) had already been tightened. authenticated=* is defined on every entity as the dormant production posture; the MCP runs a single role:anonymous instance.

The DAB MCP shim. dab-mcp-shim.js wraps dab.exe --mcp-stdio and rewrites spec-violating null envelope fields in DAB's responses (instructions: null → "", annotations: null → {}, _meta: null → {}) so strict MCP clients accept them. It is load-bearing — without it, strict clients reject DAB's replies.

The tool surface

MCP topology

Clawpilot spawns one DAB shim + one dab.exe per session connected to haleon-aiac-db — there is no shared global server. The consequence: a config change requires respawning each consuming session to pick it up; there is no global hot-reload. (This is what drives the rotation chain when the schema or config changes.)

ServerKindRole in the ring
haleon-aiac-dbnode shim → dab.exethe shared Postgres brain (all reads/writes)
azure_devops@azure-devops/mcpADO backlog read + write-back (via ado-scribe drainer). This MCP is the single source of truth for the ADO org/project/area path — the ado-bulk-triage and ado-scribe skills no longer hardcode a target (ISS-05, resolved). Currently pointed at smccormick0886/Haleon-AIAQ; that pointer is environment config and will change per deployment.
m365_* (native)builtin Clawpilot capabilitymail / calendar / Teams signal sources — a builtin, not an MCP entry, so it appears in a separate permissions section rather than m-mcp-servers.json. The deferred m365-softeria MCP was removed (ISS-21, resolved); the ring uses these native tools.
msx-mcpMicrosoft Sales Experience / DataversePipeline Watcher scout's data source — opportunities scoped by the MCP's own identity (territory + RLS). Confidentiality labels routed to SA via decisions, not enqueued raw.
github (or gh CLI)GitHubReuse Scout's data source — configured engagement org commits + PRs (allowlist).
filesystembuiltinworkspace file access
filesystem_extraserver-filesystemextra root at the loom illustrator working tree
playwrightbuiltinbrowser automation (e.g. Loop, Cowork)

Who runs on what

Model assignments

The CoS, SA and Steward all run on claude-opus-4.7-1m-internal per the haleon workflow.json (this site's model assignment) — the 1M-context build, picked uniformly for the engagement. Self-rotation has historically lifted individual roles to newer builds (a previous Steward briefly ran on claude-opus-4.8); the canonical default expressed in workflow.json is the 1M build for all three. Scout agents run as short-lived child sessions under model NULL in their agent_runs rows because the v1 scout sweeps are deterministic / rule-based and don't make LLM calls; if a future scout variant adds LLM classification, that field is populated.

Always pass an explicit model on spawn. Implicit inheritance of the parent's model previously caused a wrong-model incident. Every m_spawn_session in the ceremony skills names its model explicitly — never rely on inheritance.

Keeping rotation safe

Rotation governance

These rules were written from real incidents and are enforced by the Steward:

  • Rotator spawns the successor. A role being rotated never spawns its own replacement. The Steward is the special case: Operator is the named selfRotationRotator in haleon/workflow.json, so the outgoing Steward emits a self-rotation audit row and Operator runs rotate-role on its behalf (D6).
  • Archive, never delete. The Steward always closes with delete:false. The sole exception is a genuine ZOMBIE session, and only with an explicit Charlie m_ask_user ack quoting the zombie's sessionId + lastActivityAt (D5 ZOMBIE redesign — default is cold-spawn + archive, transcript-synthesis / delete:true is the escalated path).
  • Check before closing. Before closing any session it doesn't own, the Steward checks totalTurns + lastActivityAt and requires Charlie ack if the session looks active.
  • No boot-turn race (D3). The rotator awaits the successor's ## Context health green ack BEFORE releasing the SDK handle, and releases with keepPendingTurn:false. A red/malformed/file-not-found ack means do NOT release, do NOT shut down the predecessor.
  • No orphan-turn race (D4). A lightweight status-probe re-adopts the successor handle AFTER release and BEFORE the heavy readiness smoke-test.
  • Address by sessionId from spawn until close (D8). The reused sessionName is ambiguous between predecessor and successor for the duration of the ceremony; every op targets a sessionId.
  • Brief-missing HALTs the rotation. If the resolved briefPath from workflow.json doesn't exist on disk, rotate-role HALTs and surfaces to the workflow's contact channel — it does NOT fall through to the ZOMBIE branch (D1 second-half fix).

What is deliberately not here

Secrets

This documentation contains no credentials by design. The Postgres connection string — host, database, username and password — lives only in dab-config.json under the DAB runtime folder. It is intentionally excluded from every page of this site and must never be copied into documentation, screenshots, or any committed file. The same applies to the Loop sandbox link (an access-bearing tokenised URL) and any ADO PAT: document where they live and how things are wired, never the secret values themselves.

See what the ring produces →