Skip to main content
Status: The storage model for runs/events/receipts, and the learning-data policy (per-tenant default, cross-tenant aggregation, opt-out) — Developer preview (designed, not built). The PII pseudonymization pipeline in section 2 — Available today: implemented, tested code, not a promise.
Scope: what the fabric stores and why, the pipeline that turns a captured production issue into a test case without carrying a customer’s identifying content forward, and the learning-data policy that governs what, if anything, crosses tenant boundaries. Sources: docs/superpowers/specs/2026-07-30-maas-execution-fabric-design.md (design spec) §§1, 2, 4, 6; src/lib/platform-corpus/normalize.ts.

What is stored

Three kinds of durable record:
  • Runstask_runs: the execution record behind POST /v1/execute (or its internal equivalent), including leases, attempt counts, budgets, and a first-class waiting_approval state.
  • Eventstask_run_events: an append-only log of what happened during a run, sequenced so GET /v1/runs/:id/events can resume from a Last-Event-ID without re-delivering or skipping anything.
  • Receipts — a per-run summary of what ran, the policy version applied, approvals granted (referenced by digest), and cost. Full contents in receipts-and-audit.md. The hash-chained evidence ledger behind receipts, and their PDF/SIEM export, is a named, not-yet-shipped build item (design spec §4, workstream S3).
Retention. The design does not specify retention windows for runs, events, or receipts. This document does not state one — a specific retention period for any of these record types is not yet part of the published design and should not be assumed until it is.

Turning production issues into test cases without keeping customer content

src/lib/platform-corpus/normalize.ts is implemented, tested code — not a design commitment. It sits alongside its own test suite (normalize.test.ts, sanitizer-parity.test.ts, corpus-replay.test.ts, equivalence.test.ts) in src/lib/platform-corpus/. It is the mechanism that turns a captured event stream into a comparable, replayable fixture — for the locked-behavior corpus and shadow-equivalence testing that back the platform’s migration work — without carrying the original customer’s identifying content forward into that fixture. Every normalized batch is re-validated against the platform’s own event schema (AiPlatformEventV1Schema) before use, so a bad substitution can’t silently produce an invalid fixture.

Six pseudonymization categories

sanitizeText() runs six pattern-matched categories, in a fixed order chosen so an earlier, narrower substitution can’t be re-caught by a later, broader one: Order matters: timestamps and IPs are scrubbed before hostnames so the hostname pattern — the broadest of the six — never gets a chance to match a timestamp’s tail or a raw IP literal; emails are scrubbed before hostnames so a hostname embedded inside an email address is already gone by the time the hostname pattern runs. Already-safe values (localhost, *.example.test, an email already ending @example.test) are recognized and left alone, which also makes the function idempotent — running it twice on already- sanitized output changes nothing further. Substitution is stable within a single normalization run: a lookup table plus per-category counters means the same original value always maps to the same pseudonym for the batch of events being normalized together. A test case built this way can still show “this is the same user across three events” — the referential structure survives — without that identifier ever being the real one.

Structural fields versus content

The normalizer treats position differently. At the top level, a fixed set of structural/discriminator fields — type, sequence, requestId, replayed, toolCallId, approvalId, memoryId, schemaId, model, code, finishReason, status, retryable, apiVersion — is left completely untouched, because these are protocol shape, not content, and altering them would break the comparison the corpus exists to do. The at timestamp is always zeroed to the same epoch constant used above. Every other string — top-level or nested at any depth — passes through the six-category scrubber; at depth 1 and below there are no key-name exceptions, so every string value gets sanitized without exception.

Streamed text: an extra option, not a weaker guarantee

For text.delta events, the normalizer supports two postures, chosen by comparison mode:
  • semantic / intentionally_changed mode — the delta’s actual text is discarded outright and replaced with a length-only marker (⟨len:N⟩). The content itself does not survive normalization in this mode; only its length does.
  • exact mode — the delta text is retained, because an exact-mode comparison needs to check output byte-for-byte, but it still passes through the same six-category scrubber as any other string.
Precision matters here: the six-category pipeline removes known PII shapes from whatever text is retained. It is not a claim that all retained text is content-free — only semantic/intentionally_changed mode reduces message text to a length marker. A test case that must not carry any message text forward at all needs semantic mode specifically, not just the scrubber.

Learning: per-tenant by default

This section describes design intent (design spec §3.5). There is no evidence of a live routing-policy learning system in the product today — model routing currently runs on static effort/cost lanes and failover ladders, not a learned one — so treat everything below as target behavior for a system not yet built, not a description of something already running.
  • Per-tenant learning only, by default. A tenant’s own history informs its own future routing, not another tenant’s.
  • Cross-tenant signal, where it exists at all, is metadata only — task-class → model/tool success priors, latency/cost distributions — aggregated. Never content, never memory, never raw context. The design states this as a deliberate anti-move, not an oversight: raw context or state is not sold or shared across tenants.
  • Opt-out-able and contractual. A tenant can decline participation in cross-tenant aggregation, on contractual terms, not just a UI toggle with nothing backing it.
  • Consent labels from the first write. Every receipt is designed to carry consent/training-eligibility labels starting at creation, so that data captured for debugging can never be retroactively reclassified as training data after the fact.
  • Policy updates are eval-gated and reversible. Any learned routing-policy update is designed to ship versioned, gated by bun run eval:agentic plus Langfuse contract evals, and to be reversible — policy learning gated like a code change, never applied silently.

See also

  • security-model.md — trust boundaries and credential handling.
  • receipts-and-audit.md — receipt contents and export paths.