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
//! Explicit, confirmed lifecycle actions (WP05, T024–T029).
//!
//! # Eight actions, one confirmed request, one outcome each
//!
//! [`apply`] carries out exactly one of the eight actions
//! ([`LifecycleAction`]) and returns exactly one [`LifecycleOutcome`] with a stable
//! spelling — `applied`, `no-op` or `rejected`. [`LifecycleRequest::confirmed`] must
//! be set first: naming a cycle is not consent, so an unconfirmed request is refused
//! before anything is read or written (C-004). A reason is *required* for the four
//! actions whose whole point is an auditable decision — `close`, `extend`, `skip` and
//! `adjust-future` — refused by name when it is missing, and every action that writes
//! carries an explicit timestamp and actor (there is no clock here).
//!
//! # Where the evidence of an action lives
//!
//! The mission's evidence for a cycle is its **record**, its **snapshot**, the
//! **cursor**, and — for an applied decision — one line of the record's own
//! `## History` (T027). Cycles are not issues: the events are appended to the
//! **generated cycle record's Markdown body** through the crate's one
//! [`History`] grammar ([`HistoryEventKind::CycleClosed`] and siblings), never to an
//! issue, and there is no second log. No issue is created, moved, relabelled or
//! rewritten, and the normal move workflow is untouched (C-007).
//!
//! * `close` sets the cycle record's `state` to `closed`, appends a `cycle_closed`
//! event, **and** captures an immutable snapshot under `docs/cycle_snapshots/`
//! (the historical record, see [`crate::cycle_schedule::snapshot`]);
//! * `reopen` (mission `cycle-capacity-planning-01M3C85C`, WP04) is `close`'s inverse
//! and deliberately writes **no** snapshot: it restores the record's `state` to
//! `active` and appends exactly one `cycle_reopened` event naming the snapshot the
//! close captured. The prior snapshot and every prior event are retained verbatim
//! (C-008/NFR-002) — a reopen is the one way a closed cycle may be configured
//! again, not a second capture;
//! * `skip` records the sequence as a cycle record with `state = "skipped"` plus a
//! `cycle_skipped` event, creating the record from the schedule's own anchor when
//! it does not exist yet, so a later generation sees the key and reports it a
//! no-op (C-006);
//! * `pause`/`resume` write the schedule's own `state`, every affected instance's
//! state (the named one, or all non-terminal ones), and one event per touched
//! record;
//! * `extend` rewrites the instance's `due_date` to one more cadence period, and
//! `adjust-future` rewrites every *future* instance's dates and re-derives the
//! cursor — both through the shared [`RecurrenceRule`], never by hand;
//! * `ignore` writes nothing at all: it is the "no action" outcome and says so.
//!
//! # Every write carries the record's description and body forward
//!
//! A cycle record is a [`CycleRecord`] frontmatter plus a Markdown body. The identity
//! adapter ([`GeneratedCycle::to_record`]) has no notion of either, so a lifecycle
//! write never rebuilds a record from it: it reads the **stored** record and body
//! first (`crate::records::RecordStore::get`) and merges the change onto that. A
//! `tix cycle edit --description …`, a hand-written body, or a previously appended
//! `## History` entry therefore survives every action — and every rollback restores
//! exactly what was there, description and body included.
//!
//! # Inertness is the contract, not a nicety
//!
//! An action that cannot be coherent — closing a skipped cycle, resuming an ended
//! schedule, extending a terminal one, a cycle id that belongs to another schedule,
//! an unknown cycle, a missing reason/actor/timestamp, a malformed timestamp, a skip
//! that would fork the cursor past a hole — is `rejected` with a field-aware
//! diagnostic and **zero writes**. A **repeated** action, whose postcondition already
//! holds, is a `no-op`: it must never append a second snapshot, a second event, or
//! move a date twice. A *returned* error from the storage seam is compensated as a
//! unit — every record this call wrote is restored (or removed) and every directory it
//! created is removed when empty — so even a failure leaves the tree byte-identical
//! (or says exactly what could not be undone).
//!
//! # Idempotency by construction
//!
//! `extend` and `adjust-future` are **derived**, not accumulated: `extend` computes
//! the instance's target due date from its own (invariant) `start_date`, and
//! `adjust-future` recomputes every instance's dates from the schedule's anchor and
//! its own `sequence`. Applying either twice is therefore the *same computation*, so
//! the second call finds the postcondition already true and is a no-op.
//!
//! # No clock, no daemon
//!
//! Nothing here reads `Instant`/`SystemTime`: a close's `closed_at` and every event
//! timestamp is the explicit string the request carries, validated by
//! [`validate_timestamp`](crate::cycle_schedule::snapshot::validate_timestamp). The
//! whole module is a confirmed function call, not a service.
use BTreeMap;
use ;
use ;
use crateCapacityCalculation;
use crateCapacitySnapshot;
use crate;
use crate;
use crate;
use crate;
use crate;
use crate;
use crate;
use crateRecordStore;
use crate;
pub use *;
use *;
pub use *;
use ;
// ---------------------------------------------------------------------------
// Record and History helpers
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Shared construction and arithmetic helpers
// ---------------------------------------------------------------------------