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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .spectral-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"counts": {
"array-items": 4,
"no-$ref-siblings": 64,
"operation-operationId": 45,
"operation-operationId": 48,
"operation-success-response": 1
}
}
41 changes: 29 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ empty, and it would have cost the whole gate the day one was written.
The four decision files under `_project/` are the same idea applied to
structure. Each
holds a decision that would otherwise be spread across the document — sixty-odd
`servers` blocks, eleven tag groups' worth of operations, 187 page URLs — where
`servers` blocks, eleven tag groups' worth of operations, 203 page URLs — where
it can be read, reviewed and changed in one place. `scripts/build.py` applies
them back onto `openapi.yaml` and CI fails if the document and the decisions
disagree.
Expand All @@ -62,12 +62,12 @@ disagree.
| | |
|---|---|
| OpenAPI | 3.0.0 |
| Paths / operations | 121 / 187 |
| Components | 480, of which 442 schemas |
| Tags | 42, across 6 navigation groups |
| Paths / operations | 131 / 203 |
| Components | 521, of which 479 schemas |
| Tags | 46, across 6 navigation groups |
| Servers | 5 hosts across 4 planes — managed and self-hosted gateway, control plane, admin, admin-in-path |
| Security | 1 scheme: `Authorization: Bearer` |
| Docs URLs | 187 — one `x-mint.href` per operation, all unique |
| Docs URLs | 203 — one `x-mint.href` per operation, all unique |
| Prose fields stripped | 4,221 |
| Written descriptions | 0 — blocked on KB access |

