UNPKG

@withpica/mcp-server

Version:

MCP Server for withPICA — enables AI assistants to interact with your withPICA music catalog

697 lines • 44.5 kB
// Copyright (c) 2025-2026 Withpica Ltd. All rights reserved. export const REQUIRED_SCHEMAS_SOURCE = { "work-required": { workflow: "work-required", summary: "Required fields to register a fully-credited musical work.", primary_tool: "pica_works_create", enum_tables: [{ table: "works", columns: ["work_type", "work_status"] }], companion_calls: [ { tool: "pica_credits_update", when: "Once per work, batch — attach all writer/publisher splits in a single call.", required: ["work_id", "credits"], per_row_required: ["person_id", "role"], constraint: "splits values must sum to 100 across all credits for the work (the tool maps role→work_collaborators/recording_credits by grain and splits→split_percentage).", }, { tool: "pica_credit_update", when: "After credits exist — edit ONE credit in place (a writer's share, a role, the displayed name, notes, an instrument, or who it is for via person_id) without re-sending the set. Find the credit_id with pica_credits_list. The credit keeps its id; its attestation state is kept unless person_id changes who it is for, which sends a confirmed, disputed or declined credit back to pending.", required: ["work_id", "credit_id"], constraint: "At least one of share_pct, credit_type, credited_name, notes, instrument, person_id. share_pct is 0-100, or null to record the share as UNKNOWN (never 0). Writer-split closure is NOT enforced on a single edit — re-split one writer at a time. person_id must be a person in this organisation, not deleted, and not already holding this role on the song or recording (CREDIT_PERSON_CONFLICT); linking a name-only credit to a differently named person needs confirm_name_mismatch: true.", }, { tool: "pica_works_update", when: "After creation — to amend title, work_type, work_status, iswc, lyrics, or any other inline field. Use the same payload shape as create with the addition of `id`.", required: ["id"], }, { tool: "pica_works_attest", when: "After registering — stamp a human-creation provenance attestation for sync libraries / provenance certificates. Captures who signed it, when, and the attestation method.", required: ["id"], }, { tool: "pica_credits_send_for_attestation", when: "After attaching credits — notify each credited person (in-app + telegram + email) that they have a pending credit awaiting their attestation. Lightweight alternative to pica_split_sheet_send when a signed document isn't needed.", required: ["work_id"], }, { tool: "pica_works_verify_registrations", when: "When registration status is unknown — mode 'check' looks works up at the MLC and records what the lookup PROVED (found = positive society fact; clean not-found = verified-absent assertion); mode 'confirm_unregistered' records the user's own confirmation. Up to 25 works per call; idempotent.", required: ["work_ids", "mode"], }, ], // ⚠️ NO lyrics_language HERE. This example is copied verbatim by agents, so // a language on a work carrying no lyric sheet teaches them to assert one — // the same invention removed from the column DEFAULT and the CSV import // defaults on 2026-09-09 (migration 20260909120000, #2187), re-entering // through the MCP write path that now creates most new works. An unknown // language is stored as unknown. Pinned by "the work example asserts no // lyrics_language" in required-schemas.test.ts. example: { title: "Midnight Rivers", work_type: "song", iswc: "T-123.456.789-0", duration_seconds: 213, }, }, "recording-required": { workflow: "recording-required", summary: "Required fields to register a recording (a specific master, version, or live capture of a work).", primary_tool: "pica_recordings_create", enum_tables: [{ table: "recordings", columns: ["version_type"] }], companion_calls: [ { tool: "pica_recording_credits_update", when: "Per credit — attach a single MainArtist / Producer / Engineer credit (single form needs recording_id + credited_name + role). Also accepts a credits[] batch across release_id / recording_ids (b477cd175 write-merge) — see recording-credits-required for the batch shape. Multi-mode, so the schema-level required list is empty; mode requirements live in this prose.", required: [], }, { tool: "pica_recordings_update", when: "After creation — to amend recording_title, artist_name, version_type, isrc, duration_ms, or any other inline field. Same payload shape as create plus `id`.", required: ["id"], }, ], example: { title: "Midnight Rivers (Master)", artist_name: "The Riverbed Trio", version_type: "master", isrc: "USABC2300001", duration_ms: 213000, }, }, "work-credits-required": { workflow: "work-credits-required", summary: "Required fields per credit row when attaching writer/composer/publisher credits to a work. The tool exposes `role` (composition roles route to `work_collaborators`, master roles to `recording_credits`, keyed by grain) and `splits` (mapped to `split_percentage`). Splits must sum to 100. Valid role values are enumerated on the `role` field of `pica_credits_update`. Attestation is not a field you send: every credit starts as pending and only the credited person confirms it (pica_credits_send_for_attestation).", primary_tool: "pica_credits_update", companion_calls: [ { tool: "pica_credit_update", when: "After credits exist — edit ONE credit in place (a writer's share, a role, the displayed name, notes, an instrument, or who it is for via person_id) without re-sending the set. Find the credit_id with pica_credits_list. The credit keeps its id; its attestation state is kept unless person_id changes who it is for, which sends a confirmed, disputed or declined credit back to pending.", required: ["work_id", "credit_id"], constraint: "At least one of share_pct, credit_type, credited_name, notes, instrument, person_id. share_pct is 0-100, or null to record the share as UNKNOWN (never 0). Writer-split closure is NOT enforced on a single edit — re-split one writer at a time. person_id must be a person in this organisation, not deleted, and not already holding this role on the song or recording (CREDIT_PERSON_CONFLICT); linking a name-only credit to a differently named person needs confirm_name_mismatch: true.", }, { tool: "pica_credits_send_for_attestation", when: "After attaching credits — send confirmation prompts to credited collaborators so each person can attest or dispute their own row. Lightweight alternative to pica_split_sheet_send when a signed split sheet isn't needed.", required: ["work_id"], }, ], example: { work_id: "<uuid>", credits: [ { person_id: "<uuid>", role: "writer", splits: 50 }, { person_id: "<uuid>", role: "writer", splits: 50 }, ], }, }, "recording-credits-required": { workflow: "recording-credits-required", summary: "Required fields per credit row when attaching MainArtist / FeaturedArtist / Producer / Engineer credits to a recording. Distinct role enum from work credits. person_id is optional — credits can carry just credited_name.", primary_tool: "pica_recording_credits_update", enum_tables: [{ table: "recording_credits", columns: ["role"] }], // No companion calls. A deprecated alias used to be listed here with a // "Never — call the canonical instead" note, purely to satisfy the // forward direction of `scripts/lint-required-schemas.ts`: the alias is // registered as a clone of its canonical, so it inherits the canonical's // `workflows` tag and the lint demanded a reference. That made an // agent-facing resource carry the exact name we are retiring. The lint's // forward direction now skips alias names (`getAliasFor(name) !== undefined` // — see `runChecks` there, and the "an alias never has to be listed" // case in `scripts/__tests__/lint-required-schemas.test.ts`), so no // resource has to name one. companion_calls: [], example: { recording_id: "<uuid>", credited_name: "The Riverbed Trio", role: "MainArtist", person_id: "<uuid>", }, }, "person-required": { workflow: "person-required", summary: "Required fields to register a person (writer, performer, producer, publisher). Identifiers (IPI / ISNI / PRO / MusicBrainz / etc.) are inline-settable at create time.", primary_tool: "pica_people_create", enum_tables: [], companion_calls: [ { tool: "pica_people_update", when: "After creation — to amend name, identifiers (IPI / ISNI / PRO / MusicBrainz / etc.), or any other inline field. Same payload shape as create plus `id`.", required: ["id"], }, ], example: { first_name: "Jane", last_name: "Doe", cae_ipi_number: "00000000001", isni: "0000000123456789", pro_member_number: "PRS123456", }, }, "audio-upload-required": { workflow: "audio-upload-required", summary: "Three-step chain to add an audio master / stem / version to the catalog: presign → PUT → finalize, then optional async analyze + poll.", primary_tool: "pica_audio_presigned_upload", enum_tables: [ { table: "audio_files", columns: ["file_type", "classification"] }, ], companion_calls: [ { tool: "pica_audio_complete_upload", when: "Step 2 — after the uploader PUTs the file to the signed URL returned by step 1. Finalizes the upload, writes the audio_files row, and (optionally) links or auto-creates a recording for master files.", required: [ "upload_id", "key", "bucket", "filename", "content_type", "file_size", ], }, { tool: "pica_audio_analyze", when: "Step 3 (optional, async). Triggers BPM / key / mood feature extraction (Librosa) and lyrics transcription (Whisper). Each side bills 1 credit. Does not auto-run on complete_upload — explicit call required.", required: ["id"], }, { tool: "pica_audio_inspect", when: "Step 4 (poll). Call with sections: ['analysis', 'status'] until the analysis job reports complete. Lyrics transcripts land on the transcriptions row and auto-copy to works.lyrics only when it is empty — existing lyrics are never overwritten.", required: ["id"], }, ], example: { filename: "midnight-rivers-master.wav", content_type: "audio/wav", file_size: 48234567, work_id: "<optional uuid — auto-creates a recording for master files>", }, }, "enrichment-resolve-required": { workflow: "enrichment-resolve-required", summary: "Fan out enrichment across every eligible source for an entity (work / recording / person), then review fuzzy Tier B matches in the proposals queue. Tier A identifier matches apply directly; Tier B fuzzy matches queue as proposals.", primary_tool: "pica_resolve_work", enum_tables: [ { table: "enrichment_proposals", columns: ["status", "entity_type", "proposal_action"], }, // The values a file-type question's answer may take // (pica_enrichment_proposal_apply `file_type`). { table: "audio_files", columns: ["file_type"], }, ], companion_calls: [ { tool: "pica_resolve_recording", when: "Recording-side variant. Fans out across Spotify, YouTube, MusicBrainz, Discogs. Triggered by the canonical spotify_track_id / youtube_video_id on the recording.", required: ["recording_id"], }, { tool: "pica_resolve_release", when: "Release-side variant. Fans out across Spotify, MusicBrainz, Discogs. Triggered by trigger identifiers on the release (e.g. spotify_album_id, upc).", required: ["release_id"], }, { tool: "pica_resolve_person", when: "Person-side variant. Fans out across ISNI and MusicBrainz (Wikidata downstream). Triggered by isni / musicbrainz_id on the person.", required: ["person_id"], }, { tool: "pica_enrichment_proposals_list", when: "After resolve — list pending Tier B fuzzy matches awaiting review. Only pending status is returned.", required: [], }, { tool: "pica_enrichment_proposal_apply", when: "Commit a reviewed proposal. Drift detection runs first on update proposals — pass force=true only after the user has reviewed any conflicts from a prior drift_detected response. For a file-type question (entity_type audio_file) pass the user's chosen file_type (and, for a stem, an optional stem_label); ask them about each file, never pick for them.", required: ["proposal_id"], constraint: "file_type ∈ audio_files.file_type and only on entity_type audio_file; stem_label only with file_type stem, max 60 characters; a refused answer is INVALID_FILE_TYPE_ANSWER and writes nothing", }, { tool: "pica_enrichment_proposal_reject", when: "Dismiss a proposal. Content-hash suppression permanently blocks re-proposal of the same exact content; cascade only re-proposes if the source data genuinely changes.", required: ["proposal_id"], }, { tool: "pica_enrichment_proposals_bulk_reject", when: "Dismiss up to 100 pending proposals in one call — by explicit proposal_ids OR a filter (rule_id / source / max_confidence). Per-proposal effects identical to the single reject (permanent content-hash suppression). Pass dry_run=true first to preview the real affected set.", required: [], constraint: "exactly one of proposal_ids or filter must be supplied; dry_run previews without mutating", }, { tool: "pica_enrichment_proposals_bulk_apply", when: "Accept a reviewed batch — up to 100 pending proposals in one call, by EXPLICIT ids only. There is deliberately no filter form: a mis-filtered bulk reject costs a re-proposal, a mis-filtered bulk apply writes to the catalogue. Pass dry_run=true first to see what would land.", required: ["proposal_ids"], constraint: "proposal_ids only, max 100; partial success is the NORMAL shape — a non-empty failed[] beside a non-zero applied is expected, never report a partial as a success; dry_run=true returns { would_affect, not_pending } and writes nothing, and would_affect is an upper bound because drift is only detected at write time", }, ], example: { work_id: "<uuid>", sources: ["mlc", "spotify"], include_fuzzy: true, }, }, "agreement-required": { workflow: "agreement-required", summary: "Required fields to create an agreement, then attach works and (optionally) render from a template.", primary_tool: "pica_agreements_create", enum_tables: [ { table: "agreements", columns: ["agreement_type", "other_party_type", "status"], }, ], companion_calls: [ { tool: "pica_agreements_update", when: "After creation — to amend any inline field. Status changes trigger notifications; counterparty_org_id changes require the linked-org check at the route layer.", required: ["id"], }, { tool: "pica_agreements_link_work", when: "Attach a work to the agreement. Optionally set a royalty split percentage per work.", required: ["agreement_id", "work_id"], }, { tool: "pica_agreement_templates_render", when: "Template-driven flow — fill an agreement template with catalog data (work_id + people_ids) before creating the agreement row.", required: ["id"], }, { tool: "pica_agreements_create_from_template", when: "Template-driven flow with signers in one call — render a template and create the resulting agreement in draft status with required_signers already set. The only creation path that can set required_signers; pica_agreements_create has no such field.", required: [ "template_id", "title", "agreement_type", "required_signers", "variables", ], }, { tool: "pica_agreements_send_for_signature", when: "Send every required signer of a draft agreement a personal signing link by email.", required: ["agreement_id"], }, ], example: { title: "Songwriter Admin Deal — Midnight Rivers", agreement_type: "publishing_admin", other_party_name: "Indigo Music Publishing Ltd.", }, }, "multimedia-required": { workflow: "multimedia-required", summary: "Required fields to register a multimedia item (photo / video / audio / external streaming link), and the patterns for importing or attaching to a work.", primary_tool: "pica_multimedia_create", enum_tables: [ { table: "multimedia_items", columns: ["content_type", "classification"], }, ], companion_calls: [ { tool: "pica_multimedia_import_url", when: "Import an external image or video URL — downloads the file into PICA storage rather than referencing it externally. Use for Spotify artwork, web images, etc.", required: ["url"], }, { tool: "pica_multimedia_link_youtube", when: "YouTube-specific variant — pulls thumbnail, title, and view count automatically. Use this instead of pica_multimedia_create for YouTube videos.", required: ["youtube_video_id", "title"], }, { tool: "pica_multimedia_link_work", when: "Attach an existing multimedia item to a work with a typed relationship (music_video_for, sync_placement, album_artwork, etc.).", required: ["multimedia_id", "work_id", "relationship_type"], }, ], example: { title: "Midnight Rivers — Official Music Video", content_type: "youtube_video", url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ", }, }, "export-required": { workflow: "export-required", summary: "Generate exports of works in formats matching the destination — pica_export_catalog_csv emits per-work or per-batch industry submission files (pro / distributor / nro / sync-pdf), pica_export_song_registration bundles CSV/JSON/PDF plus a CWR field-mapping guide for PRO registration (a mapping guide, NOT a CWR file — PICA has no CWR emitter), pica_export_industry_ready packages metadata for publishers/labels/sync, pica_export_catalog_asset_report (CAR) bundles diligence/finance evidence, pica_export_ai_consent emits per-work + per-contributor AI declaration matrices. Four diligence documents complete the set: pica_export_snapshot (owner-facing, what to fix next), pica_export_ownership_record (the register of who owns what), pica_export_rights_proof (the evidence behind that register), pica_export_diligence_pack (readiness for registration or sale). For everything PICA holds about specific songs — recordings, releases, credits, people — pica_export_song_dossiers bundles one dossier per work into a ZIP.", primary_tool: "pica_export_catalog_csv", enum_tables: [], companion_calls: [ { tool: "pica_export_song_registration", when: "Song-registration bundle for PRO/CMO submission — CSVs plus a CWR field-mapping guide, not a CWR file.", required: [], }, { tool: "pica_export_industry_ready", when: "Industry-ready metadata package — formatted for distribution to publishers, labels, and sync agents.", required: [], }, { tool: "pica_export_catalog_asset_report", when: "Catalog Asset Report (CAR) — unified ZIP bundle with cover PDF, evidence folders, integrity manifest. Property-lending framing for finance / insurance / diligence. Nested `sections` object accepts boolean flags: valuation, assets, agreements, intelligence. Ownership is always included. The report is unsigned: only someone signed in to PICA can attest it, from the export panel.", required: [], }, { tool: "pica_export_ai_consent", when: "AI-usage consent declaration matrix — per-work + per-contributor declarations of human-made / AI-assisted / AI-generated.", required: [], }, { tool: "pica_export_snapshot", when: "Catalogue snapshot for the OWNER deciding what to fix — PICA score per pillar, ownership coverage, gaps ranked by severity each with a suggested action, production-asset provenance, and a prioritised what-to-do-next list. Scope with scope='work'|'selected'|'everything'. For a lender/buyer-facing document use pica_export_catalog_asset_report instead.", required: [], }, { tool: "pica_export_song_dossiers", when: "Everything PICA holds about specific songs, as one ZIP: a song dossier (PDF + JSON) per work — the work, its recordings, the releases they appear on, people, provenance, consent. 1 to 60 work_ids; more is refused, not truncated. A file for the catalogue owner; it sends nothing to anyone.", required: ["work_ids"], }, { tool: "pica_export_ownership_record", when: "The ownership REGISTER — per-contributor and per-asset record of who owns what, with claim status, share percentages, contributor IPI/ISNI, and the definitions a lawyer needs to read it. Pair with pica_export_rights_proof, which is the evidence behind this register.", required: [], }, { tool: "pica_export_rights_proof", when: "The EVIDENCE behind the ownership register — timestamped claims, contributor attestations, identity verification, evidence-integrity hashes. Use when asked to PROVE ownership rather than state it.", required: [], }, { tool: "pica_export_diligence_pack", when: "Catalogue diligence pack as a document — readiness scoring, ownership clarity, identifier completeness, uninsured insurable assets, gaps ranked by severity with a suggested action. For the same analysis as JSON (cheaper, no document) call pica_catalog_diligence instead.", required: [], }, ], example: { format: "pro", work_id: "11111111-1111-1111-1111-111111111111", }, }, "session-required": { workflow: "session-required", summary: "Required fields to log a studio session — title + start_time + end_time (ISO 8601 datetimes). Optional `session_type_id` is a UUID FK to the org's session_types lookup table; resolve via pica_sessions_types. The `status` column accepts scheduled / in_progress / completed / cancelled per the inputSchema, but the values are NOT enforced by a DB CHECK constraint — verify against the live tool's inputSchema if values matter to the caller.", primary_tool: "pica_sessions_create", enum_tables: [], companion_calls: [ { tool: "pica_sessions_types", when: "Pre-flight to discover valid session_type_id values for this org. The lookup table is org-scoped and seeded with recording / mixing / mastering / writing / production / rehearsal entries on org bootstrap.", required: [], }, { tool: "pica_sessions_inspect", when: "After creation — verify the session row + participants persisted as expected. Reads back the full session record including any participants attached at create time. Pass id OR session_id (interchangeable entry points — requiredOneOf, so the schema-level required list is empty).", required: [], }, { tool: "pica_sessions_query", when: "Discovery / browse — list recent or upcoming sessions for the workspace.", required: [], }, ], example: { title: "Vocal recording — Midnight Rivers", start_time: "2026-05-08T14:00:00+01:00", end_time: "2026-05-08T17:00:00+01:00", session_type_id: "<uuid from pica_sessions_types>", location: "Tileyard Studios", work_id: "<optional uuid>", }, }, "split-sheet-required": { workflow: "split-sheet-required", summary: "Required fields to generate a publishing split sheet on a work. Pre-condition: the work's writer credits (set via pica_credits_update) must already total 100% — pica_split_sheet_generate validates and returns 400 otherwise. The generated sheet snapshots current splits + signature state; pica_split_sheet_send notifies each credited writer for review/signature. The split_sheets.status column defaults 'draft' and is NOT enforced by a DB CHECK constraint.", primary_tool: "pica_split_sheet_generate", enum_tables: [], companion_calls: [ { tool: "pica_split_sheet_send", when: "After generate — notify each credited writer for review/signature via in-app notification + Telegram (if connected) + email. Non-PICA-user writers receive a collaboration invite email instead. Soft-fails per-writer; aggregate result reports delivered / not_a_pica_user / failed counts.", required: ["work_id", "split_sheet_id"], }, { tool: "pica_split_sheet_get", when: "Read back a specific sheet with its full content + signature state. Use after send to check who has signed.", required: ["work_id", "split_sheet_id"], }, { tool: "pica_split_sheet_list", when: "List all sheets for a work — useful when multiple revisions exist (e.g. a sheet was generated, splits changed, regenerate).", required: ["work_id"], }, ], example: { work_id: "<uuid — credits must total 100% first via pica_credits_update>", }, }, "recording-splits-required": { workflow: "recording-splits-required", summary: "Required fields per row when assigning master-rights ownership on a recording. Distinct from publishing splits on works (those flow through pica_credits_update + pica_split_sheet_*). Recording splits drive master royalty payouts and downstream credit gating; pica_recording_splits_create runs an ADR-200 Class D form-confirmation gate when the client supports `elicitation/url`. Use `dry_run: true` first — it writes nothing and returns the shares already recorded plus `likely_refusal` when the write would be refused.", primary_tool: "pica_recording_splits_create", enum_tables: [ { table: "recording_splits", columns: ["split_type", "role"] }, ], companion_calls: [ { tool: "pica_recording_splits_verify", when: "After create — explicitly mark a split as confirmed/agreed by the relevant parties. Sets verified=true + verified_at + verified_by on the row.", required: ["recording_id", "split_id"], }, { tool: "pica_recording_splits_list", when: "List all splits on a recording — verify totals and per-row verification state.", required: ["recording_id"], }, { tool: "pica_recording_splits_update", when: "A recorded share is wrong — relink a holder-less ('unknown') row to a person, or change its percentage, role, split_type, territory or dates. The 100% total is checked after the change with this row's old value left out. Changing who/how much/dates clears the holder's confirmation (verification_cleared).", required: ["recording_id", "split_id"], }, { tool: "pica_recording_splits_end", when: "A share should stop counting toward the total — sets end_date (default today) and keeps the row as history. Use instead of deleting.", required: ["recording_id", "split_id"], }, ], example: { recording_id: "<uuid>", split_type: "master", percentage: 50, role: "owner", person_id: "<optional uuid>", territory: "worldwide", }, }, "claim-required": { workflow: "claim-required", summary: "Resolve pending discoveries (credits, custody, artist links) into your catalog. Each discovery type has a typed claim verb; the generic discoveries_review surface accepts/rejects without specialised handling.", primary_tool: "pica_discoveries_query", enum_tables: [ { table: "discovered_credits", columns: ["status", "match_basis"], }, { table: "discovered_custody", columns: ["status", "custody_type"], }, { table: "discovered_artists", columns: ["status"], }, ], companion_calls: [ { tool: "pica_discoveries_review", when: "Generic accept/reject for any discovery row — accepted discoveries update your catalog automatically. Use for non-credit / non-custody / non-artist discoveries, or when the typed claim tools refuse on rate-limit / state preconditions.", required: ["discovery_id", "status"], }, { tool: "pica_claim_credit", when: "Mark a discovered_credits row as claimed — a status transition that grants view access to the linked work (no credit row is written). Rate-limited to 20 claims/hour. Re-claiming an already-resolved row returns DISCOVERY_ALREADY_RESOLVED (409).", required: ["id"], }, { tool: "pica_claim_custody", when: "Drain a discovered_custody row into a pending custody_claims row with +72h silent-consent auto-approve. The current custodian keeps the catalog row until the window elapses.", required: ["id"], }, { tool: "pica_claim_artist", when: "Drain a discovered_artists row instantly (identity evidence already validated at discovery time). Requires your identity to already be linked via pica_update_my_identity. Refuses (409) when an open artist_claims review row exists.", required: ["id"], }, ], example: { status: "pending", limit: 50, }, }, "sync-placement-required": { workflow: "sync-placement-required", summary: "Sync-placement create flow — atomic write of (placement + draft sync_license agreement + at least one agreement_party + recordings + evidence sources) in a single Postgres transaction. At least one entry in `contacts[]` is required (empty rejects with structured MISSING_CONTACT). Contacts may reference an existing `person_id` OR pass `person_name` + `role` (+ optional email/company/position) to auto-create a person row inline. The verification_status auto-advances based on evidence kinds: any non-attestation source ('cue_sheet', 'invoice', 'agreement_pdf', 'email', 'screenshot', 'web', 'spreadsheet_import') → evidence_attached; only 'user_attestation' → self_attested; none → unverified. Operator-only fields (verification_status='operator_verified'|'disputed', confidentiality_level changes, hard delete) throw OPERATOR_REQUIRED for customer agents; use update with status:'terminated' for self-service retirement.", primary_tool: "pica_sync_placements_create", enum_tables: [ { table: "sync_placements", columns: ["status", "verification_status", "confidentiality_level"], }, ], companion_calls: [ { tool: "pica_sync_placements_inspect", when: "After create — read the placement back to confirm verification_status auto-advance and inspect the linked agreement + parties + recordings + sources.", required: ["id"], }, { tool: "pica_sync_placements_update", when: "Patch fee, dates, status, territory, media_channels, brand, production_company, or any other inline field. Status transitions follow the placement state machine; illegal transitions throw INVALID_TRANSITION.", required: ["id"], }, { tool: "pica_sync_placements_cite", when: "Attach an evidence source after creation — auto-advances verification_status when the kind is non-attestation.", required: ["id", "url", "kind"], }, { tool: "pica_sync_placements_delete", when: "Operator-only hard delete (two-step confirmation). Customer agents should use pica_sync_placements_update with status:'terminated' instead for self-service soft-delete.", required: ["id"], }, { tool: "pica_sync_placements_query", when: "List placements for the org — filter by work_id, status, verification_status, brand, production_company, person_id, recording_id, source_license_enquiry_id, min_fee, currency.", required: [], }, ], example: { work_id: "00000000-0000-0000-0000-000000000000", production_title: "Midnight Rivers — Trailer 2026", production_type: "trailer", brand: "Lumière Studios", production_company: "Lumière Studios Inc.", status: "licensed", fee_amount: 25000, fee_currency: "USD", use_start_date: "2026-06-01", use_end_date: "2027-05-31", territory: ["WW"], media_channels: ["theatrical", "online"], confidentiality_level: "redacted_in_public_car", contacts: [ { person_name: "Sarah Lee", role: "music_supervisor", email: "sarah@lumiere.example", company: "Lumière Studios", }, ], recordings: [ { recording_id: "00000000-0000-0000-0000-000000000001", role: "main_master", master_fee: 12500, master_share_licensed_pct: 100, }, ], sources: [ { url: "https://example.com/agreements/midnight-rivers.pdf", kind: "agreement_pdf", field_coverage: ["fee_amount", "use_start_date", "territory"], }, ], }, }, // ADR-333 — who looks after whose catalogue. Every step sends the // version the caller read; the terms are read out to the person first. "relationship-required": { workflow: "relationship-required", summary: "Who looks after a catalogue (ADR-333). An owner invites an organisation by email with pica_relationships_grant; an organisation asks a client by email with pica_portfolio_invite. The other side answers with pica_relationships_answer: accept (optionally narrowing — never widening — type or scope), decline, or edit (a counter-proposal, or the owner's change to an active relationship). Every step on an existing relationship sends expected_version, the version read from pica_relationships_list or pica_portfolio_list; if the terms moved since, nothing happens and STATUS_CHANGED returns the current terms to read out again. Types: administrator (edits directly; splits and custody go to the owner), representative (every change goes to the owner as a proposal), viewer (sees and exports), co_owner (sees its share). Scope fields: half (both | composition | master), territories (only WW, worldwide, today: a limit to some countries is refused until a route can honour it), term_starts_on / term_ends_on (YYYY-MM-DD or null), money, directory (administrator only), catalogue (whole | chosen) with work_ids / recording_ids. Accepting, changing, revoking and an owner's invitation are the person's own decision: from a connected app they become an approval for the person; from PICA's own assistant they return HUMAN_CONFIRMATION_REQUIRED with the screen to do it on.", primary_tool: "pica_relationships_grant", companion_calls: [ { tool: "pica_portfolio_invite", when: "An organisation asks a client to let it look after their catalogue. Scope takes half, territories and term only: the client chooses what it covers when they accept.", required: ["email", "type"], }, { tool: "pica_relationships_list", when: "Before any step: read the relationship, its plain-words scope and its version; also lists invitations waiting for the person (invite_id + answer_for_one_of).", required: [], }, { tool: "pica_relationships_answer", when: "Answer a request: relationship_id + expected_version + action; or invite_id + organisation_id to accept an invitation by email (invitations can't be declined; they lapse after 30 days).", required: [], constraint: "Either relationship_id + expected_version + action, or invite_id + organisation_id. accept may only narrow type or scope; decline takes only a reason; edit needs a type or scope. With invite_id, action is accept and organisation_id is one of answer_for_one_of.", }, { tool: "pica_relationships_revoke", when: "The owner ends a relationship (or withdraws its own request). Irreversible; takes effect on the organisation's next request.", required: ["relationship_id", "expected_version"], }, { tool: "pica_relationships_step_down", when: "The organisation ends its own side (or withdraws its own request). Irreversible.", required: ["relationship_id", "expected_version"], }, { tool: "pica_relationships_activity", when: "What was done through a relationship: one relationship by relationship_id, or across the catalogue (side owner) or the organisation's clients (side organisation).", required: [], }, { tool: "pica_portfolio_list", when: "An organisation's clients: each catalogue id to pass as `catalogue` to the catalogue tools, what a change does there (direct, proposal, none), and the version for stepping down.", required: [], }, { tool: "pica_portfolio_briefing", when: "The same clients with up to five things each that need attention within scope.", required: [], }, ], example: { email: "manager@example.com", type: "administrator", scope: { half: "composition", territories: ["WW"], term_ends_on: "2027-12-31", money: false, catalogue: "whole", }, }, }, "handover-required": { workflow: "handover-required", summary: "A catalogue an organisation builds for a client, who accepts to own it (ADR-333 D5). The organisation creates it with pica_handover_create (nothing is sent; while it is being prepared it can enter facts, but nothing is published, agreed or signed in its favour), then sends it with pica_handover_send, which emails the client; they have 60 days to accept. relationship_type is what the organisation keeps once they accept: administrator, representative or viewer, over the whole catalogue. Sending is the person's own decision: from a connected app it becomes an approval for the person; otherwise PICA may return HUMAN_CONFIRMATION_REQUIRED with the screen to send it on. Accepting is the client's own act, signed in with the address it was sent to: they keep or remove each item that names the organisation, then accept or decline. No tool accepts or declines; pica_handover_answer gives the client the link.", primary_tool: "pica_handover_create", companion_calls: [ { tool: "pica_handover_send", when: "When the catalogue is ready: emails the client and starts the 60 days.", required: ["handover_id"], constraint: "expected_version is optional; when given and the hand-over changed since, nothing is sent.", }, { tool: "pica_handover_answer", when: "The client asks how to take over a catalogue prepared for them. Returns the screen link only; it never accepts or declines.", required: [], }, ], example: { catalogue_name: "Alice Example", client_name: "Alice Example", client_email: "alice@example.com", relationship_type: "administrator", }, }, }; export const REQUIRED_SCHEMA_WORKFLOWS = Object.keys(REQUIRED_SCHEMAS_SOURCE); //# sourceMappingURL=required-schemas.source.js.map