1import * as Schema from "effect/Schema";
2
3import { IsoDateTime, PositiveInt, TrimmedNonEmptyString } from "./baseSchemas.ts";
4import { ModelSelection } from "./modelSelection.ts";
5
6/**
7 * A guided review is written for the maintainer deciding whether a change should land, not for
8 * someone checking it line by line. Review agents already read every line; what is left for a
9 * person is intent, design, and the calls an agent cannot make on their behalf.
10 *
11 * So a guide is a tour of stops in reading order, each building on the ones before (contract, then
12 * the service, then its callers), with the mechanical rest grouped at the end. Each stop shows only
13 * the code that carries its idea and names the one call a maintainer should make there, if any;
14 * that call is what tells the reader where to slow down.
15 *
16 * The model writes the tour; the server holds it to the change. Excerpts and notes are clamped to
17 * lines the change touched, and a changed file the model left out still lands in a closing skim
18 * stop, so every file is accounted for.
19 */
20
21/**
22 * The one call a maintainer should make at a stop. Not a bug or a review nit; those belong to the
23 * review agents. The kind says what sort of judgment it asks for.
24 */
25export const PullRequestReviewGuideAttention = Schema.Struct({
26 kind: Schema.Literals(["tradeoff", "contract", "product", "risk", "accessibility"]),
27 note: TrimmedNonEmptyString,
28});
29export type PullRequestReviewGuideAttention = typeof PullRequestReviewGuideAttention.Type;
30
31/**
32 * Lines worth reading, always inside the change. `old` shows removed code, for a change that is
33 * mostly deletion; line numbers are on the side named.
34 */
35export const PullRequestReviewGuideExcerpt = Schema.Struct({
36 path: TrimmedNonEmptyString,
37 side: Schema.Literals(["new", "old"]),
38 start: PositiveInt,
39 end: PositiveInt,
40 caption: Schema.String,
41});
42export type PullRequestReviewGuideExcerpt = typeof PullRequestReviewGuideExcerpt.Type;
43
44/**
45 * What the code at one line does not say for itself: why it is shaped this way, what it connects
46 * to. An explanation, not a finding. Anchored inside one of its stop's excerpts.
47 */
48export const PullRequestReviewGuideNote = Schema.Struct({
49 path: TrimmedNonEmptyString,
50 line: PositiveInt,
51 title: TrimmedNonEmptyString,
52 body: Schema.String,
53});
54export type PullRequestReviewGuideNote = typeof PullRequestReviewGuideNote.Type;
55
56export const PullRequestReviewGuideStop = Schema.Struct({
57 title: TrimmedNonEmptyString,
58 summary: Schema.String,
59 attention: Schema.NullOr(PullRequestReviewGuideAttention),
60 excerpts: Schema.Array(PullRequestReviewGuideExcerpt),
61 /**
62 * Changed files the tour never excerpts, each listed by one stop. A file the tour does show may
63 * be excerpted by several stops, each showing the part that stop is about.
64 */
65 files: Schema.Array(
66 Schema.Struct({
67 path: TrimmedNonEmptyString,
68 note: Schema.optionalKey(Schema.String),
69 }),
70 ),
71 notes: Schema.Array(PullRequestReviewGuideNote),
72 /** Questions about intent a reader might ask here, for a follow-up conversation. */
73 questions: Schema.Array(TrimmedNonEmptyString),
74});
75export type PullRequestReviewGuideStop = typeof PullRequestReviewGuideStop.Type;
76
77export const PullRequestReviewGuide = Schema.Struct({
78 /** The head the guide was written against. A different head means the guide is stale. */
79 headSha: TrimmedNonEmptyString,
80 generatedAt: IsoDateTime,
81 modelSelection: ModelSelection,
82 stops: Schema.Array(PullRequestReviewGuideStop),
83});
84export type PullRequestReviewGuide = typeof PullRequestReviewGuide.Type;
85
86/**
87 * A guide run in progress or the last one that failed. A run reads the change in a checkout and
88 * takes minutes on a large one, so it runs on the server and the page watches this instead of
89 * holding a request open. A server restart drops a run in progress; nothing claims it.
90 */
91export const PullRequestReviewGuideRun = Schema.Union([
92 Schema.TaggedStruct("Running", {
93 headSha: TrimmedNonEmptyString,
94 startedAt: IsoDateTime,
95 }),
96 Schema.TaggedStruct("Failed", {
97 headSha: TrimmedNonEmptyString,
98 failedAt: IsoDateTime,
99 detail: TrimmedNonEmptyString,
100 }),
101]);
102export type PullRequestReviewGuideRun = typeof PullRequestReviewGuideRun.Type;
103
104/**
105 * The stored guide for a pull request and the head it is now at. `guide` is null when none was
106 * generated; a guide whose `headSha` differs from `currentHeadSha` was written before a push.
107 * `run` is the generation in progress, or the last failed one; a finished run leaves only `guide`.
108 */
109export const PullRequestReviewGuideResult = Schema.Struct({
110 guide: Schema.NullOr(PullRequestReviewGuide),
111 run: Schema.NullOr(PullRequestReviewGuideRun),
112 /** Null where the host did not report a head with the detail. */
113 currentHeadSha: Schema.NullOr(TrimmedNonEmptyString),
114});
115export type PullRequestReviewGuideResult = typeof PullRequestReviewGuideResult.Type;