Files
improvise/context/cloud-agents.md
T
Cursor Agent b0c4105be3 docs: add GitHub attribution as a hard rule for all agent stacks
Every GitHub write (PR, comment, review reply) must include an attribution line
identifying the AI agent. Posts go out under the repository owner's account, so
an unattributed post reads as the human's words.

Format: _Posted by <Agent Stack> (<model>)_
Examples:
- _Posted by Claude (Opus 4.5)_
- _Posted by Cursor Agent (claude-sonnet-5)_

AGENTS.md:
- Added 'GitHub Attribution (Hard Rule)' section in multi-stack docs
- Concrete format and examples
- Clarified that a thread is not 'answered' until the reply has attribution

.agents/skills/pr-watch/SKILL.md:
- Added 'GitHub Attribution (Hard Rule)' subsection
- Updated 'Answer substantive feedback with attribution' in obligations
- Cross-references AGENTS.md for full rule

context/cloud-agents.md:
- Added 'GitHub Attribution (Hard Rule)' section
- Same format and examples as AGENTS.md

This ensures any agent reading the shared docs (Cursor, Claude, etc.) knows to
attribute every GitHub write to the specific AI agent.

Co-authored-by: Ed Langley <fiddlerwoaroof@users.noreply.github.com>
2026-09-25 19:37:48 +00:00

215 lines
9.1 KiB
Markdown

