From 7e802a0ae3c0dfa2b9bc8d3fafa8922e9bbca6fc Mon Sep 17 00:00:00 2001 From: Christian Shearer Date: Wed, 18 Feb 2026 12:38:41 -0800 Subject: [PATCH 1/2] Add m008 Data Attestation Bonding mechanism spec Adds full mechanism spec for m008 following the m010 pattern: - SPEC.md with 13 sections covering bond lifecycle, Arbiter DAO resolution, type-based bond schedule, and 4-factor quality scoring - JSON schemas for attestation lifecycle, quality score output, and KPI metrics - Reference implementation (m008_score.js, m008_kpi.js) with deterministic self-test - Test vectors and replay dataset fixtures including challenge scenarios - Updates MECHANISM_CONSUMERS.md with m008 consumer mappings Co-Authored-By: Claude Opus 4.6 --- README.md | 1 + docs/MECHANISM_CONSUMERS.md | 18 ++ mechanisms/m008-attestation-bonding/README.md | 32 ++ mechanisms/m008-attestation-bonding/SPEC.md | 298 ++++++++++++++++++ .../datasets/README.md | 11 + .../fixtures/v0_challenge_sample.json | 103 ++++++ .../datasets/fixtures/v0_sample.json | 67 ++++ .../datasets/schema.json | 54 ++++ .../reference-impl/README.md | 42 +++ .../reference-impl/m008_kpi.js | 66 ++++ .../reference-impl/m008_score.js | 142 +++++++++ .../vector_v0_sample.expected.json | 54 ++++ .../test_vectors/vector_v0_sample.input.json | 95 ++++++ .../schemas/README.md | 14 + .../schemas/m008_attestation.schema.json | 78 +++++ .../schemas/m008_kpi.schema.json | 56 ++++ .../schemas/m008_quality_score.schema.json | 67 ++++ package.json | 1 + 18 files changed, 1199 insertions(+) create mode 100644 mechanisms/m008-attestation-bonding/README.md create mode 100644 mechanisms/m008-attestation-bonding/SPEC.md create mode 100644 mechanisms/m008-attestation-bonding/datasets/README.md create mode 100644 mechanisms/m008-attestation-bonding/datasets/fixtures/v0_challenge_sample.json create mode 100644 mechanisms/m008-attestation-bonding/datasets/fixtures/v0_sample.json create mode 100644 mechanisms/m008-attestation-bonding/datasets/schema.json create mode 100644 mechanisms/m008-attestation-bonding/reference-impl/README.md create mode 100644 mechanisms/m008-attestation-bonding/reference-impl/m008_kpi.js create mode 100644 mechanisms/m008-attestation-bonding/reference-impl/m008_score.js create mode 100644 mechanisms/m008-attestation-bonding/reference-impl/test_vectors/vector_v0_sample.expected.json create mode 100644 mechanisms/m008-attestation-bonding/reference-impl/test_vectors/vector_v0_sample.input.json create mode 100644 mechanisms/m008-attestation-bonding/schemas/README.md create mode 100644 mechanisms/m008-attestation-bonding/schemas/m008_attestation.schema.json create mode 100644 mechanisms/m008-attestation-bonding/schemas/m008_kpi.schema.json create mode 100644 mechanisms/m008-attestation-bonding/schemas/m008_quality_score.schema.json diff --git a/README.md b/README.md index 945e9db..da259cf 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ This repository contains the design specifications and implementation plans for +- [m008 — Data Attestation Bonding (v0 advisory)](mechanisms/m008-attestation-bonding/) - [m010 — Reputation Signal (v0 advisory)](mechanisms/m010-reputation-signal/) diff --git a/docs/MECHANISM_CONSUMERS.md b/docs/MECHANISM_CONSUMERS.md index abd7745..fcf00aa 100644 --- a/docs/MECHANISM_CONSUMERS.md +++ b/docs/MECHANISM_CONSUMERS.md @@ -18,3 +18,21 @@ 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) + +## m008 — Data Attestation Bonding +**Canonical spec** +- `mechanisms/m008-attestation-bonding/SPEC.md` + +**Outputs** +- KPI JSON block schema: `mechanisms/m008-attestation-bonding/schemas/m008_kpi.schema.json` +- Quality score schema: `mechanisms/m008-attestation-bonding/schemas/m008_quality_score.schema.json` +- Attestation lifecycle schema: `mechanisms/m008-attestation-bonding/schemas/m008_attestation.schema.json` + +**Datasets (deterministic)** +- Replay fixtures: `mechanisms/m008-attestation-bonding/datasets/fixtures/v0_sample.json` +- Challenge scenarios: `mechanisms/m008-attestation-bonding/datasets/fixtures/v0_challenge_sample.json` + +**Known consumers** +- Heartbeat character: `attestation-agent` (regen-heartbeat, planned) +- KOI MCP: attestation quality lookups via `resolve_entity` / `get_entity_documents` +- Ledger MCP: bond balance queries via `get_balance` / `get_all_balances` diff --git a/mechanisms/m008-attestation-bonding/README.md b/mechanisms/m008-attestation-bonding/README.md new file mode 100644 index 0000000..0a17c8c --- /dev/null +++ b/mechanisms/m008-attestation-bonding/README.md @@ -0,0 +1,32 @@ +# m008 — Data Attestation Bonding (v0 advisory) + +m008 creates economic incentives for high-quality ecological data attestations by requiring attesters to **bond REGEN tokens** that can be slashed for false or misleading claims. Attestation types include project boundaries, baseline measurements, credit issuance claims, and methodology validations, each with risk-proportional bond requirements. + +## What it outputs +- An **attestation quality score** (0–1000) per attestation, based on bond adequacy, attester reputation, evidence completeness, and attestation type risk. +- An **attestation lifecycle** tracking state transitions from BONDED through ACTIVE, CHALLENGED, to RELEASED/RESOLVED_VALID/SLASHED. +- **KPI metrics**: attestations submitted, challenge rate, resolution outcomes, average bond amount, slashing rate. + +## What it does not do (v0) +- No on-chain bond escrow. Bond requirements are published as guidelines only. +- No on-chain challenge deposits or arbiter resolution. Challenge workflow is admin-driven. +- No automated slashing. Quality scores are advisory/informational only. +- No Arbiter DAO integration (v1 uses DAO DAO subDAO). + +## How to reference +- Canonical spec: `mechanisms/m008-attestation-bonding/SPEC.md` +- State machine: SPEC.md section 6 +- Bond schedule: SPEC.md section 7 (ProjectBoundary 500, Baseline 1000, CreditIssuance 2000, Methodology 5000 REGEN) +- Scoring function: SPEC.md section 5 (4-factor weighted composite) +- Other mechanisms may treat m008 attestation status as an input (e.g., M009 service escrow, M001-ENH class approval). + +## Replay datasets +See `datasets/` for deterministic fixtures used to generate non-zero KPI outputs without MCP. +- `v0_sample.json` — attestations covering full lifecycle (active, released, challenged, resolved) +- `v0_challenge_sample.json` — challenge scenarios with varied resolution outcomes + +## Schemas +Canonical JSON schemas for m008 outputs live in `schemas/`. +- `m008_attestation.schema.json` — attestation lifecycle objects with bond and status +- `m008_quality_score.schema.json` — attestation quality score output +- `m008_kpi.schema.json` — KPI metrics (attestations, challenges, bond economics) diff --git a/mechanisms/m008-attestation-bonding/SPEC.md b/mechanisms/m008-attestation-bonding/SPEC.md new file mode 100644 index 0000000..9f52a67 --- /dev/null +++ b/mechanisms/m008-attestation-bonding/SPEC.md @@ -0,0 +1,298 @@ +# m008 — Data Attestation Bonding (SPEC) + +## 0. Header +- **ID:** m008 +- **Name:** Data Attestation Bonding +- **Status:** draft (advisory-only v0) +- **Owner:** (unset) +- **Last updated:** 2026-02-18 +- **Scope:** **v0 advisory** (bond schedule computation + quality signal publication only; no on-chain escrow or slashing in v0) + +## 1. Problem +The Regen ecosystem relies on ecological data attestations (project boundaries, baseline measurements, credit issuance claims, methodology validations) to back real-world environmental credits. Currently there is no economic skin-in-the-game for attesters: anyone can make claims without risking capital if those claims prove false or misleading. This creates: + +1. **No deterrent for low-quality data**: Attesters face no economic consequence for inaccurate claims. +2. **No incentive to challenge**: Without rewards, detecting and reporting bad attestations is a thankless task. +3. **No risk-proportional bonding**: High-value attestations (e.g., credit issuance claims) carry the same zero bond as trivial ones. +4. **No formalized dispute resolution**: Contested attestations have no structured resolution pathway. +5. **No attester track record**: No mechanism links attestation quality to future bonding requirements. + +## 2. Target actor and action +- **Actors:** attester (entity making ecological claims), challenger (entity disputing an attestation), Arbiter DAO (decentralized resolution body via M008 subDAO), beneficiary (project receiving attestation), admin (config authority in v0). +- **Action being evaluated (one action):** a **bonded attestation** submitted by an attester, backed by locked REGEN tokens, where the bond is at risk of slashing if the attestation is proven invalid. +- **Event source:** `CreateAttestation` submissions (v1 on-chain via CosmWasm) or equivalent off-chain intake (v0 advisory). Challenges via `ChallengeAttestation`. + +## 3. Signal definition +- **Signal name:** Attestation Bond Status +- **Unit:** bond amount (REGEN), attestation quality score (0–1000) +- **Directionality:** higher bond = higher confidence; higher quality score = better +- **Granularity:** per attestation (`attestation_id`) +- **Attestation types:** `ProjectBoundary`, `BaselineMeasurement`, `CreditIssuanceClaim`, `MethodologyValidation` + +## 4. Evidence inputs + +| Input | Source | Fields | Validity rules | Anti-spoof assumptions | Refresh cadence | +|---|---|---|---|---|---| +| Attestation document | KOI / x/data | `attestation_iri`, resolved content | IRI must resolve; content must match attestation type requirements | KOI IRI resolution is trusted; hash integrity verified | On submission | +| Bond amount | Regen bank/escrow | `bond.amount`, `bond.denom` | Must meet `min_bond[attestation_type]`; denom must be `uregen` | On-chain balances are authoritative | On submission | +| Attester identity | Regen Ledger | `attester` address | Valid bech32 address with on-chain history | Chain identity is not spoofable | On submission | +| Challenge evidence | KOI / external | `evidence_iri`, challenger rationale | IRI must resolve; evidence must be relevant to attestation type | Evidence integrity verified by hash; relevance assessed by Arbiter DAO | On challenge submission | +| Beneficiary identity | Regen Ledger | `beneficiary` address (optional) | Valid bech32 if provided | Chain identity trusted | On submission | +| Attester reputation | M010 signal store | M010 score for attester address | Score queried from M010 `(Address, attester, attestation_quality)` | M010 signal store trusted | On submission + periodic | + +## 5. Scoring function + +### 5.1 Attestation quality score + +The quality score for an attestation is computed as a function of bond adequacy, attester reputation, and evidence completeness: + +``` +quality_score = (w_bond × f_bond) + (w_reputation × f_reputation) + (w_evidence × f_evidence) + (w_type × f_type) +``` + +Where: +- `w_bond = 0.3` — Bond adequacy relative to minimum +- `w_reputation = 0.3` — Attester M010 reputation +- `w_evidence = 0.25` — Evidence document completeness +- `w_type = 0.15` — Attestation type risk factor + +### 5.2 Factor definitions + +#### Bond adequacy (`f_bond`, 0–1000) +``` +f_bond = min(1000, (bond_amount / min_bond[type]) × 500) +``` +Bonds at minimum get 500; bonds at 2× minimum get 1000 (capped). + +#### Attester reputation (`f_reputation`, 0–1000) +- If M010 score exists: `f_reputation = m010_score` +- If no M010 score: `f_reputation = 300` (cautious default, below neutral) +- Track record adjustment: `f_reputation = 0.7 × m010_score + 0.3 × (unchallenged_rate × 1000)` + +#### Evidence completeness (`f_evidence`, 0–1000) +Binary checklist by attestation type: +- IRI resolvable: 250 +- Content matches declared type: 250 +- Supporting documentation provided: 250 +- Cross-references to existing data (KOI links): 250 + +#### Attestation type risk (`f_type`, 0–1000) +Higher-risk attestation types get higher scores (reflecting the higher bond requirement and scrutiny): +| Type | f_type | Rationale | +|---|---|---| +| MethodologyValidation | 1000 | Highest bond, longest lock | +| CreditIssuanceClaim | 800 | High value at risk | +| BaselineMeasurement | 600 | Moderate risk | +| ProjectBoundary | 400 | Lower risk | + +### 5.3 Confidence + +``` +confidence = (data_available_factors / total_factors) × 1000 +``` + +Factors: M010 reputation exists, IRI resolvable, attester has prior attestations, attestation type recognized. + +## 6. State machine + +``` +States: {BONDED, ACTIVE, CHALLENGED, RESOLVED_VALID, RESOLVED_INVALID, RELEASED, SLASHED} + +Initial → BONDED + trigger: attester.create_attestation(type, iri, bond) + guard: bond >= min_bond[attestation_type]; attestation_type is valid + action: bond_pool.lock(bond), attestation.create(status=BONDED) + +BONDED → ACTIVE + trigger: activation_delay_passed(48h) AND no_challenge_submitted + guard: attestation still in BONDED state + action: attestation.activate() + note: 48h delay allows early challenges before activation + +BONDED → CHALLENGED + trigger: challenger.submit_challenge(attestation_id, evidence_iri, deposit) + guard: deposit >= bond × challenge_deposit_ratio; within challenge_window + action: challenge_pool.lock(deposit), attestation.status = CHALLENGED + note: early challenge during activation delay + +ACTIVE → CHALLENGED + trigger: challenger.submit_challenge(attestation_id, evidence_iri, deposit) + guard: deposit >= bond × challenge_deposit_ratio; within challenge_window + action: challenge_pool.lock(deposit), attestation.status = CHALLENGED + +CHALLENGED → RESOLVED_VALID + trigger: arbiter_dao.vote(VALID) OR admin.resolve(VALID) (v0) + guard: quorum_met (v1), resolution_authority_verified + action: attester.receive(bond + challenge_deposit - arbiter_fee) + attestation.status = RESOLVED_VALID + +CHALLENGED → RESOLVED_INVALID + trigger: arbiter_dao.vote(INVALID) OR admin.resolve(INVALID) (v0) + guard: quorum_met (v1), resolution_authority_verified + action: challenger.receive(bond × 0.5 + challenge_deposit - arbiter_fee) + community_pool.receive(bond × 0.5) + attestation.status = SLASHED + +ACTIVE → RELEASED + trigger: lock_period_expired AND no_active_challenge + guard: env.block.time > lock_expires_at + action: attester.receive(bond) + attestation.status = RELEASED + +Terminal states: RESOLVED_VALID, RESOLVED_INVALID/SLASHED, RELEASED +Note: RESOLVED_VALID attestations remain valid references; SLASHED attestations are permanently invalidated. +``` + +## 7. Token flows + +### Bond schedule by attestation type + +| Attestation Type | Min Bond (REGEN) | Lock Period | Challenge Window | +|---|---|---|---| +| ProjectBoundary | 500 | 90 days | 60 days | +| BaselineMeasurement | 1,000 | 180 days | 120 days | +| CreditIssuanceClaim | 2,000 | 365 days | 300 days | +| MethodologyValidation | 5,000 | 730 days | 600 days | + +### Flow diagram + +``` +┌─────────────┐ bond ┌──────────────┐ +│ Attester │ ────(B)────────→ │ Attestation │ +└─────────────┘ │ Bond Pool │ + └──────────────┘ + │ + ┌────────────────────────────────┼────────────────────────────────┐ + │ UNCHALLENGED (lock_period) │ CHALLENGED │ + ▼ ▼ +┌──────────────┐ ┌──────────────┐ +│ Attester │ │ Arbiter DAO │ +│ (bond │ │ Resolution │ +│ refund) │ └──────┬───────┘ +└──────────────┘ │ + ┌────────────────┼────────────────┐ + │ ATTESTER WINS │ CHALLENGER WINS│ + ▼ ▼ + ┌──────────────┐ ┌──────────────┐ + │ Attester: │ │ Challenger: │ + │ bond+deposit │ │ 50% bond + │ + │ - arb_fee │ │ deposit - │ + └──────────────┘ │ arb_fee │ + │ Community: │ + │ 50% bond │ + └──────────────┘ +``` + +### Invariants +- `bond_pool.balance = sum(active_bonds) + sum(challenge_deposits) - sum(disbursements)` +- Challenge deposit ratio: 10% of bond amount (1000 basis points) +- Arbiter fee: 5% of bond amount (500 basis points) +- Slash distribution: 50% to challenger, 50% to community pool + +### v0 (advisory) +No bond escrow is enforced. Quality scores and attestation lifecycle are tracked off-chain. Existing `x/data` IRI anchoring applies. Bond requirements are published as guidelines. + +## 8. PoA variant (v2) +Under M014, Arbiter DAO composition may shift: +- Authority validators may serve as default arbiters for low-value attestations (< 1000 REGEN bond) +- High-value attestations (>= 2000 REGEN) require full Arbiter DAO vote +- Validator arbiters use equal-weight voting (consistent with M014 parity) + +## 9. Security invariants + +1. **Bond coverage**: `attestation.value_at_risk <= bond × coverage_ratio` — bond must be proportional to the ecological claim value. +2. **Challenge skin-in-game**: Challengers must deposit `bond × challenge_deposit_ratio` to prevent frivolous challenges. Deposit forfeited if challenge fails. +3. **Arbiter neutrality**: Arbiters cannot be the attester, challenger, or beneficiary. v1 adds 90-day transaction history conflict check. +4. **Slash distribution**: Slashed bonds split 50/50 between challenger reward and community pool. No other distribution ratio allowed. +5. **Lock period enforcement**: Bonds cannot be released before `lock_expires_at` regardless of attestation status (except via challenge resolution). +6. **Single challenge**: Only one active challenge per attestation at a time. Re-challenge allowed after resolution. +7. **Resolution finality**: Once resolved (VALID or INVALID), the resolution cannot be changed. Appeal to governance is a separate process. + +## 10. Attack model + +### 10.1 Frivolous attestations +**Attack**: Submit low-quality attestations with minimum bond, hoping they go unchallenged. +**Mitigation**: Challenge window is proportional to risk (60–600 days). Quality scoring flags low-evidence attestations in digest. Bond locked for full lock period regardless. Attester reputation (M010) degrades with challenged/invalidated attestations. + +### 10.2 Challenge spam +**Attack**: File frivolous challenges to lock attester bonds and extract arbiter fees. +**Mitigation**: 10% deposit requirement creates economic cost. Failed challenges forfeit deposit to attester. Challenger reputation tracked via M010. + +### 10.3 Arbiter collusion +**Attack**: Arbiter DAO members collude with challenger to slash bonds. +**Mitigation**: v0: admin resolution with audit trail. v1: Arbiter DAO with random arbiter selection, conflict checks (no relationship to parties within 90 days), and governance appeal. DAO DAO quorum requirements (51% majority, 15% quorum). + +### 10.4 Bond grinding +**Attack**: Repeatedly submit and release attestations to game the quality scoring system. +**Mitigation**: Lock periods (90–730 days) make churning expensive in opportunity cost. Quality score tracks `unchallenged_rate` — successful releases improve reputation only gradually. + +### 10.5 Evidence manipulation +**Attack**: Submit attestation with valid-looking IRI that points to fabricated data. +**Mitigation**: KOI cross-referencing against existing data. Challenge mechanism allows community to dispute. v1: IRI content hash verification on-chain via `x/data`. + +## 11. Integration points + +- **KOI MCP (knowledge):** Resolve attestation IRIs and challenge evidence IRIs. Cross-reference attestation claims against existing ecosystem data. Provide context for arbiter review. +- **Ledger MCP (chain data):** Verify attester balances for bond adequacy. Query `x/data` for existing attestation anchors. Verify beneficiary addresses. +- **M010 (Reputation Signal):** Query attester reputation for quality scoring. Attestation outcomes (valid release, challenged, slashed) feed back as M010 inputs. +- **M009 (Service Escrow):** Service providers (verifiers, MRV operators) may bond attestations related to their service deliverables. +- **M001-ENH (Credit Class Approval):** Methodology validation attestations may be required as part of credit class applications. +- **Arbiter DAO (DAO DAO):** v1 challenge resolution uses DAO DAO infrastructure. Arbiter selection, voting, and fee distribution. +- **x/data module:** On-chain IRI anchoring for attestation documents. + +## 12. Acceptance tests + +### Attestation lifecycle +1) **Full lifecycle (happy path):** Attester bonds 1000 REGEN for BaselineMeasurement. 48h activation delay passes. No challenge within 120-day window. Lock period (180 days) expires. Bond released to attester. +2) **Insufficient bond:** Attester submits ProjectBoundary with 200 REGEN (< 500 minimum). Rejected with `ErrInsufficientBond`. +3) **Unknown attestation type:** Attester submits with type "InvalidType". Rejected with `ErrUnknownAttestationType`. +4) **Activation delay:** Attestation in BONDED state does not activate until 48h passes. + +### Challenge workflow +5) **Challenge during activation:** Challenger files challenge during 48h activation delay. Attestation transitions from BONDED to CHALLENGED (never reaches ACTIVE). +6) **Challenge active attestation:** Challenger deposits 100 REGEN (10% of 1000 bond) against active BaselineMeasurement. Attestation transitions to CHALLENGED. +7) **Insufficient challenge deposit:** Challenger deposits 50 REGEN (< 10% of 500 bond). Rejected with `ErrInsufficientChallengeDeposit`. +8) **Challenge window expired:** Challenger attempts to challenge after challenge_window_closes_at. Rejected with `ErrChallengeWindowClosed`. +9) **Challenge non-active attestation:** Challenger attempts to challenge RELEASED attestation. Rejected with `ErrAttestationNotChallengeable`. + +### Resolution +10) **Arbiter resolves VALID:** Arbiter DAO votes VALID. Attester receives bond + challenge deposit - arbiter fee. Attestation status = RESOLVED_VALID. +11) **Arbiter resolves INVALID:** Arbiter DAO votes INVALID. Challenger receives 50% bond + deposit - arbiter fee. Community pool receives 50% bond. Attestation status = SLASHED. +12) **Only arbiter can resolve:** Non-arbiter address attempts resolution. Rejected with `ErrUnauthorized`. + +### Bond release +13) **Release after lock period:** Lock period expires, no active challenge. Attester calls ReleaseBond. Bond returned. +14) **Release before lock period:** Attester attempts release before lock_expires_at. Rejected with `ErrLockPeriodNotExpired`. +15) **Release during active challenge:** Attester attempts release while attestation is CHALLENGED. Rejected. + +### Security +16) **Bond conservation:** After any sequence of attestations (created, challenged, resolved, released), verify `bond_pool.balance = sum(locked_bonds) + sum(locked_deposits) - sum(disbursements)`. +17) **Arbiter neutrality:** Attester attempts to resolve their own challenge. Rejected. +18) **Slash distribution:** On INVALID resolution, verify challenger receives exactly 50% of bond + deposit - fee, and community pool receives exactly 50% of bond. +19) **Single active challenge:** Second challenge filed against already-CHALLENGED attestation. Rejected. +20) **Resolution finality:** Attempt to re-resolve an already-resolved challenge. Rejected. + +## 13. Rollout plan + +### v0 checklist (advisory-only) +- Define attestation types with bond schedules (ProjectBoundary, BaselineMeasurement, CreditIssuanceClaim, MethodologyValidation). +- Implement off-chain attestation quality scoring function (4-factor weighted composite). +- Publish quality scores in weekly digest alongside attestation activity. +- Query M010 reputation scores for attester addresses. +- Query KOI MCP for attestation document analysis. +- Implement deterministic test fixtures with realistic attestation data. +- Record all attestation lifecycle events in audit log. + +### v1 outline (CosmWasm on-chain) +- Deploy `attestation-bond` CosmWasm contract with CreateAttestation, ChallengeAttestation, ResolveChallenge, ReleaseBond message handlers. +- Implement bond escrow with lock/release/slash flows. +- Integrate Arbiter DAO (DAO DAO subDAO) for decentralized challenge resolution. +- Implement challenge deposit and settlement economics. +- Implement 48h activation delay. +- Add governance-controlled parameters (min_bond, challenge_deposit_ratio, arbiter_fee_ratio). + +--- + +## Appendix A — Source anchors +- `phase-2/2.1-token-utility-mechanisms.md` — M008 protocol specification (participants, token flows, state transitions, bond schedule, security invariants) +- `phase-3/3.1-smart-contract-specs.md` — CosmWasm contract: Attestation Bond (state, messages, core logic, Arbiter DAO integration) diff --git a/mechanisms/m008-attestation-bonding/datasets/README.md b/mechanisms/m008-attestation-bonding/datasets/README.md new file mode 100644 index 0000000..c1de3f1 --- /dev/null +++ b/mechanisms/m008-attestation-bonding/datasets/README.md @@ -0,0 +1,11 @@ +# m008 replay datasets + +Fixture files for replay testing of m008 (Data Attestation Bonding) computations. + +## Files +- `schema.json` — JSON Schema for the replay dataset format. +- `fixtures/v0_sample.json` — Five attestations across all types, with quality scores matching reference-impl output. Includes ACTIVE, RELEASED, and SUBMITTED statuses. +- `fixtures/v0_challenge_sample.json` — Six attestations covering challenge and resolution scenarios: CHALLENGED (pending), SLASHED, RESOLVED_VALID, RESOLVED_INVALID, ACTIVE, and RELEASED. + +## Usage +Feed fixture files into `m008_kpi.js` to verify KPI computation. Quality scores in fixtures correspond to `m008_score.js` output for the matching factor inputs. diff --git a/mechanisms/m008-attestation-bonding/datasets/fixtures/v0_challenge_sample.json b/mechanisms/m008-attestation-bonding/datasets/fixtures/v0_challenge_sample.json new file mode 100644 index 0000000..a2aad3e --- /dev/null +++ b/mechanisms/m008-attestation-bonding/datasets/fixtures/v0_challenge_sample.json @@ -0,0 +1,103 @@ +{ + "mechanism_id": "m008", + "scope": "v0_advisory", + "as_of": "2026-02-18T12:00:00Z", + "attestations": [ + { + "attestation_id": "att-c01", + "attestation_type": "CreditIssuanceClaim", + "attestation_iri": "koi://attestation/carbon-issuance-batch-C02-disputed", + "attester": "regen1chal01attester", + "bond": { "amount": "2000", "denom": "uregen" }, + "status": "CHALLENGED", + "quality_score": 450, + "submitted_at": "2025-12-01T10:00:00Z", + "activated_at": "2025-12-03T10:00:00Z", + "challenge": { + "challenger": "regen1challenger01", + "reason": "Baseline data inconsistent with satellite imagery for claimed issuance period", + "challenged_at": "2026-01-15T08:00:00Z", + "resolved_at": null, + "outcome": null + } + }, + { + "attestation_id": "att-c02", + "attestation_type": "BaselineMeasurement", + "attestation_iri": "koi://attestation/soil-baseline-fabricated", + "attester": "regen1chal02attester", + "bond": { "amount": "1000", "denom": "uregen" }, + "status": "SLASHED", + "quality_score": 280, + "submitted_at": "2025-10-01T09:00:00Z", + "activated_at": "2025-10-03T09:00:00Z", + "challenge": { + "challenger": "regen1challenger02", + "reason": "Measurement data does not match on-site verification; suspected fabrication", + "challenged_at": "2025-11-01T12:00:00Z", + "resolved_at": "2025-12-15T16:00:00Z", + "outcome": "INVALID" + } + }, + { + "attestation_id": "att-c03", + "attestation_type": "MethodologyValidation", + "attestation_iri": "koi://attestation/methodology-review-biochar-v2", + "attester": "regen1chal03attester", + "bond": { "amount": "5000", "denom": "uregen" }, + "status": "RESOLVED_VALID", + "quality_score": 820, + "submitted_at": "2025-09-15T11:00:00Z", + "activated_at": "2025-09-17T11:00:00Z", + "challenge": { + "challenger": "regen1challenger03", + "reason": "Questioned permanence assumptions in biochar sequestration model", + "challenged_at": "2025-10-20T14:00:00Z", + "resolved_at": "2025-12-01T10:00:00Z", + "outcome": "VALID" + } + }, + { + "attestation_id": "att-c04", + "attestation_type": "ProjectBoundary", + "attestation_iri": "koi://attestation/project-boundary-overlap-detected", + "attester": "regen1chal04attester", + "bond": { "amount": "500", "denom": "uregen" }, + "status": "RESOLVED_INVALID", + "quality_score": 310, + "submitted_at": "2025-08-01T08:00:00Z", + "activated_at": "2025-08-03T08:00:00Z", + "challenge": { + "challenger": "regen1challenger04", + "reason": "Project boundary overlaps with existing registered project P-regen-018", + "challenged_at": "2025-09-10T09:00:00Z", + "resolved_at": "2025-11-05T15:00:00Z", + "outcome": "INVALID" + } + }, + { + "attestation_id": "att-c05", + "attestation_type": "CreditIssuanceClaim", + "attestation_iri": "koi://attestation/carbon-issuance-batch-C07-valid", + "attester": "regen1chal05attester", + "bond": { "amount": "3000", "denom": "uregen" }, + "status": "ACTIVE", + "quality_score": 880, + "submitted_at": "2026-01-20T13:00:00Z", + "activated_at": "2026-01-22T13:00:00Z", + "challenge": null + }, + { + "attestation_id": "att-c06", + "attestation_type": "BaselineMeasurement", + "attestation_iri": "koi://attestation/soil-baseline-plot-99", + "attester": "regen1chal06attester", + "bond": { "amount": "1500", "denom": "uregen" }, + "status": "RELEASED", + "quality_score": 650, + "submitted_at": "2025-06-01T07:00:00Z", + "activated_at": "2025-06-03T07:00:00Z", + "challenge": null + } + ] +} diff --git a/mechanisms/m008-attestation-bonding/datasets/fixtures/v0_sample.json b/mechanisms/m008-attestation-bonding/datasets/fixtures/v0_sample.json new file mode 100644 index 0000000..5faff7e --- /dev/null +++ b/mechanisms/m008-attestation-bonding/datasets/fixtures/v0_sample.json @@ -0,0 +1,67 @@ +{ + "mechanism_id": "m008", + "scope": "v0_advisory", + "as_of": "2026-02-18T12:00:00Z", + "attestations": [ + { + "attestation_id": "att-001", + "attestation_type": "BaselineMeasurement", + "attestation_iri": "koi://attestation/soil-baseline-plot-42", + "attester": "regen1abc123attester1", + "bond": { "amount": "1500", "denom": "uregen" }, + "status": "ACTIVE", + "quality_score": 769, + "submitted_at": "2026-01-10T08:00:00Z", + "activated_at": "2026-01-12T08:00:00Z", + "challenge": null + }, + { + "attestation_id": "att-002", + "attestation_type": "CreditIssuanceClaim", + "attestation_iri": "koi://attestation/carbon-issuance-batch-C04-2026", + "attester": "regen1def456attester2", + "bond": { "amount": "2000", "denom": "uregen" }, + "status": "ACTIVE", + "quality_score": 704, + "submitted_at": "2026-01-15T10:00:00Z", + "activated_at": "2026-01-17T10:00:00Z", + "challenge": null + }, + { + "attestation_id": "att-003", + "attestation_type": "ProjectBoundary", + "attestation_iri": "koi://attestation/project-boundary-P-regen-042", + "attester": "regen1ghi789attester3", + "bond": { "amount": "500", "denom": "uregen" }, + "status": "RELEASED", + "quality_score": 425, + "submitted_at": "2025-11-01T09:00:00Z", + "activated_at": "2025-11-03T09:00:00Z", + "challenge": null + }, + { + "attestation_id": "att-004", + "attestation_type": "MethodologyValidation", + "attestation_iri": "koi://attestation/methodology-review-soil-carbon-v4", + "attester": "regen1jkl012attester4", + "bond": { "amount": "10000", "denom": "uregen" }, + "status": "ACTIVE", + "quality_score": 970, + "submitted_at": "2026-02-01T14:00:00Z", + "activated_at": "2026-02-03T14:00:00Z", + "challenge": null + }, + { + "attestation_id": "att-005", + "attestation_type": "BaselineMeasurement", + "attestation_iri": "koi://attestation/weak-baseline-unresolvable", + "attester": "regen1mno345attester5", + "bond": { "amount": "1000", "denom": "uregen" }, + "status": "SUBMITTED", + "quality_score": 393, + "submitted_at": "2026-02-15T16:00:00Z", + "activated_at": null, + "challenge": null + } + ] +} diff --git a/mechanisms/m008-attestation-bonding/datasets/schema.json b/mechanisms/m008-attestation-bonding/datasets/schema.json new file mode 100644 index 0000000..0a39a95 --- /dev/null +++ b/mechanisms/m008-attestation-bonding/datasets/schema.json @@ -0,0 +1,54 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "m008_dataset_v0", + "title": "m008 replay dataset", + "description": "Replay dataset for m008 Data Attestation Bonding — attestation lifecycle records with bond, status, and quality scores.", + "type": "object", + "required": ["mechanism_id", "scope", "as_of", "attestations"], + "properties": { + "mechanism_id": { "const": "m008" }, + "scope": { "type": "string" }, + "as_of": { "type": "string", "format": "date-time" }, + "attestations": { + "type": "array", + "items": { + "type": "object", + "required": ["attestation_id", "attestation_type", "attestation_iri", "attester", "bond", "status"], + "properties": { + "attestation_id": { "type": "string" }, + "attestation_type": { + "type": "string", + "enum": ["ProjectBoundary", "BaselineMeasurement", "CreditIssuanceClaim", "MethodologyValidation"] + }, + "attestation_iri": { "type": "string" }, + "attester": { "type": "string" }, + "bond": { + "type": "object", + "required": ["amount", "denom"], + "properties": { + "amount": { "type": "string" }, + "denom": { "type": "string" } + } + }, + "status": { + "type": "string", + "enum": ["SUBMITTED", "ACTIVE", "CHALLENGED", "RESOLVED_VALID", "RESOLVED_INVALID", "RELEASED", "SLASHED"] + }, + "quality_score": { "type": ["number", "null"] }, + "submitted_at": { "type": "string", "format": "date-time" }, + "activated_at": { "type": ["string", "null"], "format": "date-time" }, + "challenge": { + "type": ["object", "null"], + "properties": { + "challenger": { "type": "string" }, + "reason": { "type": "string" }, + "challenged_at": { "type": "string", "format": "date-time" }, + "resolved_at": { "type": ["string", "null"], "format": "date-time" }, + "outcome": { "type": ["string", "null"], "enum": ["VALID", "INVALID", null] } + } + } + } + } + } + } +} diff --git a/mechanisms/m008-attestation-bonding/reference-impl/README.md b/mechanisms/m008-attestation-bonding/reference-impl/README.md new file mode 100644 index 0000000..f4ffbf5 --- /dev/null +++ b/mechanisms/m008-attestation-bonding/reference-impl/README.md @@ -0,0 +1,42 @@ +# m008 reference implementation (v0 advisory) + +This folder provides a **canonical computation** for m008 outputs so that different agents/runners +produce consistent numbers. + +## Inputs + +### Scoring (`m008_score.js`) +An input object per attestation: + +- `attestation` — attestation metadata: + - `attestation_type` (string, one of ProjectBoundary/BaselineMeasurement/CreditIssuanceClaim/MethodologyValidation) + - `attestation_iri` (string) + - `bond` (object with `amount` and `denom`) +- `factors` — pre-computed factor scores (each 0–1000): + - `bond_adequacy` — bond amount relative to minimum + - `attester_reputation` — M010 reputation score (default 300 if unavailable) + - `evidence_completeness` — document completeness checklist + - `type_risk` — attestation type risk factor + - `reputation_available` (boolean), `iri_resolvable` (boolean), `has_prior_attestations` (boolean), `type_recognized` (boolean) + +### KPI (`m008_kpi.js`) +- `as_of` (ISO-8601 string, Z-suffixed) +- `attestations[]` — each with `status`, `attestation_type`, `bond`, `quality_score` (optional) + +## Outputs + +### Score +- `score` (0–1000) — weighted composite +- `confidence` (0–1000) — data availability +- `factors` — individual factor scores + +Formula: +``` +score = 0.30 × bond_adequacy + 0.30 × attester_reputation + 0.25 × evidence_completeness + 0.15 × type_risk +``` + +### KPI block +- Attestation counts by status (submitted, active, released, challenged, slashed) +- `challenge_rate`, `slash_rate` +- Bond economics (total bonded/released/slashed, average) +- Average quality score, type breakdown diff --git a/mechanisms/m008-attestation-bonding/reference-impl/m008_kpi.js b/mechanisms/m008-attestation-bonding/reference-impl/m008_kpi.js new file mode 100644 index 0000000..ea0c8b1 --- /dev/null +++ b/mechanisms/m008-attestation-bonding/reference-impl/m008_kpi.js @@ -0,0 +1,66 @@ +export function computeM008KPI({ as_of, attestations }) { + const atts = attestations ?? []; + + const attestations_submitted = atts.length; + const attestations_active = atts.filter(a => a.status === "ACTIVE").length; + const attestations_released = atts.filter(a => a.status === "RELEASED").length; + const attestations_challenged = atts.filter(a => + ["CHALLENGED", "RESOLVED_VALID", "RESOLVED_INVALID", "SLASHED"].includes(a.status) + ).length; + const attestations_slashed = atts.filter(a => + a.status === "SLASHED" || a.status === "RESOLVED_INVALID" + ).length; + + const challenge_rate = attestations_submitted > 0 + ? Number((attestations_challenged / attestations_submitted).toFixed(4)) + : 0.0; + + const slash_rate = attestations_challenged > 0 + ? Number((attestations_slashed / attestations_challenged).toFixed(4)) + : null; + + // Bond economics + const bonds = atts.map(a => parseInt(a.bond?.amount ?? "0", 10)).filter(b => b > 0); + const total_bonded = bonds.reduce((s, b) => s + b, 0); + const released = atts.filter(a => a.status === "RELEASED").map(a => parseInt(a.bond?.amount ?? "0", 10)); + const total_released = released.reduce((s, b) => s + b, 0); + const slashed = atts.filter(a => a.status === "SLASHED" || a.status === "RESOLVED_INVALID") + .map(a => parseInt(a.bond?.amount ?? "0", 10)); + const total_slashed = slashed.reduce((s, b) => s + b, 0); + const avg_bond_amount = bonds.length > 0 + ? Number((total_bonded / bonds.length).toFixed(1)) + : null; + + // Quality scores + const scores = atts.filter(a => a.quality_score != null).map(a => a.quality_score); + const avg_quality_score = scores.length > 0 + ? Number((scores.reduce((s, q) => s + q, 0) / scores.length).toFixed(1)) + : null; + + // Type breakdown + const types = { ProjectBoundary: 0, BaselineMeasurement: 0, CreditIssuanceClaim: 0, MethodologyValidation: 0 }; + for (const a of atts) { + if (types[a.attestation_type] !== undefined) types[a.attestation_type]++; + } + + return { + mechanism_id: "m008", + scope: "v0_advisory", + as_of, + attestations_submitted, + attestations_active, + attestations_released, + attestations_challenged, + attestations_slashed, + challenge_rate, + slash_rate, + bond_economics: { + total_bonded: String(total_bonded), + total_released: String(total_released), + total_slashed: String(total_slashed), + avg_bond_amount + }, + avg_quality_score, + attestation_type_breakdown: types + }; +} diff --git a/mechanisms/m008-attestation-bonding/reference-impl/m008_score.js b/mechanisms/m008-attestation-bonding/reference-impl/m008_score.js new file mode 100644 index 0000000..011a618 --- /dev/null +++ b/mechanisms/m008-attestation-bonding/reference-impl/m008_score.js @@ -0,0 +1,142 @@ +/** + * v0 (advisory): 4-factor weighted composite scoring for bonded attestations. + * + * Factors: + * bond_adequacy (weight 0.30): Bond amount relative to minimum for type + * attester_reputation (weight 0.30): M010 reputation score for attester + * evidence_completeness(weight 0.25): Attestation document completeness + * type_risk (weight 0.15): Attestation type risk factor + * + * See SPEC.md section 5 for full formula. + * + * @param {Object} opts + * @param {Object} opts.attestation - Attestation with type, bond amount + * @param {Object} opts.factors - Pre-computed factor scores (each 0-1000) + * @returns {{ score: number, confidence: number, factors: Object }} + */ +export function computeM008Score({ attestation, factors }) { + const W_BOND = 0.30; + const W_REPUTATION = 0.30; + const W_EVIDENCE = 0.25; + const W_TYPE = 0.15; + + const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v)); + + const fBond = clamp(factors.bond_adequacy ?? 0, 0, 1000); + const fRep = clamp(factors.attester_reputation ?? 300, 0, 1000); + const fEvid = clamp(factors.evidence_completeness ?? 0, 0, 1000); + const fType = clamp(factors.type_risk ?? 0, 0, 1000); + + const score = Math.round( + W_BOND * fBond + + W_REPUTATION * fRep + + W_EVIDENCE * fEvid + + W_TYPE * fType + ); + + const confidence = computeConfidence(factors); + + return { + score: clamp(score, 0, 1000), + confidence, + factors: { + bond_adequacy: fBond, + attester_reputation: fRep, + evidence_completeness: fEvid, + type_risk: fType + } + }; +} + +/** + * Compute bond adequacy factor. + * Bonds at minimum get 500; bonds at 2x minimum get 1000 (capped). + * + * @param {number} bondAmount - Actual bond amount + * @param {number} minBond - Minimum bond for attestation type + * @returns {number} f_bond (0-1000) + */ +export function computeBondAdequacy(bondAmount, minBond) { + if (minBond <= 0) return 0; + return Math.min(1000, Math.round((bondAmount / minBond) * 500)); +} + +/** + * Get type risk factor by attestation type. + * @param {string} attestationType + * @returns {number} f_type (0-1000) + */ +export function getTypeRiskFactor(attestationType) { + const TYPE_RISK = { + MethodologyValidation: 1000, + CreditIssuanceClaim: 800, + BaselineMeasurement: 600, + ProjectBoundary: 400 + }; + return TYPE_RISK[attestationType] ?? 0; +} + +/** + * Get minimum bond for attestation type. + * @param {string} attestationType + * @returns {number} minimum bond in REGEN + */ +export function getMinBond(attestationType) { + const MIN_BONDS = { + ProjectBoundary: 500, + BaselineMeasurement: 1000, + CreditIssuanceClaim: 2000, + MethodologyValidation: 5000 + }; + return MIN_BONDS[attestationType] ?? 0; +} + +function computeConfidence(factors) { + let available = 0; + const total = 4; + if (factors.reputation_available) available++; + if (factors.iri_resolvable !== false) available++; + if (factors.has_prior_attestations) available++; + if (factors.type_recognized !== false) available++; + return Math.round((available / total) * 1000); +} + +// --- Self-test --- +const isMain = typeof process !== "undefined" && + process.argv[1] && + (process.argv[1].endsWith("m008_score.js") || process.argv[1].endsWith("m008_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.attestations.map(a => computeM008Score({ + attestation: a.attestation, + factors: a.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 attestation[${i}]: got score=${r.score}, expected score=${e.score}`); + pass = false; + } + } + + if (pass) { + console.log("m008_score self-test: PASS"); + console.log(JSON.stringify({ scores: results }, null, 2)); + } else { + process.exit(1); + } +} diff --git a/mechanisms/m008-attestation-bonding/reference-impl/test_vectors/vector_v0_sample.expected.json b/mechanisms/m008-attestation-bonding/reference-impl/test_vectors/vector_v0_sample.expected.json new file mode 100644 index 0000000..5764aae --- /dev/null +++ b/mechanisms/m008-attestation-bonding/reference-impl/test_vectors/vector_v0_sample.expected.json @@ -0,0 +1,54 @@ +{ + "scores": [ + { + "score": 769, + "confidence": 1000, + "factors": { + "bond_adequacy": 750, + "attester_reputation": 680, + "evidence_completeness": 1000, + "type_risk": 600 + } + }, + { + "score": 704, + "confidence": 1000, + "factors": { + "bond_adequacy": 500, + "attester_reputation": 820, + "evidence_completeness": 750, + "type_risk": 800 + } + }, + { + "score": 425, + "confidence": 500, + "factors": { + "bond_adequacy": 500, + "attester_reputation": 300, + "evidence_completeness": 500, + "type_risk": 400 + } + }, + { + "score": 970, + "confidence": 1000, + "factors": { + "bond_adequacy": 1000, + "attester_reputation": 900, + "evidence_completeness": 1000, + "type_risk": 1000 + } + }, + { + "score": 393, + "confidence": 250, + "factors": { + "bond_adequacy": 500, + "attester_reputation": 300, + "evidence_completeness": 250, + "type_risk": 600 + } + } + ] +} diff --git a/mechanisms/m008-attestation-bonding/reference-impl/test_vectors/vector_v0_sample.input.json b/mechanisms/m008-attestation-bonding/reference-impl/test_vectors/vector_v0_sample.input.json new file mode 100644 index 0000000..8335554 --- /dev/null +++ b/mechanisms/m008-attestation-bonding/reference-impl/test_vectors/vector_v0_sample.input.json @@ -0,0 +1,95 @@ +{ + "as_of": "2026-02-18T12:00:00Z", + "attestations": [ + { + "attestation": { + "attestation_id": "att-001", + "attestation_type": "BaselineMeasurement", + "attestation_iri": "koi://attestation/soil-baseline-plot-42", + "bond": { "amount": "1500", "denom": "uregen" } + }, + "factors": { + "bond_adequacy": 750, + "attester_reputation": 680, + "evidence_completeness": 1000, + "type_risk": 600, + "reputation_available": true, + "iri_resolvable": true, + "has_prior_attestations": true, + "type_recognized": true + } + }, + { + "attestation": { + "attestation_id": "att-002", + "attestation_type": "CreditIssuanceClaim", + "attestation_iri": "koi://attestation/carbon-issuance-batch-C04-2026", + "bond": { "amount": "2000", "denom": "uregen" } + }, + "factors": { + "bond_adequacy": 500, + "attester_reputation": 820, + "evidence_completeness": 750, + "type_risk": 800, + "reputation_available": true, + "iri_resolvable": true, + "has_prior_attestations": true, + "type_recognized": true + } + }, + { + "attestation": { + "attestation_id": "att-003", + "attestation_type": "ProjectBoundary", + "attestation_iri": "koi://attestation/project-boundary-P-regen-042", + "bond": { "amount": "500", "denom": "uregen" } + }, + "factors": { + "bond_adequacy": 500, + "attester_reputation": 300, + "evidence_completeness": 500, + "type_risk": 400, + "reputation_available": false, + "iri_resolvable": true, + "has_prior_attestations": false, + "type_recognized": true + } + }, + { + "attestation": { + "attestation_id": "att-004", + "attestation_type": "MethodologyValidation", + "attestation_iri": "koi://attestation/methodology-review-soil-carbon-v4", + "bond": { "amount": "10000", "denom": "uregen" } + }, + "factors": { + "bond_adequacy": 1000, + "attester_reputation": 900, + "evidence_completeness": 1000, + "type_risk": 1000, + "reputation_available": true, + "iri_resolvable": true, + "has_prior_attestations": true, + "type_recognized": true + } + }, + { + "attestation": { + "attestation_id": "att-005", + "attestation_type": "BaselineMeasurement", + "attestation_iri": "koi://attestation/weak-baseline-unresolvable", + "bond": { "amount": "1000", "denom": "uregen" } + }, + "factors": { + "bond_adequacy": 500, + "attester_reputation": 300, + "evidence_completeness": 250, + "type_risk": 600, + "reputation_available": false, + "iri_resolvable": false, + "has_prior_attestations": false, + "type_recognized": true + } + } + ] +} diff --git a/mechanisms/m008-attestation-bonding/schemas/README.md b/mechanisms/m008-attestation-bonding/schemas/README.md new file mode 100644 index 0000000..fe82478 --- /dev/null +++ b/mechanisms/m008-attestation-bonding/schemas/README.md @@ -0,0 +1,14 @@ +# m008 output schemas + +These JSON Schemas define **canonical output shapes** for m008 (Data Attestation Bonding) artifacts. + +## Files +- `m008_attestation.schema.json` — schema for attestation lifecycle objects (bond, status, challenge details). +- `m008_quality_score.schema.json` — schema for attestation quality score output (score, confidence, factor breakdown). +- `m008_kpi.schema.json` — schema for KPI metrics (attestations submitted, challenge rate, bond economics, quality scores). + +## 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 attestations tracks lifecycle state (BONDED → ACTIVE → CHALLENGED → RESOLVED/RELEASED/SLASHED). See SPEC.md section 6. +- Attestation types are: ProjectBoundary, BaselineMeasurement, CreditIssuanceClaim, MethodologyValidation. diff --git a/mechanisms/m008-attestation-bonding/schemas/m008_attestation.schema.json b/mechanisms/m008-attestation-bonding/schemas/m008_attestation.schema.json new file mode 100644 index 0000000..f0efdca --- /dev/null +++ b/mechanisms/m008-attestation-bonding/schemas/m008_attestation.schema.json @@ -0,0 +1,78 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "m008 attestation", + "description": "Bonded attestation lifecycle object. See SPEC.md section 6.", + "type": "object", + "additionalProperties": false, + "required": [ + "attestation_id", + "attester", + "attestation_type", + "attestation_iri", + "bond", + "status", + "bonded_at" + ], + "properties": { + "attestation_id": { + "type": "string", + "description": "Unique attestation identifier" + }, + "attester": { + "type": "string", + "description": "Bech32 address of the attester" + }, + "attestation_type": { + "type": "string", + "enum": ["ProjectBoundary", "BaselineMeasurement", "CreditIssuanceClaim", "MethodologyValidation"], + "description": "Type of ecological attestation. Determines bond schedule." + }, + "attestation_iri": { + "type": "string", + "description": "IRI pointing to the attestation document" + }, + "beneficiary": { + "type": "string", + "description": "Bech32 address of the project receiving the attestation (optional)" + }, + "bond": { + "type": "object", + "required": ["amount", "denom"], + "properties": { + "amount": { "type": "string" }, + "denom": { "type": "string" } + } + }, + "status": { + "type": "string", + "enum": ["BONDED", "ACTIVE", "CHALLENGED", "RESOLVED_VALID", "RESOLVED_INVALID", "RELEASED", "SLASHED"], + "description": "Attestation lifecycle state. See SPEC.md section 6." + }, + "bonded_at": { "type": "string", "format": "date-time" }, + "activated_at": { "type": ["string", "null"], "format": "date-time" }, + "lock_expires_at": { "type": "string", "format": "date-time" }, + "challenge_window_closes_at": { "type": "string", "format": "date-time" }, + "challenge": { + "type": "object", + "description": "Active challenge details, if any", + "properties": { + "challenge_id": { "type": "string" }, + "challenger": { "type": "string" }, + "evidence_iri": { "type": "string" }, + "deposit": { + "type": "object", + "properties": { + "amount": { "type": "string" }, + "denom": { "type": "string" } + } + }, + "challenged_at": { "type": "string", "format": "date-time" }, + "resolution": { + "type": "string", + "enum": ["ATTESTER_WINS", "CHALLENGER_WINS"] + }, + "resolved_at": { "type": ["string", "null"], "format": "date-time" } + } + } + } +} diff --git a/mechanisms/m008-attestation-bonding/schemas/m008_kpi.schema.json b/mechanisms/m008-attestation-bonding/schemas/m008_kpi.schema.json new file mode 100644 index 0000000..09bc377 --- /dev/null +++ b/mechanisms/m008-attestation-bonding/schemas/m008_kpi.schema.json @@ -0,0 +1,56 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "m008 KPI output", + "description": "KPI metrics for the attestation bonding process. See SPEC.md.", + "type": "object", + "additionalProperties": false, + "required": [ + "mechanism_id", + "scope", + "as_of", + "attestations_submitted", + "attestations_active", + "attestations_released", + "attestations_slashed" + ], + "properties": { + "mechanism_id": { "const": "m008" }, + "scope": { "type": "string" }, + "as_of": { "type": "string", "format": "date-time" }, + "attestations_submitted": { "type": "integer", "minimum": 0 }, + "attestations_active": { "type": "integer", "minimum": 0 }, + "attestations_released": { "type": "integer", "minimum": 0 }, + "attestations_challenged": { "type": "integer", "minimum": 0 }, + "attestations_slashed": { "type": "integer", "minimum": 0 }, + "challenge_rate": { + "type": "number", "minimum": 0.0, "maximum": 1.0, + "description": "challenged / total submitted" + }, + "slash_rate": { + "type": ["number", "null"], "minimum": 0.0, "maximum": 1.0, + "description": "slashed / challenged" + }, + "bond_economics": { + "type": "object", + "properties": { + "total_bonded": { "type": "string" }, + "total_released": { "type": "string" }, + "total_slashed": { "type": "string" }, + "avg_bond_amount": { "type": ["number", "null"] } + } + }, + "avg_quality_score": { + "type": ["number", "null"], "minimum": 0, "maximum": 1000 + }, + "attestation_type_breakdown": { + "type": "object", + "properties": { + "ProjectBoundary": { "type": "integer", "minimum": 0 }, + "BaselineMeasurement": { "type": "integer", "minimum": 0 }, + "CreditIssuanceClaim": { "type": "integer", "minimum": 0 }, + "MethodologyValidation": { "type": "integer", "minimum": 0 } + } + }, + "notes": { "type": "string" } + } +} diff --git a/mechanisms/m008-attestation-bonding/schemas/m008_quality_score.schema.json b/mechanisms/m008-attestation-bonding/schemas/m008_quality_score.schema.json new file mode 100644 index 0000000..2569430 --- /dev/null +++ b/mechanisms/m008-attestation-bonding/schemas/m008_quality_score.schema.json @@ -0,0 +1,67 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "m008 attestation quality score", + "description": "Quality score output for a bonded attestation. See SPEC.md section 5.", + "type": "object", + "additionalProperties": false, + "required": [ + "attestation_id", + "score", + "confidence", + "factors", + "timestamp" + ], + "properties": { + "attestation_id": { + "type": "string", + "description": "Unique attestation identifier" + }, + "score": { + "type": "integer", + "minimum": 0, + "maximum": 1000, + "description": "Weighted composite quality score (0-1000). See SPEC.md section 5.1." + }, + "confidence": { + "type": "integer", + "minimum": 0, + "maximum": 1000, + "description": "Data availability confidence (0-1000). See SPEC.md section 5.3." + }, + "factors": { + "type": "object", + "additionalProperties": false, + "required": ["bond_adequacy", "attester_reputation", "evidence_completeness", "type_risk"], + "properties": { + "bond_adequacy": { + "type": "integer", + "minimum": 0, + "maximum": 1000, + "description": "Bond amount relative to minimum (weight: 0.3)" + }, + "attester_reputation": { + "type": "integer", + "minimum": 0, + "maximum": 1000, + "description": "M010 reputation for attester address (weight: 0.3)" + }, + "evidence_completeness": { + "type": "integer", + "minimum": 0, + "maximum": 1000, + "description": "Attestation document completeness (weight: 0.25)" + }, + "type_risk": { + "type": "integer", + "minimum": 0, + "maximum": 1000, + "description": "Attestation type risk factor (weight: 0.15)" + } + } + }, + "timestamp": { + "type": "string", + "format": "date-time" + } + } +} 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", From 2b018aa39fa635ee57ee078fdea130acdb29ba63 Mon Sep 17 00:00:00 2001 From: Christian Shearer Date: Wed, 18 Feb 2026 14:33:02 -0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20Address=20review=20findings=20?= =?UTF-8?q?=E2=80=94=20state=20machine,=20schema=20alignment,=20reputation?= =?UTF-8?q?=20clarity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SPEC.md: add SUBMITTED initial state, remove RESOLVED_INVALID (invalid resolution transitions directly to SLASHED), clarify terminal states - SPEC.md: restructure reputation formula with explicit conditional logic - m008_attestation.schema.json: rename resolution → outcome, align enum with dataset schema (VALID/INVALID instead of ATTESTER_WINS/CHALLENGER_WINS) - m008_kpi.js: slash_rate returns 0.0 instead of null when no challenges Fixes Gemini review on PR #20. Co-Authored-By: Claude Opus 4.6 --- mechanisms/m008-attestation-bonding/SPEC.md | 28 +++++++++++++------ .../reference-impl/m008_kpi.js | 2 +- .../schemas/m008_attestation.schema.json | 7 +++-- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/mechanisms/m008-attestation-bonding/SPEC.md b/mechanisms/m008-attestation-bonding/SPEC.md index 9f52a67..c598831 100644 --- a/mechanisms/m008-attestation-bonding/SPEC.md +++ b/mechanisms/m008-attestation-bonding/SPEC.md @@ -65,9 +65,11 @@ f_bond = min(1000, (bond_amount / min_bond[type]) × 500) Bonds at minimum get 500; bonds at 2× minimum get 1000 (capped). #### Attester reputation (`f_reputation`, 0–1000) -- If M010 score exists: `f_reputation = m010_score` +- If M010 score exists AND attester has track record (unchallenged_rate available): + `f_reputation = 0.7 × m010_score + 0.3 × (unchallenged_rate × 1000)` +- If M010 score exists but no track record: + `f_reputation = m010_score` - If no M010 score: `f_reputation = 300` (cautious default, below neutral) -- Track record adjustment: `f_reputation = 0.7 × m010_score + 0.3 × (unchallenged_rate × 1000)` #### Evidence completeness (`f_evidence`, 0–1000) Binary checklist by attestation type: @@ -96,12 +98,20 @@ Factors: M010 reputation exists, IRI resolvable, attester has prior attestations ## 6. State machine ``` -States: {BONDED, ACTIVE, CHALLENGED, RESOLVED_VALID, RESOLVED_INVALID, RELEASED, SLASHED} +States: {SUBMITTED, BONDED, ACTIVE, CHALLENGED, RESOLVED_VALID, SLASHED, RELEASED} -Initial → BONDED - trigger: attester.create_attestation(type, iri, bond) - guard: bond >= min_bond[attestation_type]; attestation_type is valid - action: bond_pool.lock(bond), attestation.create(status=BONDED) +Note: RESOLVED_INVALID is not a distinct state; an invalid resolution transitions + directly to SLASHED. RESOLVED_VALID is a terminal state; SLASHED is a terminal state. + +Initial → SUBMITTED + trigger: attester.submit_attestation(type, iri, bond) + guard: attestation_type is valid + action: attestation.create(status=SUBMITTED) + +SUBMITTED → BONDED + trigger: bond confirmed on-chain + guard: bond >= min_bond[attestation_type] + action: bond_pool.lock(bond), attestation.status = BONDED BONDED → ACTIVE trigger: activation_delay_passed(48h) AND no_challenge_submitted @@ -126,7 +136,7 @@ CHALLENGED → RESOLVED_VALID action: attester.receive(bond + challenge_deposit - arbiter_fee) attestation.status = RESOLVED_VALID -CHALLENGED → RESOLVED_INVALID +CHALLENGED → SLASHED trigger: arbiter_dao.vote(INVALID) OR admin.resolve(INVALID) (v0) guard: quorum_met (v1), resolution_authority_verified action: challenger.receive(bond × 0.5 + challenge_deposit - arbiter_fee) @@ -139,7 +149,7 @@ ACTIVE → RELEASED action: attester.receive(bond) attestation.status = RELEASED -Terminal states: RESOLVED_VALID, RESOLVED_INVALID/SLASHED, RELEASED +Terminal states: RESOLVED_VALID, SLASHED, RELEASED Note: RESOLVED_VALID attestations remain valid references; SLASHED attestations are permanently invalidated. ``` diff --git a/mechanisms/m008-attestation-bonding/reference-impl/m008_kpi.js b/mechanisms/m008-attestation-bonding/reference-impl/m008_kpi.js index ea0c8b1..9bc174b 100644 --- a/mechanisms/m008-attestation-bonding/reference-impl/m008_kpi.js +++ b/mechanisms/m008-attestation-bonding/reference-impl/m008_kpi.js @@ -17,7 +17,7 @@ export function computeM008KPI({ as_of, attestations }) { const slash_rate = attestations_challenged > 0 ? Number((attestations_slashed / attestations_challenged).toFixed(4)) - : null; + : 0.0; // Bond economics const bonds = atts.map(a => parseInt(a.bond?.amount ?? "0", 10)).filter(b => b > 0); diff --git a/mechanisms/m008-attestation-bonding/schemas/m008_attestation.schema.json b/mechanisms/m008-attestation-bonding/schemas/m008_attestation.schema.json index f0efdca..cf2b28d 100644 --- a/mechanisms/m008-attestation-bonding/schemas/m008_attestation.schema.json +++ b/mechanisms/m008-attestation-bonding/schemas/m008_attestation.schema.json @@ -67,9 +67,10 @@ } }, "challenged_at": { "type": "string", "format": "date-time" }, - "resolution": { - "type": "string", - "enum": ["ATTESTER_WINS", "CHALLENGER_WINS"] + "outcome": { + "type": ["string", "null"], + "enum": ["VALID", "INVALID", null], + "description": "Challenge resolution outcome (null if unresolved)" }, "resolved_at": { "type": ["string", "null"], "format": "date-time" } }