> ## Documentation Index
> Fetch the complete documentation index at: https://code.storage/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Git Notes

> Attach metadata to commits without changing commit history, and keep different note streams isolated by ref.

Git notes attach text metadata to Git objects without changing the object or the commit history
around it. They are useful for build status, review state, an agent's verdict on a change, a reward
graded on one commit, and other context that should travel with a commit but should not be committed
into the repository tree.

Code Storage supports the default Git notes ref and custom notes refs. The default is
`refs/notes/commits`, which matches Git's own default for `git notes`.

## Choose a notes ref

Use the default ref when all notes belong to one stream. Use a custom notes ref when different
systems need independent note namespaces. For example:

* `ci` for build and deploy status
* `reviews` for review summaries
* `agents/verdicts` for an agent's judgment of one commit

Pass `notes_ref` to the note methods of the SDK to target a custom notes ref. `object_ref` names the
Git object of the note:

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
import { Pierre } from '@pierre/sdk';

const pierre = new Pierre(); // reads PIERRE_ORG and PIERRE_PRIVATE_KEY

const sha = 'b003fc78805954584e1ee364a4ad39d7c79e819a';
const notesRef = 'reviews';

await pierre.repos.notes.create({
  repo_name: 'acme/web',
  object_ref: sha,
  note: 'Reviewed. Ready for production.',
  notes_ref: notesRef,
});

const note = await pierre.repos.notes.get({
  repo_name: 'acme/web',
  object_ref: sha,
  notes_ref: notesRef,
});
console.log(note.note, note.ref_sha);
```

```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
from pierre import Pierre

pierre = Pierre()  # reads PIERRE_ORG and PIERRE_PRIVATE_KEY

sha = "b003fc78805954584e1ee364a4ad39d7c79e819a"
notes_ref = "reviews"

pierre.repos.notes.create(
    repo_name="acme/web",
    object_ref=sha,
    note="Reviewed. Ready for production.",
    notes_ref=notes_ref,
)

note = pierre.repos.notes.get(repo_name="acme/web", object_ref=sha, notes_ref=notes_ref)
print(note["note"], note["ref_sha"])
```

```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
import (
	"context"
	"fmt"

	pierre "github.com/pierrecomputer/sdk/go"
)

ctx := context.Background()
client, err := pierre.NewClient() // reads PIERRE_ORG and PIERRE_PRIVATE_KEY
if err != nil {
	return err
}

sha := "b003fc78805954584e1ee364a4ad39d7c79e819a"
notesRef := pierre.String("reviews")

_, err = client.Repos.Notes.Create(ctx, pierre.ReposNotesCreateParams{
	RepoName:  "acme/web",
	ObjectRef: pierre.String(sha),
	Note:      pierre.String("Reviewed. Ready for production."),
	NotesRef:  notesRef,
})
if err != nil {
	return err
}

note, err := client.Repos.Notes.Get(ctx, pierre.ReposNotesGetParams{
	RepoName:  "acme/web",
	ObjectRef: pierre.String(sha),
	NotesRef:  notesRef,
})
if err != nil {
	return err
}
fmt.Println(note.Note, note.RefSHA)
```

The HTTP API uses the same names. Pass `notes_ref` and `object_ref` in the write request bodies, and
as query parameters when you read or delete a note. `POST /notes` creates a note, and
`POST /notes/append` adds text to a note:

```bash theme={"theme":{"light":"github-light","dark":"min-dark"}}
curl "$PIERRE_API_BASE_URL/api/repos/my-repo/notes" \
  -H "Authorization: Bearer $PIERRE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "object_ref": "abc123def456",
    "note": "Reviewed. Ready for production.",
    "notes_ref": "reviews"
  }'
```

```bash theme={"theme":{"light":"github-light","dark":"min-dark"}}
curl "$PIERRE_API_BASE_URL/api/repos/my-repo/notes/append" \
  -H "Authorization: Bearer $PIERRE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "object_ref": "abc123def456",
    "note": "Deployed to production.",
    "notes_ref": "reviews"
  }'
