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
//! The `routines` reference table's caller-supplied data source (issue
//! #1592, part of the routine conversational management PRD #1482).
//!
//! [`RoutineStatusRecord`] is deliberately a PLAIN, proto-free struct rather
//! than this crate reusing `polyc_controller::Routine` directly the way
//! `crate::engine::ReferenceData::personas` reuses
//! `polyc_proto::proto::polychrome::persona::v1::PersonaProfile`: unlike
//! `polyc-proto` (a foundation this crate already depends on),
//! `polyc-controller` is a full CRD-reconciler crate that itself pulls in
//! `polyc-agent`/`polyc-llm`/`kube` — depending on it
//! directly here would drag this crate's whole isolation boundary
//! (docs/reference/datafusion-data-layer.md, "Where it lives") through nearly
//! the rest of the application. [`RoutineCatalog`] is the seam that keeps
//! that dependency out: the control plane (which already depends on
//! `polyc-controller`) implements this trait over its own
//! `kube::Api<Routine>`/`RoutineStatus`, converting each `Routine` into a
//! [`RoutineStatusRecord`] BEFORE it ever reaches this crate — the identical
//! "the caller resolves already-typed values, this crate only decodes"
//! division of labor `crate::engine::PartitionEvents`'s own doc states for
//! journal replay and `crate::engine::ReferenceData`'s doc states for
//! persona/participation resolution.
//!
//! # Status surface plus provenance/pause/schedule (#1592 → #1593)
//!
//! [`RoutineStatusRecord`] carried ONLY the routine's STATUS fields through
//! #1592 — `ready`, `phase`, `message`, `last_fire_time`, `next_fire_time`,
//! `conditions_json` — deliberately REBIND-PROOF against the concurrent
//! payload/spec reshape #1591: a typed table decoding zero spec fields
//! cannot be broken by a spec-shape change landing in parallel. #1591 has
//! since landed and the CRD schema stabilized, so #1593 — the slice that
//! DOES render spec fields — adds exactly the spec surface the routines
//! explorer page's provenance/pause-state/schedule sections need:
//! `creator_persona`/`provenance_conversation_id` (`RoutineProvenance`),
//! `suspended`/`paused_by`/`paused_at`/`pause_reason` (`RoutineSuspend`, via
//! `RoutineSpec::suspend`'s presence), and `schedule_json`/`next_fires_json`
//! (`RoutineSchedule`, plus the compiled next-fire instants — computed by
//! the caller via `polyc_controller::routine_next_fire::next_n_fires_after`,
//! the SAME shared callable the approval-preview and `routine_list` chat
//! tool already use, never reimplemented here). There is no run-as field to
//! surface: since #1802 a routine's fire principal is its owner
//! (`RoutineProvenance::creator_persona`) by rule, not a resolution a query
//! workload could read separately.
//!
//! # Prompt (the routines explorer table's row-hierarchy fix)
//!
//! `prompt` (`RoutinePayload::prompt`) was deliberately left out of #1593 —
//! at the time, no query workload needed a routine's prompt text. The
//! routines explorer table's row now leads with the prompt (primary text,
//! two-line-clamped) and demotes the routine name to a secondary sub-label,
//! so this record adds exactly that one field. Like every other spec field
//! above, it rides the Fleet-only gate — see
//! `crate::engine::registration::register_reference_tables`'s call site.
//!
//! `conditions_json`/`schedule_json`/`next_fires_json` all store their JSON
//! shape as text, the same "JSON-shaped string column" convention
//! `crate::decode::handoffs`'s `allowed` column already established for a
//! field this crate has no reason to model as a native Arrow list/struct
//! type.
use fmt;
/// One routine's STATUS-ONLY surface — see the module doc's "Status surface
/// ONLY" section for exactly which fields this is (and, just as
/// deliberately, is not).
/// [`RoutineCatalog::list_routines`] failed.
///
/// The control plane's own kube read errored, or (for a deployment with no
/// routine catalog wired at all) the caller never had a catalog to ask.
/// Deliberately a plain, opaque message: this crate logs it
/// (`tracing::warn!`) and builds an EMPTY `routines` table rather than
/// failing the whole Fleet query, the same lenient,
/// one-bad-source-must-not-fail-the-whole-query posture
/// `crate::authority::ScopedQuery::resolve_reference_data` already gives an
/// unavailable persona store.
;
/// Supplies the Fleet `routines` reference table's rows.
///
/// Resolved FRESH on every call — never cached, mirroring
/// `crate::engine::ReferenceData`'s own `personas`/`participations`
/// resolution (`crate::authority::ScopedQuery::resolve_reference_data` calls
/// `PersonaHost::reference_snapshot` fresh per candidate persona per Fleet
/// query, never a cached result — see that type's own doc). The control
/// plane's own implementation wraps `kube::Api<Routine>::list` — see the
/// module doc for why that type never appears in this crate's own
/// signature.