Ctrl K
ComunidadGuías…

AGENTS.md

Rama develop · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Agent instructions

## Commits

Before creating a commit, read the "Commits & PRs" section of `CONTRIBUTING.md` and
follow the message format and attribution rules described there.

These in particular are mandatory.

- The subject line takes the form `<type>(<scope>): <summary>`.
- **Write the subject summary and the body in English.** The same goes for PR titles
  and descriptions and for GitHub issues. (Commits made before 2026-09-25 are in
  Japanese; do not rewrite them.)
- For a bug fix or a behaviour change, the body states the root cause, the fix, and
  how it was verified.
- A commit an agent authored or materially contributed to carries a
  `Co-Authored-By` trailer at the end, separated by a blank line.
- `Co-Authored-By` names the model that actually did the work, not the CLI.
- The address is the model vendor's `noreply@<vendor domain>`.
- Do not add a `Claude-Session:` trailer. It is tolerated when Claude Code adds one
  itself over a Remote Control connection.
- Immediately before committing, re-read the finished message and confirm it meets
  the convention.

Real host names, customer names and the like must never enter the history — the
scanner behind `ci.yml`'s `release-scan` job only says so after the merge. Run the
same check on what you are about to commit:

```
git config core.hooksPath .githooks   # once per clone; worktrees share it
.githooks/pre-commit                  # or just run it by hand, any time
```

Anything it flags is fixed in the file *before* the commit, never in a follow-up
one. Don't reach for `--no-verify`.

## Comments

**Write every comment in English** — Go, TypeScript, CSS, SQL, shell alike. (Commit
messages, PRs and issues are English too; see above.) Japanese belongs in user-visible
strings, i18n catalogues, test fixtures and golden files — never in a comment. When a
comment has to name a Japanese UI label, give the English term and put the literal in
parentheses only when the reader needs it to find the string.

A comment earns its place by saying what the code cannot:

- **Why this exists, and what breaks without it.**
- **Invariants, prohibitions, preconditions** — "never compare versions here", "blocking
  here is latency before that key echoes back", "when in doubt, answer false".
- **The one line of evidence that makes a rule stick** — "measured: re-pushing provenance
  alone moves the digest". Without it the next reader deletes the rule.
- Non-obvious trade-offs, and pointers to `docs/decisions/` or `guide/ref/`.

Leave out:

- **Chronology and attribution** — "originally only docker had one", "#343 found that…",
  "removed on 2026-07-28", "was: `map[string]any{…}`". That history lives in `docs/log/`.
- **Restating what the next few lines plainly do.**
- Emphasis theatre — ★, 🔴, bold shouting, the same warning three times.
- Change logs in a file header.

When a paragraph of history carries nothing but a reason, keep the reason as one sentence
and drop the story. Go doc comments still open with the identifier's name.

One exception, and a test enforces it: a wire type that replaced a `map[string]any` must
keep the line `was: map[string]any{…}` in its doc comment. That is provenance a name
suffix cannot carry, and `wiremapConvertedMarker` (`wiremap_convert_test.go`, in both Go
modules) greps for that exact literal to decide which types owe an equivalence proof.
Delete it and the type silently leaves the scan.

The house style is already in the tree: `control-plane/reaper.go` and
`control-plane/internal/runtime/runtime_ecs_stale.go` are written to it.

## `console/node_modules` in a worktree

Installing it is ~350 MB per worktree, and sessions usually get one worktree each. When this
worktree's lockfile matches the parent clone's, share the parent's tree instead:

```
cd console
cmp -s package-lock.json ~/repos/agent-fleet/console/package-lock.json \
  && [ -e ~/repos/agent-fleet/console/node_modules/.package-lock.json ] \
  && ln -sfT ~/repos/agent-fleet/console/node_modules node_modules
```

`-fT` is not optional. `node_modules` may already be a symlink (an earlier share, or a
worktree where `af-scratch` pre-created one into `/scratch`), and a plain `ln -s` onto a
symlink to a directory creates the link
*inside* that directory and still exits 0 — the worktree keeps the empty tree. `ln -sfT`
replaces the symlink, and refuses (exit 1) when `node_modules` is a real install.

The `.package-lock.json` test is what stops `-f` from doing harm: the parent's own
`node_modules` can be a `/scratch` link left dangling by a stop, or never installed, and
`ln -sfT` would then swap a worktree tree you already installed for a link to nothing, exit 0.
When the test fails, do not link; install your own as below, removing the link first.

`npm run build`, the tests and the whole node project resolve through the link
(measured: the full suite is green, 242 files, and the production build succeeds).

`vite.config.js` is what makes the tests work: it adds `node_modules`' real path to
`server.fs.allow`, because the assets the viewer imports by URL (`…?url`) resolve to
the link's target, outside this worktree, and the stock allow-list is the project root
alone. Without that, six `src/features/viewer/` dom files fail with
`Error: Denied ID …` and read as a broken viewer.

