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
//! Public wire DTO for the deterministic repo-fact report (`ossctl facts`).
//!
//! This is the versioned surface `/oss-init` (config generation) and the
//! readiness `audit` both read, so they never disagree on maturity or the gated
//! core (ADR-0001 §3). Its field names and shape are a faithful port of
//! homebase's `infer-repo-facts.py` JSON — `ecosystems`, `packages`, `has_ci`,
//! `tags`, `committers_total`/`committers_recent_year`, `inferred_maturity`, and
//! the rest — because the prose `/oss-init` skill already relies on those exact
//! names (SCHEMA.md §4 "maturity inference signals").
//!
//! Consumers read this document under the CLI's canonical `data` envelope:
//! `{schema_version, data: <this shape>, warnings}` — the same envelope every
//! `ossctl --json` command shares (`crate::SCHEMA_VERSION` versions that wire
//! envelope). Unlike the contract document, the facts report has no
//! source-level document version of its own (it is *derived*, never authored),
//! so the envelope's `schema_version` is the single version consumers gate on.
//!
//! The report **reuses** [`Ecosystem`] and [`Maturity`] from the canonical
//! contract model rather than restating their wire strings: facts and the
//! contract must agree on `"rust"` / `"mvp"` down to the byte, and sharing the
//! one enum is how that agreement is made structural instead of coincidental.
use Serialize;
use crate;
/// The deterministic repo-fact report — a pure function of `(repo tree, git
/// HEAD)`, emitted by `ossctl facts` and consumed by `/oss-init` and `audit`.
///
/// Every field is always present (an empty/unborn repo still gets a defined
/// value for each), mirroring the Python detector's "exit 0 even for an empty
/// repo" contract.
// The report mirrors the Python detector's flat boolean signals; grouping them
// into sub-structs purely to satisfy the bool-count lint would diverge the wire
// shape from `infer-repo-facts.py` for no consumer benefit.
/// A Rust Cargo workspace's crates.io-**publishable** members and the
/// intra-workspace dependency edges among them — the graph the release planner
/// derives a dependency-ordered publish set from (`release-rust-workspace-multicrate`).
///
/// Off-wire plumbing carried on [`Facts::rust_workspace`]; see that field for why it
/// is not serialized. Members are listed in workspace declaration order (the planner
/// applies the topological ordering — a dependency before its dependents); only
/// members publishable to crates.io are included (a `publish = false` member, or one
/// restricted to a non-crates.io registry, is dropped, matching the cargo adapter's
/// cut-time `cargo metadata` filter).
/// One crates.io-publishable Cargo workspace member and its intra-workspace
/// (publishable) dependency crate names — a node in [`RustWorkspace`].
/// One detected package manifest and the name/version parsed from it.
// `package` is the field name `/oss-init` reads (SCHEMA.md §4); the
// struct-name-echo lint does not apply to a fixed wire contract.
/// The two maturity truth-table outputs (SCHEMA.md §4). Both can be `false`
/// (the tie case), which resolves to `mvp`; they are never both `true`
/// (`production` is checked first).