Same shape in both apps now that coverage is wired: one suite run exports the coverage, then the sensors read it. AGENTS.md said "sensors ... and finally both test suites" and told sensor authors only to list targets in VERIFY_SENSORS; add the export dependency and the "a check must fail when its input is missing" rule, since `mix test.coverage` exits 0 when it finds no export.
148 lines
7.4 KiB
Markdown
148 lines
7.4 KiB
Markdown
# Agent Instructions
|
|
|
|
This project has a *zero defects policy*.
|
|
When you are unsure about something, ALWAYS ask the user.
|
|
|
|
# Communication
|
|
|
|
When creating yaks or working on features, always ask clarifying questions to ensure understanding. Ask questions one at a time to maintain clear communication and allow for focused discussion.
|
|
|
|
|
|
You run in a `nono` sandbox. Always ask the user to run commands that require privilege, like docker, sudo or package installation.
|
|
|
|
# Repository structure
|
|
|
|
This is an Elixir monorepo with two parts:
|
|
|
|
```
|
|
firehose/
|
|
├── app/ # Phoenix application (OTP app: :firehose)
|
|
│ ├── lib/firehose/ # Application logic
|
|
│ ├── lib/firehose_web/ # Web layer (controllers, live views, components)
|
|
│ ├── lib/firehose/blogs/ # Blog definitions (engineering, release notes)
|
|
│ ├── priv/blog/ # Markdown posts
|
|
│ └── mix.exs
|
|
├── blogex/ # Blogex library (multi-blog engine)
|
|
│ ├── lib/
|
|
│ └── mix.exs
|
|
├── mise.toml # Runtime versions (Elixir, Erlang, Node)
|
|
├── Dockerfile # Deploy build; ARG pins must match mise.toml
|
|
├── scripts/check-pins.sh # Verifies the pin sync; runs in `make check`
|
|
├── .githooks/pre-push # Lens pass on push; install once with `make hooks`
|
|
└── Makefile # make (check, verify, test, hooks)
|
|
```
|
|
|
|
Two blogs are configured:
|
|
|
|
# Toolchain pins
|
|
|
|
`mise.toml` and the `Dockerfile` ARG pins must agree; `scripts/check-pins.sh`
|
|
(runs as part of `make check`) enforces this. When bumping versions, change
|
|
both files in one commit. Upstream drift (newer Elixir/OTP/Debian releases)
|
|
is reported by `dokku-updates firehose check` (dokku-maintenance repo).
|
|
Upgrading pins or mix packages is largely unscripted: for such tasks, a Pi
|
|
sub agent can be started in this directory — it reads this AGENTS.md and
|
|
follows the zero-defects policy above.
|
|
|
|
| Blog | Route | Description |
|
|
|------|-------|-------------|
|
|
| Engineering | `/blog/engineering` | Main blog |
|
|
| Release Notes | `/blog/releases` | What's new in an app |
|
|
|
|
# Permissions
|
|
|
|
ALWAYS ask the user for permission when you want to add a fallback, or are unsure something work. The user can perform exploratory testing.
|
|
|
|
We have a zero defects policy, so never continue when there are errors, warnings or test failures, even when they are pre-existing. ALWAYS report these to the user and discuss an action plan with root cause analysis. (see TDD below).
|
|
|
|
# Planning
|
|
|
|
we use `yx` for planning run yx --help to see options. Invoke when the user wants to make a plan, work on something for a plan or mentions 'yak' or 'yaks'.
|
|
|
|
`yx list` - shows open yaks
|
|
`yx add` - adds yaks
|
|
`yx context "yak name"` - adds human-readable description (pipe via stdin)
|
|
`yx show "yak name"` - shows yak details including context
|
|
|
|
# Building
|
|
|
|
This is a Phoenix Liveview monorepo, the blogging library is in blogex, the application is in 'app'. Use `make` to build and `brief` to find out more about mix.
|
|
|
|
Main make targets:
|
|
|
|
- `make check` - cheap static gate for both apps (no DB): app `credo --strict` +
|
|
format, blogex compile (warnings fatal) + format, then `scripts/check-pins.sh`
|
|
- `make precommit` - alias of `check`; fast, safe to run on every commit
|
|
- `make test` - both suites (app needs PostgreSQL on localhost:5432)
|
|
- `make verify` - the Lens pass: `check`, then per-app heavy sensors and both suites
|
|
- `make hooks` - install `.githooks/*` into this clone (once per clone)
|
|
|
|
# Verification: fast loop vs Lens pass
|
|
|
|
Two loops, deliberately split so commits stay cheap and pushes stay honest.
|
|
|
|
**Fast loop (every commit).** `make check` is static analysis only: no database,
|
|
no coverage, about a second. `make precommit` is the same target. Per app,
|
|
`mix precommit` (run inside `app/` or `blogex/`) is the fuller loop: compile with
|
|
warnings fatal, format, test. Run these as often as you like while working.
|
|
|
|
**Lens pass (every push, and before handing off).** `make verify` runs `check`
|
|
first (cheapest), then per app: one suite run that exports its coverage, then
|
|
that app's sensors (sobelow, coverage, CRAP, mutation, as those yaks land), which
|
|
run cheapest-first and read the export rather than re-running the suite. Measured
|
|
2026-11-01: ~11s wall (app 178 tests + blogex 156 tests + coverage floor).
|
|
|
|
`.githooks/pre-push` runs `make verify` and blocks the push when anything fails.
|
|
Install it once per clone:
|
|
|
|
make hooks
|
|
|
|
It symlinks `.githooks/*` into the clone's hooks directory; `core.hooksPath` is
|
|
deliberately left alone so bd's own hooks (pre-commit JSONL flush, post-merge
|
|
import) keep firing. On failure the hook prints one summary - last step started,
|
|
log path, bypass - and exits non-zero; the full output is in `.git/lens-pass.log`.
|
|
Deliberate override (e.g. pushing a branch you know is red): `git push --no-verify`.
|
|
|
|
## Lens practice
|
|
|
|
The Lens pass is a *separate pass with its own context*. It is not a way to find
|
|
work mid-feature: finish and commit what you are building, then run it.
|
|
|
|
- Do not chase hook or `credo` output inline while building a feature. If
|
|
something interesting surfaces, note it and file a yak; take it up in a Lens
|
|
session.
|
|
- A failing Lens pass blocks the push, and therefore the hand-off. Triage it;
|
|
never re-run until green and never ignore it.
|
|
- Adding a sensor: implement it as its own make target, add it to that app's
|
|
`VERIFY_SENSORS` in `app/Makefile` / `blogex/Makefile` (cheapest first), and
|
|
update the slot comments there. A gate that reports a check it did not run is
|
|
worse than no gate - the `VERIFY_SENSORS` list is what gets echoed. Sensors run
|
|
*after* the app's `test.coverage` step, because the coverage export they read is
|
|
produced by that suite run; if a check can pass without its input, make it fail
|
|
loudly (see the `coverage` target's guard).
|
|
|
|
### Triage classes
|
|
|
|
| Class | Looks like | What to do |
|
|
|-------|-----------|------------|
|
|
| *Real bug* | behavior is wrong; a test fails for a real reason; a sensor reports a real risk | fix inline, with a test that pins the behavior |
|
|
| *Environment-dependent test* | passes alone, fails in the pass; depends on ambient state (rows left behind, wall clock, ordering, network, locale) | the test is the bug: make it deterministic by construction - own fixtures, explicit clock, no ambient state as fixtures. Never retry-until-green |
|
|
| *Style or design nit* | credo or format suggestion, naming, structure, "this could be smaller" | do not refactor mid-build: note it and file a yak; the Lens session decides |
|
|
|
|
Reconciling this with the zero-defects policy: "never continue when there are
|
|
errors" governs *what you do with a failure* - report, triage, decide - not
|
|
*when* you fix it. Never silently absorb a finding, and never let a pre-existing
|
|
failure ride along: if it predates your change, stop and discuss it with the user
|
|
rather than working around it.
|
|
|
|
Browser tests (`make playwright*`) are *external* verification and are **not**
|
|
part of the Lens pass. Whether they ever join it is an open decision (see the yak
|
|
"ratchets, deferred decisions, handoff practice").
|
|
|
|
# Development
|
|
Always fix failing tests, credo issues and format isseus.
|
|
|
|
When developing new features, we apply TDD. Work Test-First, and Refactor when all tests are passing.
|
|
|
|
If you believe an issue is pre-existing, stop work, and have a conversation with the user on how to address the pre-existing issue, as well as how to prevent this in the future.
|