- **Remove the link before any `npm ci` / `npm install`**: `rm -rf node_modules` — *without* a
  trailing slash. `npm ci` through the link empties the parent's `node_modules` and breaks
  every other session that shares it, and `rm -rf node_modules/` deletes through the link the
  same way.
- If the lockfiles differ, don't link: `npm ci --prefer-offline` (the `~/.npm` cache is warm).
- `npm install <pkg>` replaces the link with a real tree. That is fine — it is just no longer
  shared, so don't be surprised by the disk.

The same question for the other ecosystems (what a worktree already shares, what it duplicates,
and the measurements) is `docs/build/93-worktree-deps.md`.

## Running the Go tests

The two Go modules are separate; run each from its own directory. Both suites build and run
packages in parallel, which is the usual way to exhaust memory on this host — cap it when the
container is busy.

```
(cd control-plane   && go test ./...)      # add -p 2 when memory is tight
(cd workspace/agent && go test ./...)
```

Postgres-backed tests skip themselves unless `AF_TEST_DATABASE_URL` is set; in a Workspace,
`af-db url` provides one — see `docs/build/10-development.md` §10.4. No Docker in the workspace.
The full build/reflect matrix is `docs/build/10-development.md`.

## Verifying your own work

- **Take exit codes without a pipe** (`cmd > out; echo $?`). `| tail` returns tail's `0` — a red
  run looks green — and `| grep -v` under `pipefail` returns `1` when it matches nothing, so an
  all-green run looks red. Both directions have cost us real time here.
- **Before reporting "0 hits" or "green", show that the tool caught something it should catch.**
  An empty result and a scanner that never ran look identical, and so do a check that passed and a
  check that matched nothing.
- **`gofmt -l` lists the unformatted files and still exits `0`** — read its OUTPUT, not its
  status (`out=$(gofmt -l .); [ -z "$out" ] || echo "UNFORMATTED: $out"`, once per module).
  `ci.yml` checks the output, so this is a step that goes red after `go build`, `go vet`,
  `go test` and the pre-commit hook were all green. Worth remembering when you edit Go with a
  script rather than an editor: adding one entry to a map literal or a const block re-aligns
  the lines around it, so a diff you did not write becomes part of the change.

The long form — 30 rules, each with the concrete defect behind it — is in the developer work
journal for the 2026-09 parallel refactor.

## Open work goes to a GitHub issue

When a task leaves something undone — a phase you did not build, a finding you set aside
for another PR, an acceptance run on real hardware that has not happened — **file an
issue in English** (`gh issue create`) instead of writing "residual" or "not yet filed"
into an ADR, a `docs/log/` journal, `roadmap.md` or your memory. Then point at it once:
`Follow-ups: #N` under the ADR's Status line, or the issue number at the end of the
journal's residual list. Keep the issue short and link the section that holds the
evidence. An issue is public the moment it is filed and skips the secret scans, so no
real hostnames, account IDs or tokens. Full rule: `docs/CONVENTIONS.md` §10.

## Running the Console tests

**Always run them with `console/` as the working directory.** Invoking `npx vitest`
from the repository root makes npx download a different vitest — the root has neither
a `package.json` nor `node_modules` — and start it without reading
`console/vite.config.js`. With the config inert the environment falls back to node, so
DOM tests fail with `document is not defined` and `--project` reports "project not
found". That looks like a broken config, so watch out for it.

```
cd console
npm test                       # every project
npx vitest run --project=node  # pure logic (the default)
npx vitest run --project=dom   # render tests (jsdom)
npx vitest run src/features/viewer/FileView.dom.test.tsx   # a single file also works
```

The tests are split into two projects (`console/vite.config.js`). Standing up the
jsdom environment costs about 1.3 s per test file, so node stays the default and only
tests that actually mount components opt in via `*.dom.test.tsx`.

## Showing this repository's UI to the user

To have the user look at a change in the Console (`console/`), serve the dev server on
`127.0.0.1:<port>` and tell them **the exact port and path**, pointing them at
"Preview → open in pane".

- Prefer the browser pane (open in pane) for a Vite dev server with HMR or any screen
  that uses WebSockets. The lightweight preview is enough for plain HTTP pages.
- The browser pane is a Console feature for the user; **agents have no tool to open or
  view it**. Never guess what it is showing.
- You may claim you "verified" something only when you rendered and checked it
  yourself with headless Chromium (`/usr/bin/chromium`). Keep the user's pane and your
  own headless verification separate.
- Stop the dev server when you are done; don't leave it resident (the shared host is
  memory constrained).
- Never copy secrets that surface in API keys, cookies or Console logs into logs or
  documents.

The authority on usage (terminology, recommended flow, states, constraints) is
`guide/ref/browser-pane.md`.