docs: align historical plans/specs with renamed identifiers (roundTrip -> execute, log schema)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-04 20:24:05 +02:00
parent 5ebb0f756d
commit 5b238b3f54
5 changed files with 20 additions and 20 deletions

View File

@@ -415,7 +415,7 @@ EOF
**Interfaces:**
- Consumes: nothing from other tasks.
- Produces: `Config{PythonPath, GarminEmail, GarminPassword, TokenStorePath string}`, `NewClient(cfg Config) Client`, `subprocessClient` (unexported struct implementing `Client`), `wireRequest{ID int; Cmd string; Params any}`, `wireResponse{ID int; Result json.RawMessage; Error string}`, `callParams{Method string; Args map[string]any}`, `(*subprocessClient).roundTrip(cmd string, params any) (json.RawMessage, error)`, `(*subprocessClient).ensureStarted() error`, `(*subprocessClient).close() error` (unexported, no-lock; `Close()`/`UpdateCredentials` call it while already holding `c.mu`). Later tasks (3, 4) add methods to `subprocessClient` using `roundTrip`/`ensureStarted` and must not redefine these types.
- Produces: `Config{PythonPath, GarminEmail, GarminPassword, TokenStorePath string}`, `NewClient(cfg Config) Client`, `subprocessClient` (unexported struct implementing `Client`), `wireRequest{ID int; Cmd string; Params any}`, `wireResponse{ID int; Result json.RawMessage; Error string}`, `callParams{Method string; Args map[string]any}`, `(*subprocessClient).roundTrip(cmd string, params any) (json.RawMessage, error)`, `(*subprocessClient).ensureStarted() error`, `(*subprocessClient).close() error` (unexported, no-lock; `Close()`/`UpdateCredentials` call it while already holding `c.mu`). Later tasks (3, 4) add methods to `subprocessClient` using `execute`/`ensureStarted` and must not redefine these types.
- [ ] **Step 1: Write the failing tests for the transport layer**
@@ -576,7 +576,7 @@ func TestSubprocessClient_Close_NoOpWhenNotStarted(t *testing.T) {
- [ ] **Step 2: Run the tests to verify they fail**
Run: `cd backend && go test ./internal/garmin/... -run TestSubprocessClient -v`
Expected: compile failure — `subprocessClient`, `wireResponse`, `roundTrip`, `maxWrapperLineBytes`, `Config` (new shape) don't exist yet.
Expected: compile failure — `subprocessClient`, `wireResponse`, `execute`, `maxWrapperLineBytes`, `Config` (new shape) don't exist yet.
- [ ] **Step 3: Rewrite `client.go`**
@@ -879,7 +879,7 @@ EOF
- Modify: `backend/internal/garmin/client_test.go`
**Interfaces:**
- Consumes: `subprocessClient`, `roundTrip`, `ensureStarted`, `mapAuthStatus`, `authResultWire`, `newFakeWrapperClient`/`fakeResult`/`fakeError` from Task 2.
- Consumes: `subprocessClient`, `execute`, `ensureStarted`, `mapAuthStatus`, `authResultWire`, `newFakeWrapperClient`/`fakeResult`/`fakeError` from Task 2.
- Produces: `(*subprocessClient).Authenticate(ctx) (AuthResult, error)`, `(*subprocessClient).CompleteMFA(ctx, code) (AuthResult, error)`.
- [ ] **Step 1: Write the failing tests**
@@ -992,7 +992,7 @@ Expected: compile failure — `Authenticate`/`CompleteMFA` methods don't exist o
- [ ] **Step 3: Implement `Authenticate`/`CompleteMFA`**
Append to `backend/internal/garmin/client.go` (after `roundTrip`):
Append to `backend/internal/garmin/client.go` (after `execute`):
```go
func (c *subprocessClient) Authenticate(ctx context.Context) (AuthResult, error) {

View File

@@ -4,14 +4,14 @@
**Goal:** Emit structured JSON logs on stdout for (1) every HTTP API request and (2) every Garmin wrapper subprocess call, correlated by a per-request id — without touching any existing `log.Printf` call site.
**Architecture:** A new `internal/applog` package wraps `log/slog` with a JSON handler plus context helpers (`WithLogger`/`FromContext`). `internal/api` gains a `loggingMiddleware` that logs one line per HTTP request and stashes a request-id-tagged logger into the request context. `internal/garmin`'s `client.go` reads that same logger back out of context (via the `ctx` every `Client` method already receives) inside `roundTrip`, the single funnel point all six Garmin calls go through, logging one line per subprocess round-trip.
**Architecture:** A new `internal/applog` package wraps `log/slog` with a JSON handler plus context helpers (`WithLogger`/`FromContext`). `internal/api` gains a `loggingMiddleware` that logs one line per HTTP request and stashes a request-id-tagged logger into the request context. `internal/garmin`'s `client.go` reads that same logger back out of context (via the `ctx` every `Client` method already receives) inside `execute`, the single funnel point all six Garmin calls go through, logging one line per subprocess round-trip.
**Tech Stack:** Go stdlib `log/slog` (no new dependency), `github.com/go-chi/chi/v5/middleware` (already-available subpackage of the existing chi dependency).
## Global Constraints
- Existing `log.Printf`/`log.Fatalf` call sites are untouched — they keep going to stderr, unstructured, exactly as today.
- Garmin credentials must never be logged. Confirmed safe: `authenticate`/`complete_mfa`/`call` wire params never carry email/password (only env vars at subprocess spawn do) — logging `params` in full is safe everywhere in `roundTrip`.
- Garmin credentials must never be logged. Confirmed safe: `authenticate`/`complete_mfa`/`call` wire params never carry email/password (only env vars at subprocess spawn do) — logging `params` in full is safe everywhere in `execute`.
- `gofmt -l .` must report nothing; `go vet ./...` and `go build ./...` must pass.
---
@@ -480,14 +480,14 @@ git commit -m "feat(api): add structured JSON access log with request-id correla
### Task 3: Garmin wrapper call log
**Files:**
- Modify: `backend/internal/garmin/client.go` (`roundTrip`, `ensureStarted`, all six call sites)
- Modify: `backend/internal/garmin/client_test.go` (update 3 direct-`roundTrip` calls, add 2 new tests)
- Modify: `backend/internal/garmin/client.go` (`execute`, `ensureStarted`, all six call sites)
- Modify: `backend/internal/garmin/client_test.go` (update 3 direct-`execute` calls, add 2 new tests)
**Interfaces:**
- Consumes: `applog.FromContext` (Task 1).
- Produces: `func (c *subprocessClient) roundTrip(ctx context.Context, cmdName string, params any) (result json.RawMessage, err error)` (signature change — `ctx` added as first param), `func (c *subprocessClient) ensureStarted(ctx context.Context) error` (signature change — `ctx` added).
- [ ] **Step 1: Update the 3 existing direct-`roundTrip` tests and write the 2 new failing tests**
- [ ] **Step 1: Update the 3 existing direct-`execute` tests and write the 2 new failing tests**
In `backend/internal/garmin/client_test.go`, add `"bytes"` and `"log/slog"` to the stdlib import block, and add `"geniusrun/backend/internal/applog"` as a new import group.
@@ -591,7 +591,7 @@ func TestSubprocessClient_RoundTrip_LogsErrorAtWarnLevel(t *testing.T) {
- [ ] **Step 2: Run tests to verify they fail**
Run: `cd backend && go test ./internal/garmin/... -run 'TestSubprocessClient_RoundTrip' -v`
Expected: FAIL — `roundTrip` still takes 2 args, not 3; `applog` import unresolved.
Expected: FAIL — `execute` still takes 2 args, not 3; `applog` import unresolved.
- [ ] **Step 3: Update `client.go`**
@@ -690,7 +690,7 @@ to:
}
```
Replace `roundTrip` entirely. From:
Replace `execute` entirely. From:
```go
// roundTrip sends one request and returns its result payload, or an error

View File

@@ -165,14 +165,14 @@ EOF
### Task 2: `internal/garmin/client.go` exposes `ErrNotFound`
**Files:**
- Modify: `backend/internal/garmin/client.go` (`wireResponse`, new `ErrNotFound`, `roundTrip`)
- Modify: `backend/internal/garmin/client.go` (`wireResponse`, new `ErrNotFound`, `execute`)
- Modify: `backend/internal/garmin/client_test.go` (`wireResponsePayload`/harness, new test)
**Interfaces:**
- Consumes: `wireResponse.NotFound` (wire field written by Task 1's `wrapper.py` change).
- Produces: `var ErrNotFound error` -- Task 4 (`fillPendingWorkouts`) checks
`errors.Is(err, garmin.ErrNotFound)` against errors returned by `GetWorkoutByID` (and, since
this is wired at the generic `roundTrip` level, any other `Client` method too).
this is wired at the generic `execute` level, any other `Client` method too).
- [ ] **Step 1: Write the failing test**
@@ -256,7 +256,7 @@ already added it to the test file; the actual compile failure is `wireResponse`
`NotFound` yet, and `ErrNotFound` is undefined) -- confirms the test exercises code that doesn't
exist yet.
- [ ] **Step 3: Add `ErrNotFound` and wire it through `roundTrip`**
- [ ] **Step 3: Add `ErrNotFound` and wire it through `execute`**
In `backend/internal/garmin/client.go`, replace:
@@ -289,7 +289,7 @@ type wireResponse struct {
var ErrNotFound = errors.New("garmin: resource not found")
```
Replace, in `roundTrip`:
Replace, in `execute`:
```go
if resp.Error != "" {