Skip to content

Commit d5b1422

Browse files
committed
Restructure agent docs: principles in AGENTS.md, mechanics in RUNNING.md
Root AGENTS.md becomes the stable contract layer and gains the missing piece both recent agent sessions fumbled: a definition of done for user-visible work. Done is something the user can open — (1) an e2e scenario with direct links to the runs that prove the change, (2) the session's dev server left up and reachable over the tailnet so the user can take over, (3) an honest list of what to try by hand. The same machinery runs in reverse: seed an environment into a state (live bug repro, staged data) and hand across the link. Mechanics move to a new RUNNING.md that opens by declaring itself possibly stale — trust it as a starting point, verify against the code, update on drift. It carries setup, the boot recipes (pointing at the e2e globalsetup files as source of truth), run/viewer/sharing operations, the leave-an-instance-up recipe with the Secure-cookie/ HTTPS constraint, and the hard-won environment gotchas. e2e/AGENTS.md points at both. Docs deliberately avoid pinning URL formats and flags that churn — agents discover those.
1 parent 5fa9e1e commit d5b1422

4 files changed

Lines changed: 177 additions & 58 deletions

File tree

.claude/skills/emulate/SKILL.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const github = await createEmulator({ service: "github", port: 4501 });
2525
// github.url, github.reset(), await github.close()
2626
```
2727

28-
`baseUrl` sets the *advertised* origin (redirects, form actions, spec
28+
`baseUrl` sets the _advertised_ origin (redirects, form actions, spec
2929
`servers`) when a proxy fronts the emulator — the bind stays on `port`.
3030

3131
**Hosted, zero-setup** — every service runs on Cloudflare with Durable
@@ -47,16 +47,16 @@ Every running emulator self-describes. Start at `GET /_emulate/quickstart`
4747
(plain-text, written for agents) or `GET /_emulate/manifest` (machine-readable:
4848
surfaces, auth capabilities, per-operation spec coverage, connection snippets).
4949

50-
| Endpoint | Use |
51-
| --- | --- |
52-
| `GET /_emulate/openapi` | A real OpenAPI document for the service — feed it straight to Executor's addSpec to register the emulator as an integration |
53-
| `POST /_emulate/credentials` | Mint a credential in the service's real shape: `{"type":"api-key"}`, bearer tokens, OAuth/OIDC clients, client-credentials apps |
54-
| `GET /_emulate/ledger` | Request ledger: matched operationId, sanitized headers/body, auth identity, response status, webhook deliveries. `DELETE` clears it |
55-
| `POST /_emulate/seed` | Add state via the service's seed schema (e.g. WorkOS `{"oauth":{"default_access_token_ttl_seconds":60}}` to compress token expiry) |
56-
| `POST /_emulate/reset` | Reset state + logs, replay seed |
57-
| `GET /_emulate/state` | Current store snapshot |
58-
| `GET /_emulate/coverage` | Which operations are implemented vs partial |
59-
| `GET /_emulate/connections` | Copyable SDK/env/curl snippets resolved against this instance |
50+
| Endpoint | Use |
51+
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
52+
| `GET /_emulate/openapi` | A real OpenAPI document for the service — feed it straight to Executor's addSpec to register the emulator as an integration |
53+
| `POST /_emulate/credentials` | Mint a credential in the service's real shape: `{"type":"api-key"}`, bearer tokens, OAuth/OIDC clients, client-credentials apps |
54+
| `GET /_emulate/ledger` | Request ledger: matched operationId, sanitized headers/body, auth identity, response status, webhook deliveries. `DELETE` clears it |
55+
| `POST /_emulate/seed` | Add state via the service's seed schema (e.g. WorkOS `{"oauth":{"default_access_token_ttl_seconds":60}}` to compress token expiry) |
56+
| `POST /_emulate/reset` | Reset state + logs, replay seed |
57+
| `GET /_emulate/state` | Current store snapshot |
58+
| `GET /_emulate/coverage` | Which operations are implemented vs partial |
59+
| `GET /_emulate/connections` | Copyable SDK/env/curl snippets resolved against this instance |
6060

6161
## Recipes
6262

AGENTS.md

Lines changed: 45 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,11 @@
11
# AGENTS.md
22

3-
## Fresh Checkout / Worktree Setup
4-
5-
Run `bun run bootstrap` first in any fresh checkout or worktree. It is
6-
idempotent: runs `bun install` (whose prepare hook builds the internal
7-
packages dev servers fail without) and installs Playwright chromium.
8-
Skipping it is why fresh worktrees die with "Failed to resolve entry for
9-
package '@executor-js/vite-plugin'". The `vendor/` submodules are NOT
10-
needed — nothing imports from `vendor/` at runtime; those forks are consumed
11-
from npm (see `vendor/README.md`). `bun run bootstrap --forks` inits them
12-
only when you're deliberately developing a fork.
13-
14-
## Environment Gotchas (learned the hard way)
15-
16-
- The shell is fish, and the working directory resets between Bash calls. Use
17-
absolute paths rooted at THIS worktree (check `pwd`), never
18-
`/Users/rhys/src/executor` from memory, and don't rely on a prior `cd`.
19-
- Don't write probe scripts to `/tmp` — they can't resolve workspace packages
20-
(`effect`, `playwright`, ...). Put scratch scripts under the repo root
21-
(`scratch/` is gitignored) so bun resolves the workspace.
22-
- `bun.lock` conflicts on rebase/merge: take either side, then re-run
23-
`bun install` to regenerate it — never hand-merge the lockfile.
24-
- e2e dev-server ports are derived per checkout (`cd e2e && bun run ports`).
25-
If a boot reports a squatted port, an old dev server leaked — kill it by
26-
PID from the error message; don't move your own ports to dodge it.
3+
This file is principles — the contracts that stay true while implementations
4+
churn. For how to actually run, boot, share, or navigate things today
5+
(fresh-worktree setup, dev servers, ports, environment gotchas), see
6+
[RUNNING.md](RUNNING.md) (which may lag reality; it says so itself). For
7+
writing e2e scenarios, see [e2e/AGENTS.md](e2e/AGENTS.md). Run
8+
`bun run bootstrap` first in any fresh checkout or worktree.
279

2810
## Task Completion Requirements
2911

@@ -36,43 +18,57 @@ only when you're deliberately developing a fork.
3618
- For broad or merge-ready changes, the full gates are `bun run format:check`,
3719
`bun run lint`, `bun run typecheck`, and `bun run test`.
3820

39-
## Attribution
21+
## Handing Back Work: Evidence, Not Assertions
4022

41-
Do not add any AI assistant, Claude, Anthropic, or Co-Authored-By
42-
attribution/trailers to commits, commit messages, PRs, or generated files.
23+
"Done" is something the user can open, not a claim. When work changes what a
24+
user sees or touches, the handoff has three parts, delivered unprompted:
4325

44-
Pull request titles and descriptions are going to a public GitHub repo, so
45-
avoid using specific names or internal info unless explicitly stated to.
46-
47-
## Show Changes in PR Descriptions
26+
1. **Watch it** — an e2e scenario covers the change, and the handoff links
27+
directly to the specific run(s) that prove it, with one line each on what
28+
to look at. Never hand back a bare wall of green results: the user's
29+
question is "show me the new thing working," not "is everything healthy?"
30+
2. **Touch it** — leave the session's dev server running and reachable over
31+
the user's tailnet, with credentials, so they can take over and poke at
32+
it. The instance you already booted for e2e IS this — leave it up rather
33+
than standing up something separate.
34+
3. **What to try** — name the paths worth exercising by hand, especially
35+
ones no scenario pins yet. Honesty about coverage gaps is part of the
36+
handoff. A human driving a real browser from another device reaches
37+
states the test harness structurally cannot; invite that.
4838

49-
When a change is user-visible and an e2e scenario covers it, embed the run's
50-
recording in the PR description — reviewers should see the change, not just
51-
read about it.
39+
The same machinery runs in reverse: you can seed an environment INTO a
40+
state — reproduce a bug live, stage data for the user to take over, set up
41+
a walkthrough — and hand across the link. "Here's the broken state, live"
42+
beats a paragraph describing it.
5243

53-
```
54-
bun e2e/scripts/pr-media.ts e2e/runs/<target>/<scenario-slug>
55-
```
44+
If no scenario covers the change yet, that is the cue to write one. When a
45+
change is user-visible, embed the run's recording in the PR description —
46+
reviewers should see the change, not just read about it.
5647

57-
converts the run's recording (browser `session.mp4` or `terminal.cast`) to a
58-
gif, uploads it to the `e2e-media` branch, and prints PR-ready markdown to
59-
paste into the body. Run screenshots (`*.png`) can be passed directly too. If
60-
no scenario covers the change yet, that is usually the cue to write one.
48+
Don't memorize the mechanics (ports, viewer, sharing commands) — discover
49+
them from RUNNING.md and the code; they change.
6150

6251
## Service Emulators
6352

6453
When a test or demo needs an upstream API, OAuth/OIDC provider, or webhook
6554
source, use the `@executor-js/emulate` emulators (GitHub, Google, Stripe,
6655
Resend, WorkOS, and a dozen more) instead of writing a stub. They are
6756
wire-level and stateful — real SDKs run against them unmodified — and each
68-
one serves a full OpenAPI spec (`/_emulate/openapi`, ready for addSpec),
69-
mints real-shaped credentials (`POST /_emulate/credentials`), runs working
70-
OAuth flows, and records every call in a request ledger
71-
(`/_emulate/ledger`) you can assert against. Hosted instances exist at
72-
`https://<service>.emulators.dev` with zero setup. See the `emulate` skill
57+
serves a full OpenAPI spec ready for addSpec, mints real-shaped credentials,
58+
runs working OAuth flows, and records every call in a request ledger you can
59+
assert against. Hosted instances exist at `https://<service>.emulators.dev`
60+
with zero setup. See the `emulate` skill
7361
(`.claude/skills/emulate/SKILL.md`) for the control-plane reference and
7462
recipes.
7563

