People Analytics Toolbox

Stop creating manual workarounds for the gaps in and between your operational and analytical systems.

Operating system scaffolding for organizational learning: MCP-callable analytical services with versioned contracts. 22 live analytical spokes today (+ one coming-soon Principia bridge) wired to Postgres, Insight Cards, HTTP, and MCP. Open contracts · no SKU lock-in.

The problem

Four things every PA team lives with.

01

People Analytics is a discipline. Most tools weren't built for it.

People analytics is its own discipline with distinct constraints — many segmentation dimensions change with every restructure. Privacy limits mean layered reporting touches many distinct audiences. Serious methods (, preference modeling, longitudinal stats) routinely outpace what generic BI presets ship.

Standalone BI stacks leave PA-specific joins, hierarchies, and governance as your problem; HR SaaS dashboards often omit the analytic substrate entirely. Someone still has to close the methodology gap—the People Analyst lands there by default.

02

Every PA team rebuilds the same wheel.

Routine PA studies—cohort retention curves, segmented engagement deltas, equitable pay ladders, canonical HRIS normalization—solve the same methodological shape again and again. Yet stacks rarely package those substrates as interoperable primitives.

Without shared services, analysts still rebuild static slices by hand—the craft time goes to scaffolding instead of hypotheses. None of this is inevitable; it is tooling choice.

03

The patchwork costs a fortune and doesn't compose.

Procurement sprawl buys overlapping SKUs whose data models still disagree. Stitching surveys to systems-of-record collapses back to spreadsheets—and most stacks still expose chat wrappers instead of typed, auditable primitives for agents.

04

The KPI gallery hides signal underneath "engagement dashboards."

Dense KPI galleries ask humans to sift signal manually. Insight workflows here rank anomalies, methodological flags, and statistically meaningful segment contrasts instead of dumping another unscored leaderboard.

We're building the Google-era version. Pre-compute the analyses across every relevant segment. Score them for statistical significance and executive relevance. Surface what actually matters. The algorithms do the relevance search. The user gets signal, not clutter. The architectural commitments that enable this — code-first instead of cube-first, typed data contracts between components, AI-callable from the ground up — aren't features; they're the paradigm shift.

The idea

Not dashboards — faster organizational learning.

Most stacks still push another gallery of unscored KPIs. The real product is faster organizational learning — closing the loop between evidence quality, methodological honesty, segmentation stability, and executives who deserve answers they can cite under scrutiny.

We frame the toolbox as an operating system layer for that cadence — services for segmentation, statistical enrichment, privacy-gated joins, decision simulations; composable beneath the UIs your org already mandates instead of orphaned slide factories.

Want the fuller argument — three layers (kitchen → recipes → diner), why workforce intelligence graphs over time matter, vendor lanes compared squarely against us? Read the operating-system essay.

How to start

Three steps. No procurement cycle required.

Three forward motions a People Analytics team can take in the next thirty minutes.

01

See it live.

Open the Insight Player. Real Insight Cards advance — psychometrics, segmentation, statistical enrichment — generated by production toolbox surfaces you can inspect with the MCP audit trail beside them.

02

Copy a curl.

Paste a runnable curl into your terminal. The public reads return payload immediately; POST examples need a PAT-11 service key. No OAuth dance on the proofs above the fold.

03

Pick the spoke that solves your hardest problem.

Vendor its contract. Replace one of the workarounds you're maintaining today. Need a guide? Book thirty minutes — we'll map your roadmap to the catalog and show you what the integration looks like.

What's in the toolbox

22 independently-versioned analytical microservices.

MCP-callable analytical services spanning psychometrics, stated preferences, segmentation, calculus, forecasting, wage compliance, org intelligence, workforce planning, and companion catalog surfaces — each keyed to Postgres schemas listed in TOOLBOX_SCHEMAS. Three additional cross-repo siblings (DevPlane, Principia, Meta-Factory) publish on their own stacks but ride the same marketing grid with PAT-ECOSYSTEM-A honesty chips labeling their separate CI. Advanced psychometrics, preference modeling, privacy, dataset augmentation, segmentation, statistics, and decision tools. Each owns a Postgres schema, an API namespace, and a versioned contract. Adopt one. Add another when you're ready.

illustration slot

reincarnation

reincarnation

Live

Adaptive psychometric diagnostics. Information-gain item selection, six-stage pool lifecycle, live Cronbach α — IRT that no HR platform ships.

/spokes/reincarnation

illustration slot

performance-calibration

performance-calibration

Live

Persisted calibration pairs — cycle rollups, ELT-aware ladders, trajectory windows anchored in Postgres-backed contracts.

/spokes/performance-calibration

illustration slot

performance-validity

performance-validity

Live

Rating instrument diagnostics (Q-convergence ladder) plus MEI evidence bridges into manager-effectiveness recalibration flows.

/spokes/performance-validity

illustration slot

preference-modeler

preference-modeler

Live

Stated-preference methods as services. Real MaxDiff / conjoint / penny allocation with BIBD balancing and MNL utilities. Anonymity-gated by contract.

/spokes/preference-modeler

illustration slot

program-evaluation

program-evaluation

Live

Causal experiments as services — deterministic arm pulls, immutable pre-registration digests, lift payloads with audited contracts.

/spokes/program-evaluation

illustration slot

data-anonymizer

data-anonymizer

Live

Privacy as a contract. 24-rule PII catalog, deterministic HMAC tokenization, redaction-with-spans, min-N gate enforced at the API boundary.

/spokes/data-anonymizer

illustration slot

segmentation-studio

segmentation-studio

Live

HRIS stitching done right. 35-field canonical normalization, multi-pass identity resolution, versioned segmentation packs downstream consumers pin to.

/spokes/segmentation-studio

illustration slot

calculus

calculus

Live

Statistical honesty in the toolbox. Wilson / t / normal CI auto-selected, MetricEnvelope shape, trend + anomaly + factory.

/spokes/calculus

illustration slot

factor-models

factor-models

Live

Versioned latent-factor models — validate → publish gates with fairness snapshots before calculus-scored composites ship.

/spokes/factor-models

illustration slot

forecasting

forecasting

Live

Decision theory as service APIs. Seeded Monte Carlo, EVPI / EVSI, aligned-chance decision trees — Lumina / @RISK rigor, callable from anywhere.

/spokes/forecasting

illustration slot

workforce-planning

workforce-planning

Live

Conductor reconcile loop — ATS ↔ approved headcount matching, probabilistic matchers, forecasting snapshots persisted in-schema.

/spokes/workforce-planning

illustration slot

anycomp

anycomp

Live

Comp decision logic, decomposed. Models, evaluate, cycles, BLS labor-market context. Math you can see and swap out.

/spokes/anycomp

illustration slot

metrics-catalog

metrics-catalog

Live

Catalog 1 — seeded HR definitions with stable composite IDs that calculus soft-validation already references in production.

/spokes/metrics-catalog

illustration slot

org-graph

org-graph

Live

Typed org snapshots with temporal supervisor edges — rebuild, QA, ancestry queries, workbook-style substring ELT matching.

/spokes/org-graph

illustration slot

job-family-agent

job-family-agent

Live

SOC-aligned taxonomy + classify API sourced from seeded O*NET v28.3 JSON — deterministic contract exports for consumers.

/spokes/job-family-agent

illustration slot

manager-effectiveness

manager-effectiveness

Live

Nine-domain MEI composites with tenant pillar weights plus empirical recalibration POST (`/weights/recalibrate`) and audit narration.

/spokes/manager-effectiveness

illustration slot

wage-compliance

wage-compliance

Live

Rule-versioned jurisdictional evaluations — refresh connectors, ATS offer checks, postings, temporal diff MCP / cron artefacts.

/spokes/wage-compliance

illustration slot

worker-resolution

worker-resolution

Live

Two-pass deterministic identity merges with lineage tables and failure rosters — contract-first ingestion for downstream graphs.

/spokes/worker-resolution

See it surface

The Insight Player, advancing real cards.

A paced Insight Player surfaces cards sequenced across the production spokes wired on this deploy — calculus enrichments, preference utilities, anomaly flags. Built on the same gateway this page describes.

/feed

Blocked on third-party previews? Component already falls back in production UX — this illustration assumes same-origin deploy.

Architecture

People analytics, treated as a software engineering problem.

AI native. Microservice design. Play-nice APIs. No vendor lock-in.

AxisMonolithic suiteThe toolbox
Adoption shapeAll-or-nothing; buy the suiteSpoke-by-spoke; adopt one, add others later
Algorithm ownershipLocked inside the vendor's productThe toolbox is yours; consumers vendor contracts and run their own UIs
AI integrationChat wrapper on an unchanged backendEvery contract is Zod-typed; every spoke is an MCP tool surface
Survey × systems joinBest-effort CSV bridgesFirst-class — segmentation × privacy × calculus make it the central op
Methodology transparencyBlack box ("proprietary engine")Algorithms are code, contracts are Zod, math specs are in docs/
Pricing modelPer-seat / per-employeeOpen contracts · deploy on your infra · pay your platform provider on usage you control

Contracts stay typed across HTTP + MCP — the registry JSON beneath is regenerated every request without manual copy maintenance.

Live health · GET /api/health

Fetching live status…

Contract registry

Same payload shape as JSON.stringify(registry, null, 2) from GET /api/registry — refreshed without hand-maintained slug lists beside it.

Open raw /api/registry

generatedAt 2026-06-03T12:01:21.772Zupdated just now

