fix(dashboard): render update_meta_inputs + schedule_prompt approval cards
Both kinds fell through to the spawn branch in renderApprovals, showing a misleading 'spawn' chip and agent-spawn body text. Mara saw a meta-input bump render as a spawn card for agent damocles and denied it. Backend (dashboard.rs): - Add commit_ref: None to the MergeConfigPr arm (struct was incomplete). All arms of ApprovalView now initialise every field. Frontend (call.js): - Add isUpdateMeta / isSchedule booleans alongside the existing kind flags. - Glyph: update_meta_inputs gets ↻, schedule_prompt gets ⏱. - Kind chip: 'meta-update' / 'schedule' (no kind-spawn class for either). - Body: update_meta_inputs parses commit_ref as JSON Vec<String> and shows 'bump flake inputs: foo, bar' or 'bump all flake inputs'; schedule_prompt parses SchedulePromptPayload and shows targets + first-fire time + cadence + a truncated body excerpt. - History row: add 'meta-update' and 'schedule' cases (were both 'spawn'). - Import fmtDuration from util.js (needed for schedule cadence display).
This commit is contained in:
parent
5972605aeb
commit
c65201ed7c
2 changed files with 42 additions and 5 deletions
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
import { $, el, form, Panel, appendLinkified } from './common.js';
|
||||
import { themedToast } from './modal.js';
|
||||
import { fmtAgo } from './util.js';
|
||||
import { fmtAgo, fmtDuration } from './util.js';
|
||||
import { questionsState, QUESTION_HISTORY_LIMIT } from './state.js';
|
||||
|
||||
// Registered by the dashboard entry at boot; defaults to a no-op so the
|
||||
|
|
@ -316,15 +316,17 @@ export function renderApprovals() {
|
|||
const isApply = a.kind === 'apply_commit';
|
||||
const isInit = a.kind === 'init_config';
|
||||
const isMergePr = a.kind === 'merge_config_pr';
|
||||
const isUpdateMeta = a.kind === 'update_meta_inputs';
|
||||
const isSchedule = a.kind === 'schedule_prompt';
|
||||
const li = el('li', { class: 'approval-card' });
|
||||
|
||||
// ── identity header ──────────────────────────────────────────
|
||||
const head = el('div', { class: 'approval-head' },
|
||||
el('span', { class: 'glyph' }, isApply ? '→' : isMergePr ? '⇒' : '⊕'),
|
||||
el('span', { class: 'glyph' }, isApply ? '→' : isMergePr ? '⇒' : isUpdateMeta ? '↻' : isSchedule ? '⏱' : '⊕'),
|
||||
el('span', { class: 'id' }, '#' + a.id),
|
||||
el('span', { class: 'agent' }, a.agent),
|
||||
el('span', { class: 'kind' + ((isApply || isMergePr) ? '' : ' kind-spawn') },
|
||||
isApply ? 'apply' : isMergePr ? 'merge-pr' : isInit ? 'init' : 'spawn'),
|
||||
el('span', { class: 'kind' + ((isApply || isMergePr || isUpdateMeta || isSchedule) ? '' : ' kind-spawn') },
|
||||
isApply ? 'apply' : isMergePr ? 'merge-pr' : isUpdateMeta ? 'meta-update' : isSchedule ? 'schedule' : isInit ? 'init' : 'spawn'),
|
||||
);
|
||||
if ((isApply || isMergePr) && a.sha_short) head.append(el('code', {}, a.sha_short));
|
||||
// When the approval was requested — relative time, right-aligned.
|
||||
|
|
@ -372,6 +374,28 @@ export function renderApprovals() {
|
|||
}, '↳ review PR on forge ↗'));
|
||||
}
|
||||
body.append(drill);
|
||||
} else if (isUpdateMeta) {
|
||||
let inputs;
|
||||
try { inputs = JSON.parse(a.commit_ref || '[]'); } catch (_) { inputs = []; }
|
||||
body.append(el('span', { class: 'meta' },
|
||||
inputs.length
|
||||
? 'bump flake inputs: ' + inputs.join(', ')
|
||||
: 'bump all flake inputs'));
|
||||
} else if (isSchedule) {
|
||||
let payload;
|
||||
try { payload = JSON.parse(a.commit_ref || '{}'); } catch (_) { payload = {}; }
|
||||
const targets = (payload.targets || []).join(', ');
|
||||
const firstFire = payload.first_fire_at_unix
|
||||
? new Date(payload.first_fire_at_unix * 1000).toLocaleString()
|
||||
: '?';
|
||||
const cadence = payload.interval_seconds
|
||||
? ' · ↻ every ' + fmtDuration(payload.interval_seconds)
|
||||
: ' · one-shot';
|
||||
body.append(el('div', { class: 'meta' }, '→ ' + targets + ' · first: ' + firstFire + cadence));
|
||||
if (payload.body) {
|
||||
const excerpt = payload.body.length > 80 ? payload.body.slice(0, 80) + '…' : payload.body;
|
||||
body.append(el('div', { class: 'approval-description' }, excerpt));
|
||||
}
|
||||
} else {
|
||||
body.append(el('span', { class: 'meta' },
|
||||
isInit
|
||||
|
|
@ -418,7 +442,7 @@ function renderApprovalHistory(root, history) {
|
|||
el('span', { class: 'glyph glyph-' + a.status }, glyph), ' ',
|
||||
el('span', { class: 'id' }, '#' + a.id), ' ',
|
||||
el('span', { class: 'agent' }, a.agent), ' ',
|
||||
el('span', { class: 'kind' }, a.kind === 'apply_commit' ? 'apply' : a.kind === 'merge_config_pr' ? 'merge-pr' : a.kind === 'init_config' ? 'init' : 'spawn'), ' ',
|
||||
el('span', { class: 'kind' }, a.kind === 'apply_commit' ? 'apply' : a.kind === 'merge_config_pr' ? 'merge-pr' : a.kind === 'update_meta_inputs' ? 'meta-update' : a.kind === 'schedule_prompt' ? 'schedule' : a.kind === 'init_config' ? 'init' : 'spawn'), ' ',
|
||||
);
|
||||
if (a.sha_short) row.append(el('code', {}, a.sha_short), ' ');
|
||||
row.append(
|
||||
|
|
|
|||
|
|
@ -448,6 +448,13 @@ struct ApprovalView {
|
|||
/// sha. `None` for every other kind.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pr_number: Option<u64>,
|
||||
/// Raw `commit_ref` payload for `UpdateMetaInputs` (JSON-encoded
|
||||
/// `Vec<String>` of input names; `"[]"` = all inputs) and
|
||||
/// `SchedulePrompt` (JSON-encoded `SchedulePromptPayload`). The
|
||||
/// frontend parses this to render a human-readable card body.
|
||||
/// `None` for every other kind.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
commit_ref: Option<String>,
|
||||
/// Unix seconds the approval was queued. Rendered as a relative
|
||||
/// time on the card so the operator can spot a stale request.
|
||||
requested_at: i64,
|
||||
|
|
@ -909,6 +916,7 @@ async fn build_approval_views(approvals: Vec<Approval>) -> Vec<ApprovalView> {
|
|||
diff: Some(diff),
|
||||
description: a.description,
|
||||
pr_number: None,
|
||||
commit_ref: None,
|
||||
requested_at: a.requested_at,
|
||||
}
|
||||
}
|
||||
|
|
@ -920,6 +928,7 @@ async fn build_approval_views(approvals: Vec<Approval>) -> Vec<ApprovalView> {
|
|||
diff: None,
|
||||
description: a.description,
|
||||
pr_number: None,
|
||||
commit_ref: None,
|
||||
requested_at: a.requested_at,
|
||||
},
|
||||
hive_sh4re::ApprovalKind::InitConfig => ApprovalView {
|
||||
|
|
@ -930,6 +939,7 @@ async fn build_approval_views(approvals: Vec<Approval>) -> Vec<ApprovalView> {
|
|||
diff: None,
|
||||
description: a.description,
|
||||
pr_number: None,
|
||||
commit_ref: None,
|
||||
requested_at: a.requested_at,
|
||||
},
|
||||
hive_sh4re::ApprovalKind::UpdateMetaInputs => ApprovalView {
|
||||
|
|
@ -940,6 +950,7 @@ async fn build_approval_views(approvals: Vec<Approval>) -> Vec<ApprovalView> {
|
|||
diff: None,
|
||||
description: a.description,
|
||||
pr_number: None,
|
||||
commit_ref: Some(a.commit_ref),
|
||||
requested_at: a.requested_at,
|
||||
},
|
||||
hive_sh4re::ApprovalKind::SchedulePrompt => ApprovalView {
|
||||
|
|
@ -950,6 +961,7 @@ async fn build_approval_views(approvals: Vec<Approval>) -> Vec<ApprovalView> {
|
|||
diff: None,
|
||||
description: a.description,
|
||||
pr_number: None,
|
||||
commit_ref: Some(a.commit_ref),
|
||||
requested_at: a.requested_at,
|
||||
},
|
||||
hive_sh4re::ApprovalKind::MergeConfigPr => {
|
||||
|
|
@ -971,6 +983,7 @@ async fn build_approval_views(approvals: Vec<Approval>) -> Vec<ApprovalView> {
|
|||
diff: None,
|
||||
description: a.description,
|
||||
pr_number,
|
||||
commit_ref: None,
|
||||
requested_at: a.requested_at,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue