Files
archy/.planning/phases/09-botfights-platform-upgrade/09-03-SUMMARY.md
T

15 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, requirements-completed, coverage, duration, completed, status
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions requirements-completed coverage duration completed status
09-botfights-platform-upgrade 03 docs
hono
vue3
markdown
ai-prompt
api-docs
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
09-04-canonical-arena-deployment
09-07-demo-verification
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
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
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
BOT-02
id description requirement verification human_judgment
D1 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 BOT-02
kind ref status
other 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 pass
false
id description requirement verification human_judgment
D2 GET /api/docs/prompt serves the prompt as text/markdown with {{ARENA_URL}} resolved to PUBLIC_ARENA_URL or the request origin BOT-02
kind ref status
unit 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) pass
kind ref status
other pnpm exec tsc --noEmit -p server/tsconfig.json pass
false
id description requirement verification human_judgment rationale
D3 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 BOT-02
kind ref status
other pnpm exec vue-tsc --noEmit -p frontend/tsconfig.json && grep -rl (deleted doc names) frontend/src returns nothing pass
kind ref status
e2e e2e/signup-bot.spec.ts#unified AI bot-setup prompt (BOT-02) — 2 new tests written; NOT executed this session, see Issues Encountered unknown
true 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.
53min 2026-07-31 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 secretHashsigningKey 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