push architecture slice 7: graph-status + practice surfaces ride the events channel; coverage polls accepted-by-design; stale poll comments + architecture.md observability section rewritten to the push model

This commit is contained in:
amosblomqvist
2026-10-02 18:03:47 +03:00
parent 8795345c82
commit 26131b3599
13 changed files with 190 additions and 104 deletions
+2 -2
View File
@@ -7,7 +7,7 @@ import type { ArtifactPartProps, DemoResult } from "@/src/core";
/**
* Demo artifact card (W6): while the part is input-available, a job activity
* card (poll /api/jobs — the job-card idiom); when output-available, the demo
* card (the chat view's jobs feed — the job-card idiom); when output-available, the demo
* runs in a SANDBOXED iframe (sandbox="allow-scripts", no allow-same-origin →
* no access to app storage/cookies/DOM).
*
@@ -70,7 +70,7 @@ export function reduceDemoBridgeState(state: DemoBridgeState, data: unknown): De
// ─── the demo card ──────────────────────────────────────────────────────────
/** The demo tool-call box: the part's output is the dispatch ack — the box
* is the DispatchCard (live status via the central jobs poll). The demo
* is the DispatchCard (live status via the chat view's jobs feed). The demo
* itself renders from the steer message via DemoOutputCard. */
export function DemoCard({ part }: ArtifactPartProps) {
if (part.state !== "output-available") return null;
+3 -3
View File
@@ -11,9 +11,9 @@ import { BrailleSpinner } from "@/components/braille-spinner";
* The dispatch card (contracts.ts "THE ASYNC PROTOCOL" §4): the tool-call
* box for a background sub-agent. The tool part's output is the FIRE-AND-
* FORGET ACK (a string) — never the result. Live status comes from the
* central jobs poll via JobsContext, matched by toolCallId:
* chat-view jobs feed via JobsContext, matched by toolCallId:
*
* job unknown → "dispatched" pulse (the poll hasn't seen it yet)
* job unknown → "dispatched" pulse (the jobs feed hasn't seen it yet)
* running → live activity card (activity line + elapsed)
* done/error → quiet one-liner ("delivered" / "failed") — the artifact
* itself arrives in the steer message right below, so the
@@ -85,7 +85,7 @@ function DispatchRefusedCard({ label, line, button }: { label: string; line: str
}
function DispatchActivityCard({ job, label }: { job: JobRecord | null; label: string }) {
// Elapsed from createdAt — jobs appear here only via the poll (post-mount),
// Elapsed from createdAt — jobs appear here only via the jobs feed (post-mount),
// so no SSR/hydration mismatch.
const elapsed =
job && job.createdAt ? Math.max(0, Math.round((Date.now() - Date.parse(job.createdAt)) / 1000)) : null;
+1 -1
View File
@@ -14,7 +14,7 @@ import { ToolCallCard } from "@/components/artifacts/tool-call-card";
* edit chat.tsx (see docs/plan.md file-ownership rules).
*
* Typical shape: while the part is input-available render an activity card
* (poll GET /api/jobs?chatId=…; on completion call
* (the chat view's jobs feed reads GET /api/jobs?chatId=…; on completion call
* ChatActionsContext.syncFromServer()); when output-available render the
* artifact itself from part.output.
*/
+2 -2
View File
@@ -8,7 +8,7 @@ import type { ArtifactPartProps, PdfResult, ResearchResult } from "@/src/core";
* The research tool-call box (contracts.ts "THE ASYNC PROTOCOL" §4).
*
* The part's output is the fire-and-forget dispatch ACK — the box itself is
* the DispatchCard (live status via the central jobs poll; quiet one-liner
* the DispatchCard (live status via the chat view's jobs feed; quiet one-liner
* once delivered). The research brief itself no longer renders from the
* tool part at all: it rides the steer message (the user-role delivery
* injected after the wake turn) and renders there via ResearchBriefCard.
@@ -21,7 +21,7 @@ export function JobCard({ part }: ArtifactPartProps) {
/**
* The pdf dispatch box (sources v0) — the same async-protocol idiom as
* JobCard: the ack output drives a DispatchCard (live "PDF reader" status
* via the central jobs poll); the brief itself rides the steer message
* via the chat view's jobs feed); the brief itself rides the steer message
* and renders under the PDF-complete badge in the transcript.
*/
export function PdfJobCard({ part }: ArtifactPartProps) {
+1 -1
View File
@@ -6,7 +6,7 @@ import type { ArtifactPartProps, VisualResult } from "@/src/core";
/**
* Visual artifact card (W6 → ack protocol): the tool part's output is the
* dispatch ack, so the tool-call box is the DispatchCard (live status via
* the central jobs poll). The SVG itself renders from the steer message
* the chat view's jobs feed). The SVG itself renders from the steer message
* (sub-agent result delivery) via VisualOutputCard.
*
* The SVG is model-authored for the learner's own machine (their own teacher