# Cloud Agents (LLM Reference)
Environment notes for agents running in cloud containers — Claude Code on the web,
Cursor cloud agents, and similar setups. Local checkouts are unaffected.
## Session Bootstrap
All agent stacks use the same bootstrap script: `scripts/agent-bootstrap.sh`. It:
1. **Seeds nix flake inputs** (GitHub tarballs are blocked; seeds over git instead)
2. Verifies the `bd` version matches the flake.nix pin
3. Installs `bd` if missing
4. Syncs the Dolt issue tracker from the git remote
5. Warms the cargo build **inside `nix develop`**
**After bootstrap, `nix develop` works.** This is the required toolchain — bare system
cargo that fails `edition2024` or missing wasm target is a misconfigured session.
**Claude Code**: `.claude/hooks/session-start.sh` calls the bootstrap automatically when
`CLAUDE_CODE_REMOTE=true`.
**Cursor cloud**: `.cursor/environment.json` runs the bootstrap via `install`. Skills are
in `.cursor/skills/` (symlinks to `.agents/skills/`).
**Other stacks**: Run `scripts/agent-bootstrap.sh` manually after checkout.
This doc is the "why", and the record of what bites when setup doesn't run.
---
## The issue tracker is not `issues.jsonl`
**`.beads/issues.jsonl` is a stale export.** It is a viewer/interchange
artifact, not the source of truth — `.beads/README.md` says as much
("Cross-machine sync and backups use Dolt remotes/backups, not JSONL
import/export"). The real tracker is the Dolt database published to
`refs/dolt/data` on the git remote.
The gap is not cosmetic. A session that hydrated from the JSONL saw 121
issues against the remote's 168, with every P1 bug showing OPEN though all
four were fixed and closed. Two beads were implemented from scratch that
the remote had recorded as closed weeks earlier, and which turned out to
be already implemented on an unmerged branch.
The hook syncs the database from the remote at session start. If it did
not run, or reported a failure:
```bash
bd list --all | wc -l # 121 means you are on the stale export
```
**Verify any bead against the code before starting it.** `git log --oneline
--all | grep -oE 'improvise-[a-z0-9]{3}'` names the ones already
referenced by a commit, and `git log origin/main` is worth a look for work
that landed without a bead reference.
---
## `bd init` is destructive here
A bare `bd init` in this repo rewrites `AGENTS.md`, `CLAUDE.md` and
`.claude/settings.json`, installs Cursor/Codex integration files, and
**commits all of it**. It also rewrites `sync.remote` in
`.beads/config.yaml`.
Always pass the opt-outs:
```bash
bd init --non-interactive --skip-agents --skip-hooks -q --remote git+<origin>
```
Even then it commits a small `.gitignore` change, so the hook runs it in a
throwaway directory and copies only `.beads/embeddeddolt` back. Do the
same if you need to re-sync by hand; never run it in the working tree.
---
## What does not work in this environment
| Thing | Status | Use instead |
|---|---|---|
| `nix develop` **without bootstrap** | Fails: flake inputs are GitHub **tarballs**, which are blocked | Bootstrap seeds inputs automatically; if it failed, run `scripts/nix-seed-flake-inputs.sh` manually |
| Bare system cargo | Fails: `edition2024`, missing wasm target | **Always use `nix develop`** — this is the required toolchain |
| `bd dolt push` | Fails: 403 outbound | Nothing; bead status changes stay local. Say so rather than implying the tracker was updated |
| `git+ssh://` Dolt remote in `config.yaml` | No ssh client in the container | `git+https://<origin>`, which the hook passes explicitly |
| `git push`, GitHub API | **Work normally** | — |
Nix itself installs and runs fine (no init system, so the daemon needs
starting by hand). Reads of public GitHub repos over the git protocol work —
it is specifically the `/archive/*.tar.gz` and `codeload` endpoints that are
refused.
### How nix develop works after bootstrap
The bootstrap runs `scripts/nix-seed-flake-inputs.sh` automatically, which seeds
the flake inputs over git **without touching `flake.nix` or `flake.lock`**.
After bootstrap, `nix develop` should work. If it fails, check `/tmp/nix-seed.log`.
Why the seeding is needed:
- Nix's `github:` fetcher wants a tarball. GitHub traffic **bypasses the
environment's domain allowlist entirely** and goes through a proxy that
scopes API and release-asset requests to repositories *attached to the
session*. A flake input from any other owner gets a 403. Raising network
access to **Full**, or allowlisting `codeload.github.com`, therefore does
nothing.
- Attaching the dependency repos is refused: `cross-tier adds are not
supported in v1` once the session already has sources from another owner.
- Nix has no setting that makes `github:` inputs use the git transport.
What does work is the lock's own integrity machinery. `flake.lock` records a
`narHash` per input, and Nix looks a source up in the store and its
substituters *before* fetching. The git and tarball fetchers produce
byte-identical trees for a revision, so a `git+https` prefetch at the locked
rev lands the input at exactly the store path the lock names:
```
lock narHash: sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=
git prefetch: sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=
```
The script does that for every locked input. Nix still verifies each tree
against the recorded hash, so this **satisfies** the lock rather than working
around it — a mismatch fails loudly. Do not instead rewrite the lock's nodes
to `git` type: carrying the old `narHash` onto a different fetcher is exactly
the claim the lock exists to make.
The bootstrap runs this automatically. It takes a few minutes and several GB of
store once the devShell builds. If bootstrap reported a seed failure, check
`/tmp/nix-seed.log` and re-run `scripts/nix-seed-flake-inputs.sh` manually.
Two alternatives if per-container seeding is unattractive: push the inputs to
a binary cache with `nix flake archive` and point sessions at it with
`NIX_CONFIG`/`extra-substituters` plus a **Custom** allowlist entry for the
cache host (not GitHub, so the allowlist does apply); or use a self-hosted
environment, where egress leaves through your own network boundary.
`cache.nixos.org` is reachable, so the build itself substitutes normally.
---
## Sharp edges in `bd`
- **`bd dolt push <remote>` ignores its argument** and pushes to `origin`.
Pointing it at a throwaway remote does not keep the real one out of it;
only the 403 does. Treat the named form as unsafe.
- `bd dolt pull` into a divergent local database fails with `local changes
would be stomped by merge`. Re-sync from the remote instead of merging.
- `bd` warns if `.beads` is not mode 0700.
---
## Working conventions
- One bead per pull request. Stacking a second bead onto an open PR has
been rejected in review.
- A merged PR cannot carry follow-up work: restart from the latest default
branch and open a new PR.
- Reconcile diverging history only by merge, including when bringing the
default branch into a working branch. Never rebase onto main, and never do
anything that needs a force push. Add a new Conventional Commit by default.
Only an unpushed HEAD may be amended, and only for a small fix. Never
reorder or fold commits (`.agents/skills/wave/agent-rules.md`, rule 8).
- Keep `context/repo-map.md` and `context/design-principles.md` current —
they are the two docs agents are expected to maintain by hand.
---
## PR Tracking
For any PR you own, **track both CI checks and GitHub comments/reviews**:
- **`scripts/pr-watch <n>`**: Waits for checks, then prints all reviews/comments (default).
Reviews are a separate API surface — Codex/ChatGPT reviews arrive as *reviews* not comments.
- **Answer feedback with attribution** before marking work done (see below).
- **Re-check after push** that reviews target the current head SHA.
The `pr-watch` skill (`.agents/skills/pr-watch/SKILL.md`) is the agent-facing recipe.
See AGENTS.md "PR Tracking: Checks AND Reviews" and "Shipping a wave" for the full workflow.
---
## GitHub Attribution (Hard Rule)
**Every GitHub write (PR, comment, review reply) must include an attribution line.**
Posts go out under the repository owner's account — an unattributed post reads as the
human's words.
Open the body with: `_Posted by <Agent Stack> (<model>)_`
Examples:
- `_Posted by Claude (Opus 4.5)_`
- `_Posted by Cursor Agent (claude-sonnet-5)_`
A thread is not "answered" until the reply carries this attribution. See AGENTS.md
"GitHub Attribution" for the full rule.
---
## Knowledge Base
**Use `scripts/kb`, not Prolog MCP.** The Prolog MCP is not available in all agent stacks
(Cursor cloud agents do not have it). All agents must use `scripts/kb` for KB operations:
```bash
scripts/kb check # kb_consistent, exactly what CI runs
scripts/kb query '<goal>' # run a goal; exit 1 if it fails
scripts/kb add <delta.pl> # add clauses; refuses if a key already exists
scripts/kb replace <delta.pl> # add, overwriting existing keys
scripts/kb rm <keys.pl> # remove by key; refuses if a key is absent
```
Reading `prolog-sessions/kb.pl` directly for queries is fine. See the `kb` skill at
`.agents/skills/kb/SKILL.md` for the full interface.