Repository navigation
feat(agent-003): market monitor runtime, workflows, tests, CI wiring - #112
brawlaphant wants to merge 3 commits into
Conversation
Adds agent-003-market-monitor: a Layer 1 read-only agent over the Regen ecocredit marketplace. Three deterministic OODA workflows (price anomaly detection, liquidity monitoring, retirement tracking), a Cosmos LCD ledger client, SQLite storage, vitest unit tests, and the agent-003 rows in the Agents CI typecheck matrix. Split out of regen-network#108 at the reviewer's request, scope only. Carries the implementation commits from regen-network#108 with no code change; the ci.yml hunk is rebased onto current main so it only adds the agent-003 steps. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133qQ2VHrvhormH9VACe8J6
…kflow files) The 8-line CI hunk for agent-003 is in the PR body; the runtime, storage, workflows and tests are unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133qQ2VHrvhormH9VACe8J6
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133qQ2VHrvhormH9VACe8J6
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 73abafdd29
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // baseline would pollute the class/batch medians with 1:1-priced | ||
| // garbage. Downstream code can plug in a real price oracle. | ||
| if (!isUsdStableDenom(order.ask_denom)) return null; | ||
| const pricePerCredit = ask / qty; |
There was a problem hiding this comment.
Treat ask_amount as the per-credit price
Regen marketplace ask_amount is already the amount requested for each credit unit, so dividing it by the order quantity makes identical unit prices appear cheaper as order size grows. This corrupts every anomaly baseline and z-score; the same incorrect division is repeated by askUsd in liquidity-monitor.ts, so both conversion paths need to use the unit ask amount directly, with appropriate denomination conversion.
Useful? React with 👍 / 👎.
| for (const tx of txResponses) { | ||
| const hash = typeof tx.txhash === "string" ? tx.txhash : ""; | ||
| if (hash && seenHashes.has(hash)) continue; | ||
| if (hash) seenHashes.add(hash); | ||
| retirements.push(...this.parseRetirementsFromTx(tx)); |
There was a problem hiding this comment.
Restrict retirement transactions to the configured window
The workflow labels these results as a trailing 168-hour summary, but this loop accepts every transaction returned by the count-limited query without checking tx.timestamp. On a low-activity chain the report therefore includes arbitrarily old retirements, while more than 200 retirements in a week truncates the other end of the intended window; paginate newest-first and stop/filter at the configured cutoff.
Useful? React with 👍 / 👎.
| "start": "node --import tsx src/index.ts", | ||
| "dev": "node --import tsx --watch src/index.ts", | ||
| "analyze": "node --import tsx src/index.ts --once", |
There was a problem hiding this comment.
Load the documented .env file before validating configuration
Following the README's quick start only creates agent-003-market-monitor/.env, but none of these commands loads that file and Node does not read it automatically. Consequently npm start, npm run dev, and npm run analyze all reach validateConfig() with an empty API key and exit unless the user separately exports the variables; load .env in the process or add an environment-file option compatible with the supported Node versions.
Useful? React with 👍 / 👎.
| params.set("pagination.limit", String(limit)); | ||
|
|
||
| const data = await this.get( | ||
| `/regen/ecocredit/marketplace/v1/sell-orders?${params.toString()}` | ||
| ); | ||
| return (data.sell_orders || []) as SellOrder[]; |
There was a problem hiding this comment.
Paginate through all open sell orders
This request sets a page size but never follows pagination.next_key, so only the first 200 orders for anomaly detection or first 500 for liquidity monitoring are visible. Once the marketplace exceeds either caller's limit, later orders and potentially entire credit classes are silently omitted, producing incomplete depth totals and missed anomalies; iterate pages as getCreditClasses() already does.
Useful? React with 👍 / 👎.
| // Strip any decimal suffix — the on-chain event always emits | ||
| // the smallest-unit integer, but some forwarders prettify it. | ||
| const intPart = quantityRaw.split(".")[0] || "0"; | ||
| quantityBig = BigInt(intPart); |
There was a problem hiding this comment.
Preserve fractional retirement quantities
Credit quantities can be fractional according to their credit type precision, but splitting at the decimal point makes a retirement such as 0.5 become zero and be discarded, while 12.75 is underreported as 12. This directly corrupts retirement totals and demand indices for fractional-credit batches; parse the decimal amount rather than coercing only its integer prefix.
Useful? React with 👍 / 👎.
|
|
||
| // Polling — market monitor runs faster than governance analyst | ||
| // because trade/retirement events move quickly. | ||
| pollIntervalMs: (parseInt(process.env.POLL_INTERVAL_SECONDS || "300", 10)) * 1000, |
There was a problem hiding this comment.
Reject invalid polling intervals
If POLL_INTERVAL_SECONDS contains a typo, zero, or a negative value, this expression produces NaN or a non-positive timeout. setTimeout then schedules subsequent cycles immediately, creating a tight loop that repeatedly calls the LCD and paid Anthropic API; validate that the parsed value is finite and positive before constructing the interval.
Useful? React with 👍 / 👎.
| await fetch(config.discordWebhookUrl, { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ | ||
| username: config.agentName, | ||
| content, | ||
| }), | ||
| signal: AbortSignal.timeout(10_000), | ||
| }); |
There was a problem hiding this comment.
Check Discord webhook response status
A Discord webhook returning 400, 401, 404, 429, or 5xx still fulfills fetch, so this function treats the post as successful and emits no diagnostic. For anomaly alerts this happens after the alert has been persisted for deduplication, meaning the failed delivery will not be retried on later cycles; inspect res.ok and surface non-success responses.
Useful? React with 👍 / 👎.
| "test": "vitest run", | ||
| "test:watch": "vitest", | ||
| "typecheck": "tsc --noEmit" |
There was a problem hiding this comment.
Add the new package to continuous integration
The reviewed commit adds build, typecheck, and test scripts for this standalone package, but the repository-wide search and inspection of .github/workflows/ci.yml show that the Agents job only installs and typechecks agents/, agent-001, and agent-002. As a result none of agent-003's source or tests runs in CI, despite the commit describing CI wiring, so regressions in this package can merge unchecked.
Useful? React with 👍 / 👎.
| for (const anomaly of orientation.anomalies) { | ||
| const existing = store.getAnomalyAlertSeverity(anomaly.tradeId); | ||
| // Dedupe: don't re-alert at the same or lower severity. | ||
| if (existing === "CRITICAL") continue; | ||
| if (existing === "WARNING" && anomaly.severity !== "CRITICAL") continue; | ||
|
|
||
| alertsToPublish.push(anomaly); |
There was a problem hiding this comment.
Enforce confidence thresholds before publishing alerts
This decision path publishes every WARNING or CRITICAL anomaly regardless of its computed confidence, even though the shared AGENT-003 character's decision framework in agents/packages/agents/src/characters/market-monitor.ts requires confidence below 0.7 to remain internal and 0.7–0.9 to receive review. With the minimum five samples, the workflow commonly assigns confidence around 0.10–0.17 and still posts the alert automatically, defeating the false-positive safeguard; gate publication and route intermediate confidence to review.
Useful? React with 👍 / 👎.
| } | ||
| } | ||
|
|
||
| const report = await describeLiquidity(snapshot, previous); |
There was a problem hiding this comment.
Persist deterministic snapshots before generating narratives
If Anthropic times out or rate-limits any class here, decide throws before returning and act is never called, so none of the already-computed liquidity snapshots from that cycle are persisted; a failure on a later class also discards earlier successful narrative results. This makes deterministic market history—and the prices subsequently consumed by retirement tracking—depend on LLM availability, contrary to the narrative-only role described for Claude; persist the snapshots independently and treat narrative failures per class.
Useful? React with 👍 / 👎.
agent-003-market-monitor/,.github/workflows/ci.ymlcd agent-003-market-monitor && npm ci && npx tsc --noEmit && npm testWhat this is
The implementation half of the split requested in review on #108 (#108 (comment)). Engineering surface only. No governance artifact in this PR.
Depends on: nothing. It shares no files with the RFC PR and can be reviewed and merged independently of it. It does not implement, enable or assume the M013 fee split that RFC-0001 proposes; it reads marketplace state and reports.
What is in it
src/monitor.ts,src/ooda.ts,src/index.ts: the agent loop, following the AGENT-002 shape.src/ledger.ts: direct Cosmos LCD client, including the MsgRetire tx stream used by the retirement workflow.src/store.ts: SQLite persistence.src/workflows/: price anomaly detection, liquidity monitoring, retirement tracking.src/output.ts: narrative reporting..github/workflows/ci.yml: install and typecheck steps foragent-003-market-monitor/, matching the existing agent-001 and agent-002 rows.Local results
npx tsc --noEmit: clean, Node 20.npm test(vitest): 68 tests across 4 files, all passing.node scripts/verify.mjs: PASS.node scripts/build-mechanism-index.mjs --check: OK.The
Contracts (Rust)CI job is expected to fail on this branch. It fails onmainitself and on doc-only PRs for the reason recorded in #110, which is independent of this change. Nothing in this PR touchescontracts/.History
The code carries forward the review fixes from the earlier closed PRs #80 and #103, including the Gemini review items on the MsgRetire tx stream and the Z-score numerator. The ci.yml hunk here is rebased onto current
main, so it only adds the agent-003 steps and does not disturb the agent-001 wiring from #96 or the root install change from #106.Files
24 files.
agent-003-market-monitor/(23 new files, includingpackage-lock.json) and.github/workflows/ci.yml(8 added lines).Origin
Written by an AI agent and reviewed by a person before opening. Flagged so reviewers can weight it accordingly.