Skip to content

Commit 1a76344

Browse files
authored
docs: restructure AGENTS.md and CONTEXT.md for progressive disclosure (#1402)
* docs: restructure AGENTS.md and CONTEXT.md for progressive disclosure Apply the Claude 5 context-engineering guidance to the repo's agent docs: keep the always-loaded file to gotchas and invariants, and move situational guidance one hop away behind a routing table. AGENTS.md 315 -> 229 lines. Cut generic agent-behavior boilerplate, three-way duplication (Common Mistakes restated Hard Rules; Finding Source Owners restated the registry section), and facts visible from the repo itself. Kept verbatim: the expensive-lessons principles, enforcement gates, Hard Rules, and environment traps. Split out docs/agents/{cli-flags,pull-requests,device-verification}.md and folded the Testing Matrix into docs/agents/testing.md, reframed around pnpm check:affected so the prose stops duplicating the selector. CONTEXT.md keeps all 50 terms, now grouped under a section index so a task loads one section instead of the whole glossary. * fix(check-affected): move the selector-owning sentinel to the Testing Matrix The Testing Matrix moved from AGENTS.md to docs/agents/testing.md, but the affected-check selector still treated only AGENTS.md as selector-owning. A later matrix edit would have been classified as inert docs and skipped the fail-open, so the selector could keep deriving gates from a spec that had changed underneath it. Move the sentinel with the prose, as a named SELECTOR_OWNING_DOCS set so the next move is one line, and fix the two in-code comments plus the testing.md paragraph that still pointed at the AGENTS.md matrix. * docs: restore two rules dropped by the AGENTS.md split Review caught two repo-specific rules that did not survive the move. Both are prose without any backticked identifier, so the identifier-diff used to verify the split could not see them. - "Test through public interfaces; do not add unrelated production exports solely to enable tests" returns next to the behavioral-tests rule in docs/agents/testing.md, with the reason it exists. - The guidance-ownership rule (decide whether new guidance/schema/metadata belongs to the command surface, CLI grammar, CLI help, MCP projection, or daemon runtime) returns to the always-loaded Docs & skills section, since it governs all command-surface work and not just the flag case. Also point the ADR routing row at docs/adr/README.md, which is already the "read when you touch…" index, rather than at the bare directory.
1 parent 877e68f commit 1a76344

10 files changed

Lines changed: 692 additions & 427 deletions

File tree

AGENTS.md

Lines changed: 218 additions & 300 deletions
Large diffs are not rendered by default.

CONTEXT.md

Lines changed: 241 additions & 110 deletions
Large diffs are not rendered by default.

docs/agents/cli-flags.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Adding a CLI Flag
2+
3+
A new flag touches only the layers that need to understand it. Stop at the layer where it stops
4+
mattering — threading it further is the common failure, not stopping too early.
5+
6+
1. `src/contracts/cli-flags.ts`: add to `CliFlags`; add the definition to the matching
7+
`src/commands/cli-grammar/flag-definitions-*.ts` owner and the relevant group in `flag-groups.ts`
8+
(for example `SNAPSHOT_FLAGS`). Then update the command family metadata/schema that exposes the
9+
flag; find the owner with
10+
`rg -n "<command>|supportedFlags|allowedFlags" src/commands src/cli-schema src/cli/parser`. For
11+
schema-only CLI commands (`cdp`, `auth`, `connect`, `proxy`, `react-devtools`, `web`) the owner is
12+
`SCHEMA_ONLY_CLI_COMMAND_SCHEMAS` in `src/cli-schema/command-overrides.ts`.
13+
2. `src/commands/cli-grammar/*`: read the CLI flag into command input.
14+
3. `src/commands/command-projection.ts` and command-family projection helpers: write the input into
15+
the daemon request only if the flag affects daemon execution.
16+
4. `src/commands/*-command-contracts.ts`: add to the command input schema only if the option should
17+
be available through Node.js or MCP as structured input.
18+
5. `src/client/client-types.ts`: update the public typed client option only when the Node.js
19+
interface exposes it.
20+
6. `src/client/client-normalizers.ts`: update daemon flag normalization only when the request still
21+
needs a public-to-internal translation.
22+
7. `src/daemon/context.ts` and `src/core/dispatch-context.ts`: add the field only when it flows into
23+
platform dispatch.
24+
8. Handler/platform modules: thread the option only after the command surface, grammar, and
25+
projection prove it belongs there.
26+
9. `scripts/integration-progress-model.ts`: classify the flag (device-observable vs
27+
intentionally-outside). The architecture-progress gate fails CI on unclassified public flags.
28+
10. If the flag changes interaction semantics, revisit the affected cells in
29+
`src/contracts/interaction-guarantees.ts` (scope with `appliesTo` when the flag exists only on
30+
some commands).
31+
32+
Command-only flags (like `find --first`) that never reach the platform layer usually stop at
33+
steps 1-3, plus step 9.
34+
35+
## Where CLI help and schema live
36+
37+
- Long help prose: `src/cli/parser/cli-help.ts`. Flag definitions: `src/commands/cli-grammar/`.
38+
- Command-specific usage/flag metadata lives with the command family metadata that owns the command.
39+
- Parser/help *rendering* stays in `src/cli/parser/`; command schema metadata is derived from command
40+
metadata, family declarations, and the schema-only merge path in
41+
`src/cli-schema/command-overrides.ts`. Keep the two separate.
42+
- Locating an owner: `rg -n "helpDescription|summary|supportedFlags|allowedFlags" src/commands src/cli/parser src/cli-schema`.

docs/agents/device-verification.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Manual Device Verification
2+
3+
Read this before running `agent-device` by hand against a simulator, emulator, or physical device.
4+
5+
## Before the run: defeat staleness
6+
7+
Dev-loop staleness has three layers, and each produces a convincing false negative.
8+
9+
- After changing runtime code reached through `bin/agent-device.mjs` or the daemon: `pnpm build`,
10+
then `pnpm clean:daemon` — the daemon does not self-reload.
11+
- Before any Android verification from source: `pnpm build`, `pnpm build:android`, `pnpm clean:daemon`.
12+
`build:android` refreshes and verifies both bundled Android helper artifacts for the current
13+
package version.
14+
- `shutdown` deliberately HANDS OFF a healthy simulator runner. The adopted runner keeps serving the
15+
old Swift binary until you kill its process or the source fingerprint changes, so "my change did
16+
nothing" measured against an adopted runner is a classic false negative. If Swift runner code
17+
changed, run `pnpm build:xcuitest`.
18+
19+
## Prove the path under test was actually active
20+
21+
- Android: capture `snapshot -i --json` and require `androidSnapshot.backend` to be `android-helper`
22+
with `helperVersion` equal to `package.json`'s version. A stock UIAutomator fallback is not valid
23+
verification unless the fallback itself is the behavior under test.
24+
- For repo-owned `Agent Device Tester` work, `examples/test-app/README.md` is the source of truth for
25+
simulator, physical-device, Metro/dev-client, and app-surface steps. An already-installed
26+
`com.callstack.agentdevicelab` is not sufficient — the README's Metro/dev-build and `snapshot -i`
27+
checks must prove the expected app surface is running.
28+
- For Android RN/Expo/dev-client apps on any local Metro port, `adb reverse tcp:<port> tcp:<port>` is
29+
harmless and should be run before opening the app or URL.
30+
31+
## Session hygiene
32+
33+
Every manually opened session is a resource that must be closed — including exploratory sessions and
34+
failed verification attempts.
35+
36+
- Every `agent-device open` needs a matching `close` with the same `--session`, `--platform`,
37+
`--udid`, and `--state-dir` before the agent finishes.
38+
- Use a purpose-specific session name for experiments, and an isolated `--state-dir` under
39+
`/private/tmp` when you need cleanup isolation beyond the current worktree's default daemon.
40+
- Track opened sessions in working notes; close each one before the final response.
41+
- If `close` is blocked by stale daemon metadata, inspect processes first with
42+
`ps -ax | rg "agent-device|xcodebuild test-without-building"`. Stop only exact stale PIDs belonging
43+
to this verification run, then `pnpm clean:daemon`.
44+
- If cleanup cannot be completed, report the remaining session name, state dir, PIDs, and metadata
45+
paths as a blocker.
46+
47+
## Sandboxed environments
48+
49+
Start the daemon outside the sandbox with escalation. The daemon binds localhost, and sandboxed runs
50+
fail before any product code executes, with `listen EPERM: operation not permitted 127.0.0.1` or
51+
repeated `Failed to start daemon`/metadata cleanup messages. Those are not agent-device regressions —
52+
rerun with escalation. Unit tests, typecheck, lint, and build can stay sandboxed unless they need
53+
devices or listener access.

docs/agents/domain.md

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
# Domain Docs
22

3-
This is a single-context repo.
3+
Single-context repo. Before architecture, diagnosis, TDD, triage, PRD, or roadmap work, read
4+
`CONTEXT.md` for domain vocabulary and the capture-reliability contract, plus the relevant ADRs in
5+
`docs/adr/`.
46

5-
Before architecture, diagnosis, TDD, triage, PRD, or roadmap work, read:
7+
Use `CONTEXT.md` vocabulary in issue titles, refactor proposals, test names, and architecture notes.
8+
If a proposed change contradicts an ADR, say so explicitly and explain why the decision should be
9+
reopened.
610

7-
- `CONTEXT.md` for domain vocabulary, test strategy terms, and architecture language.
8-
- Relevant ADRs in `docs/adr/` for accepted architecture decisions.
9-
- This `docs/agents/` directory for issue-tracker and triage-label conventions.
10-
- `docs/agents/web-backend.md` before changing web automation backend setup or diagnostics.
11-
- `docs/agents/testing.md` for maintainer-only test lane notes such as the live web smoke.
12-
13-
Use the vocabulary from `CONTEXT.md` in issue titles, refactor proposals, test names, and architecture notes. If a proposed change contradicts an ADR, call that out explicitly and explain why the decision should be reopened.
11+
`AGENTS.md` routes to the rest of this directory by task type.

docs/agents/pull-requests.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Pull Requests
2+
3+
## Readiness
4+
5+
- Static gates first: required checks pass, `pnpm check:fallow --base origin/main` is clean when
6+
code-quality/dead-code risk is relevant, CI guards are green, and no conflict markers or unmerged
7+
paths remain.
8+
- A local unit-only run is not CI-green. Use `pnpm test:unit` for the repo unit bundle, or
9+
`vitest run --project unit-core --project subprocess-stub` when invoking Vitest directly. The
10+
**Integration Tests** and **Coverage** jobs run the `provider-integration` project — verify those
11+
green on the actual PR head.
12+
- Device-facing behavior is not merge-ready without real simulator/emulator/device evidence for the
13+
changed path. Fixture-backed tests prove contracts; they do not replace a live run that creates or
14+
observes the artifact/state the feature claims to handle. If live verification is blocked, state
15+
the blocker and the exact command/device needed, and downgrade the PR to residual risk rather than
16+
calling it ready.
17+
- Command-surface changes preserve CLI, Node.js, daemon, MCP, help, docs, and SkillGym coverage
18+
where that surface is affected, without duplicating command contracts across layers.
19+
- Runtime output stays agent-friendly: compact defaults, top offenders first for diagnostics/perf,
20+
bounded arrays in JSON, artifact paths for large raw data, progressive lookup for deeper detail.
21+
- Close every manual `agent-device` session opened during verification
22+
(`docs/agents/device-verification.md`) and report any cleanup that could not be completed.
23+
24+
## PR body
25+
26+
Conventional commit prefixes (`feat:`, `fix:`, `chore:`, `perf:`, `refactor:`, `docs:`, `test:`,
27+
`build:`, `ci:`). No bracketed bot tags like `[codex]`. Ready-for-review by default; draft only when
28+
asked or when the work is intentionally incomplete.
29+
30+
- `## Summary`: user/API behavior, not an implementation file tour. Lead with what changed for
31+
operators, clients, command authors, or platform behavior. A compact before/after helps when it
32+
clarifies the workflow or bug fix. For new or changed public APIs, include 1-3 concrete CLI/Node/MCP
33+
examples a reviewer can scan. `Closes #123` when applicable.
34+
- `## Validation`: meaningful evidence in concise prose — scenario names, manual device/browser
35+
evidence, changed screenshots, CI status, notable failures/retries and their outcome. Avoid command
36+
accounting for routine local gates; name an exact command only when it is unusual, manually
37+
reproducible evidence, or needed to explain a residual risk. For docs-only changes, say why runtime
38+
validation does not apply instead of writing a command checklist.
39+
- Call out real tradeoffs, known gaps, and follow-ups; omit boilerplate when there are none.
40+
- Note touched-file count and whether scope expanded beyond the initial command family.
41+
42+
## Reviewing
43+
44+
- Review against the linked issue, not only the diff. State the issue's motivating behavior and
45+
verify the PR fixes *that*.
46+
- Check relevant ADRs before reviewing architecture, routing, command-surface, platform-boundary,
47+
diagnostics, or testing-strategy changes. An ADR conflict is a review finding unless the PR updates
48+
or supersedes the ADR explicitly.
49+
- Read dependency notes (`Blocked by: ...`, linked PRs, sibling branches) before judging correctness.
50+
A base/sequence problem outranks detail review.
51+
- Trace the real production route from command surface through daemon/request routing to the platform
52+
backend. Tests that mock away the router, or exercise only a helper, do not prove the shipped path.
53+
- For each key regression test, identify what deletion or revert would make it fail. If reverting the
54+
implementation still passes, the test is vacuous.
55+
- Check for hidden behavior changes separately from intended refactors: output shape,
56+
warning/error propagation, artifact paths, fallback/retry tiers.
57+
- Verify tests cover the issue's motivating failure, not just the new abstraction. Prefer
58+
before/after evidence when an issue reports a concrete divergence.
59+
- Green CI is necessary but insufficient for device-facing or routing-sensitive work.
60+
- Check whether the tightening pass removed code/tests the change made obsolete.

docs/agents/testing.md

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,52 @@
11
# Testing Notes
22

3+
## Which gates a change needs
4+
5+
Default for code changes: `pnpm check:affected --base origin/main --run`. It derives the gate set
6+
from repository sources of truth, so prefer it over interpreting the table below by hand. GitHub CI
7+
stays authoritative.
8+
9+
The mapping it encodes, for when you need to run a gate directly or reason about coverage:
10+
11+
| Change | Gate |
12+
| --- | --- |
13+
| Any TypeScript | `pnpm typecheck` or `pnpm check:quick` |
14+
| Daemon handler / shared module | `pnpm check:unit` |
15+
| Tooling/config (`package.json`, `tsconfig*.json`, `.oxlintrc.json`, `.oxfmtrc.json`) | `pnpm check:tooling` |
16+
| Platform/device response — anything emitting `platform`/`appleOs` on the wire, or shaping a daemon response | `pnpm test:integration:provider` **and** `pnpm test:coverage` |
17+
| Cross-platform behavior | `pnpm test:integration` |
18+
| iOS runner / Swift | `pnpm build:xcuitest` |
19+
| CLI help/guidance (`src/cli/parser/cli-help.ts`, `src/cli-schema/`) | `pnpm exec vitest run src/cli/parser/__tests__ src/cli-schema/command-schema-guards.test.ts` |
20+
| SkillGym prompts/assertions | `pnpm test:skillgym:case <case-id>` (broad: `pnpm test:skillgym`, filter with `-- --tag fixture-smoke` or `-- --tag skill-guidance`) |
21+
| Anything in `src/`, `test/`, `skills/` | `pnpm format` |
22+
23+
Two traps worth naming:
24+
25+
- The platform/device-response row is the one agents miss. `pnpm check:unit` does **not** exercise the
26+
`provider-integration` project, and that project holds the apple-platform-output leak guard.
27+
Internal `apple` must never reach a command response — project through `publicPlatformString`.
28+
- Fallow CI failures reproduce with `pnpm check:fallow --base origin/main`. Do not estimate
29+
complexity or dead-code impact by hand.
30+
31+
Docs/skills-only and non-TS changes with no behavior impact need no tests. Test-only DI seam CI
32+
failures are enforced by the workflow — do not add optional `typeof` DI params to production code to
33+
satisfy a test.
34+
35+
## Shared test utilities
36+
37+
Before writing a new test, inspect `src/__tests__/test-utils/index.ts`:
38+
`rg -n "export .*make|export .*DEVICE|withMocked" src/__tests__/test-utils`. Import through the
39+
barrel and prefer named shared fixtures over inlining new `DeviceInfo`, `SessionState`, snapshot,
40+
store, or mocked-binary objects. If a helper is missing, add it near the concept it serves and export
41+
it through the barrel.
42+
43+
Keep tests behavioral. Do not assert shapes or cases TypeScript already proves.
44+
45+
Test through public interfaces where practical, and do not add unrelated production exports solely
46+
to make a test easier — widening the public surface for a test is a product change, and the exports
47+
outlive the test that motivated them. If a seam is genuinely missing, add it as a real one rather
48+
than as a test affordance (the workflow separately forbids test-only `typeof` DI params).
49+
350
## Affected-check selector (`pnpm check:affected`)
451

552
`pnpm check:affected --base <ref>` derives which local checks a diff needs, so
@@ -44,8 +91,12 @@ sides of a rename are classified (a moved file cannot look docs-only by its
4491
destination alone).
4592

4693
Anything the selector cannot classify — unknown, ambiguous, workflow/tooling, or
47-
a change to the selector's own sources (including the `AGENTS.md` Testing
48-
Matrix) — **fails open to the full check set**.
94+
a change to the selector's own sources — **fails open to the full check set**.
95+
That includes this file: the Testing Matrix above is the prose the ownership
96+
rules mirror, so `docs/agents/testing.md` is selector-owning
97+
(`SELECTOR_OWNING_DOCS` in `scripts/check-affected/model.ts`) and outranks the
98+
docs-only short-circuit its path would otherwise take. If the matrix moves
99+
again, move that entry with it.
49100
The plan documents the rule and changed path behind every selected check.
50101

