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
// SPDX-License-Identifier: Apache-2.0
//! A Graph-origin check: `headwater generate` does not write one of two states
//! onto a document without saying so.
//!
//! # The gap this closes
//!
//! A relation may declare `on_target: {set_state: <state>}`, and an edge of it
//! then puts its target in that state. For a **generated** document, whose
//! facets no author writes, `headwater generate` reads the setter edges into it
//! and writes the state onto the page (`derived::set_state` in the generate
//! crate). Where two edges of two relations write two different states, the page
//! can carry only one, and generate takes the first in sorted order so that the
//! answer is the same on every run. Before this rule nothing told the author
//! that the page states one of two answers
//! ([#1086](https://github.com/headwater-ai/headwater/issues/1086), first
//! recorded in [spec 13](../../../../docs/spec/13-open-obligations.md) from
//! #820).
//!
//! # The documents it reads, and the edges
//!
//! The rule decides exactly the documents generate writes a state onto, from
//! exactly the edges generate reads, so that every finding is a statement about
//! a page that exists.
//!
//! - **The documents** are the rows the census classified as generated and
//! resolved a kind for, where that kind requires the facet in the `state`
//! role. Generate answers a required facet and no other. An authored document
//! is not one of them: its author writes its state, and
//! [`crate::lifecycle_state`] and [`crate::dependency`] read that value.
//! - **An orphan is not one of them.** The census classifies a file as
//! generated from its marker alone, so a person can place a marked file that
//! no projection writes. `headwater generate` lists that file as orphaned
//! and writes nothing onto it
//! ([#1137](https://github.com/headwater-ai/headwater/issues/1137)). This
//! crate cannot compute the projection plan, because the generate crate
//! depends on it. So the CLI builds the plan and injects the orphaned paths
//! through [`crate::Context::with_orphaned`], and the rule declares
//! [`crate::scope::CorpusCheck::NEEDS_ORPHANED`] to read them. A run that
//! injects no set reads every marked file, which is what every test that
//! does not name the set expects.
//! - **The edges** are the edges whose written target is that document's
//! identifier, that were written under the declared name of their relation,
//! and that the document did not write itself. That is `derived::incoming`
//! clause by clause, and it reads the raw target for generate's reason: the
//! answer does not depend on whether the index resolved the name. An edge written from the inverse name points the other way, so the
//! state it sets is not this document's. An edge the output wrote would make
//! the page a function of its own last version.
//!
//! # The grain is the corpus, and why not a neighbourhood
//!
//! A neighbourhood instance centers on a typed document, and a generated one is
//! not typed, so that grain never reaches the one document this rule is about.
//! A neighbour also carries no direction, and the direction is half of what
//! generate reads. The corpus view carries the edges and the generated rows
//! when a rule declares [`crate::scope::CorpusCheck::NEEDS_GRAPH`], and every
//! document either is derived from is already in its read set.
//!
//! # The instance exists only where a clash can
//!
//! One relation writes one state. So a clash needs at least two relations that
//! declare two different states, and a taxonomy with fewer runs no instance of
//! this rule: [`StateSetTwice::can_clash`] is the gate, and the runner reads it
//! before it builds the one corpus instance. This is the one corpus-scoped rule
//! with a gate, and the gate is a declaration rather than a property of any
//! corpus, which is the same generation step every other grain takes. This
//! repository's own taxonomy declares one setter today (`supersedes`), so the
//! rule costs its corpus nothing.
//!
//! # Neighbors this rule does not own
//!
//! [HW-OBL-0196](../../../../docs/obligations/0196-a-relation-writes-a-state-onto-a-kind-that-binds-no-lifecycle-regime-and-nothing-reads-that-pair.md)
//! and [#225](https://github.com/headwater-ai/headwater/issues/225) are about
//! one relation and the kind at its target end: whether that kind admits the
//! state the relation writes. That is a question about two declarations and
//! about no document, and it stays with them. Generate refuses such a state on
//! its own.
//!
//! # The severity is a warning, and the finding carries no patch
//!
//! The page still gets a deterministic state, so nothing downstream is broken in
//! a way an error would have to stop. But the page states one of two answers
//! without saying so, and the author has to choose: remove one edge, change the
//! relation one of the sources used, or change what one relation declares. Only
//! the author knows which, so the remediation is not mechanical and no patch is
//! offered. The finding anchors at the state facet the last run of generate
//! wrote, which is the line that states the chosen answer.
use crate;
use crateOutcome;
use crate;
use crateShape;
use ;
pub const RULE: &str = "lifecycle.state.set_twice";
/// The view carried no graph, which the runner never builds for this rule.
const NO_GRAPH: &str = "the view carries no edges and no generated documents";
/// The check. It carries every relation that writes a state onto its target,
/// with the state it writes, and the name of the facet in the `state` role.