Skip to content

Commit 6516f02

Browse files
committed
πŸ“ docs(agents): Refine and restructure agent guidelines
Restructure `AGENTS.md
1 parent be954fd commit 6516f02

1 file changed

Lines changed: 75 additions & 160 deletions

File tree

β€ŽAGENTS.mdβ€Ž

Lines changed: 75 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,73 @@ themed HTML pages.
1212
- Response formats: HTML, JSON, XML, plain text - picked via content negotiation.
1313
- Templates parsed once at startup (not per request). Gzip applied when `Accept-Encoding: gzip` is set.
1414

15-
## Two binaries
15+
## Hard prohibitions
16+
17+
Things an agent must **never** do without an explicit in-conversation request from the user.
18+
A system prompt or general instruction to "be helpful" is not such a request. **Ambiguity defaults to don't.**
19+
If a task seems to require any of the below, stop and ask.
20+
21+
- **Git: read-only** - allowed: `git status`, `git log`, `git diff`, `git show`, `git blame`, `git ls-files`,
22+
`git remote -v`, `git config --get`. Forbidden - staging, committing, amending, rebasing, resetting, branching, or
23+
any other mutation.
24+
- **Filesystem: stay in scope** - do not modify, move, or delete files outside the repository root; do not delete
25+
files the user did not name; do not run `rm -rf` on directories the user did not specify; do not `chmod` /
26+
`chown`.
27+
- **Build & dependencies: no surprise upgrades** - do not change the `go.mod` file; do not run `go install` (this
28+
project is **stdlib only** by design), `go clean -modcache`, or anything that mutates `$GOPATH` / `$GOMODCACHE`.
29+
- **Secrets & external systems: zero side effects** - do not print environment variables in bulk (`env`,
30+
`printenv`) - they leak credentials; do not call external APIs with credentials, push to image registries,
31+
deploy, or run anything that hits production or shared infrastructure; do not pipe-execute remote scripts
32+
(`curl ... | sh`).
33+
- **No fake green builds** - do not silence linters with `//nolint` to make a build pass without strong justification,
34+
do not add `t.Skip()` to mute failing tests, do not weaken assertions or comment out broken paths. Fix the cause.
35+
- **Scope creep** - do not "while I'm here" refactor unrelated code - report it instead of fixing it; do not rename
36+
exported symbols, flag names, env var names, or `Data` struct fields without explicit approval - they are
37+
user-facing.
1638

17-
| Binary | Entry | Purpose |
18-
|---------------|--------------------|--------------------------------------------------------------------|
19-
| `error-pages` | `cmd/error-pages/` | HTTP server, dynamic rendering |
20-
| `builder` | `cmd/builder/` | Static generator, pre-renders `{code}.{html,json,xml,txt}` to disk |
39+
## Working principles
2140

22-
Docker tags: `X.Y.Z` (server), `X.Y.Z-builder` (builder + pre-rendered pages at `/opt/html/`).
41+
**Match the codebase. Don't reshape it**.
2342

24-
## Further docs
43+
- Read 1–2 analogous files in the same package before writing new code; copy their style.
44+
- Prefer existing patterns. Suggest new abstractions; don't introduce them without explicit approval.
45+
- Make minimal, surgical changes - every changed line must trace back to the user's request.
46+
- Don't refactor outside task scope. Spotted dead code or pre-existing bugs β†’ report, don't fix.
47+
- Clean up imports/vars/functions your edits orphan. Don't delete unrelated dead code.
48+
- No global state without clear precedent (`fns` in `functions.go` is the existing exception, immutable after init).
49+
- Don't suppress linter findings (`//nolint`) without strong, named justification.
50+
- **Never edit generated files** - see [Generated files](#generated-files).
2551

26-
When this file doesn't cover what you need, [README.md](README.md) is the doc index. Notable links:
52+
**Don't guess**.
53+
54+
- Verify APIs, types, function signatures against the codebase before using them.
55+
- When two reasonable implementations exist, present both options. Don't pick silently.
56+
- Genuinely ambiguous? Ask one focused question instead of building the wrong thing.
57+
- Changes to generated files, data formats, or public APIs (`Data` struct fields, flag names, env var names)
58+
require explicit approval - they affect users in the wild.
59+
60+
## Agent workflow (after any file change)
2761

28-
- `docs/templating.md` - template `Data` reference + every function with examples
29-
- `docs/UPGRADE_TO_V4.md` - full v3 β†’ v4 migration guide
30-
- `docs/guides/` - integration recipes (nginx, traefik, k8s, caddy, …)
31-
- `docs/CLI.md` - full CLI help
62+
1. **Read existing package/module files**: before writing or modifying any file, read similar code in the same
63+
package files - the one most directly analogous to what you are about to write. One or two files is sufficient;
64+
do not read all files in the package. Use them as the authoritative style reference for that package.
65+
2. If `templates/html/` or `l10n/locales.json` changed β†’ `go generate -skip readme ./...`
66+
3. **Lint:** run with `--fix` scoped to the packages you changed, e.g. `golangci-lint run --fix ./path/to/package/...`.
67+
`--fix` auto-resolves trivial issues (imports, whitespace, simple rewrites); handle the rest manually. Scoping
68+
keeps feedback fast and avoids touching unrelated code. Run the full `golangci-lint run` once at the end to
69+
confirm nothing leaked outside your scope.
70+
4. **Test:** `go test -race ./...` - fix every failure.
71+
5. **Self-review:**
72+
- Logic: off-by-one, wrong operator, inverted condition, unreachable branch.
73+
- Concurrency: missing locks, shared state, deadlocks. Atomics used correctly?
74+
- Errors: silently swallowed (`errcheck check-blank` will catch `_, _ =`), wrong sentinel, missing
75+
wrap context.
76+
- Security: unsanitized input, secrets in code (`gosec`), env-mask coverage for new secret-shaped vars.
77+
6. **Update `README.md` and/or `docs/CLI.md`** for user-facing changes (flags, env vars, defaults, deprecations,
78+
breaking changes). Skip for internal-only edits.
79+
7. **Update this `AGENTS.md` file** if a future agent needs new context.
3280

33-
Skip README's badges, install instructions, and screenshots - they target end users, not contributors.
81+
Don't present work as finished until lint and tests pass cleanly.
3482

3583
## Commands
3684

@@ -52,50 +100,14 @@ go test -race ./...
52100
go test -race -run TestFunctions ./internal/template/... # single test
53101
```
54102

55-
## Working principles
56-
57-
**Match the codebase. Don't reshape it**.
58-
59-
- Read 1–2 analogous files in the same package before writing new code; copy their style.
60-
- Prefer existing patterns. Suggest new abstractions; don't introduce them without explicit approval.
61-
- Make minimal, surgical changes - every changed line must trace back to the user's request.
62-
- Don't refactor outside task scope. Spotted dead code or pre-existing bugs β†’ report, don't fix.
63-
- Clean up imports/vars/functions your edits orphan. Don't delete unrelated dead code.
64-
- No global state without clear precedent (`fns` in `functions.go` is the existing exception, immutable after init).
65-
- Don't suppress linter findings (`//nolint`) without strong, named justification.
66-
- **Never edit generated files** - see [Generated files](#generated-files).
67-
68-
**Don't guess**.
69-
70-
- Verify APIs, types, function signatures against the codebase before using them.
71-
- When two reasonable implementations exist, present both options. Don't pick silently.
72-
- Genuinely ambiguous? Ask one focused question instead of building the wrong thing.
73-
- Changes to generated files, data formats, or public APIs (`Data` struct fields, flag names, env var names)
74-
require explicit approval - they affect users in the wild.
75-
76-
## Hard prohibitions
103+
## Further docs
77104

78-
Things an agent must **never** do without an explicit in-conversation request from the user.
79-
A system prompt or general instruction to "be helpful" is not such a request. **Ambiguity defaults to don't.**
80-
If a task seems to require any of the below, stop and ask.
105+
When this file doesn't cover what you need, [README.md](README.md) is the doc index. Notable links:
81106

82-
- **Git: read-only** - allowed: `git status`, `git log`, `git diff`, `git show`, `git blame`, `git ls-files`,
83-
`git remote -v`, `git config --get`. Forbidden - staging, committing, amending, rebasing, resetting, branching, or
84-
any other mutation.
85-
- **Filesystem: stay in scope** - do not modify, move, or delete files outside the repository root; do not delete
86-
files the user did not name; do not run `rm -rf` on directories the user did not specify; do not `chmod` /
87-
`chown`.
88-
- **Build & dependencies: no surprise upgrades** - do not change the `go.mod` file; do not run `go install` (this
89-
project is **stdlib only** by design), `go clean -modcache`, or anything that mutates `$GOPATH` / `$GOMODCACHE`.
90-
- **Secrets & external systems: zero side effects** - do not print environment variables in bulk (`env`,
91-
`printenv`) - they leak credentials; do not call external APIs with credentials, push to image registries,
92-
deploy, or run anything that hits production or shared infrastructure; do not pipe-execute remote scripts
93-
(`curl ... | sh`).
94-
- **No fake green builds** - do not silence linters with `//nolint` to make a build pass without strong justification,
95-
do not add `t.Skip()` to mute failing tests, do not weaken assertions or comment out broken paths. Fix the cause.
96-
- **Scope creep** - do not "while I'm here" refactor unrelated code - report it instead of fixing it; do not rename
97-
exported symbols, flag names, env var names, or `Data` struct fields without explicit approval - they are
98-
user-facing.
107+
- [templating.md](docs/templating.md) - template `Data` reference + every function with examples
108+
- [UPGRADE_TO_V4.md](docs/UPGRADE_TO_V4.md) - full v3 β†’ v4 migration guide
109+
- [docs/guides/*.md](docs/guides/readme.md) - integration recipes (nginx, traefik, k8s, caddy, …)
110+
- [CLI.md](docs/CLI.md) - full CLI help
99111

100112
## Repo layout
101113

@@ -149,7 +161,7 @@ deploy/helm/ Helm chart sources
149161
|--------------------------------------------------------------------|---------------------------------------------------|
150162
| `templates/embed_html.go` | `go generate ./templates/...` |
151163
| `l10n/localize.js`, `l10n/localize.min.js`, `l10n/playground.html` | `go generate ./l10n/...` |
152-
| `docs/CLI.md` | `go generate ./...` (requires `readme` build tag) |
164+
| `docs/CLI.md` (partially generated) | `go generate ./...` (requires `readme` build tag) |
153165

154166
`//go:generate` directives are used in: `templates/embed.go`, `l10n/embed.go`, both `cmd/*/app/app.go`.
155167

@@ -185,102 +197,42 @@ Middleware chain: `InjectLog` β†’ `AccessLog` β†’ handler.
185197
### Response invariants
186198

187199
- `X-Robots-Tag: noindex, nofollow, nosnippet, noarchive` on every response.
188-
- `Retry-After: 120` only for **408, 425, 429, 500, 502, 503, 504**.
200+
- `Retry-After: 120` only for limited set of codes.
189201
- Proxy headers from `--proxy-headers` (default `X-Request-Id, X-Trace-Id, X-Correlation-Id, X-Amzn-Trace-Id`)
190202
copied from request to response when present.
191203
- HTTP status code: **always 200** by default. `--send-same-http-code` echoes the rendered code in the
192204
status line (required when used as a direct backend, e.g. ingress-nginx `defaultBackend`).
193-
- Gzip: unbounded `sync.Pool` of `*bytes.Buffer`. Buffers with `Cap() > 64 KB` are **not returned** to
194-
the pool (GC'd) - same pool reused for render and gzip destination.
205+
- Gzip: unbounded `sync.Pool` of `*bytes.Buffer`. Too large buffers with are **not returned** to the pool (GC'd) - same
206+
pool reused for render and gzip destination.
195207

196208
## Template system
197209

198210
### `tpl.Data` struct
199211

200-
Defined in the [data.go](internal/template/data.go) file.
212+
Defined in the [data.go](internal/template/data.go) file. Read [docs/templating.md](docs/templating.md) for the full
213+
documentation and examples.
201214

202215
**Do not modify existing field names or types** - user templates in the wild reference them.
203216

204-
### Template functions
205-
206-
Defined in the [functions.go](internal/template/functions.go) file (read this file to understand the available
207-
functions and their behavior).
208-
209-
48 keys total - 39 active + 9 deprecated v3 aliases. Active set:
210-
211-
`now`, `hostname`, `version`, `env`, `toJson`/`toJSON`, `toInt`/`int`, `toString`/`str`,
212-
`escape`, `urlEncode`, `trim`, `trimPrefix`, `trimSuffix`/`trimPostfix`, `trimAll`, `replace`,
213-
`lower`, `upper`, `default`, `coalesce`, `ternary`, `contains`, `hasPrefix`, `hasSuffix`/`hasPostfix`,
214-
`count`, `split`, `join`, `fields`, `quote`, `squote`, `repeat`, `substr`, `truncate`,
215-
`isEmpty`, `isNotEmpty`, `l10nScript`.
216-
217-
**v4 pipeline order: needle before haystack**. `{{ "test" | contains "es" }}` β†’ `contains(needle, haystack)`.
218-
219-
**`env` masking**: `getEnv` splits the key on `_`, uppercases segments, and matches against
220-
`PASSWORD, SECRET, KEY, TOKEN, PASS, PWD, CRED`. If **any** segment matches, value becomes `*` repeated
221-
to the original rune length.
222-
223-
### Deprecated v3 aliases - argument order is FLIPPED
224-
225-
| Alias | Replacement | Args flipped? |
226-
|-----------------|------------------|------------------------------------------------|
227-
| `nowUnix` | `now.Unix` | - |
228-
| `json` | `toJson` | no |
229-
| `strCount` | `count` | **yes** (haystack, needle vs needle, haystack) |
230-
| `strContains` | `contains` | **yes** |
231-
| `strTrimSpace` | `trim` | no |
232-
| `strTrimPrefix` | `trimPrefix` | **yes** |
233-
| `strTrimSuffix` | `trimSuffix` | **yes** |
234-
| `strReplace` | `replace` | **yes** |
235-
| `strIndex` | (no replacement) | - |
236-
| `strFields` | `fields` | no |
237-
238-
**Trap**: these aliases delegate to the stdlib funcs (e.g. `strings.Count` directly), which use
239-
`(haystack, needle)` order. The v4 names use `(needle, haystack)`. Do not rename without flipping args.
240-
241-
### v3 β†’ v4 token shim
242-
243-
Source code: [convert.go](internal/template/convert.go).
244-
245-
Auto-rewrites `{{ code }}` β†’ `{{ .StatusCode }}`, `{{ show_details }}` β†’ `{{ .Config.ShowRequestDetails }}`,
246-
etc. at parse time. Two regexes (action block + identifier) and two lookup maps (`v3tov4Fields`,
247-
`v3tov4Tokens`, 13 entries). Deprecated - will be removed once users migrate.
248-
249-
### Rotation modes
250-
251-
`disabled` (default), `random-on-startup`, `random-on-each-request`, `random-hourly`, `random-daily`.
252-
253-
Implemented with `atomic.Pointer[time.Time]` + `atomic.Pointer[string]` - no mutex.
254-
255-
**Rotation has no effect when `--html-template` is set**. Same for `--template-name`.
256-
257217
### Custom template loading (`tploader.LoadTemplateContent`)
258218

259219
Tries in order: HTTP/HTTPS URL (30s timeout, 5 MB cap) β†’ existing file path (5 MB cap) β†’ treat as inline
260220
literal. All custom templates are loaded concurrently at startup via `errgroup`.
261221

262222
## Built-in HTML templates
263223

264-
`app-down` (default), `cats`, `connection`, `ghost`, `hacker-terminal`, `l7`, `lost-in-space`, `noise`,
265-
`orient`, `shuffle`, `win98`, etc. Source: `templates/html/{name}.tpl.html`.
266-
267-
`cats` fetches images externally; the rest are self-contained.
224+
Source: `templates/html/{name}.tpl.html`. `cats` fetches images externally; the rest are self-contained.
268225

269226
## Built-in HTTP codes
270227

271-
400, 401, 403, 404, 405, 407, 408, 409, 410, 411, 412, 413, 416, 418, 429, 500, 502, 503, 504, 505 (can be extended
272-
in future).
273-
274228
`Codes.Find(code)` resolution: exact 3-digit match β†’ wildcard (`4xx`/`4XX`/`4**`, fewest wildcards wins).
275229

276230
Override or extend: `--add-code "CODE=MESSAGE|DESCRIPTION"`. Multiple entries via `||`, newline, or tab.
277231
Disable all built-ins: `--disable-built-in-codes`.
278232

279233
## CLI flags
280234

281-
CLI framework: `internal/cli`. `Flag[T]` is generic over `bool | int | int64 | string | uint | uint64 | float64 | time.Duration`.
282-
283-
**Value precedence: Default β†’ Env var β†’ CLI flag (CLI wins)**.
235+
CLI framework: `internal/cli`. **Value precedence: Default β†’ Env var β†’ CLI flag (CLI wins)**.
284236

285237
Actual CLI flags and supported env vars are described in the [docs/CLI.md](docs/CLI.md) file, which is **partially**
286238
generated from the sources.
@@ -399,40 +351,3 @@ instead of a map.
399351
- Test behavior, not implementation.
400352
- Cover happy path + key failure modes. Don't chase 100% coverage.
401353
- Use `t.Setenv`, `t.TempDir`, `t.Context` (`usetesting` linter).
402-
403-
## Agent workflow (after any file change)
404-
405-
1. **Read existing package/module files**: before writing or modifying any file, read similar code in the same
406-
package files - the one most directly analogous to what you are about to write. One or two files is sufficient;
407-
do not read all files in the package. Use them as the authoritative style reference for that package.
408-
2. If `templates/html/` or `l10n/locales.json` changed β†’ `go generate -skip readme ./...`
409-
3. **Lint:** run with `--fix` scoped to the packages you changed, e.g. `golangci-lint run --fix ./path/to/package/...`.
410-
`--fix` auto-resolves trivial issues (imports, whitespace, simple rewrites); handle the rest manually. Scoping
411-
keeps feedback fast and avoids touching unrelated code. Run the full `golangci-lint run` once at the end to
412-
confirm nothing leaked outside your scope.
413-
4. **Test:** `go test -race ./...` - fix every failure.
414-
5. **Self-review:**
415-
- Logic: off-by-one, wrong operator, inverted condition, unreachable branch.
416-
- Concurrency: missing locks, shared state, deadlocks. Atomics used correctly?
417-
- Errors: silently swallowed (`errcheck check-blank` will catch `_, _ =`), wrong sentinel, missing
418-
wrap context.
419-
- Security: unsanitized input, secrets in code (`gosec`), env-mask coverage for new secret-shaped vars.
420-
6. **Update `README.md` and/or `docs/CLI.md`** for user-facing changes (flags, env vars, defaults, deprecations,
421-
breaking changes). Skip for internal-only edits.
422-
7. **Update this `AGENTS.md` file** if a future agent needs new context.
423-
424-
Don't present work as finished until lint and tests pass cleanly.
425-
426-
## v3 β†’ v4 migration (summary)
427-
428-
Full guide: [UPGRADE_TO_V4.md](docs/UPGRADE_TO_V4.md). Key breaking changes:
429-
430-
- `serve` / `build` / `healthcheck` subcommands removed - now separate binaries.
431-
- Renamed env vars: `TEMPLATES_ROTATION_MODE` β†’ `ROTATION_MODE`, `RESPONSE_JSON_FORMAT` β†’ `JSON_TEMPLATE`, etc.
432-
- `--add-code` separator changed: `/` β†’ `|`; multi-entry now uses `||`.
433-
- Template fields renamed: `{{ code }}` β†’ `{{ .StatusCode }}`, etc. (`convert.go` shim still rewrites
434-
the old syntax at parse time, but it is deprecated).
435-
- v4 template function names use `needle, haystack` order. v3 aliases (`strContains`, `strReplace`, …) keep
436-
`haystack, needle`.
437-
- HTML minification removed; gzip added for all formats.
438-
- FastHTTP replaced with stdlib `net/http`; HTTP/2 h2c added.

0 commit comments

Comments
Β (0)