Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 153 additions & 0 deletions .agents/skills/sync-openapi-spec/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
---
name: sync-openapi-spec
description: >-
Sync the public Oz Agent API OpenAPI spec from warp-server into the docs
repo, regenerating `developers/agent-api-openapi.yaml` (the file that
powers the Scalar API reference at `docs.warp.dev/api`). Use when the
warp-server public API has changed, when the Scalar reference looks
stale, or on a scheduled cadence to keep the public API docs aligned
with the canonical spec.
---

# Sync OpenAPI Spec

Keep `developers/agent-api-openapi.yaml` in sync with the canonical spec at `warp-server/public_api/openapi.yaml`.

**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`.

## Repos

This skill requires two repos in the agent's environment:

- `warpdotdev/warp-server` — source of truth (`public_api/openapi.yaml`)
- `warpdotdev/docs` — Scalar-facing copy (`developers/agent-api-openapi.yaml`)

## Prerequisites

- Both repos checked out, with `warp-server` reachable from the docs repo (default assumption: sibling directories — `../warp-server/public_api/openapi.yaml`)
- Python 3 with `pyyaml` installed (`pip install pyyaml` or `pip install --break-system-packages pyyaml` in managed environments)
- `gh` CLI authenticated against `warpdotdev/docs`

## Workflow

### Step 1: Self-test

Run the script's self-test first to confirm `pyyaml` is available and the transform logic still passes:

```bash
python3 .agents/skills/sync-openapi-spec/scripts/sync_openapi.py --mode self-test
```

Expected output: `self-test: OK`. If this fails, fix the script before going further.

### Step 2: Diff source against target

```bash
python3 .agents/skills/sync-openapi-spec/scripts/sync_openapi.py \
--mode diff \
--source ../warp-server/public_api/openapi.yaml \
--target developers/agent-api-openapi.yaml
```

The script prints structural drift grouped into:
- Paths added/removed/modified relative to the expected docs subset
- Component schemas added/removed/modified
- Top-level changes (`openapi`, `info`, `servers`)
- Unclassified tags or paths (anything not covered by `EXCLUDED_TAGS` or the `agent`/`schedules` allowlist)

If the script reports `In sync. No changes needed.`, stop here.

### Step 3: Triage unclassified items

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:
1. Read the corresponding handler in `warp-server/router/handlers/public_api/` to confirm whether the endpoint is intended to be public.
2. If the endpoint is public-facing, leave the policy alone — the script will include it on the next `apply`.
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.
4. Re-run `--mode diff` until no `!` lines remain.

### Step 4: Apply the regenerated subset

```bash
python3 .agents/skills/sync-openapi-spec/scripts/sync_openapi.py \
--mode apply \
--source ../warp-server/public_api/openapi.yaml \
--target developers/agent-api-openapi.yaml
```

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.`

### Step 5: Validate the regenerated spec

Apply mode already catches unresolved `$ref`s (see Step 4). Run these as belt-and-braces integration checks:

```bash
# Astro picks up the new YAML and parses it through Scalar's runtime.
npm run build
```

Optional, recommended when many schemas changed (full OpenAPI lint):
```bash
npx @redocly/cli lint developers/agent-api-openapi.yaml
```

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.

### Step 6: Commit and open a PR

```bash
git checkout -b sync-openapi-spec/YYYY-MM-DD
git add developers/agent-api-openapi.yaml
git commit -m "docs: sync agent-api-openapi.yaml from warp-server

Co-Authored-By: Oz <oz-agent@warp.dev>"
git push origin sync-openapi-spec/YYYY-MM-DD
```

Open a draft PR with:
- **Title:** `docs: sync agent-api-openapi.yaml from warp-server`
- **Body:** include the full output from Step 2 (paths/schemas added/removed/modified) so reviewers can see exactly what changed and why.
- **Labels:** `documentation`

Use `report_pr` to surface the PR link.

