|
| 1 | + |
| 2 | +# Continuous review |
| 3 | + |
| 4 | +Continuous review is the system that tracks candidates for promotion, evaluates |
| 5 | +risk, records review artifacts, and provides a control plane for gates and |
| 6 | +required actions. It is designed to make review deterministic and auditable |
| 7 | +while still allowing humans and agents to step in when needed. |
| 8 | + |
| 9 | +At a high level, the flow is: policy defines expectations, Stage 0 analysis |
| 10 | +records deterministic signals, candidates are enqueued for promotion, gates and |
| 11 | +review packets provide evidence and findings, and checkpoints record how far |
| 12 | +review has progressed. |
| 13 | + |
| 14 | +## Big picture concepts |
| 15 | + |
| 16 | +- A policy snapshot is an immutable, hashed set of rules for a target branch. |
| 17 | +- A Stage 0 analysis captures deterministic signals for a reference. |
| 18 | +- A promotion queue orders candidates that want to land on a target branch. |
| 19 | +- An integration candidate represents one promotion attempt and tracks its |
| 20 | + status and required actions. |
| 21 | +- Review packets summarize findings, evidence, and chaptering for a candidate. |
| 22 | +- Review checkpoints record how far a reviewer has read through references. |
| 23 | +- Gates are discrete checks (policy, review, build/test) that produce |
| 24 | + attestations. |
| 25 | + |
| 26 | +## Key building blocks |
| 27 | + |
| 28 | +### Policy snapshots |
| 29 | + |
| 30 | +Policy snapshots are immutable rule bundles associated with a target branch. The |
| 31 | +`PolicySnapshotId` is a SHA256 hash of the policy contents plus the parser |
| 32 | +version. They are used by the queue, gates, Stage 0 analysis, and review |
| 33 | +analysis to ensure deterministic inputs. |
| 34 | + |
| 35 | +Current policy endpoints: |
| 36 | + |
| 37 | +- `POST /policy/current` returns the active snapshot for a target branch. |
| 38 | +- `POST /policy/acknowledge` records an acknowledgement for a snapshot. |
| 39 | + |
| 40 | +### Stage 0 analysis |
| 41 | + |
| 42 | +Stage 0 is the deterministic, non-model analysis layer. When a reference of type |
| 43 | +Commit, Checkpoint, or Promotion is created, the server records a Stage 0 |
| 44 | +analysis with a deterministic risk profile. Today, that profile is the default |
| 45 | +structure populated with the reference ID, policy snapshot ID (if available), |
| 46 | +and a timestamp. It is persisted through the Stage0 actor and can be read |
| 47 | +internally for later analysis. |
| 48 | + |
| 49 | +### Promotion queue |
| 50 | + |
| 51 | +Each target branch has a promotion queue that stores the list of candidate IDs |
| 52 | +and the current queue state. |
| 53 | + |
| 54 | +Queue state values: |
| 55 | + |
| 56 | +- `Idle` |
| 57 | +- `Running` |
| 58 | +- `Paused` |
| 59 | +- `Degraded` |
| 60 | + |
| 61 | +Queues are created implicitly on first enqueue. Initialization requires a policy |
| 62 | +snapshot ID; if the queue does not exist and you enqueue without a policy |
| 63 | +snapshot, the server rejects the request. |
| 64 | + |
| 65 | +### Integration candidates |
| 66 | + |
| 67 | +An integration candidate is the unit of work that moves through continuous |
| 68 | +review. It captures the target branch, optional work item, policy snapshot, |
| 69 | +status, and any required actions, conflicts, gate attestations, and review |
| 70 | +packet linkage. |
| 71 | + |
| 72 | +Candidate status values: |
| 73 | + |
| 74 | +- `Pending` |
| 75 | +- `Ready` |
| 76 | +- `Running` |
| 77 | +- `Blocked` |
| 78 | +- `Failed` |
| 79 | +- `Succeeded` |
| 80 | +- `Quarantined` |
| 81 | +- `Canceled` |
| 82 | + |
| 83 | +Candidates are created when you enqueue a candidate ID that does not yet exist. |
| 84 | + |
| 85 | +### Gates and gate attestations |
| 86 | + |
| 87 | +Gates are named checks with a definition (name, version, execution mode). |
| 88 | +Running a gate produces a GateAttestation that records the result, timestamp, |
| 89 | +and metadata. Gate attestations are stored in their own actor and referenced by |
| 90 | +the candidate. |
| 91 | + |
| 92 | +Built-in gates today: |
| 93 | + |
| 94 | +- `policy` (synchronous): passes if a policy snapshot exists, blocks otherwise. |
| 95 | +- `review` (synchronous): stub, currently always passes. |
| 96 | +- `build-test` (async callback): stub, currently returns `Skipped`. |
| 97 | + |
| 98 | +### Evidence selection |
| 99 | + |
| 100 | +Evidence selection uses a deterministic budgeted selector to build slices from a |
| 101 | +diff. It supports redaction patterns and scores slices based on change magnitude |
| 102 | +and Stage 0 signals such as sensitive paths and dependency config changes. The |
| 103 | +resulting EvidenceSetSummary is used for chaptering and model analysis receipts. |
| 104 | + |
| 105 | +### Review packets |
| 106 | + |
| 107 | +A review packet is the primary artifact for a candidate review. It contains |
| 108 | +summary text, chapters, findings, optional evidence summaries, and optional gate |
| 109 | +summaries. Chapters are built deterministically by grouping paths on the top- |
| 110 | +level folder segment. The chapter ID is a SHA256 hash of the ordered path list. |
| 111 | + |
| 112 | +Review packets are stored in the Review actor through `UpsertPacket` events. |
| 113 | +There is no public endpoint for upserting packets yet, so review packets are |
| 114 | +typically created by internal processes. |
| 115 | + |
| 116 | +### Review checkpoints |
| 117 | + |
| 118 | +Review checkpoints store how far a reviewer has read through references for a |
| 119 | +candidate or promotion group. A checkpoint includes the reviewer, reviewed-up-to |
| 120 | +reference ID, and policy snapshot ID. |
| 121 | + |
| 122 | +### Required actions |
| 123 | + |
| 124 | +Required actions are machine-readable tasks attached to a candidate, such as |
| 125 | +`AcknowledgePolicyChange` or `ResolveConflict`. The server recomputes required |
| 126 | +actions on demand and updates the candidate if the computed list differs from |
| 127 | +the stored list. |
| 128 | + |
| 129 | +The current required-actions logic adds: |
| 130 | + |
| 131 | +- `AcknowledgePolicyChange` if the candidate has no policy snapshot ID. |
| 132 | +- `ResolveConflict` if conflicts are present. |
| 133 | +- `RunGate` if the candidate is `Blocked`. |
| 134 | +- `FixGateFailure` if the candidate is `Failed`. |
| 135 | + |
| 136 | +## End-to-end flow (today) |
| 137 | + |
| 138 | +1) A reference (commit/checkpoint/promotion) is created. |
| 139 | +2) Stage 0 analysis is recorded with a deterministic risk profile. |
| 140 | +3) A candidate is enqueued for a target branch. |
| 141 | +4) The promotion queue is initialized (if needed) with a policy snapshot ID. |
| 142 | +5) The candidate can be queried, its required actions computed, and gates rerun |
| 143 | + on demand. |
| 144 | +6) Review packets and findings can be read (if they exist) and findings can be |
| 145 | + resolved. |
| 146 | +7) Review checkpoints can be recorded as reviewers progress. |
| 147 | + |
| 148 | +Note: queue processing and gate execution orchestration are not currently |
| 149 | +automatic in the server; they are expected to be driven by external services or |
| 150 | +manual commands. |
| 151 | + |
| 152 | +## API surface |
| 153 | + |
| 154 | +### Queue and candidate endpoints |
| 155 | + |
| 156 | +- `POST /queue/status` |
| 157 | +- `POST /queue/enqueue` |
| 158 | +- `POST /queue/pause` |
| 159 | +- `POST /queue/resume` |
| 160 | +- `POST /queue/dequeue` |
| 161 | +- `POST /candidate/get` |
| 162 | +- `POST /candidate/cancel` |
| 163 | +- `POST /candidate/retry` |
| 164 | +- `POST /candidate/required-actions` |
| 165 | +- `POST /candidate/attestations` |
| 166 | +- `POST /candidate/gate/rerun` |
| 167 | + |
| 168 | +Example: enqueue a candidate. |
| 169 | + |
| 170 | +```json |
| 171 | +{ |
| 172 | + "TargetBranchId": "8c6f3b1b-53b1-4a1b-84fd-9c8b5a4e2e11", |
| 173 | + "CandidateId": "4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c", |
| 174 | + "PolicySnapshotId": "e3b0c44298fc1c149afbf4c8996fb924", |
| 175 | + "WorkItemId": "f88b46e2-5c36-4b52-9e36-716f7d7a9a8b", |
| 176 | + "PromotionGroupId": "3d5c4d9a-0123-4567-89ab-987654321000", |
| 177 | + "CorrelationId": "corr-1001" |
| 178 | +} |
| 179 | +``` |
| 180 | + |
| 181 | +Example: fetch required actions for a candidate. |
| 182 | + |
| 183 | +```json |
| 184 | +{ |
| 185 | + "CandidateId": "4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c", |
| 186 | + "CorrelationId": "corr-1002" |
| 187 | +} |
| 188 | +``` |
| 189 | + |
| 190 | +Example: rerun a gate. |
| 191 | + |
| 192 | +```json |
| 193 | +{ |
| 194 | + "CandidateId": "4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c", |
| 195 | + "GateName": "policy", |
| 196 | + "CorrelationId": "corr-1003" |
| 197 | +} |
| 198 | +``` |
| 199 | + |
| 200 | +### Review endpoints |
| 201 | + |
| 202 | +- `POST /review/packet` |
| 203 | +- `POST /review/checkpoint` |
| 204 | +- `POST /review/resolve` |
| 205 | +- `POST /review/deepen` (stub, not implemented) |
| 206 | + |
| 207 | +Example: open a review packet for a candidate. |
| 208 | + |
| 209 | +```json |
| 210 | +{ |
| 211 | + "CandidateId": "4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c", |
| 212 | + "CorrelationId": "corr-2001" |
| 213 | +} |
| 214 | +``` |
| 215 | + |
| 216 | +Example: record a review checkpoint. |
| 217 | + |
| 218 | +```json |
| 219 | +{ |
| 220 | + "CandidateId": "4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c", |
| 221 | + "ReviewedUpToReferenceId": "f12a0d31-0d5a-4a5f-a5a7-3d2c3a9f5b2c", |
| 222 | + "PolicySnapshotId": "e3b0c44298fc1c149afbf4c8996fb924", |
| 223 | + "CorrelationId": "corr-2002" |
| 224 | +} |
| 225 | +``` |
| 226 | + |
| 227 | +Example: resolve a finding. |
| 228 | + |
| 229 | +```json |
| 230 | +{ |
| 231 | + "CandidateId": "4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c", |
| 232 | + "FindingId": "6e58b4de-7f3b-4a2b-9a6f-111111111111", |
| 233 | + "ResolutionState": "Approved", |
| 234 | + "Note": "Reviewed and acceptable.", |
| 235 | + "CorrelationId": "corr-2003" |
| 236 | +} |
| 237 | +``` |
| 238 | + |
| 239 | +### Policy endpoints |
| 240 | + |
| 241 | +- `POST /policy/current` |
| 242 | +- `POST /policy/acknowledge` |
| 243 | + |
| 244 | +Example: acknowledge a policy snapshot. |
| 245 | + |
| 246 | +```json |
| 247 | +{ |
| 248 | + "TargetBranchId": "8c6f3b1b-53b1-4a1b-84fd-9c8b5a4e2e11", |
| 249 | + "PolicySnapshotId": "e3b0c44298fc1c149afbf4c8996fb924", |
| 250 | + "Note": "Reviewed for Q1 rollout", |
| 251 | + "CorrelationId": "corr-3001" |
| 252 | +} |
| 253 | +``` |
| 254 | + |
| 255 | +## CLI examples |
| 256 | + |
| 257 | +Queue a candidate (initialize queue with policy snapshot if needed): |
| 258 | + |
| 259 | +```powershell |
| 260 | +./grace queue enqueue ` |
| 261 | + --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c ` |
| 262 | + --branch main ` |
| 263 | + --policy-snapshot-id e3b0c44298fc1c149afbf4c8996fb924 ` |
| 264 | + --work f88b46e2-5c36-4b52-9e36-716f7d7a9a8b |
| 265 | +``` |
| 266 | + |
| 267 | +Check queue status: |
| 268 | + |
| 269 | +```powershell |
| 270 | +./grace queue status --branch main |
| 271 | +``` |
| 272 | + |
| 273 | +Pause and resume the queue: |
| 274 | + |
| 275 | +```powershell |
| 276 | +./grace queue pause --branch main |
| 277 | +./grace queue resume --branch main |
| 278 | +``` |
| 279 | + |
| 280 | +Retry a candidate: |
| 281 | + |
| 282 | +```powershell |
| 283 | +./grace queue retry ` |
| 284 | + --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c ` |
| 285 | + --branch main |
| 286 | +``` |
| 287 | + |
| 288 | +Open a review packet: |
| 289 | + |
| 290 | +```powershell |
| 291 | +./grace review open --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c |
| 292 | +``` |
| 293 | + |
| 294 | +Record a review checkpoint: |
| 295 | + |
| 296 | +```powershell |
| 297 | +./grace review checkpoint ` |
| 298 | + --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c ` |
| 299 | + --reference f12a0d31-0d5a-4a5f-a5a7-3d2c3a9f5b2c |
| 300 | +``` |
| 301 | + |
| 302 | +Resolve a finding: |
| 303 | + |
| 304 | +```powershell |
| 305 | +./grace review resolve ` |
| 306 | + --candidate 4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c ` |
| 307 | + --finding-id 6e58b4de-7f3b-4a2b-9a6f-111111111111 ` |
| 308 | + --approve ` |
| 309 | + --note "Reviewed and acceptable." |
| 310 | +``` |
| 311 | + |
| 312 | +## SDK examples |
| 313 | + |
| 314 | +Fetch a candidate and required actions (F#): |
| 315 | + |
| 316 | +```fsharp |
| 317 | +open Grace.SDK |
| 318 | +open Grace.Shared.Parameters.Queue |
| 319 | +
|
| 320 | +let candidateParams = |
| 321 | + CandidateParameters( |
| 322 | + CandidateId = "4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c", |
| 323 | + CorrelationId = "corr-1002" |
| 324 | + ) |
| 325 | +let! candidate = Candidate.Get(candidateParams) |
| 326 | +
|
| 327 | +let actionsParams = |
| 328 | + CandidateParameters( |
| 329 | + CandidateId = "4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c", |
| 330 | + CorrelationId = "corr-1002" |
| 331 | + ) |
| 332 | +let! actions = Candidate.RequiredActions(actionsParams) |
| 333 | +``` |
| 334 | + |
| 335 | +Open a review packet (F#): |
| 336 | + |
| 337 | +```fsharp |
| 338 | +open Grace.SDK |
| 339 | +open Grace.Shared.Parameters.Review |
| 340 | +
|
| 341 | +let reviewParams = |
| 342 | + GetReviewPacketParameters( |
| 343 | + CandidateId = "4fe3c2a9-35f0-4dc2-9f8b-4d3e2f1a0b9c", |
| 344 | + CorrelationId = "corr-2001" |
| 345 | + ) |
| 346 | +let! packet = Review.GetPacket(reviewParams) |
| 347 | +``` |
| 348 | + |
| 349 | +## Deterministic chaptering and baseline drift |
| 350 | + |
| 351 | +- Chaptering groups files by their top-level folder segment (e.g., |
| 352 | + `src/api/foo.cs` and `src/api/bar.cs` live in the `src` chapter). |
| 353 | +- Chapter IDs are a SHA256 hash of the sorted path list for that chapter. |
| 354 | +- Baseline drift evaluation exists as a shared helper and compares churn totals |
| 355 | + against policy thresholds (`ChurnLines`, `FilesTouched`). It returns affected |
| 356 | + paths, chapters, and findings. The helper is not yet wired into queue-required |
| 357 | + actions. |
| 358 | + |
| 359 | +## Current limitations and stubs |
| 360 | + |
| 361 | +- Review packets cannot be created via public APIs yet; they are stored only |
| 362 | + through internal actor commands. |
| 363 | +- `review/deepen` is a placeholder and returns a not-implemented error. |
| 364 | +- `review inbox` and `review delta` CLI commands are stubs. |
| 365 | +- Gate implementations are mostly stubbed (review/build-test) and return static |
| 366 | + results. |
| 367 | +- Stage 0 analysis currently records default risk profiles; deeper deterministic |
| 368 | + analysis is not wired yet. |
| 369 | +- Queue processing and automatic candidate state transitions are not implemented |
| 370 | + in the server. |
| 371 | +- Promotion-group review packets are not supported by the CLI yet; it accepts |
| 372 | + `--promotion-group` but errors if used. |
0 commit comments