check / check (push) Successful in 54s
Remove the template's HTTP service, database and fx wiring. Add exact arithmetic on go/parser and go/constant, a client that runs simplex-chat as a child process and drives its WebSocket API, and the bot, which keeps an auto-accepting address and replies to each message. The image adds the checksum-pinned simplex-chat v7.0.2 on Ubuntu 22.04. Model: opus-5-5
153 lines
6.8 KiB
Markdown
153 lines
6.8 KiB
Markdown
---
|
|
title: Agent Guidance
|
|
last_modified: 2026-09-26
|
|
---
|
|
|
|
This file is the single source of guidance for any automated agent
|
|
working in this repository. There is no other agent-facing file, and
|
|
none is to be created: no vendor-named directory and no vendor-named
|
|
markdown file is ever committed.
|
|
|
|
Keeping such directories out of commits is what `.gitignore` is for, and
|
|
listing them there is correct — an ignore entry is plumbing, not
|
|
documentation. What must not appear is vendor-named _content_: committed
|
|
configuration, instructions or notes addressed to one particular tool.
|
|
Guidance goes here instead, written so that any agent can follow it.
|
|
|
|
Nothing in this repository names an assistant, model or vendor in its
|
|
prose.
|
|
|
|
Read `docs/REPO_POLICIES.md` as well. Where this file and that one
|
|
overlap, they agree; where this file is silent, that one governs.
|
|
|
|
## What this repository is
|
|
|
|
A SimpleX Chat bot that answers arithmetic. The Go program starts the
|
|
SimpleX Chat command-line client (`simplex-chat`) as a child process,
|
|
drives it over its local WebSocket API, gives the bot a long-term
|
|
contact address that accepts every contact request, and replies to each
|
|
text message with the value of the arithmetic in it. Both ship in one
|
|
container image; the SimpleX database lives on a volume. It was seeded
|
|
from `go-template-repo` and keeps that template's gates and conventions.
|
|
|
|
## Iron rules
|
|
|
|
These are not preferences. A change that breaks one of them is wrong
|
|
regardless of what else it achieves.
|
|
|
|
1. **Never mention any assistant, model, vendor, or the involvement of
|
|
automated assistance** — not in code, comments, documentation, commit
|
|
messages, PR bodies, or trailers. No `Co-Authored-By`, no session
|
|
links, no attribution of any kind.
|
|
|
|
2. **Linting runs in Docker, never on the host.** `script/lint` builds
|
|
`Dockerfile.lint` against a digest-pinned image. Do not install
|
|
`golangci-lint` locally and do not run it directly.
|
|
|
|
3. **Never run `go build`, `go test`, `go vet`, `gofmt` or
|
|
`golangci-lint` directly.** Use the `make` targets or the `script/`
|
|
entrypoints. They carry the flags and policies (`-count=1`, `-race`,
|
|
timeouts, docker-only linting) that a raw invocation silently
|
|
bypasses. This applies to mid-task checks, not just the final one.
|
|
|
|
4. **Configuration that is set but unparseable aborts startup.** A
|
|
default applies only to a value that is ABSENT. Silently substituting
|
|
a default for a value the operator got wrong turns their mistake into
|
|
a misconfiguration that surfaces much later, somewhere else. See
|
|
`internal/config`. Reject any change that weakens this.
|
|
|
|
5. **Never use scripted search-and-replace to edit files** — no
|
|
`sed -i`, `perl -pi`, `awk` rewrites, or scripted heredocs. Read the
|
|
file and edit it, even when there are many similar edits.
|
|
|
|
6. **Formatting only via `make fmt`.** Never hand-roll a reformat.
|
|
`make fmt-check` is the read-only form and must stay non-mutating.
|
|
|
|
7. **Pin every external reference by hash.** Docker base images by
|
|
`@sha256:`, Actions by commit SHA, Go modules by `go.sum`, the
|
|
`simplex-chat` download by the checksum on its `ADD`. A version tag
|
|
is server-mutable and therefore remote code execution.
|
|
|
|
8. **Clean up every container and image you create. Never run
|
|
`docker builder prune`, `docker image prune`, or any other prune.**
|
|
Shared infrastructure purges on its own schedule; a prune destroys
|
|
other people's work in progress.
|
|
|
|
9. **Write verbatim identifiers in backticks in markdown** — branch
|
|
names, filenames, commands, environment variables. This is what
|
|
distinguishes a branch literally named `next` from the word "none"
|
|
meaning no such branch exists.
|
|
|
|
## Entrypoints
|
|
|
|
Everything is a `script/` entrypoint with a thin `make` shim. The
|
|
scripts are the real interface; `make` exists because fingers know it.
|
|
See the Entrypoints section of `README.md` for what each one does.
|
|
|
|
The two that matter most:
|
|
|
|
- `make check` — `test` + `lint` + `fmt-check`. Never modifies files.
|
|
This is the gate for a commit.
|
|
- `script/cibuild` — the container build with the lint and test stages
|
|
cache-busted, plus assertions that they really ran and really saw the
|
|
whole repository. This is the gate for a push, and the Gitea workflow
|
|
runs it.
|
|
|
|
A green `docker build` on its own proves nothing: an unchanged tree
|
|
serves every layer from cache and exits 0 having executed nothing, and a
|
|
`.dockerignore` entry can hide a package from a linter that then
|
|
truthfully reports `0 issues.` over what is left.
|
|
`script/assert-step-ran` and `script/assert-context-complete` exist to
|
|
close both holes, and their comments explain what they still cannot see.
|
|
Do not weaken them.
|
|
|
|
## Branching model
|
|
|
|
- `main` is the default branch and is kept green.
|
|
- `next` integrates work toward the next release: branch from `next`,
|
|
merge back to `next`. Before 1.0.0, green work may also land directly
|
|
on `main`.
|
|
- Merge directly when the target branch is not protected; otherwise open
|
|
a PR.
|
|
- `docs/TODO.md` changes go in the same commit as the work they
|
|
describe.
|
|
- Push finished work to the remote. Work that exists only locally is
|
|
treated as lost.
|
|
- Never force-push, and never rewrite published history.
|
|
|
|
## Layout
|
|
|
|
```
|
|
cmd/simplexcalc/ cobra command tree; main(), run and version
|
|
internal/bot/ startup, address setup, and the reply to a message
|
|
internal/calc/ the arithmetic: go/parser and go/constant
|
|
internal/config/ viper-backed configuration; the abort-on-garbage rule
|
|
internal/logger/ log/slog, JSON always
|
|
internal/simplex/ the simplex-chat child process and its WebSocket API
|
|
script/ the entrypoints
|
|
```
|
|
|
|
## Conventions
|
|
|
|
- **Logging is `log/slog` only.** Never `zerolog`, never `logrus`. JSON
|
|
output in every environment. The chat client's own output is logged
|
|
line by line through the same handler, so that one process emits one
|
|
format.
|
|
- **Errors wrap with `%w`** and are compared with `errors.Is`. Sentinel
|
|
errors are package-level `var`s.
|
|
- **A reply never shows an error's text to a contact.** The contact gets
|
|
a chosen sentence; anything unexpected goes to the log.
|
|
- **Unknown chat client records are ignored, never fatal.** The SimpleX
|
|
API adds fields and record types between releases, and its
|
|
documentation requires clients to skip what they do not know. Decode
|
|
only the fields that are read.
|
|
- **A failure of the chat client or the connection to it ends the
|
|
process with an error**, so the container's restart policy restarts
|
|
both together. There is no reconnect loop.
|
|
- **Tests exercise behaviour, not implementation.** The client tests
|
|
speak the real WebSocket protocol to a stand-in chat client. Prefer
|
|
that over asserting on internals.
|
|
- **Comments explain why, and traps.** Delete the history, the
|
|
reasoning-out-loud and the self-justification; keep what a reader
|
|
needs in order not to fall in.
|