- {presentation.settlementMessage !== undefined && state.updateId !== undefined ? (
-
- Attempt{" "}
-
-
- ) : null}
- {state.launcherRefresh === "recommended" || state.launcherSettlement === "unsupported" ? (
-
Launcher refresh recommended
- ) : null}
- {presentation.refusalMessage !== undefined &&
- actionError !== presentation.refusalMessage ? (
-
- {presentation.refusalMessage}
-
- ) : null}
- {freshness !== "fresh" ? (
-
- {freshnessMessage}
-
- ) : null}
- {followerSummary !== undefined ? (
-
- {followerSummary}
- {followerHold !== undefined &&
- candidate !== undefined &&
- state.policy?.leaderSource !== null
- ? ` · ${candidate.label} build is not the laptop's, so it will not install`
- : ""}
-
- ) : null}
- {actionError ? (
-
- {actionError}
-
- ) : null}
- {outcome !== undefined && outcome.status !== "committed" ? (
-
-
- }
- >
- {presentation.settled
- ? "No changes"
- : outcome.status === "rolled-back"
- ? "Rolled back"
- : "Blocked"}
- : {outcomeText}
-
- {outcomeBuildDetail(state)}
-
- ) : null}
- {state.capability.reason === "bootstrap-required" ? (
-
- Installing needs a qualified launcher on this host first.
-
- ) : null}
{detailsOpen ? (
+ {otherNotes.length > 0 ? (
+
+
+ {otherNotes.map((note) => (
+
+ {note.text}
+
+ ))}
+
+
+ ) : null}
+ {followerSummary !== undefined ? (
+ {followerSummary}
+ ) : null}
+ {operation !== undefined ? (
+
+
+ {operation.planCorrelated ? Part of this laptop's update plan : null}
+
+ ) : null}
{state.launcherVersion === undefined ? null : (
{state.launcherVersion}
)}
diff --git a/apps/web/src/jones/updates/JonesUpdateControls.tsx b/apps/web/src/jones/updates/JonesUpdateControls.tsx
index 02bf6d61c5..7bde52b2e5 100644
--- a/apps/web/src/jones/updates/JonesUpdateControls.tsx
+++ b/apps/web/src/jones/updates/JonesUpdateControls.tsx
@@ -33,7 +33,7 @@ import { useAtomRefresh, useAtomValue } from "@effect/atom-react";
import * as Effect from "effect/Effect";
import { layerRemoteHttpClient } from "@t3tools/client-runtime/rpc";
import { AsyncResult, Atom } from "effect/reactivity";
-import { useEffect, useRef, useState } from "react";
+import { useEffect, useRef, useState, type ReactNode } from "react";
import { appAtomRegistry } from "../../rpc/atomRegistry";
import { ProductionExactSourceUpdateAction } from "./ProductionExactSourceUpdateAction";
import { jonesUpdates } from "./jonesUpdates";
@@ -115,11 +115,14 @@ export function JonesUpdateControls({
environmentId,
layout = "inline",
compareWithLaptop = false,
+ extraAction,
}: {
readonly environmentId: EnvironmentId;
readonly layout?: JonesBuildStatusLayout;
/** Compares this host with the verified laptop build and offers an explicit matching download. */
readonly compareWithLaptop?: boolean;
+ /** A compact control placed before Check and Download, such as automatic updates. */
+ readonly extraAction?: ReactNode;
}) {
const observed = useAtomValue(jonesUpdates.observation(environmentId));
const journal = useJonesExecutionJournal();
@@ -309,95 +312,101 @@ export function JonesUpdateControls({
}
actionError={actionError}
layout={layout}
+ check={status.check}
+ operation={
+ status.operation === undefined
+ ? undefined
+ : { ...status.operation, headline: status.headline }
+ }
extraAction={
- showMatch ? (
- {
- const latest = appAtomRegistry.get(laptopInstalledSourceAtom);
- return latest !== undefined && latest.environmentId === laptop?.environmentId
- ? latest.source.sha
- : undefined;
- }}
- subject="this laptop"
- allowInstall={false}
- buttonLabel="Match laptop"
- compact
- />
+ showMatch || extraAction !== undefined ? (
+ <>
+ {extraAction}
+ {showMatch ? (
+ {
+ const latest = appAtomRegistry.get(laptopInstalledSourceAtom);
+ return latest !== undefined && latest.environmentId === laptop?.environmentId
+ ? latest.source.sha
+ : undefined;
+ }}
+ subject="this laptop"
+ allowInstall={false}
+ buttonLabel="Match laptop"
+ compact
+ />
+ ) : null}
+ >
) : undefined
}
onRequest={(request) => void run(request)}
/>
- {status.check ? {status.check.text}
: null}
- {status.operation ? (
-
- {status.headline} · Operation {status.operation.operationId}
- {status.operation.planCorrelated ? " · Part of the Laptop's update plan" : ""}
-
- ) : null}
- {status.actions.stop ? (
- {
- const receipt = observation.state?.operation;
- if (receipt === undefined || stopping || stopDuty !== undefined) return;
- setStopping(true);
- void stopWebJonesDownload({
- environmentId,
- receipt,
- uuid: randomUUID,
- canOperate: () =>
- authority.isCurrent() &&
- (() => {
- const latest = appAtomRegistry.get(
- environmentSession.sessionStateAtom(environmentId),
- );
- return (
- latest._tag === "Success" &&
- latest.value.authenticated &&
- sessionGrantsScope(latest.value, AuthOrchestrationOperateScope)
- );
- })(),
- send: async (input) => {
- const result = await stop({
- environmentId,
- input: { action: "operationStop", input },
- });
- if (result._tag !== "Success") throw new Error("Stop is not confirmed.");
- return result.value;
- },
- })
- .then(setActionError)
- .catch(() => setActionError("Stop is not confirmed. Read its exact operation."))
- .finally(() => setStopping(false));
- }}
- >
- Stop download
-
- ) : null}
- {journal.duties.some((d) => d.environmentId === environmentId && d.kind !== "legacy") ? (
- {
- void readUnresolvedJonesExecution(environmentId).catch(() =>
- setActionError("The exact outcome is unavailable. Nothing will be resent."),
- );
- }}
- >
- Read operation status
-
- ) : null}
+
+ {status.actions.stop ? (
+ {
+ const receipt = observation.state?.operation;
+ if (receipt === undefined || stopping || stopDuty !== undefined) return;
+ setStopping(true);
+ void stopWebJonesDownload({
+ environmentId,
+ receipt,
+ uuid: randomUUID,
+ canOperate: () =>
+ authority.isCurrent() &&
+ (() => {
+ const latest = appAtomRegistry.get(
+ environmentSession.sessionStateAtom(environmentId),
+ );
+ return (
+ latest._tag === "Success" &&
+ latest.value.authenticated &&
+ sessionGrantsScope(latest.value, AuthOrchestrationOperateScope)
+ );
+ })(),
+ send: async (input) => {
+ const result = await stop({
+ environmentId,
+ input: { action: "operationStop", input },
+ });
+ if (result._tag !== "Success") throw new Error("Stop is not confirmed.");
+ return result.value;
+ },
+ })
+ .then(setActionError)
+ .catch(() => setActionError("Stop is not confirmed. Read its exact operation."))
+ .finally(() => setStopping(false));
+ }}
+ >
+ Stop download
+
+ ) : null}
+ {journal.duties.some((d) => d.environmentId === environmentId && d.kind !== "legacy") ? (
+ {
+ void readUnresolvedJonesExecution(environmentId).catch(() =>
+ setActionError("The exact outcome is unavailable. Nothing will be resent."),
+ );
+ }}
+ >
+ Read operation status
+
+ ) : null}
+
);
}
diff --git a/apps/web/src/jones/updates/disclosure.tsx b/apps/web/src/jones/updates/disclosure.tsx
index 0ec5108b24..f1af82bb00 100644
--- a/apps/web/src/jones/updates/disclosure.tsx
+++ b/apps/web/src/jones/updates/disclosure.tsx
@@ -61,11 +61,6 @@ export function DetailItem({
export const DETAIL_PANEL_CLASSNAME =
"space-y-2 rounded-md bg-muted/40 px-2.5 py-2 text-muted-foreground";
-/**
- * The two button slots that end each update line in an environment row. Stacked lines put
- * their secondary action (Check, Turn on/off) in the first slot, so their buttons share
- * edges whatever the labels; a longer label widens only its own line. Kept right-aligned
- * when the line wraps.
- */
+/** The buttons that end an update line in an environment row, kept right-aligned when it wraps. */
export const INLINE_ACTIONS_CLASSNAME =
- "ml-auto grid shrink-0 grid-cols-[minmax(4.5rem,auto)_minmax(5.25rem,auto)] items-center gap-1.5 sm:grid-cols-[minmax(4rem,auto)_minmax(4.75rem,auto)]";
+ "ml-auto flex shrink-0 flex-wrap items-center justify-end gap-1.5";
diff --git a/apps/web/src/jones/workQueue/PromptsSettings.browser.test.tsx b/apps/web/src/jones/workQueue/PromptsSettings.browser.test.tsx
index cefd1b8476..e28e8d3637 100644
--- a/apps/web/src/jones/workQueue/PromptsSettings.browser.test.tsx
+++ b/apps/web/src/jones/workQueue/PromptsSettings.browser.test.tsx
@@ -15,7 +15,6 @@ vi.mock("~/localApi", async (importOriginal) => ({
vi.mock("../../components/settings/settingsLayout", async (importOriginal) => ({
...(await importOriginal
()),
SETTINGS_PICKER_TRIGGER_CLASSNAME: "",
- SettingsPageContainer: ({ children }: { children: ReactNode }) => children,
SettingsSection: ({ children, title }: { children: ReactNode; title: string }) => (
{title}
@@ -94,7 +93,7 @@ describe("PromptsSettings persistence", () => {
await root.rerender( );
});
expect(container.querySelector("select")?.value).toBe("pending");
- expect(container.querySelector("h2")?.textContent).toBe("Jones Code");
+ expect(container.querySelector("h2")?.textContent).toBe("Prompts");
const control = container.querySelector("select")!;
const saved = new Promise((resolve) => {
storage.setClientSettings.mockImplementationOnce(async (settings) => {
diff --git a/apps/web/src/jones/workQueue/PromptsSettings.tsx b/apps/web/src/jones/workQueue/PromptsSettings.tsx
index 73501c9a2b..9c14ab5afe 100644
--- a/apps/web/src/jones/workQueue/PromptsSettings.tsx
+++ b/apps/web/src/jones/workQueue/PromptsSettings.tsx
@@ -1,11 +1,7 @@
import { ThreadActivitySettings } from "../threadStatusFilter/ThreadActivitySettings";
import { PresentationOrderSettings } from "../presentationOrder/PresentationOrderSettings";
import { persistClientSettingsPatch, useClientSettings } from "../../hooks/useSettings";
-import {
- SettingsPageContainer,
- SettingsRow,
- SettingsSection,
-} from "../../components/settings/settingsLayout";
+import { SettingsRow, SettingsSection } from "../../components/settings/settingsLayout";
import { searchableSetting } from "../../components/settings/settingsSearch";
import {
Select,
@@ -20,11 +16,11 @@ const stageLabels = { pending: "Pending", queued: "Queued", sent: "Sent" } as co
export function PromptsSettings() {
const stage = useClientSettings((settings) => settings.promptsDefaultStage);
return (
-
-
+ <>
+
-
+
{stageLabels[stage]}
-
- Pending
- Queued
- Sent
+
+
+ Pending
+
+
+ Queued
+
+
+ Sent
+
}
@@ -51,6 +50,6 @@ export function PromptsSettings() {
-
+ >
);
}
diff --git a/apps/web/src/jones/workstreams/SetupStatusContent.tsx b/apps/web/src/jones/workstreams/SetupStatusContent.tsx
index 4296280fab..8b5ba88ce8 100644
--- a/apps/web/src/jones/workstreams/SetupStatusContent.tsx
+++ b/apps/web/src/jones/workstreams/SetupStatusContent.tsx
@@ -1,9 +1,12 @@
+import { ChevronRightIcon } from "lucide-react";
import {
isNativeWorkstreams,
nativeWorkstreamsLabel,
type NativeWorkstreamsCapability,
} from "@t3tools/client-runtime/state/workstreams";
import type { WorkstreamSetupStatus } from "@t3tools/contracts/jones/workstreamOperations";
+import { Badge } from "../../components/ui/badge";
+import { useRelativeTimeState } from "../../hooks/useRelativeTimeState";
export const sourceReasonLabels = {
descriptor_absent: "Source descriptor unavailable. Check native source enrollment.",
@@ -16,6 +19,82 @@ export const sourceReasonLabels = {
authority_unavailable: "Source authority is unavailable. Restore the source connection.",
} as const;
+export type SetupStatusTone = "success" | "warning" | "error";
+
+export function SetupStatusLine({
+ tone,
+ badge,
+ children,
+}: {
+ tone: SetupStatusTone;
+ badge: string;
+ children: string;
+}) {
+ return (
+
+
+ {badge}
+
+ {children}
+
+ );
+}
+
+function sourceIssue(status: WorkstreamSetupStatus, native: boolean): string | null {
+ const reason = status.t3_source.reason;
+ if (status.t3_source.state === "ready" || reason === null) return null;
+ if (!native) return sourceReasonLabels[reason];
+ if (reason === "authority_fenced") return sourceReasonLabels.authority_fenced;
+ if (reason === "generation_mismatch")
+ return "Source generation changed. Refresh its native authority binding.";
+ return "Source authority is unavailable. Restore the source connection and refresh native setup.";
+}
+
+function summarize(
+ status: WorkstreamSetupStatus,
+ native: boolean,
+ issue: string | null,
+): { tone: SetupStatusTone; badge: string; sentence: string } {
+ if (status.registry.state === "not_configured")
+ return {
+ tone: "warning",
+ badge: "Not configured",
+ sentence: native
+ ? "Configure native Workstreams on this server."
+ : "Configure Workstreams activation on this server.",
+ };
+ if (status.registry.state === "unavailable")
+ return {
+ tone: "error",
+ badge: "Offline",
+ sentence: "Registry unreachable. Restore its connection, then refresh.",
+ };
+ if (issue) return { tone: "warning", badge: "Needs attention", sentence: issue };
+ if (status.activation.missing_keys.length > 0)
+ return {
+ tone: "warning",
+ badge: "Needs attention",
+ sentence: "Some activation settings are missing.",
+ };
+ return {
+ tone: "success",
+ badge: "Ready",
+ sentence: status.registry.can_write
+ ? "Registry connected with write access."
+ : "Registry connected, read-only.",
+ };
+}
+
+const guideLabel = (code: string, native: boolean) =>
+ code === "configure_activation"
+ ? native
+ ? "Native setup guide"
+ : "Activation guide"
+ : code === "enroll_native_source"
+ ? "Native enrollment guide"
+ : "Verification guide";
+
+/** Read-only setup diagnosis: badge and top issue first; codes, guides and timestamps in Details. */
export function SetupStatusContent({
status,
capability,
@@ -25,68 +104,94 @@ export function SetupStatusContent({
}) {
const native = isNativeWorkstreams(capability);
const label = nativeWorkstreamsLabel(capability);
+ const issue = sourceIssue(status, native);
+ const summary = summarize(status, native, issue);
+ const checked = useRelativeTimeState(status.observed_at);
+ const checkedLabel =
+ checked.status === "relative"
+ ? `Checked ${checked.suffix ? `${checked.value} ${checked.suffix}` : checked.value}`
+ : null;
+ const guides = status.next_steps.filter(
+ (step) => !native || step.code !== "enroll_native_source",
+ );
return (
-
- {label ?
{label}
: null}
+
+
+ {summary.sentence}
+
- {status.registry.state === "not_configured"
- ? native
- ? "Not configured: configure native Workstreams on this server."
- : "Not configured: configure Workstreams activation on this server."
- : status.registry.state === "unavailable"
- ? "Offline. restore the registry connection, then refresh."
- : `Registry ready · ${status.registry.can_write ? "Write access" : "Read-only"}`}
-
- {status.activation.missing_keys.length > 0 ? (
-
Missing configuration: {status.activation.missing_keys.join(", ")}
- ) : null}
-
- {status.t3_source.state === "ready"
- ? "Native source ready"
- : `${status.t3_source.reason}: ${
- native
- ? status.t3_source.reason === "authority_fenced"
- ? "Source authority is fenced. Resolve the fence before retrying."
- : status.t3_source.reason === "generation_mismatch"
- ? "Source generation changed. Refresh its native authority binding."
- : "Source authority is unavailable. Restore the source connection and refresh native setup."
- : sourceReasonLabels[status.t3_source.reason]
- }`}
-
-
- Placements: {status.placements.readiness} · Open changes: {status.operations.open}
-
-
- {native ? (
-
- Native setup uses this environment’s authority. No control-plane source enrollment is
- required.
-
- ) : null}
-
- Read-only diagnosis. Last checked {status.observed_at}.
+ Placements {status.placements.readiness} · {status.operations.open} open{" "}
+ {status.operations.open === 1 ? "change" : "changes"}
+ {checkedLabel ? (
+ <>
+ {" · "}
+ {checkedLabel}
+ >
+ ) : null}
+
+
+ Details
+
+
+
+ {label ? (
+ <>
+ Mode
+ {label}
+ >
+ ) : null}
+ Registry
+
+ {status.registry.state.replace("_", " ")}
+ {status.registry.state === "ready"
+ ? ` · ${status.registry.can_write ? "write access" : "read-only"}`
+ : null}
+
+ Source
+
+ {issue ? (
+ <>
+ {issue} {status.t3_source.reason}
+ >
+ ) : (
+ "Native source ready"
+ )}
+
+ {status.activation.missing_keys.length > 0 ? (
+ <>
+ Missing
+ {status.activation.missing_keys.join(", ")}
+ >
+ ) : null}
+ {native ? (
+ <>
+ Authority
+ This environment’s own; no source enrollment needed.
+ >
+ ) : null}
+ {guides.length > 0 ? (
+ <>
+ Guides
+
+ {guides.map((step) => (
+
+ {guideLabel(step.code, native)}
+
+ ))}
+
+ >
+ ) : null}
+ Checked
+ {status.observed_at} · read-only diagnosis
+
+
);
}
diff --git a/apps/web/src/jones/workstreams/WorkstreamsSetupStatus.tsx b/apps/web/src/jones/workstreams/WorkstreamsSetupStatus.tsx
index 8201b79eed..f5bb2c2f4a 100644
--- a/apps/web/src/jones/workstreams/WorkstreamsSetupStatus.tsx
+++ b/apps/web/src/jones/workstreams/WorkstreamsSetupStatus.tsx
@@ -6,11 +6,13 @@ import { supportsWorkstreamOperations } from "@t3tools/client-runtime/jones/work
import { primaryEnvironmentIdAtom } from "../../state/primaryEnvironment";
import { environmentSession } from "../../state/session";
import { Atom } from "effect/reactivity";
-import { SettingsPageContainer, SettingsSection } from "../../components/settings/settingsLayout";
+import { SettingsRow, SettingsSection } from "../../components/settings/settingsLayout";
import { Button } from "../../components/ui/button";
+import { RefreshIcon } from "../../components/ui/refresh-icon";
+import { Tooltip, TooltipPopup, TooltipTrigger } from "../../components/ui/tooltip";
import { environmentOperationsTransport } from "./environmentWorkstreamsRequest";
-import { SetupStatusContent } from "./SetupStatusContent";
+import { SetupStatusContent, SetupStatusLine } from "./SetupStatusContent";
import {
nativeWorkstreamsCapability,
type NativeWorkstreamsCapability,
@@ -24,7 +26,7 @@ function SetupStatusRequest({
capability: NativeWorkstreamsCapability | null;
}) {
const [status, setStatus] = useState
(null);
- const [message, setMessage] = useState(null);
+ const [failed, setFailed] = useState(false);
// oxlint-disable-next-line t3code/react-effect-budget -- Read setup capability for the selected environment and abort when its identity changes.
useEffect(() => {
if (!environmentId) return;
@@ -35,17 +37,40 @@ function SetupStatusRequest({
if (!abort.signal.aborted) setStatus(next);
})
.catch(() => {
- if (!abort.signal.aborted)
- setMessage("Offline or unavailable. Check this environment's connection, then refresh.");
+ if (!abort.signal.aborted) setFailed(true);
});
return () => abort.abort();
}, [environmentId]);
- return status ? (
-
+ if (status) return ;
+ return failed ? (
+
+ Couldn’t reach this environment. Check its connection, then refresh.
+
) : (
- {message ?? "Checking Workstreams setup…"}
+ Checking setup…
);
}
+
+function RefreshSetupButton({ onClick }: { onClick: () => void }) {
+ return (
+
+
+
+
+ }
+ />
+ Refresh setup status
+
+ );
+}
+
export function WorkstreamsSetupStatus() {
const environmentId = useAtomValue(primaryEnvironmentIdAtom);
const config = useAtomValue(
@@ -54,28 +79,25 @@ export function WorkstreamsSetupStatus() {
const supported = supportsWorkstreamOperations(config?.environment.capabilities);
const [revision, setRevision] = useState(0);
return (
-
-
- {!supported ? (
-
- Setup diagnosis requires a server with Workstream operations support. Legacy Workstreams
- remain available.
-
- ) : (
- <>
+
+ {!supported ? (
+
+ ) : (
+
-
- setRevision((value) => value + 1)}>
- Refresh setup status
-
-
- >
- )}
-
-
+ }
+ control={ setRevision((value) => value + 1)} />}
+ />
+ )}
+
);
}
diff --git a/apps/web/src/routes/_chat.settings.jones-code.tsx b/apps/web/src/routes/_chat.settings.jones-code.tsx
index d800764ac2..c16925aff8 100644
--- a/apps/web/src/routes/_chat.settings.jones-code.tsx
+++ b/apps/web/src/routes/_chat.settings.jones-code.tsx
@@ -1,14 +1,15 @@
import { createFileRoute } from "@tanstack/react-router";
+import { SettingsPageContainer } from "../components/settings/settingsLayout";
import { PromptsSettings } from "../jones/workQueue/PromptsSettings";
import { WorkstreamsSetupStatus } from "../jones/workstreams/WorkstreamsSetupStatus";
function JonesCodeSettings() {
return (
-
+
);
}