--- id: CUST-WP-0023 type: feature title: Third-Party Services Catalog (TPSC) domain: custodian status: done owner: custodian-agent topic_slug: custodian created: 2026-03-19 updated: 2026-03-20 state_hub_workstream_id: "23208a99-0ef6-4154-9454-a2f2065b6b19" --- # TPSC — Third-Party Services Catalog Track external service dependencies (APIs, SaaS, CLIs) across all repos. Primary data lives in repos (`tpsc.yaml`) and a canon catalog (`canon/tpsc/.yaml`). State-hub collects and reports. GDPR maturity scale (CNIL/IAPP CMMI-aligned): `unknown | non_compliant | initial | developing | defined | managed | certified` Pricing model: `free | paid | freemium | usage_based | unknown` ## Task: DB migration — tpsc_catalog, tpsc_snapshots, tpsc_entries ```task id: CUST-WP-0023-T01 status: done priority: high state_hub_task_id: "038a0284-bb76-4ce7-8861-1686667acbb5" ``` Create Alembic migration `j7e8f9a0b1c2_tpsc.py`. Tables: - `tpsc_catalog`: id, slug (unique), name, provider, category, website_url, pricing_model, gdpr_maturity, gdpr_notes, dpa_available, tos_url, privacy_policy_url, data_processing_regions (JSON), data_retention_notes, status (active/deprecated), created_at, updated_at - `tpsc_snapshots`: id, repo_id (FK managed_repos nullable), snapshot_at, source_file, entry_count - `tpsc_entries`: id, snapshot_id (FK), catalog_id (FK tpsc_catalog nullable), service_slug, purpose, auth_type, endpoint_override, notes --- ## Task: SQLAlchemy models ```task id: CUST-WP-0023-T02 status: done priority: high state_hub_task_id: "990d4e58-35b0-45f0-8de6-8955049aa7d5" ``` Create `api/models/tpsc.py` with TPSCCatalog, TPSCSnapshot, TPSCEntry models. Register in `api/models/__init__.py`. --- ## Task: Pydantic schemas ```task id: CUST-WP-0023-T03 status: done priority: high state_hub_task_id: "5feeb161-b654-4e4a-8a6a-bb685c239ce5" ``` Create `api/schemas/tpsc.py` with Read/Create schemas for all three models. Include `GDPRMaturity` and `PricingModel` string enums. `TPSCCatalog` schema: include `gdpr_warning: bool` computed field (True when gdpr_maturity in [unknown, non_compliant, initial]). --- ## Task: FastAPI router /tpsc/ ```task id: CUST-WP-0023-T04 status: done priority: high state_hub_task_id: "593471b4-cd3a-4251-8c5c-ee42b6a9e089" ``` Create `api/routers/tpsc.py`: - `GET /tpsc/catalog/` — list services (filter: gdpr_maturity, category, pricing_model) - `GET /tpsc/catalog/{slug}` — single service - `POST /tpsc/catalog/` — register/upsert service - `POST /tpsc/ingest/` — accept snapshot + entries for a repo - `GET /tpsc/snapshots/` — list snapshots (filter: repo_slug) - `GET /tpsc/report/gdpr` — aggregated GDPR warnings across all repos Register in `api/main.py`. --- ## Task: MCP tools ```task id: CUST-WP-0023-T05 status: done priority: high state_hub_task_id: "7370d020-06cf-4ebe-9f78-619e41c4b85c" ``` Add to `mcp_server/server.py`: - `register_service(slug, name, provider, pricing_model, gdpr_maturity, ...)` - `list_services(gdpr_maturity?, category?, pricing_model?)` - `ingest_tpsc_tool(repo_slug)` — runs ingest_tpsc.py for the repo - `get_gdpr_report()` — returns warning summary across all repos --- ## Task: Ingest script ```task id: CUST-WP-0023-T06 status: done priority: high state_hub_task_id: "5ec305d3-fdfb-4f81-b3ab-1ea57a4ec0c5" ``` Create `scripts/ingest_tpsc.py`: - Reads `tpsc.yaml` from repo root (auto-detected via registered local_path) - Resolves catalog_id by slug for each entry - POSTs snapshot + entries to `/tpsc/ingest/` - `--dry-run` flag - `--repo SLUG` or `--all` flags Add Makefile targets: ``` make ingest-tpsc REPO= make ingest-tpsc-all ``` --- ## Task: Canon service catalog seed files ```task id: CUST-WP-0023-T07 status: done priority: medium state_hub_task_id: "21c13b4d-585a-4a60-a283-29baa2dd6d7d" ``` Create `canon/tpsc/` with YAML files for: - `openai-api.yaml` - `anthropic-api.yaml` - `gemini-api.yaml` - `openrouter-api.yaml` Each file: slug, name, provider, category, pricing_model, gdpr_maturity, gdpr_notes, dpa_available, tos_url, privacy_policy_url, data_processing_regions, data_retention_notes. --- ## Task: tpsc.yaml for llm-connect ```task id: CUST-WP-0023-T08 status: done priority: medium state_hub_task_id: "d658f81b-7408-456d-b4bd-440b44a67e43" ``` Create `/home/worsch/llm-connect/tpsc.yaml` declaring: openai-api, anthropic-api (via claude_code CLI), gemini-api, openrouter-api. --- ## Task: Dashboard page ```task id: CUST-WP-0023-T09 status: done priority: medium state_hub_task_id: "a5367fc4-ef12-4f52-b642-d33b91b7cc2c" ``` Create `dashboard/src/tpsc.md`: - Service catalog table with GDPR maturity color coding (red: non_compliant/unknown, amber: initial/developing, green: defined+) - Warning cards for repos using non-green services - Per-repo breakdown table - Pricing model summary Add to `observablehq.config.js` nav. --- ## Task: Documentation page ```task id: CUST-WP-0023-T10 status: done priority: low state_hub_task_id: "6cf1aaec-5c24-4cb5-a1aa-415caeaaca10" ``` Create `dashboard/src/docs/tpsc.md` explaining: - TPSC concept and rationale - tpsc.yaml format with full example - GDPR maturity scale definitions (linked to CNIL/IAPP) - How to add a new service to the canon catalog Add to docs nav in `observablehq.config.js`.