Files
dance-lessons-coach/AGENTS.md
Gabriel Radureau c17fb4f9b4
Some checks failed
CI/CD Pipeline / Build Docker Cache (push) Successful in 10s
CI/CD Pipeline / CI Pipeline (push) Failing after 4m14s
CI/CD Pipeline / Trigger Docker Push (push) Has been skipped
🐛 fix: emit all config-loading logs in correct JSON format from the start (#16)
## Summary

Closes #15

When `logging.json: true` (or `DLC_LOGGING_JSON=true`), the logger was unconditionally initialised to console/text format at the top of `LoadConfig()`, so early log lines — most visibly **"Config file loaded"** — were always written as human-readable text regardless of configuration.

## Root cause

Classic chicken-and-egg: the format flag lives inside the config that is being loaded. The format-switch block only ran *after* `v.Unmarshal()`, too late for the config-file log.

## Changes

### `pkg/config/config.go`
- Add `peekJSONLogging()`: resolves the JSON flag **before** any log is emitted by (1) checking `DLC_LOGGING_JSON` directly via `os.Getenv`, then (2) doing a minimal throwaway Viper pre-read of the config file for the `logging.json` key. This mirrors Viper's own priority order without parsing the full config twice.
- Apply the resolved format immediately and emit **"Logging configured"** as the very first log line.
- Remove the now-redundant format-switch block that ran after `Unmarshal()`.

### `scripts/start-server.sh`, `test-graceful-shutdown.sh`, `test-opentelemetry.sh`
- Replace hardcoded `PROJECT_DIR` path with a dynamic `SCRIPTS_DIR=$(dirname $(realpath ${BASH_SOURCE[0]}))` derivation so scripts work from any worktree or clone location.

## Test plan
- [x] `go test ./pkg/...` — all pass
- [x] `scripts/test-graceful-shutdown.sh` — all JSON valid, all startup logs present
- [x] Manual smoke test: first line is `{"level":"info",...,"message":"Logging configured"}`, every line is valid JSON

Reviewed-on: #16
Co-authored-by: Gabriel Radureau <arcodange@gmail.com>
Co-committed-by: Gabriel Radureau <arcodange@gmail.com>
2026-04-12 23:28:35 +02:00

5.7 KiB

dance-lessons-coach — Agent Documentation

AI agent reference for developing, testing, and operating the dance-lessons-coach service.

Tech Stack

Component Technology Version
Language Go 1.26.1
Router Chi v5.2.5
Logging Zerolog v1.35.0
Configuration Viper v1.21.0
Telemetry OpenTelemetry v1.43.0

Project Structure

dance-lessons-coach/
├── adr/                    # Architecture Decision Records
├── cmd/
│   ├── greet/              # CLI application
│   └── server/             # Web server entry point
├── pkg/
│   ├── config/             # Viper-based configuration
│   ├── greet/              # Core domain logic + API handlers
│   ├── server/             # HTTP server, routing, graceful shutdown
│   ├── telemetry/          # OpenTelemetry instrumentation
│   ├── user/               # User domain (auth, JWT, repository)
│   └── validation/         # Request validation
├── scripts/                # Server lifecycle, build, test scripts
├── config.yaml             # Configuration file
└── config.example.yaml     # Configuration template

Server Management

# Start / stop / restart
./scripts/start-server.sh start
./scripts/start-server.sh stop
./scripts/start-server.sh restart

# Status and logs
./scripts/start-server.sh status
./scripts/start-server.sh logs

# Test all API endpoints
./scripts/start-server.sh test

Configuration

All settings can be provided via config.yaml or environment variables (DLC_ prefix).

Option Env var Default Description
Host DLC_SERVER_HOST 0.0.0.0 Bind address
Port DLC_SERVER_PORT 8080 Listening port
Shutdown timeout DLC_SHUTDOWN_TIMEOUT 30s Graceful shutdown window
JSON logging DLC_LOGGING_JSON false Structured JSON output
Log output DLC_LOGGING_OUTPUT "" File path (empty = stderr)
API v2 DLC_API_V2_ENABLED false Enable /api/v2 routes
Config file DLC_CONFIG_FILE ./config.yaml Override config path

Minimal config.yaml:

server:
  host: "0.0.0.0"
  port: 8080
shutdown:
  timeout: 30s
logging:
  json: false

Priority: env var > config file > default.

API Endpoints

Method Path Description
GET /api/health Liveness — always {"status":"healthy"}
GET /api/ready Readiness — 200 when ready, 503 during shutdown
GET /api/version Version info (?format=plain|full|json)
GET /api/v1/greet/ Default greeting
GET /api/v1/greet/{name} Personalized greeting
POST /api/v2/greet V2 greeting with validation (feature-flagged)
GET /swagger/ Swagger UI
GET /swagger/doc.json OpenAPI spec
curl http://localhost:8080/api/health
curl http://localhost:8080/api/ready
curl http://localhost:8080/api/v1/greet/Alice
curl -X POST http://localhost:8080/api/v2/greet \
  -H "Content-Type: application/json" -d '{"name":"Alice"}'

Testing

# Unit + integration tests
go test ./...
go test -v ./...

# Graceful shutdown + JSON logging validation
./scripts/test-graceful-shutdown.sh

# OpenTelemetry end-to-end
./scripts/test-opentelemetry.sh

Note: Do not call go generate unless editing API endpoint annotations. When needed: go generate ./pkg/server/

Build

./scripts/build.sh
# Produces: ./bin/server  ./bin/greet
./bin/server --version

Build injects version, commit, and date via -ldflags.

Graceful Shutdown

On SIGTERM / SIGINT:

  1. Readiness context is cancelled → /api/ready returns 503.
  2. 1-second propagation window (load balancer drains).
  3. srv.Shutdown() waits up to shutdown.timeout for active requests.
  4. Process exits cleanly.

Health endpoint stays 200 throughout; readiness endpoint goes 503 immediately on signal.

OpenTelemetry / Jaeger

Enable in config or via env:

export DLC_TELEMETRY_ENABLED=true
export DLC_TELEMETRY_OTLP_ENDPOINT="localhost:4317"

Quick Jaeger setup:

docker run -d --name jaeger \
  -e COLLECTOR_OTLP_ENABLED=true \
  -p 16686:16686 -p 4317:4317 \
  jaegertracing/all-in-one:latest

Architecture Decision Records

ADR Decision
0001 Go 1.26.1
0002 Chi router
0003 Zerolog
0004 Interface-based design
0005 Graceful shutdown
0006 Viper configuration
0007 OpenTelemetry
0008 BDD with Godog
0009 Hybrid testing strategy

Add a new ADR: copy an existing file, edit it, update adr/README.md.

Commit Conventions

Conventional Commits with optional gitmoji:

Emoji Type When
feat New feature
🐛 fix Bug fix
📝 docs Documentation
🎨 style Formatting only
♻️ refactor Structural change
🚀 perf Performance
🔒 security Security fix
📦 chore Dependencies / build
🧪 test Tests
🤖 ci CI/CD
🔥 remove Delete code/files

Examples:

feat: add JWT authentication middleware
fix: ensure first log line is JSON when json logging is enabled
docs: rewrite AGENTS.md for clarity