← Documentation home

Canonical Markdown source · Oct 20, 2018

Activity Feed Adoption Proof Runbook

ops/activity-adoption-proof.md · 296 lines · SHA-256 b3c1d8e3399b

This runbook supports the Era C exit-gate target: at least three declared external Linked Art systems consume `/api/activity` within the adoption window.

The command below records a partner-owned declared consumer ID by sending `x-linked-art-consumer-id` to `/api/activity`, then reads `/api/activity/readiness` to confirm the deployment observed that declared external consumer.

Command


BASE_URL=https://<deploy-host> \
METAMUSEUM_ACTIVITY_CONSUMER_ID=<partner-owned-consumer-id> \
pnpm activity:adoption:probe

Equivalent explicit form:


pnpm activity:adoption:probe -- --base-url https://<deploy-host> --consumer-id <partner-owned-consumer-id>

To prove the full three-consumer checkpoint in one run after all partner IDs are known:


pnpm activity:partner-pack -- \
  --base-url https://<deploy-host> \
  --consumer-id wikibase-cloud-prod \
  --consumer-id partner-harvest-bot \
  --consumer-id analytics-reader-prod

The partner pack writes sendable per-consumer read and durable-subscription

requests to `artifacts/activity-syndication/activity-partner-onboarding-pack-latest.md`

plus a timestamped

`artifacts/activity-syndication/runs/activity-partner-onboarding-pack-<timestamp>.json`

audit copy, and stays blocked until the IDs are real partner-owned values.

If the first ask is a broad Linked Art community or Slack post instead of a

named partner request, capture that trail separately:


pnpm activity:community-outreach -- --channel "<community/channel>" --message-ref "<permalink-or-thread-ref>" --posted-at "<ISO timestamp>" --owner "<owner>"

That artifact records outreach only. It stays `strictEvidence: false` and does

not move any consumer row to ready until real partner-owned IDs, feed reads, and

callback verification arrive.


BASE_URL=https://<deploy-host> \
METAMUSEUM_ACTIVITY_CONSUMER_IDS=wikibase-cloud-prod,partner-harvest-bot,analytics-reader-prod \
pnpm activity:adoption:matrix

Equivalent explicit form:


pnpm activity:adoption:matrix -- \
  --base-url https://<deploy-host> \
  --consumer-id wikibase-cloud-prod \
  --consumer-id partner-harvest-bot \
  --consumer-id analytics-reader-prod

Artifacts:

  • `artifacts/activity-adoption/activity-adoption-proof-latest.json`
  • `artifacts/activity-adoption/runs/activity-adoption-proof-<timestamp>.json`
  • `artifacts/activity-adoption/activity-adoption-matrix-latest.json`
  • `artifacts/activity-adoption/runs/activity-adoption-matrix-<timestamp>.json`
  • `artifacts/activity-syndication/activity-partner-onboarding-pack-latest.md`
  • `artifacts/activity-syndication/activity-partner-onboarding-pack-latest.json`
  • `artifacts/activity-syndication/runs/activity-partner-onboarding-pack-<timestamp>.json`

Durable Syndication Evidence

After real external consumers are observed, prove that they also have durable

ActivityStreams callback workflows registered:


pnpm activity:syndication:evidence
pnpm activity:syndication:check

The command reads:

  • `storage/activity-consumers.json`
  • `storage/activity-subscriptions.json`
  • `docs/linked-art/activity-feed-read-confirmations.json`
  • `config/era-c-exit-gate-policy.json`

If `storage/activity-subscriptions.json` does not exist yet, the evidence

command initializes it with an empty `subscriptions` array and prints

`subscription ledger: 0 row(s)`. That removes missing-ledger ambiguity for

operators, but the empty ledger is not proof and the report remains blocked

until real partner callbacks are verified.

Artifacts:

  • `artifacts/activity-syndication/activity-syndication-evidence-latest.json`
  • `artifacts/activity-syndication/runs/activity-syndication-evidence-<timestamp>.json`

The report has `status: "ready"` only when the required real declared external

consumers in the configured window have feed-read telemetry or tracked

real partner confirmations covering `Create`, `Update`, and `Delete`; each same

consumer also needs an active external HTTPS `application/activity+json`

subscription covering `Create`, `Update`, and `Delete`, plus recent callback verification evidence with a 2xx status code.

Use `consumerOnboarding.rows` as the per-partner acceptance ledger: each row

must have `feedReadObserved: true`, `durableSubscriptionReady: true`,

`callbackVerified: true`, no blockers, and a real verification evidence ref.

The callback host must be public, non-placeholder, non-private, and match the

callback URL host. Placeholder IDs, derived fingerprints, local or private

callbacks, non-HTTPS callbacks, mismatched callback hosts, incomplete event

sets, stale subscription rows, and subscriptions without callback verification

remain blocked evidence.

Each accepted row in `storage/activity-subscriptions.json` must carry real

verification metadata similar to:


{
  "verification": {
    "status": "verified",
    "verifiedAt": "2026-06-28T18:00:00.000Z",
    "statusCode": 202,
    "evidenceRef": "artifacts/activity-syndication/partner-one-callback-verification.json"
  }
}

Use partner-owned evidence references, not demo, fixture, placeholder, sample,

smoke, test, or localhost proof.

Record callback verification with the operator command instead of hand-editing

the ledger:


pnpm activity:subscription:verify -- --consumer-id <partner-owned-consumer-id> --callback-url https://<public-consumer-host>/<activity-callback-path> --status-code 204 --verified-at <ISO timestamp> --evidence-ref artifacts/activity-syndication/<real-callback-proof>.json

The command updates `storage/activity-subscriptions.json`, rejects placeholder

evidence refs, rejects `--status=verified` with non-2xx status codes, and prints

`pnpm activity:syndication:evidence` as the next verification step when the

recorded callback proof can count toward durable syndication.

The next `pnpm activity:syndication:evidence` run prints the number of complete

onboarded consumers and any pending consumer IDs.

Producing Real Update/Delete Rows

Strict activity-type coverage can only pass after the public feed has real

`Update` and `Delete` activity rows for partners to read. Do not create demo or

placeholder events for this gate.

For `Update`, make a reviewed metadata correction with real source evidence.

The current production path stores the reviewed delta under

`_source.reviewedUpdates[]`, and `/api/activity?type=Update&source=reviewed-update`

exposes that row. Operator-initiated record rewrites may also write a real

`record.update` audit row when the changed full record is posted to

`/api/records`.

For `Delete`, record a tombstone only after a canonical upstream record returns

HTTP `404` or `410` and a real evidence reference exists:


pnpm activity:tombstone:scan

The scan reads canonical Met upstream IDs from the local record ledgers, writes

`artifacts/activity-syndication/activity-tombstone-scan-latest.json` plus a

timestamped run artifact, and does not create a `Delete` row when every upstream

record still returns a non-tombstone status. When the scan artifact shows a real

`404` or `410`, rerun with `--write-tombstones` to write the deletion ledger

using that scan run as the evidence reference:


pnpm activity:tombstone:scan -- --write-tombstones

Manual single-row recording is still available when the upstream proof was

captured separately:


pnpm activity:tombstone:evidence -- --object-id <canonical-record-uri> --provider <provider-key> --upstream-status 410 --observed-at <ISO timestamp> --evidence-ref artifacts/activity-syndication/<real-upstream-404-or-410-proof>.json

Both tombstone commands write `pipeline/state/deletion-activities.json`, reject

non-`404`/`410` statuses, and reject demo, fixture, placeholder, sample, smoke,

test, localhost, or otherwise fake evidence refs. After deploying the changed

ledger, ask partners to read `/api/activity?limit=100&type=Delete` with their

declared consumer IDs and then rerun `pnpm activity:syndication:evidence`.

MetaHistoryBook Callback Follow-Up

MetaHistoryBook replied on 2026-07-05 with consumer ID

`metahistorybook-harvester-prod`, public callback URL

`https://www.metahistorybook.com/api/metamuseum/callback`, and event types

`Create`, `Update`, and `Delete`. They reported a broad cursor-feed read at

`2026-07-05T16:37Z` with `Create` present and explicit zero-item `Update` and

`Delete` reads. MetaMuseum later rechecked callback liveness, sent one signed

production `Create` Activity payload, received `204`, and recorded the callback

proof with `pnpm activity:subscription:verify` at

`2026-07-05T17:13:13.312Z`. Evidence:

`artifacts/activity-syndication/metahistorybook-harvester-prod-callback-2026-07-05T17-13-13-312Z.json`.

Never print or paste the HMAC secret or signature value into evidence artifacts,

chat, or email.

Partner-response answers:

`METAMUSEUM_CALLBACK_SECRET`. `signingSecretRef:

metahistorybook-harvester-prod-v1` remains the stored label, not the secret

itself.

event callbacks; partners may accept an `OrderedCollectionPage`, but the strict

