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
//! The §3.5 join-state table (DESIGN §3.2, §3.5, §5.1 #7): the ball × workspace
//! product enumerated once, every combination a row state, never an ad-hoc
//! branch. The binding is the claimant equality (§3.2): a ball is **bound** to a
//! workspace iff its claimant equals that workspace's name — no operator
//! identity, no stored fact.
//!
//! [`classify`] is the total function at its heart — (status, bound?) ⇒ exactly
//! one ball-driven [`JoinState`]. [`join`] enumerates the whole product over the
//! pre-fetched live balls ([`super::balls`]), the on-demand closed listing, and
//! the enumerated workspaces ([`crate::binding`]): a row per live ball, a
//! delivered row per closed ball whose claimant names a local workspace, an
//! unassigned row per named workspace no ball claims, and an orphaned row per
//! cloned-but-unlistable project.
use Status;
/// **The enumeration**, split off at §12's budget: this file is the table a row
/// is classified by, `enumerate` is the walk over the whole product.
pub use join;
/// The left axis of the §3.5 table: the ball's derived status (§5.1 #3),
/// including `Closed` (absence from the live set — the on-demand closed listing).
/// The live [`Status`] ladder lifted into the join's left axis; a closed ball
/// enters as [`JoinStatus::Closed`] directly, never through this.
pub
/// The rendered row state for one (ball × workspace) cell (§3.5). The seven §3.5
/// rows: five ball-driven ([`classify`]), plus the workspace-driven
/// [`UnassignedWorkspace`](JoinState::UnassignedWorkspace) and the project-driven
/// [`OrphanedProject`](JoinState::OrphanedProject).
/// The §3.5 table as a total function over the ball-driven cells: (status,
/// bound?) ⇒ the row state. Ready/Blocked ignore boundness (an unclaimed ball
/// names no workspace); a claim splits on the binding; a closed ball is
/// delivered (its `workspace` grouping is set by [`join`], not decided here).
/// The short roster badge for a join state (§3.5); `None` where the row needs
/// none (a startable/bound/unassigned workspace row). `claimant` fills the
/// "claimed by <who>" text.
/// The workspace name a bound ball's close / release / move-from stamps `--as`
/// (§3.2, §8.2 rider): the ball's claimant — which, for a [`Bound`](JoinState::Bound)
/// row, **is** the local workspace's name (the join binds on that equality). The
/// claimant delivers its own ball; empty for a row that names no claimant (never a
/// Bound one). Covered derivation, so the shell paints without deciding names.
/// One classified row of the ball × workspace join (§3.5). `title`/`claimant`
/// carry live-ball detail; both are absent for a delivered/unassigned/orphaned
/// row. `workspace` is the bound workspace's name when one matches, else `None`.
///
/// **Both addresses are NAMES** (REMOTE §8.1, bl-b4b5). They were a project
/// `PathBuf` and a workspace `PathBuf` — the last two path-typed fields on a
/// reply payload after `Prepared::binding`, and the ones §8.1's own list did
/// not reach. `Reply::Balls` therefore answered a thin seat two absolute paths
/// under the engine's home: unusable there and a disclosure besides. They are
/// the §5.1 #1 project name and the §3.1 workspace leaf now — the *same* two
/// words `Action::Close`/`Assign`/`Move` already take — so a seat holding a
/// name can select its own workspace's rows out of the answer without joining
/// it back against the engine's table (the shape bl-7407 refused). The engine
/// resolves either back through [`Snapshot::project_path`](crate::app::Snapshot)
/// / [`ws_path`](crate::app::Snapshot) at the one seam that owns the round trip.