diff --git a/README.md b/README.md index 945e9db..d977e1f 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ This repository contains the design specifications and implementation plans for - [m010 — Reputation Signal (v0 advisory)](mechanisms/m010-reputation-signal/) +- [m011 — Marketplace Curation & Quality Signals](mechanisms/m011-marketplace-curation/) diff --git a/docs/MECHANISM_CONSUMERS.md b/docs/MECHANISM_CONSUMERS.md index abd7745..ae269e9 100644 --- a/docs/MECHANISM_CONSUMERS.md +++ b/docs/MECHANISM_CONSUMERS.md @@ -18,3 +18,23 @@ This document maps **mechanism IDs** to known **consumers** (agents, digests, sc - Heartbeat replay runner: `scripts/replay-m010.mjs` (regen-heartbeat) - Heartbeat stub runner: `scripts/stub-run-signal-agent.mjs` (regen-heartbeat) - Heartbeat validator: `scripts/validate-signal-agent.mjs` (regen-heartbeat) + +## m011 — Marketplace Curation & Quality Signals +**Canonical spec** +- `mechanisms/m011-marketplace-curation/SPEC.md` + +**Outputs** +- KPI JSON block schema: `mechanisms/m011-marketplace-curation/schemas/m011_kpi.schema.json` +- Quality score schema: `mechanisms/m011-marketplace-curation/schemas/m011_quality_score.schema.json` +- Collection lifecycle schema: `mechanisms/m011-marketplace-curation/schemas/m011_collection.schema.json` + +**Datasets (deterministic)** +- Replay fixtures: `mechanisms/m011-marketplace-curation/datasets/fixtures/v0_sample.json` +- Collection challenges: `mechanisms/m011-marketplace-curation/datasets/fixtures/v0_collection_sample.json` + +**Known consumers** +- AGENT-003: Autonomous quality scoring, price monitoring, collection monitoring +- KOI MCP: methodology metadata analysis via `resolve_entity` +- Ledger MCP: batch metadata and trade queries +- x/ecocredit: batch, class, project data; marketplace sell orders +- Heartbeat: KPI metrics in weekly digest diff --git a/mechanisms/m011-marketplace-curation/README.md b/mechanisms/m011-marketplace-curation/README.md new file mode 100644 index 0000000..8a9b4b1 --- /dev/null +++ b/mechanisms/m011-marketplace-curation/README.md @@ -0,0 +1,35 @@ +# m011 — Marketplace Curation & Quality Signals + +Quality signals, curated collections, and price discovery for the credit marketplace. Curators stake REGEN to create vetted credit batch collections and earn trade fee shares. AGENT-003 provides automated 7-factor quality scoring. + +## What it outputs + +- **Quality score** (0–1000): 7-factor weighted assessment (project reputation 0.25, class reputation 0.20, vintage freshness 0.15, verification recency 0.15, seller reputation 0.10, price fairness 0.10, additionality confidence 0.05) +- **Confidence** (0–1000): Data availability across 7 input signals +- **KPI block**: Collection counts by status, quality score distribution, trade volume through collections, curation economics, challenge rate + +## What it does not do in v0 + +- No on-chain curation bonds or collection contracts +- No automated trade fee splitting to curators +- No on-chain challenge mechanism +- Agent quality scores are advisory only — published in digest + +## Scoring formula + +``` +score = 0.25 × project_reputation + 0.20 × class_reputation + 0.15 × vintage_freshness + + 0.15 × verification_recency + 0.10 × seller_reputation + 0.10 × price_fairness + + 0.05 × additionality_confidence +``` + +## How to reference + +- **Canonical spec**: `mechanisms/m011-marketplace-curation/SPEC.md` +- **Schemas**: `mechanisms/m011-marketplace-curation/schemas/` +- **Reference implementation**: `mechanisms/m011-marketplace-curation/reference-impl/` + +## Replay datasets + +- `datasets/fixtures/v0_sample.json` — Credit batches across multiple classes with quality scores +- `datasets/fixtures/v0_collection_sample.json` — Collection lifecycle scenarios including challenges diff --git a/mechanisms/m011-marketplace-curation/SPEC.md b/mechanisms/m011-marketplace-curation/SPEC.md new file mode 100644 index 0000000..38de8c1 --- /dev/null +++ b/mechanisms/m011-marketplace-curation/SPEC.md @@ -0,0 +1,284 @@ +# M011 — Marketplace Curation & Quality Signals + +## 0. Header + +| Field | Value | +|-------|-------| +| **Mechanism ID** | m011 | +| **Name** | Marketplace Curation & Quality Signals | +| **Status** | Draft — v0 advisory | +| **Owner** | Regen Network core / community | +| **Scope** | Credit marketplace quality scoring, curated collections, and price discovery | + +--- + +## 1. Problem + +The existing credit marketplace lacks quality differentiation. Buyers cannot easily distinguish high-integrity credits from low-quality ones. Without quality signals, curated collections, or price discovery mechanisms, the marketplace suffers from information asymmetry that depresses prices for high-quality credits and erodes buyer confidence. Curators who invest effort in vetting credits have no economic incentive mechanism. + +--- + +## 2. Target actor and action + +| Actor | Action | +|-------|--------| +| **Curator** | Creates collections of vetted credit batches, bonds REGEN, earns trade fee share | +| **AGENT-003** | Computes quality scores for credit batches; monitors pricing; flags quality violations | +| **Buyer** | Purchases credits through curated collections (higher confidence) | +| **Seller** | Lists credits on marketplace (listing fee) | +| **Challenger** | Disputes curator inclusion of low-quality batches | + +--- + +## 3. Signal definition + +### Quality Score + +The agent produces a quality assessment for each credit batch: + +| Field | Type | Range | Description | +|-------|------|-------|-------------| +| `score` | integer | 0–1000 | 7-factor weighted quality composite | +| `confidence` | integer | 0–1000 | Data availability indicator | +| `factors` | object | — | Individual factor scores | + +--- + +## 4. Evidence inputs + +| Input | Source | Required | +|-------|--------|----------| +| Project reputation score | M010 reputation signal | Preferred | +| Credit class reputation score | M010 reputation signal | Preferred | +| Seller reputation score | M010 reputation signal | Optional | +| Batch start date (vintage) | x/ecocredit batch metadata | Yes | +| Last verification timestamp | Most recent MsgCreateBatch or attestation | Preferred | +| Listing price | x/ecocredit/marketplace sell order | If listed | +| Class price median | AGENT-003 price tracking | Optional | +| Methodology metadata IRI | Credit class metadata | Optional | + +--- + +## 5. Scoring function + +### Quality score + +``` +score = 0.25 × project_reputation + + 0.20 × class_reputation + + 0.15 × vintage_freshness + + 0.15 × verification_recency + + 0.10 × seller_reputation + + 0.10 × price_fairness + + 0.05 × additionality_confidence +``` + +| Factor | Weight | Description | Range | +|--------|--------|-------------|-------| +| `project_reputation` | 0.25 | M010 reputation score for the project | 0–1000 | +| `class_reputation` | 0.20 | M010 reputation score for the credit class | 0–1000 | +| `vintage_freshness` | 0.15 | Linear decay: 1000 at issuance, 0 at 10 years | 0–1000 | +| `verification_recency` | 0.15 | Linear decay: 1000 at last verification, 0 at 3 years | 0–1000 | +| `seller_reputation` | 0.10 | M010 reputation score for the seller address | 0–1000 | +| `price_fairness` | 0.10 | 1000 at class median, decaying by deviation (0 at ±50%) | 0–1000 | +| `additionality_confidence` | 0.05 | Methodology additionality assessment from credit class metadata | 0–1000 | + +All factors are clamped to [0, 1000]. Final score is rounded to the nearest integer. + +### Confidence + +Confidence reflects data availability across seven boolean signals: + +| Signal | Check | +|--------|-------| +| `project_reputation_available` | Project has M010 reputation score | +| `class_reputation_available` | Credit class has M010 reputation score | +| `seller_reputation_available` | Seller has M010 reputation score | +| `vintage_known` | Batch start date is available | +| `verification_date_known` | Last verification timestamp available | +| `price_data_available` | Listing price and class median available | +| `methodology_available` | Credit class methodology metadata accessible | + +`confidence = round(count(true signals) / 7 × 1000)` + +### Price fairness calculation + +``` +median = class_batch_price_median(credit_type, vintage_year) +deviation = |listing_price - median| / median +price_fairness = max(0, round((1.0 - deviation * 2) × 1000)) +``` + +At median → 1000; 25% deviation → 500; ≥50% deviation → 0. + +--- + +## 6. State machine + +### Collection lifecycle + +``` +PROPOSED → ACTIVE → UNDER_REVIEW → ACTIVE (curator wins) + → SUSPENDED (challenger wins) → ACTIVE (top-up) + → CLOSED + → CLOSED (curator exit, no pending challenges) +``` + +| Transition | Trigger | Guard | +|------------|---------|-------| +| → PROPOSED | `curator.create_collection(name, criteria, bond)` | bond ≥ min_curation_bond | +| PROPOSED → ACTIVE | `activation_delay(48h) AND no_challenge` | — | +| ACTIVE → ACTIVE (add) | `curator.add_to_collection(batch_denom)` | meets criteria, quality ≥ min_quality_score | +| ACTIVE → ACTIVE (remove) | `curator.remove_from_collection(batch_denom)` | batch in collection | +| ACTIVE → UNDER_REVIEW | `challenger.challenge_inclusion(evidence) OR agent.flag_violation` | deposit ≥ challenge_deposit | +| UNDER_REVIEW → ACTIVE | `challenge.resolved(CURATOR_WINS)` | challenger deposit slashed | +| UNDER_REVIEW → SUSPENDED | `challenge.resolved(CHALLENGER_WINS)` | batch removed, bond slashed | +| SUSPENDED → ACTIVE | `curator.top_up_bond() AND suspension_expired` | bond ≥ min_curation_bond | +| SUSPENDED → CLOSED | `timeout(top_up_window) OR curator.close()` | remaining bond refunded | +| ACTIVE → CLOSED | `curator.close_collection()` | no pending challenges, unbonding period | + +--- + +## 7. Token flows + +### Collection creation + +``` +Curator ──(curation_bond: 1000 REGEN)──→ Bond Pool +``` + +### Trade through collection + +``` +Buyer ──(purchase_price)──→ Marketplace +Marketplace ──(price − trade_fee − curation_fee)──→ Seller +Marketplace ──(curation_fee: 0.5% of trade)──→ Curator +Marketplace ──(trade_fee)──→ Community Pool (via M013) +``` + +### Challenge resolution — Challenger wins + +``` +Bond Pool ──(bond × slash_percentage)──→ slash + slash ──(50%)──→ Challenger (reward) + slash ──(50%)──→ Community Pool +``` + +### Challenge resolution — Curator wins + +``` +Challenge Deposit ──(100%)──→ Community Pool (challenger loses deposit) +``` + +### Curator exit + +``` +Bond Pool ──(remaining bond)──→ Curator (after unbonding_period, 14 days) +``` + +### Governance parameters + +| Parameter | Default | Range | +|-----------|---------|-------| +| `min_curation_bond` | 1,000 REGEN | 100–10,000 | +| `listing_fee` | 10 REGEN | 0–100 | +| `curation_fee_rate` | 0.5% (50 bps) | 0–2% | +| `challenge_deposit_amount` | 100 REGEN | 10–1,000 | +| `slash_percentage` | 20% of bond | 5–50% | +| `challenge_reward_share` | 50% of slash | 25–75% | +| `activation_delay` | 48 hours | 24h–7d | +| `unbonding_period` | 14 days | 7–30 days | +| `bond_top_up_window` | 7 days | 3–14 days | +| `min_quality_score` | 300 | 100–500 | +| `max_collections_per_curator` | 5 | 1–20 | +| `quality_refresh_interval` | 24 hours | 6h–7d | + +--- + +## 8. Featured batches + +AGENT-003 can recommend batches for featured placement: + +| Criteria | Threshold | +|----------|-----------| +| Quality score | ≥ 800 (top 20%) | +| Pending challenges | None | +| Active sell orders | > 0 | + +Featured status lasts 7 days (renewable). Agent recommends (Layer 1), curator confirms (Layer 2). + +--- + +## 9. Security invariants + +1. **Bond Conservation**: `bond_pool.balance = sum(active_collection_bonds) + sum(pending_refunds)` at all times. +2. **Criteria Enforcement**: Every batch in a collection must satisfy the collection's `CurationCriteria` at addition time; AGENT-003 re-verifies daily for drift. +3. **Curator Authority**: Only the collection curator can add/remove batches; agents cannot modify collection membership directly. +4. **Slash Cap**: Cumulative slashing cannot exceed the curator's total bond. If `bond_remaining < min_curation_bond` after a slash, the collection transitions to SUSPENDED. If the bond is not topped up within `bond_top_up_window`, the collection then transitions to CLOSED. +5. **No Self-Challenge**: Curator cannot challenge own collection; challenger cannot be seller of challenged batch. +6. **Fee Integrity**: Curation fees only on trades through a collection; direct trades incur no curation fee. +7. **Quality Score Immutability**: Scores are append-only (superseded, never edited); full history preserved. +8. **Collection Uniqueness**: Each `(collection_id, batch_denom)` pair is unique; a batch can belong to multiple collections. + +--- + +## 10. Attack model + +| Attack | Mitigation | +|--------|-----------| +| **Low-quality curation** | Challenge mechanism slashes curator bond; AGENT-003 flags quality violations | +| **Sybil curators** | min_curation_bond (1000 REGEN) makes Sybil attacks expensive | +| **Challenge spam** | Challenge deposit (100 REGEN) lost if challenge fails | +| **Price manipulation** | AGENT-003 detects outlier pricing (z-score > 2.5) | +| **Wash trading for fees** | Fee rate is small (0.5%); on-chain detection via AGENT-003 | +| **Quality score gaming** | 7-factor scoring with diverse inputs; immutable score history | +| **Curator front-running** | Activation delay (48h) prevents immediate inclusion of batches | +| **Bond grinding** | max_collections_per_curator limits; unbonding_period prevents rapid cycling | + +--- + +## 11. Integration points + +| System | Integration | +|--------|-------------| +| **M010 Reputation** | Project, class, and seller reputation scores as quality inputs | +| **M013 Fee Routing** | Trade fees follow M013 distribution model | +| **AGENT-003** | Autonomous quality scoring, price monitoring, collection monitoring | +| **x/ecocredit** | Batch metadata (vintage, class, project), marketplace sell orders | +| **x/ecocredit/basket** | Basket token quality scoring (constituent analysis) | +| **KOI MCP** | Methodology metadata analysis, additionality confidence | +| **Ledger MCP** | Bond balance queries, trade volume tracking | +| **Heartbeat** | KPI metrics published in weekly digest | + +--- + +## 12. Acceptance tests + +1. **Collection lifecycle**: Curator bonds → creates collection → adds batches → earns trade fees → closes → bond refunded. +2. **Quality scoring**: AGENT-003 scores batch → score meets criteria → batch eligible for collection inclusion. +3. **Challenge — challenger wins**: Challenger deposits → challenge resolved in challenger's favor → batch removed → curator bond slashed → challenger rewarded. +4. **Challenge — curator wins**: Challenge resolved in curator's favor → challenger deposit slashed to community pool. +5. **Suspension and recovery**: Curator slashed below min bond → collection SUSPENDED → curator tops up bond → collection ACTIVE. +6. **Suspension and closure**: Curator slashed → top_up_window expires → collection CLOSED → remaining bond refunded. +7. **Featured batch**: Quality score ≥ 800, no challenges, active orders → featured for 7 days. +8. **Price fairness**: Listing at median → 1000; listing 50% above median → 0. +9. **Max collections**: Curator at max_collections_per_curator cannot create new collection. + +--- + +## 13. Rollout plan + +| Phase | Scope | Dependencies | +|-------|-------|-------------| +| **v0 (advisory)** | AGENT-003 computes quality scores off-chain; KPI metrics in digest. No on-chain curation bonds or collections. | M010 (reputation), KOI MCP | +| **v1 (on-chain curation)** | CosmWasm `marketplace-curation` contract with collection creation, bond lock, batch inclusion, challenge mechanism. Quality scores submitted on-chain. | M010, AGENT-003, DAO DAO | +| **v2 (full marketplace)** | Curation fee routing via M013, featured batches, collection performance reports, basket token support. | M013, x/ecocredit/basket | + +--- + +## Appendix: Source anchors + +| Document | Section | +|----------|---------| +| `phase-2/2.1-token-utility-mechanisms.md` | M011 Marketplace Curation & Quality Signals (lines 650–908) | +| `phase-3/3.1-smart-contract-specs.md` | CosmWasm Contract: Marketplace Curation (lines 1080–1155) | diff --git a/mechanisms/m011-marketplace-curation/datasets/README.md b/mechanisms/m011-marketplace-curation/datasets/README.md new file mode 100644 index 0000000..3acdf0e --- /dev/null +++ b/mechanisms/m011-marketplace-curation/datasets/README.md @@ -0,0 +1,11 @@ +# m011 replay datasets + +Fixture files for replay testing of m011 (Marketplace Curation & Quality Signals) computations. + +## Files +- `schema.json` — JSON Schema for the replay dataset format. +- `fixtures/v0_sample.json` — Five scored credit batches across carbon (C), biodiversity (BT), and Kasigau (KSH) types, with three curated collections (ACTIVE and CLOSED). Quality scores match reference-impl output. +- `fixtures/v0_collection_sample.json` — Three collections covering challenge lifecycle scenarios: UNDER_REVIEW (pending), SUSPENDED (challenger wins), ACTIVE (curator wins, challenge resolved). + +## Usage +Feed fixture files into `m011_kpi.js` to verify KPI computation. Quality scores in fixtures correspond to `m011_score.js` output for the matching factor inputs. diff --git a/mechanisms/m011-marketplace-curation/datasets/fixtures/v0_collection_sample.json b/mechanisms/m011-marketplace-curation/datasets/fixtures/v0_collection_sample.json new file mode 100644 index 0000000..5494873 --- /dev/null +++ b/mechanisms/m011-marketplace-curation/datasets/fixtures/v0_collection_sample.json @@ -0,0 +1,94 @@ +{ + "mechanism_id": "m011", + "scope": "v0_advisory", + "as_of": "2026-02-18T12:00:00Z", + "scored_batches": [ + { + "batch_denom": "C01-001-20250601-20260101-001", + "credit_type": "C", + "class_id": "C01", + "project_id": "P-regen-001", + "seller": "regen1seller01", + "quality_score": 818, + "confidence": 1000, + "scored_at": "2026-02-17T08:00:00Z" + }, + { + "batch_denom": "C05-001-20240601-20250101-002", + "credit_type": "C", + "class_id": "C05", + "project_id": "P-regen-030", + "seller": "regen1seller06", + "quality_score": 250, + "confidence": 571, + "scored_at": "2026-02-17T08:00:00Z" + }, + { + "batch_denom": "C02-001-20250101-20250601-001", + "credit_type": "C", + "class_id": "C02", + "project_id": "P-regen-010", + "seller": "regen1seller07", + "quality_score": 720, + "confidence": 857, + "scored_at": "2026-02-17T08:00:00Z" + } + ], + "collections": [ + { + "collection_id": "col-c01", + "curator": "regen1curatorch01", + "name": "High-Integrity Carbon", + "bond": { "amount": "2000000000", "denom": "uregen" }, + "members": [ + "C01-001-20250601-20260101-001", + "C05-001-20240601-20250101-002" + ], + "status": "UNDER_REVIEW", + "trade_volume": "8000000000", + "total_rewards": "40000000", + "challenge": { + "challenger": "regen1challenger01", + "batch_denom": "C05-001-20240601-20250101-002", + "reason": "Batch quality score 250 is below collection quality floor of 500; satellite imagery contradicts claimed sequestration area", + "outcome": null + } + }, + { + "collection_id": "col-c02", + "curator": "regen1curatorch02", + "name": "Verified Reforestation", + "bond": { "amount": "1000000000", "denom": "uregen" }, + "members": [ + "C02-001-20250101-20250601-001" + ], + "status": "SUSPENDED", + "trade_volume": "2000000000", + "total_rewards": "10000000", + "challenge": { + "challenger": "regen1challenger02", + "batch_denom": "C02-001-20250101-20250601-001", + "reason": "Verification report found to be outdated; project boundary has changed since last verification", + "outcome": "CHALLENGER_WINS" + } + }, + { + "collection_id": "col-c03", + "curator": "regen1curatorch03", + "name": "Community Carbon Fund", + "bond": { "amount": "1500000000", "denom": "uregen" }, + "members": [ + "C01-001-20250601-20260101-001" + ], + "status": "ACTIVE", + "trade_volume": "5000000000", + "total_rewards": "25000000", + "challenge": { + "challenger": "regen1challenger03", + "batch_denom": "C01-001-20250601-20260101-001", + "reason": "False claim: batch was properly verified and curator selection criteria were met", + "outcome": "CURATOR_WINS" + } + } + ] +} diff --git a/mechanisms/m011-marketplace-curation/datasets/fixtures/v0_sample.json b/mechanisms/m011-marketplace-curation/datasets/fixtures/v0_sample.json new file mode 100644 index 0000000..cfa7fd9 --- /dev/null +++ b/mechanisms/m011-marketplace-curation/datasets/fixtures/v0_sample.json @@ -0,0 +1,98 @@ +{ + "mechanism_id": "m011", + "scope": "v0_advisory", + "as_of": "2026-02-18T12:00:00Z", + "scored_batches": [ + { + "batch_denom": "C01-001-20250601-20260101-001", + "credit_type": "C", + "class_id": "C01", + "project_id": "P-regen-001", + "seller": "regen1seller01", + "quality_score": 818, + "confidence": 1000, + "scored_at": "2026-02-18T08:00:00Z" + }, + { + "batch_denom": "C04-001-20240101-20241231-003", + "credit_type": "C", + "class_id": "C04", + "project_id": "P-regen-042", + "seller": "regen1seller02", + "quality_score": 648, + "confidence": 1000, + "scored_at": "2026-02-18T08:00:00Z" + }, + { + "batch_denom": "BT01-001-20250301-20250901-001", + "credit_type": "BT", + "class_id": "BT01", + "project_id": "P-regen-050", + "seller": "regen1seller03", + "quality_score": 593, + "confidence": 714, + "scored_at": "2026-02-18T08:00:00Z" + }, + { + "batch_denom": "C09-001-20230601-20240101-002", + "credit_type": "C", + "class_id": "C09", + "project_id": "P-regen-099", + "seller": "regen1seller04", + "quality_score": 755, + "confidence": 1000, + "scored_at": "2026-02-18T08:00:00Z" + }, + { + "batch_denom": "KSH01-001-20250101-20250601-001", + "credit_type": "KSH", + "class_id": "KSH01", + "project_id": "P-regen-070", + "seller": "regen1seller05", + "quality_score": 325, + "confidence": 429, + "scored_at": "2026-02-18T08:00:00Z" + } + ], + "collections": [ + { + "collection_id": "col-001", + "curator": "regen1curator01", + "name": "Premium Carbon Credits", + "bond": { "amount": "1000000000", "denom": "uregen" }, + "members": [ + "C01-001-20250601-20260101-001", + "C04-001-20240101-20241231-003", + "C09-001-20230601-20240101-002" + ], + "status": "ACTIVE", + "trade_volume": "15000000000", + "total_rewards": "75000000", + "challenge": null + }, + { + "collection_id": "col-002", + "curator": "regen1curator02", + "name": "Biodiversity & Ecosystem", + "bond": { "amount": "1000000000", "denom": "uregen" }, + "members": [ + "BT01-001-20250301-20250901-001" + ], + "status": "ACTIVE", + "trade_volume": "3000000000", + "total_rewards": "15000000", + "challenge": null + }, + { + "collection_id": "col-003", + "curator": "regen1curator03", + "name": "Closed Collection", + "bond": { "amount": "1000000000", "denom": "uregen" }, + "members": [], + "status": "CLOSED", + "trade_volume": "500000000", + "total_rewards": "2500000", + "challenge": null + } + ] +} diff --git a/mechanisms/m011-marketplace-curation/datasets/schema.json b/mechanisms/m011-marketplace-curation/datasets/schema.json new file mode 100644 index 0000000..3810d8a --- /dev/null +++ b/mechanisms/m011-marketplace-curation/datasets/schema.json @@ -0,0 +1,69 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "m011_dataset_v0", + "title": "m011 replay dataset", + "description": "Replay dataset for m011 Marketplace Curation — credit batch quality scores and curated collection lifecycle records.", + "type": "object", + "required": ["mechanism_id", "scope", "as_of"], + "properties": { + "mechanism_id": { "const": "m011" }, + "scope": { "type": "string" }, + "as_of": { "type": "string", "format": "date-time" }, + "scored_batches": { + "type": "array", + "items": { + "type": "object", + "required": ["batch_denom", "credit_type", "quality_score"], + "properties": { + "batch_denom": { "type": "string" }, + "credit_type": { "type": "string" }, + "class_id": { "type": "string" }, + "project_id": { "type": "string" }, + "seller": { "type": ["string", "null"] }, + "quality_score": { "type": ["integer", "null"], "minimum": 0, "maximum": 1000 }, + "confidence": { "type": ["integer", "null"], "minimum": 0, "maximum": 1000 }, + "scored_at": { "type": "string", "format": "date-time" } + } + } + }, + "collections": { + "type": "array", + "items": { + "type": "object", + "required": ["collection_id", "curator", "name", "bond", "status"], + "properties": { + "collection_id": { "type": "string" }, + "curator": { "type": "string" }, + "name": { "type": "string" }, + "bond": { + "type": "object", + "required": ["amount", "denom"], + "properties": { + "amount": { "type": "string" }, + "denom": { "type": "string" } + } + }, + "members": { + "type": "array", + "items": { "type": "string" } + }, + "status": { + "type": "string", + "enum": ["PROPOSED", "ACTIVE", "SUSPENDED", "UNDER_REVIEW", "CLOSED"] + }, + "trade_volume": { "type": "string" }, + "total_rewards": { "type": "string" }, + "challenge": { + "type": ["object", "null"], + "properties": { + "challenger": { "type": "string" }, + "batch_denom": { "type": "string" }, + "reason": { "type": "string" }, + "outcome": { "type": ["string", "null"], "enum": ["CURATOR_WINS", "CHALLENGER_WINS", null] } + } + } + } + } + } + } +} diff --git a/mechanisms/m011-marketplace-curation/reference-impl/README.md b/mechanisms/m011-marketplace-curation/reference-impl/README.md new file mode 100644 index 0000000..0b0625f --- /dev/null +++ b/mechanisms/m011-marketplace-curation/reference-impl/README.md @@ -0,0 +1,51 @@ +# m011 reference implementation (v0 advisory) + +This folder provides a **canonical computation** for m011 outputs so that different agents/runners +produce consistent numbers. + +## Inputs + +### Scoring (`m011_score.js`) +An input object per credit batch: + +- `batch` — batch metadata: + - `batch_denom` (string) + - `credit_type` (string, e.g., "C", "BT") + - `class_id` (string) + - `project_id` (string) + - `seller` (string, optional) +- `factors` — pre-computed factor scores (each 0–1000): + - `project_reputation` — M010 score for the project + - `class_reputation` — M010 score for the credit class + - `vintage_freshness` — linear decay: 1000 at issuance, 0 at 10 years + - `verification_recency` — linear decay: 1000 at last verification, 0 at 3 years + - `seller_reputation` — M010 score for the seller address + - `price_fairness` — 1000 at median, 0 at ±50% deviation + - `additionality_confidence` — methodology additionality score + - Boolean availability signals: `project_reputation_available`, `class_reputation_available`, `seller_reputation_available`, `vintage_known`, `verification_date_known`, `price_data_available`, `methodology_available` + +### KPI (`m011_kpi.js`) +- `as_of` (ISO-8601 string, Z-suffixed) +- `collections[]` — each with `status`, `bond`, `members`, `trade_volume`, `total_rewards`, `challenge` (optional) +- `scored_batches[]` — each with `quality_score` + +## Outputs + +### Score +- `score` (0–1000) — weighted composite +- `confidence` (0–1000) — data availability (7 signals) +- `factors` — individual factor scores + +Formula: +``` +score = 0.25 × project_reputation + 0.20 × class_reputation + 0.15 × vintage_freshness + + 0.15 × verification_recency + 0.10 × seller_reputation + 0.10 × price_fairness + + 0.05 × additionality_confidence +``` + +### KPI block +- Collection counts by status (active, closed, suspended, under_review) +- Batch scoring stats (scored, avg score, featured count) +- `challenge_rate`, `challenge_outcome_breakdown` +- Curation economics (total bonded/slashed/rewards/volume, avg collection size) +- Quality score distribution diff --git a/mechanisms/m011-marketplace-curation/reference-impl/m011_kpi.js b/mechanisms/m011-marketplace-curation/reference-impl/m011_kpi.js new file mode 100644 index 0000000..1f8d01e --- /dev/null +++ b/mechanisms/m011-marketplace-curation/reference-impl/m011_kpi.js @@ -0,0 +1,97 @@ +export function computeM011KPI({ as_of, collections, scored_batches }) { + const colls = collections ?? []; + const batches = scored_batches ?? []; + + // Collection counts + const collections_active = colls.filter(c => + ["ACTIVE", "PROPOSED"].includes(c.status) + ).length; + const collections_closed = colls.filter(c => c.status === "CLOSED").length; + const collections_suspended = colls.filter(c => c.status === "SUSPENDED").length; + const collections_under_review = colls.filter(c => c.status === "UNDER_REVIEW").length; + + // Batch scoring stats + const batches_scored = batches.length; + const scores = batches.map(b => b.quality_score).filter(s => s != null); + const avg_quality_score = scores.length > 0 + ? Number((scores.reduce((s, q) => s + q, 0) / scores.length).toFixed(1)) + : null; + + const featured_batches = batches.filter(b => + b.quality_score != null && b.quality_score >= 800 + ).length; + + // Challenge stats + const challenged = colls.filter(c => + c.challenge != null && c.challenge.outcome != null + ); + const total_colls = colls.length; + const challenge_rate = total_colls > 0 + ? Number((colls.filter(c => c.challenge != null).length / total_colls).toFixed(4)) + : 0.0; + + const challenge_outcome_breakdown = challenged.length > 0 + ? { + curator_wins: challenged.filter(c => c.challenge.outcome === "CURATOR_WINS").length, + challenger_wins: challenged.filter(c => c.challenge.outcome === "CHALLENGER_WINS").length + } + : null; + + // Curation economics + const bonds = colls.map(c => parseInt(c.bond?.amount ?? "0", 10)).filter(b => b > 0); + const total_bonded = bonds.reduce((s, b) => s + b, 0); + + const DEFAULT_SLASH_PERCENTAGE = 0.20; // governance parameter, see SPEC.md + const slashed_colls = colls.filter(c => + c.challenge?.outcome === "CHALLENGER_WINS" + ); + const total_slashed = slashed_colls.reduce((s, c) => { + const bond = parseInt(c.bond?.amount ?? "0", 10); + return s + Math.round(bond * DEFAULT_SLASH_PERCENTAGE); + }, 0); + + const total_trade_volume = colls.reduce((s, c) => + s + parseInt(c.trade_volume ?? "0", 10), 0 + ); + const total_curator_rewards = colls.reduce((s, c) => + s + parseInt(c.total_rewards ?? "0", 10), 0 + ); + + const member_counts = colls.filter(c => c.members).map(c => c.members.length); + const avg_collection_size = member_counts.length > 0 + ? Number((member_counts.reduce((s, n) => s + n, 0) / member_counts.length).toFixed(1)) + : null; + + // Quality score distribution + const quality_score_distribution = scores.length > 0 + ? { + below_300: scores.filter(s => s < 300).length, + "300_to_599": scores.filter(s => s >= 300 && s < 600).length, + "600_to_799": scores.filter(s => s >= 600 && s < 800).length, + "800_plus": scores.filter(s => s >= 800).length + } + : null; + + return { + mechanism_id: "m011", + scope: "v0_advisory", + as_of, + collections_active, + collections_closed, + collections_suspended, + collections_under_review, + batches_scored, + avg_quality_score, + featured_batches, + challenge_rate, + challenge_outcome_breakdown, + curation_economics: { + total_bonded: String(total_bonded), + total_slashed: String(total_slashed), + total_curator_rewards: String(total_curator_rewards), + total_trade_volume: String(total_trade_volume), + avg_collection_size + }, + quality_score_distribution + }; +} diff --git a/mechanisms/m011-marketplace-curation/reference-impl/m011_score.js b/mechanisms/m011-marketplace-curation/reference-impl/m011_score.js new file mode 100644 index 0000000..41f12a2 --- /dev/null +++ b/mechanisms/m011-marketplace-curation/reference-impl/m011_score.js @@ -0,0 +1,157 @@ +/** + * v0 (advisory): 7-factor weighted composite quality scoring for credit batches. + * + * Factors: + * project_reputation (weight 0.25): M010 reputation for the project + * class_reputation (weight 0.20): M010 reputation for the credit class + * vintage_freshness (weight 0.15): Linear decay from issuance to 10 years + * verification_recency (weight 0.15): Linear decay from last verification to 3 years + * seller_reputation (weight 0.10): M010 reputation for the seller + * price_fairness (weight 0.10): Deviation from class median price + * additionality_confidence (weight 0.05): Methodology additionality assessment + * + * See SPEC.md section 5 for full formula. + * + * @param {Object} opts + * @param {Object} opts.batch - Batch metadata + * @param {Object} opts.factors - Pre-computed factor scores (each 0-1000) + * @returns {{ score: number, confidence: number, factors: Object }} + */ +export function computeM011Score({ batch, factors }) { + const W_PROJECT = 0.25; + const W_CLASS = 0.20; + const W_VINTAGE = 0.15; + const W_VERIFICATION = 0.15; + const W_SELLER = 0.10; + const W_PRICE = 0.10; + const W_ADDITIONALITY = 0.05; + + const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v)); + + const fProject = clamp(factors.project_reputation ?? 0, 0, 1000); + const fClass = clamp(factors.class_reputation ?? 0, 0, 1000); + const fVintage = clamp(factors.vintage_freshness ?? 0, 0, 1000); + const fVerification = clamp(factors.verification_recency ?? 0, 0, 1000); + const fSeller = clamp(factors.seller_reputation ?? 0, 0, 1000); + const fPrice = clamp(factors.price_fairness ?? 0, 0, 1000); + const fAdditionality = clamp(factors.additionality_confidence ?? 0, 0, 1000); + + const score = Math.round( + W_PROJECT * fProject + + W_CLASS * fClass + + W_VINTAGE * fVintage + + W_VERIFICATION * fVerification + + W_SELLER * fSeller + + W_PRICE * fPrice + + W_ADDITIONALITY * fAdditionality + ); + + const confidence = computeConfidence(factors); + + return { + score: clamp(score, 0, 1000), + confidence, + factors: { + project_reputation: fProject, + class_reputation: fClass, + vintage_freshness: fVintage, + verification_recency: fVerification, + seller_reputation: fSeller, + price_fairness: fPrice, + additionality_confidence: fAdditionality + } + }; +} + +/** + * Compute vintage freshness factor. + * Linear decay: 1000 at issuance, 0 at 10 years. + * + * @param {number} ageYears - Years since batch start date + * @returns {number} 0-1000 + */ +export function computeVintageFreshness(ageYears) { + if (ageYears <= 0) return 1000; + if (ageYears >= 10) return 0; + return Math.round((1.0 - ageYears / 10) * 1000); +} + +/** + * Compute verification recency factor. + * Linear decay: 1000 at last verification, 0 at 3 years. + * + * @param {number} ageYears - Years since last verification + * @returns {number} 0-1000 + */ +export function computeVerificationRecency(ageYears) { + if (ageYears <= 0) return 1000; + if (ageYears >= 3) return 0; + return Math.round((1.0 - ageYears / 3) * 1000); +} + +/** + * Compute price fairness factor. + * 1000 at median, 0 at ±50% deviation. + * + * @param {number} listingPrice + * @param {number} medianPrice + * @returns {number} 0-1000 + */ +export function computePriceFairness(listingPrice, medianPrice) { + if (medianPrice <= 0) return 0; + const deviation = Math.abs(listingPrice - medianPrice) / medianPrice; + return Math.max(0, Math.round((1.0 - deviation * 2) * 1000)); +} + +function computeConfidence(factors) { + let available = 0; + const total = 7; + if (factors.project_reputation_available) available++; + if (factors.class_reputation_available) available++; + if (factors.seller_reputation_available) available++; + if (factors.vintage_known !== false) available++; + if (factors.verification_date_known) available++; + if (factors.price_data_available) available++; + if (factors.methodology_available) available++; + return Math.round((available / total) * 1000); +} + +// --- Self-test --- +const isMain = typeof process !== "undefined" && + process.argv[1] && + (process.argv[1].endsWith("m011_score.js") || process.argv[1].endsWith("m011_score")); + +if (isMain) { + const fs = await import("node:fs"); + const path = await import("node:path"); + const url = await import("node:url"); + + const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); + const inputPath = path.join(__dirname, "test_vectors", "vector_v0_sample.input.json"); + const expectedPath = path.join(__dirname, "test_vectors", "vector_v0_sample.expected.json"); + + const input = JSON.parse(fs.readFileSync(inputPath, "utf8")); + const expected = JSON.parse(fs.readFileSync(expectedPath, "utf8")); + + const results = input.batches.map(b => computeM011Score({ + batch: b.batch, + factors: b.factors + })); + + let pass = true; + for (let i = 0; i < results.length; i++) { + const r = results[i]; + const e = expected.scores[i]; + if (r.score !== e.score) { + console.error(`FAIL batch[${i}]: got score=${r.score}, expected score=${e.score}`); + pass = false; + } + } + + if (pass) { + console.log("m011_score self-test: PASS"); + console.log(JSON.stringify({ scores: results }, null, 2)); + } else { + process.exit(1); + } +} diff --git a/mechanisms/m011-marketplace-curation/reference-impl/test_vectors/vector_v0_sample.expected.json b/mechanisms/m011-marketplace-curation/reference-impl/test_vectors/vector_v0_sample.expected.json new file mode 100644 index 0000000..c5507c9 --- /dev/null +++ b/mechanisms/m011-marketplace-curation/reference-impl/test_vectors/vector_v0_sample.expected.json @@ -0,0 +1,69 @@ +{ + "scores": [ + { + "score": 818, + "confidence": 1000, + "factors": { + "project_reputation": 800, + "class_reputation": 750, + "vintage_freshness": 900, + "verification_recency": 850, + "seller_reputation": 700, + "price_fairness": 950, + "additionality_confidence": 800 + } + }, + { + "score": 648, + "confidence": 1000, + "factors": { + "project_reputation": 600, + "class_reputation": 700, + "vintage_freshness": 700, + "verification_recency": 600, + "seller_reputation": 500, + "price_fairness": 800, + "additionality_confidence": 650 + } + }, + { + "score": 593, + "confidence": 714, + "factors": { + "project_reputation": 400, + "class_reputation": 500, + "vintage_freshness": 950, + "verification_recency": 900, + "seller_reputation": 300, + "price_fairness": 600, + "additionality_confidence": 500 + } + }, + { + "score": 755, + "confidence": 1000, + "factors": { + "project_reputation": 900, + "class_reputation": 850, + "vintage_freshness": 500, + "verification_recency": 400, + "seller_reputation": 800, + "price_fairness": 1000, + "additionality_confidence": 900 + } + }, + { + "score": 325, + "confidence": 429, + "factors": { + "project_reputation": 200, + "class_reputation": 300, + "vintage_freshness": 800, + "verification_recency": 200, + "seller_reputation": 100, + "price_fairness": 400, + "additionality_confidence": 300 + } + } + ] +} diff --git a/mechanisms/m011-marketplace-curation/reference-impl/test_vectors/vector_v0_sample.input.json b/mechanisms/m011-marketplace-curation/reference-impl/test_vectors/vector_v0_sample.input.json new file mode 100644 index 0000000..41c7756 --- /dev/null +++ b/mechanisms/m011-marketplace-curation/reference-impl/test_vectors/vector_v0_sample.input.json @@ -0,0 +1,130 @@ +{ + "as_of": "2026-02-18T12:00:00Z", + "batches": [ + { + "batch": { + "batch_denom": "C01-001-20250601-20260101-001", + "credit_type": "C", + "class_id": "C01", + "project_id": "P-regen-001", + "seller": "regen1seller01" + }, + "factors": { + "project_reputation": 800, + "class_reputation": 750, + "vintage_freshness": 900, + "verification_recency": 850, + "seller_reputation": 700, + "price_fairness": 950, + "additionality_confidence": 800, + "project_reputation_available": true, + "class_reputation_available": true, + "seller_reputation_available": true, + "vintage_known": true, + "verification_date_known": true, + "price_data_available": true, + "methodology_available": true + } + }, + { + "batch": { + "batch_denom": "C04-001-20240101-20241231-003", + "credit_type": "C", + "class_id": "C04", + "project_id": "P-regen-042", + "seller": "regen1seller02" + }, + "factors": { + "project_reputation": 600, + "class_reputation": 700, + "vintage_freshness": 700, + "verification_recency": 600, + "seller_reputation": 500, + "price_fairness": 800, + "additionality_confidence": 650, + "project_reputation_available": true, + "class_reputation_available": true, + "seller_reputation_available": true, + "vintage_known": true, + "verification_date_known": true, + "price_data_available": true, + "methodology_available": true + } + }, + { + "batch": { + "batch_denom": "BT01-001-20250301-20250901-001", + "credit_type": "BT", + "class_id": "BT01", + "project_id": "P-regen-050", + "seller": "regen1seller03" + }, + "factors": { + "project_reputation": 400, + "class_reputation": 500, + "vintage_freshness": 950, + "verification_recency": 900, + "seller_reputation": 300, + "price_fairness": 600, + "additionality_confidence": 500, + "project_reputation_available": true, + "class_reputation_available": true, + "seller_reputation_available": false, + "vintage_known": true, + "verification_date_known": true, + "price_data_available": true, + "methodology_available": false + } + }, + { + "batch": { + "batch_denom": "C09-001-20230601-20240101-002", + "credit_type": "C", + "class_id": "C09", + "project_id": "P-regen-099", + "seller": "regen1seller04" + }, + "factors": { + "project_reputation": 900, + "class_reputation": 850, + "vintage_freshness": 500, + "verification_recency": 400, + "seller_reputation": 800, + "price_fairness": 1000, + "additionality_confidence": 900, + "project_reputation_available": true, + "class_reputation_available": true, + "seller_reputation_available": true, + "vintage_known": true, + "verification_date_known": true, + "price_data_available": true, + "methodology_available": true + } + }, + { + "batch": { + "batch_denom": "KSH01-001-20250101-20250601-001", + "credit_type": "KSH", + "class_id": "KSH01", + "project_id": "P-regen-070", + "seller": "regen1seller05" + }, + "factors": { + "project_reputation": 200, + "class_reputation": 300, + "vintage_freshness": 800, + "verification_recency": 200, + "seller_reputation": 100, + "price_fairness": 400, + "additionality_confidence": 300, + "project_reputation_available": false, + "class_reputation_available": true, + "seller_reputation_available": false, + "vintage_known": true, + "verification_date_known": false, + "price_data_available": true, + "methodology_available": false + } + } + ] +} diff --git a/mechanisms/m011-marketplace-curation/schemas/README.md b/mechanisms/m011-marketplace-curation/schemas/README.md new file mode 100644 index 0000000..78a95dd --- /dev/null +++ b/mechanisms/m011-marketplace-curation/schemas/README.md @@ -0,0 +1,14 @@ +# m011 output schemas + +These JSON Schemas define **canonical output shapes** for m011 (Marketplace Curation & Quality Signals) artifacts. + +## Files +- `m011_quality_score.schema.json` — schema for credit batch quality score output (score, confidence, 7-factor breakdown). +- `m011_collection.schema.json` — schema for curated collection lifecycle objects (bond, members, criteria, challenge details). +- `m011_kpi.schema.json` — schema for KPI metrics (collection counts, quality distribution, curation economics, challenge rate). + +## Notes +- These schemas are intended for **validation** and consistency across repos (Heartbeat, agent skills, etc.). +- v0 is advisory-only: schemas describe outputs, not enforcement. +- The `status` field on collections tracks lifecycle state (PROPOSED → ACTIVE → UNDER_REVIEW/SUSPENDED → CLOSED). See SPEC.md section 6. +- Quality scores use 7 weighted factors from diverse data sources (M010 reputation, x/ecocredit metadata, AGENT-003 pricing). diff --git a/mechanisms/m011-marketplace-curation/schemas/m011_collection.schema.json b/mechanisms/m011-marketplace-curation/schemas/m011_collection.schema.json new file mode 100644 index 0000000..6c39ce1 --- /dev/null +++ b/mechanisms/m011-marketplace-curation/schemas/m011_collection.schema.json @@ -0,0 +1,109 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "m011_collection_v0", + "title": "m011 curated collection", + "description": "Schema for an M011 curated collection lifecycle object.", + "type": "object", + "required": [ + "collection_id", + "curator", + "name", + "bond", + "members", + "status" + ], + "properties": { + "collection_id": { + "type": "string", + "description": "Unique identifier for the collection." + }, + "curator": { + "type": "string", + "description": "Bech32 address of the collection curator." + }, + "name": { + "type": "string", + "description": "Human-readable collection name." + }, + "description": { + "type": "string" + }, + "bond": { + "type": "object", + "required": ["amount", "denom"], + "properties": { + "amount": { "type": "string", "pattern": "^[0-9]+$" }, + "denom": { "type": "string" } + } + }, + "bond_remaining": { + "type": "object", + "required": ["amount", "denom"], + "properties": { + "amount": { "type": "string", "pattern": "^[0-9]+$" }, + "denom": { "type": "string" } + } + }, + "criteria": { + "type": "object", + "properties": { + "min_project_reputation": { "type": ["integer", "null"] }, + "min_class_reputation": { "type": ["integer", "null"] }, + "allowed_credit_types": { + "type": "array", + "items": { "type": "string" } + }, + "geographic_scope": { + "type": "array", + "items": { "type": "string" } + }, + "min_vintage_year": { "type": ["integer", "null"] }, + "max_vintage_year": { "type": ["integer", "null"] } + } + }, + "members": { + "type": "array", + "items": { "type": "string" }, + "description": "List of batch denoms in this collection." + }, + "quality_floor": { + "type": "integer", + "minimum": 0, + "maximum": 1000, + "description": "Minimum quality score for batch inclusion." + }, + "fee_share": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Curator's share of trades through collection." + }, + "status": { + "type": "string", + "enum": ["PROPOSED", "ACTIVE", "SUSPENDED", "UNDER_REVIEW", "CLOSED"], + "description": "Current collection lifecycle status." + }, + "trade_volume": { "type": "string", "description": "Cumulative trade volume (uregen)." }, + "total_rewards": { "type": "string", "description": "Cumulative curator rewards (uregen)." }, + "created_at": { "type": "string", "format": "date-time" }, + "activated_at": { "type": ["string", "null"], "format": "date-time" }, + "challenge": { + "type": ["object", "null"], + "properties": { + "challenger": { "type": "string" }, + "batch_denom": { "type": "string" }, + "reason": { "type": "string" }, + "deposit": { + "type": "object", + "properties": { + "amount": { "type": "string" }, + "denom": { "type": "string" } + } + }, + "outcome": { "type": ["string", "null"], "enum": ["CURATOR_WINS", "CHALLENGER_WINS", null] }, + "challenged_at": { "type": "string", "format": "date-time" }, + "resolved_at": { "type": ["string", "null"], "format": "date-time" } + } + } + } +} diff --git a/mechanisms/m011-marketplace-curation/schemas/m011_kpi.schema.json b/mechanisms/m011-marketplace-curation/schemas/m011_kpi.schema.json new file mode 100644 index 0000000..22e7c8f --- /dev/null +++ b/mechanisms/m011-marketplace-curation/schemas/m011_kpi.schema.json @@ -0,0 +1,62 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "m011_kpi_v0", + "title": "m011 KPI block", + "description": "Schema for M011 marketplace curation KPI metrics.", + "type": "object", + "required": [ + "mechanism_id", + "scope", + "as_of", + "collections_active", + "collections_closed", + "collections_suspended", + "batches_scored", + "challenge_rate", + "curation_economics" + ], + "properties": { + "mechanism_id": { "const": "m011" }, + "scope": { "type": "string" }, + "as_of": { "type": "string", "format": "date-time" }, + "collections_active": { "type": "integer", "minimum": 0 }, + "collections_closed": { "type": "integer", "minimum": 0 }, + "collections_suspended": { "type": "integer", "minimum": 0 }, + "collections_under_review": { "type": "integer", "minimum": 0 }, + "batches_scored": { "type": "integer", "minimum": 0 }, + "avg_quality_score": { "type": ["number", "null"] }, + "featured_batches": { "type": "integer", "minimum": 0 }, + "challenge_rate": { "type": "number", "minimum": 0, "maximum": 1 }, + "challenge_outcome_breakdown": { + "type": ["object", "null"], + "properties": { + "curator_wins": { "type": "integer", "minimum": 0 }, + "challenger_wins": { "type": "integer", "minimum": 0 } + } + }, + "curation_economics": { + "type": "object", + "required": ["total_bonded", "total_slashed", "total_curator_rewards", "total_trade_volume"], + "properties": { + "total_bonded": { "type": "string" }, + "total_slashed": { "type": "string" }, + "total_curator_rewards": { "type": "string" }, + "total_trade_volume": { "type": "string" }, + "avg_collection_size": { "type": ["number", "null"] } + } + }, + "quality_score_distribution": { + "type": ["object", "null"], + "properties": { + "below_300": { "type": "integer" }, + "300_to_599": { "type": "integer" }, + "600_to_799": { "type": "integer" }, + "800_plus": { "type": "integer" } + } + }, + "credit_type_breakdown": { + "type": ["object", "null"], + "description": "Count of scored batches by credit type." + } + } +} diff --git a/mechanisms/m011-marketplace-curation/schemas/m011_quality_score.schema.json b/mechanisms/m011-marketplace-curation/schemas/m011_quality_score.schema.json new file mode 100644 index 0000000..cef980f --- /dev/null +++ b/mechanisms/m011-marketplace-curation/schemas/m011_quality_score.schema.json @@ -0,0 +1,78 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "m011_quality_score_v0", + "title": "m011 credit batch quality score", + "description": "Schema for an M011 credit batch quality score output.", + "type": "object", + "required": ["score", "confidence", "factors"], + "properties": { + "score": { + "type": "integer", + "minimum": 0, + "maximum": 1000, + "description": "7-factor weighted composite quality score for a credit batch." + }, + "confidence": { + "type": "integer", + "minimum": 0, + "maximum": 1000, + "description": "Data availability indicator (count of available signals / 7 × 1000)." + }, + "factors": { + "type": "object", + "required": [ + "project_reputation", + "class_reputation", + "vintage_freshness", + "verification_recency", + "seller_reputation", + "price_fairness", + "additionality_confidence" + ], + "properties": { + "project_reputation": { + "type": "integer", + "minimum": 0, + "maximum": 1000, + "description": "M010 reputation score for the project." + }, + "class_reputation": { + "type": "integer", + "minimum": 0, + "maximum": 1000, + "description": "M010 reputation score for the credit class." + }, + "vintage_freshness": { + "type": "integer", + "minimum": 0, + "maximum": 1000, + "description": "Linear decay from issuance (1000) to 10 years (0)." + }, + "verification_recency": { + "type": "integer", + "minimum": 0, + "maximum": 1000, + "description": "Linear decay from last verification (1000) to 3 years (0)." + }, + "seller_reputation": { + "type": "integer", + "minimum": 0, + "maximum": 1000, + "description": "M010 reputation score for the seller address." + }, + "price_fairness": { + "type": "integer", + "minimum": 0, + "maximum": 1000, + "description": "1000 at class median price, 0 at ±50% deviation." + }, + "additionality_confidence": { + "type": "integer", + "minimum": 0, + "maximum": 1000, + "description": "Methodology additionality assessment from credit class metadata." + } + } + } + } +} diff --git a/package.json b/package.json index d174e09..bf7a676 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "agentic-tokenomics", "private": true, + "type": "module", "version": "0.0.0", "scripts": { "verify": "node scripts/verify.mjs",