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
//! Public wire DTO for the sealed, content-addressed release plan
//! (`ossctl release plan` — ADR-0002 §3).
//!
//! A [`ReleasePlan`] is the read-only pre-image a human approves: the ordered
//! concrete target set a cut would publish, the invariant phase sequence the
//! coordinator will drive, the git `HEAD` it was sealed against, the chosen
//! release version, and the content-addressed [`ReleasePlan::plan_id`]. `release
//! cut --plan <plan_id>` re-derives the plan from *current* repo state and
//! refuses (`plan_stale`) if the id no longer matches — so a commit, a manifest
//! rename, a schema bump, or a different chosen version between approval and
//! execution aborts rather than silently publishing something else.
//!
//! 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). Like `facts` and `audit`, the plan is *derived*, never authored,
//! so it has no document version of its own; the envelope's `schema_version` is
//! the wire version consumers gate on. [`ReleasePlan::contract_schema_version`]
//! is a *content* field (the contract-document version the plan was sealed
//! against, part of the content address), not the wire-envelope version.
//!
//! The plan **reuses** [`Ecosystem`], [`Registry`], and [`Adapter`] from the
//! canonical contract model rather than restating their wire strings: the plan,
//! the contract, and the release adapters must agree on `"rust"` /
//! `"crates.io"` / `"cargo-publish"` down to the byte, and sharing the one enum
//! makes that agreement structural instead of coincidental.
use Serialize;
use crate;
/// A sealed, content-addressed release plan — the artifact `release plan`
/// emits and a human approves.
///
/// Every field except [`Self::plan_id`] is an *input* to the content address
/// (`plan_id` is the SHA-256 digest **over** those inputs — plus the full
/// normalized contract and a domain/seal-format tag — so it is derived, never
/// authored, and is deliberately excluded from the hashed pre-image: a hash
/// cannot cover itself). See [`crate::release::plan`] for the exact pre-image.
/// One concrete publish destination in a sealed plan.
///
/// Mirrors the contract's [`crate::contract::schema::Target`] but is a distinct
/// wire type: the plan may *resolve* a `null` package name from repo facts, so
/// its `package` is the concrete name the human approves, not necessarily the
/// contract's (which the executor would otherwise infer at cut time).
/// One phase of the coordinator's irreversibility-ordered pipeline (ADR-0002
/// §2). The sequence is invariant across every plan; [`PlanPhase::sequence`]
/// yields it in order.