From 9a9ea84fb2e1e5239baf8a7990dab129f4732ecc Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 16 Sep 2026 18:03:45 +0000 Subject: [PATCH 01/26] Add origin-apps plugin: port a GitHub App to an Origin App Adds the Origin Apps plugin with one skill, port-github-app-to-origin, which discovers a GitHub App's surface from its codebase, maps it onto the live Origin OpenAPI spec (x-origin-scopes / x-origin-webhook-events), and writes a porting brief. Planning only; it writes no code. Dual-layout packaging so partners can use it outside Cursor: root plugin.json (Agent Plugins 1.0), .cursor-plugin/plugin.json (Cursor Marketplace), and .claude-plugin/plugin.json (Claude Code). Adds a root .claude-plugin/marketplace.json so `/plugin marketplace add cursor/plugins` works in Claude Code; it lists only this plugin. --- .claude-plugin/marketplace.json | 14 ++ .cursor-plugin/marketplace.json | 5 + README.md | 1 + origin-apps/.claude-plugin/plugin.json | 21 ++ origin-apps/.cursor-plugin/plugin.json | 30 +++ origin-apps/CHANGELOG.md | 10 + origin-apps/LICENSE | 21 ++ origin-apps/README.md | 87 ++++++++ origin-apps/plugin.json | 21 ++ .../skills/port-github-app-to-origin/SKILL.md | 134 +++++++++++++ .../references/brief-template.md | 185 ++++++++++++++++++ .../references/discovery.md | 147 ++++++++++++++ .../references/gap-bar.md | 143 ++++++++++++++ .../references/origin-isms.md | 169 ++++++++++++++++ .../references/spec-mapping.md | 185 ++++++++++++++++++ .../scripts/index-origin-spec.py | 145 ++++++++++++++ 16 files changed, 1318 insertions(+) create mode 100644 .claude-plugin/marketplace.json create mode 100644 origin-apps/.claude-plugin/plugin.json create mode 100644 origin-apps/.cursor-plugin/plugin.json create mode 100644 origin-apps/CHANGELOG.md create mode 100644 origin-apps/LICENSE create mode 100644 origin-apps/README.md create mode 100644 origin-apps/plugin.json create mode 100644 origin-apps/skills/port-github-app-to-origin/SKILL.md create mode 100644 origin-apps/skills/port-github-app-to-origin/references/brief-template.md create mode 100644 origin-apps/skills/port-github-app-to-origin/references/discovery.md create mode 100644 origin-apps/skills/port-github-app-to-origin/references/gap-bar.md create mode 100644 origin-apps/skills/port-github-app-to-origin/references/origin-isms.md create mode 100644 origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md create mode 100644 origin-apps/skills/port-github-app-to-origin/scripts/index-origin-spec.py diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 000000000..0af83a862 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,14 @@ +{ + "name": "cursor-plugins", + "owner": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "plugins": [ + { + "name": "origin-apps", + "source": "./origin-apps", + "description": "Plan the port of an existing GitHub App to a Cursor Origin App: discover its GitHub surface, map it onto the live Origin API, write a porting brief." + } + ] +} diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index 50f8c2710..556d6a143 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -63,6 +63,11 @@ "source": "cursor-sdk", "description": "Build apps, scripts, and automations with the TypeScript SDK." }, + { + "name": "origin-apps", + "source": "origin-apps", + "description": "Plan the port of an existing GitHub App to a Cursor Origin App: discover its GitHub surface, map it onto the live Origin API, write a porting brief." + }, { "name": "orchestrate", "source": "orchestrate", diff --git a/README.md b/README.md index f5e95e019..f29ea65ca 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Official Cursor plugins for popular developer tools, frameworks, and SaaS produc | `pr-review-canvas` | [PR Review Canvas](pr-review-canvas/) | Cursor | Developer Tools | Render PR diffs as review canvases grouped by importance. | | `docs-canvas` | [Docs Canvas](docs-canvas/) | Cursor | Developer Tools | Render documentation as a navigable canvas. | | `cursor-sdk` | [Cursor SDK](cursor-sdk/) | Cursor | Developer Tools | Build apps, scripts, and automations with the TypeScript SDK. | +| `origin-apps` | [Origin Apps](origin-apps/) | Cursor | Developer Tools | Plan the port of an existing GitHub App to a Cursor Origin App: discover its GitHub surface, map it onto the live Origin API, write a porting brief. | | `orchestrate` | [Orchestrate](orchestrate/) | Cursor | Developer Tools | Fan large tasks out across parallel cloud agents with planners, workers, verifiers, and structured handoffs. | | `pstack` | [pstack](pstack/) | Lauren Tan | Developer Tools | if you want to go fast, go deep first. pstack helps you write less, but higher quality code. rigorous agent workflows you can parallelize with confidence. | | `advisor` | [Advisor](advisor/) | Cursor | Developer Tools | Consult a stronger model before major decisions, when stuck, and before declaring done. | diff --git a/origin-apps/.claude-plugin/plugin.json b/origin-apps/.claude-plugin/plugin.json new file mode 100644 index 000000000..9d1cb54b4 --- /dev/null +++ b/origin-apps/.claude-plugin/plugin.json @@ -0,0 +1,21 @@ +{ + "name": "origin-apps", + "description": "Plan the port of an existing GitHub App to a Cursor Origin App: discover the app's GitHub surface, map it onto the live Origin API, write a porting brief.", + "version": "0.1.0", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "homepage": "https://cursor.com/docs/api/origin", + "repository": "https://github.com/cursor/plugins", + "license": "MIT", + "keywords": [ + "origin", + "origin-api", + "github-app", + "webhooks", + "integration", + "porting" + ], + "skills": "./skills/" +} diff --git a/origin-apps/.cursor-plugin/plugin.json b/origin-apps/.cursor-plugin/plugin.json new file mode 100644 index 000000000..0d130e52d --- /dev/null +++ b/origin-apps/.cursor-plugin/plugin.json @@ -0,0 +1,30 @@ +{ + "name": "origin-apps", + "displayName": "Origin Apps", + "version": "0.1.0", + "description": "Plan the port of an existing GitHub App to a Cursor Origin App: discover the app's GitHub surface, map it onto the live Origin API, write a porting brief.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "homepage": "https://cursor.com/docs/api/origin", + "repository": "https://github.com/cursor/plugins", + "license": "MIT", + "keywords": [ + "cursor-plugin", + "origin", + "origin-api", + "github-app", + "webhooks", + "integration", + "porting" + ], + "category": "developer-tools", + "tags": [ + "origin", + "github-app", + "integration", + "planning" + ], + "skills": "./skills/" +} diff --git a/origin-apps/CHANGELOG.md b/origin-apps/CHANGELOG.md new file mode 100644 index 000000000..d5d9a2ec3 --- /dev/null +++ b/origin-apps/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +All notable changes to this plugin will be documented here. + +## 0.1.0 — initial release + +- Added the `port-github-app-to-origin` skill: discover a GitHub App's surface + from its codebase, map it onto the live Origin API spec, and write a porting + brief with a capability table, webhook field map, hello-world path, gap cards, + and up-front questions. diff --git a/origin-apps/LICENSE b/origin-apps/LICENSE new file mode 100644 index 000000000..ca2bba771 --- /dev/null +++ b/origin-apps/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Cursor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/origin-apps/README.md b/origin-apps/README.md new file mode 100644 index 000000000..8324e10b7 --- /dev/null +++ b/origin-apps/README.md @@ -0,0 +1,87 @@ +# Origin Apps + +Plugin for teams bringing an existing GitHub App to +[Cursor Origin](https://cursor.com/docs/api/origin), Cursor's code forge. +Skills only, so it runs in Cursor, Claude Code, Codex, and any agent that +reads [Agent Skills](https://agentskills.io). + +## What it includes + +- `port-github-app-to-origin`: run it inside your GitHub App's repository with + no other instructions. It discovers the app's GitHub surface from the code + (manifest, permissions, events handled, payload fields read, REST and GraphQL + calls, token minting, webhook receiver, calls your framework makes for you), + fetches the live Origin API spec, and writes a porting brief: a capability + table, the webhook fields your handlers read and where each comes from on + Origin, the scopes to request, a hello-world path to your first real event, + gaps worth raising with Cursor, and the questions your team should settle + first. + +The skill plans; it does not write port code, pick a language or SDK, or +estimate in time. Mappings come from the Origin OpenAPI spec at run time, so the +brief tracks the API as published on the day you run it. + +## When to use + +- You have a GitHub App (Probot, Octokit, go-github, hand-rolled) and want to + know what an Origin App version looks like before you start. +- You are an agent working on such a team's behalf and need a grounded plan. +- You want to check which GitHub features Origin deliberately does not + reproduce, and what the Origin idiom is instead. + +In Cursor, open the app's repository and ask to port it to Origin, or run +`/port-github-app-to-origin`. + +## Install in Cursor + +Search for **Origin Apps** in the Cursor Marketplace +([cursor.com/marketplace/origin-apps](https://cursor.com/marketplace/origin-apps)), +or open **Customize**, find the plugin, and install it at user or project +scope. + +## Use outside Cursor + +The plugin ships three manifests for one set of skills: a root `plugin.json` +([Agent Plugins](https://agent-plugins.org) 1.0), `.cursor-plugin/plugin.json` +(Cursor Marketplace), and `.claude-plugin/plugin.json` (Claude Code). The +skill uses only portable frontmatter (`name`, `description`, `license`, +`compatibility`). + +**Claude Code**, via the marketplace manifest at this repository's root: + +```text +/plugin marketplace add cursor/plugins +/plugin install origin-apps@cursor-plugins +``` + +**Any agent that reads Agent Skills** (Claude Code, Codex, and others): copy +the skill directory into the agent's skills folder. + +```bash +git clone --depth 1 https://github.com/cursor/plugins.git +# Claude Code +mkdir -p .claude/skills && cp -r plugins/origin-apps/skills/port-github-app-to-origin .claude/skills/ +# Codex +mkdir -p .codex/skills && cp -r plugins/origin-apps/skills/port-github-app-to-origin .codex/skills/ +# Cursor, without the marketplace +mkdir -p .cursor/skills && cp -r plugins/origin-apps/skills/port-github-app-to-origin .cursor/skills/ +``` + +## Requirements + +- Network access to `https://cursor.com/docs/api/origin/*` during the run. +- Read access to the app's source. No Origin credentials are needed to produce + the brief; the hello-world path in the brief is what you follow afterwards. +- Optional: `python3` with PyYAML for `scripts/index-origin-spec.py`, which + turns the fetched spec into a grep-friendly index. Without it the skill + reads the spec directly. + +## Where the brief goes + +The skill writes `ORIGIN-PORTING-BRIEF.md` at the repository root and prints +its path. Gap cards in the brief are yours to send: through your shared Slack +channel with Cursor if you have one, or to `hi@cursor.com`. + +## License + +MIT diff --git a/origin-apps/plugin.json b/origin-apps/plugin.json new file mode 100644 index 000000000..a0d396b52 --- /dev/null +++ b/origin-apps/plugin.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", + "name": "origin-apps", + "version": "0.1.0", + "description": "Plan the port of an existing GitHub App to a Cursor Origin App: discover the app's GitHub surface, map it onto the live Origin API, write a porting brief.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "homepage": "https://cursor.com/docs/api/origin", + "repository": "https://github.com/cursor/plugins", + "license": "MIT", + "keywords": [ + "origin", + "origin-api", + "github-app", + "webhooks", + "integration", + "porting" + ] +} diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md new file mode 100644 index 000000000..566f37258 --- /dev/null +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -0,0 +1,134 @@ +--- +name: port-github-app-to-origin +description: >- + Plan the port of an existing GitHub App to a Cursor Origin App. Use when a + repo is a GitHub App (manifest, Probot, Octokit or another GitHub SDK, webhook + signature handlers) and the task is to bring it to Origin or compare it with + the Origin API. Maps the app's surface onto the live Origin spec and writes a + porting brief. No code. +license: MIT +compatibility: >- + Needs network access to https://cursor.com/docs/api/origin/* at run time. + The optional indexing script needs python3 with PyYAML; without it, read the + spec directly. +--- + +# Port a GitHub App to an Origin App + +Run this inside the codebase of an existing GitHub App, with no other +instructions needed. The output is a **porting brief** +(`references/brief-template.md`), not an implementation. "The team" below +means the people who own this app; if you are running this yourself, that is +you. The team keeps its language, framework, and client strategy; the brief +tells them what maps, what changes shape, what is absent on purpose, and what +is worth raising with Cursor. + +Three rules shape everything below: + +1. **The live Origin docs are the source of truth.** Fetch them at run time + and derive every mapping from them. Nothing in this skill pins a version or + enumerates endpoints; the `references/` files explain *how to read* the spec + and *why* Origin differs, and any concrete example in them is illustrative + until you have confirmed it against today's spec. +2. **Discover, do not ask.** Read the manifest, permission declarations, event + handlers, token minting, API calls, and webhook receiver out of the code. + Never ask anyone to paste a manifest or list their endpoints. If something + is genuinely undiscoverable, record it as an open question in the brief. +3. **Origin is GitHub-shaped, not GitHub-compatible.** Many differences are + decisions, not omissions. Classify them as such and guide the port toward + the Origin idiom instead of reproducing the GitHub one. + +## Procedure + +### 1. Load the live Origin surface + +Fetch, in this order, and keep them open for the rest of the run: + +- `https://cursor.com/docs/api/origin/llms.txt` (index of everything below) +- `https://cursor.com/docs/api/origin/openapi.yaml` (the contract; every + mapping in the brief cites an `operationId` or a payload schema from it) +- `https://cursor.com/docs/api/origin/llms-full.txt` (the human reference: + installation, authentication, scopes, mirrored repositories, webhooks, + conventions, current limitations) +- `https://cursor.com/docs/api/origin/changelog` (what moved recently) + +Record `info.version` and the fetch time in the brief's provenance block. That +is provenance, not a dependency: the brief describes the API as it is today and +says so. `references/spec-mapping.md` explains the spec's `x-origin-scopes`, +`x-origin-webhook-events`, `x-origin-webhook-resource`, and +`x-cursor-visibility` extensions and how to build the mapping index from them. +`scripts/index-origin-spec.py ` prints that index (operations +with scopes, parameters, and response fields; webhook slugs with payload +fields; the scope catalog) so you can grep it instead of paging through 700 KB +of YAML; it needs python3 with PyYAML and does nothing else. + +### 2. Discover the GitHub App's shape + +Follow `references/discovery.md`. Produce an inventory with a file and line for +every fact: declared permissions and events, webhook events handled, payload +fields the handlers read, REST and GraphQL calls, authentication flow, webhook +receiver and signature verification, calls made on the app's behalf by its +framework and helper libraries, and the observed language and client +libraries (observed, never chosen). Note what you looked for and did not find. + +### 3. Map each capability onto Origin + +For each inventory row, look up the Origin counterpart in the spec index built +in step 1 (`references/spec-mapping.md` § Matching rules), then classify it +with one of the parity labels in `references/brief-template.md`. Before +labeling anything `gap`, check `references/origin-isms.md`: a GitHub feature +that Origin deliberately does not reproduce is `by-design-absent` with a +pointer to the Origin idiom, and the brief must say what to do instead rather +than raise it. Then apply the bar in `references/gap-bar.md`; only rows that +clear it become gap cards. + +Map webhook payload *fields* the code reads, not just event names. Origin +payloads are lean row snapshots; a field GitHub inlines is often a follow-up +REST read on Origin. Say which call, per field. + +Two labels are easy to misuse. A GitHub surface with no Origin counterpart +*and* no mention anywhere in the Origin docs (Marketplace billing, merge +queues, Actions, Pages) is `unknown` with a question, not a `gap`: Origin has +not said no, and the team may not need it. A behavior the code depends on +that the docs neither confirm nor deny (does event X fire in case Y? does +`updatedAt` move on comments?) is also a question, plus a step on the +hello-world path to observe it; never guess it into `same`. + +### 4. Write the brief + +Fill `references/brief-template.md` in full. Every table cell that names an +Origin operation, event, or field links to its anchor in `llms-full.txt` or +names its `operationId`. Sizes are S/M/L as defined in the template, never +time. The hello-world path is the sequence Create App → subscribe events → +install → verify ping signature → first real event on a native repository; +it is a checklist of things to verify, not code. + +### 5. Ask the up-front questions + +Close the brief with the questions in the template's final section, pruned to +what the discovery left open and extended with anything specific you found. +The first question is always whether the target repositories are Origin-native +or mirrored from GitHub, because that decides whether the app will receive +real events at all. + +## What this skill does not do + +- Write, scaffold, or vibecode port code, adapters, or SDK wrappers. +- Choose a language, framework, HTTP client, or codegen strategy. +- Estimate effort in hours, days, or sprints. +- Pin the spec version, copy endpoint lists into the brief from memory, or + claim parity from a name match without reading the operation. +- Ask for information the codebase already contains. +- Contact Cursor on the team's behalf; the brief carries the escalation cards + and the team decides what to send and where. + +## Reference files + +| File | Read when | +| --- | --- | +| `references/spec-mapping.md` | Building the spec index and matching GitHub calls, events, and payload fields to Origin operations, slugs, and schemas. | +| `references/discovery.md` | Scanning the codebase for the app's GitHub surface. | +| `references/origin-isms.md` | Deciding whether a missing GitHub feature is a decision or a gap, and what the Origin idiom is. | +| `references/gap-bar.md` | Deciding whether a gap is worth raising with Cursor, and writing the card. | +| `references/brief-template.md` | Writing the output. | +| `scripts/index-origin-spec.py` | Turning the fetched `openapi.yaml` into a grep-friendly index (operations, webhook families, scopes, one component). Optional; needs PyYAML. | diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md new file mode 100644 index 000000000..7642ca1da --- /dev/null +++ b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md @@ -0,0 +1,185 @@ +# Porting brief template + +Write the brief as one Markdown file at the repository root +(`ORIGIN-PORTING-BRIEF.md` unless the team's docs convention says otherwise) +and print its path. Fill every section; where a section is genuinely empty, +say so in one line rather than deleting it, so the team can see it was +considered. Cite spec `operationId`s and `llms-full.txt` anchors; cite the +team's code by `file:line`. + +Keep it light. A table row per capability, a line per follow-up field, a card +per gap. The team will read this in one sitting and then argue about it; give +them the shape to argue over, not prose. + +## Labels used in the tables + +**Parity** + +| Label | Meaning | +| --- | --- | +| `same` | Same capability, same shape; a path or field rename at most. | +| `reshaped` | Same capability, different shape: pagination style, identifier form, event granularity, key semantics. The code changes, the behavior does not. | +| `workaround` | Same outcome reachable with existing surface by a different route (follow-up read, client-side filter, marker). Tradeoff column is mandatory. | +| `by-design-absent` | GitHub feature Origin deliberately does not reproduce (`origin-isms.md`). Points at the Origin idiom or says "no equivalent; decision needed". | +| `gap` | No workaround, or a workaround whose tradeoff fails the bar (`gap-bar.md`). Has a card in § Gaps worth raising. | +| `unknown` | Discovery could not determine the app's use or the spec's answer. Has an up-front question. | +| `preview` (suffix) | The Origin operation is stamped `x-cursor-visibility: PREVIEW`. Usable; shape may still move. | + +**Size** — how much of the team's code changes for this row, by kind of +change, never by time: + +| Size | Meaning | +| --- | --- | +| S | Contained in the adapter or client layer: a path, header, identifier, or pagination rewrite; a re-keyed lookup. | +| M | A new code path: a follow-up read where the payload used to suffice, a handshake step, a new event handler, a data-model change for a new identifier or version concept. | +| L | A product or architecture change: a flow that depended on user OAuth, a customer-visible behavior, a dependency on the customer's repositories being Origin-native, or a capability with an open gap card. | + +--- + +## Template + +```markdown +# Origin porting brief — + +Planning document. Maps this GitHub App's surface onto the Cursor Origin API +as published on . Contains no implementation decisions about language, +framework, or client strategy. + +## Provenance + +- Origin OpenAPI `info.version`: ``, fetched +- Docs read: +- Codebase: `` at ``; scanned files +- Rows marked `workaround` or `gap` should be re-checked against the changelog before work starts; they are the rows most likely to have moved. + +## 1. What the app is today + +One paragraph in plain words: what the app does for its users, which GitHub +events drive it, what it writes back. Then the inventory: + +| Facet | Finding | Evidence | +| --- | --- | --- | +| Manifest / declared permissions | … or "none checked in; permissions derived from calls" | `file:line` | +| Declared / handled events | `pull_request.opened`, … | `file:line` | +| REST call families | , listed in § 3 | | +| GraphQL | none / documents, decomposed in § 3 | | +| Auth flow | app JWT () → installation token; user OAuth: | `file:line` | +| Webhook receiver | path, verification scheme, raw-body availability, dedupe | `file:line` | +| Git as the app | clone / push / none | `file:line` | +| Observed language and libraries | … (observed only) | | +| Looked for, not found | … | | + +## 2. First decision: which repositories + + On Origin, +an installation has full scopes only on Origin-native repositories and +stable outbound mirrors; repositories mirrored from GitHub are read-only to +apps and do not deliver push events. **Question 1 below must be answered +before the hello-world path is attempted.** + +## 3. Capability table + +One row per GitHub capability the code uses. Group rows by facet +(authentication, installation & discovery, repositories & contents, pull +requests, reviews & comments, checks, webhooks, git). Follow-up fields go in +§ 4, not here. + +| GitHub thing (evidence) | Origin equivalent | Parity | Size | Tradeoff | Open question | +| --- | --- | --- | --- | --- | --- | +| `GET /repos/{o}/{r}/pulls/{n}` (`src/x.ts:12`) | `OriginService_GetPullRequest` | same | S | — | — | +| … | … | … | … | … | … | + +Rules for the table: the Origin column names an `operationId`, an event +slug, a `llms-full.txt` anchor, or `none`; `workaround` rows always fill +Tradeoff; `gap` rows link their card; `by-design-absent` rows name the idiom +in the Tradeoff column; `unknown` rows name their question. Group rows by +facet with a bold header row (Authentication · Installation & discovery · +Configuration · Repositories & contents · Pull requests · Reviews & comments +· Checks · Webhooks: events · Webhooks: receiver · Git), and include rows +for calls a dependency makes on the app's behalf, marked as such. + +**Scopes to request** (one line under the table): the union of +`x-origin-scopes.scopes` across every Origin operation named above that an +installation token can call, minus scopes that are ambient or implied +(`write` implies `read`; `repository:metadata:read` is automatic). This is +what the install URL's `scope` parameter carries, so the team can read it +straight off the brief. + +## 4. Webhook payload fields the code reads + +For each mapped event, the fields the handlers dereference. + +| Event (GitHub → Origin) | GitHub field | Origin | How | +| --- | --- | --- | --- | +| `pull_request.synchronize` → `pull_request.head_ref.pushed` | `pull_request.head.sha` | present | `payload.pullRequest.head.sha` | +| `push` → `repository.pushed` | `commits[].added` | follow-up read | `OriginService_ListComparisonFiles` on `refUpdates[].before..after` — one call per ref update | +| … | `repository.html_url` | derivable / absent | … | + +"How" is one of: present at ``; present in envelope (`event.type` for +GitHub's `action`); follow-up read via `` (state the call count +per event); derivable (say from what, and whether the format is documented); +absent (→ § 3's label for that row: `by-design-absent`, `unknown`, or a § 6 +card). Include fields the code reads only for logging; they are the ones +teams forget until a dashboard breaks. + +## 5. Hello-world path + +The shortest route to one real event from one native repository. Each step +is something to verify, not code to write. Link each to `llms-full.txt`. +Append one step per spec-silent behavior the brief depends on (see the +questions), stated as the observation to make. + +1. **Create the app** in the target namespace's app settings; register the + Ed25519 public key only; set the webhook URL and the callback URI. +2. **Subscribe to events.** Installation lifecycle events arrive regardless; + select every repository event from § 3 explicitly. An unselected event is + silence, not an error. +3. **Install** on an Origin-native repository (or a stable outbound mirror). + Verify the installation receipt (`kid` → JWKS, `alg`, `typ`, `iss`, `aud`, + `exp`, `state`); read the installation ID from `sub`. Never send the + receipt as a Bearer token. +4. **Mint** an app JWT (EdDSA, ~5 min) and exchange it for an installation + token; call `/installation/repos` and confirm the repository is listed + and its `mirror` state is what § 2 expects. +5. **Ping** the receiver and verify `v1ed` over the raw body against the + JWKS; check timestamp skew handling and `webhook-id` dedupe. +6. **First real event**: perform the smallest action in § 3 on the native + repository (open a PR, push a branch) and confirm the delivery arrives + with the expected slug and the payload fields from § 4. If the ping + arrived and this did not, re-check steps 2 and 3 before anything else. +7. **First write back** (if the app writes): the smallest write from § 3 + (a check run with a stable `key`, a PR comment), confirming the scope + from `x-origin-scopes` is in the installation grant. + +## 6. Gaps worth raising + +Zero or more cards in the `gap-bar.md` shape. If zero, say: "No row failed +the gap bar; the workarounds in § 3 carry their tradeoffs." Do not pad. + +## 7. Questions for the team + +Pruned to what discovery left open, plus anything specific found. Always +starts with the first three. + +1. Will the app run against Origin-native repositories (or stable outbound + mirrors), or against repositories mirrored from GitHub? (Decides whether + the app receives events and can write at all.) +2. Which of the follow-up reads in § 4 are acceptable at your event volume, + and which payload fields are hard requirements? +3. Which flows depend on a user credential today (user OAuth, install-by-user + pickers, acting on behalf of a user), and what should they do on Origin? +4. Does anything key approvals or reviews by commit SHA rather than by pull + request version? +5. Do you generate clients from OpenAPI? (Then read the changelog for schema + renames and check reserved names in your language.) +6. How do you identify your own check runs / comments / reviews today, and + can a key or marker you control replace actor matching? +7. What is your first success metric: ping received, first real event, or + first write back on a native repository? +8. Anything marked `unknown` in § 1 or § 3. + +## 8. Out of scope for this brief + +No implementation, no SDK or language choice, no effort estimates in time. +The brief is a map; the route is the team's. +``` diff --git a/origin-apps/skills/port-github-app-to-origin/references/discovery.md b/origin-apps/skills/port-github-app-to-origin/references/discovery.md new file mode 100644 index 000000000..fb3532130 --- /dev/null +++ b/origin-apps/skills/port-github-app-to-origin/references/discovery.md @@ -0,0 +1,147 @@ +# Discovering the GitHub App's shape from the codebase + +Everything the brief needs about the existing app is in the repository. Search +for it; do not ask for it. Record a file and line for every fact so the team +can check your reading, and record every place you looked that turned up +nothing so they can point you at the right place if you missed it. + +Work through the seven facets below. The searches are starting points across +the common ecosystems (Node Octokit and Probot, Go `go-github`, Python +`PyGithub`/`ghapi`/`githubkit`, Java `hub4j`, Ruby `octokit.rb`, .NET +`Octokit.net`, raw HTTP in any language); extend them when the code uses +something else. Note the language and libraries you observe as a fact in the +inventory — they inform sizes in the brief and nothing else. + +## 1. Declared permissions and events + +The app's manifest or registration snapshot, if it is checked in. + +- Files: `app.yml`, `app.yaml`, `.github/app.yml`, `github-app-manifest.json`, + `manifest.json`, `app-manifest.*`, Terraform or Pulumi resources for the + app, and infrastructure-as-code that seeds a GitHub App. +- Keys: `default_permissions`, `default_events`, `hook_attributes`, + `redirect_url`, `callback_urls`, `setup_url`, `public`, + `request_oauth_on_install`, `setup_on_update`. +- Probot: `app.yml` at the repo root carries `default_events` and + `default_permissions`. + +When there is no manifest, derive the effective permissions from the calls in +facet 4 and say the manifest was absent. The union of what the code calls is +what the port needs anyway. + +## 2. Webhook events handled + +- Probot / `@octokit/webhooks`: `app.on(".", …)`, + `app.on([...])`, `webhooks.on(`, `webhooks.onAny(`, `EmitterWebhookEvent`. +- Hand-rolled receivers: a `switch` or dispatch on the `x-github-event` header + (any casing), on `payload.action`, or on a combined `"."` + string; Go `github.WebHookType(r)` / `github.ParseWebHook`; Python + `request.headers["X-GitHub-Event"]`. +- Framework routes registered for webhook paths: `/webhook`, `/webhooks`, + `/github/webhooks`, `/api/github/events`, `createNodeMiddleware`, + `createProbot`, smee/ngrok tunnel config in development scripts. + +For each handled event record the GitHub event, the action(s) handled, the +handler location, and — from facet 3 — which payload fields it reads. + +## 3. Payload fields read + +Inside each handler from facet 2, list every property path dereferenced from +the payload object: `payload.pull_request.head.sha`, `payload.repository.name`, +`payload.installation.id`, `payload.sender.login`, `payload.commits[].added`, +`payload.check_suite.pull_requests`, `payload.before`, `payload.after`, and so +on. Typed languages make this easy (struct fields accessed); in dynamic +languages grep the handler body and any helper it passes the payload to. +Include fields used only for logging or metrics — those are the ones teams +forget until the port breaks a dashboard. + +## 4. REST and GraphQL calls + +- Octokit REST: `octokit.rest..(`, `octokit..(`, + `octokit.request(" /…")`, `octokit.paginate(`, `octokit.graphql(`, + `@octokit/graphql`, `.graphql(`. +- Other SDKs: Go `client.PullRequests.`, `client.Checks.`, `client.Repositories.`, + `client.Issues.`, `client.Apps.`; Python `repo.get_pull(`, `gh.rest.`, + `githubkit`; Java `GHRepository`, `GHPullRequest`; Ruby `client.pull_request(`. +- Raw HTTP: `api.github.com`, `/repos/`, `Accept: application/vnd.github`, + `X-GitHub-Api-Version`, `uploads.github.com`, `raw.githubusercontent.com`. +- GraphQL documents: `.graphql` / `.gql` files, template strings starting with + `query` or `mutation`, generated client code. + +Record each distinct call family once (method + path, or SDK method), with +the parameters and filters the code passes (state, base, head, per_page, +sort, since, check_name, app_id, filter…), the response fields it reads, and +whether it is called in a loop or per webhook (this decides the fan-out +tradeoff on the brief row). Note pagination style in use (`Link` header, +`page`/`per_page`, `octokit.paginate`, GraphQL cursors) — it always changes. + +## 5. Authentication and token minting + +- App identity: `GITHUB_APP_ID`, `APP_ID`, `GITHUB_PRIVATE_KEY`, `PRIVATE_KEY`, + `.pem` files, `createAppAuth`, `@octokit/auth-app`, `ghinstallation` + (Go), `jwt.encode(... "RS256")`, `App.get_installation(`. +- Installation tokens: `/app/installations/{id}/access_tokens`, + `installationId`, `installation_id`, `app.auth(`, `getInstallationOctokit(`, + `ghs_` prefixes in tests or fixtures, token caches keyed by installation. +- User OAuth: `/login/oauth/authorize`, `/login/oauth/access_token`, + `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`, `@octokit/auth-oauth-*`, + `/user`, `/user/installations`, `/user/repos`, "setup URL" handlers reading + `installation_id` and `setup_action` from the callback query string. +- Git over HTTPS as the app: `x-access-token:` in clone URLs, credential + helpers, `GIT_ASKPASS`. + +Record the flows present: app JWT algorithm, how the installation is +identified after install (callback query, webhook, or DB), token lifetime +handling, whether user OAuth exists and what it is used for (identity, +repo discovery, acting on behalf of a user), and whether the app pushes or +clones git. + +## 6. Webhook receiver and verification + +- Signature: `x-hub-signature-256`, `x-hub-signature`, `verify(`, + `verifyAndReceive(`, `WEBHOOK_SECRET`, HMAC-SHA256 helpers, + `github.ValidatePayload`. +- Delivery handling: use of `x-github-delivery` for idempotency, queueing + before or after responding, retry handling, redelivery tooling against + `/app/hook/deliveries`. +- Transport: the public URL and how it is configured (manifest + `hook_attributes.url`, env var, tunnel in dev). + +Record the verification scheme, whether the raw body is available at +verification time (frameworks that parse JSON first cannot verify), and how +deliveries are deduplicated, if at all. + +## 7. Calls the framework makes on the app's behalf + +Some of the app's GitHub surface is not in its own source: it is what the +framework or a helper library does for it, and the port has to do it too. +When the code uses one of these, list the calls the dependency makes as rows +in their own right, marked "from `` (documented behavior)" so the +team knows they were inferred, not read. `node_modules` is usually not +checked in, so read the dependency's README or its source on its own +repository, not the app. + +- **Probot**: the built-in receiver (`POST /`, `@octokit/webhooks` HMAC + verification, `x-github-event` routing), per-installation token minting + and caching, `context.repo()` / `context.issue()` helpers, + `context.isBot` (`payload.sender.type`). Common companions: + `probot-config` (reads `.github/.yml`, falling back to the owner's + `.github` repository), `probot-scheduler` (lists installations and their + repositories with the app credential, then emits `schedule.repository` on + an interval), `probot-metadata` (stores state in issue bodies). +- **Octokit `App` / `@octokit/app`**: `app.webhooks.verifyAndReceive`, + `app.eachInstallation` / `app.eachRepository` (installation + repository + listing), automatic installation-token minting behind + `app.getInstallationOctokit`. +- **Go `ghinstallation`, Python `githubkit`/`gidgethub`, Ruby `octokit` + app auth**: JWT minting and installation-token exchange. +- **Frameworks' webhook middleware** in any language: which header names + they read and whether they verify against the raw body. + +## When something is missing + +Say so in the inventory: "no manifest found (searched: …)", "no signature +verification found in the receiver at …", "GraphQL client present but no +query documents found". Each missing item becomes either a row with an +`unknown` label or an up-front question in the brief. Do not fill gaps with +assumptions about what an app of this kind usually does. diff --git a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md new file mode 100644 index 000000000..1e0d32903 --- /dev/null +++ b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md @@ -0,0 +1,143 @@ +# The gap bar and the escalation card + +Most differences between GitHub and Origin are not gaps. A brief that files +every difference trains the team to ignore it and buries the two or three +things Cursor actually needs to hear. Use this bar, and when in doubt, write +the row as `workaround` with an honest tradeoff and an open question rather +than as a gap card. + +## Definitions + +- A **difference** is any row whose parity label is not `same`. +- A **workaround** is a way to get the same outcome with the surface that + exists today: a follow-up read, a re-keyed identifier, a path change, a + client-side filter, a marker the app controls. +- A **gap** is a difference with **no workaround**, or a workaround whose + tradeoff is **nontrivial**. Only gaps become escalation cards. + +## What makes a tradeoff nontrivial + +A workaround's tradeoff is nontrivial when at least one of these holds. Quote +the one that applies on the card. + +| Tradeoff | Test | +| --- | --- | +| **Fan-out at scale** | The workaround multiplies calls per event by a factor that grows with repository or activity size (N commits × M files per push; a full list scan to find one row), and the app's event volume makes that budget-relevant. One extra bounded read per event is trivial. | +| **Correctness risk** | The workaround can produce a wrong answer, not just a slower one: identifying "my own row" by a heuristic; inferring a pull request from a SHA that several versions share; assembling a URL whose format is not contractual. | +| **Security posture** | The workaround needs a broader scope, a longer-lived token, or a user credential where an installation token should do. | +| **Product behavior visible to the team's customers** | The workaround changes what their users see or can do (no team link in a comment, no user-scoped repository picker), not just how the code is organized. | +| **Load-bearing for the port** | The capability sits on the hello-world path or on the team's stated core flow, so its tradeoff decides whether the port ships. | + +If none apply, the row is `workaround`, sized honestly, with the tradeoff in +the tradeoff column and no card. + +## What is never a gap + +- Anything on the `origin-isms.md` list. Those rows are `by-design-absent` + or `reshaped`, and the brief points at the idiom. Filing them wastes the + team's and Cursor's time; the decisions are recorded. +- A field or filter the code does not actually use. Map what the code reads, + not what the SDK exposes. +- A difference that exists only because the code uses a GitHub convenience + (`Link` pagination, numeric IDs, `html_url`) in a place where the Origin + convention is a mechanical substitution. +- Something the changelog says shipped or the spec already carries. Re-check + the live spec before writing any card; the rows most likely to be stale are + the ones you are about to escalate. +- A concept the Origin docs never mention at all (Marketplace billing, merge + queues, Actions, Projects). That is `unknown` plus a question + (`spec-mapping.md` § Out-of-domain surfaces); a card asks Origin to build + something the team may not want. +- A GitHub search query. Origin has no search endpoint by design for the + surfaces it exposes; a list operation with `sortBy`/`state`/filters plus a + client-side predicate is the idiom. Count the fan-out honestly (a sorted + list read that stops at a cutoff is proportional to the matches, not the + collection), and only if that count fails the bar does it become a card + about a *filter*, never about search. + +## One pattern that does clear the bar + +A state change the app reacts to that has **no event**, when the app's +purpose is to react to exactly that change and the state is otherwise +invisible until an unrelated event arrives. Reading the state off the next +event's snapshot is the workaround; it fails on correctness and +customer-visible behavior when the app is a gate (a check, a block, a +notification) and the lag is the whole failure mode. Write the card about the +event; do not write it when the app merely logs or tidies up on that change. + +## The escalation card + +One card per gap, in the brief's "Gaps worth raising" section, in this shape. +It is written so Cursor can act on it without a call. + +```markdown +### Gap: + +- **GitHub surface the app uses:** `` or `` or ``, at ``. +- **What the app needs from it:** . +- **Why:** . +- **Closest Origin surface today:** `` / `` / none, and what it lacks. +- **Workaround considered:** . +- **Tradeoff that fails the bar:** . +- **Shape that would close it:** . +- **Blocking?** yes / no, and for which flow. +- **Spec version checked:** `` on ``. +``` + +Keep the card to those lines. Do not propose scope names, field names, or +route templates; Cursor owns the shape and applies design conventions the +card cannot see. Do not batch unrelated capabilities into one card. + +## Where the card goes + +The brief is written for the team. The cards are theirs to send, in their own +words if they prefer. Two routes exist today: + +- **A shared Slack channel with Cursor**, if the team has one from working + with Cursor on the integration. Post the card there; it reaches the people + who own the API. +- **`hi@cursor.com`**, the feedback address the Origin documentation names. + Put "Origin API" and the app name in the subject and paste the card. + +Either way, quote the spec version the card was checked against and any +`X-Request-ID` values from failed calls (every Origin error response carries +one; the API reference asks for it when you contact Cursor). A card that +states the capability and the tradeoff cleanly is the fastest path to an +answer, including a "this is by design, here is the idiom" answer, which is a +fine outcome and belongs back in the brief as a `by-design-absent` row. + +Do not send cards yourself. Write them, put them in the brief, and let the +team decide what goes out. + +## Calibration examples + +Illustrations of where the bar falls. Each is written as a pattern, because +the specific surface may have moved since this file was written; confirm +against the live spec before reusing the verdict. + +- **A list operation lacks a filter the code relies on to find its own row** + (for example, finding the app's own check run for a commit by name or key). + Workaround: page the whole list and match client-side. Trivial when the + list is small and bounded; **gap** when the list grows with activity and the + lookup runs per event (fan-out) or when the match is heuristic + (correctness). State which. +- **A web URL the app posts in comments is not in the payload or resource.** + Workaround: assemble it from slug and number. Correctness risk only if the + URL format is not contractual — check the docs; if the format is documented, + it is `derivable`, not a gap. If it is not documented, write a card that + asks for the URL field rather than guessing. +- **A membership roster the app uses to attribute approvals to a team** has + no read on Origin and no workaround (identity is a TypeID; there is no + directory). Correctness plus product behavior: **gap**, with a card that + names the capability ("who is in group X, for a repository this + installation can read") and leaves the oracle analysis to Cursor. +- **Push payload lacks the changed-file list.** Workaround: compare or list + commit files on receipt. One bounded read per push is `workaround`; if the + app fans out per commit and per file on high-volume repositories, quote the + multiplier and let the team decide — and note that the lean payload is the + documented design (`origin-isms.md` § Webhooks), so the card, if any, is + about a compare endpoint's shape, not about fattening the payload. +- **The app uses GitHub Issues.** Never a card. `by-design-absent`; the + up-front question is what the PR-scoped behavior should be. +- **The app uses commit statuses.** Never a card. `reshaped` onto check runs + with a stable key. diff --git a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md new file mode 100644 index 000000000..d81f7226b --- /dev/null +++ b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md @@ -0,0 +1,169 @@ +# Origin-isms: where Origin departs from GitHub on purpose + +Origin uses GitHub's nouns and general shape and deliberately not its wire +format. When a GitHub feature has no counterpart in the spec, check this list +before calling it a gap: a row here is `by-design-absent` (or `reshaped`) in +the brief, and the brief's job is to point the team at the Origin idiom. Each +entry gives the departure, the idiom, and the reason in one line, so you can +explain it to a team that has only ever seen GitHub. Confirm the current +wording in `llms-full.txt`; this file explains intent, the docs state the +contract. + +## Which repositories an app can act on + +- **Apps act on Origin-native repositories (and stable outbound mirrors).** A + repository mirrored *from* GitHub is read-only to an installation — metadata + and contents reads, clone/fetch/pull — and rejects pull requests, reviews, + comments, checks, rulesets, and every write with `403`. Why: a write on a + mirror would have to act on GitHub as Cursor's GitHub App, with risk Origin + cannot bound; and GitHub already sends its own webhooks for those repos. +- **`repository.pushed` is not delivered for GitHub-mirrored repos.** Why: + GitHub owns those pushes and already notifies the app; Origin would + duplicate them. Consequence for the port: a ping works on a mirror and then + nothing else does. The first up-front question in every brief is + native-or-mirror for exactly this reason. +- **`mirror.status` does not tell you whether writes are allowed.** Treat the + `403` as authoritative. + +## Identity and authentication + +- **No OAuth-app token exchange as the install path.** Install is a consent + redirect that returns a signed **installation receipt** JWT (`sub` = the + installation ID, `state` echoed) instead of `installation_id` and + `setup_action` query parameters. Why: the receipt proves the approval came + from Origin; a bare query string cannot. The receipt is never a Bearer + token. +- **App JWT is EdDSA over Ed25519, not RS256.** Register only the public key; + up to ten active keys. `kid` and `iss` are the app ID; `aud` is fixed. +- **Installation tokens are `oit_…`, short-lived, minted just in time**, and + can be attenuated to fewer scopes or to specific `repositoryIds` (IDs, not + slugs — resolve the ID first). Git over HTTPS uses Basic auth with username + `x-access-token` and the token as password; Bearer belongs to REST only. +- **User-scoped calls (`/user`, `/user/installations`, install-by-user) have + no app-side counterpart.** Repository discovery is through the + installation (`/installation/repos`), and app creation, adding + repositories to an installation, and mirror transitions are admin actions + carried by a Cursor user credential, not app calls. Read the live + Authentication section for the current user-credential story before + labeling anything here; do not assume a GitHub-OAuth analog exists. +- **Scopes, not permissions.** `repository:[:]:`; + `write` implies `read`; `repository:metadata:read` is automatic; the + installation can only narrow what the admin approved. Look each called + operation's requirement up in `x-origin-scopes` rather than translating the + manifest noun-for-noun. + +## Wire conventions + +- **Opaque TypeIDs, never integers**: `app_…`, `i_…`, `ns_…`, `repo_…`, + `user_…`, `cmt_…`, `evt_…`. Why: sequential IDs are enumerable; opaque IDs + are the stable handle to cache. A repository is addressable by ID as + `/repos/_/{repoId}`, which survives renames; there is no + `/repositories/{id}` route and no numeric ID anywhere. +- **camelCase JSON; 64-bit integers (PR numbers, version numbers) are JSON + strings; RFC 3339 timestamps.** +- **Defaults are present, not omitted.** `false`, `0`, `""`, `[]` appear in + bodies; only fields documented as optional are absent when unset. Do not + treat a missing key as the default and do not treat a present `false` as + "unset". +- **Pagination is `pageSize` / `pageToken` / `nextPageToken`.** Tokens are + opaque, bound to the resource and filters, and there is no `Link` header, + no `page`, no total count. Why: keyset cursors do not skip or duplicate + under concurrent writes; offsets do. Restart when filters change. +- **Errors are one envelope**: `google.rpc.Status` `{code, message, details}` + with typed `details` (`BadRequest` field violations, `RequestInfo` request + id on every error; further typed detail types such as machine-readable + `ErrorInfo` reasons may be added, so read the live Errors section and + tolerate unknown detail types). `X-Request-ID` is on every error. **`404` + never distinguishes not-found from no-access.** Why: anti-enumeration. + Branch on status and `code`; quote the request id when escalating. +- **No GraphQL.** REST only; decompose queries. +- **Rate limits are a per-principal point budget**, `X-RateLimit-*` plus + `Retry-After` on `429`; Git HTTPS meters separately (`X-RateLimit-Resource: + git`). Cursor can raise per-app budgets on request. +- **Public terminology is "pull request"** throughout; do not expect + "change" or "changeset" on the wire. + +## Resources GitHub has that Origin does not reproduce + +- **No commit statuses.** Check runs are the only status primitive: + `PostCheckRun` upserts on a caller-stable `key` (with `externalId` / + `externalUpdatedAt` for retries and ordering), and rulesets bind on check + keys. Why: one status model, idempotent by construction. A `statuses` + permission or `POST /statuses/{sha}` maps to checks, not to a gap. +- **No Issues.** Origin's conversation surfaces are pull requests, pull + request comments and threads, reviews, and labels on pull requests. An + `issues`-only GitHub App has no Origin equivalent for that part; say so + and ask what the team wants the PR-scoped behavior to be. GitHub's + `/issues/{n}/comments` used *on a PR* is just a path change. +- **No repository-level webhook CRUD** (`/repos/…/hooks`). Subscriptions are + an app-settings concern; there are no per-repo hook objects. +- **No app-manifest conversion endpoint and no OAuth-app token mints.** + Legacy GitHub mechanisms Origin does not reproduce. App creation is a + form (which accepts prefill query parameters) or the user-credential + `CreateApp` operation. +- **No REST git-object writes beyond the documented ones.** Commits are + pushed over Git HTTPS with an installation token (or created through the + documented commit-from-files and ref operations); do not expect a GitHub + Git Data API for arbitrary tree/blob writes. +- **No standalone threads API.** Comments are the only content write + surface; a thread materializes from its first diff-anchored comment and is + addressable for resolve/reopen. Why: one write surface, no dual bookkeeping. +- **No user/email directory.** Actors carry a TypeID (and a handle where the + contract exposes it); there is no lookup from email to user and no team + or member pages to link to. Why: an email or membership oracle. + +## Webhooks + +- **One message serves REST and webhooks.** A payload snapshots the one + object that changed exactly as its `Get…` returns it, plus compact + references (`repository`, `pullRequest`) for its containers. Why: consumers + reuse their REST decoders and never see a partially hydrated shape. +- **Webhooks notify; the API answers.** Payloads are lean by design: no + changed-file lists on pushes, no before-SHA on PR events, no web URLs, no + inlined user profiles. Follow-up reads (`GetCommit`, `CompareCommits`, + `ListComparisonFiles`, `GetRepo`, `GetPullRequest`) are the intended + pattern. Why: no emit-time joins, no size blowups, no staleness races. + Map fields the code reads to specific follow-up calls and count the + fan-out honestly; that count is the tradeoff, not a defect. +- **Slugs are `[.].`** and the action lives + in the slug (`pull_request.created`, `pull_request.review.submitted`, + `repository.check_run.completed`), never in a payload `action` field, and + there is no `previous_attributes` delta. Why: granular event types over + payload flags; one shape per event. GitHub's `pull_request` + + `action: synchronize` becomes `pull_request.head_ref.pushed`, and so on — + confirm each in `x-origin-webhook-events`. +- **Envelope**: `{deliveryId, appId, installationId, event: {id, type, + eventTime, payload}}`. `deliveryId` is stable across retries and is the + idempotency key; `event.id` identifies the domain event. Routing headers + are `webhook-id`, `webhook-timestamp`, `webhook-signature`, + `webhook-event-type`, `webhook-app-id`, `webhook-installation-id` (not + `x-github-*`); after verification the body is authoritative. +- **Signature is Ed25519 over a SHA-256 digest, scheme `v1ed`**, keys from + Origin's JWKS (rotated weekly, cache per `Cache-Control`), not an HMAC + shared secret. It tracks the Standard Webhooks spec except that it signs a + digest instead of the raw string, so off-the-shelf Standard Webhooks + verifiers do not validate it as-is. Verify the raw body before parsing; + reject timestamps more than five minutes off. +- **Installation lifecycle events are always delivered; every other event + must be selected in app settings.** Creating the app subscribes to nothing + else. Why: opt-in exposure. This is the second most common first-week + stall after mirror-vs-native. +- **Delivery is at-least-once with retries and a seven-day recovery window** + (`ListWebhookDeliveries`, `BatchRedeliverWebhookDeliveries`); persistent + failure pauses the app's delivery. + +## Checks, reviews, comments + +- **Check runs upsert on `key`**, live under a check suite keyed the same + way, and can be flagged re-requestable; a re-request arrives as an event + to the owning app. Annotations are appended, not replaced. +- **Reviews anchor to a pull request *version*, not a commit SHA.** Why: a + SHA can match several versions after a rebase or retarget. Code that keys + approvals by `commit_id` re-keys by version number. +- **Requested reviewers are addressed by identifier** (users, groups), and + Origin is deliberately careful about existence oracles on that surface. Do + not expect `created_via` or team pages on the read side. +- **Find your own writes by a key you control.** Check runs are found by + their `key`; for reviews and comments, carry a marker the app owns (a body + prefix, a stable key) rather than relying on actor identity alone, and + confirm in the spec which author filters the list operations offer. diff --git a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md new file mode 100644 index 000000000..d0dcf83f6 --- /dev/null +++ b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md @@ -0,0 +1,185 @@ +# Reading the Origin spec and matching GitHub surface to it + +The mapping in the brief is derived from the live `openapi.yaml`, not from a +table in this skill. This file explains what the spec carries and how to match +against it. Build the index once at the start of the run; every later step +looks things up in it. + +## What the spec carries + +`https://cursor.com/docs/api/origin/openapi.yaml` is OpenAPI 3.1 generated from +Origin's protobuf contract. Beyond the standard fields, four extensions matter +for porting: + +| Extension | Where | Meaning | +| --- | --- | --- | +| `x-origin-scopes` | every operation | `scopes`: the scope strings the operation requires. `tokenTypes`: which credentials it accepts (`app` JWT, `installation` token, `user`). `ambient: true`: the scopes come with the credential itself, so an app has nothing to request for it. | +| `x-origin-webhook-events` | payload schemas under `components.schemas` | The event slugs that deliver this payload shape. A schema carrying it is a webhook payload family; the slugs are the only authoritative list of subscribable events. | +| `x-origin-webhook-resource` | payload schemas, when present | Points at the REST component the payload embeds (the object a `Get…` operation returns). When absent, infer the embedded resource from the payload's `$ref` properties. Do not assume every payload has a REST twin; pushes, deletions, and reviewer requests are event-native. | +| `x-cursor-visibility: PREVIEW` | some operations | Published but badged preview. Carry the badge into the brief; a preview operation is usable, but tell the team the shape may still move. | + +Everything else you need is standard: `paths` with `operationId`, +`description`, `parameters`, request and response schemas, and `example` +blocks; `components.schemas` with field descriptions that are the contract +text itself. + +`llms-full.txt` adds what the spec cannot: the installation flow, the receipt +and app JWT, installation tokens and Git HTTPS, the scopes table, the mirrored +repositories rule, webhook headers, signature verification, the delivery +envelope, retries and recovery, pagination and error conventions, and the +current limitations list. Read those sections before mapping; several rows in +the brief (auth, receiver, pagination) map onto them rather than onto an +operation. + +## Build the index + +Parse the YAML and produce four lookups. Keep them in a scratch file; the +brief cites from them. + +1. **Operations**: for each path and method, record `operationId`, method, + path template, first sentence of `description`, `x-origin-scopes` + (`scopes`, `tokenTypes`, `ambient`), `x-cursor-visibility` if any, path and + query parameter names, request body top-level fields, and the response + component name. +2. **Webhook events**: for each schema with `x-origin-webhook-events`, record + each slug → schema name → the properties and their `$ref`s (or the + `x-origin-webhook-resource` target). Also read the Events table in + `llms-full.txt` § Webhooks reference for each slug's trigger sentence and + for which events are app-lifecycle (always delivered) versus repository + subscriptions (must be selected in app settings). +3. **Scopes**: the union of every `scopes` value seen in `x-origin-scopes`, + annotated with which operations require it, plus the Scopes table in + `llms-full.txt` for the human description and the `write` ⊃ `read` rule. + Separate the scopes an installation can request from those that are + ambient or user-credential-only; the latter tell you which GitHub flows + have no app-side equivalent by construction (for example, app creation and + installation-repository changes are admin actions on Origin, not app + calls). +4. **Resources**: component schemas returned by `Get…`/`List…` operations, + with their field names. Used to answer "which fields does the Origin object + carry" when mapping payload fields and response fields the code reads. + +## Matching rules + +Match in this order and stop at the first rule that yields a confirmed +counterpart. "Confirmed" means you read the Origin operation's description and +parameters and they answer the same question the GitHub call answers. A name +match is a candidate, never a result. + +### REST calls + +1. **Normalize the GitHub path** and look for the same shape under + `/v1/origin`: `/repos/{owner}/{repo}` → `/v1/origin/repos/{ownerSlug}/{repoName}`; + `{pull_number}` → `{pullNumber}`; `{ref}`/`{sha}` stay; GitHub's + `check-runs`, `check-suites`, `compare/{basehead}`, `contents`, `git/…`, + `pulls/{n}/{files,commits,reviews,comments,requested_reviewers,merge}`, + `labels`, `branches`, `commits` all have direct or near-direct shapes. + Custom-verb operations on Origin use a `:verb` suffix + (`…/contents:batchGet`, `…/check-runs:batchUpsert`, `…:grep`). +2. **Re-home GitHub's issue-flavored PR calls.** GitHub puts PR conversation + comments under `/issues/{n}/comments` and labels under `/issues/{n}/labels`; + on Origin those live under `/pulls/{n}/…`. This is a path change, not a + gap, when the code only ever uses them on pull requests. When the code + uses them on real issues, see `origin-isms.md` (issues are not an Origin + surface). +3. **Re-home app and installation calls.** GitHub's `/app`, + `/app/installations`, `/app/installations/{id}/access_tokens`, + `/installation/repositories` have Origin counterparts under `/v1/origin/app…` + and `/v1/origin/installation/repos`; check `tokenTypes` to confirm which + credential each takes. GitHub's `/user`, `/user/installations`, + `/orgs/{org}/…`, `/search/…`, and `/repositories/{id}` have no path + counterpart; consult `origin-isms.md` and the Authentication section + before deciding whether they are absent by design, covered by a different + idiom, or a gap. +4. **Compare parameters, not just paths.** A matching path with a missing + filter the code depends on (for example a list the code narrows by a field + Origin does not accept) is `workaround` or `gap`, not `same`. Read the + `parameters` block. +5. **Compare response fields the code reads.** Trace which response + properties the handlers dereference and check them against the Origin + component. Missing fields are common where GitHub inlines convenience data + (web URLs, nested user profiles, aggregate counts). Each missing field gets + its own line under the row: follow-up call, derivable, or absent. + +### GraphQL + +There is no GraphQL endpoint. Decompose each query or mutation into the REST +reads and writes it stands for, then map those individually. Note the fan-out +(one query → N calls) as the tradeoff on the row. If the query exists to +avoid REST pagination or to fetch a cross-repository view, say so; that is +the tradeoff the team weighs. + +### Permissions → scopes + +GitHub permissions are `: read|write`; Origin scopes are +`repository:[:]:` with `write` implying `read` +and `repository:metadata:read` granted automatically. Match the noun, then +verify by finding the operations the code actually calls in the Operations +lookup and reading *their* `x-origin-scopes` — the scope set in the brief is +the union of what the called operations require, not a translation of the +manifest. GitHub permissions with no Origin noun (`statuses`, `issues`, +`members`, `organization_*`, `pages`, `actions`, `workflows`, `secrets`, +`deployments`, `environments`) go through `origin-isms.md` before they can be +called gaps. + +### Webhook events → slugs + +Origin slugs are `[.].` with the +action in the slug, never in the payload: GitHub's single `pull_request` event +with an `action` field is several Origin events. Translate each +`event.action` pair the code handles into a candidate slug, then confirm the +slug appears in the Webhook events lookup. Candidates that do not appear are +not events on Origin; check whether the state change is observable another +way (an event on a related resource, or a REST read on receipt of one) before +classifying. Installation lifecycle events are always delivered to the app; +everything else must be subscribed explicitly, which the brief's hello-world +path calls out. + +### Payload fields → schema properties + +For each field path a handler reads off a GitHub payload +(`payload.pull_request.head.sha`, `payload.repository.full_name`, +`payload.sender.login`, `payload.commits[].added`), find the Origin payload +schema for the mapped slug and walk its properties. Record one of: + +- **present** at ``; +- **follow-up read**: not in the payload, available from `` using + identifiers the payload does carry; +- **derivable**: assembled from present fields (say how, and note whether the + format is contractual); +- **absent**: no payload field and no read that yields it (this row goes to + the gap bar). + +The payload carries references (`repository`, `pullRequest`) around the +snapshot of the one object that changed. Expect a lean shape and plan the +follow-up reads; that is the Origin design, not a defect +(`origin-isms.md` § Webhooks). Compare the webhook snapshot's schema with +the REST resource it mirrors: a field on the REST component that the webhook +twin lacks (for example a list the resource carries but the snapshot omits) +is a follow-up read via the matching `Get…`, and the code that reads it off +the GitHub payload today needs that read on every event. + +### Out-of-domain surfaces and spec-silent behavior + +Some of what a GitHub App touches is not source control at all: Marketplace +billing and plan lookups, merge queues, Actions, Pages, Projects, +Milestones, Discussions, `github.com` HTML probes. When neither the spec nor +`llms-full.txt` mentions the concept, the row is `unknown` with an up-front +question about what the team wants on Origin, never `gap` (Origin has not +declined it; the team may not need it) and never `by-design-absent` (only +`origin-isms.md` rows earn that). + +When the code depends on a behavior the docs do not state (does an event +fire for a pull request opened as a draft? does `updatedAt` move on a +comment? what does a remove return when nothing was there?), do not resolve +it from GitHub's behavior. Record the dependency in the row's Open question +column and add a step to the hello-world path that observes it on a native +repository. + +## Provenance in the brief + +Write the spec's `info.version`, the fetch timestamp, and the four URLs into +the brief's provenance block. State plainly that the brief reflects the +surface on that date and that the team should re-read the changelog before +acting on any row marked `workaround` or `gap`, because those are the rows +most likely to have moved. diff --git a/origin-apps/skills/port-github-app-to-origin/scripts/index-origin-spec.py b/origin-apps/skills/port-github-app-to-origin/scripts/index-origin-spec.py new file mode 100644 index 000000000..2bb1aa2aa --- /dev/null +++ b/origin-apps/skills/port-github-app-to-origin/scripts/index-origin-spec.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +"""Print a grep-friendly index of the Origin OpenAPI spec. + +Usage: + python3 index-origin-spec.py openapi.yaml # everything + python3 index-origin-spec.py openapi.yaml ops # operations only + python3 index-origin-spec.py openapi.yaml events # webhook payload families + python3 index-origin-spec.py openapi.yaml scopes # scope catalog + python3 index-origin-spec.py openapi.yaml schema PullRequest # one component + +Fetch the spec first: + curl -sSL https://cursor.com/docs/api/origin/openapi.yaml -o openapi.yaml + +Read-only. Needs PyYAML (`pip install pyyaml`). Everything printed comes from +the spec you pass in; nothing is pinned or embedded here. +""" + +import sys + +try: + import yaml +except ImportError: # pragma: no cover + sys.stderr.write( + "PyYAML is not installed. Run `pip install pyyaml`, or read the spec " + "directly (search for `operationId:`, `x-origin-scopes:`, and " + "`x-origin-webhook-events:`).\n" + ) + sys.exit(2) + +METHODS = ("get", "post", "put", "patch", "delete") + + +def ref_name(node): + if not isinstance(node, dict): + return None + if "$ref" in node: + return node["$ref"].rsplit("/", 1)[-1] + if "allOf" in node and node["allOf"] and "$ref" in node["allOf"][0]: + return node["allOf"][0]["$ref"].rsplit("/", 1)[-1] + if node.get("type") == "array": + inner = ref_name(node.get("items", {})) + return f"{inner}[]" if inner else "array" + return None + + +def first_sentence(text): + return " ".join((text or "").split()).split(". ")[0][:140] + + +def print_schema(components, name, depth=0, seen=None, max_depth=2): + seen = seen or set() + schema = components.get(name) + if not schema: + print(f"{' ' * depth}(no component named {name})") + return + for field, node in (schema.get("properties") or {}).items(): + kind = ref_name(node) or node.get("type", "?") + desc = first_sentence(node.get("description")) + print(f"{' ' * depth}{field}: {kind}" + (f" -- {desc}" if desc else "")) + inner = (ref_name(node) or "").rstrip("[]") + if inner and inner in components and depth < max_depth and inner not in seen: + seen.add(inner) + print_schema(components, inner, depth + 1, seen, max_depth) + + +def print_ops(spec): + print("== OPERATIONS (operationId | METHOD path | scopes | tokenTypes | ambient | visibility)") + for path, methods in spec["paths"].items(): + for method, op in methods.items(): + if method not in METHODS: + continue + xs = op.get("x-origin-scopes") or {} + params = [p["name"] for p in op.get("parameters", [])] + body = None + rb = op.get("requestBody") + if rb: + schema = rb["content"]["application/json"]["schema"] + body = ref_name(schema) or list((schema.get("properties") or {}).keys()) + resp = ( + op.get("responses", {}) + .get("200", {}) + .get("content", {}) + .get("application/json", {}) + .get("schema", {}) + ) + print( + f"{op.get('operationId')} | {method.upper()} {path} | " + f"scopes={xs.get('scopes')} tokenTypes={xs.get('tokenTypes')} " + f"ambient={xs.get('ambient')} visibility={op.get('x-cursor-visibility')}" + ) + print(f" params={params} body={body} -> {ref_name(resp) or '(empty)'}") + print(f" {first_sentence(op.get('description'))}") + + +def print_events(spec): + components = spec["components"]["schemas"] + print("== WEBHOOK PAYLOAD FAMILIES (schema | slugs | x-origin-webhook-resource)") + for name, schema in components.items(): + slugs = schema.get("x-origin-webhook-events") + if not slugs: + continue + print(f"{name} | {slugs} | resource={schema.get('x-origin-webhook-resource')}") + print_schema(components, name, depth=1, max_depth=2) + + +def print_scopes(spec): + print("== SCOPES (scope | tokenTypes seen | operations)") + table = {} + for path, methods in spec["paths"].items(): + for method, op in methods.items(): + if method not in METHODS: + continue + xs = op.get("x-origin-scopes") or {} + for scope in xs.get("scopes") or []: + entry = table.setdefault(scope, {"tokens": set(), "ops": [], "ambient": False}) + entry["tokens"].update(xs.get("tokenTypes") or []) + entry["ops"].append(op.get("operationId")) + entry["ambient"] = entry["ambient"] or bool(xs.get("ambient")) + for scope in sorted(table): + e = table[scope] + print(f"{scope} | tokenTypes={sorted(e['tokens'])} ambient={e['ambient']} | {e['ops']}") + + +def main(argv): + if len(argv) < 2: + print(__doc__) + return 1 + with open(argv[1], encoding="utf-8") as fh: + spec = yaml.safe_load(fh) + mode = argv[2] if len(argv) > 2 else "all" + print(f"# {spec['info'].get('title')} {spec['info'].get('version')}") + if mode == "schema": + print_schema(spec["components"]["schemas"], argv[3]) + return 0 + if mode in ("ops", "all"): + print_ops(spec) + if mode in ("events", "all"): + print_events(spec) + if mode in ("scopes", "all"): + print_scopes(spec) + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) From e0efb7a93e5942af772e4700a4ee6a2b6b26f52e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 23 Sep 2026 21:56:00 +0000 Subject: [PATCH 02/26] Add origin-api skill; porting skill builds on it origin-api is the general skill for anyone building on the Origin API: it points at the live docs and OpenAPI spec first and carries only the practices that hold across spec versions (credentials and token minting, minimal scopes from x-origin-scopes, webhook subscription, v1ed verification, idempotent handling and delivery behavior, opaque page tokens, TypeIDs, error envelope, rate limits, deliberate differences from GitHub). port-github-app-to-origin now defers to it for fundamentals and keeps only discovery, mapping, brief, and gap cards. Manifests, marketplace entries, README row, and CHANGELOG describe both skills with one identical description string. Co-authored-by: ali.nikseresht --- .claude-plugin/marketplace.json | 2 +- .cursor-plugin/marketplace.json | 2 +- README.md | 2 +- origin-apps/.claude-plugin/plugin.json | 5 +- origin-apps/.cursor-plugin/plugin.json | 10 +- origin-apps/CHANGELOG.md | 13 +- origin-apps/README.md | 85 +++++---- origin-apps/plugin.json | 5 +- origin-apps/skills/origin-api/SKILL.md | 173 ++++++++++++++++++ .../skills/port-github-app-to-origin/SKILL.md | 46 ++--- .../references/origin-isms.md | 3 +- 11 files changed, 268 insertions(+), 78 deletions(-) create mode 100644 origin-apps/skills/origin-api/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 0af83a862..505ac1ada 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -8,7 +8,7 @@ { "name": "origin-apps", "source": "./origin-apps", - "description": "Plan the port of an existing GitHub App to a Cursor Origin App: discover its GitHub surface, map it onto the live Origin API, write a porting brief." + "description": "Build on the Cursor Origin API: fetch the live spec first, then apply durable practices for credentials, scopes, webhooks, paging, and errors; includes a skill that plans the port of an existing GitHub App." } ] } diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index 556d6a143..484fedf2b 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -66,7 +66,7 @@ { "name": "origin-apps", "source": "origin-apps", - "description": "Plan the port of an existing GitHub App to a Cursor Origin App: discover its GitHub surface, map it onto the live Origin API, write a porting brief." + "description": "Build on the Cursor Origin API: fetch the live spec first, then apply durable practices for credentials, scopes, webhooks, paging, and errors; includes a skill that plans the port of an existing GitHub App." }, { "name": "orchestrate", diff --git a/README.md b/README.md index f29ea65ca..a6418fb7f 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Official Cursor plugins for popular developer tools, frameworks, and SaaS produc | `pr-review-canvas` | [PR Review Canvas](pr-review-canvas/) | Cursor | Developer Tools | Render PR diffs as review canvases grouped by importance. | | `docs-canvas` | [Docs Canvas](docs-canvas/) | Cursor | Developer Tools | Render documentation as a navigable canvas. | | `cursor-sdk` | [Cursor SDK](cursor-sdk/) | Cursor | Developer Tools | Build apps, scripts, and automations with the TypeScript SDK. | -| `origin-apps` | [Origin Apps](origin-apps/) | Cursor | Developer Tools | Plan the port of an existing GitHub App to a Cursor Origin App: discover its GitHub surface, map it onto the live Origin API, write a porting brief. | +| `origin-apps` | [Origin Apps](origin-apps/) | Cursor | Developer Tools | Build on the Cursor Origin API: fetch the live spec first, then apply durable practices for credentials, scopes, webhooks, paging, and errors; includes a skill that plans the port of an existing GitHub App. | | `orchestrate` | [Orchestrate](orchestrate/) | Cursor | Developer Tools | Fan large tasks out across parallel cloud agents with planners, workers, verifiers, and structured handoffs. | | `pstack` | [pstack](pstack/) | Lauren Tan | Developer Tools | if you want to go fast, go deep first. pstack helps you write less, but higher quality code. rigorous agent workflows you can parallelize with confidence. | | `advisor` | [Advisor](advisor/) | Cursor | Developer Tools | Consult a stronger model before major decisions, when stuck, and before declaring done. | diff --git a/origin-apps/.claude-plugin/plugin.json b/origin-apps/.claude-plugin/plugin.json index 9d1cb54b4..b09982da5 100644 --- a/origin-apps/.claude-plugin/plugin.json +++ b/origin-apps/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "origin-apps", - "description": "Plan the port of an existing GitHub App to a Cursor Origin App: discover the app's GitHub surface, map it onto the live Origin API, write a porting brief.", + "description": "Build on the Cursor Origin API: fetch the live spec first, then apply durable practices for credentials, scopes, webhooks, paging, and errors; includes a skill that plans the port of an existing GitHub App.", "version": "0.1.0", "author": { "name": "Cursor", @@ -12,8 +12,9 @@ "keywords": [ "origin", "origin-api", - "github-app", + "origin-app", "webhooks", + "github-app", "integration", "porting" ], diff --git a/origin-apps/.cursor-plugin/plugin.json b/origin-apps/.cursor-plugin/plugin.json index 0d130e52d..edd88eed6 100644 --- a/origin-apps/.cursor-plugin/plugin.json +++ b/origin-apps/.cursor-plugin/plugin.json @@ -2,7 +2,7 @@ "name": "origin-apps", "displayName": "Origin Apps", "version": "0.1.0", - "description": "Plan the port of an existing GitHub App to a Cursor Origin App: discover the app's GitHub surface, map it onto the live Origin API, write a porting brief.", + "description": "Build on the Cursor Origin API: fetch the live spec first, then apply durable practices for credentials, scopes, webhooks, paging, and errors; includes a skill that plans the port of an existing GitHub App.", "author": { "name": "Cursor", "email": "plugins@cursor.com" @@ -14,17 +14,19 @@ "cursor-plugin", "origin", "origin-api", - "github-app", + "origin-app", "webhooks", + "github-app", "integration", "porting" ], "category": "developer-tools", "tags": [ "origin", + "origin-api", + "webhooks", "github-app", - "integration", - "planning" + "integration" ], "skills": "./skills/" } diff --git a/origin-apps/CHANGELOG.md b/origin-apps/CHANGELOG.md index d5d9a2ec3..f7285b41c 100644 --- a/origin-apps/CHANGELOG.md +++ b/origin-apps/CHANGELOG.md @@ -4,7 +4,12 @@ All notable changes to this plugin will be documented here. ## 0.1.0 — initial release -- Added the `port-github-app-to-origin` skill: discover a GitHub App's surface - from its codebase, map it onto the live Origin API spec, and write a porting - brief with a capability table, webhook field map, hello-world path, gap cards, - and up-front questions. +- Added the `origin-api` skill: fetch the live Origin docs and OpenAPI spec + first, then apply the practices that hold across spec versions (credentials + and token minting, minimal scopes, webhook verification and idempotency, + opaque page tokens, TypeIDs, the error envelope, rate limits, and the + deliberate differences from GitHub). +- Added the `port-github-app-to-origin` skill, built on `origin-api`: discover + a GitHub App's surface from its codebase, map it onto the live Origin API + spec, and write a porting brief with a capability table, webhook field map, + hello-world path, gap cards, and up-front questions. diff --git a/origin-apps/README.md b/origin-apps/README.md index 8324e10b7..6e0ba0679 100644 --- a/origin-apps/README.md +++ b/origin-apps/README.md @@ -1,36 +1,49 @@ # Origin Apps -Plugin for teams bringing an existing GitHub App to -[Cursor Origin](https://cursor.com/docs/api/origin), Cursor's code forge. -Skills only, so it runs in Cursor, Claude Code, Codex, and any agent that -reads [Agent Skills](https://agentskills.io). +Plugin for building on [Cursor Origin](https://cursor.com/docs/api/origin), +Cursor's code forge: creating an Origin App, calling the API, receiving +webhooks, or bringing an existing GitHub App across. Skills only, so it runs +in Cursor, Claude Code, Codex, and any agent that reads +[Agent Skills](https://agentskills.io). ## What it includes -- `port-github-app-to-origin`: run it inside your GitHub App's repository with - no other instructions. It discovers the app's GitHub surface from the code - (manifest, permissions, events handled, payload fields read, REST and GraphQL - calls, token minting, webhook receiver, calls your framework makes for you), - fetches the live Origin API spec, and writes a porting brief: a capability - table, the webhook fields your handlers read and where each comes from on - Origin, the scopes to request, a hello-world path to your first real event, - gaps worth raising with Cursor, and the questions your team should settle - first. - -The skill plans; it does not write port code, pick a language or SDK, or -estimate in time. Mappings come from the Origin OpenAPI spec at run time, so the -brief tracks the API as published on the day you run it. +- `origin-api`: the general skill. Points the agent at the live docs and + OpenAPI spec first (the only source for endpoints, scopes, and event slugs), + then carries the practices that hold across spec versions: app, installation, + and user credentials and just-in-time token minting; minimal scopes derived + from `x-origin-scopes`; webhook subscription, `v1ed` signature verification, + idempotent handling, and delivery behavior; opaque page tokens; TypeIDs; the + error envelope; rate limits; and the deliberate differences from GitHub (no + commit statuses, Issues, or GraphQL). Use it for any Origin work. +- `port-github-app-to-origin`: builds on `origin-api` for one job. Run it + inside your GitHub App's repository with no other instructions. It discovers + the app's GitHub surface from the code (manifest, permissions, events + handled, payload fields read, REST and GraphQL calls, token minting, webhook + receiver, calls your framework makes for you), maps it onto the live spec, + and writes a porting brief: a capability table, the webhook fields your + handlers read and where each comes from on Origin, the scopes to request, a + hello-world path to your first real event, gaps worth raising with Cursor, + and the questions your team should settle first. It plans; it does not write + port code, pick a language or SDK, or estimate in time. + +Both skills fetch the spec at run time and refuse to name an endpoint from +memory, so their output tracks the API as published on the day you run them. ## When to use +- You are writing or reviewing code that calls Origin, mints installation + tokens, or receives Origin webhooks: `origin-api`. +- You are creating an Origin App and want the hello-world path and the + first-week traps up front: `origin-api`. - You have a GitHub App (Probot, Octokit, go-github, hand-rolled) and want to - know what an Origin App version looks like before you start. -- You are an agent working on such a team's behalf and need a grounded plan. + know what an Origin App version looks like before you start: + `port-github-app-to-origin`. - You want to check which GitHub features Origin deliberately does not - reproduce, and what the Origin idiom is instead. + reproduce, and what the Origin idiom is instead: either. -In Cursor, open the app's repository and ask to port it to Origin, or run -`/port-github-app-to-origin`. +In Cursor, ask about the Origin API or ask to port the app; or run +`/origin-api` or `/port-github-app-to-origin`. ## Install in Cursor @@ -44,7 +57,7 @@ scope. The plugin ships three manifests for one set of skills: a root `plugin.json` ([Agent Plugins](https://agent-plugins.org) 1.0), `.cursor-plugin/plugin.json` (Cursor Marketplace), and `.claude-plugin/plugin.json` (Claude Code). The -skill uses only portable frontmatter (`name`, `description`, `license`, +skills use only portable frontmatter (`name`, `description`, `license`, `compatibility`). **Claude Code**, via the marketplace manifest at this repository's root: @@ -55,32 +68,34 @@ skill uses only portable frontmatter (`name`, `description`, `license`, ``` **Any agent that reads Agent Skills** (Claude Code, Codex, and others): copy -the skill directory into the agent's skills folder. +the skill directories into the agent's skills folder. Copy both; the porting +skill refers to `origin-api` for fundamentals. ```bash git clone --depth 1 https://github.com/cursor/plugins.git # Claude Code -mkdir -p .claude/skills && cp -r plugins/origin-apps/skills/port-github-app-to-origin .claude/skills/ +mkdir -p .claude/skills && cp -r plugins/origin-apps/skills/* .claude/skills/ # Codex -mkdir -p .codex/skills && cp -r plugins/origin-apps/skills/port-github-app-to-origin .codex/skills/ +mkdir -p .codex/skills && cp -r plugins/origin-apps/skills/* .codex/skills/ # Cursor, without the marketplace -mkdir -p .cursor/skills && cp -r plugins/origin-apps/skills/port-github-app-to-origin .cursor/skills/ +mkdir -p .cursor/skills && cp -r plugins/origin-apps/skills/* .cursor/skills/ ``` ## Requirements - Network access to `https://cursor.com/docs/api/origin/*` during the run. -- Read access to the app's source. No Origin credentials are needed to produce - the brief; the hello-world path in the brief is what you follow afterwards. -- Optional: `python3` with PyYAML for `scripts/index-origin-spec.py`, which - turns the fetched spec into a grep-friendly index. Without it the skill - reads the spec directly. +- For the porting skill: read access to the app's source. No Origin + credentials are needed to produce the brief; the hello-world path in the + brief is what you follow afterwards. +- Optional: `python3` with PyYAML for the porting skill's + `scripts/index-origin-spec.py`, which turns the fetched spec into a + grep-friendly index. Without it the skill reads the spec directly. ## Where the brief goes -The skill writes `ORIGIN-PORTING-BRIEF.md` at the repository root and prints -its path. Gap cards in the brief are yours to send: through your shared Slack -channel with Cursor if you have one, or to `hi@cursor.com`. +The porting skill writes `ORIGIN-PORTING-BRIEF.md` at the repository root and +prints its path. Gap cards in the brief are yours to send: through your shared +Slack channel with Cursor if you have one, or to `hi@cursor.com`. ## License diff --git a/origin-apps/plugin.json b/origin-apps/plugin.json index a0d396b52..61a2f288b 100644 --- a/origin-apps/plugin.json +++ b/origin-apps/plugin.json @@ -2,7 +2,7 @@ "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", "name": "origin-apps", "version": "0.1.0", - "description": "Plan the port of an existing GitHub App to a Cursor Origin App: discover the app's GitHub surface, map it onto the live Origin API, write a porting brief.", + "description": "Build on the Cursor Origin API: fetch the live spec first, then apply durable practices for credentials, scopes, webhooks, paging, and errors; includes a skill that plans the port of an existing GitHub App.", "author": { "name": "Cursor", "email": "plugins@cursor.com" @@ -13,8 +13,9 @@ "keywords": [ "origin", "origin-api", - "github-app", + "origin-app", "webhooks", + "github-app", "integration", "porting" ] diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md new file mode 100644 index 000000000..21165aa4b --- /dev/null +++ b/origin-apps/skills/origin-api/SKILL.md @@ -0,0 +1,173 @@ +--- +name: origin-api +description: >- + Build on the Cursor Origin API: create an Origin App, authenticate as it, + call the REST API, receive webhooks. Use whenever code or a plan touches + Origin endpoints, installation tokens, scopes, webhook subscriptions or + signatures, page tokens, or an Origin App manifest. Points at the live docs + and spec first and carries only the practices that do not change between + spec versions. +license: MIT +compatibility: >- + Needs network access to https://cursor.com/docs/api/origin/* at run time. +--- + +# Build on the Origin API + +Origin is Cursor's code forge. Its API is GitHub-shaped (repos, pull requests, +reviews, comments, check runs, labels, branches, commits) and deliberately not +GitHub-compatible on the wire. This skill tells you where the contract lives +and what stays true across versions of it. It does not restate endpoints, +schemas, scope strings, or event slugs; those come from the spec you fetch. + +## Fetch the spec; do not trust memory + +Before naming any endpoint, scope, event slug, header, or field, fetch: + +- `https://cursor.com/docs/api/origin/llms.txt` — index of the docs +- `https://cursor.com/docs/api/origin/openapi.yaml` — the contract (OpenAPI + 3.1). Every operation carries `x-origin-scopes` (`scopes`, `tokenTypes`, + `ambient`); every webhook payload schema carries `x-origin-webhook-events` + (the slugs that deliver it) and often `x-origin-webhook-resource`; + `x-cursor-visibility: PREVIEW` marks operations whose shape may still move. +- `https://cursor.com/docs/api/origin/llms-full.txt` — the prose the spec + cannot carry: installation flow, authentication, scopes table, mirrored + repositories, webhook headers and signature verification, delivery + envelope, retries, pagination, errors, current limitations +- `https://cursor.com/docs/api/origin/changelog` — what moved recently + +Cite `operationId`s and `llms-full.txt` anchors in anything you write. Record +`info.version` and the fetch time when the output will outlive the session. +Where this file and the fetched docs disagree, the docs win. + +## Practices that hold across spec versions + +### Credentials + +- Three principals, in order of power: **user** credential (admin actions: + create the app, add repositories to an installation, mirror transitions), + **app** JWT (EdDSA over Ed25519; register only the public key; identifies + the app to app-level operations), **installation** token (`oit_…`, + short-lived, minted from the app JWT for one installation; the credential + for repository work). Check an operation's `x-origin-scopes.tokenTypes` to + see which it accepts. +- Mint installation tokens just in time and let them expire; never persist + one as a long-lived secret. Attenuate at mint time to the scopes and + `repositoryIds` the job needs (IDs, not slugs). +- The installation **receipt** JWT returned from the install redirect proves + consent and carries the installation ID. It is never a Bearer token. +- Git over HTTPS uses Basic auth with username `x-access-token` and an + installation token as the password; Bearer is for REST only. + +### Scopes + +- Scopes are `repository:[:]:`; `write` implies `read`; + `repository:metadata:read` comes with every installation. +- Request the union of `x-origin-scopes.scopes` across the operations you + actually call, nothing more. An installation can only narrow what the admin + approved, so an over-broad manifest is a review burden, not a convenience. +- `ambient: true` means the credential already carries the scope; there is + nothing to request for it. + +### Repositories + +- Apps act on Origin-native repositories. A repository mirrored *from* GitHub + is read-only to an installation (writes return `403`) and does not deliver + `repository.pushed`, because GitHub already notifies apps for it. Confirm + native-or-mirror before anything else; a mirror produces a successful ping + and then silence. + +### Webhooks + +- Only installation lifecycle events are delivered by default. Every other + event must be selected in app settings. Take the slug list from + `x-origin-webhook-events`, not from GitHub habit. +- Verify before parsing. Scheme `v1ed`: Ed25519 over a SHA-256 digest of the + raw body, keys from Origin's JWKS (cache per `Cache-Control`; keys rotate). + Reject `webhook-timestamp` more than five minutes off. It follows Standard + Webhooks except for signing a digest, so off-the-shelf verifiers do not + validate it unmodified. Routing headers are `webhook-*`, not `x-github-*`; + after verification the body is authoritative. +- Handle idempotently. `deliveryId` in the envelope is stable across retries + and is the dedupe key; `event.id` identifies the domain event. Delivery is + at-least-once. +- Acknowledge fast, process later. Retries follow a schedule and persistent + failure pauses delivery for the app; the recovery window is finite and + redelivery is through the deliveries operations, so a slow handler costs + you events. Return `2xx` after verification and enqueue. +- Payloads are lean snapshots of the one object that changed, plus compact + references to its containers. No changed-file lists, before-SHAs, web URLs, + or inlined profiles. The intended pattern is a follow-up `Get…` with the + identifiers the payload carries; count that fan-out when you design. +- The action lives in the slug (`pull_request.created`, + `pull_request.review.submitted`); there is no `action` field and no + `previous_attributes` delta. + +### Pagination + +- `pageSize` / `pageToken` / `nextPageToken`. Tokens are opaque and bound to + the resource and filters: never construct, parse, persist across filter + changes, or share them between requests with different parameters. +- Pass `pageSize` on every request, including continuations; do not rely on + the token to remember it. No `Link` header, no page numbers, no total count. + Loop until `nextPageToken` is absent or empty. + +### Identifiers and wire shape + +- IDs are opaque prefixed TypeIDs (`app_…`, `i_…`, `repo_…`, `user_…`, + `cmt_…`). Never integers, never derived. Cache IDs, not slugs; a repository + is addressable as `/repos/_/{repoId}`, which survives renames. +- camelCase JSON; 64-bit integers (PR numbers, versions) are JSON strings; + RFC 3339 timestamps; defaults are present (`false`, `0`, `""`, `[]`), so a + present `false` is a value and a missing key is only the default when the + field is documented optional. +- Public terminology is "pull request"; do not look for "change" on the wire. + +### Errors + +- One envelope: `google.rpc.Status` `{code, message, details}` with typed + `details` (`BadRequest` field violations, `RequestInfo` with the request + id; more types may appear, so tolerate unknown ones). `X-Request-ID` is on + every error; quote it when escalating. +- `404` does not distinguish not-found from no-access. Branch on HTTP status + and `code`, never on message text. + +### Rate limits + +- A per-principal point budget: read `X-RateLimit-*` on every response and + honor `Retry-After` on `429`. Git over HTTPS is metered separately + (`X-RateLimit-Resource: git`). Cursor raises per-app budgets on request; + ask rather than spinning. + +### Deliberate differences from GitHub + +Design decisions, not gaps. Build the Origin idiom instead of emulating the +GitHub one: + +- **No commit statuses.** Check runs are the one status primitive; they upsert + on a caller-stable `key` and rulesets bind on that key. +- **No Issues.** Conversation is pull request comments, threads, reviews, and + labels on pull requests. +- **No GraphQL.** REST only; decompose queries and accept the fan-out. +- **No per-repository webhook CRUD.** Subscriptions are app settings. +- **No user or email directory**, no team pages; actors are TypeIDs (plus a + handle where the contract exposes it). +- **Reviews anchor to a pull request version**, not a commit SHA. +- **No standalone threads API.** A thread materializes from its first + diff-anchored comment. + +## Hello-world path + +Create the app (form or user-credential `CreateApp`) → register the Ed25519 +public key → select webhook events → install on an Origin-native repository → +verify the ping signature → mint an installation token → first `Get…` → first +real event. Each arrow is a step to observe, not code to write; when a step +is silent, the answer is almost always native-or-mirror or an unsubscribed +event. + +## Related skill + +`port-github-app-to-origin`, in this plugin, applies these fundamentals to +an existing GitHub App: it discovers the app's GitHub surface from code, maps +it onto the spec, and writes a porting brief. Use it for a port; use this +skill for everything else on Origin. diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index 566f37258..08083651e 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -23,40 +23,31 @@ you. The team keeps its language, framework, and client strategy; the brief tells them what maps, what changes shape, what is absent on purpose, and what is worth raising with Cursor. -Three rules shape everything below: - -1. **The live Origin docs are the source of truth.** Fetch them at run time - and derive every mapping from them. Nothing in this skill pins a version or - enumerates endpoints; the `references/` files explain *how to read* the spec - and *why* Origin differs, and any concrete example in them is illustrative - until you have confirmed it against today's spec. -2. **Discover, do not ask.** Read the manifest, permission declarations, event +Fundamentals come from the `origin-api` skill in this plugin: which docs to +fetch, credentials and token minting, scopes, webhook verification and +idempotency, pagination, TypeIDs, errors, rate limits, and the list of +deliberate departures from GitHub. Follow it first; this skill adds only what +a port needs on top. Two rules shape the porting work: + +1. **Discover, do not ask.** Read the manifest, permission declarations, event handlers, token minting, API calls, and webhook receiver out of the code. Never ask anyone to paste a manifest or list their endpoints. If something is genuinely undiscoverable, record it as an open question in the brief. -3. **Origin is GitHub-shaped, not GitHub-compatible.** Many differences are - decisions, not omissions. Classify them as such and guide the port toward - the Origin idiom instead of reproducing the GitHub one. +2. **Classify departures as decisions, not omissions.** A GitHub feature that + Origin deliberately does not reproduce is `by-design-absent` with a pointer + to the Origin idiom, never a gap card. Nothing in this skill pins a spec + version or enumerates endpoints; every concrete example in `references/` + is illustrative until confirmed against today's spec. ## Procedure ### 1. Load the live Origin surface -Fetch, in this order, and keep them open for the rest of the run: - -- `https://cursor.com/docs/api/origin/llms.txt` (index of everything below) -- `https://cursor.com/docs/api/origin/openapi.yaml` (the contract; every - mapping in the brief cites an `operationId` or a payload schema from it) -- `https://cursor.com/docs/api/origin/llms-full.txt` (the human reference: - installation, authentication, scopes, mirrored repositories, webhooks, - conventions, current limitations) -- `https://cursor.com/docs/api/origin/changelog` (what moved recently) - -Record `info.version` and the fetch time in the brief's provenance block. That -is provenance, not a dependency: the brief describes the API as it is today and -says so. `references/spec-mapping.md` explains the spec's `x-origin-scopes`, -`x-origin-webhook-events`, `x-origin-webhook-resource`, and -`x-cursor-visibility` extensions and how to build the mapping index from them. +Fetch the four URLs from `origin-api` § Fetch the spec and keep them open for +the run. Record `info.version` and the fetch time in the brief's provenance +block; that is provenance, not a dependency, and the brief says so. +`references/spec-mapping.md` explains how to turn the spec's `x-origin-*` +extensions into the mapping index every later step looks things up in. `scripts/index-origin-spec.py ` prints that index (operations with scopes, parameters, and response fields; webhook slugs with payload fields; the scope catalog) so you can grep it instead of paging through 700 KB @@ -126,9 +117,10 @@ real events at all. | File | Read when | | --- | --- | +| `../origin-api/SKILL.md` | Always, first: the docs to fetch and the fundamentals every mapping row assumes. | | `references/spec-mapping.md` | Building the spec index and matching GitHub calls, events, and payload fields to Origin operations, slugs, and schemas. | | `references/discovery.md` | Scanning the codebase for the app's GitHub surface. | -| `references/origin-isms.md` | Deciding whether a missing GitHub feature is a decision or a gap, and what the Origin idiom is. | +| `references/origin-isms.md` | Deciding whether a missing GitHub feature is a decision or a gap, and what the Origin idiom is, with the reasoning a team that only knows GitHub needs. | | `references/gap-bar.md` | Deciding whether a gap is worth raising with Cursor, and writing the card. | | `references/brief-template.md` | Writing the output. | | `scripts/index-origin-spec.py` | Turning the fetched `openapi.yaml` into a grep-friendly index (operations, webhook families, scopes, one component). Optional; needs PyYAML. | diff --git a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md index d81f7226b..112c988ca 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md +++ b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md @@ -7,7 +7,8 @@ the brief, and the brief's job is to point the team at the Origin idiom. Each entry gives the departure, the idiom, and the reason in one line, so you can explain it to a team that has only ever seen GitHub. Confirm the current wording in `llms-full.txt`; this file explains intent, the docs state the -contract. +contract. The `origin-api` skill carries the same conventions as practices +for building; this file carries them as parity decisions for a port. ## Which repositories an app can act on From a2f9147c01bee1713b7dd676ffcbb911e8230f43 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 23 Sep 2026 22:16:29 +0000 Subject: [PATCH 03/26] Slim both skills to their behavior-changing core origin-api becomes a fetch-first source list plus a gotcha checklist (173 -> 80 lines). The porting skill keeps the discover -> fetch -> map -> brief workflow and the gap-card rules; references drop ecosystem grep lists, path examples, calibration examples, and everything that restated the spec or origin-api. origin-isms is now a classification table (GitHub surface -> label -> Origin idiom). 1231 -> 567 prose lines. Co-authored-by: ali.nikseresht --- origin-apps/README.md | 38 ++- origin-apps/skills/origin-api/SKILL.md | 227 +++++---------- .../skills/port-github-app-to-origin/SKILL.md | 146 ++++------ .../references/brief-template.md | 215 ++++++--------- .../references/discovery.md | 199 ++++--------- .../references/gap-bar.md | 173 ++++-------- .../references/origin-isms.md | 207 +++----------- .../references/spec-mapping.md | 261 ++++++------------ 8 files changed, 450 insertions(+), 1016 deletions(-) diff --git a/origin-apps/README.md b/origin-apps/README.md index 6e0ba0679..c6e4f05f7 100644 --- a/origin-apps/README.md +++ b/origin-apps/README.md @@ -8,27 +8,23 @@ in Cursor, Claude Code, Codex, and any agent that reads ## What it includes -- `origin-api`: the general skill. Points the agent at the live docs and - OpenAPI spec first (the only source for endpoints, scopes, and event slugs), - then carries the practices that hold across spec versions: app, installation, - and user credentials and just-in-time token minting; minimal scopes derived - from `x-origin-scopes`; webhook subscription, `v1ed` signature verification, - idempotent handling, and delivery behavior; opaque page tokens; TypeIDs; the - error envelope; rate limits; and the deliberate differences from GitHub (no - commit statuses, Issues, or GraphQL). Use it for any Origin work. -- `port-github-app-to-origin`: builds on `origin-api` for one job. Run it - inside your GitHub App's repository with no other instructions. It discovers - the app's GitHub surface from the code (manifest, permissions, events - handled, payload fields read, REST and GraphQL calls, token minting, webhook - receiver, calls your framework makes for you), maps it onto the live spec, - and writes a porting brief: a capability table, the webhook fields your - handlers read and where each comes from on Origin, the scopes to request, a - hello-world path to your first real event, gaps worth raising with Cursor, - and the questions your team should settle first. It plans; it does not write - port code, pick a language or SDK, or estimate in time. - -Both skills fetch the spec at run time and refuse to name an endpoint from -memory, so their output tracks the API as published on the day you run them. +- `origin-api`: the general skill. Fetch-first sources (the live OpenAPI spec + and docs are the only source for endpoints, scopes, and event slugs) plus a + checklist of the Origin gotchas GitHub instinct gets wrong: native vs + mirrored repositories, opt-in webhook events, `v1ed` signatures, `deliveryId` + idempotency, lean payloads, credential kinds and token minting, scopes from + `x-origin-scopes`, opaque page tokens, TypeIDs, `404` semantics, rate + limits, and the deliberate differences from GitHub. Use it for any Origin + work. +- `port-github-app-to-origin`: builds on `origin-api`. Run it inside your + GitHub App's repository. It discovers the app's GitHub surface from the code, + maps it onto the live spec, and writes a porting brief: capability table, + webhook fields your handlers read and where each comes from on Origin, + scopes to request, hello-world path, gaps worth raising with Cursor, and the + questions to settle first. It plans; it writes no code and estimates no + time. + +Both fetch the spec at run time and never name an endpoint from memory. ## When to use diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md index 21165aa4b..d670dc84c 100644 --- a/origin-apps/skills/origin-api/SKILL.md +++ b/origin-apps/skills/origin-api/SKILL.md @@ -4,9 +4,8 @@ description: >- Build on the Cursor Origin API: create an Origin App, authenticate as it, call the REST API, receive webhooks. Use whenever code or a plan touches Origin endpoints, installation tokens, scopes, webhook subscriptions or - signatures, page tokens, or an Origin App manifest. Points at the live docs - and spec first and carries only the practices that do not change between - spec versions. + signatures, page tokens, or an Origin App manifest. Fetch-first sources plus + the gotchas GitHub instinct gets wrong. license: MIT compatibility: >- Needs network access to https://cursor.com/docs/api/origin/* at run time. @@ -14,160 +13,68 @@ compatibility: >- # Build on the Origin API -Origin is Cursor's code forge. Its API is GitHub-shaped (repos, pull requests, -reviews, comments, check runs, labels, branches, commits) and deliberately not -GitHub-compatible on the wire. This skill tells you where the contract lives -and what stays true across versions of it. It does not restate endpoints, -schemas, scope strings, or event slugs; those come from the spec you fetch. - -## Fetch the spec; do not trust memory - -Before naming any endpoint, scope, event slug, header, or field, fetch: - -- `https://cursor.com/docs/api/origin/llms.txt` — index of the docs -- `https://cursor.com/docs/api/origin/openapi.yaml` — the contract (OpenAPI - 3.1). Every operation carries `x-origin-scopes` (`scopes`, `tokenTypes`, - `ambient`); every webhook payload schema carries `x-origin-webhook-events` - (the slugs that deliver it) and often `x-origin-webhook-resource`; - `x-cursor-visibility: PREVIEW` marks operations whose shape may still move. -- `https://cursor.com/docs/api/origin/llms-full.txt` — the prose the spec - cannot carry: installation flow, authentication, scopes table, mirrored - repositories, webhook headers and signature verification, delivery - envelope, retries, pagination, errors, current limitations -- `https://cursor.com/docs/api/origin/changelog` — what moved recently - -Cite `operationId`s and `llms-full.txt` anchors in anything you write. Record -`info.version` and the fetch time when the output will outlive the session. -Where this file and the fetched docs disagree, the docs win. - -## Practices that hold across spec versions - -### Credentials - -- Three principals, in order of power: **user** credential (admin actions: - create the app, add repositories to an installation, mirror transitions), - **app** JWT (EdDSA over Ed25519; register only the public key; identifies - the app to app-level operations), **installation** token (`oit_…`, - short-lived, minted from the app JWT for one installation; the credential - for repository work). Check an operation's `x-origin-scopes.tokenTypes` to - see which it accepts. -- Mint installation tokens just in time and let them expire; never persist - one as a long-lived secret. Attenuate at mint time to the scopes and - `repositoryIds` the job needs (IDs, not slugs). -- The installation **receipt** JWT returned from the install redirect proves - consent and carries the installation ID. It is never a Bearer token. -- Git over HTTPS uses Basic auth with username `x-access-token` and an - installation token as the password; Bearer is for REST only. - -### Scopes - -- Scopes are `repository:[:]:`; `write` implies `read`; - `repository:metadata:read` comes with every installation. -- Request the union of `x-origin-scopes.scopes` across the operations you - actually call, nothing more. An installation can only narrow what the admin - approved, so an over-broad manifest is a review burden, not a convenience. -- `ambient: true` means the credential already carries the scope; there is - nothing to request for it. - -### Repositories - -- Apps act on Origin-native repositories. A repository mirrored *from* GitHub - is read-only to an installation (writes return `403`) and does not deliver - `repository.pushed`, because GitHub already notifies apps for it. Confirm - native-or-mirror before anything else; a mirror produces a successful ping - and then silence. - -### Webhooks - -- Only installation lifecycle events are delivered by default. Every other - event must be selected in app settings. Take the slug list from - `x-origin-webhook-events`, not from GitHub habit. -- Verify before parsing. Scheme `v1ed`: Ed25519 over a SHA-256 digest of the - raw body, keys from Origin's JWKS (cache per `Cache-Control`; keys rotate). - Reject `webhook-timestamp` more than five minutes off. It follows Standard - Webhooks except for signing a digest, so off-the-shelf verifiers do not - validate it unmodified. Routing headers are `webhook-*`, not `x-github-*`; - after verification the body is authoritative. -- Handle idempotently. `deliveryId` in the envelope is stable across retries - and is the dedupe key; `event.id` identifies the domain event. Delivery is - at-least-once. -- Acknowledge fast, process later. Retries follow a schedule and persistent - failure pauses delivery for the app; the recovery window is finite and - redelivery is through the deliveries operations, so a slow handler costs - you events. Return `2xx` after verification and enqueue. -- Payloads are lean snapshots of the one object that changed, plus compact - references to its containers. No changed-file lists, before-SHAs, web URLs, - or inlined profiles. The intended pattern is a follow-up `Get…` with the - identifiers the payload carries; count that fan-out when you design. -- The action lives in the slug (`pull_request.created`, - `pull_request.review.submitted`); there is no `action` field and no - `previous_attributes` delta. - -### Pagination - -- `pageSize` / `pageToken` / `nextPageToken`. Tokens are opaque and bound to - the resource and filters: never construct, parse, persist across filter - changes, or share them between requests with different parameters. -- Pass `pageSize` on every request, including continuations; do not rely on - the token to remember it. No `Link` header, no page numbers, no total count. - Loop until `nextPageToken` is absent or empty. - -### Identifiers and wire shape - -- IDs are opaque prefixed TypeIDs (`app_…`, `i_…`, `repo_…`, `user_…`, - `cmt_…`). Never integers, never derived. Cache IDs, not slugs; a repository - is addressable as `/repos/_/{repoId}`, which survives renames. -- camelCase JSON; 64-bit integers (PR numbers, versions) are JSON strings; - RFC 3339 timestamps; defaults are present (`false`, `0`, `""`, `[]`), so a - present `false` is a value and a missing key is only the default when the - field is documented optional. -- Public terminology is "pull request"; do not look for "change" on the wire. - -### Errors - -- One envelope: `google.rpc.Status` `{code, message, details}` with typed - `details` (`BadRequest` field violations, `RequestInfo` with the request - id; more types may appear, so tolerate unknown ones). `X-Request-ID` is on - every error; quote it when escalating. -- `404` does not distinguish not-found from no-access. Branch on HTTP status - and `code`, never on message text. - -### Rate limits - -- A per-principal point budget: read `X-RateLimit-*` on every response and - honor `Retry-After` on `429`. Git over HTTPS is metered separately - (`X-RateLimit-Resource: git`). Cursor raises per-app budgets on request; - ask rather than spinning. - -### Deliberate differences from GitHub - -Design decisions, not gaps. Build the Origin idiom instead of emulating the -GitHub one: - -- **No commit statuses.** Check runs are the one status primitive; they upsert - on a caller-stable `key` and rulesets bind on that key. -- **No Issues.** Conversation is pull request comments, threads, reviews, and - labels on pull requests. -- **No GraphQL.** REST only; decompose queries and accept the fan-out. -- **No per-repository webhook CRUD.** Subscriptions are app settings. -- **No user or email directory**, no team pages; actors are TypeIDs (plus a - handle where the contract exposes it). -- **Reviews anchor to a pull request version**, not a commit SHA. -- **No standalone threads API.** A thread materializes from its first - diff-anchored comment. - -## Hello-world path - -Create the app (form or user-credential `CreateApp`) → register the Ed25519 -public key → select webhook events → install on an Origin-native repository → -verify the ping signature → mint an installation token → first `Get…` → first -real event. Each arrow is a step to observe, not code to write; when a step -is silent, the answer is almost always native-or-mirror or an unsubscribed -event. - -## Related skill - -`port-github-app-to-origin`, in this plugin, applies these fundamentals to -an existing GitHub App: it discovers the app's GitHub surface from code, maps -it onto the spec, and writes a porting brief. Use it for a port; use this -skill for everything else on Origin. +## Fetch first; never name an endpoint, scope, slug, or header from memory + +- `https://cursor.com/docs/api/origin/openapi.yaml` — the contract. Every + operation carries `x-origin-scopes` (`scopes`, `tokenTypes`, `ambient`); + every webhook payload schema carries `x-origin-webhook-events`, the only + authoritative list of subscribable slugs. +- `https://cursor.com/docs/api/origin/llms-full.txt` — installation flow, + authentication, scopes table, mirrored repositories, webhook headers and + verification, delivery envelope, retries, pagination, errors, limitations. +- `https://cursor.com/docs/api/origin/llms.txt` (index) and + `https://cursor.com/docs/api/origin/changelog` (what moved). + +Cite `operationId`s and `llms-full.txt` anchors. Where this file and the +fetched docs disagree, the docs win. + +## Gotchas + +- **Native or mirror, first.** Apps get full scopes only on Origin-native + repositories. A repository mirrored from GitHub returns `403` on every write + and never delivers `repository.pushed`; the ping succeeds and then nothing + else arrives. +- **Only installation lifecycle events are delivered by default.** Select + every other event in app settings; an unselected event is silence, not an + error. +- **Signature `v1ed` is Ed25519 over a SHA-256 digest of the raw body**, keys + from Origin's JWKS. It is Standard Webhooks except for the digest, so + off-the-shelf verifiers fail unmodified. Verify the raw body before parsing; + reject `webhook-timestamp` more than five minutes off. Headers are + `webhook-*`, not `x-github-*`; after verification the body is authoritative. +- **`deliveryId` is the idempotency key** (stable across retries); + `event.id` is the domain event. Return `2xx` after verification and process + asynchronously: persistent failure pauses delivery for the app. +- **Payloads are lean snapshots** of the one object that changed plus + container references: no changed-file lists, before-SHAs, web URLs, or + inlined profiles. Follow up with the `Get…` for the object; count the + fan-out. The action is in the slug (`pull_request.review.submitted`); there + is no `action` field. +- **The installation receipt JWT is proof of consent, never a Bearer token.** + Its `sub` is the installation ID. +- **App JWT is EdDSA over Ed25519, not RS256.** Register only the public key. +- **Installation tokens are short-lived; mint just in time** from the app JWT + and attenuate to the scopes and `repositoryIds` the job needs (IDs, not + slugs). Git over HTTPS is Basic auth, user `x-access-token`, token as + password; Bearer is REST only. +- **Scopes come from the operations you call**: request the union of their + `x-origin-scopes.scopes`. `write` implies `read`; `repository:metadata:read` + is automatic; `ambient: true` needs no request. Operations whose + `tokenTypes` is user-only (create app, add repositories to an installation, + mirror transitions) have no app-side path; there is no `/user` analog. +- **Page tokens are opaque and bound to the resource and filters.** Never + construct, parse, or reuse across filter changes. Send `pageSize` on every + request, including continuations. No `Link` header, no total. +- **IDs are TypeIDs** (`repo_…`, `i_…`, `cmt_…`), never integers. Cache IDs, + not slugs; `/repos/_/{repoId}` survives renames. 64-bit integers (PR + numbers, versions) are JSON strings. Defaults are present (`false`, `0`, + `[]`), so a present `false` is a value. +- **`404` is not-found *or* no-access.** Branch on status and `code`, never + message text. Quote `X-Request-ID` when escalating. +- **Rate limit is a per-principal point budget**; honor `Retry-After` on + `429`. Git HTTPS is metered separately. Per-app raises exist; ask. +- **Deliberate differences, not gaps:** no commit statuses (check runs upsert + on a caller-stable `key`); no Issues (conversation is PR comments, threads, + reviews, labels); no GraphQL; no per-repository webhook CRUD; no user or + email directory; reviews anchor to a pull request *version*, not a SHA; a + thread materializes from its first diff-anchored comment. diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index 08083651e..dbccfa728 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -15,112 +15,62 @@ compatibility: >- # Port a GitHub App to an Origin App -Run this inside the codebase of an existing GitHub App, with no other -instructions needed. The output is a **porting brief** -(`references/brief-template.md`), not an implementation. "The team" below -means the people who own this app; if you are running this yourself, that is -you. The team keeps its language, framework, and client strategy; the brief -tells them what maps, what changes shape, what is absent on purpose, and what -is worth raising with Cursor. +Run inside the GitHub App's codebase. Output is a **porting brief** +(`references/brief-template.md`), not an implementation: what maps, what +changes shape, what is absent on purpose, what is worth raising with Cursor. -Fundamentals come from the `origin-api` skill in this plugin: which docs to -fetch, credentials and token minting, scopes, webhook verification and -idempotency, pagination, TypeIDs, errors, rate limits, and the list of -deliberate departures from GitHub. Follow it first; this skill adds only what -a port needs on top. Two rules shape the porting work: +Fundamentals (which docs to fetch, credentials, scopes, webhooks, paging, +IDs, errors, deliberate differences) are the `origin-api` skill in this +plugin. Follow it first; nothing here repeats it. Two rules on top: -1. **Discover, do not ask.** Read the manifest, permission declarations, event - handlers, token minting, API calls, and webhook receiver out of the code. - Never ask anyone to paste a manifest or list their endpoints. If something - is genuinely undiscoverable, record it as an open question in the brief. -2. **Classify departures as decisions, not omissions.** A GitHub feature that - Origin deliberately does not reproduce is `by-design-absent` with a pointer - to the Origin idiom, never a gap card. Nothing in this skill pins a spec - version or enumerates endpoints; every concrete example in `references/` - is illustrative until confirmed against today's spec. +1. **Discover, do not ask.** Read permissions, events, handlers, calls, token + minting, and the receiver out of the code. Never ask for a manifest or an + endpoint list. What is genuinely undiscoverable becomes an open question. +2. **Departures are decisions, not omissions.** Anything in + `references/origin-isms.md` is `by-design-absent` or `reshaped` with a + pointer to the idiom, never a gap card. ## Procedure -### 1. Load the live Origin surface - -Fetch the four URLs from `origin-api` § Fetch the spec and keep them open for -the run. Record `info.version` and the fetch time in the brief's provenance -block; that is provenance, not a dependency, and the brief says so. -`references/spec-mapping.md` explains how to turn the spec's `x-origin-*` -extensions into the mapping index every later step looks things up in. -`scripts/index-origin-spec.py ` prints that index (operations -with scopes, parameters, and response fields; webhook slugs with payload -fields; the scope catalog) so you can grep it instead of paging through 700 KB -of YAML; it needs python3 with PyYAML and does nothing else. - -### 2. Discover the GitHub App's shape - -Follow `references/discovery.md`. Produce an inventory with a file and line for -every fact: declared permissions and events, webhook events handled, payload -fields the handlers read, REST and GraphQL calls, authentication flow, webhook -receiver and signature verification, calls made on the app's behalf by its -framework and helper libraries, and the observed language and client -libraries (observed, never chosen). Note what you looked for and did not find. - -### 3. Map each capability onto Origin - -For each inventory row, look up the Origin counterpart in the spec index built -in step 1 (`references/spec-mapping.md` § Matching rules), then classify it -with one of the parity labels in `references/brief-template.md`. Before -labeling anything `gap`, check `references/origin-isms.md`: a GitHub feature -that Origin deliberately does not reproduce is `by-design-absent` with a -pointer to the Origin idiom, and the brief must say what to do instead rather -than raise it. Then apply the bar in `references/gap-bar.md`; only rows that -clear it become gap cards. - -Map webhook payload *fields* the code reads, not just event names. Origin -payloads are lean row snapshots; a field GitHub inlines is often a follow-up -REST read on Origin. Say which call, per field. - -Two labels are easy to misuse. A GitHub surface with no Origin counterpart -*and* no mention anywhere in the Origin docs (Marketplace billing, merge -queues, Actions, Pages) is `unknown` with a question, not a `gap`: Origin has -not said no, and the team may not need it. A behavior the code depends on -that the docs neither confirm nor deny (does event X fire in case Y? does -`updatedAt` move on comments?) is also a question, plus a step on the -hello-world path to observe it; never guess it into `same`. - -### 4. Write the brief - -Fill `references/brief-template.md` in full. Every table cell that names an -Origin operation, event, or field links to its anchor in `llms-full.txt` or -names its `operationId`. Sizes are S/M/L as defined in the template, never -time. The hello-world path is the sequence Create App → subscribe events → -install → verify ping signature → first real event on a native repository; -it is a checklist of things to verify, not code. - -### 5. Ask the up-front questions - -Close the brief with the questions in the template's final section, pruned to -what the discovery left open and extended with anything specific you found. -The first question is always whether the target repositories are Origin-native -or mirrored from GitHub, because that decides whether the app will receive -real events at all. - -## What this skill does not do - -- Write, scaffold, or vibecode port code, adapters, or SDK wrappers. -- Choose a language, framework, HTTP client, or codegen strategy. -- Estimate effort in hours, days, or sprints. -- Pin the spec version, copy endpoint lists into the brief from memory, or - claim parity from a name match without reading the operation. -- Ask for information the codebase already contains. -- Contact Cursor on the team's behalf; the brief carries the escalation cards - and the team decides what to send and where. +1. **Load the spec** (`origin-api` § Fetch first). Record `info.version` and + the fetch time for the brief's provenance. Build the mapping index per + `references/spec-mapping.md`; `scripts/index-origin-spec.py openapi.yaml` + prints it (needs PyYAML). +2. **Discover** per `references/discovery.md`: a file and line for every fact, + including payload fields read only for logging and calls the framework + makes on the app's behalf. Note what you looked for and did not find. +3. **Map** each inventory row (`references/spec-mapping.md` § Matching) and + label it with the parity labels in the brief template. Map webhook payload + *fields* the code reads, not just event names; a field GitHub inlines is + often a follow-up read on Origin, so name the call per field. Then: + - `origin-isms.md` before `gap`; `gap-bar.md` before any card. + - A GitHub surface the Origin docs never mention (Marketplace billing, + merge queues, Actions, Pages) is `unknown` with a question, never `gap`. + - A behavior the code depends on that the docs neither confirm nor deny + (does event X fire in case Y? does `updatedAt` move on comments?) is a + question plus a hello-world step that observes it; never guess it into + `same` from GitHub behavior. +4. **Write the brief** from the template in full. Every Origin cell names an + `operationId`, slug, or `llms-full.txt` anchor. Sizes are S/M/L, never + time. +5. **Close with the questions**, pruned to what discovery left open. The + first is always native-or-mirror: it decides whether the app receives + events at all. + +## Not in scope + +Writing port code or adapters; choosing a language, framework, or client; +estimating in time; asking for anything the codebase contains; sending gap +cards to Cursor (the brief carries them; the team decides). ## Reference files | File | Read when | | --- | --- | -| `../origin-api/SKILL.md` | Always, first: the docs to fetch and the fundamentals every mapping row assumes. | -| `references/spec-mapping.md` | Building the spec index and matching GitHub calls, events, and payload fields to Origin operations, slugs, and schemas. | -| `references/discovery.md` | Scanning the codebase for the app's GitHub surface. | -| `references/origin-isms.md` | Deciding whether a missing GitHub feature is a decision or a gap, and what the Origin idiom is, with the reasoning a team that only knows GitHub needs. | -| `references/gap-bar.md` | Deciding whether a gap is worth raising with Cursor, and writing the card. | +| `../origin-api/SKILL.md` | First. Sources and fundamentals. | +| `references/discovery.md` | Scanning the codebase. | +| `references/spec-mapping.md` | Building the index; matching calls, events, and fields. | +| `references/origin-isms.md` | Labeling a missing GitHub feature. | +| `references/gap-bar.md` | Deciding whether a difference earns a card; writing it. | | `references/brief-template.md` | Writing the output. | -| `scripts/index-origin-spec.py` | Turning the fetched `openapi.yaml` into a grep-friendly index (operations, webhook families, scopes, one component). Optional; needs PyYAML. | +| `scripts/index-origin-spec.py` | Turning `openapi.yaml` into a grep-friendly index. Optional. | diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md index 7642ca1da..f20b5ae31 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md +++ b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md @@ -1,40 +1,33 @@ # Porting brief template -Write the brief as one Markdown file at the repository root -(`ORIGIN-PORTING-BRIEF.md` unless the team's docs convention says otherwise) -and print its path. Fill every section; where a section is genuinely empty, -say so in one line rather than deleting it, so the team can see it was -considered. Cite spec `operationId`s and `llms-full.txt` anchors; cite the -team's code by `file:line`. +One Markdown file at the repository root (`ORIGIN-PORTING-BRIEF.md` unless the +team's docs convention says otherwise); print its path. Fill every section; an +empty section says so in one line rather than disappearing. Cite spec +`operationId`s and `llms-full.txt` anchors; cite the team's code by +`file:line`. A table row per capability, a line per follow-up field, a card +per gap; the team will argue over it in one sitting. -Keep it light. A table row per capability, a line per follow-up field, a card -per gap. The team will read this in one sitting and then argue about it; give -them the shape to argue over, not prose. - -## Labels used in the tables +## Labels **Parity** | Label | Meaning | | --- | --- | | `same` | Same capability, same shape; a path or field rename at most. | -| `reshaped` | Same capability, different shape: pagination style, identifier form, event granularity, key semantics. The code changes, the behavior does not. | -| `workaround` | Same outcome reachable with existing surface by a different route (follow-up read, client-side filter, marker). Tradeoff column is mandatory. | -| `by-design-absent` | GitHub feature Origin deliberately does not reproduce (`origin-isms.md`). Points at the Origin idiom or says "no equivalent; decision needed". | -| `gap` | No workaround, or a workaround whose tradeoff fails the bar (`gap-bar.md`). Has a card in § Gaps worth raising. | -| `unknown` | Discovery could not determine the app's use or the spec's answer. Has an up-front question. | -| `preview` (suffix) | The Origin operation is stamped `x-cursor-visibility: PREVIEW`. Usable; shape may still move. | +| `reshaped` | Same capability, different shape (pagination, identifier form, event granularity, key semantics). Code changes, behavior does not. | +| `workaround` | Same outcome by a different route (follow-up read, client-side filter, marker). Tradeoff column mandatory. | +| `by-design-absent` | Origin deliberately does not reproduce it (`origin-isms.md`). Names the idiom or "no equivalent; decision needed". | +| `gap` | No workaround, or one that fails `gap-bar.md`. Has a card in § 6. | +| `unknown` | Discovery or the spec could not answer. Has a question in § 7. | +| `preview` (suffix) | Origin operation is `x-cursor-visibility: PREVIEW`. Usable; shape may move. | -**Size** — how much of the team's code changes for this row, by kind of -change, never by time: +**Size** (kind of change, never time) | Size | Meaning | | --- | --- | -| S | Contained in the adapter or client layer: a path, header, identifier, or pagination rewrite; a re-keyed lookup. | -| M | A new code path: a follow-up read where the payload used to suffice, a handshake step, a new event handler, a data-model change for a new identifier or version concept. | -| L | A product or architecture change: a flow that depended on user OAuth, a customer-visible behavior, a dependency on the customer's repositories being Origin-native, or a capability with an open gap card. | - ---- +| S | Adapter or client layer: path, header, identifier, or pagination rewrite; re-keyed lookup. | +| M | New code path: a follow-up read where the payload sufficed, a handshake step, a new handler, a data-model change for a new identifier or version concept. | +| L | Product or architecture change: a flow that depended on user OAuth, a customer-visible behavior, a dependency on native repositories, an open gap card. | ## Template @@ -42,144 +35,116 @@ change, never by time: # Origin porting brief — Planning document. Maps this GitHub App's surface onto the Cursor Origin API -as published on . Contains no implementation decisions about language, -framework, or client strategy. +as published on . No decisions about language, framework, or client. ## Provenance - Origin OpenAPI `info.version`: ``, fetched -- Docs read: -- Codebase: `` at ``; scanned files -- Rows marked `workaround` or `gap` should be re-checked against the changelog before work starts; they are the rows most likely to have moved. +- Docs read: +- Codebase: `` at `` +- Re-check `workaround` and `gap` rows against the changelog before work + starts; they move most. ## 1. What the app is today -One paragraph in plain words: what the app does for its users, which GitHub -events drive it, what it writes back. Then the inventory: +One paragraph: what it does for its users, which events drive it, what it +writes back. Then: | Facet | Finding | Evidence | | --- | --- | --- | -| Manifest / declared permissions | … or "none checked in; permissions derived from calls" | `file:line` | -| Declared / handled events | `pull_request.opened`, … | `file:line` | -| REST call families | , listed in § 3 | | +| Manifest / declared permissions | … or "none checked in; derived from calls" | `file:line` | +| Events handled | … | `file:line` | +| REST call families | , in § 3 | | | GraphQL | none / documents, decomposed in § 3 | | | Auth flow | app JWT () → installation token; user OAuth: | `file:line` | -| Webhook receiver | path, verification scheme, raw-body availability, dedupe | `file:line` | +| Webhook receiver | path, scheme, raw-body availability, dedupe | `file:line` | | Git as the app | clone / push / none | `file:line` | -| Observed language and libraries | … (observed only) | | +| Observed language and libraries | … | | | Looked for, not found | … | | ## 2. First decision: which repositories - On Origin, -an installation has full scopes only on Origin-native repositories and -stable outbound mirrors; repositories mirrored from GitHub are read-only to -apps and do not deliver push events. **Question 1 below must be answered -before the hello-world path is attempted.** + Apps have full +scopes only on Origin-native repositories and stable outbound mirrors; +repositories mirrored from GitHub are read-only to apps and deliver no push +events. **Question 1 must be answered before § 5 is attempted.** ## 3. Capability table -One row per GitHub capability the code uses. Group rows by facet -(authentication, installation & discovery, repositories & contents, pull -requests, reviews & comments, checks, webhooks, git). Follow-up fields go in -§ 4, not here. +One row per GitHub capability the code uses, grouped by facet with a bold +header row (Authentication · Installation & discovery · Configuration · +Repositories & contents · Pull requests · Reviews & comments · Checks · +Webhooks: events · Webhooks: receiver · Git). Include rows for calls a +dependency makes on the app's behalf, marked as such. | GitHub thing (evidence) | Origin equivalent | Parity | Size | Tradeoff | Open question | | --- | --- | --- | --- | --- | --- | -| `GET /repos/{o}/{r}/pulls/{n}` (`src/x.ts:12`) | `OriginService_GetPullRequest` | same | S | — | — | -| … | … | … | … | … | … | - -Rules for the table: the Origin column names an `operationId`, an event -slug, a `llms-full.txt` anchor, or `none`; `workaround` rows always fill -Tradeoff; `gap` rows link their card; `by-design-absent` rows name the idiom -in the Tradeoff column; `unknown` rows name their question. Group rows by -facet with a bold header row (Authentication · Installation & discovery · -Configuration · Repositories & contents · Pull requests · Reviews & comments -· Checks · Webhooks: events · Webhooks: receiver · Git), and include rows -for calls a dependency makes on the app's behalf, marked as such. - -**Scopes to request** (one line under the table): the union of -`x-origin-scopes.scopes` across every Origin operation named above that an -installation token can call, minus scopes that are ambient or implied -(`write` implies `read`; `repository:metadata:read` is automatic). This is -what the install URL's `scope` parameter carries, so the team can read it -straight off the brief. +| `GET /repos/{o}/{r}/pulls/{n}` (`src/x.ts:12`) | `` | same | S | — | — | -## 4. Webhook payload fields the code reads +The Origin column names an `operationId`, a slug, a `llms-full.txt` anchor, +or `none`. `workaround` rows fill Tradeoff; `gap` rows link their card; +`by-design-absent` rows name the idiom; `unknown` rows name their question. + +**Scopes to request:** the union of `x-origin-scopes.scopes` across every +Origin operation above that an installation token can call, minus ambient +and implied scopes (`write` implies `read`; `repository:metadata:read` is +automatic). This is what the install URL's `scope` parameter carries. -For each mapped event, the fields the handlers dereference. +## 4. Webhook payload fields the code reads | Event (GitHub → Origin) | GitHub field | Origin | How | | --- | --- | --- | --- | -| `pull_request.synchronize` → `pull_request.head_ref.pushed` | `pull_request.head.sha` | present | `payload.pullRequest.head.sha` | -| `push` → `repository.pushed` | `commits[].added` | follow-up read | `OriginService_ListComparisonFiles` on `refUpdates[].before..after` — one call per ref update | -| … | `repository.html_url` | derivable / absent | … | +| `pull_request.synchronize` → `` | `pull_request.head.sha` | present | `payload.pullRequest.head.sha` | +| `push` → `` | `commits[].added` | follow-up read | ``, one call per ref update | "How" is one of: present at ``; present in envelope (`event.type` for -GitHub's `action`); follow-up read via `` (state the call count -per event); derivable (say from what, and whether the format is documented); -absent (→ § 3's label for that row: `by-design-absent`, `unknown`, or a § 6 -card). Include fields the code reads only for logging; they are the ones -teams forget until a dashboard breaks. +GitHub's `action`); follow-up read via `` with the call count +per event; derivable (from what; is the format documented); absent (→ § 3's +label). Include fields read only for logging. ## 5. Hello-world path -The shortest route to one real event from one native repository. Each step -is something to verify, not code to write. Link each to `llms-full.txt`. -Append one step per spec-silent behavior the brief depends on (see the -questions), stated as the observation to make. - -1. **Create the app** in the target namespace's app settings; register the - Ed25519 public key only; set the webhook URL and the callback URI. -2. **Subscribe to events.** Installation lifecycle events arrive regardless; - select every repository event from § 3 explicitly. An unselected event is - silence, not an error. -3. **Install** on an Origin-native repository (or a stable outbound mirror). - Verify the installation receipt (`kid` → JWKS, `alg`, `typ`, `iss`, `aud`, - `exp`, `state`); read the installation ID from `sub`. Never send the - receipt as a Bearer token. -4. **Mint** an app JWT (EdDSA, ~5 min) and exchange it for an installation - token; call `/installation/repos` and confirm the repository is listed - and its `mirror` state is what § 2 expects. -5. **Ping** the receiver and verify `v1ed` over the raw body against the - JWKS; check timestamp skew handling and `webhook-id` dedupe. -6. **First real event**: perform the smallest action in § 3 on the native - repository (open a PR, push a branch) and confirm the delivery arrives - with the expected slug and the payload fields from § 4. If the ping - arrived and this did not, re-check steps 2 and 3 before anything else. -7. **First write back** (if the app writes): the smallest write from § 3 - (a check run with a stable `key`, a PR comment), confirming the scope - from `x-origin-scopes` is in the installation grant. +Shortest route to one real event from one native repository. Each step is a +verification, linked to `llms-full.txt`; append one step per spec-silent +behavior the brief depends on, stated as the observation to make. + +1. Create the app; register the Ed25519 public key; set webhook URL and + callback. +2. Select every repository event from § 3 in app settings. +3. Install on an Origin-native repository; verify the receipt JWT and read + the installation ID from `sub`. +4. Mint an app JWT, exchange for an installation token, confirm the + repository is listed and its mirror state matches § 2. +5. Verify the ping (`v1ed` over the raw body, timestamp skew, `deliveryId` + dedupe). +6. Perform the smallest action in § 3 and confirm the slug and the § 4 + fields arrive. Ping but no event: re-check steps 2 and 3 first. +7. Smallest write from § 3 (check run with a stable `key`, PR comment), + confirming its scope is in the grant. ## 6. Gaps worth raising -Zero or more cards in the `gap-bar.md` shape. If zero, say: "No row failed -the gap bar; the workarounds in § 3 carry their tradeoffs." Do not pad. +Zero or more cards in the `gap-bar.md` shape. If zero: "No row failed the gap +bar; the workarounds in § 3 carry their tradeoffs." Do not pad. ## 7. Questions for the team -Pruned to what discovery left open, plus anything specific found. Always -starts with the first three. - -1. Will the app run against Origin-native repositories (or stable outbound - mirrors), or against repositories mirrored from GitHub? (Decides whether - the app receives events and can write at all.) -2. Which of the follow-up reads in § 4 are acceptable at your event volume, - and which payload fields are hard requirements? -3. Which flows depend on a user credential today (user OAuth, install-by-user - pickers, acting on behalf of a user), and what should they do on Origin? -4. Does anything key approvals or reviews by commit SHA rather than by pull - request version? -5. Do you generate clients from OpenAPI? (Then read the changelog for schema - renames and check reserved names in your language.) -6. How do you identify your own check runs / comments / reviews today, and - can a key or marker you control replace actor matching? -7. What is your first success metric: ping received, first real event, or - first write back on a native repository? -8. Anything marked `unknown` in § 1 or § 3. - -## 8. Out of scope for this brief - -No implementation, no SDK or language choice, no effort estimates in time. -The brief is a map; the route is the team's. +Always the first three; then what discovery left open. + +1. Native repositories (or stable outbound mirrors), or repositories mirrored + from GitHub? Decides whether the app receives events and can write. +2. Which follow-up reads in § 4 are acceptable at your event volume, and + which payload fields are hard requirements? +3. Which flows depend on a user credential today, and what should they do on + Origin? +4. Does anything key approvals or reviews by commit SHA rather than PR + version? +5. How do you identify your own check runs / comments / reviews today; can a + key or marker you control replace actor matching? +6. Do you generate clients from OpenAPI? (Read the changelog for renames.) + +## 8. Out of scope + +No implementation, no SDK or language choice, no time estimates. The brief is +a map; the route is the team's. ``` diff --git a/origin-apps/skills/port-github-app-to-origin/references/discovery.md b/origin-apps/skills/port-github-app-to-origin/references/discovery.md index fb3532130..a6258c3b5 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/discovery.md +++ b/origin-apps/skills/port-github-app-to-origin/references/discovery.md @@ -1,147 +1,56 @@ # Discovering the GitHub App's shape from the codebase -Everything the brief needs about the existing app is in the repository. Search -for it; do not ask for it. Record a file and line for every fact so the team -can check your reading, and record every place you looked that turned up -nothing so they can point you at the right place if you missed it. - -Work through the seven facets below. The searches are starting points across -the common ecosystems (Node Octokit and Probot, Go `go-github`, Python -`PyGithub`/`ghapi`/`githubkit`, Java `hub4j`, Ruby `octokit.rb`, .NET -`Octokit.net`, raw HTTP in any language); extend them when the code uses -something else. Note the language and libraries you observe as a fact in the -inventory — they inform sizes in the brief and nothing else. - -## 1. Declared permissions and events - -The app's manifest or registration snapshot, if it is checked in. - -- Files: `app.yml`, `app.yaml`, `.github/app.yml`, `github-app-manifest.json`, - `manifest.json`, `app-manifest.*`, Terraform or Pulumi resources for the - app, and infrastructure-as-code that seeds a GitHub App. -- Keys: `default_permissions`, `default_events`, `hook_attributes`, - `redirect_url`, `callback_urls`, `setup_url`, `public`, - `request_oauth_on_install`, `setup_on_update`. -- Probot: `app.yml` at the repo root carries `default_events` and - `default_permissions`. - -When there is no manifest, derive the effective permissions from the calls in -facet 4 and say the manifest was absent. The union of what the code calls is -what the port needs anyway. - -## 2. Webhook events handled - -- Probot / `@octokit/webhooks`: `app.on(".", …)`, - `app.on([...])`, `webhooks.on(`, `webhooks.onAny(`, `EmitterWebhookEvent`. -- Hand-rolled receivers: a `switch` or dispatch on the `x-github-event` header - (any casing), on `payload.action`, or on a combined `"."` - string; Go `github.WebHookType(r)` / `github.ParseWebHook`; Python - `request.headers["X-GitHub-Event"]`. -- Framework routes registered for webhook paths: `/webhook`, `/webhooks`, - `/github/webhooks`, `/api/github/events`, `createNodeMiddleware`, - `createProbot`, smee/ngrok tunnel config in development scripts. - -For each handled event record the GitHub event, the action(s) handled, the -handler location, and — from facet 3 — which payload fields it reads. - -## 3. Payload fields read - -Inside each handler from facet 2, list every property path dereferenced from -the payload object: `payload.pull_request.head.sha`, `payload.repository.name`, -`payload.installation.id`, `payload.sender.login`, `payload.commits[].added`, -`payload.check_suite.pull_requests`, `payload.before`, `payload.after`, and so -on. Typed languages make this easy (struct fields accessed); in dynamic -languages grep the handler body and any helper it passes the payload to. -Include fields used only for logging or metrics — those are the ones teams -forget until the port breaks a dashboard. - -## 4. REST and GraphQL calls - -- Octokit REST: `octokit.rest..(`, `octokit..(`, - `octokit.request(" /…")`, `octokit.paginate(`, `octokit.graphql(`, - `@octokit/graphql`, `.graphql(`. -- Other SDKs: Go `client.PullRequests.`, `client.Checks.`, `client.Repositories.`, - `client.Issues.`, `client.Apps.`; Python `repo.get_pull(`, `gh.rest.`, - `githubkit`; Java `GHRepository`, `GHPullRequest`; Ruby `client.pull_request(`. -- Raw HTTP: `api.github.com`, `/repos/`, `Accept: application/vnd.github`, - `X-GitHub-Api-Version`, `uploads.github.com`, `raw.githubusercontent.com`. -- GraphQL documents: `.graphql` / `.gql` files, template strings starting with - `query` or `mutation`, generated client code. - -Record each distinct call family once (method + path, or SDK method), with -the parameters and filters the code passes (state, base, head, per_page, -sort, since, check_name, app_id, filter…), the response fields it reads, and -whether it is called in a loop or per webhook (this decides the fan-out -tradeoff on the brief row). Note pagination style in use (`Link` header, -`page`/`per_page`, `octokit.paginate`, GraphQL cursors) — it always changes. - -## 5. Authentication and token minting - -- App identity: `GITHUB_APP_ID`, `APP_ID`, `GITHUB_PRIVATE_KEY`, `PRIVATE_KEY`, - `.pem` files, `createAppAuth`, `@octokit/auth-app`, `ghinstallation` - (Go), `jwt.encode(... "RS256")`, `App.get_installation(`. -- Installation tokens: `/app/installations/{id}/access_tokens`, - `installationId`, `installation_id`, `app.auth(`, `getInstallationOctokit(`, - `ghs_` prefixes in tests or fixtures, token caches keyed by installation. -- User OAuth: `/login/oauth/authorize`, `/login/oauth/access_token`, - `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`, `@octokit/auth-oauth-*`, - `/user`, `/user/installations`, `/user/repos`, "setup URL" handlers reading - `installation_id` and `setup_action` from the callback query string. -- Git over HTTPS as the app: `x-access-token:` in clone URLs, credential - helpers, `GIT_ASKPASS`. - -Record the flows present: app JWT algorithm, how the installation is -identified after install (callback query, webhook, or DB), token lifetime -handling, whether user OAuth exists and what it is used for (identity, -repo discovery, acting on behalf of a user), and whether the app pushes or -clones git. - -## 6. Webhook receiver and verification - -- Signature: `x-hub-signature-256`, `x-hub-signature`, `verify(`, - `verifyAndReceive(`, `WEBHOOK_SECRET`, HMAC-SHA256 helpers, - `github.ValidatePayload`. -- Delivery handling: use of `x-github-delivery` for idempotency, queueing - before or after responding, retry handling, redelivery tooling against - `/app/hook/deliveries`. -- Transport: the public URL and how it is configured (manifest - `hook_attributes.url`, env var, tunnel in dev). - -Record the verification scheme, whether the raw body is available at -verification time (frameworks that parse JSON first cannot verify), and how -deliveries are deduplicated, if at all. - -## 7. Calls the framework makes on the app's behalf - -Some of the app's GitHub surface is not in its own source: it is what the -framework or a helper library does for it, and the port has to do it too. -When the code uses one of these, list the calls the dependency makes as rows -in their own right, marked "from `` (documented behavior)" so the -team knows they were inferred, not read. `node_modules` is usually not -checked in, so read the dependency's README or its source on its own -repository, not the app. - -- **Probot**: the built-in receiver (`POST /`, `@octokit/webhooks` HMAC - verification, `x-github-event` routing), per-installation token minting - and caching, `context.repo()` / `context.issue()` helpers, - `context.isBot` (`payload.sender.type`). Common companions: - `probot-config` (reads `.github/.yml`, falling back to the owner's - `.github` repository), `probot-scheduler` (lists installations and their - repositories with the app credential, then emits `schedule.repository` on - an interval), `probot-metadata` (stores state in issue bodies). -- **Octokit `App` / `@octokit/app`**: `app.webhooks.verifyAndReceive`, - `app.eachInstallation` / `app.eachRepository` (installation + repository - listing), automatic installation-token minting behind - `app.getInstallationOctokit`. -- **Go `ghinstallation`, Python `githubkit`/`gidgethub`, Ruby `octokit` - app auth**: JWT minting and installation-token exchange. -- **Frameworks' webhook middleware** in any language: which header names - they read and whether they verify against the raw body. - -## When something is missing - -Say so in the inventory: "no manifest found (searched: …)", "no signature -verification found in the receiver at …", "GraphQL client present but no -query documents found". Each missing item becomes either a row with an -`unknown` label or an up-front question in the brief. Do not fill gaps with -assumptions about what an app of this kind usually does. +Everything the brief needs about the app is in the repository. Search; do not +ask. Record `file:line` for every fact and list every place you looked that +turned up nothing. Note the language and libraries as an observation; they +inform sizes and nothing else. + +Seven facets. For each, what to record: + +1. **Declared permissions and events.** The manifest or registration snapshot + if checked in (`app.yml`, manifest JSON, IaC that seeds the app; Probot + keeps `default_events` / `default_permissions` in `app.yml`). If absent, + say so and derive permissions from facet 4; the union of what the code + calls is what the port needs anyway. +2. **Webhook events handled.** Each GitHub event and action pair the code + dispatches on (`app.on("pull_request.opened")`, a switch on + `x-github-event` + `payload.action`, SDK parsers), with the handler + location. +3. **Payload fields read.** Every property path dereferenced from the payload + inside each handler and the helpers it passes the payload to. Include + fields used only for logging or metrics; those break dashboards after the + port. +4. **REST and GraphQL calls.** Each distinct call family once (method + path + or SDK method) with the parameters and filters passed, the response fields + read, whether it runs per webhook or in a loop (decides the fan-out + tradeoff), and the pagination style in use (it always changes). GraphQL + documents count as calls; they are decomposed during mapping. +5. **Authentication and token minting.** App JWT algorithm; how the + installation is identified after install (callback query, webhook, DB); + token lifetime handling; whether user OAuth exists and for what (identity, + repository discovery, acting for a user); whether the app clones or pushes + git as itself. +6. **Webhook receiver and verification.** Signature scheme; whether the raw + body is available at verification time (frameworks that parse JSON first + cannot verify); how deliveries are deduplicated, if at all; where the + public URL is configured. +7. **Calls the framework makes on the app's behalf.** Not in the app's + source, but the port has to do them. List them as rows marked "from + `` (documented behavior)" and read the dependency's docs or + source, not the app. Common cases: + - **Probot**: built-in receiver and HMAC verification, per-installation + token minting and caching, `context.repo()` / `context.issue()`, + `context.isBot`; companions `probot-config` (reads `.github/.yml`, + falling back to the owner's `.github` repository), `probot-scheduler` + (lists installations and repositories with the app credential, emits + `schedule.repository`), `probot-metadata` (state in issue bodies). + - **Octokit `App`**: `webhooks.verifyAndReceive`, `eachInstallation` / + `eachRepository`, automatic token minting behind + `getInstallationOctokit`. + - **`ghinstallation`, `githubkit`, `gidgethub`, `octokit.rb` app auth**: + JWT minting and installation-token exchange. + +When something is missing, say so in the inventory ("no manifest found +(searched: …)", "no signature verification found in the receiver at …"). +Each missing item becomes an `unknown` row or an up-front question. Do not +fill gaps with what an app of this kind usually does. diff --git a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md index 1e0d32903..fd732713c 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md +++ b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md @@ -1,143 +1,74 @@ # The gap bar and the escalation card -Most differences between GitHub and Origin are not gaps. A brief that files -every difference trains the team to ignore it and buries the two or three -things Cursor actually needs to hear. Use this bar, and when in doubt, write -the row as `workaround` with an honest tradeoff and an open question rather -than as a gap card. - -## Definitions +Most differences are not gaps. A brief that files every difference buries +the two or three things Cursor needs to hear. When in doubt, write the row as +`workaround` with an honest tradeoff and an open question, not as a card. - A **difference** is any row whose parity label is not `same`. -- A **workaround** is a way to get the same outcome with the surface that - exists today: a follow-up read, a re-keyed identifier, a path change, a - client-side filter, a marker the app controls. -- A **gap** is a difference with **no workaround**, or a workaround whose - tradeoff is **nontrivial**. Only gaps become escalation cards. +- A **workaround** reaches the same outcome with today's surface: a follow-up + read, a re-keyed identifier, a path change, a client-side filter, a marker + the app controls. +- A **gap** is a difference with no workaround, or a workaround whose tradeoff + is nontrivial. Only gaps become cards. -## What makes a tradeoff nontrivial +## Nontrivial tradeoff -A workaround's tradeoff is nontrivial when at least one of these holds. Quote -the one that applies on the card. +At least one must hold; quote it on the card. | Tradeoff | Test | | --- | --- | -| **Fan-out at scale** | The workaround multiplies calls per event by a factor that grows with repository or activity size (N commits × M files per push; a full list scan to find one row), and the app's event volume makes that budget-relevant. One extra bounded read per event is trivial. | -| **Correctness risk** | The workaround can produce a wrong answer, not just a slower one: identifying "my own row" by a heuristic; inferring a pull request from a SHA that several versions share; assembling a URL whose format is not contractual. | -| **Security posture** | The workaround needs a broader scope, a longer-lived token, or a user credential where an installation token should do. | -| **Product behavior visible to the team's customers** | The workaround changes what their users see or can do (no team link in a comment, no user-scoped repository picker), not just how the code is organized. | -| **Load-bearing for the port** | The capability sits on the hello-world path or on the team's stated core flow, so its tradeoff decides whether the port ships. | - -If none apply, the row is `workaround`, sized honestly, with the tradeoff in -the tradeoff column and no card. - -## What is never a gap - -- Anything on the `origin-isms.md` list. Those rows are `by-design-absent` - or `reshaped`, and the brief points at the idiom. Filing them wastes the - team's and Cursor's time; the decisions are recorded. -- A field or filter the code does not actually use. Map what the code reads, - not what the SDK exposes. -- A difference that exists only because the code uses a GitHub convenience - (`Link` pagination, numeric IDs, `html_url`) in a place where the Origin - convention is a mechanical substitution. +| **Fan-out at scale** | Calls per event multiply by a factor that grows with repository or activity size (N commits × M files; a full list scan to find one row) and the app's volume makes that budget-relevant. One bounded extra read per event is trivial. | +| **Correctness risk** | The workaround can be wrong, not just slower: heuristic "my own row" matching, inferring a PR from a SHA several versions share, assembling a URL whose format is not contractual. | +| **Security posture** | Needs a broader scope, a longer-lived token, or a user credential where an installation token should do. | +| **Customer-visible behavior** | Changes what the team's users see or can do, not how the code is organized. | +| **Load-bearing** | Sits on the hello-world path or the team's stated core flow. | + +## Never a gap + +- Anything in `origin-isms.md`. +- A field or filter the code does not actually use. +- A GitHub convenience (`Link` pagination, numeric IDs, `html_url`) where the + Origin convention is a mechanical substitution. - Something the changelog says shipped or the spec already carries. Re-check - the live spec before writing any card; the rows most likely to be stale are - the ones you are about to escalate. -- A concept the Origin docs never mention at all (Marketplace billing, merge - queues, Actions, Projects). That is `unknown` plus a question - (`spec-mapping.md` § Out-of-domain surfaces); a card asks Origin to build - something the team may not want. -- A GitHub search query. Origin has no search endpoint by design for the - surfaces it exposes; a list operation with `sortBy`/`state`/filters plus a - client-side predicate is the idiom. Count the fan-out honestly (a sorted - list read that stops at a cutoff is proportional to the matches, not the - collection), and only if that count fails the bar does it become a card - about a *filter*, never about search. + the live spec before writing any card. +- A concept the Origin docs never mention (Marketplace billing, merge queues, + Actions, Projects): `unknown` plus a question. +- A GitHub Search query. The idiom is a list operation with its filters plus + a client-side predicate; a sorted list read that stops at a cutoff is + proportional to the matches, not the collection. Only if that count fails + the bar does it become a card about a *filter*, never about search. ## One pattern that does clear the bar -A state change the app reacts to that has **no event**, when the app's -purpose is to react to exactly that change and the state is otherwise -invisible until an unrelated event arrives. Reading the state off the next -event's snapshot is the workaround; it fails on correctness and -customer-visible behavior when the app is a gate (a check, a block, a -notification) and the lag is the whole failure mode. Write the card about the -event; do not write it when the app merely logs or tidies up on that change. +A state change the app reacts to that has **no event**, when reacting to +exactly that change is the app's purpose and the state is invisible until an +unrelated event arrives. Reading it off the next snapshot fails on +correctness and customer-visible behavior when the app is a gate (a check, a +block, a notification). Write the card about the event; not when the app +merely logs or tidies up on that change. -## The escalation card +## The card -One card per gap, in the brief's "Gaps worth raising" section, in this shape. -It is written so Cursor can act on it without a call. +One per gap, in the brief's "Gaps worth raising" section, written so Cursor +can act without a call: ```markdown -### Gap: +### Gap: -- **GitHub surface the app uses:** `` or `` or ``, at ``. -- **What the app needs from it:** . +- **GitHub surface the app uses:** `` / `` / ``, at ``. +- **What the app needs from it:** . - **Why:** . -- **Closest Origin surface today:** `` / `` / none, and what it lacks. -- **Workaround considered:** . -- **Tradeoff that fails the bar:** . -- **Shape that would close it:** . -- **Blocking?** yes / no, and for which flow. +- **Closest Origin surface today:** `` / `` / none, and what it lacks. +- **Workaround considered:** . +- **Tradeoff that fails the bar:** . +- **Shape that would close it:** . +- **Blocking?** yes / no, for which flow. - **Spec version checked:** `` on ``. ``` -Keep the card to those lines. Do not propose scope names, field names, or -route templates; Cursor owns the shape and applies design conventions the -card cannot see. Do not batch unrelated capabilities into one card. - -## Where the card goes - -The brief is written for the team. The cards are theirs to send, in their own -words if they prefer. Two routes exist today: - -- **A shared Slack channel with Cursor**, if the team has one from working - with Cursor on the integration. Post the card there; it reaches the people - who own the API. -- **`hi@cursor.com`**, the feedback address the Origin documentation names. - Put "Origin API" and the app name in the subject and paste the card. - -Either way, quote the spec version the card was checked against and any -`X-Request-ID` values from failed calls (every Origin error response carries -one; the API reference asks for it when you contact Cursor). A card that -states the capability and the tradeoff cleanly is the fastest path to an -answer, including a "this is by design, here is the idiom" answer, which is a -fine outcome and belongs back in the brief as a `by-design-absent` row. - -Do not send cards yourself. Write them, put them in the brief, and let the -team decide what goes out. - -## Calibration examples - -Illustrations of where the bar falls. Each is written as a pattern, because -the specific surface may have moved since this file was written; confirm -against the live spec before reusing the verdict. - -- **A list operation lacks a filter the code relies on to find its own row** - (for example, finding the app's own check run for a commit by name or key). - Workaround: page the whole list and match client-side. Trivial when the - list is small and bounded; **gap** when the list grows with activity and the - lookup runs per event (fan-out) or when the match is heuristic - (correctness). State which. -- **A web URL the app posts in comments is not in the payload or resource.** - Workaround: assemble it from slug and number. Correctness risk only if the - URL format is not contractual — check the docs; if the format is documented, - it is `derivable`, not a gap. If it is not documented, write a card that - asks for the URL field rather than guessing. -- **A membership roster the app uses to attribute approvals to a team** has - no read on Origin and no workaround (identity is a TypeID; there is no - directory). Correctness plus product behavior: **gap**, with a card that - names the capability ("who is in group X, for a repository this - installation can read") and leaves the oracle analysis to Cursor. -- **Push payload lacks the changed-file list.** Workaround: compare or list - commit files on receipt. One bounded read per push is `workaround`; if the - app fans out per commit and per file on high-volume repositories, quote the - multiplier and let the team decide — and note that the lean payload is the - documented design (`origin-isms.md` § Webhooks), so the card, if any, is - about a compare endpoint's shape, not about fattening the payload. -- **The app uses GitHub Issues.** Never a card. `by-design-absent`; the - up-front question is what the PR-scoped behavior should be. -- **The app uses commit statuses.** Never a card. `reshaped` onto check runs - with a stable key. +Do not propose scope, field, or route names; Cursor owns the shape. Do not +batch unrelated capabilities. Do not send cards yourself; the team decides +what goes out (their shared Slack channel with Cursor, or `hi@cursor.com` +with "Origin API" and the app name in the subject), quoting the spec version +and any `X-Request-ID` from failed calls. "This is by design, here is the +idiom" is a fine answer and goes back into the brief as `by-design-absent`. diff --git a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md index 112c988ca..db4f021a0 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md +++ b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md @@ -1,170 +1,37 @@ -# Origin-isms: where Origin departs from GitHub on purpose - -Origin uses GitHub's nouns and general shape and deliberately not its wire -format. When a GitHub feature has no counterpart in the spec, check this list -before calling it a gap: a row here is `by-design-absent` (or `reshaped`) in -the brief, and the brief's job is to point the team at the Origin idiom. Each -entry gives the departure, the idiom, and the reason in one line, so you can -explain it to a team that has only ever seen GitHub. Confirm the current -wording in `llms-full.txt`; this file explains intent, the docs state the -contract. The `origin-api` skill carries the same conventions as practices -for building; this file carries them as parity decisions for a port. - -## Which repositories an app can act on - -- **Apps act on Origin-native repositories (and stable outbound mirrors).** A - repository mirrored *from* GitHub is read-only to an installation — metadata - and contents reads, clone/fetch/pull — and rejects pull requests, reviews, - comments, checks, rulesets, and every write with `403`. Why: a write on a - mirror would have to act on GitHub as Cursor's GitHub App, with risk Origin - cannot bound; and GitHub already sends its own webhooks for those repos. -- **`repository.pushed` is not delivered for GitHub-mirrored repos.** Why: - GitHub owns those pushes and already notifies the app; Origin would - duplicate them. Consequence for the port: a ping works on a mirror and then - nothing else does. The first up-front question in every brief is - native-or-mirror for exactly this reason. -- **`mirror.status` does not tell you whether writes are allowed.** Treat the - `403` as authoritative. - -## Identity and authentication - -- **No OAuth-app token exchange as the install path.** Install is a consent - redirect that returns a signed **installation receipt** JWT (`sub` = the - installation ID, `state` echoed) instead of `installation_id` and - `setup_action` query parameters. Why: the receipt proves the approval came - from Origin; a bare query string cannot. The receipt is never a Bearer - token. -- **App JWT is EdDSA over Ed25519, not RS256.** Register only the public key; - up to ten active keys. `kid` and `iss` are the app ID; `aud` is fixed. -- **Installation tokens are `oit_…`, short-lived, minted just in time**, and - can be attenuated to fewer scopes or to specific `repositoryIds` (IDs, not - slugs — resolve the ID first). Git over HTTPS uses Basic auth with username - `x-access-token` and the token as password; Bearer belongs to REST only. -- **User-scoped calls (`/user`, `/user/installations`, install-by-user) have - no app-side counterpart.** Repository discovery is through the - installation (`/installation/repos`), and app creation, adding - repositories to an installation, and mirror transitions are admin actions - carried by a Cursor user credential, not app calls. Read the live - Authentication section for the current user-credential story before - labeling anything here; do not assume a GitHub-OAuth analog exists. -- **Scopes, not permissions.** `repository:[:]:`; - `write` implies `read`; `repository:metadata:read` is automatic; the - installation can only narrow what the admin approved. Look each called - operation's requirement up in `x-origin-scopes` rather than translating the - manifest noun-for-noun. - -## Wire conventions - -- **Opaque TypeIDs, never integers**: `app_…`, `i_…`, `ns_…`, `repo_…`, - `user_…`, `cmt_…`, `evt_…`. Why: sequential IDs are enumerable; opaque IDs - are the stable handle to cache. A repository is addressable by ID as - `/repos/_/{repoId}`, which survives renames; there is no - `/repositories/{id}` route and no numeric ID anywhere. -- **camelCase JSON; 64-bit integers (PR numbers, version numbers) are JSON - strings; RFC 3339 timestamps.** -- **Defaults are present, not omitted.** `false`, `0`, `""`, `[]` appear in - bodies; only fields documented as optional are absent when unset. Do not - treat a missing key as the default and do not treat a present `false` as - "unset". -- **Pagination is `pageSize` / `pageToken` / `nextPageToken`.** Tokens are - opaque, bound to the resource and filters, and there is no `Link` header, - no `page`, no total count. Why: keyset cursors do not skip or duplicate - under concurrent writes; offsets do. Restart when filters change. -- **Errors are one envelope**: `google.rpc.Status` `{code, message, details}` - with typed `details` (`BadRequest` field violations, `RequestInfo` request - id on every error; further typed detail types such as machine-readable - `ErrorInfo` reasons may be added, so read the live Errors section and - tolerate unknown detail types). `X-Request-ID` is on every error. **`404` - never distinguishes not-found from no-access.** Why: anti-enumeration. - Branch on status and `code`; quote the request id when escalating. -- **No GraphQL.** REST only; decompose queries. -- **Rate limits are a per-principal point budget**, `X-RateLimit-*` plus - `Retry-After` on `429`; Git HTTPS meters separately (`X-RateLimit-Resource: - git`). Cursor can raise per-app budgets on request. -- **Public terminology is "pull request"** throughout; do not expect - "change" or "changeset" on the wire. - -## Resources GitHub has that Origin does not reproduce - -- **No commit statuses.** Check runs are the only status primitive: - `PostCheckRun` upserts on a caller-stable `key` (with `externalId` / - `externalUpdatedAt` for retries and ordering), and rulesets bind on check - keys. Why: one status model, idempotent by construction. A `statuses` - permission or `POST /statuses/{sha}` maps to checks, not to a gap. -- **No Issues.** Origin's conversation surfaces are pull requests, pull - request comments and threads, reviews, and labels on pull requests. An - `issues`-only GitHub App has no Origin equivalent for that part; say so - and ask what the team wants the PR-scoped behavior to be. GitHub's - `/issues/{n}/comments` used *on a PR* is just a path change. -- **No repository-level webhook CRUD** (`/repos/…/hooks`). Subscriptions are - an app-settings concern; there are no per-repo hook objects. -- **No app-manifest conversion endpoint and no OAuth-app token mints.** - Legacy GitHub mechanisms Origin does not reproduce. App creation is a - form (which accepts prefill query parameters) or the user-credential - `CreateApp` operation. -- **No REST git-object writes beyond the documented ones.** Commits are - pushed over Git HTTPS with an installation token (or created through the - documented commit-from-files and ref operations); do not expect a GitHub - Git Data API for arbitrary tree/blob writes. -- **No standalone threads API.** Comments are the only content write - surface; a thread materializes from its first diff-anchored comment and is - addressable for resolve/reopen. Why: one write surface, no dual bookkeeping. -- **No user/email directory.** Actors carry a TypeID (and a handle where the - contract exposes it); there is no lookup from email to user and no team - or member pages to link to. Why: an email or membership oracle. - -## Webhooks - -- **One message serves REST and webhooks.** A payload snapshots the one - object that changed exactly as its `Get…` returns it, plus compact - references (`repository`, `pullRequest`) for its containers. Why: consumers - reuse their REST decoders and never see a partially hydrated shape. -- **Webhooks notify; the API answers.** Payloads are lean by design: no - changed-file lists on pushes, no before-SHA on PR events, no web URLs, no - inlined user profiles. Follow-up reads (`GetCommit`, `CompareCommits`, - `ListComparisonFiles`, `GetRepo`, `GetPullRequest`) are the intended - pattern. Why: no emit-time joins, no size blowups, no staleness races. - Map fields the code reads to specific follow-up calls and count the - fan-out honestly; that count is the tradeoff, not a defect. -- **Slugs are `[.].`** and the action lives - in the slug (`pull_request.created`, `pull_request.review.submitted`, - `repository.check_run.completed`), never in a payload `action` field, and - there is no `previous_attributes` delta. Why: granular event types over - payload flags; one shape per event. GitHub's `pull_request` + - `action: synchronize` becomes `pull_request.head_ref.pushed`, and so on — - confirm each in `x-origin-webhook-events`. -- **Envelope**: `{deliveryId, appId, installationId, event: {id, type, - eventTime, payload}}`. `deliveryId` is stable across retries and is the - idempotency key; `event.id` identifies the domain event. Routing headers - are `webhook-id`, `webhook-timestamp`, `webhook-signature`, - `webhook-event-type`, `webhook-app-id`, `webhook-installation-id` (not - `x-github-*`); after verification the body is authoritative. -- **Signature is Ed25519 over a SHA-256 digest, scheme `v1ed`**, keys from - Origin's JWKS (rotated weekly, cache per `Cache-Control`), not an HMAC - shared secret. It tracks the Standard Webhooks spec except that it signs a - digest instead of the raw string, so off-the-shelf Standard Webhooks - verifiers do not validate it as-is. Verify the raw body before parsing; - reject timestamps more than five minutes off. -- **Installation lifecycle events are always delivered; every other event - must be selected in app settings.** Creating the app subscribes to nothing - else. Why: opt-in exposure. This is the second most common first-week - stall after mirror-vs-native. -- **Delivery is at-least-once with retries and a seven-day recovery window** - (`ListWebhookDeliveries`, `BatchRedeliverWebhookDeliveries`); persistent - failure pauses the app's delivery. - -## Checks, reviews, comments - -- **Check runs upsert on `key`**, live under a check suite keyed the same - way, and can be flagged re-requestable; a re-request arrives as an event - to the owning app. Annotations are appended, not replaced. -- **Reviews anchor to a pull request *version*, not a commit SHA.** Why: a - SHA can match several versions after a rebase or retarget. Code that keys - approvals by `commit_id` re-keys by version number. -- **Requested reviewers are addressed by identifier** (users, groups), and - Origin is deliberately careful about existence oracles on that surface. Do - not expect `created_via` or team pages on the read side. -- **Find your own writes by a key you control.** Check runs are found by - their `key`; for reviews and comments, carry a marker the app owns (a body - prefix, a stable key) rather than relying on actor identity alone, and - confirm in the spec which author filters the list operations offer. +# Origin-isms: GitHub surfaces Origin departs from on purpose + +Check here before labeling anything `gap`. A row below is `by-design-absent` +or `reshaped`; the brief points at the idiom and never files a card. The +reasoning behind each lives in the `origin-api` skill; this table carries only +what the classifier needs. Confirm current wording in `llms-full.txt`. + +| GitHub surface the app uses | Label | Origin idiom | +| --- | --- | --- | +| Acting on a repository mirrored *from* GitHub (writes, `push` events) | `by-design-absent` | Install on Origin-native repositories or stable outbound mirrors. Mirrors are read-only to apps and deliver no `repository.pushed`. First question of every brief. | +| Install callback with `installation_id` + `setup_action` query params | `reshaped` | Signed installation receipt JWT; `sub` is the installation ID. Not a Bearer. | +| RS256 app JWT | `reshaped` | EdDSA over Ed25519; register the public key. | +| `ghs_` installation tokens, long cache | `reshaped` | `oit_…`, short-lived, minted just in time, attenuable to scopes and `repositoryIds`. | +| User OAuth: `/user`, `/user/installations`, `/user/repos`, install-by-user picker | `by-design-absent` | Repository discovery is `/installation/repos`. App creation and installation-repository changes are admin actions with a user credential, not app calls. Ask what the flow should do. | +| Permissions `: read\|write` | `reshaped` | Scopes `repository:[:]:` taken from `x-origin-scopes` of the called operations. | +| Numeric IDs, `/repositories/{id}` | `reshaped` | TypeIDs; `/repos/_/{repoId}`. | +| `Link` / `page` / `per_page` pagination, total counts | `reshaped` | `pageSize` / `pageToken` / `nextPageToken`; opaque; no total. | +| GraphQL | `by-design-absent` | REST; decompose and count the fan-out. | +| Commit statuses (`statuses` permission, `POST /statuses/{sha}`) | `reshaped` | Check runs upserting on a caller-stable `key`; rulesets bind on the key. | +| Issues (`issues` permission, `issues.*` events, `/issues/{n}` not on a PR) | `by-design-absent` | PR comments, threads, reviews, labels on pull requests. Ask what the PR-scoped behavior should be. | +| `/issues/{n}/comments`, `/issues/{n}/labels` used on a pull request | `reshaped` | Same calls under `/pulls/{n}/…`. | +| Repository webhook CRUD (`/repos/…/hooks`) | `by-design-absent` | Subscriptions are app settings. | +| App-manifest conversion, OAuth-app token mints | `by-design-absent` | App creation form (accepts prefill params) or user-credential `CreateApp`. | +| Git Data API tree/blob writes | `by-design-absent` | Push over Git HTTPS with an installation token, or the documented commit-from-files and ref operations. | +| Standalone review-thread objects | `reshaped` | A thread materializes from its first diff-anchored comment and is addressable for resolve/reopen. | +| User, email, team, and member lookups | `by-design-absent` | Actors are TypeIDs (plus a handle where exposed). No directory. | +| Single `pull_request` event with `action` field, `previous_attributes` | `reshaped` | One slug per action (`pull_request.head_ref.pushed`); no `action` field, no delta. Confirm each slug in `x-origin-webhook-events`. | +| `x-github-*` headers, HMAC `x-hub-signature-256` | `reshaped` | `webhook-*` headers; `v1ed` Ed25519 over a SHA-256 digest, JWKS keys. | +| Payload inlines: changed files on push, before-SHA, `html_url`, `sender` profile | `reshaped` | Follow-up `Get…` / `CompareCommits` / `ListComparisonFiles` with identifiers the payload carries. Name the call per field; count the fan-out. | +| Default delivery of all events after app creation | `reshaped` | Only installation lifecycle is default; select the rest. | +| Reviews keyed by `commit_id` | `reshaped` | Reviews anchor to a pull request version. | +| Finding own check runs / comments by actor | `reshaped` | Check runs by `key`; comments and reviews by a marker the app controls. | +| Requested-reviewer team pages, `created_via` | `by-design-absent` | Reviewers addressed by identifier only. | + +Not on this list, and not mentioned anywhere in the Origin docs (Marketplace +billing, merge queues, Actions, Pages, Projects): `unknown` with a question, +never `by-design-absent`. diff --git a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md index d0dcf83f6..0a788ba56 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md +++ b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md @@ -1,185 +1,94 @@ # Reading the Origin spec and matching GitHub surface to it -The mapping in the brief is derived from the live `openapi.yaml`, not from a -table in this skill. This file explains what the spec carries and how to match -against it. Build the index once at the start of the run; every later step -looks things up in it. +Every mapping in the brief is derived from the fetched `openapi.yaml`, not +from a table here. Build the index once; every later step looks things up in +it. -## What the spec carries - -`https://cursor.com/docs/api/origin/openapi.yaml` is OpenAPI 3.1 generated from -Origin's protobuf contract. Beyond the standard fields, four extensions matter -for porting: +## Extensions the spec carries | Extension | Where | Meaning | | --- | --- | --- | -| `x-origin-scopes` | every operation | `scopes`: the scope strings the operation requires. `tokenTypes`: which credentials it accepts (`app` JWT, `installation` token, `user`). `ambient: true`: the scopes come with the credential itself, so an app has nothing to request for it. | -| `x-origin-webhook-events` | payload schemas under `components.schemas` | The event slugs that deliver this payload shape. A schema carrying it is a webhook payload family; the slugs are the only authoritative list of subscribable events. | -| `x-origin-webhook-resource` | payload schemas, when present | Points at the REST component the payload embeds (the object a `Get…` operation returns). When absent, infer the embedded resource from the payload's `$ref` properties. Do not assume every payload has a REST twin; pushes, deletions, and reviewer requests are event-native. | -| `x-cursor-visibility: PREVIEW` | some operations | Published but badged preview. Carry the badge into the brief; a preview operation is usable, but tell the team the shape may still move. | - -Everything else you need is standard: `paths` with `operationId`, -`description`, `parameters`, request and response schemas, and `example` -blocks; `components.schemas` with field descriptions that are the contract -text itself. - -`llms-full.txt` adds what the spec cannot: the installation flow, the receipt -and app JWT, installation tokens and Git HTTPS, the scopes table, the mirrored -repositories rule, webhook headers, signature verification, the delivery -envelope, retries and recovery, pagination and error conventions, and the -current limitations list. Read those sections before mapping; several rows in -the brief (auth, receiver, pagination) map onto them rather than onto an -operation. +| `x-origin-scopes` | every operation | `scopes` required; `tokenTypes` accepted (`app`, `installation`, `user`); `ambient: true` means nothing to request. | +| `x-origin-webhook-events` | payload schemas | The slugs that deliver this payload shape. A schema carrying it is a webhook family; these slugs are the only authoritative event list. | +| `x-origin-webhook-resource` | some payload schemas | The REST component the payload embeds. When absent, infer from `$ref`s; pushes, deletions, and reviewer requests are event-native with no REST twin. | +| `x-cursor-visibility: PREVIEW` | some operations | Usable, shape may move. Carry the badge into the brief as a `preview` suffix. | ## Build the index -Parse the YAML and produce four lookups. Keep them in a scratch file; the -brief cites from them. - -1. **Operations**: for each path and method, record `operationId`, method, - path template, first sentence of `description`, `x-origin-scopes` - (`scopes`, `tokenTypes`, `ambient`), `x-cursor-visibility` if any, path and - query parameter names, request body top-level fields, and the response - component name. -2. **Webhook events**: for each schema with `x-origin-webhook-events`, record - each slug → schema name → the properties and their `$ref`s (or the - `x-origin-webhook-resource` target). Also read the Events table in - `llms-full.txt` § Webhooks reference for each slug's trigger sentence and - for which events are app-lifecycle (always delivered) versus repository - subscriptions (must be selected in app settings). -3. **Scopes**: the union of every `scopes` value seen in `x-origin-scopes`, - annotated with which operations require it, plus the Scopes table in - `llms-full.txt` for the human description and the `write` ⊃ `read` rule. - Separate the scopes an installation can request from those that are - ambient or user-credential-only; the latter tell you which GitHub flows - have no app-side equivalent by construction (for example, app creation and - installation-repository changes are admin actions on Origin, not app - calls). -4. **Resources**: component schemas returned by `Get…`/`List…` operations, - with their field names. Used to answer "which fields does the Origin object - carry" when mapping payload fields and response fields the code reads. - -## Matching rules - -Match in this order and stop at the first rule that yields a confirmed -counterpart. "Confirmed" means you read the Origin operation's description and -parameters and they answer the same question the GitHub call answers. A name -match is a candidate, never a result. - -### REST calls - -1. **Normalize the GitHub path** and look for the same shape under - `/v1/origin`: `/repos/{owner}/{repo}` → `/v1/origin/repos/{ownerSlug}/{repoName}`; - `{pull_number}` → `{pullNumber}`; `{ref}`/`{sha}` stay; GitHub's - `check-runs`, `check-suites`, `compare/{basehead}`, `contents`, `git/…`, - `pulls/{n}/{files,commits,reviews,comments,requested_reviewers,merge}`, - `labels`, `branches`, `commits` all have direct or near-direct shapes. - Custom-verb operations on Origin use a `:verb` suffix - (`…/contents:batchGet`, `…/check-runs:batchUpsert`, `…:grep`). -2. **Re-home GitHub's issue-flavored PR calls.** GitHub puts PR conversation - comments under `/issues/{n}/comments` and labels under `/issues/{n}/labels`; - on Origin those live under `/pulls/{n}/…`. This is a path change, not a - gap, when the code only ever uses them on pull requests. When the code - uses them on real issues, see `origin-isms.md` (issues are not an Origin - surface). -3. **Re-home app and installation calls.** GitHub's `/app`, - `/app/installations`, `/app/installations/{id}/access_tokens`, - `/installation/repositories` have Origin counterparts under `/v1/origin/app…` - and `/v1/origin/installation/repos`; check `tokenTypes` to confirm which - credential each takes. GitHub's `/user`, `/user/installations`, - `/orgs/{org}/…`, `/search/…`, and `/repositories/{id}` have no path - counterpart; consult `origin-isms.md` and the Authentication section - before deciding whether they are absent by design, covered by a different - idiom, or a gap. -4. **Compare parameters, not just paths.** A matching path with a missing - filter the code depends on (for example a list the code narrows by a field - Origin does not accept) is `workaround` or `gap`, not `same`. Read the - `parameters` block. -5. **Compare response fields the code reads.** Trace which response - properties the handlers dereference and check them against the Origin - component. Missing fields are common where GitHub inlines convenience data - (web URLs, nested user profiles, aggregate counts). Each missing field gets - its own line under the row: follow-up call, derivable, or absent. - -### GraphQL - -There is no GraphQL endpoint. Decompose each query or mutation into the REST -reads and writes it stands for, then map those individually. Note the fan-out -(one query → N calls) as the tradeoff on the row. If the query exists to -avoid REST pagination or to fetch a cross-repository view, say so; that is -the tradeoff the team weighs. - -### Permissions → scopes - -GitHub permissions are `: read|write`; Origin scopes are -`repository:[:]:` with `write` implying `read` -and `repository:metadata:read` granted automatically. Match the noun, then -verify by finding the operations the code actually calls in the Operations -lookup and reading *their* `x-origin-scopes` — the scope set in the brief is -the union of what the called operations require, not a translation of the -manifest. GitHub permissions with no Origin noun (`statuses`, `issues`, -`members`, `organization_*`, `pages`, `actions`, `workflows`, `secrets`, -`deployments`, `environments`) go through `origin-isms.md` before they can be -called gaps. - -### Webhook events → slugs - -Origin slugs are `[.].` with the -action in the slug, never in the payload: GitHub's single `pull_request` event -with an `action` field is several Origin events. Translate each -`event.action` pair the code handles into a candidate slug, then confirm the -slug appears in the Webhook events lookup. Candidates that do not appear are -not events on Origin; check whether the state change is observable another -way (an event on a related resource, or a REST read on receipt of one) before -classifying. Installation lifecycle events are always delivered to the app; -everything else must be subscribed explicitly, which the brief's hello-world -path calls out. - -### Payload fields → schema properties - -For each field path a handler reads off a GitHub payload -(`payload.pull_request.head.sha`, `payload.repository.full_name`, -`payload.sender.login`, `payload.commits[].added`), find the Origin payload -schema for the mapped slug and walk its properties. Record one of: - -- **present** at ``; -- **follow-up read**: not in the payload, available from `` using - identifiers the payload does carry; -- **derivable**: assembled from present fields (say how, and note whether the - format is contractual); -- **absent**: no payload field and no read that yields it (this row goes to - the gap bar). - -The payload carries references (`repository`, `pullRequest`) around the -snapshot of the one object that changed. Expect a lean shape and plan the -follow-up reads; that is the Origin design, not a defect -(`origin-isms.md` § Webhooks). Compare the webhook snapshot's schema with -the REST resource it mirrors: a field on the REST component that the webhook -twin lacks (for example a list the resource carries but the snapshot omits) -is a follow-up read via the matching `Get…`, and the code that reads it off -the GitHub payload today needs that read on every event. - -### Out-of-domain surfaces and spec-silent behavior - -Some of what a GitHub App touches is not source control at all: Marketplace -billing and plan lookups, merge queues, Actions, Pages, Projects, -Milestones, Discussions, `github.com` HTML probes. When neither the spec nor -`llms-full.txt` mentions the concept, the row is `unknown` with an up-front -question about what the team wants on Origin, never `gap` (Origin has not -declined it; the team may not need it) and never `by-design-absent` (only -`origin-isms.md` rows earn that). - -When the code depends on a behavior the docs do not state (does an event -fire for a pull request opened as a draft? does `updatedAt` move on a -comment? what does a remove return when nothing was there?), do not resolve -it from GitHub's behavior. Record the dependency in the row's Open question -column and add a step to the hello-world path that observes it on a native -repository. - -## Provenance in the brief - -Write the spec's `info.version`, the fetch timestamp, and the four URLs into -the brief's provenance block. State plainly that the brief reflects the -surface on that date and that the team should re-read the changelog before -acting on any row marked `workaround` or `gap`, because those are the rows -most likely to have moved. +1. **Operations**: `operationId`, method, path, `x-origin-scopes`, + visibility, parameter names, request top-level fields, response component. +2. **Webhook events**: slug → schema → properties and `$ref`s (or the + `x-origin-webhook-resource` target). From `llms-full.txt` § Webhooks, + which slugs are app-lifecycle (always delivered) versus repository events + (must be selected). +3. **Scopes**: union of every `scopes` value, annotated with the operations + that need it; separate installation-requestable scopes from ambient and + user-only ones (the latter tell you which GitHub flows have no app-side + equivalent by construction). +4. **Resources**: component schemas returned by `Get…`/`List…`, with field + names, for "does the Origin object carry this field". + +`scripts/index-origin-spec.py openapi.yaml [ops|events|scopes|schema ]` +prints all four. + +## Matching + +Match in this order; stop at the first rule that yields a *confirmed* +counterpart. Confirmed means you read the Origin operation's description and +parameters and it answers the same question. A name match is a candidate, +never a result. + +**REST calls** + +1. Normalize the GitHub path to the same shape under `/v1/origin` + (`{owner}/{repo}` → `{ownerSlug}/{repoName}`, `{pull_number}` → + `{pullNumber}`; custom verbs are `:verb` suffixes such as + `…/contents:batchGet`). +2. Re-home GitHub's issue-flavored PR calls: `/issues/{n}/comments` and + `/issues/{n}/labels` used *on a pull request* live under `/pulls/{n}/…`. + Path change, not a gap. Used on real issues: `origin-isms.md`. +3. Re-home `/app`, `/app/installations`, access-token minting, and + `/installation/repositories` under `/v1/origin/app…` and + `/v1/origin/installation/repos`; confirm `tokenTypes`. `/user`, + `/user/installations`, `/orgs/…`, `/search/…`, `/repositories/{id}` have + no path counterpart: consult `origin-isms.md` before labeling. +4. Compare parameters, not just paths. A matching path missing a filter the + code depends on is `workaround` or `gap`, not `same`. +5. Compare response fields the code reads. Each missing field gets its own + line: follow-up call, derivable, or absent. GitHub inlines convenience + data (web URLs, nested profiles, counts) that Origin does not. + +**GraphQL.** No endpoint. Decompose each document into the REST reads and +writes it stands for, map those, and record the fan-out as the row's +tradeoff. + +**Permissions → scopes.** Do not translate the manifest noun-for-noun. Find +the operations the code calls and take the union of *their* +`x-origin-scopes.scopes`. GitHub permissions with no Origin noun +(`statuses`, `issues`, `members`, `organization_*`, `pages`, `actions`, +`workflows`, `deployments`) go through `origin-isms.md` first. + +**Events → slugs.** Each GitHub `event` + `action` pair becomes a candidate +slug (`pull_request` + `synchronize` → `pull_request.head_ref.pushed`); +confirm it exists in the webhook index. A candidate that does not exist is +not an event on Origin; check whether the state change is observable another +way before classifying. + +**Payload fields → schema properties.** For each field path a handler reads, +walk the mapped slug's payload schema and record one of: **present** at +``; **follow-up read** via `` with identifiers the payload +carries; **derivable** from present fields (say how, and whether the format +is contractual); **absent** (goes to the gap bar). A field on the REST +component that the webhook twin lacks is a follow-up `Get…` on every event. + +## Out of domain and spec-silent + +- A concept neither the spec nor `llms-full.txt` mentions (Marketplace + billing, merge queues, Actions, Pages, Projects, Discussions, HTML probes) + is `unknown` with an up-front question. Never `gap` (Origin has not + declined it) and never `by-design-absent` (only `origin-isms.md` rows earn + that). +- A behavior the code depends on that the docs do not state (does an event + fire for a draft PR? does `updatedAt` move on a comment?) is an open + question plus a hello-world step that observes it on a native repository. + Do not resolve it from GitHub's behavior. From e97c83e73109f85d0202bc135467e5ddfc3eacab Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 24 Sep 2026 15:50:10 +0000 Subject: [PATCH 04/26] Unslop pass over skills, references, README, and manifests Wording only. Removes em dashes, connector colons, semicolon chains, and passive voice; replaces "surface" with the concrete noun; one thought per sentence. Rules and content unchanged. Plugin description is one new identical string across the three manifests, both marketplace entries, and the README row. Co-authored-by: ali.nikseresht --- .claude-plugin/marketplace.json | 2 +- .cursor-plugin/marketplace.json | 2 +- README.md | 2 +- origin-apps/.claude-plugin/plugin.json | 2 +- origin-apps/.cursor-plugin/plugin.json | 2 +- origin-apps/CHANGELOG.md | 20 ++-- origin-apps/README.md | 86 +++++++------- origin-apps/plugin.json | 2 +- origin-apps/skills/origin-api/SKILL.md | 111 +++++++++--------- .../skills/port-github-app-to-origin/SKILL.md | 73 ++++++------ .../references/brief-template.md | 95 +++++++-------- .../references/discovery.md | 86 +++++++------- .../references/gap-bar.md | 81 ++++++------- .../references/origin-isms.md | 18 +-- .../references/spec-mapping.md | 85 +++++++------- 15 files changed, 340 insertions(+), 327 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 505ac1ada..8d18083d7 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -8,7 +8,7 @@ { "name": "origin-apps", "source": "./origin-apps", - "description": "Build on the Cursor Origin API: fetch the live spec first, then apply durable practices for credentials, scopes, webhooks, paging, and errors; includes a skill that plans the port of an existing GitHub App." + "description": "Skills for building on the Cursor Origin API. Fetch the live spec first, then follow the Origin rules for credentials, scopes, webhooks, paging, and errors. Includes a skill that plans the port of an existing GitHub App." } ] } diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index 484fedf2b..bf0d0b7a4 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -66,7 +66,7 @@ { "name": "origin-apps", "source": "origin-apps", - "description": "Build on the Cursor Origin API: fetch the live spec first, then apply durable practices for credentials, scopes, webhooks, paging, and errors; includes a skill that plans the port of an existing GitHub App." + "description": "Skills for building on the Cursor Origin API. Fetch the live spec first, then follow the Origin rules for credentials, scopes, webhooks, paging, and errors. Includes a skill that plans the port of an existing GitHub App." }, { "name": "orchestrate", diff --git a/README.md b/README.md index a6418fb7f..59acad9d5 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Official Cursor plugins for popular developer tools, frameworks, and SaaS produc | `pr-review-canvas` | [PR Review Canvas](pr-review-canvas/) | Cursor | Developer Tools | Render PR diffs as review canvases grouped by importance. | | `docs-canvas` | [Docs Canvas](docs-canvas/) | Cursor | Developer Tools | Render documentation as a navigable canvas. | | `cursor-sdk` | [Cursor SDK](cursor-sdk/) | Cursor | Developer Tools | Build apps, scripts, and automations with the TypeScript SDK. | -| `origin-apps` | [Origin Apps](origin-apps/) | Cursor | Developer Tools | Build on the Cursor Origin API: fetch the live spec first, then apply durable practices for credentials, scopes, webhooks, paging, and errors; includes a skill that plans the port of an existing GitHub App. | +| `origin-apps` | [Origin Apps](origin-apps/) | Cursor | Developer Tools | Skills for building on the Cursor Origin API. Fetch the live spec first, then follow the Origin rules for credentials, scopes, webhooks, paging, and errors. Includes a skill that plans the port of an existing GitHub App. | | `orchestrate` | [Orchestrate](orchestrate/) | Cursor | Developer Tools | Fan large tasks out across parallel cloud agents with planners, workers, verifiers, and structured handoffs. | | `pstack` | [pstack](pstack/) | Lauren Tan | Developer Tools | if you want to go fast, go deep first. pstack helps you write less, but higher quality code. rigorous agent workflows you can parallelize with confidence. | | `advisor` | [Advisor](advisor/) | Cursor | Developer Tools | Consult a stronger model before major decisions, when stuck, and before declaring done. | diff --git a/origin-apps/.claude-plugin/plugin.json b/origin-apps/.claude-plugin/plugin.json index b09982da5..dde7dcf6d 100644 --- a/origin-apps/.claude-plugin/plugin.json +++ b/origin-apps/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "origin-apps", - "description": "Build on the Cursor Origin API: fetch the live spec first, then apply durable practices for credentials, scopes, webhooks, paging, and errors; includes a skill that plans the port of an existing GitHub App.", + "description": "Skills for building on the Cursor Origin API. Fetch the live spec first, then follow the Origin rules for credentials, scopes, webhooks, paging, and errors. Includes a skill that plans the port of an existing GitHub App.", "version": "0.1.0", "author": { "name": "Cursor", diff --git a/origin-apps/.cursor-plugin/plugin.json b/origin-apps/.cursor-plugin/plugin.json index edd88eed6..cefa3aa43 100644 --- a/origin-apps/.cursor-plugin/plugin.json +++ b/origin-apps/.cursor-plugin/plugin.json @@ -2,7 +2,7 @@ "name": "origin-apps", "displayName": "Origin Apps", "version": "0.1.0", - "description": "Build on the Cursor Origin API: fetch the live spec first, then apply durable practices for credentials, scopes, webhooks, paging, and errors; includes a skill that plans the port of an existing GitHub App.", + "description": "Skills for building on the Cursor Origin API. Fetch the live spec first, then follow the Origin rules for credentials, scopes, webhooks, paging, and errors. Includes a skill that plans the port of an existing GitHub App.", "author": { "name": "Cursor", "email": "plugins@cursor.com" diff --git a/origin-apps/CHANGELOG.md b/origin-apps/CHANGELOG.md index f7285b41c..38ba40c99 100644 --- a/origin-apps/CHANGELOG.md +++ b/origin-apps/CHANGELOG.md @@ -2,14 +2,14 @@ All notable changes to this plugin will be documented here. -## 0.1.0 — initial release +## 0.1.0, initial release -- Added the `origin-api` skill: fetch the live Origin docs and OpenAPI spec - first, then apply the practices that hold across spec versions (credentials - and token minting, minimal scopes, webhook verification and idempotency, - opaque page tokens, TypeIDs, the error envelope, rate limits, and the - deliberate differences from GitHub). -- Added the `port-github-app-to-origin` skill, built on `origin-api`: discover - a GitHub App's surface from its codebase, map it onto the live Origin API - spec, and write a porting brief with a capability table, webhook field map, - hello-world path, gap cards, and up-front questions. +- Added the `origin-api` skill. It lists the live Origin docs and OpenAPI spec + to fetch first, then the Origin rules that GitHub habits get wrong: + credentials and token minting, minimal scopes, webhook verification and + idempotency, opaque page tokens, TypeIDs, the error envelope, rate limits, + and the differences from GitHub that are decisions rather than gaps. +- Added the `port-github-app-to-origin` skill, built on `origin-api`. It reads + what a GitHub App uses from GitHub out of its codebase, maps that onto the + live Origin spec, and writes a porting brief with a capability table, a + webhook field map, a hello-world path, gap cards, and up-front questions. diff --git a/origin-apps/README.md b/origin-apps/README.md index c6e4f05f7..785328a94 100644 --- a/origin-apps/README.md +++ b/origin-apps/README.md @@ -1,70 +1,70 @@ # Origin Apps -Plugin for building on [Cursor Origin](https://cursor.com/docs/api/origin), -Cursor's code forge: creating an Origin App, calling the API, receiving -webhooks, or bringing an existing GitHub App across. Skills only, so it runs -in Cursor, Claude Code, Codex, and any agent that reads -[Agent Skills](https://agentskills.io). +Two skills for building on [Cursor Origin](https://cursor.com/docs/api/origin), +Cursor's code forge. They cover creating an Origin App, calling the API, +receiving webhooks, and bringing an existing GitHub App across. The plugin is +skills only, so it runs in Cursor, Claude Code, Codex, and any agent that +reads [Agent Skills](https://agentskills.io). ## What it includes -- `origin-api`: the general skill. Fetch-first sources (the live OpenAPI spec - and docs are the only source for endpoints, scopes, and event slugs) plus a - checklist of the Origin gotchas GitHub instinct gets wrong: native vs - mirrored repositories, opt-in webhook events, `v1ed` signatures, `deliveryId` - idempotency, lean payloads, credential kinds and token minting, scopes from - `x-origin-scopes`, opaque page tokens, TypeIDs, `404` semantics, rate - limits, and the deliberate differences from GitHub. Use it for any Origin - work. -- `port-github-app-to-origin`: builds on `origin-api`. Run it inside your - GitHub App's repository. It discovers the app's GitHub surface from the code, - maps it onto the live spec, and writes a porting brief: capability table, - webhook fields your handlers read and where each comes from on Origin, - scopes to request, hello-world path, gaps worth raising with Cursor, and the - questions to settle first. It plans; it writes no code and estimates no - time. - -Both fetch the spec at run time and never name an endpoint from memory. +`origin-api` is the general skill. It lists the sources to fetch first (the +live OpenAPI spec and docs are the only source for endpoints, scopes, and +event slugs) and then the Origin rules that GitHub habits get wrong. Native +versus mirrored repositories. Opt-in webhook events. `v1ed` signatures. +`deliveryId` idempotency. Lean payloads. Credential kinds and token minting. +Scopes from `x-origin-scopes`. Opaque page tokens. TypeIDs. `404` semantics. +Rate limits. The differences from GitHub that are decisions, not gaps. Use it +for any Origin work. + +`port-github-app-to-origin` builds on `origin-api`. Run it inside your GitHub +App's repository. It reads what the app uses from GitHub out of the code, maps +that onto the live spec, and writes a porting brief with a capability table, +the webhook fields your handlers read and where each comes from on Origin, +the scopes to request, a hello-world path, the gaps worth raising with Cursor, +and the questions to settle first. It plans. It writes no code and estimates +no time. + +Both skills fetch the spec at run time and never name an endpoint from memory. ## When to use -- You are writing or reviewing code that calls Origin, mints installation - tokens, or receives Origin webhooks: `origin-api`. -- You are creating an Origin App and want the hello-world path and the - first-week traps up front: `origin-api`. -- You have a GitHub App (Probot, Octokit, go-github, hand-rolled) and want to - know what an Origin App version looks like before you start: +- Writing or reviewing code that calls Origin, mints installation tokens, or + receives Origin webhooks: `origin-api`. +- Creating an Origin App and wanting the first-week traps up front: + `origin-api`. +- Holding a GitHub App (Probot, Octokit, go-github, hand-rolled) and wanting + to know what an Origin App version looks like before starting: `port-github-app-to-origin`. -- You want to check which GitHub features Origin deliberately does not - reproduce, and what the Origin idiom is instead: either. +- Checking which GitHub features Origin deliberately does not reproduce, and + what to do instead: either skill. -In Cursor, ask about the Origin API or ask to port the app; or run +In Cursor, ask about the Origin API or ask to port the app, or run `/origin-api` or `/port-github-app-to-origin`. ## Install in Cursor -Search for **Origin Apps** in the Cursor Marketplace +Search for Origin Apps in the Cursor Marketplace ([cursor.com/marketplace/origin-apps](https://cursor.com/marketplace/origin-apps)), -or open **Customize**, find the plugin, and install it at user or project -scope. +or open Customize, find the plugin, and install it at user or project scope. ## Use outside Cursor -The plugin ships three manifests for one set of skills: a root `plugin.json` +The plugin ships three manifests for one set of skills. A root `plugin.json` ([Agent Plugins](https://agent-plugins.org) 1.0), `.cursor-plugin/plugin.json` (Cursor Marketplace), and `.claude-plugin/plugin.json` (Claude Code). The skills use only portable frontmatter (`name`, `description`, `license`, `compatibility`). -**Claude Code**, via the marketplace manifest at this repository's root: +Claude Code, via the marketplace manifest at this repository's root: ```text /plugin marketplace add cursor/plugins /plugin install origin-apps@cursor-plugins ``` -**Any agent that reads Agent Skills** (Claude Code, Codex, and others): copy -the skill directories into the agent's skills folder. Copy both; the porting +Any agent that reads Agent Skills (Claude Code, Codex, and others): copy the +skill directories into the agent's skills folder. Copy both. The porting skill refers to `origin-api` for fundamentals. ```bash @@ -80,9 +80,9 @@ mkdir -p .cursor/skills && cp -r plugins/origin-apps/skills/* .cursor/skills/ ## Requirements - Network access to `https://cursor.com/docs/api/origin/*` during the run. -- For the porting skill: read access to the app's source. No Origin - credentials are needed to produce the brief; the hello-world path in the - brief is what you follow afterwards. +- For the porting skill, read access to the app's source. Producing the brief + needs no Origin credentials. You follow the brief's hello-world path + afterwards. - Optional: `python3` with PyYAML for the porting skill's `scripts/index-origin-spec.py`, which turns the fetched spec into a grep-friendly index. Without it the skill reads the spec directly. @@ -90,8 +90,8 @@ mkdir -p .cursor/skills && cp -r plugins/origin-apps/skills/* .cursor/skills/ ## Where the brief goes The porting skill writes `ORIGIN-PORTING-BRIEF.md` at the repository root and -prints its path. Gap cards in the brief are yours to send: through your shared -Slack channel with Cursor if you have one, or to `hi@cursor.com`. +prints its path. The gap cards in the brief are yours to send, through your +shared Slack channel with Cursor if you have one, or to `hi@cursor.com`. ## License diff --git a/origin-apps/plugin.json b/origin-apps/plugin.json index 61a2f288b..75ab6a385 100644 --- a/origin-apps/plugin.json +++ b/origin-apps/plugin.json @@ -2,7 +2,7 @@ "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", "name": "origin-apps", "version": "0.1.0", - "description": "Build on the Cursor Origin API: fetch the live spec first, then apply durable practices for credentials, scopes, webhooks, paging, and errors; includes a skill that plans the port of an existing GitHub App.", + "description": "Skills for building on the Cursor Origin API. Fetch the live spec first, then follow the Origin rules for credentials, scopes, webhooks, paging, and errors. Includes a skill that plans the port of an existing GitHub App.", "author": { "name": "Cursor", "email": "plugins@cursor.com" diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md index d670dc84c..eb6e1ae5e 100644 --- a/origin-apps/skills/origin-api/SKILL.md +++ b/origin-apps/skills/origin-api/SKILL.md @@ -1,11 +1,11 @@ --- name: origin-api description: >- - Build on the Cursor Origin API: create an Origin App, authenticate as it, + Build on the Cursor Origin API. Create an Origin App, authenticate as it, call the REST API, receive webhooks. Use whenever code or a plan touches Origin endpoints, installation tokens, scopes, webhook subscriptions or - signatures, page tokens, or an Origin App manifest. Fetch-first sources plus - the gotchas GitHub instinct gets wrong. + signatures, page tokens, or an Origin App manifest. Lists the sources to + fetch first and the Origin rules that GitHub habits get wrong. license: MIT compatibility: >- Needs network access to https://cursor.com/docs/api/origin/* at run time. @@ -13,68 +13,73 @@ compatibility: >- # Build on the Origin API -## Fetch first; never name an endpoint, scope, slug, or header from memory +## Fetch first. Never name an endpoint, scope, slug, or header from memory. -- `https://cursor.com/docs/api/origin/openapi.yaml` — the contract. Every - operation carries `x-origin-scopes` (`scopes`, `tokenTypes`, `ambient`); - every webhook payload schema carries `x-origin-webhook-events`, the only +- `https://cursor.com/docs/api/origin/openapi.yaml` is the contract. Every + operation carries `x-origin-scopes` (`scopes`, `tokenTypes`, `ambient`). + Every webhook payload schema carries `x-origin-webhook-events`, the only authoritative list of subscribable slugs. -- `https://cursor.com/docs/api/origin/llms-full.txt` — installation flow, - authentication, scopes table, mirrored repositories, webhook headers and - verification, delivery envelope, retries, pagination, errors, limitations. -- `https://cursor.com/docs/api/origin/llms.txt` (index) and - `https://cursor.com/docs/api/origin/changelog` (what moved). +- `https://cursor.com/docs/api/origin/llms-full.txt` covers the installation + flow, authentication, the scopes table, mirrored repositories, webhook + headers and verification, the delivery envelope, retries, pagination, + errors, and current limitations. +- `https://cursor.com/docs/api/origin/llms.txt` is the index. + `https://cursor.com/docs/api/origin/changelog` says what moved. -Cite `operationId`s and `llms-full.txt` anchors. Where this file and the -fetched docs disagree, the docs win. +Cite `operationId`s and `llms-full.txt` anchors. If this file and the fetched +docs disagree, the docs win. -## Gotchas +## Rules GitHub habits get wrong -- **Native or mirror, first.** Apps get full scopes only on Origin-native - repositories. A repository mirrored from GitHub returns `403` on every write - and never delivers `repository.pushed`; the ping succeeds and then nothing - else arrives. +- **Check native or mirror before anything else.** Apps get full scopes only + on Origin-native repositories. A repository mirrored from GitHub returns + `403` on every write and never delivers `repository.pushed`. The ping + succeeds and then nothing else arrives. - **Only installation lifecycle events are delivered by default.** Select - every other event in app settings; an unselected event is silence, not an + every other event in app settings. An unselected event is silence, not an error. -- **Signature `v1ed` is Ed25519 over a SHA-256 digest of the raw body**, keys - from Origin's JWKS. It is Standard Webhooks except for the digest, so - off-the-shelf verifiers fail unmodified. Verify the raw body before parsing; - reject `webhook-timestamp` more than five minutes off. Headers are - `webhook-*`, not `x-github-*`; after verification the body is authoritative. -- **`deliveryId` is the idempotency key** (stable across retries); - `event.id` is the domain event. Return `2xx` after verification and process - asynchronously: persistent failure pauses delivery for the app. -- **Payloads are lean snapshots** of the one object that changed plus - container references: no changed-file lists, before-SHAs, web URLs, or - inlined profiles. Follow up with the `Get…` for the object; count the - fan-out. The action is in the slug (`pull_request.review.submitted`); there +- **Signature `v1ed` is Ed25519 over a SHA-256 digest of the raw body**, with + keys from Origin's JWKS. It matches Standard Webhooks except for the digest, + so off-the-shelf verifiers fail unmodified. Verify the raw body before + parsing. Reject `webhook-timestamp` more than five minutes off. Headers are + `webhook-*`, not `x-github-*`. After verification the body is authoritative. +- **`deliveryId` is the idempotency key.** It is stable across retries. + `event.id` identifies the domain event. Return `2xx` after verification and + process asynchronously, because persistent failure pauses delivery for the + app. +- **Payloads are lean snapshots** of the one object that changed, plus + references to its containers. No changed-file lists, before-SHAs, web URLs, + or inlined profiles. Follow up with the `Get…` for the object and count the + fan-out. The action is in the slug (`pull_request.review.submitted`). There is no `action` field. -- **The installation receipt JWT is proof of consent, never a Bearer token.** +- **The installation receipt JWT proves consent. It is never a Bearer token.** Its `sub` is the installation ID. -- **App JWT is EdDSA over Ed25519, not RS256.** Register only the public key. -- **Installation tokens are short-lived; mint just in time** from the app JWT - and attenuate to the scopes and `repositoryIds` the job needs (IDs, not - slugs). Git over HTTPS is Basic auth, user `x-access-token`, token as - password; Bearer is REST only. -- **Scopes come from the operations you call**: request the union of their - `x-origin-scopes.scopes`. `write` implies `read`; `repository:metadata:read` - is automatic; `ambient: true` needs no request. Operations whose +- **The app JWT is EdDSA over Ed25519, not RS256.** Register only the public + key. +- **Installation tokens are short-lived. Mint them just in time** from the + app JWT and attenuate to the scopes and `repositoryIds` the job needs (IDs, + not slugs). Git over HTTPS uses Basic auth with user `x-access-token` and + the token as password. Bearer is REST only. +- **Scopes come from the operations you call.** Request the union of their + `x-origin-scopes.scopes`. `write` implies `read`. `repository:metadata:read` + is automatic. `ambient: true` needs no request. Operations whose `tokenTypes` is user-only (create app, add repositories to an installation, - mirror transitions) have no app-side path; there is no `/user` analog. + mirror transitions) have no app-side path, and there is no `/user` analog. - **Page tokens are opaque and bound to the resource and filters.** Never - construct, parse, or reuse across filter changes. Send `pageSize` on every - request, including continuations. No `Link` header, no total. + construct, parse, or reuse one across filter changes. Send `pageSize` on + every request, including continuations. There is no `Link` header and no + total. - **IDs are TypeIDs** (`repo_…`, `i_…`, `cmt_…`), never integers. Cache IDs, - not slugs; `/repos/_/{repoId}` survives renames. 64-bit integers (PR + not slugs. `/repos/_/{repoId}` survives renames. 64-bit integers (PR numbers, versions) are JSON strings. Defaults are present (`false`, `0`, `[]`), so a present `false` is a value. -- **`404` is not-found *or* no-access.** Branch on status and `code`, never - message text. Quote `X-Request-ID` when escalating. -- **Rate limit is a per-principal point budget**; honor `Retry-After` on - `429`. Git HTTPS is metered separately. Per-app raises exist; ask. -- **Deliberate differences, not gaps:** no commit statuses (check runs upsert - on a caller-stable `key`); no Issues (conversation is PR comments, threads, - reviews, labels); no GraphQL; no per-repository webhook CRUD; no user or - email directory; reviews anchor to a pull request *version*, not a SHA; a +- **`404` means not found or no access.** Branch on status and `code`, never + on message text. Quote `X-Request-ID` when escalating. +- **The rate limit is a per-principal point budget.** Honor `Retry-After` on + `429`. Git HTTPS is metered separately. Cursor raises per-app budgets on + request. +- **These are decisions, not gaps.** No commit statuses (check runs upsert on + a caller-stable `key`). No Issues (conversation is PR comments, threads, + reviews, and labels). No GraphQL. No per-repository webhook CRUD. No user or + email directory. Reviews anchor to a pull request version, not a SHA. A thread materializes from its first diff-anchored comment. diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index dbccfa728..0f17e23e0 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -4,8 +4,8 @@ description: >- Plan the port of an existing GitHub App to a Cursor Origin App. Use when a repo is a GitHub App (manifest, Probot, Octokit or another GitHub SDK, webhook signature handlers) and the task is to bring it to Origin or compare it with - the Origin API. Maps the app's surface onto the live Origin spec and writes a - porting brief. No code. + the Origin API. Maps what the app uses from GitHub onto the live Origin spec + and writes a porting brief. No code. license: MIT compatibility: >- Needs network access to https://cursor.com/docs/api/origin/* at run time. @@ -15,53 +15,58 @@ compatibility: >- # Port a GitHub App to an Origin App -Run inside the GitHub App's codebase. Output is a **porting brief** -(`references/brief-template.md`), not an implementation: what maps, what -changes shape, what is absent on purpose, what is worth raising with Cursor. +Run inside the GitHub App's codebase. The output is a porting brief +(`references/brief-template.md`), not an implementation. It says what maps, +what changes shape, what is absent on purpose, and what is worth raising with +Cursor. -Fundamentals (which docs to fetch, credentials, scopes, webhooks, paging, -IDs, errors, deliberate differences) are the `origin-api` skill in this -plugin. Follow it first; nothing here repeats it. Two rules on top: +The `origin-api` skill in this plugin covers which docs to fetch, credentials, +scopes, webhooks, paging, IDs, errors, and the deliberate differences from +GitHub. Follow it first. Nothing here repeats it. Two rules on top: 1. **Discover, do not ask.** Read permissions, events, handlers, calls, token minting, and the receiver out of the code. Never ask for a manifest or an - endpoint list. What is genuinely undiscoverable becomes an open question. + endpoint list. Anything you cannot find becomes an open question. 2. **Departures are decisions, not omissions.** Anything in - `references/origin-isms.md` is `by-design-absent` or `reshaped` with a - pointer to the idiom, never a gap card. + `references/origin-isms.md` is `by-design-absent` or `reshaped`, with a + pointer to the idiom. It never becomes a gap card. ## Procedure -1. **Load the spec** (`origin-api` § Fetch first). Record `info.version` and +1. **Load the spec** (`origin-api`, "Fetch first"). Record `info.version` and the fetch time for the brief's provenance. Build the mapping index per - `references/spec-mapping.md`; `scripts/index-origin-spec.py openapi.yaml` - prints it (needs PyYAML). -2. **Discover** per `references/discovery.md`: a file and line for every fact, - including payload fields read only for logging and calls the framework - makes on the app's behalf. Note what you looked for and did not find. -3. **Map** each inventory row (`references/spec-mapping.md` § Matching) and - label it with the parity labels in the brief template. Map webhook payload - *fields* the code reads, not just event names; a field GitHub inlines is - often a follow-up read on Origin, so name the call per field. Then: - - `origin-isms.md` before `gap`; `gap-bar.md` before any card. - - A GitHub surface the Origin docs never mention (Marketplace billing, - merge queues, Actions, Pages) is `unknown` with a question, never `gap`. + `references/spec-mapping.md`. `scripts/index-origin-spec.py openapi.yaml` + prints it and needs PyYAML. +2. **Discover** per `references/discovery.md`. Record a file and line for + every fact, including payload fields read only for logging and calls the + framework makes on the app's behalf. Note what you looked for and did not + find. +3. **Map** each inventory row (`references/spec-mapping.md`, "Matching") and + label it with the parity labels in the brief template. Map the webhook + payload fields the code reads, not only the event names. A field GitHub + inlines is often a follow-up read on Origin, so name the call for each + field. Then: + - Check `origin-isms.md` before writing `gap`. Check `gap-bar.md` before + writing any card. + - A GitHub feature the Origin docs never mention (Marketplace billing, + merge queues, Actions, Pages) is `unknown` with a question. It is never + `gap`. - A behavior the code depends on that the docs neither confirm nor deny (does event X fire in case Y? does `updatedAt` move on comments?) is a - question plus a hello-world step that observes it; never guess it into + question plus a hello-world step that observes it. Never guess it into `same` from GitHub behavior. 4. **Write the brief** from the template in full. Every Origin cell names an - `operationId`, slug, or `llms-full.txt` anchor. Sizes are S/M/L, never - time. + `operationId`, a slug, or an `llms-full.txt` anchor. Sizes are S, M, or L, + never time. 5. **Close with the questions**, pruned to what discovery left open. The - first is always native-or-mirror: it decides whether the app receives - events at all. + first is always native or mirror, because it decides whether the app + receives events at all. ## Not in scope -Writing port code or adapters; choosing a language, framework, or client; -estimating in time; asking for anything the codebase contains; sending gap -cards to Cursor (the brief carries them; the team decides). +Writing port code or adapters. Choosing a language, framework, or client. +Estimating in time. Asking for anything the codebase contains. Sending gap +cards to Cursor. The brief carries them and the team decides. ## Reference files @@ -69,8 +74,8 @@ cards to Cursor (the brief carries them; the team decides). | --- | --- | | `../origin-api/SKILL.md` | First. Sources and fundamentals. | | `references/discovery.md` | Scanning the codebase. | -| `references/spec-mapping.md` | Building the index; matching calls, events, and fields. | +| `references/spec-mapping.md` | Building the index. Matching calls, events, and fields. | | `references/origin-isms.md` | Labeling a missing GitHub feature. | -| `references/gap-bar.md` | Deciding whether a difference earns a card; writing it. | +| `references/gap-bar.md` | Deciding whether a difference earns a card, and writing it. | | `references/brief-template.md` | Writing the output. | | `scripts/index-origin-spec.py` | Turning `openapi.yaml` into a grep-friendly index. Optional. | diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md index f20b5ae31..04c9e23f7 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md +++ b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md @@ -1,11 +1,11 @@ # Porting brief template -One Markdown file at the repository root (`ORIGIN-PORTING-BRIEF.md` unless the -team's docs convention says otherwise); print its path. Fill every section; an -empty section says so in one line rather than disappearing. Cite spec -`operationId`s and `llms-full.txt` anchors; cite the team's code by -`file:line`. A table row per capability, a line per follow-up field, a card -per gap; the team will argue over it in one sitting. +Write one Markdown file at the repository root (`ORIGIN-PORTING-BRIEF.md` +unless the team's docs convention says otherwise) and print its path. Fill +every section. An empty section says so in one line rather than disappearing. +Cite spec `operationId`s and `llms-full.txt` anchors. Cite the team's code by +`file:line`. One table row per capability, one line per follow-up field, one +card per gap. The team will argue over it in one sitting. ## Labels @@ -13,29 +13,30 @@ per gap; the team will argue over it in one sitting. | Label | Meaning | | --- | --- | -| `same` | Same capability, same shape; a path or field rename at most. | -| `reshaped` | Same capability, different shape (pagination, identifier form, event granularity, key semantics). Code changes, behavior does not. | -| `workaround` | Same outcome by a different route (follow-up read, client-side filter, marker). Tradeoff column mandatory. | +| `same` | Same capability, same shape. A path or field rename at most. | +| `reshaped` | Same capability, different shape (pagination, identifier form, event granularity, key semantics). The code changes, the behavior does not. | +| `workaround` | Same outcome by a different route (follow-up read, client-side filter, marker). The Tradeoff column is mandatory. | | `by-design-absent` | Origin deliberately does not reproduce it (`origin-isms.md`). Names the idiom or "no equivalent; decision needed". | | `gap` | No workaround, or one that fails `gap-bar.md`. Has a card in § 6. | | `unknown` | Discovery or the spec could not answer. Has a question in § 7. | -| `preview` (suffix) | Origin operation is `x-cursor-visibility: PREVIEW`. Usable; shape may move. | +| `preview` (suffix) | The Origin operation is `x-cursor-visibility: PREVIEW`. Usable. The shape may move. | **Size** (kind of change, never time) | Size | Meaning | | --- | --- | -| S | Adapter or client layer: path, header, identifier, or pagination rewrite; re-keyed lookup. | -| M | New code path: a follow-up read where the payload sufficed, a handshake step, a new handler, a data-model change for a new identifier or version concept. | -| L | Product or architecture change: a flow that depended on user OAuth, a customer-visible behavior, a dependency on native repositories, an open gap card. | +| S | Adapter or client layer. A path, header, identifier, or pagination rewrite, or a re-keyed lookup. | +| M | A new code path. A follow-up read where the payload used to suffice, a handshake step, a new handler, a data-model change for a new identifier or version concept. | +| L | A product or architecture change. A flow that depended on user OAuth, a customer-visible behavior, a dependency on native repositories, an open gap card. | ## Template ```markdown -# Origin porting brief — +# Origin porting brief for -Planning document. Maps this GitHub App's surface onto the Cursor Origin API -as published on . No decisions about language, framework, or client. +Planning document. Maps what this GitHub App uses from GitHub onto the Cursor +Origin API as published on . It makes no decisions about language, +framework, or client. ## Provenance @@ -43,12 +44,12 @@ as published on . No decisions about language, framework, or client. - Docs read: - Codebase: `` at `` - Re-check `workaround` and `gap` rows against the changelog before work - starts; they move most. + starts. They are the rows most likely to have moved. ## 1. What the app is today -One paragraph: what it does for its users, which events drive it, what it -writes back. Then: +One paragraph on what it does for its users, which events drive it, and what +it writes back. Then: | Facet | Finding | Evidence | | --- | --- | --- | @@ -65,9 +66,9 @@ writes back. Then: ## 2. First decision: which repositories Apps have full -scopes only on Origin-native repositories and stable outbound mirrors; -repositories mirrored from GitHub are read-only to apps and deliver no push -events. **Question 1 must be answered before § 5 is attempted.** +scopes only on Origin-native repositories and stable outbound mirrors. +Repositories mirrored from GitHub are read-only to apps and deliver no push +events. **Answer question 1 before attempting § 5.** ## 3. Capability table @@ -79,16 +80,16 @@ dependency makes on the app's behalf, marked as such. | GitHub thing (evidence) | Origin equivalent | Parity | Size | Tradeoff | Open question | | --- | --- | --- | --- | --- | --- | -| `GET /repos/{o}/{r}/pulls/{n}` (`src/x.ts:12`) | `` | same | S | — | — | +| `GET /repos/{o}/{r}/pulls/{n}` (`src/x.ts:12`) | `` | same | S | none | none | The Origin column names an `operationId`, a slug, a `llms-full.txt` anchor, -or `none`. `workaround` rows fill Tradeoff; `gap` rows link their card; -`by-design-absent` rows name the idiom; `unknown` rows name their question. +or `none`. `workaround` rows fill Tradeoff. `gap` rows link their card. +`by-design-absent` rows name the idiom. `unknown` rows name their question. **Scopes to request:** the union of `x-origin-scopes.scopes` across every Origin operation above that an installation token can call, minus ambient -and implied scopes (`write` implies `read`; `repository:metadata:read` is -automatic). This is what the install URL's `scope` parameter carries. +and implied scopes (`write` implies `read`, and `repository:metadata:read` is +automatic). The install URL's `scope` parameter carries this list. ## 4. Webhook payload fields the code reads @@ -97,39 +98,41 @@ automatic). This is what the install URL's `scope` parameter carries. | `pull_request.synchronize` → `` | `pull_request.head.sha` | present | `payload.pullRequest.head.sha` | | `push` → `` | `commits[].added` | follow-up read | ``, one call per ref update | -"How" is one of: present at ``; present in envelope (`event.type` for -GitHub's `action`); follow-up read via `` with the call count -per event; derivable (from what; is the format documented); absent (→ § 3's -label). Include fields read only for logging. +"How" is one of five values. Present at ``. Present in the envelope +(`event.type` for GitHub's `action`). Follow-up read via ``, +with the call count per event. Derivable, saying from what and whether the +format is documented. Absent, pointing at the row's label in § 3. Include +fields read only for logging. ## 5. Hello-world path -Shortest route to one real event from one native repository. Each step is a -verification, linked to `llms-full.txt`; append one step per spec-silent -behavior the brief depends on, stated as the observation to make. +The shortest route to one real event from one native repository. Each step +is a verification, linked to `llms-full.txt`. Append one step for each +spec-silent behavior the brief depends on, stated as the observation to make. -1. Create the app; register the Ed25519 public key; set webhook URL and - callback. +1. Create the app, register the Ed25519 public key, and set the webhook URL + and callback. 2. Select every repository event from § 3 in app settings. -3. Install on an Origin-native repository; verify the receipt JWT and read +3. Install on an Origin-native repository. Verify the receipt JWT and read the installation ID from `sub`. -4. Mint an app JWT, exchange for an installation token, confirm the - repository is listed and its mirror state matches § 2. +4. Mint an app JWT, exchange it for an installation token, and confirm the + repository is listed with the mirror state § 2 expects. 5. Verify the ping (`v1ed` over the raw body, timestamp skew, `deliveryId` dedupe). 6. Perform the smallest action in § 3 and confirm the slug and the § 4 - fields arrive. Ping but no event: re-check steps 2 and 3 first. + fields arrive. If the ping arrived and this did not, re-check steps 2 and + 3 first. 7. Smallest write from § 3 (check run with a stable `key`, PR comment), confirming its scope is in the grant. ## 6. Gaps worth raising -Zero or more cards in the `gap-bar.md` shape. If zero: "No row failed the gap -bar; the workarounds in § 3 carry their tradeoffs." Do not pad. +Zero or more cards in the `gap-bar.md` shape. If zero, write "No row failed +the gap bar. The workarounds in § 3 carry their tradeoffs." Do not pad. ## 7. Questions for the team -Always the first three; then what discovery left open. +Always the first three, then what discovery left open. 1. Native repositories (or stable outbound mirrors), or repositories mirrored from GitHub? Decides whether the app receives events and can write. @@ -139,12 +142,12 @@ Always the first three; then what discovery left open. Origin? 4. Does anything key approvals or reviews by commit SHA rather than PR version? -5. How do you identify your own check runs / comments / reviews today; can a - key or marker you control replace actor matching? +5. How do you identify your own check runs, comments, and reviews today? Can + a key or marker you control replace actor matching? 6. Do you generate clients from OpenAPI? (Read the changelog for renames.) ## 8. Out of scope No implementation, no SDK or language choice, no time estimates. The brief is -a map; the route is the team's. +a map. The route is the team's. ``` diff --git a/origin-apps/skills/port-github-app-to-origin/references/discovery.md b/origin-apps/skills/port-github-app-to-origin/references/discovery.md index a6258c3b5..bc408bc8b 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/discovery.md +++ b/origin-apps/skills/port-github-app-to-origin/references/discovery.md @@ -1,56 +1,54 @@ -# Discovering the GitHub App's shape from the codebase +# Discovering what the GitHub App uses, from its codebase -Everything the brief needs about the app is in the repository. Search; do not -ask. Record `file:line` for every fact and list every place you looked that -turned up nothing. Note the language and libraries as an observation; they -inform sizes and nothing else. +Everything the brief needs about the app is in the repository. Search for it. +Do not ask for it. Record `file:line` for every fact and list every place you +looked that turned up nothing. Note the language and libraries as an +observation. They inform sizes and nothing else. Seven facets. For each, what to record: 1. **Declared permissions and events.** The manifest or registration snapshot - if checked in (`app.yml`, manifest JSON, IaC that seeds the app; Probot - keeps `default_events` / `default_permissions` in `app.yml`). If absent, - say so and derive permissions from facet 4; the union of what the code - calls is what the port needs anyway. + if it is checked in (`app.yml`, a manifest JSON, IaC that seeds the app; + Probot keeps `default_events` and `default_permissions` in `app.yml`). If + there is none, say so and derive permissions from facet 4. The union of + what the code calls is what the port needs anyway. 2. **Webhook events handled.** Each GitHub event and action pair the code dispatches on (`app.on("pull_request.opened")`, a switch on - `x-github-event` + `payload.action`, SDK parsers), with the handler + `x-github-event` plus `payload.action`, SDK parsers), with the handler location. -3. **Payload fields read.** Every property path dereferenced from the payload - inside each handler and the helpers it passes the payload to. Include - fields used only for logging or metrics; those break dashboards after the - port. -4. **REST and GraphQL calls.** Each distinct call family once (method + path - or SDK method) with the parameters and filters passed, the response fields - read, whether it runs per webhook or in a loop (decides the fan-out - tradeoff), and the pagination style in use (it always changes). GraphQL - documents count as calls; they are decomposed during mapping. -5. **Authentication and token minting.** App JWT algorithm; how the - installation is identified after install (callback query, webhook, DB); - token lifetime handling; whether user OAuth exists and for what (identity, - repository discovery, acting for a user); whether the app clones or pushes - git as itself. -6. **Webhook receiver and verification.** Signature scheme; whether the raw - body is available at verification time (frameworks that parse JSON first - cannot verify); how deliveries are deduplicated, if at all; where the - public URL is configured. -7. **Calls the framework makes on the app's behalf.** Not in the app's - source, but the port has to do them. List them as rows marked "from - `` (documented behavior)" and read the dependency's docs or - source, not the app. Common cases: - - **Probot**: built-in receiver and HMAC verification, per-installation - token minting and caching, `context.repo()` / `context.issue()`, - `context.isBot`; companions `probot-config` (reads `.github/.yml`, - falling back to the owner's `.github` repository), `probot-scheduler` - (lists installations and repositories with the app credential, emits - `schedule.repository`), `probot-metadata` (state in issue bodies). - - **Octokit `App`**: `webhooks.verifyAndReceive`, `eachInstallation` / - `eachRepository`, automatic token minting behind - `getInstallationOctokit`. - - **`ghinstallation`, `githubkit`, `gidgethub`, `octokit.rb` app auth**: - JWT minting and installation-token exchange. +3. **Payload fields read.** Every property path each handler and its helpers + dereference from the payload. Include fields used only for logging or + metrics. Those break dashboards after the port. +4. **REST and GraphQL calls.** Each distinct call family once (method and path, + or SDK method) with the parameters and filters the code passes, the + response fields it reads, whether it runs per webhook or in a loop (this + decides the fan-out tradeoff), and the pagination style in use. Pagination + always changes. GraphQL documents count as calls. Mapping decomposes them. +5. **Authentication and token minting.** The app JWT algorithm. How the code + identifies the installation after install (callback query, webhook, DB). + Token lifetime handling. Whether user OAuth exists and what it is for + (identity, repository discovery, acting for a user). Whether the app clones + or pushes git as itself. +6. **Webhook receiver and verification.** The signature scheme. Whether the + raw body is available at verification time (a framework that parses JSON + first cannot verify). How the code deduplicates deliveries, if it does. + Where the public URL is configured. +7. **Calls the framework makes on the app's behalf.** They are not in the + app's source, but the port has to make them. List them as rows marked + "from `` (documented behavior)" and read the dependency's docs + or source, not the app. Common cases: + - Probot: the built-in receiver and HMAC verification, per-installation + token minting and caching, `context.repo()` and `context.issue()`, + `context.isBot`. Companions: `probot-config` reads `.github/.yml` + and falls back to the owner's `.github` repository; `probot-scheduler` + lists installations and repositories with the app credential and emits + `schedule.repository`; `probot-metadata` stores state in issue bodies. + - Octokit `App`: `webhooks.verifyAndReceive`, `eachInstallation` and + `eachRepository`, token minting behind `getInstallationOctokit`. + - `ghinstallation`, `githubkit`, `gidgethub`, `octokit.rb` app auth: JWT + minting and installation-token exchange. When something is missing, say so in the inventory ("no manifest found (searched: …)", "no signature verification found in the receiver at …"). Each missing item becomes an `unknown` row or an up-front question. Do not -fill gaps with what an app of this kind usually does. +fill it in with what an app of this kind usually does. diff --git a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md index fd732713c..60ee0d17f 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md +++ b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md @@ -1,74 +1,75 @@ # The gap bar and the escalation card -Most differences are not gaps. A brief that files every difference buries -the two or three things Cursor needs to hear. When in doubt, write the row as -`workaround` with an honest tradeoff and an open question, not as a card. +Most differences are not gaps. A brief that files every difference buries the +two or three things Cursor needs to hear. When in doubt, write the row as +`workaround` with the tradeoff and an open question, not as a card. -- A **difference** is any row whose parity label is not `same`. -- A **workaround** reaches the same outcome with today's surface: a follow-up - read, a re-keyed identifier, a path change, a client-side filter, a marker - the app controls. -- A **gap** is a difference with no workaround, or a workaround whose tradeoff - is nontrivial. Only gaps become cards. +- A difference is any row whose parity label is not `same`. +- A workaround reaches the same outcome with today's API by another route. A + follow-up read, a re-keyed identifier, a path change, a client-side filter, + a marker the app controls. +- A gap is a difference with no workaround, or a workaround whose tradeoff + fails one of the five tests below. Only gaps become cards. ## Nontrivial tradeoff -At least one must hold; quote it on the card. +At least one must hold. Quote it on the card. | Tradeoff | Test | | --- | --- | -| **Fan-out at scale** | Calls per event multiply by a factor that grows with repository or activity size (N commits × M files; a full list scan to find one row) and the app's volume makes that budget-relevant. One bounded extra read per event is trivial. | -| **Correctness risk** | The workaround can be wrong, not just slower: heuristic "my own row" matching, inferring a PR from a SHA several versions share, assembling a URL whose format is not contractual. | -| **Security posture** | Needs a broader scope, a longer-lived token, or a user credential where an installation token should do. | -| **Customer-visible behavior** | Changes what the team's users see or can do, not how the code is organized. | -| **Load-bearing** | Sits on the hello-world path or the team's stated core flow. | +| Fan-out at scale | Calls per event multiply by a factor that grows with repository or activity size (N commits × M files, or a full list scan to find one row), and the app's volume makes that budget-relevant. One bounded extra read per event is trivial. | +| Correctness risk | The workaround can return a wrong answer, not only a slower one. Heuristic "my own row" matching. Inferring a PR from a SHA several versions share. Assembling a URL whose format is not contractual. | +| Security posture | The workaround needs a broader scope, a longer-lived token, or a user credential where an installation token should do. | +| Customer-visible behavior | The workaround changes what the team's users see or can do, not how the code is organized. | +| Load-bearing | The capability sits on the hello-world path or the team's stated core flow. | ## Never a gap - Anything in `origin-isms.md`. -- A field or filter the code does not actually use. +- A field or filter the code does not use. - A GitHub convenience (`Link` pagination, numeric IDs, `html_url`) where the Origin convention is a mechanical substitution. -- Something the changelog says shipped or the spec already carries. Re-check - the live spec before writing any card. +- Anything the changelog says shipped or the spec already carries. Re-read the + live spec before writing any card. - A concept the Origin docs never mention (Marketplace billing, merge queues, - Actions, Projects): `unknown` plus a question. -- A GitHub Search query. The idiom is a list operation with its filters plus - a client-side predicate; a sorted list read that stops at a cutoff is - proportional to the matches, not the collection. Only if that count fails - the bar does it become a card about a *filter*, never about search. + Actions, Projects). That is `unknown` plus a question. +- A GitHub Search query. The idiom is a list operation with its filters plus a + client-side predicate. A sorted list read that stops at a cutoff costs + proportional to the matches, not the collection. If that count fails the + bar, the card is about a filter, never about search. ## One pattern that does clear the bar -A state change the app reacts to that has **no event**, when reacting to -exactly that change is the app's purpose and the state is invisible until an -unrelated event arrives. Reading it off the next snapshot fails on -correctness and customer-visible behavior when the app is a gate (a check, a -block, a notification). Write the card about the event; not when the app -merely logs or tidies up on that change. +A state change the app reacts to that has no event, when reacting to exactly +that change is the app's purpose and the state is invisible until an +unrelated event arrives. Reading it off the next snapshot fails on correctness +and customer-visible behavior when the app is a gate (a check, a block, a +notification). Write the card about the event. Do not write it when the app +only logs or tidies up on that change. ## The card -One per gap, in the brief's "Gaps worth raising" section, written so Cursor -can act without a call: +One per gap, in the brief's "Gaps worth raising" section. Write it so Cursor +can act without a call. ```markdown ### Gap: -- **GitHub surface the app uses:** `` / `` / ``, at ``. +- **GitHub call, event, or permission:** `` / `` / ``, at ``. - **What the app needs from it:** . - **Why:** . -- **Closest Origin surface today:** `` / `` / none, and what it lacks. +- **Closest Origin operation today:** `` / `` / none, and what it lacks. - **Workaround considered:** . - **Tradeoff that fails the bar:** . -- **Shape that would close it:** . +- **Shape that would close it:** - **Blocking?** yes / no, for which flow. - **Spec version checked:** `` on ``. ``` -Do not propose scope, field, or route names; Cursor owns the shape. Do not -batch unrelated capabilities. Do not send cards yourself; the team decides -what goes out (their shared Slack channel with Cursor, or `hi@cursor.com` -with "Origin API" and the app name in the subject), quoting the spec version -and any `X-Request-ID` from failed calls. "This is by design, here is the -idiom" is a fine answer and goes back into the brief as `by-design-absent`. +Do not propose scope, field, or route names. Cursor owns the shape. Do not +batch unrelated capabilities. Do not send cards yourself. The team decides +what goes out, through their shared Slack channel with Cursor or to +`hi@cursor.com` with "Origin API" and the app name in the subject, quoting the +spec version and any `X-Request-ID` from failed calls. "This is by design, +here is the idiom" is a fine answer. It goes back into the brief as +`by-design-absent`. diff --git a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md index db4f021a0..c4ca23260 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md +++ b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md @@ -1,11 +1,11 @@ -# Origin-isms: GitHub surfaces Origin departs from on purpose +# Origin-isms: GitHub features Origin departs from on purpose Check here before labeling anything `gap`. A row below is `by-design-absent` -or `reshaped`; the brief points at the idiom and never files a card. The -reasoning behind each lives in the `origin-api` skill; this table carries only -what the classifier needs. Confirm current wording in `llms-full.txt`. +or `reshaped`. The brief points at the idiom and never files a card. The +`origin-api` skill carries the reasoning. This table carries only the label +and the idiom. Confirm current wording in `llms-full.txt`. -| GitHub surface the app uses | Label | Origin idiom | +| GitHub call, event, or permission | Label | Origin idiom | | --- | --- | --- | | Acting on a repository mirrored *from* GitHub (writes, `push` events) | `by-design-absent` | Install on Origin-native repositories or stable outbound mirrors. Mirrors are read-only to apps and deliver no `repository.pushed`. First question of every brief. | | Install callback with `installation_id` + `setup_action` query params | `reshaped` | Signed installation receipt JWT; `sub` is the installation ID. Not a Bearer. | @@ -26,12 +26,12 @@ what the classifier needs. Confirm current wording in `llms-full.txt`. | User, email, team, and member lookups | `by-design-absent` | Actors are TypeIDs (plus a handle where exposed). No directory. | | Single `pull_request` event with `action` field, `previous_attributes` | `reshaped` | One slug per action (`pull_request.head_ref.pushed`); no `action` field, no delta. Confirm each slug in `x-origin-webhook-events`. | | `x-github-*` headers, HMAC `x-hub-signature-256` | `reshaped` | `webhook-*` headers; `v1ed` Ed25519 over a SHA-256 digest, JWKS keys. | -| Payload inlines: changed files on push, before-SHA, `html_url`, `sender` profile | `reshaped` | Follow-up `Get…` / `CompareCommits` / `ListComparisonFiles` with identifiers the payload carries. Name the call per field; count the fan-out. | +| Fields GitHub inlines in payloads (changed files on push, before-SHA, `html_url`, `sender` profile) | `reshaped` | Follow-up `Get…`, `CompareCommits`, or `ListComparisonFiles` with identifiers the payload carries. Name the call for each field and count the fan-out. | | Default delivery of all events after app creation | `reshaped` | Only installation lifecycle is default; select the rest. | | Reviews keyed by `commit_id` | `reshaped` | Reviews anchor to a pull request version. | | Finding own check runs / comments by actor | `reshaped` | Check runs by `key`; comments and reviews by a marker the app controls. | | Requested-reviewer team pages, `created_via` | `by-design-absent` | Reviewers addressed by identifier only. | -Not on this list, and not mentioned anywhere in the Origin docs (Marketplace -billing, merge queues, Actions, Pages, Projects): `unknown` with a question, -never `by-design-absent`. +A feature that is not on this list and that the Origin docs never mention +(Marketplace billing, merge queues, Actions, Pages, Projects) is `unknown` +with a question, never `by-design-absent`. diff --git a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md index 0a788ba56..a3928bfde 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md +++ b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md @@ -1,30 +1,29 @@ -# Reading the Origin spec and matching GitHub surface to it +# Reading the Origin spec and matching GitHub calls and events to it -Every mapping in the brief is derived from the fetched `openapi.yaml`, not -from a table here. Build the index once; every later step looks things up in -it. +Every mapping in the brief comes from the fetched `openapi.yaml`, not from a +table here. Build the index once. Every later step looks things up in it. ## Extensions the spec carries | Extension | Where | Meaning | | --- | --- | --- | -| `x-origin-scopes` | every operation | `scopes` required; `tokenTypes` accepted (`app`, `installation`, `user`); `ambient: true` means nothing to request. | -| `x-origin-webhook-events` | payload schemas | The slugs that deliver this payload shape. A schema carrying it is a webhook family; these slugs are the only authoritative event list. | -| `x-origin-webhook-resource` | some payload schemas | The REST component the payload embeds. When absent, infer from `$ref`s; pushes, deletions, and reviewer requests are event-native with no REST twin. | -| `x-cursor-visibility: PREVIEW` | some operations | Usable, shape may move. Carry the badge into the brief as a `preview` suffix. | +| `x-origin-scopes` | every operation | `scopes` the operation requires. `tokenTypes` it accepts (`app`, `installation`, `user`). `ambient: true` means there is nothing to request. | +| `x-origin-webhook-events` | payload schemas | The slugs that deliver this payload shape. A schema carrying it is a webhook family. These slugs are the only authoritative event list. | +| `x-origin-webhook-resource` | some payload schemas | The REST component the payload embeds. When absent, infer it from `$ref`s. Pushes, deletions, and reviewer requests are event-native with no REST twin. | +| `x-cursor-visibility: PREVIEW` | some operations | Usable. The shape may move. Carry the badge into the brief as a `preview` suffix. | ## Build the index 1. **Operations**: `operationId`, method, path, `x-origin-scopes`, - visibility, parameter names, request top-level fields, response component. + visibility, parameter names, top-level request fields, response component. 2. **Webhook events**: slug → schema → properties and `$ref`s (or the `x-origin-webhook-resource` target). From `llms-full.txt` § Webhooks, which slugs are app-lifecycle (always delivered) versus repository events (must be selected). -3. **Scopes**: union of every `scopes` value, annotated with the operations - that need it; separate installation-requestable scopes from ambient and - user-only ones (the latter tell you which GitHub flows have no app-side - equivalent by construction). +3. **Scopes**: the union of every `scopes` value, annotated with the + operations that need it. Separate installation-requestable scopes from + ambient and user-only ones. The user-only set tells you which GitHub flows + have no app-side equivalent. 4. **Resources**: component schemas returned by `Get…`/`List…`, with field names, for "does the Origin object carry this field". @@ -33,10 +32,10 @@ prints all four. ## Matching -Match in this order; stop at the first rule that yields a *confirmed* +Match in this order and stop at the first rule that yields a confirmed counterpart. Confirmed means you read the Origin operation's description and -parameters and it answers the same question. A name match is a candidate, -never a result. +parameters and it answers the same question the GitHub call answers. A name +match is a candidate, never a result. **REST calls** @@ -46,21 +45,22 @@ never a result. `…/contents:batchGet`). 2. Re-home GitHub's issue-flavored PR calls: `/issues/{n}/comments` and `/issues/{n}/labels` used *on a pull request* live under `/pulls/{n}/…`. - Path change, not a gap. Used on real issues: `origin-isms.md`. + That is a path change, not a gap. When the code uses them on real issues, + see `origin-isms.md`. 3. Re-home `/app`, `/app/installations`, access-token minting, and `/installation/repositories` under `/v1/origin/app…` and `/v1/origin/installation/repos`; confirm `tokenTypes`. `/user`, - `/user/installations`, `/orgs/…`, `/search/…`, `/repositories/{id}` have - no path counterpart: consult `origin-isms.md` before labeling. -4. Compare parameters, not just paths. A matching path missing a filter the - code depends on is `workaround` or `gap`, not `same`. -5. Compare response fields the code reads. Each missing field gets its own - line: follow-up call, derivable, or absent. GitHub inlines convenience - data (web URLs, nested profiles, counts) that Origin does not. - -**GraphQL.** No endpoint. Decompose each document into the REST reads and -writes it stands for, map those, and record the fan-out as the row's -tradeoff. + `/user/installations`, `/orgs/…`, `/search/…`, and `/repositories/{id}` + have no path counterpart. Consult `origin-isms.md` before labeling them. +4. Compare parameters as well as paths. A matching path that lacks a filter + the code depends on is `workaround` or `gap`, not `same`. +5. Compare the response fields the code reads. Each missing field gets its + own line as follow-up call, derivable, or absent. GitHub inlines web URLs, + nested profiles, and counts that Origin does not. + +**GraphQL.** There is no endpoint. Decompose each document into the REST +reads and writes it stands for, map those, and record the fan-out as the +row's tradeoff. **Permissions → scopes.** Do not translate the manifest noun-for-noun. Find the operations the code calls and take the union of *their* @@ -69,26 +69,27 @@ the operations the code calls and take the union of *their* `workflows`, `deployments`) go through `origin-isms.md` first. **Events → slugs.** Each GitHub `event` + `action` pair becomes a candidate -slug (`pull_request` + `synchronize` → `pull_request.head_ref.pushed`); -confirm it exists in the webhook index. A candidate that does not exist is -not an event on Origin; check whether the state change is observable another -way before classifying. +slug (`pull_request` plus `synchronize` becomes `pull_request.head_ref.pushed`). +Confirm it exists in the webhook index. A candidate that does not exist is +not an event on Origin. Check whether the state change is observable another +way before classifying it. **Payload fields → schema properties.** For each field path a handler reads, -walk the mapped slug's payload schema and record one of: **present** at -``; **follow-up read** via `` with identifiers the payload -carries; **derivable** from present fields (say how, and whether the format -is contractual); **absent** (goes to the gap bar). A field on the REST -component that the webhook twin lacks is a follow-up `Get…` on every event. +walk the mapped slug's payload schema and record one of four outcomes. +Present at ``. Follow-up read via `` with identifiers the +payload carries. Derivable from present fields, saying how and whether the +format is contractual. Absent, which goes to the gap bar. A field on the REST +component that the webhook twin lacks means a follow-up `Get…` on every +event. ## Out of domain and spec-silent - A concept neither the spec nor `llms-full.txt` mentions (Marketplace billing, merge queues, Actions, Pages, Projects, Discussions, HTML probes) - is `unknown` with an up-front question. Never `gap` (Origin has not - declined it) and never `by-design-absent` (only `origin-isms.md` rows earn - that). + is `unknown` with an up-front question. It is never `gap`, because Origin + has not declined it, and never `by-design-absent`, because only + `origin-isms.md` rows earn that. - A behavior the code depends on that the docs do not state (does an event - fire for a draft PR? does `updatedAt` move on a comment?) is an open + fire for a draft PR? does `updatedAt` move on a comment?) becomes an open question plus a hello-world step that observes it on a native repository. - Do not resolve it from GitHub's behavior. + Do not settle it from GitHub's behavior. From cc906921c14e183f53e117a0890594a3e3487b5f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 24 Sep 2026 16:16:14 +0000 Subject: [PATCH 05/26] Apply skill-authoring audit picks Porting skill gains step 6, a copyable check-and-fix list run before the brief is declared done. Both descriptions are third person. The porting reference table names the origin-api skill instead of a relative path. brief-template gets a contents line. gap-bar drops "today". "PR" becomes "pull request" in prose. Co-authored-by: ali.nikseresht --- origin-apps/skills/origin-api/SKILL.md | 10 +++++----- .../skills/port-github-app-to-origin/SKILL.md | 20 +++++++++++++++++-- .../references/brief-template.md | 9 ++++++--- .../references/gap-bar.md | 6 +++--- .../references/origin-isms.md | 2 +- .../references/spec-mapping.md | 4 ++-- 6 files changed, 35 insertions(+), 16 deletions(-) diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md index eb6e1ae5e..e139d2655 100644 --- a/origin-apps/skills/origin-api/SKILL.md +++ b/origin-apps/skills/origin-api/SKILL.md @@ -1,8 +1,8 @@ --- name: origin-api description: >- - Build on the Cursor Origin API. Create an Origin App, authenticate as it, - call the REST API, receive webhooks. Use whenever code or a plan touches + Guides building on the Cursor Origin API: creating an Origin App, + authenticating as it, calling the REST API, receiving webhooks. Use whenever code or a plan touches Origin endpoints, installation tokens, scopes, webhook subscriptions or signatures, page tokens, or an Origin App manifest. Lists the sources to fetch first and the Origin rules that GitHub habits get wrong. @@ -70,8 +70,8 @@ docs disagree, the docs win. every request, including continuations. There is no `Link` header and no total. - **IDs are TypeIDs** (`repo_…`, `i_…`, `cmt_…`), never integers. Cache IDs, - not slugs. `/repos/_/{repoId}` survives renames. 64-bit integers (PR - numbers, versions) are JSON strings. Defaults are present (`false`, `0`, + not slugs. `/repos/_/{repoId}` survives renames. 64-bit integers (pull + request numbers, versions) are JSON strings. Defaults are present (`false`, `0`, `[]`), so a present `false` is a value. - **`404` means not found or no access.** Branch on status and `code`, never on message text. Quote `X-Request-ID` when escalating. @@ -79,7 +79,7 @@ docs disagree, the docs win. `429`. Git HTTPS is metered separately. Cursor raises per-app budgets on request. - **These are decisions, not gaps.** No commit statuses (check runs upsert on - a caller-stable `key`). No Issues (conversation is PR comments, threads, + a caller-stable `key`). No Issues (conversation is pull request comments, threads, reviews, and labels). No GraphQL. No per-repository webhook CRUD. No user or email directory. Reviews anchor to a pull request version, not a SHA. A thread materializes from its first diff-anchored comment. diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index 0f17e23e0..3f1b24461 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -1,7 +1,7 @@ --- name: port-github-app-to-origin description: >- - Plan the port of an existing GitHub App to a Cursor Origin App. Use when a + Plans the port of an existing GitHub App to a Cursor Origin App. Use when a repo is a GitHub App (manifest, Probot, Octokit or another GitHub SDK, webhook signature handlers) and the task is to bring it to Origin or compare it with the Origin API. Maps what the app uses from GitHub onto the live Origin spec @@ -61,6 +61,22 @@ GitHub. Follow it first. Nothing here repeats it. Two rules on top: 5. **Close with the questions**, pruned to what discovery left open. The first is always native or mirror, because it decides whether the app receives events at all. +6. **Check the brief and fix.** Copy this list, tick each line, fix what + fails, and repeat until a pass changes nothing. Both validation runs found + a missed row on this pass. + + - [ ] Every Origin cell names an `operationId`, slug, or anchor that + exists in the files fetched in step 1. + - [ ] Every `gap` row has a card, and the card quotes one of the five + tradeoff tests in `gap-bar.md`. + - [ ] Every `unknown` row has a question in § 7. + - [ ] No `origin-isms.md` row is labeled `gap`. + - [ ] Every event the code handles has a § 4 row for each payload field + it reads, including log-only fields. + - [ ] Calls the framework makes on the app's behalf appear as rows. + - [ ] The scopes line equals the union of `x-origin-scopes.scopes` over + the § 3 operations. + - [ ] Question 1 is native or mirror. ## Not in scope @@ -72,7 +88,7 @@ cards to Cursor. The brief carries them and the team decides. | File | Read when | | --- | --- | -| `../origin-api/SKILL.md` | First. Sources and fundamentals. | +| `origin-api` skill (install both) | First. Sources and fundamentals. | | `references/discovery.md` | Scanning the codebase. | | `references/spec-mapping.md` | Building the index. Matching calls, events, and fields. | | `references/origin-isms.md` | Labeling a missing GitHub feature. | diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md index 04c9e23f7..5f893c11c 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md +++ b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md @@ -1,5 +1,8 @@ # Porting brief template +Contents: [Labels](#labels) (parity, size) and [Template](#template) +(provenance, §§ 1-8). + Write one Markdown file at the repository root (`ORIGIN-PORTING-BRIEF.md` unless the team's docs convention says otherwise) and print its path. Fill every section. An empty section says so in one line rather than disappearing. @@ -122,7 +125,7 @@ spec-silent behavior the brief depends on, stated as the observation to make. 6. Perform the smallest action in § 3 and confirm the slug and the § 4 fields arrive. If the ping arrived and this did not, re-check steps 2 and 3 first. -7. Smallest write from § 3 (check run with a stable `key`, PR comment), +7. Smallest write from § 3 (check run with a stable `key`, pull request comment), confirming its scope is in the grant. ## 6. Gaps worth raising @@ -140,8 +143,8 @@ Always the first three, then what discovery left open. which payload fields are hard requirements? 3. Which flows depend on a user credential today, and what should they do on Origin? -4. Does anything key approvals or reviews by commit SHA rather than PR - version? +4. Does anything key approvals or reviews by commit SHA rather than pull + request version? 5. How do you identify your own check runs, comments, and reviews today? Can a key or marker you control replace actor matching? 6. Do you generate clients from OpenAPI? (Read the changelog for renames.) diff --git a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md index 60ee0d17f..0fff925d0 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md +++ b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md @@ -5,7 +5,7 @@ two or three things Cursor needs to hear. When in doubt, write the row as `workaround` with the tradeoff and an open question, not as a card. - A difference is any row whose parity label is not `same`. -- A workaround reaches the same outcome with today's API by another route. A +- A workaround reaches the same outcome with the current API by another route. A follow-up read, a re-keyed identifier, a path change, a client-side filter, a marker the app controls. - A gap is a difference with no workaround, or a workaround whose tradeoff @@ -18,7 +18,7 @@ At least one must hold. Quote it on the card. | Tradeoff | Test | | --- | --- | | Fan-out at scale | Calls per event multiply by a factor that grows with repository or activity size (N commits × M files, or a full list scan to find one row), and the app's volume makes that budget-relevant. One bounded extra read per event is trivial. | -| Correctness risk | The workaround can return a wrong answer, not only a slower one. Heuristic "my own row" matching. Inferring a PR from a SHA several versions share. Assembling a URL whose format is not contractual. | +| Correctness risk | The workaround can return a wrong answer, not only a slower one. Heuristic "my own row" matching. Inferring a pull request from a SHA several versions share. Assembling a URL whose format is not contractual. | | Security posture | The workaround needs a broader scope, a longer-lived token, or a user credential where an installation token should do. | | Customer-visible behavior | The workaround changes what the team's users see or can do, not how the code is organized. | | Load-bearing | The capability sits on the hello-world path or the team's stated core flow. | @@ -58,7 +58,7 @@ can act without a call. - **GitHub call, event, or permission:** `` / `` / ``, at ``. - **What the app needs from it:** . - **Why:** . -- **Closest Origin operation today:** `` / `` / none, and what it lacks. +- **Closest Origin operation:** `` / `` / none, and what it lacks. - **Workaround considered:** . - **Tradeoff that fails the bar:** . - **Shape that would close it:** diff --git a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md index c4ca23260..3bbda21a3 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md +++ b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md @@ -17,7 +17,7 @@ and the idiom. Confirm current wording in `llms-full.txt`. | `Link` / `page` / `per_page` pagination, total counts | `reshaped` | `pageSize` / `pageToken` / `nextPageToken`; opaque; no total. | | GraphQL | `by-design-absent` | REST; decompose and count the fan-out. | | Commit statuses (`statuses` permission, `POST /statuses/{sha}`) | `reshaped` | Check runs upserting on a caller-stable `key`; rulesets bind on the key. | -| Issues (`issues` permission, `issues.*` events, `/issues/{n}` not on a PR) | `by-design-absent` | PR comments, threads, reviews, labels on pull requests. Ask what the PR-scoped behavior should be. | +| Issues (`issues` permission, `issues.*` events, `/issues/{n}` not on a pull request) | `by-design-absent` | Pull request comments, threads, reviews, and labels. Ask what the pull-request-scoped behavior should be. | | `/issues/{n}/comments`, `/issues/{n}/labels` used on a pull request | `reshaped` | Same calls under `/pulls/{n}/…`. | | Repository webhook CRUD (`/repos/…/hooks`) | `by-design-absent` | Subscriptions are app settings. | | App-manifest conversion, OAuth-app token mints | `by-design-absent` | App creation form (accepts prefill params) or user-credential `CreateApp`. | diff --git a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md index a3928bfde..836743a82 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md +++ b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md @@ -43,7 +43,7 @@ match is a candidate, never a result. (`{owner}/{repo}` → `{ownerSlug}/{repoName}`, `{pull_number}` → `{pullNumber}`; custom verbs are `:verb` suffixes such as `…/contents:batchGet`). -2. Re-home GitHub's issue-flavored PR calls: `/issues/{n}/comments` and +2. Re-home GitHub's issue-flavored pull request calls: `/issues/{n}/comments` and `/issues/{n}/labels` used *on a pull request* live under `/pulls/{n}/…`. That is a path change, not a gap. When the code uses them on real issues, see `origin-isms.md`. @@ -90,6 +90,6 @@ event. has not declined it, and never `by-design-absent`, because only `origin-isms.md` rows earn that. - A behavior the code depends on that the docs do not state (does an event - fire for a draft PR? does `updatedAt` move on a comment?) becomes an open + fire for a draft pull request? does `updatedAt` move on a comment?) becomes an open question plus a hello-world step that observes it on a native repository. Do not settle it from GitHub's behavior. From 3bd5434dc47f691034fca0a033a0374e3d254587 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 24 Sep 2026 16:46:15 +0000 Subject: [PATCH 06/26] Keep only payload-field indexing in the script; cut internal context index-origin-spec.py drops the ops and scopes modes, which two rg one-liners cover, and keeps events and schema (payload fields with $ref resolution). Constants are named; missing file, invalid YAML, and non-Origin specs exit with a message instead of a traceback. Prose loses process history and internal justifications: the validation run note in step 6, "Cursor owns the shape", the manifests paragraph in the README, and the CHANGELOG iteration notes. Co-authored-by: ali.nikseresht --- origin-apps/CHANGELOG.md | 14 +- origin-apps/README.md | 14 +- .../skills/port-github-app-to-origin/SKILL.md | 14 +- .../references/gap-bar.md | 6 +- .../references/spec-mapping.md | 28 ++-- .../scripts/index-origin-spec.py | 134 ++++++++---------- 6 files changed, 89 insertions(+), 121 deletions(-) diff --git a/origin-apps/CHANGELOG.md b/origin-apps/CHANGELOG.md index 38ba40c99..0becd4d61 100644 --- a/origin-apps/CHANGELOG.md +++ b/origin-apps/CHANGELOG.md @@ -1,15 +1,5 @@ # Changelog -All notable changes to this plugin will be documented here. +## 0.1.0 -## 0.1.0, initial release - -- Added the `origin-api` skill. It lists the live Origin docs and OpenAPI spec - to fetch first, then the Origin rules that GitHub habits get wrong: - credentials and token minting, minimal scopes, webhook verification and - idempotency, opaque page tokens, TypeIDs, the error envelope, rate limits, - and the differences from GitHub that are decisions rather than gaps. -- Added the `port-github-app-to-origin` skill, built on `origin-api`. It reads - what a GitHub App uses from GitHub out of its codebase, maps that onto the - live Origin spec, and writes a porting brief with a capability table, a - webhook field map, a hello-world path, gap cards, and up-front questions. +Initial release with two skills, `origin-api` and `port-github-app-to-origin`. diff --git a/origin-apps/README.md b/origin-apps/README.md index 785328a94..c9955bf55 100644 --- a/origin-apps/README.md +++ b/origin-apps/README.md @@ -31,7 +31,7 @@ Both skills fetch the spec at run time and never name an endpoint from memory. - Writing or reviewing code that calls Origin, mints installation tokens, or receives Origin webhooks: `origin-api`. -- Creating an Origin App and wanting the first-week traps up front: +- Creating an Origin App and wanting the rules to know up front: `origin-api`. - Holding a GitHub App (Probot, Octokit, go-github, hand-rolled) and wanting to know what an Origin App version looks like before starting: @@ -50,11 +50,9 @@ or open Customize, find the plugin, and install it at user or project scope. ## Use outside Cursor -The plugin ships three manifests for one set of skills. A root `plugin.json` -([Agent Plugins](https://agent-plugins.org) 1.0), `.cursor-plugin/plugin.json` -(Cursor Marketplace), and `.claude-plugin/plugin.json` (Claude Code). The -skills use only portable frontmatter (`name`, `description`, `license`, -`compatibility`). +The skills use only the portable +[Agent Skills](https://agentskills.io) frontmatter, so they work unchanged in +other agents. Claude Code, via the marketplace manifest at this repository's root: @@ -84,8 +82,8 @@ mkdir -p .cursor/skills && cp -r plugins/origin-apps/skills/* .cursor/skills/ needs no Origin credentials. You follow the brief's hello-world path afterwards. - Optional: `python3` with PyYAML for the porting skill's - `scripts/index-origin-spec.py`, which turns the fetched spec into a - grep-friendly index. Without it the skill reads the spec directly. + `scripts/index-origin-spec.py`, which prints webhook payload fields with + their references resolved. Without it the skill reads the spec directly. ## Where the brief goes diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index 3f1b24461..5ec551f8f 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -9,8 +9,8 @@ description: >- license: MIT compatibility: >- Needs network access to https://cursor.com/docs/api/origin/* at run time. - The optional indexing script needs python3 with PyYAML; without it, read the - spec directly. + The optional payload-field script needs python3 with PyYAML; without it, + read the spec directly. --- # Port a GitHub App to an Origin App @@ -35,8 +35,9 @@ GitHub. Follow it first. Nothing here repeats it. Two rules on top: 1. **Load the spec** (`origin-api`, "Fetch first"). Record `info.version` and the fetch time for the brief's provenance. Build the mapping index per - `references/spec-mapping.md`. `scripts/index-origin-spec.py openapi.yaml` - prints it and needs PyYAML. + `references/spec-mapping.md`: two `rg` commands for operations, scopes, + and slugs, and `scripts/index-origin-spec.py openapi.yaml` (needs PyYAML) + for webhook payload fields. 2. **Discover** per `references/discovery.md`. Record a file and line for every fact, including payload fields read only for logging and calls the framework makes on the app's behalf. Note what you looked for and did not @@ -62,8 +63,7 @@ GitHub. Follow it first. Nothing here repeats it. Two rules on top: first is always native or mirror, because it decides whether the app receives events at all. 6. **Check the brief and fix.** Copy this list, tick each line, fix what - fails, and repeat until a pass changes nothing. Both validation runs found - a missed row on this pass. + fails, and repeat until a pass changes nothing. - [ ] Every Origin cell names an `operationId`, slug, or anchor that exists in the files fetched in step 1. @@ -94,4 +94,4 @@ cards to Cursor. The brief carries them and the team decides. | `references/origin-isms.md` | Labeling a missing GitHub feature. | | `references/gap-bar.md` | Deciding whether a difference earns a card, and writing it. | | `references/brief-template.md` | Writing the output. | -| `scripts/index-origin-spec.py` | Turning `openapi.yaml` into a grep-friendly index. Optional. | +| `scripts/index-origin-spec.py` | Listing webhook payload families with their fields resolved. Optional. | diff --git a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md index 0fff925d0..578d20eb7 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md +++ b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md @@ -1,7 +1,7 @@ # The gap bar and the escalation card Most differences are not gaps. A brief that files every difference buries the -two or three things Cursor needs to hear. When in doubt, write the row as +two or three that are worth raising. When in doubt, write the row as `workaround` with the tradeoff and an open question, not as a card. - A difference is any row whose parity label is not `same`. @@ -66,8 +66,8 @@ can act without a call. - **Spec version checked:** `` on ``. ``` -Do not propose scope, field, or route names. Cursor owns the shape. Do not -batch unrelated capabilities. Do not send cards yourself. The team decides +Do not propose scope, field, or route names. Do not batch unrelated +capabilities. Do not send cards yourself. The team decides what goes out, through their shared Slack channel with Cursor or to `hi@cursor.com` with "Origin API" and the app name in the subject, quoting the spec version and any `X-Request-ID` from failed calls. "This is by design, diff --git a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md index 836743a82..5c27442c5 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md +++ b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md @@ -14,22 +14,22 @@ table here. Build the index once. Every later step looks things up in it. ## Build the index -1. **Operations**: `operationId`, method, path, `x-origin-scopes`, - visibility, parameter names, top-level request fields, response component. -2. **Webhook events**: slug → schema → properties and `$ref`s (or the - `x-origin-webhook-resource` target). From `llms-full.txt` § Webhooks, - which slugs are app-lifecycle (always delivered) versus repository events - (must be selected). -3. **Scopes**: the union of every `scopes` value, annotated with the - operations that need it. Separate installation-requestable scopes from - ambient and user-only ones. The user-only set tells you which GitHub flows - have no app-side equivalent. -4. **Resources**: component schemas returned by `Get…`/`List…`, with field +1. **Operations and scopes**: `rg -B1 -A4 'x-origin-scopes:' openapi.yaml` + prints every `operationId` with its `scopes`, `tokenTypes`, and `ambient` + flag. From it, note the union of scopes with the operations that need + each, and separate installation-requestable scopes from ambient and + user-only ones. The user-only set tells you which GitHub flows have no + app-side equivalent. Read parameters and response components from the + spec when a rule below asks for them. +2. **Webhook events**: `rg -A3 'x-origin-webhook-events:' openapi.yaml` lists + every slug with its payload schema. `scripts/index-origin-spec.py + openapi.yaml` prints each payload family with its fields and `$ref`s + resolved two levels deep; `schema ` does the same for one component. + From `llms-full.txt` § Webhooks, note which slugs are app-lifecycle + (always delivered) versus repository events (must be selected). +3. **Resources**: component schemas returned by `Get…`/`List…`, with field names, for "does the Origin object carry this field". -`scripts/index-origin-spec.py openapi.yaml [ops|events|scopes|schema ]` -prints all four. - ## Matching Match in this order and stop at the first rule that yields a confirmed diff --git a/origin-apps/skills/port-github-app-to-origin/scripts/index-origin-spec.py b/origin-apps/skills/port-github-app-to-origin/scripts/index-origin-spec.py index 2bb1aa2aa..b50ab04e9 100644 --- a/origin-apps/skills/port-github-app-to-origin/scripts/index-origin-spec.py +++ b/origin-apps/skills/port-github-app-to-origin/scripts/index-origin-spec.py @@ -1,16 +1,17 @@ #!/usr/bin/env python3 -"""Print a grep-friendly index of the Origin OpenAPI spec. +"""Print the Origin webhook payload families with their fields resolved. Usage: - python3 index-origin-spec.py openapi.yaml # everything - python3 index-origin-spec.py openapi.yaml ops # operations only - python3 index-origin-spec.py openapi.yaml events # webhook payload families - python3 index-origin-spec.py openapi.yaml scopes # scope catalog + python3 index-origin-spec.py openapi.yaml # every payload family python3 index-origin-spec.py openapi.yaml schema PullRequest # one component Fetch the spec first: curl -sSL https://cursor.com/docs/api/origin/openapi.yaml -o openapi.yaml +Operations and scopes do not need this script. Grep the spec directly: + rg -B1 -A4 'x-origin-scopes:' openapi.yaml + rg -A3 'x-origin-webhook-events:' openapi.yaml + Read-only. Needs PyYAML (`pip install pyyaml`). Everything printed comes from the spec you pass in; nothing is pinned or embedded here. """ @@ -22,12 +23,17 @@ except ImportError: # pragma: no cover sys.stderr.write( "PyYAML is not installed. Run `pip install pyyaml`, or read the spec " - "directly (search for `operationId:`, `x-origin-scopes:`, and " - "`x-origin-webhook-events:`).\n" + "directly (search for `x-origin-webhook-events:` and follow the " + "`$ref`s by hand).\n" ) sys.exit(2) -METHODS = ("get", "post", "put", "patch", "delete") +# Field descriptions are cut to one sentence and this many characters so a +# family fits on one screen. +DESCRIPTION_CHARS = 140 +# How many `$ref` levels to expand under a payload. Two reaches the embedded +# resource and its direct children, which is what field mapping needs. +SCHEMA_DEPTH = 2 def ref_name(node): @@ -44,10 +50,10 @@ def ref_name(node): def first_sentence(text): - return " ".join((text or "").split()).split(". ")[0][:140] + return " ".join((text or "").split()).split(". ")[0][:DESCRIPTION_CHARS] -def print_schema(components, name, depth=0, seen=None, max_depth=2): +def print_schema(components, name, depth=0, seen=None): seen = seen or set() schema = components.get(name) if not schema: @@ -58,86 +64,60 @@ def print_schema(components, name, depth=0, seen=None, max_depth=2): desc = first_sentence(node.get("description")) print(f"{' ' * depth}{field}: {kind}" + (f" -- {desc}" if desc else "")) inner = (ref_name(node) or "").rstrip("[]") - if inner and inner in components and depth < max_depth and inner not in seen: + if inner and inner in components and depth < SCHEMA_DEPTH and inner not in seen: seen.add(inner) - print_schema(components, inner, depth + 1, seen, max_depth) - - -def print_ops(spec): - print("== OPERATIONS (operationId | METHOD path | scopes | tokenTypes | ambient | visibility)") - for path, methods in spec["paths"].items(): - for method, op in methods.items(): - if method not in METHODS: - continue - xs = op.get("x-origin-scopes") or {} - params = [p["name"] for p in op.get("parameters", [])] - body = None - rb = op.get("requestBody") - if rb: - schema = rb["content"]["application/json"]["schema"] - body = ref_name(schema) or list((schema.get("properties") or {}).keys()) - resp = ( - op.get("responses", {}) - .get("200", {}) - .get("content", {}) - .get("application/json", {}) - .get("schema", {}) - ) - print( - f"{op.get('operationId')} | {method.upper()} {path} | " - f"scopes={xs.get('scopes')} tokenTypes={xs.get('tokenTypes')} " - f"ambient={xs.get('ambient')} visibility={op.get('x-cursor-visibility')}" - ) - print(f" params={params} body={body} -> {ref_name(resp) or '(empty)'}") - print(f" {first_sentence(op.get('description'))}") - - -def print_events(spec): - components = spec["components"]["schemas"] + print_schema(components, inner, depth + 1, seen) + + +def print_events(components): print("== WEBHOOK PAYLOAD FAMILIES (schema | slugs | x-origin-webhook-resource)") for name, schema in components.items(): slugs = schema.get("x-origin-webhook-events") if not slugs: continue print(f"{name} | {slugs} | resource={schema.get('x-origin-webhook-resource')}") - print_schema(components, name, depth=1, max_depth=2) - - -def print_scopes(spec): - print("== SCOPES (scope | tokenTypes seen | operations)") - table = {} - for path, methods in spec["paths"].items(): - for method, op in methods.items(): - if method not in METHODS: - continue - xs = op.get("x-origin-scopes") or {} - for scope in xs.get("scopes") or []: - entry = table.setdefault(scope, {"tokens": set(), "ops": [], "ambient": False}) - entry["tokens"].update(xs.get("tokenTypes") or []) - entry["ops"].append(op.get("operationId")) - entry["ambient"] = entry["ambient"] or bool(xs.get("ambient")) - for scope in sorted(table): - e = table[scope] - print(f"{scope} | tokenTypes={sorted(e['tokens'])} ambient={e['ambient']} | {e['ops']}") + print_schema(components, name, depth=1) + + +def load_spec(path): + try: + with open(path, encoding="utf-8") as fh: + spec = yaml.safe_load(fh) + except FileNotFoundError: + sys.stderr.write(f"{path}: not found. Fetch it first (command in --help).\n") + return None + except yaml.YAMLError as err: + sys.stderr.write(f"{path}: not valid YAML ({err}).\n") + return None + if not isinstance(spec, dict) or "components" not in spec or "info" not in spec: + sys.stderr.write(f"{path}: not an OpenAPI document (no `info` or `components`).\n") + return None + components = (spec.get("components") or {}).get("schemas") or {} + if not any("x-origin-webhook-events" in s for s in components.values() if isinstance(s, dict)): + sys.stderr.write( + f"{path}: no schema carries `x-origin-webhook-events`; is this the Origin spec?\n" + ) + return None + return spec def main(argv): - if len(argv) < 2: + if len(argv) < 2 or argv[1] in ("-h", "--help"): print(__doc__) return 1 - with open(argv[1], encoding="utf-8") as fh: - spec = yaml.safe_load(fh) - mode = argv[2] if len(argv) > 2 else "all" + spec = load_spec(argv[1]) + if spec is None: + return 1 + components = spec["components"]["schemas"] + schema_mode = len(argv) > 2 and argv[2] == "schema" + if schema_mode and len(argv) < 4: + sys.stderr.write("schema mode needs a component name.\n") + return 1 print(f"# {spec['info'].get('title')} {spec['info'].get('version')}") - if mode == "schema": - print_schema(spec["components"]["schemas"], argv[3]) - return 0 - if mode in ("ops", "all"): - print_ops(spec) - if mode in ("events", "all"): - print_events(spec) - if mode in ("scopes", "all"): - print_scopes(spec) + if schema_mode: + print_schema(components, argv[3]) + else: + print_events(components) return 0 From 3992065c0c8a09f18af1f2995477fe4815c7078f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 24 Sep 2026 17:01:31 +0000 Subject: [PATCH 07/26] Point at the Origin docs instead of restating them origin-api becomes a fetch-first list, a where-to-look table of llms-full.txt anchors, four priority rules, and a short coming-from-GitHub list for facts the docs do not carry yet. origin-isms rows point at docs anchors instead of restating the idiom. spec-mapping drops copied paths and slugs and the x-origin-webhook-resource extension, which the current spec does not have; x-cursor-visibility is corrected to a field badge. brief-template's mirror rule and hello-world mechanics defer to the docs. gap-bar and README drop a feedback address the docs do not name. Co-authored-by: ali.nikseresht --- origin-apps/README.md | 18 ++- origin-apps/skills/origin-api/SKILL.md | 136 +++++++++--------- .../references/brief-template.md | 34 +++-- .../references/gap-bar.md | 16 +-- .../references/origin-isms.md | 56 ++++---- .../references/spec-mapping.md | 56 ++++---- 6 files changed, 156 insertions(+), 160 deletions(-) diff --git a/origin-apps/README.md b/origin-apps/README.md index c9955bf55..52a45a3f2 100644 --- a/origin-apps/README.md +++ b/origin-apps/README.md @@ -8,14 +8,12 @@ reads [Agent Skills](https://agentskills.io). ## What it includes -`origin-api` is the general skill. It lists the sources to fetch first (the -live OpenAPI spec and docs are the only source for endpoints, scopes, and -event slugs) and then the Origin rules that GitHub habits get wrong. Native -versus mirrored repositories. Opt-in webhook events. `v1ed` signatures. -`deliveryId` idempotency. Lean payloads. Credential kinds and token minting. -Scopes from `x-origin-scopes`. Opaque page tokens. TypeIDs. `404` semantics. -Rate limits. The differences from GitHub that are decisions, not gaps. Use it -for any Origin work. +`origin-api` is the general skill. It sends the agent to the live OpenAPI +spec and docs for every fact, gives a table of which docs section answers +which question, and names the four rules to check first (native versus +mirrored repositories, event subscriptions, webhook verification, scopes +from the spec) plus the GitHub features Origin does not have. Use it for any +Origin work. `port-github-app-to-origin` builds on `origin-api`. Run it inside your GitHub App's repository. It reads what the app uses from GitHub out of the code, maps @@ -88,8 +86,8 @@ mkdir -p .cursor/skills && cp -r plugins/origin-apps/skills/* .cursor/skills/ ## Where the brief goes The porting skill writes `ORIGIN-PORTING-BRIEF.md` at the repository root and -prints its path. The gap cards in the brief are yours to send, through your -shared Slack channel with Cursor if you have one, or to `hi@cursor.com`. +prints its path. The gap cards in the brief are yours to send through +whatever contact route you have with Cursor. ## License diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md index e139d2655..bfa17d3a4 100644 --- a/origin-apps/skills/origin-api/SKILL.md +++ b/origin-apps/skills/origin-api/SKILL.md @@ -2,10 +2,11 @@ name: origin-api description: >- Guides building on the Cursor Origin API: creating an Origin App, - authenticating as it, calling the REST API, receiving webhooks. Use whenever code or a plan touches - Origin endpoints, installation tokens, scopes, webhook subscriptions or - signatures, page tokens, or an Origin App manifest. Lists the sources to - fetch first and the Origin rules that GitHub habits get wrong. + authenticating as it, calling the REST API, receiving webhooks. Use whenever + code or a plan touches Origin endpoints, installation tokens, scopes, webhook + subscriptions or signatures, page tokens, or an Origin App manifest. Points + at the docs section that answers each question and names the few rules that + GitHub habits get wrong. license: MIT compatibility: >- Needs network access to https://cursor.com/docs/api/origin/* at run time. @@ -13,73 +14,72 @@ compatibility: >- # Build on the Origin API -## Fetch first. Never name an endpoint, scope, slug, or header from memory. +The docs are the only source of truth. This skill tells you where to look and +which rules to check first. It restates nothing you can read there. -- `https://cursor.com/docs/api/origin/openapi.yaml` is the contract. Every - operation carries `x-origin-scopes` (`scopes`, `tokenTypes`, `ambient`). - Every webhook payload schema carries `x-origin-webhook-events`, the only - authoritative list of subscribable slugs. -- `https://cursor.com/docs/api/origin/llms-full.txt` covers the installation - flow, authentication, the scopes table, mirrored repositories, webhook - headers and verification, the delivery envelope, retries, pagination, - errors, and current limitations. -- `https://cursor.com/docs/api/origin/llms.txt` is the index. - `https://cursor.com/docs/api/origin/changelog` says what moved. +## Fetch first. Never name an endpoint, scope, slug, header, or limit from memory. + +- `https://cursor.com/docs/api/origin/openapi.yaml`: the contract. Every + operation carries `x-origin-scopes`; every webhook payload schema carries + `x-origin-webhook-events`, the list of slugs that deliver it. +- `https://cursor.com/docs/api/origin/llms-full.txt`: the prose reference. + Anchors below are sections of this file. +- `https://cursor.com/docs/api/origin/llms.txt` (index) and + `https://cursor.com/docs/api/origin/changelog` (what moved). Cite `operationId`s and `llms-full.txt` anchors. If this file and the fetched docs disagree, the docs win. -## Rules GitHub habits get wrong +## Where to look + +| Question | Section of `llms-full.txt` | +| --- | --- | +| Which credential for which call; how to mint and how long it lives | `#authentication` through `#git-https-authentication` | +| Install flow and the callback receipt | `#installation`, `#installation-receipt` | +| Which scope an operation needs | `x-origin-scopes` on the operation; `#scopes` for the table and the rules | +| What an installation can do on a mirrored repository | `#mirrored-repositories` | +| Webhook headers, signature, envelope, retries, pausing, recovery | `#webhooks` and its subsections | +| Which events exist and which are delivered without subscribing | `#events` | +| Payload shapes | `#event-payloads` and the schema's `x-origin-webhook-events` | +| Pagination, errors, request IDs, repository paths, ID form | `#common-conventions` | +| Rate limits and headers | `#rate-limits` | +| Check-run keys, attempts, stale writes | `#check-runs` | +| What is not there yet | `#current-limitations` | +| A checklist to build against | `#implementation-checklist` | + +## Rules to check first + +In priority order. Each is one line in the docs; getting it wrong costs a +week. + +1. **Native or mirror.** Confirm the target repositories are Origin-native + or stable outbound mirrors before anything else. On any other mirror + state an installation can only read, and pushes are not delivered + (`#mirrored-repositories`, `#events`). +2. **Subscribe.** Only the `installation.*` events arrive without a + subscription. Select every other event the app needs; a missing + subscription is silence, not an error (`#events`). +3. **Verify, dedupe, acknowledge.** Verify the signature over the raw body + before parsing, dedupe on the delivery ID, return `2xx`, then process + (`#signature-verification`, `#retries`, `#automatic-disable`). The digest + step differs from the Standard Webhooks spec, so do not assume a generic + verifier passes. +4. **Scopes from the spec.** Request the union of `x-origin-scopes.scopes` + over the operations the app calls, and nothing else (`#scopes`). + +## Coming from GitHub + +Origin does not have these. Build the Origin idiom instead of emulating the +GitHub one. Until the docs carry this list, it lives here: -- **Check native or mirror before anything else.** Apps get full scopes only - on Origin-native repositories. A repository mirrored from GitHub returns - `403` on every write and never delivers `repository.pushed`. The ping - succeeds and then nothing else arrives. -- **Only installation lifecycle events are delivered by default.** Select - every other event in app settings. An unselected event is silence, not an - error. -- **Signature `v1ed` is Ed25519 over a SHA-256 digest of the raw body**, with - keys from Origin's JWKS. It matches Standard Webhooks except for the digest, - so off-the-shelf verifiers fail unmodified. Verify the raw body before - parsing. Reject `webhook-timestamp` more than five minutes off. Headers are - `webhook-*`, not `x-github-*`. After verification the body is authoritative. -- **`deliveryId` is the idempotency key.** It is stable across retries. - `event.id` identifies the domain event. Return `2xx` after verification and - process asynchronously, because persistent failure pauses delivery for the - app. -- **Payloads are lean snapshots** of the one object that changed, plus - references to its containers. No changed-file lists, before-SHAs, web URLs, - or inlined profiles. Follow up with the `Get…` for the object and count the - fan-out. The action is in the slug (`pull_request.review.submitted`). There - is no `action` field. -- **The installation receipt JWT proves consent. It is never a Bearer token.** - Its `sub` is the installation ID. -- **The app JWT is EdDSA over Ed25519, not RS256.** Register only the public - key. -- **Installation tokens are short-lived. Mint them just in time** from the - app JWT and attenuate to the scopes and `repositoryIds` the job needs (IDs, - not slugs). Git over HTTPS uses Basic auth with user `x-access-token` and - the token as password. Bearer is REST only. -- **Scopes come from the operations you call.** Request the union of their - `x-origin-scopes.scopes`. `write` implies `read`. `repository:metadata:read` - is automatic. `ambient: true` needs no request. Operations whose - `tokenTypes` is user-only (create app, add repositories to an installation, - mirror transitions) have no app-side path, and there is no `/user` analog. -- **Page tokens are opaque and bound to the resource and filters.** Never - construct, parse, or reuse one across filter changes. Send `pageSize` on - every request, including continuations. There is no `Link` header and no - total. -- **IDs are TypeIDs** (`repo_…`, `i_…`, `cmt_…`), never integers. Cache IDs, - not slugs. `/repos/_/{repoId}` survives renames. 64-bit integers (pull - request numbers, versions) are JSON strings. Defaults are present (`false`, `0`, - `[]`), so a present `false` is a value. -- **`404` means not found or no access.** Branch on status and `code`, never - on message text. Quote `X-Request-ID` when escalating. -- **The rate limit is a per-principal point budget.** Honor `Retry-After` on - `429`. Git HTTPS is metered separately. Cursor raises per-app budgets on - request. -- **These are decisions, not gaps.** No commit statuses (check runs upsert on - a caller-stable `key`). No Issues (conversation is pull request comments, threads, - reviews, and labels). No GraphQL. No per-repository webhook CRUD. No user or - email directory. Reviews anchor to a pull request version, not a SHA. A - thread materializes from its first diff-anchored comment. +- Issues. Conversation is pull request comments, threads, reviews, labels. +- Commit statuses. Check runs with a stable `key` (`#check-runs`). +- GraphQL. REST only. +- Per-repository webhook CRUD. Subscriptions are app settings. +- User, email, team, or member directory. Actors are IDs, plus a handle + where the payload exposes one. +- `/user`-style flows. Discover repositories through the installation. +- Reviews keyed by commit SHA. Reviews reference a pull request version. +- Numeric IDs and page numbers. IDs and page tokens are opaque strings; do + not parse or construct them, and cache repository IDs rather than slugs + (`#repository-paths`, `#pagination`). diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md index 5f893c11c..876fb506b 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md +++ b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md @@ -109,24 +109,22 @@ fields read only for logging. ## 5. Hello-world path -The shortest route to one real event from one native repository. Each step -is a verification, linked to `llms-full.txt`. Append one step for each -spec-silent behavior the brief depends on, stated as the observation to make. - -1. Create the app, register the Ed25519 public key, and set the webhook URL - and callback. -2. Select every repository event from § 3 in app settings. -3. Install on an Origin-native repository. Verify the receipt JWT and read - the installation ID from `sub`. -4. Mint an app JWT, exchange it for an installation token, and confirm the - repository is listed with the mirror state § 2 expects. -5. Verify the ping (`v1ed` over the raw body, timestamp skew, `deliveryId` - dedupe). -6. Perform the smallest action in § 3 and confirm the slug and the § 4 - fields arrive. If the ping arrived and this did not, re-check steps 2 and - 3 first. -7. Smallest write from § 3 (check run with a stable `key`, pull request comment), - confirming its scope is in the grant. +The shortest route to one real event from one native repository. The +mechanics are in `llms-full.txt#implementation-checklist` and the sections it +links; this list is the observations to make, in order. Append one step for +each spec-silent behavior the brief depends on. + +1. App created, signing key registered, webhook URL and callback set. +2. Every repository event from § 3 selected in app settings. +3. Installed on an Origin-native repository; receipt verified; installation + ID recorded. +4. Installation token minted; the repository appears in the installation's + repositories with the mirror state § 2 expects. +5. Ping received and verified; a retried delivery is deduplicated. +6. Smallest action in § 3 performed; the expected slug and the § 4 fields + arrive. If the ping arrived and this did not, re-check steps 2 and 3 + first. +7. Smallest write from § 3 succeeds with the scopes from the § 3 line. ## 6. Gaps worth raising diff --git a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md index 578d20eb7..64eb3f170 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md +++ b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md @@ -33,8 +33,9 @@ At least one must hold. Quote it on the card. live spec before writing any card. - A concept the Origin docs never mention (Marketplace billing, merge queues, Actions, Projects). That is `unknown` plus a question. -- A GitHub Search query. The idiom is a list operation with its filters plus a - client-side predicate. A sorted list read that stops at a cutoff costs +- A GitHub Search query, when the spec has no search operation for that + resource. The idiom is a list operation with its filters plus a client-side + predicate. A sorted list read that stops at a cutoff costs proportional to the matches, not the collection. If that count fails the bar, the card is about a filter, never about search. @@ -67,9 +68,8 @@ can act without a call. ``` Do not propose scope, field, or route names. Do not batch unrelated -capabilities. Do not send cards yourself. The team decides -what goes out, through their shared Slack channel with Cursor or to -`hi@cursor.com` with "Origin API" and the app name in the subject, quoting the -spec version and any `X-Request-ID` from failed calls. "This is by design, -here is the idiom" is a fine answer. It goes back into the brief as -`by-design-absent`. +capabilities. Do not send cards yourself. The team decides what goes out, +through whatever contact route they have with Cursor, quoting the spec +version and any request ID from failed calls (`llms-full.txt#errors`). "This +is by design, here is the idiom" is a fine answer. It goes back into the +brief as `by-design-absent`. diff --git a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md index 3bbda21a3..3342bdfd4 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md +++ b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md @@ -2,35 +2,37 @@ Check here before labeling anything `gap`. A row below is `by-design-absent` or `reshaped`. The brief points at the idiom and never files a card. The -`origin-api` skill carries the reasoning. This table carries only the label -and the idiom. Confirm current wording in `llms-full.txt`. +third column says where the Origin answer lives: an anchor in +`llms-full.txt`, or "Coming from GitHub" in the `origin-api` skill for items +the docs do not yet carry. Read the source; do not copy this table into the +brief. -| GitHub call, event, or permission | Label | Origin idiom | +| GitHub call, event, or permission | Label | Where the Origin answer lives | | --- | --- | --- | -| Acting on a repository mirrored *from* GitHub (writes, `push` events) | `by-design-absent` | Install on Origin-native repositories or stable outbound mirrors. Mirrors are read-only to apps and deliver no `repository.pushed`. First question of every brief. | -| Install callback with `installation_id` + `setup_action` query params | `reshaped` | Signed installation receipt JWT; `sub` is the installation ID. Not a Bearer. | -| RS256 app JWT | `reshaped` | EdDSA over Ed25519; register the public key. | -| `ghs_` installation tokens, long cache | `reshaped` | `oit_…`, short-lived, minted just in time, attenuable to scopes and `repositoryIds`. | -| User OAuth: `/user`, `/user/installations`, `/user/repos`, install-by-user picker | `by-design-absent` | Repository discovery is `/installation/repos`. App creation and installation-repository changes are admin actions with a user credential, not app calls. Ask what the flow should do. | -| Permissions `: read\|write` | `reshaped` | Scopes `repository:[:]:` taken from `x-origin-scopes` of the called operations. | -| Numeric IDs, `/repositories/{id}` | `reshaped` | TypeIDs; `/repos/_/{repoId}`. | -| `Link` / `page` / `per_page` pagination, total counts | `reshaped` | `pageSize` / `pageToken` / `nextPageToken`; opaque; no total. | -| GraphQL | `by-design-absent` | REST; decompose and count the fan-out. | -| Commit statuses (`statuses` permission, `POST /statuses/{sha}`) | `reshaped` | Check runs upserting on a caller-stable `key`; rulesets bind on the key. | -| Issues (`issues` permission, `issues.*` events, `/issues/{n}` not on a pull request) | `by-design-absent` | Pull request comments, threads, reviews, and labels. Ask what the pull-request-scoped behavior should be. | -| `/issues/{n}/comments`, `/issues/{n}/labels` used on a pull request | `reshaped` | Same calls under `/pulls/{n}/…`. | -| Repository webhook CRUD (`/repos/…/hooks`) | `by-design-absent` | Subscriptions are app settings. | -| App-manifest conversion, OAuth-app token mints | `by-design-absent` | App creation form (accepts prefill params) or user-credential `CreateApp`. | -| Git Data API tree/blob writes | `by-design-absent` | Push over Git HTTPS with an installation token, or the documented commit-from-files and ref operations. | -| Standalone review-thread objects | `reshaped` | A thread materializes from its first diff-anchored comment and is addressable for resolve/reopen. | -| User, email, team, and member lookups | `by-design-absent` | Actors are TypeIDs (plus a handle where exposed). No directory. | -| Single `pull_request` event with `action` field, `previous_attributes` | `reshaped` | One slug per action (`pull_request.head_ref.pushed`); no `action` field, no delta. Confirm each slug in `x-origin-webhook-events`. | -| `x-github-*` headers, HMAC `x-hub-signature-256` | `reshaped` | `webhook-*` headers; `v1ed` Ed25519 over a SHA-256 digest, JWKS keys. | -| Fields GitHub inlines in payloads (changed files on push, before-SHA, `html_url`, `sender` profile) | `reshaped` | Follow-up `Get…`, `CompareCommits`, or `ListComparisonFiles` with identifiers the payload carries. Name the call for each field and count the fan-out. | -| Default delivery of all events after app creation | `reshaped` | Only installation lifecycle is default; select the rest. | -| Reviews keyed by `commit_id` | `reshaped` | Reviews anchor to a pull request version. | -| Finding own check runs / comments by actor | `reshaped` | Check runs by `key`; comments and reviews by a marker the app controls. | -| Requested-reviewer team pages, `created_via` | `by-design-absent` | Reviewers addressed by identifier only. | +| Writes or `push` events on a repository mirrored from GitHub | `by-design-absent` | `#mirrored-repositories`, `#events`. First question of every brief. | +| Install callback query parameters (`installation_id`, `setup_action`) | `reshaped` | `#installation-receipt` | +| RS256 app JWT | `reshaped` | `#app-jwt` | +| Long-lived installation tokens | `reshaped` | `#installation-access-token` | +| User OAuth, `/user`, `/user/installations`, install-by-user picker | `by-design-absent` | `#scopes` (user-credential operations); Coming from GitHub. Ask what the flow should do. | +| Permissions `: read\|write` | `reshaped` | `#scopes`; `x-origin-scopes` per operation | +| Numeric IDs, `/repositories/{id}` | `reshaped` | `#repository-paths` | +| `Link` / `page` / `per_page` pagination, total counts | `reshaped` | `#pagination` | +| GraphQL | `by-design-absent` | Coming from GitHub. Decompose and count the fan-out. | +| Commit statuses (`statuses` permission, `POST /statuses/{sha}`) | `reshaped` | `#check-runs`; Coming from GitHub | +| Issues (`issues` permission, `issues.*` events, `/issues/{n}` not on a pull request) | `by-design-absent` | Coming from GitHub. Ask what the pull-request-scoped behavior should be. | +| `/issues/{n}/comments`, `/issues/{n}/labels` used on a pull request | `reshaped` | Pull requests endpoint reference; same calls under `/pulls/{n}/…` | +| Repository webhook CRUD (`/repos/…/hooks`) | `by-design-absent` | Coming from GitHub; subscriptions are app settings (`#events`) | +| App-manifest conversion, OAuth-app token mints | `by-design-absent` | `#installation`, Create App in the endpoint reference | +| Git Data API tree/blob writes | `by-design-absent` | `#git-https-authentication`; Git data endpoint reference | +| Standalone review-thread objects | `reshaped` | `#current-limitations`; Pull requests endpoint reference | +| User, email, team, and member lookups | `by-design-absent` | `#resource-references`; Coming from GitHub | +| Single `pull_request` event with an `action` field, `previous_attributes` | `reshaped` | `#events`, `#event-payloads` | +| `x-github-*` headers, HMAC `x-hub-signature-256` | `reshaped` | `#headers`, `#signature-verification` | +| Payload inlines (changed files on push, before-SHA, `html_url`, `sender` profile) | `reshaped` | `#current-limitations`, `#resource-references`. Name the follow-up call per field and count the fan-out. | +| All events delivered after app creation | `reshaped` | `#events` | +| Reviews keyed by `commit_id` | `reshaped` | Coming from GitHub; `pullRequestVersion` in the review schema | +| Finding own check runs or comments by actor | `reshaped` | `#check-runs` (`key`); comments and reviews by a marker the app controls | +| Requested-reviewer team pages, `created_via` | `by-design-absent` | Pull requests endpoint reference (reviewers by identifier) | A feature that is not on this list and that the Origin docs never mention (Marketplace billing, merge queues, Actions, Pages, Projects) is `unknown` diff --git a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md index 5c27442c5..878510511 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md +++ b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md @@ -5,28 +5,27 @@ table here. Build the index once. Every later step looks things up in it. ## Extensions the spec carries -| Extension | Where | Meaning | +| Extension | Where | Use | | --- | --- | --- | -| `x-origin-scopes` | every operation | `scopes` the operation requires. `tokenTypes` it accepts (`app`, `installation`, `user`). `ambient: true` means there is nothing to request. | -| `x-origin-webhook-events` | payload schemas | The slugs that deliver this payload shape. A schema carrying it is a webhook family. These slugs are the only authoritative event list. | -| `x-origin-webhook-resource` | some payload schemas | The REST component the payload embeds. When absent, infer it from `$ref`s. Pushes, deletions, and reviewer requests are event-native with no REST twin. | -| `x-cursor-visibility: PREVIEW` | some operations | Usable. The shape may move. Carry the badge into the brief as a `preview` suffix. | +| `x-origin-scopes` | every operation | The scope, credential, and ambient rules for that operation. Rules in `llms-full.txt#scopes`. | +| `x-origin-webhook-events` | payload schemas | The slugs that deliver this payload shape. A schema carrying it is a webhook family. Infer the embedded resource from its `$ref`s; some families (pushes, deletions, reviewer changes) have no REST twin. | +| `x-cursor-visibility` | some schema fields | A stability badge on a field. Carry it into the brief as a `preview` suffix on rows that read the field. | ## Build the index 1. **Operations and scopes**: `rg -B1 -A4 'x-origin-scopes:' openapi.yaml` - prints every `operationId` with its `scopes`, `tokenTypes`, and `ambient` - flag. From it, note the union of scopes with the operations that need - each, and separate installation-requestable scopes from ambient and - user-only ones. The user-only set tells you which GitHub flows have no - app-side equivalent. Read parameters and response components from the - spec when a rule below asks for them. + prints every `operationId` with its scope block. From it, note the union + of scopes with the operations that need each, and separate + installation-requestable scopes from ambient and user-only ones + (`llms-full.txt#scopes` explains the difference). The user-only set tells + you which GitHub flows have no app-side equivalent. Read parameters and + response components from the spec when a rule below asks for them. 2. **Webhook events**: `rg -A3 'x-origin-webhook-events:' openapi.yaml` lists every slug with its payload schema. `scripts/index-origin-spec.py openapi.yaml` prints each payload family with its fields and `$ref`s resolved two levels deep; `schema ` does the same for one component. - From `llms-full.txt` § Webhooks, note which slugs are app-lifecycle - (always delivered) versus repository events (must be selected). + From `llms-full.txt#events`, note which slugs are delivered without a + subscription and which must be selected. 3. **Resources**: component schemas returned by `Get…`/`List…`, with field names, for "does the Origin object carry this field". @@ -39,17 +38,17 @@ match is a candidate, never a result. **REST calls** -1. Normalize the GitHub path to the same shape under `/v1/origin` - (`{owner}/{repo}` → `{ownerSlug}/{repoName}`, `{pull_number}` → - `{pullNumber}`; custom verbs are `:verb` suffixes such as - `…/contents:batchGet`). -2. Re-home GitHub's issue-flavored pull request calls: `/issues/{n}/comments` and - `/issues/{n}/labels` used *on a pull request* live under `/pulls/{n}/…`. - That is a path change, not a gap. When the code uses them on real issues, - see `origin-isms.md`. -3. Re-home `/app`, `/app/installations`, access-token minting, and - `/installation/repositories` under `/v1/origin/app…` and - `/v1/origin/installation/repos`; confirm `tokenTypes`. `/user`, +1. Look for the same resource path under the Origin base path + (`llms-full.txt#repository-paths` gives the path shape). Most GitHub + repository, pull request, check, label, branch, and commit paths have a + direct or near-direct counterpart. +2. Re-home GitHub's issue-flavored pull request calls (`/issues/{n}/comments`, + `/issues/{n}/labels` used *on a pull request*) to the pull request + endpoints. That is a path change, not a gap. When the code uses them on + real issues, see `origin-isms.md`. +3. Re-home app and installation calls (`/app`, `/app/installations`, + access-token minting, `/installation/repositories`) to the Apps and + installations endpoints and confirm the credential each accepts. `/user`, `/user/installations`, `/orgs/…`, `/search/…`, and `/repositories/{id}` have no path counterpart. Consult `origin-isms.md` before labeling them. 4. Compare parameters as well as paths. A matching path that lacks a filter @@ -68,11 +67,10 @@ the operations the code calls and take the union of *their* (`statuses`, `issues`, `members`, `organization_*`, `pages`, `actions`, `workflows`, `deployments`) go through `origin-isms.md` first. -**Events → slugs.** Each GitHub `event` + `action` pair becomes a candidate -slug (`pull_request` plus `synchronize` becomes `pull_request.head_ref.pushed`). -Confirm it exists in the webhook index. A candidate that does not exist is -not an event on Origin. Check whether the state change is observable another -way before classifying it. +**Events → slugs.** Each GitHub `event` + `action` pair maps to at most one +slug in `llms-full.txt#events`; the action is part of the slug. A pair with +no slug is not an event on Origin. Check whether the state change is +observable another way before classifying it. **Payload fields → schema properties.** For each field path a handler reads, walk the mapped slug's payload schema and record one of four outcomes. From bfd034de172b2eaf25d9f13bd8de52aa0731a4ff Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 24 Sep 2026 17:32:51 +0000 Subject: [PATCH 08/26] Point at the new docs anchors The docs now carry coming-from-github, ids, preview, and the x-origin-* summary, so the in-skill Coming from GitHub list becomes a pointer, ID form points at #ids, PREVIEW points at #preview without claiming where the badge appears, and the subscription and pageSize rules defer to #events and #pagination. Co-authored-by: ali.nikseresht --- origin-apps/skills/origin-api/SKILL.md | 35 ++++++++----------- .../references/brief-template.md | 2 +- .../references/origin-isms.md | 22 ++++++------ .../references/spec-mapping.md | 8 ++--- 4 files changed, 29 insertions(+), 38 deletions(-) diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md index bfa17d3a4..837c5c861 100644 --- a/origin-apps/skills/origin-api/SKILL.md +++ b/origin-apps/skills/origin-api/SKILL.md @@ -19,9 +19,8 @@ which rules to check first. It restates nothing you can read there. ## Fetch first. Never name an endpoint, scope, slug, header, or limit from memory. -- `https://cursor.com/docs/api/origin/openapi.yaml`: the contract. Every - operation carries `x-origin-scopes`; every webhook payload schema carries - `x-origin-webhook-events`, the list of slugs that deliver it. +- `https://cursor.com/docs/api/origin/openapi.yaml`: the contract. Its + `x-origin-*` extensions are summarized under `#endpoint-reference`. - `https://cursor.com/docs/api/origin/llms-full.txt`: the prose reference. Anchors below are sections of this file. - `https://cursor.com/docs/api/origin/llms.txt` (index) and @@ -40,8 +39,11 @@ docs disagree, the docs win. | What an installation can do on a mirrored repository | `#mirrored-repositories` | | Webhook headers, signature, envelope, retries, pausing, recovery | `#webhooks` and its subsections | | Which events exist and which are delivered without subscribing | `#events` | -| Payload shapes | `#event-payloads` and the schema's `x-origin-webhook-events` | -| Pagination, errors, request IDs, repository paths, ID form | `#common-conventions` | +| Payload shapes and the `x-origin-webhook-events` extension | `#event-payloads` | +| Pagination, errors, request IDs, repository paths | `#common-conventions` | +| ID form and stability | `#ids` | +| What a `PREVIEW` badge means | `#preview` | +| GitHub features Origin does not have | `#coming-from-github` | | Rate limits and headers | `#rate-limits` | | Check-run keys, attempts, stale writes | `#check-runs` | | What is not there yet | `#current-limitations` | @@ -57,8 +59,8 @@ week. state an installation can only read, and pushes are not delivered (`#mirrored-repositories`, `#events`). 2. **Subscribe.** Only the `installation.*` events arrive without a - subscription. Select every other event the app needs; a missing - subscription is silence, not an error (`#events`). + subscription. `#events` says what else delivery needs; a missing + subscription is silence, not an error. 3. **Verify, dedupe, acknowledge.** Verify the signature over the raw body before parsing, dedupe on the delivery ID, return `2xx`, then process (`#signature-verification`, `#retries`, `#automatic-disable`). The digest @@ -66,20 +68,11 @@ week. verifier passes. 4. **Scopes from the spec.** Request the union of `x-origin-scopes.scopes` over the operations the app calls, and nothing else (`#scopes`). +5. **Opaque tokens and IDs.** Page tokens and IDs are not yours to build or + parse (`#pagination`, `#ids`). ## Coming from GitHub -Origin does not have these. Build the Origin idiom instead of emulating the -GitHub one. Until the docs carry this list, it lives here: - -- Issues. Conversation is pull request comments, threads, reviews, labels. -- Commit statuses. Check runs with a stable `key` (`#check-runs`). -- GraphQL. REST only. -- Per-repository webhook CRUD. Subscriptions are app settings. -- User, email, team, or member directory. Actors are IDs, plus a handle - where the payload exposes one. -- `/user`-style flows. Discover repositories through the installation. -- Reviews keyed by commit SHA. Reviews reference a pull request version. -- Numeric IDs and page numbers. IDs and page tokens are opaque strings; do - not parse or construct them, and cache repository IDs rather than slugs - (`#repository-paths`, `#pagination`). +GitHub habits do not carry over. Before mapping a GitHub feature onto Origin, +read `#coming-from-github` for what Origin does not have and what to use +instead. diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md index 876fb506b..e2c6b384d 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md +++ b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md @@ -22,7 +22,7 @@ card per gap. The team will argue over it in one sitting. | `by-design-absent` | Origin deliberately does not reproduce it (`origin-isms.md`). Names the idiom or "no equivalent; decision needed". | | `gap` | No workaround, or one that fails `gap-bar.md`. Has a card in § 6. | | `unknown` | Discovery or the spec could not answer. Has a question in § 7. | -| `preview` (suffix) | The Origin operation is `x-cursor-visibility: PREVIEW`. Usable. The shape may move. | +| `preview` (suffix) | The row touches an element badged `x-cursor-visibility: PREVIEW` (`llms-full.txt#preview`). | **Size** (kind of change, never time) diff --git a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md index 3342bdfd4..1be7fe4d9 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md +++ b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md @@ -2,10 +2,8 @@ Check here before labeling anything `gap`. A row below is `by-design-absent` or `reshaped`. The brief points at the idiom and never files a card. The -third column says where the Origin answer lives: an anchor in -`llms-full.txt`, or "Coming from GitHub" in the `origin-api` skill for items -the docs do not yet carry. Read the source; do not copy this table into the -brief. +third column is the `llms-full.txt` anchor where the Origin answer lives. +Read the source; do not copy this table into the brief. | GitHub call, event, or permission | Label | Where the Origin answer lives | | --- | --- | --- | @@ -13,24 +11,24 @@ brief. | Install callback query parameters (`installation_id`, `setup_action`) | `reshaped` | `#installation-receipt` | | RS256 app JWT | `reshaped` | `#app-jwt` | | Long-lived installation tokens | `reshaped` | `#installation-access-token` | -| User OAuth, `/user`, `/user/installations`, install-by-user picker | `by-design-absent` | `#scopes` (user-credential operations); Coming from GitHub. Ask what the flow should do. | +| User OAuth, `/user`, `/user/installations`, install-by-user picker | `by-design-absent` | `#coming-from-github`; `#scopes` (user-credential operations). Ask what the flow should do. | | Permissions `: read\|write` | `reshaped` | `#scopes`; `x-origin-scopes` per operation | -| Numeric IDs, `/repositories/{id}` | `reshaped` | `#repository-paths` | +| Numeric IDs, `/repositories/{id}` | `reshaped` | `#ids`, `#repository-paths` | | `Link` / `page` / `per_page` pagination, total counts | `reshaped` | `#pagination` | -| GraphQL | `by-design-absent` | Coming from GitHub. Decompose and count the fan-out. | -| Commit statuses (`statuses` permission, `POST /statuses/{sha}`) | `reshaped` | `#check-runs`; Coming from GitHub | -| Issues (`issues` permission, `issues.*` events, `/issues/{n}` not on a pull request) | `by-design-absent` | Coming from GitHub. Ask what the pull-request-scoped behavior should be. | +| GraphQL | `by-design-absent` | `#coming-from-github`. Decompose and count the fan-out. | +| Commit statuses (`statuses` permission, `POST /statuses/{sha}`) | `reshaped` | `#coming-from-github`, `#check-runs` | +| Issues (`issues` permission, `issues.*` events, `/issues/{n}` not on a pull request) | `by-design-absent` | `#coming-from-github`. Ask what the pull-request-scoped behavior should be. | | `/issues/{n}/comments`, `/issues/{n}/labels` used on a pull request | `reshaped` | Pull requests endpoint reference; same calls under `/pulls/{n}/…` | -| Repository webhook CRUD (`/repos/…/hooks`) | `by-design-absent` | Coming from GitHub; subscriptions are app settings (`#events`) | +| Repository webhook CRUD (`/repos/…/hooks`) | `by-design-absent` | `#coming-from-github`, `#events` | | App-manifest conversion, OAuth-app token mints | `by-design-absent` | `#installation`, Create App in the endpoint reference | | Git Data API tree/blob writes | `by-design-absent` | `#git-https-authentication`; Git data endpoint reference | | Standalone review-thread objects | `reshaped` | `#current-limitations`; Pull requests endpoint reference | -| User, email, team, and member lookups | `by-design-absent` | `#resource-references`; Coming from GitHub | +| User, email, team, and member lookups | `by-design-absent` | `#coming-from-github`, `#resource-references` | | Single `pull_request` event with an `action` field, `previous_attributes` | `reshaped` | `#events`, `#event-payloads` | | `x-github-*` headers, HMAC `x-hub-signature-256` | `reshaped` | `#headers`, `#signature-verification` | | Payload inlines (changed files on push, before-SHA, `html_url`, `sender` profile) | `reshaped` | `#current-limitations`, `#resource-references`. Name the follow-up call per field and count the fan-out. | | All events delivered after app creation | `reshaped` | `#events` | -| Reviews keyed by `commit_id` | `reshaped` | Coming from GitHub; `pullRequestVersion` in the review schema | +| Reviews keyed by `commit_id` | `reshaped` | `#coming-from-github` | | Finding own check runs or comments by actor | `reshaped` | `#check-runs` (`key`); comments and reviews by a marker the app controls | | Requested-reviewer team pages, `created_via` | `by-design-absent` | Pull requests endpoint reference (reviewers by identifier) | diff --git a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md index 878510511..fb5826de7 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md +++ b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md @@ -7,9 +7,9 @@ table here. Build the index once. Every later step looks things up in it. | Extension | Where | Use | | --- | --- | --- | -| `x-origin-scopes` | every operation | The scope, credential, and ambient rules for that operation. Rules in `llms-full.txt#scopes`. | -| `x-origin-webhook-events` | payload schemas | The slugs that deliver this payload shape. A schema carrying it is a webhook family. Infer the embedded resource from its `$ref`s; some families (pushes, deletions, reviewer changes) have no REST twin. | -| `x-cursor-visibility` | some schema fields | A stability badge on a field. Carry it into the brief as a `preview` suffix on rows that read the field. | +| `x-origin-scopes` | every operation | The scope and credential rules for that operation. `llms-full.txt#scopes`, `#endpoint-reference`. | +| `x-origin-webhook-events` | payload schemas | The slugs that deliver this payload shape; a schema carrying it is a webhook family. `#event-payloads`. Infer the embedded resource from its `$ref`s; some families have no REST twin. | +| `x-cursor-visibility: PREVIEW` | operations, parameters, schemas, fields | `#preview`. Carry it into the brief as a `preview` suffix on any row that touches a badged element. | ## Build the index @@ -39,7 +39,7 @@ match is a candidate, never a result. **REST calls** 1. Look for the same resource path under the Origin base path - (`llms-full.txt#repository-paths` gives the path shape). Most GitHub + (`llms-full.txt#repository-paths` and `#ids` give the path forms). Most GitHub repository, pull request, check, label, branch, and commit paths have a direct or near-direct counterpart. 2. Re-home GitHub's issue-flavored pull request calls (`/issues/{n}/comments`, From 06ad1fa25685b1efa2529f5db4a7202a910f7ec4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 24 Sep 2026 17:44:45 +0000 Subject: [PATCH 09/26] Keep GitHub differences in the porting skill only The docs will not carry a coming-from-github section, so origin-api points at the porting skill in one line and origin-isms rows name the Origin answer or its docs anchor directly. The payload-field rule lives in the porting mapping step and points at the per-field notes under #event-payloads. Co-authored-by: ali.nikseresht --- origin-apps/skills/origin-api/SKILL.md | 6 ++---- .../skills/port-github-app-to-origin/SKILL.md | 6 +++--- .../references/origin-isms.md | 14 +++++++------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md index 837c5c861..258b7f1ab 100644 --- a/origin-apps/skills/origin-api/SKILL.md +++ b/origin-apps/skills/origin-api/SKILL.md @@ -43,7 +43,6 @@ docs disagree, the docs win. | Pagination, errors, request IDs, repository paths | `#common-conventions` | | ID form and stability | `#ids` | | What a `PREVIEW` badge means | `#preview` | -| GitHub features Origin does not have | `#coming-from-github` | | Rate limits and headers | `#rate-limits` | | Check-run keys, attempts, stale writes | `#check-runs` | | What is not there yet | `#current-limitations` | @@ -73,6 +72,5 @@ week. ## Coming from GitHub -GitHub habits do not carry over. Before mapping a GitHub feature onto Origin, -read `#coming-from-github` for what Origin does not have and what to use -instead. +GitHub habits do not carry over. The `port-github-app-to-origin` skill in +this plugin covers the differences. diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index 5ec551f8f..195a09b2b 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -44,9 +44,9 @@ GitHub. Follow it first. Nothing here repeats it. Two rules on top: find. 3. **Map** each inventory row (`references/spec-mapping.md`, "Matching") and label it with the parity labels in the brief template. Map the webhook - payload fields the code reads, not only the event names. A field GitHub - inlines is often a follow-up read on Origin, so name the call for each - field. Then: + payload fields the code reads, not only the event names. If a payload + lacks a field the REST resource has, read the resource; see the per-field + notes under `#event-payloads`. Name the call for each such field. Then: - Check `origin-isms.md` before writing `gap`. Check `gap-bar.md` before writing any card. - A GitHub feature the Origin docs never mention (Marketplace billing, diff --git a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md index 1be7fe4d9..8a3d7e26f 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md +++ b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md @@ -11,24 +11,24 @@ Read the source; do not copy this table into the brief. | Install callback query parameters (`installation_id`, `setup_action`) | `reshaped` | `#installation-receipt` | | RS256 app JWT | `reshaped` | `#app-jwt` | | Long-lived installation tokens | `reshaped` | `#installation-access-token` | -| User OAuth, `/user`, `/user/installations`, install-by-user picker | `by-design-absent` | `#coming-from-github`; `#scopes` (user-credential operations). Ask what the flow should do. | +| User OAuth, `/user`, `/user/installations`, install-by-user picker | `by-design-absent` | `#scopes` (user-credential operations); repository discovery through the installation. Ask what the flow should do. | | Permissions `: read\|write` | `reshaped` | `#scopes`; `x-origin-scopes` per operation | | Numeric IDs, `/repositories/{id}` | `reshaped` | `#ids`, `#repository-paths` | | `Link` / `page` / `per_page` pagination, total counts | `reshaped` | `#pagination` | -| GraphQL | `by-design-absent` | `#coming-from-github`. Decompose and count the fan-out. | -| Commit statuses (`statuses` permission, `POST /statuses/{sha}`) | `reshaped` | `#coming-from-github`, `#check-runs` | -| Issues (`issues` permission, `issues.*` events, `/issues/{n}` not on a pull request) | `by-design-absent` | `#coming-from-github`. Ask what the pull-request-scoped behavior should be. | +| GraphQL | `by-design-absent` | No GraphQL endpoint in the spec. Decompose into REST calls and count the fan-out. | +| Commit statuses (`statuses` permission, `POST /statuses/{sha}`) | `reshaped` | `#check-runs` (check runs with a stable `key`) | +| Issues (`issues` permission, `issues.*` events, `/issues/{n}` not on a pull request) | `by-design-absent` | No Issues in the spec; conversation is pull request comments, threads, reviews, labels. Ask what the pull-request-scoped behavior should be. | | `/issues/{n}/comments`, `/issues/{n}/labels` used on a pull request | `reshaped` | Pull requests endpoint reference; same calls under `/pulls/{n}/…` | -| Repository webhook CRUD (`/repos/…/hooks`) | `by-design-absent` | `#coming-from-github`, `#events` | +| Repository webhook CRUD (`/repos/…/hooks`) | `by-design-absent` | Subscriptions are app settings (`#events`) | | App-manifest conversion, OAuth-app token mints | `by-design-absent` | `#installation`, Create App in the endpoint reference | | Git Data API tree/blob writes | `by-design-absent` | `#git-https-authentication`; Git data endpoint reference | | Standalone review-thread objects | `reshaped` | `#current-limitations`; Pull requests endpoint reference | -| User, email, team, and member lookups | `by-design-absent` | `#coming-from-github`, `#resource-references` | +| User, email, team, and member lookups | `by-design-absent` | `#resource-references`; no directory operations in the spec | | Single `pull_request` event with an `action` field, `previous_attributes` | `reshaped` | `#events`, `#event-payloads` | | `x-github-*` headers, HMAC `x-hub-signature-256` | `reshaped` | `#headers`, `#signature-verification` | | Payload inlines (changed files on push, before-SHA, `html_url`, `sender` profile) | `reshaped` | `#current-limitations`, `#resource-references`. Name the follow-up call per field and count the fan-out. | | All events delivered after app creation | `reshaped` | `#events` | -| Reviews keyed by `commit_id` | `reshaped` | `#coming-from-github` | +| Reviews keyed by `commit_id` | `reshaped` | `pullRequestVersion` on the review schema | | Finding own check runs or comments by actor | `reshaped` | `#check-runs` (`key`); comments and reviews by a marker the app controls | | Requested-reviewer team pages, `created_via` | `by-design-absent` | Pull requests endpoint reference (reviewers by identifier) | From db4e4ee49588c59d51852100087ec58549f9a000 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 24 Sep 2026 20:31:02 +0000 Subject: [PATCH 10/26] Retire by-design-absent; relax roadmap absolutes origin-isms rows are reshaped where the docs give a path and not-available where the current spec has nothing; not-available always carries a question and goes through the gap bar. Rows now cite documented limitations instead of asserting intent, and two factual fixes land: reviewer identifiers resolve by email or group slug, and app-level event subscriptions are the documented webhook path. Related sentences in the porting SKILL, gap-bar, brief-template, spec-mapping, README, and origin-api drop "deliberate", "on purpose", and "never a gap". Co-authored-by: ali.nikseresht --- origin-apps/README.md | 6 ++-- origin-apps/skills/origin-api/SKILL.md | 4 +-- .../skills/port-github-app-to-origin/SKILL.md | 17 ++++----- .../references/brief-template.md | 5 +-- .../references/gap-bar.md | 10 +++--- .../references/origin-isms.md | 35 ++++++++++--------- .../references/spec-mapping.md | 4 +-- 7 files changed, 43 insertions(+), 38 deletions(-) diff --git a/origin-apps/README.md b/origin-apps/README.md index 52a45a3f2..6118ada17 100644 --- a/origin-apps/README.md +++ b/origin-apps/README.md @@ -12,7 +12,7 @@ reads [Agent Skills](https://agentskills.io). spec and docs for every fact, gives a table of which docs section answers which question, and names the four rules to check first (native versus mirrored repositories, event subscriptions, webhook verification, scopes -from the spec) plus the GitHub features Origin does not have. Use it for any +from the spec) plus how GitHub features map onto Origin. Use it for any Origin work. `port-github-app-to-origin` builds on `origin-api`. Run it inside your GitHub @@ -34,8 +34,8 @@ Both skills fetch the spec at run time and never name an endpoint from memory. - Holding a GitHub App (Probot, Octokit, go-github, hand-rolled) and wanting to know what an Origin App version looks like before starting: `port-github-app-to-origin`. -- Checking which GitHub features Origin deliberately does not reproduce, and - what to do instead: either skill. +- Checking which GitHub features map differently on Origin, and what to use + instead: either skill. In Cursor, ask about the Origin API or ask to port the app, or run `/origin-api` or `/port-github-app-to-origin`. diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md index 258b7f1ab..4979deabb 100644 --- a/origin-apps/skills/origin-api/SKILL.md +++ b/origin-apps/skills/origin-api/SKILL.md @@ -72,5 +72,5 @@ week. ## Coming from GitHub -GitHub habits do not carry over. The `port-github-app-to-origin` skill in -this plugin covers the differences. +Several GitHub conventions map differently on Origin. The +`port-github-app-to-origin` skill in this plugin covers the differences. diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index 195a09b2b..450753d5a 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -17,19 +17,19 @@ compatibility: >- Run inside the GitHub App's codebase. The output is a porting brief (`references/brief-template.md`), not an implementation. It says what maps, -what changes shape, what is absent on purpose, and what is worth raising with -Cursor. +what changes shape, what is not available today, and what is worth raising +with Cursor. The `origin-api` skill in this plugin covers which docs to fetch, credentials, -scopes, webhooks, paging, IDs, errors, and the deliberate differences from -GitHub. Follow it first. Nothing here repeats it. Two rules on top: +scopes, webhooks, paging, IDs, and errors. Follow it first. Nothing here repeats it. Two rules on top: 1. **Discover, do not ask.** Read permissions, events, handlers, calls, token minting, and the receiver out of the code. Never ask for a manifest or an endpoint list. Anything you cannot find becomes an open question. -2. **Departures are decisions, not omissions.** Anything in - `references/origin-isms.md` is `by-design-absent` or `reshaped`, with a - pointer to the idiom. It never becomes a gap card. +2. **Check the documented path first.** Anything in + `references/origin-isms.md` has a documented Origin path or a documented + limitation. Use the row's label; `not-available` rows still go through + the gap bar. ## Procedure @@ -70,7 +70,8 @@ GitHub. Follow it first. Nothing here repeats it. Two rules on top: - [ ] Every `gap` row has a card, and the card quotes one of the five tradeoff tests in `gap-bar.md`. - [ ] Every `unknown` row has a question in § 7. - - [ ] No `origin-isms.md` row is labeled `gap`. + - [ ] No row `origin-isms.md` labels `reshaped` is labeled `gap`; every + `not-available` row has a question. - [ ] Every event the code handles has a § 4 row for each payload field it reads, including log-only fields. - [ ] Calls the framework makes on the app's behalf appear as rows. diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md index e2c6b384d..71114cfe6 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md +++ b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md @@ -19,7 +19,7 @@ card per gap. The team will argue over it in one sitting. | `same` | Same capability, same shape. A path or field rename at most. | | `reshaped` | Same capability, different shape (pagination, identifier form, event granularity, key semantics). The code changes, the behavior does not. | | `workaround` | Same outcome by a different route (follow-up read, client-side filter, marker). The Tradeoff column is mandatory. | -| `by-design-absent` | Origin deliberately does not reproduce it (`origin-isms.md`). Names the idiom or "no equivalent; decision needed". | +| `not-available` | Nothing in the current spec covers it (`origin-isms.md` or `#current-limitations`). Names the closest idiom and has a question in § 7; eligible for a card. | | `gap` | No workaround, or one that fails `gap-bar.md`. Has a card in § 6. | | `unknown` | Discovery or the spec could not answer. Has a question in § 7. | | `preview` (suffix) | The row touches an element badged `x-cursor-visibility: PREVIEW` (`llms-full.txt#preview`). | @@ -87,7 +87,8 @@ dependency makes on the app's behalf, marked as such. The Origin column names an `operationId`, a slug, a `llms-full.txt` anchor, or `none`. `workaround` rows fill Tradeoff. `gap` rows link their card. -`by-design-absent` rows name the idiom. `unknown` rows name their question. +`not-available` rows name the closest idiom and their question. `unknown` +rows name their question. **Scopes to request:** the union of `x-origin-scopes.scopes` across every Origin operation above that an installation token can call, minus ambient diff --git a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md index 64eb3f170..94e76c9b3 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md +++ b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md @@ -23,9 +23,9 @@ At least one must hold. Quote it on the card. | Customer-visible behavior | The workaround changes what the team's users see or can do, not how the code is organized. | | Load-bearing | The capability sits on the hello-world path or the team's stated core flow. | -## Never a gap +## Not a gap -- Anything in `origin-isms.md`. +- Anything `origin-isms.md` labels `reshaped`. - A field or filter the code does not use. - A GitHub convenience (`Link` pagination, numeric IDs, `html_url`) where the Origin convention is a mechanical substitution. @@ -70,6 +70,6 @@ can act without a call. Do not propose scope, field, or route names. Do not batch unrelated capabilities. Do not send cards yourself. The team decides what goes out, through whatever contact route they have with Cursor, quoting the spec -version and any request ID from failed calls (`llms-full.txt#errors`). "This -is by design, here is the idiom" is a fine answer. It goes back into the -brief as `by-design-absent`. +version and any request ID from failed calls (`llms-full.txt#errors`). "Not +planned" or "here is the idiom" is a fine answer. Record it in the brief with +the label it earns. diff --git a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md index 8a3d7e26f..8e01de091 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md +++ b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md @@ -1,37 +1,40 @@ -# Origin-isms: GitHub features Origin departs from on purpose +# Origin-isms: GitHub features that map differently on Origin -Check here before labeling anything `gap`. A row below is `by-design-absent` -or `reshaped`. The brief points at the idiom and never files a card. The -third column is the `llms-full.txt` anchor where the Origin answer lives. +Check here before labeling anything `gap`. Each row names the label to use +and where the Origin answer lives today (an anchor in `llms-full.txt` unless +noted). `reshaped` rows have a documented path and get no card. +`not-available` rows get a question, and a card if they fail the gap bar. Read the source; do not copy this table into the brief. | GitHub call, event, or permission | Label | Where the Origin answer lives | | --- | --- | --- | -| Writes or `push` events on a repository mirrored from GitHub | `by-design-absent` | `#mirrored-repositories`, `#events`. First question of every brief. | +| Writes or `push` events on a repository mirrored from GitHub | `reshaped` | Read-only until the mirror becomes a stable outbound mirror (`#mirrored-repositories`); pushes are not delivered for GitHub-sourced mirrors (`#events`). Transitioning is a user-credential operation. First question of every brief. | | Install callback query parameters (`installation_id`, `setup_action`) | `reshaped` | `#installation-receipt` | | RS256 app JWT | `reshaped` | `#app-jwt` | | Long-lived installation tokens | `reshaped` | `#installation-access-token` | -| User OAuth, `/user`, `/user/installations`, install-by-user picker | `by-design-absent` | `#scopes` (user-credential operations); repository discovery through the installation. Ask what the flow should do. | +| User OAuth, `/user`, `/user/installations`, install-by-user picker | `not-available` | No user-credential flow for apps in the current spec. Repository discovery is through the installation; namespace-wide listing is under `#current-limitations`. Ask what the flow should do. | | Permissions `: read\|write` | `reshaped` | `#scopes`; `x-origin-scopes` per operation | | Numeric IDs, `/repositories/{id}` | `reshaped` | `#ids`, `#repository-paths` | | `Link` / `page` / `per_page` pagination, total counts | `reshaped` | `#pagination` | -| GraphQL | `by-design-absent` | No GraphQL endpoint in the spec. Decompose into REST calls and count the fan-out. | +| GraphQL | `not-available` | No GraphQL endpoint in the current spec. Decompose into REST calls and count the fan-out; a decomposition that fails the gap bar earns a card about that read. | | Commit statuses (`statuses` permission, `POST /statuses/{sha}`) | `reshaped` | `#check-runs` (check runs with a stable `key`) | -| Issues (`issues` permission, `issues.*` events, `/issues/{n}` not on a pull request) | `by-design-absent` | No Issues in the spec; conversation is pull request comments, threads, reviews, labels. Ask what the pull-request-scoped behavior should be. | +| Issues (`issues` permission, `issues.*` events, `/issues/{n}` not on a pull request) | `not-available` | No Issues endpoints or events in the current spec. Pull request comments, threads, reviews, and labels cover the pull-request half. Ask what the team needs for the rest; an issue-driven app may earn a card. | | `/issues/{n}/comments`, `/issues/{n}/labels` used on a pull request | `reshaped` | Pull requests endpoint reference; same calls under `/pulls/{n}/…` | -| Repository webhook CRUD (`/repos/…/hooks`) | `by-design-absent` | Subscriptions are app settings (`#events`) | -| App-manifest conversion, OAuth-app token mints | `by-design-absent` | `#installation`, Create App in the endpoint reference | -| Git Data API tree/blob writes | `by-design-absent` | `#git-https-authentication`; Git data endpoint reference | -| Standalone review-thread objects | `reshaped` | `#current-limitations`; Pull requests endpoint reference | -| User, email, team, and member lookups | `by-design-absent` | `#resource-references`; no directory operations in the spec | +| Repository webhook CRUD (`/repos/…/hooks`) | `reshaped` | Subscriptions are set per app through Create App / Update App `events` (`#events`). | +| App-manifest conversion | `reshaped` | App creation form or `CreateApp` (`#installation`, endpoint reference) | +| OAuth-app token mints | `not-available` | Nothing in the current spec. Ask what the flow was for. | +| Git Data API commit and ref writes | `reshaped` | Create Commit From Files, Create Git Ref (Git data endpoint reference); `#git-https-authentication` for pushes | +| Git Data API arbitrary blob or tree writes | `not-available` | Not in the current spec. Ask whether commit-from-files or a push covers the use. | +| Standalone review-thread objects | `reshaped` | A thread comes from its first diff-anchored comment (Pull requests endpoint reference); thread listing is under `#current-limitations`. | +| User, email, team, and member lookups | `not-available` | No directory reads in the current spec. Reviewer identifiers resolve by public id, user email, or group slug; `handle` is present when the profile is public (`#resource-references`). | | Single `pull_request` event with an `action` field, `previous_attributes` | `reshaped` | `#events`, `#event-payloads` | | `x-github-*` headers, HMAC `x-hub-signature-256` | `reshaped` | `#headers`, `#signature-verification` | -| Payload inlines (changed files on push, before-SHA, `html_url`, `sender` profile) | `reshaped` | `#current-limitations`, `#resource-references`. Name the follow-up call per field and count the fan-out. | +| Payload inlines (changed files on push, before-SHA, `html_url`, `sender` profile) | `reshaped` | `#resource-references`; the push commit list is under `#current-limitations` and may change. Name the follow-up call per field and count the fan-out. | | All events delivered after app creation | `reshaped` | `#events` | | Reviews keyed by `commit_id` | `reshaped` | `pullRequestVersion` on the review schema | | Finding own check runs or comments by actor | `reshaped` | `#check-runs` (`key`); comments and reviews by a marker the app controls | -| Requested-reviewer team pages, `created_via` | `by-design-absent` | Pull requests endpoint reference (reviewers by identifier) | +| Requested-reviewer team pages, `created_via` | `not-available` | Groups exist and resolve by slug; there is no group membership read in the current spec. | A feature that is not on this list and that the Origin docs never mention (Marketplace billing, merge queues, Actions, Pages, Projects) is `unknown` -with a question, never `by-design-absent`. +with a question. diff --git a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md index fb5826de7..893c3222e 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md +++ b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md @@ -85,8 +85,8 @@ event. - A concept neither the spec nor `llms-full.txt` mentions (Marketplace billing, merge queues, Actions, Pages, Projects, Discussions, HTML probes) is `unknown` with an up-front question. It is never `gap`, because Origin - has not declined it, and never `by-design-absent`, because only - `origin-isms.md` rows earn that. + has not declined it, and never `not-available` on the strength of this + skill alone; only `origin-isms.md` rows earn that. - A behavior the code depends on that the docs do not state (does an event fire for a draft pull request? does `updatedAt` move on a comment?) becomes an open question plus a hello-world step that observes it on a native repository. From 89b461d5a6cd28d350269da509c3aec797f533f2 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 24 Sep 2026 20:50:58 +0000 Subject: [PATCH 11/26] Drop the spec indexer; llms-full.txt already expands nested fields The docs render every webhook payload family and every endpoint's response fields with nested objects expanded to dotted paths, deeper than the script's two-level expansion, so the script and its PyYAML requirement go. spec-mapping points the REST and payload-field steps at the matching llms-full.txt headings. origin-api gains a lookup-order rule (llms.txt first, one section for a narrow question, full files only for broad work); the porting skill inherits it. Co-authored-by: ali.nikseresht --- origin-apps/README.md | 3 - origin-apps/skills/origin-api/SKILL.md | 5 + .../skills/port-github-app-to-origin/SKILL.md | 13 +- .../references/spec-mapping.md | 19 +-- .../scripts/index-origin-spec.py | 125 ------------------ 5 files changed, 21 insertions(+), 144 deletions(-) delete mode 100644 origin-apps/skills/port-github-app-to-origin/scripts/index-origin-spec.py diff --git a/origin-apps/README.md b/origin-apps/README.md index 6118ada17..92d10dc36 100644 --- a/origin-apps/README.md +++ b/origin-apps/README.md @@ -79,9 +79,6 @@ mkdir -p .cursor/skills && cp -r plugins/origin-apps/skills/* .cursor/skills/ - For the porting skill, read access to the app's source. Producing the brief needs no Origin credentials. You follow the brief's hello-world path afterwards. -- Optional: `python3` with PyYAML for the porting skill's - `scripts/index-origin-spec.py`, which prints webhook payload fields with - their references resolved. Without it the skill reads the spec directly. ## Where the brief goes diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md index 4979deabb..f0400dc07 100644 --- a/origin-apps/skills/origin-api/SKILL.md +++ b/origin-apps/skills/origin-api/SKILL.md @@ -26,6 +26,11 @@ which rules to check first. It restates nothing you can read there. - `https://cursor.com/docs/api/origin/llms.txt` (index) and `https://cursor.com/docs/api/origin/changelog` (what moved). +Lookup order: for one question, read `llms.txt` to find the section, then +fetch only that section of `llms-full.txt` or the page it links. Fetch the +whole `llms-full.txt` or `openapi.yaml` only when the task needs broad +coverage, such as a porting brief. + Cite `operationId`s and `llms-full.txt` anchors. If this file and the fetched docs disagree, the docs win. diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index 450753d5a..897ba9195 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -9,8 +9,6 @@ description: >- license: MIT compatibility: >- Needs network access to https://cursor.com/docs/api/origin/* at run time. - The optional payload-field script needs python3 with PyYAML; without it, - read the spec directly. --- # Port a GitHub App to an Origin App @@ -33,11 +31,11 @@ scopes, webhooks, paging, IDs, and errors. Follow it first. Nothing here repeats ## Procedure -1. **Load the spec** (`origin-api`, "Fetch first"). Record `info.version` and - the fetch time for the brief's provenance. Build the mapping index per - `references/spec-mapping.md`: two `rg` commands for operations, scopes, - and slugs, and `scripts/index-origin-spec.py openapi.yaml` (needs PyYAML) - for webhook payload fields. +1. **Load the spec** (`origin-api`, "Fetch first"). A brief needs broad + coverage, so fetch the full `openapi.yaml` and `llms-full.txt`; the + narrow lookup order in `origin-api` is for later single questions. Record + `info.version` and the fetch time for the brief's provenance. Build the + mapping index per `references/spec-mapping.md`. 2. **Discover** per `references/discovery.md`. Record a file and line for every fact, including payload fields read only for logging and calls the framework makes on the app's behalf. Note what you looked for and did not @@ -95,4 +93,3 @@ cards to Cursor. The brief carries them and the team decides. | `references/origin-isms.md` | Labeling a missing GitHub feature. | | `references/gap-bar.md` | Deciding whether a difference earns a card, and writing it. | | `references/brief-template.md` | Writing the output. | -| `scripts/index-origin-spec.py` | Listing webhook payload families with their fields resolved. Optional. | diff --git a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md index 893c3222e..16c22e674 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md +++ b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md @@ -21,13 +21,15 @@ table here. Build the index once. Every later step looks things up in it. you which GitHub flows have no app-side equivalent. Read parameters and response components from the spec when a rule below asks for them. 2. **Webhook events**: `rg -A3 'x-origin-webhook-events:' openapi.yaml` lists - every slug with its payload schema. `scripts/index-origin-spec.py - openapi.yaml` prints each payload family with its fields and `$ref`s - resolved two levels deep; `schema ` does the same for one component. - From `llms-full.txt#events`, note which slugs are delivered without a - subscription and which must be selected. -3. **Resources**: component schemas returned by `Get…`/`List…`, with field - names, for "does the Origin object carry this field". + every slug with its payload schema. Each family's fields, with nested + objects expanded to dotted paths, are under its heading in + `llms-full.txt` (`rg -n '^### Pull Request Events$' llms-full.txt`, then + read to the next `###`). From `#events`, note which slugs are delivered + without a subscription and which must be selected. +3. **Resources**: each endpoint's "Response Fields" in `llms-full.txt` + (`rg -n '^### Get Pull Request$' llms-full.txt`) lists the fields the + object carries, nested objects expanded, for "does the Origin object carry + this field". ## Matching @@ -73,7 +75,8 @@ no slug is not an event on Origin. Check whether the state change is observable another way before classifying it. **Payload fields → schema properties.** For each field path a handler reads, -walk the mapped slug's payload schema and record one of four outcomes. +walk the mapped family's "Payload Fields" list in `llms-full.txt` and record +one of four outcomes. Present at ``. Follow-up read via `` with identifiers the payload carries. Derivable from present fields, saying how and whether the format is contractual. Absent, which goes to the gap bar. A field on the REST diff --git a/origin-apps/skills/port-github-app-to-origin/scripts/index-origin-spec.py b/origin-apps/skills/port-github-app-to-origin/scripts/index-origin-spec.py deleted file mode 100644 index b50ab04e9..000000000 --- a/origin-apps/skills/port-github-app-to-origin/scripts/index-origin-spec.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env python3 -"""Print the Origin webhook payload families with their fields resolved. - -Usage: - python3 index-origin-spec.py openapi.yaml # every payload family - python3 index-origin-spec.py openapi.yaml schema PullRequest # one component - -Fetch the spec first: - curl -sSL https://cursor.com/docs/api/origin/openapi.yaml -o openapi.yaml - -Operations and scopes do not need this script. Grep the spec directly: - rg -B1 -A4 'x-origin-scopes:' openapi.yaml - rg -A3 'x-origin-webhook-events:' openapi.yaml - -Read-only. Needs PyYAML (`pip install pyyaml`). Everything printed comes from -the spec you pass in; nothing is pinned or embedded here. -""" - -import sys - -try: - import yaml -except ImportError: # pragma: no cover - sys.stderr.write( - "PyYAML is not installed. Run `pip install pyyaml`, or read the spec " - "directly (search for `x-origin-webhook-events:` and follow the " - "`$ref`s by hand).\n" - ) - sys.exit(2) - -# Field descriptions are cut to one sentence and this many characters so a -# family fits on one screen. -DESCRIPTION_CHARS = 140 -# How many `$ref` levels to expand under a payload. Two reaches the embedded -# resource and its direct children, which is what field mapping needs. -SCHEMA_DEPTH = 2 - - -def ref_name(node): - if not isinstance(node, dict): - return None - if "$ref" in node: - return node["$ref"].rsplit("/", 1)[-1] - if "allOf" in node and node["allOf"] and "$ref" in node["allOf"][0]: - return node["allOf"][0]["$ref"].rsplit("/", 1)[-1] - if node.get("type") == "array": - inner = ref_name(node.get("items", {})) - return f"{inner}[]" if inner else "array" - return None - - -def first_sentence(text): - return " ".join((text or "").split()).split(". ")[0][:DESCRIPTION_CHARS] - - -def print_schema(components, name, depth=0, seen=None): - seen = seen or set() - schema = components.get(name) - if not schema: - print(f"{' ' * depth}(no component named {name})") - return - for field, node in (schema.get("properties") or {}).items(): - kind = ref_name(node) or node.get("type", "?") - desc = first_sentence(node.get("description")) - print(f"{' ' * depth}{field}: {kind}" + (f" -- {desc}" if desc else "")) - inner = (ref_name(node) or "").rstrip("[]") - if inner and inner in components and depth < SCHEMA_DEPTH and inner not in seen: - seen.add(inner) - print_schema(components, inner, depth + 1, seen) - - -def print_events(components): - print("== WEBHOOK PAYLOAD FAMILIES (schema | slugs | x-origin-webhook-resource)") - for name, schema in components.items(): - slugs = schema.get("x-origin-webhook-events") - if not slugs: - continue - print(f"{name} | {slugs} | resource={schema.get('x-origin-webhook-resource')}") - print_schema(components, name, depth=1) - - -def load_spec(path): - try: - with open(path, encoding="utf-8") as fh: - spec = yaml.safe_load(fh) - except FileNotFoundError: - sys.stderr.write(f"{path}: not found. Fetch it first (command in --help).\n") - return None - except yaml.YAMLError as err: - sys.stderr.write(f"{path}: not valid YAML ({err}).\n") - return None - if not isinstance(spec, dict) or "components" not in spec or "info" not in spec: - sys.stderr.write(f"{path}: not an OpenAPI document (no `info` or `components`).\n") - return None - components = (spec.get("components") or {}).get("schemas") or {} - if not any("x-origin-webhook-events" in s for s in components.values() if isinstance(s, dict)): - sys.stderr.write( - f"{path}: no schema carries `x-origin-webhook-events`; is this the Origin spec?\n" - ) - return None - return spec - - -def main(argv): - if len(argv) < 2 or argv[1] in ("-h", "--help"): - print(__doc__) - return 1 - spec = load_spec(argv[1]) - if spec is None: - return 1 - components = spec["components"]["schemas"] - schema_mode = len(argv) > 2 and argv[2] == "schema" - if schema_mode and len(argv) < 4: - sys.stderr.write("schema mode needs a component name.\n") - return 1 - print(f"# {spec['info'].get('title')} {spec['info'].get('version')}") - if schema_mode: - print_schema(components, argv[3]) - else: - print_events(components) - return 0 - - -if __name__ == "__main__": - sys.exit(main(sys.argv)) From ecb830457e5324dce680e81f71c9d8c206820d12 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 24 Sep 2026 21:09:49 +0000 Subject: [PATCH 12/26] Frame gap cards and questions as feedback Cursor wants gap-bar opens with the ask to raise anything that blocks, costs, or would help the team; the bar orders cards versus questions rather than deciding whether to speak up. Drops the product-area examples for undocumented concepts, softens "never about search" and "fails the bar", and closes by encouraging the team to send cards and questions. The same tone sweep touches the porting SKILL, spec-mapping, origin-isms, brief-template, and README. Co-authored-by: ali.nikseresht --- origin-apps/README.md | 4 +- .../skills/port-github-app-to-origin/SKILL.md | 5 +- .../references/brief-template.md | 7 ++- .../references/gap-bar.md | 55 +++++++++++-------- .../references/origin-isms.md | 10 ++-- .../references/spec-mapping.md | 9 ++- 6 files changed, 49 insertions(+), 41 deletions(-) diff --git a/origin-apps/README.md b/origin-apps/README.md index 92d10dc36..32866b1b0 100644 --- a/origin-apps/README.md +++ b/origin-apps/README.md @@ -83,8 +83,8 @@ mkdir -p .cursor/skills && cp -r plugins/origin-apps/skills/* .cursor/skills/ ## Where the brief goes The porting skill writes `ORIGIN-PORTING-BRIEF.md` at the repository root and -prints its path. The gap cards in the brief are yours to send through -whatever contact route you have with Cursor. +prints its path. The gap cards and questions in the brief are yours to send +through whatever contact route you have with Cursor; Cursor wants them. ## License diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index 897ba9195..bce442db4 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -47,9 +47,8 @@ scopes, webhooks, paging, IDs, and errors. Follow it first. Nothing here repeats notes under `#event-payloads`. Name the call for each such field. Then: - Check `origin-isms.md` before writing `gap`. Check `gap-bar.md` before writing any card. - - A GitHub feature the Origin docs never mention (Marketplace billing, - merge queues, Actions, Pages) is `unknown` with a question. It is never - `gap`. + - A GitHub feature the Origin docs do not mention is `unknown` with a + question. The question is how the team tells Cursor they need it. - A behavior the code depends on that the docs neither confirm nor deny (does event X fire in case Y? does `updatedAt` move on comments?) is a question plus a hello-world step that observes it. Never guess it into diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md index 71114cfe6..d50e44081 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md +++ b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md @@ -8,7 +8,7 @@ unless the team's docs convention says otherwise) and print its path. Fill every section. An empty section says so in one line rather than disappearing. Cite spec `operationId`s and `llms-full.txt` anchors. Cite the team's code by `file:line`. One table row per capability, one line per follow-up field, one -card per gap. The team will argue over it in one sitting. +card per gap. The team should be able to review it in one sitting. ## Labels @@ -129,8 +129,9 @@ each spec-silent behavior the brief depends on. ## 6. Gaps worth raising -Zero or more cards in the `gap-bar.md` shape. If zero, write "No row failed -the gap bar. The workarounds in § 3 carry their tradeoffs." Do not pad. +Zero or more cards in the `gap-bar.md` shape. If zero, write "No row met +the gap bar. The workarounds in § 3 carry their tradeoffs, and § 7 carries +the asks." Cursor reads both sections. ## 7. Questions for the team diff --git a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md index 94e76c9b3..c6828ba23 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md +++ b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md @@ -1,52 +1,59 @@ # The gap bar and the escalation card -Most differences are not gaps. A brief that files every difference buries the -two or three that are worth raising. When in doubt, write the row as -`workaround` with the tradeoff and an open question, not as a card. +Cursor wants to hear what the team needs from Origin. Raise anything that +blocks the team's core flow, costs them correctness, security, or scale, or +that they would simply like Origin to do. The bar below decides whether an +item is a card or a question, not whether to speak up. Its other job is +ordering: put the asks that block the port ahead of the ones that are +conveniences, so the important ones are read first. When a difference has a +workaround, writing the row as `workaround` with its tradeoff and a question +is often the right answer; a card adds the tradeoff analysis Cursor needs to +prioritize it. - A difference is any row whose parity label is not `same`. - A workaround reaches the same outcome with the current API by another route. A follow-up read, a re-keyed identifier, a path change, a client-side filter, a marker the app controls. - A gap is a difference with no workaround, or a workaround whose tradeoff - fails one of the five tests below. Only gaps become cards. + meets one of the five tests below. Gaps become cards; everything else the + team wants to raise becomes a question in § 7. ## Nontrivial tradeoff -At least one must hold. Quote it on the card. +At least one should hold for a card. Quote it on the card. | Tradeoff | Test | | --- | --- | -| Fan-out at scale | Calls per event multiply by a factor that grows with repository or activity size (N commits × M files, or a full list scan to find one row), and the app's volume makes that budget-relevant. One bounded extra read per event is trivial. | +| Fan-out at scale | Calls per event multiply by a factor that grows with repository or activity size (N commits × M files, or a full list scan to find one row), and the app's volume makes that budget-relevant. One bounded extra read per event is a workaround. | | Correctness risk | The workaround can return a wrong answer, not only a slower one. Heuristic "my own row" matching. Inferring a pull request from a SHA several versions share. Assembling a URL whose format is not contractual. | | Security posture | The workaround needs a broader scope, a longer-lived token, or a user credential where an installation token should do. | | Customer-visible behavior | The workaround changes what the team's users see or can do, not how the code is organized. | | Load-bearing | The capability sits on the hello-world path or the team's stated core flow. | -## Not a gap +## Usually a workaround or a question, not a card -- Anything `origin-isms.md` labels `reshaped`. +- Anything `origin-isms.md` labels `reshaped`: a documented path exists. - A field or filter the code does not use. - A GitHub convenience (`Link` pagination, numeric IDs, `html_url`) where the Origin convention is a mechanical substitution. - Anything the changelog says shipped or the spec already carries. Re-read the live spec before writing any card. -- A concept the Origin docs never mention (Marketplace billing, merge queues, - Actions, Projects). That is `unknown` plus a question. +- A concept the Origin docs do not mention. That is `not-available` or + `unknown` with a question; the team should still ask if they need it. - A GitHub Search query, when the spec has no search operation for that resource. The idiom is a list operation with its filters plus a client-side - predicate. A sorted list read that stops at a cutoff costs - proportional to the matches, not the collection. If that count fails the - bar, the card is about a filter, never about search. + predicate. A sorted list read that stops at a cutoff costs proportional to + the matches, not the collection. If that count meets the bar, the card is + usually about a filter rather than search. -## One pattern that does clear the bar +## One pattern that does meet the bar A state change the app reacts to that has no event, when reacting to exactly that change is the app's purpose and the state is invisible until an unrelated event arrives. Reading it off the next snapshot fails on correctness and customer-visible behavior when the app is a gate (a check, a block, a -notification). Write the card about the event. Do not write it when the app -only logs or tidies up on that change. +notification). Write the card about the event. When the app only logs or +tidies up on that change, a question is enough. ## The card @@ -61,15 +68,17 @@ can act without a call. - **Why:** . - **Closest Origin operation:** `` / `` / none, and what it lacks. - **Workaround considered:** . -- **Tradeoff that fails the bar:** . +- **Tradeoff that meets the bar:** . - **Shape that would close it:** - **Blocking?** yes / no, for which flow. - **Spec version checked:** `` on ``. ``` -Do not propose scope, field, or route names. Do not batch unrelated -capabilities. Do not send cards yourself. The team decides what goes out, -through whatever contact route they have with Cursor, quoting the spec -version and any request ID from failed calls (`llms-full.txt#errors`). "Not -planned" or "here is the idiom" is a fine answer. Record it in the brief with -the label it earns. +Describe the capability rather than proposing scope, field, or route names; +that leaves Cursor free to fit it to the API's conventions. One capability +per card. Do not send cards yourself: the team decides what goes out, and +they are encouraged to send both the cards and the § 7 questions to Cursor +through whatever contact route they have, quoting the spec version and any +request ID from failed calls (`llms-full.txt#errors`). A reply of "here is +the idiom" or "not planned" is useful too; record it in the brief with the +label it earns. diff --git a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md index 8e01de091..576d2418e 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md +++ b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md @@ -2,8 +2,9 @@ Check here before labeling anything `gap`. Each row names the label to use and where the Origin answer lives today (an anchor in `llms-full.txt` unless -noted). `reshaped` rows have a documented path and get no card. -`not-available` rows get a question, and a card if they fail the gap bar. +noted). `reshaped` rows have a documented path, so they get a question if +the team wants the GitHub shape back rather than a card. `not-available` +rows get a question, and a card if they meet the gap bar. Read the source; do not copy this table into the brief. | GitHub call, event, or permission | Label | Where the Origin answer lives | @@ -35,6 +36,5 @@ Read the source; do not copy this table into the brief. | Finding own check runs or comments by actor | `reshaped` | `#check-runs` (`key`); comments and reviews by a marker the app controls | | Requested-reviewer team pages, `created_via` | `not-available` | Groups exist and resolve by slug; there is no group membership read in the current spec. | -A feature that is not on this list and that the Origin docs never mention -(Marketplace billing, merge queues, Actions, Pages, Projects) is `unknown` -with a question. +A feature that is not on this list and that the Origin docs do not mention +is `unknown` with a question. diff --git a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md index 16c22e674..fccbe6656 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md +++ b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md @@ -85,11 +85,10 @@ event. ## Out of domain and spec-silent -- A concept neither the spec nor `llms-full.txt` mentions (Marketplace - billing, merge queues, Actions, Pages, Projects, Discussions, HTML probes) - is `unknown` with an up-front question. It is never `gap`, because Origin - has not declined it, and never `not-available` on the strength of this - skill alone; only `origin-isms.md` rows earn that. +- A concept neither the spec nor `llms-full.txt` mentions is `unknown` + with an up-front question rather than `gap`: there is no Origin answer yet + to compare against, and the question is how the team tells Cursor they + need it. Use `not-available` only for `origin-isms.md` rows. - A behavior the code depends on that the docs do not state (does an event fire for a draft pull request? does `updatedAt` move on a comment?) becomes an open question plus a hello-world step that observes it on a native repository. From 0a71c0cf9500dc3fbe2e49ee812a0c0346ba457f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 24 Sep 2026 21:13:07 +0000 Subject: [PATCH 13/26] Address review: drop porting/integration keywords, add envelope outcome Manifests lose the porting and integration keywords and tags. spec-mapping's payload outcomes now include "present in the envelope" so GitHub's action field maps to event.type instead of falling through to the gap bar, matching the brief template's five How values. The porting skill's out-of-scope line says the team sends the cards. Co-authored-by: ali.nikseresht --- origin-apps/.claude-plugin/plugin.json | 4 +--- origin-apps/.cursor-plugin/plugin.json | 7 ++----- origin-apps/plugin.json | 4 +--- origin-apps/skills/port-github-app-to-origin/SKILL.md | 2 +- .../port-github-app-to-origin/references/spec-mapping.md | 9 +++++---- 5 files changed, 10 insertions(+), 16 deletions(-) diff --git a/origin-apps/.claude-plugin/plugin.json b/origin-apps/.claude-plugin/plugin.json index dde7dcf6d..ac67f840d 100644 --- a/origin-apps/.claude-plugin/plugin.json +++ b/origin-apps/.claude-plugin/plugin.json @@ -14,9 +14,7 @@ "origin-api", "origin-app", "webhooks", - "github-app", - "integration", - "porting" + "github-app" ], "skills": "./skills/" } diff --git a/origin-apps/.cursor-plugin/plugin.json b/origin-apps/.cursor-plugin/plugin.json index cefa3aa43..bace67665 100644 --- a/origin-apps/.cursor-plugin/plugin.json +++ b/origin-apps/.cursor-plugin/plugin.json @@ -16,17 +16,14 @@ "origin-api", "origin-app", "webhooks", - "github-app", - "integration", - "porting" + "github-app" ], "category": "developer-tools", "tags": [ "origin", "origin-api", "webhooks", - "github-app", - "integration" + "github-app" ], "skills": "./skills/" } diff --git a/origin-apps/plugin.json b/origin-apps/plugin.json index 75ab6a385..5f0961dbe 100644 --- a/origin-apps/plugin.json +++ b/origin-apps/plugin.json @@ -15,8 +15,6 @@ "origin-api", "origin-app", "webhooks", - "github-app", - "integration", - "porting" + "github-app" ] } diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index bce442db4..68f2ec558 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -80,7 +80,7 @@ scopes, webhooks, paging, IDs, and errors. Follow it first. Nothing here repeats Writing port code or adapters. Choosing a language, framework, or client. Estimating in time. Asking for anything the codebase contains. Sending gap -cards to Cursor. The brief carries them and the team decides. +cards to Cursor yourself; the brief carries them and the team sends them. ## Reference files diff --git a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md index fccbe6656..016f16aeb 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md +++ b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md @@ -76,10 +76,11 @@ observable another way before classifying it. **Payload fields → schema properties.** For each field path a handler reads, walk the mapped family's "Payload Fields" list in `llms-full.txt` and record -one of four outcomes. -Present at ``. Follow-up read via `` with identifiers the -payload carries. Derivable from present fields, saying how and whether the -format is contractual. Absent, which goes to the gap bar. A field on the REST +one of five outcomes, matching the brief template's "How" column. +Present at ``. Present in the envelope (`event.type` carries what +GitHub puts in `action`). Follow-up read via `` with identifiers +the payload carries. Derivable from present fields, saying how and whether +the format is contractual. Absent, which goes to the gap bar. A field on the REST component that the webhook twin lacks means a follow-up `Get…` on every event. From 6490acce4d7f28896f2cc1e747b862dc8d538229 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 24 Sep 2026 21:15:18 +0000 Subject: [PATCH 14/26] Rename gap cards to feedback for Cursor The brief's section is Feedback for Cursor with Feedback: entries; the reference is the feedback bar and format. The gap parity label stays: a gap row produces a feedback entry. Section 7 is scoped to decisions the team must make so it does not duplicate section 6. Co-authored-by: ali.nikseresht --- origin-apps/README.md | 6 +-- .../skills/port-github-app-to-origin/SKILL.md | 8 ++-- .../references/brief-template.md | 21 +++++----- .../references/gap-bar.md | 39 ++++++++++--------- .../references/origin-isms.md | 9 +++-- 5 files changed, 44 insertions(+), 39 deletions(-) diff --git a/origin-apps/README.md b/origin-apps/README.md index 32866b1b0..22061a261 100644 --- a/origin-apps/README.md +++ b/origin-apps/README.md @@ -19,7 +19,7 @@ Origin work. App's repository. It reads what the app uses from GitHub out of the code, maps that onto the live spec, and writes a porting brief with a capability table, the webhook fields your handlers read and where each comes from on Origin, -the scopes to request, a hello-world path, the gaps worth raising with Cursor, +the scopes to request, a hello-world path, feedback for Cursor, and the questions to settle first. It plans. It writes no code and estimates no time. @@ -83,8 +83,8 @@ mkdir -p .cursor/skills && cp -r plugins/origin-apps/skills/* .cursor/skills/ ## Where the brief goes The porting skill writes `ORIGIN-PORTING-BRIEF.md` at the repository root and -prints its path. The gap cards and questions in the brief are yours to send -through whatever contact route you have with Cursor; Cursor wants them. +prints its path. The feedback entries and questions in the brief are yours to +send through whatever contact route you have with Cursor; Cursor wants them. ## License diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index 68f2ec558..5a1a44c9f 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -46,7 +46,7 @@ scopes, webhooks, paging, IDs, and errors. Follow it first. Nothing here repeats lacks a field the REST resource has, read the resource; see the per-field notes under `#event-payloads`. Name the call for each such field. Then: - Check `origin-isms.md` before writing `gap`. Check `gap-bar.md` before - writing any card. + writing any feedback entry. - A GitHub feature the Origin docs do not mention is `unknown` with a question. The question is how the team tells Cursor they need it. - A behavior the code depends on that the docs neither confirm nor deny @@ -64,7 +64,7 @@ scopes, webhooks, paging, IDs, and errors. Follow it first. Nothing here repeats - [ ] Every Origin cell names an `operationId`, slug, or anchor that exists in the files fetched in step 1. - - [ ] Every `gap` row has a card, and the card quotes one of the five + - [ ] Every `gap` row has a feedback entry, and the entry quotes one of the five tradeoff tests in `gap-bar.md`. - [ ] Every `unknown` row has a question in § 7. - [ ] No row `origin-isms.md` labels `reshaped` is labeled `gap`; every @@ -80,7 +80,7 @@ scopes, webhooks, paging, IDs, and errors. Follow it first. Nothing here repeats Writing port code or adapters. Choosing a language, framework, or client. Estimating in time. Asking for anything the codebase contains. Sending gap -cards to Cursor yourself; the brief carries them and the team sends them. +feedback to Cursor yourself; the brief carries it and the team sends it. ## Reference files @@ -90,5 +90,5 @@ cards to Cursor yourself; the brief carries them and the team sends them. | `references/discovery.md` | Scanning the codebase. | | `references/spec-mapping.md` | Building the index. Matching calls, events, and fields. | | `references/origin-isms.md` | Labeling a missing GitHub feature. | -| `references/gap-bar.md` | Deciding whether a difference earns a card, and writing it. | +| `references/gap-bar.md` | Deciding whether a difference is feedback for Cursor, and writing the entry. | | `references/brief-template.md` | Writing the output. | diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md index d50e44081..a5ac56f50 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md +++ b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md @@ -8,7 +8,7 @@ unless the team's docs convention says otherwise) and print its path. Fill every section. An empty section says so in one line rather than disappearing. Cite spec `operationId`s and `llms-full.txt` anchors. Cite the team's code by `file:line`. One table row per capability, one line per follow-up field, one -card per gap. The team should be able to review it in one sitting. +feedback entry per gap. The team should be able to review it in one sitting. ## Labels @@ -19,8 +19,8 @@ card per gap. The team should be able to review it in one sitting. | `same` | Same capability, same shape. A path or field rename at most. | | `reshaped` | Same capability, different shape (pagination, identifier form, event granularity, key semantics). The code changes, the behavior does not. | | `workaround` | Same outcome by a different route (follow-up read, client-side filter, marker). The Tradeoff column is mandatory. | -| `not-available` | Nothing in the current spec covers it (`origin-isms.md` or `#current-limitations`). Names the closest idiom and has a question in § 7; eligible for a card. | -| `gap` | No workaround, or one that fails `gap-bar.md`. Has a card in § 6. | +| `not-available` | Nothing in the current spec covers it (`origin-isms.md` or `#current-limitations`). Names the closest idiom and has a question in § 7; eligible for a feedback entry. | +| `gap` | No workaround, or one whose tradeoff meets the bar in `gap-bar.md`. Has a feedback entry in § 6. | | `unknown` | Discovery or the spec could not answer. Has a question in § 7. | | `preview` (suffix) | The row touches an element badged `x-cursor-visibility: PREVIEW` (`llms-full.txt#preview`). | @@ -30,7 +30,7 @@ card per gap. The team should be able to review it in one sitting. | --- | --- | | S | Adapter or client layer. A path, header, identifier, or pagination rewrite, or a re-keyed lookup. | | M | A new code path. A follow-up read where the payload used to suffice, a handshake step, a new handler, a data-model change for a new identifier or version concept. | -| L | A product or architecture change. A flow that depended on user OAuth, a customer-visible behavior, a dependency on native repositories, an open gap card. | +| L | A product or architecture change. A flow that depended on user OAuth, a customer-visible behavior, a dependency on native repositories, an open feedback entry. | ## Template @@ -86,7 +86,7 @@ dependency makes on the app's behalf, marked as such. | `GET /repos/{o}/{r}/pulls/{n}` (`src/x.ts:12`) | `` | same | S | none | none | The Origin column names an `operationId`, a slug, a `llms-full.txt` anchor, -or `none`. `workaround` rows fill Tradeoff. `gap` rows link their card. +or `none`. `workaround` rows fill Tradeoff. `gap` rows link their feedback entry. `not-available` rows name the closest idiom and their question. `unknown` rows name their question. @@ -127,15 +127,16 @@ each spec-silent behavior the brief depends on. first. 7. Smallest write from § 3 succeeds with the scopes from the § 3 line. -## 6. Gaps worth raising +## 6. Feedback for Cursor -Zero or more cards in the `gap-bar.md` shape. If zero, write "No row met -the gap bar. The workarounds in § 3 carry their tradeoffs, and § 7 carries -the asks." Cursor reads both sections. +Capabilities Origin should add, one entry per `gap` row in the `gap-bar.md` +format. If none, write "No row met the feedback bar; the workarounds in § 3 +carry their tradeoffs." ## 7. Questions for the team -Always the first three, then what discovery left open. +Decisions the team must make before the port, not asks of Cursor. Always the +first three, then what discovery left open. 1. Native repositories (or stable outbound mirrors), or repositories mirrored from GitHub? Decides whether the app receives events and can write. diff --git a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md index c6828ba23..0b56fc540 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md +++ b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md @@ -1,26 +1,28 @@ -# The gap bar and the escalation card +# The feedback bar and the feedback format Cursor wants to hear what the team needs from Origin. Raise anything that blocks the team's core flow, costs them correctness, security, or scale, or that they would simply like Origin to do. The bar below decides whether an -item is a card or a question, not whether to speak up. Its other job is +item is feedback for Cursor or a question for the team, not whether to speak +up. Its other job is ordering: put the asks that block the port ahead of the ones that are conveniences, so the important ones are read first. When a difference has a workaround, writing the row as `workaround` with its tradeoff and a question -is often the right answer; a card adds the tradeoff analysis Cursor needs to -prioritize it. +is often the right answer; a feedback entry adds the tradeoff analysis Cursor +needs to prioritize it. - A difference is any row whose parity label is not `same`. - A workaround reaches the same outcome with the current API by another route. A follow-up read, a re-keyed identifier, a path change, a client-side filter, a marker the app controls. - A gap is a difference with no workaround, or a workaround whose tradeoff - meets one of the five tests below. Gaps become cards; everything else the - team wants to raise becomes a question in § 7. + meets one of the five tests below. Gaps become feedback entries in § 6: + capabilities Origin should add. Things the team must decide go to § 7 as + questions; the two sections do not repeat each other. ## Nontrivial tradeoff -At least one should hold for a card. Quote it on the card. +At least one should hold for a feedback entry. Quote it in the entry. | Tradeoff | Test | | --- | --- | @@ -30,21 +32,21 @@ At least one should hold for a card. Quote it on the card. | Customer-visible behavior | The workaround changes what the team's users see or can do, not how the code is organized. | | Load-bearing | The capability sits on the hello-world path or the team's stated core flow. | -## Usually a workaround or a question, not a card +## Usually a workaround or a question, not feedback - Anything `origin-isms.md` labels `reshaped`: a documented path exists. - A field or filter the code does not use. - A GitHub convenience (`Link` pagination, numeric IDs, `html_url`) where the Origin convention is a mechanical substitution. - Anything the changelog says shipped or the spec already carries. Re-read the - live spec before writing any card. + live spec before writing any feedback entry. - A concept the Origin docs do not mention. That is `not-available` or `unknown` with a question; the team should still ask if they need it. - A GitHub Search query, when the spec has no search operation for that resource. The idiom is a list operation with its filters plus a client-side predicate. A sorted list read that stops at a cutoff costs proportional to - the matches, not the collection. If that count meets the bar, the card is - usually about a filter rather than search. + the matches, not the collection. If that count meets the bar, the feedback + is usually about a filter rather than search. ## One pattern that does meet the bar @@ -52,16 +54,16 @@ A state change the app reacts to that has no event, when reacting to exactly that change is the app's purpose and the state is invisible until an unrelated event arrives. Reading it off the next snapshot fails on correctness and customer-visible behavior when the app is a gate (a check, a block, a -notification). Write the card about the event. When the app only logs or +notification). Write the feedback entry about the event. When the app only logs or tidies up on that change, a question is enough. -## The card +## The feedback format -One per gap, in the brief's "Gaps worth raising" section. Write it so Cursor -can act without a call. +One entry per gap, in the brief's "Feedback for Cursor" section. Write it +so Cursor can act without a call. ```markdown -### Gap: +### Feedback: - **GitHub call, event, or permission:** `` / `` / ``, at ``. - **What the app needs from it:** . @@ -76,8 +78,9 @@ can act without a call. Describe the capability rather than proposing scope, field, or route names; that leaves Cursor free to fit it to the API's conventions. One capability -per card. Do not send cards yourself: the team decides what goes out, and -they are encouraged to send both the cards and the § 7 questions to Cursor +per entry. Do not send feedback yourself: the team decides what goes out, +and they are encouraged to send the feedback entries, and any § 7 questions +they want Cursor's view on, to Cursor through whatever contact route they have, quoting the spec version and any request ID from failed calls (`llms-full.txt#errors`). A reply of "here is the idiom" or "not planned" is useful too; record it in the brief with the diff --git a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md index 576d2418e..31329c296 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md +++ b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md @@ -3,8 +3,9 @@ Check here before labeling anything `gap`. Each row names the label to use and where the Origin answer lives today (an anchor in `llms-full.txt` unless noted). `reshaped` rows have a documented path, so they get a question if -the team wants the GitHub shape back rather than a card. `not-available` -rows get a question, and a card if they meet the gap bar. +the team wants the GitHub shape back rather than a feedback entry. +`not-available` rows get a question, and a feedback entry if they meet the +bar. Read the source; do not copy this table into the brief. | GitHub call, event, or permission | Label | Where the Origin answer lives | @@ -17,9 +18,9 @@ Read the source; do not copy this table into the brief. | Permissions `: read\|write` | `reshaped` | `#scopes`; `x-origin-scopes` per operation | | Numeric IDs, `/repositories/{id}` | `reshaped` | `#ids`, `#repository-paths` | | `Link` / `page` / `per_page` pagination, total counts | `reshaped` | `#pagination` | -| GraphQL | `not-available` | No GraphQL endpoint in the current spec. Decompose into REST calls and count the fan-out; a decomposition that fails the gap bar earns a card about that read. | +| GraphQL | `not-available` | No GraphQL endpoint in the current spec. Decompose into REST calls and count the fan-out; a decomposition that meets the feedback bar earns an entry about that read. | | Commit statuses (`statuses` permission, `POST /statuses/{sha}`) | `reshaped` | `#check-runs` (check runs with a stable `key`) | -| Issues (`issues` permission, `issues.*` events, `/issues/{n}` not on a pull request) | `not-available` | No Issues endpoints or events in the current spec. Pull request comments, threads, reviews, and labels cover the pull-request half. Ask what the team needs for the rest; an issue-driven app may earn a card. | +| Issues (`issues` permission, `issues.*` events, `/issues/{n}` not on a pull request) | `not-available` | No Issues endpoints or events in the current spec. Pull request comments, threads, reviews, and labels cover the pull-request half. Ask what the team needs for the rest; an issue-driven app may earn a feedback entry. | | `/issues/{n}/comments`, `/issues/{n}/labels` used on a pull request | `reshaped` | Pull requests endpoint reference; same calls under `/pulls/{n}/…` | | Repository webhook CRUD (`/repos/…/hooks`) | `reshaped` | Subscriptions are set per app through Create App / Update App `events` (`#events`). | | App-manifest conversion | `reshaped` | App creation form or `CreateApp` (`#installation`, endpoint reference) | From 32bbb4e20c3ae1eb1042adfdbc390eaa2e696e57 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 24 Sep 2026 23:48:00 +0000 Subject: [PATCH 15/26] Final pass: partner audience, use-case feedback, guidance not procedure Feedback for Cursor now carries only the use case and the API gap in Origin terms, with no file paths, module names, framework internals, or repository names; the team strips internals before forwarding. The porting skill states it does not write or change code without an explicit ask, its procedure is guidance with a short self-check, and the brief is a default template whose Feedback section is the part to keep exact. origin-api is a router: docs pointers and five rules, triggered by Origin mentions only. Capability tables and payload maps use neutral "today" framing instead of GitHub-versus-Origin. Co-authored-by: ali.nikseresht --- origin-apps/README.md | 34 +++-- origin-apps/skills/origin-api/SKILL.md | 70 +++++------ .../skills/port-github-app-to-origin/SKILL.md | 119 ++++++++---------- .../references/brief-template.md | 65 +++++----- .../references/gap-bar.md | 103 +++++++-------- .../references/spec-mapping.md | 6 +- 6 files changed, 180 insertions(+), 217 deletions(-) diff --git a/origin-apps/README.md b/origin-apps/README.md index 22061a261..8794cd716 100644 --- a/origin-apps/README.md +++ b/origin-apps/README.md @@ -8,20 +8,18 @@ reads [Agent Skills](https://agentskills.io). ## What it includes -`origin-api` is the general skill. It sends the agent to the live OpenAPI -spec and docs for every fact, gives a table of which docs section answers -which question, and names the four rules to check first (native versus -mirrored repositories, event subscriptions, webhook verification, scopes -from the spec) plus how GitHub features map onto Origin. Use it for any -Origin work. - -`port-github-app-to-origin` builds on `origin-api`. Run it inside your GitHub -App's repository. It reads what the app uses from GitHub out of the code, maps -that onto the live spec, and writes a porting brief with a capability table, -the webhook fields your handlers read and where each comes from on Origin, -the scopes to request, a hello-world path, feedback for Cursor, -and the questions to settle first. It plans. It writes no code and estimates -no time. +`origin-api` routes questions to the section of the Origin docs that answers +them and names the rules to check first (native versus mirrored repositories, +event subscriptions, webhook verification, scopes from the spec, opaque +tokens and IDs). Use it for any Origin work. + +`port-github-app-to-origin` plans the move of an existing GitHub App. Run it +inside the app's repository. It reads what the app uses out of the code, maps +that onto the live Origin spec, and writes a porting brief: a capability +table, the webhook fields your handlers read and where each comes from on +Origin, the scopes to request, a hello-world path, feedback for Cursor, and +the questions your team should settle first. It plans; it writes no code +unless you ask. Both skills fetch the spec at run time and never name an endpoint from memory. @@ -34,8 +32,8 @@ Both skills fetch the spec at run time and never name an endpoint from memory. - Holding a GitHub App (Probot, Octokit, go-github, hand-rolled) and wanting to know what an Origin App version looks like before starting: `port-github-app-to-origin`. -- Checking which GitHub features map differently on Origin, and what to use - instead: either skill. +- Checking how a capability your app relies on today maps onto Origin: + `port-github-app-to-origin`. In Cursor, ask about the Origin API or ask to port the app, or run `/origin-api` or `/port-github-app-to-origin`. @@ -83,8 +81,8 @@ mkdir -p .cursor/skills && cp -r plugins/origin-apps/skills/* .cursor/skills/ ## Where the brief goes The porting skill writes `ORIGIN-PORTING-BRIEF.md` at the repository root and -prints its path. The feedback entries and questions in the brief are yours to -send through whatever contact route you have with Cursor; Cursor wants them. +prints its path. The Feedback for Cursor section is written to be sent as is; +the rest of the brief is for your team. ## License diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md index f0400dc07..f45fa68ef 100644 --- a/origin-apps/skills/origin-api/SKILL.md +++ b/origin-apps/skills/origin-api/SKILL.md @@ -1,23 +1,24 @@ --- name: origin-api description: >- - Guides building on the Cursor Origin API: creating an Origin App, - authenticating as it, calling the REST API, receiving webhooks. Use whenever - code or a plan touches Origin endpoints, installation tokens, scopes, webhook - subscriptions or signatures, page tokens, or an Origin App manifest. Points - at the docs section that answers each question and names the few rules that - GitHub habits get wrong. + Routes questions about the Cursor Origin API to the right section of the + Origin docs and names the few rules to check first. Use when a task mentions + Origin, the Origin API, Origin Apps, or Origin webhooks, including creating + an Origin App, authenticating as one, calling Origin endpoints, or handling + Origin webhook deliveries. license: MIT compatibility: >- Needs network access to https://cursor.com/docs/api/origin/* at run time. --- -# Build on the Origin API +# Origin API -The docs are the only source of truth. This skill tells you where to look and -which rules to check first. It restates nothing you can read there. +The docs are the source of truth. This skill says where to look and which +rules to check first; it does not restate the docs. -## Fetch first. Never name an endpoint, scope, slug, header, or limit from memory. +## Fetch first + +Do not name an endpoint, scope, event slug, header, or limit from memory. - `https://cursor.com/docs/api/origin/openapi.yaml`: the contract. Its `x-origin-*` extensions are summarized under `#endpoint-reference`. @@ -26,56 +27,49 @@ which rules to check first. It restates nothing you can read there. - `https://cursor.com/docs/api/origin/llms.txt` (index) and `https://cursor.com/docs/api/origin/changelog` (what moved). -Lookup order: for one question, read `llms.txt` to find the section, then -fetch only that section of `llms-full.txt` or the page it links. Fetch the -whole `llms-full.txt` or `openapi.yaml` only when the task needs broad -coverage, such as a porting brief. +For one question, use `llms.txt` to find the section, then read only that +section. Fetch the whole `llms-full.txt` or `openapi.yaml` when the task +needs broad coverage, such as a porting brief. -Cite `operationId`s and `llms-full.txt` anchors. If this file and the fetched -docs disagree, the docs win. +Cite `operationId`s and `llms-full.txt` anchors. Where this file and the docs +disagree, the docs win. ## Where to look | Question | Section of `llms-full.txt` | | --- | --- | -| Which credential for which call; how to mint and how long it lives | `#authentication` through `#git-https-authentication` | +| Which credential for which call; minting and lifetime | `#authentication` through `#git-https-authentication` | | Install flow and the callback receipt | `#installation`, `#installation-receipt` | -| Which scope an operation needs | `x-origin-scopes` on the operation; `#scopes` for the table and the rules | +| Which scope an operation needs | `x-origin-scopes` on the operation; `#scopes` | | What an installation can do on a mirrored repository | `#mirrored-repositories` | -| Webhook headers, signature, envelope, retries, pausing, recovery | `#webhooks` and its subsections | -| Which events exist and which are delivered without subscribing | `#events` | -| Payload shapes and the `x-origin-webhook-events` extension | `#event-payloads` | +| Webhook headers, signature, envelope, retries, pausing, recovery | `#webhooks` | +| Which events exist and which arrive without subscribing | `#events` | +| Payload shapes | `#event-payloads` | | Pagination, errors, request IDs, repository paths | `#common-conventions` | | ID form and stability | `#ids` | | What a `PREVIEW` badge means | `#preview` | -| Rate limits and headers | `#rate-limits` | +| Rate limits | `#rate-limits` | | Check-run keys, attempts, stale writes | `#check-runs` | | What is not there yet | `#current-limitations` | | A checklist to build against | `#implementation-checklist` | ## Rules to check first -In priority order. Each is one line in the docs; getting it wrong costs a -week. - 1. **Native or mirror.** Confirm the target repositories are Origin-native - or stable outbound mirrors before anything else. On any other mirror - state an installation can only read, and pushes are not delivered - (`#mirrored-repositories`, `#events`). + or stable outbound mirrors. On any other mirror state an installation can + only read, and pushes are not delivered (`#mirrored-repositories`, + `#events`). 2. **Subscribe.** Only the `installation.*` events arrive without a - subscription. `#events` says what else delivery needs; a missing - subscription is silence, not an error. + subscription; a missing subscription is silence, not an error (`#events`). 3. **Verify, dedupe, acknowledge.** Verify the signature over the raw body before parsing, dedupe on the delivery ID, return `2xx`, then process (`#signature-verification`, `#retries`, `#automatic-disable`). The digest - step differs from the Standard Webhooks spec, so do not assume a generic + step differs from the Standard Webhooks spec; do not assume a generic verifier passes. 4. **Scopes from the spec.** Request the union of `x-origin-scopes.scopes` - over the operations the app calls, and nothing else (`#scopes`). -5. **Opaque tokens and IDs.** Page tokens and IDs are not yours to build or - parse (`#pagination`, `#ids`). - -## Coming from GitHub + over the operations the app calls (`#scopes`). +5. **Opaque tokens and IDs.** Do not build or parse page tokens or IDs + (`#pagination`, `#ids`). -Several GitHub conventions map differently on Origin. The -`port-github-app-to-origin` skill in this plugin covers the differences. +Porting an existing GitHub App: the `port-github-app-to-origin` skill in +this plugin covers how its capabilities map onto Origin. diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index 5a1a44c9f..5467e11e1 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -1,11 +1,11 @@ --- name: port-github-app-to-origin description: >- - Plans the port of an existing GitHub App to a Cursor Origin App. Use when a - repo is a GitHub App (manifest, Probot, Octokit or another GitHub SDK, webhook - signature handlers) and the task is to bring it to Origin or compare it with - the Origin API. Maps what the app uses from GitHub onto the live Origin spec - and writes a porting brief. No code. + Plans the port of an existing GitHub App to a Cursor Origin App. Use when the + task is to bring a GitHub App to Origin or compare what it uses against the + Origin API. Reads the app's needs out of its code, maps them onto the live + Origin spec, and writes a porting brief with feedback for Cursor. Planning + only. license: MIT compatibility: >- Needs network access to https://cursor.com/docs/api/origin/* at run time. @@ -13,82 +13,67 @@ compatibility: >- # Port a GitHub App to an Origin App -Run inside the GitHub App's codebase. The output is a porting brief -(`references/brief-template.md`), not an implementation. It says what maps, -what changes shape, what is not available today, and what is worth raising -with Cursor. +Run inside the app's codebase. The output is a porting brief +(`references/brief-template.md`): what maps, what changes shape, what is not +available today, and what to tell Cursor. This skill plans; it does not write +or change code unless the user explicitly asks for that after reading the +brief. -The `origin-api` skill in this plugin covers which docs to fetch, credentials, -scopes, webhooks, paging, IDs, and errors. Follow it first. Nothing here repeats it. Two rules on top: +The `origin-api` skill in this plugin covers the docs and the rules to check +first; follow it. Two rules on top: 1. **Discover, do not ask.** Read permissions, events, handlers, calls, token - minting, and the receiver out of the code. Never ask for a manifest or an - endpoint list. Anything you cannot find becomes an open question. -2. **Check the documented path first.** Anything in - `references/origin-isms.md` has a documented Origin path or a documented - limitation. Use the row's label; `not-available` rows still go through - the gap bar. + minting, and the receiver out of the code. Anything you cannot find becomes + an open question. +2. **Feedback describes use cases, not the team's code.** Team-facing parts of + the brief may cite files and lines. The Feedback for Cursor section names + only what the app needs to do and what Origin lacks for it, in Origin + terms, with no file paths, module names, framework internals, or + repository names. -## Procedure +## Suggested procedure -1. **Load the spec** (`origin-api`, "Fetch first"). A brief needs broad - coverage, so fetch the full `openapi.yaml` and `llms-full.txt`; the - narrow lookup order in `origin-api` is for later single questions. Record - `info.version` and the fetch time for the brief's provenance. Build the - mapping index per `references/spec-mapping.md`. -2. **Discover** per `references/discovery.md`. Record a file and line for - every fact, including payload fields read only for logging and calls the - framework makes on the app's behalf. Note what you looked for and did not - find. -3. **Map** each inventory row (`references/spec-mapping.md`, "Matching") and - label it with the parity labels in the brief template. Map the webhook - payload fields the code reads, not only the event names. If a payload - lacks a field the REST resource has, read the resource; see the per-field - notes under `#event-payloads`. Name the call for each such field. Then: - - Check `origin-isms.md` before writing `gap`. Check `gap-bar.md` before - writing any feedback entry. - - A GitHub feature the Origin docs do not mention is `unknown` with a - question. The question is how the team tells Cursor they need it. - - A behavior the code depends on that the docs neither confirm nor deny - (does event X fire in case Y? does `updatedAt` move on comments?) is a - question plus a hello-world step that observes it. Never guess it into - `same` from GitHub behavior. -4. **Write the brief** from the template in full. Every Origin cell names an - `operationId`, a slug, or an `llms-full.txt` anchor. Sizes are S, M, or L, - never time. -5. **Close with the questions**, pruned to what discovery left open. The - first is always native or mirror, because it decides whether the app - receives events at all. -6. **Check the brief and fix.** Copy this list, tick each line, fix what - fails, and repeat until a pass changes nothing. +Adapt the steps to the app. The brief format matters most in its Feedback +section. - - [ ] Every Origin cell names an `operationId`, slug, or anchor that - exists in the files fetched in step 1. - - [ ] Every `gap` row has a feedback entry, and the entry quotes one of the five - tradeoff tests in `gap-bar.md`. - - [ ] Every `unknown` row has a question in § 7. - - [ ] No row `origin-isms.md` labels `reshaped` is labeled `gap`; every - `not-available` row has a question. - - [ ] Every event the code handles has a § 4 row for each payload field - it reads, including log-only fields. - - [ ] Calls the framework makes on the app's behalf appear as rows. - - [ ] The scopes line equals the union of `x-origin-scopes.scopes` over - the § 3 operations. - - [ ] Question 1 is native or mirror. +1. **Load the spec.** A brief needs broad coverage, so fetch the full + `openapi.yaml` and `llms-full.txt`. Record `info.version` and the fetch + time for provenance. Build the index per `references/spec-mapping.md`. +2. **Discover** per `references/discovery.md`, including payload fields read + only for logging and calls the framework makes on the app's behalf. Note + what you looked for and did not find. +3. **Map** each capability (`references/spec-mapping.md`) and label it with + the parity labels in the brief template. Map the payload fields the code + reads, not only the event names; if a payload lacks a field the REST + resource has, a follow-up read is the usual answer (`#event-payloads`). + Check `references/origin-isms.md` before labeling `gap`, and + `references/gap-bar.md` before writing feedback. A capability the Origin + docs do not mention is `unknown` with a question. A behavior the docs + neither confirm nor deny becomes a question plus a hello-world step that + observes it, rather than an assumption carried over from the app's + current platform. +4. **Write the brief** from the template. Every Origin cell names an + `operationId`, a slug, or an `llms-full.txt` anchor. Sizes are S, M, or L, + not time. +5. **Self-check** before finishing: every Origin cell resolves in the fetched + files; every `gap` row has a feedback entry that names a tradeoff from + `gap-bar.md`; every `not-available` and `unknown` row has a question; the + Feedback section contains nothing that reveals the team's internals; the + first question is native or mirror. ## Not in scope -Writing port code or adapters. Choosing a language, framework, or client. -Estimating in time. Asking for anything the codebase contains. Sending gap -feedback to Cursor yourself; the brief carries it and the team sends it. +Writing or changing code without the user's explicit ask. Choosing a +language, framework, or client. Estimating in time. Sending feedback to +Cursor yourself; the brief carries it and the team sends it. ## Reference files | File | Read when | | --- | --- | -| `origin-api` skill (install both) | First. Sources and fundamentals. | +| `origin-api` skill (install both) | First. Docs pointers and the rules to check. | | `references/discovery.md` | Scanning the codebase. | | `references/spec-mapping.md` | Building the index. Matching calls, events, and fields. | -| `references/origin-isms.md` | Labeling a missing GitHub feature. | -| `references/gap-bar.md` | Deciding whether a difference is feedback for Cursor, and writing the entry. | +| `references/origin-isms.md` | Labeling a capability that maps differently. | +| `references/gap-bar.md` | Deciding what is feedback for Cursor, and writing the entry. | | `references/brief-template.md` | Writing the output. | diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md index a5ac56f50..d17796acd 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md +++ b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md @@ -3,12 +3,12 @@ Contents: [Labels](#labels) (parity, size) and [Template](#template) (provenance, §§ 1-8). -Write one Markdown file at the repository root (`ORIGIN-PORTING-BRIEF.md` -unless the team's docs convention says otherwise) and print its path. Fill -every section. An empty section says so in one line rather than disappearing. -Cite spec `operationId`s and `llms-full.txt` anchors. Cite the team's code by -`file:line`. One table row per capability, one line per follow-up field, one -feedback entry per gap. The team should be able to review it in one sitting. +A default shape; adapt it to the app. Write one Markdown file at the +repository root (`ORIGIN-PORTING-BRIEF.md` unless the team's docs convention +says otherwise) and print its path. Cite spec `operationId`s and +`llms-full.txt` anchors. Sections 1 to 5 and 7 are for the team and may cite +their code by `file:line`. Section 6 is for Cursor and must not reveal the +team's internals; its format is the part to keep exact. ## Labels @@ -20,11 +20,11 @@ feedback entry per gap. The team should be able to review it in one sitting. | `reshaped` | Same capability, different shape (pagination, identifier form, event granularity, key semantics). The code changes, the behavior does not. | | `workaround` | Same outcome by a different route (follow-up read, client-side filter, marker). The Tradeoff column is mandatory. | | `not-available` | Nothing in the current spec covers it (`origin-isms.md` or `#current-limitations`). Names the closest idiom and has a question in § 7; eligible for a feedback entry. | -| `gap` | No workaround, or one whose tradeoff meets the bar in `gap-bar.md`. Has a feedback entry in § 6. | +| `gap` | No workaround, or one whose tradeoff meets a test in `gap-bar.md`. Has a feedback entry in § 6. | | `unknown` | Discovery or the spec could not answer. Has a question in § 7. | | `preview` (suffix) | The row touches an element badged `x-cursor-visibility: PREVIEW` (`llms-full.txt#preview`). | -**Size** (kind of change, never time) +**Size** (kind of change, not time) | Size | Meaning | | --- | --- | @@ -37,9 +37,9 @@ feedback entry per gap. The team should be able to review it in one sitting. ```markdown # Origin porting brief for -Planning document. Maps what this GitHub App uses from GitHub onto the Cursor -Origin API as published on . It makes no decisions about language, -framework, or client. +Planning document. Maps what this app uses today onto the Cursor Origin API +as published on . It makes no decisions about language, framework, or +client. ## Provenance @@ -68,20 +68,20 @@ it writes back. Then: ## 2. First decision: which repositories - Apps have full -scopes only on Origin-native repositories and stable outbound mirrors. -Repositories mirrored from GitHub are read-only to apps and deliver no push -events. **Answer question 1 before attempting § 5.** + What an +installation can do on a mirrored repository is defined in +`llms-full.txt#mirrored-repositories` and `#events`. Answer question 1 before +attempting § 5. ## 3. Capability table -One row per GitHub capability the code uses, grouped by facet with a bold -header row (Authentication · Installation & discovery · Configuration · -Repositories & contents · Pull requests · Reviews & comments · Checks · -Webhooks: events · Webhooks: receiver · Git). Include rows for calls a -dependency makes on the app's behalf, marked as such. +One row per capability the code uses today, grouped by facet (authentication, +installation and discovery, configuration, repositories and contents, pull +requests, reviews and comments, checks, webhook events, webhook receiver, +git). Include rows for calls a dependency makes on the app's behalf, marked as +such. -| GitHub thing (evidence) | Origin equivalent | Parity | Size | Tradeoff | Open question | +| Capability today (evidence) | Origin equivalent | Parity | Size | Tradeoff | Open question | | --- | --- | --- | --- | --- | --- | | `GET /repos/{o}/{r}/pulls/{n}` (`src/x.ts:12`) | `` | same | S | none | none | @@ -91,19 +91,18 @@ or `none`. `workaround` rows fill Tradeoff. `gap` rows link their feedback entry rows name their question. **Scopes to request:** the union of `x-origin-scopes.scopes` across every -Origin operation above that an installation token can call, minus ambient -and implied scopes (`write` implies `read`, and `repository:metadata:read` is -automatic). The install URL's `scope` parameter carries this list. +Origin operation above that an installation token can call, minus the scopes +`llms-full.txt#scopes` says are automatic or implied. ## 4. Webhook payload fields the code reads -| Event (GitHub → Origin) | GitHub field | Origin | How | +| Event (today → Origin) | Field read today | Origin | How | | --- | --- | --- | --- | -| `pull_request.synchronize` → `` | `pull_request.head.sha` | present | `payload.pullRequest.head.sha` | -| `push` → `` | `commits[].added` | follow-up read | ``, one call per ref update | +| `` → `` | `` | present | `payload.pullRequest.head.sha` | +| `` → `` | `` | follow-up read | ``, one call per ref update | "How" is one of five values. Present at ``. Present in the envelope -(`event.type` for GitHub's `action`). Follow-up read via ``, +(`event.type` carries the action). Follow-up read via ``, with the call count per event. Derivable, saying from what and whether the format is documented. Absent, pointing at the row's label in § 3. Include fields read only for logging. @@ -130,16 +129,18 @@ each spec-silent behavior the brief depends on. ## 6. Feedback for Cursor Capabilities Origin should add, one entry per `gap` row in the `gap-bar.md` -format. If none, write "No row met the feedback bar; the workarounds in § 3 -carry their tradeoffs." +format: use case and API gap, in Origin terms, with nothing that reveals the +team's internals. If none, write "No row met the feedback bar; the +workarounds in § 3 carry their tradeoffs." ## 7. Questions for the team Decisions the team must make before the port, not asks of Cursor. Always the first three, then what discovery left open. -1. Native repositories (or stable outbound mirrors), or repositories mirrored - from GitHub? Decides whether the app receives events and can write. +1. Native repositories (or stable outbound mirrors), or repositories in + another mirror state? Decides whether the app receives events and can + write. 2. Which follow-up reads in § 4 are acceptable at your event volume, and which payload fields are hard requirements? 3. Which flows depend on a user credential today, and what should they do on diff --git a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md index 0b56fc540..16c0d7b29 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md +++ b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md @@ -2,86 +2,71 @@ Cursor wants to hear what the team needs from Origin. Raise anything that blocks the team's core flow, costs them correctness, security, or scale, or -that they would simply like Origin to do. The bar below decides whether an -item is feedback for Cursor or a question for the team, not whether to speak -up. Its other job is -ordering: put the asks that block the port ahead of the ones that are -conveniences, so the important ones are read first. When a difference has a -workaround, writing the row as `workaround` with its tradeoff and a question -is often the right answer; a feedback entry adds the tradeoff analysis Cursor -needs to prioritize it. +that they would like Origin to do. The bar below sorts items into feedback +for Cursor (a capability Origin should add) and questions for the team +(decisions the team must make); it does not decide whether to speak up. It +also orders feedback so the items that block the port are read first. - A difference is any row whose parity label is not `same`. -- A workaround reaches the same outcome with the current API by another route. A - follow-up read, a re-keyed identifier, a path change, a client-side filter, - a marker the app controls. +- A workaround reaches the same outcome with the current API by another + route: a follow-up read, a re-keyed identifier, a path change, a + client-side filter, a marker the app controls. A `workaround` row with its + tradeoff is often the right answer. - A gap is a difference with no workaround, or a workaround whose tradeoff - meets one of the five tests below. Gaps become feedback entries in § 6: - capabilities Origin should add. Things the team must decide go to § 7 as - questions; the two sections do not repeat each other. + meets one of the tests below. Gaps become feedback entries. -## Nontrivial tradeoff - -At least one should hold for a feedback entry. Quote it in the entry. +## Tradeoffs that make a workaround insufficient | Tradeoff | Test | | --- | --- | -| Fan-out at scale | Calls per event multiply by a factor that grows with repository or activity size (N commits × M files, or a full list scan to find one row), and the app's volume makes that budget-relevant. One bounded extra read per event is a workaround. | -| Correctness risk | The workaround can return a wrong answer, not only a slower one. Heuristic "my own row" matching. Inferring a pull request from a SHA several versions share. Assembling a URL whose format is not contractual. | +| Fan-out at scale | Calls per event multiply by a factor that grows with repository or activity size, and the app's volume makes that budget-relevant. One bounded extra read per event is a workaround. | +| Correctness risk | The workaround can return a wrong answer, not only a slower one: heuristic matching of the app's own rows, inferring a pull request from a SHA several versions share, assembling a URL whose format is not contractual. | | Security posture | The workaround needs a broader scope, a longer-lived token, or a user credential where an installation token should do. | -| Customer-visible behavior | The workaround changes what the team's users see or can do, not how the code is organized. | +| Customer-visible behavior | The workaround changes what the team's users see or can do. | | Load-bearing | The capability sits on the hello-world path or the team's stated core flow. | -## Usually a workaround or a question, not feedback +## Usually a workaround or a question - Anything `origin-isms.md` labels `reshaped`: a documented path exists. - A field or filter the code does not use. -- A GitHub convenience (`Link` pagination, numeric IDs, `html_url`) where the - Origin convention is a mechanical substitution. -- Anything the changelog says shipped or the spec already carries. Re-read the - live spec before writing any feedback entry. -- A concept the Origin docs do not mention. That is `not-available` or - `unknown` with a question; the team should still ask if they need it. -- A GitHub Search query, when the spec has no search operation for that - resource. The idiom is a list operation with its filters plus a client-side - predicate. A sorted list read that stops at a cutoff costs proportional to - the matches, not the collection. If that count meets the bar, the feedback - is usually about a filter rather than search. - -## One pattern that does meet the bar +- A convention difference (pagination style, identifier form, URL fields) + where the Origin convention is a mechanical substitution. +- Anything the changelog says shipped or the spec already carries. Re-read + the live spec before writing feedback. +- A capability the Origin docs do not mention: `not-available` or `unknown` + with a question. The team should still ask if they need it. +- A query the app runs against a search API, when the spec has no search + operation for that resource. A list operation with its filters plus a + client-side predicate is the idiom; if that fails the fan-out test, the + feedback is usually about a filter. -A state change the app reacts to that has no event, when reacting to exactly -that change is the app's purpose and the state is invisible until an -unrelated event arrives. Reading it off the next snapshot fails on correctness -and customer-visible behavior when the app is a gate (a check, a block, a -notification). Write the feedback entry about the event. When the app only logs or -tidies up on that change, a question is enough. +One pattern that does meet the bar: a state change the app reacts to that +has no event, when reacting to that change is the app's purpose and the +state is invisible until an unrelated event arrives. That fails correctness +and customer-visible behavior when the app is a gate. Write the feedback +about the event. ## The feedback format -One entry per gap, in the brief's "Feedback for Cursor" section. Write it -so Cursor can act without a call. +One entry per gap, in the brief's "Feedback for Cursor" section. Describe the +use case and the API gap relative to it, in Origin terms. No file paths, +module names, framework internals, code structure, or repository names; those +belong in the team-facing sections of the brief. ```markdown ### Feedback: -- **GitHub call, event, or permission:** `` / `` / ``, at ``. -- **What the app needs from it:** . -- **Why:** . -- **Closest Origin operation:** `` / `` / none, and what it lacks. -- **Workaround considered:** . -- **Tradeoff that meets the bar:** . -- **Shape that would close it:** +- **Use case:** the app needs to , . +- **Origin today:** . +- **Workaround considered:** . - **Blocking?** yes / no, for which flow. -- **Spec version checked:** `` on ``. +- **Spec version checked:** ``, . ``` -Describe the capability rather than proposing scope, field, or route names; -that leaves Cursor free to fit it to the API's conventions. One capability -per entry. Do not send feedback yourself: the team decides what goes out, -and they are encouraged to send the feedback entries, and any § 7 questions -they want Cursor's view on, to Cursor -through whatever contact route they have, quoting the spec version and any -request ID from failed calls (`llms-full.txt#errors`). A reply of "here is -the idiom" or "not planned" is useful too; record it in the brief with the -label it earns. +Describe the capability rather than proposing scope, field, or route names, +so Cursor can fit it to the API's conventions. One capability per entry. + +The team sends the feedback, not you. Before they forward it, they should +strip anything that reveals their internals. A reply of "here is the idiom" +or "not planned" is useful too; record it in the brief with the label it +earns. diff --git a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md index 016f16aeb..f043a6c7c 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md +++ b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md @@ -33,10 +33,10 @@ table here. Build the index once. Every later step looks things up in it. ## Matching -Match in this order and stop at the first rule that yields a confirmed +Try these in order and stop at the first rule that yields a confirmed counterpart. Confirmed means you read the Origin operation's description and -parameters and it answers the same question the GitHub call answers. A name -match is a candidate, never a result. +parameters and it answers the same question the current call answers. A name +match is a candidate, not a result. **REST calls** From a8da80c63e9f4641944dc086660124fb05aba2cb Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 25 Sep 2026 01:05:35 +0000 Subject: [PATCH 16/26] Brief template becomes guidance; four optional marks; feedback file brief-template.md describes what a good brief does and offers a default outline the agent adapts or skips, with a small app's brief fitting one screen. Fixed question lists, column counts, and boilerplate sections are gone; what remains is what makes the brief trustworthy: evidence for app claims, Origin claims resolved against the fetched docs, feedback free of partner internals. unknown folds into not-available; same and reshaped fold into maps; the four marks are optional vocabulary. Feedback for Cursor stays last and is also written to ORIGIN-FEEDBACK.md when there is at least one entry. Co-authored-by: ali.nikseresht --- origin-apps/README.md | 7 +- .../skills/port-github-app-to-origin/SKILL.md | 38 +-- .../references/brief-template.md | 235 ++++++------------ .../references/discovery.md | 2 +- .../references/gap-bar.md | 22 +- .../references/origin-isms.md | 58 ++--- .../references/spec-mapping.md | 10 +- 7 files changed, 149 insertions(+), 223 deletions(-) diff --git a/origin-apps/README.md b/origin-apps/README.md index 8794cd716..4b7027c99 100644 --- a/origin-apps/README.md +++ b/origin-apps/README.md @@ -17,7 +17,7 @@ tokens and IDs). Use it for any Origin work. inside the app's repository. It reads what the app uses out of the code, maps that onto the live Origin spec, and writes a porting brief: a capability table, the webhook fields your handlers read and where each comes from on -Origin, the scopes to request, a hello-world path, feedback for Cursor, and +Origin, the scopes to request, a first-run path, feedback for Cursor, and the questions your team should settle first. It plans; it writes no code unless you ask. @@ -81,8 +81,9 @@ mkdir -p .cursor/skills && cp -r plugins/origin-apps/skills/* .cursor/skills/ ## Where the brief goes The porting skill writes `ORIGIN-PORTING-BRIEF.md` at the repository root and -prints its path. The Feedback for Cursor section is written to be sent as is; -the rest of the brief is for your team. +prints its path. When there is feedback for Cursor, it also writes that +section to `ORIGIN-FEEDBACK.md`, ready to send as is; the rest of the brief +is for your team. ## License diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index 5467e11e1..f40aabfe2 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -42,24 +42,24 @@ section. 2. **Discover** per `references/discovery.md`, including payload fields read only for logging and calls the framework makes on the app's behalf. Note what you looked for and did not find. -3. **Map** each capability (`references/spec-mapping.md`) and label it with - the parity labels in the brief template. Map the payload fields the code - reads, not only the event names; if a payload lacks a field the REST - resource has, a follow-up read is the usual answer (`#event-payloads`). - Check `references/origin-isms.md` before labeling `gap`, and - `references/gap-bar.md` before writing feedback. A capability the Origin - docs do not mention is `unknown` with a question. A behavior the docs - neither confirm nor deny becomes a question plus a hello-world step that - observes it, rather than an assumption carried over from the app's - current platform. -4. **Write the brief** from the template. Every Origin cell names an - `operationId`, a slug, or an `llms-full.txt` anchor. Sizes are S, M, or L, - not time. -5. **Self-check** before finishing: every Origin cell resolves in the fetched - files; every `gap` row has a feedback entry that names a tradeoff from - `gap-bar.md`; every `not-available` and `unknown` row has a question; the - Feedback section contains nothing that reveals the team's internals; the - first question is native or mirror. +3. **Map** each capability (`references/spec-mapping.md`). Map the payload + fields the code reads, not only the event names; if a payload lacks a + field the REST resource has, a follow-up read is the usual answer + (`#event-payloads`). Check `references/origin-isms.md` before calling + anything a gap, and `references/gap-bar.md` before writing feedback. A + capability the Origin docs do not mention is not available today and gets + a question. A behavior the docs neither confirm nor deny becomes a + question plus a first-run step that observes it, rather than an assumption + carried over from the app's current platform. +4. **Write the brief** per `references/brief-template.md`: guidance and a + default outline, not a form. Every Origin claim names an `operationId`, a + slug, or an `llms-full.txt` anchor. When there is feedback, also write the + Feedback section to `ORIGIN-FEEDBACK.md` beside the brief. +5. **Self-check** before finishing: every Origin claim resolves in the + fetched files; every gap has a feedback entry that names a tradeoff from + `gap-bar.md`; the Feedback section and `ORIGIN-FEEDBACK.md` contain nothing + that reveals the team's internals; the summary names the native-or-mirror + question. ## Not in scope @@ -76,4 +76,4 @@ Cursor yourself; the brief carries it and the team sends it. | `references/spec-mapping.md` | Building the index. Matching calls, events, and fields. | | `references/origin-isms.md` | Labeling a capability that maps differently. | | `references/gap-bar.md` | Deciding what is feedback for Cursor, and writing the entry. | -| `references/brief-template.md` | Writing the output. | +| `references/brief-template.md` | Writing the brief and the feedback file. | diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md index d17796acd..8061f7235 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md +++ b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md @@ -1,158 +1,79 @@ -# Porting brief template - -Contents: [Labels](#labels) (parity, size) and [Template](#template) -(provenance, §§ 1-8). - -A default shape; adapt it to the app. Write one Markdown file at the -repository root (`ORIGIN-PORTING-BRIEF.md` unless the team's docs convention -says otherwise) and print its path. Cite spec `operationId`s and -`llms-full.txt` anchors. Sections 1 to 5 and 7 are for the team and may cite -their code by `file:line`. Section 6 is for Cursor and must not reveal the -team's internals; its format is the part to keep exact. - -## Labels - -**Parity** - -| Label | Meaning | -| --- | --- | -| `same` | Same capability, same shape. A path or field rename at most. | -| `reshaped` | Same capability, different shape (pagination, identifier form, event granularity, key semantics). The code changes, the behavior does not. | -| `workaround` | Same outcome by a different route (follow-up read, client-side filter, marker). The Tradeoff column is mandatory. | -| `not-available` | Nothing in the current spec covers it (`origin-isms.md` or `#current-limitations`). Names the closest idiom and has a question in § 7; eligible for a feedback entry. | -| `gap` | No workaround, or one whose tradeoff meets a test in `gap-bar.md`. Has a feedback entry in § 6. | -| `unknown` | Discovery or the spec could not answer. Has a question in § 7. | -| `preview` (suffix) | The row touches an element badged `x-cursor-visibility: PREVIEW` (`llms-full.txt#preview`). | - -**Size** (kind of change, not time) - -| Size | Meaning | -| --- | --- | -| S | Adapter or client layer. A path, header, identifier, or pagination rewrite, or a re-keyed lookup. | -| M | A new code path. A follow-up read where the payload used to suffice, a handshake step, a new handler, a data-model change for a new identifier or version concept. | -| L | A product or architecture change. A flow that depended on user OAuth, a customer-visible behavior, a dependency on native repositories, an open feedback entry. | - -## Template - -```markdown -# Origin porting brief for - -Planning document. Maps what this app uses today onto the Cursor Origin API -as published on . It makes no decisions about language, framework, or -client. - -## Provenance - -- Origin OpenAPI `info.version`: ``, fetched -- Docs read: -- Codebase: `` at `` -- Re-check `workaround` and `gap` rows against the changelog before work - starts. They are the rows most likely to have moved. - -## 1. What the app is today - -One paragraph on what it does for its users, which events drive it, and what -it writes back. Then: - -| Facet | Finding | Evidence | -| --- | --- | --- | -| Manifest / declared permissions | … or "none checked in; derived from calls" | `file:line` | -| Events handled | … | `file:line` | -| REST call families | , in § 3 | | -| GraphQL | none / documents, decomposed in § 3 | | -| Auth flow | app JWT () → installation token; user OAuth: | `file:line` | -| Webhook receiver | path, scheme, raw-body availability, dedupe | `file:line` | -| Git as the app | clone / push / none | `file:line` | -| Observed language and libraries | … | | -| Looked for, not found | … | | - -## 2. First decision: which repositories - - What an -installation can do on a mirrored repository is defined in -`llms-full.txt#mirrored-repositories` and `#events`. Answer question 1 before -attempting § 5. - -## 3. Capability table - -One row per capability the code uses today, grouped by facet (authentication, -installation and discovery, configuration, repositories and contents, pull -requests, reviews and comments, checks, webhook events, webhook receiver, -git). Include rows for calls a dependency makes on the app's behalf, marked as -such. - -| Capability today (evidence) | Origin equivalent | Parity | Size | Tradeoff | Open question | -| --- | --- | --- | --- | --- | --- | -| `GET /repos/{o}/{r}/pulls/{n}` (`src/x.ts:12`) | `` | same | S | none | none | - -The Origin column names an `operationId`, a slug, a `llms-full.txt` anchor, -or `none`. `workaround` rows fill Tradeoff. `gap` rows link their feedback entry. -`not-available` rows name the closest idiom and their question. `unknown` -rows name their question. - -**Scopes to request:** the union of `x-origin-scopes.scopes` across every -Origin operation above that an installation token can call, minus the scopes -`llms-full.txt#scopes` says are automatic or implied. - -## 4. Webhook payload fields the code reads - -| Event (today → Origin) | Field read today | Origin | How | -| --- | --- | --- | --- | -| `` → `` | `` | present | `payload.pullRequest.head.sha` | -| `` → `` | `` | follow-up read | ``, one call per ref update | - -"How" is one of five values. Present at ``. Present in the envelope -(`event.type` carries the action). Follow-up read via ``, -with the call count per event. Derivable, saying from what and whether the -format is documented. Absent, pointing at the row's label in § 3. Include -fields read only for logging. - -## 5. Hello-world path - -The shortest route to one real event from one native repository. The -mechanics are in `llms-full.txt#implementation-checklist` and the sections it -links; this list is the observations to make, in order. Append one step for -each spec-silent behavior the brief depends on. - -1. App created, signing key registered, webhook URL and callback set. -2. Every repository event from § 3 selected in app settings. -3. Installed on an Origin-native repository; receipt verified; installation - ID recorded. -4. Installation token minted; the repository appears in the installation's - repositories with the mirror state § 2 expects. -5. Ping received and verified; a retried delivery is deduplicated. -6. Smallest action in § 3 performed; the expected slug and the § 4 fields - arrive. If the ping arrived and this did not, re-check steps 2 and 3 - first. -7. Smallest write from § 3 succeeds with the scopes from the § 3 line. - -## 6. Feedback for Cursor - -Capabilities Origin should add, one entry per `gap` row in the `gap-bar.md` -format: use case and API gap, in Origin terms, with nothing that reveals the -team's internals. If none, write "No row met the feedback bar; the -workarounds in § 3 carry their tradeoffs." - -## 7. Questions for the team - -Decisions the team must make before the port, not asks of Cursor. Always the -first three, then what discovery left open. - -1. Native repositories (or stable outbound mirrors), or repositories in - another mirror state? Decides whether the app receives events and can - write. -2. Which follow-up reads in § 4 are acceptable at your event volume, and - which payload fields are hard requirements? -3. Which flows depend on a user credential today, and what should they do on - Origin? -4. Does anything key approvals or reviews by commit SHA rather than pull - request version? -5. How do you identify your own check runs, comments, and reviews today? Can - a key or marker you control replace actor matching? -6. Do you generate clients from OpenAPI? (Read the changelog for renames.) - -## 8. Out of scope - -No implementation, no SDK or language choice, no time estimates. The brief is -a map. The route is the team's. +# The porting brief + +The brief is for the team that owns the app, plus one section they can send +to Cursor as is. Write it as one Markdown file at the repository root +(`ORIGIN-PORTING-BRIEF.md` unless the team's docs convention says otherwise) +and print its path. A small app's brief fits on one screen; parts that have +nothing to say collapse to a line or disappear. Choose table shapes and +headings to fit the app. + +## What a good brief does + +- **Leads with a summary.** Three to five lines: the verdict (ports as is, + ports with N workarounds, blocked on X), the one question that decides the + rest (usually native or mirror), and whether there is feedback for Cursor + and if any of it blocks. +- **Maps what the app uses to Origin.** Every capability the code relies on, + with the Origin operation, slug, or `llms-full.txt` anchor it maps to, or a + note that nothing does. Where a webhook handler reads specific payload + fields, say per field whether it is present, comes from the envelope, needs + a follow-up read (and how many per event), is derivable, or is absent. + Group by facet if the table is long. End with the scopes to request: the + union of `x-origin-scopes.scopes` over the operations named, minus what + `#scopes` says is automatic or implied. +- **Gives an app-specific first-run path when it helps.** The events to + select by slug, the mirror-state check, the first event that should arrive + and what it should carry, the first write. Generic setup steps belong to + `llms-full.txt#implementation-checklist`, not here. Skip for a read-only app + with one event. +- **Adds plan notes.** What drives the size of the port (a few bullets, no + time estimates) and how to roll it out: dual-run or cutover, what a mirror + trial can and cannot show, what to gate. +- **Asks only what the team must decide.** Repository set, tolerable event + volume and follow-up reads, what replaces a flow that has no Origin + equivalent. Do not restate a row as a question. +- **Ends with two lines of provenance.** Spec `info.version` and fetch time; + codebase and commit. +- **Closes with Feedback for Cursor.** Last section, unnumbered, written to be + copied verbatim (`gap-bar.md` has the shape). Nothing in it reveals the + team's internals. When it has at least one entry, also write it to + `ORIGIN-FEEDBACK.md` beside the brief. When nothing meets the bar, no file; + one line in the brief saying so. + +## What makes it trustworthy + +- Every claim about the app cites evidence: `file:line`, or "from + `` (documented behavior)". Team-facing sections only. +- Every claim about Origin resolves in the fetched `openapi.yaml` or + `llms-full.txt`: an `operationId`, a slug, or an anchor. Nothing from + memory. +- Behavior the docs do not state is a question plus a first-run step that + observes it, never an assumption. +- Feedback for Cursor describes use cases and the API gap in Origin terms, + with no file paths, module names, framework internals, or repository names. + +## Vocabulary, if you want one + +Plain notes serve the reader as well as labels. If the table needs a compact +mark, these four are shared with the other references: `maps` (a documented +path exists, same or reshaped), `workaround` (same outcome by another route; +say the tradeoff), `not-available` (nothing in the current spec; carries a +question), `gap` (no workaround, or one whose tradeoff meets a test in +`gap-bar.md`; produces a feedback entry). Size marks S/M/L, if used, mean +adapter change, new code path, product or architecture change. + +## Default outline + +Adapt or skip parts; the Feedback section is the one to keep exact. + +```text +# Origin porting brief: +Summary +1. The app today one paragraph; observed stack; looked for, not found +2. Capability map table(s); payload fields under webhook events; scopes line +3. First run app-specific, five steps or fewer (optional) +4. Plan notes size drivers; rollout (optional) +5. Questions for the team +Provenance two lines +Feedback for Cursor unnumbered, last, copy verbatim; also ORIGIN-FEEDBACK.md ``` diff --git a/origin-apps/skills/port-github-app-to-origin/references/discovery.md b/origin-apps/skills/port-github-app-to-origin/references/discovery.md index bc408bc8b..f9c86ab01 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/discovery.md +++ b/origin-apps/skills/port-github-app-to-origin/references/discovery.md @@ -50,5 +50,5 @@ Seven facets. For each, what to record: When something is missing, say so in the inventory ("no manifest found (searched: …)", "no signature verification found in the receiver at …"). -Each missing item becomes an `unknown` row or an up-front question. Do not +Each missing item becomes an up-front question. Do not fill it in with what an app of this kind usually does. diff --git a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md index 16c0d7b29..e14f68769 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md +++ b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md @@ -7,13 +7,16 @@ for Cursor (a capability Origin should add) and questions for the team (decisions the team must make); it does not decide whether to speak up. It also orders feedback so the items that block the port are read first. -- A difference is any row whose parity label is not `same`. +- A difference is any capability whose Origin path is not a straight + substitution. - A workaround reaches the same outcome with the current API by another route: a follow-up read, a re-keyed identifier, a path change, a - client-side filter, a marker the app controls. A `workaround` row with its - tradeoff is often the right answer. + client-side filter, a marker the app controls. Noting the workaround with + its tradeoff is often the right answer. - A gap is a difference with no workaround, or a workaround whose tradeoff - meets one of the tests below. Gaps become feedback entries. + meets one of the tests below. Gaps become feedback entries; when there is + at least one, the Feedback section is also written to `ORIGIN-FEEDBACK.md` + beside the brief. ## Tradeoffs that make a workaround insufficient @@ -27,14 +30,14 @@ also orders feedback so the items that block the port are read first. ## Usually a workaround or a question -- Anything `origin-isms.md` labels `reshaped`: a documented path exists. +- Anything `origin-isms.md` marks `maps`: a documented path exists. - A field or filter the code does not use. - A convention difference (pagination style, identifier form, URL fields) where the Origin convention is a mechanical substitution. - Anything the changelog says shipped or the spec already carries. Re-read the live spec before writing feedback. -- A capability the Origin docs do not mention: `not-available` or `unknown` - with a question. The team should still ask if they need it. +- A capability the Origin docs do not mention: not available today, with a + question. The team should still ask if they need it. - A query the app runs against a search API, when the spec has no search operation for that resource. A list operation with its filters plus a client-side predicate is the idiom; if that fails the fan-out test, the @@ -48,8 +51,9 @@ about the event. ## The feedback format -One entry per gap, in the brief's "Feedback for Cursor" section. Describe the -use case and the API gap relative to it, in Origin terms. No file paths, +One entry per gap, in the brief's "Feedback for Cursor" section. A suggested +shape, not a form; keep whatever lines carry information. Describe the use +case and the API gap relative to it, in Origin terms. No file paths, module names, framework internals, code structure, or repository names; those belong in the team-facing sections of the brief. diff --git a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md index 31329c296..90304a1fb 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md +++ b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md @@ -1,41 +1,41 @@ # Origin-isms: GitHub features that map differently on Origin -Check here before labeling anything `gap`. Each row names the label to use -and where the Origin answer lives today (an anchor in `llms-full.txt` unless -noted). `reshaped` rows have a documented path, so they get a question if -the team wants the GitHub shape back rather than a feedback entry. -`not-available` rows get a question, and a feedback entry if they meet the -bar. -Read the source; do not copy this table into the brief. +Check here before calling anything a gap. Each row says whether a documented +Origin path exists (`maps`) or nothing in the current spec covers it +(`not-available`), and where the Origin answer lives (an anchor in +`llms-full.txt` unless noted). A `maps` row is a question only if the team +wants the old shape back. A `not-available` row is a question, and feedback +if it meets the bar in `gap-bar.md`. Read the source; do not copy this table +into the brief. -| GitHub call, event, or permission | Label | Where the Origin answer lives | +| What the app uses today | Mark | Where the Origin answer lives | | --- | --- | --- | -| Writes or `push` events on a repository mirrored from GitHub | `reshaped` | Read-only until the mirror becomes a stable outbound mirror (`#mirrored-repositories`); pushes are not delivered for GitHub-sourced mirrors (`#events`). Transitioning is a user-credential operation. First question of every brief. | -| Install callback query parameters (`installation_id`, `setup_action`) | `reshaped` | `#installation-receipt` | -| RS256 app JWT | `reshaped` | `#app-jwt` | -| Long-lived installation tokens | `reshaped` | `#installation-access-token` | +| Writes or `push` events on a repository mirrored from GitHub | `maps` | Read-only until the mirror becomes a stable outbound mirror (`#mirrored-repositories`); pushes are not delivered for GitHub-sourced mirrors (`#events`). Transitioning is a user-credential operation. First question of every brief. | +| Install callback query parameters (`installation_id`, `setup_action`) | `maps` | `#installation-receipt` | +| RS256 app JWT | `maps` | `#app-jwt` | +| Long-lived installation tokens | `maps` | `#installation-access-token` | | User OAuth, `/user`, `/user/installations`, install-by-user picker | `not-available` | No user-credential flow for apps in the current spec. Repository discovery is through the installation; namespace-wide listing is under `#current-limitations`. Ask what the flow should do. | -| Permissions `: read\|write` | `reshaped` | `#scopes`; `x-origin-scopes` per operation | -| Numeric IDs, `/repositories/{id}` | `reshaped` | `#ids`, `#repository-paths` | -| `Link` / `page` / `per_page` pagination, total counts | `reshaped` | `#pagination` | +| Permissions `: read\|write` | `maps` | `#scopes`; `x-origin-scopes` per operation | +| Numeric IDs, `/repositories/{id}` | `maps` | `#ids`, `#repository-paths` | +| `Link` / `page` / `per_page` pagination, total counts | `maps` | `#pagination` | | GraphQL | `not-available` | No GraphQL endpoint in the current spec. Decompose into REST calls and count the fan-out; a decomposition that meets the feedback bar earns an entry about that read. | -| Commit statuses (`statuses` permission, `POST /statuses/{sha}`) | `reshaped` | `#check-runs` (check runs with a stable `key`) | +| Commit statuses (`statuses` permission, `POST /statuses/{sha}`) | `maps` | `#check-runs` (check runs with a stable `key`) | | Issues (`issues` permission, `issues.*` events, `/issues/{n}` not on a pull request) | `not-available` | No Issues endpoints or events in the current spec. Pull request comments, threads, reviews, and labels cover the pull-request half. Ask what the team needs for the rest; an issue-driven app may earn a feedback entry. | -| `/issues/{n}/comments`, `/issues/{n}/labels` used on a pull request | `reshaped` | Pull requests endpoint reference; same calls under `/pulls/{n}/…` | -| Repository webhook CRUD (`/repos/…/hooks`) | `reshaped` | Subscriptions are set per app through Create App / Update App `events` (`#events`). | -| App-manifest conversion | `reshaped` | App creation form or `CreateApp` (`#installation`, endpoint reference) | +| `/issues/{n}/comments`, `/issues/{n}/labels` used on a pull request | `maps` | Pull requests endpoint reference; same calls under `/pulls/{n}/…` | +| Repository webhook CRUD (`/repos/…/hooks`) | `maps` | Subscriptions are set per app through Create App / Update App `events` (`#events`). | +| App-manifest conversion | `maps` | App creation form or `CreateApp` (`#installation`, endpoint reference) | | OAuth-app token mints | `not-available` | Nothing in the current spec. Ask what the flow was for. | -| Git Data API commit and ref writes | `reshaped` | Create Commit From Files, Create Git Ref (Git data endpoint reference); `#git-https-authentication` for pushes | +| Git Data API commit and ref writes | `maps` | Create Commit From Files, Create Git Ref (Git data endpoint reference); `#git-https-authentication` for pushes | | Git Data API arbitrary blob or tree writes | `not-available` | Not in the current spec. Ask whether commit-from-files or a push covers the use. | -| Standalone review-thread objects | `reshaped` | A thread comes from its first diff-anchored comment (Pull requests endpoint reference); thread listing is under `#current-limitations`. | +| Standalone review-thread objects | `maps` | A thread comes from its first diff-anchored comment (Pull requests endpoint reference); thread listing is under `#current-limitations`. | | User, email, team, and member lookups | `not-available` | No directory reads in the current spec. Reviewer identifiers resolve by public id, user email, or group slug; `handle` is present when the profile is public (`#resource-references`). | -| Single `pull_request` event with an `action` field, `previous_attributes` | `reshaped` | `#events`, `#event-payloads` | -| `x-github-*` headers, HMAC `x-hub-signature-256` | `reshaped` | `#headers`, `#signature-verification` | -| Payload inlines (changed files on push, before-SHA, `html_url`, `sender` profile) | `reshaped` | `#resource-references`; the push commit list is under `#current-limitations` and may change. Name the follow-up call per field and count the fan-out. | -| All events delivered after app creation | `reshaped` | `#events` | -| Reviews keyed by `commit_id` | `reshaped` | `pullRequestVersion` on the review schema | -| Finding own check runs or comments by actor | `reshaped` | `#check-runs` (`key`); comments and reviews by a marker the app controls | +| Single `pull_request` event with an `action` field, `previous_attributes` | `maps` | `#events`, `#event-payloads` | +| `x-github-*` headers, HMAC `x-hub-signature-256` | `maps` | `#headers`, `#signature-verification` | +| Payload inlines (changed files on push, before-SHA, `html_url`, `sender` profile) | `maps` | `#resource-references`; the push commit list is under `#current-limitations` and may change. Name the follow-up call per field and count the fan-out. | +| All events delivered after app creation | `maps` | `#events` | +| Reviews keyed by `commit_id` | `maps` | `pullRequestVersion` on the review schema | +| Finding own check runs or comments by actor | `maps` | `#check-runs` (`key`); comments and reviews by a marker the app controls | | Requested-reviewer team pages, `created_via` | `not-available` | Groups exist and resolve by slug; there is no group membership read in the current spec. | -A feature that is not on this list and that the Origin docs do not mention -is `unknown` with a question. +A capability that is not on this list and that the Origin docs do not +mention is not available today; ask the team whether they need it. diff --git a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md index f043a6c7c..54c456345 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md +++ b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md @@ -9,7 +9,7 @@ table here. Build the index once. Every later step looks things up in it. | --- | --- | --- | | `x-origin-scopes` | every operation | The scope and credential rules for that operation. `llms-full.txt#scopes`, `#endpoint-reference`. | | `x-origin-webhook-events` | payload schemas | The slugs that deliver this payload shape; a schema carrying it is a webhook family. `#event-payloads`. Infer the embedded resource from its `$ref`s; some families have no REST twin. | -| `x-cursor-visibility: PREVIEW` | operations, parameters, schemas, fields | `#preview`. Carry it into the brief as a `preview` suffix on any row that touches a badged element. | +| `x-cursor-visibility: PREVIEW` | operations, parameters, schemas, fields | `#preview`. Note it in the brief on any capability that touches a badged element. | ## Build the index @@ -54,7 +54,7 @@ match is a candidate, not a result. `/user/installations`, `/orgs/…`, `/search/…`, and `/repositories/{id}` have no path counterpart. Consult `origin-isms.md` before labeling them. 4. Compare parameters as well as paths. A matching path that lacks a filter - the code depends on is `workaround` or `gap`, not `same`. + the code depends on is a workaround or a gap, not a straight match. 5. Compare the response fields the code reads. Each missing field gets its own line as follow-up call, derivable, or absent. GitHub inlines web URLs, nested profiles, and counts that Origin does not. @@ -86,10 +86,10 @@ event. ## Out of domain and spec-silent -- A concept neither the spec nor `llms-full.txt` mentions is `unknown` - with an up-front question rather than `gap`: there is no Origin answer yet +- A concept neither the spec nor `llms-full.txt` mentions is not available + today and gets a question rather than a gap: there is no Origin answer yet to compare against, and the question is how the team tells Cursor they - need it. Use `not-available` only for `origin-isms.md` rows. + need it. - A behavior the code depends on that the docs do not state (does an event fire for a draft pull request? does `updatedAt` move on a comment?) becomes an open question plus a hello-world step that observes it on a native repository. From e7f7503434b6e7919ae8ce6cf8365a597621ab23 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 26 Sep 2026 18:35:25 +0000 Subject: [PATCH 17/26] Point at per-section reference pages Every docs pointer becomes reference/.md under the Origin docs base URL. origin-api's lookup rule is llms.txt first, then the one page; llms-full.txt and openapi.yaml stay for broad work. The field-map steps fetch the endpoint's or payload family's own page, with rg over llms-full.txt kept as the broad-run fallback. Co-authored-by: ali.nikseresht --- origin-apps/skills/origin-api/SKILL.md | 63 ++++++++++--------- .../skills/port-github-app-to-origin/SKILL.md | 9 +-- .../references/brief-template.md | 8 +-- .../references/origin-isms.md | 42 ++++++------- .../references/spec-mapping.md | 35 ++++++----- 5 files changed, 81 insertions(+), 76 deletions(-) diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md index f45fa68ef..18fdb15d9 100644 --- a/origin-apps/skills/origin-api/SKILL.md +++ b/origin-apps/skills/origin-api/SKILL.md @@ -20,56 +20,57 @@ rules to check first; it does not restate the docs. Do not name an endpoint, scope, event slug, header, or limit from memory. -- `https://cursor.com/docs/api/origin/openapi.yaml`: the contract. Its - `x-origin-*` extensions are summarized under `#endpoint-reference`. -- `https://cursor.com/docs/api/origin/llms-full.txt`: the prose reference. - Anchors below are sections of this file. -- `https://cursor.com/docs/api/origin/llms.txt` (index) and - `https://cursor.com/docs/api/origin/changelog` (what moved). +All paths below are under `https://cursor.com/docs/api/origin/`. -For one question, use `llms.txt` to find the section, then read only that -section. Fetch the whole `llms-full.txt` or `openapi.yaml` when the task -needs broad coverage, such as a porting brief. +- `llms.txt`: the index. Every section, endpoint, and webhook payload has its + own page at `reference/.md`, and the index links to each. +- `openapi.yaml`: the contract. Its `x-origin-*` extensions are summarized in + `reference/endpoint-reference.md`. +- `llms-full.txt`: every reference page in one file. `changelog`: what moved. -Cite `operationId`s and `llms-full.txt` anchors. Where this file and the docs +For one question, read `llms.txt`, then fetch the one `reference/.md` +that answers it. Fetch the whole `llms-full.txt` or `openapi.yaml` only for +broad work, such as a porting brief. + +Cite `operationId`s and reference pages. Where this file and the docs disagree, the docs win. ## Where to look -| Question | Section of `llms-full.txt` | +| Question | Page | | --- | --- | -| Which credential for which call; minting and lifetime | `#authentication` through `#git-https-authentication` | -| Install flow and the callback receipt | `#installation`, `#installation-receipt` | -| Which scope an operation needs | `x-origin-scopes` on the operation; `#scopes` | -| What an installation can do on a mirrored repository | `#mirrored-repositories` | -| Webhook headers, signature, envelope, retries, pausing, recovery | `#webhooks` | -| Which events exist and which arrive without subscribing | `#events` | -| Payload shapes | `#event-payloads` | -| Pagination, errors, request IDs, repository paths | `#common-conventions` | -| ID form and stability | `#ids` | -| What a `PREVIEW` badge means | `#preview` | -| Rate limits | `#rate-limits` | -| Check-run keys, attempts, stale writes | `#check-runs` | -| What is not there yet | `#current-limitations` | -| A checklist to build against | `#implementation-checklist` | +| Which credential for which call; minting and lifetime | `reference/authentication.md` and its subsections through `reference/git-https-authentication.md` | +| Install flow and the callback receipt | `reference/installation.md`, `reference/installation-receipt.md` | +| Which scope an operation needs | `x-origin-scopes` on the operation; `reference/scopes.md` | +| What an installation can do on a mirrored repository | `reference/mirrored-repositories.md` | +| Webhook headers, signature, envelope, retries, pausing, recovery | `reference/webhooks.md` | +| Which events exist and which arrive without subscribing | `reference/events.md` | +| Payload shapes | `reference/event-payloads.md` | +| Pagination, errors, request IDs, repository paths | `reference/common-conventions.md` | +| ID form and stability | `reference/ids.md` | +| What a `PREVIEW` badge means | `reference/preview.md` | +| Rate limits | `reference/rate-limits.md` | +| Check-run keys, attempts, stale writes | `reference/check-runs.md` | +| What is not there yet | `reference/current-limitations.md` | +| A checklist to build against | `reference/implementation-checklist.md` | ## Rules to check first 1. **Native or mirror.** Confirm the target repositories are Origin-native or stable outbound mirrors. On any other mirror state an installation can - only read, and pushes are not delivered (`#mirrored-repositories`, - `#events`). + only read, and pushes are not delivered (`reference/mirrored-repositories.md`, + `reference/events.md`). 2. **Subscribe.** Only the `installation.*` events arrive without a - subscription; a missing subscription is silence, not an error (`#events`). + subscription; a missing subscription is silence, not an error (`reference/events.md`). 3. **Verify, dedupe, acknowledge.** Verify the signature over the raw body before parsing, dedupe on the delivery ID, return `2xx`, then process - (`#signature-verification`, `#retries`, `#automatic-disable`). The digest + (`reference/signature-verification.md`, `reference/retries.md`, `reference/automatic-disable.md`). The digest step differs from the Standard Webhooks spec; do not assume a generic verifier passes. 4. **Scopes from the spec.** Request the union of `x-origin-scopes.scopes` - over the operations the app calls (`#scopes`). + over the operations the app calls (`reference/scopes.md`). 5. **Opaque tokens and IDs.** Do not build or parse page tokens or IDs - (`#pagination`, `#ids`). + (`reference/pagination.md`, `reference/ids.md`). Porting an existing GitHub App: the `port-github-app-to-origin` skill in this plugin covers how its capabilities map onto Origin. diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index f40aabfe2..4478a40d8 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -37,15 +37,16 @@ Adapt the steps to the app. The brief format matters most in its Feedback section. 1. **Load the spec.** A brief needs broad coverage, so fetch the full - `openapi.yaml` and `llms-full.txt`. Record `info.version` and the fetch - time for provenance. Build the index per `references/spec-mapping.md`. + `openapi.yaml` and `llms-full.txt` (see `origin-api` for URLs). Record + `info.version` and the fetch time for provenance. Build the index per + `references/spec-mapping.md`. 2. **Discover** per `references/discovery.md`, including payload fields read only for logging and calls the framework makes on the app's behalf. Note what you looked for and did not find. 3. **Map** each capability (`references/spec-mapping.md`). Map the payload fields the code reads, not only the event names; if a payload lacks a field the REST resource has, a follow-up read is the usual answer - (`#event-payloads`). Check `references/origin-isms.md` before calling + (`reference/event-payloads.md`). Check `references/origin-isms.md` before calling anything a gap, and `references/gap-bar.md` before writing feedback. A capability the Origin docs do not mention is not available today and gets a question. A behavior the docs neither confirm nor deny becomes a @@ -53,7 +54,7 @@ section. carried over from the app's current platform. 4. **Write the brief** per `references/brief-template.md`: guidance and a default outline, not a form. Every Origin claim names an `operationId`, a - slug, or an `llms-full.txt` anchor. When there is feedback, also write the + slug, or a `reference/.md` page. When there is feedback, also write the Feedback section to `ORIGIN-FEEDBACK.md` beside the brief. 5. **Self-check** before finishing: every Origin claim resolves in the fetched files; every gap has a feedback entry that names a tradeoff from diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md index 8061f7235..b8f34debb 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md +++ b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md @@ -14,17 +14,17 @@ headings to fit the app. rest (usually native or mirror), and whether there is feedback for Cursor and if any of it blocks. - **Maps what the app uses to Origin.** Every capability the code relies on, - with the Origin operation, slug, or `llms-full.txt` anchor it maps to, or a + with the Origin operation, slug, or reference page it maps to, or a note that nothing does. Where a webhook handler reads specific payload fields, say per field whether it is present, comes from the envelope, needs a follow-up read (and how many per event), is derivable, or is absent. Group by facet if the table is long. End with the scopes to request: the union of `x-origin-scopes.scopes` over the operations named, minus what - `#scopes` says is automatic or implied. + `reference/scopes.md` says is automatic or implied. - **Gives an app-specific first-run path when it helps.** The events to select by slug, the mirror-state check, the first event that should arrive and what it should carry, the first write. Generic setup steps belong to - `llms-full.txt#implementation-checklist`, not here. Skip for a read-only app + `reference/implementation-checklist.md`, not here. Skip for a read-only app with one event. - **Adds plan notes.** What drives the size of the port (a few bullets, no time estimates) and how to roll it out: dual-run or cutover, what a mirror @@ -45,7 +45,7 @@ headings to fit the app. - Every claim about the app cites evidence: `file:line`, or "from `` (documented behavior)". Team-facing sections only. - Every claim about Origin resolves in the fetched `openapi.yaml` or - `llms-full.txt`: an `operationId`, a slug, or an anchor. Nothing from + `llms-full.txt`: an `operationId`, a slug, or a reference page. Nothing from memory. - Behavior the docs do not state is a question plus a first-run step that observes it, never an assumption. diff --git a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md index 90304a1fb..8acec6d4a 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md +++ b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md @@ -2,39 +2,39 @@ Check here before calling anything a gap. Each row says whether a documented Origin path exists (`maps`) or nothing in the current spec covers it -(`not-available`), and where the Origin answer lives (an anchor in -`llms-full.txt` unless noted). A `maps` row is a question only if the team +(`not-available`), and where the Origin answer lives (a page under +`https://cursor.com/docs/api/origin/` unless noted). A `maps` row is a question only if the team wants the old shape back. A `not-available` row is a question, and feedback if it meets the bar in `gap-bar.md`. Read the source; do not copy this table into the brief. | What the app uses today | Mark | Where the Origin answer lives | | --- | --- | --- | -| Writes or `push` events on a repository mirrored from GitHub | `maps` | Read-only until the mirror becomes a stable outbound mirror (`#mirrored-repositories`); pushes are not delivered for GitHub-sourced mirrors (`#events`). Transitioning is a user-credential operation. First question of every brief. | -| Install callback query parameters (`installation_id`, `setup_action`) | `maps` | `#installation-receipt` | -| RS256 app JWT | `maps` | `#app-jwt` | -| Long-lived installation tokens | `maps` | `#installation-access-token` | -| User OAuth, `/user`, `/user/installations`, install-by-user picker | `not-available` | No user-credential flow for apps in the current spec. Repository discovery is through the installation; namespace-wide listing is under `#current-limitations`. Ask what the flow should do. | -| Permissions `: read\|write` | `maps` | `#scopes`; `x-origin-scopes` per operation | -| Numeric IDs, `/repositories/{id}` | `maps` | `#ids`, `#repository-paths` | -| `Link` / `page` / `per_page` pagination, total counts | `maps` | `#pagination` | +| Writes or `push` events on a repository mirrored from GitHub | `maps` | Read-only until the mirror becomes a stable outbound mirror (`reference/mirrored-repositories.md`); pushes are not delivered for GitHub-sourced mirrors (`reference/events.md`). Transitioning is a user-credential operation. First question of every brief. | +| Install callback query parameters (`installation_id`, `setup_action`) | `maps` | `reference/installation-receipt.md` | +| RS256 app JWT | `maps` | `reference/app-jwt.md` | +| Long-lived installation tokens | `maps` | `reference/installation-access-token.md` | +| User OAuth, `/user`, `/user/installations`, install-by-user picker | `not-available` | No user-credential flow for apps in the current spec. Repository discovery is through the installation; namespace-wide listing is under `reference/current-limitations.md`. Ask what the flow should do. | +| Permissions `: read\|write` | `maps` | `reference/scopes.md`; `x-origin-scopes` per operation | +| Numeric IDs, `/repositories/{id}` | `maps` | `reference/ids.md`, `reference/repository-paths.md` | +| `Link` / `page` / `per_page` pagination, total counts | `maps` | `reference/pagination.md` | | GraphQL | `not-available` | No GraphQL endpoint in the current spec. Decompose into REST calls and count the fan-out; a decomposition that meets the feedback bar earns an entry about that read. | -| Commit statuses (`statuses` permission, `POST /statuses/{sha}`) | `maps` | `#check-runs` (check runs with a stable `key`) | +| Commit statuses (`statuses` permission, `POST /statuses/{sha}`) | `maps` | `reference/check-runs.md` (check runs with a stable `key`) | | Issues (`issues` permission, `issues.*` events, `/issues/{n}` not on a pull request) | `not-available` | No Issues endpoints or events in the current spec. Pull request comments, threads, reviews, and labels cover the pull-request half. Ask what the team needs for the rest; an issue-driven app may earn a feedback entry. | | `/issues/{n}/comments`, `/issues/{n}/labels` used on a pull request | `maps` | Pull requests endpoint reference; same calls under `/pulls/{n}/…` | -| Repository webhook CRUD (`/repos/…/hooks`) | `maps` | Subscriptions are set per app through Create App / Update App `events` (`#events`). | -| App-manifest conversion | `maps` | App creation form or `CreateApp` (`#installation`, endpoint reference) | +| Repository webhook CRUD (`/repos/…/hooks`) | `maps` | Subscriptions are set per app through Create App / Update App `events` (`reference/events.md`). | +| App-manifest conversion | `maps` | App creation form or `CreateApp` (`reference/installation.md`, endpoint reference) | | OAuth-app token mints | `not-available` | Nothing in the current spec. Ask what the flow was for. | -| Git Data API commit and ref writes | `maps` | Create Commit From Files, Create Git Ref (Git data endpoint reference); `#git-https-authentication` for pushes | +| Git Data API commit and ref writes | `maps` | Create Commit From Files, Create Git Ref (Git data endpoint reference); `reference/git-https-authentication.md` for pushes | | Git Data API arbitrary blob or tree writes | `not-available` | Not in the current spec. Ask whether commit-from-files or a push covers the use. | -| Standalone review-thread objects | `maps` | A thread comes from its first diff-anchored comment (Pull requests endpoint reference); thread listing is under `#current-limitations`. | -| User, email, team, and member lookups | `not-available` | No directory reads in the current spec. Reviewer identifiers resolve by public id, user email, or group slug; `handle` is present when the profile is public (`#resource-references`). | -| Single `pull_request` event with an `action` field, `previous_attributes` | `maps` | `#events`, `#event-payloads` | -| `x-github-*` headers, HMAC `x-hub-signature-256` | `maps` | `#headers`, `#signature-verification` | -| Payload inlines (changed files on push, before-SHA, `html_url`, `sender` profile) | `maps` | `#resource-references`; the push commit list is under `#current-limitations` and may change. Name the follow-up call per field and count the fan-out. | -| All events delivered after app creation | `maps` | `#events` | +| Standalone review-thread objects | `maps` | A thread comes from its first diff-anchored comment (Pull requests endpoint reference); thread listing is under `reference/current-limitations.md`. | +| User, email, team, and member lookups | `not-available` | No directory reads in the current spec. Reviewer identifiers resolve by public id, user email, or group slug; `handle` is present when the profile is public (`reference/resource-references.md`). | +| Single `pull_request` event with an `action` field, `previous_attributes` | `maps` | `reference/events.md`, `reference/event-payloads.md` | +| `x-github-*` headers, HMAC `x-hub-signature-256` | `maps` | `reference/headers.md`, `reference/signature-verification.md` | +| Payload inlines (changed files on push, before-SHA, `html_url`, `sender` profile) | `maps` | `reference/resource-references.md`; the push commit list is under `reference/current-limitations.md` and may change. Name the follow-up call per field and count the fan-out. | +| All events delivered after app creation | `maps` | `reference/events.md` | | Reviews keyed by `commit_id` | `maps` | `pullRequestVersion` on the review schema | -| Finding own check runs or comments by actor | `maps` | `#check-runs` (`key`); comments and reviews by a marker the app controls | +| Finding own check runs or comments by actor | `maps` | `reference/check-runs.md` (`key`); comments and reviews by a marker the app controls | | Requested-reviewer team pages, `created_via` | `not-available` | Groups exist and resolve by slug; there is no group membership read in the current spec. | A capability that is not on this list and that the Origin docs do not diff --git a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md index 54c456345..fb728aea6 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md +++ b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md @@ -1,5 +1,7 @@ # Reading the Origin spec and matching GitHub calls and events to it +Paths like `reference/.md` are under `https://cursor.com/docs/api/origin/`. + Every mapping in the brief comes from the fetched `openapi.yaml`, not from a table here. Build the index once. Every later step looks things up in it. @@ -7,9 +9,9 @@ table here. Build the index once. Every later step looks things up in it. | Extension | Where | Use | | --- | --- | --- | -| `x-origin-scopes` | every operation | The scope and credential rules for that operation. `llms-full.txt#scopes`, `#endpoint-reference`. | -| `x-origin-webhook-events` | payload schemas | The slugs that deliver this payload shape; a schema carrying it is a webhook family. `#event-payloads`. Infer the embedded resource from its `$ref`s; some families have no REST twin. | -| `x-cursor-visibility: PREVIEW` | operations, parameters, schemas, fields | `#preview`. Note it in the brief on any capability that touches a badged element. | +| `x-origin-scopes` | every operation | The scope and credential rules for that operation. `reference/scopes.md`, `reference/endpoint-reference.md`. | +| `x-origin-webhook-events` | payload schemas | The slugs that deliver this payload shape; a schema carrying it is a webhook family. `reference/event-payloads.md`. Infer the embedded resource from its `$ref`s; some families have no REST twin. | +| `x-cursor-visibility: PREVIEW` | operations, parameters, schemas, fields | `reference/preview.md`. Note it in the brief on any capability that touches a badged element. | ## Build the index @@ -17,19 +19,20 @@ table here. Build the index once. Every later step looks things up in it. prints every `operationId` with its scope block. From it, note the union of scopes with the operations that need each, and separate installation-requestable scopes from ambient and user-only ones - (`llms-full.txt#scopes` explains the difference). The user-only set tells + (`reference/scopes.md` explains the difference). The user-only set tells you which GitHub flows have no app-side equivalent. Read parameters and response components from the spec when a rule below asks for them. 2. **Webhook events**: `rg -A3 'x-origin-webhook-events:' openapi.yaml` lists - every slug with its payload schema. Each family's fields, with nested - objects expanded to dotted paths, are under its heading in - `llms-full.txt` (`rg -n '^### Pull Request Events$' llms-full.txt`, then - read to the next `###`). From `#events`, note which slugs are delivered + every slug with its payload schema. Each family has its own page with the + fields expanded to dotted paths (`reference/pull-request-events.md`; find + the page in `llms.txt`). In a broad run with `llms-full.txt` already + fetched, `rg -n '^### Pull Request Events$' llms-full.txt` and read to the + next `###`. From `reference/events.md`, note which slugs are delivered without a subscription and which must be selected. -3. **Resources**: each endpoint's "Response Fields" in `llms-full.txt` - (`rg -n '^### Get Pull Request$' llms-full.txt`) lists the fields the - object carries, nested objects expanded, for "does the Origin object carry - this field". +3. **Resources**: each endpoint's page lists its "Response Fields" with nested + objects expanded (`reference/get-pull-request.md`), for "does the Origin + object carry this field". Fallback in a broad run: + `rg -n '^### Get Pull Request$' llms-full.txt`. ## Matching @@ -41,7 +44,7 @@ match is a candidate, not a result. **REST calls** 1. Look for the same resource path under the Origin base path - (`llms-full.txt#repository-paths` and `#ids` give the path forms). Most GitHub + (`reference/repository-paths.md` and `reference/ids.md` give the path forms). Most GitHub repository, pull request, check, label, branch, and commit paths have a direct or near-direct counterpart. 2. Re-home GitHub's issue-flavored pull request calls (`/issues/{n}/comments`, @@ -70,12 +73,12 @@ the operations the code calls and take the union of *their* `workflows`, `deployments`) go through `origin-isms.md` first. **Events → slugs.** Each GitHub `event` + `action` pair maps to at most one -slug in `llms-full.txt#events`; the action is part of the slug. A pair with +slug in `reference/events.md`; the action is part of the slug. A pair with no slug is not an event on Origin. Check whether the state change is observable another way before classifying it. **Payload fields → schema properties.** For each field path a handler reads, -walk the mapped family's "Payload Fields" list in `llms-full.txt` and record +walk the mapped family's "Payload Fields" list on its reference page and record one of five outcomes, matching the brief template's "How" column. Present at ``. Present in the envelope (`event.type` carries what GitHub puts in `action`). Follow-up read via `` with identifiers @@ -86,7 +89,7 @@ event. ## Out of domain and spec-silent -- A concept neither the spec nor `llms-full.txt` mentions is not available +- A concept neither the spec nor the reference pages mention is not available today and gets a question rather than a gap: there is no Origin answer yet to compare against, and the question is how the team tells Cursor they need it. From f18177954a8f51b5accff72831e2eb78685c3ad2 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 28 Sep 2026 23:04:07 +0000 Subject: [PATCH 18/26] Name docs sections by heading instead of hardcoding page URLs Pointers become quoted section headings resolved through llms.txt at run time, so the skill does not depend on a docs deploy order and survives renamed anchors. The base URL stays for llms.txt, llms-full.txt, and openapi.yaml. Co-authored-by: ali.nikseresht --- origin-apps/skills/origin-api/SKILL.md | 61 ++++++++++--------- .../skills/port-github-app-to-origin/SKILL.md | 4 +- .../references/brief-template.md | 8 +-- .../references/origin-isms.md | 42 ++++++------- .../references/spec-mapping.md | 36 +++++------ 5 files changed, 77 insertions(+), 74 deletions(-) diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md index 18fdb15d9..5a336b65e 100644 --- a/origin-apps/skills/origin-api/SKILL.md +++ b/origin-apps/skills/origin-api/SKILL.md @@ -20,57 +20,58 @@ rules to check first; it does not restate the docs. Do not name an endpoint, scope, event slug, header, or limit from memory. -All paths below are under `https://cursor.com/docs/api/origin/`. +The docs live under `https://cursor.com/docs/api/origin/`: -- `llms.txt`: the index. Every section, endpoint, and webhook payload has its - own page at `reference/.md`, and the index links to each. +- `llms.txt`: the index. It links every section, endpoint, and webhook + payload page. Quoted names below ("Scopes", "Events") are section headings; + find the current link for one in `llms.txt`. - `openapi.yaml`: the contract. Its `x-origin-*` extensions are summarized in - `reference/endpoint-reference.md`. -- `llms-full.txt`: every reference page in one file. `changelog`: what moved. + "Endpoint reference". +- `llms-full.txt`: the whole reference in one file. `changelog`: what moved. -For one question, read `llms.txt`, then fetch the one `reference/.md` -that answers it. Fetch the whole `llms-full.txt` or `openapi.yaml` only for -broad work, such as a porting brief. +For one question, read `llms.txt`, then fetch only the section that answers +it. Fetch the whole `llms-full.txt` or `openapi.yaml` for broad work, such as +a porting brief. -Cite `operationId`s and reference pages. Where this file and the docs +Cite `operationId`s and section names. Where this file and the docs disagree, the docs win. ## Where to look -| Question | Page | +| Question | Section | | --- | --- | -| Which credential for which call; minting and lifetime | `reference/authentication.md` and its subsections through `reference/git-https-authentication.md` | -| Install flow and the callback receipt | `reference/installation.md`, `reference/installation-receipt.md` | -| Which scope an operation needs | `x-origin-scopes` on the operation; `reference/scopes.md` | -| What an installation can do on a mirrored repository | `reference/mirrored-repositories.md` | -| Webhook headers, signature, envelope, retries, pausing, recovery | `reference/webhooks.md` | -| Which events exist and which arrive without subscribing | `reference/events.md` | -| Payload shapes | `reference/event-payloads.md` | -| Pagination, errors, request IDs, repository paths | `reference/common-conventions.md` | -| ID form and stability | `reference/ids.md` | -| What a `PREVIEW` badge means | `reference/preview.md` | -| Rate limits | `reference/rate-limits.md` | -| Check-run keys, attempts, stale writes | `reference/check-runs.md` | -| What is not there yet | `reference/current-limitations.md` | -| A checklist to build against | `reference/implementation-checklist.md` | +| Which credential for which call; minting and lifetime | "Authentication" and its subsections | +| Install flow and the callback receipt | "Installation", "Installation receipt" | +| Which scope an operation needs | `x-origin-scopes` on the operation; "Scopes" | +| What an installation can do on a mirrored repository | "Mirrored repositories" | +| Webhook headers, signature, envelope, retries, pausing, recovery | "Webhooks" | +| Which events exist and which arrive without subscribing | "Events" | +| Payload shapes | "Event payloads" | +| Pagination, errors, request IDs, repository paths | "Common conventions" | +| ID form and stability | "IDs" | +| What a `PREVIEW` badge means | "Preview" | +| Rate limits | "Rate limits" | +| Check-run keys, attempts, stale writes | "Check runs" | +| What is not there yet | "Current limitations" | +| A checklist to build against | "Implementation checklist" | ## Rules to check first 1. **Native or mirror.** Confirm the target repositories are Origin-native or stable outbound mirrors. On any other mirror state an installation can - only read, and pushes are not delivered (`reference/mirrored-repositories.md`, - `reference/events.md`). + only read, and pushes are not delivered ("Mirrored repositories", + "Events"). 2. **Subscribe.** Only the `installation.*` events arrive without a - subscription; a missing subscription is silence, not an error (`reference/events.md`). + subscription; a missing subscription is silence, not an error ("Events"). 3. **Verify, dedupe, acknowledge.** Verify the signature over the raw body before parsing, dedupe on the delivery ID, return `2xx`, then process - (`reference/signature-verification.md`, `reference/retries.md`, `reference/automatic-disable.md`). The digest + ("Signature verification", "Retries", "Automatic disable"). The digest step differs from the Standard Webhooks spec; do not assume a generic verifier passes. 4. **Scopes from the spec.** Request the union of `x-origin-scopes.scopes` - over the operations the app calls (`reference/scopes.md`). + over the operations the app calls ("Scopes"). 5. **Opaque tokens and IDs.** Do not build or parse page tokens or IDs - (`reference/pagination.md`, `reference/ids.md`). + ("Pagination", "IDs"). Porting an existing GitHub App: the `port-github-app-to-origin` skill in this plugin covers how its capabilities map onto Origin. diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index 4478a40d8..1d7085745 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -46,7 +46,7 @@ section. 3. **Map** each capability (`references/spec-mapping.md`). Map the payload fields the code reads, not only the event names; if a payload lacks a field the REST resource has, a follow-up read is the usual answer - (`reference/event-payloads.md`). Check `references/origin-isms.md` before calling + ("Event payloads"). Check `references/origin-isms.md` before calling anything a gap, and `references/gap-bar.md` before writing feedback. A capability the Origin docs do not mention is not available today and gets a question. A behavior the docs neither confirm nor deny becomes a @@ -54,7 +54,7 @@ section. carried over from the app's current platform. 4. **Write the brief** per `references/brief-template.md`: guidance and a default outline, not a form. Every Origin claim names an `operationId`, a - slug, or a `reference/.md` page. When there is feedback, also write the + slug, or a docs section. When there is feedback, also write the Feedback section to `ORIGIN-FEEDBACK.md` beside the brief. 5. **Self-check** before finishing: every Origin claim resolves in the fetched files; every gap has a feedback entry that names a tradeoff from diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md index b8f34debb..43c0e157f 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md +++ b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md @@ -14,17 +14,17 @@ headings to fit the app. rest (usually native or mirror), and whether there is feedback for Cursor and if any of it blocks. - **Maps what the app uses to Origin.** Every capability the code relies on, - with the Origin operation, slug, or reference page it maps to, or a + with the Origin operation, slug, or docs section it maps to, or a note that nothing does. Where a webhook handler reads specific payload fields, say per field whether it is present, comes from the envelope, needs a follow-up read (and how many per event), is derivable, or is absent. Group by facet if the table is long. End with the scopes to request: the union of `x-origin-scopes.scopes` over the operations named, minus what - `reference/scopes.md` says is automatic or implied. + "Scopes" says is automatic or implied. - **Gives an app-specific first-run path when it helps.** The events to select by slug, the mirror-state check, the first event that should arrive and what it should carry, the first write. Generic setup steps belong to - `reference/implementation-checklist.md`, not here. Skip for a read-only app + "Implementation checklist", not here. Skip for a read-only app with one event. - **Adds plan notes.** What drives the size of the port (a few bullets, no time estimates) and how to roll it out: dual-run or cutover, what a mirror @@ -45,7 +45,7 @@ headings to fit the app. - Every claim about the app cites evidence: `file:line`, or "from `` (documented behavior)". Team-facing sections only. - Every claim about Origin resolves in the fetched `openapi.yaml` or - `llms-full.txt`: an `operationId`, a slug, or a reference page. Nothing from + `llms-full.txt`: an `operationId`, a slug, or a section. Nothing from memory. - Behavior the docs do not state is a question plus a first-run step that observes it, never an assumption. diff --git a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md index 8acec6d4a..160ddd10a 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md +++ b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md @@ -2,39 +2,39 @@ Check here before calling anything a gap. Each row says whether a documented Origin path exists (`maps`) or nothing in the current spec covers it -(`not-available`), and where the Origin answer lives (a page under -`https://cursor.com/docs/api/origin/` unless noted). A `maps` row is a question only if the team +(`not-available`), and where the Origin answer lives (a section heading in +the Origin docs unless noted; find its link in `llms.txt`). A `maps` row is a question only if the team wants the old shape back. A `not-available` row is a question, and feedback if it meets the bar in `gap-bar.md`. Read the source; do not copy this table into the brief. | What the app uses today | Mark | Where the Origin answer lives | | --- | --- | --- | -| Writes or `push` events on a repository mirrored from GitHub | `maps` | Read-only until the mirror becomes a stable outbound mirror (`reference/mirrored-repositories.md`); pushes are not delivered for GitHub-sourced mirrors (`reference/events.md`). Transitioning is a user-credential operation. First question of every brief. | -| Install callback query parameters (`installation_id`, `setup_action`) | `maps` | `reference/installation-receipt.md` | -| RS256 app JWT | `maps` | `reference/app-jwt.md` | -| Long-lived installation tokens | `maps` | `reference/installation-access-token.md` | -| User OAuth, `/user`, `/user/installations`, install-by-user picker | `not-available` | No user-credential flow for apps in the current spec. Repository discovery is through the installation; namespace-wide listing is under `reference/current-limitations.md`. Ask what the flow should do. | -| Permissions `: read\|write` | `maps` | `reference/scopes.md`; `x-origin-scopes` per operation | -| Numeric IDs, `/repositories/{id}` | `maps` | `reference/ids.md`, `reference/repository-paths.md` | -| `Link` / `page` / `per_page` pagination, total counts | `maps` | `reference/pagination.md` | +| Writes or `push` events on a repository mirrored from GitHub | `maps` | Read-only until the mirror becomes a stable outbound mirror ("Mirrored repositories"); pushes are not delivered for GitHub-sourced mirrors ("Events"). Transitioning is a user-credential operation. First question of every brief. | +| Install callback query parameters (`installation_id`, `setup_action`) | `maps` | "Installation receipt" | +| RS256 app JWT | `maps` | "App JWT" | +| Long-lived installation tokens | `maps` | "Installation access token" | +| User OAuth, `/user`, `/user/installations`, install-by-user picker | `not-available` | No user-credential flow for apps in the current spec. Repository discovery is through the installation; namespace-wide listing is under "Current limitations". Ask what the flow should do. | +| Permissions `: read\|write` | `maps` | "Scopes"; `x-origin-scopes` per operation | +| Numeric IDs, `/repositories/{id}` | `maps` | "IDs", "Repository paths" | +| `Link` / `page` / `per_page` pagination, total counts | `maps` | "Pagination" | | GraphQL | `not-available` | No GraphQL endpoint in the current spec. Decompose into REST calls and count the fan-out; a decomposition that meets the feedback bar earns an entry about that read. | -| Commit statuses (`statuses` permission, `POST /statuses/{sha}`) | `maps` | `reference/check-runs.md` (check runs with a stable `key`) | +| Commit statuses (`statuses` permission, `POST /statuses/{sha}`) | `maps` | "Check runs" (check runs with a stable `key`) | | Issues (`issues` permission, `issues.*` events, `/issues/{n}` not on a pull request) | `not-available` | No Issues endpoints or events in the current spec. Pull request comments, threads, reviews, and labels cover the pull-request half. Ask what the team needs for the rest; an issue-driven app may earn a feedback entry. | | `/issues/{n}/comments`, `/issues/{n}/labels` used on a pull request | `maps` | Pull requests endpoint reference; same calls under `/pulls/{n}/…` | -| Repository webhook CRUD (`/repos/…/hooks`) | `maps` | Subscriptions are set per app through Create App / Update App `events` (`reference/events.md`). | -| App-manifest conversion | `maps` | App creation form or `CreateApp` (`reference/installation.md`, endpoint reference) | +| Repository webhook CRUD (`/repos/…/hooks`) | `maps` | Subscriptions are set per app through Create App / Update App `events` ("Events"). | +| App-manifest conversion | `maps` | App creation form or `CreateApp` ("Installation", endpoint reference) | | OAuth-app token mints | `not-available` | Nothing in the current spec. Ask what the flow was for. | -| Git Data API commit and ref writes | `maps` | Create Commit From Files, Create Git Ref (Git data endpoint reference); `reference/git-https-authentication.md` for pushes | +| Git Data API commit and ref writes | `maps` | Create Commit From Files, Create Git Ref (Git data endpoint reference); "Git HTTPS authentication" for pushes | | Git Data API arbitrary blob or tree writes | `not-available` | Not in the current spec. Ask whether commit-from-files or a push covers the use. | -| Standalone review-thread objects | `maps` | A thread comes from its first diff-anchored comment (Pull requests endpoint reference); thread listing is under `reference/current-limitations.md`. | -| User, email, team, and member lookups | `not-available` | No directory reads in the current spec. Reviewer identifiers resolve by public id, user email, or group slug; `handle` is present when the profile is public (`reference/resource-references.md`). | -| Single `pull_request` event with an `action` field, `previous_attributes` | `maps` | `reference/events.md`, `reference/event-payloads.md` | -| `x-github-*` headers, HMAC `x-hub-signature-256` | `maps` | `reference/headers.md`, `reference/signature-verification.md` | -| Payload inlines (changed files on push, before-SHA, `html_url`, `sender` profile) | `maps` | `reference/resource-references.md`; the push commit list is under `reference/current-limitations.md` and may change. Name the follow-up call per field and count the fan-out. | -| All events delivered after app creation | `maps` | `reference/events.md` | +| Standalone review-thread objects | `maps` | A thread comes from its first diff-anchored comment (Pull requests endpoint reference); thread listing is under "Current limitations". | +| User, email, team, and member lookups | `not-available` | No directory reads in the current spec. Reviewer identifiers resolve by public id, user email, or group slug; `handle` is present when the profile is public ("Resource references"). | +| Single `pull_request` event with an `action` field, `previous_attributes` | `maps` | "Events", "Event payloads" | +| `x-github-*` headers, HMAC `x-hub-signature-256` | `maps` | "Headers", "Signature verification" | +| Payload inlines (changed files on push, before-SHA, `html_url`, `sender` profile) | `maps` | "Resource references"; the push commit list is under "Current limitations" and may change. Name the follow-up call per field and count the fan-out. | +| All events delivered after app creation | `maps` | "Events" | | Reviews keyed by `commit_id` | `maps` | `pullRequestVersion` on the review schema | -| Finding own check runs or comments by actor | `maps` | `reference/check-runs.md` (`key`); comments and reviews by a marker the app controls | +| Finding own check runs or comments by actor | `maps` | "Check runs" (`key`); comments and reviews by a marker the app controls | | Requested-reviewer team pages, `created_via` | `not-available` | Groups exist and resolve by slug; there is no group membership read in the current spec. | A capability that is not on this list and that the Origin docs do not diff --git a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md index fb728aea6..9683511bb 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md +++ b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md @@ -1,6 +1,7 @@ # Reading the Origin spec and matching GitHub calls and events to it -Paths like `reference/.md` are under `https://cursor.com/docs/api/origin/`. +Quoted names ("Scopes", "Events") are section headings in the Origin docs; +find each one's link in `llms.txt`. Every mapping in the brief comes from the fetched `openapi.yaml`, not from a table here. Build the index once. Every later step looks things up in it. @@ -9,9 +10,9 @@ table here. Build the index once. Every later step looks things up in it. | Extension | Where | Use | | --- | --- | --- | -| `x-origin-scopes` | every operation | The scope and credential rules for that operation. `reference/scopes.md`, `reference/endpoint-reference.md`. | -| `x-origin-webhook-events` | payload schemas | The slugs that deliver this payload shape; a schema carrying it is a webhook family. `reference/event-payloads.md`. Infer the embedded resource from its `$ref`s; some families have no REST twin. | -| `x-cursor-visibility: PREVIEW` | operations, parameters, schemas, fields | `reference/preview.md`. Note it in the brief on any capability that touches a badged element. | +| `x-origin-scopes` | every operation | The scope and credential rules for that operation. "Scopes", "Endpoint reference". | +| `x-origin-webhook-events` | payload schemas | The slugs that deliver this payload shape; a schema carrying it is a webhook family. "Event payloads". Infer the embedded resource from its `$ref`s; some families have no REST twin. | +| `x-cursor-visibility: PREVIEW` | operations, parameters, schemas, fields | "Preview". Note it in the brief on any capability that touches a badged element. | ## Build the index @@ -19,19 +20,20 @@ table here. Build the index once. Every later step looks things up in it. prints every `operationId` with its scope block. From it, note the union of scopes with the operations that need each, and separate installation-requestable scopes from ambient and user-only ones - (`reference/scopes.md` explains the difference). The user-only set tells + ("Scopes" explains the difference). The user-only set tells you which GitHub flows have no app-side equivalent. Read parameters and response components from the spec when a rule below asks for them. 2. **Webhook events**: `rg -A3 'x-origin-webhook-events:' openapi.yaml` lists - every slug with its payload schema. Each family has its own page with the - fields expanded to dotted paths (`reference/pull-request-events.md`; find - the page in `llms.txt`). In a broad run with `llms-full.txt` already - fetched, `rg -n '^### Pull Request Events$' llms-full.txt` and read to the - next `###`. From `reference/events.md`, note which slugs are delivered + every slug with its payload schema. Each family has its own section, with + the fields expanded to dotted paths ("Pull Request Events" and its + siblings under "Event payloads"; find them in `llms.txt`). In a broad run + with `llms-full.txt` already fetched, `rg -n '^### Pull Request Events$' + llms-full.txt` and read to the next `###`. From "Events", note which slugs + are delivered without a subscription and which must be selected. -3. **Resources**: each endpoint's page lists its "Response Fields" with nested - objects expanded (`reference/get-pull-request.md`), for "does the Origin - object carry this field". Fallback in a broad run: +3. **Resources**: each endpoint's section lists its "Response Fields" with + nested objects expanded ("Get Pull Request" under "Pull requests"), for + "does the Origin object carry this field". Fallback in a broad run: `rg -n '^### Get Pull Request$' llms-full.txt`. ## Matching @@ -44,7 +46,7 @@ match is a candidate, not a result. **REST calls** 1. Look for the same resource path under the Origin base path - (`reference/repository-paths.md` and `reference/ids.md` give the path forms). Most GitHub + ("Repository paths" and "IDs" give the path forms). Most GitHub repository, pull request, check, label, branch, and commit paths have a direct or near-direct counterpart. 2. Re-home GitHub's issue-flavored pull request calls (`/issues/{n}/comments`, @@ -73,12 +75,12 @@ the operations the code calls and take the union of *their* `workflows`, `deployments`) go through `origin-isms.md` first. **Events → slugs.** Each GitHub `event` + `action` pair maps to at most one -slug in `reference/events.md`; the action is part of the slug. A pair with +slug in "Events"; the action is part of the slug. A pair with no slug is not an event on Origin. Check whether the state change is observable another way before classifying it. **Payload fields → schema properties.** For each field path a handler reads, -walk the mapped family's "Payload Fields" list on its reference page and record +walk the mapped family's "Payload Fields" list in its docs section and record one of five outcomes, matching the brief template's "How" column. Present at ``. Present in the envelope (`event.type` carries what GitHub puts in `action`). Follow-up read via `` with identifiers @@ -89,7 +91,7 @@ event. ## Out of domain and spec-silent -- A concept neither the spec nor the reference pages mention is not available +- A concept neither the spec nor the docs mention is not available today and gets a question rather than a gap: there is no Origin answer yet to compare against, and the question is how the team tells Cursor they need it. From 2b2734db1f311659b9156d20e0fa61512b8e59c8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 28 Sep 2026 23:38:24 +0000 Subject: [PATCH 19/26] Slim the plugin to two skills and one reference; apply eval fixes Folds discovery, spec-mapping, gap-bar, origin-isms, and brief-template into the porting SKILL.md plus one reference (brief, feedback bar, crib); the eval scored this shape level with the longer one. Also from the eval: the mirror rule states facts (merging and default-branch changes are native-only; on a GitHub-sourced mirror every event except repository.pushed arrives and calls beyond metadata and contents reads return 403); brevity is structural (list only what does not map straight across, payload fields only when absent or a follow-up read, a word budget by app size); a three-line filter sits before feedback; the stale user-OAuth item points at "Acting on behalf of users" and names the user-token permission probe as a workaround; ORIGIN-FEEDBACK.md carries no license header, repo or product name, or other forge, and docs contradictions go to Cursor. Co-authored-by: ali.nikseresht --- origin-apps/README.md | 44 ++----- origin-apps/skills/origin-api/SKILL.md | 58 ++++----- .../skills/port-github-app-to-origin/SKILL.md | 117 +++++++++--------- .../references/brief-template.md | 79 ------------ .../references/brief.md | 115 +++++++++++++++++ .../references/discovery.md | 54 -------- .../references/gap-bar.md | 76 ------------ .../references/origin-isms.md | 41 ------ .../references/spec-mapping.md | 101 --------------- 9 files changed, 208 insertions(+), 477 deletions(-) delete mode 100644 origin-apps/skills/port-github-app-to-origin/references/brief-template.md create mode 100644 origin-apps/skills/port-github-app-to-origin/references/brief.md delete mode 100644 origin-apps/skills/port-github-app-to-origin/references/discovery.md delete mode 100644 origin-apps/skills/port-github-app-to-origin/references/gap-bar.md delete mode 100644 origin-apps/skills/port-github-app-to-origin/references/origin-isms.md delete mode 100644 origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md diff --git a/origin-apps/README.md b/origin-apps/README.md index 4b7027c99..3c0c4b68a 100644 --- a/origin-apps/README.md +++ b/origin-apps/README.md @@ -25,18 +25,9 @@ Both skills fetch the spec at run time and never name an endpoint from memory. ## When to use -- Writing or reviewing code that calls Origin, mints installation tokens, or - receives Origin webhooks: `origin-api`. -- Creating an Origin App and wanting the rules to know up front: - `origin-api`. -- Holding a GitHub App (Probot, Octokit, go-github, hand-rolled) and wanting - to know what an Origin App version looks like before starting: - `port-github-app-to-origin`. -- Checking how a capability your app relies on today maps onto Origin: - `port-github-app-to-origin`. - -In Cursor, ask about the Origin API or ask to port the app, or run -`/origin-api` or `/port-github-app-to-origin`. +Ask about the Origin API, or ask to port a GitHub App, and the matching skill +loads. In Cursor you can also run `/origin-api` or +`/port-github-app-to-origin`. ## Install in Cursor @@ -46,30 +37,11 @@ or open Customize, find the plugin, and install it at user or project scope. ## Use outside Cursor -The skills use only the portable -[Agent Skills](https://agentskills.io) frontmatter, so they work unchanged in -other agents. - -Claude Code, via the marketplace manifest at this repository's root: - -```text -/plugin marketplace add cursor/plugins -/plugin install origin-apps@cursor-plugins -``` - -Any agent that reads Agent Skills (Claude Code, Codex, and others): copy the -skill directories into the agent's skills folder. Copy both. The porting -skill refers to `origin-api` for fundamentals. - -```bash -git clone --depth 1 https://github.com/cursor/plugins.git -# Claude Code -mkdir -p .claude/skills && cp -r plugins/origin-apps/skills/* .claude/skills/ -# Codex -mkdir -p .codex/skills && cp -r plugins/origin-apps/skills/* .codex/skills/ -# Cursor, without the marketplace -mkdir -p .cursor/skills && cp -r plugins/origin-apps/skills/* .cursor/skills/ -``` +The skills use only portable [Agent Skills](https://agentskills.io) +frontmatter. In Claude Code: `/plugin marketplace add cursor/plugins` then +`/plugin install origin-apps@cursor-plugins`. Any other agent: copy +`origin-apps/skills/*` into its skills folder (copy both; the porting skill +refers to `origin-api`). ## Requirements diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md index 5a336b65e..e73c478de 100644 --- a/origin-apps/skills/origin-api/SKILL.md +++ b/origin-apps/skills/origin-api/SKILL.md @@ -13,28 +13,17 @@ compatibility: >- # Origin API -The docs are the source of truth. This skill says where to look and which -rules to check first; it does not restate the docs. +The docs are the source of truth. Do not name an endpoint, scope, event slug, +header, or limit from memory. Where this file and the docs disagree, the docs +win. -## Fetch first - -Do not name an endpoint, scope, event slug, header, or limit from memory. - -The docs live under `https://cursor.com/docs/api/origin/`: - -- `llms.txt`: the index. It links every section, endpoint, and webhook - payload page. Quoted names below ("Scopes", "Events") are section headings; - find the current link for one in `llms.txt`. -- `openapi.yaml`: the contract. Its `x-origin-*` extensions are summarized in - "Endpoint reference". -- `llms-full.txt`: the whole reference in one file. `changelog`: what moved. - -For one question, read `llms.txt`, then fetch only the section that answers -it. Fetch the whole `llms-full.txt` or `openapi.yaml` for broad work, such as -a porting brief. - -Cite `operationId`s and section names. Where this file and the docs -disagree, the docs win. +Under `https://cursor.com/docs/api/origin/`: `llms.txt` is the index and +links every section, endpoint, and webhook payload; `openapi.yaml` is the +contract (its `x-origin-*` extensions are summarized in "Endpoint +reference"); `llms-full.txt` is the whole reference in one file; `changelog` +says what moved. For one question, read `llms.txt` and fetch only the section +that answers it. Fetch `llms-full.txt` or `openapi.yaml` whole for broad work +such as a porting brief. Cite `operationId`s and section names. ## Where to look @@ -47,9 +36,7 @@ disagree, the docs win. | Webhook headers, signature, envelope, retries, pausing, recovery | "Webhooks" | | Which events exist and which arrive without subscribing | "Events" | | Payload shapes | "Event payloads" | -| Pagination, errors, request IDs, repository paths | "Common conventions" | -| ID form and stability | "IDs" | -| What a `PREVIEW` badge means | "Preview" | +| Pagination, errors, request IDs, repository paths, IDs | "Common conventions" | | Rate limits | "Rate limits" | | Check-run keys, attempts, stale writes | "Check runs" | | What is not there yet | "Current limitations" | @@ -57,21 +44,24 @@ disagree, the docs win. ## Rules to check first -1. **Native or mirror.** Confirm the target repositories are Origin-native - or stable outbound mirrors. On any other mirror state an installation can - only read, and pushes are not delivered ("Mirrored repositories", - "Events"). -2. **Subscribe.** Only the `installation.*` events arrive without a - subscription; a missing subscription is silence, not an error ("Events"). +1. **Native or mirror.** An installation keeps its full scopes only on + native repositories and stable outbound mirrors, and some writes are + native-only (merging a pull request, changing the default branch); read + each operation's description for mirror limits. On a GitHub-sourced + mirror, every event except `repository.pushed` still arrives, and every + call beyond metadata and contents reads returns `403` ("Mirrored + repositories", "Events"). +2. **Subscribe.** Only `installation.*` events arrive without a subscription; + a missing subscription is silence, not an error ("Events"). 3. **Verify, dedupe, acknowledge.** Verify the signature over the raw body before parsing, dedupe on the delivery ID, return `2xx`, then process ("Signature verification", "Retries", "Automatic disable"). The digest - step differs from the Standard Webhooks spec; do not assume a generic - verifier passes. + step differs from Standard Webhooks; do not assume a generic verifier + passes. 4. **Scopes from the spec.** Request the union of `x-origin-scopes.scopes` over the operations the app calls ("Scopes"). 5. **Opaque tokens and IDs.** Do not build or parse page tokens or IDs ("Pagination", "IDs"). -Porting an existing GitHub App: the `port-github-app-to-origin` skill in -this plugin covers how its capabilities map onto Origin. +Porting an existing GitHub App: use `port-github-app-to-origin` in this +plugin. diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index 1d7085745..b177a46bc 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -13,68 +13,73 @@ compatibility: >- # Port a GitHub App to an Origin App -Run inside the app's codebase. The output is a porting brief -(`references/brief-template.md`): what maps, what changes shape, what is not -available today, and what to tell Cursor. This skill plans; it does not write -or change code unless the user explicitly asks for that after reading the -brief. +Run inside the app's codebase. The output is a porting brief for the team plus +a Feedback for Cursor section they can send as is (`references/brief.md`). +This skill plans; it does not write or change code unless the user explicitly +asks after reading the brief. Follow the `origin-api` skill for the docs and +the rules to check first. Two rules on top: -The `origin-api` skill in this plugin covers the docs and the rules to check -first; follow it. Two rules on top: - -1. **Discover, do not ask.** Read permissions, events, handlers, calls, token - minting, and the receiver out of the code. Anything you cannot find becomes - an open question. +1. **Discover, do not ask.** Read what the app uses out of the code. Anything + you cannot find becomes an open question. 2. **Feedback describes use cases, not the team's code.** Team-facing parts of - the brief may cite files and lines. The Feedback for Cursor section names - only what the app needs to do and what Origin lacks for it, in Origin - terms, with no file paths, module names, framework internals, or - repository names. + the brief may cite `file:line`. Feedback for Cursor names only what the + app needs to do and what Origin lacks for it, in Origin terms, with no file + paths, module names, framework internals, or repository names. + +## What to discover -## Suggested procedure +Record a `file:line` for each, and note what you looked for and did not find. -Adapt the steps to the app. The brief format matters most in its Feedback -section. +- Declared permissions and events (manifest or IaC, if checked in; otherwise + derive from the calls). +- Webhook events handled, and every payload field each handler reads, + including fields used only for logging. +- REST and GraphQL call families, with the parameters and filters passed, the + response fields read, whether each runs per webhook or in a loop, and the + pagination style in use. +- Authentication: JWT algorithm, how the installation is identified after + install, token lifetime handling, any user sign-in and what it is for, + whether the app clones or pushes git. +- Webhook receiver: signature scheme, whether the raw body is available at + verification time, how deliveries are deduplicated. +- Calls a framework or helper library makes on the app's behalf (Probot's + receiver, token cache, and config loader; Octokit `App`'s installation and + repository listing; app-auth libraries). Read the dependency's docs and + list these as rows marked "from ``". -1. **Load the spec.** A brief needs broad coverage, so fetch the full - `openapi.yaml` and `llms-full.txt` (see `origin-api` for URLs). Record - `info.version` and the fetch time for provenance. Build the index per - `references/spec-mapping.md`. -2. **Discover** per `references/discovery.md`, including payload fields read - only for logging and calls the framework makes on the app's behalf. Note - what you looked for and did not find. -3. **Map** each capability (`references/spec-mapping.md`). Map the payload - fields the code reads, not only the event names; if a payload lacks a - field the REST resource has, a follow-up read is the usual answer - ("Event payloads"). Check `references/origin-isms.md` before calling - anything a gap, and `references/gap-bar.md` before writing feedback. A - capability the Origin docs do not mention is not available today and gets - a question. A behavior the docs neither confirm nor deny becomes a - question plus a first-run step that observes it, rather than an assumption - carried over from the app's current platform. -4. **Write the brief** per `references/brief-template.md`: guidance and a - default outline, not a form. Every Origin claim names an `operationId`, a - slug, or a docs section. When there is feedback, also write the - Feedback section to `ORIGIN-FEEDBACK.md` beside the brief. -5. **Self-check** before finishing: every Origin claim resolves in the - fetched files; every gap has a feedback entry that names a tradeoff from - `gap-bar.md`; the Feedback section and `ORIGIN-FEEDBACK.md` contain nothing - that reveals the team's internals; the summary names the native-or-mirror - question. +## How to map -## Not in scope +Fetch `openapi.yaml` and `llms-full.txt` whole; a brief needs broad coverage. +`rg -B1 -A4 'x-origin-scopes:' openapi.yaml` lists every operation with its +scope block; `rg -A3 'x-origin-webhook-events:' openapi.yaml` lists every +event slug with its payload schema. Each endpoint and each payload family has +a docs section with its fields expanded to dotted paths. -Writing or changing code without the user's explicit ask. Choosing a -language, framework, or client. Estimating in time. Sending feedback to -Cursor yourself; the brief carries it and the team sends it. +- A name match is a candidate, not a result. Confirm by reading the + operation's description, parameters, and response fields against what the + code passes and reads. A missing parameter or field the code depends on is + a workaround or a gap, not a match. +- GitHub's issue-flavored pull request calls (`/issues/{n}/comments`, + `/issues/{n}/labels` on a pull request) live under the pull request + endpoints. Used on real issues, see the crib in `references/brief.md`. +- GraphQL has no counterpart; decompose each document into REST calls and + record the fan-out. +- Scopes are the union of `x-origin-scopes.scopes` over the operations you + named, not a translation of the manifest. +- Each event and action pair maps to at most one slug in "Events"; the action + is part of the slug. A pair with no slug is not an event on Origin. +- For each payload field the code reads, record whether it is present, comes + from the envelope (`event.type` carries the action), needs a follow-up read + (say which operation and how many calls per event), is derivable, or is + absent. Payloads are snapshots; a field on the REST resource that the + payload lacks is a follow-up read. +- A capability the docs do not mention is not available today and gets a + question. A behavior the docs neither confirm nor deny gets a question plus + a first-run step that observes it, not an assumption carried over from + GitHub. -## Reference files +## Before finishing -| File | Read when | -| --- | --- | -| `origin-api` skill (install both) | First. Docs pointers and the rules to check. | -| `references/discovery.md` | Scanning the codebase. | -| `references/spec-mapping.md` | Building the index. Matching calls, events, and fields. | -| `references/origin-isms.md` | Labeling a capability that maps differently. | -| `references/gap-bar.md` | Deciding what is feedback for Cursor, and writing the entry. | -| `references/brief-template.md` | Writing the brief and the feedback file. | +Every Origin claim resolves in the fetched files. Every gap has a feedback +entry naming a tradeoff. The feedback contains nothing that reveals the +team's internals. The summary names the native-or-mirror question. diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md b/origin-apps/skills/port-github-app-to-origin/references/brief-template.md deleted file mode 100644 index 43c0e157f..000000000 --- a/origin-apps/skills/port-github-app-to-origin/references/brief-template.md +++ /dev/null @@ -1,79 +0,0 @@ -# The porting brief - -The brief is for the team that owns the app, plus one section they can send -to Cursor as is. Write it as one Markdown file at the repository root -(`ORIGIN-PORTING-BRIEF.md` unless the team's docs convention says otherwise) -and print its path. A small app's brief fits on one screen; parts that have -nothing to say collapse to a line or disappear. Choose table shapes and -headings to fit the app. - -## What a good brief does - -- **Leads with a summary.** Three to five lines: the verdict (ports as is, - ports with N workarounds, blocked on X), the one question that decides the - rest (usually native or mirror), and whether there is feedback for Cursor - and if any of it blocks. -- **Maps what the app uses to Origin.** Every capability the code relies on, - with the Origin operation, slug, or docs section it maps to, or a - note that nothing does. Where a webhook handler reads specific payload - fields, say per field whether it is present, comes from the envelope, needs - a follow-up read (and how many per event), is derivable, or is absent. - Group by facet if the table is long. End with the scopes to request: the - union of `x-origin-scopes.scopes` over the operations named, minus what - "Scopes" says is automatic or implied. -- **Gives an app-specific first-run path when it helps.** The events to - select by slug, the mirror-state check, the first event that should arrive - and what it should carry, the first write. Generic setup steps belong to - "Implementation checklist", not here. Skip for a read-only app - with one event. -- **Adds plan notes.** What drives the size of the port (a few bullets, no - time estimates) and how to roll it out: dual-run or cutover, what a mirror - trial can and cannot show, what to gate. -- **Asks only what the team must decide.** Repository set, tolerable event - volume and follow-up reads, what replaces a flow that has no Origin - equivalent. Do not restate a row as a question. -- **Ends with two lines of provenance.** Spec `info.version` and fetch time; - codebase and commit. -- **Closes with Feedback for Cursor.** Last section, unnumbered, written to be - copied verbatim (`gap-bar.md` has the shape). Nothing in it reveals the - team's internals. When it has at least one entry, also write it to - `ORIGIN-FEEDBACK.md` beside the brief. When nothing meets the bar, no file; - one line in the brief saying so. - -## What makes it trustworthy - -- Every claim about the app cites evidence: `file:line`, or "from - `` (documented behavior)". Team-facing sections only. -- Every claim about Origin resolves in the fetched `openapi.yaml` or - `llms-full.txt`: an `operationId`, a slug, or a section. Nothing from - memory. -- Behavior the docs do not state is a question plus a first-run step that - observes it, never an assumption. -- Feedback for Cursor describes use cases and the API gap in Origin terms, - with no file paths, module names, framework internals, or repository names. - -## Vocabulary, if you want one - -Plain notes serve the reader as well as labels. If the table needs a compact -mark, these four are shared with the other references: `maps` (a documented -path exists, same or reshaped), `workaround` (same outcome by another route; -say the tradeoff), `not-available` (nothing in the current spec; carries a -question), `gap` (no workaround, or one whose tradeoff meets a test in -`gap-bar.md`; produces a feedback entry). Size marks S/M/L, if used, mean -adapter change, new code path, product or architecture change. - -## Default outline - -Adapt or skip parts; the Feedback section is the one to keep exact. - -```text -# Origin porting brief: -Summary -1. The app today one paragraph; observed stack; looked for, not found -2. Capability map table(s); payload fields under webhook events; scopes line -3. First run app-specific, five steps or fewer (optional) -4. Plan notes size drivers; rollout (optional) -5. Questions for the team -Provenance two lines -Feedback for Cursor unnumbered, last, copy verbatim; also ORIGIN-FEEDBACK.md -``` diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief.md b/origin-apps/skills/port-github-app-to-origin/references/brief.md new file mode 100644 index 000000000..ed6bd09a6 --- /dev/null +++ b/origin-apps/skills/port-github-app-to-origin/references/brief.md @@ -0,0 +1,115 @@ +# The brief, the feedback, and the crib + +## The brief + +One Markdown file at the repository root (`ORIGIN-PORTING-BRIEF.md` unless +the team's convention says otherwise); print its path. Budget about 800 +words for a small app and about 2,000 for a large one; parts with nothing to +say collapse or disappear. Choose table shapes to fit the app. A good brief: + +- leads with a summary: the verdict (ports as is, ports with workarounds, + blocked on X), the question that decides the rest (usually native or + mirror), and whether there is feedback for Cursor and if any of it blocks; +- lists only the capabilities that do not map straight across, each with + the Origin operation, slug, or docs section it maps to or a note that + nothing does, and closes the list with one line naming the rest + ("maps directly: 14 operations, covered by the scopes line"); shows a + payload field only when it is absent or needs a follow-up read; ends with + the scopes to request; +- gives an app-specific first-run path when it helps (events to select, the + mirror check, the first event and what it carries, the first write); skips + generic setup, which "Implementation checklist" covers; +- notes what drives the size of the port and how to roll it out (dual-run + or cutover), without time estimates; +- asks only what the team must decide, never restating a row; +- ends with two lines of provenance (spec `info.version` and fetch time; + codebase and commit) and then Feedback for Cursor, last and unnumbered. + +Evidence for every app claim (`file:line`, or "from ``"); every +Origin claim resolved against the fetched docs. If you want compact marks: +`maps` (a documented path exists), `workaround` (say the tradeoff), +`not-available` (nothing in the current spec; carries a question), `gap` +(produces a feedback entry). + +## Feedback for Cursor + +Cursor wants to hear what the team needs. Raise anything that blocks the +team's core flow, costs them correctness, security, or scale, or that they +would like Origin to do. The bar sorts items into feedback (a capability +Origin should add) and questions (decisions the team must make); it does not +decide whether to speak up. + +A workaround reaches the same outcome by another route (a follow-up read, a +re-keyed identifier, a path change, a client-side filter, a marker the app +controls) and is often the right answer. It becomes a gap, and a feedback +entry, when its tradeoff is one of: fan-out that grows with repository or +activity size at the app's volume; a possibly wrong answer (heuristic own-row +matching, a pull request inferred from a SHA several versions share, a URL +whose format is not contractual); a broader scope, longer-lived token, or +user credential where an installation token should do; a change to what the +team's users see or can do; or a capability on the hello-world path or the +team's core flow. A state change the app exists to react to, with no event +and no other way to observe it, meets the bar. Something the docs never +mention is not available today and gets a question; it becomes feedback +only if it blocks the core flow. + +Not feedback, only a question or a note: a field or filter the code does not +use; a convention difference with a mechanical substitute; a documented +design choice such as token lifetime or no GraphQL. + +One entry per gap, in Origin terms, with nothing that reveals the team's +internals. When there is at least one, also write the section to +`ORIGIN-FEEDBACK.md` beside the brief; when there is none, no file and one +line saying so. The file carries no license header, repository name, +product name, or mention of another forge. A contradiction between the docs +and observed behavior goes in a short "Docs questions for Cursor" list at +the end of the feedback, not in the questions for the team. A suggested +shape: + +```markdown +### Feedback: +- **Use case:** the app needs to , . +- **Origin today:** . +- **Workaround considered:** . +- **Blocking?** yes / no, for which flow. +- **Spec version checked:** , . +``` + +Describe the capability rather than proposing scope, field, or route names. +The team sends the feedback, not you; before they do, they strip anything +that reveals their internals. + +## Crib: where GitHub habits land on Origin + +Check before calling anything a gap; confirm each in the fetched docs. + +Documented path exists: install callback params → "Installation receipt"; +RS256 app JWT → "App JWT"; long-lived installation tokens → "Installation +access token"; permissions → "Scopes" and `x-origin-scopes`; numeric IDs and +`/repositories/{id}` → "IDs", "Repository paths"; `Link` pagination and +totals → "Pagination"; commit statuses → check runs with a stable `key` +("Check runs"); `/issues/{n}/comments` and `/issues/{n}/labels` on a pull +request → the pull request endpoints; repository webhook CRUD → per-app +`events` on Create App / Update App; a single `pull_request` event with an +`action` field → one slug per action ("Events"); `x-github-*` headers and +HMAC → "Headers", "Signature verification"; inlined payload data (changed +files, before-SHA, URLs, profiles) → follow-up reads ("Resource references", +"Current limitations"); reviews keyed by commit SHA → `pullRequestVersion`; +finding own rows by actor → check-run `key`, or a marker the app controls; +writes on a repository mirrored from GitHub → metadata and contents reads +only, until it is a stable outbound mirror, and merging and default-branch +changes stay native-only ("Mirrored repositories"); user sign-in and acting +as a user → "Acting on behalf of users" (user confirmation receipt, +installation user tokens). + +Not available in the current spec (question, and feedback if it blocks the +core flow): GraphQL (decompose); Issues (pull request comments, threads, +reviews, and labels cover the pull-request half); OAuth-app token mints; +arbitrary blob or tree writes (commit-from-files and pushes exist); user, +email, team, and member directory reads (reviewer identifiers resolve by +public id, email, or group slug); group membership and effective-permission +reads. For the last two, the workaround to name is a user token capped to a +repository and scopes: minting it returns `403` unless the user holds that +permission, so it doubles as a permission probe. + +Crib rows go stale; the fetched docs win. diff --git a/origin-apps/skills/port-github-app-to-origin/references/discovery.md b/origin-apps/skills/port-github-app-to-origin/references/discovery.md deleted file mode 100644 index f9c86ab01..000000000 --- a/origin-apps/skills/port-github-app-to-origin/references/discovery.md +++ /dev/null @@ -1,54 +0,0 @@ -# Discovering what the GitHub App uses, from its codebase - -Everything the brief needs about the app is in the repository. Search for it. -Do not ask for it. Record `file:line` for every fact and list every place you -looked that turned up nothing. Note the language and libraries as an -observation. They inform sizes and nothing else. - -Seven facets. For each, what to record: - -1. **Declared permissions and events.** The manifest or registration snapshot - if it is checked in (`app.yml`, a manifest JSON, IaC that seeds the app; - Probot keeps `default_events` and `default_permissions` in `app.yml`). If - there is none, say so and derive permissions from facet 4. The union of - what the code calls is what the port needs anyway. -2. **Webhook events handled.** Each GitHub event and action pair the code - dispatches on (`app.on("pull_request.opened")`, a switch on - `x-github-event` plus `payload.action`, SDK parsers), with the handler - location. -3. **Payload fields read.** Every property path each handler and its helpers - dereference from the payload. Include fields used only for logging or - metrics. Those break dashboards after the port. -4. **REST and GraphQL calls.** Each distinct call family once (method and path, - or SDK method) with the parameters and filters the code passes, the - response fields it reads, whether it runs per webhook or in a loop (this - decides the fan-out tradeoff), and the pagination style in use. Pagination - always changes. GraphQL documents count as calls. Mapping decomposes them. -5. **Authentication and token minting.** The app JWT algorithm. How the code - identifies the installation after install (callback query, webhook, DB). - Token lifetime handling. Whether user OAuth exists and what it is for - (identity, repository discovery, acting for a user). Whether the app clones - or pushes git as itself. -6. **Webhook receiver and verification.** The signature scheme. Whether the - raw body is available at verification time (a framework that parses JSON - first cannot verify). How the code deduplicates deliveries, if it does. - Where the public URL is configured. -7. **Calls the framework makes on the app's behalf.** They are not in the - app's source, but the port has to make them. List them as rows marked - "from `` (documented behavior)" and read the dependency's docs - or source, not the app. Common cases: - - Probot: the built-in receiver and HMAC verification, per-installation - token minting and caching, `context.repo()` and `context.issue()`, - `context.isBot`. Companions: `probot-config` reads `.github/.yml` - and falls back to the owner's `.github` repository; `probot-scheduler` - lists installations and repositories with the app credential and emits - `schedule.repository`; `probot-metadata` stores state in issue bodies. - - Octokit `App`: `webhooks.verifyAndReceive`, `eachInstallation` and - `eachRepository`, token minting behind `getInstallationOctokit`. - - `ghinstallation`, `githubkit`, `gidgethub`, `octokit.rb` app auth: JWT - minting and installation-token exchange. - -When something is missing, say so in the inventory ("no manifest found -(searched: …)", "no signature verification found in the receiver at …"). -Each missing item becomes an up-front question. Do not -fill it in with what an app of this kind usually does. diff --git a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md b/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md deleted file mode 100644 index e14f68769..000000000 --- a/origin-apps/skills/port-github-app-to-origin/references/gap-bar.md +++ /dev/null @@ -1,76 +0,0 @@ -# The feedback bar and the feedback format - -Cursor wants to hear what the team needs from Origin. Raise anything that -blocks the team's core flow, costs them correctness, security, or scale, or -that they would like Origin to do. The bar below sorts items into feedback -for Cursor (a capability Origin should add) and questions for the team -(decisions the team must make); it does not decide whether to speak up. It -also orders feedback so the items that block the port are read first. - -- A difference is any capability whose Origin path is not a straight - substitution. -- A workaround reaches the same outcome with the current API by another - route: a follow-up read, a re-keyed identifier, a path change, a - client-side filter, a marker the app controls. Noting the workaround with - its tradeoff is often the right answer. -- A gap is a difference with no workaround, or a workaround whose tradeoff - meets one of the tests below. Gaps become feedback entries; when there is - at least one, the Feedback section is also written to `ORIGIN-FEEDBACK.md` - beside the brief. - -## Tradeoffs that make a workaround insufficient - -| Tradeoff | Test | -| --- | --- | -| Fan-out at scale | Calls per event multiply by a factor that grows with repository or activity size, and the app's volume makes that budget-relevant. One bounded extra read per event is a workaround. | -| Correctness risk | The workaround can return a wrong answer, not only a slower one: heuristic matching of the app's own rows, inferring a pull request from a SHA several versions share, assembling a URL whose format is not contractual. | -| Security posture | The workaround needs a broader scope, a longer-lived token, or a user credential where an installation token should do. | -| Customer-visible behavior | The workaround changes what the team's users see or can do. | -| Load-bearing | The capability sits on the hello-world path or the team's stated core flow. | - -## Usually a workaround or a question - -- Anything `origin-isms.md` marks `maps`: a documented path exists. -- A field or filter the code does not use. -- A convention difference (pagination style, identifier form, URL fields) - where the Origin convention is a mechanical substitution. -- Anything the changelog says shipped or the spec already carries. Re-read - the live spec before writing feedback. -- A capability the Origin docs do not mention: not available today, with a - question. The team should still ask if they need it. -- A query the app runs against a search API, when the spec has no search - operation for that resource. A list operation with its filters plus a - client-side predicate is the idiom; if that fails the fan-out test, the - feedback is usually about a filter. - -One pattern that does meet the bar: a state change the app reacts to that -has no event, when reacting to that change is the app's purpose and the -state is invisible until an unrelated event arrives. That fails correctness -and customer-visible behavior when the app is a gate. Write the feedback -about the event. - -## The feedback format - -One entry per gap, in the brief's "Feedback for Cursor" section. A suggested -shape, not a form; keep whatever lines carry information. Describe the use -case and the API gap relative to it, in Origin terms. No file paths, -module names, framework internals, code structure, or repository names; those -belong in the team-facing sections of the brief. - -```markdown -### Feedback: - -- **Use case:** the app needs to , . -- **Origin today:** . -- **Workaround considered:** . -- **Blocking?** yes / no, for which flow. -- **Spec version checked:** ``, . -``` - -Describe the capability rather than proposing scope, field, or route names, -so Cursor can fit it to the API's conventions. One capability per entry. - -The team sends the feedback, not you. Before they forward it, they should -strip anything that reveals their internals. A reply of "here is the idiom" -or "not planned" is useful too; record it in the brief with the label it -earns. diff --git a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md b/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md deleted file mode 100644 index 160ddd10a..000000000 --- a/origin-apps/skills/port-github-app-to-origin/references/origin-isms.md +++ /dev/null @@ -1,41 +0,0 @@ -# Origin-isms: GitHub features that map differently on Origin - -Check here before calling anything a gap. Each row says whether a documented -Origin path exists (`maps`) or nothing in the current spec covers it -(`not-available`), and where the Origin answer lives (a section heading in -the Origin docs unless noted; find its link in `llms.txt`). A `maps` row is a question only if the team -wants the old shape back. A `not-available` row is a question, and feedback -if it meets the bar in `gap-bar.md`. Read the source; do not copy this table -into the brief. - -| What the app uses today | Mark | Where the Origin answer lives | -| --- | --- | --- | -| Writes or `push` events on a repository mirrored from GitHub | `maps` | Read-only until the mirror becomes a stable outbound mirror ("Mirrored repositories"); pushes are not delivered for GitHub-sourced mirrors ("Events"). Transitioning is a user-credential operation. First question of every brief. | -| Install callback query parameters (`installation_id`, `setup_action`) | `maps` | "Installation receipt" | -| RS256 app JWT | `maps` | "App JWT" | -| Long-lived installation tokens | `maps` | "Installation access token" | -| User OAuth, `/user`, `/user/installations`, install-by-user picker | `not-available` | No user-credential flow for apps in the current spec. Repository discovery is through the installation; namespace-wide listing is under "Current limitations". Ask what the flow should do. | -| Permissions `: read\|write` | `maps` | "Scopes"; `x-origin-scopes` per operation | -| Numeric IDs, `/repositories/{id}` | `maps` | "IDs", "Repository paths" | -| `Link` / `page` / `per_page` pagination, total counts | `maps` | "Pagination" | -| GraphQL | `not-available` | No GraphQL endpoint in the current spec. Decompose into REST calls and count the fan-out; a decomposition that meets the feedback bar earns an entry about that read. | -| Commit statuses (`statuses` permission, `POST /statuses/{sha}`) | `maps` | "Check runs" (check runs with a stable `key`) | -| Issues (`issues` permission, `issues.*` events, `/issues/{n}` not on a pull request) | `not-available` | No Issues endpoints or events in the current spec. Pull request comments, threads, reviews, and labels cover the pull-request half. Ask what the team needs for the rest; an issue-driven app may earn a feedback entry. | -| `/issues/{n}/comments`, `/issues/{n}/labels` used on a pull request | `maps` | Pull requests endpoint reference; same calls under `/pulls/{n}/…` | -| Repository webhook CRUD (`/repos/…/hooks`) | `maps` | Subscriptions are set per app through Create App / Update App `events` ("Events"). | -| App-manifest conversion | `maps` | App creation form or `CreateApp` ("Installation", endpoint reference) | -| OAuth-app token mints | `not-available` | Nothing in the current spec. Ask what the flow was for. | -| Git Data API commit and ref writes | `maps` | Create Commit From Files, Create Git Ref (Git data endpoint reference); "Git HTTPS authentication" for pushes | -| Git Data API arbitrary blob or tree writes | `not-available` | Not in the current spec. Ask whether commit-from-files or a push covers the use. | -| Standalone review-thread objects | `maps` | A thread comes from its first diff-anchored comment (Pull requests endpoint reference); thread listing is under "Current limitations". | -| User, email, team, and member lookups | `not-available` | No directory reads in the current spec. Reviewer identifiers resolve by public id, user email, or group slug; `handle` is present when the profile is public ("Resource references"). | -| Single `pull_request` event with an `action` field, `previous_attributes` | `maps` | "Events", "Event payloads" | -| `x-github-*` headers, HMAC `x-hub-signature-256` | `maps` | "Headers", "Signature verification" | -| Payload inlines (changed files on push, before-SHA, `html_url`, `sender` profile) | `maps` | "Resource references"; the push commit list is under "Current limitations" and may change. Name the follow-up call per field and count the fan-out. | -| All events delivered after app creation | `maps` | "Events" | -| Reviews keyed by `commit_id` | `maps` | `pullRequestVersion` on the review schema | -| Finding own check runs or comments by actor | `maps` | "Check runs" (`key`); comments and reviews by a marker the app controls | -| Requested-reviewer team pages, `created_via` | `not-available` | Groups exist and resolve by slug; there is no group membership read in the current spec. | - -A capability that is not on this list and that the Origin docs do not -mention is not available today; ask the team whether they need it. diff --git a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md b/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md deleted file mode 100644 index 9683511bb..000000000 --- a/origin-apps/skills/port-github-app-to-origin/references/spec-mapping.md +++ /dev/null @@ -1,101 +0,0 @@ -# Reading the Origin spec and matching GitHub calls and events to it - -Quoted names ("Scopes", "Events") are section headings in the Origin docs; -find each one's link in `llms.txt`. - -Every mapping in the brief comes from the fetched `openapi.yaml`, not from a -table here. Build the index once. Every later step looks things up in it. - -## Extensions the spec carries - -| Extension | Where | Use | -| --- | --- | --- | -| `x-origin-scopes` | every operation | The scope and credential rules for that operation. "Scopes", "Endpoint reference". | -| `x-origin-webhook-events` | payload schemas | The slugs that deliver this payload shape; a schema carrying it is a webhook family. "Event payloads". Infer the embedded resource from its `$ref`s; some families have no REST twin. | -| `x-cursor-visibility: PREVIEW` | operations, parameters, schemas, fields | "Preview". Note it in the brief on any capability that touches a badged element. | - -## Build the index - -1. **Operations and scopes**: `rg -B1 -A4 'x-origin-scopes:' openapi.yaml` - prints every `operationId` with its scope block. From it, note the union - of scopes with the operations that need each, and separate - installation-requestable scopes from ambient and user-only ones - ("Scopes" explains the difference). The user-only set tells - you which GitHub flows have no app-side equivalent. Read parameters and - response components from the spec when a rule below asks for them. -2. **Webhook events**: `rg -A3 'x-origin-webhook-events:' openapi.yaml` lists - every slug with its payload schema. Each family has its own section, with - the fields expanded to dotted paths ("Pull Request Events" and its - siblings under "Event payloads"; find them in `llms.txt`). In a broad run - with `llms-full.txt` already fetched, `rg -n '^### Pull Request Events$' - llms-full.txt` and read to the next `###`. From "Events", note which slugs - are delivered - without a subscription and which must be selected. -3. **Resources**: each endpoint's section lists its "Response Fields" with - nested objects expanded ("Get Pull Request" under "Pull requests"), for - "does the Origin object carry this field". Fallback in a broad run: - `rg -n '^### Get Pull Request$' llms-full.txt`. - -## Matching - -Try these in order and stop at the first rule that yields a confirmed -counterpart. Confirmed means you read the Origin operation's description and -parameters and it answers the same question the current call answers. A name -match is a candidate, not a result. - -**REST calls** - -1. Look for the same resource path under the Origin base path - ("Repository paths" and "IDs" give the path forms). Most GitHub - repository, pull request, check, label, branch, and commit paths have a - direct or near-direct counterpart. -2. Re-home GitHub's issue-flavored pull request calls (`/issues/{n}/comments`, - `/issues/{n}/labels` used *on a pull request*) to the pull request - endpoints. That is a path change, not a gap. When the code uses them on - real issues, see `origin-isms.md`. -3. Re-home app and installation calls (`/app`, `/app/installations`, - access-token minting, `/installation/repositories`) to the Apps and - installations endpoints and confirm the credential each accepts. `/user`, - `/user/installations`, `/orgs/…`, `/search/…`, and `/repositories/{id}` - have no path counterpart. Consult `origin-isms.md` before labeling them. -4. Compare parameters as well as paths. A matching path that lacks a filter - the code depends on is a workaround or a gap, not a straight match. -5. Compare the response fields the code reads. Each missing field gets its - own line as follow-up call, derivable, or absent. GitHub inlines web URLs, - nested profiles, and counts that Origin does not. - -**GraphQL.** There is no endpoint. Decompose each document into the REST -reads and writes it stands for, map those, and record the fan-out as the -row's tradeoff. - -**Permissions → scopes.** Do not translate the manifest noun-for-noun. Find -the operations the code calls and take the union of *their* -`x-origin-scopes.scopes`. GitHub permissions with no Origin noun -(`statuses`, `issues`, `members`, `organization_*`, `pages`, `actions`, -`workflows`, `deployments`) go through `origin-isms.md` first. - -**Events → slugs.** Each GitHub `event` + `action` pair maps to at most one -slug in "Events"; the action is part of the slug. A pair with -no slug is not an event on Origin. Check whether the state change is -observable another way before classifying it. - -**Payload fields → schema properties.** For each field path a handler reads, -walk the mapped family's "Payload Fields" list in its docs section and record -one of five outcomes, matching the brief template's "How" column. -Present at ``. Present in the envelope (`event.type` carries what -GitHub puts in `action`). Follow-up read via `` with identifiers -the payload carries. Derivable from present fields, saying how and whether -the format is contractual. Absent, which goes to the gap bar. A field on the REST -component that the webhook twin lacks means a follow-up `Get…` on every -event. - -## Out of domain and spec-silent - -- A concept neither the spec nor the docs mention is not available - today and gets a question rather than a gap: there is no Origin answer yet - to compare against, and the question is how the team tells Cursor they - need it. -- A behavior the code depends on that the docs do not state (does an event - fire for a draft pull request? does `updatedAt` move on a comment?) becomes an open - question plus a hello-world step that observes it on a native repository. - Do not settle it from GitHub's behavior. From 74af145809e372efd9fd7649b0d69e8d48a28723 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 28 Sep 2026 23:46:24 +0000 Subject: [PATCH 20/26] Plain-language pass over the skills and README Wording only. "Crib" becomes "Where GitHub features live on Origin"; "the bar", "first-run path", "marks", "provenance", "fan-out", "rows", "call families", and "payload family" become ordinary words; native repositories and stable outbound mirrors are explained once in plain terms; optional table labels are plain words. Rules, eval fixes, and the section-name pointers are unchanged. Co-authored-by: ali.nikseresht --- origin-apps/README.md | 28 +-- origin-apps/skills/origin-api/SKILL.md | 39 ++-- .../skills/port-github-app-to-origin/SKILL.md | 117 +++++----- .../references/brief.md | 203 ++++++++++-------- 4 files changed, 202 insertions(+), 185 deletions(-) diff --git a/origin-apps/README.md b/origin-apps/README.md index 3c0c4b68a..dabd85fd1 100644 --- a/origin-apps/README.md +++ b/origin-apps/README.md @@ -1,25 +1,25 @@ # Origin Apps Two skills for building on [Cursor Origin](https://cursor.com/docs/api/origin), -Cursor's code forge. They cover creating an Origin App, calling the API, -receiving webhooks, and bringing an existing GitHub App across. The plugin is +Cursor's code host. They cover creating an Origin App, calling the API, +receiving webhooks, and moving an existing GitHub App over. The plugin is skills only, so it runs in Cursor, Claude Code, Codex, and any agent that reads [Agent Skills](https://agentskills.io). ## What it includes -`origin-api` routes questions to the section of the Origin docs that answers -them and names the rules to check first (native versus mirrored repositories, -event subscriptions, webhook verification, scopes from the spec, opaque -tokens and IDs). Use it for any Origin work. +`origin-api` sends the agent to the section of the Origin docs that answers +its question and lists the rules to check first: native versus mirrored +repositories, event subscriptions, webhook verification, scopes from the +spec, opaque tokens and IDs. Use it for any Origin work. `port-github-app-to-origin` plans the move of an existing GitHub App. Run it -inside the app's repository. It reads what the app uses out of the code, maps -that onto the live Origin spec, and writes a porting brief: a capability -table, the webhook fields your handlers read and where each comes from on -Origin, the scopes to request, a first-run path, feedback for Cursor, and -the questions your team should settle first. It plans; it writes no code -unless you ask. +inside the app's repository. It reads what the app uses out of the code, +maps that onto the live Origin spec, and writes a porting brief with what +carries over and what does not, the webhook fields your handlers read and +where each comes from on Origin, the scopes to request, an end-to-end test +for your app, feedback for Cursor, and the questions your team has to +decide. It plans. It writes no code unless you ask. Both skills fetch the spec at run time and never name an endpoint from memory. @@ -46,8 +46,8 @@ refers to `origin-api`). ## Requirements - Network access to `https://cursor.com/docs/api/origin/*` during the run. -- For the porting skill, read access to the app's source. Producing the brief - needs no Origin credentials. You follow the brief's hello-world path +- For the porting skill, read access to the app's source. Writing the brief + needs no Origin credentials. You run the brief's end-to-end test afterwards. ## Where the brief goes diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md index e73c478de..d90948281 100644 --- a/origin-apps/skills/origin-api/SKILL.md +++ b/origin-apps/skills/origin-api/SKILL.md @@ -17,13 +17,14 @@ The docs are the source of truth. Do not name an endpoint, scope, event slug, header, or limit from memory. Where this file and the docs disagree, the docs win. -Under `https://cursor.com/docs/api/origin/`: `llms.txt` is the index and -links every section, endpoint, and webhook payload; `openapi.yaml` is the -contract (its `x-origin-*` extensions are summarized in "Endpoint -reference"); `llms-full.txt` is the whole reference in one file; `changelog` -says what moved. For one question, read `llms.txt` and fetch only the section -that answers it. Fetch `llms-full.txt` or `openapi.yaml` whole for broad work -such as a porting brief. Cite `operationId`s and section names. +The docs live under `https://cursor.com/docs/api/origin/`. `llms.txt` is +the index and links every section, endpoint, and webhook payload. +`openapi.yaml` is the contract, and "Endpoint reference" explains its +`x-origin-*` extensions. `llms-full.txt` is the whole reference in one file. +`changelog` says what moved. For one question, read `llms.txt` and fetch +only the section that answers it. Fetch `llms-full.txt` or `openapi.yaml` +whole when you need all of it, as a porting brief does. Cite `operationId`s +and section names. ## Where to look @@ -33,7 +34,7 @@ such as a porting brief. Cite `operationId`s and section names. | Install flow and the callback receipt | "Installation", "Installation receipt" | | Which scope an operation needs | `x-origin-scopes` on the operation; "Scopes" | | What an installation can do on a mirrored repository | "Mirrored repositories" | -| Webhook headers, signature, envelope, retries, pausing, recovery | "Webhooks" | +| Webhook headers, signature, delivery format, retries, pausing, recovery | "Webhooks" | | Which events exist and which arrive without subscribing | "Events" | | Payload shapes | "Event payloads" | | Pagination, errors, request IDs, repository paths, IDs | "Common conventions" | @@ -45,19 +46,19 @@ such as a porting brief. Cite `operationId`s and section names. ## Rules to check first 1. **Native or mirror.** An installation keeps its full scopes only on - native repositories and stable outbound mirrors, and some writes are - native-only (merging a pull request, changing the default branch); read - each operation's description for mirror limits. On a GitHub-sourced - mirror, every event except `repository.pushed` still arrives, and every - call beyond metadata and contents reads returns `403` ("Mirrored - repositories", "Events"). -2. **Subscribe.** Only `installation.*` events arrive without a subscription; - a missing subscription is silence, not an error ("Events"). + native repositories (created on Origin) and stable outbound mirrors + (Origin is the source and pushes to GitHub). Some writes work only on + native repositories, such as merging a pull request or changing the + default branch, so read each operation's description for mirror limits. + On a repository mirrored from GitHub, every event except + `repository.pushed` still arrives, and every call beyond metadata and + contents reads returns `403` ("Mirrored repositories", "Events"). +2. **Subscribe.** Only `installation.*` events arrive without a subscription. + A missing subscription produces silence, not an error ("Events"). 3. **Verify, dedupe, acknowledge.** Verify the signature over the raw body before parsing, dedupe on the delivery ID, return `2xx`, then process - ("Signature verification", "Retries", "Automatic disable"). The digest - step differs from Standard Webhooks; do not assume a generic verifier - passes. + ("Signature verification", "Retries", "Automatic disable"). Origin signs + a digest, which Standard Webhooks does not, so a generic verifier fails. 4. **Scopes from the spec.** Request the union of `x-origin-scopes.scopes` over the operations the app calls ("Scopes"). 5. **Opaque tokens and IDs.** Do not build or parse page tokens or IDs diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index b177a46bc..082b78bcb 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -13,73 +13,76 @@ compatibility: >- # Port a GitHub App to an Origin App -Run inside the app's codebase. The output is a porting brief for the team plus -a Feedback for Cursor section they can send as is (`references/brief.md`). -This skill plans; it does not write or change code unless the user explicitly -asks after reading the brief. Follow the `origin-api` skill for the docs and -the rules to check first. Two rules on top: +Run this inside the app's codebase. The output is a porting brief for the +team plus a Feedback for Cursor section they can send as is +(`references/brief.md`). This skill plans. It does not write or change code +unless the user asks for that after reading the brief. Follow the +`origin-api` skill for the docs and the rules to check first. Two more rules: -1. **Discover, do not ask.** Read what the app uses out of the code. Anything - you cannot find becomes an open question. -2. **Feedback describes use cases, not the team's code.** Team-facing parts of - the brief may cite `file:line`. Feedback for Cursor names only what the - app needs to do and what Origin lacks for it, in Origin terms, with no file - paths, module names, framework internals, or repository names. +1. **Find it in the code, do not ask.** Read what the app uses out of its + source. Anything you cannot find becomes a question for the team. +2. **Feedback describes use cases, not the team's code.** The team's parts of + the brief may cite `file:line`. Feedback for Cursor says only what the app + needs to do and what Origin lacks for it, in Origin terms, with no file + paths, module names, framework details, or repository names. -## What to discover +## What to find -Record a `file:line` for each, and note what you looked for and did not find. +Record `file:line` for each item, and note what you looked for but did not +find. -- Declared permissions and events (manifest or IaC, if checked in; otherwise - derive from the calls). -- Webhook events handled, and every payload field each handler reads, - including fields used only for logging. -- REST and GraphQL call families, with the parameters and filters passed, the - response fields read, whether each runs per webhook or in a loop, and the - pagination style in use. -- Authentication: JWT algorithm, how the installation is identified after - install, token lifetime handling, any user sign-in and what it is for, - whether the app clones or pushes git. -- Webhook receiver: signature scheme, whether the raw body is available at - verification time, how deliveries are deduplicated. -- Calls a framework or helper library makes on the app's behalf (Probot's - receiver, token cache, and config loader; Octokit `App`'s installation and - repository listing; app-auth libraries). Read the dependency's docs and - list these as rows marked "from ``". +- Declared permissions and events, from a manifest or infrastructure code + if one is checked in. Otherwise derive them from the calls. +- Every webhook event the app handles, and every payload field each handler + reads, including fields it only logs. +- Every REST and GraphQL call, with the parameters and filters the code + passes, the response fields it reads, whether it runs on every webhook or + in a loop, and how it paginates. +- Authentication: the JWT algorithm, how the app learns the installation ID + after install, how it handles token expiry, any user sign-in and what it is + for, and whether the app clones or pushes git. +- The webhook receiver: how it verifies signatures, whether it has the raw + body when it verifies, and how it deduplicates deliveries. +- Calls a framework or library makes for the app. Probot's receiver, token + cache, and config loader; Octokit `App`'s installation and repository + listing; app-auth libraries. Read the dependency's docs and list these + calls marked "from ``". ## How to map -Fetch `openapi.yaml` and `llms-full.txt` whole; a brief needs broad coverage. +Fetch `openapi.yaml` and `llms-full.txt` whole. A brief needs all of it. `rg -B1 -A4 'x-origin-scopes:' openapi.yaml` lists every operation with its -scope block; `rg -A3 'x-origin-webhook-events:' openapi.yaml` lists every -event slug with its payload schema. Each endpoint and each payload family has -a docs section with its fields expanded to dotted paths. +scopes. `rg -A3 'x-origin-webhook-events:' openapi.yaml` lists every event +slug with its payload schema. Each endpoint and each webhook payload type has +a docs section that spells out its fields as dotted paths. -- A name match is a candidate, not a result. Confirm by reading the - operation's description, parameters, and response fields against what the - code passes and reads. A missing parameter or field the code depends on is +- A matching name is a candidate, not an answer. Read the operation's + description, parameters, and response fields against what the code passes + and reads. If a parameter or field the code depends on is missing, that is a workaround or a gap, not a match. -- GitHub's issue-flavored pull request calls (`/issues/{n}/comments`, - `/issues/{n}/labels` on a pull request) live under the pull request - endpoints. Used on real issues, see the crib in `references/brief.md`. -- GraphQL has no counterpart; decompose each document into REST calls and - record the fan-out. -- Scopes are the union of `x-origin-scopes.scopes` over the operations you - named, not a translation of the manifest. -- Each event and action pair maps to at most one slug in "Events"; the action - is part of the slug. A pair with no slug is not an event on Origin. -- For each payload field the code reads, record whether it is present, comes - from the envelope (`event.type` carries the action), needs a follow-up read - (say which operation and how many calls per event), is derivable, or is - absent. Payloads are snapshots; a field on the REST resource that the - payload lacks is a follow-up read. -- A capability the docs do not mention is not available today and gets a - question. A behavior the docs neither confirm nor deny gets a question plus - a first-run step that observes it, not an assumption carried over from - GitHub. +- GitHub's pull request calls that live under `/issues/{n}/…` (comments, + labels) live under the pull request endpoints on Origin. If the code uses + them on real issues, see "Where GitHub features live on Origin" in + `references/brief.md`. +- Origin has no GraphQL. Break each query into REST calls and count the calls + per event. +- The scopes to request are the union of `x-origin-scopes.scopes` over the + operations you named. Do not translate the GitHub manifest. +- Each GitHub event and action pair maps to at most one slug in "Events". The + action is part of the slug. A pair with no slug is not an event on Origin. +- For each payload field the code reads, record whether it is in the + payload, in the delivery envelope around the payload (`event.type` carries + the action), needs an extra read (say which operation and how many calls + per event), can be computed from other fields, or is missing. Payloads are + snapshots. A field the REST resource has but the payload lacks needs an + extra read. +- A capability the docs never mention is not available today. Ask the team + about it. A behavior the docs neither confirm nor deny gets a question plus + a step in the end-to-end test that checks it. Do not assume it works the + way it did on GitHub. ## Before finishing -Every Origin claim resolves in the fetched files. Every gap has a feedback -entry naming a tradeoff. The feedback contains nothing that reveals the -team's internals. The summary names the native-or-mirror question. +Every claim about Origin points at something in the fetched files. Every gap +has a feedback entry that names its cost. The feedback reveals nothing about +the team's internals. The summary names the native-or-mirror question. diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief.md b/origin-apps/skills/port-github-app-to-origin/references/brief.md index ed6bd09a6..34138071b 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/brief.md +++ b/origin-apps/skills/port-github-app-to-origin/references/brief.md @@ -1,115 +1,128 @@ -# The brief, the feedback, and the crib +# The brief and the feedback ## The brief -One Markdown file at the repository root (`ORIGIN-PORTING-BRIEF.md` unless -the team's convention says otherwise); print its path. Budget about 800 -words for a small app and about 2,000 for a large one; parts with nothing to -say collapse or disappear. Choose table shapes to fit the app. A good brief: - -- leads with a summary: the verdict (ports as is, ports with workarounds, - blocked on X), the question that decides the rest (usually native or - mirror), and whether there is feedback for Cursor and if any of it blocks; -- lists only the capabilities that do not map straight across, each with - the Origin operation, slug, or docs section it maps to or a note that - nothing does, and closes the list with one line naming the rest - ("maps directly: 14 operations, covered by the scopes line"); shows a - payload field only when it is absent or needs a follow-up read; ends with - the scopes to request; -- gives an app-specific first-run path when it helps (events to select, the - mirror check, the first event and what it carries, the first write); skips - generic setup, which "Implementation checklist" covers; -- notes what drives the size of the port and how to roll it out (dual-run - or cutover), without time estimates; -- asks only what the team must decide, never restating a row; -- ends with two lines of provenance (spec `info.version` and fetch time; - codebase and commit) and then Feedback for Cursor, last and unnumbered. - -Evidence for every app claim (`file:line`, or "from ``"); every -Origin claim resolved against the fetched docs. If you want compact marks: -`maps` (a documented path exists), `workaround` (say the tradeoff), -`not-available` (nothing in the current spec; carries a question), `gap` -(produces a feedback entry). +Write one Markdown file at the repository root (`ORIGIN-PORTING-BRIEF.md` +unless the team names files differently) and print its path. Aim for about +800 words for a small app and about 2,000 for a large one. Leave out any part +that has nothing to say. Pick whatever table shape fits the app. A good brief: + +- Opens with a summary. The verdict (ports as is, ports with workarounds, or + blocked on X), the question that decides the rest (usually whether the + repositories are native or mirrored), and whether there is feedback for + Cursor and if any of it blocks the port. +- Lists only the capabilities that do not carry over as is. For each, the + Origin operation, event slug, or docs section it maps to, or a note that + nothing does. Closes the list with one line for the rest, such as "14 + operations map directly; see the scopes line". Shows a webhook payload + field only when it is missing or needs an extra API call. Ends with the + scopes to request. +- Describes an end-to-end test for this app when that helps. Which events to + select, how to check the repository's mirror state, the first event that + should arrive and what it should contain, the first write. Skip generic + setup; the docs' "Implementation checklist" covers it. +- Says what makes the port big or small and how to roll it out (run both + versions side by side, or cut over). No time estimates. +- Asks only the questions the team has to decide. Do not turn a finding into + a question. +- Ends with two lines saying which spec version (`info.version` and fetch + time) and which code commit the brief is based on, then Feedback for + Cursor as the last section, without a number. + +Back every claim about the app with `file:line`, or with "from +``" when a library does it for the app. Back every claim about +Origin with something in the fetched docs. If you want short labels in a +table, use plain ones: works as is, workaround (say the cost), not available +(ask the team), gap (write feedback). ## Feedback for Cursor Cursor wants to hear what the team needs. Raise anything that blocks the -team's core flow, costs them correctness, security, or scale, or that they -would like Origin to do. The bar sorts items into feedback (a capability -Origin should add) and questions (decisions the team must make); it does not -decide whether to speak up. - -A workaround reaches the same outcome by another route (a follow-up read, a -re-keyed identifier, a path change, a client-side filter, a marker the app -controls) and is often the right answer. It becomes a gap, and a feedback -entry, when its tradeoff is one of: fan-out that grows with repository or -activity size at the app's volume; a possibly wrong answer (heuristic own-row -matching, a pull request inferred from a SHA several versions share, a URL -whose format is not contractual); a broader scope, longer-lived token, or -user credential where an installation token should do; a change to what the -team's users see or can do; or a capability on the hello-world path or the -team's core flow. A state change the app exists to react to, with no event -and no other way to observe it, meets the bar. Something the docs never -mention is not available today and gets a question; it becomes feedback -only if it blocks the core flow. +team's main flow, costs them correctness, security, or scale, or that they +would like Origin to do. The tests below only sort items into feedback (a +capability Origin should add) and questions (decisions the team must make). +They never decide whether to speak up. + +A workaround gets the same result another way, for example an extra read, a +different identifier, a changed path, filtering on the client, or a marker +the app controls. Often it is the right answer. It becomes a gap, and gets a +feedback entry, when it costs one of these: + +- extra calls per event that grow with repository or activity size, at this + app's volume; +- a possibly wrong answer, such as guessing which check run or comment is the + app's own, inferring a pull request from a SHA that several versions + share, or building a URL whose format the docs do not promise; +- a broader scope, a longer-lived token, or a user credential where an + installation token should be enough; +- a change to what the team's users see or can do; +- a capability the app's main flow or its first end-to-end test depends on. + +A state change the app exists to react to, with no event for it and no other +way to notice it, is also a gap. Something the docs never mention is not +available today and gets a question; it becomes feedback only if it blocks +the main flow. Not feedback, only a question or a note: a field or filter the code does not -use; a convention difference with a mechanical substitute; a documented +use; a convention that differs but has a mechanical substitute; a documented design choice such as token lifetime or no GraphQL. -One entry per gap, in Origin terms, with nothing that reveals the team's -internals. When there is at least one, also write the section to -`ORIGIN-FEEDBACK.md` beside the brief; when there is none, no file and one -line saying so. The file carries no license header, repository name, -product name, or mention of another forge. A contradiction between the docs -and observed behavior goes in a short "Docs questions for Cursor" list at -the end of the feedback, not in the questions for the team. A suggested -shape: +Write one entry per gap, in Origin terms, with nothing that reveals the +team's internals. When there is at least one entry, also write the section +to `ORIGIN-FEEDBACK.md` next to the brief. When there is none, write no file +and say so in one line. The file carries no license header, repository name, +product name, or mention of another code host. If the docs and observed +behavior disagree, put that in a short "Docs questions for Cursor" list at +the end of the feedback, not in the team's questions. A suggested shape: ```markdown ### Feedback: - **Use case:** the app needs to , . - **Origin today:** . -- **Workaround considered:** . +- **Workaround considered:** . - **Blocking?** yes / no, for which flow. - **Spec version checked:** , . ``` -Describe the capability rather than proposing scope, field, or route names. -The team sends the feedback, not you; before they do, they strip anything -that reveals their internals. - -## Crib: where GitHub habits land on Origin - -Check before calling anything a gap; confirm each in the fetched docs. - -Documented path exists: install callback params → "Installation receipt"; -RS256 app JWT → "App JWT"; long-lived installation tokens → "Installation -access token"; permissions → "Scopes" and `x-origin-scopes`; numeric IDs and -`/repositories/{id}` → "IDs", "Repository paths"; `Link` pagination and -totals → "Pagination"; commit statuses → check runs with a stable `key` -("Check runs"); `/issues/{n}/comments` and `/issues/{n}/labels` on a pull -request → the pull request endpoints; repository webhook CRUD → per-app -`events` on Create App / Update App; a single `pull_request` event with an -`action` field → one slug per action ("Events"); `x-github-*` headers and -HMAC → "Headers", "Signature verification"; inlined payload data (changed -files, before-SHA, URLs, profiles) → follow-up reads ("Resource references", -"Current limitations"); reviews keyed by commit SHA → `pullRequestVersion`; -finding own rows by actor → check-run `key`, or a marker the app controls; -writes on a repository mirrored from GitHub → metadata and contents reads -only, until it is a stable outbound mirror, and merging and default-branch -changes stay native-only ("Mirrored repositories"); user sign-in and acting -as a user → "Acting on behalf of users" (user confirmation receipt, -installation user tokens). - -Not available in the current spec (question, and feedback if it blocks the -core flow): GraphQL (decompose); Issues (pull request comments, threads, -reviews, and labels cover the pull-request half); OAuth-app token mints; -arbitrary blob or tree writes (commit-from-files and pushes exist); user, -email, team, and member directory reads (reviewer identifiers resolve by -public id, email, or group slug); group membership and effective-permission -reads. For the last two, the workaround to name is a user token capped to a -repository and scopes: minting it returns `403` unless the user holds that -permission, so it doubles as a permission probe. - -Crib rows go stale; the fetched docs win. +Describe the capability. Do not propose scope, field, or route names. The +team sends the feedback, not you, and they remove anything that reveals +their internals first. + +## Where GitHub features live on Origin + +Check this before calling anything a gap, then confirm in the fetched docs. +This list goes stale; the docs win. + +Has an Origin equivalent: install callback parameters → "Installation +receipt"; RS256 app JWT → "App JWT"; long-lived installation tokens → +"Installation access token"; permissions → "Scopes" and `x-origin-scopes`; +numeric IDs and `/repositories/{id}` → "IDs", "Repository paths"; `Link` +pagination and total counts → "Pagination"; commit statuses → check runs +with a stable `key` ("Check runs"); `/issues/{n}/comments` and +`/issues/{n}/labels` on a pull request → the pull request endpoints; +repository webhook CRUD → the app's `events` list on Create App and Update +App; one `pull_request` event with an `action` field → one slug per action +("Events"); `x-github-*` headers and HMAC signatures → "Headers", "Signature +verification"; data GitHub inlines in payloads (changed files, before-SHA, +URLs, user profiles) → extra reads ("Resource references", "Current +limitations"); reviews keyed by commit SHA → `pullRequestVersion`; finding +the app's own check runs or comments by author → the check run `key`, or a +marker the app controls; user sign-in and acting as a user → "Acting on +behalf of users" (user confirmation receipt, installation user tokens). + +Repositories mirrored from GitHub: an installation can only read metadata +and contents until the mirror becomes a stable outbound mirror (Origin is +the source and pushes to GitHub). Merging a pull request and changing the +default branch work only on native repositories, the ones created on Origin +("Mirrored repositories"). + +Not in the current spec (ask the team; feedback only if it blocks the main +flow): GraphQL (break each query into REST calls); Issues (pull request +comments, threads, reviews, and labels cover the pull request half); +OAuth-app token minting; writing arbitrary blobs or trees (commit-from-files +and pushes exist); looking up users, emails, teams, or members (reviewer +identifiers resolve by public id, email, or group slug); reading group +membership or a user's effective permission. For those last two, the +workaround to name is a user token limited to a repository and scopes. +Minting it returns `403` unless the user holds that permission, so it +doubles as a permission check. From 14989dda0038928f26b3e15fb858d27e6bb70937 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 28 Sep 2026 23:48:10 +0000 Subject: [PATCH 21/26] Download the spec to disk and search it; never read it whole openapi.yaml and llms-full.txt are about 870 KB and 575 KB, roughly 350k tokens together, more than a context window holds. Both skills now say to curl them to disk and rg them, reading only the matching sections. Narrow questions still go through llms.txt to the one section. Co-authored-by: ali.nikseresht --- origin-apps/README.md | 2 +- origin-apps/skills/origin-api/SKILL.md | 8 ++++--- .../skills/port-github-app-to-origin/SKILL.md | 22 ++++++++++++++----- .../references/brief.md | 4 ++-- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/origin-apps/README.md b/origin-apps/README.md index dabd85fd1..01151e055 100644 --- a/origin-apps/README.md +++ b/origin-apps/README.md @@ -21,7 +21,7 @@ where each comes from on Origin, the scopes to request, an end-to-end test for your app, feedback for Cursor, and the questions your team has to decide. It plans. It writes no code unless you ask. -Both skills fetch the spec at run time and never name an endpoint from memory. +Both skills read the live spec at run time and never name an endpoint from memory. ## When to use diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md index d90948281..eba4eedbf 100644 --- a/origin-apps/skills/origin-api/SKILL.md +++ b/origin-apps/skills/origin-api/SKILL.md @@ -22,9 +22,11 @@ the index and links every section, endpoint, and webhook payload. `openapi.yaml` is the contract, and "Endpoint reference" explains its `x-origin-*` extensions. `llms-full.txt` is the whole reference in one file. `changelog` says what moved. For one question, read `llms.txt` and fetch -only the section that answers it. Fetch `llms-full.txt` or `openapi.yaml` -whole when you need all of it, as a porting brief does. Cite `operationId`s -and section names. +only the section that answers it. `llms-full.txt` and `openapi.yaml` are +each several hundred kilobytes, more than a context window holds. When you +need all of them, as a porting brief does, download them to disk with +`curl -o` and search with `rg`, reading only the matching sections. Never +read or paste either file whole. Cite `operationId`s and section names. ## Where to look diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index 082b78bcb..fafd2d212 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -50,11 +50,21 @@ find. ## How to map -Fetch `openapi.yaml` and `llms-full.txt` whole. A brief needs all of it. -`rg -B1 -A4 'x-origin-scopes:' openapi.yaml` lists every operation with its -scopes. `rg -A3 'x-origin-webhook-events:' openapi.yaml` lists every event -slug with its payload schema. Each endpoint and each webhook payload type has -a docs section that spells out its fields as dotted paths. +A brief needs the whole spec, but the files are too large to read into +context (about 870 KB and 575 KB). Download them to disk and search them: + +```bash +curl -sSL https://cursor.com/docs/api/origin/openapi.yaml -o openapi.yaml +curl -sSL https://cursor.com/docs/api/origin/llms-full.txt -o llms-full.txt +rg -B1 -A4 'x-origin-scopes:' openapi.yaml # every operation with its scopes +rg -A3 'x-origin-webhook-events:' openapi.yaml # every event slug with its payload schema +rg -n '^### Get Pull Request$' llms-full.txt # then read to the next ### heading +``` + +Read only the sections a match points at. Each endpoint and each webhook +payload type has a section in `llms-full.txt` that spells out its fields as +dotted paths. For a single question later, `llms.txt` names the one section +to fetch. - A matching name is a candidate, not an answer. Read the operation's description, parameters, and response fields against what the code passes @@ -83,6 +93,6 @@ a docs section that spells out its fields as dotted paths. ## Before finishing -Every claim about Origin points at something in the fetched files. Every gap +Every claim about Origin points at something in the downloaded files. Every gap has a feedback entry that names its cost. The feedback reveals nothing about the team's internals. The summary names the native-or-mirror question. diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief.md b/origin-apps/skills/port-github-app-to-origin/references/brief.md index 34138071b..effb33d11 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/brief.md +++ b/origin-apps/skills/port-github-app-to-origin/references/brief.md @@ -31,7 +31,7 @@ that has nothing to say. Pick whatever table shape fits the app. A good brief: Back every claim about the app with `file:line`, or with "from ``" when a library does it for the app. Back every claim about -Origin with something in the fetched docs. If you want short labels in a +Origin with something in the downloaded docs. If you want short labels in a table, use plain ones: works as is, workaround (say the cost), not available (ask the team), gap (write feedback). @@ -90,7 +90,7 @@ their internals first. ## Where GitHub features live on Origin -Check this before calling anything a gap, then confirm in the fetched docs. +Check this before calling anything a gap, then confirm in the downloaded docs. This list goes stale; the docs win. Has an Origin equivalent: install callback parameters → "Installation From 7624c3f96079351f8113a30dde3343fdbe31eeee Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 28 Sep 2026 23:52:08 +0000 Subject: [PATCH 22/26] Keep forge and fan-out Co-authored-by: ali.nikseresht --- origin-apps/README.md | 2 +- origin-apps/skills/port-github-app-to-origin/SKILL.md | 4 ++-- .../skills/port-github-app-to-origin/references/brief.md | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/origin-apps/README.md b/origin-apps/README.md index 01151e055..272a728d8 100644 --- a/origin-apps/README.md +++ b/origin-apps/README.md @@ -1,7 +1,7 @@ # Origin Apps Two skills for building on [Cursor Origin](https://cursor.com/docs/api/origin), -Cursor's code host. They cover creating an Origin App, calling the API, +Cursor's code forge. They cover creating an Origin App, calling the API, receiving webhooks, and moving an existing GitHub App over. The plugin is skills only, so it runs in Cursor, Claude Code, Codex, and any agent that reads [Agent Skills](https://agentskills.io). diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index fafd2d212..9951e3209 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -74,8 +74,8 @@ to fetch. labels) live under the pull request endpoints on Origin. If the code uses them on real issues, see "Where GitHub features live on Origin" in `references/brief.md`. -- Origin has no GraphQL. Break each query into REST calls and count the calls - per event. +- Origin has no GraphQL. Break each query into REST calls and record the + fan-out. - The scopes to request are the union of `x-origin-scopes.scopes` over the operations you named. Do not translate the GitHub manifest. - Each GitHub event and action pair maps to at most one slug in "Events". The diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief.md b/origin-apps/skills/port-github-app-to-origin/references/brief.md index effb33d11..22173fa61 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/brief.md +++ b/origin-apps/skills/port-github-app-to-origin/references/brief.md @@ -48,8 +48,8 @@ different identifier, a changed path, filtering on the client, or a marker the app controls. Often it is the right answer. It becomes a gap, and gets a feedback entry, when it costs one of these: -- extra calls per event that grow with repository or activity size, at this - app's volume; +- fan-out, meaning extra calls per event, that grows with repository or + activity size at this app's volume; - a possibly wrong answer, such as guessing which check run or comment is the app's own, inferring a pull request from a SHA that several versions share, or building a URL whose format the docs do not promise; @@ -71,7 +71,7 @@ Write one entry per gap, in Origin terms, with nothing that reveals the team's internals. When there is at least one entry, also write the section to `ORIGIN-FEEDBACK.md` next to the brief. When there is none, write no file and say so in one line. The file carries no license header, repository name, -product name, or mention of another code host. If the docs and observed +product name, or mention of another forge. If the docs and observed behavior disagree, put that in a short "Docs questions for Cursor" list at the end of the feedback, not in the team's questions. A suggested shape: From 551b37a7d5d3275f337ecb484ae96e6ac699f25c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 29 Sep 2026 00:20:48 +0000 Subject: [PATCH 23/26] Doc-access eval: no plain reads of the big files; mirrors are not merge targets Co-authored-by: ali.nikseresht --- origin-apps/skills/origin-api/SKILL.md | 7 ++++--- origin-apps/skills/port-github-app-to-origin/SKILL.md | 8 +++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md index eba4eedbf..299591073 100644 --- a/origin-apps/skills/origin-api/SKILL.md +++ b/origin-apps/skills/origin-api/SKILL.md @@ -49,9 +49,10 @@ read or paste either file whole. Cite `operationId`s and section names. 1. **Native or mirror.** An installation keeps its full scopes only on native repositories (created on Origin) and stable outbound mirrors - (Origin is the source and pushes to GitHub). Some writes work only on - native repositories, such as merging a pull request or changing the - default branch, so read each operation's description for mirror limits. + (Origin is the source and pushes to GitHub). A stable outbound mirror is + not a merge target: Merge Pull Request works only on native repositories, + and so does changing the default branch. Read each operation's description + for mirror limits. On a repository mirrored from GitHub, every event except `repository.pushed` still arrives, and every call beyond metadata and contents reads returns `403` ("Mirrored repositories", "Events"). diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index 9951e3209..f43a24ea6 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -61,9 +61,11 @@ rg -A3 'x-origin-webhook-events:' openapi.yaml # every event slug with its pa rg -n '^### Get Pull Request$' llms-full.txt # then read to the next ### heading ``` -Read only the sections a match points at. Each endpoint and each webhook -payload type has a section in `llms-full.txt` that spells out its fields as -dotted paths. For a single question later, `llms.txt` names the one section +Read only the sections a match points at. Do not open `openapi.yaml` or +`llms-full.txt` with a plain file read; file readers stop after about 50 KB, +so find the line with `rg -n` and read that line range. Each endpoint and +each webhook payload type has a section in `llms-full.txt` that spells out +its fields as dotted paths. For a single question later, `llms.txt` names the one section to fetch. - A matching name is a candidate, not an answer. Read the operation's From 5dda5041a4e1abe140b7ab1dfcf81097f9db1542 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 29 Sep 2026 05:04:35 +0000 Subject: [PATCH 24/26] Describe doc access without naming tools; save specs outside the repo Replaces the curl and rg commands with tool-neutral intent (save locally, search for the heading or annotation, read the matching part) and keeps the concrete search targets. Saved copies go in a temporary location outside the app's repository, so a download cannot overwrite or litter the working tree. Co-authored-by: ali.nikseresht --- origin-apps/skills/origin-api/SKILL.md | 13 ++++---- .../skills/port-github-app-to-origin/SKILL.md | 31 +++++++++---------- .../references/brief.md | 4 +-- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md index 299591073..a791bc455 100644 --- a/origin-apps/skills/origin-api/SKILL.md +++ b/origin-apps/skills/origin-api/SKILL.md @@ -21,12 +21,13 @@ The docs live under `https://cursor.com/docs/api/origin/`. `llms.txt` is the index and links every section, endpoint, and webhook payload. `openapi.yaml` is the contract, and "Endpoint reference" explains its `x-origin-*` extensions. `llms-full.txt` is the whole reference in one file. -`changelog` says what moved. For one question, read `llms.txt` and fetch -only the section that answers it. `llms-full.txt` and `openapi.yaml` are -each several hundred kilobytes, more than a context window holds. When you -need all of them, as a porting brief does, download them to disk with -`curl -o` and search with `rg`, reading only the matching sections. Never -read or paste either file whole. Cite `operationId`s and section names. +`changelog` says what moved. For one question, start at `llms.txt` and +fetch only the section that answers it. `llms-full.txt` and `openapi.yaml` +are each several hundred kilobytes, too large to read into context whole. +When you need all of them, as a porting brief does, save them locally if you +can, outside any repository you are working in, search them for the section +heading or annotation you need, and read only the matching part. Cite +`operationId`s and section names. ## Where to look diff --git a/origin-apps/skills/port-github-app-to-origin/SKILL.md b/origin-apps/skills/port-github-app-to-origin/SKILL.md index f43a24ea6..0002dcf48 100644 --- a/origin-apps/skills/port-github-app-to-origin/SKILL.md +++ b/origin-apps/skills/port-github-app-to-origin/SKILL.md @@ -50,23 +50,22 @@ find. ## How to map -A brief needs the whole spec, but the files are too large to read into -context (about 870 KB and 575 KB). Download them to disk and search them: +A brief needs the whole spec, but `openapi.yaml` and `llms-full.txt` are +each several hundred kilobytes, too large to read into context whole. Save +them locally if you can, in a temporary location outside the app's +repository so nothing in the working tree is overwritten or left behind, +then search them and read only the matching part: -```bash -curl -sSL https://cursor.com/docs/api/origin/openapi.yaml -o openapi.yaml -curl -sSL https://cursor.com/docs/api/origin/llms-full.txt -o llms-full.txt -rg -B1 -A4 'x-origin-scopes:' openapi.yaml # every operation with its scopes -rg -A3 'x-origin-webhook-events:' openapi.yaml # every event slug with its payload schema -rg -n '^### Get Pull Request$' llms-full.txt # then read to the next ### heading -``` +- `x-origin-scopes:` in `openapi.yaml` marks every operation with its + scopes; the `operationId` sits a line above. +- `x-origin-webhook-events:` in `openapi.yaml` marks every webhook payload + schema with the event slugs that deliver it. +- `### ` and `### ` headings in `llms-full.txt` + start the section that spells out that endpoint's or payload's fields as + dotted paths; read from the heading to the next `###`. -Read only the sections a match points at. Do not open `openapi.yaml` or -`llms-full.txt` with a plain file read; file readers stop after about 50 KB, -so find the line with `rg -n` and read that line range. Each endpoint and -each webhook payload type has a section in `llms-full.txt` that spells out -its fields as dotted paths. For a single question later, `llms.txt` names the one section -to fetch. +For a single question later, start at `llms.txt` and fetch just that +section. - A matching name is a candidate, not an answer. Read the operation's description, parameters, and response fields against what the code passes @@ -95,6 +94,6 @@ to fetch. ## Before finishing -Every claim about Origin points at something in the downloaded files. Every gap +Every claim about Origin points at something in the saved docs. Every gap has a feedback entry that names its cost. The feedback reveals nothing about the team's internals. The summary names the native-or-mirror question. diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief.md b/origin-apps/skills/port-github-app-to-origin/references/brief.md index 22173fa61..4117c4f4e 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/brief.md +++ b/origin-apps/skills/port-github-app-to-origin/references/brief.md @@ -31,7 +31,7 @@ that has nothing to say. Pick whatever table shape fits the app. A good brief: Back every claim about the app with `file:line`, or with "from ``" when a library does it for the app. Back every claim about -Origin with something in the downloaded docs. If you want short labels in a +Origin with something in the saved docs. If you want short labels in a table, use plain ones: works as is, workaround (say the cost), not available (ask the team), gap (write feedback). @@ -90,7 +90,7 @@ their internals first. ## Where GitHub features live on Origin -Check this before calling anything a gap, then confirm in the downloaded docs. +Check this before calling anything a gap, then confirm in the saved docs. This list goes stale; the docs win. Has an Origin equivalent: install callback parameters → "Installation From 277537cff6b8589da3f48ae798c881003c62fe46 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 30 Sep 2026 04:39:10 +0000 Subject: [PATCH 25/26] Drop the where-to-look table; llms.txt already indexes those sections Every row mapped a question to a section whose title in llms.txt says the same thing, or to a section a ranked rule already names. The one mapping the index does not make obvious (stale check-run posts are under "Ordering writes") stays as a single line. Co-authored-by: ali.nikseresht --- origin-apps/skills/origin-api/SKILL.md | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md index a791bc455..45c3addd2 100644 --- a/origin-apps/skills/origin-api/SKILL.md +++ b/origin-apps/skills/origin-api/SKILL.md @@ -29,23 +29,6 @@ can, outside any repository you are working in, search them for the section heading or annotation you need, and read only the matching part. Cite `operationId`s and section names. -## Where to look - -| Question | Section | -| --- | --- | -| Which credential for which call; minting and lifetime | "Authentication" and its subsections | -| Install flow and the callback receipt | "Installation", "Installation receipt" | -| Which scope an operation needs | `x-origin-scopes` on the operation; "Scopes" | -| What an installation can do on a mirrored repository | "Mirrored repositories" | -| Webhook headers, signature, delivery format, retries, pausing, recovery | "Webhooks" | -| Which events exist and which arrive without subscribing | "Events" | -| Payload shapes | "Event payloads" | -| Pagination, errors, request IDs, repository paths, IDs | "Common conventions" | -| Rate limits | "Rate limits" | -| Check-run keys, attempts, stale writes | "Check runs" | -| What is not there yet | "Current limitations" | -| A checklist to build against | "Implementation checklist" | - ## Rules to check first 1. **Native or mirror.** An installation keeps its full scopes only on @@ -68,5 +51,8 @@ heading or annotation you need, and read only the matching part. Cite 5. **Opaque tokens and IDs.** Do not build or parse page tokens or IDs ("Pagination", "IDs"). +One section name the index does not make obvious: what happens to a +check-run post that arrives out of order is under "Ordering writes". + Porting an existing GitHub App: use `port-github-app-to-origin` in this plugin. From 0390402f4dea53c78be71734cd600378b00cd1b6 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 30 Sep 2026 05:47:37 +0000 Subject: [PATCH 26/26] State the mirror rule as native versus GitHub mirror Outbound mirrors are not externally shipped, so the skills no longer mention them. Native repositories get full scopes and every write; on a GitHub mirror every event except repository.pushed arrives, calls beyond metadata and contents reads return 403, and merge and default-branch changes are not available. Co-authored-by: ali.nikseresht --- origin-apps/skills/origin-api/SKILL.md | 15 ++++++--------- .../port-github-app-to-origin/references/brief.md | 9 ++++----- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/origin-apps/skills/origin-api/SKILL.md b/origin-apps/skills/origin-api/SKILL.md index 45c3addd2..803dc25a9 100644 --- a/origin-apps/skills/origin-api/SKILL.md +++ b/origin-apps/skills/origin-api/SKILL.md @@ -31,15 +31,12 @@ heading or annotation you need, and read only the matching part. Cite ## Rules to check first -1. **Native or mirror.** An installation keeps its full scopes only on - native repositories (created on Origin) and stable outbound mirrors - (Origin is the source and pushes to GitHub). A stable outbound mirror is - not a merge target: Merge Pull Request works only on native repositories, - and so does changing the default branch. Read each operation's description - for mirror limits. - On a repository mirrored from GitHub, every event except - `repository.pushed` still arrives, and every call beyond metadata and - contents reads returns `403` ("Mirrored repositories", "Events"). +1. **Native or mirror.** A repository is either native (created on Origin) + or a GitHub mirror. On a native repository an installation has its full + scopes and every write. On a GitHub mirror, every event except + `repository.pushed` still arrives, every call beyond metadata and + contents reads returns `403`, and merging a pull request or changing the + default branch is not available ("Mirrored repositories", "Events"). 2. **Subscribe.** Only `installation.*` events arrive without a subscription. A missing subscription produces silence, not an error ("Events"). 3. **Verify, dedupe, acknowledge.** Verify the signature over the raw body diff --git a/origin-apps/skills/port-github-app-to-origin/references/brief.md b/origin-apps/skills/port-github-app-to-origin/references/brief.md index 4117c4f4e..41640b75c 100644 --- a/origin-apps/skills/port-github-app-to-origin/references/brief.md +++ b/origin-apps/skills/port-github-app-to-origin/references/brief.md @@ -110,11 +110,10 @@ the app's own check runs or comments by author → the check run `key`, or a marker the app controls; user sign-in and acting as a user → "Acting on behalf of users" (user confirmation receipt, installation user tokens). -Repositories mirrored from GitHub: an installation can only read metadata -and contents until the mirror becomes a stable outbound mirror (Origin is -the source and pushes to GitHub). Merging a pull request and changing the -default branch work only on native repositories, the ones created on Origin -("Mirrored repositories"). +GitHub mirrors: an installation can only read metadata and contents, and +merging a pull request or changing the default branch is not available. +Every write needs a native repository, one created on Origin ("Mirrored +repositories"). Not in the current spec (ask the team; feedback only if it blocks the main flow): GraphQL (break each query into REST calls); Issues (pull request