Skip to content

Commit 8f81081

Browse files
Add sync-openapi-spec skill (#50)
* Add sync-openapi-spec skill Adds a new skill at .agents/skills/sync-openapi-spec/ that compares warp-server/public_api/openapi.yaml against developers/agent-api-openapi.yaml (the file the Scalar API reference at docs.warp.dev/api renders from) and regenerates the docs subset deterministically. The skill is documented in SKILL.md, with the exclusion policy (which tags and paths are kept vs dropped) recorded in references/sync-policy.md and encoded in scripts/sync_openapi.py as EXCLUDED_TAGS / EXCLUDED_PATHS. The script supports --mode {diff,apply,self-test}. This PR ships only the skill itself. Running the skill (and updating developers/agent-api-openapi.yaml) is intentionally a follow-up, so reviewers can vet the policy before any changes land in the public API reference. Co-Authored-By: Oz <oz-agent@warp.dev> * Validate $refs in apply mode and clarify Step 5 Audit-driven follow-up: - Add _validate_output() that walks the regenerated spec and fails if any $ref points at a missing component. Apply mode now exits with code 3 and refuses to overwrite the target on validation failure. - Self-test asserts no unresolved refs after transform. - Fix SKILL.md Step 5 wording: npm run build does not catch dangling $refs (Astro just YAML-parses the file). The new validator covers that case; npm run build remains a belt-and-braces integration check. - Update troubleshooting entry to match the new behavior. Co-Authored-By: Oz <oz-agent@warp.dev> --------- Co-authored-by: Oz <oz-agent@warp.dev>
1 parent 5a923e3 commit 8f81081

3 files changed

Lines changed: 776 additions & 0 deletions

File tree

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
---
2+
name: sync-openapi-spec
3+
description: >-
4+
Sync the public Oz Agent API OpenAPI spec from warp-server into the docs
5+
repo, regenerating `developers/agent-api-openapi.yaml` (the file that
6+
powers the Scalar API reference at `docs.warp.dev/api`). Use when the
7+
warp-server public API has changed, when the Scalar reference looks
8+
stale, or on a scheduled cadence to keep the public API docs aligned
9+
with the canonical spec.
10+
---
11+
12+
# Sync OpenAPI Spec
13+
14+
Keep `developers/agent-api-openapi.yaml` in sync with the canonical spec at `warp-server/public_api/openapi.yaml`.
15+
16+
**Direction:** warp-server → docs. The server spec is the source of truth. The docs file is a curated subset (drops `memory_stores`/`harness-support` and a handful of internal `agent` paths) that Scalar renders on `docs.warp.dev/api`.
17+
18+
## Repos
19+
20+
This skill requires two repos in the agent's environment:
21+
22+
- `warpdotdev/warp-server` — source of truth (`public_api/openapi.yaml`)
23+
- `warpdotdev/docs` — Scalar-facing copy (`developers/agent-api-openapi.yaml`)
24+
25+
## Prerequisites
26+
27+
- Both repos checked out, with `warp-server` reachable from the docs repo (default assumption: sibling directories — `../warp-server/public_api/openapi.yaml`)
28+
- Python 3 with `pyyaml` installed (`pip install pyyaml` or `pip install --break-system-packages pyyaml` in managed environments)
29+
- `gh` CLI authenticated against `warpdotdev/docs`
30+
31+
## Workflow
32+
33+
### Step 1: Self-test
34+
35+
Run the script's self-test first to confirm `pyyaml` is available and the transform logic still passes:
36+
37+
```bash
38+
python3 .agents/skills/sync-openapi-spec/scripts/sync_openapi.py --mode self-test
39+
```
40+
41+
Expected output: `self-test: OK`. If this fails, fix the script before going further.
42+
43+
### Step 2: Diff source against target
44+
45+
```bash
46+
python3 .agents/skills/sync-openapi-spec/scripts/sync_openapi.py \
47+
--mode diff \
48+
--source ../warp-server/public_api/openapi.yaml \
49+
--target developers/agent-api-openapi.yaml
50+
```
51+
52+
The script prints structural drift grouped into:
53+
- Paths added/removed/modified relative to the expected docs subset
54+
- Component schemas added/removed/modified
55+
- Top-level changes (`openapi`, `info`, `servers`)
56+
- Unclassified tags or paths (anything not covered by `EXCLUDED_TAGS` or the `agent`/`schedules` allowlist)
57+
58+
If the script reports `In sync. No changes needed.`, stop here.
59+
60+
### Step 3: Triage unclassified items
61+
62+
Any line prefixed with `!` flags a tag or path the policy doesn't recognize. Do NOT auto-include or auto-drop these. For each one:
63+
1. Read the corresponding handler in `warp-server/router/handlers/public_api/` to confirm whether the endpoint is intended to be public.
64+
2. If the endpoint is public-facing, leave the policy alone — the script will include it on the next `apply`.
65+
3. If the endpoint should remain hidden, extend `EXCLUDED_TAGS` or `EXCLUDED_PATHS` in `scripts/sync_openapi.py` and update `references/sync-policy.md` to record the rationale.
66+
4. Re-run `--mode diff` until no `!` lines remain.
67+
68+
### Step 4: Apply the regenerated subset
69+
70+
```bash
71+
python3 .agents/skills/sync-openapi-spec/scripts/sync_openapi.py \
72+
--mode apply \
73+
--source ../warp-server/public_api/openapi.yaml \
74+
--target developers/agent-api-openapi.yaml
75+
```
76+
77+
This rewrites `developers/agent-api-openapi.yaml` with the regenerated subset. Apply mode validates every `$ref` in the output before writing the file: if any reference is unresolved, the script exits with code 3 and refuses to write. On success it prints `All $refs resolve in the regenerated spec.`
78+
79+
### Step 5: Validate the regenerated spec
80+
81+
Apply mode already catches unresolved `$ref`s (see Step 4). Run these as belt-and-braces integration checks:
82+
83+
```bash
84+
# Astro picks up the new YAML and parses it through Scalar's runtime.
85+
npm run build
86+
```
87+
88+
Optional, recommended when many schemas changed (full OpenAPI lint):
89+
```bash
90+
npx @redocly/cli lint developers/agent-api-openapi.yaml
91+
```
92+
93+
If `npm run build` fails, the most common cause is a malformed path or missing `description` field. Schema-ref breakage is already prevented by Step 4's validator.
94+
95+
### Step 6: Commit and open a PR
96+
97+
```bash
98+
git checkout -b sync-openapi-spec/YYYY-MM-DD
99+
git add developers/agent-api-openapi.yaml
100+
git commit -m "docs: sync agent-api-openapi.yaml from warp-server
101+
102+
Co-Authored-By: Oz <oz-agent@warp.dev>"
103+
git push origin sync-openapi-spec/YYYY-MM-DD
104+
```
105+
106+
Open a draft PR with:
107+
- **Title:** `docs: sync agent-api-openapi.yaml from warp-server`
108+
- **Body:** include the full output from Step 2 (paths/schemas added/removed/modified) so reviewers can see exactly what changed and why.
109+
- **Labels:** `documentation`
110+
111+
Use `report_pr` to surface the PR link.
112+
113+
### Step 7: Report
114+
115+
Summarize:
116+
- Source commit SHA used (capture with `cd ../warp-server && git rev-parse HEAD`)
117+
- Number of paths added / removed / modified in the regenerated subset
118+
- Number of schemas added / removed / modified
119+
- Any items flagged for triage and how they were resolved
120+
- Or confirm `In sync. No changes needed.`
121+
122+
## Sync policy
123+
124+
The policy is encoded in `scripts/sync_openapi.py` as `EXCLUDED_TAGS` and `EXCLUDED_PATHS`. See `references/sync-policy.md` for the rationale behind each entry and the rules for adding new ones.
125+
126+
## Schedule
127+
128+
Run on demand whenever `warp-server/public_api/openapi.yaml` has changed materially since the last docs sync, or on a weekly cadence as a safety net.
129+
130+
## Troubleshooting
131+
132+
### `ModuleNotFoundError: No module named 'yaml'`
133+
Install pyyaml: `pip install pyyaml`. On Debian-based images with externally managed Python, use `pip install --break-system-packages pyyaml`.
134+
135+
### `error: source spec not found at ...`
136+
The `warp-server` repo isn't where the script expected. Pass `--source /absolute/path/to/warp-server/public_api/openapi.yaml`.
137+
138+
### `--mode apply` exits with code 3 and "unresolved $refs"
139+
Apply mode refuses to write the target if any `$ref` in the regenerated spec doesn't resolve to a defined component. The script's recursive `$ref` walker is supposed to keep transitive references (`allOf`/`oneOf`/`anyOf`/`items`/`additionalProperties`/etc.) reachable, so this means either:
140+
- The source spec itself has a dangling reference (fix it in `warp-server`), or
141+
- The walker is missing a reference shape (file a bug against the script).
142+
143+
The error output lists the offending JSON pointer paths so you can locate the reference quickly. Apply will not overwrite `developers/agent-api-openapi.yaml` while this fails.
144+
145+
### Diff shows changes that aren't in the source spec
146+
Make sure `../warp-server` is on the branch you intended to compare against (usually `develop`). Run `cd ../warp-server && git status -sb && git --no-pager log -1` to confirm.
147+
148+
## References
149+
150+
- `scripts/sync_openapi.py` — the diff/apply tool
151+
- `references/sync-policy.md` — exclusion policy and how to extend it
152+
- `../warp-server/.agents/skills/update-open-api-spec/SKILL.md` — server-side workflow for editing the canonical spec
153+
- `../../../src/pages/api.astro` — how the docs site loads the YAML into Scalar
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Sync Policy
2+
3+
This document records what `developers/agent-api-openapi.yaml` keeps from `warp-server/public_api/openapi.yaml`, and why. The exclusion lists live in `scripts/sync_openapi.py` as `EXCLUDED_TAGS` and `EXCLUDED_PATHS`. Update both this document and the script when the policy changes.
4+
5+
## How filtering works
6+
7+
`scripts/sync_openapi.py` applies these rules, top-down:
8+
9+
1. Drop every tag listed in `EXCLUDED_TAGS`.
10+
2. Drop every path whose tags are a subset of `EXCLUDED_TAGS`, plus every path listed explicitly in `EXCLUDED_PATHS`.
11+
3. Keep every surviving path verbatim, including any `x-internal: true` markers on its operations.
12+
4. Keep top-level `openapi`, `info`, `servers`, and `components.securitySchemes` verbatim.
13+
5. Keep only the `components.schemas` entries that are reachable from the surviving paths via `$ref` walking (recursive over `allOf`/`oneOf`/`anyOf`/`items`/`additionalProperties`/etc.).
14+
15+
## Excluded tags
16+
17+
### `memory_stores`
18+
Memory stores are gated as `x-internal: true` server-side. They are not part of the public Oz Agent API surface today and are excluded from the docs reference until they ship publicly. If/when this tag goes public, remove it from `EXCLUDED_TAGS` and update this section.
19+
20+
### `harness-support`
21+
The `/harness-support/*` endpoints form the worker-to-server contract used by Oz workers (transcripts, snapshots, finish-task signaling, etc.). They are not part of the public API contract — customers should not call them directly. Excluded permanently.
22+
23+
## Excluded paths (within otherwise-public tags)
24+
25+
These five `agent`-tag paths are excluded individually because the `agent` tag itself remains public:
26+
27+
- `/agent/runs/{runId}/followups` — internal followup-prompt mechanism used by the harness; not for direct customer use.
28+
- `/agent/runs/{runId}/handoff/attachments` — handoff plumbing tied to local-to-cloud session handoff.
29+
- `/agent/handoff/upload-snapshot` — handoff plumbing (snapshot upload from a local worker).
30+
- `/agent/conversations/{conversation_id}/fork` — conversation-forking primitive used by the harness, not stable public API.
31+
- `/agent/conversations/{conversationId}/redirect` — internal redirect endpoint.
32+
33+
If any of these become stable public surfaces, remove them from `EXCLUDED_PATHS` and update this list.
34+
35+
## What we deliberately KEEP that you might expect to be hidden
36+
37+
The script keeps `x-internal: true` operations under public paths. Today this means the `/agent/messages/*` and `/agent/events/*` operations are present in the docs file even though they're flagged `x-internal` in the source. This matches the pre-existing state of `developers/agent-api-openapi.yaml` and the way Scalar already renders the reference. If we want to start stripping `x-internal` operations from the docs spec, change the policy here and update `_should_keep_path`/the operation-level filter in `scripts/sync_openapi.py`.
38+
39+
## Adding a new exclusion
40+
41+
Use the script's `_unknown_classifications` warnings as the trigger. When the diff flags a new tag or path with `!`:
42+
1. Read the corresponding handler in `warp-server/router/handlers/public_api/` to determine intent.
43+
2. If the endpoint should be hidden:
44+
- For an entire new tag, add the tag name to `EXCLUDED_TAGS` in `scripts/sync_openapi.py`.
45+
- For a single path, add it to `EXCLUDED_PATHS`.
46+
3. Add a short rationale to this document under "Excluded tags" or "Excluded paths."
47+
4. Re-run `--mode diff` to confirm there are no remaining `!` warnings.
48+
5. Then run `--mode apply` and proceed with the normal PR flow.
49+
50+
## Removing an exclusion
51+
52+
When an internal endpoint becomes a stable public surface:
53+
1. Remove it from `EXCLUDED_TAGS` or `EXCLUDED_PATHS`.
54+
2. Remove its bullet from this document.
55+
3. Run `--mode apply`. The path and its referenced schemas will be added to the docs file automatically.
56+
4. Open the PR with the standard sync flow.

0 commit comments

Comments
 (0)