Task 02 (the whole runner) ended at 21:08 with no file and no commit: Ornith wrote the
implementation out in its reply (~700 lines), OpenCode compacted the session, and the model
then emitted a malformed tool call. It had also looked for the given test at a wrong path
("_files/…" relative to the plan: my ambiguity).
- 02-runner → 02-runner-core (types, Submit, Status, Touch, Cancel; no goroutines) and
03-runner-workers; the given test split to match (runner_core_test.go gains three tests:
queued storage, cancel while queued, Status never moves the lease); later tasks renumbered
04-09 and every cross-reference checked.
- Copy paths spelled from the repo root.
- guard.PreCheck (+ its test) is committed here by the owner: the old task 05 told the
implementer to edit internal/guard/, which AGENTS.md forbids. My contradiction.
- AGENTS.md rule 6: write code into files early, in small steps.
Both runner test files compile against a panic-only skeleton (the core file also alone) and
fail there on panic: todo.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5.6 KiB
AGENTS.md
sift is the fleet's fetch and search service for LLM agents: it fetches a URL through an SSRF guard, cleans the result (HTML → Markdown, JSON, text, and from P5a PDF), optionally digests it with a small model, and serves Hermes agents through a Firecrawl-compatible API. It is written in Go and runs on orion under a hardened systemd unit. You are implementing it one task at a time.
How you work
- The owner gives you one task file,
docs/plans/<plan>/NN-name.md. Read it fully. Do that task and nothing else. Do not start the next task. - Do the steps in order. Where a step shows a command and its expected output, run it and compare.
- Do not read other task files,
docs/PLAN.mdor other phase docs unless the task tells you to. The task file quotes what you need. - If something in the task is impossible, contradictory, or fails twice in the same way, stop.
Do not improvise, do not change a test, do not weaken a check. Add your row to
docs/implementer-log.mdwith statusstopped, say what you tried and what happened, commit only that file, and tell the owner. - Work only from files inside this repository. Never read another checkout, and never search the file system for code. If you are stuck, stop as in point 4.
- Write code into files early and in small steps. Start each new file with its types and signatures, make it compile, then fill in one function at a time and run the tests as you go. Never write a whole implementation out in your reply before putting it in a file: a long session is compacted, and what was only in the reply is lost (P6 task 02, 2026-09-26).
Files you must never edit
docs/plans/,docs/PLAN.md,docs/P*.md,AGENTS.md,tools/run-plan.shinternal/guard/andinternal/auth/— the SSRF guard and the key store are the security boundary; no task changes them.go.mod,go.sum,vendor/— every dependency is already vendored. Never add a module and never rungo getorgo mod tidy/vendor; the build is offline.nix/andflake.nix, unless the task names the exact change.- Anything a task told you to copy from
docs/plans/**/_files/— tests, fixtures,internal/pdftest— in this plan or any earlier one. If a copied test fails, your code is wrong. If a copied test can no longer be right because the task changes what it tested, that is the owner's error: stop and report it.
Code rules
- Go 1.26 (
go.mod). Standard library plus what is already invendor/. - No new source file over 400 lines. Do not grow
internal/api/api.go: new API code goes in the new file the task names. - Library code (
internal/...) never panics on input: nopanic, no indexing that can go out of range on data from a file, a request, a PDF or a peer. Check lengths.mainmay exit with a message. - Errors are values: return them, wrap with
fmt.Errorf("...: %w", err). A check that cannot do its job fails; it does not return "ok". - Never log, print or store a request or response body, a document's text or its title. Log
lines carry caller, host, sizes, counts, durations and outcomes. For a restricted caller
(
PermitRestricted) use the existing helpers (logHost,restrictedLog,logErr): they already blank what must not be written. - Anything read from the network, a child process or a file is bounded (
io.LimitReader, deadlines). - Exported names, field names and JSON tags are exactly as the task gives them; the tests compile against them.
- Comments say why, not what.
gofmtdecides layout.
Lessons from earlier work (crossbar, same owner, same process)
- A type assertion on a value from outside your package uses the two-value form and handles
false. - When a rule says "every" or "everywhere", finish by listing each place it applies and checking them one by one.
- Never end a turn by describing what you are about to do. Do it, then report.
- Work only inside this repository. Scratch programs under
/tmpor anywhere else are refused by the sandbox, and a refused tool call is not a reason to end the turn: write the experiment as a_test.gofile inside the repository (delete it before committing), or reason it out. - Never change when production code releases, flushes or records something just to make a given test's timing pass. If a given test seems to check a value before the code could settle it, that is the owner's bug: stop and report it.
The gate
make gate must pass before a task is done:
make gate # gofmt check, go vet, go test -race ./..., staticcheck if installed
It runs offline against vendor/. Run gofmt -w on the files you touched first. go test ./internal/<pkg>/ runs one package.
Git
- Work on the branch the task names. One task is one commit.
- Stage only the paths the task lists:
git add <path> .... Nevergit add -Aorgit add .. - Never push, amend, rebase, reset, or switch to another branch.
- Commit message: the subject line the task gives, a blank line, a short body if useful, then the
Co-Authored-By:trailer line the owner's prompt gives you, exactly, as the last line. - A message with more than one line goes in
.state/commit-msg.txt(inside the repository and ignored by git;/tmpis refused) and is committed withgit commit -F .state/commit-msg.txt.
The implementer log
Before you commit, add one row to the table in docs/implementer-log.md and include the file in
the commit:
| <plan>/<task> | <date> | done or stopped | <sessions> | gate pass/fail | deviations | what you did |
Be honest: the log is how the owner judges the process. A deviation you log is a finding; one you hide is a defect.