evidence row should point to the concrete single delivery that returned 2xx.

string, signed as `<timestamp>.<raw JSON payload>`.

follow-up work; the current strict gate records recent real 2xx verification

evidence with `pnpm activity:subscription:verify`, and MetaHistoryBook now has

an accepted `204` callback row.

real reviewed update rows and real upstream `404`/`410` tombstone rows.

MetaHistoryBook rechecked after callback verification and confirmed

`type=Update -> 0` and `type=Delete -> 0`; MetaMuseum independently rechecked

at `2026-07-05T17:21:34Z` and observed `totalItems: 0` for both URLs. Their

reader is ready to re-read both typed feeds after real rows deploy.

feed now uses the same dereferenceable `object.id`, `object.sourceId`, valid

UTC `endTime`, and default-row dedupe as the Change Discovery layer while

retaining `linkedArt` embeds.

  • R1 secret exchange: complete; the local `.env` key is
  • R2 delivery shape: send a single Activity payload for verification and normal
  • R3 timestamp format: `x-metamuseum-signature-timestamp` is an ISO 8601 UTC
  • R4 retry/verification semantics: production retry/backoff delivery is still
  • R5 `Update`/`Delete`: ping MetaHistoryBook after the deployed feed contains
  • R6 reconciliation: keep Wikidata QIDs in `object.equivalent[]`; the cursor

Consumer ID Rules

  • Use a stable ID owned by the external consumer, for example `wikibase-cloud-prod` or `partner-harvest-bot`.
  • Do not use placeholder IDs such as `test-`, `demo-`, `example-`, `local-`, or localhost-oriented names for production evidence.
  • The command rejects placeholder/local-looking IDs by default. `--allow-placeholder` exists only for local rehearsals.

Pass Criteria

The proof report has `status: "pass"` when:

  • `/api/activity` returns HTTP 2xx.
  • `/api/activity/readiness` returns HTTP 2xx.
  • Readiness telemetry contains the provided consumer ID as:
  • `class: "declared"`
  • matching `declaredId`
  • `isExternal: true`
  • non-empty `lastSeenAt`
  • `observedActivityTypes` containing the activity types returned to that consumer

The overall adoption exit gate still requires three unique declared external consumers within the configured window. A single passing proof only confirms one consumer was observed.

The matrix report has `status: "pass"` only when every requested consumer probes `/api/activity` successfully, every requested consumer appears in readiness as declared/external, `/api/activity/readiness` reports the `>=3` checkpoint as ready, and the matrix readiness block shows no missing `Create`/`Update`/`Delete` activity types. The matrix command performs one broad feed read plus one `type=Create`, `type=Update`, and `type=Delete` read for each requested consumer, so a three-consumer run records 12 activity probes before the readiness check. Run `pnpm activity:syndication:evidence` afterward to confirm the same declared consumers have durable callback evidence too.

Partner Hand-Off

Send partners this minimum request:


curl -H "Accept: application/json" \
  -H "x-linked-art-consumer-id: <partner-owned-consumer-id>" \
  "https://<deploy-host>/api/activity?limit=100"

curl -H "Accept: application/json" \
  -H "x-linked-art-consumer-id: <partner-owned-consumer-id>" \
  "https://<deploy-host>/api/activity?limit=100&type=Create"

curl -H "Accept: application/json" \
  -H "x-linked-art-consumer-id: <partner-owned-consumer-id>" \
  "https://<deploy-host>/api/activity?limit=100&type=Update"

curl -H "Accept: application/json" \
  -H "x-linked-art-consumer-id: <partner-owned-consumer-id>" \
  "https://<deploy-host>/api/activity?limit=100&type=Delete"

After the partner runs it, operators can verify:


curl "https://<deploy-host>/api/activity/readiness"
pnpm activity:adoption:matrix -- --base-url https://<deploy-host> --consumer-ids <id-1>,<id-2>,<id-3>
pnpm activity:syndication:evidence
pnpm era-c:exit-gate:evidence

Evidence Hygiene

callback hosts for production syndication evidence.

external HTTPS subscriptions with recent 2xx callback verification evidence

in `activity-syndication-evidence-latest.json`.

  • Derived fingerprints are diagnostic only and do not satisfy the gate.
  • Operators must not run production proof with internal placeholder IDs.
  • Operators must not use `example.*`, `.local`, localhost, or private-network
  • Keep partner IDs stable across checks so the 30-day activity window reflects real adoption.
  • Do not claim durable syndication until the same partner IDs have matching