Decision Log

Every strategic decision Mandantis has resolved. 202 registered · 44 published · filter by BA element to narrow.

44 shown
  1. DEC-053

    Multi-newsletter engine + AI Governance Intelligence as Mandantis's first commercial newsletter

    2026-05-02 · resolved

    Decision

    The Mandantis newsletter engine (E-016 F-118..F-127) is **multi-tenant from v1.0**. Mandantis runs ≥2 newsletters. **AI Governance Intelligence** is the first commercial product (paid B2B, weekly Tuesday 13:00 UTC, region-grouped editorial, no ads, no sponsorship — hard product constraint). The AI Governance Intelligence newsletter is owned by a new sibling Epic **E-033 — Commercial Newsletters v1.0**, gated on E-016 engine landing.

    ### Concretely

    1. **Engine is multi-tenant by design.** Each newsletter is a tenant identified by `slug` (e.g. `mandantis-authority`, `ai-governance`). Engine primitives — subscribers, segments, editions, send queue, tracking, compliance, Glass Box — are tenant-aware. Schema TBD at F-118 pickup; the leading shape is per-tenant rows in shared tables (`subscribers.newsletter_slug`, `editions.newsletter_slug`) keyed by slug + RLS where applicable. 2. **Two newsletters in v1.0 scope.** - **Mandantis authority newsletter** (E-016, free, audience-building, default-public Glass Box per E-011 Q10 / DEC-033) — Mandantis's own thesis-led publication; first-party content drawn from `signal.articles`. - **AI Governance Intelligence** (E-033, paid B2B, no ads/sponsorship, region-grouped editorial structure: Europe / North America / APAC primary AU+NZ+SG+JP / APAC watching-brief KR+CN+IN / Standards / Frontier Lab Policy / "What to do this week" closer; weekly Tuesday 13:00 UTC) — sourced from a curated regulator/standards-body source list (~8 RSS + ~31 web_search ground-truthed during PoC). 3. **ESP = Resend.** Retains DEC-115 lineage; aligns with DEC-039 (outbound notification email = Resend through `mail.mandantis.com`). Sending domain for newsletters TBD at F-118 pickup — `mail.mandantis.com` is in use for transactional and is a candidate; `news.mandantis.com` (DEC-115's choice) remains an option for newsletter-only segregation if subscriber-side recognition / deliverability data warrants it. 4. **Subscribers + editions + send-events live in Supabase per DEC-001.** Substrate diff from legacy DEC-115: subscribers move from Dataverse → Supabase. All writes go through mgp-api per DEC-032. Per DEC-045, newsletter is **authoritative-shared** (multiple consumers — mgp-api for ops, public Glass Box reads, future surfaces) — no direct-binding shortcut; everything flows through the `mgp-api` chokepoint. 5. **Engine-vs-product split is governance-load-bearing.** Engine code (adapters, send orchestration, tracking, compliance, Glass Box primitives) is shared infrastructure under E-016 — multi-tenant from the first commit. Product code (per-newsletter source lists, editorial templates, scheduling, paywall/access control if any) is per-Epic, instantiating the engine. This split keeps revisability local: engine-architecture decisions stay in E-016; commercial-product decisions stay in E-033 (and future commercial-newsletter Epics). 6. **No ads, no sponsorship, no syndication for AI Governance Intelligence in v1.0.** Subscriber-funded only. Pricing model TBD at E-033 product Feature pickup (separate decision; not bundled into this DEC).

    ### Out of scope

    - **Editorial content** — DEC ratifies the structure, not the words. - **Per-newsletter pricing model + payment integration** — separate decision at E-033 Feature pickup. - **LinkedIn Newsletter** (LinkedIn's native product) — already deferred to v1.1 per E-016 §"Out of scope". - **PoC repo cleanup** (`/mnt/repos/projects/newsletter` + sovereign CF resources `nle-*` on Mandantis CF account) — separate Feature under E-010 or one-off task. - **Multi-lingual / localised editions** — v1.x.

    Rationale

    The 2026-05-01 → 2026-05-02 sovereign newsletter PoC (`/mnt/repos/projects/newsletter`) built a working source-discovery + ingestion stack for AI Governance content (RSS adapter + web_search adapter via Sonnet + `web_search_20250305` + prompt caching, validated end-to-end across 25 of 31 web_search sources) before being retired into Mandantis governance. The PoC settled three things worth ratifying as Mandantis decisions:

    1. **AI Governance is a real product, distinct from Mandantis's authority voice.** Different audience (governance/compliance practitioners vs strategy buyers), different commercial model (paid B2B vs free audience-building), different cadence (weekly Tuesday vs ad-hoc when an article ships). Folding it into E-016's existing Mandantis-authority scope would muddy the Epic-as-decision-unit principle — separate Epic is the right boundary. 2. **The engine is naturally multi-tenant.** PoC's `packages/newsletters/<slug>/` convention proved that tenant-keyed editorial config + a shared engine works. Building the engine multi-tenant from day 1 is cheaper than retrofitting. 3. **The substrate must be the new world's substrate.** DEC-115's Dataverse subscribers + four-Worker layout + sovereign D1/Vectorize/R2/Queue stack do **not** carry forward — they're artefacts of the retiring PoC and the legacy mgp-systems repo. The new world is Supabase + mgp-api + (post-E-017) mgp-mcp. Reaffirming Resend keeps the one DEC-115 element that does carry forward.

    The alternatives considered:

    - **Single Epic (expand E-016 to cover both newsletters)** — rejected. Couples two distinct commercial models into one revisability boundary; AI Governance pricing/access decisions would force E-016 reopens long after the engine is closed. - **Two DECs (one for multi-tenant engine; one for AI Governance as first commercial product)** — rejected. The two are tightly coupled at this moment in time; splitting fragments the decision without aiding revisability. - **No new DEC (amend E-016 + Feature stubs only)** — rejected. The pivot itself (sovereign PoC retired into Mandantis governance + commercial newsletter strategy + multi-tenant engine commitment) is forward-looking and DEC-worthy. Without a DEC, future sessions have no anchor to cite when adding the third newsletter.

    BA element
    Content Delivery capability · Publish stage
    Business processes
    BP-016
    SPEC reference
    DEC-001 (Supabase primary); DEC-002 (Cloudflare Workers sole compute); DEC-003 (four integration patterns); DEC-008 (lowercase-hyphenated naming); DEC-032 (business-system data via mgp-api); DEC-039 (outbound notification email = Resend through mail.mandantis.com); DEC-045 (Boundary-of-Authority); legacy DEC-115 (retired-world Resend on news.mandantis.com / Dataverse subscribers / Mandantis authority newsletter)
    Register entry
    DEC-053-multi-newsletter-engine-ai-governance-first-commercial.md
  2. DEC-052

    Pre-bash hook layer as defence-in-depth on top of mgp-secret wrapper

    2026-05-01 · resolved

    Decision

    The pre-bash hook installed at `~/.claude/settings.json` (script source at `packages/mgp-cli/hooks/block-unsafe-infisical.sh`) is **supplementary** to the `mgp-secret` wrapper and credentials register. It is not load-bearing on its own.

    The wrapper + register are the **structural** defence (any bypass requires editing source code or governance docs). The hook is the **harness-layer** defence — it catches careless `infisical secrets [list]` invocations before they reach bash, regardless of which tool typed them.

    This DEC exists because the hook is a separate enforcement surface with its own kill switch (`cli:mgp-hook:enabled`), telemetry events (`mgp.hook.block`, `mgp.hook.disabled`), and bypass posture. The expectation that it is supplementary, not primary, is a governance fact that must not be implicitly forgotten.

    ### Posture

    - Pre-bash matching: blocks unsafe Infisical CLI patterns (`infisical secrets` list form; `infisical secrets get` without `--plain`; pipes to display commands; `mgp-secret get NAME` piped to display when `--output`/`--env` not used). - Pattern coverage: 14+ block patterns + 5+ allow patterns; all in `hooks/test-block-patterns.sh` (CI-runnable). - Performance: under 50ms per command (verified in CI). - Telemetry: every block emits `mgp.hook.block` to the local audit sink with sanitised command. Hook does not call mgp-api directly (no remote sink dependency at the harness layer). - Kill switch: `cli:mgp-hook:enabled` (DEC-048 hierarchy layer 5). When false, the hook passes commands through and emits one `mgp.hook.disabled` event per session. - Bypass: removing the hook from `~/.claude/settings.json` is permitted but flagged in audit (the absence of expected `mgp.hook.*` events is itself detectable).

    ### Why hook stays supplementary

    A hook that tried to be primary defence would accumulate an ever-growing list of patterns to keep up with new providers, new tool names, new escape sequences. The wrapper + register pattern is closed-set: the only safe path is `mgp-secret`, and the wrapper enforces every guarantee. Patterns to block in bash are **the known unsafe alternatives to the wrapper** — a finite set that grows slowly.

    ### Quarterly review

    Block patterns are reviewed quarterly. The review process: 1. List `mgp.hook.disabled` events from the past quarter — if disabled often, the hook is too aggressive. 2. List newly-introduced provider CLIs and credential-handling tools — add patterns if they have unsafe defaults. 3. Test new and existing patterns against `hooks/test-block-patterns.sh`. 4. Update the standard at `docs/standards/kill-switch-pattern.md` (which references this hook) if the discipline changes.

    Quarterly review is not a STOP gate; reviewers can defer to next quarter if no changes are warranted.

    Rationale

    The hook addresses a specific failure mode that the wrapper alone cannot: **the operator types a raw `infisical secrets ...` command into bash, bypassing `mgp-secret` entirely.** Without the hook, that command runs and leaks values to transcript before any wrapper sees it. With the hook, the harness blocks the command before bash interprets it.

    Putting this enforcement at the harness layer (not in bash, not in shell aliases) makes it survive shell config drift, multiple operator hosts, and casual environment changes. Claude Code processes every command through `~/.claude/settings.json` hooks; this is the right place for harness-level CLI gating.

    BA element
    Pillar 02 Decision Architecture · secret-handling-discipline
    Business processes
    BP-009
    SPEC reference
    DEC-046 (credential operations hardening); ~/.claude/settings.json hook contract
    Register entry
    DEC-052-pre-bash-hook-defence-in-depth.md
  3. DEC-051

    Register-as-index — credentials register set is the authoritative index of stored secrets

    2026-05-01 · resolved

    Decision

    The credentials register set at `mandantis-governance/governance/credentials/tokens-*.md` is the **authoritative index** of every secret stored in Infisical. A secret with no register entry **does not exist** for the purposes of `mgp-secret`.

    ### Concretely

    - Every `tokens-*.md` register file declares a YAML frontmatter (subject, account, owner, governing decisions, rotation, infisical path) plus a markdown table inventorying named secrets. - `mgp-secret get NAME`, `mgp-secret put NAME`, `mgp-secret keys`, and `mgp-secret validate` consult this index before any Infisical call. - A `get` or `put` for a name not in any register file exits non-zero with: *"Secret 'NAME' (path PATH) is not in any credentials register file. Add a register entry first, then retry. Direct Infisical access is not permitted."*

    ### Register format

    The wrapper reads the **existing** register format — YAML frontmatter + token-inventory table with first column being the secret name. **No new fenced-block format is introduced.** Register files migrate in-place from `cf-*.md` naming to `tokens-*.md` naming as part of E-032 F-307 (Phase 0).

    Required frontmatter fields: ```yaml id: cred-{slug} type: credential entity: "..." owner: "Sean Simmons (sean@mandantis.com)" status: active | rotated | archived created: YYYY-MM-DD governing_decisions: [DEC-NNN, ...] rotation: "..." # human-readable cadence infisical_path: "prod/{provider}/..." ```

    Required body section: a markdown table with at least these columns: - **Name** (the Infisical secret name) - **Type** (e.g. Meta, Scoped, Manual) - **Provider** (e.g. cloudflare, microsoft, supabase) - **Infisical** (path) - **Status** (active, rotated, deprecated)

    Additional columns (token ID, scope, etc.) are encouraged but not required.

    ### Bootstrap chicken-and-egg resolution

    `mgp-bootstrap-app init` writes the register entry to a local file **before** calling `mgp-secret put`. The wrapper accepts a `--bootstrap-mode` flag that trusts in-process register additions (the entry is on disk, awaiting commit; the bootstrap writes Infisical, then opens the PR). PR is opened **last**, with the full diff including the register entry.

    This avoids the chicken-and-egg of "register entry must exist before secret stored" vs "secret must be stored before register can be authored as a PR."

    ### Migration of existing secrets

    `mgp-secret seed-register --provider P --path /prefix` reads existing Infisical state for a provider and generates register stubs with default metadata (created date, ownership, scope). Operator reviews + commits the seed PR before `mgp-secret` enforcement turns on. Run once per provider during E-032 Phase 2 install.

    Without seed-register, day-1 of `mgp-secret` enforcement would fail every existing call. The seed step is a Phase 2 prerequisite, not Phase 0.

    Rationale

    A register that drifts from the secret store is an audit-trail liability. Today's state (pre-DEC-051) has registers covering CF + MS Graph + Infisical machine identity, but no register entry for Anthropic, Resend, Cal.com, Axiom, Supabase project keys, or any future provider's secrets. New secrets land via ad-hoc `infisical secrets set` with no governance trail.

    Making the register the **gate**, not just the **log**, eliminates this drift class. Every secret in Infisical has a register entry by construction; every register entry has a secret in Infisical (validated by `mgp-secret validate`). The two are kept in lockstep.

    BA element
    Pillar 02 Decision Architecture · secret-handling-discipline
    Business processes
    BP-009
    SPEC reference
    MGP-GOV-001 §12 (agent register pattern; this DEC parallels it for credentials); DEC-046 (credential operations hardening); DEC-018 (operational traceability)
    Register entry
    DEC-051-register-as-index-policy.md
  4. DEC-050

    Credential rotation cadence — 12-month default; structural defences over rotation frequency

    2026-05-01 · resolved

    Decision

    Mandantis credential rotation cadence stays at the **12-month default** for all programmatically-rotatable secrets. The 24-month MS Graph scoped exception (DEC-042) stays as a local tactical choice, justified by long horizon + event-driven rotation.

    **The 2026-04-30 leak event does not justify accelerated cadence.** The leak was caused by an unsafe CLI invocation pattern (`infisical secrets` list form), not by token age. Faster rotation does not prevent the next pattern-class leak — the wrapper + hook + register-as-index (DEC-046) does. Rotation is a recovery tool, not a prevention mechanism.

    ### Cadence table

    | Credential class | Cadence | Rotation method | Owner | |---|---|---|---| | Provider meta-tokens (CF `agt-001-meta`, MS Graph self-bootstrap, Supabase Mgmt PAT) | 12-month | Manual via dashboard (programmatic-mint capable but cannot self-rotate due to provider design) | Principal | | Programmatically-rotatable scoped tokens (CF scoped tokens, MS Graph scoped apps, Supabase project keys) | 12-month default; 24-month for MS Graph scoped per DEC-042 | Programmatic via adapter `rotate()` | AGT-001 self-serve | | Manually-issued tokens (Anthropic, OpenAI, Resend, Cal.com, Axiom, etc.) | 12-month | Operator dashboard + `mgp-secret put --from-prompt` | Principal | | Infisical machine identity client secret | 12-month | Manual (Infisical UI) | Principal | | Control-plane token (CF KV-read for kill-switch resolution) | 12-month or on host change | Manual (CF dashboard) | Principal |

    ### Event-driven rotation triggers (all classes)

    Regardless of cadence: - Suspected leak → immediate rotation, kill-switch first if blast radius warrants - Tool/transcript leak → rotation per the leaked-credentials list, P1 incident response per MGP-GOV-001 §08 - Staff change with credential access → rotation - Provider-side compromise advisory → rotation per provider guidance - Cadence anniversary → opportunistic rotation at next regular maintenance window

    Rationale

    The prompt MGP-PROMPT-CRED-001 originally proposed monthly meta-token rotation + quarterly scoped, motivated by the 2026-04-30 leak. Reviewing actual leak mechanics:

    - **Token age was zero in the leak window.** All seven scoped CF tokens had been recently minted; the meta-token was the original from 2026-04-21. Age was not a factor in the leak. - **The leak vector was the CLI form** — `infisical secrets` list-form printed every value to stdout, captured in transcript. This would leak a 1-day-old token as readily as a 1-year-old token. - **Rotation is recovery, not prevention.** Faster rotation reduces the window of exposure but does nothing to prevent the next leak event. - **Aggressive rotation has cost.** Monthly rotation of meta-tokens means 12× the dashboard work per year and 12× the chance of in-flight rotation incidents (one of which already happened — see DEC-043's note about a mid-rotation B-value loss).

    The structural defences in DEC-046 (wrapper, hook, register-as-index, kill-switch hierarchy, audit telemetry) are the prevention layer. Rotation cadence is a secondary control. Correct relative weighting: invest in structural prevention; keep rotation cadence at the proven 12-month baseline.

    BA element
    Pillar 02 Decision Architecture · secret-handling-discipline
    Business processes
    BP-009
    SPEC reference
    DEC-014 (CF tokens, annual); DEC-013 (Infisical machine identity, annual); DEC-042 (MS Graph scoped, 24-month); DEC-043 (MS Graph self-bootstrap, 12-month)
    Register entry
    DEC-050-rotation-cadence-structural-defence.md
  5. DEC-049

    Audit telemetry on mgp_os.cli_events sibling table — single ledger, fit-for-purpose schema

    2026-05-01 · resolved

    Decision

    CLI credential-operation events live in a new table **`mgp_os.cli_events`** — a sibling of `mgp_os.events` in the same schema, with shape designed for invocation-type events. **No parallel `audit_events` schema or table.** Single governance ledger, two fit-for-purpose tables.

    A union view `mgp_os.unified_events` joins both for cross-cutting queries.

    ### Why a sibling table, not extending `mgp_os.events`

    `mgp_os.events` (introduced in F-247, migration `0015_mgp_os_bootstrap`) carries `entity_type` and `entity_id` as **NOT NULL** — events are anchored to work-item entities (epics, features, capabilities). CLI invocations don't have an entity to anchor to. Three options were considered:

    | Option | Trade-off | |---|---| | Mutate `mgp_os.events` to allow nullable entity columns | Touches the F-247 substrate that 18 substrate Features depend on; high blast radius | | Synthetic entity (`entity_type='cli_invocation'`, `entity_id=<run uuid>`) | Cheap; pollutes the work-item ledger with non-work-item rows | | **Sibling table `mgp_os.cli_events`** | Same schema, fit-for-purpose shape, union view for cross-cutting queries — chosen |

    The sibling-table choice keeps the consolidation argument intact (one schema for governance/operations data) while avoiding the entity-column contortion.

    ### `mgp_os.cli_events` shape

    ```sql CREATE TABLE mgp_os.cli_events ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id uuid, event_type text NOT NULL, -- 'mgp.cli.invoke', 'mgp.secret.read', etc. binary text NOT NULL, -- 'mgp-secret', 'mgp-bootstrap-app', 'mgp-hook' outcome text NOT NULL, -- 'success', 'failure', 'blocked', 'denied' actor text NOT NULL, -- e.g. 'AGT-001@hostname' actor_type mgp_os.actor_type NOT NULL, actor_pid int, actor_user text, actor_session_id text, -- CLAUDE_CODE_SESSION_ID env if present test_mode boolean NOT NULL DEFAULT false, subject_secret_name text, -- only register-known names; never leaks discovery subject_provider text, subject_path text, subject_app text, subject_template text, duration_ms int, kill_switch_state text NOT NULL, -- 'enabled' or 'denied' denied_at text, -- resolution layer if denied binary_version text NOT NULL, error_sanitised text, -- sanitised; never includes secret values emitted_at timestamptz NOT NULL DEFAULT now() );

    CREATE INDEX cli_events_emitted_idx ON mgp_os.cli_events (emitted_at DESC); CREATE INDEX cli_events_actor_idx ON mgp_os.cli_events (actor, emitted_at DESC); CREATE INDEX cli_events_event_type_idx ON mgp_os.cli_events (event_type, emitted_at DESC); ```

    Schema-level RLS template aligned with `mgp_os.events` (multi-tenant readiness retained).

    ### Telemetry endpoint

    `POST /v1/audit/events` on `mgp-api`. Per DEC-003, the CLI is a client of the locked system-caller pattern.

    **Auth posture: anonymous + heavy rate-limit + schema validation + server-side `redact()`.**

    - Schema validates the TelemetryEvent shape; rejects malformed events. - Server-side `redact()` runs the same regex rules as the CLI (`cfat_`, `cfut_`, `sk-`, `eyJ`, control-plane token format). If any fire, the row is rejected and a `mgp.audit.redaction_violation` event is logged identifying the originating CLI version + hostname. - Per-IP rate limit via existing `RATE_LIMIT` KV (5/sec burst, 100/min sustained). - Forging events would only pollute the audit log; cannot exfiltrate or escalate.

    Alternative considered: HMAC over a shared secret. Rejected — adds another bootstrap, doesn't materially raise the bar against forgery (the only meaningful threat).

    ### Dual-sink discipline

    | Sink | Path | Behaviour | |---|---|---| | Local | `~/.cache/mgp-cli/audit.jsonl` (mode 0600, weekly logrotate) | Source of truth. Synchronous write before remote attempt. If local write fails, CLI continues with stderr warning (failing closed during incident response is wrong). | | Remote | `POST /v1/audit/events` → `mgp_os.cli_events` | Best-effort; fire-and-forget with 500ms timeout. Failure tags the local-sink record `remote_delivery: failed`. Replay via `mgp-secret audit-replay`. |

    ### Forbidden in event payload

    - Secret values (any provider) - Full token IDs — use first 4 + last 2 chars only (e.g. `cfat_***ab`) - Control-plane token in any form - Raw stack traces that might contain values - Environment variable dumps

    A typed `redact()` helper enforces this — every payload passes through it before emission. Server-side validation runs the same rules as defence-in-depth.

    Rationale

    A separate `audit_events` schema would fork the alerting story (per MGP-GOV-001 §07), break `closeoff_governance_check`, and create a second governance ledger to maintain. Keeping audit telemetry in `mgp_os` consolidates the single-source-of-truth posture.

    The fit-for-purpose schema (no `entity_type`/`entity_id`) matches what CLI events actually are: invocation traces, not work-item state changes. Forcing them into a synthetic-entity shape would be both ugly and confusing for downstream queries.

    BA element
    Pillar 02 Decision Architecture · telemetry-completeness
    Business processes
    BP-009 · BP-018
    SPEC reference
    MGP-GOV-001 §06 (logging); DEC-018 (operational traceability — five-layer test pyramid + audit trail); DEC-003 (mgp-api as system caller pattern)
    Register entry
    DEC-049-audit-telemetry-on-mgp-os-cli-events.md
  6. DEC-048

    Kill-switch hierarchy + key naming convention for CLI tools

    2026-05-01 · resolved

    Decision

    Kill switches are **discrete, named, first-class steps** — not policy bolted onto operations. Every credential-operation CLI invocation passes a documented resolution sequence before any side-effect, and emits a `mgp.kill_switch.check` telemetry event for the resolution.

    ### KV namespace

    Existing `mandantis-platform` KV namespace, no new namespace. Reuses the namespace already provisioned for `agent:claude-code:enabled` per F-001 + MGP-GOV-001 §10.

    ### Resolution order (any false short-circuits, default-true semantics)

    | Order | Key | Scope | Default | Owner | |---|---|---|---|---| | 1 | `agent:claude-code:enabled` | Top-level Claude Code kill | `true` | Principal | | 2 | `agent:mandantis-agt-001:enabled` | AGT-001 specifically | `true` | Principal | | 3 | `cli:mgp-cli:enabled` | Both binaries (`mgp-secret`, `mgp-bootstrap-app`) | `true` | Principal | | 4 | `cli:mgp-bootstrap:enabled` | Bootstrap-only kill (allows read-only secret access during a provisioning freeze) | `true` | Principal | | 5 | `cli:mgp-hook:enabled` | Pre-bash hook only | `true` | Principal | | 6 | `provider:{name}:enabled` | Per-provider kill (only when context.provider set) | `true` | Principal | | 7 | `meta:enabled` | `_meta` path reads (only when path starts with `/_meta`) | `true` | Principal |

    Default-true: a missing key is treated as enabled. Missing keys are reported in the telemetry event so they don't silently fail open in production.

    ### Per-CLI-invocation cache

    Resolution is performed once at process start and reused for the lifetime of the process. Long-running processes (`mgp-bootstrap-app init` across multiple providers + interactive prompts) **do not re-resolve mid-run** — flipping a provider kill mid-bootstrap must not abort an in-progress flow that has already revealed a value to the operator. Flips take effect on the next CLI invocation.

    This complies with MGP-GOV-001 §10's "activatable in under 60 seconds" requirement: every new CLI invocation is fresh resolution.

    ### Control-plane token

    KV reads need a CF API token. The CLI cannot use the same token store it gates (chicken-and-egg). Solution:

    - A separate, **low-privilege** CF API token with `KV: read` scope on the `mandantis-platform` namespace **only**. - Stored at `~/.config/mgp-cli/control-plane.token`, mode `0600`. - Provisioned manually once per host during install (documented in `packages/mgp-cli/INSTALL.md`). - **Never rotated programmatically** — operator rotates via CF dashboard. Documented in `tokens-_meta.md`. - Missing file → CLI fails closed with: *"control-plane token not found at ~/.config/mgp-cli/control-plane.token. CLI cannot resolve kill-switch state. Install per packages/mgp-cli/INSTALL.md."*

    Accepted risk: this token is not in Infisical and cannot self-rotate. Blast radius is one KV read. Manual handling tolerable.

    ### Adding a new key

    Adding a kill-switch key requires an entry in `docs/standards/kill-switch-keys.md` in the same PR. The CLI's kill-switch-keys.ts registry mirrors that file.

    Rationale

    A kill switch that is policy-implemented rather than structurally-implemented depends on every author remembering to wire it in. The 2026-04-30 leak demonstrates the failure mode: tooling that can do dangerous things should be gated by a discrete check that is unmissable in the call graph.

    Per-binary and per-provider keys exist because a "kill the whole CLI" hammer is too coarse for incident response. If a single provider's meta-token is suspected compromised, the operator wants to deny access to that provider's calls while rotations are in flight, without halting unrelated work.

    Existing keys (`agent:claude-code:enabled`, `agent:mandantis-agt-001:enabled`) sit at the top of the chain. Both must be true for any operation to proceed; the CLI keys are below them in the resolution order. This preserves the existing emergency-stop semantic.

    BA element
    Pillar 02 Decision Architecture · kill-switch-discipline
    Business processes
    BP-009
    SPEC reference
    MGP-GOV-001 §10 (kill switch); MGP-SPEC-ARCH-001 fitness function kill_switch_respect
    Register entry
    DEC-048-kill-switch-hierarchy.md
  7. DEC-047

    @mandantis/cli — permitted package category for operator-host CLI tooling

    2026-05-01 · resolved

    Decision

    A new package category is permitted in `mandantis-platform`: **operator-host CLI tooling**, distributed via `pnpm install -g`, executed on the operator's machine.

    `@mandantis/cli` (in `packages/mgp-cli/`) is the first such package. It carries two binaries (`mgp-secret`, `mgp-bootstrap-app`) plus a pre-bash hook script.

    Rationale

    DEC-002 locked Cloudflare Workers as sole compute for **runtime services**. CLI tooling for operator-side workflows isn't a runtime service — it runs on the developer's machine to gate credential operations, bootstrap infrastructure, and so on. Forcing it into a Worker shape would defeat its purpose (it must work offline, before Workers exist, and before any token is reachable).

    The CLI is a **client** of the four locked integration patterns (DEC-003), not a fifth pattern: - It calls `mgp-api` for audit telemetry (`/v1/audit/events`) — system caller pattern - It calls Infisical's REST API directly (the Infisical CLI is a thin wrapper; the REST API is the actual contract) - It calls Cloudflare KV directly for kill-switch resolution (single read, scoped token)

    DEC-003's four patterns govern cross-system **server** integration. CLI tooling is a peer concern.

    BA element
    Platform Development capability · Tools & Build
    Business processes
    BP-001
    SPEC reference
    DEC-002 (Cloudflare Workers sole compute); DEC-003 (four integration patterns)
    Register entry
    DEC-047-mgp-cli-permitted-package-category.md
  8. DEC-046

    Credential Operations Hardening — wrapper + register-as-index + hook + telemetry

    2026-05-01 · resolved

    Decision

    Direct `infisical secrets` use is **deprecated** for AGT-001 sessions. All credential operations — for any provider, in any project — go through `mgp-secret` and `mgp-bootstrap-app` (the `@mandantis/cli` package).

    Five structural defences, layered:

    1. **`mgp-secret` wrapper** mediates every read and write. Refuses any name not present in the credentials register. `get` refuses TTY printing without `--output`/`--env`/`$()`. `put` refuses values as CLI arguments. 2. **Pre-bash hook** in `~/.claude/settings.json` blocks unsafe Infisical CLI patterns at the harness layer (defence in depth on top of the wrapper). 3. **Register-as-index policy** (DEC-051): the credentials register set is the single authoritative index of every secret stored in Infisical. A secret with no register entry does not exist for the purposes of `mgp-secret`. 4. **Kill-switch hierarchy** (DEC-048): every CLI invocation passes a discrete, named kill-switch resolution step at agent / cli / binary / provider / `_meta` layers. Default-true semantics. 5. **Audit telemetry** (DEC-049): every operation emits structured events to a dual-sink (local `~/.cache/mgp-cli/audit.jsonl` first, then `mgp-api` `/v1/audit/events` → `mgp_os.cli_events`). Server-side redaction as defence-in-depth.

    Multi-provider by design — the wrapper and hook are store-agnostic; provider-specific handling lives in adapters per Story 4.1 of MGP-PROMPT-CRED-001.

    Rationale

    **Triggering incident:** AGT-001 leaked nine Cloudflare tokens and two Turnstile keys via `infisical secrets [list]` on 2026-04-30 during a `newsletter-engine` work session. Rotation completed; root-cause class (the tool conflated discovery and retrieval, with no structural guardrail) was unaddressed.

    A correction at the prompt-discipline layer ("don't use the list form") is fragile — it relies on AGT-001 remembering, in every session, in every project. The next leak is one careless invocation away. The correct move is to make the unsafe form **structurally impossible** (or at least require explicit `--no-verify`-style bypass) rather than rely on memory.

    The leak originated in a non-Mandantis project but exposed Mandantis credentials. The wrappers therefore install at the operator-host scope (`~/.local/bin/mgp-secret`, `~/.claude/settings.json` hook) — not bound to any one project — and gate all `infisical` calls regardless of which project the session is in.

    BA element
    Pillar 02 Decision Architecture · secret-handling-discipline
    Business processes
    BP-009 · BP-010
    SPEC reference
    MGP-GOV-001 §06 (logging) · §07 (alerting) · §10 (kill switch) · §12 (agent register); MGP-SPEC-ARCH-001 (fitness functions secret_boundary, kill_switch_respect)
    Register entry
    DEC-046-credential-operations-hardening.md
  9. DEC-045

    Boundary-of-Authority — chokepoint applies to authoritative-shared writes; single-writer-derived writes use bindings directly

    2026-05-01 · resolved

    Decision

    The api worker (`mgp-api` for Mandantis Platform; `nle-api` for Newsletter Engine) is the **single chokepoint** for cross-trust-boundary calls and authoritative-shared writes. It is **not** a chokepoint for single-writer-derived writes by their author-of-record worker.

    Concretely, four rules:

    1. **Schema authority is always api.** The api worker owns every D1 migration, every R2 key-prefix convention, every Vectorize metadata schema, every Dataverse schema interaction, every shared-store data shape. Types export from the shared package authored by api's owner. **No other worker issues DDL or designs key/metadata shapes.** This is non-negotiable; it's how the system retains a single safe place to evolve schemas.

    2. **Trust-boundary calls route through the chokepoint.** - UI / public HTTP / third-party callback → **api**. - Any worker → external AI vendor → **mcp** (per DEC-003). - Any worker → external system with its own auth (Dataverse, Stripe, Resend, Microsoft Graph, etc.) → **api**.

    3. **Authoritative-shared writes route through api.** A write is authoritative-shared when **any** of these is true: - multiple actors can legitimately mutate the same row; - invariants span more than one column/row, and a partial write would violate them; - audit-by-construction is required (governance, financial, compliance, identity, decisions, user actions).

    4. **Single-writer-derived writes can use bindings directly.** A write is single-writer-derived when **all** of these are true: - exactly one worker is the author of record for the data, and that worker IS the audit (the worker's invocation log + telemetry trail = the audit trail); - no other actor mutates the row after it's written (append-only, or write-once-update-never); - the data is operational/derived rather than authoritative (logs, computed indexes, intermediate artefacts, ledgers maintained by a single writer).

    Such workers may bind D1, R2, Vectorize, KV, or Workers AI directly. Schema authority remains in api; runtime path does not.

    ### The classification test for any new write

    Ask in order. Stop at the first "yes."

    1. **Does it cross a trust boundary?** (UI/external in, or external service out) → **chokepoint**. 2. **Will multiple actors mutate the same row after creation?** → **chokepoint**. 3. **Are there cross-row/cross-write invariants whose partial failure would corrupt state?** → **chokepoint, in a transaction**. 4. **Is the data authoritative — decisions, user actions, financial, compliance, identity?** → **chokepoint**. 5. **Otherwise — single-writer-derived.** Direct binding is permitted, with telemetry.

    Rationale

    1. **The chokepoint earns its keep when there is something to protect.** The api worker exists because some calls cross a trust boundary (untrusted in → trusted system, or trusted system → external vendor with its own auth) and some writes touch state with cross-actor mutation, multi-write invariants, or audit-by-construction needs. Those calls/writes pay for the chokepoint with audit, authorisation, schema control, and concurrency mediation. Calls/writes lacking those properties pay only the cost — latency, indirection, code volume — and receive nothing in return.

    2. **Mandantis Platform's existing pattern was correct, but for a more specific reason than "all DB through api."** Pre-2026-05-01, the rule was stated as "all DB through mgp-api." That rule held because the DB in question was Dataverse — across a trust boundary requiring service-principal auth that mgp-api uniquely held. Local D1, used by Mandantis Platform packages like mgp-crm and mgp-signal alongside Dataverse, has no such trust boundary. The "all through api" rule was a *correct heuristic for the Dataverse case generalised one step too far*. This DEC keeps the heuristic where it earned its keep (rules 1–4) and relaxes it where it didn't (rule 5).

    3. **Newsletter Engine's data plane is shaped differently from Mandantis Platform's.** NLE has high-volume single-writer pipelines: discovery writes hundreds of `source_runs` per day; ingest writes dozens of `items` per hour; cluster-trending updates hundreds of cluster scores per hour; mcp inserts spend-ledger rows per Anthropic call. Each of those is a single-writer-derived case under rule 4. Routing them all through nle-api adds an HTTP hop per row, multiplied by row count, multiplied by daily cron frequency, with **zero audit, invariant, or authorisation return** — because the writer is the unique author of record and the binding has its own audit (R2 request logs, Vectorize ops log, D1's own write log).

    4. **The DEC does not relax MGP.** Mandantis Platform's existing writes are mostly authoritative-shared (CRM contacts, opportunities, decisions, content, governance). Those continue to route through mgp-api. The DEC permits — but does not require — high-volume single-writer logs (e.g. mgp-mcp's spend ledger when it lands, mgp-signal's discovery audit) to use direct bindings under rule 4. Existing patterns are not retroactively rewritten; new patterns gain a principled path.

    5. **Auditability is preserved by telemetry, not by the chokepoint.** Single-writer-derived workers still emit structured telemetry on every write (Workers Analytics Engine for NLE; Axiom for MGP). The audit trail exists independently of whether the write went through api. The chokepoint adds *another* audit layer for the cases where it pays (rules 1–4); it doesn't *create* the only audit trail.

    6. **Schema-change discipline is unaffected.** Rule 1 is non-negotiable. Migrations land in api. Types export from the shared package. New tables are designed in api regardless of who writes to them. The runtime path being direct does not give other workers permission to invent schema.

    7. **The classification test is operationally simple.** Five steps, ordered, stop at first "yes." Any team member can apply it to any new write in under a minute. Misclassification is auditable: every direct-binding write site is enumerable, every write has a class assignment in the project's architecture doc, and an annual or per-Feature audit pass walks the list and re-classifies.

    BA element
    Platform Development capability · Architecture Standards
    Business processes
    BP-001 · BP-010
    SPEC reference
    MGP-SPEC-ARCH-001 (3-layer pattern); DEC-002 (Cloudflare Workers sole compute); DEC-003 (four integration patterns + mgp-mcp); DEC-018 (operational traceability); DEC-034 (API contract discipline)
    Register entry
    DEC-045-boundary-of-authority.md
  10. DEC-038

    Supabase service-role key — self-bootstrap via Management API (no Principal mint required)

    2026-04-24 · resolved

    Decision

    The Supabase project's `service_role` JWT is **fetched on demand from the Supabase Management API** using the existing `/supabase/management-pat` that AGT-001 already holds, then stored as `/supabase/service-role-key` in Infisical and bound to mgp-api at deploy time. **No Principal action required to provision it.**

    The Management API endpoint:

    ``` GET https://api.supabase.com/v1/projects/{project_ref}/api-keys?reveal=true Authorization: Bearer {management-pat} ```

    Returns both `anon` and `service_role` keys with their full `api_key` value when `reveal=true` is set. Confirmed working with `/supabase/management-pat` (already in Infisical from F-001 provisioning).

    Storage convention:

    - **Source of truth:** Supabase project (rotates whenever the project rotates its keys — Principal-initiated, infrequent) - **Cache:** Infisical at `/supabase/service-role-key` (env=prod), set by an idempotent re-fetch any time we want to refresh - **Worker binding:** `wrangler secret put SUPABASE_SERVICE_ROLE_KEY` on the mgp-api Worker; same secret is the Worker's `env.SUPABASE_SERVICE_ROLE_KEY` referenced by `supabase-client.ts`

    Rationale

    The original Series 1 build sequence treated `SUPABASE_SERVICE_ROLE_KEY` as a Principal-only mint — listed in CLAUDE.md and several Feature specs (F-073, F-068.1, E-013 CRM endpoint bundle) as a "blocked on Principal pre-req" item. This forced the Series 1 backlog to wait indefinitely on a Principal action that was never going to happen because the key already existed in the Supabase project; only the storage step in Infisical was missing.

    The lesson is the same shape as DEC-037 (mgp-devmgmt PAT supersession): **always check what existing tokens can fetch before assuming a Principal action is required.** AGT-001's `/supabase/management-pat` has full project-management scope including `api-keys:read`, which covers the service-role retrieval. We were one HTTP call away from the "blocker," and never made it.

    The discovery happened mid-E-011 Series 1 when scoping F-073: a defensive Infisical scan turned up no `service-role-key` entry, and a `GET /api-keys?reveal=true` against the Mgmt API returned the actual `service_role` JWT in the `api_key` field. Same as DEC-037 — checking what we *can* do beats assuming we *can't*.

    BA element
    Platform Development capability · Tools & Build
    Business processes
    BP-001 · BP-010
    SPEC reference
    DEC-001 (Supabase AU canonical) + DEC-003 (mgp-api sole holder of service-role key) + DEC-013 (Infisical secret storage)
    Register entry
    DEC-038-supabase-service-role-key-self-bootstrap.md
  11. DEC-037

    mgp-devmgmt GitHub authentication — supersede DEC-025: switch from Principal PAT to GitHub App identity

    2026-04-24 · resolved

    Decision

    mgp-devmgmt uses the **`mandantis-agt-001[bot]` GitHub App identity** (DEC-009) for all GitHub reads and writes — replacing the Principal-identity fine- grained PAT specified by DEC-025.

    - Identity on writes: `mandantis-agt-001[bot]` (App installation 125680877) - Token mint: in-Worker, per-isolate cache. Worker reads `GH_APP_ID` + `GH_APP_INSTALLATION_ID` + `GH_APP_PRIVATE_KEY` from secret bindings, mints a 10-min App JWT (RS256 via SubtleCrypto), exchanges for a 1-hour installation token, caches in module-level state until 1-min before expiry - Storage: existing Infisical `/github/agt-001-app/{app-id, installation-id, private-key}` — no new secrets to provision. The DEC-025 entry `/github/mgp-devmgmt/pat` was never written and is no longer required - Rotation: implicit. App private key rotates per DEC-009; installation tokens are 1-hour and re-minted on demand

    Rationale

    DEC-025 (2026-04-22) selected a PAT to keep writes attributed to the Principal's identity rather than the bot. F-062 cutover (2026-04-24) revealed two issues with that posture:

    1. **The PAT was never minted.** F-055..F-061 all explicitly deferred the mint to F-062 deploy time. At cutover, the secret didn't exist in Infisical and the cutover was effectively blocked on a Principal manual action that hadn't happened.

    2. **The audit-trail concern is overstated for a single-user console.** In an autonomous-build context where AGT-001 self-merges PRs (DEC-017) and operates the entire Layer 3 surface, having the dev-management console also write under the bot identity is operationally consistent — and is in fact what the Principal does *through* the console. Real governance accountability lives in DECs and session logs, not in GitHub commit author lines.

    The supersession also removes a class of operational drift: any future Worker that needs GitHub writes can reuse the same App credentials through the same helper, instead of demanding a new Principal PAT mint each time.

    BA element
    Platform Development capability · Tools & Build
    Business processes
    BP-001 · BP-010
    SPEC reference
    MGP-SPEC-DEV-001 Part 4 DEC-DEV-001 (revised)
    Register entry
    DEC-037-mgp-devmgmt-github-app-identity-supersedes-pat.md
  12. DEC-036

    Shared-infrastructure schema refactor — introduce strategy / autonomy / economics / audit schemas

    2026-04-24 · resolved

    Decision

    Cross-capability tables currently housed in the `signal` schema (because Signal shipped first) **move to dedicated per-concern schemas** before CRM introduces its own schema:

    | New schema | Moved tables (from `signal.*`) | Concern | |---|---|---| | **`strategy`** | `authority_objectives`, `patterns` | Cross-capability strategic positioning + learned patterns | | **`autonomy`** | `autonomy_policies`, `action_classes`, `autonomy_policy_events`, `action_events`, `agent_handoffs` | F-068 earn-autonomy engine (already capability-agnostic) | | **`economics`** | `cost_events`, `budget_policies` | Cross-capability cost governance (paired with `mcp.cost_events` from E-008) | | **`audit`** | `visibility_change_events`, plus new `actor_type` enum | Universal audit trail |

    Signal's own capability-specific tables stay in `signal.*` (articles, position_claims, topic_clusters, deployments, posts, publications, performance_snapshots, critic_annotations, audience_members — the last **also deprecates** per E-013 Q5: moves to `crm.contacts`).

    Rationale

    Three reasons:

    1. **Ownership-by-shipment-order is governance debt.** `signal.authority_objectives` reads "Signal owns objectives" — but objectives are **strategic positioning**, not Signal-specific content authority. CRM + PSA + future capabilities reference the same rows. Naming should reflect concept ownership, not shipment sequence.

    2. **F-068's autonomy engine was designed capability-agnostic.** The engine lives in `packages/mgp-api/src/autonomy/`. The fact that its tables live in `signal.*` is accidental. `autonomy.*` matches the engine's scope.

    3. **The cost is low right now.** Signal has zero rows in most moved tables. Migration is ~15 minutes + test + snapshot regen. Every capability that ships against `signal.*` for cross-capability entities increases the future migration cost.

    BA element
    Platform Development capability · Tools & Build
    Business processes
    BP-001 · BP-002
    SPEC reference
    MGP-SPEC-DEV-001 (integration pattern discipline); DEC-003 (four integration patterns); DEC-032 (Supabase substrate)
    Register entry
    DEC-036-shared-infrastructure-schema-refactor.md
  13. DEC-035

    CRM Worker identity + URL + Principal-only CF Access posture (crm.mandantis.com)

    2026-04-24 · resolved

    Decision

    CRM is deployed as its **own Cloudflare Worker** at `packages/mgp-crm/`, served from:

    - **`crm.mandantis.com`** — authed Principal-only operator app. **Single-policy CF Access** (Principal only; no guest tier in v1.0). - **`mandantis.com/proof/*`** (shared with Signal) — public Glass Box aggregate surfaces: `/proof/pipeline`, `/proof/velocity`, `/proof/acquisition`, `/proof/retention`. Served by mgp-crm via CF route pattern. No PII; aggregates only.

    CRM is **not** an extension of mgp-devmgmt (Principal's internal dev console) and is **not** an extension of mgp-signal (authority publishing platform). All three are separate Workers with separate access posture + telemetry + kill-switch.

    Rationale

    Three reasons for own-Worker-own-URL-Principal-only:

    1. **CRM is substantial + capability-complete.** 14 Features in Core alone (E-013), another 10 in Intelligence (E-018). Distinct mental model from Signal (outbound authority) and devmgmt (dev operations). Own identity helps operator navigation + future team onboarding.

    2. **PII sensitivity.** CRM holds contact records, relationship-health scores, interaction content — materially more sensitive than Signal (which is market-facing content) or devmgmt (which is Mandantis- internal governance). Principal-only CF Access is the minimum appropriate posture.

    3. **Independent deploy + kill-switch discipline.** CRM failure shouldn't cascade to Signal or devmgmt. Kill-switch per Worker is the established DEC-004 pattern.

    BA element
    Client Relationship capability · Client Acquisition stream
    Business processes
    BP-002 · BP-004
    SPEC reference
    MGP-SPEC-DEV-001 (integration pattern discipline); DEC-027 (2-policy CF Access for most Workers); DEC-032 (all business-system data via mgp-api); DEC-033 (Signal Worker precedent)
    Register entry
    DEC-035-crm-worker-url-auth-posture.md
  14. DEC-034

    API contract discipline — OpenAPI from Zod for mgp-api · MCP-native tools/list for mgp-mcp

    2026-04-23 · resolved

    Decision

    Every Mandantis API surface maintains a **machine-readable contract** automatically derived from the request/response validation schemas already present in code. Two formats, one per surface type:

    1. **mgp-api (REST):** auto-generated **OpenAPI 3.1** spec per route group, emitted at `/v1/<group>/openapi.json`. Zod schemas (used for ingress validation + TypeScript inference) are the single source of truth; `zod-to-openapi` (or equivalent) emits the spec as part of the build. A browsable **Swagger UI** is hosted at `/v1/docs`. Applies to every current + future `/v1/*` route group — **signal**, **mcp** (operator endpoints), **autonomy**, **public/proof**, and any capability Epic (PSA, CRM, Partnerships) added later.

    2. **mgp-mcp (JSON-RPC / MCP):** the **MCP-native `tools/list` method** is the contract. mgp-mcp exposes the F-048 tool catalog (canonical `text.generate` + `image.generate` schemas + provider routing) via the standard MCP introspection method; any MCP client (Claude Code, Cursor, Signal's agents, future capability agents) can discover available tools + their JSON Schemas without out-of-band docs. No OpenAPI for mgp-mcp's `/mcp` surface — it's not REST; it's MCP.

    Both formats are **auto-generated, not hand-maintained**. Drift between source code and published contract is prevented by construction.

    Rationale

    1. **Zod schemas already exist.** F-047 + F-048 + F-068 + F-073 + F-079 + F-102 and every other API Feature in E-011 + E-017 uses Zod for ingress validation + TypeScript type inference. The schemas are the source of truth for "what shape does this endpoint accept / return." Deriving OpenAPI from them is additive, not duplicative. Hand-maintained OpenAPI drifts from code inevitably; auto-generation eliminates that failure mode.

    2. **Multiple consumers need the contract.** Signal Worker calls mgp-api. mgp-devmgmt console calls mgp-api. Future capability Epics (PSA F-xxx, CRM F-xxx) will call mgp-api. External integrators (if ever — Principal decides case-by-case) will call mgp-api. Each consumer today either reads TypeScript types (same-repo consumers via `@mandantis/*`) or guesses from code (cross-repo consumers). An OpenAPI contract gives them a durable, machine-verifiable shape description that doesn't require TypeScript runtime knowledge.

    3. **MCP has its own answer; respect it.** MCP is not REST; JSON-RPC 2.0 + the MCP protocol's `tools/list` method already serves exactly the role OpenAPI serves for REST — introspection of available operations + their schemas. Retrofitting OpenAPI onto `/mcp` would duplicate work + create a second source of truth. MCP clients already know to call `tools/list`; honouring the protocol standard is the right move.

    4. **Cost is near-zero.** `zod-to-openapi` is a dependency + a few lines of setup per route group. `tools/list` exposure is an RPC method handler that serialises F-048's catalog. Neither is a Feature-sized lift; both are small deltas layered into existing Features.

    5. **Operator affordance.** Principal routinely uses `curl` against mgp-api + mgp-mcp for smoke tests + runbook verification. A `/v1/docs` Swagger UI makes endpoint discovery + request-shape exploration dramatically faster — less "dig into the code to find the right field name," more "click, fill, execute."

    6. **Fitness-function compatibility.** Once spec emission is mechanical, an `api-contract-drift-check` fitness gate is a natural next step: compare the currently-registered Zod schemas against the last-committed OpenAPI snapshot; drift fails CI. Turns the contract into a load-bearing artefact.

    BA element
    Platform Development capability · Integration Map
    Business processes
    BP-001 · BP-010
    SPEC reference
    MGP-SPEC-DEV-001 (integration pattern discipline); DEC-003 (four integration patterns); DEC-016 (MCP-first vendor calls); DEC-018 (operational traceability)
    Register entry
    DEC-034-api-contract-discipline.md
  15. DEC-033

    Signal Worker identity + URL + 2-policy CF Access posture (signal.mandantis.com + mandantis.com/proof/*)

    2026-04-23 · resolved

    Decision

    Signal is deployed as its **own Cloudflare Worker** at `packages/mgp-signal/`, served from:

    - **`signal.mandantis.com`** — authed Signal operator app (17 screens per brief v1.3, 2-policy CF Access: Principal full + guest read-only per DEC-027) - **`mandantis.com/proof/*`** — public Glass-Box surfaces (no auth), served from the same Signal Worker via CF route config

    Signal is **not** an extension of mgp-devmgmt; the two surfaces stay separate (mgp-devmgmt is Principal's internal operations console; Signal is Mandantis' operational product).

    Rationale

    Three reasons for own-Worker-own-URL:

    1. **Signal is a substantial product deserving its own identity.** 17 screens, 4 agents, newsletter engine, Glass Box publication. Deploying it as a sub-path under mgp-devmgmt would blur operational-product-for-Mandantis with internal-tooling-for-Principal — short-term saving, long-term tangle.

    2. **Different auth posture.** `mandantis.com/proof/*` must be publicly readable (Q14 Glass Box commitment). mgp-devmgmt's CF Access + CSP posture is designed for authed internal use; Signal's route-level split (public vs authed) requires its own Worker-level access logic. Trying to route public paths through mgp-devmgmt creates inconsistent CSP + security-headers behaviour.

    3. **Independent deploy + kill-switch discipline.** Signal's operational cadence (publishes content on schedule; 4 agents with their own kill-switches) shouldn't share a deploy lock with mgp-devmgmt (Principal's dev console). Failure in one shouldn't take down the other.

    BA element
    Platform Development capability · Tools & Build
    Business processes
    BP-001 · BP-010
    SPEC reference
    MGP-SPEC-DEV-001 (integration pattern discipline); DEC-027 (2-policy CF Access); DEC-032 (all business-system data in Supabase via mgp-api)
    Register entry
    DEC-033-signal-worker-url-auth-posture.md
  16. DEC-032

    All business-system data substrates are Supabase via mgp-api — no Git-as-DB exceptions for capability Epics

    2026-04-23 · resolved

    Decision

    Every business-capability Epic — **E-011 Signal, E-012 PSA, E-013 CRM, E-014 Content, E-015 Partnerships** — stores its entity data in **Supabase via mgp-api**. Git-as-DB (structured data as markdown in a repo) is explicitly ruled out for capability entities.

    This **confirms and tightens** DEC-001 (Supabase primary data store) + DEC-003 (mgp-api is the gateway) against an emerging temptation: extend the governance-portal's compile-time-TS-from-Git pattern into the capability layer. Principal decision 2026-04-23: don't.

    Rationale

    1. **Cardinality + query shape don't fit Git.** The capability entities above grow to thousands-to-millions of rows over time with read patterns (aggregates, filters, time-window scans) that Git can't serve without rebuilding every render into a DB-like structure anyway. 2. **Audit integrity is easier in Postgres than Git for operational data.** Immutable audit of "who changed an invoice line item and when" is more naturally served by row-level triggers + a history table than by commit-level attribution. 3. **RLS and per-tenant isolation** (DEC-001's foundation) only makes sense against a relational substrate. Git-as-DB has no per-caller row-filtering concept. 4. **Migration risk.** Starting a capability Epic on Git-as-DB means a v1.1 migration to Supabase once volume hits — additional engineering cost, data-consistency risk during migration, and a period where the capability's audit trail is split across two substrates. 5. **Consistency with the integration pattern discipline.** DEC-003 says all data access goes through one of four patterns; mgp-api is the Supabase gateway. Letting capability Epics bypass mgp-api with Git reads would create a fifth unofficial pattern and erode the discipline.

    BA element
    Platform Development capability · Information Map
    Business processes
    BP-001 · BP-010
    SPEC reference
    MGP-SPEC-DEV-001 (integration pattern discipline); DEC-001 (Supabase primary data store); DEC-003 (mgp-api as the Supabase gateway)
    Register entry
    DEC-032-business-systems-supabase-no-git-as-db.md
  17. DEC-031

    mgp-devmgmt AI surfaces in v1.0 via mgp-mcp — E-008 sequencing pull-forward

    2026-04-22 · resolved

    Decision

    mgp-devmgmt includes **AI-driven surfaces in v1.0**, not v1.1, delivered via the **mgp-mcp pattern** (not direct Anthropic SDK calls from the console Worker). This reverses the SPEC-DEV-001 Part 3 Pillar 05 default ("no AI in v1.0") and triggers sequencing changes for **E-008 (mgp-mcp v1.0)**.

    ### AI surfaces in scope (all v1.0)

    | Surface | Where it renders | What the AI does | |---|---|---| | **narrative** | Dashboard weekly summary | Generate natural-language weekly operating-log draft from sprint data (replaces the template-fill of Q4.2) | | **decision-suggest** | Decision Tracker | Draft proposed resolution for open `[DECISION-REQUIRED]` items; Principal accepts / edits / overrides | | **triage** | New-issue surfaces across Dashboard + Sprint Board | Suggest BA capability + pillar tag for newly-created issues missing those fields | | **agent-narrative** | Agent Console | Daily AGT-001 activity summary in natural language |

    ### Delivery pattern

    - All 4 surfaces invoke AI through **mgp-mcp** (E-008) — the Worker-hosted MCP pattern that brokers model calls - Console calls mgp-mcp via authenticated REST (same-account Worker-to-Worker binding); never a direct Anthropic SDK call from the console Worker - Keeps AI model credentials out of the console's env / Infisical scope — mgp-mcp owns model auth (per DEC-016: MCP-first for vendor calls)

    Rationale

    The "manage with least effort" north star (Q1.1) is materially improved by AI that drafts work the Principal would otherwise type — weekly narratives, decision resolutions, triage fields. Template-fill alternatives exist but save less time per cycle.

    Routing through mgp-mcp (rather than a direct SDK call) aligns with DEC-016 (MCP-first for vendor calls) — the MCP pattern was defined precisely to be the broker for this kind of call. Skipping the pattern to save sequencing effort would regress a governance decision.

    BA element
    Platform Development capability · Tools & Build
    Business processes
    BP-001 · BP-010
    SPEC reference
    MGP-SPEC-DEV-001 Part 3 Pillar 05 Q5.1 (rebut)
    Register entry
    DEC-031-devmgmt-ai-surfaces-mgp-mcp-pull-forward.md
  18. DEC-030

    mgp-devmgmt data caching — CF KV, 5-minute TTL, board-only

    2026-04-22 · resolved

    Decision

    mgp-devmgmt uses **Cloudflare KV with a 5-minute TTL** as its only cache layer.

    - **Cached:** GraphQL Projects v2 board reads (issue list + positions + custom fields) — the heaviest per-request query - **Not cached:** individual issue detail, comments, decision-log render — always fetched live - **Key naming:** `devmgmt:projects:board:{sprint-id}` (future-proof for multi-sprint views) - **Invalidation:** TTL expiry only — no manual purge in v1.0; Principal's manual refresh button bypasses cache via a URL param (`?fresh=1` → skips KV read)

    Rationale

    Single-user console + 5-minute auto-refresh (DEC-028) means the same heavy query runs repeatedly as the Principal leaves the tab open. KV eliminates that redundant load from both the GitHub rate limit and the Worker response time. Rate-limit headroom is already comfortable (5000 req/h per auth token), but saving it protects against future multi-tab / multi-device use.

    Issue detail stays uncached so opened items always reflect the latest state — the mismatch between "the list is 4m stale" and "the item I just opened is current" is acceptable; the inverse would surprise.

    D1 (Option C) was rejected because the data is transient read-through cache, not structured store. No queries beyond "give me the cached board blob" are needed.

    BA element
    Platform Development capability · Tools & Build
    Business processes
    BP-001 · BP-010
    SPEC reference
    MGP-SPEC-DEV-001 Part 4 DEC-DEV-006
    Register entry
    DEC-030-devmgmt-caching-kv.md
  19. DEC-029

    mgp-devmgmt decision-log promotion — fully auto-commit via console

    2026-04-22 · resolved

    Decision

    When the Principal closes a `[decision]` GitHub Issue with a D-series ID set (via a dropdown / field on the Decision Tracker page), the console **automatically commits** the corresponding entry to `docs/decision-log.md` in `Mandantis-Group/mandantis-governance` via the GitHub REST API.

    - **Trigger:** Principal sets D-series ID + closes issue in the console - **Commit:** console calls `PUT /repos/{owner}/{repo}/contents/docs/decision-log.md` with the updated content, authored as the Principal - **Scope required:** `contents:write` on `mandantis-governance` (included in DEC-025 PAT scope) - **Error posture:** if the commit fails, the close still happens (GitHub is still source of truth); console surfaces a banner with the error + the pre-filled entry text the Principal can paste manually

    Rationale

    Q2.1 (+auto-D-series in v1.0 writes) and Q2.3 (contents:write scope) together make auto-commit both available and worth building. The spec's semi-auto (Option B) was recommended to avoid `contents:write` scope — the Principal explicitly accepted that scope, so the semi-auto intermediate step is friction without value.

    True one-click promotion supports the "manage with least effort" north star (Q1.1): Principal makes the strategic decision, the mechanical log update happens for free.

    BA element
    Platform Development capability · Operate & Steward
    Business processes
    BP-001 · BP-010
    SPEC reference
    MGP-SPEC-DEV-001 Part 4 DEC-DEV-005
    Register entry
    DEC-029-devmgmt-decision-log-auto-commit.md
  20. DEC-028

    mgp-devmgmt refresh cadence — polling (5-min auto + manual)

    2026-04-22 · resolved

    Decision

    mgp-devmgmt refreshes data via **polling**:

    - **Manual refresh button** (bound to a top-nav control on every page) - **Automatic refresh every 5 minutes** while the tab is focused; no refresh when backgrounded (save rate-limit budget) - No WebSockets, no Server-Sent Events, no webhook push in v1.0

    Rationale

    Daily governance review is a session of attention, not a persistent monitor. Real-time delivery adds significant complexity:

    - Webhooks require standing up `mgp-webhook` pattern (currently undefined for this project's context) - SSE requires connection-management code in the Worker + reconnect logic on the client - Neither materially improves the "Principal manages with least effort" north star (Q1.1) — a 5-minute staleness window is invisible inside a 5-minute review

    The KV cache (DEC-030) absorbs redundant refreshes so the 5-min cadence is cheap.

    BA element
    Platform Development capability · Tools & Build
    Business processes
    BP-001 · BP-010
    SPEC reference
    MGP-SPEC-DEV-001 Part 4 DEC-DEV-004
    Register entry
    DEC-028-devmgmt-realtime-vs-polling.md
  21. DEC-027

    mgp-devmgmt URL — console.mandantis.com behind Cloudflare Access

    2026-04-22 · resolved

    Decision

    mgp-devmgmt ships at **`console.mandantis.com`** on a Cloudflare Worker, gated by Cloudflare Access.

    - **Two CF Access policies** on the subdomain (per DEC-031 / Q2.4): 1. Principal — email OTP for `sean@mandantis.com` + `hello@mandantis.com`; full read + write access 2. Guest — email OTP for a named guest list; read-only access (enforced by middleware checking CF-Access-Authenticated-User-Email against the guest list) - Dev-deploy surface: `mgp-devmgmt.mandantis-group.workers.dev` (mirrors F-030 pattern on governance portal), same auth posture - Zone-transfer dependency: requires `mandantis.com` to be in the Mandantis CF account (gated on `sched-zone-transfer-retry-2026-04-25`)

    Rationale

    Public URL behind CF Access is simpler operationally than CF Tunnel: no VPN, no per-user cert install, same auth layer managed in one place. Naming is parallel with `governance.mandantis.com` (role-named subdomain pattern). Worker runtime keeps the stack consistent — no per-package hosting divergence.

    Two-policy model (Principal + guest) supports the partial-Glass-Box read surface (DEC-031) without standing up a separate public site.

    BA element
    Platform Development capability · Release & Deploy
    Business processes
    BP-001 · BP-010
    SPEC reference
    MGP-SPEC-DEV-001 Part 4 DEC-DEV-003
    Register entry
    DEC-027-devmgmt-url-hosting.md
  22. DEC-026

    mgp-devmgmt GitHub API — hybrid GraphQL + REST

    2026-04-22 · resolved

    Decision

    mgp-devmgmt uses a **hybrid GitHub API strategy**:

    - **GraphQL** for GitHub Projects v2 operations — board position, sprint assignment, custom field reads (Status, Feature, BA capability, Start date, Target date) - **REST** for Issue CRUD, label operations, comment CRUD, milestone CRUD - Types generated from GitHub's public GraphQL schema; REST types handwritten or imported from `@octokit/openapi-types`

    Rationale

    Projects v2 is GraphQL-only — the custom fields the Mandantis board uses (populated by `bin/sync-project-board.mjs`) have no REST equivalent. Reconstructing the board from Issues + Milestones is possible but introduces a sync burden that defeats the "console IS the Projects board, re-rendered" principle of Q3.2.

    Issue and label operations are faster and clearer in REST; forcing every call through GraphQL would add query complexity without benefit. Hybrid respects each API's strengths.

    BA element
    Platform Development capability · Tools & Build
    Business processes
    BP-001 · BP-010
    SPEC reference
    MGP-SPEC-DEV-001 Part 4 DEC-DEV-002
    Register entry
    DEC-026-devmgmt-github-projects-api.md
  23. DEC-024

    Governance portal analytics — Cloudflare Web Analytics (layered with DEC-007 Axiom)

    2026-04-22 · resolved

    Decision

    `packages/mgp-governance/` v1.0 uses **two non-overlapping observability surfaces**:

    1. **Cloudflare Web Analytics** — anonymous visitor analytics (page views, referrers, country-level geography). Zero-config for CF Pages deployments, no cookies, no consent banner. Provides the Glass Box visibility signal: which proof pages are most referenced. 2. **Axiom via `@mandantis/telemetry`** (per DEC-007) — app-layer telemetry: structured request logs, SSR render durations, kill-switch hits, handler errors, and any custom business-process events under the `business_process_id` schema (DEC-018).

    Azure Application Insights is **explicitly rejected** — violates the platform-wide "No Azure App Config / Key Vault / Dataverse references" rule and diverges from DEC-007's vendor choice.

    Rationale

    The two surfaces answer different questions:

    | Question | Surface | |---|---| | "How many people read the first-conversation page last week?" | CF Web Analytics | | "Which proof documents are most downloaded?" | CF Web Analytics | | "How long did SSR take for `/decision-log`?" | Axiom | | "Did the kill switch trigger on any request?" | Axiom | | "How many `business_process_id=BP-010` events in the last hour?" | Axiom |

    Neither surface duplicates the other. CF Web Analytics has a zero-ops footprint on CF Pages; Axiom integration comes for free via `@mandantis/telemetry`'s `initTelemetry()` at the Hono entrypoint (DEC-019 + F-020).

    Privacy posture: CF Web Analytics is cookieless; no consent banner needed; consistent with Mandantis' vendor-neutral positioning.

    BA element
    Platform Development capability · Release & Deploy
    Business processes
    BP-006 · BP-007 · BP-010 · BP-011
    SPEC reference
    MGP-SPEC-GOV-001 Part 4 DEC-GOV-006
    GitHub Issue
    Mandantis-Group/mandantis-platform#48
    Register entry
    DEC-024-governance-portal-analytics.md
  24. DEC-023

    Governance portal — no search in v1.0

    2026-04-22 · resolved

    Decision

    `packages/mgp-governance/` v1.0 **ships without search**. The F-021 layout component carries a `TODO(v1.1)` comment marking the intended insertion point for a future search affordance. Navigation alone (sidebar + hamburger per DEC-020) provides single-hop access to every section of the ~15-page site.

    Rationale

    Search earns its UX weight when the content surface is large enough that navigation can't reach any item in one hop. The v1.0 portal has:

    - 8 top-level routes (home, agent register, decision log, BA, build standards, frameworks index, glass-box index, health) - 12 framework detail pages under `/frameworks/:id` - 3 glass-box proof pages under `/glass-box/:section`

    All section-level items live in the persistent sidebar (DEC-020). Framework and proof detail pages are listed on their index pages. No reader is more than two clicks from any content. A search input would absorb screen real estate (especially on mobile) with no corresponding reach gain.

    When v1.1 introduces per-decision filtering and larger decision volumes, the TODO anchor in F-021's layout is the insertion point for a semantic-search integration that benefits from the scale.

    BA element
    Platform Development capability · UI & Design System
    Business processes
    BP-006 · BP-007 · BP-010
    SPEC reference
    MGP-SPEC-GOV-001 Part 4 DEC-GOV-005
    GitHub Issue
    Mandantis-Group/mandantis-platform#47
    Register entry
    DEC-023-governance-portal-no-search-v1.md
  25. DEC-022

    Governance portal URL structure — /frameworks/:id + /glass-box/:section prefixes

    2026-04-22 · resolved

    Decision

    `packages/mgp-governance/` uses **two path prefixes** to cluster content by kind:

    - **`/frameworks/:id`** — every MGP framework detail page (12 documents: `bae-001`, `gov-001`, `dev-001`, `arc-001`, `cnt-001`, `crm-001`, `framework-001`, `idx-001`, `mkt-001`, `ops-001`, `prt-001`, `psa-001`). - **`/glass-box/:section`** — Glass Box proof pages (`caa`, `index-score`, `first-conversation`).

    All other routes remain flat at the root:

    - `/` — dashboard - `/agent-register` - `/decision-log` - `/business-architecture` - `/build-standards` - `/frameworks` — framework index (no trailing ID) - `/glass-box` — glass-box index (no trailing section) - `/health`

    Rationale

    Two-prefix hybrid does two things:

    1. **Distinguishes methodology from proof** at a glance. `/frameworks/mgp-bae-001` signals "this is a published framework". `/glass-box/caa` signals "this is evidence of the framework in practice". Both audiences (Claude Code + public visitor) parse the URL shape instantly. 2. **Preserves canonical URL mapping.** `MANDANTIS.suite` already declares each framework's URL under `/frameworks/:id`. Flat URL schemes would have required re-mapping 12 documents across the canonical data surface.

    Non-prefixed routes remain flat for the categories that have a single member (dashboard, decision log, agent register, business architecture, build standards, health) — a prefix for a single child adds no clarity.

    BA element
    Platform Development capability · UI & Content
    Business processes
    BP-006 · BP-007 · BP-010
    SPEC reference
    MGP-SPEC-GOV-001 Part 4 DEC-GOV-004
    GitHub Issue
    Mandantis-Group/mandantis-platform#46
    Register entry
    DEC-022-governance-portal-url-structure.md
  26. DEC-021

    Governance portal content mechanism — structured src/data + download links

    2026-04-22 · resolved

    Decision

    Portal content is rendered from **structured TypeScript data modules** at `packages/mgp-governance/src/data/*.ts`, typed against canonical types from `@mandantis/shared`. Hono route handlers read those modules and compose the web response via the F-021 layout. The portfolio HTML files at `packages/mgp-governance/portfolio/{frameworks,glass-box,proof}/*.html` remain **print-optimised A4 artefacts**, accessible via download links on each detail page but **not** re-rendered into the web response.

    Rationale

    Structured data gives three properties the alternatives can't:

    1. **Type safety at render time.** A route handler that tries to render a non-existent field fails at `tsc` rather than on a visitor's screen. The `AgentEntry`, `DecisionEntry`, `BACapability`, etc. types in `@mandantis/shared` become a contract every consumer respects. 2. **Single canonical source.** Framework titles/versions/summaries live in `MANDANTIS.suite` (one place). Agent entries live in `src/data/agents.ts` (one place, typed). Decision entries live in `governance/decisions/DEC-*.md` with a CI-gated generator to `src/data/decisions.ts`. No duplication, no drift. 3. **Clean v1.1 upgrade.** When the Supabase layer arrives, the `src/data/*.ts` modules swap to API reads behind the same type surface — route handlers are unchanged.

    Portfolio HTMLs retain their original role: print/PDF artefacts, A4-ready, downloadable.

    BA element
    Platform Development capability · UI & Content
    Business processes
    BP-006 · BP-007 · BP-010
    SPEC reference
    MGP-SPEC-GOV-001 Part 4 DEC-GOV-003
    GitHub Issue
    Mandantis-Group/mandantis-platform#45
    Register entry
    DEC-021-governance-portal-content-mechanism.md
  27. DEC-020

    Governance portal navigation — sidebar (desktop) + hamburger (mobile), dashboard as default entry

    2026-04-22 · resolved

    Decision

    `packages/mgp-governance/` uses a **hybrid sidebar-plus-hamburger navigation pattern**:

    - **Desktop (≥840px):** persistent sidebar on the left listing all top-level sections (home, agent register, decision log, business architecture, build standards, frameworks, glass box). Current-section active state. - **Mobile (<840px):** top navigation bar with a hamburger menu that expands to the same section list. Home dashboard (`/`) is the default entry point from any mobile landing.

    Rationale

    One-hop section access is the load-bearing requirement: both audiences benefit from seeing "where am I, what else exists" on every page without having to return home. Sidebar on desktop makes the section inventory visible without consuming the content column (typical 1200px layout leaves ≥900px after a 240px sidebar). Mobile drops to hamburger to preserve vertical content space at 320px widths.

    BA element
    Platform Development capability · UI & Design System
    Business processes
    BP-006 · BP-007 · BP-010
    SPEC reference
    MGP-SPEC-GOV-001 Part 4 DEC-GOV-002
    GitHub Issue
    Mandantis-Group/mandantis-platform#44
    Register entry
    DEC-020-governance-portal-navigation.md
  28. DEC-019

    Governance portal rendering strategy — full SSR via Hono Worker

    2026-04-22 · resolved

    Decision

    `packages/mgp-governance/` renders **all routes via a Hono Worker on Cloudflare** (full server-side rendering). No static generation, no hybrid split. Every request to `governance.mandantis.com` — framework detail pages, glass-box proof pages, dashboard, health — hits a running Worker that composes the response from structured data plus the F-021 layout.

    Rationale

    Option A keeps a single invariant across the portal lifecycle: *every route is a Hono handler*. v1.0 reads from `@mandantis/shared` MANDANTIS + local `src/data/*.ts` modules. v1.1 switches those modules to read from Supabase via `mgp-api` — with no change to the handler shape, the layout, the URL structure, or the deployment topology. The layer that swaps is entirely below `data/`.

    Full SSR also makes the `entrypoint-telemetry` fitness gate meaningful for this package from day one, rather than forcing a gate-flip halfway through v1.x.

    BA element
    Platform Development capability · Tools & Build
    Business processes
    BP-006 · BP-007 · BP-010
    SPEC reference
    MGP-SPEC-GOV-001 Part 4 DEC-GOV-001
    GitHub Issue
    Mandantis-Group/mandantis-platform#43
    Register entry
    DEC-019-governance-portal-rendering-strategy.md
  29. DEC-018

    Business-process register + operational traceability + five-layer test pyramid

    2026-04-21 · resolved

    Decision

    Mandantis introduces a **business-process register** as a first-class governance artefact. Every Feature declares which business processes it serves. Every Axiom telemetry event carries a `business_process_id` attribute. Every Feature's acceptance criteria are mechanically covered by a five-layer test pyramid (unit / integration / contract / e2e / ux+a11y). The Mandantis Index's Pillar 05 diagnostic (MGP-IDX-001) gains an **operational counterpart** — a continuously computed per-process AI-coverage + effectiveness measure — sourced from this register plus telemetry.

    This closes the Glass Box gap: Mandantis sells per-process AI measurement (the Index and the CAA loop in MGP-BAE-001) but has not run it on itself. DEC-018 makes the operating register real and makes every downstream Feature traceable through it.

    ### The register

    Location: `governance/business-processes/BP-NNN-<slug>.md` (sibling to `credentials/`, `vendors/`, `session-logs/`, `governance-tasks/`). Index + schema at `governance/business-processes/README.md`.

    Per-process schema (full form in `governance/business-processes/README.md`):

    ```yaml id: BP-NNN name: "<imperative process name>" capability: "<MGP-BAE-001 capability>" value_stream: "<MGP-BAE-001 value stream>" value_stream_stage: "<Governance|Plan|Execute|Validate|Deploy|Monitor>" owner_human: "<role or person>" owner_agent: "<agent id or 'none'>" status: active | draft | retired ai_coverage_mode: manual | human-assisted-by-ai | ai-first-human-checkpointed | fully-autonomous current_ai_coverage_pct: <0-100> handover_points: - { from, to, trigger, artifact } telemetry: axiom_dataset: <name> event_types: [<name>, ...] effectiveness_metrics: - { name, unit, target } governing_decs: [DEC-NNN, ...] related_features: [F-NNN, ...] ```

    ### Traceability chain

    | Layer | Artefact | Field / convention | |---|---|---| | Register | `governance/business-processes/BP-NNN-<slug>.md` | Frontmatter + body | | Feature | `governance/rebuild/features/F-NNN-<slug>.md` | `business_processes: [BP-NNN, ...]` (required) | | Story | `governance/rebuild/stories/S-NNN-<slug>.md` | `business_processes` optional — inherits from parent Feature; override only when Story spans processes | | Code (runtime) | `@mandantis/telemetry` API | Every span/event carries `business_process_id` attribute | | Tests | `tests/acceptance/F-NNN.spec.ts` (per Feature) + `S-NNN.unit.spec.ts` etc. | Test file name or tag carries Feature/Story ID; assertions map 1..n → AC bullets | | Operational Index | Axiom dashboards at `governance.mandantis.com/business-processes` | Aggregates live telemetry back to register entries; computes per-process AI coverage % continuously |

    ### Five-layer test pyramid

    | Layer | Tool | Scope | Per-package location | |---|---|---|---| | **Unit** | vitest | Pure functions, handlers in isolation, data transforms | `packages/*/src/**/*.unit.spec.ts` | | **Integration** | vitest + miniflare + supabase-cli local | Workers runtime + DB + inter-package boundaries within monorepo | `packages/*/src/**/*.integration.spec.ts` | | **Contract** | JSON Schema / OpenAPI validators | `mgp-api` ↔ consumer schema compatibility | `packages/*/tests/contract/` | | **E2E** | Playwright | Deployed preview environment, user journeys | `packages/*/tests/e2e/` | | **UX + a11y** | Playwright + `@axe-core/playwright` + Lighthouse CI | Accessibility, perceived performance, SEO | `packages/*/tests/ux/` |

    Plus: **business-acceptance suite** per Feature at `tests/acceptance/F-NNN.spec.ts` (repo-level, not per-package) — verifies the Feature's process outcome independent of how Stories implemented it. Runs against deployed preview. This is the test that maps directly back to the process register's effectiveness metrics.

    ### AC → test mapping convention

    - Story AC is a bullet checklist. Each bullet must be covered by ≥ 1 test assertion. - Test file naming: `<Story-ID>.<layer>.spec.ts` (e.g. `S-002.unit.spec.ts`, or `S-002.integration.spec.ts`). Business-acceptance files use `F-NNN.spec.ts` (no layer, inherently e2e/contract mixed). - Test `describe` blocks tag the Story ID: `describe('S-002 — root workspace config', () => { ... })` so coverage reports can slice per Story. - Layer 4 self-review threshold **"Tests ≥ 0.85"** is reshaped from judgemental to mechanical: `(ACs covered by passing tests / total ACs) ≥ 0.85`. Fitness function (extends S-010) computes and gates this on PRs.

    ### Telemetry attribution (activates when E-004 lands)

    When `@mandantis/telemetry` lands in E-004, its API requires a `business_process_id` on every span/event. Fitness function S-010 extends: source files that call the telemetry API must pass a process id that exists in the register. Events without a valid `business_process_id` are rejected at the telemetry-client level (not the server — catch early).

    ### Index operational extension

    MGP-IDX-001 v1.1 defines a 5-pillar diagnostic (0-20). The Index **operational** mode — computed per-process, continuously, from telemetry + register — is the natural output of the CAA loop (MGP-BAE-001). Operational Index per process:

    ``` operational_score = current_ai_coverage_pct × weighted_effectiveness_score ```

    Pillar 05 (Intelligent Operations) of the diagnostic becomes the aggregate of all active processes' operational scores, weighted by capability strategic priority. This makes the Index self-updating rather than a snapshot.

    ### Reshapes (E-009 consolidation will land the canonical edits)

    | Document | Current | Post DEC-018 | |---|---|---| | MGP-BAE-001 §"BA elements" | Lists 4: Capability, Value Streams, Information, Organisation | Adds: Process Register (as the operational artefact for Value Streams) | | MGP-BAE-001 CAA loop | BA → EA → Build → Monitor → Divergence → Governed Decision → BA Update | Adds: Build carries `business_process_id` → Monitor aggregates to register → register feeds Divergence detection | | MGP-IDX-001 §"Measurement" | Static 5-pillar diagnostic | Adds: Operational Index (continuous per-process AI coverage × effectiveness) as companion to the diagnostic | | MGP-SPEC-ARCH-001 Layer 4 thresholds | "Tests ≥ 0.85" (judgemental) | "(ACs covered / ACs total) ≥ 0.85" (mechanical, fitness-function-gated) | | MGP-SPEC-ARCH-001 §"Resolution evidence" | Self-review scores, free-form | Adds: per-AC test coverage table + telemetry attribution check | | governance/rebuild/SCHEMA.md | `ba_element` required | Adds: `business_processes` required on Feature, optional inherit on Story |

    ### Implementation — seeded via F-012 (this Epic, E-002)

    F-012 (Test framework foundation + business-process traceability) lands the mechanical parts:

    - vitest workspace config + coverage + AC-tag reporter - AC → test naming convention enforced via fitness function - Business-acceptance suite template (`tests/acceptance/F-NNN.spec.ts`) - `@mandantis/telemetry` stub with `business_process_id` attribute API (full impl at E-004) - Fitness function extension: `business_processes` non-empty on Features; values exist in register - Retroactive test suite for F-007 (demonstrates the pipeline on a real completed Feature) - Process-register fitness function: every Feature's listed `business_processes` entries must exist as files in `governance/business-processes/`

    Stub tooling (miniflare, supabase-cli, Playwright, axe-core, Lighthouse) lands under later Epics:

    - E-003 `@mandantis/shared` — integration harness activates (miniflare) - E-004 `@mandantis/telemetry` — full telemetry API + business_process_id enforcement - E-005 `@mandantis/mgp-governance` v1 — e2e + UX/a11y layer activates (Playwright + axe + Lighthouse against deployed preview)

    Rationale

    **Why a register (not just capability labels):**

    `ba_element` already tells us which capability a Feature belongs to ("Platform Development · Tools & Build"). That's static — it says who owns the work, not what flow the work serves. Processes are dynamic — they have handovers, they measure, they degrade, they improve. A process register lets us answer "how much of X process is AI now?" — which is the question Mandantis exists to help clients answer. Capability labels cannot answer it.

    **Why the register is governance, not product:**

    The register is the operational substrate. The Index diagnostic (MGP-IDX-001) is a view over the register + telemetry. The governance portal (E-005) is another view. Future Blueprint engagements (MGP-PRT-001) slice the register for clients. The register belongs in governance because it is the source of truth — product views are derived.

    **Why `business_processes` is required on Feature, optional on Story:**

    Features are the unit of business commitment — they deliver a capability-to-process mapping. Stories are implementation detail; they inherit the parent Feature's processes unless a Story spans multiple (rare). Requiring the field on every Story creates form-filling without signal. Requiring it on Features makes the mapping audit-able at the right granularity.

    **Why five layers (not three):**

    Three-layer pyramids (unit / integration / e2e) miss contract boundaries (where integration-tested code still breaks production because consumers have different expectations) and UX/a11y (where e2e passes but real users can't use the thing). Mandantis's architecture has explicit contract boundaries (DEC-003's four integration patterns) and brand/a11y rules — both deserve their own layer.

    **Why test file names carry Story IDs:**

    Without a mechanical link, AC coverage is a judgement. With the link, coverage is computable. Layer 4 self-review threshold "Tests ≥ 0.85" becomes "% of ACs with ≥ 1 passing test ≥ 0.85" — gatable in CI, queryable from the register view, visible in the Glass Box.

    **Trade-offs accepted:**

    - Register maintenance overhead — every new Feature must declare processes, every new process needs a register entry. Mitigation: fitness function catches missing references; new-Feature template pre-fills. - Test-file naming discipline — contributors must put the Story ID in the file name. Mitigation: template scaffolder (in `templates/new-package/`) generates the right files; fitness function warns on missing convention. - Operational Index complexity — aggregation formula is opinionated; will need iteration. Mitigation: register + telemetry are the raw data; aggregation can be reshaped without rewriting the substrate.

    BA element
    BA/EA Alignment capability · Continuous Architecture Alignment
    Business processes
    BP-007 · BP-008
    Register entry
    DEC-018-business-process-register-operational-traceability.md
  30. DEC-017

    Autonomous PR merge authority under pre/mid/post-session checkpoint discipline

    2026-04-21 · resolved

    Decision

    AGT-001 is authorised to **self-merge all pull requests** (governance *and* code) in both `Mandantis-Group/mandantis-governance` and `Mandantis-Group/mandantis-platform`. Principal oversight is exercised through **three formalised session checkpoints** — pre-session, mid-session, and post-session — not through per-PR merge approval.

    This supersedes DEC-015 (governance-only auto-merge) and completes the narrowing of MGP-SPEC-ARCH-001 Layer 3's "never merges" rule. AGT-001 is an automated development platform; the PR-merge gate is removed.

    ### Checkpoint discipline (the Principal oversight mechanism)

    | Checkpoint | Who posts | When | What it contains | What Principal does | |---|---|---|---|---| | **Pre-session** | AGT-001 | Session start, before any Layer 3 work | Layer 2 plan: work items, complexity scores, artefacts to touch, risks, out-of-scope | `[LGTM]` to authorise · redirect · scope-adjust. **Kill switch must be verified before this.** | | **Mid-session** | AGT-001 | At each natural milestone — merged PR, unexpected state, scope expansion request, Story complete, blocker surfaced | Terse summary: what merged, what's next, deviations from plan, pending confirmations | Optional: redirect · pause · expand scope · confirm · ignore (silent = continue) | | **Post-session** | AGT-001 | Session pause or close | Full closeoff: PRs merged, artefacts produced, pending items, next-session pickup paths · appended / new session log in `governance/session-logs/` · memory updates | Review the delta (Layer 6 weekly reporting re-uses the session logs) |

    The **pre-session `[LGTM]`** is the single authorisation moment for the session's scope. Any scope change mid-session is a **mid-session checkpoint** event — AGT-001 surfaces it, Principal confirms, and the new scope applies from that point forward (no separate pre-session re-ack).

    ### Mechanism for autonomous merge

    - AGT-001 opens PRs via GitHub App installation token (author = `mandantis-agt-001[bot]`) - Self-merge via `gh pr merge --rebase --admin --delete-branch` using the same installation token - `--admin` bypass is retained because branch protection requires 1 approving review and the App cannot self-approve; DEC-017 authorises that bypass - `auto-merge` label still applied at PR creation as the auditable signal - GitHub audit log captures every `--admin` merge

    ### What still requires Principal action

    | Action | Rationale | |---|---| | Kill-switch flip (`agent:claude-code:enabled` = `false`) | Independent hard stop, out of autonomous scope by design | | Production deploys (STOP gate) | MGP-SPEC-ARCH-001 Part 1 §"Human Intervention Points" item 2 — preserved | | Issue closure in GitHub | MGP-GOV-001 v1.1 §02 Class 3 — retained as Principal duty (separate from PR merge) | | DEC reshape of DEC-004 / DEC-009 / DEC-012 / DEC-013 / DEC-014 / DEC-017 | Reshaping AGT-001's own governance frame requires Principal authorship or explicit `[PRINCIPAL-APPROVED]` + co-authored DEC | | `.github/workflows/**`, `.github/CODEOWNERS`, branch-protection config, repo settings | Infrastructure-level; affects all future agent activity | | Anything Principal marks `[PRINCIPAL-MERGE]` in-session | Per-PR carve-out, always available |

    Rationale

    **Why the PR-merge gate was the wrong gate:**

    - The Layer 2 plan posted pre-session is the real authorisation moment. It names the artefacts to be touched before they are. By the time a PR reaches the merge button, it is execution of an approved plan. Putting Principal at merge time duplicates the gate without adding signal. - Across 2026-04-21 morning (DEC-015 period) and 2026-04-21 continuation (this session), the pattern was: plan → `[LGTM]` → work → PRs open → Principal clicks merge. The click was mechanical. Principal was not discovering anything at click-time that wasn't already in the plan. - AGT-001's operating model is **automated development platform**. A development platform that requires a human click to merge its own PRs is not automated — it is a code-review assistant with grandiose naming.

    **Why checkpoint discipline is sufficient:**

    - **Pre-session** captures scope upfront — the Principal sees what will happen before it happens - **Mid-session** captures deviation — the Principal hears about anything unexpected *as it emerges*, not at the end - **Post-session** captures the delta — the Principal has a written record of what actually happened, for Layer 6 reporting and future reference - Combined, these three checkpoints surface everything a per-PR review would catch, without blocking throughput

    **Why not autonomy on issue closure too:**

    - MGP-GOV-001 v1.1 §02 Class 3 couples issue closure to governance closure ("work is governance-complete"). Issue closure means "this work is done and the governance register is up to date." That judgement remains a Principal-level act. - Issue closure is rare and batched (post-session usually); PR merge is frequent and mid-session. Removing the PR gate unblocks throughput; keeping the issue gate preserves governance closure as a human act.

    **Why not autonomy on production deploys:**

    - Production deploys change user-visible state with blast radius measurable in minutes/outages. The STOP gate pre-dates AGT-001's autonomy discussion and stays as the backstop that bounds runtime risk.

    **Trade-offs accepted:**

    - AGT-001 can self-merge a code PR that passes its own Layer 4 self-review but has latent issues. Mitigations: (1) Layer 4 thresholds are strict (≥0.85 overall); (2) post-session checkpoint surfaces all merges — Principal can revert via a follow-up PR; (3) GitHub audit log captures every `--admin` merge for independent audit; (4) kill switch remains the hard stop for broader concerns; (5) CI / fitness functions (F-008 landing under E-002) add a third independent check on every code PR. - The velocity of autonomous merging means mid-session checkpoints need to fire at the right moments (too few = Principal discovers issues at post-session only; too many = noise). Pattern: one mid-session update per merged PR by default, condensed into a single update when multiple PRs land in quick succession.

    BA element
    Organisation Map · Platform Development capability
    Register entry
    DEC-017-autonomous-pr-merge-checkpoint-discipline.md
  31. DEC-016

    AGT-001 vendor calls — MCP-first for atomic ops, bash for multi-step orchestration

    2026-04-21 · resolved

    Decision

    AGT-001 makes vendor API calls via **MCP tools by default**, with **shell (bash + curl + vendor CLI) reserved for multi-step orchestration and edge-endpoint cases**. The rule applies to all vendors with an MCP wrapper available in the Claude Code environment; vendors without MCPs continue via shell until an MCP exists.

    Rationale

    **Why MCP-first:**

    - **Audit clarity.** A tool call labeled `mcp__github__add_issue_comment` is self-describing in the session log; a 30-line bash block hides the action behind shell syntax. Layer 6 weekly review reads MCP names directly; bash blocks need to be parsed. - **Type safety.** MCP tools accept structured parameters; shell flows construct JSON via string interpolation, which has bitten this session multiple times (backtick interpolation in PR body, missing escaping in policy JSON, etc.). - **Consistent error surface.** MCPs return structured errors that Claude can act on programmatically; curl returns text that needs grep/awk parsing. - **Less context noise.** MCP returns the structured result; bash dumps full HTTP response JSON which crowds the context window. - **Precedent across the platform.** MCPs are the canonical interface for AGT-001 to talk to vendors per the design intent of F-006 ("GitHub MCP wired for AGT-001"). The 2026-04-21 session defaulted to bash out of the agent's habit, not architectural intent — DEC-016 corrects the default.

    **Why shell remains valid (escape hatch):**

    - **Atomicity.** The DEC-013 self-bootstrap is conceptually one operation: "wake up and become operational." Splitting it across 4 separate MCP calls would inflate the audit log without governance value, and would break if any intermediate call's output isn't directly assignable to the next call's input. One bash block keeps the whole chain in one place. - **Coverage gaps.** MCPs don't cover every vendor endpoint. CF `POST /user/tokens` with custom `permission_groups` is not in the Cloudflare MCP today. Forcing MCP-only would push us to wait for MCP coverage before doing legitimate work — that defeats Class 3 autonomy. - **Vendor without MCP.** Infisical has no MCP in the environment as of 2026-04-21. CLI is the only path. Shell is necessary, not optional.

    BA element
    Organisation Map · Platform Development capability
    Register entry
    DEC-016-mcp-first-vendor-calls.md
  32. DEC-014

    AGT-001 Cloudflare access — meta-token + self-provisioned scoped tokens via Infisical

    2026-04-21 · resolved

    Decision

    AGT-001 holds a set of Cloudflare tokens for autonomous CF operation, all stored in Infisical, all retrieved per-session via the DEC-013 flow:

    - **`agt-001-meta`** (`User → API Tokens → Edit`) — persistent token-management capability. AGT-001 uses this to mint, rotate, and revoke any scoped token without Principal involvement. - **`agt-001-kv`** (Workers KV Storage Write) — KV ops - **`agt-001-pages`** (Pages Write) — Pages deploys - **`agt-001-workers`** (Workers Scripts Write) — Worker scripts - **`agt-001-dns`** (DNS Write, all-zones-in-account) — DNS records (effective when zones transfer) - **`agt-001-access`** (Access: Apps and Policies Write) — Access policies (effective when Access enabled)

    The meta-token must be created by Sean at the CF dashboard. CF explicitly prohibits sub-tokens from holding the `API Tokens Write` permission (verified empirically — `POST /user/tokens` returns `"sub-token is not allowed to have permissions to manage other tokens"`). The meta is therefore the only CF capability that originates from the human; everything else AGT-001 self-provisions via the meta.

    Scope posture follows DEC-012: master capability is intentional and policy-enforced (audit log + Layer 4/6 review + kill switch). The meta living persistently in Infisical is consistent with DEC-009 (GitHub App pem in Infisical → mint App JWTs) and DEC-013 (Universal Auth Client Secret on disk → mint Infisical JWTs). All three identity legs hold a long-lived master credential whose only protection is policy + kill switch.

    Rationale

    **Third leg of the agent-auth tripod:**

    | System | Identity primitive | Anchor secret location | Per-operation creds | |---|---|---|---| | GitHub (DEC-009) | App `mandantis-agt-001` | pem in Infisical (`prod/github/agt-001-app/private-key`) | Installation tokens (1h, minted on demand) | | Infisical (DEC-013) | Machine identity `mandantis-work-claude-code` | Universal-auth Client Secret on disk (`~/.config/infisical/agt-001.env`, mode 600) | JWT (2h, exchanged at session start) | | **Cloudflare (this DEC)** | **Meta-token `agt-001-meta` + self-provisioned scoped tokens** | **Meta-token in Infisical** (`prod/cloudflare/api-token-agt-001-meta`) | **Long-lived scoped tokens in Infisical** (`prod/cloudflare/api-token-<purpose>`) |

    CF has no native bot/App identity primitive. Closest equivalent is named tokens with audit-log attribution by token name — naming convention `agt-001-<purpose>` carries the actor identity through audit log review.

    **Why a persistent meta-token in Infisical (not use-once-then-revoke):**

    - **Consistency with DEC-009 / DEC-013** — both already authorise long-lived master capability in the agent's secret store: GitHub App pem (mints App JWTs → installation tokens with broad scope) and Infisical Universal Auth Client Secret (mints JWTs with project-wide write). CF should follow the same pattern; ring-fencing CF token-management as the only "must be ephemeral" capability would be inconsistent and was rejected after Principal review on 2026-04-21. - **Class 3 autonomy posture (DEC-012)** — Layer discipline is enforced as policy (audit log + Layer 4/6 + kill switch), not as technical sandboxing. Meta-token-in-Infisical is governed identically to the GitHub App's `administration:write` scope: technical capability exists; policy says don't use it without `[DECISION-REQUIRED]`. - **No 3-step ceremony for routine ops** — adding a new scope (R2, Queues, D1, etc.), rotating any token, or revoking a compromised token is fully self-serve via meta-from-Infisical. Principal involvement only when meta itself rotates (annual) or at compromise response.

    **Why the meta-token must originate from Principal at the CF dashboard (not API-mintable):**

    - CF explicitly prohibits sub-tokens from holding the `API Tokens Write` permission group. Empirical verification 2026-04-21: `POST /user/tokens` with that permission group via a meta-token returns: ``` "code": 1001, "message": "sub-token is not allowed to have permissions to manage other tokens" ``` - This is a CF design decision: token-management is reserved for the human user account. AGT-001 cannot self-provision a peer to the meta. Initial creation (and any future rotation of the meta itself) requires Sean at the CF dashboard. - Once provisioned, the meta lives indefinitely in Infisical and AGT-001 self-serves all subsequent token operations (scoped token mint/rotate/revoke).

    **Why scoped tokens at all (vs just using meta for everything):**

    - **Audit log clarity** — operations attributed to the specific token name (`agt-001-kv`, `agt-001-pages`) make audit review trivially answer "what did AGT-001 do to KV?" - **Blast radius containment** — a leaked `agt-001-kv` token can only edit KV; can't deploy Workers, modify DNS, or change Access policies. - **Rotation isolation** — KV token compromise rotates one token, not five.

    **Trade-off accepted:** Infisical compromise yields meta-token compromise yields full CF account capability for Sean's user (mint any scope, revoke any token). Mitigations: (1) DEC-013's universal-auth Client Secret already gates Infisical access; compromising the CF meta requires first compromising the agent's auth-on-disk; (2) kill switch at CF KV is read by AGT-001 before any write — independent of token capability; (3) annual meta rotation; (4) GitHub audit log + CF audit log + Layer 6 weekly review surface anomalies. Same risk surface as the GitHub App holding `administration:write`; explicitly accepted under DEC-012.

    BA element
    Organisation Map · Platform Development capability
    Register entry
    DEC-014-cf-meta-token.md
  33. DEC-013

    AGT-001 Infisical access — universal-auth machine identity with client creds on disk

    2026-04-21 · resolved

    Decision

    AGT-001 authenticates to Infisical via **Universal Auth** on the `mandantis-work-claude-code` machine identity (ID `23c1e7f4-2fc2-45a5-bb70-d67bfc249270`). Client ID + Client Secret are stored on the Linux box at `~/.config/infisical/agt-001.env` (mode `600`, owner `ssimmons1972`), sourced at session start. AGT-001 exchanges them for a 2-hour JWT via the Infisical CLI (`infisical login --method=universal-auth`) on each session, no human paste required.

    Rationale

    **Why universal-auth over alternatives:**

    - **Token auth (long-lived static tokens)** — same secret-on-disk exposure but without a refresh ceiling, worse for rotation discipline. Rejected. - **OIDC auth / Kubernetes / AWS IAM** — requires AGT-001 to run in a context that presents a trusted OIDC claim. This box runs interactive Claude Code as a Linux user — no OIDC provider attached. Rejected for v1. - **Per-session paste** — the pattern used 2026-04-20 for F-002 bootstrap. Works but violates "AGT-001 is autonomous" — Principal becomes the critical path for every session restart. Directly motivated this DEC (*"how can i set you up in infisical so you dont need me to get a jwt every session"* — Sean, 2026-04-21).

    **Trade-off accepted:** Client Secret lives on disk. Disk compromise = Infisical access compromise = platform secrets access. Mitigations:

    - File mode `600`, directory mode `700` — owner-only read - Box is single-user (`ssimmons1972`), no shared shell - No backup of `~/.config/infisical/` to any cloud/sync - Client Secret is rotatable via Infisical UI (one call, new secret replaces old, old instantly revoked) - Kill switch (CF KV `mandantis-platform/agent:claude-code:enabled`) is an independent hard-stop — disk-resident Infisical creds don't bypass it

    **Why not keep Infisical creds inside Infisical:** bootstrap paradox. Something has to anchor the chain. The client secret on disk is that anchor; everything else derives from it.

    BA element
    Organisation Map · Platform Development capability
    Register entry
    DEC-013-infisical-universal-auth.md
  34. DEC-012

    AGT-001 GitHub App holds full scope — Layer discipline enforced as policy, not permissions

    2026-04-21 · resolved

    Decision

    The `mandantis-agt-001` GitHub App (App ID `3447537`, owner `@Mandantis-Group`) holds a **broad permission surface** spanning repository write, organisation administration, members, secrets, workflows, and related scopes. **This is intentional.** Class 3 autonomy requires it.

    **Layer discipline is enforced as policy, not as technical sandboxing.** Rules in MGP-SPEC-ARCH-001 ("AGT-001 never merges", "never closes Issues", "never deploys to prod without STOP gate") are behavioural rules the agent follows — they are **not** reduced to a narrow OAuth scope set.

    Rationale

    **Why not narrow the scopes:**

    - **Autonomous work selection breaks under narrow scopes** — Layer 2 requires AGT-001 to pick work across any Epic/Feature without a human pre-clearing each permission change. A narrow scope set ("issues:write, contents:write, pull_requests:write") forces a per-scope governance cycle every time Layer 2 identifies work that touches a new capability (workflows, secrets, runners, etc.). That cadence defeats autonomy. - **Class 3 agents are not sandboxed operators** — MGP-GOV-001 v1.1 classifies AGT-001 as Class 3 (autonomous development with Principal governance). Class 3 = trusted with discretion; discipline enforced by governance, not sandboxing. Class 1/2 agents get narrow scopes; Class 3 does not. - **Policy > permissions as the enforcement primitive** — "Never merges" is a *rule* AGT-001 follows, not a *capability* it lacks. Rule compliance is verified via audit log review (Layer 4/6) and by Principal's PR reviews. A misbehaving Class 3 agent is a trust failure that a narrower scope set would not have prevented (the agent could still post damaging PRs, open inflammatory Issues, etc. with minimal scopes). - **Bootstrap friction** — forcing AGT-001 back to Sean for every "need a new scope" decision puts Principal on the critical path of every Layer 3 task. That inverts the operating model.

    **Risks accepted (and their mitigations):**

    | Risk | Mitigation | |---|---| | AGT-001 merges a PR despite "never merges" rule | (1) Kill switch in CF KV `mandantis-platform/agent:claude-code:enabled` (hard stop) · (2) Branch protection `required_pull_request_reviews: 1` still blocks — App would need `--admin`-style bypass path which is observable in audit log · (3) Layer 6 weekly report surfaces merge-author anomalies | | AGT-001 deletes a repo / rotates a secret destructively | (1) Kill switch · (2) No persistent agent process — each session is short-lived with new prompting · (3) GitHub audit log for org-admin actions is retained; reviewed at Layer 6 · (4) Sensitive operations require explicit in-session Principal direction (pattern continues from bootstrap — e.g., PR merges are `[DEVIATION]`-logged today) | | AGT-001 modifies org membership | (1) Members of `Mandantis-Group` are Sean-only for v1 · (2) Any membership change is a `[DECISION-REQUIRED]` for Principal · (3) Audit log | | AGT-001 alters branch protection | (1) Kill switch · (2) Audit log anomaly detection · (3) Layer 4 validation threshold (architecture ≥ 0.9) would fail review for unauthorised protection changes | | Scope creep — agent starts using new scopes without Layer 1 approval | (1) Every new scope use triggers `[DECISION-REQUIRED]` — policy | (2) Scope telemetry audited at Layer 6 |

    **Residual risk** is real and accepted. The alternative (narrow scopes) was judged worse: permission friction destroys autonomy, and a narrow scope set does not prevent a misbehaving agent from causing harm — only from causing *some specific types* of harm.

    BA element
    Organisation Map · Platform Development capability
    Register entry
    DEC-012-agt-001-full-scope.md
  35. DEC-011

    Early content promotion — push markdown shell ahead of full E-002 scaffold for remote-session enablement

    2026-04-21 · resolved

    Decision

    Authorise a **one-time, markdown-only content promotion** of:

    - `build-packages/mandantis-platform/*` → `Mandantis-Group/mandantis-platform` - `governance/*` + `docs/*` → `Mandantis-Group/mandantis-governance`

    ahead of E-002's full execution. The promotion ships **structure and content, not runtime**. Specifically it includes and excludes:

    | Included | Excluded (stays E-002 scope) | |---|---| | Markdown (SPECs, DECs, registers, framework docs, READMEs, CLAUDE.md) | `package.json`, `pnpm-workspace.yaml`, `turbo.json` | | Directory structure (`packages/`, `src/`, `templates/`, `docs/`) | Turborepo 2.9 install, pnpm 9.15 workspaces | | Existing TypeScript source (`src/data/mgp-suite.ts`) | `tsconfig.base.json`, ESLint, Prettier, lefthook | | Templates (`templates/new-package/`) | Matrix CI workflow with per-env OIDC (F-008) | | Empty package shells with `README.md` + `.gitkeep` only | Any `pnpm install` / package build / test run |

    Rationale

    **Why early promotion is needed:**

    - Sean requires **remote sessions** (Claude Code web, scheduled triggers, mobile) to drive AGT-001 under the Layer 2/3 cadence. Remote sessions read from GitHub — without repo content there is nowhere for a web session to open. - Content currently lives in `/mnt/repos/projects/mandantis/` on a single local machine. This is a single-point-of-failure and blocks any non-local collaboration or Layer 6 reporting cadence. - F-006 (2026-04-20 partial close) already provisioned both repos with branch protection, CODEOWNERS, labels, milestone, 10 Epic Issues, Issue+PR templates. The repos are ready to receive content; only the push is missing.

    **Why it doesn't invalidate E-002:**

    - E-002's core deliverables (F-007 Turborepo + pnpm scaffold, F-008 matrix CI, F-009 CODEOWNERS) remain untouched and unstarted. This DEC promotes *content*, not *build system*. - No `pnpm install` runs. No CI workflow authored. No package actually builds. A future pnpm scaffold will install on top of this markdown shell without conflict (template files and empty `packages/*/` dirs survive a Turborepo init cleanly). - When E-002 activates, F-010 ("promote `build-packages/mandantis-platform/` contents") is effectively already satisfied — F-010 is re-scoped under E-009-style reconciliation to "verify post-DEC-011 content matches E-002 expectations; add any drift."

    **Why not wait for E-002:**

    - E-002 depends on E-001 completing (F-001 through F-006). F-001 (Cloudflare account + kill-switch KV) is still open; F-005 (identity/email via DEC-009) has pending items (CF Email Routing enablement, AGT-001 GitHub App). Waiting serialises remote sessions behind all of that. - The cost of early promotion is ~zero (markdown moves preserve git-move semantics; no runtime risk). The benefit is immediate remote-session capability.

    BA element
    Organisation Map · Platform Development capability
    Register entry
    DEC-011-early-content-promotion.md
  36. DEC-010

    Repo content split — mandantis-platform (code monorepo) vs mandantis-governance (register)

    2026-04-21 · resolved

    Decision

    Two repositories exist in the `Mandantis-Group` GitHub organisation. Each has a fixed content remit; no content is duplicated across them.

    | Repo | Purpose | Root layout | |---|---|---| | **`Mandantis-Group/mandantis-platform`** | **The monorepo.** All application code, packages, build specs, integration specs. Consumed by AGT-001 when executing Epics E-002 through E-008. | `CLAUDE.md`, `README.md`, `MGP-SPEC-ARCH-001.md`, `MGP-SPEC-DEV-001.md`, `MGP-SPEC-GOV-001.md`, `packages/`, `src/`, `templates/new-package/`, `docs/` (decision-log, integration-register, portfolio-manifest, observability) | | **`Mandantis-Group/mandantis-governance`** | **The register.** All authoritative governance data + canonical framework authorities. Consumed by Sean (Principal) and AGT-001 during Layer 1/2/5/6 operations. | `CLAUDE.md`, `README.md`, `decisions/`, `credentials/`, `vendors/`, `rules/`, `scheduled-jobs/`, `session-logs/`, `work-items/`, `governance-tasks/`, `rebuild/`, `docs/` (frameworks, standards, thesis, proof, legacy) |

    ### Routing rules

    - **New DEC** → `mandantis-governance/decisions/` - **New framework version / standard update** → `mandantis-governance/docs/frameworks/` or `docs/standards/` - **New package** → `mandantis-platform/packages/<name>/` - **New integration pattern example** → `mandantis-platform/docs/integration-register.md` - **Credential / vendor / rule record** → `mandantis-governance/` (never `mandantis-platform/`) - **Session log** → `mandantis-governance/session-logs/` - **Epic / Feature / Story / Task markdown** → `mandantis-governance/rebuild/` (mirrors GitHub Issues on `mandantis-platform` via Issue numbers, not content duplication)

    ### Workspace folder (`/mnt/repos/projects/mandantis/`)

    Becomes a **two-checkout working directory** — contains `mandantis-platform/` and `mandantis-governance/` as sibling git clones. The workspace root itself is not a repo and carries only a thin `README.md` + `CLAUDE.md` orchestrator pointing into the two repos.

    Rationale

    - **Cadence mismatch** — governance content churns on human decisions (weeks between DECs); platform content churns on AGT-001 commits (multiple PRs per session). Co-locating them creates noise in either direction. - **Access pattern mismatch** — governance is read by humans + AGT-001's read-only governance checks; platform is written by AGT-001 continuously. Separate repos allow separate branch-protection postures and CODEOWNERS patterns as they evolve. - **Framework authorities travel with governance, not code** — MGP-GOV-001, MGP-BAE-001, the thesis, the operating standards: these are *what the platform conforms to*, not *what the platform is*. They belong alongside DECs. - **Per-spec copies in the monorepo are intentional duplication** — `mandantis-platform` carries MGP-SPEC-ARCH-001 / MGP-SPEC-DEV-001 / MGP-SPEC-GOV-001 at its root because those three documents are the monorepo's own build-time contract. Drift between the copy in `mandantis-platform` and canonical in `mandantis-governance/docs/frameworks/` is reconciled under E-009. - **Remote session enablement** — Claude Code web, scheduled triggers, and mobile sessions all require a GitHub repo. Two repos give Sean a clear "which do I open" signal: code work → `mandantis-platform`, governance work → `mandantis-governance`.

    BA element
    Organisation Map · Platform Development capability
    Register entry
    DEC-010-repo-content-split.md
  37. DEC-008

    Repo + package naming — lowercase-hyphenated

    2026-04-20 · resolved

    Decision

    All Mandantis platform repos, packages, workspace directories, and Cloudflare project names use **lowercase-hyphenated** naming:

    - Monorepo: `mandantis-platform` (not `Mandantis-Platform`) - Packages: `mgp-governance`, `mgp-devmgmt`, `mgp-api`, `mgp-mcp`, `shared` - npm scopes: `@mandantis/mgp-governance` etc. - Cloudflare project names, wrangler.toml `name` fields, GitHub repo names — all lowercase

    Rationale

    - Consistency with npm package ecosystem conventions (`@scope/package-name`) - Consistency with Linux / Cloudflare / GitHub URL-safe conventions - Eliminates case-sensitivity bugs across macOS (case-insensitive default) + Linux (case-sensitive) + Windows (case-insensitive) - No PascalCase / CamelCase mixing in commits, paths, or references - Matches how SPEC-ARCH-001, SPEC-GOV-001, SPEC-DEV-001, and the monorepo-root CLAUDE.md already refer to the repo and packages

    BA element
    Platform Development capability
    Supersedes
    POC-era "Mandantis-ProperCase" naming convention (originally DEC-173 in the wiped POC register). The PascalCase convention is retired.
    Register entry
    DEC-008-repo-naming-lowercase.md
  38. DEC-007

    Observability — Axiom (OTEL + R2-backed event store)

    2026-04-20 · resolved

    Decision

    Observability for Mandantis-Platform is **Axiom**. OpenTelemetry-native ingestion, APL (SQL-derived) query language, R2-backed event store, sub-second queries over high-cardinality events.

    No Azure Application Insights. No Cloudflare-native observability as the primary target (Workers Analytics Engine remains available for edge-specific metrics where it's a better fit, but Axiom is the structured-logs + traces + events system of record).

    Rationale

    The Glass Box "world-leading" principle requires global best, not pragmatic CF-continuation. Evaluated against that filter:

    - **Axiom** — event-based, OTEL-native, APL (SQL-derived) queryable, R2-backed at-rest storage, sub-second on billions of events, pricing model enables high-cardinality without cost-pain, architecturally coherent with the CF-Workers-on-R2 stack. Selected. - **Honeycomb** — current category leader for event-based observability but commercial pricing model punishes high-cardinality; proprietary query DSL; less architecturally coherent with R2-native backbone. - **Grafana Cloud** — world-leading for breadth (Loki/Tempo/Mimir/Pyroscope/Faro) and data ownership, but operationally heavier and split across multiple backends. - **Datadog / New Relic** — enterprise default, not world-leading; increasing cost concerns; broad but shallow. - **Cloudflare Workers Observability / Analytics Engine** — convenient for CF-native workloads but lacks tracing depth and cross-service event analysis; not world-leading today.

    Axiom aligns with every Mandantis principle: world-leading (yes), vendor-neutral in posture (OTEL = portable, no lock-in), architecturally coherent with the stack (R2-backed, same as planned content stores), cost-efficient at high cardinality (sustainable for growth).

    BA element
    Platform Development capability · Governance & Compliance
    Supersedes
    - MGP-GOV-001 v1.1 §06 reference to "mgp-telemetry tail Worker → Azure Application Insights" (v1.0 language; Azure retired per DEC-002, now observability off Azure per this decision). - MGP-SPEC-ARCH-001 stack translation line 33: `App Insights → Cloudflare Workers telemetry → Azure Application Insights` — now `→ Axiom`. - The POC-era assumption that Azure Application Insights was the monitoring tier.
    Register entry
    DEC-007-observability-axiom.md
  39. DEC-006

    Content Agent — Class 2 (Notify), KV Kill Switch

    2026-04-20 · resolved

    Decision

    Content drafting AI classified as **Class 2 (Notify)** agent under MGP-GOV-001 v1.1. Drafts produced for human approval; no autonomous publish path.

    - **Kill switch:** Cloudflare KV - Key: `agent:content-draft:enabled` - **Tool manifest:** - Anthropic API — write - Signal API — read - No autonomous publication path (cannot write to publishing channels directly)

    Rationale

    DIV-001 (CAA simulation) detected that content was being produced outside the registered agent framework. Option A selected: register the agent with the appropriate classification rather than suspend content production or redefine the Business Architecture. Class 2 is correct because the agent produces output (draft) for human decision (approve / publish) — it does not act autonomously.

    BA element
    Content Production capability · Content Delivery Stream
    Register entry
    DEC-006-content-agent-class-2.md
  40. DEC-005

    CAA Monitoring — Continuous and Event-Driven

    2026-04-20 · resolved

    Decision

    CAA (Continuous Architecture Alignment) monitoring runs on every material event — agent deploy, system build, D-series decision, organisational change, client engagement change. Not periodic. Continuous. Divergence threshold triggers automatic escalation to Principal Consultant.

    Rationale

    TOGAF Phase H (Architecture Change Management) is the closest existing equivalent — and it is periodic and human-driven. Mandantis CAA solves what Phase H describes but cannot deliver: continuous, autonomous, governed architecture alignment. This decision is the founding commitment to that operating model.

    BA element
    BA/EA Alignment stream · All four BA elements
    Register entry
    DEC-005-caa-monitoring-continuous.md
  41. DEC-004

    Agent Governance — claude-code@mandantis.com classified Class 3, AGT-001

    2026-04-20 · resolved

    Decision

    `claude-code@mandantis.com` classified as **Class 3 (Write)** agent under MGP-GOV-001 v1.1. Registered as **AGT-001**. Tool manifest defined. Kill switch provisioned and tested.

    - **Kill switch:** Cloudflare KV - Namespace: `mandantis-platform` - Key: `agent:claude-code:enabled` - Value: `true` (active) / `false` (disabled) - **Kill switch tested:** 2026-04-20 - **Agent register:** AGT-001 — published to governance.mandantis.com/agent-register (when portal live)

    Rationale

    The Glass Box theorem requires that Mandantis applies MGP-GOV-001 to its own agents before claiming it applies them for clients. This is the first and most visible proof that the governance framework is operational, not theoretical. AGT-001 is Mandantis' tenant-zero demonstration of the agent governance model.

    BA element
    Organisation Map · Platform Development capability
    Register entry
    DEC-004-agent-governance-claude-code-class-3.md
  42. DEC-003

    Integration Pattern Discipline — Four Locked Patterns

    2026-04-20 · resolved

    Decision

    Four integration patterns locked. Every system interaction follows one. No new pattern without a D-series governance decision.

    | Pattern | Use when | |---|---| | `mgp-api` | System/automation caller, service principal. No UI touches data stores directly. | | `mgp-mcp` | AI model caller, OBO/delegated auth. Class 3 default. Explicit tool allow list. | | `mgp-webhook` | Inbound event ingress. HMAC verified. Sub-pattern of mgp-api. | | `mgp-portal-api` | Authenticated human REST. WorkOS AuthKit / CF Access. No AI. |

    Rationale

    Pattern discipline is the governance mechanism. Without locked patterns, every developer makes individual integration decisions. Patterns make security, governance, and auditability structural rather than discretionary.

    BA element
    All value streams
    Register entry
    DEC-003-four-integration-patterns-locked.md
  43. DEC-002

    Compute — Cloudflare Workers Only

    2026-04-20 · resolved

    Decision

    Cloudflare Workers as sole compute layer for Mandantis-Platform. Zero Azure-native resources. Any Terraform scaffold for Azure stays dormant for when/if Azure earns its place.

    Rationale

    The CF Workers + Supabase + Xero + Stripe + Infisical stack has nothing to land in an Azure resource group. Simplifies the stack, reduces cost, removes Azure dependency for compute.

    BA element
    Platform Development capability
    Register entry
    DEC-002-cloudflare-workers-sole-compute.md
  44. DEC-001

    Primary Data Store — Supabase Postgres (AU)

    2026-04-20 · resolved

    Decision

    Supabase Postgres (AU region) as primary data store. Multi-tenant via RLS + `tenant_id`. Mandantis = tenant zero. Drizzle ORM + postgres.js driver.

    Rationale

    Dataverse retired (platform-level decision, POC phase). Vendor-neutral, AU data sovereignty compliant, no Azure dependency for data layer, mgp-api exclusive access enforced. No UI touches Postgres directly.

    BA element
    Information Map — all critical data assets
    Supersedes
    Dataverse as primary data store (decision inherited from POC phase).
    Register entry
    DEC-001-supabase-primary-data-store.md