### Step 7: Report

Summarize:
- Source commit SHA used (capture with `cd ../warp-server && git rev-parse HEAD`)
- Number of paths added / removed / modified in the regenerated subset
- Number of schemas added / removed / modified
- Any items flagged for triage and how they were resolved
- Or confirm `In sync. No changes needed.`

## Sync policy

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.

## Schedule

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.

## Troubleshooting

### `ModuleNotFoundError: No module named 'yaml'`
Install pyyaml: `pip install pyyaml`. On Debian-based images with externally managed Python, use `pip install --break-system-packages pyyaml`.

### `error: source spec not found at ...`
The `warp-server` repo isn't where the script expected. Pass `--source /absolute/path/to/warp-server/public_api/openapi.yaml`.

### `--mode apply` exits with code 3 and "unresolved $refs"
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:
- The source spec itself has a dangling reference (fix it in `warp-server`), or
- The walker is missing a reference shape (file a bug against the script).

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.

### Diff shows changes that aren't in the source spec
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.

## References

- `scripts/sync_openapi.py` — the diff/apply tool
- `references/sync-policy.md` — exclusion policy and how to extend it
- `../warp-server/.agents/skills/update-open-api-spec/SKILL.md` — server-side workflow for editing the canonical spec
- `../../../src/pages/api.astro` — how the docs site loads the YAML into Scalar
56 changes: 56 additions & 0 deletions .agents/skills/sync-openapi-spec/references/sync-policy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Sync Policy

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.

## How filtering works

`scripts/sync_openapi.py` applies these rules, top-down:

1. Drop every tag listed in `EXCLUDED_TAGS`.
2. Drop every path whose tags are a subset of `EXCLUDED_TAGS`, plus every path listed explicitly in `EXCLUDED_PATHS`.
3. Keep every surviving path verbatim, including any `x-internal: true` markers on its operations.
4. Keep top-level `openapi`, `info`, `servers`, and `components.securitySchemes` verbatim.
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.).

## Excluded tags

### `memory_stores`
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.

### `harness-support`
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.

## Excluded paths (within otherwise-public tags)

These five `agent`-tag paths are excluded individually because the `agent` tag itself remains public:

- `/agent/runs/{runId}/followups` — internal followup-prompt mechanism used by the harness; not for direct customer use.
- `/agent/runs/{runId}/handoff/attachments` — handoff plumbing tied to local-to-cloud session handoff.
- `/agent/handoff/upload-snapshot` — handoff plumbing (snapshot upload from a local worker).
- `/agent/conversations/{conversation_id}/fork` — conversation-forking primitive used by the harness, not stable public API.
- `/agent/conversations/{conversationId}/redirect` — internal redirect endpoint.

If any of these become stable public surfaces, remove them from `EXCLUDED_PATHS` and update this list.

## What we deliberately KEEP that you might expect to be hidden

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`.

## Adding a new exclusion

Use the script's `_unknown_classifications` warnings as the trigger. When the diff flags a new tag or path with `!`:
1. Read the corresponding handler in `warp-server/router/handlers/public_api/` to determine intent.
2. If the endpoint should be hidden:
- For an entire new tag, add the tag name to `EXCLUDED_TAGS` in `scripts/sync_openapi.py`.
- For a single path, add it to `EXCLUDED_PATHS`.
3. Add a short rationale to this document under "Excluded tags" or "Excluded paths."
4. Re-run `--mode diff` to confirm there are no remaining `!` warnings.
5. Then run `--mode apply` and proceed with the normal PR flow.

## Removing an exclusion

When an internal endpoint becomes a stable public surface:
1. Remove it from `EXCLUDED_TAGS` or `EXCLUDED_PATHS`.
2. Remove its bullet from this document.
3. Run `--mode apply`. The path and its referenced schemas will be added to the docs file automatically.
4. Open the PR with the standard sync flow.
Loading
Loading