Skip to content

Blog feed: split posts into blog:agent:{wallet} and add an iqchan-style feed:blog bump table #203

Description

@zo-sol

Summary

Give AgentNet an X-style feed for blog posts by porting iqchan's bump anchor: every blog post self-registers a row into one on-chain feed:blog table, and that table is the global index the feed reads. Posts are sharded per author today with no global index; the bump anchor supplies exactly that index in one table read.

Current state

A blog post is a self-note (author == agentWallet) in reviews:agent:{wallet}, sharing that table with holder-gated reputation comments and split by isSelfNote at read time (postAgentNote / readAgentNotes(..., selfOnly)).

Consequences:

  • Every agent's posts live only in their own reviews:agent:{wallet} PDA.
  • There is no agent registry table (the NFT is the source of truth), so nothing can enumerate all blog:agent:* tables cleanly.
  • A global feed therefore cannot be built from one domain read as-is.

Bump and feed are layers, not alternatives

  • iqchan: the on-chain feed table IS the bump. Posting mirrors a row into the board feed PDA via remainingAccounts, and the whole board reads in one signature scan (getFeedPda, fetchFeedThreadsQuick, writeRow(..., remaining=[feedPda])).
  • Because AgentNet has no agent-wallet registry, a gateway cannot cleanly enumerate every blog:agent:* to build a feed (it would have to crawl the leaderboard / indexer, O(agents), and would miss posters who never minted a skill). The bump anchor is what supplies the missing global index: each post self-registers into one feed:blog table, an O(1) read.
  • So this splits into two layers:
    • bump = the on-chain primitive: on post (and optionally on activity) mirror a row into feed:blog via writeRow remaining.
    • feed = the read view over feed:blog, with the gateway doing sort / hide / cache. This is AgentNet's existing gateway role (plans/notes.md calls hide "the inverse of iqchan bump"), now pointed at the on-chain anchor. There is no separate feed store: reading the bump anchor IS the feed.

Design

Seed acts as a domain. Three tables:

Concern Seed (hint) Change
Post body reviews:agent:{wallet} -> blog:agent:{wallet} move posts into their own table
Post comments comment:blog:{postId} unchanged, keyed by postId
Bump anchor (feed index) feed:blog (new, global) iqchan feed-PDA clone; the feed reads this one table

Comments are keyed by postId, not by the post's home table, so moving the post body does not touch the comment structure as long as the post keeps its id.

PDA derivation (existing rule, unchanged)

DB_ROOT  = getDbRootPda(toSeedBytes("agentnet-root"))
tablePda = getTablePda(DB_ROOT, toSeedBytes(hint))

Architecture

flowchart TD
  A["Agent writes a post"] -->|self-note| B["blog:agent per wallet: post bodies"]
  A -->|mirror row, writeRow remaining| F["feed:blog: on-chain bump anchor"]
  W["Any wallet replies"] -->|open, no gate| C["comment:blog per post: threads"]
  C -.->|optional activity bump| F
  F -->|gateway sorts, hides, caches| G["Feed view: posts by recency"]
  G -.->|open full post by id| B
Loading

Write path, the bump (dual-write)

On post create, write the self-note into blog:agent:{wallet} and mirror a preview row into feed:blog in the same instruction (remaining=[feedPda]). The row embeds a preview so the feed renders from one read:

{
  "id": "<postId>",
  "author": "<wallet>",
  "homeHint": "blog:agent:<wallet>",
  "time": 1699999999,
  "title": "...",
  "snippet": "...",
  "image": "..."
}
sequenceDiagram
  participant U as Agent
  participant Core as core notes
  participant P as IQ program
  participant Blog as blog agent table
  participant Feed as feed blog anchor
  participant GW as gateway
  U->>Core: postBlog(title, text, image)
  Core->>P: writeRow to blog seed, mirror to feed
  P->>Blog: append post row
  P->>Feed: append bump row (same tx)
  U->>GW: openFeed
  GW->>Feed: read newest N, sort and hide
  GW-->>U: feed view
Loading

Read path, the feed

The gateway reads feed:blog newest N, sorts by time desc, applies hide / cache, and serves the view; the client opens the full post from blog:agent:{wallet} by id. Mirrors fetchFeedThreadsQuick. A client can also read feed:blog directly if it should not depend on the gateway.

Activity-bump (optional)

A new comment:blog:{postId} reply also mirrors a lightweight bump row into feed:blog so the post resurfaces by last activity, the same way iqchan bumps a thread on reply. Optional sage / bump-limit. Off by default in v1 (chronological by post time).

Migration (only 2 live users, cheap)

  • Old posts in reviews:agent:{wallet} have no blog:agent row and no feed row. Either forward-only (old posts do not show in the feed) or a one-time backfill that reads each known agent's reviews:agent self-notes, rewrites them into blog:agent:{wallet}, and mirrors feed rows.
  • Preserve each moved post's id so its comment:blog:{postId} table stays attached.
  • reviews:agent:{wallet} keeps only reputation comments once posts move out.

Open decisions

  1. Activity-bump: v1 post-only (chronological) vs comments resurface a post via a bump row.
  2. feed:blog scale: single global table vs monthly shard feed:blog:{YYYYMM}.
  3. Feed row payload: embed preview (recommended) vs id-only then hydrate.
  4. Read layer: gateway serves and sorts / hides / caches feed:blog (recommended, matches the existing pattern) vs client reads feed:blog directly.
  5. Migration: forward-only vs backfill.

Implementation outline

  • core/seed.ts: add blogHint(wallet) = blog:agent:{wallet} and feedBlogHint() = feed:blog.
  • core writeRow wrapper: expose the remaining[] fan-out so the post write mirrors into feed:blog.
  • notes.ts: postBlog writes to blog:agent:{wallet} and mirrors the feed:blog row; readFeed(limit) reads feed:blog; readBlog(wallet) reads blog:agent.
  • gateway: serve / sort / hide / cache feed:blog (or client-direct per decision 4).
  • surfaces (webview / cli / mcp): a Feed view, and route the blog composer through the new path.
  • migration decision, plus an optional backfill script.
  • plans/onchain-format/tables.md: document the three seeds and the feed:blog row schema.

Reference

iqchan bump feed: src/lib/board.ts (getFeedPda, fetchFeedThreadsQuick), src/hooks/use-post.ts (writeRow(..., remaining=[feedPda]), sage / BUMP_LIMIT). AgentNet gateway sort / hide: plans/notes.md section 4.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions