# Spool target product contract

Target scope approved 16 October 2026. Spool is an application error tracking and crash reporting product for a US development team replacing Sentry, Datadog Error Tracking, Rollbar or Bugsnag (SmartBear). It serves the complete error-to-repair job: capture web/server/mobile failures, understand impact with source and execution context, route accountable team work, and establish whether a released repair held. PostHog Error Tracking, GlitchTip and Better Stack Error Tracking set the emerging-product comparison; the agent clauses go further through reproduction, tested source repair and live exposure-based verification.

This is the product being built toward. A clause is an obligation, not a claim that it ships today. The status table is based on current source/tests at `8f4d8fe3398de51567a0f0b04514c310e08547ba`, starting from the 16 October code audit at `9dc0f7ab3083035e56556fe6b773a443a843b3fc`. Changed code since the audit was re-read, including email destination management/testing, delivery issue links, setup/handoff guidance and the later workspace/packet read optimizations; the optimizations preserve the recorded feature boundaries. `done` means the stated bounded behavior has implementation and checked-in public-door tests; it does not turn local/provider-double results into hosted or commercial acceptance. `partial` names a working subset; `missing` has no implementation proof. ACCEPTANCE.md carries every clause's required journey and current status. Buyer review round 1 (17 October 2026) added SDK-006, TEAM-004, TEAM-005, SRC-003, CTX-003 and ALERT-005 and sharpened ALERT-002, ALERT-003, SRC-001, SDK-002 and CTX-002; their statuses were judged against the same source, and the later main commits through `f5acd4c` change hosting, demo and test-budget code only, not any clause status. Buyer review round 2 (17 October 2026) added SDK-007 (every Sentry envelope item type an unmodified SDK sends, and the clause each feeds) and API-002 (Sentry Web API and Terraform compatibility), and sharpened SDK-001, SDK-003, SDK-004, SDK-005, CTX-001, CTX-002, API-001, BILL-001, MIG-001, ALERT-002, FIND-001 and TEAM-002 against the same source. Buyer review round 3 (17 October 2026) added SDK-008 (the project identity policy for the Sentry `user` interface, one affected-user definition, user search keys and end-user erasure) and CTX-004 (performance issues from transactions and spans), and sharpened ERR-003, SDK-001, SDK-003, SDK-005, SDK-007, FIND-001, FIND-002, TEAM-003, ALERT-001, CTX-001 and MIG-001 against the same source. Buyer review round 4 (17 October 2026) added SDK-009 (release identity for unmodified Sentry SDKs: events with no release, automatic release creation, the per-project release ordering rule and plain Resolve) and sharpened ERR-001, ERR-006, SDK-001, SDK-005, SDK-007, TEAM-003, TEAM-004, REL-001, FIND-001, FIND-002, ALERT-001, CTX-001, GROUP-001, GOV-001, MIG-001 and BILL-001 against the same source. Buyer review round 5 (17 October 2026) added SDK-010 (what an admitted Sentry event keeps and shows: the whole exception chain and exception groups, full frames with SDK-sent source context and local variables, tags, contexts, request and message events) and SDK-011 (every content encoding Sentry SDKs use, published per-item and per-envelope size limits at least equal to Sentry's, and per-item refusal), and sharpened ERR-003, SDK-002, SDK-003, SDK-007, CTX-002, GROUP-001, FIND-001, FIND-002, ALERT-001, BILL-001 and MIG-001 against the same source. Buyer review round 6 (17 October 2026) added GOV-003 (published retention of at least 90 days with automatic expiry, retained volume sold per month in USD tiers with no retained-event count limit, the only named volume refusals and a published sustained intake rate) and SRC-004 (published source map and debug-file capacity with no artifact count limit, indexed maps and `sourceRoot`, per-file failure reasons, artifact retention and opt-in source fetching), and sharpened ERR-001, ERR-005, ERR-009, SDK-002, SDK-007, SDK-008, SRC-001, SRC-002, SRC-003, FIND-002, CTX-001, MIG-001, API-001, GOV-002, BILL-001 and BASE-DATA-003 against the same source; ERR-001, ERR-005, ERR-009 and BASE-DATA-003 moved from done to partial because their restated obligations exceeded today's 10,000-event, 120-a-minute and 20-map caps. For the 0.3.0 release record (18 October 2026) those four published clauses state the shipped limits, and GOV-003 and SRC-004 keep every larger obligation; with the restated text they are done.

The first buyer is a US application business with several engineers, a web/server/mobile application, GitHub or GitLab, a delivery tracker and existing error monitoring. Prices, budgets and invoices are USD; US hosting/timezone/privacy and sales-tax expectations apply. Incumbent names identify switching obligations, not claims about their current plans or feature availability.

## Product target

Spool is the application error tracking and crash reporting product a US development team buys instead of Sentry, Datadog Error Tracking, Rollbar or Bugsnag (SmartBear), and it goes beyond PostHog Error Tracking, GlitchTip and Better Stack Error Tracking. This contract describes that product.

| | |
| --- | --- |
| Category | Application error tracking and crash reporting |
| Incumbents a buyer switches from | Sentry, Datadog Error Tracking, Rollbar, Bugsnag (SmartBear) (up-and-comers it must also beat: PostHog Error Tracking, GlitchTip, Better Stack Error Tracking) |
| First buyer | A US application business with several engineers, a web/server/mobile application, GitHub or GitLab, a delivery tracker and existing error monitoring, moving its error capture, triage and repair work off its current incumbent; prices, budgets and invoices in USD |

## Product separation and honest boundaries

- Spool owns error/crash records, release health, error-linked traces/replay, triage and the repair-verification loop. Mobile SDKs, native symbolication, provider applications, billing and heavy sandbox workers are required engineering, not reasons to reduce this scope.
- General work planning remains [Vector](https://github.com/just-every/run-vector), and rotations, SMS/voice escalation and incident response remain [Oncall](https://github.com/just-every/run-oncall) or PagerDuty. TRACK-001 and ALERT-002 connect those products; Spool does not recreate their broader job.
- Whole-platform infrastructure APM, general log warehousing, product analytics and model-evaluation suites remain connected services such as Datadog, Splunk and PostHog. CTX-001/002 and AI-007 still require the diagnosis context the error buyer needs; these boundaries do not exclude it.
- Scheduled-job and heartbeat monitoring is [Uptime](https://github.com/just-every/run-uptime)'s product. SDK-001 accepts Sentry SDK check-in envelopes (`capture_checkin`, `@monitor`, `Sentry.withMonitor`) at the DSN and forwards them to the buyer's connected Uptime heartbeat, linking the job's errors to it; without a connection they are counted as unrouted in capability diagnostics, never silently dropped.
- Cloudflare is the bounded intake/control plane, not a requirement to run native symbolication, replay processing or persistent sandboxes inside request execution. Use declared isolated worker/storage providers with quotas, custody, portability and erasure coverage; SDK-011's attachment, minidump, profile and replay items and SRC-004's source maps and debug files stream there rather than being held in request memory, and the request-size limits SDK-011 publishes still bind. SDK-002, SDK-011, GOV-002 and AI-002 retain the full obligation.
- Production deployment remains the buyer's CI/CD action after review. Telemetry and model suggestions authorize nothing. AI-003 may write only an approved branch/PR; it never auto-merges or deploys. Replay/raw requests/identity are opt-in with preview and redaction; no blanket claim of perfect secret removal or legal compliance is made. Health/payment data requires buyer-approved agreements and controls before capture, rather than an implicit assurance.
- Local deletion cannot remove already downloaded archives, delivered messages or remote-provider copies; show the limits and partner deletion path. BASE-DATA-003 defines the exact offline-journal and backup limits. No unlimited-ingest or complete-capture claim is permitted; GOV-003 and SRC-004 publish the limited figures a buyer buys (retention, retained volume, sustained intake rate and artifact capacity), and no lower undisclosed cap may stand in for them.

## Named clauses

Existing ERR-001 through ERR-010 and BASE-DATA-003 identities remain stable. Expanded category clauses supplement their bounded guarantees; they do not turn the current single-owner implementation into a scope boundary.

### Evidence core

- **ERR-001 — Bounded admission.** Native version-1 JavaScript, Python and Go exception envelopes require active exact-project ingest credentials, registered release/build identity and bounded fields. Invalid, oversized, volume-limited and conflicting retries have explicit outcomes. Each project admits at most 120 outcomes a minute (`minute_budget`) and retains at most 10,000 events (`retained_event_budget`); over-limit intake returns 429 with `Retry-After` and a counted outcome. GOV-003 carries the target that replaces both limits with its named volume reasons and published sustained rate. Artifact URLs in envelopes are never fetched. Supported Sentry SDK exception events need the same registered release/build; SDK-009 carries admitting them with no release or an unregistered one. The broader SDK target is SDK-001.

  **Acceptance evidence:** Send valid, invalid, oversize, foreign-project and conflicting-retry envelopes through public ingest; inspect the outcome and unchanged rows for refusals. Exceed the per-minute and retained-event limits and confirm the explicit 429, `Retry-After` and counted reason, with no partial rows.

- **ERR-002 — Atomic acceptance.** Accepted means a sanitized occurrence, its group aggregate and its local new/regression alert commit atomically. Retry identity is project plus event ID and SHA-256 of canonical original accepted JSON before scrubbing: identical retry never increments, changed raw content conflicts even if its sanitized result matches. Raw bytes are not retained. Group algorithm version and basis remain inspectable.

  **Acceptance evidence:** Ingest an event twice, then change its raw secret under the same ID; inspect one occurrence and aggregate increment, one local alert and an explicit conflict without partial rows.

- **ERR-003 — Scrub before storage.** Apply the native allowlist, bounded text, query/fragment removal and sensitive-text scrubbing before persistence. Native envelopes contain no headers, cookies, request bodies or personal user identity; the `user` interface that unmodified Sentry SDKs send is governed by SDK-008's project identity policy instead. An optional random installation identifier supports bounded exposure measurement; it is never a name or address. Telemetry, source and notes are untrusted data and cannot authorize execution. The event fields SDK-010 keeps from unmodified Sentry SDKs (frame local variables, SDK-sent source context lines, exception values, tags, contexts, request, extra and thread stacks) pass through these scrubbers and SDK-003's server-side rules before storage, and default secret names are masked inside frame variables. Default protection persists when SDK-003 and CTX-002 introduce opt-in context; those captures require their own field policies and previews. Redaction does not promise removal of arbitrary semantic personal data.

  **Acceptance evidence:** Ingest privacy canaries and inspect stored events, issue views, packets, archives and delivery content; common secrets are removed while ordinary diagnostic content survives.

- **ERR-004 — Separated authority.** Every private API/document checks the current owner session or its exact ingest, deploy or read-only project grant itself. Cookie writes enforce same origin. Revocation and identity fencing take effect on the next request; confidential maps are never public. Ingest cannot read, deploy can only register releases/upload artifacts, and read-only agent grants cannot mutate. TEAM-001 extends this model to collaborative membership without broadening existing credentials.

  **Acceptance evidence:** Call each private transport anonymously and with wrong-scope, foreign-project, revoked and fenced grants; denied writes change no state, and direct map URLs reveal no private bytes.

- **ERR-005 — Source map identity.** Private flat Source Map V3 lookup matches project, release, build, debug ID and SHA-256 before presenting original coordinates. Missing, invalid or mismatched maps retain generated frames and a reason. Late uploads never rewrite immutable admission snapshots; current lookup is labelled separately. Lines are one-based, columns zero-based. A project stores at most 20 flat maps of 256 KiB; indexed (`sections`) maps are refused with a reason and `sourceRoot` is not applied. SRC-004 carries indexed maps, `sourceRoot`, published capacity and opt-in source fetching. Spool never fetches a remote URL.

  **Acceptance evidence:** Capture a minified second-origin exception before and after uploading the matching map; compare original coordinates, immutable admission diagnostics, mismatch refusal and confidential source exclusion from packets. Upload an indexed (`sections`) map and a 21st map in the same project and confirm each is refused with its reason.

- **ERR-006 — Audited triage and regression.** Revision-checked open, acknowledged, resolved and suppressed changes are audited. An issue resolved in its current release reopens as a regression on a recurrence in a later release; delayed older-release events cannot invent one. For registered releases the order is the registered sequence, never guessed semver; releases auto-created from Sentry SDK events are ordered by the project's stated rule in SDK-009. TEAM-003 adds resolve in the next or a named release, TEAM-004 resolution from a commit's release, and SDK-009 plain Resolve, which regresses on the next accepted event in any release. Recognized JavaScript bundle hash suffixes do not split grouping; native anchors and reported frames stay immutable. Corrections retain exact occurrence provenance and reversible fenced apply/undo.

  **Acceptance evidence:** Resolve a captured issue, admit its fingerprint in a newer and then older release, and inspect regression identity. Preview/apply/undo split and merge through owner HTTP/UI, reject stale revisions and inspect original membership.

- **ERR-007 — Sanitized evidence packet.** A compatible v1 packet exposes issue/lifecycle/grouping, retained release sequence and per-event release/build/environment, generated/original coordinates, immutable diagnostics beside current lookup, links and coded coverage gaps. UTF-8 JSON is bounded to 256 KiB and ten events, removing oldest events first and naming included/available counts even if none fit. It excludes source snippets, sourcesContent and artifact bytes. It carries the latest computed repair observation and newest three owner-recorded repairs, with omitted history named. CLI and stdio MCP keep exact-project read authority and untrusted/no-execution labels; they cause no model egress.

  **Acceptance evidence:** Read the same packet through HTTP, CLI and official MCP transport at size/retention/import/loss boundaries; compare projections and verify no secrets, source bytes, writes or cross-project reads.

- **ERR-008 — Loss counters and repair observation.** Expose windowed accepted/rejected/budget outcomes and unknown pre-transport loss. Only the owner records revision-checked repair links, scrubbed notes, checks, approval/merge/deploy facts and an exercised-flow record. Links are HTTPS origin/path text and never fetched. Manual repair writes send no notifications or external work. Compute recur/reasoned inconclusive/no-recurrence-after-exercise from stored release sequence, maps, events, exercise and loss; list older-release events separately. Missing exercise, capture, map, exposure or retention yields inconclusive. Never label a quiet feed fixed. AI-004 adds rate-based live proof.

  *Status:* done

  **Acceptance evidence:** Record a repair through owner HTTP, deny every project-grant write, then exercise deployed/no-events, healthy exercise, older-release event, loss and recurrence cases; inspect packet verdicts and absence of side effects from manual records.

- **ERR-009 — Archive, restore and retention.** Versioned private archives carry stored source bytes, checksums, migration identity, triage, maps, repairs/checks, operator corrections, reproduction/fix/exposure history and erasure journal. Base owns staging, JSONL integrity, empty-target guards and atomic inactive restore; HTTP/CLI and legacy v1/v2 decoding share semantic validation. Run tokens, step leases, owner sessions, project grants, alert destination records/credentials and dispatch queues never travel; retained alert rules restore disabled and delivery history is historical. Older archives restore newly introduced collections empty under versioned rules. An archive holds at most 10,000 events and 20 artifacts per project and refuses more with `Archive event budget` or `Archive artifact budget`; GOV-003 carries streaming archives of any retained volume. Manual cutoff retention removes private derived content, keeps repair snapshots with coverage gaps and exposes the deduplication horizon. Imported history creates no active grant, dispatch or current repair proof.

  **Acceptance evidence:** Export a populated installation via browser and rer, restore into an independent empty destination, compare retained records and inspect inert work. Refuse tampered, conflicting and nonempty-target imports before mutation; retain repair gaps after cutoff.

- **ERR-010 — Release gates.** A release must exercise real capture, symbolication, privacy, permissions, persistence and independent restore through the shipped app. Fresh setup has no demo rows. Private flows are accessible and responsive with explicit empty, failure and unavailable states. Source presence, local providers or a green build cannot substitute for hosted, mobile, integration or real-account acceptance. UX-001 and each connector clause specify the expanded product gates.

  **Acceptance evidence:** Run the named implemented journeys on the emitted Worker, inspect 1440/390 end states, then qualify each supported runtime/device and configured real provider with a buyer-controlled account.

- **OPS-001 — Base commands and native admission.** The public rer command contract uses declared Node/pnpm pins and one setup manifest for terminal/browser. Setup claims or preserves the owner and private settings, stops its temporary app and leaves empty product data; rer dev uses real assets/HMR and private local identity. Local dev grants never authorize deployed entry. Reset is confirmed/contained and preserves source, exports and deployment receipts. Export/import share Base integrity and inactive empty-target restore. Deployment dry-run makes no provider request. Managed signed first-owner admission precedes auth dispatch; editor config independently checks the owner and has short expiry/exact-origin CSP.

  **Acceptance evidence:** Use public rer install/setup/dev/export/import/reset and deployment dry-run in a fresh checkout and independent destination; preserve existing setup, reject unsafe answer paths and reused managed claims, and deny anonymous/project-grant editor access.

- **BASE-DATA-003 — Project and installation-owner erasure.** The owner has count-only preview, typed confirmation bound to the exact current plan, resumable destructive steps and a completion receipt with per-table counts/times and no personal values. Project-root cascade, anonymized permanent audit, portability tombstones, source-import custody withdrawal, stale-write fences are preserved, and project erasure completes within five minutes for a project at today's 10,000-event admission limit. GOV-003 carries erasure within 15 minutes for 150,000 retained events. Owner self-erasure first transfers authority to a verified successor; it refuses unsupported/ambiguous attribution rather than damaging other people's data. All current declared tables and outbox copies participate; new capture stores must extend this declaration before release.

  **Acceptance evidence:** Run public setup/dev/HTTP/export/import erasure journeys with canaries in every declared store, count-only preview/receipt, wrong-scope/origin/stale denials, a 10,000-event project erased within 300 seconds, successor handoff, resumed partial erasure and same/fresh-destination old-archive fences.

## Target clauses

### Teams and triage

- **TEAM-001 — Organizations, members and project permissions.** A US development team creates an organization, invites multiple simultaneous members, organizes projects by team, and grants owner/admin, billing (usage, budgets and invoices only), project maintainer, member and read-only access with least privilege. Removal revokes sessions, keys and linked service authority; service accounts have explicit project scopes. Private data, search, exports and integrations obey membership on every transport. Owner transfer remains available without restricting the organization to one active contributor.

  **Acceptance evidence:** Invite two teammates and an external read-only collaborator; grant access to disjoint projects, triage concurrently, remove a member and verify immediate denial across UI, HTTP, SDK-management, CLI and exports.

- **TEAM-002 — Accountable assignment and ownership rules.** Assign issues to real members/teams, import GitHub/GitLab CODEOWNERS at the exact linked revision, and define service/path/environment fallback ownership. Preview precedence and unmatched cases, route notifications to the assigned team, and notify assignees of due or unowned work. Work is due when an assigned issue stays unacknowledged past the project's acknowledgement window (set by a maintainer, default 24 hours), when a snooze, suppression or resolve-in-next-release deadline expires, or when a linked delivery ticket's due date passes; unowned work is an issue with no assignee after the project's unowned window. A due notice goes to the assignee, then to the owning team and the ALERT-004 alternate if still unacknowledged. Assignment history survives member departure without conferring access. Rules can be tested and disabled by a maintainer.

  **Acceptance evidence:** Connect a repository with overlapping ownership rules, capture matched and unmatched errors, confirm accountable assignment and destination, let an assigned issue pass its acknowledgement window and an unassigned one pass its unowned window and confirm one due notice to the assignee and one unowned notice to the team, then remove the owner and inspect reassignment/history without leaked access.

- **TEAM-003 — Collaborative lifecycle and bulk triage.** Teams comment, mention, subscribe (delivery per TEAM-005) and inspect an issue activity timeline; bulk assign, acknowledge, resolve, merge or suppress a selected search result with a preview. Resolution may be plain (Resolve with no release: the next accepted event in any release, or with no release, regresses it, as in Sentry, under SDK-009), in the current release, in the next release, in a named release, or from a commit/PR citation (TEAM-004). The recurrence policy is stated in project settings, with this default: after a release resolution, a same-release repeat does not reopen the issue but is counted on it as "recurred in resolved release" and shown on its timeline; a maintainer can set the project to reopen same-release repeats instead, previewed and audited. Old-release repeats remain separately visible and cannot fabricate newer-release regression; an event with no release after a release resolution follows SDK-009. Suppression (archive) supports reason, forever, expiry/count/affected-user threshold, until escalating (unarchive when volume exceeds the issue's forecast from its own history, with the forecast shown) and snooze, with due follow-up and an audit trail. Every issue has a priority (high, medium or low), set automatically when it opens from its level and history (fatal and error high, warning medium, info and debug low; escalation or regression raises it) and editable by a member. A manual edit sticks against later automatic changes, appears with its actor in the timeline and is audited, and FIND-001 filters, ALERT-001 conditions and TEAM-005 settings can use it. Concurrent edits refuse stale decisions.

  *Status:* partial

  **Acceptance evidence:** Two members discuss and bulk-triage searched issues, resolve for the next release, exercise same/new/old-release recurrence (a same-release repeat after a release resolution is counted as recurred in resolved release without reopening, and reopens once the project policy is switched), plain-resolve an issue and confirm the next same-release event regresses it, expire a suppression and push an archived-until-escalating issue past its shown forecast; confirm one accountable follow-up and stale-write refusal. Confirm a new fatal error opens as high priority and a warning as medium, set one to low by hand and confirm a later escalation leaves the manual priority with its actor shown.

- **TEAM-004 — Issue short IDs and resolution from commits and pull requests.** Every issue has a stable human-readable short ID per project (for example `CHECKOUT-1A3`), shown in the UI, alerts, packets, exports and search; it survives merge/split as an alias. A commit message or a GitHub PR / GitLab MR title or description containing `Fixes`, `Resolves` or `Closes` followed by a short ID (case-insensitive) links that commit or PR to the issue as soon as SRC-002 receives it, visible on the issue timeline. When a release includes the commit, or the merged PR's merge commit, whether the release was registered through REL-001 or SRC-003, or auto-created under SDK-009 and given commits by a later registration or by SDK-009's commit-SHA association, the issue resolves in that release with commit, PR and author recorded. A later occurrence in a newer release reopens it as a regression under ERR-006; an occurrence in an older release cannot. Citations of unknown, other-project or inaccessible short IDs change nothing and appear in the repository link log with the reason.

  **Acceptance evidence:** Push a commit and merge a PR that each cite an issue's short ID, register a release containing both through the release API and through `sentry-cli releases set-commits`, and confirm both issues show the link immediately and resolve in that release. Capture each fingerprint in a newer release (regression) and an older one (no regression), and cite another project's short ID (no change, logged). Repeat with an auto-created release named by the merge commit's full SHA and no CI registration, and confirm the issue resolves in it.

- **TEAM-005 — Per-member notification settings and issue subscriptions.** Each member chooses, per project and per notification type, what reaches them and on which personal channel (email, Slack direct message, Microsoft Teams chat). The types are workflow (resolution, regression, assignment, comment, mention), issue alert, deploy and weekly report. A member is subscribed to an issue by being assigned directly or through a team, commenting, being mentioned, bookmarking it or pressing subscribe; the issue shows why, and an explicit unsubscribe overrides automatic subscription until the member subscribes again. Workflow notifications go to subscribers other than the actor. Deploy notifications go to members whose commits are in the release. The weekly report is opt-in per member. A member can opt out of a project's personal alert email without disabling the rule for others; only an admin can make a rule's delivery mandatory, and the member's settings show it. Every message carries a settings and one-click unsubscribe link that applies before the next message.

  **Acceptance evidence:** Two members save different per-project and per-type settings. Trigger an assignment, comment, mention, resolution, regression, issue alert, a deploy containing one member's commit and the weekly report, and confirm each member receives exactly the messages and channels they chose. Unsubscribe through a message link and confirm the next qualifying event sends nothing to that member while the other is unaffected.

### Capture and release health

- **SDK-001 — Multi-language SDKs and DSN-compatible intake.** Developers capture automatic unhandled and manual exceptions using maintained browser JavaScript, Node.js, Python, Go, Java/JVM, Ruby, PHP and .NET integrations, plus iOS, Android, React Native, Flutter, Electron, Unity, Rust, Elixir and Kotlin Multiplatform. A guided setup command, comparable to the Sentry setup wizard, detects the framework, installs the SDK, writes DSN/release/source-map configuration as a reviewable diff and sends a test event. Publish framework setup for React/Next.js, Express, Django/FastAPI, Spring, Rails, Laravel and ASP.NET Core. Existing standard Sentry SDK applications repoint their DSN without rewriting capture code: every envelope item they send, not only errors, is accepted and routed to the clause that uses it as listed in SDK-007 (sessions, transactions and spans, profiles, replays, feedback, attachments, client reports, logs and metrics), and events with no `release`, an SDK-inferred release or a release CI never registered are accepted under SDK-009; their cron check-in envelopes route to Uptime as stated in the boundaries. SDK-007's versioned capability diagnostics identify each unsupported item type and loss until full support lands; unsupported behavior stays an engineering gap. Browser capture routed around ad-blockers keeps working at cutover: Sentry's Next.js `tunnelRoute` rewrite only targets Sentry's hosted ingest, so Spool ships a drop-in Next.js tunnel route handler and a generic server tunnel handler (Node and Python) for the SDK's standard `tunnel` option, which check the envelope's DSN header against the project and forward the client address for SDK-008, and it serves project ingest on a buyer-owned first-party subdomain (CNAME with managed TLS) that the DSN names. Setup names the `tunnelRoute` limitation and the replacement. Sites that embed Sentry's JavaScript Loader Script (`js.sentry-cdn.com/<key>.min.js`) get a Spool loader script per DSN client key, served from Spool or the first-party subdomain. Its SDK version and its tracing, replay and feedback options are set in the key's settings, and lazy loading and `Sentry.onLoad`/`sentryOnLoad` behave as before; MIG-001 lists each Sentry loader key with its settings and the replacement script tag.

  **Acceptance evidence:** For every named runtime, execute an unmodified Sentry SDK with only its DSN changed plus Spool setup, with its default integrations and the sessions, tracing, logs, replay (browser/mobile), feedback widget and attachment options the Sentry setup wizard enables; capture handled/unhandled failures and flush/restart; reconcile persisted issue, release, session, trace, replay, feedback, attachment and discard outcomes per SDK-007 through the public UI/API. Run the guided setup on a fresh Next.js and Django app and send one check-in with and without a connected Uptime heartbeat. With a standard ad-blocker list enabled in the browser, deploy a Next.js app first with Sentry's `tunnelRoute` (setup shows the named limitation), then with the Spool tunnel handler, then with the first-party ingest subdomain, and confirm every browser error arrives in the last two and reconciles in the MIG-001 dual run. Replace a site's Sentry loader script tag with the Spool loader configured with the same key settings and confirm lazy loading, `Sentry.onLoad` and an error capture.

- **SDK-002 — Native crashes and symbolication.** Collect iOS/macOS native crashes, Android crashes and ANRs, hybrid React Native/Flutter failures, Electron/desktop native crashes as Crashpad/Breakpad minidumps, and their device/OS/app version context under privacy controls. CI uploads dSYM, ELF/native debug symbols, ProGuard/R8 mappings and Flutter split-debug information, at their real build sizes under SRC-004's published debug-file capacity (up to 2 GiB per file, no file count limit), through Spool's CLI or, unmodified, the Sentry Android Gradle plugin, the Xcode build-phase upload script, `fastlane-plugin-sentry`, `@sentry/react-native` and `sentry_dart_plugin` paths listed in SRC-003. Match UUID/build/architecture before symbolication, show original frames and inline context with explicit missing/mismatch diagnostics, and preserve offline crash delivery across app restart. ANR and app-hang events keep every thread's stack with its crashed/current flags and state (SDK-010). Minidumps, Apple crash reports, screenshots and view hierarchies are accepted up to SDK-011's published limits, never refused under a smaller request bound. Heavy symbolication runs in isolated bounded workers.

  **Acceptance evidence:** Build signed test iOS/Android and React Native/Flutter apps, crash and hang on real devices, relaunch offline/online, upload matching/mismatched symbols from CI through the unmodified Gradle plugin, Xcode build phase, Fastlane action and Dart plugin, and inspect one deobfuscated issue without duplicate sessions; crash an Electron app and symbolicate its minidump. Upload a 40 MiB dSYM and the next release's ProGuard mapping and confirm both symbolicate.

- **SDK-003 — Automatic breadcrumbs and private context.** Opt into bounded console, network, DOM interaction, navigation and server-operation breadcrumbs with category switches, sampling, per-field allow/deny rules and client-side before-send hooks. Default SDK capture excludes secrets and request bodies; user identity follows SDK-008's per-field project identity policy, and adding approved request properties requires explicit project policy, preview and erasure coverage. Capture ordering and truncation are visible so investigation never implies a complete user history. Server-side rules also run on every item SDK-007 accepts from unmodified Sentry SDKs (message and `logentry` params, exception values, stack frame local variables and SDK-sent source context lines kept under SDK-010, thread stacks, tags, extra, contexts, breadcrumbs, request, user fields kept under SDK-008, span and log attributes, and attachment metadata) before storage, with no SDK redeploy. A rule's field selector can address any of these, including one frame variable by name or every frame variable (Sentry's `$frame.vars.<name>` and `$frame.vars.**`), a request header, an `extra` key, a context key, the exception value and the log entry, and MIG-001 imports Sentry advanced scrubbing rules with those selectors unchanged: a maintainer adds organization or project additional sensitive fields and safe fields, and custom rules that apply a built-in pattern (credit card, IP address, email, password, auth token, US Social Security number) or a custom regex to a field selector with remove, mask, hash or replace. Each rule is previewed against retained events, audited and applies to later intake only; organization rules apply to every project and cannot be weakened by a project.

  **Acceptance evidence:** Toggle each instrumentation category in browser/server setup, trigger a failure with secret-bearing URLs/forms, and compare the occurrence timeline, before-send filtering, truncation and stored/forwarded privacy projection. Add an organization sensitive field, a project safe field and a custom regex mask rule in the app, preview them, then send an unmodified Sentry SDK event, span and log carrying canaries in tags, extra, contexts and message text, and confirm the canaries are removed or masked in storage, issue views, packets and exports while safe fields survive. From an unmodified Python SDK with default `include_local_variables`, raise an error whose frame locals hold `password`, `api_key` and an ordinary value, add a `$frame.vars.order_note` mask rule, and confirm `password` and `api_key` are masked by default, `order_note` is masked by the rule, and the ordinary local survives in issue detail.

- **SDK-004 — User feedback attached to failures.** An accessible, localizable feedback widget lets the affected user submit a description and optional contact/screenshot against an event or independently. It asks consent, strips sensitive metadata, supports attachment review and abuse controls, and connects duplicate reports to the issue without publishing identity. Submissions from the unmodified Sentry feedback widget, `captureFeedback`, the crash-report modal and legacy `user_report` items arrive through SDK-007 and get the same attachment, consent, abuse and erasure handling. The assigned team can reply through approved email with delivery state; withdrawal/erasure removes feedback and attachments across exports.

  *Status:* partial

  **Acceptance evidence:** An end user reports a browser/mobile failure with optional screenshot/contact, a teammate triages and replies, an anonymous abuse attempt is bounded, and withdrawal removes the canaries from issue, storage and export. Submit the same through an unmodified Sentry feedback widget, `captureFeedback` and a legacy `user_report` and confirm each attaches to its issue; open Spool's widget in Spanish and confirm its visible labels and accessible names are Spanish.

- **SDK-005 — Crash-free sessions and release health.** Ingest sessions from unmodified Sentry SDK `session` and `sessions` items (SDK-007) and from Spool SDKs, including sessions whose release CI never registered, which auto-create it under SDK-009, honouring each SDK's session mode (per page load or navigation in the browser, per app start and foreground on mobile, request aggregates on servers) and status (ok, exited, errored, crashed, abnormal). Publish sessions, crash-free users/sessions (users counted from each session's `did` and the event user key under SDK-008's one definition), affected users, version adoption, crash/ANR rates and trends by environment, app version, device and OS, with explicit exposure denominators, sampling and capture loss. Define session boundaries; session identity follows SDK-008's project policy. Compare rollouts and rollbacks against comparable cohorts: a partly adopted release is compared with the previous release over the same window, environment, device and OS mix, with its adoption share and confidence shown, and a rollback shows the restored release's rates beside the rolled-back one's; healthy sessions must be observed rather than inferred from absent exceptions. Export reports with their denominator and uncertainty.

  **Acceptance evidence:** Run known healthy/crashing sessions across two app versions and devices, through unmodified Sentry browser, mobile and server SDKs with default release health and through Spool SDKs, vary sampling and offline delivery, then reconcile adoption and crash-free session and user rates against the sessions each SDK submitted and visible gaps. Roll a new version out to about 20% of devices with a higher crash rate and confirm the comparison shows both releases' crash-free session and user rates over the same window with adoption share and confidence; then roll back and confirm the rolled-back release stops gaining sessions and the comparison shows the restored release's rates beside it.

- **SDK-006 — Ingest-time inbound filters and allowed domains.** A project maintainer turns on server-side inbound filters in the app, with no SDK redeploy: browser-extension errors, legacy browsers (by named browser and version), localhost and loopback, known web crawlers (maintained user-agent list), React hydration errors, ChunkLoadError and failed dynamic imports, client IP addresses and CIDR ranges, error-message patterns, specific releases and environments, and allowed `Origin`/`Referer` domains (exact and `*.domain` wildcard) for browser DSNs. Filters run at intake, before grouping, alert evaluation and billable usage: a matching event creates no issue, occurrence, alert or billable unit and is counted per filter as `filtered` in BILL-001 usage. Each filter shows its recent match count and a preview against retained events before it is enabled; changes are audited and apply to later intake only. A browser event whose `Origin` or `Referer` is not an allowed domain is refused with an explicit status and no CORS grant, protecting a public browser DSN from abuse. New projects name their default-on filters at creation, and each can be turned off.

  **Acceptance evidence:** Enable each named filter in the app, send matching and non-matching events through an unmodified Sentry SDK and native ingest, and confirm the matching ones create no issue, alert or billable usage and appear per filter as filtered in usage while the others are accepted. Send from a non-allowed `Origin` and `Referer` and confirm refusal without a CORS grant; disable a filter and confirm later matching events are accepted.

- **SDK-007 — Sentry envelope item coverage from unmodified SDKs.** When a team changes only the DSN, Spool accepts every item its unmodified Sentry SDKs send (browser/Next.js, Node, Python, iOS, Android, React Native and the other SDK-001 runtimes) at the `/envelope/` and legacy `/store/` endpoints and at Sentry's dedicated `/minidump/`, `/unreal/` and `/security/` endpoints, and routes each item type to the clause that uses it:
  - `event` (errors and default events, including ANR and app-hang `threads`) → ERR-002 atomic acceptance, keeping and showing the whole event as SDK-010 lists it (every exception in the chain and exception groups, full frames with source context and local variables, tags, contexts, request, extra and message events), under SDK-009's release rules (accepted with no release, or with an unregistered release that is created automatically; ERR-001's registration precondition does not apply unless the project chose registered sequence only), SDK-001 and SDK-002; the event's `user` interface and the identity data `sendDefaultPii` adds pass through SDK-008's identity policy before storage.
  - `session` (per-session updates) and `sessions` (server-mode aggregates) → SDK-005 crash-free session and user rates, adoption and release health, with each session's `did` under SDK-008; an unregistered session release is created automatically under SDK-009, and a session with no release, which Sentry also refuses, is counted as invalid with that reason.
  - `transaction` and `span` (including standalone and v2 span items), with the envelope's dynamic sampling context → CTX-001: the trace waterfall beside every error that shares its `trace_id`, with sampled-out spans labelled.
  - `profile` and `profile_chunk` → CTX-001: the profile of the transaction or span that contains the error.
  - `replay_event`, `replay_recording` and `replay_video` → CTX-002, under its consent, masking, sampling, retention, role-scoped playback and erasure rules; server-side masking is reapplied to recording payloads.
  - `feedback` (Sentry feedback widget, `captureFeedback`, crash-report modal) and legacy `user_report` → SDK-004, attached to the associated event and issue or kept standalone.
  - `attachment` (`event.attachment` files including screenshots, `event.view_hierarchy`, `event.minidump`, `event.applecrashreport`, and Unreal context and log types) → API-001 storage and role-checked viewing, and SDK-002 native crash processing, under ERR-003/SDK-003 scrubbing and SDK-011's published per-item limits.
  - `client_report` (client-side discards by reason and data category) → BILL-001 client-discarded volume and the ERR-008/SDK-005 loss inputs.
  - `log` (Sentry Logs, `Sentry.logger` and console capture) → CTX-001 structured logs correlated to the error by trace and span.
  - `trace_metric` (Sentry's trace-connected metrics) → CTX-001 metric points on the trace beside the error.
  - `check_in` → Uptime, as stated in the boundaries.
  - `/security/` CSP violation reports → issues grouped by directive and blocked URI, under SDK-006 filters. `/minidump/` and `/unreal/` crash uploads → SDK-002 crash events.

  *Status:* partial

  Diagnosed-unsupported: `statsd` and `metric_meta` (Sentry's superseded statsd-format metrics), the item types Sentry reserves and forbids SDKs to write in envelopes (`security`, `unreal_report`, `form_data`), and any unknown type. Neither these nor an accepted type whose target clause has not shipped are ever silently dropped: each is counted per item type and reason in project outcomes and in the capability diagnostics shown in setup and usage, with the SDK name and version that sent it. Every endpoint accepts the content encodings and item sizes SDK-011 publishes, and an item over its limit is refused alone under SDK-011 while the rest of the envelope is accepted. Every accepted type obeys SDK-006 filters and BILL-001 budgets per data category; only GOV-003's named volume reasons rate-limit an item, and limits return `X-Sentry-Rate-Limits` naming the limited category, so the SDK backs off only that category. MIG-001's dual run compares counts per item type.

  **Acceptance evidence:** For the browser (Next.js), Node, Python, iOS, Android and React Native Sentry SDKs, change only the DSN and enable sessions, tracing, profiling, logs, metrics, replay (browser and mobile), the feedback widget and screenshot and view-hierarchy attachments, then trigger errors. Confirm that SDK-005 crash-free session and user rates reconcile with the sessions each SDK sent; that the error's issue detail shows its Sentry-SDK trace waterfall, profile, logs and replay at the error time; that widget feedback attaches to the issue; that the screenshot opens for a permitted member and is refused for an unpermitted one; and that client-report discards appear in BILL-001 usage. Send a CSP report and an Electron minidump and confirm each becomes an issue. Send a `statsd` item and an unknown item type and confirm each appears in capability diagnostics with its count and no silent drop. Exhaust a replay-category budget and confirm `X-Sentry-Rate-Limits` names only that category while errors are still accepted. Run the whole journey with each SDK's default transport compression (including the Python SDK with `brotli` installed) and confirm that no envelope is refused for its encoding or size (SDK-011) and that each error's issue detail shows its full exception chain, tags and contexts (SDK-010).

- **SDK-008 — Sentry user identity policy, affected users and end-user erasure.** Each project has an identity policy for the `user` interface that unmodified Sentry SDKs send through `Sentry.setUser` and framework integrations: `id`, `email`, `username`, `ip_address` (with `{{auto}}` resolved at intake to the connecting client address, or to the client address forwarded by an SDK-001 tunnel the project trusts, never from an untrusted forwarded header), `name`, `geo`, `segment` and custom user data, each session's `did`, and the identity data `sendDefaultPii` adds (framework-populated user fields and the request's client address). For each field a maintainer chooses keep as sent, hash (a keyed per-project hash, so equal values still count and match as one user and the raw value is never stored; the key travels only inside the private ERR-009 archive) or drop (never stored, and `{{auto}}` never resolved). Project creation shows the policy and states its default: keep every field as sent, as Sentry does, except where an organization floor or a MIG-001-imported Sentry setting ("prevent storing of IP addresses", data scrubbing of user fields) requires hash or drop. Cookies, authorization headers and request bodies that `sendDefaultPii` adds stay under ERR-003 and SDK-003 scrubbing; keep never retains them. Every policy change is previewed against retained events (events, users and saved searches affected), audited under AUDIT-001 and applies to later intake; retained values can be converted only toward a stricter choice (keep to hash to drop), with a preview. Organization policy sets a floor that projects cannot weaken, and SDK-003 server-side rules run on kept values after the policy.

  An event's user key is the first present of `user.id`, `user.email`, `user.username` and `user.ip_address` after the policy (a hashed value counts as itself; a dropped field is skipped), the same precedence as Sentry's `user` tag so MIG-001 counts reconcile; Spool SDK events use their random installation identifier (ERR-003). Every affected-user figure uses this one definition: the issue stream's Users column and sort, issue detail, FIND-002 reports and widgets, SDK-005 crash-free users, ALERT-001 user conditions, AI-004 cohorts and MIG-001 comparisons. Each figure also shows how many events had no user key; when no event in scope has one, it shows unavailable with the reason (the policy drops identity, or the SDK sent none), never 0.

  FIND-001 accepts `user.id`, `user.email`, `user.username` and `user.ip` (exact values, and CIDR ranges for `user.ip`) as search keys and facets with counts. A hashed field matches only its hashed token, so a raw-value search finds nothing; a dropped field is unsearchable and the search says so. Raw identity values are visible, searchable and exportable only to roles the project grants identity access (owner, admin, maintainer and member by default); read-only collaborators and service grants see counts and hashed tokens unless granted. ERR-007 packets, AI-001 egress, alert and chat deliveries and ALERT-005 unfurls carry counts only, never raw identity.

  End-user erasure: a maintainer enters one user's id, email, username or IP address, and a count-only preview lists the matching events, sessions, feedback, attachments, replays and search entries (a hashed field is matched by hashing the supplied value). On typed confirmation Spool removes that user's identity values from events, sessions, search indexes and facets, SDK-004 feedback contact, CTX-002 replays and attachment metadata, or deletes the user's events when the maintainer chooses. Later exports and archives omit the values, and the receipt has counts and times only. AUDIT-001 records the action without the value. The erasure joins the BASE-DATA-003 journal and restore fence, so a restore reapplies it and an older archive that still holds the value is refused. It completes within 300 seconds in a project holding GOV-003's 150,000-event published volume.

  **Acceptance evidence:** With an unmodified Sentry browser SDK and Python SDK and only the DSN changed, send errors to one issue from three identified users (`setUser` with id, with email only and with username only) and from two anonymous visitors whose browser SDK sends `ip_address: "{{auto}}"`. Under keep: the issue stream's Users column and issue detail show 5 affected users, matching Sentry's count in the MIG-001 dual run, `user.email:<one of them>` finds exactly that user's issues, the user facet lists the five keys with counts, and a read-only collaborator sees the count but no values. Change the policy to hash with its preview and audit entry: counts stay 5, no raw email or IP exists in storage, issue views, packets, exports or the archive, and a raw email search finds nothing. Under drop: no identity is stored and the Users column shows unavailable with its reason, never 0. Erase one user by email: the count-only preview names the matching records, and afterwards the value is gone from search, facets, issue views, exports and the next archive, the receipt and audit carry no value, and restoring an older archive is refused.

- **SDK-009 — Release identity from unmodified Sentry SDKs: no release, automatic releases, ordering and plain Resolve.** In Sentry a `release` is optional and needs no registration first, so Spool accepts Sentry SDK events and sessions with the release identity unmodified SDKs send, on every SDK-001 runtime, without a CI step:
  - **No release.** An event with no `release` is accepted, grouped and stored with release shown as "none". It counts in issue totals, the issue stream, FIND-001 search (`!has:release`), ALERT-001 conditions and FIND-002 dashboards. It is excluded only from release-scoped figures (release history, per-release counts, SDK-005 adoption and crash-free rates by release, REL-002 comparison), and each of those names how many events had no release.
  - **Automatic release creation.** The first event or session carrying a `release` (with or without `dist`) that the project has not seen creates that release, as Sentry does, whether the SDK took it from configuration, `SENTRY_RELEASE`, an inferred git commit SHA (Python, Node), the Apple default `CFBundleIdentifier@CFBundleShortVersionString+CFBundleVersion`, the Android, React Native or Flutter default `package@versionName+versionCode`, or a build plugin's injected value. An absent `dist` is its own identity and never means "latest". The release records its first-seen time, first environment and the SDK name and version that created it, and is labelled "auto-created" until CI registers commits, artifacts or a deploy for it. Later registration of the same name through REL-001, `sentry-cli releases new/set-commits/finalize/deploys` or a build plugin (SRC-003) enriches that release and removes the label; it never splits it or changes its first-seen time. A name Sentry's release naming rules refuse (empty, `.`, `..`, `latest`, containing a newline, tab, `/` or `\`, or over 200 characters) creates no release: the event is accepted with release "none" and an "invalid release name" processing note counted per reason. When the name is a full commit SHA that exists in a repository linked through SRC-002, the release is associated with that commit, so suspect commits and TEAM-004 resolution work without a CI step.
  - **Ordering rule.** Each project shows its release ordering rule in project settings; it decides resolve in the current or next release, TEAM-004 resolution, regression, `release:latest` and REL-002 comparison. The default matches Sentry: two releases that both parse as semantic versions (`package@1.2.3+45` or `1.2.3`) are ordered by version number (major, minor, patch, pre-release, then build number); any other pair is ordered by first-seen time, with registration counting as seen. A maintainer can instead choose "registered sequence only", the rule Spool's native intake uses: a Sentry SDK event or session with no release, or with an unregistered release or `dist`, is then refused with its named reason (`missing_release_register_required` or `unregistered_release_or_build`) and a pointer to the registration path, and each refusal is counted under that reason in project outcomes, BILL-001 usage and the MIG-001 dual run. A rule change is previewed (which resolved issues' regression boundaries move), audited and applies to later events.
  - **Plain Resolve.** Resolve with no release, in the issue view, bulk triage (TEAM-003) and chat actions (ALERT-005), regresses the issue on the next accepted event in any release or with no release, as in Sentry, and fires the ALERT-001 regression condition once. Resolve in the current, next or a named release keeps ERR-006's older-release protection under the project's ordering rule. An event with no release on an issue resolved in a release cannot be ordered, so it does not reopen the issue; it is counted on the issue as "seen with no release after resolve", shown on its timeline and offered as an ALERT-001 condition.
  - **Bounds.** Automatic creation obeys SDK-006 filters (including release patterns and allowed domains for browser DSNs) and BILL-001 key rate limits. A maintainer can limit automatic creation to release names matching a pattern; an event whose release falls outside it is accepted with release "none" and a counted reason, never refused.

  **Acceptance evidence:** With only the DSN changed, send errors from an unmodified Python SDK with no release, from a Python SDK with an auto-detected git-SHA release, and from iOS and Android SDKs with their default `bundle@version+build` release, none of them registered. Confirm all are accepted and create issues and alerts, the two releases appear as auto-created with first-seen time and creating SDK, and the release-less events count in issue totals and search and are named as "no release" in release-scoped views. Register one of those releases afterwards through `sentry-cli releases new` and `set-commits` and confirm it enriches the same release. Plain-resolve one issue and send the same error in the same release: it is Regressed and one regression alert fires. Resolve another issue in the next release across two auto-created semver releases (`app@1.2.0`, `app@1.10.0`) and two SHA releases: regression follows the stated order, an older-release event does not regress it, and a release-less event is counted on the issue without reopening it. Send an event with an invalid release name and confirm it is accepted as release "none" with its counted note. Switch the project to "registered sequence only" with its preview and confirm an unregistered release and a missing release are each refused with the named reason and counted in project outcomes, usage and the MIG-001 dual run.

- **SDK-010 — Sentry event fidelity: exception chains, frames, local variables, tags, contexts and request.** Every `event` that SDK-007 admits from an unmodified Sentry SDK (through `/envelope/` or `/store/`) keeps the data below. Issue detail, FIND-001 search, GROUP-001 grouping, SDK-003 scrubbing and API-001 export use it. Nothing in this list is discarded at conversion. Any trimming is named on the event and counted in SDK-007 capability diagnostics, never silent. An event is never refused because it has an exception chain, an exception group, frame variables, many frames or no stack trace.
  - **Exception chain.** Every `exception.values` entry is kept in SDK order, each with its `type`, `value`, `module`, its own stack trace and its `mechanism` (`type`, `handled`, `synthetic`, `source`, `is_exception_group`, `exception_id`, `parent_id`). This covers:
    - JavaScript and Next.js `error.cause` chains from the default LinkedErrors integration (five levels);
    - Python `raise … from` and implicit `__context__` chains;
    - Python 3.11+ `ExceptionGroup`/`BaseExceptionGroup`, including `asyncio.TaskGroup` failures, with every child;
    - Java/Kotlin `Caused by` and suppressed exceptions;
    - .NET `InnerException` and `AggregateException`;
    - Go wrapped errors.

    Exception groups are accepted on every runtime. Issue detail shows each exception with its own frames, marks the root cause, and shows exception-group children as a tree under "Related Exceptions", as Sentry does. The issue title follows Sentry's rule: the type and value of the exception Sentry would title the issue with. FIND-001 searches every exception in the chain (`error.type`, `error.value`, `error.mechanism`, `error.handled`, `error.unhandled`).
  - **Frames.** Each frame keeps `filename`, `abs_path`, `module`, `package`, `function`, `raw_function`, `symbol`, `lineno`, `colno`, `in_app`, `platform`, `pre_context`, `context_line`, `post_context`, `vars`, `instruction_addr`, `symbol_addr`, `image_addr`, `addr_mode` and `trust`, plus the event's `debug_meta` images for SDK-002 symbolication.
    - **Frame bound.** Spool keeps up to 1,000 frames per stack trace, above every SDK-001 runtime's default capture. Beyond that it removes middle frames, keeps the 500 innermost and 500 outermost, and shows "N frames omitted" in their place.
    - **Variable bound.** Frame variables are kept as sent, within SDK-011's 1 MiB event item limit, to 10 levels of nesting and 16 KiB per value. A deeper or longer value is cut with a visible mark.
    - **SDK trimming.** The SDK's own `_meta` trimming and filtering remarks are kept and shown.

    Issue detail shows each frame's SDK-sent source context lines (`pre_context`, `context_line`, `post_context`), labelled as sent by the SDK and kept distinct from ERR-005 mapped source and SRC-002 repository links. It also shows the frame's local variables, expandable by key. A frame whose variables set `__traceback_hide__` to true is hidden from the view and from grouping (GROUP-001). A raw stack trace view shows the whole chain as text in the runtime's own format (Python traceback, JavaScript `Error.stack`, Java `Caused by`), minified or original. It has one-click copy and shows only what the viewer's role and the scrubbers allow.
  - **Event data.** Spool keeps:
    - `level`, `logger`, `transaction`, `server_name`, `environment`, `dist`, `platform`, `modules` and `fingerprint`;
    - `sdk` (name, version, integrations and packages);
    - `message` and `logentry` (`message`, `params`, `formatted`);
    - `tags`, both SDK automatic tags and custom `setTag` values;
    - `contexts`: browser, os, device, runtime, app, gpu, culture, cloud_resource, trace, response, state, feature flags and custom contexts;
    - `request`: `url`, `method`, `query_string` after ERR-003 query scrubbing, and `headers`, `env`, `cookies` and `data` under SDK-003 rules and SDK-008;
    - `extra`;
    - `threads`, with each thread's stack, `crashed`/`current` flags and state;
    - every breadcrumb the SDK sends (its default 100), each with type, category, level, timestamp, message and data.

    Spool derives Sentry's event tags: `level`, `handled`, `mechanism`, `environment`, `release`, `dist`, `transaction`, `url`, `server_name`, `browser`, `browser.name`, `os`, `os.name`, `device`, `device.family`, `runtime`, `runtime.name` and `user` (SDK-008). Each event shows its tags, contexts, request and extra as structured sections. Every tag is a FIND-001 search key and facet. Issue detail shows a per-issue tag summary that lists each tag key's top values, with counts and percentages over all the issue's events and an "other" bucket.
  - **Message events.** A `captureMessage` event, or any event with no exception and no stack trace, is accepted as an issue with its level and tags. It is grouped by its message template (`logentry.message` before parameters, otherwise `message`). A stack trace sent by `attachStacktrace` is shown and used for grouping.
  - **Grouping, scrubbing and egress.** GROUP-001 groups over the whole exception chain and exception group. SDK-003 server rules, ERR-003 scrubbers and default secret-name masking run on every kept field, including frame variables, context lines, exception values, request, contexts, extra and thread stacks, before storage. ERR-007 packets gain the chain, frame identities, tags and contexts within their 256 KiB bound and named truncation. SDK-sent context lines and frame variables reach agents or model providers only through AI-001's previewed field selection.

  **Acceptance evidence:** Change only the DSN on unmodified Python, Node, Java and browser SDKs with their default integrations, and send each of the following.
  - A Python `raise ValueError(...) from KeyError(...)` whose frame locals hold a `password` and an ordinary value. Confirm both exceptions appear in issue detail with their own frames, SDK-sent source context lines and local variables. Confirm the `password` local is masked in storage, issue views, packets and exports while the ordinary local survives.
  - The same wrapper exception with two different root causes. Confirm two issues and two ALERT-001 new-issue alerts.
  - A Python `ExceptionGroup` with two children, and an `asyncio.TaskGroup` failure. Confirm each is accepted and the children appear under Related Exceptions.
  - A Node error with `cause`, a Next.js server error wrapped with `cause`, and a Java `Caused by` chain. Confirm each chain is shown with its root cause marked.
  - A browser event with `setTag("tenant","acme")` and the SDK's browser and os contexts. Confirm the tags and contexts appear on the event, the issue's tag summary shows `tenant`, `browser.name` and `os.name` with counts and percentages, and `tenant:acme browser.name:Chrome` finds the issue.
  - A Django error with `request` data. Confirm the URL, method and allowed headers show while the cookie and authorization headers are scrubbed.
  - A bare `captureMessage("quota exceeded for %s", ...)`. Confirm it is accepted as an issue grouped by the template.
  - A 1,200-frame recursion. Confirm 1,000 frames are kept with "200 frames omitted" shown.
  - Copy the raw stack trace of the Python chain. Confirm the copied text shows both tracebacks with no masked value revealed.

- **SDK-011 — Sentry intake encodings, published size limits and per-item refusal.** Everything an unmodified Sentry SDK sends with its default transport gets through Spool's intake door. This covers SDK-007's `/envelope/`, `/store/`, `/minidump/`, `/unreal/` and `/security/` endpoints, the SDK-001 tunnel handlers and the first-party ingest subdomain.
  - **Encodings.** Request bodies are accepted uncompressed or with `Content-Encoding` `gzip`, `deflate`, `br` (Brotli) or `zstd`, and with or without a `Content-Length` (chunked transfer). This includes the Python SDK, which uses Brotli whenever the `brotli` module is installed. Decompression is streamed and stops at the limits below, so a decompression bomb is refused as too large without exhausting memory. An encoding outside this list is refused with 415 naming the encoding, and the refusal is counted.
  - **Published limits.** Setup, documentation and capability diagnostics publish these limits, each at least equal to Sentry's documented ingest limits:
    - 40 MiB for a compressed request;
    - 200 MiB for an envelope after decompression, including all items;
    - 1 MiB for each event (error or transaction), span, log or metric item;
    - 100 KiB for a check-in item;
    - 4 KiB for a client report item;
    - 50 MiB for a profile item;
    - 10 MiB for a compressed replay item and 100 MiB after decompression;
    - 200 MiB of uncompressed attachments per event, covering attachments, screenshots, view hierarchies, minidumps, Apple crash reports and Unreal crash files, including through `/minidump/` and `/unreal/`;
    - 100 sessions per envelope and 100 session aggregate buckets per `sessions` item.

    A maintainer can lower a project's limits, with a preview of affected recent traffic, but cannot raise them above the published figures. Attachment, minidump, profile and replay items stream to the isolated storage named in the boundaries and are never held whole in request memory.
  - **Per-item refusal.** An item over its limit, or an item that fails to parse, is refused alone with its reason (`item_too_large` or `invalid_item`, with the item type), and the rest of the envelope is accepted. The response and a `client_report`-compatible outcome name the refused item. Only a request over the compressed-request or decompressed-envelope limit is refused whole, with 413 and the limit named.
  - **Counting.** Each refusal, whether an unsupported encoding, an oversized request, an oversized item or an invalid item, is counted per item type, reason, SDK name and version in project outcomes, SDK-007 capability diagnostics, BILL-001 usage and the MIG-001 dual run. None of them consumes billable quota.

  **Acceptance evidence:** With only the DSN changed:
  - Send errors from an unmodified Python SDK with `brotli` installed. Confirm the issue is created.
  - Send `deflate`, `zstd`, `gzip`, uncompressed and chunked-transfer envelopes. Confirm each is accepted. Send an unsupported encoding and confirm a counted 415 that names it.
  - Send a 900 KiB error event. Confirm it is accepted.
  - Send an iOS and an Android crash with `attachScreenshot` and a 2 MiB view hierarchy, an Electron crash with a 5 MiB minidump through `/minidump/`, and a mobile replay segment near the 10 MiB compressed limit. Confirm each is stored and viewable by a permitted member.
  - Send one envelope holding a 1.5 MiB event item and a valid error. Confirm the error is accepted and the oversized item is refused alone with `item_too_large`. Confirm the refusal is counted in project outcomes, capability diagnostics, BILL-001 usage (no quota consumed) and the MIG-001 dual run.
  - Send a small compressed body that expands past 200 MiB. Confirm a 413 naming the limit, with no memory exhaustion and nothing stored.
  - Lower one project's attachment limit with its preview. Confirm a larger screenshot is refused alone and counted.

### Investigation and source

- **GROUP-001 — Explainable grouping and durable corrections.** Group occurrences stably across builds and runtimes, explain the fingerprint/rule version, support SDK fingerprints and project grouping rules, and preview future rule effects against retained events. Bulk split/merge corrections preserve original grouping, alert/repair coverage and undo provenance. New intake follows the chosen versioned rule; historical regrouping is explicit and cannot erase previous failure evidence. Stack trace rules honour the per-frame `in_app` flag and the in-app include/exclude options that unmodified Sentry SDKs send, and let a maintainer mark frames in-app or not in-app (`+app`/`-app`) and include or exclude them from grouping (`+group`/`-group`) by path, module, package, function and platform family (Sentry's `stack.abs_path`, `stack.module`, `stack.package`, `stack.function` and `family` matchers), previewed against retained events and applied to later intake. The issue view collapses frames that are not in-app, and grouping uses in-app frames when any exist; MIG-001 imports Sentry stack trace and fingerprint rules. The default rule groups over the whole exception chain SDK-010 keeps, processing each exception in turn as Sentry's default does. The same wrapper exception with different root causes therefore makes different issues. An exception group is grouped by its top-level exception and its children. Frames hidden by `__traceback_hide__` are left out of grouping. A message event with no stack groups by its message template. Fingerprint rules can match any exception in the chain (`error.type`, `error.value`), the message, the level, the logger, tags and SDK name, as Sentry's fingerprint matchers do. Each event has a grouping information view. It shows the rule version, the variant used (exception chain, stack, message or custom fingerprint) and, per exception, which frames and values contributed to the hash and which did not, with the reason (not in-app, hidden by a `-group` rule, or `__traceback_hide__`).

  **Acceptance evidence:** Capture equivalent and distinct failures across builds, preview a custom rule, apply it to future and explicitly selected historical events, undo a correction and inspect alert rates, immutable originals and repair gaps. Add a `stack.module:vendor.* -app` rule and a `-group` rule, preview them, and confirm later events collapse those frames in the issue view and group by the remaining in-app frames, and that a frame an unmodified Sentry SDK sends with `in_app: false` is collapsed. Send one wrapper exception with two different root causes and confirm two issues, then open each event's grouping information view and confirm it names the chain variant and marks the contributing and non-contributing frames with reasons. Add a fingerprint rule on `error.type` of the inner exception and confirm later events follow it. Send a Python frame with `__traceback_hide__` set and confirm it is hidden and excluded from the hash.

- **FIND-001 — Search, facets and saved investigation views.** Search the entire authorized retained corpus by time window, message, stack/function, project/team, environment, release (including `release:latest` under the project's SDK-009 ordering rule and `!has:release` for events sent with no release), severity, priority (TEAM-003), status, owner, every tag SDK-010 keeps or derives (SDK automatic tags such as `browser.name`, `os.name`, `device.family`, `runtime.name`, `url`, `transaction`, `server_name`, `handled`, `mechanism` and `level`, and every custom `setTag` key), any exception in the chain (`error.type`, `error.value`, `error.handled`), the `user.id`, `user.email`, `user.username` and `user.ip` keys under SDK-008's identity policy and role scope, and approved event properties. The issue stream shows and sorts by affected users (SDK-008) and by priority. Facets, counts, ordering and cursor pagination reflect the full query. A For Review inbox per project and team lists new, regressed and escalating issues that no member has reviewed yet, with a count; a member marks one or many reviewed, and it leaves the inbox for everyone with an audited actor. Save/share views and deep links without conferring access; exports use the same query and report bounds or refusals rather than silently truncating.

  *Status:* partial

  **Acceptance evidence:** Ingest more than the current list limit across projects/releases, find an older off-page event with combined filters, open the For Review inbox, mark an issue reviewed and confirm it leaves the inbox for a teammate, save a view, open it as permitted/forbidden teammates and reconcile query counts and export. Search `user.email:<a customer's address>` and confirm exactly that user's issues and user-facet counts, repeat as a read-only collaborator and confirm the values are hidden, and filter and sort the stream by priority and by affected users. Search `tenant:acme browser.name:Chrome` and `error.type:KeyError` over events sent by unmodified Sentry SDKs, and confirm the matching issues and facet counts, including an issue whose `KeyError` is the inner cause of a wrapper exception.

- **FIND-002 — Impact, trends and team reporting.** Issue detail shows every exception in the chain with its own frames and exception-group children under Related Exceptions (SDK-010). It shows the SDK-sent source context lines and local variables of each frame, labelled apart from ERR-005 mapped source and SRC-002 repository links, and the event's tags, contexts, request, extra, threads and breadcrumbs. It has a per-issue tag summary (top values per key, with counts and percentages over all events in the issue) and a raw stack trace view with copy. An All Events list shows every retained event in the issue, filtered by any FIND-001 query (tags, release, environment, user), with oldest, latest, recommended (the most complete recent event), previous and next event navigation; the detail view follows the event chosen. It also shows first/last occurrence, occurrence totals, distinct affected users (SDK-008's one definition, with the count of events that had no user key) and sessions, rate and release history. Release-scoped figures (release history, per-release counts, SDK-005 adoption and crash-free rates by release, REL-002 comparison) exclude events sent with no release and name how many there were (SDK-009); every other figure counts them. Dashboards report error volume, new/regressed/resolved backlog, time to acknowledge/repair, team ownership and release stability with transparent time zones, sampling, retention and dropped-event denominators. Custom dashboards hold widgets that run FIND-001 queries (count, rate, affected users or sessions, grouped by any approved field), and a free-form event query table saves and shares queries. Share permissioned reports and export CSV/JSON.

  *Status:* partial

  **Acceptance evidence:** Open a multi-release issue and a team report over a known event/session set that includes events sent with no release (counted in totals, excluded and named in release-scoped figures); reconcile totals, affected counts and triage durations, compare time zones, build a custom widget and saved event query that match the same totals, and verify a read-only export omits inaccessible projects. Open an issue from an unmodified Python SDK chained exception and confirm the chain, context lines, local variables, tags, contexts, request and tag summary match the events sent, then copy the raw stack trace. Open that issue's All Events list, filter it by `tenant:acme`, step from the oldest to the latest event and confirm the detail follows each chosen event.

- **SRC-001 — Build artifacts and CI pipelines.** Automate exact-release/debug-ID artifact publication from Vite, webpack, Rollup and esbuild builds in GitHub Actions and GitLab CI, through Spool's own CLI and plugins and through the unmodified sentry-cli subcommands and Sentry build plugins enumerated in SRC-003. Verify upload checksums, required artifact sets, access and retention before deployment; expose incomplete builds and support safe late artifacts. Source bundles and native symbols are private, separately authorized and portable, with the capacity, map formats, retention and per-file failure reporting SRC-004 publishes; a build's full artifact set is stored for every release.

  **Acceptance evidence:** Build equivalent minified apps with each named bundler, upload from GitHub Actions and GitLab CI with Spool's CLI and with each SRC-003 path using deploy-only authority, capture a real failure and check mapped frames; deny reads/writes outside artifact scope and show a failed/incomplete upload. Upload a second and third release of the same build and confirm every map of each release is stored and used.

- **SRC-002 — GitHub, GitLab and Bitbucket source context.** Connect GitHub, GitLab and Bitbucket with explicit repository grants. Link stack frames to exact revision/file/line, ingest releases and included commits, read CODEOWNERS, and show suspect commits with reasons and uncertainty. When an issue first appears in a release that includes a PR/MR, Spool comments once on that PR/MR with the issue link, short ID and suspect reason. Frame paths map to repository paths through per-repository code mappings (a stack root such as `webpack://_N_E/./src/`, `app:///` or `/app/` to a source root and branch), derived automatically from in-app frames that match repository files and editable by maintainers; MIG-001 imports Sentry's code mappings, and an unmapped frame says so rather than linking a guessed path. Handle renames, monorepos and revoked/private repositories without leaking source. GitHub App and GitLab application setup, credential rotation, webhook signature verification and read-back reconciliation are part of delivery.

  *Status:* partial

  **Acceptance evidence:** Connect a private repository on each host, deploy a commit that changes the failing frame, follow its source link and suspect explanation, see one comment on the introducing PR/MR, revoke the grant and inspect immediate denial without stale privileged reads. Confirm a derived code mapping links a `webpack://` Next.js frame and an edited mapping links a Django `/app/` frame to the right file and line, and that an unmapped frame says it is unmapped.

- **SRC-003 — sentry-cli and Sentry build-plugin compatibility.** A team's existing CI keeps working against Spool when only the Sentry URL, organization/project slugs and auth token change. Compatible sentry-cli subcommands: `releases new`, `releases set-commits` (`--auto` and explicit `--commit`), `releases finalize`, `releases deploys new`, `deploys new`, `sourcemaps inject`, `sourcemaps upload` (debug-ID and release/dist), `debug-files upload` (dSYM, ELF/DWARF, Breakpad, ProGuard/R8 mappings and Dart symbols), `debug-files bundle-sources` and `send-event`. Compatible build integrations: `@sentry/webpack-plugin`, `@sentry/vite-plugin`, `@sentry/rollup-plugin`, `@sentry/esbuild-plugin`, `@sentry/nextjs` `withSentryConfig` source-map upload, the Sentry Android Gradle plugin, the Sentry Xcode build-phase upload script, `fastlane-plugin-sentry` (`sentry_create_release`, `sentry_upload_sourcemap`, `sentry_debug_files_upload`, `sentry_finalize_release`, `sentry_create_deploy`), `@sentry/react-native` bundle and Hermes source-map upload (including Expo) and `sentry_dart_plugin`. Spool serves the Sentry API endpoints these clients call (chunk upload and assemble, release, commit, deploy and debug-file routes) under deploy-only project authority with the ERR-005 and SDK-002 identity checks and SRC-004's published capacity: the chunk-upload options these clients read advertise sizes that fit a production build, every map and debug file each release emits is stored, and a failed file is named with its reason in the client's output. An unsupported flag or endpoint fails with an explicit error naming the command, never a silent success.

  **Acceptance evidence:** In GitHub Actions and GitLab CI, run each listed sentry-cli subcommand and each listed bundler, Next.js, Gradle, Xcode, Fastlane, React Native and Dart integration unmodified except for URL, slugs and token. Capture a real failure from each built app and confirm the release, commits, deploy, maps and symbols are registered and frames are original. Run the Next.js and React Native uploads for three consecutive releases of a production-sized build and confirm no upload is refused for artifact count or for a map within SRC-004's size limit. Run an unsupported flag (named failure) and repeat an upload with an ingest or read-only token (refused, nothing stored).

- **SRC-004 — Published source map and debug-file capacity.** Every artifact a production build emits is stored and used, release after release:
  - **Capacity.** There is no per-project or per-release limit on the number of source maps, source files, artifact bundles or debug files. Each source map or source file may be up to 190 MiB, Sentry's only stated per-map limit, with any number of `sources` and mappings within that size. Each debug file (dSYM, ELF/DWARF, PDB, Breakpad, ProGuard/R8 mapping, Dart/Flutter symbols, Hermes and React Native bundle maps) may be up to 2 GiB. Spool's chunk-upload options endpoint advertises chunk size, concurrency and maximum file size so that unmodified `sentry-cli` and the SRC-003 plugins upload files of these sizes. Uploads stream through chunked assembly to the isolated storage the boundaries name, never held whole in request memory.
  - **Formats.** Indexed (`sections`) maps, `sourceRoot`, `sourcesContent`, `ignoreList`/`x_google_ignoreList` (those frames are marked not in-app under GROUP-001), debug IDs (`debugId`/`debug_id`) and release/dist artifact names including `~/` URL prefixes are applied under ERR-005's identity checks, not refused.
  - **Retention and cost.** An artifact is kept while any retained event in its release or build uses it, and for at least the project's GOV-003 retention window after its last upload or use. It then expires under GOV-003's rules with a named gap, and an issue whose map has expired shows generated frames with that reason. Artifact storage is metered in USD under BILL-001, and the only capacity refusal is a buyer-set artifact storage cap (`artifact_storage_cap`).
  - **Failures.** A failed upload is named per file and reason (too large, malformed map, identity mismatch, storage cap, authority) in the `sentry-cli`/plugin assemble response and CI output, in Spool's CLI output and in the release's artifact view, and the release is marked incomplete under SRC-001. It is never a bare 429.
  - **Source fetching.** A project that hosts public maps may opt in (off by default) to fetching minified JavaScript and its `sourceMappingURL` map from named HTTPS origins, optionally sending a project secret header as Sentry's security token does. Fetching runs in an isolated worker under these size limits, refuses private-network addresses and redirects to another origin, labels the map as fetched with its URL and fetch time, and keeps fetched maps private under the same retention. MIG-001 imports Sentry's source-fetching and allowed-domain settings, and setup offers the CI upload step as the default path.

  **Acceptance evidence:** Build a production Next.js App Router app (more than 100 chunks, a vendor chunk map over 2 MiB) and a React Native Hermes release, and upload with unmodified `withSentryConfig` and `@sentry/react-native` for three consecutive releases. Confirm every map is stored, and that an error in a vendor-chunk frame and an app frame in the third release shows original source. Upload an indexed map, a map with `sourceRoot` and a 50 MiB map, and confirm each resolves. Upload a 40 MiB dSYM and a ProGuard mapping, and confirm they symbolicate and deobfuscate. Confirm no upload is refused for artifact count. Set an artifact storage cap and confirm the next upload names each refused file and the cap in CI output and the release view. Advance the clock past retention for a release with no retained events and confirm its maps expire with a named gap. Opt one project into source fetching from an allowed origin, confirm a frame resolves from the fetched map, and confirm a private-network URL is refused.

- **REL-001 — Release and deployment history.** Register builds, commits and per-environment deployment start/success/failure/rollback with actor, timing and included commit range. CI retries are idempotent; abandoned starts become visibly timed out and notify their owner. Releases auto-created from Sentry SDK events (SDK-009) appear in release history labelled auto-created, with first-seen time, first environment and creating SDK; a later registration of the same name through the release API, `sentry-cli` or a build plugin enriches that release with commits, artifacts and deploys and removes the label, never creating a second release. The project's SDK-009 ordering rule governs regression, while deployment history records multiple environments, progressive exposure and rollback without rewriting past event identity.

  **Acceptance evidence:** Report started/completed/failed deployments from CI, repeat callbacks, abandon one, roll back production and inspect included commits, timeout follow-up and regression ordering for delayed old events. Let a Sentry SDK event auto-create a release, then register the same name with commits and a deploy through `sentry-cli`, and confirm release history shows one enriched release with its original first-seen time.

- **REL-002 — Suspect change and release comparisons.** Connect first occurrence, reactivation and rate change to the release/deployment window, suspect commit and changed source; compare release cohorts, adoption and loss before attributing impact. Keep correlation distinct from a demonstrated cause and preserve older-release failures during rollout. An engineer can confirm or reject a suspect and record evidence for later agent learning.

  **Acceptance evidence:** Deploy two commits while retaining old clients, trigger first/regressed errors and an unrelated spike, inspect candidate reasons and comparable release rates, then reject a false suspect without changing occurrences.

- **CTX-001 — Error-linked traces, logs and request performance.** Capture OpenTelemetry traces/spans, and the `transaction`, `span`, `profile`, `profile_chunk`, `log` and `trace_metric` items unmodified Sentry SDKs send (SDK-007), with error links across browser, backend and jobs, showing the failing request waterfall, latency, dependencies, correlated structured logs, the profile of the failing transaction, trace-connected metrics and approved RUM context (including web vitals) beside the stack. A web vitals view per page and release shows LCP, INP, CLS, FCP and TTFB at p75 with the good/needs-improvement/poor distribution and sample count, and a transaction summary shows p50/p95 duration, failure rate and throughput; these are the views ALERT-001 metric alerts link to. Preserve trace/span IDs through intake, sampling and exports. OTLP and Sentry-SDK telemetry intake follows GOV-003's published sustained rate and named volume reasons. Link or forward context to Datadog and Splunk with permissioned deep links and explicit data/minimization controls. Performance problems found in the same spans open as issues under CTX-004. This is error diagnosis, not ownership of their entire APM/log warehouse.

  **Acceptance evidence:** Exercise a browser-to-service-to-job failure with propagated trace IDs, once instrumented with OpenTelemetry and once with unmodified Sentry SDKs with tracing, profiling and logs enabled, inspect one correlated waterfall/log/profile path and missing-sample labels for each, open the page's web vitals view and reconcile p75 LCP and CLS with the sent measurements, then open authorized Datadog/Splunk context and verify revoked export credentials stop forwarding.

- **CTX-002 — Privacy-controlled session replay.** Attach browser and mobile session replay to the exact error/session/time, with console/network timeline and trace links. Offer first-party capture, which accepts the unmodified Sentry replay format (`replay_event`, `replay_recording` and `replay_video` from `replayIntegration` and the mobile SDKs, linked by the event's `replay_id`, per SDK-007) as well as Spool's own recorder, and link replays recorded elsewhere through the CTX-003 connectors. First-party capture has consent and sampling, default input/text/media masking, exclusion zones, retention and role-scoped playback. Replay segments are accepted up to SDK-011's published replay limits (10 MiB compressed, 100 MiB decompressed per item). Replay never includes production credentials by default; access/export/erasure is audited and missing replay remains explicit.

  **Acceptance evidence:** Opt in on a realistic checkout/account flow with Spool's recorder and, separately, with an unmodified Sentry `replayIntegration` and mobile replay, trigger an error, play the preceding path from issue detail at the error time, verify masked fields in stored replay and exports, then revoke access and erase the session across playback/artifacts.

- **CTX-003 — Third-party replay connectors.** Link errors to replays the buyer already records in PostHog Session Replay, LogRocket, FullStory and Datadog RUM Session Replay. Each connector uses a least-privilege buyer API credential and reads the session identifier that provider's browser SDK attaches to the Spool event, or matches by an approved session property and time. Issue detail shows the linked replay at the error timestamp as a permissioned deep link with provider metadata (start, duration, page); Spool never copies replay content. Missing, expired or inaccessible replays carry a reason. Revoking the connector credential or the viewing member's Spool permission stops new links and refuses opening existing ones from Spool; disconnecting removes stored replay references under BASE-DATA-003. Sentry Replay links imported by MIG-001 follow the same model.

  **Acceptance evidence:** For each of PostHog, LogRocket, FullStory and Datadog, record a session with the provider SDK beside the Spool or Sentry SDK, trigger an error and open the linked replay from issue detail at the error time. Revoke the connector permission and confirm the link is refused with a reason, and that no replay content exists in Spool storage or exports.

- **CTX-004 — Performance issues from transactions and spans.** Spool detects performance problems in the `transaction` and `span` data that unmodified Sentry SDKs and OpenTelemetry send (SDK-007, CTX-001) and opens them as issues in the same issue stream as errors, with the same short IDs, ownership, priority, triage, alerts, tracker links and AI-002/AI-003 handoff. Detectors: N+1 database queries, N+1 API calls, slow database queries, consecutive database queries, consecutive HTTP calls, large HTTP payloads, large render-blocking assets, uncompressed assets, and main-thread file I/O and database work on mobile. Each detector is on by default with a stated threshold (duration, repetition count or size) that a maintainer can change or disable per project, with a preview against retained spans; changes are audited and apply to later intake. Each issue groups by the offending span pattern (normalized query or URL and parent operation) and shows the evidence span tree, the repeated or slow spans, the transaction and release, affected users (SDK-008) and occurrence counts, with a link to the trace waterfall; sampling rates are shown with the counts. Resolution and regression follow ERR-006 and TEAM-003, and detected issues bill against the span data category in BILL-001, not the error category.

  **Acceptance evidence:** In a Node or Python service and a Next.js app instrumented once with unmodified Sentry SDKs with tracing on and once with OpenTelemetry, trigger an N+1 query loop, a slow query, consecutive HTTP calls and a large render-blocking script, and a main-thread file read in a React Native app. Confirm one performance issue per pattern appears in the issue stream with its span evidence, trace link and affected users, alerts under ALERT-001 and is assigned under TEAM-002. Raise one detector's threshold above the observed value and confirm later traffic opens no new issue, then deploy a fixed release and confirm resolve and regression behave as for errors.

### Alerts and delivery

- **ALERT-001 — Filtered rules and real anomaly detection.** Configure new issue, reactivation, regression, frequency/count/rate threshold, seen by more than N users in a window (SDK-008's affected-user definition), percent of sessions affected in a window (SDK-005 denominators) and crash-rate/spike rules by project/team, environment, severity, priority, release and approved payload properties. A user condition on a project whose identity policy leaves no user key, or a session condition without session data, is refused at preview with the reason rather than evaluated against 0. Historical-baseline detection accounts for traffic, seasonality, sampling and missing capture rather than renaming a fixed threshold anomaly detection. Preview the matched history, owner, evaluation window and destination before enabling; deduplicate each rule/window transition. The new issue condition fires for a new root cause under an existing wrapper exception, because GROUP-001 groups over the whole exception chain (SDK-010). The regression condition fires once for every regression ERR-006, TEAM-003 and SDK-009 define, including the first accepted event after a plain Resolve, in any release or with no release. Metric alerts on the transactions and spans Spool ingests under CTX-001 and SDK-007 cover p50/p75/p95/p99 duration, failure rate, throughput, Apdex and the web vitals LCP, INP, CLS, FCP and TTFB, filtered by transaction name, project, environment and release, with warning and critical thresholds or a change-from-baseline comparison, a resolve threshold, and the same preview, deduplication and destinations; MIG-001 imports Sentry metric alert rules. Alerts on host, container or infrastructure metrics stay with the connected APM named in the boundaries.

  **Acceptance evidence:** Create filtered rules in the app, replay normal/burst/seasonal/low-traffic windows with loss, inspect baseline and threshold reasons, and reconcile exactly one alert per qualifying transition and no alert outside its filter. Fire one seen-by-more-than-3-users rule and one percent-of-sessions-affected rule from known user and session sets, and confirm the user rule is refused at preview on a project whose policy drops identity. Plain-resolve two issues and send one event in the same release to the first and one with no release to the second; confirm one regression alert each. With unmodified Sentry tracing, push a transaction's p95 duration and a page's LCP over warning and then critical thresholds and back, and confirm one alert per transition and a resolve.

- **ALERT-002 — Email, Slack, Teams, Oncall and PagerDuty.** Route actionable error alerts to email, Slack, Microsoft Teams, sibling Oncall and PagerDuty, with project/issue/release links, short ID, ownership, severity and recovery/update semantics, plus the inbound triage actions named in ALERT-005. Use named provider adapters and least-privilege account setup, signed inbound actions, rotation and revocation. Teams choose separate destinations per rule. Discord channels receive formatted alerts through a channel webhook with issue links. Signed HTTP webhooks remain available for custom automation. Mail adapters support configured US providers such as Amazon SES, Resend and SendGrid; provider acknowledgement stays distinct from recipient receipt.

  **Acceptance evidence:** Connect each destination, including a Discord channel webhook, using buyer-controlled accounts, test and trigger a filtered new/regressed issue, follow its exact issue link, perform each ALERT-005 action for that destination (Slack and Teams resolve, archive and assign; PagerDuty and Oncall acknowledge and resolve sync), rotate/revoke credentials and reconcile delivery history without duplicate pages.

- **ALERT-003 — Destination tests, digests and snoozing.** Test every destination before enabling a rule with distinct test wording and no issue mutation, then show each channel's delivery states separately: email accepted by provider, delivered, bounced or complained (from provider events); Slack and Teams posted with message identifier; PagerDuty and Oncall triggered, acknowledged and resolved; webhook HTTP status and signature receipt. Deliver daily/weekly project and personal error digests in the buyer timezone, with new/regressed/unowned work, usage and failed automation. Snooze by issue/rule/team with an expiry and one resumption notice; maintenance and quiet hours cannot hide event loss or erase backlog.

  **Acceptance evidence:** Add and test each channel, inspect its named delivery states, enable only after reviewing its result, schedule a US-timezone digest, snooze through a burst and expiry, and inspect one digest/resumption plus unchanged issue evidence for test sends.

- **ALERT-004 — Durable follow-up and visible automation failure.** Every alert, integration action, deployment timeout and agent step has an accountable owner, due condition, durable queued/running/blocked/failed/uncertain/completed/cancelled state and inspectable attempts. Restart and repeated evaluation do not duplicate messages or work. Only proven-unaccepted sends may retry; uncertain effects need explicit reconciliation or authorized resend. Broken destination/rule and overdue unacknowledged work notify an alternate operator without recursive alert storms. Restored history remains inert.

  **Acceptance evidence:** Interrupt before and after the send boundary, restart, expire a lease, revoke a rule and lose a provider response; inspect durable state, deduplication, operator follow-up and explicit reconciliation, then restore without replay.

- **ALERT-005 — Chat and paging triage actions and link unfurls.** Slack and Microsoft Teams issue alerts carry Resolve (now or in the next release), Archive (forever, until escalating, or for a count or duration) and Assign (to a member or team of that project) controls. Each action checks the clicking chat user's linked Spool member identity and project role; an unlinked or under-privileged user gets a private explanation with an account-link prompt, and the issue does not change. The message updates in place with the new state and actor, and the action appears in the issue timeline and AUDIT-001. Spool issue, event and release links pasted in Slack unfurl into a preview (title, short ID, status, assignee, counts, last seen, release) for linked members with access, and stay bare links otherwise; no event payload is revealed. PagerDuty and Oncall incidents created from Spool sync both ways: acknowledging the incident acknowledges the issue, resolving it resolves the issue, and the reverse, with loop prevention. A linked member can start an AI-002/AI-003 investigation from Slack under the app's approval and budget policy. Inbound actions are signature-verified (Slack signing secret, Teams bot authentication, PagerDuty and Oncall webhook signatures), replay-protected and idempotent.

  **Acceptance evidence:** From a Slack alert and a Teams alert, a linked member resolves one issue, archives another until escalating and assigns a third to a team; confirm state, timeline, audit and the updated message. An unlinked user and a read-only member are refused with no change. Paste an issue link and confirm the unfurl for an authorized member and none for an unauthorized one. Acknowledge and resolve a PagerDuty and an Oncall incident and confirm the issue follows, then resolve in Spool and confirm the incident resolves. Replay a signed action (one effect) and forge one (refused).

### Delivery integrations

- **TRACK-001 — Vector, Jira and Linear issue sync.** Create or link a delivery issue in sibling Vector, Jira or Linear with a previewed sanitized diagnostic summary, source/release links and owner. Sync resolution/reopening and external status idempotently with signed callbacks; for Jira also sync assignee and comments both ways, and offer create-ticket as an ALERT-001 rule action, loop prevention and conflict visibility. GitHub/GitLab Issues are optional destinations under the same model. Spool owns error triage; the connected tracker owns the delivery backlog and planning.

  **Acceptance evidence:** Create a ticket from a Spool issue in each tracker, repeat the action, resolve/reopen on both sides, sync a Jira assignee and comment both ways, create a Jira ticket from an alert rule, reject a stale/forged callback and disconnect; confirm one ticket, consistent links and visible conflict rather than silent overwrite.

### Migration and portability

- **MIG-001 — Sentry organization migration.** Guide a real Sentry buyer through organization/project, member and team roster (as pending invitations with mapped roles), environment, release, issue/event, triage, ownership, alert, saved search, source artifact, attachment, feedback, replay link, dashboard, data scrubbing settings (sensitive and safe fields and advanced rules, mapped to SDK-003), inbound filter (mapped to SDK-006), IP-storage and user identity settings (mapped to SDK-008) JavaScript Loader Script key settings (mapped to the SDK-001 loader), stack trace and fingerprint rules (mapped to GROUP-001), code mappings (mapped to SRC-002), JavaScript source fetching and allowed-domain settings (mapped to SRC-004), retention and quota settings (mapped to GOV-003 and BILL-001), metric alert rules (mapped to ALERT-001) and release names, versions and dates (so SDK-009 ordering continues across cutover) migration using authorized export/API adapters. Preview source-to-target mapping, unsupported items and count/byte reconciliation; retain provenance/private originals, resume safely and undo only unchanged imported records. Repoint standard SDK DSNs for a measured dual-run/cutover that compares Sentry and Spool counts per SDK-007 item type (errors, affected users under SDK-008's definition, sessions and crash-free rates, transactions, replays, feedback, attachments and client reports), so each difference is attributed to a named cause (for example an identity policy that hashes or drops a field, an inbound filter, sampling, tunnel loss, a release refused under SDK-009's registered-sequence-only rule, or an item refused under a project limit lowered below SDK-011's published figures; events with no release, SDK-default releases, every SDK-default content encoding and items within Sentry's documented limits are accepted and are never an unexplained difference; a refusal for retained event count, a fixed per-minute budget or source map count is never an explained difference and fails the dual run under GOV-003 and SRC-004), and it compares sample events field by field (exception chain length, frame count, tags and contexts kept under SDK-010), with retained rollback configuration and a checked rollback procedure. Import history cannot send alerts, grant account authority or prove current health; explicitly reauthorize teams, integrations and secrets.

  **Acceptance evidence:** With a real buyer export/API account, preview/reconcile all named collections, apply/resume, compare issue/event/artifact samples and the imported scrubbing rules and inbound filters, dual-run unchanged SDKs, including services that send no release, mobile builds with SDK-default releases, a Python service with `brotli` installed and mobile crashes with screenshots, and reconcile per-item-type counts, affected users and crash-free rates with Sentry at the buyer's production volume through a deploy-incident burst, with no deficit attributed to retained capacity or a fixed per-minute budget, then cut over; inspect unsupported mappings and guarded undo preserving native edits.

- **MIG-002 — Datadog Error Tracking migration.** Import Datadog Error Tracking issues, events, service/environment/version tags, triage, release and available trace/log links with authorized API/export adapters. Map monitors/notification rules into previewed Spool rules and external destinations; preserve source IDs, timestamps and unsupported fields. Reconcile counts and time windows, retain read-only history, dual-run SDK/OTel intake and switch error monitoring without requiring migration of the full Datadog observability platform.

  **Acceptance evidence:** Import a real Datadog buyer project, review mappings and exceptions, reconcile sampled issue/event/history records and monitor rules, dual-run one service and cut over with no imported alert replay or duplicate native events.

- **MIG-003 — Rollbar migration.** Import Rollbar projects/items/occurrences, environments, deploys, status/assignment, grouping fingerprints, comments, tracker links and notification policies through a documented authorized export/API path. Preserve source identities and private artifacts where obtainable; name unavailable data. Provide dry-run, reconciliation, resumable apply/guarded undo and a SDK cutover guide that preserves diagnostic context and explicitly reports conversion losses.

  **Acceptance evidence:** Migrate a real Rollbar project with resolved and reactivated items, compare counts/stacks/deploys and rules, resume an interruption, exercise the replacement capture setup and verify history causes no outbound work.

- **MIG-004 — Bugsnag migration.** Import Bugsnag/SmartBear organizations/projects, errors/events, stability and release metadata, app versions/stages, triage, collaborators, source-map/symbol references and integrations available through buyer-authorized APIs/exports. Reconcile event and session denominators separately; missing historical sessions never become a fabricated crash-free rate. Preview mapping and unmigratable artifacts, resume/undo safely and document mobile/web SDK and symbol-upload cutover.

  **Acceptance evidence:** Migrate a real Bugsnag web/mobile project, reconcile sampled errors/events and release stability inputs, repoint capture and symbol CI, inspect an original native stack and show gaps where source exports lack historical sessions.

- **MIG-005 — Up-and-comer switching paths.** Offer documented, reviewed import/cutover adapters for PostHog Error Tracking, GlitchTip and Better Stack Error Tracking. Preserve available errors, lifecycle, releases, project tags and replay/trace links, with per-source mapping, loss and reconciliation reports. PostHog analytics stays in PostHog or the buyer's chosen analytics product; imported replay links require their original permissions and never imply ownership of external data.

  **Acceptance evidence:** For a buyer-controlled project in each named service, preview/import representative errors and lifecycle, reconcile source counts and links, cut over capture, and verify resumed imports are idempotent and imported history stays inactive.

- **API-001 — Public APIs, attachments and full export.** Expose versioned, paginated, project-scoped APIs for issues, occurrences, release/session reports, feedback, approved attachments and audit, with documented limits, cursors and compatibility. Export a complete authorized project or search in JSON/CSV and an integrity-checked private archive at any GOV-003 retained volume, streamed rather than refused for the project's event count; preserve attachment identity and erasure provenance. Attachments include the screenshots, view hierarchies, minidumps and crash reports unmodified Sentry SDKs send (SDK-007); each is viewable and downloadable only by members with access to its project, and every view is audited. API/webhook integrations including Sentry integration-platform workflows receive explicit capability mapping; Sentry Web API scripts and Terraform keep working through API-002.

  **Acceptance evidence:** Use a read-only service grant to paginate all retained records and attachments, open a Sentry-SDK screenshot as a permitted member and be refused as an unpermitted one, reconcile with the UI/archive, reject foreign scope, export a large query with named bounds and restore without secrets or active work. Export a 160,000-event project whole in JSON and as a private archive and reconcile its counts.

- **API-002 — Sentry Web API and Terraform compatibility.** Scripts and infrastructure-as-code written against Sentry's Web API keep working when only the base URL and auth token change. Spool serves Sentry's `/api/0/` endpoints for organizations, teams, members, projects, project client keys (DSNs, including per-key rate limits), issues (list with Sentry search syntax, get, update, bulk mutate and delete), events, releases, issue alert rules and metric alert rules, with Sentry's cursor `Link` pagination. Sentry-style organization and personal auth tokens map to TEAM-001 roles and are limited to their scopes and projects. The community Terraform provider for Sentry (`jianyuan/sentry`) manages `sentry_team`, `sentry_project`, `sentry_key`, `sentry_issue_alert` and `sentry_metric_alert` unmodified except for base URL and token, and plan, apply and import are idempotent. An unsupported endpoint or field fails with an explicit error naming it, never a silent success; every write is audited under AUDIT-001.

  **Acceptance evidence:** Run a script that lists issues with a Sentry search query, paginates past the first page, assigns one and bulk-resolves several through `/api/0/`, and confirm the UI matches. Run `terraform plan`, `apply` and `import` with the `jianyuan/sentry` provider against Spool to create a team, a project, a key with a rate limit and issue and metric alerts, then re-plan and confirm no changes. Call an unsupported endpoint (named error) and attempt a write with a read-only token (refused, nothing changed).

### Governance and operation

- **GOV-001 — Enterprise identity and administration.** Connect Auth0, Okta and standard SAML/OIDC identity providers for organization SSO, enforce verified domains, MFA/step-up policy and SCIM provisioning/deprovisioning, and audit role changes. Preserve a controlled recovery owner without bypassing provider revocation. Members can also sign in with Google (Workspace or personal account) or GitHub as plain login options linked to their member identity; an admin can require one, restrict Google sign-in to verified domains and GitHub sign-in to a named GitHub organization, and removal, MFA and step-up still apply. Native email-link/local development setup remains usable; managed first-owner admission and editor access reuse native authority rather than adding a parallel identity model.

  **Acceptance evidence:** Configure SSO with each named provider, provision a member and project role, enforce step-up, revoke the IdP user while a session/grant is active and verify all private doors deny access; recover through the documented owner procedure. Sign in one member with Google and another with GitHub, restrict Google sign-in to the verified domain and confirm an outside Google account is refused, then remove the GitHub member and confirm the next request is denied.

- **GOV-002 — Retention, residency and data custody.** Publish US-first hosting/subprocessor and residency options, telemetry/artifact/replay retention windows and deletion timing, none below GOV-003's minimums (errors at least 90 days by default, every data type at least 30 days), with automatic expiry under GOV-003 and artifact retention under SRC-004. Owners configure per-project retention with dry-run, schedules, storage/usage estimates and legal-hold/export controls; longer retention is an explicit USD plan choice. No expiry silently improves historical denominators or deletes independent anonymized audit. Record retention and dedup cutoffs in packets and reports. New storage/worker providers require a declared data-flow and erasure policy.

  **Acceptance evidence:** Set different retention windows, preview counts/coverage, hold/export one project and run the scheduled cutoff; inspect private artifacts removed, historical gaps, cost estimate and complete erasure/restore fences. Advance the clock past a project's window with no manual action and confirm its oldest events expire automatically while new events are still accepted.

- **GOV-003 — Published retention, retained volume and sustained intake rate.** A buyer knows how long Spool keeps data, how much it keeps and how fast it accepts it, and no other limit stops the core job:
  - **Retention.** Errors with their events and attachment metadata, sessions and release-health aggregates are kept at least 90 days by default on every plan, as on Sentry Team and Business; GOV-002 publishes each data type's window (none below 30 days) and longer windows as USD plan choices, and an owner may shorten one project's window with GOV-002's preview. Expiry runs automatically at least daily. It removes data older than the window under ERR-009's cutoff, occurrence tombstone, deduplication horizon and coverage-gap rules, records the gap in packets, reports and FIND-002 denominators, and never refuses or delays new intake. A GOV-002 legal hold pauses expiry for the held project only.
  - **Retained volume.** BILL-001 sells volume as errors accepted per month in USD tiers, the smallest including at least 50,000 errors a month (Sentry Team's figure), with pay-as-you-go or a raised budget above the included volume. A project keeps every accepted event for its full retention window, however many there are. There is no per-project or per-installation retained-event count, and no project is refused because of how many events it holds.
  - **Volume refusal reasons.** The only volume refusals are the buyer's monthly budget or cap (`over_budget`), BILL-001 spike protection against the project's own baseline (`spike_protected`), an admin-set DSN key rate limit (`key_rate_limited`) and intake above the sustained rate below (`server_rate_limited`). Each returns 429 with `Retry-After` and `X-Sentry-Rate-Limits` naming the limited data category and scope, and is counted under its reason in project outcomes, BILL-001 usage, capability diagnostics and the MIG-001 dual run. Non-volume refusals (SDK-006 filters, SDK-009's stricter rule, SDK-011 limits, invalid items) are counted separately.
  - **Sustained intake rate.** Each project accepts at least 6,000 events a minute sustained, with bursts to 10,000 in any one minute, and an organization at least 30,000 events a minute across its projects, without refusal, on native, Sentry SDK and OTLP (CTX-001) intake alike. Events accepted at that rate are searchable in FIND-001 within 60 seconds. Above it, refusals are `server_rate_limited` and counted, never silently dropped.
  - **Obligations at volume.** Clauses once tied to the current cap are restated against this volume. A project holding 150,000 retained events (three months of the smallest tier) or more is archived, exported and restored whole by streaming (ERR-009, API-001). Project erasure completes within 15 minutes for 150,000 retained events and at no less than 10,000 events a minute above that (BASE-DATA-003), and end-user erasure within 300 seconds in such a project (SDK-008). FIND-001 search and FIND-002 counts cover every retained event.

  **Acceptance evidence:** With unmodified Sentry browser (Next.js) and Python (Django) SDKs and only the DSN changed, send 60,000 errors to one project over a simulated month, including a burst of 5,000 in one minute, once with spike protection off and once at its default. Confirm none is refused for retained capacity or a fixed per-minute budget, that any refusal with spike protection on is counted only as `spike_protected`, and that every accepted event is searchable within 60 seconds and counted. Continue past 160,000 retained events and confirm new errors, including a brand-new crash, are still accepted. Send more than 10,000 in one minute and confirm `server_rate_limited` 429s with `Retry-After` and counted outcomes. Advance the clock past the 90-day window and confirm the oldest events expire automatically with a named gap while new events are accepted. Set a monthly budget below the volume and confirm refusals start only at that budget, as `over_budget`. Archive and export the 160,000-event project, restore it into an empty destination and reconcile counts, then erase one user within 300 seconds and the project within 16 minutes.

- **BILL-001 — Predictable USD usage and ingestion budgets.** A US software team sees accepted, filtered (per SDK-006 filter), invalid, server-rate-limited (only above GOV-003's published sustained rate), key-rate-limited, spike-protected, client-discarded (from Sentry SDK `client_report` items and Spool SDK reports, by reason and data category, per SDK-007), refused for a missing or unregistered release (only on projects that chose SDK-009's registered-sequence-only rule), refused under SDK-011 (unsupported encoding, oversized request, oversized or invalid item, by item type and reason) and over-budget volume separately from billable events, sessions, replay, artifacts and retention. Publish USD tiers by errors accepted a month (the smallest including at least 50,000 errors, as Sentry Team does) with pay-as-you-go or a raised budget above the included volume, plus overage, project budgets and forecasts with owner-controlled caps and warning destinations. No volume is refused because of a project's retained event count or under a fixed per-minute budget; GOV-003 lists the only volume refusals. Automatic spike protection, on by default and owner-adjustable, caps a sudden volume surge above the project's recent baseline so it does not consume quota, records the dropped count and notifies the budget destination. An admin can set a rate limit on an individual DSN client key (events per minute or hour); exceeding it returns 429 with `Retry-After` and `X-Sentry-Rate-Limits` for that key only and counts as key-rate-limited. Caps return explicit SDK backpressure and retry guidance; operators can tune sampling with a preview of diagnostic loss. Commercial checkout, invoices and applicable US sales-tax handling use Stripe or an authorized billing partner.

  *Status:* partial

  **Acceptance evidence:** Run representative volume through a project budget, reconcile usage and displayed USD forecast, trigger warning/cap outcomes and a spike-protection cap, confirm filtered events consume no quota, send Sentry SDK client reports and reconcile discarded counts by reason and category, exceed one DSN key's rate limit while a second key keeps ingesting, send an envelope with one oversized item and confirm it is counted as refused under SDK-011 without consuming quota, adjust sampling, set a monthly budget below a month's volume and confirm refusals start only at that budget as `over_budget` with nothing refused for retained count, and complete a buyer-controlled billing sandbox invoice with tax and cancellation handling.

- **UX-001 — Mobile operations and accessible investigation.** Engineers use the complete triage, search, assignment, release, feedback, alert and repair path on desktop and mobile web with keyboard/screen reader support, stable deep links, touch controls and no horizontal page overflow. Loading, empty, permission-denied, offline/stale, error and success states name what is known. Preserve drafts/focus during refresh and refuse duplicate busy actions. Native application crash SDK coverage is SDK-001/002 and remains required.

  **Acceptance evidence:** At 1440 and 390, onboard a realistic team project, capture/map an error, search/assign/comment, follow an alert, approve a repair and inspect all end states with keyboard, screen reader checks and revoked/offline sessions.

- **AUDIT-001 — Permanent privacy-safe action audit.** Record actor/service, time, action, subject and outcome for triage, grouping, permissions, exports, retention/erasure, integration writes, notification attempts and agent approvals/steps. Keep append-only, portable, permissioned history without secrets, raw payloads or personal values in permanent subject/actor labels. Erasure anonymizes attribution and retains count-only receipts; denied/stale writes cannot claim completion. Exportable organization audit and admin investigation must cover new team/provider paths.

  **Acceptance evidence:** Perform owner/member/agent and denied actions, restart/export/restore, erase an actor/project and inspect retained nonpersonal chronology, correct outcomes and absence of secret/erased identifiers.

### Deployment integrations

- **INT-001 — Vercel and Netlify deployment connectors.** Connect Vercel and Netlify projects so preview/production deploys, build identity and commit metadata automatically register in Spool, with environment mapping, artifact-upload guidance, rollback and failed-build visibility. Verify signed callbacks and reconcile missed events through least-privilege read-back. Account/repository disconnect stops work and queued old callbacks cannot recreate erased projects.

  **Acceptance evidence:** Deploy preview and production from each provider, upload artifacts, trigger a mapped error, fail/roll back a deployment and replay a callback; inspect one deployment identity and immediate disconnect/erasure fences.

## Push further

- **AI-001 — Controlled coding-agent handoff.** Cursor, GitHub Copilot and Claude can consume the same permissioned CLI/MCP packet, source/release links, gaps and current repair observations. The engineer selects project/repository scope, data fields, model/provider and budget; no telemetry instruction can expand them. Preview external data egress, retain audit, expire/revoke access and distinguish suggestions from confirmed causes. Read-only diagnosis never silently acquires repository write or production authority.

  **Acceptance evidence:** In each named agent client, request one issue packet, inspect its untrusted labels and missing coverage, refuse cross-project/mutation/prompt-injection requests, revoke the grant and confirm the next HTTP/CLI/MCP call is denied.

- **AI-002 — Generate and test the smallest reproduction.** An agent combines sanitized occurrence, exact release/build/symbol/source artifacts, breadcrumbs, trace/replay and owner-approved input to construct and minimize a deterministic reproduction. It tests competing causes and records counterexamples, failing input, environment and unsuccessful attempts in an isolated budgeted runner for each supported runtime/mobile target. No production credentials/network access are inherited; the owner approves repository scope, dependency/network policy and cost. Unsupported, not reproduced, failed and reproduced remain distinct.

  **Acceptance evidence:** Approve a real captured failure, inspect a minimal failing case and competing-cause experiment, run malicious telemetry/timeout/missing-artifact cases, and repeat against the fixed release; failed setup cannot become not-reproduced success.

- **AI-003 — Minimal code repair as a tested pull request.** From an approved reproduction and repository grant, an agent prepares the smallest source change and regression case, compares candidates, runs the buyer's relevant tests/type/build/security checks in isolation and opens a draft GitHub PR or GitLab merge request with failing-before/passing-after evidence, changed files, risks and unresolved cases. Configurable trigger policies can start investigation only within preapproved scope/budget. Humans approve provider write, expansion, merge and deployment; no automatic merge/deploy. Prompt content cannot authorize changes or bypass CI.

  **Acceptance evidence:** Authorize a scoped repair, observe a real source patch and regression failure-before/pass-after in a buyer repository, open one draft PR/MR, cancel/expire/revoke between steps and inspect durable results without unauthorized writes or duplicate branches.

- **AI-004 — Prove the released repair from live rates.** After human-approved merge/deploy and success criteria, an agent links the fix commit to actual deployed cohorts and exercises the failing customer flow. Compare before/after failure rates against observed comparable traffic, confidence/minimum exposure, adoption, sampling, old releases, capture loss, retention and grouping changes. Require the reproduction to pass and live evidence to support improvement; continue a bounded watch and reopen/escalate recurrence. Quiet or missing telemetry is inconclusive, never success. Show the exact denominator, window, counterexamples and reasons; the engineer may stop or revise criteria without rewriting observations.

  **Acceptance evidence:** Deploy a reviewed fix to only part of traffic, generate known healthy and failing flow exposure, introduce old-version errors and capture gaps, then restore coverage and recur; inspect rate comparison, inconclusive/reached/recurred states and one accountable recurrence follow-up.

- **AI-005 — Explain grouping proposals before applying.** An agent compares stack, release, route and trace variants, proposes split/merge/rule changes and shows exact affected events, counterexamples, ownership and alert/rate/repair-verification effects. An engineer previews and explicitly applies or rejects; undo preserves immutable originals and prior decisions. The model cannot silently rewrite a group, suppress alerting or manufacture better repair results.

  **Acceptance evidence:** Give the agent deceptively similar and rebuilt failures, inspect a proposal with counterexamples and rate/alert differences, reject then approve another, undo it and verify preserved originals and grouping-change coverage gaps.

- **AI-006 — Prevent related failures from recurring.** An agent mines accepted fixes, rejected hypotheses, production recurrence and approved repository code for shared violated invariants. It proposes reusable regression/property tests, identifies other affected call sites with evidence and counterexamples, and watches later releases for that invariant. Related-code guesses remain hypotheses. Engineers approve every new repository/service scope, test and patch; budgets, stop controls and known false positives stay visible.

  **Acceptance evidence:** Accept a repair, introduce a related failure at another authorized call site, inspect the inferred invariant and a failing regression case, reject a false positive, approve a scoped patch and observe later recurrence without automatic expansion.

- **AI-007 — Agent-runtime failure diagnosis and evaluated improvements.** For applications using LLM agents, correlate approved model/tool spans, latency, token/cost and sanitized failures with the application issue. Owner-configured detectors cluster failed production runs into improvement hypotheses; the agent investigates connected code in a sandbox and compares offline eval cases before suggesting a patch. Keep this scoped to diagnosing application failures; integrate dedicated agent analytics/evaluation systems for their broader job. Model choices, dataset consent, redaction, spend and promotion remain human-controlled.

  **Acceptance evidence:** Import/capture authorized failing tool/model runs, cluster a repeated fault with counterexamples, inspect cost/latency and error links, compare baseline/candidate on an approved offline dataset and refuse promotion or external data egress without approval.

## Retained erasure and correction custody

The following detail is part of BASE-DATA-003, ERR-006, ERR-008 and ERR-009, not an exclusion of future team, SDK or replay work. It describes the current declared stores and exact-identifier erasure policy. Team membership will extend permissions while preserving owner-only destructive confirmation and a verified authority successor. Every new personal-data store must join the cascade before its feature is accepted.

### Project-root scope under BASE-DATA-003

Implemented at `c279cd2d1fc02acfbc1160201782935e5324ddef`; GitHub Verify run 38215335300 passed `test:project-erasure` on that head. The owner's explicit project-root default is adopted: owner-only project erasure with a count-only preview, typed confirmation (the exact project identifier and the previewed plan) and a completion receipt with counts and times and no personal values. It runs on Base main's erasure helper (`src/modules/erasure`), declared in `src/domain/erasure/declaration.ts`; one helper line carries a correctness fix not yet on Base (docs/PROJECT-ERASURE.md).

Erasure deletes the project and all project events, issues, releases, repairs, repair checks, reproducers and their runs and run tokens, fix workflows, their step leases and receipts, exposure reports, artifacts, admission counters, alerts, alert destinations, alert rules, cursors, intents and history, mail jobs and attempts, project keys and their dev-minted marks, browser origins and guards, Sentry DSN aliases, integration rows in the project's namespace, Sentry-import targets and project-related development outbox content. Every deleted row gets a Base portability tombstone and stored transfer snapshots are invalidated in each batch. A Sentry-imported project's import-kit custody (its source records and plan entries) and every original export artifact that holds the project are withdrawn in the same import-kit batch as the cascade; other projects in such an artifact keep their native rows, but that artifact's bytes become unavailable to them too. Completion is measured today within five minutes at the current 10,000-event admission cap; the target is GOV-003's published retained volume, within 15 minutes for 150,000 retained events and at no less than 10,000 events a minute above that. Manual retention's visible cutoff, occurrence identities, guarded tombstones and deduplication horizon are unchanged. One erasure runs at a time; an erased project's identifier cannot be reused, since its tombstone would refuse every later archive holding the new project.

Retained scope exceptions, each named in the preview and receipt: the installation owner, claim ledgers and authentication state remain because project erasure preserves installation authority; unrelated projects remain because they lie outside the selected project; project audit entries survive with only their kind and time, their project reference replaced by a random label shared only with the receipt, so a database holder cannot join them back; receipts keep counts and times only; portability tombstones are keyed hashes. Owner self-erasure uses the separate workflow below; project erasure alone does not erase its retained owner. Selective owner erasure remains blocked for shared import custody and unsupported or ambiguous attribution.

Older archives are refused by the installation's restore fence, including archives holding only child rows, and by a fresh destination that first receives the post-erasure archive, or a later archive given with `rer import --journal`. A brand-new store given only an older archive has no later journal and cannot recognise the erasure. Already downloaded archives, delivered mail, remote provider records, D1 Time Travel and backups, raw database imports and destination secrets (`ALERT_DESTINATIONS`) lie outside the local fence; erasure reports these limits rather than claiming their deletion. The public-door proof is described in [ACCEPTANCE.md](ACCEPTANCE.md).

### Installation-owner scope under BASE-DATA-003

The authenticated owner can erase their own personal data through the native HTTP workflow in [OWNER-ERASURE.md](docs/OWNER-ERASURE.md): a count-only preview, then an explicit typed confirmation bound to the previewed plan, run on Base's erasure helper (`src/modules/erasure`) with the same resumable multi-step plan, portability tombstones and receipts as project erasure, declared in `src/domain/erasure/owner-declaration.ts`. The operation names only the signed-in owner; it cannot target another person.

Authority moves first. A real successor must accept an ordinary invitation; the first atomic step rechecks both parties' sessions, promotes the successor and retires the predecessor, so exactly one active owner remains and the successor signs in and manages normally. Without a verified successor the operation refuses with a plain reason and changes nothing. A change of authority or captured data before the first destructive commit cancels the untouched operation and releases its write freeze; committed partial deletion keeps the freeze until the final receipt and stays resumable.

Deleted: the owner's operator row, tokens, sessions, external identities, provider session data, step-up challenges, agent and deploy keys they minted, development marks, setup links, pending dependency approvals and their auth outbox mail. Retired: the bootstrap and managed claim ledgers keep only installation, time and completion facts, so the installation stays claimed and cannot be reopened; auth audit actors become random unjoinable labels. Captured data: exact known identifiers (operator ID, email, display name, bound IdP subjects) are replaced inside project names, issue titles, event messages, breadcrumbs and context, map JSON, diagnostics, repair notes, links and checks, alert titles and development outbox JSON, leaving every other value in the row intact; they match only as whole tokens, and an identifier found only inside a longer word or address is a hold. Project ingest keys keep ingesting and stay listed with their minter attribution cleared, and any key stays revocable afterwards. Inviting a different successor retires the earlier invitation and invitee. Other people's operators, sessions, grants and captured values are unchanged. Retained import custody, an identifier inside a structural key, and a display name shared with another person are explicit holds that refuse without mutation; prose that names the owner without an exact identifier, unknown aliases and encoded text are outside the declared scope. Receipts carry counts and times only.

Archives exported before the erasure are refused by the same restore fence as project erasure, including archives holding only captured child rows, by a fresh destination that first receives the post-erasure archive, and with `rer import --journal`. A brand-new store given only an older archive cannot recognise the erasure. Downloaded archives, delivered mail, identity-provider records, `OWNER_EMAIL` and alert destination configuration, D1 Time Travel and backups lie outside the local fence.

Owner writes admitted before the handoff: every owner write rechecks the writer's session inside its own D1 batch, before and after its statements (still unrevoked and unexpired, at the operator's current version, and that operator still the active owner). This covers project creation and credential minting, key revocation, browser origins, triage, assignment, suppression policies, corrections and their undo, alert rules and delivery resolution, and repair records; retention and restore apply the same check to every batch they commit. A predecessor request admitted before the handoff and completed after it is refused with `409 authority_changed` and changes no row.


Operator correction extension: exact retained-occurrence previews, revision/occurrence-fenced apply and undo, immutable original grouping provenance, operator-label assignment, event-time literal suppression receipts and release/environment deployment manifests are defined in [OPERATOR-CORRECTIONS.md](docs/OPERATOR-CORRECTIONS.md). Future capture still uses the default grouping algorithm; deployment manifests do not change the registered-sequence admission/regression contract. New tables are archived and project-erased; personal attribution in these immutable operator records causes an explicit owner-erasure hold.

Correction custody also applies to ERR-008 and ERR-009. Repair recurrence includes retained native admission membership, current membership, and exact occurrence membership during that repair's lifetime, even after further corrections or undo. Any applied or undone correction involving the issue leaves a `grouping_changed` coverage gap; without retained recurrence its observation is inconclusive, because future default grouping cannot establish the corrected group's coverage. Unaffected issues retain the existing successful-observation conditions. Retention follows each erased occurrence through its original, current and intermediate groups, withdraws those groups' historical alert headlines and local delivery copies, and cancels their pending delivery work in the same transaction. Current issue headlines come only from retained member occurrences. IDs/counts, repair snapshots, safe alert chronology, audit and restore fences remain; delivered mail and external provider records remain outside local retention.

## Status

Judged from the code and tests at commit `8f4d8fe3398de51567a0f0b04514c310e08547ba` (16 October 2026).

Evidence paths are repository-relative. Missing rows deliberately contain no implementation citation. Existing test files are proof specifications; their presence alone is not a run result. New acceptance journeys are targets, not invented test files. The older audit's exposure-verification row was done for its bounded local behavior; AI-004 is partial because it now also requires an agent and live rate comparison. Its previously missing email-test row is partial in ALERT-003 after reading current main.

| Clauses | Done | Partial | Missing | Total |
| --- | ---: | ---: | ---: | ---: |
| Published | 11 | 1 | 0 | 12 |
| Target and push further | 0 | 40 | 16 | 56 |
| All | 11 | 41 | 16 | 68 |

| Clause | Title | Kind | Status | Evidence | Gap |
| --- | --- | --- | --- | --- | --- |
| `ERR-001` | Bounded admission | published | done | `src/domain/admission.ts`; `src/domain/capture.ts`; `migrations/core/0008_occurrence_identity.sql`; `tests/product-runtime.mjs`; `tests/multilingual.mjs` | Python/Go tests use constructed envelopes; broad runtime SDK compatibility is SDK-001. Removing the 120-a-minute and 10,000-retained limits is GOV-003. |
| `ERR-002` | Atomic acceptance | published | done | `src/domain/admission.ts`; `src/domain/capture.ts`; `tests/product-runtime.mjs` |  |
| `ERR-003` | Scrub before storage | published | done | `src/domain/privacy.ts`; `src/domain/scrub.ts`; `tests/privacy-table.mjs` | Coverage is the present native/Sentry projection; new context and replay controls remain unfinished. |
| `ERR-004` | Separated authority | published | done | `src/domain/api.ts`; `src/domain/credential-fence.ts`; `src/domain/agent-access.ts`; `tests/agent-handoff.mjs`; `tests/dev-credentials.mjs` |  |
| `ERR-005` | Source map identity | published | done | `src/domain/artifacts.ts`; `src/domain/packet.ts`; `tests/triage-journey.mjs`; `tests/sentry-sdk.mjs` | Indexed maps, `sourceRoot` and published capacity are SRC-004; broader artifact processing is SRC-001 and SDK-002. |
| `ERR-006` | Audited triage and regression | published | done | `src/domain/admission.ts`; `src/domain/operators/grouping.ts`; `tests/alerts-native.mjs`; `tests/operator-flow.mjs`; `tests/operator-ui.mjs` | Today's Resolve records the current registered release as its watermark (resolve in the current release); same-release recurrence policies, next/named-release resolution, bulk actions and collaborative triage are TEAM-003, and plain Resolve and automatic Sentry releases are SDK-009. |
| `ERR-007` | Sanitized evidence packet | published | done | `src/domain/packet.ts`; `src/domain/agent-projection.ts`; `packages/agent/bin/spool-agent.mjs`; `tests/agent-handoff.mjs` |  |
| `ERR-008` | Loss counters and repair observation | published | done | `src/domain/repairs/observation.ts`; `src/domain/exposure/proof.ts`; `src/domain/sentry/client-reports.ts`; `tests/client-report-flow.mjs`; `tests/repair-recovery.mjs` | Windowed server receipts and SDK-reported error discards are explicit loss inputs; reported discarded errors prevent a quiet feed from proving a repair. Existing revision-fenced manual repair observation remains. Unknown pre-transport loss, full session denominators and live agent proof remain gaps. |
| `ERR-009` | Archive, restore and retention | published | done | `src/domain/archive.ts`; `src/domain/portability.ts`; `src/domain/retention.ts`; `scripts/installer/portability.mjs`; `tests/product-runtime.mjs`; `tests/agent-handoff.mjs` | This is the retained Base portability replacement: source/maps are portable; installation authority and executable grants are not. Scheduled policies are GOV-002; streaming archives above 10,000 events or 20 artifacts per project are GOV-003 and SRC-004. |
| `ERR-010` | Release gates | published | partial | `tests/triage-journey.mjs`; `tests/agent-handoff.mjs`; `tests/repair-recovery.mjs`; `tests/execution-journey.mjs` | Local journeys exist; full target, hosted/provider and native-device acceptance remain open. |
| `TEAM-001` | Organizations, members and project permissions | target | partial | `src/auth/session.server.ts`; `src/domain/owner-authority.ts`; `tests/owner-erasure.mjs` | Only one active owner and isolated projects exist. Successor invitation is transfer, not team membership. |
| `TEAM-002` | Accountable assignment and ownership rules | target | partial | `src/domain/operators/policies.ts`; `tests/operator-flow.mjs` | Current assignment is an operator label; authenticated teammates, CODEOWNERS and assignment notifications are absent. |
| `TEAM-003` | Collaborative lifecycle and bulk triage | target | partial | `src/domain/operators/bulk-triage.ts`; `src/ui/workbench/BulkTriage.tsx`; `tests/bulk-triage-flow.mjs`; `tests/investigation-flow.mjs`; `docs/BULK-TRIAGE.md` | Owner search results support page selection, preview and atomic acknowledge, reopen and resolve-in-current-registered-release actions, with project/revision/occurrence/authority fences and per-issue audit. Shared team discussion, member assignment, priority, next/named/plain resolution and collaborative recurrence/suppression policies remain absent. |
| `TEAM-004` | Issue short IDs and resolution from commits and pull requests | target | missing |  | No implementation proof yet: issue short IDs and resolution from commits and pull requests. |
| `TEAM-005` | Per-member notification settings and issue subscriptions | target | missing |  | No implementation proof yet: per-member notification settings and issue subscriptions. |
| `SDK-001` | Multi-language SDKs and DSN-compatible intake | target | partial | `src/domain/sentry/convert.ts`; `src/domain/sentry/routes.ts`; `packages/sdk/src`; `tests/sentry-sdk.mjs`; `tests/multilingual.mjs` | Real browser/Node SDK journeys only; Python/Go payload subsets are tested. Other runtimes and mobile SDKs are absent. |
| `SDK-002` | Native crashes and symbolication | target | missing |  | No implementation proof yet: native crashes and symbolication. |
| `SDK-003` | Automatic breadcrumbs and private context | target | partial | `src/domain/capture.ts`; `src/domain/sentry/convert.ts`; `packages/sdk/src/browser.ts`; `tests/privacy-table.mjs` | Supplied breadcrumb messages are stored; full automatic instrumentation and configurable context capture are absent. |
| `SDK-004` | User feedback attached to failures | target | partial | `src/domain/feedback/`; `src/ui/workbench/FeedbackPanel.tsx`; `tests/feedback-flow.mjs`; `tests/sentry-sdk.mjs`; `docs/FEEDBACK.md` | Text descriptions from modern Sentry feedback events/items and legacy user_report are scrubbed, retry-safe and linked to an exact retained occurrence or shown as standalone/unavailable-reference reports in the owner inbox. Issue review and withdrawal, archive restore, retention and project/owner erasure cover this store. Contact and attachments are excluded; a Spool consent/localization widget, contact/replies, screenshot review, broader abuse policy and end-user identity erasure remain absent. |
| `SDK-005` | Crash-free sessions and release health | target | partial | `src/domain/exposure/proof.ts`; `src/domain/exposure/intake.ts`; `tests/execution-journey.mjs` | Repair-specific exposure proof exists; general session intake and release-health dashboards do not. |
| `SDK-006` | Ingest-time inbound filters and allowed domains | target | partial | `src/domain/filters/`; `src/domain/admission.ts`; `src/domain/origins.ts`; `src/ui/workbench/InboundFilters.tsx`; `tests/inbound-filter-flow.mjs`; `docs/INBOUND-FILTERS.md` | Owner settings enable every named error filter, preview all retained events, show 24-hour per-filter counts and save audited revision-fenced policies. Native/Sentry error intake filters before groups, alerts and accepted usage; exact/wildcard Origin and Referer refusals return 403 without CORS. Creation names default-on extension/crawler filters; each can be disabled. Historical client IP and missing transport evidence cannot be reconstructed and preview names them unavailable; role-based maintainer authority and other SDK-007 data categories remain gaps. Rer-dev 1440/390 proof is in work/sd-cp-spool/shots/SDK-006/. |
| `SDK-007` | Sentry envelope item coverage from unmodified SDKs | target | partial | `src/domain/sentry/wire.ts`; `src/domain/sentry/routes.ts`; `src/domain/sentry/client-reports.ts`; `src/domain/feedback/`; `tests/sentry-http.mjs`; `tests/client-report-flow.mjs`; `tests/sentry-sdk.mjs` | Errors, text feedback and client_report items are routed after complete envelope framing. SDK-reported discards are counted by bounded reason/category and quantity, separately from accepted events; invalid reports are individually counted while valid companion errors continue. The real Node SDK flush emits reports through its normal transport; automatic browser lifecycle report emission remains unqualified. Unsupported categories remain diagnosed. Sessions, traces, profiles, replay, logs, metrics, check-ins, dedicated crash/security doors, category backpressure and full runtime qualification remain absent. |
| `SDK-008` | Sentry user identity policy, affected users and end-user erasure | target | partial | `src/domain/privacy.ts`; `src/domain/exposure/proof.ts`; `src/domain/sentry/convert.ts`; `tests/execution/exposure.mjs` | A native optional random installation `client` identifier is pattern-checked and counted as distinct affected users, with unattributed events counted separately, in repair exposure proof. The Sentry `user` interface is discarded at conversion with no policy or diagnostic; keep/hash/drop policy, the shared affected-user figure, user search keys, role scope and end-user erasure are absent. |
| `SDK-009` | Release identity from unmodified Sentry SDKs: no release, automatic releases, ordering and plain Resolve | target | partial | `src/domain/releases/`; `src/domain/admission.ts`; `src/domain/sentry/convert.ts`; `src/ui/workbench/ReleasePolicy.tsx`; `tests/release-identity-flow.mjs`; `docs/SENTRY-RELEASES.md` | Sentry errors accept no release, auto-create releases with first-seen/environment/SDK metadata and named dist records, enrich through native registration, and accept invalid or pattern-excluded names as none with counted notes. Settings preview and audit ordering/strict-policy changes; semantic release comparisons and independent observed boundaries protect current-release resolution. Plain owner and bulk resolution regresses once on the next accepted unsuppressed event in any release or none; no-release repeats after release resolution are counted visibly. Native v1 stays registered-only. Private restore preserves the new stores and project erasure declares them. Unmodified Node SDK no-release and inferred-SHA intake are qualified locally. Sessions, next/named resolution, sentry-cli/plugins, linked-commit association, policy-driven latest/list order, release-less alert conditions, full runtime/device qualification and migration reconciliation remain gaps. An explicit dist equal to the legacy no-dist marker still collides with absence; mixed semantic/nonsemantic total order needs ct clarification. |
| `SDK-010` | Sentry event fidelity: exception chains, frames, local variables, tags, contexts and request | target | partial | `src/domain/sentry/evidence.ts`; `src/domain/sentry/grouping.ts`; `src/ui/workbench/SentryEvent.tsx`; `tests/fidelity-flow.mjs`; `tests/multilingual.mjs` | Sanitized SDK exception chains/groups, up to 1,000 frames per stack with named middle omission, source context, bounded masked locals, tags/derived tags, contexts, allowed request headers, extra, threads and breadcrumbs are retained. Stackless messages and SDK fingerprints are admitted; private archive restore preserves rich evidence while agent packets exclude source/locals. Rer-dev 1440/390 proof covers selection, chains/groups, variables and raw copy. Project custom scrubbing rules, previewed agent field selection and full unmodified SDK/runtime qualification remain absent; user identity and raw request bodies/cookies are excluded with notes. Primary map lookup remains the existing 30-coordinate projection and SDK-011 wire limits remain. |
| `SDK-011` | Sentry intake encodings, published size limits and per-item refusal | target | partial | `src/domain/sentry/wire.ts`; `src/domain/sentry/routes.ts`; `tests/sentry-http.mjs`; `tests/sentry-sdk.mjs` | Uncompressed and `gzip` bodies are accepted with streamed, bounded decompression (the real Node SDK's gzip envelope and an HTTP gzip envelope are tested, and an over-limit gzip body gets 413). `deflate`, `br` and `zstd` are refused with 415 `unsupported_content_encoding`; the whole request is capped at 256 KiB compressed, 1 MiB expanded and 100 items, so any larger item or envelope is refused whole with `body_too_large`. Published Sentry-equal limits, per-item refusal, streamed heavy-item storage and per-reason refusal counting in usage and the dual run are absent. |
| `GROUP-001` | Explainable grouping and durable corrections | target | partial | `src/domain/grouping/`; `src/domain/sentry/grouping.ts`; `src/ui/workbench/GroupingRules.tsx`; `tests/grouping-rules-flow.mjs`; `tests/operator-flow.mjs`; `docs/GROUPING-RULES.md` | Owner project stack rules cover path/module/package/function/family and app/group actions; fingerprint rules cover any exception, message, level, logger, SDK and tags. Full retained-event preview, audited revision-fenced activation, immutable admission policy snapshots, contribution explanations and effective library-frame collapse are implemented for Sentry intake. Private archive restore preserves rules and snapshots; project erasure declares the store. Native v1 configurable rules, automatic rule-based historical regroup plans, Sentry rule migration and collaborative maintainer roles remain absent; existing explicit split/merge and undo preserve originals and repair gaps. |
| `FIND-001` | Search, facets and saved investigation views | target | partial | `src/domain/investigation/search.ts`; `src/domain/investigation/query.ts`; `src/ui/workbench/IssueList.tsx`; `tests/investigation-flow.mjs`; `tests/bulk-triage-flow.mjs`; `docs/INVESTIGATION.md`; `docs/BULK-TRIAGE.md` | Full retained-project search, combined event filters, whole-query facets/counts, cursor pages and bounded JSON/CSV exports work. The owner can select the displayed result page and preview atomic triage; counts and exports reflect the changes. Team/identity/priority search, collaborative review inbox and saved shared views remain absent. |
| `FIND-002` | Impact, trends and team reporting | target | partial | `src/domain/investigation/events.ts`; `src/ui/workbench/EventExplorer.tsx`; `src/ui/workbench/SentryEvent.tsx`; `src/ui/workbench/RepositorySource.tsx`; `tests/fidelity-flow.mjs`; `tests/repository-source-flow.mjs` | Owner detail selects exact retained occurrences with cursor pages, combined event queries, navigation, chain/context/raw evidence and all-issue tag counts. Selected source frames now link through explicit recorded mappings to exact repository revisions, distinct from SDK context and map coordinates. User/session analytics, custom dashboards, team reports, permissioned report exports and provider qualification remain absent; agent packets retain their minimized scope. |
| `SRC-001` | Build artifacts and CI pipelines | target | partial | `packages/sdk/src/sourcemaps/plugin.mjs`; `packages/sdk/src/sourcemaps/cli.mjs`; `src/domain/sentry/deploy-routes.ts`; `tests/sdk-package.mjs` | Vite/Rollup and Spool CLI upload exist; webpack/esbuild and sentry-cli/CI compatibility remain incomplete. |
| `SRC-002` | GitHub, GitLab and Bitbucket source context | target | partial | `src/domain/repository-links.ts`; `src/domain/operators/deployments.ts`; `src/ui/workbench/RepositorySource.tsx`; `tests/repository-source-flow.mjs`; `docs/REPOSITORY-SOURCE.md` | Explicit owner/deploy-recorded manifest and path mappings link Python/Go reported coordinates and mapped JavaScript originals to full-SHA GitHub, GitLab and Bitbucket file/line URLs. Missing/unsafe/unmatched evidence is visibly unmapped; links are labelled unverified and no source is fetched. Provider grants, access revocation, automatic mappings, suspect commits, CODEOWNERS, PR comments and real-account qualification remain absent. Existing GitHub draft-PR read-back uses a provider double. |
| `SRC-003` | sentry-cli and Sentry build-plugin compatibility | target | missing |  | No implementation proof yet: sentry-cli and Sentry build-plugin compatibility. |
| `SRC-004` | Published source map and debug-file capacity | target | partial | `src/domain/artifacts.ts`; `src/domain/sentry/deploy-routes.ts`; `packages/sdk/src/sourcemaps/cli.mjs`; `tests/sdk-package.mjs`; `tests/triage-journey.mjs` | Flat Source Map V3 upload with debug-ID and SHA-256 identity, and lookup, work. A project can hold at most 20 artifacts for its whole life (`artifact_budget` in `migrations/core/0008_occurrence_identity.sql`, returned as `429 Artifact budget exhausted`), each map at most 256 KiB with 100 sources and 128 KiB of mappings; indexed maps are refused and `sourceRoot` is not applied. Debug files, chunked assembly, streamed storage, per-file failure reasons, use-based artifact retention and source fetching are absent. |
| `REL-001` | Release and deployment history | target | partial | `src/domain/sentry/deploy-routes.ts`; `src/domain/operators/deployments.ts`; `tests/operator-flow.mjs` | Completed manifests and registered sequence exist; included-commit history, start/timeout workflow and full rollout history do not. |
| `REL-002` | Suspect change and release comparisons | target | partial | `src/domain/issue-release.ts`; `src/domain/exposure/proof.ts`; `tests/alerts-native.mjs`; `tests/execution-journey.mjs` | First/last release and recurrence correlation exist; commit causality and general release comparison do not. |
| `CTX-001` | Error-linked traces, logs and request performance | target | partial | `src/domain/otlp/routes.ts`; `src/domain/otlp/convert.ts`; `tests/otlp-flow.mjs` | Only bounded OTLP/HTTP JSON logs are accepted; trace/span identity is not persisted and traces/metrics are refused. Forwarders are absent. OTLP intake shares the 120-a-minute and 10,000-event caps that GOV-003 removes. |
| `CTX-002` | Privacy-controlled session replay | target | missing |  | No implementation proof yet: privacy-controlled session replay. |
| `CTX-003` | Third-party replay connectors | target | missing |  | No implementation proof yet: third-party replay connectors. |
| `CTX-004` | Performance issues from transactions and spans | target | missing |  | No implementation proof yet: performance issues from transactions and spans. |
| `ALERT-001` | Filtered rules and real anomaly detection | target | partial | `src/domain/alerts/rules.ts`; `src/domain/alerts/spikes.ts`; `tests/alerts-native.mjs` | New/regression and fixed-window count rules exist; historical baselines, crash-rate and rich routing filters are absent. |
| `ALERT-002` | Email, Slack, Teams, Oncall and PagerDuty | target | partial | `src/domain/alerts/mail.ts`; `src/domain/alerts/webhook.server.ts`; `src/domain/alerts/destination-management.server.ts`; `tests/alerts-egress.mjs`; `tests/alerts-ui.mjs` | Page-managed email and signed generic webhooks exist; native Slack/Teams/Oncall/PagerDuty adapters and real delivery qualification are missing. |
| `ALERT-003` | Destination tests, digests and snoozing | target | partial | `src/domain/alerts/destination-management.server.ts`; `tests/alerts-ui.mjs` | Main now tests page-managed email destinations. Cross-channel tests, digests, recipient confirmation and timed snoozing remain missing. |
| `ALERT-004` | Durable follow-up and visible automation failure | target | partial | `src/domain/alerts/dispatch.ts`; `src/domain/fixes/workflow.ts`; `tests/alerts-runtime.mjs`; `tests/alerts-immediate.mjs`; `tests/execution-journey.mjs` | Durable bounded alert/fix histories exist; alternate failure notification and universal overdue/lease follow-up are incomplete. |
| `ALERT-005` | Chat and paging triage actions and link unfurls | target | missing |  | No implementation proof yet: chat and paging triage actions and link unfurls. |
| `TRACK-001` | Vector, Jira and Linear issue sync | target | partial | `src/domain/repairs/routes.ts`; `src/domain/fixes/workflow.ts`; `tests/repair-recovery.mjs` | Manual repair links and draft PR history exist; tracker creation and status synchronization do not. |
| `MIG-001` | Sentry organization migration | target | partial | `src/domain/sentry-import/source.ts`; `src/domain/sentry-import/mapping.ts`; `tests/sentry-import.mjs`; `tests/sentry-sdk.mjs` | Custom bounded synthetic organization bundles and browser/Node DSNs work; real Sentry export/API reconciliation and full collection mapping are unproved. |
| `MIG-002` | Datadog Error Tracking migration | target | missing |  | No implementation proof yet: datadog Error Tracking migration. |
| `MIG-003` | Rollbar migration | target | missing |  | No implementation proof yet: rollbar migration. |
| `MIG-004` | Bugsnag migration | target | missing |  | No implementation proof yet: bugsnag migration. |
| `MIG-005` | Up-and-comer switching paths | target | missing |  | No implementation proof yet: up-and-comer switching paths. |
| `API-001` | Public APIs, attachments and full export | target | partial | `src/domain/investigation/search.ts`; `src/domain/agent-access.ts`; `tests/investigation-flow.mjs`; `docs/INVESTIGATION.md`; `src/domain/packet.ts`; `src/domain/archive.ts` | Versioned cursor-paginated issue query and matching bounded JSON/CSV exports now enforce owner or exact-project read authority, with explicit bounds/refusals. Rer-dev query/download proof passed at 1440/390; screenshots: `../shots/API-001/`. Occurrence/report/attachment APIs, attachment viewing audit and streaming exports/archives at GOV-003 volume remain absent. |
| `API-002` | Sentry Web API and Terraform compatibility | target | missing |  | No implementation proof yet: sentry Web API and Terraform compatibility. |
| `GOV-001` | Enterprise identity and administration | target | partial | `src/auth/bindings.ts`; `src/editor`; `tests/owner-claim.mjs`; `tests/owner-editor.mjs` | Native owner auth and linked identity bindings exist; organization SSO/SCIM and named enterprise-provider qualification do not. |
| `GOV-002` | Retention, residency and data custody | target | partial | `src/domain/retention.ts`; `src/domain/privacy.ts`; `tests/agent-handoff.mjs`; `tests/project-erasure.mjs` | Manual cutoff, tombstones and explicit gaps exist; scheduled/plan retention, residency choice and legal holds do not. |
| `GOV-003` | Published retention, retained volume and sustained intake rate | target | partial | `src/domain/retention.ts`; `src/domain/admission.ts`; `tests/agent-handoff.mjs`; `tests/project-erasure-cap.mjs` | An owner-run retention cutoff with occurrence tombstones, deduplication horizon and named retention gaps exists, and over-limit intake returns 429 with `Retry-After` and a counted outcome. Today `src/domain/admission.ts` and `migrations/core/0008_occurrence_identity.sql` refuse every new event once a project retains 10,000 (`retained_event_budget`) and above 120 a minute (`minute_budget`), and `src/domain/archive.ts` refuses a project archive above 10,000 events. Automatic expiry, published windows and plan volume, the published sustained rate and the named volume reasons are absent. |
| `BILL-001` | Predictable USD usage and ingestion budgets | target | partial | `src/domain/admission.ts`; `src/domain/sentry/client-reports.ts`; `src/ui/workbench/CaptureReceipts.tsx`; `tests/client-report-flow.mjs`; `docs/CLIENT-REPORTS.md`; `src/domain/filters/` | Settings and capture receipts separate accepted, filtered and SDK client-discarded quantities; SDK discards retain reason/category without creating issues, alerts or accepted units, and survive private archive restore. Commercial USD tiers, forecasts, billing, spike protection, key limits and the named volume reasons remain absent. Fixed 120-a-minute and 10,000-retained-event refusals remain GOV-003 gaps. |
| `UX-001` | Mobile operations and accessible investigation | target | partial | `src/ui/workbench`; `tests/triage-journey.mjs`; `tests/alerts-ui.mjs`; `tests/execution-journey.mjs` | Current single-owner core has responsive/Axe journeys; full target team/mobile workflows are absent. |
| `OPS-001` | Base commands and native admission | published | done | `scripts/installer/local.mjs`; `scripts/installer/portability.mjs`; `src/editor`; `tests/commands.mjs`; `tests/owner-claim.mjs`; `tests/dev-credentials.mjs` | Local command behavior is implemented; actual owner-account deployment and provider acceptance are separate gates. |
| `AUDIT-001` | Permanent privacy-safe action audit | target | partial | `src/domain/owner-authority.ts`; `src/domain/erasure`; `tests/project-erasure.mjs`; `tests/owner-erasure.mjs` | Current destructive/triage and workflow audit exists; organization-wide audit UI/export and future provider paths are incomplete. |
| `BASE-DATA-003` | Project and installation-owner erasure | published | done | `src/domain/erasure/declaration.ts`; `src/domain/erasure/owner-declaration.ts`; `src/modules/erasure`; `tests/project-erasure.mjs`; `tests/project-erasure-cap.mjs`; `tests/owner-erasure.mjs` | Done for the current declared store and exact-identifier policy, not unimplemented mobile/replay/feedback stores or external copies. Full retained limits appear in the custody section above. Completion is measured at 10,000 retained events within 300 seconds (`tests/project-erasure-cap.mjs`), today's admission limit; the 150,000-event obligation is GOV-003. |
| `INT-001` | Vercel and Netlify deployment connectors | target | missing |  | No implementation proof yet: vercel and Netlify deployment connectors. |
| `AI-001` | Controlled coding-agent handoff | push-further | partial | `packages/agent/src`; `src/domain/agent-access.ts`; `src/domain/packet.ts`; `tests/agent-handoff.mjs` | Generic read-only CLI/MCP is implemented; named client qualification and model-egress controls for automated work are incomplete. |
| `AI-002` | Generate and test the smallest reproduction | push-further | partial | `src/domain/reproducers/classify.ts`; `src/domain/reproducers/sandbox.ts`; `scripts/reproduce/runner.mjs`; `tests/execution-journey.mjs` | Bounded mapped-JavaScript reproductions with supplied calls exist; automatic minimization, cause experiments and non-JS/mobile reproduction do not. |
| `AI-003` | Minimal code repair as a tested pull request | push-further | partial | `src/domain/fixes/workflow.ts`; `src/domain/fixes/provider.ts`; `tests/execution-journey.mjs` | Current GitHub double journey opens a data-only artifact PR and observes merge/checks; generated code fixes, real repository acceptance and GitLab are absent. |
| `AI-004` | Prove the released repair from live rates | push-further | partial | `src/domain/exposure/proof.ts`; `src/domain/repairs/observation.ts`; `tests/execution-journey.mjs`; `tests/repair-recovery.mjs` | Current proof already compares baseline rates with a confidence bound, affected cohorts, rollout and capture gaps; autonomous production measurement, an ongoing watch and real-account acceptance remain missing. |
| `AI-005` | Explain grouping proposals before applying | push-further | partial | `src/domain/operators/grouping.ts`; `src/domain/operators/provenance.ts`; `tests/operator-flow.mjs` | Exact manual previews and reversible provenance exist; agent proposals/counterexamples and projected rate effects are absent. |
| `AI-006` | Prevent related failures from recurring | push-further | missing |  | No implementation proof yet: prevent related failures from recurring. |
| `AI-007` | Agent-runtime failure diagnosis and evaluated improvements | push-further | missing |  | No implementation proof yet: agent-runtime failure diagnosis and evaluated improvements. |