64+
## Attribution
65+
66+
Do not add any AI assistant, Claude, Anthropic, or Co-Authored-By
67+
attribution/trailers to commits, commit messages, PRs, or generated files.
68+
69+
Pull request titles and descriptions are going to a public GitHub repo, so
70+
avoid using specific names or internal info unless explicitly stated to.
71+
7672
## Collaboration Notes
7773

7874
The user uses speech to text occasionally, so if sentences are weird or words
@@ -116,4 +112,6 @@ before using it.
116112

117113
## Other
118114

119-
Please make note of mistakes you make in MISTAKES.md. If you find you wish you had more context or tools, write that down in DESIRES.md. If you learn anything about your env write that down in LEARNINGS.md.
115+
Please make note of mistakes you make in MISTAKES.md. If you find you wish you
116+
had more context or tools, write that down in DESIRES.md. If you learn anything
117+
about your env write that down in LEARNINGS.md.

RUNNING.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# RUNNING.md — how things run today
2+
3+
> **This document may be out of date.** It describes how things run today,
4+
> not how they must run. Trust it as a starting point; if you hit weirdness,
5+
> the implementation has probably moved and this file is why. Verify against
6+
> the code, then update this file when you notice drift. The principles in
7+
> [AGENTS.md](AGENTS.md) are the stable contract; everything below is
8+
> implementation detail that churns.
9+
10+
## Fresh checkout / worktree setup
11+
12+
`bun run bootstrap` from the repo root — idempotent: `bun install` (whose
13+
prepare hook builds `@executor-js/vite-plugin` and `packages/react`, the
14+
artifacts dev servers fail without) plus Playwright chromium. A fresh
15+
worktree that skips it dies with "Failed to resolve entry for package
16+
'@executor-js/vite-plugin'".
17+
18+
The `vendor/` submodules (emulate, mcporter) are NOT required — nothing
19+
imports from `vendor/` at runtime; those packages come from npm
20+
(`@executor-js/emulate`, `@executor-js/mcporter`). `bun run bootstrap
21+
--forks` inits them only when deliberately developing a fork (see
22+
`vendor/README.md`).
23+
24+
## Dev servers
25+
26+
- Everything except desktop/cloud: `bun run dev` (turbo, from root)
27+
- One app: `bun run dev` from its `apps/<name>` directory
28+
- Self-host boots standalone with just env vars — see
29+
`e2e/setup/selfhost.globalsetup.ts` for the canonical recipe (data dir,
30+
bootstrap admin email/password, base URL, `EXECUTOR_ALLOW_LOCAL_NETWORK`)
31+
- Cloud needs WorkOS + Autumn; for a no-.env boot, point it at emulators —
32+
see `e2e/setup/cloud.globalsetup.ts` for the canonical recipe (the real
33+
SDKs against emulated services, PGlite dev DB)
34+
35+
The e2e globalsetup files are the source of truth for "how do I boot a
36+
working instance of X" — read them before inventing a boot path.
37+
38+
## E2E: running, viewing, sharing
39+
40+
`e2e/AGENTS.md` covers writing scenarios. Operationally:
41+
42+
- `cd e2e && bun run test` boots dev servers and runs everything;
43+
`--project cloud|selfhost` narrows. `E2E_CLOUD_URL`/`E2E_SELFHOST_URL`
44+
attach to an already-running server instead of booting.
45+
- Runs land in `e2e/runs/<target>/<scenario-slug>/``result.json`, step
46+
screenshots, `session.mp4` + `trace.zip` for browser scenarios, and the
47+
scenario source as `test.ts`.
48+
- `cd e2e && bun run serve` builds the viewer and serves the scenario ×
49+
target matrix over HTTP, bound to all interfaces (reachable over the
50+
tailnet). Individual runs are at `#/<target>/<slug>` hash routes — when
51+
handing results to the user, link those directly, not the bare matrix.
52+
- `bun e2e/scripts/pr-media.ts e2e/runs/<target>/<slug>` converts a run's
53+
recording to a gif, uploads it to the `e2e-media` branch, and prints
54+
PR-ready markdown.
55+
56+
E2E dev-server ports are derived and CLAIMED per checkout (`cd e2e && bun
57+
run ports` prints this checkout's block; see `e2e/src/ports.ts`) — each
58+
checkout hashes its repo root to a preferred block, atomically locks it,
59+
and walks to the next free block if squatted, so concurrent worktrees never
60+
collide or attach to each other's servers. `E2E_*_PORT` env vars pin ports
61+
explicitly. If a boot reports a squatted port, an old dev server leaked —
62+
`bun run reap` (repo root) lists and kills orphaned stacks.
63+
64+
## The dev CLI: live instances, interactively
65+
66+
`cd e2e && bun run cli` — the same primitives scenarios use, as commands.
67+
Boot a target, mint identities, make typed API calls, drive MCP, read the
68+
emulator ledger — develop interactively, then crystallize the journey into
69+
a scenario.
70+
71+
```sh
72+
bun run cli up selfhost --share # boot, reachable over the tailnet, stays up
73+
bun run cli up cloud --share # emulated WorkOS+Autumn, tailscale-HTTPS fronted
74+
bun run cli status # what's running, URLs, creds
75+
bun run cli identity selfhost # fresh identity (headers / cookies / creds)
76+
bun run cli api selfhost tools.list
77+
bun run cli mcp selfhost call execute '{"code":"return 1+1;"}'
78+
bun run cli ledger cloud workos # what hit the emulator
79+
bun run cli down selfhost # tear down (also removes tailscale serves)
80+
```
81+
82+
Instances persist until `down``up --share` IS the "touch it" handoff
83+
artifact, and the seeding direction too: boot, drive the product into a
84+
state (API/MCP/UI), hand across the URL. State files in `e2e/.dev/` mark
85+
deliberate long-lived instances (vs leaks); attach scenarios to a running
86+
instance with `E2E_<TARGET>_URL`.
87+
88+
Why cloud `--share` is more involved (encoded in the CLI, kept here for
89+
when you hit it manually): the cloud app sets `secure: true` auth cookies,
90+
so login breaks over plain http from any non-localhost origin ("Invalid
91+
login state"). Both the app AND the WorkOS emulator get fronted with
92+
`tailscale serve` HTTPS, the emulator advertises its public URL on both
93+
sides (its `baseUrl` and the app's `WORKOS_API_URL` — the browser-facing
94+
authorize URL derives from the latter), and Vite must allow the public
95+
hostname (`__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS`).
96+
97+
## Environment gotchas (learned the hard way)
98+
99+
- The shell is fish, and the working directory resets between Bash calls.
100+
Use absolute paths rooted at THIS worktree; don't rely on a prior `cd`.
101+
- Don't write probe scripts to `/tmp` — they can't resolve workspace
102+
packages (`effect`, `playwright`, …). Put scratch scripts under the repo
103+
root (`scratch/` is gitignored) so bun resolves the workspace.
104+
- A fresh worktree's Vite dep-optimizer cache can serve PRE-REBASE code
105+
(symptom: behavior matching old code only in dev servers, while unit
106+
tests pass). Kill the server, clear `node_modules/.vite` /
107+
`.tanstack`-adjacent caches, reboot.
108+
- The real Tailscale CLI on this machine is
109+
`/opt/homebrew/opt/tailscale/bin/tailscale`; `/usr/local/bin/tailscale`
110+
is a broken shim pointing at a deleted app. The tailnet IP is on the
111+
`utun` interface (100.x.y.z) if the CLI fails.
112+
- `bun.lock` conflicts on rebase: take either side, re-run `bun install`,
113+
never hand-merge.
114+
- Long-lived demo servers you left up for the user look like leaks to
115+
cleanup tooling — `e2e/.dev/<target>.json` marks deliberate instances;
116+
check it before reaping, and `bun run cli down <target>` is the clean
117+
teardown.

e2e/AGENTS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ Each run writes `runs/<target>/<slug>/result.json` plus any browser artifacts
101101
(trace.zip / session.mp4 / screenshots). `bun run serve` hosts the scenario ×
102102
target matrix; a run page links the trace into Playwright's trace viewer.
103103

104+
When handing results to the user, follow the evidence contract in the root
105+
[AGENTS.md](../AGENTS.md) (direct run links + a live instance + what to try);
106+
[RUNNING.md](../RUNNING.md) has the current sharing/demo mechanics.
107+
104108
## Discovering endpoints
105109

106110
- The full OpenAPI spec: `curl http://127.0.0.1:<cloud port>/api/openapi.json`

0 commit comments

Comments
 (0)