hive-c0re: remove the dashboard's ask/answer surface

This commit is contained in:
damocles 2026-08-29 23:20:17 +02:00
commit 47cac50e6e
8 changed files with 25 additions and 463 deletions

View file

@ -52,15 +52,6 @@ pub(super) struct StateSnapshot {
/// Last 30 resolved approvals (approved / denied / failed), newest-
/// first. Drives the "history" tab on the approvals section.
approval_history: Vec<ApprovalHistoryView>,
/// Pending operator-targeted questions (`target IS NULL`). Any
/// agent can `ask` the operator and `ask` returns immediately with
/// the id; on `/answer-question` we mark the row answered and
/// fire `HelperEvent::QuestionAnswered` back into the asker's
/// inbox. Peer-to-peer questions live in the same table but never
/// surface here (see `OperatorQuestions::pending`).
questions: Vec<QuestionView>,
/// Last 20 answered questions, newest-first.
question_history: Vec<QuestionView>,
/// State dirs (config history + claude creds + /state/ notes) that
/// survive after a destroy-without-purge. The operator can re-spawn
/// with the same name to resume, or PURG3 to wipe them.
@ -155,35 +146,6 @@ async fn infra_container_views() -> Vec<InfraContainerView> {
infra_containers
}
/// `OpQuestion` + computed `question_refs` / `answer_refs`. Built
/// from the snapshot read; the live channel attaches the same
/// fields directly on `QuestionAdded` / `QuestionResolved`.
#[derive(Serialize)]
struct QuestionView {
#[serde(flatten)]
inner: crate::operator_questions::OpQuestion,
#[serde(skip_serializing_if = "Vec::is_empty")]
question_refs: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
answer_refs: Vec<String>,
}
impl QuestionView {
fn from_question(q: crate::operator_questions::OpQuestion) -> Self {
let question_refs = scan_validated_paths(&q.question);
let answer_refs = q
.answer
.as_deref()
.map(scan_validated_paths)
.unwrap_or_default();
Self {
inner: q,
question_refs,
answer_refs,
}
}
}
#[derive(Serialize)]
struct PortConflict {
port: u16,
@ -275,12 +237,12 @@ const CRASH_WARNING_WINDOW: std::time::Duration = std::time::Duration::from_mins
/// Cold-load snapshot of the whole dashboard.
///
/// Includes the roster, approvals (+ history), questions (+ history),
/// Includes the roster, approvals (+ history),
/// tombstones, job queue, meta inputs, and more. Live clients then
/// follow `/api/dashboard/stream` (SSE) for incremental updates keyed
/// off `seq`.
// `StateSnapshot` is a large tree of nested view types (`ContainerView`,
// `ApprovalView`, `QuestionView`, ...) with no `ToSchema` anywhere in that
// `ApprovalView`, ...) with no `ToSchema` anywhere in that
// graph; wiring it up is a schema-modelling project of its own, well past
// "annotate what's reachable". `serde_json::Value` placeholder for now —
// see the batch report.
@ -354,23 +316,6 @@ pub(super) async fn api_state(
let tombstones = build_tombstone_views(&state.coord, &containers);
let port_conflicts = build_port_conflicts(&containers);
// Both operator-targeted and peer threads surface on the dashboard
// (the client filters by target). Each row is wrapped in QuestionView
// so the snapshot carries the same file_refs the live event variants
// attach.
let questions: Vec<QuestionView> =
log_default("questions.pending_all", state.coord.questions.pending_all())
.into_iter()
.map(QuestionView::from_question)
.collect();
let question_history: Vec<QuestionView> = log_default(
"questions.recent_answered_all",
state.coord.questions.recent_answered_all(20),
)
.into_iter()
.map(QuestionView::from_question)
.collect();
// Banner warnings: host probes (disk) + agent-state (pending logins,
// crashing agents). Built before the response struct because the
// agent-state producer borrows `containers`, which moves in below.
@ -396,8 +341,6 @@ pub(super) async fn api_state(
approval_history,
meta_inputs: read_meta_inputs(),
meta_update_running: state.coord.meta_update_in_progress(),
questions,
question_history,
tombstones,
port_conflicts,
forge_present: crate::forge::is_present().await,