Expand Down Expand Up @@ -157,7 +157,7 @@ changing either is a migration rather than a spec edit.**
```

`{tag-path}` is the tag kebab-cased, with `>` becoming a path separator, and
`{operation-slug}` is the `operationId` kebab-cased. The **45 operations with no
`{operation-slug}` is the `operationId` kebab-cased. The **48 operations with no
`operationId` get a slug from their method and path** — `by-id` for a `{id}`
segment — and *not* an `operationId`. The distinction is why this could be done
while Phase 2 waits: an `operationId` is a contract key that SDK generators turn
Expand Down Expand Up @@ -266,11 +266,11 @@ shrink, never grow. What is in there today:
| Count | Rule | What it is |
|---|---|---|
| 64 | `no-$ref-siblings` | `title`, `nullable`, `type` and `x-oaiExpandable` beside a `$ref`, which OpenAPI 3.0 silently ignores |
| 45 | `operation-operationId` | The known gap |
| 48 | `operation-operationId` | The known gap. 45 inherited; 3 more with the analytics groups ported on 2026-11-02, which have none upstream either |
| 4 | `array-items` | Arrays with no `items` |
| 1 | `operation-success-response` | `GET /realtime` declares no 2xx |

114 findings across 4 rules, down from 193 across 6 before the drops.
117 findings across 4 rules, down from 193 across 6 before the drops.
`oas3-unused-component` went from 26 to **0**: the drop pruned every component
nothing reaches, including 52 the base was already carrying unreferenced.

Expand Down Expand Up @@ -310,7 +310,7 @@ domain and not subordinate to the KB (recorded exemption, Q6, 2026-09-07). The
reconciliation loop is therefore three-node — KB ↔ spec ↔ docs — and drift from
*either* side is a defect, with no authoritative side to fall back on.

`x-airs-provenance` is scaffolded on all 187 operations with `claims` empty. It
`x-airs-provenance` is scaffolded on all 203 operations with `claims` empty. It
is not only "which claim supports this description"; it is the **join key that
makes drift detectable**. Without a claim reference on an operation, nothing can
tell that the KB moved and the spec did not.
Expand Down Expand Up @@ -503,6 +503,17 @@ about files that still decide things: does `openapi.yaml` agree with
`tags-map.yaml`? The base is gone; the decisions taken against it are still
enforced.

Closed means nothing *follows* Portkey automatically, not that nothing may be
taken from it. Changes are ported one at a time, as decisions, structure only:
`POST /decisions` from Portkey-AI/openapi#160 (2026-09-29), and on 2026-11-02
everything upstream merged up to `adfd74b` — the Anthropic integration and its
WIF/OAuth auth, integration tags, the deployment and workspace analytics
filters, three analytics groups, Egress Allowlist (admin), Access Policies
(control plane), the Anthropic Messages API (gateway), and the
`createThreadMessage` rename. Each carries its source commit in its commit
message and, in `_project/planes.yaml`, in its `how`. What came over is
Portkey's shape, as unverified against the Prisma AIRS API as the rest.

The strip itself was recorded in `PROSE-INVENTORY.csv` — a pointer, length and
SHA-256 digest per field, never the text. It was frozen at the end of Phase 1,
because it could only be derived by diffing against the base, and removed on
Expand All @@ -521,7 +532,7 @@ wanted.
reachable from this workspace. The left-hand column of the inherit/re-ground
split is *machine-verifiable in principle* — send a request, compare the
response — and that verification has not been done.
- **45 operations have no `operationId`** (listed in `build-report.txt`). An
- **48 operations have no `operationId`** (listed in `build-report.txt`). An
inherited gap. Inventing identifiers is not the same as recovering them, so
they are reported for engineering rather than filled in. This also blocks SDK
generation: Stainless, Speakeasy and Fern all derive method names from it.
Expand All @@ -535,7 +546,7 @@ wanted.
- **No code samples.** Mintlify generates one per operation from the schema, and
it dumps every property: `POST /chat/completions` renders all 24, `seed` and
`logit_bias` included, because Mintlify does not use `required` to trim the
example. Hand-written cURL samples for all 187 operations are Phase 3 work.
example. Hand-written cURL samples for all 203 operations are Phase 3 work.
Until then the generated sample is correct but verbose. cURL only is the
intended end state — a single `x-codeSamples` entry suppresses the other
language tabs, and other languages are a separate decision.
Expand All @@ -546,7 +557,13 @@ wanted.
For the three `PUT`s, requiring nothing may already be correct, since a
partial update where every field is optional is a normal design. Wants
engineering.
- **114 lint findings are baselined**, all inherited. See the linting section.
- **117 lint findings are baselined**, all inherited or carried in with ported
structure. See the linting section.
- **Lint fails on `path-params`**, and has since PR #3 (2026-09-18).
`POST /api-keys/{sub-type}` and `/api-keys/{id}` are the same template to
OpenAPI, which forbids that. It is not baselined, because it is a defect in
the URL design rather than inherited debt: either the create path changes or
the two are merged into one path item. Wants an API decision.
- **One operation is documented as needing no credentials** —
`GET /model-configs/pricing/{provider}/{model}`, which the base declared with
`security: []`. Preserved rather than quietly reversed: making it require auth
Expand Down
18 changes: 17 additions & 1 deletion _project/hrefs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,20 @@ hrefs:
PUT /deployments/{deploymentId}: /aigw/api-reference/deployments/update-deployment
DELETE /deployments/{deploymentId}: /aigw/api-reference/deployments/delete-deployment
GET /deployments/{deploymentId}/ping: /aigw/api-reference/deployments/ping-deployment
pinned: []
GET /analytics/groups/a2a: /aigw/api-reference/analytics/groups/get-analytics-groups-a2a
GET /analytics/groups/mcp: /aigw/api-reference/analytics/groups/get-analytics-groups-mcp
GET /analytics/groups/workspaces: /aigw/api-reference/analytics/groups/get-analytics-groups-workspaces
GET /egress-allowlist: /aigw/api-reference/egress-allowlist/list-egress-allowlist
POST /egress-allowlist: /aigw/api-reference/egress-allowlist/create-egress-allowlist-entry
GET /egress-allowlist/{egressAllowlistId}: /aigw/api-reference/egress-allowlist/get-egress-allowlist-entry
PUT /egress-allowlist/{egressAllowlistId}: /aigw/api-reference/egress-allowlist/update-egress-allowlist-entry
DELETE /egress-allowlist/{egressAllowlistId}: /aigw/api-reference/egress-allowlist/delete-egress-allowlist-entry
POST /policies/access: /aigw/api-reference/access-policies/create-access-policy
GET /policies/access: /aigw/api-reference/access-policies/list-access-policies
GET /policies/access/{accessPolicyId}: /aigw/api-reference/access-policies/get-access-policy
PUT /policies/access/{accessPolicyId}: /aigw/api-reference/access-policies/update-access-policy
DELETE /policies/access/{accessPolicyId}: /aigw/api-reference/access-policies/delete-access-policy
POST /messages: /aigw/api-reference/messages/create-message
POST /messages/count_tokens: /aigw/api-reference/messages/count-message-tokens
pinned:
- POST /threads/{thread_id}/messages
17 changes: 17 additions & 0 deletions _project/planes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
# (Integrations, MCP Integrations, Secret References,
# Org Guardrails, Deployments) and written out here
# as the paths those capabilities cover
# vrushank.2026-11-02 -- ported from Portkey-AI/openapi after the base
# linkage closed (upstream commits up to adfd74b);
# the plane was decided by Vrushank, not read off
# Portkey's server overrides

# Tags whose operations are deliberately not all on one plane. `check.py` fails
# on any other tag that spans two, because the split was decided by capability
Expand Down Expand Up @@ -60,6 +64,9 @@ control-plane:
"/analytics/groups/ai-models": inherited
"/analytics/groups/metadata/{metadataKey}": inherited
"/analytics/groups/provider": inherited
"/analytics/groups/a2a": vrushank.2026-11-02
"/analytics/groups/mcp": vrushank.2026-11-02
"/analytics/groups/workspaces": vrushank.2026-11-02
"/analytics/groups/users": inherited
"/analytics/summary/cache": inherited
"/api-keys": inherited
Expand All @@ -83,6 +90,9 @@ control-plane:
"/mcp-servers/{mcpServerId}/user-access": inherited
"/model-configs/pricing/{provider}/{model}": classification.yaml
"/models/{model}": classification.yaml
# Ported from Portkey-AI/openapi 2bc08ce and 700ece6 (Vrushank, 2026-11-02).
"/policies/access": vrushank.2026-11-02
"/policies/access/{accessPolicyId}": vrushank.2026-11-02
"/policies/rate-limits": classification.yaml
"/policies/rate-limits/{rateLimitsPolicyId}": classification.yaml
"/policies/usage-limits": classification.yaml
Expand Down Expand Up @@ -117,6 +127,10 @@ admin:
"/deployments/{deploymentId}": vrushank.2026-09-15
"/deployments/{deploymentId}/ping": vrushank.2026-09-15

# Ported from Portkey-AI/openapi 8f91369. An admin API (Vrushank, 2026-11-02).
"/egress-allowlist": vrushank.2026-11-02
"/egress-allowlist/{egressAllowlistId}": vrushank.2026-11-02

# The org-level guardrails, which the API serves at the same suffix as the
# control-plane ones. The `/admin/v2` in these keys is the server prefix, moved
# here because a document cannot hold two paths spelled `/guardrails`.
Expand All @@ -140,6 +154,9 @@ gateway:
"/completions": inherited
"/decisions": portkey-openapi#160
"/embeddings": inherited
# Anthropic Messages API, ported from Portkey-AI/openapi 9283142 and 6ac6e5d.
"/messages": vrushank.2026-11-02
"/messages/count_tokens": vrushank.2026-11-02
"/files": inherited
"/files/{file_id}": inherited
"/files/{file_id}/content": inherited
Expand Down
25 changes: 14 additions & 11 deletions build-report.txt
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
Generated by scripts/build.py from openapi.yaml.

operations 188
paths 122
schemas 446
tags 43
operations 203
paths 131
schemas 479
tags 46

docs URLs 188
docs URLs 203
x-mint.href on every operation, from _project/hrefs.yaml. These are
public URLs: the build re-derives each one and fails if it has moved,
because a tag rename changes them silently and links point at them.

prose 0 written of 420 fields
prose 0 written of 453 fields
Authored in overlays/docs-prose.yaml under the grounding gate and
merged into openapi.yaml by this script. Every prose field here is
stripped and reapplied on each build, so nothing reaches the
published document without passing that gate.

servers 1 root + 69 path override(s)
servers 1 root + 76 path override(s)
Every URL comes from _project/servers.yaml; which plane a path is on
comes from _project/planes.yaml. Edit a host there and rebuild. The
first entry in a plane is the one Mintlify builds its sample from;
the rest appear in its base-URL dropdown. The gateway is the root
block, so its paths carry no override.
gateway 53 path(s)
gateway 55 path(s)
https://aigw.portkey.ai/v1 -- Managed
https://self-hosted-gateway-url.example.com/v1 -- Self-hosted -- replace with your own gateway host
control-plane 51 path(s)
control-plane 56 path(s)
https://api.apps.paloaltonetworks.com/ai_gw/v2 -- Managed
admin 14 path(s)
admin 16 path(s)
https://api.apps.paloaltonetworks.com/ai_gw/admin/v2 -- Managed
admin-in-path 4 path(s)
https://api.apps.paloaltonetworks.com/ai_gw -- Managed
Expand All @@ -48,7 +48,7 @@ components pruned this run 0

null defaults dropped this run 0

operations lacking operationId 45
operations lacking operationId 48
Inherited gaps, not introduced here. operationId values are never
renamed, and inventing them is not the same as recovering them, so
they are reported for engineering rather than filled in. Phase 2.
Expand All @@ -73,10 +73,13 @@ operations lacking operationId 45
GET /analytics/graphs/tokens
GET /analytics/graphs/users
GET /analytics/graphs/users/requests
GET /analytics/groups/a2a
GET /analytics/groups/ai-models
GET /analytics/groups/mcp
GET /analytics/groups/metadata/{metadataKey}
GET /analytics/groups/provider
GET /analytics/groups/users
GET /analytics/groups/workspaces
GET /analytics/summary/cache
GET /api-keys
GET /api-keys/{id}
Expand Down
44 changes: 43 additions & 1 deletion docs-navigation.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,17 @@
"POST /images/variations"
]
},
{
"group": "Messages",
"openapi": {
"source": "https://raw.githubusercontent.com/PaloAltoNetworks/openapi/refs/heads/main/openapi.yaml",
"directory": "api-reference"
},
"pages": [
"POST /messages",
"POST /messages/count_tokens"
]
},
{
"group": "Model Pricing",
"openapi": {
Expand Down Expand Up @@ -465,6 +476,20 @@
"POST /api-keys/{id}/rotate"
]
},
{
"group": "Access Policies",
"openapi": {
"source": "https://raw.githubusercontent.com/PaloAltoNetworks/openapi/refs/heads/main/openapi.yaml",
"directory": "api-reference"
},
"pages": [
"POST /policies/access",
"GET /policies/access",
"GET /policies/access/{accessPolicyId}",
"PUT /policies/access/{accessPolicyId}",
"DELETE /policies/access/{accessPolicyId}"
]
},
{
"group": "Deployments",
"openapi": {
Expand All @@ -480,6 +505,20 @@
"GET /deployments/{deploymentId}/ping"
]
},
{
"group": "Egress Allowlist",
"openapi": {
"source": "https://raw.githubusercontent.com/PaloAltoNetworks/openapi/refs/heads/main/openapi.yaml",
"directory": "api-reference"
},
"pages": [
"GET /egress-allowlist",
"POST /egress-allowlist",
"GET /egress-allowlist/{egressAllowlistId}",
"PUT /egress-allowlist/{egressAllowlistId}",
"DELETE /egress-allowlist/{egressAllowlistId}"
]
},
{
"group": "Org Guardrails",
"openapi": {
Expand Down Expand Up @@ -568,7 +607,10 @@
"GET /analytics/groups/users",
"GET /analytics/groups/ai-models",
"GET /analytics/groups/provider",
"GET /analytics/groups/metadata/{metadataKey}"
"GET /analytics/groups/metadata/{metadataKey}",
"GET /analytics/groups/a2a",
"GET /analytics/groups/mcp",
"GET /analytics/groups/workspaces"
]
},
{
Expand Down
Loading
Loading