Files
egress-proxies-operator/CLAUDE.md
Jan Novak a5f0aa95d2 Codify plan-save ordering and add plan execution summaries
The plan for this build was written to docs/plans/ only after Step 0's
implementation had already started, instead of as its own first action
right after ExitPlanMode. CLAUDE.md said *where* to save plans but not
*when* relative to other work, so that ordering wasn't actually enforced.
Makes it explicit: copying the plan into docs/plans/ and committing it is
its own checkpoint that blocks starting Step 0.

Also adds a docs/plans-executions/ convention: one running, chronological
summary file per plan, appended to after each completed step, for a
human-readable narrative of how the plan actually went (judgment calls,
spec gaps, surprises) without duplicating the plan or the diff.

Backfills the Step 0 entry for the proxy-operator plan retroactively.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-07 20:34:05 +02:00

7.6 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

What this is

A Kubernetes operator for managing egress proxies — currently an empty scaffold with no controller code yet. Intended shape: a controller-runtime manager reconciling custom resources (or existing workload types) to provision/configure egress proxy infrastructure. TODO: confirm the actual CRD(s)/API group once implemented.

Commands

No Makefile, linter, or entrypoint exists yet. Verified state as of bootstrap:

go build ./...   # nothing to build yet — no .go source files exist
go test ./...    # no test files exist yet

No linting or formatting tools are configured in this project yet — golangci-lint and gofumpt are pre-approved in .claude/settings.json for when they're wired up, but neither has a config file or CI step yet. There is no main.go or cmd/ entrypoint yet, so there's nothing to run locally.

Architecture

Not yet built. TODO: once the controller-runtime manager and reconcile loop exist, document the data-flow diagram and package/role table here, following the standard reconcile-loop shape: fetch → resolve → list related → compute desired state → merge → patch only on diff.

Conventions

Go Conventions

  • Use the latest stable Go version (check https://go.dev/dl/ before starting a new feature).
  • Use slog for structured logging.
  • context.Context is the first parameter of every function that does I/O or can be cancelled.
  • No init() unless there is no other option.
  • Error wrapping: fmt.Errorf("...: %w", err) for single errors; errors.Join for multiple.
  • Prefer stdlib over third-party dependencies where reasonable.
  • No vendoring unless explicitly requested.
  • Fail fast, return early.
  • No obvious comments.

Testing

All new Go code must have corresponding tests.

  • Use stdlib testing — no testify unless already present in the module.
  • Table-driven tests are the default pattern.
  • Test files live next to source: foo.gofoo_test.go.
  • Name tests Test<Function>_<scenario> (e.g. TestParseRoster_emptyInput).
  • Use t.Parallel() where safe.
  • Use subtests for table entries: t.Run(tc.name, func(t *testing.T) { ... }).
  • Test behaviour, not implementation.
  • Guard slow tests with testing.Short(): if testing.Short() { t.Skip() }.
  • Integration tests that need real infra (DB, network) go behind //go:build integration and use testcontainers-go rather than mocking the infra away.

Useful test commands

go test ./...               # all unit tests
go test -v ./...            # verbose
go test -cover ./...        # with coverage
go test -short ./...        # skip slow/integration tests
go test -v ./internal/...   # specific package tree
go test -race -v -run TestName ./path/to/package   # single test, race detector on
go test -tags=integration ./...                    # integration tests (requires infra)

Plans

When Claude Code's plan mode is used, save the plan file inside the repo at docs/plans/YYYY-MM-DD-HHMM-<slug>.md instead of the default ~/.claude/plans/ location. Get the timestamp with date "+%Y-%m-%d-%H%M". The <slug> is a short kebab-case summary of the plan's topic.

Include the same timestamp in the plan's header, e.g.:

# Plan: <title>
**Created:** 2026-08-07 15:30

Create docs/plans/ on first use. Plan files are committed to the repo so other contributors can review historical decisions.

Ordering matters: plan mode itself restricts writes to the default ~/.claude/plans/ location — that's a hard tool restriction during plan mode, not a choice. So the copy into docs/plans/ cannot happen until after ExitPlanMode. When it happens, it must be the very first action taken post-exit, committed on its own, before any implementation work starts (branching, scaffolding, editing code). Do not fold the plan-file commit into the first implementation commit, and do not defer it until "later" in the same turn — treat "save and commit the plan" as its own checkpoint that blocks starting Step 0.

Plan execution summaries

After finishing each step of an approved plan, append a short, human-readable summary to docs/plans-executions/YYYY-MM-DD-HHMM-<slug>.md — same timestamp and slug as the originating plan in docs/plans/, so the two files pair up 1:1. Create the file (and docs/plans-executions/ on first use) after the first step completes; append a new section per step after that, in chronological order (bottom of the file), not newest-first like CHANGELOG.md.

Keep each entry light — a few sentences on what was done, plus anything genuinely interesting or non-obvious about how it went (a judgment call made, a spec gap found, something that didn't work as expected). This is not a duplicate of the plan or the diff; skip steps that went exactly as planned with nothing worth flagging. Commit the summary update together with that step's implementation commit.

Changelog

Maintain a running changelog in CHANGELOG.md at the repo root. After every significant change, fix, or update — once the user confirms it works — append a new entry at the top of the file in this format:

## YYYY-MM-DD HH:MM TZ — short title

- One-line summary of what changed and why.
- Key files touched (optional, only if useful for traceability).

Get the timestamp with date "+%Y-%m-%d %H:%M %Z". Never write a literal placeholder like HH:MM — always run date first to get the real current time. Skip trivial edits (typos, formatting, comment tweaks); only log changes a future reader would care about.

Branching & Merge Requests

The remote is Gitea (gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator). For features, do not commit to main directly. Use a branch + merge request flow:

  1. Create a branch off main before starting work:

    • feat/<slug> for features (e.g. feat/roster-import)
    • fix/<slug> for bug-fix branches the user explicitly asks for
    • <slug> is short kebab-case
  2. Commit on the branch following the commit conventions below.

  3. Push the branch to origin with -u so it tracks.

  4. Open the MR with tea rather than printing a compare URL:

    tea pr create \
      --title "<short title>" \
      --description "<body>" \
      --base main \
      --head <branch>
    

    tea is already authenticated against the Gitea instance; just run it. Print the resulting PR URL for the user. If tea is unavailable, fall back to printing the compare URL (gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/compare/main...<branch>) and let the user open the MR manually.

  5. Do not merge or delete the branch from the CLI — neither via tea, gh, nor git push --delete. The user does that in Gitea.

Exceptions — committing straight to main is fine for:

  • Small bug fixes / hotfixes the user describes as such.
  • Typo / comment / formatting tweaks.
  • Edits the user explicitly says to push to main.

When uncertain whether something is a feature or a small fix, ask before branching.

Git Commits

Always append a Co-Authored-By trailer to indicate AI assistance:

Co-Authored-By: Claude <noreply@anthropic.com>

TODO: no .gitea/workflows/ CI pipeline exists yet — add a CI/CD subsection here once one is set up.

Gotchas

  • This repo currently contains only go.mod (module gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator) — no controller code, main.go, Dockerfile, k8s manifests, Makefile, or CI config exist yet. Nothing in this file describes working software; it's the convention scaffold to build against.