```

```bash theme={"theme":{"light":"github-light","dark":"min-dark"}}
curl "$PIERRE_API_BASE_URL/api/repos/my-repo/notes?object_ref=abc123def456&notes_ref=reviews" \
  -H "Authorization: Bearer $PIERRE_TOKEN"
```

```bash theme={"theme":{"light":"github-light","dark":"min-dark"}}
curl -X DELETE "$PIERRE_API_BASE_URL/api/repos/my-repo/notes?object_ref=abc123def456&notes_ref=reviews" \
  -H "Authorization: Bearer $PIERRE_TOKEN"
```

## Ref rules

An empty or omitted `notes_ref` uses `refs/notes/commits`.

Custom notes refs are canonicalized like Git's `git notes --ref` option:

| Input | Canonical ref |
| - | - |
| `reviews` | `refs/notes/reviews` |
| `notes/reviews` | `refs/notes/reviews` |
| `refs/notes/reviews` | `refs/notes/reviews` |

If the input is already fully qualified with a `refs/` prefix, it must be a safe `refs/notes/*` ref.
Short inputs are resolved inside `refs/notes/` before the same validation runs.

The server rejects refs outside the notes namespace and unsafe ref paths, including parent
traversal, empty path segments, trailing slashes, trailing dots, and `.lock` suffixes. Invalid notes
refs return `400`.

## List notes refs

Use [`repos.notes.refs.list`](https://code.storage/docs/api/repos/notes-refs-list) to discover custom notes namespaces before
reading individual notes. It returns notes refs under a `refs/notes/` prefix in lexical order.

```bash theme={"theme":{"light":"github-light","dark":"min-dark"}}
curl "$PIERRE_API_BASE_URL/api/repos/my-repo/notes/refs?prefix=reviews/&limit=20" \
  -H "Authorization: Bearer $PIERRE_TOKEN"
```

```json theme={"theme":{"light":"github-light","dark":"min-dark"}}
{
  "refs": [
    {
      "cursor": "opaque-cursor",
      "ref": "refs/notes/reviews/session-a",
      "sha": "a1b2c3d4e5f6..."
    }
  ],
  "next_cursor": "opaque-cursor",
  "has_more": true,
  "prefix": "refs/notes/reviews/"
}
```

The `prefix` is canonicalized like `notes_ref` above: short inputs resolve inside `refs/notes/`, and
a fully qualified `refs/notes/*` prefix also works. A trailing slash restricts the listing to
descendants of that ref. Omit `prefix` to list everything under `refs/notes/`.

| Prefix | Canonical prefix | Matches |
| - | - | - |
| *(omitted)* | `refs/notes/` | All notes refs |
| `reviews` | `refs/notes/reviews` | `refs/notes/reviews` and descendants |
| `reviews/` | `refs/notes/reviews/` | Descendants under `refs/notes/reviews/` |
| `refs/notes/reviews` | `refs/notes/reviews` | `refs/notes/reviews` and descendants |

Use `next_cursor` as the `cursor` query parameter to read the next page. `limit` defaults to `20`
and is capped at `100`. Responses echo the normalized full prefix and full notes ref names. Invalid
prefixes and cursors return `400`.

## Write behavior

[`repos.notes.create`](https://code.storage/docs/api/repos/note-create-or-append) creates a new note and fails if the target
already has a note on the selected notes ref. To replace a note, delete it first with
[`repos.notes.delete`](https://code.storage/docs/api/repos/note-delete), then create the new note.

[`repos.notes.append`](https://code.storage/docs/api/repos/note-append) appends text to the note on the selected ref. If no
note exists, the server creates one.

`repos.notes.delete` removes the note from the selected ref.

`notes_ref` in write responses is always the canonical full ref, such as `refs/notes/reviews`.
`ref_sha` in read responses is the current SHA of the notes ref that was read.

## Concurrency and policies

`expected_notes_ref_sha` protects the selected notes ref with optimistic concurrency. When
`notes_ref` is omitted, the guard applies to `refs/notes/commits`. When `notes_ref` is set, it
applies to that custom notes ref.

The ref policies of the token apply to the selected notes ref for `repos.notes.create`,
`repos.notes.append`, and `repos.notes.delete`. See [Ref Policies](https://code.storage/docs/repos/ref-policies.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.