governance: RFC-0001 — M013 fee split (28/25/45/2 → 15/30/50/5)#108
governance: RFC-0001 — M013 fee split (28/25/45/2 → 15/30/50/5)#108brawlaphant wants to merge 8 commits into
Conversation
Mirrors the AGENT-002 Governance Analyst structure to give AGENT-003 Market Monitor the same full standalone TypeScript implementation that AGENT-002 has: - `agent-003-market-monitor/` — standalone Node.js process - `src/index.ts` — banner, cycle runner, polling vs single-run mode - `src/config.ts` — config + thresholds mirrored from the character - `src/types.ts` — ecocredit marketplace types + per-workflow shapes - `src/ledger.ts` — LCD client for ecocredit + marketplace endpoints - `src/store.ts` — SQLite state (trade obs, anomaly dedupe, liquidity snapshots, retirement summaries, workflow executions) - `src/ooda.ts` — generic OODA executor (same shape as agent-002) - `src/monitor.ts` — Claude narrative layer, one function per workflow - `src/output.ts` — console + optional Discord webhook dispatcher - `src/workflows/price-anomaly-detection.ts` — WF-MM-01 - `src/workflows/liquidity-monitor.ts` — WF-MM-02 - `src/workflows/retirement-tracking.ts` — WF-MM-03 Design decisions documented in the agent README: 1. Deterministic numbers, narrative-only LLM calls. All severity classification, z-score computation, liquidity health scoring, and demand index derivation happen locally. Claude only writes the report from those numbers. Keeps the agent cheap, reproducible, and auditable. 2. Sell-order-as-trade proxy for the MVP until Ledger MCP exposes filled-trade events. The code path swaps cleanly once real trade events are available. 3. Batch-supply delta as the retirement source for the MVP, capped at 100 most recent batches per cycle to protect the LCD. A follow-up PR will plug in a real MsgRetire tx-stream client. 4. Alert dedupe by (trade_id, severity) — a trade that escalates from WARNING to CRITICAL still fires a new alert; a same-severity re-alert does not. Thresholds (`config.market.*`) mirror the character definition at `agents/packages/agents/src/characters/market-monitor.ts` so downstream tooling has a single source of truth. Anomaly severity boundaries (WARNING >= 2.0, CRITICAL >= 3.5 z-score) and the liquidity depth floor live in `config.ts` and are referenced from the system prompt. CI: the new agent is added to the `agents` job so `npx tsc --noEmit` runs against it on every PR, matching how agent-002-governance-analyst is wired. - Lands in: `agent-003-market-monitor/`, `.github/workflows/ci.yml` - Changes: new standalone AGENT-003 process with 3 workflows (WF-MM-01/02/03) - Validate: `cd agent-003-market-monitor && npm ci && npx tsc --noEmit` Refs phase-2/2.2-agentic-workflows.md §WF-MM-01, §WF-MM-02, §WF-MM-03 Refs agents/packages/agents/src/characters/market-monitor.ts (regen-network#64)
Follow-up to PR regen-network#80 — promised in the PR description as "Unit tests for median, stddev, classifyAnomaly, scoreHealth, computeDemandIndex, etc., planned as a separate test-only PR so the original PR stays a single-concern 'add the agent' change." Adds 52 unit tests across 3 test files covering every deterministic helper function in the AGENT-003 workflows. The helpers compute severity, liquidity health, and demand signals — they're the parts most likely to silently drift in a future refactor, so pinning their exact behavior prevents regressions that a typecheck cannot catch. ## Changes ### Helper exports Eight previously module-private helpers are now exported so the test file can import them: price-anomaly-detection.ts: median, stddev, classifyAnomaly, classIdFromBatchDenom, isUsdStableDenom liquidity-monitor.ts: askUsd, scoreHealth retirement-tracking.ts: computeDemandIndex The export is the only production-code change — no behavior change, no API rename. Module consumers unchanged. ### Test files src/workflows/price-anomaly-detection.test.ts (30 tests) median — empty, one-element, odd length, even length, non-mutation, negatives, floats stddev — empty, single, two-element, known five-element, n-1 denominator (sample, not population) classifyAnomaly — INFO / WARNING / CRITICAL boundaries, max of two inputs, symmetry for negative z-scores classIdFromBatchDenom — canonical regen format, single-letter, no dash, empty, degenerate leading dash isUsdStableDenom — USDC/USDT/DAI case-insensitive, ibc/* wrappers, non-stable rejects, empty string src/workflows/liquidity-monitor.test.ts (13 tests) askUsd — normal, zero quantity, negative quantity, non-finite ask, non-finite quantity, fractional scoreHealth — CRITICAL at sub-half depth, DEGRADED between half and full, HEALTHY at 3x floor with strong count, score-cap when depth saturates, count-cap at 20, zero case src/workflows/retirement-tracking.test.ts (9 tests) computeDemandIndex — zero, volume cap (60), count cap (20 at 10 retirements), breadth cap (20 at 5 retirees), total cap (100), moderate activity, log10 scaling, sub-1 floor, rounding ### Vitest setup vitest.config.ts (8 lines) — standard config, node env package.json — adds "test" and "test:watch" scripts + vitest ^2.1.0 devDep tsconfig.json — excludes *.test.ts from the production typecheck path (tests still typecheck via vitest itself) .gitignore — adds *.db-shm and *.db-wal (SQLite WAL side files from running tests locally; the base *.db pattern doesn't catch them) ## Validation $ cd agent-003-market-monitor && npm test Test Files 3 passed (3) Tests 52 passed (52) $ cd agent-003-market-monitor && npx tsc --noEmit (exit 0) Both CI checks continue to pass after this PR. ## Scope Does NOT touch the workflow OODA loops themselves, the Claude narrative layer, the LCD client, the SQLite store, or any output formatting. The tests cover the pure functions only — the surrounding integration machinery is tested implicitly when AGENT-003 runs a live cycle against the Regen LCD. - Lands in: `agent-003-market-monitor/` - Changes: 52 unit tests + vitest setup + 8 helper exports - Validate: `cd agent-003-market-monitor && npm test` ## PR relationship This PR is based on PR regen-network#80's branch (the AGENT-003 implementation) because the helpers don't exist on upstream/main. If regen-network#80 merges first, this PR rebases cleanly.
Replaces the batch-supply-delta MVP proxy in WF-MM-03 with a real tx-search client that reads recent MsgRetire events from the Cosmos LCD. This closes the follow-up documented in PR regen-network#80's design-decision regen-network#3: "Batch supply delta as retirement source (MVP). A follow-up PR will plug in a real tx-stream client for MsgRetire once the LCD event endpoint is available." ## What changes in the workflow The observe phase no longer walks batches → supplies. Instead: const retirements = await ledger.getRecentRetirementTxs(200); The orient phase no longer synthesizes retirement records from supply deltas. Instead it calls a new pure function `aggregateRetirementsByClass` that groups a list of real Retirement records by class id, computes the top retiree by cumulative quantity (not count), counts unique retirees via a Set, and measures the jurisdiction-metadata coverage percentage. The act phase is unchanged — it still persists, outputs, and alerts on demand-index jumps. ## What changes in the ledger client Adds two new methods to `LedgerClient`: - `getRecentRetirementTxs(limit)` — queries the LCD tx-search endpoint with `events=message.action='/regen.ecocredit.v1.MsgRetire'`, reverse-ordered, limited. Parses each tx response into zero or more Retirement records. Returns [] on error (transient LCD failures degrade to "no recent retirements" rather than crashing the cycle). - `parseRetirementsFromTx(tx)` — public parser exposed so unit tests can feed synthetic tx responses. Walks `logs[].events[]` AND top-level `events[]` for cross-SDK compatibility, filters on the Regen v1 and v1beta1 EventRetire type URLs, and extracts the owner / batch_denom / amount / jurisdiction / reason attributes. Skips malformed events (missing batch_denom, non-finite amount, non-positive amount). The parser is a pure function — no side effects, no LCD calls — so it is fully unit-testable with synthetic inputs. ## New tests (16 total across 2 files) **src/ledger.test.ts** (9 tests) - empty tx - single EventRetire with all attributes - v1beta1 fallback event type - non-retirement events ignored - missing batch_denom ignored - non-finite, zero, and negative amounts ignored - multiple retirements in one tx (batched) - events read from tx.events[] as well as logs[].events[] - tx hash and timestamp carried through to Retirement **src/workflows/retirement-tracking.test.ts** (7 new tests for aggregateRetirementsByClass, on top of the existing 9 for computeDemandIndex) - empty retirements → empty map - single retirement → one-entry summary - multiple retirements grouped by class id - top retiree identified by cumulative quantity, not count - jurisdiction metadata percentage - classes with zero total quantity skipped - empty retiree string handled without crashing Full test suite: 68 passed across 4 files (up from 52 in PR regen-network#99). ## What this unlocks The old MVP proxy produced RetirementSummary records with empty `topRetiree` / `topRetireeQuantity` / `pctWithJurisdiction` fields — the supply-delta source couldn't carry retiree identity or compliance metadata. The new implementation populates every field on the Retirement type. Downstream: - Demand index calculations now distinguish "5 unique retirees each retiring 200" from "1 whale retiring 1000". - Jurisdiction-metadata coverage surfaces compliance-driven demand (per the SPEC note: ">50% jurisdiction coverage typically implies compliance-driven demand"). - Top-retiree identification feeds the narrative layer with concrete context instead of a placeholder. - Each Retirement carries its real tx hash and timestamp, so downstream auditing can link back to the exact on-chain event. ## Scope Does NOT touch WF-MM-01 (price anomaly detection) or WF-MM-02 (liquidity monitor). The price oracle integration noted elsewhere in the README is still future work. The LCD-level error handling returns empty arrays on failure — a future follow-up might distinguish transient failures from genuine "no recent retirements". - Lands in: `agent-003-market-monitor/` - Changes: new tx-search client methods + rewritten WF-MM-03 + 16 new tests - Validate: `cd agent-003-market-monitor && npm test && npx tsc --noEmit` ## PR relationship Based on PR regen-network#99 (AGENT-003 unit tests) which is based on PR regen-network#80 (AGENT-003 initial implementation). If both land first, this PR rebases cleanly. Sibling PR to a forthcoming AGENT-004 real delegation tx-stream follow-up. Refs phase-2/2.2-agentic-workflows.md §WF-MM-03 Refs PR regen-network#80's design decision regen-network#3 (MVP proxy → real tx-stream)
Addresses Gemini review feedback on PR regen-network#103: ledger.getRecentRetirementTxs: * Query both v1 and v1beta1 MsgRetire type URLs instead of only v1. Older forwarders still emit v1beta1 and the parser already handles both event shapes — the tx-search filter was the only thing restricting the stream. Dedupe by tx hash so a tx appearing under both queries is only processed once. * Replace the empty catch with console.error so network issues are visible instead of silently degrading to "no recent retirements". parseRetirementsFromTx: * Prefer the flattened `tx.events[]` list over walking `tx.logs[i].events[]`. In modern Cosmos SDK versions the flat list is a superset, so walking both double-counted every event. Fall back to the per-log list only when the flat list is empty (very old LCD builds). * Parse the raw integer amount via BigInt first so higher-precision credit types (18-decimal biodiversity tokens on the roadmap) do not lose precision at the boundary. The downstream Retirement record still uses `number` for ergonomics — a safe downcast at current 6-decimal precision — with a comment noting the boundary. aggregateRetirementsByClass / WF-MM-03 orient: * USD value for retirement volume now uses the most recent WF-MM-02 median ask price per class (via a new store.getLatestMedianAsk helper and a `classPriceUsd` map threaded into the aggregator). Falls back to 1:1 USD when no snapshot exists yet. The aggregator's new optional `classPriceUsd` parameter keeps the unit tests' 1:1 assumption backwards-compatible. Full vitest suite: 68/68 passing. Typecheck clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ator Z-score numerator has to pair with its denominator's center. stddev is measured around the mean, so subtracting the median while dividing by stddev silently biases every score by (mean − median). On a skewed price distribution that bias is large enough to flip anomaly severity. Keep the median on the anomaly record as the human-readable reference price — it reads more intuitively than mean for thin markets — but compute the statistical test with the mean. Addresses Gemini review on feat/agent-003-real-retire-tx-stream: - price-anomaly-detection.ts:169 (class Z-score) - price-anomaly-detection.ts:178 (batch Z-score) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…k#80 onto regen-network#103 The real MsgRetire tx-stream branch (this PR) was cut from agent-003's pre-fixup state, so it was missing the four non-z-score fixes from regen-network#80's gemini-review commit (5d04ae6). Bringing them forward so regen-network#80 can be closed as fully superseded: - `LedgerClient.getCreditClasses` — walks every pagination page up to a 25-page safety cap (was silently truncating to oldest 200 classes; new classes were invisible to every workflow). - `liquidity-monitor` — top-10 depth sort pre-computes askUsd once per order instead of recomputing it in each comparator call. Also filters non-USD-stablecoin asks so arbitrary-denominated orders don't pollute class/batch baselines. - `index.ts` main loop — `setInterval` → recursive `setTimeout` so a slow cycle can't overlap with the next tick (no race conditions or database contention when the LCD/LLM is slow). - Z-score comment refreshed; the `-- mean not median --` fix from 8c75610 is kept as the authoritative version. The regen-network#80 retirement-delta work (batch-supply-delta + per-batch cumulative state) is intentionally **not** ported — this PR's tx-stream approach supersedes it wholesale and is exact where the delta method was approximate. The now-unused `batch_retirement_state` table and its `getPreviousRetiredAmount`/`upsertRetiredAmount` helpers are removed in the same commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…f/concurrency Addresses Gemini review feedback on PR regen-network#99: * Extract the duplicated helpers (median, stddev, classIdFromBatchDenom, isUsdStableDenom, computeDemandIndex) into a shared src/utils.ts. Workflow files now import from utils and re-export for backwards-compatible test imports. * ledger.ts: every empty catch block now logs via console.error with the method name + failing argument so a network hiccup or 5xx no longer silently degrades the agent's data. * liquidity-monitor.ts: the top-10 depth sort pre-computes askUsd once per order instead of recomputing the ratio in every comparator call. * retirement-tracking.ts: batch supply fan-out is chunked at concurrency 5 instead of firing 100 parallel LCD requests that would trip rate limits on public endpoints. * price-anomaly-detection.ts: z-score now uses mean/stddev instead of mixing median numerator with stddev denominator, and sellOrderToTrade skips non-USD-stablecoin asks instead of treating them as 1:1 USD. * retirement-tracking.test.ts now imports computeDemandIndex directly from utils.ts so the test does not transitively construct the SQLite-backed Store singleton — previously the test was failing with "database is locked" when run alongside the other workflow tests. Full vitest suite: 52/52 passing. Typecheck clean. Note: the PR regen-network#99 Gemini review flagged `claude-sonnet-4-5-20250929` as a "future date typo" in the config default — that is a false positive, the 2025-09-29 sonnet release is real and ships today, so no change is needed there. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
RFC-0001 packages the M013 fee distribution decision (15/30/50/5 split) derived from Phase 2 open-questions resolution. Marks agent authorship (Claude Code) with brawlaphant accountable; human sponsor required before graduation per RFC graduation rules (GRADUATION_RULES.md §2.1). This RFC unblocks Economic Reboot Proposal 1 (M013 activation) and Proposal 3 (M012 hard-cap setting, which depends on burn pool size). Gregory Landua recommended this as the first real test of the RFC governance pipeline (mentioned in PR regen-network#107 context). Evidence: - E1: M012 cadCAD equilibrium validation at burn_share=0.15 - E2: M012 equilibrium analysis (supply/validator compensation) - E3: Economic Reboot Proposals governance doc (Proposal A) - E4: M013 SPEC (parameter bounds) - E5: M012 SPEC (hard-cap burn interaction) All foundational infrastructure files (GRADUATION_RULES.md, _template.md, rfc-review-schema.yaml) also committed from PR regen-network#107 to unblock the RFC pipeline. Status: draft (awaiting human sponsor assignment) Next step: human sponsor posts forum thread, manages community deliberation (min 7 days), shepherds to ratified (requires ratifier sign-off). No code changes; governance doc only.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Code Review
This pull request introduces AGENT-003: Regen Market Monitor, a standalone Node.js agent that monitors the Regen ecocredit marketplace using three deterministic OODA workflows: price anomaly detection, liquidity monitoring, and retirement tracking. It includes direct Cosmos LCD integration, SQLite storage, and Claude-powered narrative reporting. Feedback on the implementation suggests correcting a typo in the default Anthropic model name, removing a redundant nullish coalescing check for batchDenom in the ledger client, and importing utility helpers directly in test files rather than re-exporting them from workflow files to reduce unnecessary coupling.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
|
||
| // Anthropic | ||
| anthropicApiKey: process.env.ANTHROPIC_API_KEY || "", | ||
| model: process.env.ANTHROPIC_MODEL || "claude-sonnet-4-5-20250929", |
There was a problem hiding this comment.
The default model name claude-sonnet-4-5-20250929 appears to contain a future date, which is likely a typo. This could cause runtime errors if the specified model does not exist. Please use a known valid model name, for example, claude-3-5-sonnet-20240620.
| model: process.env.ANTHROPIC_MODEL || "claude-sonnet-4-5-20250929", | |
| model: process.env.ANTHROPIC_MODEL || "claude-3-5-sonnet-20240620", |
| return hit ? hit.value : null; | ||
| }; | ||
|
|
||
| const batchDenom = attr("batch_denom") ?? attr("batchDenom") ?? ""; |
There was a problem hiding this comment.
The nullish coalescing operator is used with two very similar keys, batch_denom and batchDenom. It seems batchDenom is a typo and this is redundant. The correct attribute key for the batch denom in both v1 and v1beta1 EventRetire events is batch_denom.
| const batchDenom = attr("batch_denom") ?? attr("batchDenom") ?? ""; | |
| const batchDenom = attr("batch_denom") ?? ""; |
|
|
||
| // Re-exported so the existing unit-test suite can import helpers from | ||
| // this workflow file (the tests pre-date the utils.ts extraction). | ||
| export { median, stddev, classIdFromBatchDenom, isUsdStableDenom }; |
There was a problem hiding this comment.
First real test of the RFC governance pipeline proposed in #107 (deliberation chamber: Spec → RFC → on-chain proposal).
What this is
rfcs/_template.md,rfcs/GRADUATION_RULES.md,rfcs/schema/rfc-review-schema.yaml) so there's somewhere for an RFC to live.rfcs/RFC-0001-m013-fee-split.md: the M013 fee-split change (28/25/45/2 → 15/30/50/5 burn/validator/community/agent), which Gregory flagged in governance: bootstrap RFC process (rfcs/, graduation rules, review schema) #107 as the natural first real-world test case.Evidence cited in the RFC
docs/governance/needs-governance-proposals.mdProposal A (existing WG risk matrix + voting guidance)Status
draft— sponsor required before this moves to the next lifecycle stage. I'm tagged as the accountable party (stewarding the Tokenomics WG as of 2026-06-24). This is a proposal for the WG to react to, not a ratified decision — open questions are tracked at the bottom of the RFC (sponsor confirmation, community preference validation, review cadence).Built by an AI agent working from the existing merged mechanism contracts + your own governance docs as source of truth; I reviewed it before opening this PR. Flagging that origin clearly so reviewers weight it accordingly.