10 — Decision Log (ADRs)

Why the system is shaped this way. Each entry: the decision, the alternatives that lost, and the cost we accepted.


ADR-001 — Own the inference gateway rather than reselling a provider

Decision. Serve open-weight models from our own GPU cluster behind an OpenAI-compatible gateway.

Alternatives rejected. Proxying or reselling a third-party API, which would put user prompts in someone else's infrastructure and leave no privacy story of our own.

Why. It is the version where "your prompts stay in our infrastructure" is an architectural fact rather than a policy promise.

Cost accepted. We own capacity planning, GPU reliability and model upgrades. The gateway abstraction keeps model ids stable regardless of what serves them.


ADR-002 — LangChain/LangGraph as the agent runtime (replacing pydantic-ai)

Decision. Rebuild the agent core on langchain.agents.create_agent with LangGraph, and express autonomous work as explicit StateGraphs.

Alternatives rejected. Keep pydantic-ai (clean, but request/response shaped); write a bespoke orchestrator (all the cost, none of the ecosystem).

Why. Chat is one graph among many. Autonomous pipelines need explicit state, conditional loops with budgets, per-node retries, and — later — checkpointing for resumable long runs. That is a graph runtime. LangGraph also brings langchain-mcp-adapters, which made the tool plane a config file rather than integration code.

Cost accepted. A heavier dependency tree and a fast-moving API. Contained by keeping the public surface (chat_request, memory) unchanged, so the rest of the app was untouched by the migration.

Bonus discovered during migration. Exposing the knowledge base as a search_knowledge tool made retrieval available directly in chat.


ADR-003 — twitterapi.io for reads, official API for writes

Decision. All X/Twitter reads go through twitterapi.io in the MCP server. Writes stay on the official API in workers. twikit is removed everywhere.

Alternatives rejected. (a) Session-scraping libraries, which log in with real account credentials and carry ban risk. (b) The official API for reads, which does not fit the read patterns or budget of this workload.

Why. Reads are the fuel for every signal; they must be cheap, structured and safe. Pay-per-request with an API key has no account to ban and no session to expire.

Cost accepted. A third-party dependency on the read path, absorbed by retries with backoff and a normalised tweet shape that isolates consumers from the provider — which is why the swap touched one file.


ADR-004 — Entitlement from chain state, cached off-chain

Decision. Tiers derive from HunterTierLock / HunterStaking / HunterSubscription; an indexer materialises wallet→tier; the gateway reads the cache.

Alternatives rejected. (a) RPC call per inference request — adds latency and a hard dependency on RPC uptime to every paid call. (b) Database as source of truth — then access rights could be granted without payment, destroying the token's utility.

Why. The chain is the only trustworthy record of who paid, but it is too slow to consult per request.

Cost accepted. Eventual consistency (~minutes) between locking and access. Made explicit in §04, and the resolver fails closed when it has never seen a wallet.


ADR-005 — uv over poetry for Python services

Decision. Standardise on uv with uv.lock; delete poetry workflows.

Why. poetry install does not install PEP 621 [project.optional-dependencies], so CI environments could come up without a test runner while appearing correctly configured. uv matches the declared PEP 621 metadata and is dramatically faster.

Cost accepted. Contributors need uv installed. Documented in every README.


ADR-006 — Non-upgradeable contracts

Decision. No proxies, no delegatecall. Changes mean a new deployment and a migration.

Alternatives rejected. UUPS/transparent proxies — convenient, but an upgrade key is a key that can rewrite the rules under users holding locked tokens.

Why. The product promise is that locking is safe and revenue routing is fixed. An upgrade key contradicts that, and it is the single largest trust liability in a small-cap token contract.

Cost accepted. Migrations are manual and public. Deliberate friction where money is involved.


ADR-007 — Pin the npm wasm solc compiler

Decision. Override Hardhat's compiler-fetch subtask to use the npm-installed solc wasm build for 0.8.24.

Why. Default Hardhat downloads from binaries.soliditylang.org, which fails in restricted networks (it failed in this very build environment) and makes builds non-reproducible across machines.

Cost accepted. Slightly slower compilation; the version must be bumped in two places (hardhat.config.js and package.json).


ADR-008 — x402 for agent-payable inference

Decision. Accept per-call USDC payment on Base for requests without an API key, capped at a low tier.

Why. Agent-to-agent commerce is real (Virtuals ACP, x402 micropayments) and buyers are agents that cannot complete a signup flow. This turns the gateway into infrastructure other agents can consume, and every payment is revenue for the splitter.

Cost accepted. Verify-then-serve-then-settle means a settlement failure yields one unpaid call. Bounded by the per-call price and logged for reconciliation rather than retried inline.


ADR-009 — Publish signals before outcomes are known

Decision. Record every published call at publication time with a unique external_id; score it later in a separate table; expose both publicly.

Why. An accuracy record is only credible if losers cannot be quietly dropped. Recording first, scoring second, and letting anyone read both makes the number adversarially checkable.

Cost accepted. Our misses are public. That is the point.


ADR-010 — Raw JSON-RPC on the marketing site instead of a web3 SDK

Decision. The flywheel dashboard reads Base with fetch + eth_call and precomputed function selectors; no viem/wagmi dependency.

Why. A public marketing page should not carry a wallet SDK to display four numbers. The result has zero new dependencies and cannot break on an SDK upgrade.

Cost accepted. Selectors are hand-computed constants with a comment. If the page ever needs writes, adopt viem and upgrade TypeScript then.


ADR-011 — Interfaces frozen, planes owned end to end

Decision. Four contracts (§05) are the coordination points; work is dispatched per plane, not per repository.

Why. Across many repositories, per-repo assignment creates constant cross-plane blocking. Freezing the interfaces means the tool plane, app plane and chain plane can each move without a release train.

Cost accepted. Interface changes are expensive by design and need the versioning process in §05.


ADR-012 — Route inference through OpenRouter, not the owned cluster, for now

Decision. hunter-gateway's default HUNTER_MODELS_CONFIG points at OpenRouter (https://openrouter.ai/api/v1) rather than a local vLLM endpoint. UpstreamModel gained an optional api_key_env field so an upstream can require auth; self-hosted vLLM still needs none, so this is additive, not a rewrite of the proxy.

Why. The owned cluster (2× DGX + 3× AGX) is not production-ready, and research/09 in hunter-strategy showed self-hosting only pays off at sustained utilization we don't have yet — idle owned hardware is worth nothing, but shipping today is worth more than waiting for the cluster. The gateway's model routing was already upstream-agnostic (upstream_base_url per model), so this is a config change, not an architecture change: swapping back to owned hardware later touches HUNTER_MODELS_CONFIG, not calling code.

Cost accepted — and this is the one to be honest about. The privacy guarantee in §08 ("no third-party model sees user data") does not hold while OpenRouter is upstream: prompts leave our infrastructure and are subject to OpenRouter's data policy and that of whichever provider it routes to. §08 and the litepaper are updated to say so in present tense rather than describing the end state as already true. This should be treated as temporary and disclosed, not quietly normalized — revisit when the cluster is ready.

HUNTER · $DRPXBT · drpxbt.xyz edit this page