{
  "spokes": [
    {
      "slug": "reincarnation",
      "schema": "reincarnation",
      "contractVersion": "1.2.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/reincarnation/contracts/types.ts",
      "contracts": [
        {
          "id": "reincarnation.adaptive-selection",
          "description": "Information-gain-weighted adaptive item selection from a galaxy's pools.",
          "endpoint": "/api/spokes/reincarnation/adaptive-selection",
          "method": "POST",
          "requestSchema": "AdaptiveSelectionRequestSchema",
          "responseSchema": "AdaptiveSelectionResponseSchema"
        },
        {
          "id": "reincarnation.responses-ingest",
          "description": "Bulk-ingest respondent answers for psychometric scoring.",
          "endpoint": "/api/spokes/reincarnation/responses/ingest",
          "method": "POST",
          "requestSchema": "ResponseIngestRequestSchema",
          "responseSchema": "ResponseIngestResponseSchema"
        },
        {
          "id": "reincarnation.psychometric-feed",
          "description": "Per-galaxy psychometric statistics feed (Cronbach alpha, IRT, virtues).",
          "endpoint": "/api/spokes/reincarnation/psychometric-feed",
          "method": "GET",
          "responseSchema": "PsychometricFeedResponseSchema"
        },
        {
          "id": "reincarnation.galaxies.register",
          "description": "Idempotent consumer bootstrap: register a pool galaxy, universal items (RIDs), and study items (SIDs) in one atomic transaction.",
          "endpoint": "/api/spokes/reincarnation/galaxies",
          "method": "POST",
          "requestSchema": "GalaxyRegisterRequestSchema",
          "responseSchema": "GalaxyRegisterResponseSchema"
        },
        {
          "id": "reincarnation.galaxies.items-append",
          "description": "Append RIDs and/or study items to an existing galaxy (404 if galaxy_id is unknown); same idempotency semantics as galaxies.register.",
          "endpoint": "/api/spokes/reincarnation/galaxies/[id]/items",
          "method": "POST",
          "requestSchema": "GalaxyAppendItemsRequestSchema",
          "responseSchema": "GalaxyAppendItemsResponseSchema"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "cardId": "PFX-7",
          "status": "in-progress"
        },
        {
          "name": "vela",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "performance-calibration",
      "schema": "performance_calibration",
      "contractVersion": "1.0.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/performance-calibration/contracts/types.ts",
      "contracts": [
        {
          "id": "performance-calibration.health",
          "description": "Per-spoke Postgres reachability shim on `performance_calibration` (heartbeat table probe against shared Supabase).",
          "endpoint": "/api/spokes/performance-calibration/health",
          "method": "GET",
          "responseSchema": "SpokeHealth"
        },
        {
          "id": "performance-calibration.pairs.ingest",
          "description": "UPSERT `{ employeeId × cycleId }` calibration rows per tenant (`parseRating` normalization; optional ELT/L3/L4/L5 leader chain persisted for rollups).",
          "endpoint": "/api/spokes/performance-calibration/pairs",
          "method": "POST",
          "requestSchema": "IngestRequestSchema",
          "responseSchema": "IngestResponseSchema"
        },
        {
          "id": "performance-calibration.cycles.summary",
          "description": "Side-by-side cycle stats: bumped up/down/unchanged + percentages (pct unchanged ≡ calibration-room accuracy among complete parses) plus per-leader aggregates at elt/l3/l4/l5 layers.",
          "endpoint": "/api/spokes/performance-calibration/summary",
          "method": "GET",
          "responseSchema": "CycleQueryResponseSchema"
        },
        {
          "id": "performance-calibration.employees.trajectory",
          "description": "Dedup latest row per calibration cycle then return the trailing N cycles for one employee (`lastNCycles`, default 6) with deltas + attribution to elt/l3 leaders captured at ingest time.",
          "endpoint": "/api/spokes/performance-calibration/trajectory",
          "method": "GET",
          "responseSchema": "EmployeeTrajectoryResponseSchema"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "status": "planned"
        },
        {
          "name": "vela",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "performance-validity",
      "schema": "performance_validity",
      "contractVersion": "1.2.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/performance-validity/contracts/types.ts",
      "contracts": [
        {
          "id": "performance-validity.health",
          "description": "Postgres heartbeat on `performance_validity` proving the diagnostics spoke DDL is reachable in the shared project.",
          "endpoint": "/api/spokes/performance-validity/health",
          "method": "GET",
          "responseSchema": "SpokeHealth"
        },
        {
          "id": "performance-validity.analyze.rating-ops-convergence",
          "description": "Q1: Pearson convergence between normalized ratings + operational composites with structured diagnostic-chain rankings.",
          "endpoint": "/api/spokes/performance-validity/rating-ops-convergence",
          "method": "POST",
          "requestSchema": "RatingOpsConvergenceRequestSchema",
          "responseSchema": "RatingOpsConvergenceResponseSchema"
        },
        {
          "id": "performance-validity.analyze.validity-scorecard",
          "description": "Q2: Reliability facets (test–retest, optional ICC inter-rater, cross-cycle stability) plus convergent / predictive / discriminant correlations.",
          "endpoint": "/api/spokes/performance-validity/validity-scorecard",
          "method": "POST",
          "requestSchema": "ValidityScorecardRequestSchema",
          "responseSchema": "ValidityScorecardResponseSchema"
        },
        {
          "id": "performance-validity.analyze.predictability-decomposition",
          "description": "Q3: Stacked additive OLS R² deltas using shared inference primitives (baseline → progressively richer covariates).",
          "endpoint": "/api/spokes/performance-validity/predictability-decomposition",
          "method": "POST",
          "requestSchema": "PredictabilityDecompositionRequestSchema",
          "responseSchema": "PredictabilityDecompositionResponseSchema"
        },
        {
          "id": "performance-validity.analyze.trajectory-slope",
          "description": "Q4: Per-employee longitudinal slope/regression diagnostics with pooled-mean regression-to-mean heuristics.",
          "endpoint": "/api/spokes/performance-validity/trajectory-analysis",
          "method": "POST",
          "requestSchema": "TrajectoryAnalysisRequestSchema",
          "responseSchema": "TrajectoryAnalysisResponseSchema"
        },
        {
          "id": "performance-validity.analyze.calibration-value-add",
          "description": "Q5: Five-test calibration ROI panel inferred from PAT-159 rollups fetched over HTTP — validity/reliability/convergence/noise probes.",
          "endpoint": "/api/spokes/performance-validity/calibration-value-add",
          "method": "POST",
          "requestSchema": "CalibrationValueAddRequestSchema",
          "responseSchema": "CalibrationValueAddResponseSchema"
        },
        {
          "id": "performance-validity.analyze.change-attribution",
          "description": "Q6: Ranked explanatory factors comparing two calibration cycles via performance-calibration trajectories fetched over HTTP.",
          "endpoint": "/api/spokes/performance-validity/change-attribution",
          "method": "POST",
          "requestSchema": "ChangeAttributionRequestSchema",
          "responseSchema": "ChangeAttributionResponseSchema"
        },
        {
          "id": "performance-validity.mei.evidence.upsert",
          "description": "Persist univariate predictive r² summaries (per MEI domain × analytic cycle × outcome) that feed manager-effectiveness empirical weight blends.",
          "endpoint": "/api/spokes/performance-validity/mei-predictive-evidence",
          "method": "POST",
          "requestSchema": "MeiPredictiveEvidenceUpsertRequestSchema",
          "responseSchema": "MeiPredictiveEvidenceUpsertResponseSchema"
        },
        {
          "id": "performance-validity.alignment",
          "description": "PA Instrument — Rater alignment diagnostics: per-item convergence between observed ratings.",
          "endpoint": "/api/spokes/performance-validity/alignment",
          "method": "POST",
          "requestSchema": "AlignmentRequestSchema",
          "responseSchema": "AlignmentResultSchema"
        },
        {
          "id": "performance-validity.directional-alignment",
          "description": "PA Instrument — Up/down/lateral directional alignment between a focal rater and each cohort.",
          "endpoint": "/api/spokes/performance-validity/directional-alignment",
          "method": "POST",
          "requestSchema": "DirectionalAlignmentRequestSchema",
          "responseSchema": "DirectionalAlignmentResultSchema"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "preference-modeler",
      "schema": "preference_modeler",
      "contractVersion": "1.6.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/preference-modeler/contracts/types.ts",
      "contracts": [
        {
          "id": "preference-modeler.surveys.create",
          "description": "Create a survey definition with sections + questions in one bundle.",
          "endpoint": "/api/spokes/preference-modeler/surveys",
          "method": "POST",
          "requestSchema": "CreateSurveyRequestSchema",
          "responseSchema": "FullSurveyPackageSchema"
        },
        {
          "id": "preference-modeler.surveys.get",
          "description": "Fetch the full survey package by ID.",
          "endpoint": "/api/spokes/preference-modeler/surveys/[id]",
          "method": "GET",
          "responseSchema": "FullSurveyPackageSchema"
        },
        {
          "id": "preference-modeler.surveys.responses",
          "description": "Submit respondent answers; per-question rejection reasons returned alongside accepts.",
          "endpoint": "/api/spokes/preference-modeler/surveys/[id]/responses",
          "method": "POST",
          "requestSchema": "RespondentSubmissionSchema",
          "responseSchema": "SubmitResponseResponseSchema"
        },
        {
          "id": "preference-modeler.surveys.tasks",
          "description": "Materialize deterministic MaxDiff / Conjoint task assignments for one survey respondent.",
          "endpoint": "/api/spokes/preference-modeler/surveys/[id]/respondents/[respondentId]/tasks",
          "method": "POST",
          "responseSchema": "GeneratedPreferenceTasksResponseSchema"
        },
        {
          "id": "preference-modeler.surveys.preferences",
          "description": "Aggregated preference weights, anonymity-gated by minimumResponseThreshold. Optional ?bySegment=true adds bySegment[] (per-segment weights + anonymity); cohorts below threshold omitted.",
          "endpoint": "/api/spokes/preference-modeler/surveys/[id]/preferences",
          "method": "GET",
          "responseSchema": "PreferenceWeightsResponseSchema"
        },
        {
          "id": "preference-modeler.maxdiff.generate",
          "description": "PA Instrument — Generate deterministic MaxDiff task sets from an item list.",
          "endpoint": "/api/spokes/preference-modeler/maxdiff/generate",
          "method": "POST",
          "requestSchema": "MaxDiffGenerateRequestSchema",
          "responseSchema": "MaxDiffTasksSchema"
        },
        {
          "id": "preference-modeler.maxdiff.score",
          "description": "PA Instrument — Score MaxDiff responses into per-item and per-variable preference weights.",
          "endpoint": "/api/spokes/preference-modeler/maxdiff/score",
          "method": "POST",
          "requestSchema": "MaxDiffScoreRequestSchema",
          "responseSchema": "MaxDiffScoresSchema"
        },
        {
          "id": "preference-modeler.present-future",
          "description": "PA Instrument — Present-vs-future dimension gap analysis from marker responses.",
          "endpoint": "/api/spokes/preference-modeler/present-future",
          "method": "POST",
          "requestSchema": "PresentFutureRequestSchema",
          "responseSchema": "PresentFutureResultSchema"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "cardId": "PFX-4",
          "status": "in-progress"
        }
      ]
    },
    {
      "slug": "program-evaluation",
      "schema": "program_evaluation",
      "contractVersion": "0.2.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/program-evaluation/contracts/types.ts",
      "contracts": [
        {
          "id": "program-evaluation.health",
          "description": "Per-spoke heartbeat shim for Postgres reachability on `program_evaluation`.",
          "endpoint": "/api/spokes/program-evaluation/health",
          "method": "GET",
          "responseSchema": "SpokeHealth"
        },
        {
          "id": "program-evaluation.experiments.create",
          "description": "Service-key gated experiment metadata + treatment arms (normalized randomization weights) under PAT-N7 tenant context.",
          "endpoint": "/api/spokes/program-evaluation/experiments",
          "method": "POST",
          "requestSchema": "ExperimentCreateRequestSchema",
          "responseSchema": "ExperimentCreateResponseSchema"
        },
        {
          "id": "program-evaluation.experiments.get",
          "description": "Public read of experiment detail (arms + optional pre-registration). Returns 409 when stored pre-registration digest fails verification.",
          "endpoint": "/api/spokes/program-evaluation/experiments/[id]",
          "method": "GET",
          "responseSchema": "ExperimentDetailResponseSchema"
        },
        {
          "id": "program-evaluation.experiments.pre-register",
          "description": "Immutable pre-registration bundle with SHA-256 digest (409 if an operator attempts to rewrite an existing row).",
          "endpoint": "/api/spokes/program-evaluation/experiments/[id]/pre-register",
          "method": "POST",
          "requestSchema": "PreRegistrationWriteRequestSchema",
          "responseSchema": "PreRegistrationWriteResponseSchema"
        },
        {
          "id": "program-evaluation.assignments.resolve",
          "description": "Deterministic HMAC-weighted arm selection for `(experimentId, subjectId)` with lazy assignment persistence.",
          "endpoint": "/api/spokes/program-evaluation/assignments/resolve",
          "method": "POST",
          "requestSchema": "AssignmentResolveRequestSchema",
          "responseSchema": "AssignmentResolveResponseSchema"
        },
        {
          "id": "program-evaluation.lift.estimate",
          "description": "Difference-in-means vs baseline with bootstrap CI; requires successful pre-registration digest verification first.",
          "endpoint": "/api/spokes/program-evaluation/experiments/[id]/lift",
          "method": "POST",
          "requestSchema": "LiftComputeRequestSchema",
          "responseSchema": "LiftComputeResponseSchema"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "talent-value",
      "schema": "talent_value",
      "contractVersion": "0.5.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/talent-value/contracts/types.ts",
      "contracts": [
        {
          "id": "talent-value.health",
          "description": "Per-spoke heartbeat shim for Postgres reachability on `talent_value`.",
          "endpoint": "/api/spokes/talent-value/health",
          "method": "GET",
          "responseSchema": "SpokeHealth"
        },
        {
          "id": "talent-value.elv.compute",
          "description": "ELV (Employee Lifetime Value) per segment from HCROI × avg annual cost × tenure-at-exit, inputs exposed as a drill-down + optional seeded-MC uncertainty. Relative/prioritization, not GAAP. Falls back to seeded examples when the tenant has no stored inputs.",
          "endpoint": "/api/spokes/talent-value/elv",
          "method": "GET",
          "responseSchema": "ElvReadResponseSchema"
        },
        {
          "id": "talent-value.inputs.upsert",
          "description": "Service-key gated upsert of a tenant's per-segment ELV inputs (HCROI, avg annual cost, tenure-at-exit + provenance) keyed by segmentation-studio segment id.",
          "endpoint": "/api/spokes/talent-value/inputs",
          "method": "POST",
          "requestSchema": "ElvInputsUpsertRequestSchema",
          "responseSchema": "ElvInputsUpsertResponseSchema"
        },
        {
          "id": "talent-value.nav.compute",
          "description": "NA% (share activated), NAV (NA% × ELV), and Opportunity (ELV − NAV) per segment, sorted by Opportunity descending with portfolio totals. NA% denominator headcount (canonical) or survey-response (labeled). The capital-allocation prioritization signal.",
          "endpoint": "/api/spokes/talent-value/nav",
          "method": "GET",
          "responseSchema": "NavReadResponseSchema"
        },
        {
          "id": "talent-value.activation.upsert",
          "description": "Service-key gated upsert of a tenant's per-segment activation readings (activated/eligible headcount or precomputed NA% + basis + provenance) — the NA% basis for NAV.",
          "endpoint": "/api/spokes/talent-value/activation",
          "method": "POST",
          "requestSchema": "ActivationUpsertRequestSchema",
          "responseSchema": "ActivationUpsertResponseSchema"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "cardId": "PFX-75",
          "status": "planned"
        },
        {
          "name": "performix",
          "cardId": "PFX-74",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "leadership-quality",
      "schema": "leadership_quality",
      "contractVersion": "0.1.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/leadership-quality/contracts/types.ts",
      "contracts": [
        {
          "id": "leadership-quality.health",
          "description": "Per-spoke heartbeat shim for Postgres reachability on `leadership_quality`.",
          "endpoint": "/api/spokes/leadership-quality/health",
          "method": "GET",
          "responseSchema": "SpokeHealth"
        },
        {
          "id": "leadership-quality.score.read",
          "description": "Leadership Quality (0–100, decomposable) per stored subject — composite over performance-program / activation-CAMS / comp-stewardship component sub-indexes, each independently retrievable. Falls back to seeded examples.",
          "endpoint": "/api/spokes/leadership-quality/score",
          "method": "GET",
          "responseSchema": "LeadershipReadResponseSchema"
        },
        {
          "id": "leadership-quality.score.compute",
          "description": "Service-key gated stateless compute of a Leadership Quality index from a supplied component-reading set (no persistence).",
          "endpoint": "/api/spokes/leadership-quality/score",
          "method": "POST",
          "requestSchema": "LeadershipScoreRequestSchema",
          "responseSchema": "LeadershipScoreResponseSchema"
        },
        {
          "id": "leadership-quality.readings.upsert",
          "description": "Service-key gated upsert of a subject's component readings (sub-index + drill-down + provenance) keyed by component.",
          "endpoint": "/api/spokes/leadership-quality/readings",
          "method": "POST",
          "requestSchema": "ReadingsUpsertRequestSchema",
          "responseSchema": "ReadingsUpsertResponseSchema"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "cardId": "PFX-79",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "survey-orchestrator",
      "schema": "survey_orchestrator",
      "contractVersion": "0.4.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/survey-orchestrator/contracts/types.ts",
      "contracts": [
        {
          "id": "survey-orchestrator.health",
          "description": "Per-spoke heartbeat shim for Postgres reachability on `survey_orchestrator`.",
          "endpoint": "/api/spokes/survey-orchestrator/health",
          "method": "GET",
          "responseSchema": "SpokeHealth"
        },
        {
          "id": "survey-orchestrator.programs.list",
          "description": "Public read of a tenant's survey program catalog.",
          "endpoint": "/api/spokes/survey-orchestrator/programs",
          "method": "GET",
          "responseSchema": "ProgramListResponseSchema"
        },
        {
          "id": "survey-orchestrator.programs.upsert",
          "description": "Service-key gated upsert of a survey program (cadence/engine/status + optional triggers + fatigue policy).",
          "endpoint": "/api/spokes/survey-orchestrator/programs",
          "method": "POST",
          "requestSchema": "ProgramUpsertRequestSchema",
          "responseSchema": "ProgramUpsertResponseSchema"
        },
        {
          "id": "survey-orchestrator.waves.schedule",
          "description": "Service-key gated wave scheduling/materialization with an audience snapshot hash.",
          "endpoint": "/api/spokes/survey-orchestrator/waves",
          "method": "POST",
          "requestSchema": "WaveScheduleRequestSchema",
          "responseSchema": "WaveScheduleResponseSchema"
        },
        {
          "id": "survey-orchestrator.triggers.ingest",
          "description": "Service-key gated ATS/HRIS event ingestion → matching-trigger evaluation → wave scheduling (fatigue-respecting).",
          "endpoint": "/api/spokes/survey-orchestrator/triggers/ingest",
          "method": "POST",
          "requestSchema": "TriggerIngestRequestSchema",
          "responseSchema": "TriggerIngestResponseSchema"
        },
        {
          "id": "survey-orchestrator.responses.link",
          "description": "Service-key gated wave invite/response record linked to the program's longitudinal respondent (same person across waves) + fatigue decision.",
          "endpoint": "/api/spokes/survey-orchestrator/responses",
          "method": "POST",
          "requestSchema": "ResponseLinkRequestSchema",
          "responseSchema": "ResponseLinkResponseSchema"
        },
        {
          "id": "survey-orchestrator.journey.read",
          "description": "Public per-respondent longitudinal journey across a program's waves (14d→90d→quarterly→yearly), ordered, with response pointers.",
          "endpoint": "/api/spokes/survey-orchestrator/journey",
          "method": "GET",
          "responseSchema": "RespondentJourneySchema"
        },
        {
          "id": "survey-orchestrator.programs.trend",
          "description": "Public per-wave participation/response trend for a program — a cohort aggregate, min-N suppressed (privacy floor).",
          "endpoint": "/api/spokes/survey-orchestrator/trend",
          "method": "GET",
          "responseSchema": "ProgramTrendResponseSchema"
        },
        {
          "id": "survey-orchestrator.attrition.records.upsert",
          "description": "Service-key gated from-to movement records (reverse-exit 'from' + key-talent-exit 'to') — the attrition-analysis substrate (PAT-183).",
          "endpoint": "/api/spokes/survey-orchestrator/attrition/records",
          "method": "POST",
          "requestSchema": "MovementRecordsUpsertRequestSchema",
          "responseSchema": "MovementRecordsUpsertResponseSchema"
        },
        {
          "id": "survey-orchestrator.attrition.analyze",
          "description": "Public from-to attrition analysis: talent-competitor map (win-from / lose-to + why), regretted + avoidable rates, movement drivers — segment-aligned, min-N suppressed (PAT-183).",
          "endpoint": "/api/spokes/survey-orchestrator/attrition/analysis",
          "method": "GET",
          "responseSchema": "AttritionAnalysisSchema"
        },
        {
          "id": "survey-orchestrator.attraction.analyze",
          "description": "Service-key gated stateless Attraction analysis: funnel-stage yields + offer-accept rate + 0–100 brand/consideration index per segment, min-N suppressed (PAT-185, low priority).",
          "endpoint": "/api/spokes/survey-orchestrator/attraction/analyze",
          "method": "POST",
          "requestSchema": "AttractionAnalyzeRequestSchema",
          "responseSchema": "AttractionAnalyzeResponseSchema"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "cardId": "PFX-76",
          "status": "planned"
        },
        {
          "name": "performix",
          "cardId": "PFX-77",
          "status": "planned"
        },
        {
          "name": "performix",
          "cardId": "PFX-78",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "data-anonymizer",
      "schema": "data_anonymizer",
      "contractVersion": "1.2.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/data-anonymizer/contracts/types.ts",
      "contracts": [
        {
          "id": "data-anonymizer.pii-rules",
          "description": "List active PII detection rules (header + content patterns).",
          "endpoint": "/api/spokes/data-anonymizer/pii-rules",
          "method": "GET",
          "responseSchema": "PiiRulesResponseSchema"
        },
        {
          "id": "data-anonymizer.redact",
          "description": "Redact PII spans from text; returns redacted text + flagged spans with category/risk.",
          "endpoint": "/api/spokes/data-anonymizer/redact",
          "method": "POST",
          "requestSchema": "RedactionRequestSchema",
          "responseSchema": "RedactionResponseSchema"
        },
        {
          "id": "data-anonymizer.tokenize",
          "description": "Deterministic HMAC-keyed tokenization with per-tenant cache.",
          "endpoint": "/api/spokes/data-anonymizer/tokenize",
          "method": "POST",
          "requestSchema": "TokenizationRequestSchema",
          "responseSchema": "TokenizationResponseSchema"
        },
        {
          "id": "data-anonymizer.min-n-check",
          "description": "Cohort-size privacy gate.",
          "endpoint": "/api/spokes/data-anonymizer/min-n-check",
          "method": "POST",
          "requestSchema": "MinNCheckRequestSchema",
          "responseSchema": "MinNCheckResponseSchema"
        },
        {
          "id": "data-anonymizer.transform",
          "description": "Apply a deterministic anonymization strategy to a scalar value (faker + keyed hashing; tenant-scoped).",
          "endpoint": "/api/spokes/data-anonymizer/transform",
          "method": "POST",
          "requestSchema": "TransformRequestSchema",
          "responseSchema": "TransformResponseSchema"
        },
        {
          "id": "data-anonymizer.strategies",
          "description": "List supported anonymization strategies for discovery (name, category, description).",
          "endpoint": "/api/spokes/data-anonymizer/strategies",
          "method": "GET",
          "responseSchema": "StrategiesResponseSchema"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "cardId": "PFX-8",
          "status": "in-progress"
        }
      ]
    },
    {
      "slug": "segmentation-studio",
      "schema": "segmentation_studio",
      "contractVersion": "2.11.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/segmentation-studio/contracts/types.ts",
      "contracts": [
        {
          "id": "segmentation-studio.canonical-fields",
          "description": "Active canonical HRIS field definitions: global catalog, or merged with tenant-specific rows (tenant wins at the same key) when ?tenantId= is set.",
          "endpoint": "/api/spokes/segmentation-studio/canonical-fields",
          "method": "GET",
          "responseSchema": "CanonicalFieldsListResponseSchema"
        },
        {
          "id": "segmentation-studio.hris-ingest",
          "description": "Raw HRIS rows → normalized rows + suggested field mappings against the canonical-field priority catalog.",
          "endpoint": "/api/spokes/segmentation-studio/hris/ingest",
          "method": "POST",
          "requestSchema": "HrisIngestRequestSchema",
          "responseSchema": "HrisIngestResponseSchema"
        },
        {
          "id": "segmentation-studio.hris.sync.workday",
          "description": "PAT-65 — Workday SOAP (`Get_Workers`) OR PAT-92 OAuth + Reports-as-a-Service pull mapped through canonical-fields and persisted into tenant-scoped `segmentation_studio.workforce_*` plus `segmentation_studio.ingestion_jobs` audit (returns `runId`, `datasetId`, counts).",
          "endpoint": "/api/spokes/segmentation-studio/hris/sync/workday",
          "method": "POST",
          "requestSchema": "`WorkdaySoapConfig` body for SOAP ({ wsdlUrl, username, password, isuName, tenant?, … }) OR PAT-92 `WorkdayOAuthSyncBodySchema` sans wsdl (`configRef`). Both require Toolbox tenant context.",
          "responseSchema": "HrisWorkdaySyncPersistedResponseSchema"
        },
        {
          "id": "segmentation-studio.hris.sync.bamboo",
          "description": "PAT-65-FU-A — BambooHR REST (+ Basic auth via env-referenced api key pull) persisted into tenant-scoped workforce datasets plus `segmentation_studio.ingestion_jobs` audit (`runId`), matching the Workday PAT-65 loop. Toolbox tenant context required.",
          "endpoint": "/api/spokes/segmentation-studio/hris/sync/bamboo",
          "method": "POST",
          "requestSchema": "`BambooSyncBodySchema` ({ `organizationId` optional cross-check; `configRef`: inline BambooConfig OR `{ ref: \"env\" }` using BAMBOO_* env; optional resolveIdentities).",
          "responseSchema": "HrisBambooSyncPersistedResponseSchema"
        },
        {
          "id": "segmentation-studio.segments-define",
          "description": "Register a SegmentDefinition (criteria-based) into custom_segments.",
          "endpoint": "/api/spokes/segmentation-studio/segments/define",
          "method": "POST",
          "requestSchema": "DefineSegmentRequestSchema",
          "responseSchema": "SegmentSchema"
        },
        {
          "id": "segmentation-studio.cohorts-resolve",
          "description": "Multi-membership cohort resolution: criteria → memberIds + segmentNodeIds.",
          "endpoint": "/api/spokes/segmentation-studio/cohorts/resolve",
          "method": "POST",
          "requestSchema": "ResolveCohortRequestSchema",
          "responseSchema": "ResolveCohortResponseSchema"
        },
        {
          "id": "segmentation-studio.identity-resolve",
          "description": "Batch union-find identity clustering on normalized email + exact/fuzzy name (Levenshtein-backed); returns clusters, deduplication rate, and conflict strings.",
          "endpoint": "/api/spokes/segmentation-studio/identity/resolve",
          "method": "POST",
          "requestSchema": "ResolveIdentitiesRequestSchema",
          "responseSchema": "ResolveIdentitiesResponseSchema"
        },
        {
          "id": "segmentation-studio.recipes-define",
          "description": "Register a derived-dimension recipe (bin breakpoints, key map, or template string) against normalized HRIS fields.",
          "endpoint": "/api/spokes/segmentation-studio/recipes/define",
          "method": "POST",
          "requestSchema": "DefineRecipeRequestSchema",
          "responseSchema": "RecipeSchema"
        },
        {
          "id": "segmentation-studio.recipes-run",
          "description": "Execute a stored recipe on normalized rows; persists recipe_runs audit row and returns synthetic dimension nodes + memberships.",
          "endpoint": "/api/spokes/segmentation-studio/recipes/run",
          "method": "POST",
          "requestSchema": "RunRecipeRequestSchema",
          "responseSchema": "RunRecipeResponseSchema"
        },
        {
          "id": "segmentation-studio.packs-publish",
          "description": "Snapshot current dimensions, nodes, and memberships into a versioned segmentation_packs row; returns the full pack envelope.",
          "endpoint": "/api/spokes/segmentation-studio/packs/publish",
          "method": "POST",
          "requestSchema": "PublishPackRequestSchema",
          "responseSchema": "PublishPackResponseSchema"
        },
        {
          "id": "segmentation-studio.packs-latest",
          "description": "Fetch the most recently published segmentation pack (published_at DESC).",
          "endpoint": "/api/spokes/segmentation-studio/packs/latest",
          "method": "GET",
          "responseSchema": "GetPackResponseSchema"
        },
        {
          "id": "segmentation-studio.packs-version",
          "description": "Fetch a historical segmentation pack by its immutable version string.",
          "endpoint": "/api/spokes/segmentation-studio/packs/[version]",
          "method": "GET",
          "responseSchema": "GetPackResponseSchema"
        },
        {
          "id": "segmentation-studio.data-join.run",
          "description": "Anchor-supplemental data join: merge an anchor HRIS file with N supplemental files using per-column FieldAction policy (OVERWRITE / IGNORE / FILL_HOLES). Returns joinedData, column lineage, overlap conflict report, and unjoined records (PAT-20-FU-A; sequel to identity-resolve).",
          "endpoint": "/api/spokes/segmentation-studio/data-join/run",
          "method": "POST",
          "requestSchema": "DataJoinRequestSchema",
          "responseSchema": "DataJoinResponseSchema"
        },
        {
          "id": "segmentation-studio.hris-ingest.onemodel",
          "description": "Ingest a OneModel admin-export ZIP (base64-encoded). Parses the four required catalogs (metrics / dimensions / tables / entity_relationships), runs canonical-field detection on dimension labels, and persists a semantic_profiles row (PAT-20-FU-B; donor conductor `/api/import/onemodel-zip`).",
          "endpoint": "/api/spokes/segmentation-studio/hris/ingest/onemodel",
          "method": "POST",
          "requestSchema": "OneModelImportRequestSchema",
          "responseSchema": "OneModelImportResponseSchema"
        },
        {
          "id": "segmentation-studio.canonical-segments.list",
          "description": "Canonical Segments Catalog (Catalog 2; PAT-41 / PAT-61): list canonical segment definitions. Optional `dimension`, `category`, and non-negative integer `depth` (roots = 0) query params.",
          "endpoint": "/api/spokes/segmentation-studio/canonical-segments",
          "method": "GET",
          "responseSchema": "CanonicalSegmentListResultSchema"
        },
        {
          "id": "segmentation-studio.canonical-segments.lookup",
          "description": "Canonical Segments Catalog (Catalog 2; PAT-41): fetch one canonical segment by its stable composite id (\"segment.<dimension>.<slug>\").",
          "endpoint": "/api/spokes/segmentation-studio/canonical-segments/[id]",
          "method": "GET",
          "responseSchema": "CanonicalSegmentSchema"
        },
        {
          "id": "segmentation-studio.canonical-segments.resolve",
          "description": "Canonical Segments Catalog (Catalog 2; PAT-41): evaluate a canonical segment's predicate (eq / in / gte / lt / between + and / or / not) against the optionally tenant-scoped employee universe and return matched member ids + counts.",
          "endpoint": "/api/spokes/segmentation-studio/canonical-segments/[id]/resolve",
          "method": "GET",
          "responseSchema": "CanonicalSegmentResolveResultSchema"
        },
        {
          "id": "segmentation-studio.canonical-segments.ancestors",
          "description": "PAT-61 — Ordered ancestor chain (root-first) for a canonical segment id; 404 when missing.",
          "endpoint": "/api/spokes/segmentation-studio/canonical-segments/[id]/ancestors",
          "method": "GET",
          "responseSchema": "CanonicalSegmentAncestorsResultSchema"
        },
        {
          "id": "segmentation-studio.canonical-segments.descendants",
          "description": "PAT-61 — Breadth-first descendant subtree with optional `maxDepth` query param (default 10, cap 50).",
          "endpoint": "/api/spokes/segmentation-studio/canonical-segments/[id]/descendants",
          "method": "GET",
          "responseSchema": "CanonicalSegmentDescendantsResultSchema"
        },
        {
          "id": "segmentation-studio.org.reporting-chain.resolve",
          "description": "Org reporting-chain resolver (PAT-53): walks the manager-employee graph downstream from a focal person and returns everyone reporting up to them, with optional depth cap + tenant scoping. Reads `segmentation_studio.employee_directory`; returns empty arrays when the directory is unseeded.",
          "endpoint": "/api/spokes/segmentation-studio/org/reporting-chain/resolve",
          "method": "GET",
          "responseSchema": "ReportingChainResolveResultSchema"
        },
        {
          "id": "segmentation-studio.custom-segments.list",
          "description": "Tenant custom segments (PAT-58): list the latest version of every distinct (tenantId, name) for a tenant. Requires `?tenantId=` query param.",
          "endpoint": "/api/spokes/segmentation-studio/custom-segments",
          "method": "GET",
          "responseSchema": "TenantCustomSegmentListResultSchema"
        },
        {
          "id": "segmentation-studio.custom-segments.author",
          "description": "Tenant custom segments (PAT-58): author a new logical segment (v1) or append a new version when (tenantId, name) already exists. Predicate uses the same JSON-tree language as canonical segments.",
          "endpoint": "/api/spokes/segmentation-studio/custom-segments",
          "method": "POST",
          "requestSchema": "CreateTenantCustomSegmentRequestSchema",
          "responseSchema": "TenantCustomSegmentSchema"
        },
        {
          "id": "segmentation-studio.custom-segments.get",
          "description": "Tenant custom segments (PAT-58): fetch a single segment by id plus the full version history for its (tenantId, name) pair.",
          "endpoint": "/api/spokes/segmentation-studio/custom-segments/[id]",
          "method": "GET",
          "responseSchema": "TenantCustomSegmentWithHistorySchema"
        },
        {
          "id": "segmentation-studio.custom-segments.update",
          "description": "Tenant custom segments (PAT-58): publish a new monotonically-incremented version for the same (tenantId, name) keyed by an existing row's id; carries a new predicate + actor.",
          "endpoint": "/api/spokes/segmentation-studio/custom-segments/[id]",
          "method": "PATCH",
          "requestSchema": "UpdateTenantCustomSegmentRequestSchema",
          "responseSchema": "TenantCustomSegmentSchema"
        },
        {
          "id": "segmentation-studio.workforce-datasets.upload",
          "description": "Multipart HRIS file upload; creates tenant-scoped workforce_dataset with content-hash idempotency (409 returns existingDatasetId). Requires x-toolbox-tenant-id.",
          "endpoint": "/api/spokes/segmentation-studio/workforce-datasets/upload",
          "method": "POST",
          "responseSchema": "WorkforceDatasetUploadResponseSchema"
        },
        {
          "id": "segmentation-studio.workforce-datasets.get",
          "description": "Workforce dataset status + summaries.",
          "endpoint": "/api/spokes/segmentation-studio/workforce-datasets/[id]",
          "method": "GET",
          "responseSchema": "WorkforceDatasetSchema"
        },
        {
          "id": "segmentation-studio.workforce-datasets.parse",
          "description": "Parse uploaded CSV (skips vendor metadata rows); stages workforce_dataset_rows.",
          "endpoint": "/api/spokes/segmentation-studio/workforce-datasets/[id]/parse",
          "method": "POST",
          "responseSchema": "WorkforceDatasetSchema"
        },
        {
          "id": "segmentation-studio.workforce-datasets.recognize",
          "description": "Multi-signal column recognition Pass 1-4 with Bayesian-weighted aggregation.",
          "endpoint": "/api/spokes/segmentation-studio/workforce-datasets/[id]/recognize",
          "method": "POST",
          "responseSchema": "RecognizeWorkforceDatasetResponseSchema"
        },
        {
          "id": "segmentation-studio.workforce-datasets.map",
          "description": "Operator mapping confirmations; updates signal_accuracy_priors and lynchpin cascade.",
          "endpoint": "/api/spokes/segmentation-studio/workforce-datasets/[id]/map",
          "method": "POST",
          "requestSchema": "WorkforceDatasetMapRequestSchema",
          "responseSchema": "WorkforceDatasetMappingSchema"
        },
        {
          "id": "segmentation-studio.workforce-datasets.review",
          "description": "Identity resolution, value-level segment mapping, source profile card generation.",
          "endpoint": "/api/spokes/segmentation-studio/workforce-datasets/[id]/review",
          "method": "POST",
          "responseSchema": "WorkforceDatasetReviewResponseSchema"
        },
        {
          "id": "segmentation-studio.workforce-datasets.persist",
          "description": "Mark dataset persisted (idempotent).",
          "endpoint": "/api/spokes/segmentation-studio/workforce-datasets/[id]/persist",
          "method": "POST",
          "responseSchema": "WorkforceDatasetSchema"
        },
        {
          "id": "segmentation-studio.workforce-datasets.rows",
          "description": "Paged normalized rows including normalized._segments canonical segment ids.",
          "endpoint": "/api/spokes/segmentation-studio/workforce-datasets/[id]/rows",
          "method": "GET",
          "responseSchema": "WorkforceDatasetRowsResponseSchema"
        },
        {
          "id": "segmentation-studio.workforce-datasets.profile-card",
          "description": "Source profile card for wizard PROFILE step.",
          "endpoint": "/api/spokes/segmentation-studio/workforce-datasets/[id]/profile-card",
          "method": "GET",
          "responseSchema": "SourceProfileCardSchema"
        },
        {
          "id": "segmentation-studio.workforce-datasets.delete",
          "description": "Soft-delete workforce dataset.",
          "endpoint": "/api/spokes/segmentation-studio/workforce-datasets/[id]",
          "method": "DELETE",
          "responseSchema": "WorkforceDatasetSchema"
        },
        {
          "id": "segmentation-studio.schemas.list",
          "description": "PAT-122: Paginated SegmentationSchemaVersion discovery with optional tenant + purpose filtering + opaque cursor paging.",
          "endpoint": "/api/spokes/segmentation-studio/schemas",
          "method": "GET",
          "responseSchema": "{ items: SegmentationSchemaVersion[], nextCursor?: string }"
        },
        {
          "id": "segmentation-studio.schemas.create",
          "description": "PAT-122: Persist a SegmentationSchemaVersion (hash + UUID assigned server-side; draft default unless caller passes active). Comparable groups mirrored into segmentation_comparable_groups.",
          "endpoint": "/api/spokes/segmentation-studio/schemas",
          "method": "POST",
          "requestSchema": "SegmentationSchemaVersionCreateBodySchema",
          "responseSchema": "SegmentationSchemaVersionSchema"
        },
        {
          "id": "segmentation-studio.schemas.getByVersionId",
          "description": "PAT-137: Fetch SegmentationSchemaVersion by immutable `schemaVersionId` row id (paired with PAT-122 composite GET for schema authoring UX).",
          "endpoint": "/api/spokes/segmentation-studio/schemas/version/[schemaVersionId]",
          "method": "GET",
          "responseSchema": "SegmentationSchemaVersionSchema"
        },
        {
          "id": "segmentation-studio.schemas.get",
          "description": "PAT-122: Fetch a single SegmentationSchemaVersion by `{schemaId, version}` semantic key.",
          "endpoint": "/api/spokes/segmentation-studio/schemas/[schemaId]/[version]",
          "method": "GET",
          "responseSchema": "SegmentationSchemaVersionSchema"
        },
        {
          "id": "segmentation-studio.schemas.branch",
          "description": "PAT-122: Cut a descendant draft schema off a validated SchemaRef (`version` increments `-branchName.<n>` per schema_id family).",
          "endpoint": "/api/spokes/segmentation-studio/schemas/branch",
          "method": "POST",
          "requestSchema": "BranchSegmentationSchemaRequestSchema",
          "responseSchema": "SegmentationSchemaVersionSchema"
        },
        {
          "id": "segmentation-studio.schemas.diff",
          "description": "PAT-122: Compute `SchemaDiff` between two stored schema refs (`computeSchemaDiff` PAT-121) — unsalted read path without service keys.",
          "endpoint": "/api/spokes/segmentation-studio/schemas/diff",
          "method": "POST",
          "requestSchema": "SchemaDiffPairRequestSchema",
          "responseSchema": "SchemaDiffSchema"
        },
        {
          "id": "segmentation-studio.schemas.snapshot",
          "description": "PAT-122: Idempotently freeze a `{schemaVersionId,schemaHash}` row into segmentation_schema_snapshots (201 on first insert; 200 on repeat).",
          "endpoint": "/api/spokes/segmentation-studio/schemas/snapshot",
          "method": "POST",
          "requestSchema": "SchemaSnapshotCreateRequestSchema",
          "responseSchema": "PersistedSegmentationSnapshotResponseSchema"
        },
        {
          "id": "segmentation-studio.declarative-segmentation.rules.get",
          "description": "PAT-160: Loads per-tenant declarative segmentation rules (Config_Segmentation workbook parity — ordered rules, first match wins).",
          "endpoint": "/api/spokes/segmentation-studio/declarative-segmentation/rules",
          "method": "GET",
          "responseSchema": "DeclarativeSegmentationRulesGetResponseSchema"
        },
        {
          "id": "segmentation-studio.declarative-segmentation.rules.put",
          "description": "PAT-160: Replace the entire declarative ruleset for a tenant (optional donor sheet rows appended). Service-key gated.",
          "endpoint": "/api/spokes/segmentation-studio/declarative-segmentation/rules",
          "method": "POST",
          "requestSchema": "PutDeclarativeSegmentationRulesRequestSchema",
          "responseSchema": "DeclarativeSegmentationRulesGetResponseSchema"
        },
        {
          "id": "segmentation-studio.declarative-segmentation.evaluate",
          "description": "PAT-160: Evaluate declarative rules over a workforce record batch; uses tenant store and/or inline rules plus optional donor sheet overlays. Service-key gated.",
          "endpoint": "/api/spokes/segmentation-studio/declarative-segmentation/evaluate",
          "method": "POST",
          "requestSchema": "EvaluateDeclarativeSegmentationRequestSchema",
          "responseSchema": "EvaluateDeclarativeSegmentationResponseSchema"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "cardId": "PFX-9",
          "status": "in-progress"
        }
      ]
    },
    {
      "slug": "calculus",
      "schema": "calculus",
      "contractVersion": "1.14.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/calculus/contracts/types.ts",
      "contracts": [
        {
          "id": "calculus.oaxaca",
          "description": "Oaxaca–Blinder mean outcome gap decomposition (pooled / two-fold / three-fold) between two cohorts.",
          "endpoint": "/api/spokes/calculus/oaxaca",
          "method": "POST",
          "requestSchema": "OaxacaInputSchema",
          "responseSchema": "OaxacaResultSchema"
        },
        {
          "id": "calculus.diagnostics",
          "description": "OLS regression diagnostics (leverage, Cook's D, residuals, fitted) plus optional 1-D DBSCAN on residuals.",
          "endpoint": "/api/spokes/calculus/diagnostics",
          "method": "POST",
          "requestSchema": "DiagnosticsRequestSchema",
          "responseSchema": "DiagnosticsResultSchema"
        },
        {
          "id": "calculus.stats-enrich",
          "description": "Enrich a metric envelope with confidence intervals, z-score, percentile, and change-rate.",
          "endpoint": "/api/spokes/calculus/stats/enrich",
          "method": "POST",
          "requestSchema": "StatsEnrichRequestSchema",
          "responseSchema": "StatsEnrichResponseSchema"
        },
        {
          "id": "calculus.stats-trend",
          "description": "Classify a periods array as rising / stable / falling with OLS slope and change rate.",
          "endpoint": "/api/spokes/calculus/stats/trend",
          "method": "POST",
          "requestSchema": "StatsTrendRequestSchema",
          "responseSchema": "StatsTrendResponseSchema"
        },
        {
          "id": "calculus.stats-impute",
          "description": "Expand an irregular period grid (monthly/quarterly cadence) with forward-fill, linear interpolation, or gap flagging.",
          "endpoint": "/api/spokes/calculus/stats/impute",
          "method": "POST",
          "requestSchema": "ImputePeriodsRequestSchema",
          "responseSchema": "ImputePeriodsResponseSchema"
        },
        {
          "id": "calculus.stats-anomaly",
          "description": "Flag time-series points via z-score threshold, IQR fences, and step-change outliers on first differences.",
          "endpoint": "/api/spokes/calculus/stats/anomaly",
          "method": "POST",
          "requestSchema": "AnomalyDetectRequestSchema",
          "responseSchema": "AnomalyDetectResponseSchema"
        },
        {
          "id": "calculus.factory-build",
          "description": "Combinatorial metric × segment × period grid: build envelopes from a values array, enrich each, return ranked list (impact / significance / change / recency / sample-size). Optional ?persist=true writes envelopes to metric_envelopes.",
          "endpoint": "/api/spokes/calculus/factory/build",
          "method": "POST",
          "requestSchema": "FactoryBuildRequestSchema",
          "responseSchema": "FactoryBuildResponseSchema"
        },
        {
          "id": "calculus.governance.check",
          "description": "Governance-flag layer over a record set: distribution-equity, harshness-bias, recency-bias, calibration-spread (PAT-6-FU).",
          "endpoint": "/api/spokes/calculus/governance/check",
          "method": "POST",
          "requestSchema": "GovernanceCheckRequestSchema",
          "responseSchema": "GovernanceCheckResponseSchema"
        },
        {
          "id": "calculus.monte-carlo.run",
          "description": "PAT-147-C: deterministic Monte Carlo batches with JSONB persistence (per-tenant idempotent run_key) plus numeric column summaries.",
          "endpoint": "/api/spokes/calculus/monte-carlo/run",
          "method": "POST",
          "requestSchema": "MonteCarloRunInputSchema",
          "responseSchema": "MonteCarloRunResultSchema"
        },
        {
          "id": "calculus.regression-surrogate.fit",
          "description": "PAT-147-C: fits an OLS surrogate (ridge ε=1e-6 fallback when singular) from inline trials or a stored MC run id and inserts `surrogate_models`.",
          "endpoint": "/api/spokes/calculus/regression-surrogate/fit",
          "method": "POST",
          "requestSchema": "SurrogateFitRequestSchema",
          "responseSchema": "SurrogateModelSchema"
        },
        {
          "id": "calculus.regression-surrogate.forward",
          "description": "PAT-147-C surrogate forward prediction with t-based 95% mean-response interval.",
          "endpoint": "/api/spokes/calculus/regression-surrogate/forward",
          "method": "POST",
          "requestSchema": "SurrogateForwardInputSchema",
          "responseSchema": "SurrogateForwardOutputSchema"
        },
        {
          "id": "calculus.regression-surrogate.inverse",
          "description": "PAT-147-C surrogate inverse payout solver (interaction-aware) with linearized CI propagation from mean-response SE.",
          "endpoint": "/api/spokes/calculus/regression-surrogate/inverse",
          "method": "POST",
          "requestSchema": "SurrogateInverseInputSchema",
          "responseSchema": "SurrogateInverseOutputSchema"
        },
        {
          "id": "calculus.regression-surrogate.get",
          "description": "Reads a persisted surrogate snapshot (requires tenantId query parameter for scoping).",
          "endpoint": "/api/spokes/calculus/regression-surrogate/[modelId]",
          "method": "GET",
          "responseSchema": "SurrogateModelSchema"
        },
        {
          "id": "calculus.metric-keys.unknown",
          "description": "PAT-40 diagnostic: recent envelope metricKeys that failed metrics-catalog soft-validation. In-process buffer; lost on cold start.",
          "endpoint": "/api/spokes/calculus/metric-keys/unknown",
          "method": "GET",
          "responseSchema": "UnknownMetricKeysResponseSchema"
        },
        {
          "id": "calculus.distribution-fit",
          "description": "PA Instrument — Fit a sample distribution and report goodness-of-fit diagnostics.",
          "endpoint": "/api/spokes/calculus/distribution-fit",
          "method": "POST",
          "requestSchema": "DistributionFitRequestSchema",
          "responseSchema": "DistributionFitResultSchema"
        },
        {
          "id": "calculus.importance-reconcile",
          "description": "PA Instrument — Reconcile stated vs derived importance weights across items.",
          "endpoint": "/api/spokes/calculus/importance-reconcile",
          "method": "POST",
          "requestSchema": "ImportanceReconcileRequestSchema",
          "responseSchema": "ImportanceReconcileResultSchema"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "status": "planned"
        },
        {
          "name": "vela",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "factor-models",
      "schema": "factor_models",
      "contractVersion": "0.4.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/factor-models/contracts/types.ts",
      "contracts": [
        {
          "id": "factor-models.health",
          "description": "Per-spoke heartbeat for Postgres `factor_models` reachability.",
          "endpoint": "/api/spokes/factor-models/health",
          "method": "GET",
          "responseSchema": "SpokeHealth"
        },
        {
          "id": "factor-models.models.create",
          "description": "Create a tenant-scoped factor model header (latent factors + outcome) before registering weight versions.",
          "endpoint": "/api/spokes/factor-models/models",
          "method": "POST",
          "requestSchema": "CreateFactorModelRequestSchema",
          "responseSchema": "CreateFactorModelResponseSchema"
        },
        {
          "id": "factor-models.models.get",
          "description": "Fetch the model row including optional currentVersionId pointer.",
          "endpoint": "/api/spokes/factor-models/models/[id]",
          "method": "GET",
          "responseSchema": "FactorModelSchema"
        },
        {
          "id": "factor-models.versions.register",
          "description": "Append an immutable model_version row plus relational factor_weights edges.",
          "endpoint": "/api/spokes/factor-models/models/[id]/versions",
          "method": "POST",
          "requestSchema": "RegisterModelVersionRequestSchema",
          "responseSchema": "RegisterModelVersionResponseSchema"
        },
        {
          "id": "factor-models.versions.validate",
          "description": "Compute holdout correlation / MAE / R² and persist segment fairness snapshots for publish gating.",
          "endpoint": "/api/spokes/factor-models/models/[id]/versions/[versionId]/validate",
          "method": "POST",
          "requestSchema": "ValidateModelVersionRequestSchema",
          "responseSchema": "ValidateModelVersionResponseSchema"
        },
        {
          "id": "factor-models.publish",
          "description": "Advance models.current_version_id only when a passing validation_report exists (409 otherwise).",
          "endpoint": "/api/spokes/factor-models/models/[id]/publish",
          "method": "POST",
          "requestSchema": "PublishModelVersionRequestSchema",
          "responseSchema": "PublishModelVersionResponseSchema"
        },
        {
          "id": "factor-models.scores.compute",
          "description": "Score a subject against the published current version; lazily memoizes `(versionId × subject × factor)` rows for deterministic repeat reads.",
          "endpoint": "/api/spokes/factor-models/scores/[modelId]/[subjectId]",
          "method": "POST",
          "requestSchema": "ScoreSubjectRequestSchema",
          "responseSchema": "ScoreSubjectResponseSchema"
        },
        {
          "id": "factor-models.analytics-plan",
          "description": "PA Instrument — Analytics-Plan Generator: rank candidate models by exec-priority relevance + emit a value-of-information-ranked measurement plan.",
          "endpoint": "/api/spokes/factor-models/analytics-plan",
          "method": "POST",
          "requestSchema": "AnalyticsPlanRequestSchema",
          "responseSchema": "AnalyticsPlanSchema"
        },
        {
          "id": "factor-models.empirical-importance",
          "description": "PA Instrument — Empirical importance from observed-outcome variance (√variance × priority weight, normalized 0–100); feeds calculus.importance-reconcile.",
          "endpoint": "/api/spokes/factor-models/empirical-importance",
          "method": "POST",
          "requestSchema": "EmpiricalImportanceRequestSchema",
          "responseSchema": "EmpiricalImportanceResultSchema"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "status": "in-progress"
        },
        {
          "name": "vela",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "forecasting",
      "schema": "forecasting",
      "contractVersion": "1.4.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/forecasting/contracts/types.ts",
      "contracts": [
        {
          "id": "forecasting.monte-carlo.run",
          "description": "Run a deterministic seeded Monte Carlo over named distributions and a closed-form expression.",
          "endpoint": "/api/spokes/forecasting/monte-carlo/run",
          "method": "POST",
          "requestSchema": "MonteCarloSpecSchema",
          "responseSchema": "MonteCarloRunResponseSchema"
        },
        {
          "id": "forecasting.decision-models.create",
          "description": "Register a reusable decision tree (VOI / EVPI substrate).",
          "endpoint": "/api/spokes/forecasting/decision-models",
          "method": "POST",
          "requestSchema": "RegisterDecisionModelRequestSchema",
          "responseSchema": "DecisionModelRecordSchema"
        },
        {
          "id": "forecasting.decision-models.get",
          "description": "Fetch a stored decision tree by id.",
          "endpoint": "/api/spokes/forecasting/decision-models/[id]",
          "method": "GET",
          "responseSchema": "DecisionModelRecordSchema"
        },
        {
          "id": "forecasting.voi.compute",
          "description": "Compute baseline EV, perfect-information EV, EVPI, and optional discrete EVSI for aligned shared uncertainty.",
          "endpoint": "/api/spokes/forecasting/voi/compute",
          "method": "POST",
          "requestSchema": "VOIComputeRequestSchema",
          "responseSchema": "VOIComputeResponseSchema"
        },
        {
          "id": "forecasting.interval-scoring",
          "description": "PA Instrument — Score interval estimates against realized outcomes (coverage + sharpness).",
          "endpoint": "/api/spokes/forecasting/interval-scoring",
          "method": "POST",
          "requestSchema": "IntervalScoreRequestSchema",
          "responseSchema": "IntervalScoreResultSchema"
        },
        {
          "id": "forecasting.bayesian-combine",
          "description": "PA Instrument — Bayesian combination of multiple estimate sources into a posterior.",
          "endpoint": "/api/spokes/forecasting/bayesian-combine",
          "method": "POST",
          "requestSchema": "BayesianCombineRequestSchema",
          "responseSchema": "BayesianCombineResultSchema"
        },
        {
          "id": "forecasting.measurement-recommend",
          "description": "PA Instrument — Recommend a measurement method given a construct and constraints.",
          "endpoint": "/api/spokes/forecasting/measurement-recommend",
          "method": "POST",
          "requestSchema": "MeasurementRecommendationRequestSchema",
          "responseSchema": "MeasurementRecommendationResultSchema"
        },
        {
          "id": "forecasting.measurement-catalog",
          "description": "PA Instrument — Catalog of available measurement methods and ranges.",
          "endpoint": "/api/spokes/forecasting/measurement-catalog",
          "method": "GET",
          "responseSchema": "MeasurementCatalogResultSchema"
        }
      ],
      "consumers": [
        {
          "name": "decision-wizard",
          "status": "planned"
        },
        {
          "name": "vela",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "workforce-planning",
      "schema": "workforce_planning",
      "contractVersion": "0.5.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/workforce-planning/contracts/types.ts",
      "contracts": [
        {
          "id": "workforce-planning.health",
          "description": "Per-spoke aggregate heartbeat shim for Postgres reachability.",
          "endpoint": "/api/spokes/workforce-planning/health",
          "method": "GET",
          "responseSchema": "SpokeHealth"
        },
        {
          "id": "workforce-planning.positions.list",
          "description": "List canonical toolbox positions keyed by `(tenant × source × source row id)` plus Greenhouse-aligned status filters.",
          "endpoint": "/api/spokes/workforce-planning/positions",
          "method": "GET",
          "responseSchema": "PositionListResponseSchema"
        },
        {
          "id": "workforce-planning.positions.upsert",
          "description": "Service-key gated batch upsert that writes append-only audit events feeding denormalized `positions` totals.",
          "endpoint": "/api/spokes/workforce-planning/positions",
          "method": "POST",
          "requestSchema": "PositionUpsertBatchRequestSchema",
          "responseSchema": "PositionUpsertBatchResponseSchema"
        },
        {
          "id": "workforce-planning.forecast.snapshots.list",
          "description": "Read immutable forecast_snapshots sorted newest-first (`TrailingAverageForecaster` v1; window = 30d|60d|90d JSON field).",
          "endpoint": "/api/spokes/workforce-planning/forecast/snapshots",
          "method": "GET",
          "responseSchema": "ForecastSnapshotsListResponseSchema"
        },
        {
          "id": "workforce-planning.forecast.run",
          "description": "Materialize a persisted forecast_snapshot using rolling-average hires/exits heuristics bounded by toolbox position_events.",
          "endpoint": "/api/spokes/workforce-planning/forecast/run",
          "method": "POST",
          "requestSchema": "ForecastRunRequestSchema",
          "responseSchema": "ForecastRunResponseSchema"
        },
        {
          "id": "workforce-planning.forecast.exits",
          "description": "Segment-aware trailing exit-rate forecast (defaults to ~12-month lookback) with optional regretted attribution from structured position_events taxonomy.",
          "endpoint": "/api/spokes/workforce-planning/forecast/exits",
          "method": "POST",
          "requestSchema": "ForecastRunRequestSchema",
          "responseSchema": "ExitForecastResultSchema"
        },
        {
          "id": "workforce-planning.forecast.funnelThroughput",
          "description": "ATS funnel throughput forward hires from cohort conversion rates (application_status + offer_states) with persisted forecast_snapshot telemetry.",
          "endpoint": "/api/spokes/workforce-planning/forecast/funnel-throughput",
          "method": "POST",
          "requestSchema": "ForecastRunRequestSchema",
          "responseSchema": "FunnelForecastResultSchema"
        },
        {
          "id": "workforce-planning.forecast.projection",
          "description": "Combined head-count balance sheet marrying exit pacing + ATS funnel hires; persists a composite forecast_snapshot envelope.",
          "endpoint": "/api/spokes/workforce-planning/forecast/projection",
          "method": "POST",
          "requestSchema": "ForecastRunRequestSchema",
          "responseSchema": "ProjectedHeadcountResultSchema"
        },
        {
          "id": "workforce-planning.reconciliation.snapshots.list",
          "description": "Historical cross-system deltas (HR filled vs ATS open vs finance budgeted HC JSON per PAT-D1 scout).",
          "endpoint": "/api/spokes/workforce-planning/reconciliation/snapshots",
          "method": "GET",
          "responseSchema": "ReconciliationSnapshotsListResponseSchema"
        },
        {
          "id": "workforce-planning.reconciliation.run",
          "description": "Freezes reconciliation_snapshots capturing HR/Budget/ATS variance using live `positions` grain inputs.",
          "endpoint": "/api/spokes/workforce-planning/reconciliation/run",
          "method": "POST",
          "requestSchema": "ReconciliationRunRequestSchema",
          "responseSchema": "ReconciliationRunResponseSchema"
        },
        {
          "id": "workforce-planning.ats.webhook.greenhouse",
          "description": "Signature-verified Greenhouse webhook ingest that upserts ATS rows + optional hire/exit deltas when JOB→POSITION env map is wired.",
          "endpoint": "/api/spokes/workforce-planning/ats/webhook/greenhouse",
          "method": "POST",
          "requestSchema": "WorkforceGreenhouseWebhookEnvelopeSchema",
          "responseSchema": "WorkforceInboundAckSchema"
        },
        {
          "id": "workforce-planning.matches.propose",
          "description": "PAT-D1-B-FU-C batch Conductor matcher — Beta prior + feature likelihood ratios over open HRIS slots; persists requisition_matches + audit rows.",
          "endpoint": "/api/spokes/workforce-planning/matches/propose",
          "method": "POST",
          "requestSchema": "MatchProposeRequestSchema",
          "responseSchema": "MatchProposeResponseSchema"
        },
        {
          "id": "workforce-planning.matches.get",
          "description": "Read ranked candidates + tenant prior snapshot for one ATS requisition key.",
          "endpoint": "/api/spokes/workforce-planning/matches/[requisitionId]",
          "method": "GET",
          "responseSchema": "MatchDetailResponseSchema"
        },
        {
          "id": "workforce-planning.matches.accept",
          "description": "Operator accepts a ranked slot; flips HRIS lifecycle to reconciled-by-operator + updates conjugate Beta on success ledger insert.",
          "endpoint": "/api/spokes/workforce-planning/matches/[requisitionId]/accept",
          "method": "POST",
          "requestSchema": "MatchAcceptRequestSchema",
          "responseSchema": "MatchAcceptAckSchema"
        },
        {
          "id": "workforce-planning.matches.rejectAndCreateNew",
          "description": "Operator rejects all suggestions and mints `open-newly-created`; ledger bumps Beta \"reject\" atomically once.",
          "endpoint": "/api/spokes/workforce-planning/matches/[requisitionId]/reject-and-create-new",
          "method": "POST",
          "requestSchema": "MatchRejectAndCreateRequestSchema",
          "responseSchema": "MatchRejectCreateAckSchema"
        },
        {
          "id": "workforce-planning.matches.override",
          "description": "Operator binds an off-list toolbox position without diluting conjugate Beta (explicit override semantics + audited reason).",
          "endpoint": "/api/spokes/workforce-planning/matches/[requisitionId]/override",
          "method": "POST",
          "requestSchema": "MatchOverrideRequestSchema",
          "responseSchema": "MatchOverrideAckSchema"
        },
        {
          "id": "workforce-planning.matches.reconcileExpiredExits",
          "description": "Idempotent TTL job — flips stale `open-from-exit` rows to `open-newly-created` when exit_date + exit_ttl_days has elapsed.",
          "endpoint": "/api/spokes/workforce-planning/matches/reconcile-expired-exits",
          "method": "POST",
          "responseSchema": "MatchReconcileExpiredExitsResponseSchema"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "anycomp",
      "schema": "anycomp",
      "contractVersion": "1.44.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/anycomp/contracts/types.ts",
      "contracts": [
        {
          "id": "anycomp.models.create",
          "description": "Register a compensation model and its market bands (dimensions × pay mix).",
          "endpoint": "/api/spokes/anycomp/models",
          "method": "POST",
          "requestSchema": "RegisterCompModelRequestSchema",
          "responseSchema": "CompModelSchema"
        },
        {
          "id": "anycomp.models.get",
          "description": "Fetch a comp model with all bands.",
          "endpoint": "/api/spokes/anycomp/models/[id]",
          "method": "GET",
          "responseSchema": "CompModelSchema"
        },
        {
          "id": "anycomp.evaluate",
          "description": "Stateless evaluation: employees × model → recommendations (percentile, target pay, confidence).",
          "endpoint": "/api/spokes/anycomp/evaluate",
          "method": "POST",
          "requestSchema": "CompEvaluateRequestSchema",
          "responseSchema": "CompEvaluateResponseSchema"
        },
        {
          "id": "anycomp.cycles.run",
          "description": "Bulk evaluation with persistence to comp_evaluations (audit trail per cycle).",
          "endpoint": "/api/spokes/anycomp/cycles/run",
          "method": "POST",
          "requestSchema": "CompCycleEvaluationRequestSchema",
          "responseSchema": "CompCycleEvaluationResponseSchema"
        },
        {
          "id": "anycomp.market.labor-rate",
          "description": "Look up a BLS-derived labor-market rate (voluntary attrition / hire / opening / employment) for (socCode, geo, period). Geo/period fallback supplied (PAT-18-FU-A; donor market-data-backend).",
          "endpoint": "/api/spokes/anycomp/market/labor-rate",
          "method": "POST",
          "requestSchema": "LaborMarketRateRequestSchema",
          "responseSchema": "LaborMarketRateResponseSchema"
        },
        {
          "id": "anycomp.market.exit-risk-prior",
          "description": "BLS-baseline exit-risk prior for a segment, with optional compensation-percentile opportunity adjustment (PAT-18-FU-A).",
          "endpoint": "/api/spokes/anycomp/market/exit-risk-prior",
          "method": "POST",
          "requestSchema": "ExitRiskPriorRequestSchema",
          "responseSchema": "ExitRiskPriorResponseSchema"
        },
        {
          "id": "anycomp.market.tier-classify",
          "description": "Classify a value into Low / Mid / High tier using 33rd / 66th cohort-percentile thresholds (BLS labor metrics; PAT-18-FU-A).",
          "endpoint": "/api/spokes/anycomp/market/tier-classify",
          "method": "POST",
          "requestSchema": "MarketTierClassificationRequestSchema",
          "responseSchema": "MarketTierClassificationResponseSchema"
        },
        {
          "id": "anycomp.market-movement.history.ingest",
          "description": "PAT-147-G — Batch append canonical or tenant-overlay market midpoint history for (job family, job level); tenant context required.",
          "endpoint": "/api/spokes/anycomp/market-movement/history/ingest",
          "method": "POST",
          "requestSchema": "MarketReferenceHistoryIngestRequestSchema",
          "responseSchema": "Record written count"
        },
        {
          "id": "anycomp.market-movement.fit",
          "description": "PAT-147-G — YoY derivation + lag surrogate fit (pooled fallback &lt;10 rows) persisted to calculus.surrogate_models.",
          "endpoint": "/api/spokes/anycomp/market-movement/fit",
          "method": "POST",
          "requestSchema": "MarketMovementFitHttpRequestSchema",
          "responseSchema": "SurrogateModel"
        },
        {
          "id": "anycomp.market-movement.forecast",
          "description": "PAT-147-G — MC envelope + surrogate mean CI forward movement percentiles for configured horizon.",
          "endpoint": "/api/spokes/anycomp/market-movement/forecast",
          "method": "POST",
          "requestSchema": "MarketMovementForecastHttpRequestSchema",
          "responseSchema": "MarketMovementForecastSchema"
        },
        {
          "id": "anycomp.engines.merit.run",
          "description": "PAT-147-B CompEngine merit matrix on shared enriched population; optional per-cycle JSON config persistence.",
          "endpoint": "/api/spokes/anycomp/engines/merit/run",
          "method": "POST",
          "requestSchema": "MeritEngineRunRequestSchema",
          "responseSchema": "MeritEngineRunResponseSchema"
        },
        {
          "id": "anycomp.engines.variable-pay.run",
          "description": "PAT-147-B variable-pay / annual incentive engine (Mode A v0; Mode B placeholder) with per-unit aggregates.",
          "endpoint": "/api/spokes/anycomp/engines/variable-pay/run",
          "method": "POST",
          "requestSchema": "VariablePayEngineRunRequestSchema",
          "responseSchema": "VariablePayEngineRunResponseSchema"
        },
        {
          "id": "anycomp.engines.equity.run",
          "description": "PAT-147-B equity pool allocator — proportional split of per–decision-unit budgets by rating multipliers.",
          "endpoint": "/api/spokes/anycomp/engines/equity/run",
          "method": "POST",
          "requestSchema": "EquityEngineRunRequestSchema",
          "responseSchema": "EquityEngineRunResponseSchema"
        },
        {
          "id": "anycomp.engines.discretionary.run",
          "description": "PAT-147-B discretionary cash engine keyed by rating with optional population-status adjustments.",
          "endpoint": "/api/spokes/anycomp/engines/discretionary/run",
          "method": "POST",
          "requestSchema": "DiscretionaryEngineRunRequestSchema",
          "responseSchema": "DiscretionaryEngineRunResponseSchema"
        },
        {
          "id": "anycomp.eib.generate",
          "description": "PAT-147-E — Build Workday-style EIB CSV from persisted CompEngine outputs (merit / variable-pay / equity / discretionary). Optional download=1 as text/csv attachment.",
          "endpoint": "/api/spokes/anycomp/eib/generate",
          "method": "POST",
          "requestSchema": "EibGenerateRequestSchema",
          "responseSchema": "EibGenerateResponseSchema"
        },
        {
          "id": "anycomp.eib.diff",
          "description": "PAT-147-E — Compare two prior Workday EIB CSV strings and summarize added / removed / changed award lines (employeeId or lineKey join).",
          "endpoint": "/api/spokes/anycomp/eib/diff",
          "method": "POST",
          "requestSchema": "EibDiffRequestSchema",
          "responseSchema": "EibDiffResponseSchema"
        },
        {
          "id": "anycomp.analytics.vs-plan",
          "description": "Consulting vs.Plan variance: merges area × function × geo actual increments vs budgets (+ optional FY merit % targets) with rollup totals.",
          "endpoint": "/api/spokes/anycomp/analytics/vs-plan",
          "method": "POST",
          "requestSchema": "VsPlanRequestSchema",
          "responseSchema": "VsPlanResponseSchema"
        },
        {
          "id": "anycomp.analytics.pool-metrics",
          "description": "Pool merit / promo / adj spend versus payroll denominator with optional weighted geo-average splits.",
          "endpoint": "/api/spokes/anycomp/analytics/pool-metrics",
          "method": "POST",
          "requestSchema": "PoolMetricsRequestSchema",
          "responseSchema": "PoolMetricsResponseSchema"
        },
        {
          "id": "anycomp.analytics.hr-finance-crossover",
          "description": "Tenant-config HR↔finance bridge ratios (comp per FTE, comp % revenue proxy, comp % EBITDA) from explicit payroll + FP&A assumptions.",
          "endpoint": "/api/spokes/anycomp/analytics/hr-finance-crossover",
          "method": "POST",
          "requestSchema": "HrFinanceCrossoverRequestSchema",
          "responseSchema": "HrFinanceCrossoverResponseSchema"
        },
        {
          "id": "anycomp.analytics.comp-lifecycle",
          "description": "Seven-column comp lifecycle workbook vector (hourly detect ×2080, pre/post merit TC vs band-mid promo-isolated ratios).",
          "endpoint": "/api/spokes/anycomp/analytics/comp-lifecycle",
          "method": "POST",
          "requestSchema": "CompLifecycleComputationRequestSchema",
          "responseSchema": "CompLifecycleComputationResponseSchema"
        },
        {
          "id": "anycomp.analytics.cr-drift-histogram",
          "description": "Fine-grained comp-ratio histogram with paired employee pre/post counts and configurable bucket windows.",
          "endpoint": "/api/spokes/anycomp/analytics/cr-drift-histogram",
          "method": "POST",
          "requestSchema": "CrDriftHistogramRequestSchema",
          "responseSchema": "CrDriftHistogramResponseSchema"
        },
        {
          "id": "anycomp.analytics.dual-nine-box",
          "description": "Perf × Pay-Equity dual nine-box matrices (pre/post) + movement arcs using segmentation-stable bucket IDs.",
          "endpoint": "/api/spokes/anycomp/analytics/dual-nine-box",
          "method": "POST",
          "requestSchema": "Dual9BoxRequestSchema",
          "responseSchema": "Dual9BoxResponseSchema"
        },
        {
          "id": "anycomp.scenarios.generate",
          "description": "Decision layer (PAT-AC1): priority weights + budget → several distinct scenarios scored on every three-value measure, plus a tradeoff radar (the 'never one option' loop).",
          "endpoint": "/api/spokes/anycomp/scenarios",
          "method": "POST",
          "requestSchema": "GenerateScenariosRequestSchema",
          "responseSchema": "ScenarioBundleSchema"
        },
        {
          "id": "anycomp.engagements.run",
          "description": "Persisted engagement: save strategy + priorities, run the decision loop, persist scenarios + impacts + audit in one transaction; returns the bundle.",
          "endpoint": "/api/spokes/anycomp/engagements",
          "method": "POST",
          "requestSchema": "EngagementRunRequestSchema",
          "responseSchema": "EngagementResponseSchema"
        },
        {
          "id": "anycomp.engagements.get",
          "description": "Reload a persisted engagement (strategy + its scenarios/impacts) by strategyId, tenant-scoped.",
          "endpoint": "/api/spokes/anycomp/engagements/[strategyId]",
          "method": "GET",
          "responseSchema": "EngagementGetResponseSchema"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "metrics-catalog",
      "schema": "metrics_catalog",
      "contractVersion": "1.1.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/metrics-catalog/contracts/types.ts",
      "contracts": [
        {
          "id": "metrics-catalog.list",
          "description": "List canonical HR metric definitions (6 categories, 102 metrics). Optional ?categoryId=&limit=&offset= for filtering / pagination.",
          "endpoint": "/api/spokes/metrics-catalog/metrics",
          "method": "GET",
          "responseSchema": "MetricListResultSchema"
        },
        {
          "id": "metrics-catalog.lookup",
          "description": "Fetch a single MetricDefinition by stable composite id 'hr-metric.<category-slug>.<metric-slug>'.",
          "endpoint": "/api/spokes/metrics-catalog/metrics/[id]",
          "method": "GET",
          "responseSchema": "MetricDefinitionSchema"
        },
        {
          "id": "metrics-catalog.search",
          "description": "Case-insensitive substring search across name / description / slug. Optional ?categoryId=&limit= (default 20, max 100).",
          "endpoint": "/api/spokes/metrics-catalog/metrics/search",
          "method": "GET",
          "responseSchema": "MetricsCatalogSearchMcpOutputSchema"
        },
        {
          "id": "metrics-catalog.list-categories",
          "description": "List the 6 catalog categories (workforce-composition, compensation-benefits, talent-acquisition, performance-development, engagement-retention, workforce-planning).",
          "endpoint": "/api/spokes/metrics-catalog/categories",
          "method": "GET",
          "responseSchema": "MetricCategoryListResultSchema"
        }
      ],
      "consumers": [
        {
          "name": "calculus",
          "cardId": "PAT-40-phase-3",
          "status": "live"
        },
        {
          "name": "performix",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "org-graph",
      "schema": "org_graph",
      "contractVersion": "1.1.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/org-graph/contracts/types.ts",
      "contracts": [
        {
          "id": "org-graph.health",
          "description": "Per-spoke heartbeat for Postgres reachability on `org_graph`.",
          "endpoint": "/api/spokes/org-graph/health",
          "method": "GET",
          "responseSchema": "SpokeHealth"
        },
        {
          "id": "org-graph.graph.build",
          "description": "Idempotent graph rebuild — materializes org nodes + typed temporal edges for `(tenantId, snapshotId)` from worker-resolution rows.",
          "endpoint": "/api/spokes/org-graph/graph/build",
          "method": "POST",
          "requestSchema": "BuildGraphRequestSchema",
          "responseSchema": "BuildGraphResponseSchema"
        },
        {
          "id": "org-graph.query.resolve-ancestor",
          "description": "As-of supervisory (or typed hierarchy) parent chain for a single worker node.",
          "endpoint": "/api/spokes/org-graph/query/resolve-ancestor",
          "method": "POST",
          "requestSchema": "ResolveAncestorRequestSchema",
          "responseSchema": "ResolveAncestorResponseSchema"
        },
        {
          "id": "org-graph.query.ancestor-chains",
          "description": "Memoized ancestor stacks for many leaf nodes in one call.",
          "endpoint": "/api/spokes/org-graph/query/ancestor-chains",
          "method": "POST",
          "requestSchema": "AncestorChainsRequestSchema",
          "responseSchema": "AncestorChainsResponseSchema"
        },
        {
          "id": "org-graph.elt.substring-match",
          "description": "Stateless substring roster match for ELT workbook-style HRIS strings.",
          "endpoint": "/api/spokes/org-graph/elt/substring",
          "method": "POST",
          "requestSchema": "EltSubstringRequestSchema",
          "responseSchema": "EltSubstringResponseSchema"
        },
        {
          "id": "org-graph.inference.dominant-dept",
          "description": "Dominant dept→ELT empirical edges with fractional confidence.",
          "endpoint": "/api/spokes/org-graph/inference/dominant-dept",
          "method": "POST",
          "requestSchema": "DominantDeptInferenceRequestSchema",
          "responseSchema": "DominantDeptInferenceResponseSchema"
        },
        {
          "id": "org-graph.consistency.report",
          "description": "Orphans, cycles, multi-parent clashes, dangling manager edges.",
          "endpoint": "/api/spokes/org-graph/consistency/report",
          "method": "POST",
          "requestSchema": "ConsistencyReportRequestSchema",
          "responseSchema": "ConsistencyReportSchema"
        },
        {
          "id": "org-graph.snapshot.diff",
          "description": "Diff two snapshots — mover map, subtree graft heuristic, span-of-control deltas with optional magnitude threshold.",
          "endpoint": "/api/spokes/org-graph/snapshot/diff",
          "method": "POST",
          "requestSchema": "DiffRequestSchema",
          "responseSchema": "DiffResponseSchema"
        },
        {
          "id": "org-graph.rollup.leader-metrics",
          "description": "Leader-per-layer rollup of leaf measures mirroring workbook leader-metrics passes.",
          "endpoint": "/api/spokes/org-graph/rollup/leaders",
          "method": "POST",
          "requestSchema": "LeaderAggregationRequestSchema",
          "responseSchema": "LeaderAggregationResponseSchema"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "job-family-agent",
      "schema": "job_family_agent",
      "contractVersion": "1.2.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/job-family-agent/contracts/types.ts",
      "contracts": [
        {
          "id": "job-family-agent.soc.list",
          "description": "Paginated list of canonical SOC codes (O*NET 28.3 / SOC 2018). 1,016 occupations across 23 SOC major groups; default 100/page, max 500.",
          "endpoint": "/api/spokes/job-family-agent/soc/list",
          "method": "GET",
          "responseSchema": "SocListResponseSchema"
        },
        {
          "id": "job-family-agent.soc.lookup",
          "description": "Fetch a single SOC code by canonical id (e.g., '15-1252.00'). Accepts compact ('15-1252') or full forms.",
          "endpoint": "/api/spokes/job-family-agent/soc/[code]",
          "method": "GET",
          "responseSchema": "SocLookupResponseSchema"
        },
        {
          "id": "job-family-agent.families.list",
          "description": "List 23 canonical job families with hierarchy + alternative strings + resolved SOC codes (by major-group alignment).",
          "endpoint": "/api/spokes/job-family-agent/families/list",
          "method": "GET",
          "responseSchema": "JobFamilyListResponseSchema"
        },
        {
          "id": "job-family-agent.families.lookup",
          "description": "Fetch a single job family by stable id (e.g., 'jf.computer-mathematical') with resolved SOC codes.",
          "endpoint": "/api/spokes/job-family-agent/families/[id]",
          "method": "GET",
          "responseSchema": "JobFamilyLookupResponseSchema"
        },
        {
          "id": "job-family-agent.functions.list",
          "description": "List 26 canonical job functions with parent-family links + alternative strings + SOC codes (parsed from source definitions).",
          "endpoint": "/api/spokes/job-family-agent/functions/list",
          "method": "GET",
          "responseSchema": "JobFunctionListResponseSchema"
        },
        {
          "id": "job-family-agent.functions.lookup",
          "description": "Fetch a single job function by stable id (e.g., 'jfn.engineering') with parent family + alt-strings + SOC codes.",
          "endpoint": "/api/spokes/job-family-agent/functions/[id]",
          "method": "GET",
          "responseSchema": "JobFunctionLookupResponseSchema"
        },
        {
          "id": "job-family-agent.classify",
          "description": "Token-overlap heuristic classifier: free text → up to 8 SOC matches with confidence + best-guess job family + job function. Direct SOC matches detected in text override the heuristic. Stateless; public + IP-rate-limited at 100 req/min.",
          "endpoint": "/api/spokes/job-family-agent/classify",
          "method": "POST",
          "requestSchema": "ClassifyRequestSchema",
          "responseSchema": "ClassifyResponseSchema"
        },
        {
          "id": "job-family-agent.resolve-title",
          "description": "JobFrame canon (PAT-JF1): resolve a messy observed title to ranked Family×Focus×Level profile candidates (profileKey e.g. 'SWE.GEN.P6') with confidence band, evidence, and recommendedAction. Alias-exact in MVP; public + also an MCP tool.",
          "endpoint": "/api/spokes/job-family-agent/resolve-title",
          "method": "GET",
          "requestSchema": "ResolveTitleRequestSchema",
          "responseSchema": "ResolveTitleResponseSchema"
        },
        {
          "id": "job-family-agent.construct",
          "description": "JobFrame canon: assemble a draft profile top-down from family/focus/level (+ context modifiers) — no blank-page JD. Returns a canonical_job_profile draft.",
          "endpoint": "/api/spokes/job-family-agent/construct",
          "method": "GET",
          "responseSchema": "CanonicalProfileSchema"
        },
        {
          "id": "job-family-agent.export",
          "description": "JobFrame canon: export a canonical profile as JSON or Markdown (?profileKey=&format=). Plus HRIS bulk-mapping + single-JD analysis + mapping-decision (tenant-overlay) write routes — see the spoke README surface table.",
          "endpoint": "/api/spokes/job-family-agent/export",
          "method": "GET",
          "responseSchema": "JobExportSchema"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "cardId": "PFX-5",
          "status": "in-progress"
        },
        {
          "name": "vela",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "manager-effectiveness",
      "schema": "manager_effectiveness",
      "contractVersion": "1.2.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/manager-effectiveness/contracts/types.ts",
      "contracts": [
        {
          "id": "manager-effectiveness.health",
          "description": "Per-spoke Postgres reachability shim on `manager_effectiveness` (heartbeat + tenant weight profile table presence).",
          "endpoint": "/api/spokes/manager-effectiveness/health",
          "method": "GET",
          "responseSchema": "SpokeHealth"
        },
        {
          "id": "manager-effectiveness.composite.compute",
          "description": "Nine-domain hierarchical Manager Effectiveness Index (MEI) composite (0–100) with optional measure ladder payload, flight-risk penalty, and tenant-persisted pillar weights.",
          "endpoint": "/api/spokes/manager-effectiveness/composite",
          "method": "POST",
          "requestSchema": "CompositeComputeRequestSchema",
          "responseSchema": "MeiCompositeScoreSchema"
        },
        {
          "id": "manager-effectiveness.tenant.weights.get",
          "description": "Return merged MEI pillar weights for a tenant (Postgres profile when present, otherwise toolbox defaults mirroring the FiveTran MQI mix).",
          "endpoint": "/api/spokes/manager-effectiveness/weights",
          "method": "GET",
          "responseSchema": "TenantWeightsResponseSchema"
        },
        {
          "id": "manager-effectiveness.tenant.weights.upsert",
          "description": "UPSERT the nine canonical pillar weights per tenant (service key; sum-to-one validation via Zod).",
          "endpoint": "/api/spokes/manager-effectiveness/weights",
          "method": "POST",
          "requestSchema": "TenantWeightsUpsertSchema",
          "responseSchema": "TenantWeightsResponseSchema"
        },
        {
          "id": "manager-effectiveness.tenant.weights.recalibrate",
          "description": "Blend pooled Q2-derived per-domain explanatory mass with FiveTran-informed priors, UPSERT merged MEI pillar weights per tenant, and append an analyst-facing audit row.",
          "endpoint": "/api/spokes/manager-effectiveness/weights/recalibrate",
          "method": "POST",
          "requestSchema": "RecalibrationRequestSchema",
          "responseSchema": "RecalibrationResponseSchema"
        },
        {
          "id": "manager-effectiveness.archetype.classify",
          "description": "Classify team staffing archetype from net growth, exit rate, and headcount (defaults align with toolbox workforce analytics cut points).",
          "endpoint": "/api/spokes/manager-effectiveness/archetype",
          "method": "GET",
          "responseSchema": "ArchetypeClassifyResponseSchema"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "status": "planned"
        },
        {
          "name": "vela",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "wage-benchmark",
      "schema": "wage_benchmark",
      "contractVersion": "0.4.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/wage-benchmark/contracts/types.ts",
      "contracts": [
        {
          "id": "wage-benchmark.benchmark.query",
          "description": "Median hourly wage + p10/p25/p75/p90 + a confidence interval for an SOC × geography. National cells are observed (BLS OEWS, tight CI); sub-national cells are projected from the national anchor with an honestly wide CI until OEWS metro/state tables are ingested. Flags: basis / confidence / observedForCell.",
          "endpoint": "/api/spokes/wage-benchmark/benchmark",
          "method": "GET",
          "responseSchema": "BenchmarkQueryResponseSchema"
        },
        {
          "id": "wage-benchmark.benchmark.coverage",
          "description": "Honest dataset coverage: occupation count, data year, observed vs. projected geo levels, sources, and the widest-error-bars-first widening roadmap.",
          "endpoint": "/api/spokes/wage-benchmark/coverage",
          "method": "GET",
          "responseSchema": "BenchmarkCoverageResponseSchema"
        },
        {
          "id": "wage-benchmark.health",
          "description": "Per-spoke Postgres reachability shim on `wage_benchmark` (heartbeat probe).",
          "endpoint": "/api/spokes/wage-benchmark/health",
          "method": "GET",
          "responseSchema": "SpokeHealth"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "wage-compliance",
      "schema": "wage_compliance",
      "contractVersion": "0.17.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/wage-compliance/contracts/types.ts",
      "contracts": [
        {
          "id": "wage-compliance.jurisdictions.resolve",
          "description": "Resolve a work-location address into its hierarchical jurisdiction chain with precision tier + ambiguity flags + rooftop-pending indicator (PAT-89 future).",
          "endpoint": "/api/spokes/wage-compliance/jurisdictions/resolve",
          "method": "POST",
          "requestSchema": "ResolveJurisdictionRequestSchema",
          "responseSchema": "ResolveJurisdictionResponseSchema"
        },
        {
          "id": "wage-compliance.jurisdictions.rules",
          "description": "Return the active applied rule_version for a known jurisdiction_id + rule family + classification + evaluation date.",
          "endpoint": "/api/spokes/wage-compliance/jurisdictions/[id]/rules",
          "method": "GET",
          "responseSchema": "RuleLookupResponseSchema"
        },
        {
          "id": "wage-compliance.evaluate.single",
          "description": "Stateless single-worker compliance evaluation: pass/fail/warning/unknown with full evaluation trace + jurisdiction chain + applied rule.",
          "endpoint": "/api/spokes/wage-compliance/evaluate/single",
          "method": "POST",
          "requestSchema": "EvaluateSingleRequestSchema",
          "responseSchema": "EvaluateSingleResponseSchema"
        },
        {
          "id": "wage-compliance.evaluate.bulk",
          "description": "Stateless bulk compliance evaluation (≤10,000 workers) with per-worker results + aggregate pass/fail/warning/unknown counts.",
          "endpoint": "/api/spokes/wage-compliance/evaluate/bulk",
          "method": "POST",
          "requestSchema": "EvaluateBulkRequestSchema",
          "responseSchema": "EvaluateBulkResponseSchema"
        },
        {
          "id": "wage-compliance.evaluate.offer",
          "description": "PAT-90 — Stateless single-offer (ATS / pre-hire) compliance evaluation. Resolves the candidate's location, looks up the applicable rule, returns pass/warning/fail/unknown plus a `recommendedHourlyWage` (required + 1¢ buffer) when failing. Writes an `api`-source row to wage_compliance.offer_evaluations_audit (fire-and-forget).",
          "endpoint": "/api/spokes/wage-compliance/evaluate/offer",
          "method": "POST",
          "requestSchema": "OfferEvaluationRequestSchema",
          "responseSchema": "OfferEvaluationResponseSchema"
        },
        {
          "id": "wage-compliance.evaluate.posting",
          "description": "PAT-100 — Stateless pay-transparency posting / offer-letter validation. Resolves the jurisdiction (id or location), looks up the applicable `pay-transparency` rule_version, and returns pass/fail + `missingFields[]`. `noObligation=true` when the jurisdiction has no transparency rule on file (outcome still `pass`).",
          "endpoint": "/api/spokes/wage-compliance/evaluate/posting",
          "method": "POST",
          "requestSchema": "EvaluatePostingRequestSchema",
          "responseSchema": "EvaluatePostingResponseSchema"
        },
        {
          "id": "wage-compliance.evaluate.paid-leave",
          "description": "PAT-99-FU-A — Stateless paid-leave accrual + eligibility evaluation. Given a rule_version id (from the PAT-99 seed) + worker YTD hours-worked + employment start date + optional carryover, returns a `PaidLeaveFinding` (accrued hours, available balance, days-until-eligible, paid / job-protected / FMLA-compatible flags). Supports per-hour / front-load / fixed-bank accrual methods.",
          "endpoint": "/api/spokes/wage-compliance/evaluate/paid-leave",
          "method": "POST",
          "requestSchema": "PaidLeaveEvaluationRequestSchema",
          "responseSchema": "PaidLeaveEvaluationResponseSchema"
        },
        {
          "id": "wage-compliance.worker-classification.evaluate",
          "description": "PAT-97 — Advisory FLSA + state heuristic classifier for industry-specific exemption / tipped / prevailing-wage / independent-contractor patterns. Stateless; optional `industry` selects layered rulesets (generic default). Does not substitute for legal advice.",
          "endpoint": "/api/spokes/wage-compliance/worker-classification",
          "method": "POST",
          "requestSchema": "WorkerClassificationEvaluationRequestSchema",
          "responseSchema": "WorkerClassificationEvaluationResponseSchema"
        },
        {
          "id": "wage-compliance.alerts.list",
          "description": "List compliance alerts (law change / new failure / expiring rule / conflict / refresh failure). Filterable by organizationId, status, severity, alertType. v0 read surface; PAT-87 temporal diff agent populates rows.",
          "endpoint": "/api/spokes/wage-compliance/alerts",
          "method": "GET",
          "responseSchema": "ListAlertsResponseSchema"
        },
        {
          "id": "wage-compliance.alerts.acknowledge",
          "description": "Acknowledge a compliance alert — flip status from `open` to `reviewed` or `resolved`. Idempotent.",
          "endpoint": "/api/spokes/wage-compliance/alerts/[id]/acknowledge",
          "method": "POST",
          "requestSchema": "AcknowledgeAlertRequestSchema",
          "responseSchema": "AcknowledgeAlertResponseSchema"
        },
        {
          "id": "wage-compliance.rule-changes.recent",
          "description": "Temporal diff feed of rule-version changes (increase / decrease / correction / expiration / override). Filterable by `since` + `changeType`. v0 read surface; PAT-87 temporal diff agent populates rows.",
          "endpoint": "/api/spokes/wage-compliance/rule-changes/recent",
          "method": "GET",
          "responseSchema": "ListRuleChangesResponseSchema"
        },
        {
          "id": "wage-compliance.review-queue.create",
          "description": "PAT-94 — Create a review-queue item from an existing compliance_evaluation row. Writes a 'created' audit event. Persistent; pairs with stateless evaluate.single / evaluate.bulk to land specific results into the operator review workflow.",
          "endpoint": "/api/spokes/wage-compliance/review-queue",
          "method": "POST",
          "requestSchema": "CreateReviewQueueItemRequestSchema",
          "responseSchema": "CreateReviewQueueItemResponseSchema"
        },
        {
          "id": "wage-compliance.review-queue.list",
          "description": "PAT-94 — List review-queue items for an organization. Filterable by status + assigned_to; cursor-paginated newest-first.",
          "endpoint": "/api/spokes/wage-compliance/review-queue",
          "method": "GET",
          "responseSchema": "ListReviewQueueItemsResponseSchema"
        },
        {
          "id": "wage-compliance.review-queue.get",
          "description": "PAT-94 — Fetch a single review-queue item with embedded recent notes + recent audit events. One call powers the operator item-detail view.",
          "endpoint": "/api/spokes/wage-compliance/review-queue/[id]",
          "method": "GET",
          "responseSchema": "GetReviewQueueItemResponseSchema"
        },
        {
          "id": "wage-compliance.review-queue.assign",
          "description": "PAT-94 — Assign (or unassign with `assigneeId: null`) a review-queue item to an operator. Writes an 'assigned' / 'unassigned' audit event. Idempotent.",
          "endpoint": "/api/spokes/wage-compliance/review-queue/[id]/assign",
          "method": "POST",
          "requestSchema": "AssignReviewQueueItemRequestSchema",
          "responseSchema": "AssignReviewQueueItemResponseSchema"
        },
        {
          "id": "wage-compliance.review-queue.set-status",
          "description": "PAT-94 — Transition a review-queue item's status (new / under_review / approved / exported / resolved / dismissed). Writes a 'status_changed' (or 'exported') audit event capturing the previous status.",
          "endpoint": "/api/spokes/wage-compliance/review-queue/[id]/status",
          "method": "POST",
          "requestSchema": "SetReviewQueueItemStatusRequestSchema",
          "responseSchema": "SetReviewQueueItemStatusResponseSchema"
        },
        {
          "id": "wage-compliance.review-queue.add-note",
          "description": "PAT-94 — Append a free-text note to a review-queue item; atomically bumps notes_count and writes a 'note_added' audit event referencing the new note id.",
          "endpoint": "/api/spokes/wage-compliance/review-queue/[id]/notes",
          "method": "POST",
          "requestSchema": "AddReviewQueueNoteRequestSchema",
          "responseSchema": "AddReviewQueueNoteResponseSchema"
        },
        {
          "id": "wage-compliance.review-queue.audit",
          "description": "PAT-94 — Paginated audit history for a single review-queue item (created / assigned / unassigned / status_changed / note_added / exported). Newest-first.",
          "endpoint": "/api/spokes/wage-compliance/review-queue/[id]/audit",
          "method": "GET",
          "responseSchema": "GetReviewQueueAuditResponseSchema"
        },
        {
          "id": "wage-compliance.exports.generate",
          "description": "PAT-91 — Generate a payroll-vendor export pack (ADP / UKG / Paychex) for a set of compliance_evaluation rows. Uploads CSV to Vercel Blob with a 7-day signed-URL TTL, writes a row to wage_compliance.exports_audit, and (when reviewQueueId is provided) a companion 'exported' audit event to review_queue_audit. Vendor is part of the path: /api/spokes/wage-compliance/exports/[vendor].",
          "endpoint": "/api/spokes/wage-compliance/exports/[vendor]",
          "method": "POST",
          "requestSchema": "ExportRequestSchema",
          "responseSchema": "ExportResponseSchema"
        },
        {
          "id": "wage-compliance.diff-agent.run",
          "description": "PAT-87 — Run the temporal diff agent. Scans pending `rule_versions` added in the lookback window, classifies the change vs the current canonical version, emits one `rule_change_events` row per change + tiered `compliance_alerts` per affected organization (informational ≥90d / warning ≥30d / critical ≥7d / immediate <7d or active). Scheduled daily at 09:00 UTC via vercel.ts; MCP tool is the manual trigger.",
          "endpoint": "/api/cron/wage-compliance/run-diff-agent",
          "method": "GET",
          "responseSchema": "DiffAgentRunResultSchema"
        },
        {
          "id": "wage-compliance.jurisdiction-discovery.scan",
          "description": "PAT-102 — Jurisdiction-discovery AI agent (third toolbox AI consumer). Scans curated US government sources for new minimum-wage ordinances or statutes not yet represented in `wage_compliance.jurisdictions`. Official-domain trust scoring (.gov / state.xx.us → high; HR aggregators → medium; everything else → low). Persists every candidate to `wage_compliance.discovery_candidates`; high-trust + confidence ≥ 0.9 candidates auto-escalate to the PAT-94 review queue. Scheduled weekly at 10:00 UTC Tuesdays via vercel.ts; MCP tool is the operator override for ad-hoc scans.",
          "endpoint": "/api/cron/wage-compliance/jurisdiction-discovery",
          "method": "GET",
          "responseSchema": "JurisdictionDiscoveryScanResultSchema"
        },
        {
          "id": "wage-compliance.rules.get",
          "description": "PAT-84-FU-B — Drill-down read of a single rule_version: version row + jurisdiction_rule + rule_family + full jurisdiction chain + source citations.",
          "endpoint": "/api/spokes/wage-compliance/rules/[ruleVersionId]",
          "method": "GET",
          "responseSchema": "GetRuleVersionResponseSchema"
        },
        {
          "id": "wage-compliance.jurisdictions.list",
          "description": "PAT-84-FU-B-LIST — Paginated jurisdictions hierarchy fetch. Filterable by parentJurisdictionId, jurisdictionType, stateCode; cursor-paginated (canonical_name asc).",
          "endpoint": "/api/spokes/wage-compliance/jurisdictions",
          "method": "GET",
          "responseSchema": "ListJurisdictionsResponseSchema"
        },
        {
          "id": "wage-compliance.alerts.assign",
          "description": "PAT-84-FU-C — Assign a compliance_alert to an operator. Writes to wage_compliance.alert_assignments; fire-and-forget audit to mcp.mcp_audit.",
          "endpoint": "/api/spokes/wage-compliance/alerts/[id]/assign",
          "method": "POST",
          "requestSchema": "AssignAlertRequestSchema",
          "responseSchema": "AssignAlertResponseSchema"
        },
        {
          "id": "wage-compliance.alerts.add-note",
          "description": "PAT-84-FU-C — Append a free-text note (≤10,000 chars) to a compliance_alert. Writes to wage_compliance.alert_notes; fire-and-forget audit to mcp.mcp_audit.",
          "endpoint": "/api/spokes/wage-compliance/alerts/[id]/notes",
          "method": "POST",
          "requestSchema": "AddAlertNoteRequestSchema",
          "responseSchema": "AddAlertNoteResponseSchema"
        },
        {
          "id": "wage-compliance.evaluations.aggregate",
          "description": "PAT-84-FU-D — Dashboard KPI aggregate. Returns total/failure/pending-review/upcoming-rule-change counts, estimated annual payroll exposure (|discrepancy| × 2080), per-state failure + exposure rollup, and the last 10 compliance_alerts for an organization. Counts + aggregates only — no PII.",
          "endpoint": "/api/spokes/wage-compliance/evaluations/aggregate",
          "method": "GET",
          "responseSchema": "EvaluationsAggregateResponseSchema"
        },
        {
          "id": "wage-compliance.conflicts.list",
          "description": "PAT-95-FU-A — List source-citation conflicts: tuples (jurisdictionId × ruleFamilyId × effectiveStart) where ≥2 rule_versions exist. Returns candidate rule_versions, their citations, and the wage disagreement span.",
          "endpoint": "/api/spokes/wage-compliance/conflicts",
          "method": "GET",
          "responseSchema": "ListConflictsResponseSchema"
        },
        {
          "id": "wage-compliance.conflicts.resolve",
          "description": "PAT-95-FU-B — Resolve a source-citation conflict. Flips the accepted rule_version to `validated`, supersedes siblings on the same (jurisdictionId × ruleFamilyId × effectiveStart) tuple, writes an audit row to wage_compliance.conflict_resolutions.",
          "endpoint": "/api/spokes/wage-compliance/rules/[ruleVersionId]/resolve-conflict",
          "method": "POST",
          "requestSchema": "ResolveConflictRequestSchema",
          "responseSchema": "ResolveConflictResponseSchema"
        },
        {
          "id": "wage-compliance.ai-source-validation.scan",
          "description": "PAT-103 — Source-validation AI agent. Re-scores wage_compliance.data_sources rows on the high/medium/low trust ladder using a deterministic domain heuristic (`.gov` → high, aggregators → medium) plus AI judgment for the unclassified tail. Writes audit rows to `source_validations` and updates `data_sources.trust_level` in place. Weekly cron + MCP operator tool.",
          "endpoint": "/api/cron/wage-compliance/source-validation",
          "method": "GET",
          "requestSchema": "ValidateDataSourcesRequestSchema",
          "responseSchema": "ValidateDataSourcesResponseSchema"
        },
        {
          "id": "wage-compliance.ai-conflict-detection.scan",
          "description": "PAT-104 — Conflict-detection AI agent. Reads the PAT-95-FU-A conflict list and writes one draft resolution per conflict tuple to `conflict_resolution_drafts` for operator approval. Drafts never mutate rule_versions directly — committing routes through PAT-95-FU-B. Weekly cron + MCP operator tool.",
          "endpoint": "/api/cron/wage-compliance/ai-conflict-detection",
          "method": "GET",
          "requestSchema": "DetectConflictsRequestSchema",
          "responseSchema": "DetectConflictsResponseSchema"
        },
        {
          "id": "wage-compliance.ai-confidence-scoring.score",
          "description": "PAT-105 — Confidence-scoring agent. 4-factor model (0.40 source / 0.20 extraction / 0.20 temporal / 0.20 cross-source) over each rule_version; writes `rule_versions.confidence_score` in place. Daily cron + inline hook from PAT-85 refresh + MCP operator tool.",
          "endpoint": "/api/cron/wage-compliance/ai-confidence-scoring",
          "method": "GET",
          "requestSchema": "ScoreRuleVersionConfidenceRequestSchema",
          "responseSchema": "ScoreRuleVersionConfidenceResponseSchema"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "worker-resolution",
      "schema": "worker_resolution",
      "contractVersion": "1.0.0",
      "status": "live",
      "contractsTypesPath": "src/spokes/worker-resolution/contracts/types.ts",
      "contracts": [
        {
          "id": "worker-resolution.resolve",
          "description": "Two-Pass Self-Healing join: learning index fortification followed by deterministic merge with HRIS vault, ladder matching, lineage, admissions policy, aliases learned, failures roster.",
          "endpoint": "/api/spokes/worker-resolution/resolve",
          "method": "POST",
          "requestSchema": "ResolveRequestSchema",
          "responseSchema": "ResolveResponseSchema"
        },
        {
          "id": "worker-resolution.health",
          "description": "Per-spoke Postgres heartbeat shim on `worker_resolution`.",
          "endpoint": "/api/spokes/worker-resolution/health",
          "method": "GET",
          "responseSchema": "SpokeHealth"
        }
      ],
      "consumers": [
        {
          "name": "performix",
          "status": "planned"
        },
        {
          "name": "vela",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "principia-connector",
      "schema": "principia_connector",
      "contractVersion": "0.0.0",
      "status": "coming-soon",
      "contractsTypesPath": "src/spokes/principia-connector/contracts/types.ts",
      "contracts": [],
      "consumers": [
        {
          "name": "calculus",
          "status": "planned"
        },
        {
          "name": "performix",
          "status": "planned"
        }
      ]
    },
    {
      "slug": "glass-ox",
      "schema": "glass_ox",
      "contractVersion": "0.1.0",
      "status": "coming-soon",
      "contractsTypesPath": "src/spokes/glass-ox/contracts/types.ts",
      "contracts": [
        {
          "id": "glass-ox.health",
          "description": "Per-spoke Postgres reachability shim on `glass_ox` (heartbeat probe). Also seeds the reference `companalyst-coding` plan on first hit so the plans list is never empty on a fresh deploy.",
          "endpoint": "/api/spokes/glass-ox/health",
          "method": "GET",
          "responseSchema": "SpokeHealth"
        },
        {
          "id": "glass-ox.runs.list",
          "description": "List persisted `RunReport` summaries, newest-reported-first. Query params: planSlug, overallStatus (ok|warn|halt), organizationId, limit (cap 200), offset.",
          "endpoint": "/api/spokes/glass-ox/runs",
          "method": "GET",
          "responseSchema": "RunSummarySchema"
        },
        {
          "id": "glass-ox.runs.get",
          "description": "Fetch a single persisted RunReport by `runId` — rehydrates the full StepManifest DAG (assertions + field profiles + drops-by-reason + joins).",
          "endpoint": "/api/spokes/glass-ox/runs/[runId]",
          "method": "GET",
          "responseSchema": "RunReportSchema"
        },
        {
          "id": "glass-ox.plans.list",
          "description": "List registered Glass Ox plans. POST registration ships in Slice 3; today the table is seeded with the reference `companalyst-coding` plan.",
          "endpoint": "/api/spokes/glass-ox/plans",
          "method": "GET",
          "responseSchema": "PlanSchema"
        }
      ],
      "consumers": []
    }
  ],
  "crossCutting": [
    {
      "id": "insight-player.contract",
      "description": "Canonical Insight Card contract + pure sequencing core (PAT-34). Vendor `src/lib/insight-player/contract.ts` + `sequence.ts` to consume. Bump-via-toolbox is the only path to evolve.",
      "version": "0.2.0",
      "contractsTypesPath": "src/lib/insight-player/contract.ts",
      "schemaNames": [
        "InsightCardSchema",
        "InsightCardVisualSchema",
        "InsightCardVisualKindSchema",
        "InsightCardFreshnessSchema",
        "InsightCardRevealSchema",
        "InsightCardSignalRequestSchema",
        "InsightCardEmitterSchema",
        "InsightCardFocusSchema",
        "BuildPlayerQueueRequestSchema",
        "BuildPlayerQueueResponseSchema"
      ]
    },
    {
      "id": "toolbox.analyses",
      "description": "Analysis Catalog (PAT-42 / Catalog 3 of 7): named, versioned methodology-defined analytical units composing one or more spokes. Each entry encodes 100-250 words of methodology prose with citations. Static typed catalog in v1 (DB-back when entries exceed ~100). MCP tools: `toolbox.analyses.list / lookup / compose`.",
      "version": "0.1.0",
      "contractsTypesPath": "src/lib/analyses/contract.ts",
      "schemaNames": [
        "AnalysisDefinitionSchema",
        "AnalysisCategorySchema",
        "AnalysisInputDescriptorSchema",
        "AnalysisOutputDescriptorSchema",
        "AnalysisCompositionRequestSchema",
        "AnalysisCompositionResultSchema",
        "AnalysisDispatchStepSchema",
        "AnalysisListResultSchema"
      ],
      "entryCount": 20
    },
    {
      "id": "toolbox.viz",
      "description": "Visualization Catalog (PAT-43, Catalog 4 of 7): named visualization templates with declared input shapes + AI-routing tags. Templates back the InsightCard `visual.kind` enum. Three MCP tools: toolbox.viz.list-templates, toolbox.viz.lookup-template, toolbox.viz.infer-template-for-envelope.",
      "version": "0.4.0",
      "contractsTypesPath": "src/lib/visualizations/contract.ts",
      "schemaNames": [
        "VizTemplateSchema",
        "VizPayloadSchema",
        "VizRoutingTagSchema",
        "VizInferenceRequestSchema",
        "VizInferenceResultSchema",
        "VizChartKindSchema",
        "VizIntentSchema",
        "VizDataShapeTagSchema",
        "VizInteractionPatternSchema",
        "VizAccessibilityMetadataSchema",
        "VizColorScaleModeSchema",
        "VizPalettePolicySchema",
        "VizChartTypeRegistryEntrySchema",
        "VizAriaRoleSuggestionSchema"
      ],
      "entryCount": 38
    },
    {
      "id": "toolbox.data-sources",
      "description": "Data Source Catalog (PAT-45 / Catalog 7 of 7): catalog of authoritative People Analytics data sources — BLS / Census / O*NET / EEOC / NAICS / commercial / internal. Records each source's provider, license, freshness, access path, and integration status (connected / partial / planned / blocked). Two MCP tools: toolbox.data-sources.list / lookup.",
      "version": "0.2.0",
      "contractsTypesPath": "src/lib/data-sources/contract.ts",
      "schemaNames": [
        "DataSourceSchema",
        "DataSourceProviderSchema",
        "DataSourceCategorySchema",
        "FreshnessCadenceSchema",
        "LicenseSchema",
        "IntegrationStatusSchema",
        "DataSourceListResultSchema"
      ],
      "entryCount": 26
    },
    {
      "id": "toolbox.etl",
      "description": "Generic ETL connector library (PAT-N4 — Objective 1). JSON / CSV / read-only Postgres / REST ingestion with TenantContext, canonical-field projection, and persistence into `segmentation_studio.workforce_dataset_rows`. HTTP POST `/api/connectors/etl/ingest`; MCP tool `toolbox.etl.ingest`.",
      "version": "0.1.0",
      "contractsTypesPath": "src/lib/connectors/etl/shared/types.ts",
      "schemaNames": [
        "EtlSourceConfigSchema",
        "EtlIngestRequestSchema",
        "EtlIngestResultSchema",
        "EtlIngestRouteResponseSchema"
      ]
    }
  ],
  "generatedAt": "2026-06-03T12:01:21.772Z"
}

Never hand-maintain slug lists beside this artefact.

Dev Console

Try the API without leaving the browser.

Browse the live spoke catalog, fire real API + MCP calls, and watch the response in the call inspector. Every action hits a real endpoint — no mocks.

Representative API Explorer view — the live console at /console issues real calls.

Try it

Copy a curl. Get real data back.

The reads are public. Paste the first snippet into any terminal and see live data from the toolbox in production.

reincarnation.psychometric-feed

GET

Per-galaxy psychometric statistics — Cronbach α, IRT summaries, pool distribution. Public; runs in 5 seconds.

curl -sS "https://people-analytics-toolbox.vercel.app/api/spokes/reincarnation/psychometric-feed"

anycomp.market.labor-rate

POST

SERVICE KEY REQUIRED

BLS-derived labor-market rate lookup with geo/period fallback. Service key required (POST endpoint).

curl -sS -X POST "https://people-analytics-toolbox.vercel.app/api/spokes/anycomp/market/labor-rate" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOOLBOX_SERVICE_KEY" \
  -d '{
  "metric": "voluntary_attrition",
  "socCode": "15-1252",
  "geo": "national",
  "period": "2024"
}'

calculus.stats-trend

POST

SERVICE KEY REQUIRED

OLS-classified trend over a periods array. Method auto-selected; CI returned.

curl -sS -X POST "https://people-analytics-toolbox.vercel.app/api/spokes/calculus/stats/trend" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOOLBOX_SERVICE_KEY" \
  -d '{
  "periods": [
    { "period": "2025-Q1", "value": 70 },
    { "period": "2025-Q2", "value": 75 },
    { "period": "2025-Q3", "value": 80 }
  ]
}'

Pick one spoke. Solve one problem this quarter.

Stop rebuilding foundational PA primitives every roadmap cycle. Pick one catalog row (see grid above): psychology, experimentation, segmentation, calculus, forecasting, economics, wage law, graphs, staffing — each exposes contracts you vendor, curls you can replay, MCP tools you audit.

Without the toolbox

Keep gluing tools together. Watch the spend climb. Maintain the workarounds. Lose another quarter to integration glue when you could be doing methodology.

With the toolbox

Replace the patchwork. Compose what you need. Own your contracts. Spend the quarter shipping research, not gluing systems.