[ CONCEPTS ]
Concepts and glossary
Six objects make up the whole system. Learn what each one is and the rest of the documentation stops needing footnotes.
Operator
The human who owns a deck. Every row in the database — every watch, proposal, ledger event, and token — is scoped to one operator address, and no query ever crosses that boundary.
There are two ways to become an operator. A wallet session proves control of an Ethereum address by signing an EIP-4361 message, verified server-side; your address becomes your scope. A demo session mints a fresh throwaway address per visit, so two people trying the demo never see each other’s data.
The session itself is an HMAC-signed, httpOnly cookie with a 12-hour life. It carries no database row — tampering with it fails signature verification and you land back on the login screen.
Watch
A standing instruction to keep an eye on one symbol. A watch has a symbol, a threshold, an action, an assigned agent, a status, and an optional note the agent reads when drafting.
| FIELD | VALUES | MEANING |
|---|---|---|
symbol | Uppercase ticker, ≤12 chars | What is being watched. |
thresholdPct | −2, −3, −5, −7, −10 | Trips when change since previous close is at or below this. |
action | alert · dca · rebalance | What kind of proposal gets drafted when it trips. |
agent | Assigned automatically | Derived from the action, so the agent shown before you submit is the one that does the work. |
status | active · paused | A paused watch is evaluated by nothing. |
Proposal
A drafted portfolio move waiting on a human. It carries a title, a rationale, the current allocation, a proposed allocation, the notional the change would move, and a status.
pending
Freshly drafted. The only state from which approve and reject are legal.
approved
You signed off. It is now work waiting to be done on some other rail — this is what an agent should be looking for.
executed
Someone reported a fill against it through record_execution. Terminal.
rejected
You said no. Terminal.
Illegal transitions are refused with a 409 rather than silently ignored. You cannot execute something that was never approved, and you cannot re-decide something already decided.
The lifecycle
watch (active)
│ price ≤ threshold, not in cooldown
▼
proposal (pending) ──── operator rejects ───▶ rejected ┐
│ │ terminal
│ operator approves │
▼ │
proposal (approved) │
│ your agent executes elsewhere, │
│ then calls record_execution │
▼ │
proposal (executed) ─────────────────────────────────────┘
every arrow above also appends one ledger eventLedger
The append-only record of everything that happened, newest first. Nothing in the system edits or deletes a ledger event. Eight kinds are recorded:
request— an operator asked for something, e.g. filed a watch.alert— a threshold was crossed.plan— an agent drafted a proposal.approval/rejection— a human decided.execution— a fill was reported from outside.settlement— a reported fill settled.system— something operational, such as a failed draft.
The point of separating these is attribution. Read a ledger and you can always say which actions an agent took and which a person took.
Agent
A named role that drafts. Which agent takes a watch is decided by its action, not chosen at random:
| ACTION | AGENT | DRAFTS |
|---|---|---|
| alert | CYCLOPS-MONITOR | An escalation — what moved and whether it warrants attention. |
| dca | ARGUS-INSPECTOR | A staged accumulation plan. |
| rebalance | PENELOPE-WEAVER | A target allocation across the tracked symbols. |
Behind all three is one model call through OpenRouter, defaulting to deepseek/deepseek-v4-flash and overridable with OPENROUTER_MODEL. The model is given a JSON schema and its reply is re-validated against that schema in code — a model is never trusted to have obeyed its own contract.
Market status
Every priced response carries provenance alongside the numbers, so nothing has to infer whether a price is real:
source— which provider answered.live— true only when every symbol resolved from a real market source. One fallback makes it false.stale— a resolved quote is older than the 120-second refresh window.detail— why the prices are not live, when they are not. This is the first field to read when something looks wrong.
Quotes are cached in D1 and shared by every deck: a quote for NVDA is the same fact for everyone, and caching it globally is what keeps the system inside the provider’s rate limit.