Skip to content

Commit baf3ecb

Browse files
VedranZoricicclaude
andcommitted
Add CLAUDE.md and generate-openapi-from-pr skill
Add repo-level CLAUDE.md documenting the OpenAPI spec structure, CI/CD workflows, editing conventions, and SDK generation safety rules. Add a Claude skill that automates generating OpenAPI spec changes from intercom monolith PRs. The skill analyzes PR diffs (controllers, presenters, version changes, routes) and produces the corresponding YAML updates. Includes reference guides for Ruby-to-OpenAPI mapping, YAML patterns, and version propagation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 223e8d9 commit baf3ecb

5 files changed

Lines changed: 1760 additions & 0 deletions

File tree

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
---
2+
name: generate-openapi-from-pr
3+
description: Generate OpenAPI spec changes from an intercom monolith PR. Use when a user provides an intercom/intercom PR URL or number and wants to create the corresponding OpenAPI documentation changes in this repo.
4+
allowed-tools: Task, Read, Glob, Grep, Write, Edit, Bash, AskUserQuestion
5+
---
6+
7+
# Generate OpenAPI Spec from Intercom PR
8+
9+
This skill takes an intercom monolith PR (from `intercom/intercom`) and generates the corresponding OpenAPI spec changes in this repo (`Intercom-OpenAPI`).
10+
11+
## When to Activate
12+
13+
- User provides an `intercom/intercom` PR URL or number
14+
- User asks to "generate docs from PR", "create OpenAPI from PR", "document this API change"
15+
16+
## Workflow
17+
18+
### Step 1: Parse Input
19+
20+
Extract the PR number from the user's input. Accept:
21+
- Full URL: `https://github.com/intercom/intercom/pull/12345`
22+
- Short reference: `intercom/intercom#12345`
23+
- Just a number: `12345` (assume intercom/intercom)
24+
25+
### Step 2: Fetch PR Details
26+
27+
```bash
28+
# Get PR description, metadata, and state
29+
gh pr view <NUMBER> --repo intercom/intercom --json title,body,files,labels,state
30+
31+
# Get the full diff (works for open and merged PRs)
32+
gh pr diff <NUMBER> --repo intercom/intercom
33+
```
34+
35+
If the diff is too large, fetch individual changed files instead:
36+
```bash
37+
gh pr view <NUMBER> --repo intercom/intercom --json files --jq '.files[].path'
38+
```
39+
40+
Then fetch specific files of interest (controllers, models, version changes, routes).
41+
42+
For merged PRs where you need the full file (not just diff), fetch from the default branch:
43+
```bash
44+
gh api repos/intercom/intercom/contents/<path> --jq '.content' | base64 -d
45+
```
46+
47+
### Step 3: Analyze the Diff
48+
49+
Scan the diff for these file patterns and extract API-relevant information:
50+
51+
#### 3a. Controllers (`app/controllers/api/v3/`)
52+
53+
Look for:
54+
- **New controller files** → new API resource with endpoints
55+
- **New actions** (`def index`, `def show`, `def create`, `def update`, `def destroy`) → new operations
56+
- **`requires_version_change`** → which version change gates this endpoint
57+
- **`render_json Api::V3::Models::XxxResponse`** → identifies the response model/presenter
58+
- **`params.slice(...).permit(...)`** or **request parser classes** (`RequestParser`, `StrongParams`) → request body fields
59+
- **Error handling** (`raise Api::V3::Errors::ApiCodedError`) → error responses
60+
- **`before_action :check_api_version!`** → version-gated endpoint
61+
62+
#### 3b. Models/Presenters (`app/presenters/api/v3/` or `app/lib/api/v3/models/`)
63+
64+
Look for:
65+
- **`serialized_attributes do`** blocks → response schema properties
66+
- **`attribute :field_name`** → schema field definition
67+
- **`stringify: true`** → field is string type (even if integer in DB)
68+
- **`from_model` method** → how the model maps from internal objects
69+
- **Conditional attributes** based on version → version-specific fields
70+
71+
#### 3c. Version Changes (`app/lib/api/versioning/changes/`)
72+
73+
Look for:
74+
- **`define_description`** → description of the API change (use in PR/commit message)
75+
- **`define_is_breaking`** → whether this is a breaking change
76+
- **`define_is_ready_for_release`** → usually `false` for new changes
77+
- **`define_transformation ... data.except(:field1, :field2)`** → these fields are NEW (removed for old versions)
78+
- **`define_transformation` with data modification** → field format/value changed between versions
79+
80+
#### 3d. Version Registration (`app/lib/api/versioning/service.rb`)
81+
82+
Look for which version block the new change is added to:
83+
- `UnstableVersion.new(changes: [...])` → goes in Unstable (version `0/`)
84+
- `Version.new(id: "2.15", changes: [...])` → goes in that specific version
85+
86+
#### 3e. Routes (`config/routes/api_v3.rb`)
87+
88+
Look for:
89+
- `resources :things` → standard CRUD: index, show, create, update, destroy
90+
- `resources :things, only: [:index, :show]` → limited operations
91+
- `member do ... end` → actions on specific resource (e.g., PUT `/things/{id}/action`)
92+
- `collection do ... end` → actions on resource collection (e.g., POST `/things/search`)
93+
- Nested resources → parent/child paths (e.g., `/contacts/{id}/tags`)
94+
95+
#### 3f. OAuth Scopes (`app/lib/policy/api_controller_routes_oauth_scope_policy.rb`)
96+
97+
Look for scope mappings to understand required auth scope for the endpoint.
98+
99+
### Step 4: Ask User for Version Targeting
100+
101+
Present the findings and ask:
102+
103+
```
104+
I found the following API changes in PR #XXXXX:
105+
- [list of changes found]
106+
107+
Which API versions should I update?
108+
- Unstable only (default for new features)
109+
- Specific versions (for bug fixes/backports)
110+
```
111+
112+
Default to Unstable (`descriptions/0/api.intercom.io.yaml`) unless the PR clearly targets specific versions.
113+
114+
### Step 5: Read Target Spec File(s)
115+
116+
Read the target spec file(s) to understand:
117+
- Existing endpoints in the same resource group (for consistent naming/style)
118+
- Existing schemas that can be reused or extended
119+
- The `intercom_version` enum (to verify version values)
120+
- Where to insert new paths/schemas (maintain alphabetical or logical grouping)
121+
- **All inline examples that reference the affected schema** — when adding a field, you must update every response example that returns that schema. Search with: `grep -n 'schemas/<name>' <spec_file>`
122+
123+
### Step 6: Generate OpenAPI Changes
124+
125+
Use the patterns from the companion guide files:
126+
- **[./ruby-to-openapi-mapping.md](./ruby-to-openapi-mapping.md)** — Ruby pattern → OpenAPI mapping rules
127+
- **[./openapi-patterns.md](./openapi-patterns.md)** — Concrete YAML templates
128+
- **[./version-propagation.md](./version-propagation.md)** — Cross-version propagation rules
129+
130+
#### Adding a new field to an existing schema (most common change)
131+
132+
This is the most frequent PR type. You must update TWO places in the spec:
133+
134+
1. **Schema property** — add the new field to `components/schemas/<schema_name>/properties`
135+
2. **Inline response examples** — search for ALL endpoints that return this schema and update their inline example `value` objects to include the new field
136+
137+
Example from a real PR (adding `previous_ticket_state_id` to ticket):
138+
```yaml
139+
# 1. In components/schemas/ticket/properties — add the field:
140+
previous_ticket_state_id:
141+
type: string
142+
nullable: true
143+
description: The ID of the previous ticket state.
144+
example: '7493'
145+
146+
# 2. In EVERY endpoint response example that returns a ticket — add the value:
147+
examples:
148+
Successful response:
149+
value:
150+
type: ticket
151+
id: '494'
152+
# ... existing fields ...
153+
previous_ticket_state_id: '7490' # ADD THIS
154+
```
155+
156+
**To find all examples that need updating**, search the spec file for the schema name:
157+
```bash
158+
grep -n 'schemas/ticket' descriptions/0/api.intercom.io.yaml
159+
```
160+
Then check each endpoint that references that schema and update its inline examples.
161+
162+
#### Required elements for every new endpoint:
163+
164+
1. **`summary`** — short description used as page title in docs (required)
165+
166+
2. **`description`** — longer explanation of what the endpoint does
167+
168+
3. **`Intercom-Version` header parameter** — always reference the schema:
169+
```yaml
170+
parameters:
171+
- name: Intercom-Version
172+
in: header
173+
schema:
174+
"$ref": "#/components/schemas/intercom_version"
175+
```
176+
177+
4. **`tags`** — group name matching existing tags or new tag for new resource
178+
179+
5. **`operationId`** — unique, camelCase identifier (e.g., `listTags`, `createContact`, `retrieveTicket`)
180+
181+
6. **Response with inline examples + schema ref**:
182+
```yaml
183+
responses:
184+
'200':
185+
description: Successful response
186+
content:
187+
application/json:
188+
examples:
189+
Successful response:
190+
value:
191+
type: resource
192+
id: '123'
193+
schema:
194+
"$ref": "#/components/schemas/resource_schema"
195+
```
196+
197+
7. **Standard error responses** — at minimum `401 Unauthorized`:
198+
```yaml
199+
'401':
200+
description: Unauthorized
201+
content:
202+
application/json:
203+
examples:
204+
Unauthorized:
205+
value:
206+
type: error.list
207+
request_id: <uuid>
208+
errors:
209+
- code: unauthorized
210+
message: Access Token Invalid
211+
schema:
212+
"$ref": "#/components/schemas/error"
213+
```
214+
215+
8. **Request body** (for POST/PUT) with schema and examples
216+
217+
#### Required elements for new schemas:
218+
219+
1. **`title`** — Title Case
220+
2. **`type: object`**
221+
3. **`x-tags`** — tag group linkage (must match a top-level tag name)
222+
4. **`description`**
223+
5. **`properties`** — each with `type`, `description`, `example`
224+
6. **`nullable: true`** — on fields that can be null
225+
7. **Timestamps** — `type: integer` + `format: date-time`
226+
227+
#### Top-level `tags` section (for new resources)
228+
229+
If adding a completely new API resource (not just new endpoints on an existing resource), you MUST add a tag entry to the **top-level `tags` array** at the bottom of the spec file (after `security`):
230+
231+
```yaml
232+
tags:
233+
# ... existing tags ...
234+
- name: Your Resource
235+
description: Everything about your Resource
236+
```
237+
238+
This tag name must match:
239+
- The `tags` array on each endpoint (e.g., `tags: [Your Resource]`)
240+
- The `x-tags` on related schemas (e.g., `x-tags: [Your Resource]`)
241+
242+
Existing top-level tags include: Admins, AI Content, Articles, Away Status Reasons, Brands, Calls, Companies, Contacts, Conversations, Custom Channel Events, Custom Object Instances, Data Attributes, Data Events, Data Export, Emails, Help Center, Internal Articles, Jobs, Macros, Messages, News, Notes, Segments, Subscription Types, Switch, Tags, Teams, Ticket States, Ticket Type Attributes, Ticket Types, Tickets, Visitors, Workflows.
243+
244+
#### Reusable `components/responses` (optional)
245+
246+
The spec defines reusable error responses in `components/responses`:
247+
- `Unauthorized` — 401 with access token invalid
248+
- `TypeNotFound` — 404 for custom object types
249+
- `ObjectNotFound` — 404 for objects/custom objects/integrations
250+
- `ValidationError` — validation errors
251+
- `BadRequest` — bad request errors
252+
- `CustomChannelNotificationSuccess` — custom channel success
253+
254+
You can reference these with `"$ref": "#/components/responses/Unauthorized"` instead of inlining the error response, but most existing endpoints inline their errors. **Match the style of nearby endpoints** in the same resource group.
255+
256+
### Step 7: Apply Changes
257+
258+
Use the Edit tool to insert changes into the spec file(s). Be careful about:
259+
- YAML indentation (2-space indent throughout)
260+
- Inserting paths in logical order (group related endpoints together)
261+
- Inserting schemas alphabetically in `components/schemas`
262+
- Adding new top-level tags in alphabetical order in the `tags` array
263+
- Not breaking existing content
264+
265+
### Step 8: Validate
266+
267+
Run Fern validation:
268+
```bash
269+
fern check
270+
```
271+
272+
If validation fails, read the error output and fix the issues.
273+
274+
### Step 9: Summarize
275+
276+
Report to the user:
277+
- What was added/changed (new endpoints, new schemas, new fields, new top-level tags)
278+
- Which files were modified
279+
- Which versions were updated
280+
- Any manual follow-up needed
281+
282+
#### Follow-up Checklist
283+
284+
Always remind the user of remaining manual steps:
285+
286+
1. **Review generated changes** for accuracy against the actual API behavior
287+
2. **Fern overrides** — if new endpoints were added to Unstable, check if `fern/unstable-openapi-overrides.yml` needs SDK method name entries
288+
3. **Developer-docs PR** — copy the updated spec to the `developer-docs` repo:
289+
- Copy `descriptions/0/api.intercom.io.yaml` → `docs/references/@Unstable/rest-api/api.intercom.io.yaml`
290+
- For stable versions: `descriptions/2.15/api.intercom.io.yaml` → `docs/references/@2.15/rest-api/api.intercom.io.yaml`
291+
4. **Changelog** — if the change should appear in the public changelog, update `docs/references/@<version>/changelog.md` in the developer-docs repo (newest entries at top)
292+
5. **Cross-version changes** — if this is an unversioned change (affects all versions), also update `docs/build-an-integration/learn-more/rest-apis/unversioned-changes.md` in developer-docs
293+
6. **Run `fern check`** to validate before committing
294+
295+
## Important Notes
296+
297+
- **Do NOT run `fern generate` without `--preview`** — this would auto-submit PRs to SDK repos
298+
- **Examples must be realistic** — use plausible IDs, emails, timestamps
299+
- **Match existing style** — look at nearby endpoints for naming and formatting conventions
300+
- **Cross-reference with existing schemas** — reuse `$ref` to existing schemas wherever possible
301+
- **Nullable fields** — always explicitly mark with `nullable: true`
302+
- **The `error` schema** is already defined — always reference it with `"$ref": "#/components/schemas/error"`
303+
- **Top-level tags** — new resources need a tag in the `tags` array at the bottom of the spec
304+
- **Servers** — the spec already defines 3 regional servers (US, EU, AU) — do not modify
305+
- **Security** — global `bearerAuth` is already configured — do not modify

0 commit comments

Comments
 (0)