Skip to content

Commit 38e9484

Browse files
committed
skills: stop writing new code with legacy ops, lead with refs
Reorder the policy documentation so the per-ref `refs` claim is presented as the way to add branch protection. The repo-wide `ops` claim is now labeled "legacy — do not use in new code" and explicitly redirects to the equivalent `refs: [{ pattern: '*', ops: [...] }]` form. The example procedure (Mint a Force-Push-Prevented Remote URL) and the JWT payload example are updated to use `refs` so anyone copying from the skill writes the modern form.
1 parent d19009d commit 38e9484

1 file changed

Lines changed: 22 additions & 19 deletions

File tree

skills/code-storage/SKILL.md

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ Every request requires a JWT signed with your private key. Tokens are per-reposi
4444
"sub": "@pierre/storage", // Subject (the SDKs set this to the package name)
4545
"repo": "team/project", // Repository the token grants access to
4646
"scopes": ["git:read", "git:write"],
47-
"ops": ["no-force-push"], // Optional repo-wide policy ops (legacy; see below)
4847
"refs": [ // Optional per-ref policy rules (first match wins)
4948
["refs/heads/main", ["no-push"]],
5049
["refs/heads/feature/*", ["no-force-push"]]
@@ -63,21 +62,21 @@ JWT header: `{ "alg": "ES256", "typ": "JWT" }` (RS256 and EdDSA also supported)
6362
| `no-force-push` | TS `OP_NO_FORCE_PUSH` / Py `OP_NO_FORCE_PUSH` / Go `storage.OpNoForcePush` | Rejects force pushes / non-fast-forward ref updates. |
6463
| `no-push` | TS `OP_NO_PUSH` / Py `OP_NO_PUSH` / Go `storage.OpNoPush` | Rejects any push to matching refs. |
6564

66-
#### Repo-wide `ops` (legacy)
65+
#### Per-ref `refs` (preferred — use this for new code)
6766

68-
The optional top-level `ops` claim applies to every ref. On verify it is folded
69-
into a trailing `*` rule when no explicit catch-all exists.
70-
71-
Pass via `getRemoteURL` / `getEphemeralRemoteURL` / `getImportRemoteURL`
72-
(`{ ops: [OP_NO_FORCE_PUSH] }`) or include `ops` directly when minting manually.
73-
74-
#### Per-ref `refs` (preferred)
75-
76-
The optional `refs` claim is an **ordered** array of `[pattern, [ops...]]` tuples.
67+
The `refs` claim is an **ordered** array of `[pattern, [ops...]]` tuples.
7768
Rules are evaluated in declaration order; the first pattern that matches the ref
7869
wins. Patterns may be fully-qualified refs (`refs/heads/main`), prefix globs
7970
(`refs/heads/feature/*`, `refs/tags/*`), or `*` for every ref. Short branch names
80-
like `main` are normalized to `refs/heads/main` on verify.
71+
like `main` are normalized to `refs/heads/main` on verify. `refs` is accepted by
72+
every URL-minting method and every ref-mutating REST method.
73+
74+
#### Repo-wide `ops` (legacy — do not use in new code)
75+
76+
The optional top-level `ops` claim applies to every ref. On verify it is folded
77+
into a trailing `*` rule when no explicit catch-all exists. Only available on
78+
the URL-minting methods (`getRemoteURL` / `getEphemeralRemoteURL` /
79+
`getImportRemoteURL`). Use `refs: [{ pattern: '*', ops: [...] }]` instead.
8180

8281
```typescript
8382
await repo.getRemoteURL({
@@ -822,7 +821,7 @@ const repo = store.repo({ id: 'team/project' });
822821
const safeRemote = await repo.getRemoteURL({
823822
permissions: ['git:write'],
824823
ttl: 3600,
825-
ops: [OP_NO_FORCE_PUSH],
824+
refs: [{ pattern: '*', ops: [OP_NO_FORCE_PUSH] }],
826825
});
827826
// git push to safeRemote — non-fast-forward updates are rejected.
828827
```
@@ -835,14 +834,18 @@ repo = store.repo(id="team/project")
835834
safe_remote = await repo.get_remote_url(
836835
permissions=["git:write"],
837836
ttl=3600,
838-
ops=[OP_NO_FORCE_PUSH],
837+
refs=[{"pattern": "*", "ops": [OP_NO_FORCE_PUSH]}],
839838
)
840839
```
841840

842-
The same `ops` array also works on `getEphemeralRemoteURL` and
843-
`getImportRemoteURL`. For per-ref rules, pass `refs` instead (see Policy
844-
Operations above). When minting JWTs by hand, add `"ops"` or `"refs"` to the
845-
payload before signing.
841+
`refs` is also accepted by `getEphemeralRemoteURL`, `getImportRemoteURL`, and
842+
every ref-mutating REST method (`createBranch`, `merge`, `createCommit`, notes,
843+
tags, etc.) — define the policy once and reuse it. When minting JWTs by hand,
844+
add `"refs"` to the payload before signing.
845+
846+
> The legacy top-level `ops` claim is still accepted on URL-minting methods for
847+
> backwards compatibility (folded into a catch-all `*` rule on verify), but new
848+
> code should use `refs` everywhere.
846849
847850
## PROCEDURE 8: Rollback a Branch
848851

@@ -939,5 +942,5 @@ git push origin feature-branch
939942
| Pagination | Cursor-based. Pass `next_cursor` as `cursor` param. Stop when `has_more: false`. |
940943
| Blob data encoding | Always base64. Max 4 MiB per chunk. Use multiple chunks for large files. |
941944
| `expected_head_sha` | Optimistic lock. Provide current branch tip SHA to enforce fast-forward semantics. |
942-
| Policy ops | JWT-level guards via `ops` (repo-wide) or `refs` (per-ref, first match wins). `no-force-push` (TS/Py `OP_NO_FORCE_PUSH`, Go `OpNoForcePush`) blocks non-FF updates; `no-push` (`OP_NO_PUSH`/`OpNoPush`) blocks pushes to matching refs. |
945+
| Policy ops | JWT-level guards via `refs` (per-ref, first match wins; preferred). `no-force-push` (TS/Py `OP_NO_FORCE_PUSH`, Go `OpNoForcePush`) blocks non-FF updates; `no-push` (`OP_NO_PUSH`/`OpNoPush`) blocks pushes to matching refs. Top-level `ops` is a legacy alias on URL-minting methods only. |
943946
| Merge endpoint | `POST /repos/merge`; strategies: `merge`, `ff_only`, `ff_prefer`; optional `squash` (not with `ff_only`). 409 on conflict. |

0 commit comments

Comments
 (0)