diff --git a/README.md b/README.md index 0748b4a..638ec13 100644 --- a/README.md +++ b/README.md @@ -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). + diff --git a/psf-memo-client/specifier-prompt.md b/psf-memo-client/specifier-prompt.md deleted file mode 100644 index 92a5e91..0000000 --- a/psf-memo-client/specifier-prompt.md +++ /dev/null @@ -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`, 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 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/`) 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. diff --git a/psf-memo-client/specs/feature-backlog.md b/psf-memo-client/specs/feature-backlog.md deleted file mode 100644 index fe54b98..0000000 --- a/psf-memo-client/specs/feature-backlog.md +++ /dev/null @@ -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. diff --git a/psf-memo-db/specs/README.md b/psf-memo-db/specs/README.md new file mode 100644 index 0000000..e066377 --- /dev/null +++ b/psf-memo-db/specs/README.md @@ -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 `` placeholders for mutation-relevant values. diff --git a/psf-memo-indexer/specs/README.md b/psf-memo-indexer/specs/README.md new file mode 100644 index 0000000..7bffac8 --- /dev/null +++ b/psf-memo-indexer/specs/README.md @@ -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 `` placeholders for mutation-relevant values. diff --git a/specifier-prompt.md b/specifier-prompt.md new file mode 100644 index 0000000..cc53fc9 --- /dev/null +++ b/specifier-prompt.md @@ -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`, 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 + 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`. +- 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. + +### 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: + 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 `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: `). 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. diff --git a/specs/README.md b/specs/README.md new file mode 100644 index 0000000..c80f021 --- /dev/null +++ b/specs/README.md @@ -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 `` 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. diff --git a/specs/feature-backlog.md b/specs/feature-backlog.md new file mode 100644 index 0000000..75d3d54 --- /dev/null +++ b/specs/feature-backlog.md @@ -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 ` 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. diff --git a/swarmforge/constitution/articles/monorepo.prompt b/swarmforge/constitution/articles/monorepo.prompt new file mode 100644 index 0000000..c891ee0 --- /dev/null +++ b/swarmforge/constitution/articles/monorepo.prompt @@ -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. diff --git a/swarmforge/swarmforge.conf b/swarmforge/swarmforge.conf index 8574f47..42b3197 100644 --- a/swarmforge/swarmforge.conf +++ b/swarmforge/swarmforge.conf @@ -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