generated from coulomb/repo-seed
Adds preferred workplan REST/event surfaces, legacy-meter telemetry and weekly review summaries, documentation/dashboard terminology updates, dashboard API loading fixes, and close-out sync for STATE-WP-0052 and STATE-WP-0054.
8.2 KiB
8.2 KiB
title
| title |
|---|
| Workplan Queue |
import {apiFetch, waitForVisible, pollDelay, POLL_HEAVY} from "./components/config.js";
const queueState = (async function*() {
let failures = 0;
while (true) {
let stack = [], semantics = {}, ok = false;
try {
const [stackResponse, semanticsResponse] = await Promise.all([
apiFetch("/execution/workplan-stack"),
apiFetch("/execution/semantics"),
]);
ok = stackResponse.ok && semanticsResponse.ok;
if (ok) {
[stack, semantics] = await Promise.all([
stackResponse.json(),
semanticsResponse.json(),
]);
}
} catch {}
failures = ok ? 0 : failures + 1;
yield {stack, semantics, ok, ts: new Date()};
await waitForVisible(pollDelay({ok, base: POLL_HEAVY, failures}));
}
})();
const stack = queueState.stack ?? [];
const semantics = queueState.semantics ?? {};
const _ok = queueState.ok ?? false;
const _ts = queueState.ts;
Workplan Queue
display(html`<div class="queue-live">
<span style="color:${_ok ? 'var(--theme-foreground-focus)' : 'red'}">●</span>
${_ok ? `Live · updated ${_ts?.toLocaleTimeString()}` : html`<span style="color:red">Offline</span>`}
</div>`);
const launchModes = Object.keys(semantics.launch_modes ?? {manual: "", queued: "", scheduled: "", immediate: ""});
const concurrencyModes = Object.keys(semantics.concurrency_modes ?? {sequential: "", parallel: ""});
function optionList(values, selected) {
return values.map(value => html`<option value=${value} selected=${value === selected}>${value}</option>`);
}
function statusCell(row) {
const classes = ["queue-status", row.eligible ? "eligible" : "blocked"].join(" ");
return html`<span class=${classes}>${row.eligible ? "eligible" : "blocked"}</span>`;
}
function blockers(row) {
const parts = [];
if (row.blocked_by_workstream_ids?.length) parts.push(`${row.blocked_by_workstream_ids.length} workstream`);
if (row.blocked_by_task_ids?.length) parts.push(`${row.blocked_by_task_ids.length} task`);
return parts.length ? parts.join(", ") : "—";
}
function queueControls(row) {
const root = html`<div class="queue-controls"></div>`;
const mode = html`<select class="queue-select">${optionList(launchModes, row.launch_mode)}</select>`;
const concurrency = html`<select class="queue-select">${optionList(concurrencyModes, row.concurrency_mode)}</select>`;
const rank = html`<input class="queue-rank" type="number" min="0" step="1" value=${row.queue_rank ?? ""} aria-label="Queue rank">`;
const group = html`<input class="queue-group" type="text" value=${row.execution_group ?? ""} aria-label="Execution group">`;
const message = html`<span class="queue-message"></span>`;
const save = html`<button class="queue-btn" type="button">Save</button>`;
const launch = html`<button class="queue-btn queue-btn-primary" type="button">Request</button>`;
const payload = () => ({
execution_state: mode.value === "manual" ? "manual" : mode.value === "scheduled" ? "scheduled" : "queued",
launch_mode: mode.value,
concurrency_mode: concurrency.value,
queue_rank: rank.value === "" ? null : Number(rank.value),
execution_group: group.value.trim() || null,
});
async function run(label, action) {
message.textContent = label;
message.className = "queue-message";
save.disabled = true;
launch.disabled = true;
try {
await action();
message.textContent = "saved";
message.classList.add("ok");
setTimeout(() => location.reload(), 450);
} catch (error) {
message.textContent = error?.message ?? "failed";
message.classList.add("error");
save.disabled = false;
launch.disabled = false;
}
}
save.onclick = () => run("saving", async () => {
const response = await apiFetch(`/execution/workplans/${row.workstream_id}/intent`, {
method: "PATCH",
headers: {"Content-Type": "application/json"},
body: JSON.stringify(payload()),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
});
launch.onclick = () => run("requesting", async () => {
const intent = payload();
const response = await apiFetch("/execution/launch-requests", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({
workstream_id: row.workstream_id,
requested_by: "dashboard",
requested_actor: "activity-core",
launch_mode: intent.launch_mode,
concurrency_mode: intent.concurrency_mode,
immediate_pickup: intent.launch_mode === "immediate",
priority: row.planning_priority,
notes: `Queue request from dashboard for ${row.slug}`,
}),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
});
root.append(mode, concurrency, rank, group, save, launch, message);
return root;
}
if (stack.length === 0) {
display(html`<p class="queue-empty">No queue candidates.</p>`);
} else {
display(html`<table class="queue-table">
<thead>
<tr>
<th>State</th>
<th>Rank</th>
<th>Workplan</th>
<th>Lifecycle</th>
<th>Priority</th>
<th>Eligibility</th>
<th>Blocked By</th>
<th>Intent</th>
</tr>
</thead>
<tbody>${stack.map(row => html`<tr>
<td>${row.execution_state}</td>
<td>${row.queue_rank ?? row.planning_order ?? "—"}</td>
<td><a href=${`./workstreams/${row.workstream_id}`}>${row.slug}</a><div class="queue-title">${row.title}</div></td>
<td>${row.status}</td>
<td>${row.planning_priority ?? "—"}</td>
<td>${statusCell(row)}</td>
<td>${blockers(row)}</td>
<td>${queueControls(row)}</td>
</tr>`)}</tbody>
</table>`);
}