A SimpleX Chat bot that answers arithmetic (closes #1)
check / check (push) Successful in 54s
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
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Agent Guidance
|
||||
last_modified: 2026-08-22
|
||||
last_modified: 2026-09-26
|
||||
---
|
||||
|
||||
This file is the single source of guidance for any automated agent
|
||||
@@ -15,18 +15,20 @@ 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. That rule propagates: every project seeded from this template
|
||||
inherits it.
|
||||
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 template. It is a working HTTP service — it builds, tests, lints and
|
||||
runs — whose purpose is to be copied. Nothing in it is precious. The
|
||||
`widgets` table, the index page and the debug panic route are there to
|
||||
prove the machinery works end to end, and are meant to be deleted.
|
||||
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
|
||||
|
||||
@@ -56,17 +58,15 @@ regardless of what else it achieves.
|
||||
|
||||
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. The single
|
||||
exception is `script/rename`, which is a tool an operator runs once,
|
||||
against a known-shaped tree, at seed time — and which deletes itself
|
||||
afterwards.
|
||||
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`. A version
|
||||
tag is server-mutable and therefore remote code execution.
|
||||
`@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.**
|
||||
@@ -104,9 +104,11 @@ Do not weaken them.
|
||||
## Branching model
|
||||
|
||||
- `main` is the default branch and is kept green.
|
||||
- Work happens on a branch off `main`.
|
||||
- Merge to `main` directly when the branch is not protected; otherwise
|
||||
open a PR.
|
||||
- `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
|
||||
@@ -116,63 +118,35 @@ Do not weaken them.
|
||||
## Layout
|
||||
|
||||
```
|
||||
cmd/simplexcalc/ cobra command tree; main() and serve
|
||||
internal/app/ the fx object graph — the only place that knows
|
||||
which concrete type satisfies what
|
||||
internal/config/ viper-backed configuration; the abort-on-garbage rule
|
||||
internal/database/ sqlite handle, embedded schema, migration runner
|
||||
internal/globals/ build-time metadata (ldflags)
|
||||
internal/handlers/ HTTP handlers, one struct, fx-injected dependencies
|
||||
internal/logger/ log/slog, JSON always
|
||||
internal/middleware/ request id, logging, metrics, recovery, timeout,
|
||||
body cap, security headers, CSRF
|
||||
internal/render/ template compilation and execution
|
||||
internal/server/ lifecycle, route table, static file serving
|
||||
internal/telemetry/ Sentry and Prometheus
|
||||
templates/ go:embed HTML: base document, pages, partials
|
||||
static/ go:embed CSS and JS
|
||||
script/ the entrypoints
|
||||
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
|
||||
|
||||
- **Dependency injection through `fx`.** A constructor takes an
|
||||
`fx.Lifecycle` and a `Params` struct; it never reaches for a
|
||||
package-level singleton. Returning an error from a constructor aborts
|
||||
startup, which is how configuration failures become refusals to start.
|
||||
- **Logging is `log/slog` only.** Never `zerolog`, never `logrus`. JSON
|
||||
output in every environment. `net/http`'s own error output is routed
|
||||
through the same handler so that one process emits one format.
|
||||
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 named `errThing`.
|
||||
- **A handler never shows an error's text to a client.** The client gets
|
||||
a chosen message; the error goes to the log and to Sentry, joined by
|
||||
the request id that is also in the response header.
|
||||
- **Route labels in metrics are chi route PATTERNS, never paths.**
|
||||
Labelling by path makes every distinct URL a new time series, and a
|
||||
crawler then owns the process's memory.
|
||||
- **Tests exercise behaviour, not implementation.** The server tests
|
||||
start a real listener against a real database and speak HTTP to it.
|
||||
Prefer that over asserting on internals.
|
||||
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.
|
||||
|
||||
## What to change when seeding a new project
|
||||
|
||||
1. Clone, then run `script/rename <newname> [module-path]` on a clean
|
||||
tree. It rewrites the placeholder name and module path everywhere,
|
||||
renames `cmd/simplexcalc`, and deletes itself. Review the diff.
|
||||
2. Delete the example domain:
|
||||
`internal/database/schema/001_widgets.sql`,
|
||||
`internal/database/model_widget.go`, the widget parts of
|
||||
`internal/handlers/index.go` and `templates/index.html`, and their
|
||||
tests. Start your own schema at `001`.
|
||||
3. Rewrite `README.md` for the new project. It currently describes the
|
||||
template, which the new project is not.
|
||||
4. Rewrite this file's "What this repository is" section, and delete
|
||||
this list. Keep the iron rules, the entrypoints, the branching model
|
||||
and the conventions — they are why the template exists.
|
||||
5. Reset `docs/TODO.md`: keep the Workflow section, replace the rest.
|
||||
6. Decide on `CSRF_KEY` and the metrics credentials before the first
|
||||
deployment. See `README.md` for what each one does when unset.
|
||||
|
||||
Reference in New Issue
Block a user