# Callback Base URL Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Separate server bind address from callback URL generation and default callback base to localhost when unset. **Architecture:** Keep `LISTEN_ADDR` only for HTTP server binding. Add `CALLBACK_BASE_URL` in config, default to `http://localhost` when empty, and build dispatch callback URL from this base plus `/callback`. **Tech Stack:** Go, stdlib `net/url` + `strings`, existing project packages. --- ### Task 1: Add callback base URL config with localhost fallback **Files:** - Modify: `internal/config/config.go` - Test: `internal/config/config_test.go` (create) - [ ] **Step 1: Write the failing tests** ```go func TestLoad_DefaultCallbackBaseURL_WhenUnset(t *testing.T) { t.Setenv("CALLBACK_BASE_URL", "") setRequiredEnv(t) cfg, err := Load() require.NoError(t, err) require.Equal(t, "http://localhost", cfg.CallbackBaseURL) } func TestLoad_UsesCallbackBaseURL_WhenSet(t *testing.T) { t.Setenv("CALLBACK_BASE_URL", "https://bridge.example.com") setRequiredEnv(t) cfg, err := Load() require.NoError(t, err) require.Equal(t, "https://bridge.example.com", cfg.CallbackBaseURL) } ``` - [ ] **Step 2: Run tests to verify failure** Run: `go test ./internal/config -run CallbackBaseURL -v` Expected: FAIL because `Config` has no `CallbackBaseURL` field yet. - [ ] **Step 3: Write minimal implementation** ```go type Config struct { // ...existing fields... CallbackBaseURL string } cfg := &Config{ // ...existing env reads... CallbackBaseURL: os.Getenv("CALLBACK_BASE_URL"), } if cfg.CallbackBaseURL == "" { cfg.CallbackBaseURL = "http://localhost" } ``` - [ ] **Step 4: Run tests to verify pass** Run: `go test ./internal/config -run CallbackBaseURL -v` Expected: PASS. - [ ] **Step 5: Commit** ```bash git add internal/config/config.go internal/config/config_test.go git commit -m "feat(config): add callback base URL with localhost default" ``` ### Task 2: Build callback URL from callback base URL **Files:** - Modify: `internal/ai_client/client.go` - Test: `internal/ai_client/client_test.go` (create) - [ ] **Step 1: Write the failing tests** ```go func TestBuildCallbackURL_DefaultLocalhostBase(t *testing.T) { got := buildCallbackURL("http://localhost") require.Equal(t, "http://localhost/callback", got) } func TestBuildCallbackURL_TrimsTrailingSlash(t *testing.T) { got := buildCallbackURL("https://bridge.example.com/") require.Equal(t, "https://bridge.example.com/callback", got) } ``` - [ ] **Step 2: Run tests to verify failure** Run: `go test ./internal/ai_client -run BuildCallbackURL -v` Expected: FAIL because helper function does not exist. - [ ] **Step 3: Write minimal implementation** ```go func buildCallbackURL(base string) string { base = strings.TrimRight(base, "/") return base + "/callback" } // in Dispatch: callbackURL := buildCallbackURL(d.cfg.CallbackBaseURL) ``` - [ ] **Step 4: Run tests to verify pass** Run: `go test ./internal/ai_client -run BuildCallbackURL -v` Expected: PASS. - [ ] **Step 5: Commit** ```bash git add internal/ai_client/client.go internal/ai_client/client_test.go git commit -m "fix(ai-client): use callback base URL for callback endpoint" ``` ### Task 3: Update environment docs/examples **Files:** - Modify: `.env.example` - Modify: `README.md` - [ ] **Step 1: Add config key to `.env.example`** ```dotenv LISTEN_ADDR=:8080 CALLBACK_BASE_URL=http://localhost ``` - [ ] **Step 2: Document purpose in README** ```md - `LISTEN_ADDR`: local bind address for HTTP server (e.g. `:8080`) - `CALLBACK_BASE_URL`: public base URL used to build callback endpoint; defaults to `http://localhost` when unset ``` - [ ] **Step 3: Run full test suite** Run: `go test ./...` Expected: PASS. - [ ] **Step 4: Commit** ```bash git add .env.example README.md git commit -m "docs: document callback base URL configuration" ``` ### Task 4: Prepare PR **Files:** - No code changes expected - [ ] **Step 1: Push branch** Run: `git push -u origin ` Expected: branch published. - [ ] **Step 2: Open PR with summary and test evidence** Include: - Problem: callback URL incorrectly built from listen addr - Fix: separate `CALLBACK_BASE_URL` with localhost fallback - Tests: `go test ./internal/config -run CallbackBaseURL -v`, `go test ./internal/ai_client -run BuildCallbackURL -v`, `go test ./...`