docs(09-03): complete unified AI bot-setup prompt plan (BOT-02)

- SUMMARY for 09-03 (botfight repo commits bbc3c7a, a080956, 2dd9947)
- WINDOWS.md: record e2e suite as unrun-verify (local dev backend port
  9100 is occupied by the live archi-dev-box demo container)

Per explicit user instruction this run, STATE.md/ROADMAP.md/REQUIREMENTS.md
are intentionally left untouched (shared coordination files across
concurrent phase-09 agents).
This commit is contained in:
archipelago 2026-07-30 22:37:23 -04:00
parent 903481afb3
commit 8b1aac0174
2 changed files with 204 additions and 3 deletions

View File

@ -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
}
]
````

View File

@ -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`