diff --git a/specifier-prompt.md b/specifier-prompt.md new file mode 100644 index 0000000..a5956a8 --- /dev/null +++ b/specifier-prompt.md @@ -0,0 +1,259 @@ +# 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. + +**Tier P1 — Core social verbs (write + read) — do these next, in order:** +1. ✅ Post a Memo (`0x6d02`) — DONE +2. Set display name (`0x6d01`) — **NEXT** (breadcrumb: the feed already renders + display names; the write action is missing) +3. Reply to a Memo (`0x6d03`) — thread already renders; add reply broadcast +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`, 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 + bb gherkin-ir-dry-checker [--include-exact] + # 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 `` 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: + commit: <10-char-commit-abbrev> + ``` + ```bash + SWARMFORGE_ROLE=specifier swarm_handoff.sh tmp/ + ``` +- 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: `). 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 limit and the counter currently count characters + (`input.length`, UTF-16), not bytes. The user is aware; multi-byte unicode may + diverge. 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. + +--- + +## 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: **Set display name, `0x6d01`**).