Managed Linked Art Pilot Runbook
This runbook covers the first concierge paid pilots for the "Managed Linked Art Launch Pilot" offer. It is an operator checklist for onboarding one collection export into a hosted Meta Museum workspace without waiting for self-serve signup or automated billing.
Scope
Use this runbook when a prospect has agreed to a bounded pilot conversation and a human operator is preparing a workspace, entitlement record, support path, and monthly evidence packet.
The pilot covers:
- one customer organization
- one source data export or one bounded source API
- one hosted workspace namespace
- one public or review-only collection slice
- one monthly evidence packet
- manual invoice-backed entitlement tracking
The pilot does not cover:
- self-serve signup
- automated billing
- custom feature development
- bulk migration guarantees
- publication without human approval
Workspace Setup
- Assign an owner and reviewer.
- Create the pilot namespace:
- Create the source dataset namespace:
- Record publication boundary:
- Confirm source data minimums:
- Run import, validation, and review smoke checks against the pilot dataset.
- Capture first-value proof:
- Operator: Sun & Rain Works pilot owner.
- Customer: one named collection-side review owner.
- `pilot-{org_slug}-{YYYYMM}`
- Example: `pilot-benton-202606`
- `pilot/{org_slug}/{source_slug}/v1`
- Example: `pilot/benton/collection-export/v1`
- public slice allowed
- review-only slice allowed
- internal-only records excluded from public projection
- stable identifiers
- titles or display labels
- dates or timespan notes
- makers or creator notes when available
- rights text or rights review status
- image references when available
- searchable records
- at least one reviewed public or review-only collection slice
- one record detail page
- one Linked Art JSON response
- one unresolved-risk list
Tenant Namespace Convention
Until every route and persisted store propagates tenant context, every pilot artifact must include an explicit namespace string so it can be separated later without guessing from file paths or record labels.
Required namespace fields:
- `tenantId`: `pilot-{org_slug}-{YYYYMM}`
- `sourceNamespace`: `pilot/{org_slug}/{source_slug}/v1`
- `plan`: `pilot`
- `billingMode`: `manual_invoice`
- `publicationMode`: `review_only`, `public_slice`, or `mixed`
Do not infer tenant identity from a URL, display label, or provider name. The namespace must be explicit in operator notes, entitlement records, evidence packets, and any pilot-specific storage export.
Service-level scoped storage now exists for records, manual jobs, and persisted AgentTask review artifacts through `orgId`/`tenantId` options. Tenant-tagged records, jobs, and AgentTask route callers now pass the exact tenant ID into their scoped backing stores. Operators should still record the namespace explicitly and avoid mixing real pilot data in remaining shared routes until activity, annotation, audit, export, and backup evidence paths are covered.
Manual Plan Entitlement
Create a manual entitlement record before onboarding data. `src/services/pilot-entitlements.ts` is the executable contract for the interim plan gates, namespace shape, manual invoice fields, durable ledger storage, exact tenant lookup, and validation rules until a first-class entitlement table exists.
The durable interim ledger is `storage/pilot-entitlements.json`. It is registered as a managed storage document, so `pnpm storage:export:postgres` exports it with the other storage-of-record JSON documents during Postgres cutover.
{
"tenantId": "pilot-benton-202606",
"organizationName": "Benton Museum of Art at Pomona College",
"organizationSlug": "benton",
"sourceNamespace": "pilot/benton/collection-export/v1",
"plan": "pilot",
"workspaceOwner": "operator-name",
"reviewOwner": "customer-name",
"publicationBoundary": "Public browse slice after rights review; sensitive records held.",
"evidencePacketCadence": "monthly",
"effectiveFrom": "2026-06-14",
"billing": {
"invoiceMode": "manual",
"invoiceReference": "INV-2026-001"
},
"status": "active-pilot",
"planLimits": {
"importsPerMonth": 3,
"aiCallsPerMonth": 250,
"storageGb": 25,
"users": 5,
"exportsPerMonth": 4,
"apiRateLimitPerMinute": 120
}
}
Operator rules:
- Do not enable production publication until publication boundary and review owner are recorded.
- Do not exceed the `pilot` plan gates without written scope approval.
- Do not promise self-serve billing, automated entitlements, or custom integrations during this pilot.
- Do not search by loose organization slug when checking access; read by exact `tenantId`.
- Do not run tenant-tagged imports from a public role. Provider import paths are generated from the capability registry and require `researcher` or higher for both direct legacy and `/api/providers/{provider}/import` facade routes.
- Run `pnpm test -- tests/services/pilot-entitlements.test.ts` after changing plan IDs, gates, namespace rules, or manual invoice evidence.
- Run `node --import tsx --test tests/services/org-storage-isolation.test.ts` after changing records, jobs, AgentTask artifact persistence, or scoped storage path rules.
Outreach Evidence
Record real outreach status with `pnpm pilot:outreach` instead of changing the static offer page by hand. The command writes to managed `pilot-outreach-events.json`, validates the account id against the named `/pilot` outreach queue, and rejects incomplete or out-of-sequence stage evidence before changing the ledger.
pnpm pilot:outreach --account=benton-museum-of-art-at-pomona-college --stage=sent --occurred-at=2026-06-19T16:00:00Z --owner="operator-name" --evidence=channel=email --evidence=messageRef=outreach/benton/2026-06-19 --evidence=followUpAt=2026-06-26
Use `pnpm pilot:outreach --summary` before updating buyer-facing notes. Do not claim first outreach has been sent until the `sent` event has `accountId`, `owner`, `channel`, `messageRef`, `sentAt`, and `followUpAt` evidence for one named account. The `followUpAt` date must be on or after `sentAt`, and `replied` evidence requires a prior `sent` event for the same named account with `repliedAt` on or after `sentAt`.
Pilot Usage Gate
Use `evaluatePilotUsage` for dry-run plan checks, `assertPilotUsageAllowed` for service callers, and `enforcePilotRouteUsageGate` for route handlers before work consumes monthly or per-minute plan capacity. The first enforced dimensions are:
- imports per month
- AI calls per month
- storage GB
- exports per month
- API requests per minute
Route-level gates are now wired for:
- `app/api/providers/[provider]/import` through the shared provider facade, counting one import and one API request
- `app/api/providers/[provider]/profile` and `app/api/providers/[provider]/search` through the shared provider facade, counting one API request
- `app/api/ai/query`, `app/api/ai/chat`, `app/api/ai/embeddings`, `app/api/ai/visual-similarity`, and `app/api/ai/mapping-assist`, counting one AI call and one API request
- `app/api/content/generate`, counting one AI call and one API request
- `app/api/records`, counting tenant-tagged API reads/writes and one export when `?export=1`, `?download=1`, `?format=export`, `?format=jsonl`, `?format=csv`, or `x-metamuseum-export-request: true` is present
Pilot automation must send the exact tenant ID. Route gates now derive current usage from `storage/pilot-usage-counters.json` through `src/services/pilot-usage-counters.ts`; that document is registered as managed storage, so `pnpm storage:export:postgres` carries usage counters alongside entitlement records during Postgres cutover.
| Header | Meaning |
|---|---|
| `x-metamuseum-tenant-id` | Exact tenant ID, for example `pilot-benton-202606`. |
| `x-metamuseum-usage-imports-this-month` | Legacy override only: imports already consumed in the current billing month. |
| `x-metamuseum-usage-ai-calls-this-month` | Legacy override only: AI calls already consumed in the current billing month. |
| `x-metamuseum-usage-storage-gb-used` | Legacy override only: current tenant storage usage in GB. |
| `x-metamuseum-usage-exports-this-month` | Legacy override only: exports already consumed in the current billing month. |
| `x-metamuseum-usage-api-requests-this-minute` | Legacy override only: API calls already consumed in the current rate-limit minute. |
Requests without `x-metamuseum-tenant-id` remain public/evaluation behavior. Tenant-tagged requests require an exact active entitlement, read the current month/minute counter snapshot by exact tenant ID, reject malformed legacy usage snapshot headers when those overrides are present, and return a CORS-preserving `402` response before import, AI, export, or API work runs when the requested usage would exceed the `pilot` plan.
Successful tenant-tagged `2xx` responses on those gated shared route paths increment the same counter ledger after route work completes. Denied, malformed, validation-error, and upstream-error responses do not consume quota.
Direct provider-specific routes such as `/api/met/import`, `/api/getty/sparql`, or `/api/rijks/ldes` remain public/evaluation legacy surfaces only when no pilot tenant header is present. `proxy.ts` blocks tenant-tagged direct provider routes with `409` and points supported `profile`, `search`, and `import` operations at `/api/providers/{provider}/{operation}` so pilot usage cannot bypass facade gates and counters before multi-org hosting.
Focused tenant-isolation tests now prove one over-plan pilot tenant cannot deny or mutate another tenant's counter snapshot at the entitlement/counter gate layer, service-level storage tests prove records, manual jobs, and persisted AgentTask artifacts can be isolated under the same storage root by exact org/tenant scope, and route tests prove tenant-tagged records, jobs, and AgentTask history do not bleed into sibling tenants or the unscoped default store. This does not replace the SaaS-2 requirement to propagate tenant identity through the remaining shared route callers or to add isolation coverage for activity metrics, annotations, audit logs, exports, and backup/restore evidence before shared multi-org hosting.
Seven-Day Activation Checklist
The pilot reaches first value only when all items below are true within seven calendar days of receiving usable source data:
- workspace namespace assigned
- manual entitlement recorded
- source export ingested or staged
- imported records are searchable
- one representative record detail page is reviewable
- Linked Art JSON response is available for at least one record
- rights/reuse status is visible to the reviewer
- critical validation issues are listed
- customer review owner has viewed the workspace or evidence packet
- next customer decision is recorded
Usage And Activation Events
Track these events manually until product analytics is tenant-aware:
The public `/pilot` activation evidence ledger mirrors these event names, and `src/services/pilot-activation-events.ts` records the same events in managed `pilot-activation-events.json` by exact tenant id. Keep every public milestone at `not_started` until a real tenant has dated evidence; do not mark a milestone complete from a demo, seed fixture, or internal smoke run. Later milestones require prior activation evidence for the same tenant, and every event's evidence `tenantId` must match the top-level tenant id.
Use the operator command to append real tenant evidence instead of hand-editing JSON:
pnpm pilot:activation --tenant=pilot-benton-202606 --event=pilot.workspace.created --occurred-at=2026-06-19T12:00:00Z --evidence=workspaceOwner="operator-name" --evidence=createdAt=2026-06-19T12:00:00Z
Use `pnpm pilot:activation --tenant=pilot-benton-202606 --summary` before sending a customer update to confirm the completed milestone count and the next required evidence. The command writes through the same managed storage service, auto-attaches `tenantId` to evidence, and rejects incomplete, tenant-mismatched, or out-of-sequence required fields before changing the ledger.
| Event | Required fields | Why it matters |
|---|---|---|
| `pilot.workspace.created` | `tenantId`, `workspaceOwner`, `createdAt` | Starts the seven-day activation clock. |
| `pilot.source.received` | `tenantId`, `sourceNamespace`, `recordEstimate`, `receivedAt` | Proves data readiness and source scope. |
| `pilot.records.imported` | `tenantId`, `sourceNamespace`, `recordCount`, `importedAt` | Confirms first ingestion. |
| `pilot.review.slice_ready` | `tenantId`, `recordCount`, `sliceUrl`, `readyAt` | Confirms first value for reviewers. |
| `pilot.customer.viewed` | `tenantId`, `reviewOwner`, `viewedAt` | Confirms non-engineer access. |
| `pilot.evidence.sent` | `tenantId`, `packetPath`, `sentAt` | Confirms recurring evidence delivery. |
Support Intake
Use one support channel per pilot. Record every issue with:
- `tenantId`
- requester
- severity: `blocking`, `high`, `normal`, or `question`
- record or source reference when relevant
- decision owner
- next response date
- resolution summary
Record support load with `pnpm pilot:support` instead of keeping issue state in ad hoc notes. The command writes to managed `pilot-support-issues.json`, validates severity/status fields, keeps issues scoped by exact tenant id, rejects response deadlines before `openedAt`, rejects existing issue updates that rewrite the original `requester`, `summary`, `openedAt`, or `severity`, rejects updates to already resolved issues that rewrite `resolvedAt` or `resolutionSummary`, rejects open issues that include resolution evidence, and rejects resolved issues without both `resolvedAt` and `resolutionSummary` or with `resolvedAt` before `openedAt`.
pnpm pilot:support --tenant=pilot-benton-202606 --issue=SUP-1 --opened-at=2026-06-20T10:00:00Z --requester="customer-name" --severity=normal --title="Question about rights warning wording" --decision-owner="operator-name" --next-response-at=2026-06-24T18:00:00Z
Use `pnpm pilot:support --tenant=pilot-benton-202606 --summary` before sending customer updates or monthly evidence packets. Support severities are `blocking`, `high`, `normal`, and `question`; support statuses are `open` and `resolved`.
Support boundaries:
- Blocking import or access issues get a next-business-day response.
- Mapping, provenance, and rights questions are triaged weekly unless blocking publication.
- Feature requests are logged as product feedback, not accepted as pilot scope by default.
Monthly Evidence Packet Template
Generate the current operator packet with:
pnpm pilot:evidence --tenant=pilot-benton-202606 --account=benton-museum-of-art-at-pomona-college --markdown
The command writes `artifacts/pilot-evidence/pilot-evidence-latest.json`, `artifacts/pilot-evidence/pilot-evidence-latest.md`, and timestamped packet files. The JSON packet is the machine-readable source of truth; the Markdown packet is the customer/buyer-facing summary for email, docs, or procurement review. Each packet includes support-load counts from `pilot-support-issues.json`. A packet is `ready` only when the exact tenant has an active manual entitlement, at least one named outreach account has a sent/replied event, all activation milestones have dated evidence, no blocking support issue remains open, and no open support issue is past `nextResponseAt`; otherwise it is `blocked` with explicit open blockers.
Each monthly packet must include:
- Pilot summary
- Activation status
- Data quality
- Rights and provenance
- Product usage
- Support load
- Next decision
- tenant ID
- source namespace
- record count
- publication mode
- current status
- seven-day checklist result
- first-value date
- reviewer access status
- validation summary
- critical issues
- unresolved mapping questions
- enrichment coverage when available
- rights/reuse status summary
- records held for review
- provenance risks
- review sessions or customer views
- API/export examples used
- AI review runs consumed
- open issues
- resolved issues
- operator minutes estimate
- continue pilot
- expand source scope
- pause
- convert to recurring subscription
Exit Criteria
A pilot can be called successful when:
- first value is reached within seven days
- customer review owner can inspect records without routine developer help
- monthly evidence packet is delivered
- support load is recorded
- conversion decision is explicit
Do not claim profitable SaaS readiness from this runbook alone. Profitability still needs recurring revenue, support-load evidence, retention, gross margin, and acquisition-channel proof.