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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
//! The tga → trusty-review DD-manifest adapter (DOC-67 §6, #5236).
//!
//! Why: `tga audit` and trusty-review are separate processes with no Cargo edge
//! between them (DOC-67 §5). One TOML file is the entire contract: tga names the
//! engagement and the repositories, trusty-review renders. Keeping the builder
//! pure — data in, structure out, the caller writes the file — is what makes the
//! field mapping provable in unit tests instead of only observable by running a
//! full audit.
//! What: [`DdManifest`] and its two sections, [`DdManifestOptions`] for the
//! run-scoped engagement metadata, and [`build_dd_manifest`], which maps a
//! resolved tga [`Config`] onto trusty-review's manifest schema per §6's
//! field table. [`DdManifest::to_toml`] serializes.
//! Test: `super::dd_manifest_tests`.
//!
//! ## Two properties a reviewer should check first
//!
//! **No credential reaches the file.** The manifest is handed to a third party.
//! Every string this module emits passes through
//! [`trusty_common::credentials::scrub_secrets`] with the credentials the
//! config holds as needles, so a token that reached a repository name, a CLI
//! title, or a stage's error message is removed rather than merely unlikely to
//! be there. It is a guarantee about the output, not a claim about the inputs.
//!
//! That guarantee has one precondition the caller owes, because `scrub_secrets`
//! matches a credential's whole value: nothing may shorten a string between the
//! moment it is captured and the moment it arrives here. Stage messages are the
//! one channel that shortens — [`crate::audit::sweep_gap_lines`] excerpts them —
//! so it redacts first, using [`configured_secrets`] on the same config (#5239).
//!
//! **The same input yields the same bytes** (DOC-67 §9). Nothing here reads the
//! clock, the environment, or the filesystem, and every collection is an
//! ordered `Vec` walked in config order. The one machine-dependent value is the
//! repository path, which is load-bearing — trusty-review scans that checkout —
//! and so is emitted as configured.
use ;
use Serialize;
use scrub_secrets;
use crateConfig;
/// Failures the DD-manifest adapter can report.
///
/// Why: a library module, so a typed error rather than `anyhow` — the caller
/// (`tga audit`) turns these into operator-facing text.
/// What: an empty repository set (the manifest schema requires at least one
/// entry, so producing it would only move the failure into trusty-review with a
/// less actionable message), and TOML serialization failure.
/// Test: `super::dd_manifest_tests::empty_config_is_an_actionable_error`.
/// Engagement metadata for one audit run.
///
/// Why: DOC-67 §6 maps the report's title/analyst/client from CLI flags, and §2
/// forbids obtaining any of them interactively — so each is simply absent when
/// not supplied and the template renders its own fallback. `gaps` is the channel
/// §9 requires: areas the sweep could not assess, carried into the report rather
/// than left in the orchestrator's stderr.
/// What: the four run-scoped values; everything else comes from [`Config`].
/// Test: `super::dd_manifest_tests::maps_engagement_metadata`.
/// A trusty-review report manifest, as tga produces it.
///
/// Why: mirrors `trusty_review::report::manifest`'s TOML shape without a Cargo
/// dependency on that crate (DOC-67 §5 — the file is the seam). Only the four
/// fields §6's table maps are declared; every other key trusty-review accepts is
/// deliberately absent so its own defaults apply.
/// What: the `[report]` section plus one `[[repositories]]` entry per configured
/// repository, in config order.
/// Test: `super::dd_manifest_tests::round_trips_through_the_review_schema`.
/// The `[report]` section of a DD manifest.
/// One audited repository.
///
/// Why: §6 fixes the mapping — every AUDIT repo is a local checkout by
/// construction, `slug` is trusty-review's to derive, `git_ref` is whatever HEAD
/// is at collection time, and `metrics` must stay unset so the live `--analyze`
/// fetch is not blocked by a declared file.
/// What: the name and the checkout path, and nothing else.
/// Test: `super::dd_manifest_tests::names_fall_back_to_the_directory_basename`.
/// Build the DD manifest for one audit run.
///
/// Why: this is the whole tga→trusty-review seam (DOC-67 §6). It exists as a
/// pure function so the field mapping, the determinism property, and the
/// no-credential property are provable from unit tests rather than from a live
/// audit — none of which would be true if it wrote the file itself.
///
/// What: maps `cfg.repositories` onto `[[repositories]]` in config order, taking
/// each entry's `name` or falling back to its directory basename, and fills
/// `[report]` from `opts`. Every emitted string is scrubbed of the credentials
/// `cfg` holds, so a token that leaked into a name, a title, or a gap line is
/// removed before it can reach an artifact. No I/O, no clock, no environment
/// read: two calls on the same input produce equal values.
///
/// Test: `super::dd_manifest_tests` — the field mapping, the basename fallback,
/// `two_builds_are_byte_identical`, and `configured_token_never_reaches_the_manifest`.
///
/// # Errors
///
/// [`DdManifestError::NoRepositories`] when the config names none.
/// The `[[repositories]]` entries for one audit run.
///
/// Why: #5670 — `tga audit` indexes each repository before the renderer asks for
/// it, and the index id trusty-review looks up is derived from the checkout path
/// in THIS list. Sharing the mapping rather than re-deriving it beside the caller
/// is what makes the two agree by construction: index an anchored path the
/// manifest does not carry, or under a name the manifest does not use, and the
/// run indexes repositories nobody ever queries while still rendering hollow
/// sections.
/// What: one entry per configured repository, in config order, with the same
/// name fallback and the same base-dir anchoring [`build_dd_manifest`] emits,
/// scrubbed against the same needles. Pure: no I/O, no clock, no environment.
/// Test: `super::dd_manifest_tests::names_fall_back_to_the_directory_basename`,
/// and `crate::audit::tests::index_ids_match_the_manifest_paths_the_renderer_reads`.
/// Anchor a possibly-relative repository path to `base`.
///
/// Why/What: see [`DdManifestOptions::base_dir`]. An absolute path, or an empty
/// `base`, is returned unchanged; a pure join otherwise, with no filesystem
/// access and therefore no canonicalization.
/// Test: `super::dd_manifest_tests::relative_paths_are_anchored_to_base_dir`.
/// The display name for a repository: its configured name, else the directory
/// basename (`config/mod.rs`'s own documented fallback).
///
/// Why: this is the ONE derivation of a repository's name in tga, and it has to
/// be, because two independent copies is a silent-zero join. `commits.
/// repository` is written by [`crate::collect::git::GitCollector`] at collection
/// time and read back by an equality filter at report time (#5453's per-repo
/// authorship artifact) — a name the two sides spell differently matches no rows
/// and renders a confident "0 authors, bus factor 0" instead of an error. The
/// collector calls this too since #5453's review; before that it had its own
/// copy, which disagreed on a configured name that was empty or whitespace.
/// What: the configured name when it is non-empty after trimming, else the
/// basename of the tilde-expanded path, else the path itself. Expansion happens
/// HERE so a caller holding the raw config path and a caller holding an
/// already-expanded one still agree.
/// Test: `super::dd_manifest_tests::{names_fall_back_to_the_directory_basename,
/// a_blank_configured_name_falls_back_the_same_way_for_every_caller}`.
/// Every credential the resolved config holds, as scrub needles.
///
/// Why: `scrub_secrets` can only remove values the caller already knows, so the
/// needle set decides how much the guarantee is worth. These are the fields tga
/// itself reads to authenticate — the ones an error message or an expanded
/// `${GITHUB_TOKEN}` can carry into text this module emits. Public since #5239
/// because [`crate::audit::sweep_gap_lines`] must redact a stage message
/// *before* it excerpts it, and it has to redact against the same needles this
/// builder does — a second derivation here would be the drift the
/// common-entry-point rule exists to prevent.
/// What: the GitHub / Bitbucket / JIRA / Linear / Azure-DevOps / OpenRouter
/// credentials, skipping absent and empty values. Returns raw secrets: the only
/// correct use is as a needle set, never logged, displayed, or serialized.
/// Test: `super::dd_manifest_tests::configured_token_never_reaches_the_manifest`.