Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions workspace/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,46 @@ Avoids re-running `nfts list-by-account` on every heartbeat. Invalidate a chain'

So `events by-account` only streams the tail since last heartbeat.

### `memory/scan_state.json` — once-per-day scan timestamps
### `memory/scan_state.json` — once-per-day scan timestamps + resumable cursors

Tracks the last run of HEARTBEAT.md steps 5 (drops) and 6 (trending), which run at most every ~20 hours.
Tracks the last run of HEARTBEAT.md steps 5 (drops) and 6 (trending), which run at most every ~20 hours. Also holds resumable cursors for any per-collection trait scan in progress, so a heartbeat that hits 429 mid-scan can pick up where it left off.

```json
{
"last_drop_scan": "2026-04-17T02:00:00Z",
"last_trending_scan": "2026-04-17T02:00:00Z"
"last_trending_scan": "2026-04-17T02:00:00Z",
"trait_scans": {
"<slug>": {
"started_at": "2026-04-17T02:00:00Z",
"completed_at": null,
"next_cursor": "abc123",
"tokens_seen": 1450
}
}
}
```

When a trait scan completes, set `completed_at`, clear `next_cursor`, and write the result to `memory/trait_holders.<slug>.json` (below).

### `memory/trait_holders.<slug>.json` — per-collection trait index

Built by the whole-collection scan pattern in SOUL.md → *How You Work* → *Trait filtering*. Re-scan at most once per day. Index is `trait_type` → `value` → `token_id[]`.

```json
{
"slug": "tiny-dinos-eth",
"updated_at": "2026-04-17T02:30:00Z",
"by_trait": {
"feet": {
"hoverboard": ["6292", "5996"],
"skateboard": []
}
}
}
```

Only create one of these for collections the user has explicitly asked to track by trait — they're expensive to maintain.

### `MEMORY.md` (in `workspace/`, not `memory/`)

Free-text long-form observations that don't fit a schema: API quirks, volatile slugs, seller wallets that pattern wash-trade, drops that disappointed, specific things the user said about their taste that you want to remember word-for-word.
Expand Down
1 change: 1 addition & 0 deletions workspace/SOUL.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Before submitting any transaction, run the gate in order. Any RED stops the flow
- **Ordering:** run requests sequentially on the shared `OPENSEA_API_KEY`. Parallel fans out 429s.
- **Memory:** `memory/floors.json` = latest watchlist snapshot (overwrite each heartbeat). `memory/actions.jsonl` = append-only log of anything with a side-effect. `memory/taste.json` = structured taste model you maintain per collection. See `AGENTS.md` for schemas.
- **Stale-reject listings:** before proposing a buy, re-fetch the listing — OpenSea will 404 or return a new order hash if it expired.
- **Trait filtering is client-side.** Enumerate the schema with `collections traits <slug>`, then fetch tokens and filter on `traits[]` locally. Two patterns: (a) listed-only — pull `listings all <slug>`, then `nfts get` per token (cheap, ~1 + N_listings calls); (b) whole-collection — paginate `nfts list-by-collection`, fetch traits per token, cache the trait → token-id index in a per-collection `memory/trait_holders.<slug>.json` (expensive, do at most once per day, throttle to ≤1 req / 0.3s, persist cursor in `memory/scan_state.json`, resume on 429). See `skills/opensea/SKILL.md` → *Reading NFT data* → *Filtering NFTs by trait*.

## Wallet

Expand Down
Loading