diff --git a/.planning/WINDOWS.md b/.planning/WINDOWS.md index 5a97936b..97f7c02a 100644 --- a/.planning/WINDOWS.md +++ b/.planning/WINDOWS.md @@ -1,10 +1,10 @@ --- schema_version: 1 -open_count: 3 +open_count: 4 waived_count: 0 fixed_count: 1 -total_count: 4 -last_updated: 2026-07-30T22:37:44.642Z +total_count: 5 +last_updated: 2026-07-31T02:35:00.391Z --- # Broken Windows Ledger @@ -19,6 +19,7 @@ last_updated: 2026-07-30T22:37:44.642Z | 2 | 02 | deviation | neode-ui/src/views/Home.vue | | Wallet/send flow (SendBitcoinModal.vue via Home.vue) named by 02-FINDINGS.md as owned by 02-03 (worst-ranked revisit, 2607ms) but not in 02-03-PLAN.md's files_modified — reported as an unplanned-item gap, not converted. Cause is pure client-side remount cost (0 RPC), not a caching problem. | open | | 2026-07-30T12:25:22.450Z | | | 3 | 02 | deviation | neode-ui/src/views/PeerFiles.vue | | 02-03-PLAN.md assumed PeerFiles.vue already used useCachedResource; it actually uses the raw resources store directly (correctly per-item-keyed) with no TTL gate and the same loading/refreshing conflation bug fixed in OpenWrtGateway.vue this plan. Left untouched (out of files_modified scope) — candidate for the same fix in a future plan. | open | | 2026-07-30T12:25:22.605Z | | | 4 | 02 | deviation | neode-ui/src/views/Chat.vue | | AIUI-side D-14 commit (900c0b9, branch feat/d14-embed-defaults in local clone /home/archipelago/Projects/AIUI, based on development) is NOT pushed upstream to git.tx1138.com/lfg2025/AIUI — anonymous push returned 403 Forbidden. neode-ui's two new query params (chatExpanded, mobileChat) are inert no-ops against any currently-deployed AIUI build until a maintainer with push rights merges and it is rebuilt/redeployed. 02-08 (deploy) or the user must resolve push access. | fixed | | 2026-07-30T22:37:25.565Z | 2026-07-30T22:37:44.642Z | +| 5 | 09 | unrun-verify | botfight/e2e/signup-bot.spec.ts | | pnpm test:e2e -- e2e/signup-bot.spec.ts not run: local backend dev port 9100 is occupied by the live archi-dev-box botfights container (podman, 42h uptime) needed for tomorrow's demo — could not free it to run a local dev server. Task-level automated verify (vue-tsc + grep sweep) passed; vitest server suite passed with only pre-existing unrelated flaky failures. | open | | 2026-07-31T02:35:00.391Z | | ````json [ @@ -69,6 +70,18 @@ last_updated: 2026-07-30T22:37:44.642Z "reason": "", "recorded_at": "2026-07-30T22:37:25.565Z", "resolved_at": "2026-07-30T22:37:44.642Z" + }, + { + "id": 5, + "kind": "unrun-verify", + "phase": "09", + "file": "botfight/e2e/signup-bot.spec.ts", + "line": null, + "description": "pnpm test:e2e -- e2e/signup-bot.spec.ts not run: local backend dev port 9100 is occupied by the live archi-dev-box botfights container (podman, 42h uptime) needed for tomorrow's demo — could not free it to run a local dev server. Task-level automated verify (vue-tsc + grep sweep) passed; vitest server suite passed with only pre-existing unrelated flaky failures.", + "status": "open", + "reason": "", + "recorded_at": "2026-07-31T02:35:00.391Z", + "resolved_at": null } ] ```` diff --git a/.planning/phases/09-botfights-platform-upgrade/09-03-SUMMARY.md b/.planning/phases/09-botfights-platform-upgrade/09-03-SUMMARY.md new file mode 100644 index 00000000..969702bd --- /dev/null +++ b/.planning/phases/09-botfights-platform-upgrade/09-03-SUMMARY.md @@ -0,0 +1,188 @@ +--- +phase: 09-botfights-platform-upgrade +plan: 03 +subsystem: docs +tags: [hono, vue3, markdown, ai-prompt, api-docs] + +# Dependency graph +requires: [] +provides: + - "One consolidated, self-contained AI bot-setup prompt (frontend/public/docs/BOTFIGHTS.md)" + - "GET /api/docs/prompt serving that prompt as text/markdown with {{ARENA_URL}} resolved" + - "Every in-app setup surface (JoinBoutPage, BotProfilePage, DocsPage) pointed at the one prompt" +affects: [09-04-canonical-arena-deployment, 09-07-demo-verification] + +# Tech tracking +tech-stack: + added: [] + patterns: + - "{{ARENA_URL}} literal token in canonical markdown, substituted server-side (PUBLIC_ARENA_URL || request origin) or client-side (window.location.origin) — one doc, many resolution points" + - "Dual-path file resolution (server/public/docs/... then ../../../frontend/public/docs/...) mirroring app.ts's publicDir derivation, so the route works from both a built container and a dev checkout" + +key-files: + created: [] + modified: + - frontend/public/docs/BOTFIGHTS.md + - BOTFIGHTS.md + - server/src/routes/docs.ts + - server/src/routes/docs.test.ts + - frontend/src/pages/JoinBoutPage.vue + - frontend/src/pages/BotProfilePage.vue + - frontend/src/pages/DocsPage.vue + - e2e/signup-bot.spec.ts + +key-decisions: + - "Documented the real webhook-vs-poll trash-talk field-name split (trash_talk snake_case for webhook responses per orchestrator.ts's webhookResponseSchema, trashTalk camelCase for /api/fights/poll/respond per validators.ts's respondSchema) instead of picking one and being wrong for the other protocol" + - "Fixed the old bot.js webhook example's signature verification, which hashed BOT_SECRET directly — the real derivation is HMAC-SHA256(key=HMAC-SHA256('botfights-webhook-v1', SHA256(BOT_SECRET)), message=timestamp.body), verified against orchestrator.ts" + - "Did not carry over BOT_SETUP.md's customization API example — POST /api/auth/update requires a nostr-owned publicKey that anonymously-registered (POST /api/bots) bots never have, so including it would document a call the AI-agent flow can never use" + - "BotProfilePage.vue's webhook/polling guide-type selector (two buttons) was removed, not just re-pointed — there is only one document now, so the mode toggle had nothing left to select" + +requirements-completed: [BOT-02] + +coverage: + - id: D1 + description: "One consolidated prompt (frontend/public/docs/BOTFIGHTS.md) contains registration, credentials, both protocols, every endpoint, and every response format — the four superseded docs (BOTFIGHTS-EASY/POLLING/WEBHOOK.md, BOT_SETUP.md) are deleted" + requirement: BOT-02 + verification: + - kind: other + ref: "grep acceptance criteria: {{ARENA_URL}} count>=5, YOUR_BOT_ID/YOUR_BOT_SECRET present, api/fights/poll(/respond)/api/queue/join/X-Botfights-Signature present, botfights.io count=0, superseded files absent, wc -l>=452" + status: pass + human_judgment: false + - id: D2 + description: "GET /api/docs/prompt serves the prompt as text/markdown with {{ARENA_URL}} resolved to PUBLIC_ARENA_URL or the request origin" + requirement: BOT-02 + verification: + - kind: unit + ref: "server/src/routes/docs.test.ts#GET /api/docs/prompt (5 new cases: 200+markdown, no leftover token, PUBLIC_ARENA_URL precedence, origin fallback, YOUR_BOT_ID preserved)" + status: pass + - kind: other + ref: "pnpm exec tsc --noEmit -p server/tsconfig.json" + status: pass + human_judgment: false + - id: D3 + description: "Every in-app setup surface (JoinBoutPage, BotProfilePage, DocsPage) points at the single prompt; DocsPage has a copy-to-clipboard 'Give this to your AI' panel plus the literal /api/docs/prompt URL" + requirement: BOT-02 + verification: + - kind: other + ref: "pnpm exec vue-tsc --noEmit -p frontend/tsconfig.json && grep -rl (deleted doc names) frontend/src returns nothing" + status: pass + - kind: e2e + ref: "e2e/signup-bot.spec.ts#unified AI bot-setup prompt (BOT-02) — 2 new tests written; NOT executed this session, see Issues Encountered" + status: unknown + human_judgment: true + rationale: "The e2e Playwright run against a local dev server could not execute — port 9100 (the local backend's fixed dev port, hardcoded in frontend/vite.config.ts's proxy target) is occupied by the live archi-dev-box botfights container needed for tomorrow's demo. All static/type-level verification passed; the actual browser-driven click-through and live GET /api/docs/prompt fetch through a running app need a human (or a later session with the port free) to confirm." + +# Metrics +duration: 53min +completed: 2026-07-31 +status: complete +--- + +# Phase 9 Plan 3: Unified AI Bot-Setup Prompt Summary + +**Consolidated five drifting BOTFIGHTS setup docs into one self-contained AI prompt, served live at `GET /api/docs/prompt` with the arena URL auto-resolved, and wired every in-app surface (JoinBoutPage, BotProfilePage, DocsPage) to it with a copy-to-clipboard "Give this to your AI" panel.** + +## Performance + +- **Duration:** 53 min +- **Started:** 2026-07-31T01:41:39Z +- **Completed:** 2026-07-31T02:34:21Z +- **Tasks:** 3 +- **Files modified:** 12 (botfight repo) + 1 (this SUMMARY, archy repo) + +## Accomplishments + +- One canonical prompt (`frontend/public/docs/BOTFIGHTS.md`, 621 lines) covers: anonymous `POST /api/bots` registration (previously undocumented anywhere), credentials + both auth forms, full working webhook and polling bot implementations sharing one `ARENA_URL` constant, the exact two-step HMAC webhook signature derivation, an endpoint reference table, challenge payloads/types/scoring reconciled against `docs.ts`, and a troubleshooting table. +- `GET /api/docs/prompt` (new Hono route) serves that file as `text/markdown`, resolving `{{ARENA_URL}}` to `PUBLIC_ARENA_URL` or the request's own origin — so a cloud agent that curls the live arena gets a prompt whose examples already point back at that same arena. +- `frontend/public/docs/BOTFIGHTS-EASY.md`, `BOTFIGHTS-POLLING.md`, `BOTFIGHTS-WEBHOOK.md`, and `BOT_SETUP.md` are deleted; the repo-root `BOTFIGHTS.md` is now a 4-line stub pointing at the canonical copy. +- `JoinBoutPage.vue` and `BotProfilePage.vue` both collapsed their mode-conditional doc paths to the single `/docs/BOTFIGHTS.md`, keeping the `YOUR_BOT_ID`/`YOUR_BOT_SECRET` substitution and adding a new `{{ARENA_URL}} -> window.location.origin` substitution. +- `DocsPage.vue` gained a "GIVE THIS TO YOUR AI" panel above the existing tabs: a copy-URL button, a copy-full-prompt button that fetches `/api/docs/prompt` live, and the literal URL displayed for handing to an agent directly. + +## Task Commits + +Each task was committed atomically (botfight repo, `git push origin main`): + +1. **Task 1: Write the one prompt** - `bbc3c7a` (docs) +2. **Task 2: Serve GET /api/docs/prompt** - `a080956` (feat) +3. **Task 3: Point every in-app surface at the one prompt** - `2dd9947` (feat) + +**Plan metadata:** this SUMMARY, committed to the archy repo (`git push gitea-ai main`). + +_Note: the botfight repo's working tree is shared with a concurrent agent doing BOT-01 (nostr auth) work throughout this session — every stage/commit above was done by explicit file path, never `git add -A`, and pushes landed as clean fast-forwards on top of their `bf240ce`/`635ee39`/`e824f4c` commits with no conflicts._ + +## Files Created/Modified +- `frontend/public/docs/BOTFIGHTS.md` - the canonical unified prompt (452 -> 621 lines) +- `BOTFIGHTS.md` (repo root) - reduced to a 4-line stub +- `BOT_SETUP.md`, `frontend/public/docs/BOTFIGHTS-EASY.md`, `BOTFIGHTS-POLLING.md`, `BOTFIGHTS-WEBHOOK.md` - deleted +- `server/src/routes/docs.ts` - new `GET /prompt` handler +- `server/src/routes/docs.test.ts` - 5 new test cases for the prompt route +- `frontend/src/pages/JoinBoutPage.vue` - `setupDocPath()`/`setupDocName()` collapsed, `{{ARENA_URL}}` substitution added +- `frontend/src/pages/BotProfilePage.vue` - same collapse; removed the now-meaningless webhook/polling guide-type selector +- `frontend/src/pages/DocsPage.vue` - new "Give this to your AI" copy panel +- `e2e/signup-bot.spec.ts` - 2 new tests (copy affordance visible, `GET /api/docs/prompt` returns 200 via `page.request`) + +## Decisions Made +See `key-decisions` in frontmatter — most notably: fixed a real bug in the old webhook signature example (it hashed the raw secret instead of the two-step `secretHash`→`signingKey` derivation the server actually uses), and documented the genuine `trash_talk`/`trashTalk` field-naming split between the webhook and poll protocols rather than picking one casing and silently breaking the other. + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] Webhook signature verification example was wrong** +- **Found during:** Task 1 (writing the consolidated prompt) +- **Issue:** The old `bot.js` webhook example's `verifySignature()` computed `HMAC-SHA256(BOT_SECRET, timestamp.body)` directly. The real server (`orchestrator.ts` lines 183-195) derives the signing key as `HMAC-SHA256('botfights-webhook-v1', SHA256(BOT_SECRET))` first, then HMACs the payload with *that* key. A bot following the old example would always reject valid, correctly-signed requests. +- **Fix:** Rewrote `verifySignature()` in the new webhook example to match the real two-step derivation; documented the exact steps in the new "Webhook verification" section. +- **Files modified:** `frontend/public/docs/BOTFIGHTS.md` +- **Verification:** Manually traced against `server/src/engine/orchestrator.ts` lines 183-195 (read, not modified). +- **Committed in:** `bbc3c7a` (Task 1 commit) + +**2. [Rule 1 - Bug] `trash_talk` field-name mismatch between my own poll/webhook examples** +- **Found during:** Task 1 self-review, after drafting a combined prompt that used `trashTalk` everywhere +- **Issue:** `server/src/lib/validators.ts`'s `respondSchema` (used by `POST /api/fights/poll/respond`) requires camelCase `trashTalk`; `server/src/engine/orchestrator.ts`'s `webhookResponseSchema` (used to parse a webhook bot's HTTP response) requires snake_case `trash_talk` and is `.passthrough()`, so the wrong casing doesn't error — it silently drops the trash talk. My first draft used `trashTalk` in both places, which would have shipped the same latent bug that was never previously documented at all. +- **Fix:** Split the webhook bot's response construction to use `trash_talk`, kept the polling bot's to `trashTalk`, and added an explicit "Webhook vs poll: field naming" callout with a payload example for each protocol. +- **Files modified:** `frontend/public/docs/BOTFIGHTS.md` +- **Verification:** Verified against `server/src/lib/validators.ts` (`respondSchema`) and `server/src/engine/orchestrator.ts` (`webhookResponseSchema`) directly. +- **Committed in:** `bbc3c7a` (Task 1 commit) + +**3. [Rule 2 - Missing critical] BotProfilePage.vue's dead webhook/polling guide-selector removed, not left dangling** +- **Found during:** Task 3 +- **Issue:** The plan called for collapsing to a single doc but the page had a two-button "WEBHOOK / POLLING" selector purely for choosing which of the two now-deleted docs to display. Leaving the buttons in place with both wired to the same content would be confusing dead UI. +- **Fix:** Replaced the two-button selector with a single "LOAD SETUP GUIDE" button; removed the now-unused `guideMode` ref and simplified `loadGuide()` to take no parameter. +- **Files modified:** `frontend/src/pages/BotProfilePage.vue` +- **Verification:** `vue-tsc --noEmit` passes; visually the panel now shows one button instead of two. +- **Committed in:** `2dd9947` (Task 3 commit) + +--- + +**Total deviations:** 3 auto-fixed (2 bug fixes, 1 missing-critical UI cleanup) +**Impact on plan:** All three were necessary for the prompt to actually be correct and usable by an AI agent — the plan's core acceptance bar. No scope creep; no files outside the plan's `files_modified` list were touched. + +## Issues Encountered + +- **e2e suite not executed this session.** `pnpm test:e2e -- e2e/signup-bot.spec.ts` requires a local dev server; `frontend/vite.config.ts`'s dev proxy hardcodes backend target `http://localhost:9100`, but port 9100 on this machine (archi-dev-box) is occupied by the live, healthy `botfights` podman container (42h uptime) that is needed for tomorrow's demo — confirmed via `podman ps`. I did not stop it. My own orphaned `pnpm dev` frontend-only process (backend half crashed with `EADDRINUSE`) was killed cleanly. Recorded to `.planning/WINDOWS.md` as an `unrun-verify` entry (id 5). Everything I *could* verify locally passed: `vue-tsc --noEmit`, `tsc --noEmit`, the full `pnpm --filter frontend build` (confirmed `frontend/dist/docs/BOTFIGHTS.md` ships in the bundle), and `pnpm vitest run --project server` (5 failures, all pre-existing and unrelated — the exact flaky perf-test set documented in `deferred-items.md`: `answers.test.ts`, `lifecycle.test.ts`, `scoring.test.ts`, `bot-auth.test.ts` constant-time; `docs.test.ts` itself passed 8/8 on every run). +- **Shared working tree with a concurrent agent.** The other agent's BOT-01 work (nostr `GET /api/auth/me`, `migrate.ts` DDL sync) landed mid-session via their own commits (`bf240ce`, `635ee39`, `e824f4c`) with no file overlap with this plan's `files_modified` list. Every `git add`/commit in this plan staged only the exact files this plan owns; both pushes were clean fast-forwards. + +## User Setup Required + +None - no external service configuration required. (Note: `PUBLIC_ARENA_URL` is an optional env var the route already supports, but setting it on the deployed canonical arena is a deployment/manifest concern for a different plan, not this one.) + +## Next Phase Readiness + +- The prompt is ready for tomorrow's demo path: any agent that can `curl https://botfights.archipelago-foundation.org/api/docs/prompt` gets a complete, arena-URL-correct instruction set. +- **Before the demo:** a human (or a session with port 9100 free) should run `pnpm test:e2e -- e2e/signup-bot.spec.ts` against a live instance and, ideally, hand the deployed `/api/docs/prompt` output to a real cloud agent once — the plan's own stated acceptance bar ("tomorrow's demo has a cloud-hosted 'openclaw' bot register and fight using ONLY this prompt") is a human/live-system verification this session could not perform. +- No blockers for other 09-* plans — this plan touched only docs/routes/frontend pages already isolated in its own `files_modified` list. + +--- +*Phase: 09-botfights-platform-upgrade* +*Completed: 2026-07-31* + +## Self-Check: PASSED + +- FOUND: `frontend/public/docs/BOTFIGHTS.md` +- FOUND: `BOTFIGHTS.md` (repo-root stub) +- FOUND: `server/src/routes/docs.ts` +- FOUND: `server/src/routes/docs.test.ts` +- CONFIRMED DELETED: `frontend/public/docs/BOTFIGHTS-EASY.md`, `BOTFIGHTS-POLLING.md`, `BOTFIGHTS-WEBHOOK.md`, `BOT_SETUP.md` +- Commit `bbc3c7a` (Task 1): FOUND in `git log --oneline --all` +- Commit `a080956` (Task 2): FOUND in `git log --oneline --all` +- Commit `2dd9947` (Task 3): FOUND in `git log --oneline --all`