1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! The text the LLM sees, plus the line numbers that text legitimately
//! covers.
//!
//! The format exists to solve one problem: **line-number provenance**. If the
//! model is handed a bare diff it must infer file line numbers from the `@@`
//! header, which it does unreliably; every finding then points at the wrong
//! code and looks perfectly plausible. So the payload states each line's real
//! file line number explicitly in the gutter, and the caller keeps the set of
//! numbers that were actually shown. A later phase drops any finding whose
//! line is not in that set, because such a finding is about code the model was
//! never shown.
//!
//! The numbering itself is not implemented here: it comes from
//! [`Hunk::numbered_lines`], which is the single home of the rule that a
//! removed line does not consume a line number.
use BTreeSet;
use Write as _;
use crateHunk;
use crateLanguageSupport;
/// The largest payload drep will send to the model, in bytes.
///
/// Declared here, beside the thing it measures, and **enforced by
/// [`crate::analysis::code_quality::CodeQualityAnalyzer::analyze_file`]** on
/// the text `render` returns - `render` itself has no size opinion and no way
/// to report one, since it returns `Option<Payload>` and `None` already means
/// "no hunks".
///
/// It used to live in `cli::check` and was consulted only in paths mode, so a
/// newly-added 5 MB file reached the model whole through `--staged` or
/// `--diff`, the two modes a commit gate actually runs in. The check belongs
/// on the rendered payload because that is the one thing every input mode
/// produces.
///
/// A payload over the ceiling is a
/// [`crate::analysis::result::FailureReason::PayloadTooLarge`] failure, never a
/// skip: 1.x returned an empty finding list for anything over 32k chars, which
/// under this codebase's contract is the banned move - a file drep declined to
/// analyze is not clean.
///
/// `u64` rather than `usize` so it compares directly against the byte counts
/// `FailureReason` carries; only one site measures a `str` length, and that one
/// casts.
pub const PAYLOAD_MAX_BYTES: u64 = 256 * 1024;
/// A rendered payload plus the file line numbers it legitimately covers.
/// Render every hunk belonging to one file into a single payload.
///
/// The file path is taken from the hunks themselves rather than passed
/// alongside them — they all carry it, and a separate argument would be a
/// second copy for the caller to keep in sync with no way to check it.
/// `hunks` must therefore all share a `file_path`; they are rendered in
/// ascending `new_start` order. Returns `None` when `hunks` is empty.
///
/// The language arrives as a [`LanguageSupport`], not a string: `languages/`
/// is the only place a language is named, and `display_name` is the name
/// meant for the model. A bare `&str` here would let a caller pass the
/// registry key (`"rust"`) where the prompt wants `"Rust"`, with nothing to
/// catch it.
/// Pick the right scope sentence for this set of hunks.
///
/// Whole-file mode (every line is `Context`) tells the model to review the
/// whole file; diff mode (some line is `Added` or `Removed`) tells it to focus
/// on the marked lines and never report findings on removed lines. The
/// decision reads from the data so the caller cannot get a flag wrong; it is
/// sound because git never emits a hunk with no changed line, as noted on
/// [`Hunk::whole_file`].