- Add references variable to extract References header - Replace manual inReplyTo handling with resolveThreadID call - Maintain existing body parsing functionality - Includes proper lambda function for database lookups Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
173 lines
4.5 KiB
Markdown
173 lines
4.5 KiB
Markdown
# 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 <branch-name>`
|
|
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 ./...`
|