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
Commercial Readiness Gate
The public `/pilot` page must stay explicit about the pre-revenue state until
real commercial evidence exists:
entitlement, source data received, and seven-day activation evidence
- paid pilots: `0`
- billing path: manual invoice-backed entitlement only
- in-app billing: not built
- next revenue evidence: first signed pilot with invoice reference, active
Do not treat a researched account, warm conversation, demo, or internal dataset
as revenue evidence. A pilot becomes commercial evidence only after all of the
following are true:
- Signed scope or written approval names the customer organization, price,
publication boundary, workspace owner, and customer review owner.
- A real manual invoice reference exists and is recorded in the pilot
entitlement. Demo, fixture, internal, placeholder, sample, smoke, or test
invoice references do not count as commercial evidence.
- The entitlement is active, exact-scoped by `tenantId` or `orgId`, and
exportable through managed storage.
- Source data has been received for the named tenant.
- The activation ledger has dated customer evidence, not demo or smoke data.
Only after at least one invoice-referenced pilot reaches first value should the
roadmap consider checkout, subscription webhooks, pricing pages, or self-serve
plan changes.
Buyer Onboarding Pack
Before turning a signed pilot into launch evidence, generate the buyer-specific
capture pack:
pnpm pilot:buyer-pack -- --account <real-account-id> --tenant <tenant-id> --organization "<buyer organization>" --invoice-ref <real-invoice-ref> --owner "<operator owner>"
For web forms or first-touch enquiries where pricing would be premature, generate
a form-safe draft that keeps pricing and invoice language out of the outbound
message body while preserving private evidence fields in the response template:
pnpm pilot:buyer-pack -- --account <real-account-id> --tenant <tenant-id> --organization "<buyer organization>" --invoice-ref <real-invoice-ref> --owner "<operator owner>" --submission-mode=form --omit-pricing
The command writes:
- `artifacts/pilot-evidence/pilot-buyer-onboarding-pack-latest.json`
- `artifacts/pilot-evidence/pilot-buyer-onboarding-pack-latest.md`
- `artifacts/pilot-evidence/runs/pilot-buyer-onboarding-pack-<timestamp>.json`
The pack is `needs-buyer-evidence` until the operator supplies a real account,
tenant, buyer organization, owner, and invoice reference. It rejects
demo/fixture/local/placeholder/sample/smoke/test-looking account, tenant, and
invoice values unless explicitly run as a placeholder rehearsal, and placeholder
rehearsals do not count as external pilot evidence. A `ready-to-send` pack is
still only an operator handoff; the 10/10 gate clears only after the real
entitlement, outreach, activation, support, KPI, retention, gross-margin, and
packet artifacts pass. Until then, the buyer pack is not strict paid-pilot
proof.
SaaS Packaging Gate
Use `pnpm pilot:packaging` before promising that the concierge path is
supportable as SaaS packaging. The command reads the managed
`pilot-entitlements.json` ledger and writes:
- `artifacts/pilot-packaging/pilot-packaging-latest.json`
- `artifacts/pilot-packaging/pilot-packaging-<timestamp>.json`
Default concierge check:
pnpm pilot:packaging
pnpm pilot:packaging:check
Repeatable SaaS check:
pnpm pilot:packaging -- --target=repeatable-saas --check
Packaging rules:
active pilots with invoice-backed entitlements, workspace owner, review owner,
publication boundary, and monthly evidence cadence.
support limit or when the target is repeatable SaaS.
entitlement sync, customer billing portal, and plan-change audit evidence are
implemented and configured.
alone; the `institution` tier is package intent until billing and onboarding
workflows are executable.
- Manual invoice billing is supportable only for bounded concierge load: 1-3
- Subscription billing is required when active concierge pilots exceed the
- Repeatable SaaS remains blocked until subscription checkout, webhook
- Do not mark an institution plan as available from commercial plan metadata
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 entitlement validator rejects synthetic invoice references that tokenize as demo, fixture, internal, placeholder, sample, smoke, or test evidence.
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.
KPI Evidence
Record monthly outcome evidence with `pnpm pilot:kpi` instead of deriving business readiness from usage counters alone. The command writes to managed `pilot-kpi-events.json`, keeps entries scoped by exact tenant id, validates known metric IDs, requires a numeric value, source, evidence reference, and owner, and rejects evidence references that look like demo, fixture, smoke, or sample data.
pnpm pilot:kpi --tenant=pilot-benton-202606 --metric=pilot.first_value_days --value=4 --measured-at=2026-06-24T12:00:00Z --source="pilot-evidence/benton/activation-ledger" --evidence-ref="artifacts/pilot-evidence/benton-2026-06.json" --owner="operator-name"
Use `pnpm pilot:kpi --tenant=pilot-benton-202606 --summary` before generating a monthly evidence packet. A ready packet requires one latest entry for each required KPI:
| Metric | Meaning |
|---|---|
| `pilot.first_value_days` | Calendar days from usable source receipt to first reviewed value. |
| `pilot.imported_record_count` | Count of records imported for the pilot tenant. |
| `pilot.customer_view_count` | Count of customer review views or equivalent named reviewer access events. |
| `pilot.support_minutes` | Operator support minutes spent during the packet period, including zero when documented. |
| `pilot.validation_critical_open_count` | Open critical validation issues at packet close. |
| `pilot.retention_signal_count` | Count of explicit continue, convert, expand-scope, or renewal signals from the customer. |
| `pilot.gross_margin_percent` | Gross-margin percentage for the pilot period, backed by invoice revenue minus support, infra, and operator cost evidence. |
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
Use `--check` in closeout, CI, or pre-send review when the packet must be
ready rather than merely generated:
pnpm pilot:evidence --tenant=pilot-benton-202606 --account=benton-museum-of-art-at-pomona-college --markdown --check
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 invoice-backed commercial evidence, support-load counts from `pilot-support-issues.json`, and KPI evidence from `pilot-kpi-events.json`. A packet is `ready` only when the exact tenant has an active manual entitlement with a real invoice reference that is not demo, fixture, internal, placeholder, sample, smoke, or test evidence, at least one named outreach account has a sent/replied event, all activation milestones have dated evidence, support-load evidence is tied to an invoice-backed pilot period with `pilot.support_minutes` recorded, no blocking support issue remains open, no open support issue is past `nextResponseAt`, all required KPI metrics have real tenant evidence, `pilot.retention_signal_count` records a real continue, convert, expand-scope, or renewal signal, and `pilot.gross_margin_percent` records real invoice-minus-cost evidence for the pilot period; otherwise it is `blocked` with explicit open blockers. An empty support ledger before an invoice-backed pilot exists is a pending capture state, not strict support-load proof.
With `--check`, a blocked packet exits non-zero while still writing the JSON and
Markdown artifacts, so missing real outreach, entitlement, activation, or
support or KPI evidence cannot pass an automated readiness check silently.
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.