a73x

specs/015-gerrit-style-patchsets/data-model.md

Ref:   Size: 3.7 KiB

# Data Model: Patch Revisions

## Entities

### Revision

A numbered snapshot of the branch state at a point in time.

| Field | Type | Description |
|-------|------|-------------|
| number | u32 | 1-indexed, derived from topological DAG order during materialization |
| commit | String | Branch tip commit OID at time of recording |
| tree | String | Tree OID of the commit (for fast interdiff without re-walking history) |
| body | Option\<String\> | Optional author note (e.g., "addressed review feedback") |
| timestamp | String | ISO 8601 timestamp of the PatchRevision event |

**Identity**: Revision number is derived, not stored. The commit OID is the true identifier.
**Uniqueness**: Within a patch, no two revisions share the same commit OID (dedup during materialization).
**Lifecycle**: Append-only. Revisions are never deleted or modified.

### PatchState (modified)

Existing entity, gains a `revisions` field.

| New Field | Type | Description |
|-----------|------|-------------|
| revisions | Vec\<Revision\> | Ordered list of all revisions, oldest first |

**Derivation**: Revision 1 comes from `PatchCreate` event (if `commit`/`tree` fields present). Subsequent revisions come from `PatchRevision` events in topological order. Duplicates (same commit OID) are skipped.

### InlineComment (modified)

Existing entity, gains a `revision` field.

| New Field | Type | Description |
|-----------|------|-------------|
| revision | Option\<u32\> | Revision number this comment was made on. None for legacy comments. |

### Review (modified)

Existing entity, gains a `revision` field.

| New Field | Type | Description |
|-----------|------|-------------|
| revision | Option\<u32\> | Revision number this review was made on. None for legacy reviews. |

## Event Schema Changes

### New: `patch.revision`

```rust
Action::PatchRevision {
    commit: String,     // branch tip commit OID
    tree: String,       // tree OID of the commit
    body: Option<String>, // optional revision note
}
```

Serialized as:
```json
{
  "type": "patch.revision",
  "commit": "abc123...",
  "tree": "def456...",
  "body": "addressed review feedback"
}
```

### Modified: `patch.create`

Add optional fields (backward-compatible via `skip_serializing_if`):

```rust
Action::PatchCreate {
    title: String,
    body: String,
    base_ref: String,
    branch: String,
    fixes: Option<String>,
    commit: Option<String>,  // NEW: branch tip at creation
    tree: Option<String>,    // NEW: tree OID at creation
}
```

### Modified: `patch.inline_comment`

Add optional field:

```rust
Action::PatchInlineComment {
    file: String,
    line: u32,
    body: String,
    revision: Option<u32>,  // NEW: revision number at time of commenting
}
```

### Modified: `patch.review`

Add optional field:

```rust
Action::PatchReview {
    verdict: ReviewVerdict,
    body: String,
    revision: Option<u32>,  // NEW: revision number at time of review
}
```

## State Transitions

Revisions are append-only. The materialization flow:

```
PatchCreate (with commit/tree) → revision 1
PatchRevision (commit A)       → revision 2
PatchRevision (commit A)       → SKIPPED (dedup: same commit OID)
PatchRevision (commit B)       → revision 3
```

## Relationships

```
Patch 1──* Revision       (a patch has zero or more revisions)
Revision 1──* InlineComment (a revision has zero or more inline comments)
Revision 1──* Review        (a revision has zero or more reviews)
Patch 1──* Comment          (thread comments belong to the patch, not a revision)
```

## Config Model

### Merge Policy (in `refs/collab/config`)

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| merge.require_approval_on_latest | bool | false | When true, patch merge requires approval on the latest revision |