Repository navigation
feat: operations UI — useOperation, OperationProgress, header tray and attention (#895 PR2) - #979
Merged
Merged
Conversation
…895) useOperation(id, { bootId }) follows one server operation over the shell's single SSE connection and reconciles from GET /api/operations/:id?children=1 on bakin.reconcile. A view replaces the held one only with a higher rev; events from another boot never apply; a read from another boot latches `restarted` and nothing after it applies; a same-boot 404 is `expired`; a failed read is `unreachable` and retries with bounded backoff while keeping the last view. Children are tracked by parentId and returned in the parent's declared order. cancel() posts to the cancel route. startOperation(fetcher) returns a converted route's { operationId, bootId } and throws OperationStartError with the route's own status and body. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The one progress surface for long-running work, in @makinbakin/sdk/patterns
(SDK-owned beside the search patterns: it renders the SDK's operation
types, and the private kit may not depend on the SDK). It composes
Timeline, Progress, Alert, Button, StatusBadge, Collapsible and CopyButton
with no new tokens:
- the declared plan as a Timeline; the active stage carries a Progress bar
that is determinate only when the size is known, with the value in words;
items render as a nested timeline (reindex tables, sync files);
- quiet and stuck judged from progress alone via operationQuietState,
output shown as liveness and never resetting the clock; a restart never
also reads as stuck;
- the waiting overlay names the holder (stale holders say so);
- Cancel only while allowed, otherwise the reason ("Can't be cancelled",
"Will finish — past the point of no return");
- an Alert for stuck, failed, partially applied, verification failed,
cancelling, cancelled, expired, restarted and unreachable, with Re-check
where the outcome is unknown;
- child operations nest under the parent's first stage (repair: children,
then verifying);
- the activity log under a collapsed Details with copy;
- a compact variant for the tray and rows.
Storybook: Components/Feedback/OperationProgress, CanonicalUsage plus 22
state stories with play assertions. Five approved visual baselines
(desktop and mobile). Public API inventory and performance ceilings
updated with the maintainer's approval (+~15 KB minified in the shared
SDK chunk; host initial JS unchanged).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The header shows an operations tray only while something runs or finished in the last two minutes: a Popover of compact OperationProgress rows with Cancel (while allowed; a refused cancel says why) and a link to the operation's href. The live list merges GET /api/operations with operation.updated under the rev/boot guards, lists top-level operations only, resets on a new boot, and ignores a malformed answer instead of crashing the header. OperationsAttentionProvider, mounted beside ApprovalsAttentionProvider, toasts and sends an OS notification for finished and failed (once per operation, surviving SSE milestone replay) and stuck (once per stage, judged with operationQuietState on the registry's stuck milestone) — only for web/cli/discord origins, never for cancellations, children or system work, and not while the person is on the operation's page. Host initial JS +6.5 KB (ceiling recorded). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#895) UI overview gains "Long-running work" (startOperation + useOperation + OperationProgress, phases, onRecheck, what the tray and attention already cover); the plugin server-contracts page points at it; the style guide's feedback rules name operations as the only long-running UI; the operations knowledge doc gains the Browser section, map rows and tests; the CLAUDE.md Operations entry names the browser surface. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… census (#895) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR2 of #895 (plan:
.claude/specs/operations-progress/PLAN.md§ PR 2). Builds on the operations primitive from #976. No flows are converted yet (PR3–PR6); this ships the browser surface they will use.What's in it
useOperation(id, { bootId })+startOperation(fetcher)(@makinbakin/sdk/hooks): follows one operation over the shell's single SSE connection and reconciles onbakin.reconcile. Highestrevwins; events from another boot never apply; a read answered by another boot latchesrestarted(a late old-boot answer is ignored); same-boot 404 →expired; failed reads →unreachablewith 1/2/4/8/15s backoff, keeping the last view.startOperationthrowsOperationStartErrorcarrying the route's own status and body.OperationProgress(@makinbakin/sdk/patterns, SDK-owned beside the search patterns, because the private kit can't depend on SDK types). Composes Timeline, Progress, Alert, Button, StatusBadge, Collapsible and CopyButton; no new tokens. It shows:Components/Feedback/OperationProgress: CanonicalUsage + 22 states, all play-asserted; 5 visual baselines (desktop + mobile), approved.hreflink. Malformed list answers are ignored instead of crashing the header.OperationsAttentionProvider(besideApprovalsAttentionProvider): toast + OS notification for finished/failed (once per operation, safe under SSE replay) and stuck (once per stage), forweb/cli/discordorigins only. Never for cancellations, children, system work, or while on the operation's page.style-guide.md;.claude/knowledge/operations.mdBrowser section;Performance
Measured against main with
bakin-bits-officialpinned to its compatibility ref:Ceilings were regenerated; the regeneration also tightened several unrelated ceilings.
Verification
tests/hooks/use-operation.test.tsx(rev/boot guards, reconcile, expired, restarted + late old-boot answer, unreachable recovery);tests/ui/patterns/operation-progress.test.tsx;tests/host/operations-{tray,attention}.test.tsx;ui:conformance --full: every stage up to repository tests passes locally; the later stages run in CI.bun run build: all three binaries built. The compiled-binary smoke is gated on Linux CI.🤖 Generated with Claude Code