51102
Model and catalog live under `scripts/check-affected/`; the derivation is guarded

scripts/check-affected/checks.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,10 @@ export const CHECK_CATALOG: readonly CheckSpec[] = [
145145
id: 'skillgym',
146146
label: 'SkillGym command-planning suite',
147147
kind: { type: 'script', script: 'test:skillgym' },
148-
// No GitHub workflow runs SkillGym; per the AGENTS.md testing matrix it is
149-
// a local-only gate (`pnpm test:skillgym`). Keep it locally runnable rather
150-
// than claiming a CI job that does not exist and silently skipping it.
148+
// No GitHub workflow runs SkillGym; per the Testing Matrix in
149+
// docs/agents/testing.md it is a local-only gate (`pnpm test:skillgym`).
150+
// Keep it locally runnable rather than claiming a CI job that does not
151+
// exist and silently skipping it.
151152
ciJobs: [],
152153
localRunnable: true,
153154
},

scripts/check-affected/model.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ test('workflow/tooling and selector-owning changes fail open', () => {
135135
plan(['scripts/check-affected/model.ts']).failOpenReasons[0]?.rule,
136136
'selector-owning',
137137
);
138-
assert.equal(plan(['AGENTS.md']).failOpenReasons[0]?.rule, 'selector-owning');
138+
// The Testing Matrix lives here; a matrix edit must outrank the docs-only
139+
// short-circuit that its `docs/` path would otherwise take.
140+
assert.equal(plan(['docs/agents/testing.md']).failOpenReasons[0]?.rule, 'selector-owning');
139141
});
140142

