feat(web): surface ai loop writeback receipts
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 55s
CD Pipeline / build-and-deploy (push) Successful in 4m44s
CD Pipeline / post-deploy-checks (push) Successful in 1m51s

This commit is contained in:
Your Name
2026-07-02 01:05:50 +08:00
parent 38cd190169
commit ef1c4dbf55
4 changed files with 405 additions and 5 deletions

View File

@@ -200,6 +200,53 @@ type PriorityWorkOrderPayload = {
} | null;
};
type LogConsumerReadbackPayload = {
status?: string | null;
active_blockers?: string[] | null;
controlled_consume?: {
controlled_consume_allowed?: boolean | null;
runtime_target_write_performed?: boolean | null;
} | null;
target_rollups?: Array<{
target?: string | null;
binding_count?: number | null;
ready_binding_count?: number | null;
target_write_performed?: boolean | null;
context_receipt_write_count?: number | null;
metadata_only?: boolean | null;
}> | null;
rollups?: {
dispatch_ledger_row_count?: number | null;
consumer_apply_receipt_row_count?: number | null;
consumer_binding_count?: number | null;
ready_consumer_binding_count?: number | null;
ready_target_count?: number | null;
metadata_only_receipt_count?: number | null;
post_apply_verifier_ref_count?: number | null;
target_context_receipt_write_count?: number | null;
controlled_consumer_readback_ready?: boolean | null;
runtime_target_write_performed?: boolean | null;
km_consumer_binding_count?: number | null;
rag_consumer_binding_count?: number | null;
playbook_consumer_binding_count?: number | null;
mcp_consumer_binding_count?: number | null;
verifier_consumer_binding_count?: number | null;
ai_agent_consumer_binding_count?: number | null;
km_context_receipt_write_count?: number | null;
rag_context_receipt_write_count?: number | null;
playbook_context_receipt_write_count?: number | null;
mcp_context_receipt_write_count?: number | null;
verifier_context_receipt_write_count?: number | null;
ai_agent_context_receipt_write_count?: number | null;
} | null;
operation_boundaries?: {
runtime_target_write_performed?: boolean | null;
raw_log_payload_persisted?: boolean | null;
secret_value_collection_allowed?: boolean | null;
github_api_used?: boolean | null;
} | null;
};
type PanelMode = "full" | "compact";
type Tone = "ok" | "warn" | "neutral";
type WorkFilter = "all" | "completed" | "active" | "pending" | "blocked";
@@ -297,6 +344,23 @@ async function fetchPriorityWorkOrder(): Promise<PriorityWorkOrderPayload | null
}
}
async function fetchLogConsumerReadback(): Promise<LogConsumerReadbackPayload | null> {
const controller = new AbortController();
const timeout = window.setTimeout(() => controller.abort(), 12_000);
try {
const response = await fetch(`${API_BASE}/api/v1/agents/agent-log-controlled-writeback-consumer-readback`, {
cache: "no-store",
signal: controller.signal,
});
if (!response.ok) return null;
return (await response.json()) as LogConsumerReadbackPayload;
} catch {
return null;
} finally {
window.clearTimeout(timeout);
}
}
export function AutonomousRuntimeReceiptPanel({
mode = "full",
}: {
@@ -306,6 +370,7 @@ export function AutonomousRuntimeReceiptPanel({
const locale = useLocale();
const [payload, setPayload] = useState<RuntimeControlPayload | null>(null);
const [priorityPayload, setPriorityPayload] = useState<PriorityWorkOrderPayload | null>(null);
const [consumerPayload, setConsumerPayload] = useState<LogConsumerReadbackPayload | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(false);
const [updatedAt, setUpdatedAt] = useState<Date | null>(null);
@@ -313,12 +378,14 @@ export function AutonomousRuntimeReceiptPanel({
const refresh = useCallback(async () => {
setLoading(true);
const [next, priority] = await Promise.all([
const [next, priority, consumer] = await Promise.all([
fetchRuntimeControl(),
fetchPriorityWorkOrder(),
fetchLogConsumerReadback(),
]);
setPayload(next);
setPriorityPayload(priority);
setConsumerPayload(consumer);
setError(next === null);
setUpdatedAt(next ? new Date() : null);
setLoading(false);
@@ -347,6 +414,14 @@ export function AutonomousRuntimeReceiptPanel({
const sourceFamilyItems = readback?.work_item_progress?.source_family_items ?? [];
const latestFlow = readback?.latest_flow_closure;
const rollups = payload?.rollups ?? {};
const consumerRollups = consumerPayload?.rollups ?? {};
const consumerBlockers = consumerPayload?.active_blockers ?? [];
const consumerReady = consumerRollups.controlled_consumer_readback_ready === true
|| consumerPayload?.status === "controlled_writeback_consumer_readback_ready";
const runtimeTargetWritePerformed = consumerRollups.runtime_target_write_performed === true
|| consumerPayload?.controlled_consume?.runtime_target_write_performed === true
|| consumerPayload?.operation_boundaries?.runtime_target_write_performed === true
|| rollups.live_log_controlled_writeback_runtime_target_write_count === 1;
const closed = ledger?.closed === true || latestFlow?.closed === true;
const dbOk = readback?.db_read_status === "ok";
const missingStages = traceLedger?.missing_required_stage_ids
@@ -607,6 +682,125 @@ export function AutonomousRuntimeReceiptPanel({
icon: Activity,
tone: toNumber(rollups.live_log_classified_event_total ?? logRollups.classified_event_total) > 0 ? "ok" as Tone : "neutral" as Tone,
},
{
key: "consumer",
label: t("proof.consumer"),
value: `${numberValue(
rollups.live_log_controlled_writeback_consumer_apply_receipt_count
?? consumerRollups.consumer_apply_receipt_row_count
)}/${numberValue(
rollups.live_log_controlled_writeback_consumer_dispatch_ledger_count
?? consumerRollups.dispatch_ledger_row_count
)}`,
detail: t("proof.consumerDetail", {
targets: numberValue(consumerRollups.ready_target_count),
}),
icon: Database,
tone: consumerReady ? "ok" as Tone : "warn" as Tone,
},
];
const writebackCards = [
{
key: "dispatch",
label: t("writeback.dispatch"),
value: numberValue(
rollups.live_log_controlled_writeback_consumer_dispatch_ledger_count
?? consumerRollups.dispatch_ledger_row_count
),
detail: t("writeback.dispatchDetail"),
icon: Send,
tone: consumerReady ? "ok" as Tone : "warn" as Tone,
},
{
key: "apply",
label: t("writeback.apply"),
value: numberValue(
rollups.live_log_controlled_writeback_consumer_apply_receipt_count
?? consumerRollups.consumer_apply_receipt_row_count
),
detail: t("writeback.applyDetail"),
icon: CheckCircle2,
tone: runtimeTargetWritePerformed ? "ok" as Tone : "neutral" as Tone,
},
{
key: "bindings",
label: t("writeback.bindings"),
value: `${numberValue(consumerRollups.ready_consumer_binding_count)}/${numberValue(consumerRollups.consumer_binding_count)}`,
detail: t("writeback.bindingsDetail"),
icon: Bot,
tone: toNumber(consumerRollups.ready_consumer_binding_count) === toNumber(consumerRollups.consumer_binding_count)
&& toNumber(consumerRollups.consumer_binding_count) > 0
? "ok" as Tone
: "warn" as Tone,
},
{
key: "targets",
label: t("writeback.targets"),
value: numberValue(consumerRollups.ready_target_count),
detail: t("writeback.targetsDetail"),
icon: ListChecks,
tone: toNumber(consumerRollups.ready_target_count) >= 6 ? "ok" as Tone : "warn" as Tone,
},
{
key: "context",
label: t("writeback.contextWrites"),
value: numberValue(consumerRollups.target_context_receipt_write_count),
detail: t("writeback.contextWritesDetail"),
icon: BookOpenCheck,
tone: runtimeTargetWritePerformed ? "ok" as Tone : "neutral" as Tone,
},
{
key: "blockers",
label: t("writeback.blockers"),
value: numberValue(consumerBlockers.length),
detail: consumerBlockers.length > 0 ? consumerBlockers[0] : t("writeback.noBlockers"),
icon: TriangleAlert,
tone: consumerBlockers.length > 0 ? "warn" as Tone : "ok" as Tone,
},
];
const targetWritebackCards = [
{
key: "km",
label: t("writeback.targetsMap.km"),
binding: consumerRollups.km_consumer_binding_count,
writes: consumerRollups.km_context_receipt_write_count,
icon: BookOpenCheck,
},
{
key: "rag",
label: t("writeback.targetsMap.rag"),
binding: consumerRollups.rag_consumer_binding_count,
writes: consumerRollups.rag_context_receipt_write_count,
icon: Database,
},
{
key: "playbook",
label: t("writeback.targetsMap.playbook"),
binding: consumerRollups.playbook_consumer_binding_count,
writes: consumerRollups.playbook_context_receipt_write_count,
icon: ShieldCheck,
},
{
key: "mcp",
label: t("writeback.targetsMap.mcp"),
binding: consumerRollups.mcp_consumer_binding_count,
writes: consumerRollups.mcp_context_receipt_write_count,
icon: Bot,
},
{
key: "verifier",
label: t("writeback.targetsMap.verifier"),
binding: consumerRollups.verifier_consumer_binding_count,
writes: consumerRollups.verifier_context_receipt_write_count,
icon: Gauge,
},
{
key: "aiAgent",
label: t("writeback.targetsMap.aiAgent"),
binding: consumerRollups.ai_agent_consumer_binding_count,
writes: consumerRollups.ai_agent_context_receipt_write_count,
icon: Rocket,
},
];
const visibleWorkItems = orderedWorkItems.filter((item) => matchesWorkFilter(item, workFilter));
const orderedCompleted = orderedWorkItems.filter((item) => item.status === "completed").length;
@@ -674,7 +868,7 @@ export function AutonomousRuntimeReceiptPanel({
<div
data-testid="ai-automation-production-proof"
className="grid gap-px border-b border-[#e0ddd4] bg-[#e0ddd4] md:grid-cols-5"
className="grid gap-px border-b border-[#e0ddd4] bg-[#e0ddd4] md:grid-cols-3 xl:grid-cols-6"
>
{proofCards.map((card) => {
const Icon = card.icon;
@@ -699,6 +893,60 @@ export function AutonomousRuntimeReceiptPanel({
})}
</div>
<div
data-testid="ai-loop-consumer-writeback-readback"
className="border-b border-[#e0ddd4] bg-white"
>
<div className="grid gap-px bg-[#e0ddd4] md:grid-cols-3 xl:grid-cols-6">
{writebackCards.map((card) => {
const Icon = card.icon;
return (
<div key={card.key} className="bg-white px-4 py-3">
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<p className="text-xs font-semibold text-[#77736a]">{card.label}</p>
<p className="mt-2 truncate font-mono text-lg font-semibold text-[#141413]">
{card.value}
</p>
</div>
<span className={cn("flex h-8 w-8 shrink-0 items-center justify-center border", toneClass(card.tone))}>
<Icon className="h-4 w-4" aria-hidden="true" />
</span>
</div>
<p className="mt-1 line-clamp-2 text-xs leading-5 text-[#5f5b52]">
{card.detail}
</p>
</div>
);
})}
</div>
<div className="grid gap-px border-t border-[#e0ddd4] bg-[#e0ddd4] md:grid-cols-3 xl:grid-cols-6">
{targetWritebackCards.map((card) => {
const Icon = card.icon;
const bindingCount = toNumber(card.binding);
const writeCount = toNumber(card.writes);
return (
<div key={card.key} className="bg-white px-4 py-3">
<div className="flex items-start justify-between gap-3">
<div>
<p className="text-xs font-semibold text-[#77736a]">{card.label}</p>
<p className="mt-2 font-mono text-lg font-semibold text-[#141413]">
{numberValue(writeCount)}
{" / "}
{numberValue(bindingCount)}
</p>
</div>
<Icon className="h-4 w-4 text-[#87867f]" aria-hidden="true" />
</div>
<p className="mt-1 text-xs leading-5 text-[#5f5b52]">
{t("writeback.targetDetail")}
</p>
</div>
);
})}
</div>
</div>
<div className="grid grid-cols-2 gap-px bg-[#e0ddd4] md:grid-cols-5 xl:grid-cols-12">
<div className="bg-white px-4 py-3">
<p className="text-xs font-semibold text-[#77736a]">{t("metrics.loop")}</p>