Marketplace Tables — Clustering, Contribution, Scoring
Overview
The marketplace system enables precedent discovery and sharing through a network effect: groups of related precedents (clusters) become more valuable as multiple tenants contribute. Three tables work together:marketplace_clusters — Derived intelligence from grouped precedentscontribution_scores — Per-tenant contribution metrics to each clustermarketplace_queries — Audit log of marketplace access (credit tracking)Core Concept
Cluster: A deterministic grouping of precedents bycluster_id = "Payer Denial Type LOC".
Example clusters:
"Cigna Frequency Limit IOP" — All frequency limit denials for Cigna IOP
"UHC Medical Necessity PHP" — Medical necessity denials for UHC PHP
"Anthem Level of Care " — All LOC denials for Anthem (LOC = NULL)
Network Effect Gate: Cluster only appears in marketplace after 2+ distinct tenants contribute to it. Prevents single-tenant seed data from appearing.
Credit Economy: Tenants earn credits for contributing precedents, spend credits to query clusters.
Schema Definition
marketplace_clusters
CREATE TABLE marketplace_clusters (
id UUID PRIMARY KEY,
-- Cluster Identity (deterministic grouping)
cluster_id TEXT NOT NULL UNIQUE, -- "Payer Denial Type LOC" or "Payer Denial Type "
payer TEXT NOT NULL,
denial_type TEXT NOT NULL,
loc TEXT, -- NULL if non-LOC denial
-- Maturity Tier (network effect indicator)
maturity_tier TEXT,
-- Embryonic (1 contributor)
-- Emerging (2-3 contributors, <10 precedents)
-- Mature (4+ contributors, 10+ precedents, stable outcomes)
-- Canonical (20+ precedents, 70%+ win rate, <7 days avg resolution)
-- Aggregate Metrics
ipo_count INTEGER DEFAULT 0, -- Total precedents in cluster
contributor_count INTEGER DEFAULT 0, -- Distinct tenants who contributed
-- Outcome Analytics
win_rate DECIMAL(5, 2), -- % of precedents with outcome="Won"
avg_resolution_days DECIMAL(8, 2), -- Average days to resolution
-- Evidence Synthesis (JSONB)
top_evidence_types JSONB,
-- Shape: { "clinical_notes": 85, "psychiatric_evaluation": 78, "gaf_score": 92 }
narrative_patterns JSONB,
-- Shape: { "functional_impact": 0.85, "evidence_combination": 0.72, "escalation_path": 0.40 }
-- Composite Evidence Kit (merged from all precedents)
evidence_kit_composite JSONB,
-- Shape: { "required": [...], "recommended": [...], "optional": [...] }
-- Drift Detection
drift_signals JSONB,
-- Shape: { "outcome_trending_down": false, "resolution_time_increasing": false }
-- Metadata
last_refreshed TIMESTAMPTZ DEFAULT NOW(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
-- Indexes
INDEX idx_cluster_payer (payer, denial_type, loc),
INDEX idx_cluster_maturity (maturity_tier),
INDEX idx_cluster_refresh (last_refreshed DESC),
INDEX idx_cluster_tenant (tenant_id)
);
contribution_scores
CREATE TABLE contribution_scores (
id UUID PRIMARY KEY,
-- Who & Where
tenant_id UUID NOT NULL REFERENCES tenants(id),
cluster_id TEXT NOT NULL REFERENCES marketplace_clusters(cluster_id),
-- Contribution Metrics
ipo_count INTEGER DEFAULT 0, -- How many precedents this tenant added
gap_score DECIMAL(8, 2) DEFAULT 0, -- Gap coverage in cluster (0–100)
outcome_bonus DECIMAL(8, 2) DEFAULT 0, -- +X for high-quality outcomes
-- Total Score (for ranking contributors)
total_score DECIMAL(10, 2) GENERATED ALWAYS AS (ipo_count 10 + gap_score + outcome_bonus) STORED,
-- Metadata
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(tenant_id, cluster_id),
INDEX idx_contribution_tenant (tenant_id, total_score DESC),
INDEX idx_contribution_cluster (cluster_id, total_score DESC)
);
marketplace_queries
CREATE TABLE marketplace_queries (
id UUID PRIMARY KEY,
-- Access Log
tenant_id UUID NOT NULL REFERENCES tenants(id),
cluster_id TEXT NOT NULL, -- cluster_id queried
query_type TEXT, -- "view_cluster", "export", "search"
-- Credit Ledger Integration
credits_spent DECIMAL(10, 2),
receipt_hash TEXT, -- Hash of credit_ledger entry
-- Metadata
queried_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
INDEX idx_query_tenant_cluster (tenant_id, cluster_id, queried_at DESC),
INDEX idx_query_cluster (cluster_id, queried_at DESC)
);
Cluster Lifecycle
1. Initial Contribution (Embryonic)
When tenant A adds first precedent to cluster:
If marketplace_clusters row doesn't exist: CREATE (maturity_tier = "Embryonic")
Add to contribution_scores with ipo_count=1
Row NOT visible in marketplace yet (gate: <2 contributors)
INSERT INTO marketplace_clusters (cluster_id, payer, denial_type, loc, contributor_count, ipo_count)
VALUES ('Cigna Frequency Limit IOP', 'Cigna', 'Frequency Limit', 'IOP', 1, 1);
INSERT INTO contribution_scores (tenant_id, cluster_id, ipo_count)
VALUES ('tenant-a-uuid', 'Cigna Frequency Limit IOP', 1);
2. Network Effect Gate (Emerging)
When tenant B adds second precedent:
Increment contributor_count to 2
Update maturity_tier to "Emerging"
Cluster now appears in marketplace
UPDATE marketplace_clusters
SET contributor_count = 2, maturity_tier = 'Emerging'
WHERE cluster_id = 'Cigna Frequency Limit IOP';
INSERT INTO contribution_scores (tenant_id, cluster_id, ipo_count)
VALUES ('tenant-b-uuid', 'Cigna Frequency Limit
IOP', 1);
3. Maturity Progression (Mature/Canonical)
As cluster grows:Contribution Scoring
gap_score (0–100)
Measures what this tenant uniquely contributed:
ipo_count / total_cluster_ipo_count 100outcome_bonus
Added for high-quality contributions:
total_score
Ranks contributors per cluster:
total_score = (ipo_count 10) + gap_score + outcome_bonus
Example:
Query Patterns
Get Marketplace Clusters (Browse)
SELECT cluster_id, payer, denial_type, loc, maturity_tier, ipo_count, win_rate
FROM marketplace_clusters
WHERE tenant_id = $1 AND maturity_tier IN ('Emerging', 'Mature', 'Canonical')
ORDER BY ipo_count DESC
LIMIT 50;
View Cluster Profile
SELECT
c.cluster_id, c.payer, c.denial_type, c.loc,
c.maturity_tier, c.ipo_count, c.win_rate, c.avg_resolution_days,
c.top_evidence_types, c.narrative_patterns,
COUNT(DISTINCT s.tenant_id) AS contributor_count
FROM marketplace_clusters c
LEFT JOIN contribution_scores s ON s.cluster_id = c.cluster_id
WHERE c.cluster_id = $1
GROUP BY c.id;
Get Top Contributors to Cluster
SELECT tenant_id, ipo_count, gap_score, total_score
FROM contribution_scores
WHERE cluster_id = $1
ORDER BY total_score DESC
LIMIT 10;
Find Clusters I Can Access (Credit Check)
-- Step 1: Get tenant's credit balance
SELECT SUM(amount) AS balance
FROM credit_ledger
WHERE tenant_id = $1
GROUP BY tenant_id;
-- Step 2: Get queryable clusters (2+ contributors)
SELECT c.cluster_id, c.ipo_count, c.maturity_tier
FROM marketplace_clusters c
WHERE c.contributor_count >= 2
ORDER BY c.ipo_count DESC
LIMIT 25;
Maturity Tier Calculation
Refresh nightly or on new precedent:UPDATE marketplace_clusters
SET maturity_tier = CASE
WHEN contributor_count = 1 THEN 'Embryonic'
WHEN contributor_count <= 3 AND ipo_count < 10 THEN 'Emerging'
WHEN contributor_count >= 4 AND ipo_count >= 10 AND win_rate > 50 THEN 'Mature'
WHEN ipo_count >= 20 AND win_rate > 70 AND avg_resolution_days < 7 THEN 'Canonical'
ELSE 'Emerging'
END
WHERE last_refreshed < NOW() - INTERVAL '1 day';
Credit Ledger Integration
When a tenant queries a cluster:SELECT SUM(amount) FROM credit_ledger WHERE tenant_id = $1;
cost = 1 + (ipo_count / 10 0.5)INSERT INTO credit_ledger (event_type='query_burn', amount=-cost, reference_type='marketplace_cluster')INSERT INTO marketplace_queries (cluster_id, credits_spent, receipt_hash)Drift Detection
drift_signals flags clusters where metrics are trending negatively:
{
"outcome_trending_down": false, // Win rate decreasing month-over-month
"resolution_time_increasing": false, // Avg resolution time > 2x baseline
"contributor_churn": false, // Contributors leaving (contributor_count decreasing)
"evidence_effectiveness_declining": false // New precedents have lower win rate
}
Recalculate on precedent update:
-- Example: Check if win rate is declining
SELECT
AVG(CASE WHEN outcome='Won' THEN 1 ELSE 0 END) AS current_win_rate,
LAG(AVG(CASE WHEN outcome='Won' THEN 1 ELSE 0 END))
OVER (ORDER BY DATE(created_at)) AS prior_month_rate
FROM precedent_objects
WHERE cluster_id = $1
AND created_at > NOW() - INTERVAL '60 days'
GROUP BY DATE(created_at);
Compliance Notes
Multi-Tenancy:marketplace_clusters has tenant_id (but represents aggregate across all tenants in that tenant's network)has_sud_content = true if any precedent in cluster has data_origin_type='SUD'marketplace_queries with timestamp and tenantSee also
Code reference
src/models/marketplace.ts — Query helpers (clustering, maturity tiers, scoring)src/services/marketplace.ts — Cluster refresh logic, drift detection, tier calculationsrc/routes/marketplace.ts — API endpoints (browse, profile, query)src/db/marketplace-schema.sql — Table definitions