141143
test('a fail-open path in a mixed changeset forces the full set', () => {

scripts/check-affected/model.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,17 @@ const ROOT_TOOLING = new Set([
106106
'.npmrc',
107107
]);
108108

109+
// Prose that specifies this selector's own behavior — the Testing Matrix these
110+
// ownership rules mirror. It is docs by path, but editing it can invalidate the
111+
// derivation below, and the selector cannot tell whether it did. Keep this in
112+
// sync when the matrix moves; the docs short-circuit would otherwise treat it as
113+
// inert Markdown.
114+
const SELECTOR_OWNING_DOCS = new Set(['docs/agents/testing.md']);
115+
109116
function isSelectorOwning(file: string): boolean {
110117
return (
111-
file === 'AGENTS.md' || (file.startsWith('scripts/check-affected/') && !file.endsWith('.md'))
118+
SELECTOR_OWNING_DOCS.has(file) ||
119+
(file.startsWith('scripts/check-affected/') && !file.endsWith('.md'))
112120
);
113121
}
114122

@@ -221,7 +229,8 @@ const nodeIntegrationOwnership: OwnershipRule = ({ file }) =>
221229
: [];
222230

223231
// SkillGym validates skill guidance (`skills/`) and owns its harness
224-
// (`test/skillgym/`); AGENTS.md routes skill-prompt/assertion changes here.
232+
// (`test/skillgym/`); the Testing Matrix in docs/agents/testing.md routes
233+
// skill-prompt/assertion changes here.
225234
const skillgymOwnership: OwnershipRule = ({ file, underSkills }) =>
226235
underSkills || file.startsWith('test/skillgym/')
227236
? [

0 commit comments

Comments
 (0)