docs/plans/p5a: 01 kind+fetch limits, 02 pdftext child + runner, 03 dcache PDF tables, 04 pdfpool, 05 pdfdoc render (map/pages/grep), 06 api, 07 digest citations, metrics, CLI, skill, unit, README. Given tests compile against a panic-only skeleton and fail there for the intended reasons; PDFs for tests are built by internal/pdftest (given), plus two qpdf-encrypted fixtures. Owner changes for the run: vendor go-pdfkit/extract v0.3.0 + reader v0.6.0 (pinned via tools.go until task 02); AGENTS.md, tools/run-plan.sh (enforces a Co-Authored-By trailer for the model), docs/implementer-log.md, `make gate`; gofmt (whitespace) on clean.go and guard; TestUnsupportedType uses a zip now that PDFs are supported; requestDeadline 90→210 s and writeTimeout 120→240 s for the PDF path's worst case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5.2 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.
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.