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
//! The single owner of epic behaviour (mission `epics-work-items-01M3BYW4`,
//! WP02 — `contracts/epic-service.md`).
//!
//! Everything about an epic lives here: identity and resolution, the registry
//! lifecycle (create / edit / delete / list), the derived hierarchy with its
//! dedicated iterative cycle walk, reference diagnosis across all five kinds,
//! derived membership and the index that makes an epic filter a bucket lookup,
//! and — in exactly one place — the epic rollup (completion, child counts,
//! weight, blocked work). A second identity resolver, a second membership index,
//! a second progress implementation, a second relationship mechanism, or a
//! second milestone/cycle store is the divergence the mission's charter
//! escalates (C-002, C-008).
//!
//! ## What this module deliberately does not do
//!
//! - **It performs no file I/O** — no `Store`, no `std::fs`, no `tempfile`. It
//! takes an in-memory [`Config`] and an in-memory issue set and returns values;
//! the write path is storage's (WP03) and this module owns the rule. Every
//! operation is therefore testable with no repository at all (T012 step 4).
//! [`ReferenceTargets::from_planner`] is the one optional adapter that consumes
//! another owner's filesystem-backed lookup; the sets it produces are plain
//! in-memory values, and a caller can supply them directly instead.
//! - **It does not know about the issue model.** [`EpicNode`] is the minimum an
//! issue must expose — its number, title, status, and its `epics` id list — and
//! WP03 implements it for [`crate::model::Issue`], exactly as the project
//! service's `ProjectNode` and the hierarchy service's `HierarchyNode` are
//! implemented there. WP02 is forbidden from touching `src/model.rs`, so the
//! service is defined against the shape WP03 provides. Because the trait has
//! **no label accessor**, membership is structurally incapable of telling a
//! `task`-labelled issue from any other — that is the C-001 guard, not a
//! convention.
//! - **It never mutates or returns a whole `Issue`.** [`reference_epic`] and
//! [`dereference_epic`] return an [`EpicChange`]: the resulting reference list
//! plus the History event to append. Storage applies it. That boundary is what
//! keeps this module model-free.
//! - **It stores no membership.** An epic's members are derived by inverting the
//! issues' reference lists and are never persisted (data-model §4, C-002). The
//! registry record carries no member list at all.
//! - **It does not recompute blocked state.** [`EpicIndex::rollup`] consumes
//! source issue 169's derived [`RelationshipGraph::blocked_state`] and reuses
//! its `reason` text verbatim (research R4). Re-deriving blocked work here
//! would create a second owner for state another Mission owns.
//! - **It does not reimplement milestone or cycle lookup.** [`ReferenceTargets`]
//! consumes `Planner::milestone_names()`, `Planner::cycle_names()` and
//! `Planner::cycle()` (`src/planning.rs`) through
//! [`ReferenceTargets::from_planner`]; no second store is introduced (research
//! R2). Project references are checked against the project registry itself,
//! never by reversing this module's index (research R6).
//! - **It does not share the issue-parent cycle walk.** An epic chain and an
//! issue-parent chain are different semantics (C-002); the walk below *follows
//! the same shape* as `crate::hierarchy`'s and is dedicated to epics.
//!
//! ## The index is built once per read
//!
//! [`EpicIndex`] is the derived view over one snapshot. It is built once
//! ([`EpicIndex::build`]) and every read — [`EpicIndex::membership`],
//! [`EpicIndex::resolve`], [`EpicIndex::hierarchy`], [`EpicIndex::rollup`],
//! [`EpicIndex::missing`] — reads that one value rather than rebuilding. The
//! contract's free operations each build exactly one index per call, so a filter
//! is a bucket lookup and a rollup never re-scans the issue set (NFR-001).
//!
//! ## Ordering is deterministic
//!
//! Members are ordered by issue number, epics by declaration order, child lists
//! by declaration order, ancestors root-first, and diagnostics by
//! `(holder, kind, target)`, so repeated reads of the same snapshot are
//! identical (contract §8).
use ;
use fmt;
use IndexMap;
use crate;
use crateDEFAULT_CYCLE_NAME;
use crate;
use crate;
use crateRelationshipGraph;
pub use *;
pub use *;
pub use *;
pub use *;
pub use crate;
/// The minimum an issue must expose for epic derivation.
///
/// WP03 implements this for [`crate::model::Issue`]. Keeping it a trait is what
/// lets this module be written and tested without `src/model.rs`, which WP03
/// owns. There is deliberately **no** label accessor: C-001 requires a
/// `task`-labelled issue to be an ordinary member, and a trait that cannot see
/// labels cannot make it anything else.
// ---------------------------------------------------------------------------
// T006 — the Epic view, resolution, and lifecycle
// ---------------------------------------------------------------------------
/// The id a **dereference** should match for `reference` (WP09 item B).
///
/// One owner for the resolve-or-keep-verbatim classification the repair path
/// needs: a reference that names a live epic resolves to its stable id, and
/// anything else — a stale id, a malformed one, or a name-shaped string the
/// registry no longer holds — is passed through **verbatim** so
/// `tix epic remove` can still drop it (C-005). A second spelling of this rule
/// would let one surface refuse a removal another surface performs.
///
/// The callers are `commands::epic::dereference_from_epic`,
/// `commands::epic::plan_edit_epics`, and MCP's `epic_dissociate_issues`, so the
/// three cannot drift.
pub
// ---------------------------------------------------------------------------
// T007 — the derived index and membership
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// T008 — the dedicated iterative epic-chain walk and hierarchy navigation
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// T009 — reference diagnosis across all five kinds
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// T010/T011 — the typed rollup: the one progress computation (FR-003, C-008)
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// T008 — association mutations, with History events
// ---------------------------------------------------------------------------