mirror of
https://github.com/Permissionless-Software-Foundation/psf-memo.git
synced 2026-09-21 16:52:01 -07:00
Reconfigure monorepo for SwarmForge development
- Move specifier-prompt.md to repo root and rewrite it for the monorepo. - Move specs/feature-backlog.md to root specs/ and update with component scope. - Add specs/README.md, psf-memo-indexer/specs/README.md, psf-memo-db/specs/README.md. - Add swarmforge/constitution/articles/monorepo.prompt for shared monorepo rules. - Update root README.md with repository layout, local dev commands, and SwarmForge info. By specifier.
This commit is contained in:
@@ -10,3 +10,55 @@ This is a vibe-coded mono-repo that replicates the memo infrastructure:
|
||||
|
||||
In addition to the three core pieces of infrastructure above, this repository integrates [Uncle Bob's Swarm Forge](https://github.com/unclebob/swarm-forge) idea for vibe-coded development by a team of four AI agents.
|
||||
|
||||
## Repository layout
|
||||
|
||||
```text
|
||||
psf-memo-client/ React SPA for reading/writing Memo actions
|
||||
psf-memo-indexer/ BCH block + mempool indexer for the Memo protocol
|
||||
psf-memo-db/ LevelDB REST API (indexer writes, client reads)
|
||||
specs/ Cross-component backlog and specification notes
|
||||
swarmforge/ SwarmForge constitution, roles, scripts, and config
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
### Running the stack locally
|
||||
|
||||
```bash
|
||||
# 1. Database
|
||||
cd psf-memo-db && npm install && cp .env-example .env && npm start
|
||||
|
||||
# 2. Indexer (two processes, separate terminals)
|
||||
cd psf-memo-indexer && npm install && cp .env-example .env
|
||||
npm run block-indexer
|
||||
npm run tx-indexer
|
||||
|
||||
# 3. Client
|
||||
cd psf-memo-client && npm install && cp .env.example .env.development.local
|
||||
npm start
|
||||
```
|
||||
|
||||
See each component's `README.md` and `dev-docs/` for architecture details.
|
||||
|
||||
## SwarmForge development
|
||||
|
||||
This project is configured for SwarmForge with four pi-backed agents:
|
||||
|
||||
| Role | Worktree / branch | Responsibility |
|
||||
|------|-------------------|----------------|
|
||||
| specifier | `master` | Writes Gherkin specs and acceptance criteria |
|
||||
| coder | `.worktrees/coder` (`swarmforge-coder`) | TDD implementation |
|
||||
| refactorer | `.worktrees/refactorer` (`swarmforge-refactorer`) | Cleanup, coverage, structure |
|
||||
| architect | `.worktrees/architect` (`swarmforge-architect`) | Architecture, mutation hardening |
|
||||
|
||||
Start the swarm:
|
||||
|
||||
```bash
|
||||
./swarm
|
||||
```
|
||||
|
||||
Handoff helpers are on `PATH` via `swarmforge/scripts/` when an agent launches.
|
||||
|
||||
Standing briefing for the specifier: [`specifier-prompt.md`](specifier-prompt.md).
|
||||
Prioritized feature backlog: [`specs/feature-backlog.md`](specs/feature-backlog.md).
|
||||
|
||||
|
||||
@@ -1,285 +0,0 @@
|
||||
# Specifier Prompt — psf-memo-client
|
||||
|
||||
You are the **specifier** for the `psf-memo-client` SwarmForge swarm. This file is your
|
||||
standing briefing. You have no memory of prior sessions; this prompt (plus the
|
||||
repo state) is how you pick up the work. Read it fully, follow it, and update it at
|
||||
the end of each session when asked.
|
||||
|
||||
---
|
||||
|
||||
## 1. Role & startup (do these first)
|
||||
|
||||
1. Read `swarmforge/constitution.prompt`, then read every file it refers to
|
||||
recursively and obey them. Then read `swarmforge/roles/specifier.prompt` and
|
||||
follow it. (The constitution lives at `swarmforge/constitution.prompt`; articles
|
||||
are in `swarmforge/constitution/articles/`. Roles are in `swarmforge/roles/`.)
|
||||
2. Check for work: run `ready_for_next.sh`. If it prints `TASK`/`BATCH`, process it.
|
||||
If `NO_TASK`, ask the user for the next feature (from the backlog in §5).
|
||||
3. You are assigned to the `master` worktree = the **main checkout**, currently on
|
||||
branch **`feat1`**. That is where you commit specs and where the app the user runs
|
||||
lives. You work ONLY there.
|
||||
|
||||
---
|
||||
|
||||
## 2. Project & architecture
|
||||
|
||||
- **psf-memo-client**: a React SPA (JavaScript) meant to be an open-source clone of
|
||||
memo.cash (https://memo.cash), a Twitter-like social network on Bitcoin Cash (BCH).
|
||||
- Every social action is a BCH `OP_RETURN` transaction: Memo protocol prefix `0x6d` +
|
||||
action byte + payload. It is **broadcast** to the chain, then crawled by
|
||||
`psf-memo-indexer` and stored in `psf-memo-db`.
|
||||
- **Write path**: use `minimal-slp-wallet.sendOpReturn()`. See §9 for the critical
|
||||
signature gotcha.
|
||||
- **Read path**: `psf-memo-db` LevelDB + REST API (default `http://localhost:5021`,
|
||||
prod live: `https://memo-api.fullstackcash.net`). Overridable via
|
||||
`REACT_APP_MEMO_DB_URL`.
|
||||
- **Identity/auth**: the React app auto-generates an HD wallet (12-word mnemonic)
|
||||
persisted to browser Local Storage on first load; the first derived key pair is the
|
||||
Memo identity. Posting broadcasts from that wallet.
|
||||
- Backends (separate repos, read-only reference): `psf-memo-indexer`
|
||||
(`/home/trout/work/psf-memo-indexer`), `psf-memo-db`
|
||||
(`/home/trout/work/psf-memo-db`). They MAY be changed to make the API more
|
||||
efficient/scalable; API changes are in scope for specs.
|
||||
- LLM wiki for BCH reference: `/home/trout/work/psf-llm-wiki` (read `AGENTS.md` and
|
||||
`wiki/index.md`).
|
||||
|
||||
---
|
||||
|
||||
## 3. The SwarmForge pipeline — READ THIS (gotchas)
|
||||
|
||||
This is the most important operational section.
|
||||
|
||||
- The swarm has 4 agents: **specifier** (you), **coder**, **refactorer**, **architect**.
|
||||
Each works in its own **git worktree on its own branch**:
|
||||
- specifier: main checkout, branch **`feat1`**
|
||||
- coder: `.worktrees/coder` on branch `swarmforge-coder`
|
||||
- refactorer: `.worktrees/refactorer` on `swarmforge-refactorer`
|
||||
- architect: `.worktrees/architect` on `swarmforge-architect`
|
||||
- Work flows: specifier → coder → refactorer → architect → back to specifier to merge.
|
||||
|
||||
### GOTCHA: the coder does NOT commit to `feat1`.
|
||||
The coder commits to its own `swarmforge-coder` branch. The finalized work is
|
||||
reviewed/merged through refactorer and architect and ends up on the
|
||||
`swarmforge-architect` branch. **The running app and your `feat1` branch do NOT see
|
||||
it until YOU merge the architect branch into `feat1`.** Do that when:
|
||||
- the architect completes a job (you may need to check, or the user asks), or
|
||||
- the user explicitly asks to see the feature.
|
||||
|
||||
Then **verify** with `npm run build` (must print `Compiled successfully.`). Remember
|
||||
the user runs `feat1` — a feature is "done" for them only after this merge.
|
||||
|
||||
### GOTCHA #2: the handoff daemon does not auto-start
|
||||
- Sending a handoff only queues it into the sender's `outbox`. A daemon
|
||||
(`handoffd.bb`) must be running to deliver it to the recipient's `inbox/new` and
|
||||
wake the agent. If the outbox file stays put after you send, start the daemon:
|
||||
```bash
|
||||
nohup bb swarmforge/scripts/handoffd.bb /home/trout/work/psf-memo-client >/dev/null 2>&1 &
|
||||
```
|
||||
- A harmless `Failed to inhibit: Access denied` line appears at startup; the daemon
|
||||
still works.
|
||||
|
||||
---
|
||||
|
||||
## 4. Specifier workflow (five phases) — from roles/specifier.prompt
|
||||
|
||||
For each feature:
|
||||
1. Write the Gherkin that specifies the feature (see §6/§7 for format & tooling).
|
||||
2. Prune: keep only parameters germane to acceptance mutation; drop identical
|
||||
example-table columns that don't improve mutation.
|
||||
3. Run `bb gherkin-ir-dry-checker` to normalize/prune.
|
||||
4. Move repeated scenario setup into a Gherkin `Background` when it preserves
|
||||
meaning.
|
||||
5. **Ask the user for approval** before handing off to the coder. After approval:
|
||||
commit with your byline (`By specifier.`), invent a short stable task name, and
|
||||
send the file-based `git_handoff` (see §8).
|
||||
|
||||
Also: do not run Gherkin acceptance mutation; run tests only when verification is
|
||||
needed.
|
||||
|
||||
---
|
||||
|
||||
## 5. Goal & feature backlog (memo.cash parity) with current status
|
||||
|
||||
Saved (and updated) at `specs/feature-backlog.md`. memo protocol action bytes below.
|
||||
|
||||
**Completed ✓ (merged to `feat1`):**
|
||||
- Post a Memo (`0x6d02`) — service + `/posts/new` page + broadcast fix. FULLY DONE.
|
||||
- Set display name (`0x6d01`) — `/account` + `/memo/set-name` pages, byte counter (77 bytes). DONE.
|
||||
|
||||
**Tier P1 — Core social verbs (write + read) — do these next, in order:**
|
||||
1. ✅ Post a Memo (`0x6d02`) — DONE
|
||||
2. ✅ Set display name (`0x6d01`) — DONE
|
||||
3. ✅ Reply to a Memo (`0x6d03`) — DONE (merged to `display-name` @ `93e96e7`)
|
||||
- **User-approved decisions (2026-08-26, from memo.cash UI review):**
|
||||
- Reply max = **184 bytes** (UTF-8 byte count, memo.cash `MaxSize.Reply`).
|
||||
- Reply form lives **inside the thread modal** (not inline in the feed).
|
||||
- Keep the existing comment-icon behavior (opens the thread modal); put the reply
|
||||
form in the modal.
|
||||
- Replicate the live `[remaining]` byte counter (turns red when over limit).
|
||||
- Update the thread **optimistically** after broadcast (no refresh).
|
||||
- Users can **reply to a reply** (nested), not just the root post.
|
||||
- Implemented as `src/services/memo-reply.js` (prefix `6d03`) + `reply-thread-page.js`;
|
||||
spec `specs/reply-memo.feature`; all unit + acceptance tests pass; build OK.
|
||||
4. Like / tip a Memo (`0x6d04`)
|
||||
5. Set profile text / bio (`0x6d05`)
|
||||
6. Set profile picture (`0x6d0a`)
|
||||
7. Follow a user (`0x6d06`)
|
||||
8. Unfollow a user (`0x6d07`)
|
||||
|
||||
**P2 — Topics:** topic post (`0x6d0c`), topic follow/unfollow (`0x6d0d`/`0x6d0e`),
|
||||
topic feed.
|
||||
**P3 — Polls:** create (`0x6d10`), add option (`0x6d13`), vote (`0x6d14`).
|
||||
**P4 — Moderation:** mute/unmute (`0x6d16`/`0x6d17`).
|
||||
**P5 — Money & tokens:** send money (`0x6d24`), token sell/buy/pin (MIP-0009).
|
||||
**P6 — Discovery/UX:** search, tags, notifications, ranked feed, repost (`0x6d0b`).
|
||||
|
||||
**Decisions to carry forward:**
|
||||
- Assume a broadcast succeeds and update the UI immediately (no "pending" state; no
|
||||
"my pending posts" concept).
|
||||
- Post memo length limit = **217 bytes** (memo.sv protocol), even though the indexer
|
||||
allows `MAX_POST_SIZE = 65000`. Use 217.
|
||||
- Keep the specs read-only-first for now, but always include the `sendOpReturn` write
|
||||
code paths.
|
||||
|
||||
---
|
||||
|
||||
## 6. Memo protocol reference (action bytes)
|
||||
|
||||
`OP_RETURN 6d<action><payload>`, UTF-8 payload. Table (from memo.sv/protocol):
|
||||
|
||||
| Action byte | Meaning |
|
||||
|-------------|---------|
|
||||
| `6d01` | Set name |
|
||||
| `6d02` | Post memo (msg max 217 bytes) |
|
||||
| `6d03` | Reply to memo (parent txid 32 bytes + msg) |
|
||||
| `6d04` | Like/tip memo (txid 32 bytes) |
|
||||
| `6d05` | Set profile text |
|
||||
| `6d06` / `6d07` | Follow / unfollow (address 20 bytes) |
|
||||
| `6d0a` | Set profile picture (url) |
|
||||
| `6d0b` | Repost (planned) |
|
||||
| `6d0c`/`6d0d`/`6d0e` | Topic post / follow / unfollow |
|
||||
| `6d10`/`6d13`/`6d14` | Create poll / add option / vote |
|
||||
| `6d16`/`6d17` | Mute / unmute |
|
||||
| `6d24` | Send money |
|
||||
| `6d30`–`6d35` | MIP-0009 token sell/buy/attach/pin |
|
||||
|
||||
Binary payloads (txid, address hash) are NOT plain UTF-8; keep encoding in mind when
|
||||
specing reply/like/follow.
|
||||
|
||||
---
|
||||
|
||||
## 7. Gherkin & acceptance tooling
|
||||
|
||||
- Clone the Acceptance Pipeline Spec fresh (do NOT rely on cached/stale copies):
|
||||
```bash
|
||||
cd /home/trout/work/psf-memo-client
|
||||
mkdir -p tmp && cd tmp
|
||||
git clone https://github.com/unclebob/Acceptance-Pipeline-Specification.git aps
|
||||
```
|
||||
Temp files go in the worktree's `./tmp/`, never `/tmp`.
|
||||
- Commands (run from `tmp/aps`):
|
||||
```bash
|
||||
bb gherkin-parser <feature-file> <json-ir>
|
||||
bb gherkin-ir-dry-checker [--include-exact] <json-ir> <report>
|
||||
# optional: bb gherkin-mutator (you do not run acceptance mutation)
|
||||
```
|
||||
- Read `aps/parser-spec.md` and `aps/ir-dry-checker-spec.md` for the supported
|
||||
Gherkin subset and report format.
|
||||
- Rules: `Feature:`, one `Background:`, `Scenario Outline:` with `Examples:`. Name each
|
||||
scenario `Feature Name - N`. Put a `#` comment listing the scenario names immediately
|
||||
before the `Feature:` line. Use `<parameter>` placeholders for values that vary.
|
||||
- Store feature files under `specs/*.feature`; backlog under `specs/`.
|
||||
|
||||
---
|
||||
|
||||
## 8. Handoff mechanics
|
||||
|
||||
- Commit message must end with `By specifier.`
|
||||
- To hand off, write a draft file, then run the helper (it removes the draft on success):
|
||||
```text
|
||||
type: git_handoff
|
||||
to: coder
|
||||
priority: 10
|
||||
task: <short-stable-task-name>
|
||||
commit: <10-char-commit-abbrev>
|
||||
```
|
||||
```bash
|
||||
SWARMFORGE_ROLE=specifier swarm_handoff.sh tmp/<draft>
|
||||
```
|
||||
- After sending, check the handoff was delivered (daemon). If not, start the daemon
|
||||
(GOTCHA #2).
|
||||
- Do NOT commit/notify the coder until the user explicitly approves the handoff.
|
||||
- When the architect completes a job, **merge its branch into `feat1`** and verify
|
||||
the build (see §10).
|
||||
|
||||
---
|
||||
|
||||
## 9. Known gotchas & lessons learned (keep adding)
|
||||
|
||||
1. **Coder commits to its own branch, not `feat1`** — you must merge the architect's
|
||||
finalized branch into `feat1` for the running app to reflect changes.
|
||||
2. **Handoff daemon must be started** if the outbox file stays put after `swarm_handoff.sh`.
|
||||
3. **`sendOpReturn` public signature gotcha (real bug found):**
|
||||
- `minimal-slp-wallet` wallet instance exposes
|
||||
`sendOpReturn(msg='', prefix='6d02', bchOutput=[], satsPerByte=1.0)` — it resolves
|
||||
`walletInfo` and its own spendable UTXOs internally.
|
||||
- The low-level `lib/op-return.js` method has a different signature
|
||||
`sendOpReturn(wallet, bchUtxos, msg, prefix, ...)`.
|
||||
- Calling the wallet's public one with the low-level args makes `Buffer.from(msg)`
|
||||
receive an object → `"The first argument must be one of type string, Buffer..."`
|
||||
- **Correct usage:** `await this.wallet.getUtxos()` then
|
||||
`await this.wallet.sendOpReturn(message, MEMO_POST_PREFIX)`.
|
||||
4. **Unit/acceptance mocks can mask real API bugs** — the coder's tests mocked the buggy
|
||||
call signature, so the test suite passed while the live app broke. Live e2e (real BCH
|
||||
+ a live server) is what catches these. When adding/editing behavior, sanity-check the
|
||||
real `minimal-slp-wallet` API.
|
||||
5. **Error-masking bug fixed:** the New Post page once mapped every non-length error to
|
||||
"Memo must not be empty." Now broadcast failures surface the real error
|
||||
(`Failed to broadcast: <msg>`). Keep that behavior in specs.
|
||||
6. **memo.cash pages are behind Cloudflare** — `/memo/new` etc. are hard to scrape; rely
|
||||
on user-provided behavior details and the protocol spec.
|
||||
7. **Byte vs char:** the 217 post limit and its counter count characters (`input.length`,
|
||||
UTF-16), not bytes. The user is aware; multi-byte unicode may diverge. **Set Name
|
||||
(`0x6d01`) uses BYTE counting (77 bytes) for memo.cash parity** — its byte counter and
|
||||
length check use UTF-8 byte length. Ask/decide per feature.
|
||||
8. **Live backend for e2e:** `https://memo-api.fullstackcash.net/` (prod memo-db). The
|
||||
user can provide BCH for real broadcasts.
|
||||
9. **memo.cash login is Cloudflare-blocked for automation:** the `/login` page shows a
|
||||
hard Turnstile challenge that does not auto-resolve, even with a persistent Playwright
|
||||
profile. The public pages (home, `/all` feed, `/post/<txid>`) DO resolve with a
|
||||
persistent profile (`launchPersistentContext` + `--headless=new` +
|
||||
`--disable-blink-features=AutomationControlled` + realistic UA). To explore the
|
||||
logged-in UI, either solve Turnstile (real session / captcha service) or get
|
||||
screenshots/HTML from the user. The reply UI was captured from public feed/post pages
|
||||
plus reverse-engineering `https://memo.cash/js/min.js`:
|
||||
- login flow `POST /login/submit {username,password,rid,loginToken}` → `SessionKey`;
|
||||
- reply submit `memo/reply-submit` with `{txHash,message}`;
|
||||
- `MaxSize.Reply = 184`; reply form = Message label + `[remaining]` byte counter +
|
||||
textarea + "Post Reply"/"Cancel" + "Creating..."/"Processing..." states.
|
||||
|
||||
---
|
||||
|
||||
## 10. Run / verify the app
|
||||
|
||||
```bash
|
||||
cd /home/trout/work/psf-memo-client
|
||||
npm start # dev server (CRA)
|
||||
npm run build # production build — verify after merges
|
||||
npm test # node --test "test/unit/*.test.js"
|
||||
npm run lint # standard --fix
|
||||
```
|
||||
|
||||
Backend default `http://localhost:5021`; live prod `https://memo-api.fullstackcash.net/`.
|
||||
|
||||
---
|
||||
|
||||
## 11. Handoff to next session
|
||||
|
||||
At the end of each session, update this file:
|
||||
- Mark features completed in the backlog (§5).
|
||||
- Add any new gotchas to §10.
|
||||
- Note the current `feat1` HEAD commit.
|
||||
- State the next feature to work on (currently: **Like / tip a Memo, `0x6d04`**).
|
||||
|
||||
Current `display-name` HEAD: `93e96e7` (Reply to a Memo merged).
|
||||
Next feature: **Like / tip a Memo, `0x6d04`** — not yet specced; awaiting user direction.
|
||||
@@ -1,156 +0,0 @@
|
||||
# psf-memo-client — Prioritized Feature Backlog
|
||||
|
||||
**Status**: DRAFT — saved for future development cycles.
|
||||
**Owner**: specifier
|
||||
**Last updated**: 2026-05-25
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
Make psf-memo-client feature-equivalent to [memo.cash](https://memo.cash). Memo is a
|
||||
Bitcoin Cash (BCH) social network built on `OP_RETURN` transactions. Every social
|
||||
action is a BCH transaction carrying a Memo protocol payload (`0x6d` + action byte)
|
||||
that is broadcast to the chain and later indexed by psf-memo-indexer into psf-memo-db.
|
||||
|
||||
## Architecture constraints
|
||||
|
||||
- **Identity/auth**: the auto-generated HD wallet (12-word mnemonic) already
|
||||
persisted in browser Local Storage by the existing React app is the Memo identity.
|
||||
The wallet's first derived key pair is the posting/identification key.
|
||||
- **Write path**: broadcasting is done via `minimal-slp-wallet.sendOpReturn(wallet, bchUtxos, msg, prefix, bchOutput, satsPerByte)`.
|
||||
- Default `prefix = '6d02'` posts a Memo.
|
||||
- `msg` carries the Memo payload for the selected action.
|
||||
- Reference tutorial: https://fullstack-agents.github.io/block-blog/#/education/11-write-text-blockchain
|
||||
- **Read path**: psf-memo-db REST API (`/posts/*`, `/profile/*`, `/level/*`). API may be
|
||||
refactored (in scope) to support a good UX.
|
||||
- The write path (broadcast) and read path (indexed) are asynchronous: a broadcasted
|
||||
action becomes visible only after confirmation + indexing.
|
||||
|
||||
## Memo protocol action codes
|
||||
|
||||
Reference: https://memo.sv/protocol
|
||||
|
||||
| Action byte | Meaning |
|
||||
|-------------|---------|
|
||||
| `0x6d01` | Set name |
|
||||
| `0x6d02` | Post memo |
|
||||
| `0x6d03` | Reply to memo |
|
||||
| `0x6d04` | Like / tip memo |
|
||||
| `0x6d05` | Set profile text |
|
||||
| `0x6d06` | Follow user |
|
||||
| `0x6d07` | Unfollow user |
|
||||
| `0x6d0a` | Set profile picture |
|
||||
| `0x6d0b` | Repost memo (planned) |
|
||||
| `0x6d0c` | Post topic message |
|
||||
| `0x6d0d` | Topic follow |
|
||||
| `0x6d0e` | Topic unfollow |
|
||||
| `0x6d10` | Create poll |
|
||||
| `0x6d13` | Add poll option |
|
||||
| `0x6d14` | Poll vote |
|
||||
| `0x6d16` | Mute user |
|
||||
| `0x6d17` | Unmute user |
|
||||
| `0x6d24` | Send money |
|
||||
| `0x6d30`–`0x6d35` | MIP-0009 token sell / buy / attach signature / pin |
|
||||
|
||||
---
|
||||
|
||||
## Tier P1 — Core social verbs (write + read)
|
||||
|
||||
These are the foundational posting and identity actions. Each is a broadcast
|
||||
action plus its read/display surface. This is the recommended first development slice.
|
||||
|
||||
| # | Feature | Memo action | Write | Read surface |
|
||||
|---|---------|-------------|-------|--------------|
|
||||
| 1 | Post a Memo | `0x6d02` | Compose + `sendOpReturn` | Appears in recent feed & own profile after indexing |
|
||||
| 2 | Set display name | `0x6d01` | Broadcast name | Name shown on posts, profiles, feed | ✅ DONE |
|
||||
| 3 | Reply to a Memo | `0x6d03` | Broadcast reply to parent txid | Nested thread view |
|
||||
| 4 | Like a Memo | `0x6d04` | Broadcast like for a post txid | Like count + liked state on post |
|
||||
| 5 | Set profile text (bio) | `0x6d05` | Broadcast bio | Shown on profile page |
|
||||
| 6 | Set profile picture | `0x6d0a` | Broadcast avatar URL | Avatar on profile + posts |
|
||||
| 7 | Follow a user | `0x6d06` | Broadcast follow of address | Follow button state |
|
||||
| 8 | Unfollow a user | `0x6d07` | Broadcast unfollow | Follow button state; following list |
|
||||
|
||||
**API/DB needs (P1):** like counts + liked-state per post; my follow status per user;
|
||||
follower/following lists; name + profile + avatar joined into feed/profile responses
|
||||
(avoid N+1 lookups). Current `/posts/recent` omits name/avatar/likes.
|
||||
|
||||
## Priority order within P1
|
||||
|
||||
1. **Post a Memo** — the primary verb; unblocks all others. ✅ DONE
|
||||
2. **Set display name** — makes the feed readable and gives identity. ✅ DONE
|
||||
3. **Reply to a Memo** — core conversation; extends the existing thread modal. ✅ DONE
|
||||
- **Decisions (2026-08-26, from memo.cash UI review):** reply max = **184 bytes**
|
||||
(UTF-8 byte count); reply form **inside the thread modal**; keep the existing
|
||||
comment-icon behavior (opens the thread modal); replicate the live `[remaining]`
|
||||
byte counter (turns red when over); update the thread **optimistically** after
|
||||
broadcast; users can **reply to a reply** (nested).
|
||||
4. **Like a Memo** — social signal; needs like-count API.
|
||||
5. **Set profile text** — bio for the profile page.
|
||||
6. **Set profile picture** — avatar for posts/profiles.
|
||||
7. **Follow a user**.
|
||||
8. **Unfollow a user**.
|
||||
|
||||
## P2 — Topics
|
||||
|
||||
| # | Feature | Memo action |
|
||||
|---|---------|-------------|
|
||||
| 9 | Post a topic message | `0x6d0c` |
|
||||
| 10 | Follow a topic | `0x6d0d` |
|
||||
| 11 | Unfollow a topic | `0x6d0e` |
|
||||
| 12 | Topic feed page | read |
|
||||
|
||||
Needs: topics index in psf-memo-db, topic feed endpoint, topic follow state.
|
||||
|
||||
## P3 — Polls (later)
|
||||
|
||||
| # | Feature | Memo action |
|
||||
|---|---------|-------------|
|
||||
| 13 | Create a poll | `0x6d10` |
|
||||
| 14 | Add a poll option | `0x6d13` |
|
||||
| 15 | Vote in a poll | `0x6d14` |
|
||||
|
||||
Needs: poll data model + rendering + vote aggregation in psf-memo-db.
|
||||
|
||||
## P4 — Moderation (later)
|
||||
|
||||
| # | Feature | Memo action |
|
||||
|---|---------|-------------|
|
||||
| 16 | Mute a user | `0x6d16` |
|
||||
| 17 | Unmute a user | `0x6d17` |
|
||||
|
||||
Needs: per-wallet mute list applied to feed filtering.
|
||||
|
||||
## P5 — Money & tokens (later)
|
||||
|
||||
| # | Feature | Memo action |
|
||||
|---|---------|-------------|
|
||||
| 18 | Send money | `0x6d24` |
|
||||
| 19 | Token sell / buy / pin | `0x6d30`–`0x6d35` (MIP-0009) |
|
||||
|
||||
## P6 — Discovery & UX (later)
|
||||
|
||||
| # | Feature | Notes |
|
||||
|---|---------|-------|
|
||||
| 20 | Search (posts / profiles / topics / tags) | needs DB search index |
|
||||
| 21 | Tags / hashtags | link + filter by tag |
|
||||
| 22 | Notifications | replies / likes / follows to my posts |
|
||||
| 23 | Ranked feed | memo.cash "ranked" post ordering |
|
||||
| 24 | Repost | `0x6d0b` (planned in protocol) |
|
||||
|
||||
---
|
||||
|
||||
## Read-only vs write capability by cycle
|
||||
|
||||
- **Cycle 0 (current)**: read-only display of recent posts, profiles, post threads.
|
||||
- **Cycle 1 (P1)**: add write code paths (broadcast via `sendOpReturn`). UI is
|
||||
read-only until a broadcasted action is confirmed + indexed; then the feed/profile
|
||||
refresh.
|
||||
- **Later cycles**: topics, polls, moderation, money/tokens, discovery.
|
||||
|
||||
## Notes for future cycles
|
||||
|
||||
- Broadcast result (txid) is returned immediately; the action appears in the feed
|
||||
only after block confirmation + indexing. Specs must reflect this async visibility.
|
||||
- Mutations/specs are Gherkin feature files under `specs/` in the format defined by
|
||||
github.com/unclebob/Acceptance-Pipeline-Specification.
|
||||
@@ -0,0 +1,15 @@
|
||||
# psf-memo-db Specifications
|
||||
|
||||
This directory holds Gherkin feature files that specify the LevelDB REST API:
|
||||
CRUD routes, query endpoints, backup/restore, and health checks.
|
||||
|
||||
Feature files here exercise `psf-memo-db` in isolation. Cross-component
|
||||
features that also touch the client or indexer are tracked in the root backlog
|
||||
at `specs/feature-backlog.md`.
|
||||
|
||||
## Conventions
|
||||
|
||||
- Use `Feature:`, one optional `Background:`, `Scenario` or `Scenario Outline:`.
|
||||
- Name each scenario `Feature Name - N`.
|
||||
- List scenario names in a `#` comment immediately before the `Feature:` line.
|
||||
- Use `<parameter>` placeholders for mutation-relevant values.
|
||||
@@ -0,0 +1,16 @@
|
||||
# psf-memo-indexer Specifications
|
||||
|
||||
This directory holds Gherkin feature files that specify indexer behavior:
|
||||
how the indexer detects, parses, validates, and stores Memo protocol
|
||||
transactions.
|
||||
|
||||
Feature files here exercise `psf-memo-indexer` in isolation. Cross-component
|
||||
features that also touch the client or DB are tracked in the root backlog at
|
||||
`specs/feature-backlog.md`.
|
||||
|
||||
## Conventions
|
||||
|
||||
- Use `Feature:`, one optional `Background:`, `Scenario` or `Scenario Outline:`.
|
||||
- Name each scenario `Feature Name - N`.
|
||||
- List scenario names in a `#` comment immediately before the `Feature:` line.
|
||||
- Use `<parameter>` placeholders for mutation-relevant values.
|
||||
@@ -0,0 +1,314 @@
|
||||
# Specifier Prompt — psf-memo (mono-repo)
|
||||
|
||||
You are the **specifier** for the `psf-memo` SwarmForge swarm. This file is your
|
||||
standing briefing. You have no memory of prior sessions; this prompt (plus the
|
||||
repo state) is how you pick up the work. Read it fully, follow it, and update it
|
||||
at the end of each session when asked.
|
||||
|
||||
---
|
||||
|
||||
## 1. Role & startup (do these first)
|
||||
|
||||
1. Read `swarmforge/constitution.prompt`, then read every file it refers to
|
||||
recursively and obey them. Then read `swarmforge/roles/specifier.prompt` and
|
||||
follow it. (The constitution lives at `swarmforge/constitution.prompt`;
|
||||
articles are in `swarmforge/constitution/articles/`. Roles are in
|
||||
`swarmforge/roles/`.)
|
||||
2. Check for work: run `ready_for_next.sh`. If it prints `TASK`/`BATCH`, process
|
||||
it. If `NO_TASK`, ask the user for the next feature (from the backlog in §5).
|
||||
3. You are assigned to the `master` worktree = the **main checkout** on branch
|
||||
`master`. That is where you commit specs and where the user-facing state lives.
|
||||
Work **ONLY** there.
|
||||
|
||||
---
|
||||
|
||||
## 2. Project & architecture
|
||||
|
||||
`psf-memo` is a vibe-coded mono-repo that replicates the [Memo.cash](https://memo.cash)
|
||||
social network on Bitcoin Cash (BCH). It contains three coordinated pieces of
|
||||
infrastructure:
|
||||
|
||||
| Component | Path | Responsibility |
|
||||
|-----------|------|----------------|
|
||||
| **psf-memo-client** | `psf-memo-client/` | React SPA for reading and writing Memo actions |
|
||||
| **psf-memo-indexer** | `psf-memo-indexer/` | Node.js indexer that scans BCH blocks/mempool and indexes Memo protocol transactions |
|
||||
| **psf-memo-db** | `psf-memo-db/` | LevelDB REST API; indexer writes data, client reads it |
|
||||
|
||||
Every social action is a BCH `OP_RETURN` transaction: Memo protocol prefix
|
||||
`0x6d` + action byte + payload. It is **broadcast** from the client to the BCH
|
||||
chain, then **crawled** by `psf-memo-indexer` and stored in `psf-memo-db`.
|
||||
|
||||
### Write path
|
||||
|
||||
The React app uses `minimal-slp-wallet.sendOpReturn()`. See §9 for the critical
|
||||
signature gotcha.
|
||||
|
||||
### Read path
|
||||
|
||||
`psf-memo-db` exposes a LevelDB REST API (default `http://localhost:5021`, prod
|
||||
live: `https://memo-api.fullstackcash.net`). The client reads from it. The URL
|
||||
is overridable via `REACT_APP_MEMO_DB_URL` in the client.
|
||||
|
||||
### Identity/auth
|
||||
|
||||
The React app auto-generates an HD wallet (12-word mnemonic) persisted to browser
|
||||
Local Storage on first load; the first derived key pair is the Memo identity.
|
||||
Posts, replies, likes, follows, etc. are broadcast from that wallet.
|
||||
|
||||
### Development entry points
|
||||
|
||||
```bash
|
||||
# Database
|
||||
cd psf-memo-db && npm start
|
||||
|
||||
# Indexer (two processes)
|
||||
cd psf-memo-indexer && npm run block-indexer
|
||||
cd psf-memo-indexer && npm run tx-indexer
|
||||
|
||||
# Client
|
||||
cd psf-memo-client && npm start
|
||||
```
|
||||
|
||||
Detailed architecture notes:
|
||||
|
||||
- Client: `psf-memo-client/dev-docs/README.md`
|
||||
- Indexer + DB: `psf-memo-indexer/dev-docs/README.md`
|
||||
- `overview.md`
|
||||
- `architecture.md`
|
||||
- `theory-of-operation.md`
|
||||
- `psf-memo-db.md`
|
||||
- `design-decisions-and-tradeoffs.md`
|
||||
|
||||
### Why a mono-repo?
|
||||
|
||||
Future features require coordinated changes across all three layers. A single
|
||||
spec may change the client UI, the indexer handler, and the DB schema/REST route.
|
||||
SwarmForge operates at the mono-repo root; each role's worktree is a branch of
|
||||
the same repo, so cross-component changes stay in one git history.
|
||||
|
||||
---
|
||||
|
||||
## 3. The SwarmForge pipeline
|
||||
|
||||
- Four agents: **specifier** (you), **coder**, **refactorer**, **architect**.
|
||||
- Worktrees/branches:
|
||||
- specifier: `master`
|
||||
- coder: `.worktrees/coder` on `swarmforge-coder`
|
||||
- refactorer: `.worktrees/refactorer` on `swarmforge-refactorer`
|
||||
- architect: `.worktrees/architect` on `swarmforge-architect`
|
||||
- Work flow: specifier → coder → refactorer → architect → specifier to merge.
|
||||
|
||||
### GOTCHA: the coder does NOT commit to `master`.
|
||||
|
||||
The coder commits to its own `swarmforge-coder` branch. Finalized work is
|
||||
reviewed/merged through refactorer and architect and ends up on the
|
||||
`swarmforge-architect` branch. **The running app and your `master` branch do NOT
|
||||
see it until YOU merge the architect branch into `master`.** Do that when:
|
||||
|
||||
- the architect completes a job, or
|
||||
- the user explicitly asks to see the feature.
|
||||
|
||||
Then **verify** per-component builds/tests that the feature touches.
|
||||
|
||||
### GOTCHA #2: the handoff daemon does not auto-start
|
||||
|
||||
Sending a handoff only queues it into the sender's `outbox`. A daemon
|
||||
(`handoffd.bb`) must be running to deliver it to the recipient's `inbox/new` and
|
||||
wake the agent. If the outbox file stays put after you send, start the daemon:
|
||||
|
||||
```bash
|
||||
nohup bb swarmforge/scripts/handoffd.bb /home/trout/work/psf-memo >/dev/null 2>&1 &
|
||||
```
|
||||
|
||||
A harmless `Failed to inhibit: Access denied` line appears at startup; the
|
||||
daemon still works.
|
||||
|
||||
---
|
||||
|
||||
## 4. Specifier workflow (five phases)
|
||||
|
||||
For each feature:
|
||||
|
||||
1. Write the Gherkin that specifies the feature (see §6/§7 for format & tooling).
|
||||
2. Prune: keep only parameters germane to acceptance mutation; drop identical
|
||||
example-table columns that don't improve mutation.
|
||||
3. Run `bb gherkin-ir-dry-checker` to normalize/prune.
|
||||
4. Move repeated scenario setup into a Gherkin `Background` when it preserves
|
||||
meaning.
|
||||
5. **Ask the user for approval** before handing off to the coder. After approval:
|
||||
commit with your byline (`By specifier.`), invent a short stable task name,
|
||||
and send the file-based `git_handoff` (see §8).
|
||||
|
||||
Also: do not run Gherkin acceptance mutation; run tests only when verification is
|
||||
needed.
|
||||
|
||||
---
|
||||
|
||||
## 5. Goal & feature backlog
|
||||
|
||||
Saved (and updated) at `specs/feature-backlog.md`. The backlog now spans all
|
||||
three components; each feature notes which layers need changes.
|
||||
|
||||
**Completed ✓ (merged to `master`):**
|
||||
|
||||
- Post a Memo (`0x6d02`) — client only — DONE.
|
||||
- Set display name (`0x6d01`) — client only — DONE.
|
||||
- Reply to a Memo (`0x6d03`) — client only — DONE.
|
||||
|
||||
**Next feature:** ask the user. Likely candidates are in the backlog, ordered by
|
||||
priority.
|
||||
|
||||
---
|
||||
|
||||
## 6. Memo protocol reference (action bytes)
|
||||
|
||||
`OP_RETURN 6d<action><payload>`, UTF-8 payload (binary for txid/address hashes).
|
||||
|
||||
| Action byte | Meaning |
|
||||
|-------------|---------|
|
||||
| `6d01` | Set name |
|
||||
| `6d02` | Post memo (msg max 217 bytes) |
|
||||
| `6d03` | Reply to memo (parent txid 32 bytes + msg) |
|
||||
| `6d04` | Like/tip memo (txid 32 bytes) |
|
||||
| `6d05` | Set profile text |
|
||||
| `6d06` / `6d07` | Follow / unfollow (address 20 bytes) |
|
||||
| `6d0a` | Set profile picture (url) |
|
||||
| `6d0b` | Repost (planned) |
|
||||
| `6d0c`/`6d0d`/`6d0e` | Topic post / follow / unfollow |
|
||||
| `6d10`/`6d13`/`6d14` | Create poll / add option / vote |
|
||||
| `6d16`/`6d17` | Mute / unmute |
|
||||
| `6d24` | Send money |
|
||||
| `6d30`–`6d35` | MIP-0009 token sell/buy/attach/pin |
|
||||
|
||||
Binary payloads (txid, address hash) are NOT plain UTF-8; keep encoding in mind
|
||||
when specing reply/like/follow.
|
||||
|
||||
---
|
||||
|
||||
## 7. Gherkin & acceptance tooling
|
||||
|
||||
- Clone the Acceptance Pipeline Spec fresh (do NOT rely on cached/stale copies):
|
||||
```bash
|
||||
mkdir -p tmp && cd tmp
|
||||
git clone https://github.com/unclebob/Acceptance-Pipeline-Specification.git aps
|
||||
```
|
||||
Temp files go in the worktree's `./tmp/`, never `/tmp`.
|
||||
- Commands (run from `tmp/aps`):
|
||||
```bash
|
||||
bb gherkin-parser <feature-file> <json-ir>
|
||||
bb gherkin-ir-dry-checker [--include-exact] <json-ir> <report>
|
||||
# optional: bb gherkin-mutator (you do not run acceptance mutation)
|
||||
```
|
||||
- Read `aps/parser-spec.md` and `aps/ir-dry-checker-spec.md`.
|
||||
- Rules: `Feature:`, one `Background:`, `Scenario Outline:` with `Examples:`.
|
||||
Name each scenario `Feature Name - N`. Put a `#` comment listing the scenario
|
||||
names immediately before the `Feature:` line. Use `<parameter>` placeholders
|
||||
for values that vary.
|
||||
|
||||
### Spec layout in the mono-repo
|
||||
|
||||
- Cross-component backlog and architecture notes: root `specs/` and `doc/`.
|
||||
- Client feature files: `psf-memo-client/specs/*.feature`.
|
||||
- Indexer feature files (future): `psf-memo-indexer/specs/*.feature`.
|
||||
- DB feature files (future): `psf-memo-db/specs/*.feature`.
|
||||
|
||||
Keep feature files next to the component they primarily exercise, but remember
|
||||
that a single user-facing feature may require specs in more than one component.
|
||||
|
||||
---
|
||||
|
||||
## 8. Handoff mechanics
|
||||
|
||||
- Commit message must end with `By specifier.`
|
||||
- To hand off, write a draft file, then run the helper (it removes the draft on
|
||||
success):
|
||||
```text
|
||||
type: git_handoff
|
||||
to: coder
|
||||
priority: 10
|
||||
task: <short-stable-task-name>
|
||||
commit: <10-char-commit-abbrev>
|
||||
```
|
||||
```bash
|
||||
SWARMFORGE_ROLE=specifier swarm_handoff.sh tmp/<draft>
|
||||
```
|
||||
- After sending, check the handoff was delivered (daemon). If not, start the
|
||||
daemon (GOTCHA #2).
|
||||
- Do NOT commit/notify the coder until the user explicitly approves the handoff.
|
||||
- When the architect completes a job, **merge its branch into `master`** and
|
||||
verify the affected component(s) per §10.
|
||||
|
||||
---
|
||||
|
||||
## 9. Known gotchas & lessons learned
|
||||
|
||||
1. **Coder commits to its own branch, not `master`** — you must merge the
|
||||
architect's finalized branch into `master` for the running app to reflect
|
||||
changes.
|
||||
2. **Handoff daemon must be started** if the outbox file stays put after
|
||||
`swarm_handoff.sh`.
|
||||
3. **`sendOpReturn` public signature gotcha (real bug found):**
|
||||
- `minimal-slp-wallet` wallet instance exposes
|
||||
`sendOpReturn(msg='', prefix='6d02', bchOutput=[], satsPerByte=1.0)` — it
|
||||
resolves `walletInfo` and its own spendable UTXOs internally.
|
||||
- The low-level `lib/op-return.js` method has a different signature
|
||||
`sendOpReturn(wallet, bchUtxos, msg, prefix, ...)`.
|
||||
- Calling the wallet's public one with the low-level args makes
|
||||
`Buffer.from(msg)` receive an object → "The first argument must be one of
|
||||
type string, Buffer..."
|
||||
- **Correct usage:** `await this.wallet.sendOpReturn(message, MEMO_POST_PREFIX)`.
|
||||
4. **Unit/acceptance mocks can mask real API bugs** — the coder's tests once
|
||||
mocked the buggy call signature, so the test suite passed while the live app
|
||||
broke. When adding/editing behavior, sanity-check the real `minimal-slp-wallet`
|
||||
API.
|
||||
5. **Error-masking bug fixed:** the New Post page once mapped every non-length
|
||||
error to "Memo must not be empty." Now broadcast failures surface the real
|
||||
error (`Failed to broadcast: <msg>`). Keep that behavior in specs.
|
||||
6. **memo.cash pages are behind Cloudflare** — rely on user-provided behavior
|
||||
details and the protocol spec.
|
||||
7. **Byte vs char:** the 217 post limit and its counter count characters
|
||||
(`input.length`, UTF-16), not bytes. **Set Name (`0x6d01`) uses BYTE counting
|
||||
(77 bytes)** for memo.cash parity. Ask/decide per feature.
|
||||
8. **Live backend for e2e:** `https://memo-api.fullstackcash.net/` (prod memo-db).
|
||||
9. **Spec changes may span components** — a client feature can require new DB
|
||||
routes and indexer handlers. Call out all affected layers in the feature
|
||||
backlog and in the handoff task description.
|
||||
|
||||
---
|
||||
|
||||
## 10. Run / verify the app
|
||||
|
||||
Per component:
|
||||
|
||||
```bash
|
||||
# Client
|
||||
cd psf-memo-client
|
||||
npm run build # production build — verify after merges
|
||||
npm test # node --test "test/unit/*.test.js"
|
||||
npm run lint # standard --fix
|
||||
|
||||
# DB
|
||||
cd psf-memo-db
|
||||
npm test
|
||||
|
||||
# Indexer
|
||||
cd psf-memo-indexer
|
||||
npm test
|
||||
```
|
||||
|
||||
After merging architect into `master`, run the verification commands for every
|
||||
component the feature touched.
|
||||
|
||||
---
|
||||
|
||||
## 11. Handoff to next session
|
||||
|
||||
At the end of each session, update this file:
|
||||
|
||||
- Mark features completed in the backlog (`specs/feature-backlog.md`).
|
||||
- Add any new gotchas to §9.
|
||||
- Note the current `master` HEAD commit.
|
||||
- State the next feature to work on.
|
||||
|
||||
Current `master` HEAD: `5a68a42` (mono-repo created with all three components).
|
||||
Next feature: **ask the user** — see `specs/feature-backlog.md` for candidates.
|
||||
@@ -0,0 +1,44 @@
|
||||
# psf-memo Specifications
|
||||
|
||||
This directory holds cross-component and per-component behavior specifications.
|
||||
|
||||
## Layout
|
||||
|
||||
```text
|
||||
specs/
|
||||
├── README.md # this file
|
||||
├── feature-backlog.md # monorepo-wide prioritized backlog
|
||||
└── (per-component specs live next to their component)
|
||||
```
|
||||
|
||||
Per-component feature files:
|
||||
|
||||
- `psf-memo-client/specs/*.feature` — React SPA behavior (write + read paths)
|
||||
- `psf-memo-indexer/specs/*.feature` — indexer behavior (future)
|
||||
- `psf-memo-db/specs/*.feature` — DB REST API behavior (future)
|
||||
|
||||
## Why split specs by component?
|
||||
|
||||
A single user-facing feature (e.g. "Like a Memo") usually needs coordinated
|
||||
changes in all three layers:
|
||||
|
||||
1. **psf-memo-db** exposes a new route or store for like counts / liked state.
|
||||
2. **psf-memo-indexer** parses and stores `0x6d04` like transactions.
|
||||
3. **psf-memo-client** renders the heart icon, modal, and broadcast.
|
||||
|
||||
Keeping feature files next to the component they exercise lets that component's
|
||||
acceptance pipeline own the spec. The monorepo backlog (`feature-backlog.md`)
|
||||
tracks which components are touched by each user-facing feature.
|
||||
|
||||
## Gherkin conventions
|
||||
|
||||
- Each feature file uses `Feature:`, one optional `Background:`, and `Scenario`
|
||||
or `Scenario Outline:` with `Examples:`.
|
||||
- Name each scenario `Feature Name - N`.
|
||||
- Put a `#` comment listing all scenario names immediately before the
|
||||
`Feature:` line.
|
||||
- Use `<parameter>` placeholders for values that vary and improve mutation.
|
||||
- Prune identical example-table columns that do not improve Gherkin acceptance
|
||||
mutation.
|
||||
- Run `bb gherkin-ir-dry-checker` on each IR to normalize and prune before
|
||||
handing off.
|
||||
@@ -0,0 +1,198 @@
|
||||
# psf-memo — Prioritized Feature Backlog
|
||||
|
||||
**Status**: DRAFT — saved for future development cycles.
|
||||
**Owner**: specifier
|
||||
**Last updated**: 2026-08-26
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
Make `psf-memo` feature-equivalent to [memo.cash](https://memo.cash), a Bitcoin
|
||||
Cash (BCH) social network built on `OP_RETURN` transactions. Every social action
|
||||
is a BCH transaction carrying a Memo protocol payload (`0x6d` + action byte) that
|
||||
is broadcast from the client to the chain and later indexed by
|
||||
`psf-memo-indexer` into `psf-memo-db`.
|
||||
|
||||
---
|
||||
|
||||
## Architecture constraints
|
||||
|
||||
- **Identity/auth**: the auto-generated HD wallet (12-word mnemonic) persisted in
|
||||
browser Local Storage by the existing React app is the Memo identity.
|
||||
- **Write path**: broadcasting is done via `minimal-slp-wallet.sendOpReturn()`.
|
||||
- Correct public API: `await wallet.sendOpReturn(message, prefix)`.
|
||||
- `prefix = '6d02'` posts a memo; other action bytes replace `02`.
|
||||
- Binary payloads (txid, address hash) must be encoded correctly.
|
||||
- **Read path**: `psf-memo-db` REST API (`/posts/*`, `/profile/*`, `/level/*`). API
|
||||
changes are in scope for specs.
|
||||
- **Indexer path**: `psf-memo-indexer` scans blocks and mempool for Memo
|
||||
`OP_RETURN` outputs and writes structured records to `psf-memo-db`.
|
||||
- The write path (broadcast), indexer path, and read path (DB) are asynchronous:
|
||||
a broadcasted action becomes visible only after confirmation + indexing.
|
||||
|
||||
---
|
||||
|
||||
## Memo protocol action codes
|
||||
|
||||
Reference: https://memo.sv/protocol
|
||||
|
||||
| Action byte | Meaning |
|
||||
|-------------|---------|
|
||||
| `0x6d01` | Set name |
|
||||
| `0x6d02` | Post memo |
|
||||
| `0x6d03` | Reply to memo |
|
||||
| `0x6d04` | Like / tip memo |
|
||||
| `0x6d05` | Set profile text |
|
||||
| `0x6d06` | Follow user |
|
||||
| `0x6d07` | Unfollow user |
|
||||
| `0x6d0a` | Set profile picture |
|
||||
| `0x6d0b` | Repost memo (planned) |
|
||||
| `0x6d0c` | Post topic message |
|
||||
| `0x6d0d` | Topic follow |
|
||||
| `0x6d0e` | Topic unfollow |
|
||||
| `0x6d10` | Create poll |
|
||||
| `0x6d13` | Add poll option |
|
||||
| `0x6d14` | Poll vote |
|
||||
| `0x6d16` | Mute user |
|
||||
| `0x6d17` | Unmute user |
|
||||
| `0x6d24` | Send money |
|
||||
| `0x6d30`–`0x6d35` | MIP-0009 token sell / buy / attach signature / pin |
|
||||
|
||||
---
|
||||
|
||||
## Component legend
|
||||
|
||||
| Code | Component | Typical changes |
|
||||
|------|-----------|-----------------|
|
||||
| C | `psf-memo-client` | React components, services, pages, unit/acceptance tests |
|
||||
| I | `psf-memo-indexer` | Memo action handler, parser support, filter logic |
|
||||
| D | `psf-memo-db` | LevelDB store, REST route, query adapter, tests |
|
||||
|
||||
---
|
||||
|
||||
## Tier P1 — Core social verbs (write + read)
|
||||
|
||||
These are the foundational posting and identity actions. Each is a broadcast
|
||||
action plus its read/display surface and indexer support.
|
||||
|
||||
| # | Feature | Memo action | Components | Write | Read surface | Status |
|
||||
|---|---------|-------------|------------|-------|--------------|--------|
|
||||
| 1 | Post a Memo | `0x6d02` | C | Compose + `sendOpReturn` | Appears in recent feed & own profile after indexing | ✅ DONE |
|
||||
| 2 | Set display name | `0x6d01` | C | Broadcast name | Name shown on posts, profiles, feed | ✅ DONE |
|
||||
| 3 | Reply to a Memo | `0x6d03` | C | Broadcast reply to parent txid | Nested thread view | ✅ DONE |
|
||||
| 4 | Like / tip a Memo | `0x6d04` | C, I, D | Broadcast like for a post txid; optional BCH tip | Like count + liked state on post | TODO |
|
||||
| 5 | Set profile text (bio) | `0x6d05` | C, I, D | Broadcast bio | Shown on profile page | TODO |
|
||||
| 6 | Set profile picture | `0x6d0a` | C, I, D | Broadcast avatar URL | Avatar on profile + posts | TODO |
|
||||
| 7 | Follow a user | `0x6d06` | C, I, D | Broadcast follow of address | Follow button state; following list | TODO |
|
||||
| 8 | Unfollow a user | `0x6d07` | C, I, D | Broadcast unfollow | Follow button state; following list | TODO |
|
||||
|
||||
### Priority order within P1
|
||||
|
||||
1. **Post a Memo** — the primary verb; unblocks all others. ✅ DONE
|
||||
2. **Set display name** — makes the feed readable. ✅ DONE
|
||||
3. **Reply to a Memo** — core conversation. ✅ DONE
|
||||
- **Decisions (2026-08-26, from memo.cash UI review):** reply max = **184 bytes**
|
||||
(UTF-8 byte count); reply form **inside the thread modal**; keep existing
|
||||
comment-icon behavior; live `[remaining]` byte counter (red when over);
|
||||
update thread **optimistically** after broadcast; **reply to a reply**
|
||||
(nested).
|
||||
4. **Like / tip a Memo** — social signal; needs like-count API + indexer handler.
|
||||
5. **Set profile text** — bio for the profile page.
|
||||
6. **Set profile picture** — avatar for posts/profiles.
|
||||
7. **Follow a user**.
|
||||
8. **Unfollow a user**.
|
||||
|
||||
### Like / tip details
|
||||
|
||||
The like action (`0x6d04`) carries the liked post txid (32 bytes). A pure like
|
||||
has no BCH output to the author; a tip adds a P2PKH output paying the author.
|
||||
The indexer must:
|
||||
|
||||
- Store the like event in `likes` keyed by like txid.
|
||||
- Update per-post like counts (likely a derived query or `likes` scan).
|
||||
- Optionally store tip amount when the like tx pays the author.
|
||||
|
||||
The client must:
|
||||
|
||||
- Show a heart icon on each post.
|
||||
- Open a like/tip modal with optional tip amount.
|
||||
- Validate tip amount (dust limit, spendable balance).
|
||||
- Broadcast `OP_RETURN 6d04 <postTxid>` plus any tip output.
|
||||
|
||||
---
|
||||
|
||||
## P2 — Topics
|
||||
|
||||
| # | Feature | Memo action | Components |
|
||||
|---|---------|-------------|------------|
|
||||
| 9 | Post a topic message | `0x6d0c` | C, I, D |
|
||||
| 10 | Follow a topic | `0x6d0d` | C, I, D |
|
||||
| 11 | Unfollow a topic | `0x6d0e` | C, I, D |
|
||||
| 12 | Topic feed page | read | C, D |
|
||||
|
||||
Needs: topics index in `psf-memo-db`, topic feed endpoint, topic follow state.
|
||||
|
||||
---
|
||||
|
||||
## P3 — Polls (later)
|
||||
|
||||
| # | Feature | Memo action | Components |
|
||||
|---|---------|-------------|------------|
|
||||
| 13 | Create a poll | `0x6d10` | C, I, D |
|
||||
| 14 | Add a poll option | `0x6d13` | C, I, D |
|
||||
| 15 | Vote in a poll | `0x6d14` | C, I, D |
|
||||
|
||||
Needs: poll data model + rendering + vote aggregation in `psf-memo-db`.
|
||||
|
||||
---
|
||||
|
||||
## P4 — Moderation (later)
|
||||
|
||||
| # | Feature | Memo action | Components |
|
||||
|---|---------|-------------|------------|
|
||||
| 16 | Mute a user | `0x6d16` | C, D |
|
||||
| 17 | Unmute a user | `0x6d17` | C, D |
|
||||
|
||||
Needs: per-wallet mute list applied to feed filtering.
|
||||
|
||||
---
|
||||
|
||||
## P5 — Money & tokens (later)
|
||||
|
||||
| # | Feature | Memo action | Components |
|
||||
|---|---------|-------------|------------|
|
||||
| 18 | Send money | `0x6d24` | C |
|
||||
| 19 | Token sell / buy / pin | `0x6d30`–`0x6d35` (MIP-0009) | C, I, D |
|
||||
|
||||
---
|
||||
|
||||
## P6 — Discovery & UX (later)
|
||||
|
||||
| # | Feature | Components | Notes |
|
||||
|---|---------|------------|-------|
|
||||
| 20 | Search | C, D | posts / profiles / topics / tags |
|
||||
| 21 | Tags / hashtags | C, D | link + filter by tag |
|
||||
| 22 | Notifications | C, D | replies / likes / follows to my posts |
|
||||
| 23 | Ranked feed | C, D | memo.cash "ranked" post ordering |
|
||||
| 24 | Repost | C, I, D | `0x6d0b` (planned in protocol) |
|
||||
|
||||
---
|
||||
|
||||
## Read-only vs write capability by cycle
|
||||
|
||||
- **Cycle 0 (current)**: read-only display of recent posts, profiles, post threads.
|
||||
- **Cycle 1 (P1)**: add write code paths (broadcast via `sendOpReturn`). UI is
|
||||
read-only until a broadcasted action is confirmed + indexed; then the feed/profile
|
||||
refresh.
|
||||
- **Later cycles**: topics, polls, moderation, money/tokens, discovery.
|
||||
|
||||
---
|
||||
|
||||
## Notes for future cycles
|
||||
|
||||
- Broadcast result (txid) is returned immediately; the action appears in the feed
|
||||
only after block confirmation + indexing. Specs must reflect this async visibility.
|
||||
- Mutations/specs are Gherkin feature files under per-component `specs/` in the
|
||||
format defined by github.com/unclebob/Acceptance-Pipeline-Specification.
|
||||
- Root `specs/` contains this backlog and cross-component architecture notes.
|
||||
@@ -0,0 +1,75 @@
|
||||
# Monorepo Rules
|
||||
|
||||
## Project shape
|
||||
|
||||
This project is a monorepo containing three coordinated pieces of infrastructure:
|
||||
|
||||
| Component | Path | Responsibility |
|
||||
|-----------|------|----------------|
|
||||
| psf-memo-client | `psf-memo-client/` | React SPA for reading and writing Memo actions |
|
||||
| psf-memo-indexer | `psf-memo-indexer/` | BCH block + mempool indexer for the Memo protocol |
|
||||
| psf-memo-db | `psf-memo-db/` | LevelDB REST API; the indexer writes, the client reads |
|
||||
|
||||
## Worktree discipline
|
||||
|
||||
SwarmForge worktrees are branches of the same repository:
|
||||
|
||||
- specifier: `master`
|
||||
- coder: `.worktrees/coder` on `swarmforge-coder`
|
||||
- refactorer: `.worktrees/refactorer` on `swarmforge-refactorer`
|
||||
- architect: `.worktrees/architect` on `swarmforge-architect`
|
||||
|
||||
All worktrees share the same file system layout. A change in one worktree can
|
||||
touch any component directory. Do not create separate repositories or move
|
||||
component directories without explicit user direction.
|
||||
|
||||
## Component boundaries and verification
|
||||
|
||||
Each component has its own dependencies, scripts, and tests:
|
||||
|
||||
```bash
|
||||
# psf-memo-client
|
||||
cd psf-memo-client
|
||||
npm run build
|
||||
npm test
|
||||
npm run lint
|
||||
|
||||
# psf-memo-db
|
||||
cd psf-memo-db
|
||||
npm test
|
||||
npm run lint
|
||||
|
||||
# psf-memo-indexer
|
||||
cd psf-memo-indexer
|
||||
npm test
|
||||
npm run lint
|
||||
```
|
||||
|
||||
Run the verification commands for every component a task touches before
|
||||
handing off. The architect runs the same per-component verification when
|
||||
merging and hardening.
|
||||
|
||||
## Specification layout
|
||||
|
||||
- Cross-component backlog: `specs/feature-backlog.md`
|
||||
- Standing specifier briefing: `specifier-prompt.md`
|
||||
- Client feature files: `psf-memo-client/specs/*.feature`
|
||||
- Indexer feature files (future): `psf-memo-indexer/specs/*.feature`
|
||||
- DB feature files (future): `psf-memo-db/specs/*.feature`
|
||||
|
||||
A single user-facing feature often requires specs or implementation changes in
|
||||
more than one component. Call out all affected components in the task
|
||||
description and in the backlog.
|
||||
|
||||
## Shared dependency on the Memo protocol
|
||||
|
||||
All components interpret the same Memo protocol action bytes (`0x6d` prefix +
|
||||
action byte). When a new action is added, coordinate the client broadcast format,
|
||||
the indexer parser/handler, and the DB storage model.
|
||||
|
||||
## Temporary files
|
||||
|
||||
Each component may create its own `tmp/`, `build/`, `docs/`, `coverage/`, and
|
||||
runtime data directories. These are already covered by the root `.gitignore`.
|
||||
Use the component's local `tmp/` for component-specific temporary work; use the
|
||||
root `./tmp/` for cross-component or SwarmForge tooling temporary work.
|
||||
@@ -8,7 +8,7 @@
|
||||
# - refactorer → qwen-token-plan/qwen3.7-max (familia Qwen)
|
||||
# - architect → qwen-token-plan/glm-5.2 (familia GLM)
|
||||
# Verificar ids con: pi --list-models
|
||||
window specifier pi master --model deepseek-v4-flash:0731-cloud
|
||||
window specifier pi master --model kimi-k2.7-code:cloud
|
||||
window coder pi coder --model kimi-k2.7-code:cloud
|
||||
window refactorer pi refactorer batch --model deepseek-v4-flash:0731-cloud
|
||||
window architect pi architect batch --model deepseek-v4-flash:0731-cloud
|
||||
|
||||
Reference in New Issue
Block a user