{"id":"ops/era-c-exit-gate-evidence","relativePath":"ops/era-c-exit-gate-evidence.md","title":"Era C Exit-Gate Evidence Pack","markdown":"# Era C Exit-Gate Evidence Pack\n\nThis runbook turns the four Era C exit checks into automated, dated pass/fail evidence.\n\n## Scope\n\nThe evidence pack evaluates:\n\n1. SOTA §20.4 p95 SLOs over a rolling 30-day window: cached Record, cold Record, keyword+facet search, whitelisted SPARQL, and IIIF tile serving.\n2. Public-read uptime >= 99.9% over 30 days.\n3. >= 3 declared external consumers of `/api/activity` within the adoption window.\n4. SOTA §26 KPI thresholds.\n\n## Artifact schema\n\nCanonical schemas:\n\n- `docs/schemas/era-c-exit-gate-evidence.schema.json`\n- `docs/schemas/review-goals.schema.json`\n\nLatest and historical artifacts:\n\n- `artifacts/exit-gate/era-c-exit-gate-latest.json`\n- `artifacts/exit-gate/runs/era-c-exit-gate-<timestamp>.json`\n- `artifacts/exit-gate/trend-index.json`\n- `artifacts/long-term-evidence/long-term-evidence-runway-latest.json`\n- `artifacts/long-term-evidence/runs/long-term-evidence-runway-<timestamp>.json`\n- `artifacts/activity-syndication/activity-syndication-evidence-latest.json`\n- `artifacts/activity-syndication/runs/activity-syndication-evidence-<timestamp>.json`\n- `artifacts/review-goals/review-goals-latest.json`\n- `artifacts/review-goals/review-goals-latest.md`\n- `artifacts/review-goals/review-goals-<timestamp>.json`\n- `artifacts/review-goals/review-goals-<timestamp>.md`\n\nThe trend index stores each check's pass/fail state plus compact `failedChecks` reasons so agents can identify the next blocking gate without opening every historical run artifact.\n\nTop-level artifact status is intentionally three-state:\n\n- `passed` — all four Era C exit checks meet policy.\n- `blocked` — production exit is waiting on missing, insufficient, stale, or not-yet-external evidence. This is the expected state while collecting 30-day SLO/uptime samples, declared ActivityStreams consumers, or aggregate KPI exports.\n- `failed` — measured evidence is present and below policy target, such as a p95 breach, uptime below 99.9% with enough samples, or evidence-backed KPI values below threshold.\n\n`--check` exits non-zero for both `blocked` and `failed`; the distinction prevents missing evidence from being mistaken for a measured production-quality regression.\n\nThe long-term runway artifact is not a replacement for the exit gate. It is the operator-facing accumulation view for the 12+ month reliability/adoption work: it reports SLO sample count, distinct UTC observation days, public-read uptime sample depth, ignored placeholder consumer IDs, real declared external consumer IDs, and the next evidence needed before a 30-day production claim is defensible. Use `pnpm longterm:evidence:check` only when the runway is expected to be complete.\n\nWhen the runway is `failed` because old samples are still inside the 30-day\nwindow, run `pnpm longterm:evidence:maintenance`. It writes\n`artifacts/long-term-evidence/long-term-evidence-maintenance-latest.json` and\n`.md` with the failed SLO samples, failed uptime probe paths, passing distinct\ndays, per-failed-sample age-out dates, earliest failure-free window date,\ndaily evidence-ready date, and remaining external consumer count. The\nmaintenance report is read-only and must not be used to delete or launder\nhistorical evidence.\n\nRead the two runway dates separately:\n\n- `failureFreeWindowReadyAt` / `blockingEvidenceAgesOutAt` says when retained\n  failed or incomplete SLO/uptime samples leave the active policy window.\n- `dailyEvidenceReadyAt` says when enough distinct passing UTC days can exist\n  if every scheduled daily run passes.\n\nThe strict long-window claim can become green no earlier than the later of\nthose dates, and only if real external ActivityStreams and KPI/pilot evidence\nare also satisfied where selected by review goals.\n\nThe ActivityStreams syndication artifact complements adoption telemetry by\nrequiring the same real declared external consumer IDs to have active external\nHTTPS subscriptions covering `Create`, `Update`, and `Delete`. It prevents a\nfeed-read probe from being mistaken for durable callback workflow evidence.\n\nThe review-goals artifact is the 10/10 audit surface. It intentionally records\n`external-evidence-required` while production SLO/uptime history, real external\nActivityStreams consumers, and paid-pilot evidence are still absent; it should\nnot be run with `--check` in nightly evidence collection until those blockers\nare expected to be complete. Its JSON output is self-describing via the\n`$schema` value in `docs/schemas/review-goals.schema.json`, and each goal lists\n`evidenceArtifacts`, observed `evidenceArtifactStatuses`, and selected\n`evidenceArtifactCheckStatuses` so operators can jump straight to the latest\nlaunch, long-term, ActivityStreams, or pilot proof target and see whether that\nartifact or goal-specific check is missing, present, blocked, failed, or\npassing.\n\nSupporting performance trend input:\n\n- `artifacts/performance/k6-slo-summary.json`\n- `artifacts/performance/k6-slo-trend.json`\n\nNew performance samples must include all five p95 metrics. Legacy three-metric samples remain in history, but they fail the all-SLO gate until replaced by 30 days of complete samples.\nExit-gate reports include `missingMetricsInWindow` plus per-sample `metricDetails` so partial k6 summaries are distinguishable from actual p95 threshold breaches.\n\n## Policy and inputs\n\nPolicy thresholds:\n\n- `config/era-c-exit-gate-policy.json`\n  - Public-read uptime evidence must be recent: `uptime.maxSnapshotAgeHours` defaults to `48`, so stale or undated snapshots cannot satisfy the 99.9% gate.\n  - SOTA §26 KPI evidence must be recent: `kpis26.maxSnapshotAgeHours` defaults to `48`.\n  - AI query cost must come from real usage telemetry when `kpis26.requireAiQueryCostTelemetry` is `true`; fallback default cost values do not satisfy the KPI gate.\n  - AI query rows written by `/api/ai/query` include `costUsd`, `costCurrency`, `costSource`, and usage counts. The deterministic local planner records `costUsd: 0` with `costSource: \"deterministic-local-planner\"` because it makes no paid model call.\n\nMetric inputs:\n\n- Uptime snapshot: `monitoring/public-read-uptime.json`\n  - Exit-gate reports include the uptime `source` (`prometheus`, `probe`, or `unavailable`) and source `notes` so missing 30-day proof is actionable from the artifact alone.\n- Uptime history: `monitoring/public-read-uptime-history.json`\n  - Probe-based samples retain per-path `checks` with URL, HTTP status, duration, deployment-protection flag, and error text, so a failed public-read uptime sample can be traced back to `/`, `/api/health`, `/api/activity`, or any custom `METAMUSEUM_UPTIME_PROBE_PATHS` route.\n- KPI snapshot: `monitoring/kpis.json`\n  - Exit-gate reports include KPI `sources`, snapshot `notes`, and per-failed-metric details (`metric`, `source`, `reason`) so SOTA §26 blockers can be triaged from the evidence artifact.\n  - Optional aggregate KPI input: `monitoring/kpi-evidence.json` supplies production-like record-enrichment and reconciliation review counts before `pnpm monitoring:telemetry:sync`; see `docs/ops/kpi-evidence.md`.\n  - `aiQueryCostUsd` is calculated from `storage/ai-query-log.json`; run at least one representative `/api/ai/query` request in the target environment before telemetry sync so the source is no longer the fallback default.\n- Activity adoption telemetry: `storage/activity-consumers.json`\n  - Declared consumer rows must preserve `class: \"declared\"` and `declaredId`; the exit-gate normalizer intentionally ignores derived fingerprints for adoption credit.\n  - Proof command: `pnpm activity:adoption:probe` records one partner-owned declared consumer and writes `artifacts/activity-adoption/activity-adoption-proof-latest.json`.\n  - Matrix proof command: `pnpm activity:adoption:matrix` records/checks three partner-owned declared consumers and writes `artifacts/activity-adoption/activity-adoption-matrix-latest.json`.\n- Activity subscription telemetry: `storage/activity-subscriptions.json`\n  - Durable workflow proof command: `pnpm activity:syndication:evidence` ties the required declared consumer IDs to active external HTTPS callback subscriptions and writes `artifacts/activity-syndication/activity-syndication-evidence-latest.json`.\n\nActivity adoption counts only declared external consumers (`x-linked-art-consumer-id`). Derived fingerprints remain useful diagnostics, but they do not satisfy the external Linked Art systems exit gate.\nSee `docs/ops/activity-adoption-proof.md` for partner hand-off instructions and placeholder-ID safeguards.\n\n## Commands\n\nRefresh telemetry snapshots first:\n\n```bash\npnpm monitoring:telemetry:sync\n```\n\nFor manual public evidence refreshes, prefer the public helper so local\n`.env.local` values cannot redirect uptime probes to `localhost`:\n\n```bash\npnpm monitoring:telemetry:public\n```\n\nGenerate evidence report (always writes artifact):\n\n```bash\npnpm era-c:exit-gate:evidence\n```\n\nGenerate evidence from a freshly probed public uptime snapshot:\n\n```bash\npnpm era-c:exit-gate:public\n```\n\nGenerate the long-term accumulation runway report:\n\n```bash\npnpm longterm:evidence\n```\n\nGenerate the read-only maintenance report for failed or incomplete windows:\n\n```bash\npnpm longterm:evidence:maintenance\n```\n\nFor manual public runway refreshes, regenerate the public telemetry and Era C\nsnapshot first; this command also publishes the maintenance report:\n\n```bash\npnpm longterm:evidence:public\n```\n\nGenerate the durable ActivityStreams syndication report:\n\n```bash\npnpm activity:syndication:evidence\n```\n\nScan canonical upstream records for real ActivityStreams tombstones:\n\n```bash\npnpm activity:tombstone:scan\n```\n\nThe tombstone scanner is provider-aware: it reports supported, conditional, skipped, and duplicate upstream targets across the stored corpus, while `Delete` evidence remains strict and is written only after a genuine upstream `404`/`410` appears.\n\nGenerate the 10/10 review-goals audit and enforce local gates:\n\n```bash\npnpm review:goals:local\n```\n\nEnforce as a hard gate (non-zero exit on any failed check):\n\n```bash\npnpm era-c:exit-gate:check\n```\n\n## Nightly automation\n\nWorkflow:\n\n- `.github/workflows/era-c-exit-gate-evidence.yml`\n\nNightly job does:\n\n1. Run `pnpm k6:slo` against the deployed target when `METAMUSEUM_EVIDENCE_BASE_URL` and `METAMUSEUM_EVIDENCE_IIIF_TILE_URL` are configured; otherwise run local `pnpm k6:slo:ci` as a fallback.\n2. Seed one deployed `/api/ai/query` request when `METAMUSEUM_EVIDENCE_BASE_URL` is configured so cost telemetry can flow into SOTA §26 KPI evidence.\n3. Probe the declared `/api/activity` adoption matrix when `METAMUSEUM_ACTIVITY_CONSUMER_IDS` is configured; fall back to one declared consumer when only `METAMUSEUM_ACTIVITY_CONSUMER_ID` is configured.\n4. Run `pnpm era-c:exit-gate:public` with production public-read probes.\n5. Run `pnpm longterm:evidence` to publish a compact accumulation/runway artifact.\n6. Run `pnpm longterm:evidence:maintenance` to publish the read-only repair report that names failed SLO/uptime samples, their age-out dates, clean passing days, daily evidence-ready date, failure-free window date, and remaining external-consumer count.\n7. Run `pnpm activity:syndication:evidence` to publish durable consumer/subscription evidence.\n8. Run `pnpm activity:tombstone:scan` to keep the real upstream `404`/`410` watch fresh without writing a fake `Delete` row.\n9. Run `pnpm review:goals:local` to publish the 10/10 audit artifact and fail only when local commands/docs/tests are incomplete; expected external-evidence blockers remain visible in the artifact.\n10. Commit compact rolling evidence inputs back to `main` when the workflow runs on `main`, including the k6 trend, uptime history, latest long-term runway, latest long-term maintenance report, latest exit-gate, latest syndication, latest tombstone scan, latest review-goals, and activity consumer/subscription files.\n11. Upload exit-gate, long-term runway/maintenance, activity-syndication, review-goals, performance, activity-adoption, uptime, and KPI artifacts.\n\nThe workflow always uploads artifacts so failed checks still produce dated proof.\nOnly compact rolling inputs and latest summaries are committed; timestamped run\nartifacts remain upload artifacts so the repository does not grow by one full\nevidence packet per night. The committed allowlist includes the k6 trend,\npublic-read uptime history, latest runway, latest maintenance JSON/Markdown,\nlatest Era C exit-gate packet, latest review-goals packet, latest\nActivityStreams evidence, latest tombstone scan, and activity\nconsumer/subscription stores. The\nworkflow has `contents: write`, a single concurrency group, a main-branch guard,\nand no push trigger, so the bot commit can accumulate the 30-day windows\nwithout creating a workflow loop.\n\nDeployed-target workflow variables:\n\n- `METAMUSEUM_EVIDENCE_BASE_URL` — deployed app URL used as `BASE_URL` for k6, AI query seeding, and adoption proof.\n- `METAMUSEUM_EVIDENCE_IIIF_TILE_URL` — CDN-backed IIIF tile used as `IIIF_TILE_URL` for the tile-serving p95 check.\n- `METAMUSEUM_EVIDENCE_SPARQL_URL` — whitelisted SPARQL endpoint used as `SPARQL_URL` by `pnpm k6:slo`.\n- `METAMUSEUM_EVIDENCE_SPARQL_QUERY` — optional whitelisted SPARQL query override for `pnpm k6:slo`.\n- `METAMUSEUM_EVIDENCE_AI_QUERY` — optional representative query for the deployed `/api/ai/query` telemetry seed.\n- `METAMUSEUM_ACTIVITY_CONSUMER_IDS` — comma-separated partner-owned declared consumer IDs for the full adoption matrix proof.\n- `METAMUSEUM_ACTIVITY_CONSUMER_ID` — partner-owned declared consumer ID for the single-consumer adoption proof fallback.\n\n## Telemetry source configuration\n\nPrimary uptime source (recommended):\n\n- `METAMUSEUM_UPTIME_PROMETHEUS_URL`\n- `METAMUSEUM_UPTIME_PROMQL_AVAILABILITY`\n- `METAMUSEUM_UPTIME_PROMQL_SAMPLE_COUNT`\n\nGitHub Actions wiring:\n\n- secret: `METAMUSEUM_UPTIME_PROMETHEUS_URL`\n- repo/org var: `METAMUSEUM_UPTIME_PROMQL_AVAILABILITY`\n- repo/org var: `METAMUSEUM_UPTIME_PROMQL_SAMPLE_COUNT`\n\nFallback uptime source:\n\n- `METAMUSEUM_PUBLIC_READ_BASE_URL`\n- `METAMUSEUM_UPTIME_PROBE_PATHS`\n- `METAMUSEUM_UPTIME_PROBE_TIMEOUT_MS`\n\nManual public probe helper:\n\n- `pnpm monitoring:telemetry:public` runs `scripts/monitoring-telemetry-sync.ts --probe-base-url=https://www.metamuseum.org --require-probe`.\n- `pnpm era-c:exit-gate:public` runs the same forced public telemetry refresh before writing the Era C exit-gate artifact.\n\nWindow controls:\n\n- `METAMUSEUM_UPTIME_WINDOW_DAYS` (default `30`)\n- `METAMUSEUM_UPTIME_HISTORY_RETENTION_DAYS` (default `45`)\n","sections":[{"level":2,"heading":"Scope","anchor":"scope"},{"level":2,"heading":"Artifact schema","anchor":"artifact-schema"},{"level":2,"heading":"Policy and inputs","anchor":"policy-and-inputs"},{"level":2,"heading":"Commands","anchor":"commands"},{"level":2,"heading":"Nightly automation","anchor":"nightly-automation"},{"level":2,"heading":"Telemetry source configuration","anchor":"telemetry-source-configuration"}],"html":"<h1 id=\"era-c-exit-gate-evidence-pack\">Era C Exit-Gate Evidence Pack</h1>\n<p>This runbook turns the four Era C exit checks into automated, dated pass/fail evidence.</p>\n<h2 id=\"scope\">Scope</h2>\n<p>The evidence pack evaluates:</p>\n<ol><li>SOTA §20.4 p95 SLOs over a rolling 30-day window: cached Record, cold Record, keyword+facet search, whitelisted SPARQL, and IIIF tile serving.</li></ol>\n<ol><li>Public-read uptime &gt;= 99.9% over 30 days.</li></ol>\n<ol><li>&gt;= 3 declared external consumers of `/api/activity` within the adoption window.</li></ol>\n<ol><li>SOTA §26 KPI thresholds.</li></ol>\n<h2 id=\"artifact-schema\">Artifact schema</h2>\n<p>Canonical schemas:</p>\n<ul><li>`docs/schemas/era-c-exit-gate-evidence.schema.json`</li><li>`docs/schemas/review-goals.schema.json`</li></ul>\n<p>Latest and historical artifacts:</p>\n<ul><li>`artifacts/exit-gate/era-c-exit-gate-latest.json`</li><li>`artifacts/exit-gate/runs/era-c-exit-gate-&lt;timestamp&gt;.json`</li><li>`artifacts/exit-gate/trend-index.json`</li><li>`artifacts/long-term-evidence/long-term-evidence-runway-latest.json`</li><li>`artifacts/long-term-evidence/runs/long-term-evidence-runway-&lt;timestamp&gt;.json`</li><li>`artifacts/activity-syndication/activity-syndication-evidence-latest.json`</li><li>`artifacts/activity-syndication/runs/activity-syndication-evidence-&lt;timestamp&gt;.json`</li><li>`artifacts/review-goals/review-goals-latest.json`</li><li>`artifacts/review-goals/review-goals-latest.md`</li><li>`artifacts/review-goals/review-goals-&lt;timestamp&gt;.json`</li><li>`artifacts/review-goals/review-goals-&lt;timestamp&gt;.md`</li></ul>\n<p>The trend index stores each check&#39;s pass/fail state plus compact `failedChecks` reasons so agents can identify the next blocking gate without opening every historical run artifact.</p>\n<p>Top-level artifact status is intentionally three-state:</p>\n<ul><li>`passed` — all four Era C exit checks meet policy.</li><li>`blocked` — production exit is waiting on missing, insufficient, stale, or not-yet-external evidence. This is the expected state while collecting 30-day SLO/uptime samples, declared ActivityStreams consumers, or aggregate KPI exports.</li><li>`failed` — measured evidence is present and below policy target, such as a p95 breach, uptime below 99.9% with enough samples, or evidence-backed KPI values below threshold.</li></ul>\n<p>`--check` exits non-zero for both `blocked` and `failed`; the distinction prevents missing evidence from being mistaken for a measured production-quality regression.</p>\n<p>The long-term runway artifact is not a replacement for the exit gate. It is the operator-facing accumulation view for the 12+ month reliability/adoption work: it reports SLO sample count, distinct UTC observation days, public-read uptime sample depth, ignored placeholder consumer IDs, real declared external consumer IDs, and the next evidence needed before a 30-day production claim is defensible. Use `pnpm longterm:evidence:check` only when the runway is expected to be complete.</p>\n<p>When the runway is `failed` because old samples are still inside the 30-day</p>\n<p>window, run `pnpm longterm:evidence:maintenance`. It writes</p>\n<p>`artifacts/long-term-evidence/long-term-evidence-maintenance-latest.json` and</p>\n<p>`.md` with the failed SLO samples, failed uptime probe paths, passing distinct</p>\n<p>days, per-failed-sample age-out dates, earliest failure-free window date,</p>\n<p>daily evidence-ready date, and remaining external consumer count. The</p>\n<p>maintenance report is read-only and must not be used to delete or launder</p>\n<p>historical evidence.</p>\n<p>Read the two runway dates separately:</p>\n<p>  failed or incomplete SLO/uptime samples leave the active policy window.</p>\n<p>  if every scheduled daily run passes.</p>\n<ul><li>`failureFreeWindowReadyAt` / `blockingEvidenceAgesOutAt` says when retained</li><li>`dailyEvidenceReadyAt` says when enough distinct passing UTC days can exist</li></ul>\n<p>The strict long-window claim can become green no earlier than the later of</p>\n<p>those dates, and only if real external ActivityStreams and KPI/pilot evidence</p>\n<p>are also satisfied where selected by review goals.</p>\n<p>The ActivityStreams syndication artifact complements adoption telemetry by</p>\n<p>requiring the same real declared external consumer IDs to have active external</p>\n<p>HTTPS subscriptions covering `Create`, `Update`, and `Delete`. It prevents a</p>\n<p>feed-read probe from being mistaken for durable callback workflow evidence.</p>\n<p>The review-goals artifact is the 10/10 audit surface. It intentionally records</p>\n<p>`external-evidence-required` while production SLO/uptime history, real external</p>\n<p>ActivityStreams consumers, and paid-pilot evidence are still absent; it should</p>\n<p>not be run with `--check` in nightly evidence collection until those blockers</p>\n<p>are expected to be complete. Its JSON output is self-describing via the</p>\n<p>`$schema` value in `docs/schemas/review-goals.schema.json`, and each goal lists</p>\n<p>`evidenceArtifacts`, observed `evidenceArtifactStatuses`, and selected</p>\n<p>`evidenceArtifactCheckStatuses` so operators can jump straight to the latest</p>\n<p>launch, long-term, ActivityStreams, or pilot proof target and see whether that</p>\n<p>artifact or goal-specific check is missing, present, blocked, failed, or</p>\n<p>passing.</p>\n<p>Supporting performance trend input:</p>\n<ul><li>`artifacts/performance/k6-slo-summary.json`</li><li>`artifacts/performance/k6-slo-trend.json`</li></ul>\n<p>New performance samples must include all five p95 metrics. Legacy three-metric samples remain in history, but they fail the all-SLO gate until replaced by 30 days of complete samples.</p>\n<p>Exit-gate reports include `missingMetricsInWindow` plus per-sample `metricDetails` so partial k6 summaries are distinguishable from actual p95 threshold breaches.</p>\n<h2 id=\"policy-and-inputs\">Policy and inputs</h2>\n<p>Policy thresholds:</p>\n<ul><li>`config/era-c-exit-gate-policy.json`</li><li>Public-read uptime evidence must be recent: `uptime.maxSnapshotAgeHours` defaults to `48`, so stale or undated snapshots cannot satisfy the 99.9% gate.</li><li>SOTA §26 KPI evidence must be recent: `kpis26.maxSnapshotAgeHours` defaults to `48`.</li><li>AI query cost must come from real usage telemetry when `kpis26.requireAiQueryCostTelemetry` is `true`; fallback default cost values do not satisfy the KPI gate.</li><li>AI query rows written by `/api/ai/query` include `costUsd`, `costCurrency`, `costSource`, and usage counts. The deterministic local planner records `costUsd: 0` with `costSource: &quot;deterministic-local-planner&quot;` because it makes no paid model call.</li></ul>\n<p>Metric inputs:</p>\n<ul><li>Uptime snapshot: `monitoring/public-read-uptime.json`</li><li>Exit-gate reports include the uptime `source` (`prometheus`, `probe`, or `unavailable`) and source `notes` so missing 30-day proof is actionable from the artifact alone.</li><li>Uptime history: `monitoring/public-read-uptime-history.json`</li><li>Probe-based samples retain per-path `checks` with URL, HTTP status, duration, deployment-protection flag, and error text, so a failed public-read uptime sample can be traced back to `/`, `/api/health`, `/api/activity`, or any custom `METAMUSEUM_UPTIME_PROBE_PATHS` route.</li><li>KPI snapshot: `monitoring/kpis.json`</li><li>Exit-gate reports include KPI `sources`, snapshot `notes`, and per-failed-metric details (`metric`, `source`, `reason`) so SOTA §26 blockers can be triaged from the evidence artifact.</li><li>Optional aggregate KPI input: `monitoring/kpi-evidence.json` supplies production-like record-enrichment and reconciliation review counts before `pnpm monitoring:telemetry:sync`; see `docs/ops/kpi-evidence.md`.</li><li>`aiQueryCostUsd` is calculated from `storage/ai-query-log.json`; run at least one representative `/api/ai/query` request in the target environment before telemetry sync so the source is no longer the fallback default.</li><li>Activity adoption telemetry: `storage/activity-consumers.json`</li><li>Declared consumer rows must preserve `class: &quot;declared&quot;` and `declaredId`; the exit-gate normalizer intentionally ignores derived fingerprints for adoption credit.</li><li>Proof command: `pnpm activity:adoption:probe` records one partner-owned declared consumer and writes `artifacts/activity-adoption/activity-adoption-proof-latest.json`.</li><li>Matrix proof command: `pnpm activity:adoption:matrix` records/checks three partner-owned declared consumers and writes `artifacts/activity-adoption/activity-adoption-matrix-latest.json`.</li><li>Activity subscription telemetry: `storage/activity-subscriptions.json`</li><li>Durable workflow proof command: `pnpm activity:syndication:evidence` ties the required declared consumer IDs to active external HTTPS callback subscriptions and writes `artifacts/activity-syndication/activity-syndication-evidence-latest.json`.</li></ul>\n<p>Activity adoption counts only declared external consumers (`x-linked-art-consumer-id`). Derived fingerprints remain useful diagnostics, but they do not satisfy the external Linked Art systems exit gate.</p>\n<p>See `docs/ops/activity-adoption-proof.md` for partner hand-off instructions and placeholder-ID safeguards.</p>\n<h2 id=\"commands\">Commands</h2>\n<p>Refresh telemetry snapshots first:</p>\n<pre><code>\npnpm monitoring:telemetry:sync\n</code></pre>\n<p>For manual public evidence refreshes, prefer the public helper so local</p>\n<p>`.env.local` values cannot redirect uptime probes to `localhost`:</p>\n<pre><code>\npnpm monitoring:telemetry:public\n</code></pre>\n<p>Generate evidence report (always writes artifact):</p>\n<pre><code>\npnpm era-c:exit-gate:evidence\n</code></pre>\n<p>Generate evidence from a freshly probed public uptime snapshot:</p>\n<pre><code>\npnpm era-c:exit-gate:public\n</code></pre>\n<p>Generate the long-term accumulation runway report:</p>\n<pre><code>\npnpm longterm:evidence\n</code></pre>\n<p>Generate the read-only maintenance report for failed or incomplete windows:</p>\n<pre><code>\npnpm longterm:evidence:maintenance\n</code></pre>\n<p>For manual public runway refreshes, regenerate the public telemetry and Era C</p>\n<p>snapshot first; this command also publishes the maintenance report:</p>\n<pre><code>\npnpm longterm:evidence:public\n</code></pre>\n<p>Generate the durable ActivityStreams syndication report:</p>\n<pre><code>\npnpm activity:syndication:evidence\n</code></pre>\n<p>Scan canonical upstream records for real ActivityStreams tombstones:</p>\n<pre><code>\npnpm activity:tombstone:scan\n</code></pre>\n<p>The tombstone scanner is provider-aware: it reports supported, conditional, skipped, and duplicate upstream targets across the stored corpus, while `Delete` evidence remains strict and is written only after a genuine upstream `404`/`410` appears.</p>\n<p>Generate the 10/10 review-goals audit and enforce local gates:</p>\n<pre><code>\npnpm review:goals:local\n</code></pre>\n<p>Enforce as a hard gate (non-zero exit on any failed check):</p>\n<pre><code>\npnpm era-c:exit-gate:check\n</code></pre>\n<h2 id=\"nightly-automation\">Nightly automation</h2>\n<p>Workflow:</p>\n<ul><li>`.github/workflows/era-c-exit-gate-evidence.yml`</li></ul>\n<p>Nightly job does:</p>\n<ol><li>Run `pnpm k6:slo` against the deployed target when `METAMUSEUM_EVIDENCE_BASE_URL` and `METAMUSEUM_EVIDENCE_IIIF_TILE_URL` are configured; otherwise run local `pnpm k6:slo:ci` as a fallback.</li></ol>\n<ol><li>Seed one deployed `/api/ai/query` request when `METAMUSEUM_EVIDENCE_BASE_URL` is configured so cost telemetry can flow into SOTA §26 KPI evidence.</li></ol>\n<ol><li>Probe the declared `/api/activity` adoption matrix when `METAMUSEUM_ACTIVITY_CONSUMER_IDS` is configured; fall back to one declared consumer when only `METAMUSEUM_ACTIVITY_CONSUMER_ID` is configured.</li></ol>\n<ol><li>Run `pnpm era-c:exit-gate:public` with production public-read probes.</li></ol>\n<ol><li>Run `pnpm longterm:evidence` to publish a compact accumulation/runway artifact.</li></ol>\n<ol><li>Run `pnpm longterm:evidence:maintenance` to publish the read-only repair report that names failed SLO/uptime samples, their age-out dates, clean passing days, daily evidence-ready date, failure-free window date, and remaining external-consumer count.</li></ol>\n<ol><li>Run `pnpm activity:syndication:evidence` to publish durable consumer/subscription evidence.</li></ol>\n<ol><li>Run `pnpm activity:tombstone:scan` to keep the real upstream `404`/`410` watch fresh without writing a fake `Delete` row.</li></ol>\n<ol><li>Run `pnpm review:goals:local` to publish the 10/10 audit artifact and fail only when local commands/docs/tests are incomplete; expected external-evidence blockers remain visible in the artifact.</li></ol>\n<ol><li>Commit compact rolling evidence inputs back to `main` when the workflow runs on `main`, including the k6 trend, uptime history, latest long-term runway, latest long-term maintenance report, latest exit-gate, latest syndication, latest tombstone scan, latest review-goals, and activity consumer/subscription files.</li></ol>\n<ol><li>Upload exit-gate, long-term runway/maintenance, activity-syndication, review-goals, performance, activity-adoption, uptime, and KPI artifacts.</li></ol>\n<p>The workflow always uploads artifacts so failed checks still produce dated proof.</p>\n<p>Only compact rolling inputs and latest summaries are committed; timestamped run</p>\n<p>artifacts remain upload artifacts so the repository does not grow by one full</p>\n<p>evidence packet per night. The committed allowlist includes the k6 trend,</p>\n<p>public-read uptime history, latest runway, latest maintenance JSON/Markdown,</p>\n<p>latest Era C exit-gate packet, latest review-goals packet, latest</p>\n<p>ActivityStreams evidence, latest tombstone scan, and activity</p>\n<p>consumer/subscription stores. The</p>\n<p>workflow has `contents: write`, a single concurrency group, a main-branch guard,</p>\n<p>and no push trigger, so the bot commit can accumulate the 30-day windows</p>\n<p>without creating a workflow loop.</p>\n<p>Deployed-target workflow variables:</p>\n<ul><li>`METAMUSEUM_EVIDENCE_BASE_URL` — deployed app URL used as `BASE_URL` for k6, AI query seeding, and adoption proof.</li><li>`METAMUSEUM_EVIDENCE_IIIF_TILE_URL` — CDN-backed IIIF tile used as `IIIF_TILE_URL` for the tile-serving p95 check.</li><li>`METAMUSEUM_EVIDENCE_SPARQL_URL` — whitelisted SPARQL endpoint used as `SPARQL_URL` by `pnpm k6:slo`.</li><li>`METAMUSEUM_EVIDENCE_SPARQL_QUERY` — optional whitelisted SPARQL query override for `pnpm k6:slo`.</li><li>`METAMUSEUM_EVIDENCE_AI_QUERY` — optional representative query for the deployed `/api/ai/query` telemetry seed.</li><li>`METAMUSEUM_ACTIVITY_CONSUMER_IDS` — comma-separated partner-owned declared consumer IDs for the full adoption matrix proof.</li><li>`METAMUSEUM_ACTIVITY_CONSUMER_ID` — partner-owned declared consumer ID for the single-consumer adoption proof fallback.</li></ul>\n<h2 id=\"telemetry-source-configuration\">Telemetry source configuration</h2>\n<p>Primary uptime source (recommended):</p>\n<ul><li>`METAMUSEUM_UPTIME_PROMETHEUS_URL`</li><li>`METAMUSEUM_UPTIME_PROMQL_AVAILABILITY`</li><li>`METAMUSEUM_UPTIME_PROMQL_SAMPLE_COUNT`</li></ul>\n<p>GitHub Actions wiring:</p>\n<ul><li>secret: `METAMUSEUM_UPTIME_PROMETHEUS_URL`</li><li>repo/org var: `METAMUSEUM_UPTIME_PROMQL_AVAILABILITY`</li><li>repo/org var: `METAMUSEUM_UPTIME_PROMQL_SAMPLE_COUNT`</li></ul>\n<p>Fallback uptime source:</p>\n<ul><li>`METAMUSEUM_PUBLIC_READ_BASE_URL`</li><li>`METAMUSEUM_UPTIME_PROBE_PATHS`</li><li>`METAMUSEUM_UPTIME_PROBE_TIMEOUT_MS`</li></ul>\n<p>Manual public probe helper:</p>\n<ul><li>`pnpm monitoring:telemetry:public` runs `scripts/monitoring-telemetry-sync.ts --probe-base-url=https://www.metamuseum.org --require-probe`.</li><li>`pnpm era-c:exit-gate:public` runs the same forced public telemetry refresh before writing the Era C exit-gate artifact.</li></ul>\n<p>Window controls:</p>\n<ul><li>`METAMUSEUM_UPTIME_WINDOW_DAYS` (default `30`)</li><li>`METAMUSEUM_UPTIME_HISTORY_RETENTION_DAYS` (default `45`)</li></ul>","updatedAt":"2018-10-20T01:46:40.000Z","checksum":"4fdaaa29056aaadf2576dbac2f116d5137f0fe9c4b9b7491c15c945befa23c32","checksumPrefix":"4fdaaa29056a","anchorCount":6,"lineCount":265,"rawUrl":"/api/docs/content?path=ops%2Fera-c-exit-gate-evidence.md","htmlUrl":"/docs?doc=ops%2Fera-c-exit-gate-evidence.md","apiUrl":"/api/docs/content?path=ops%2Fera-c-exit-gate-evidence.md"}