mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 14:40:52 +00:00
fix(onboarding): the AI panel claims an outage only when one was seen
The done screen folded the `unknown` provider verdict into `unhealthy` and painted "This instance's provider is not answering right now." `unknown` is an empty `provider_health` table, which is the state of every instance until its first AI call — so the most common path through the screen made a claim about something nobody had measured. It now falls to the neutral panel, which says nothing either way; the offer is withheld in both cases as before. The projection itself drew the same unearned conclusion from staleness alone: a quiet instance whose last success was two days old read `unhealthy` without any failure behind it. A verdict now needs an observed one — a row whose last outcome was a failure — and a merely old success reads `unknown`.
This commit is contained in:
1 parent
1885b7867d
commit
65a62350ea
6 files changed
+92
-41
No files matched your search
@@ -186,24 +186,41 @@ describe("<DoneScreen> shared-provider offer", () => {
|
||||
expect(html).not.toContain('data-slot="onboarding-ai-unavailable"');
|
||||
});
|
||||
|
||||
it.each(["unhealthy", "unknown"] as const)(
|
||||
"says the provider is not answering on %s, and offers no button",
|
||||
(health) => {
|
||||
aiProviderState.data = {
|
||||
managedBy: "server",
|
||||
serverProviderHealth: health,
|
||||
serverProviderOffer: false,
|
||||
serverProviderConsent: false,
|
||||
};
|
||||
const html = render();
|
||||
expect(html).toContain('data-slot="onboarding-ai-unavailable"');
|
||||
expect(html).not.toContain('data-slot="onboarding-ai-offer-grant"');
|
||||
expect(html).not.toContain('data-slot="onboarding-ai-shared-key"');
|
||||
// The rest of the panel is untouched in every branch.
|
||||
expect(html).toContain('data-slot="onboarding-ai-keyless"');
|
||||
expect(html).toContain('href="/settings/ai"');
|
||||
},
|
||||
);
|
||||
it("says the provider is not answering only when a failure earned it", () => {
|
||||
aiProviderState.data = {
|
||||
managedBy: "server",
|
||||
serverProviderHealth: "unhealthy",
|
||||
serverProviderOffer: false,
|
||||
serverProviderConsent: false,
|
||||
};
|
||||
const html = render();
|
||||
expect(html).toContain('data-slot="onboarding-ai-unavailable"');
|
||||
expect(html).not.toContain('data-slot="onboarding-ai-offer-grant"');
|
||||
expect(html).not.toContain('data-slot="onboarding-ai-shared-key"');
|
||||
// The rest of the panel is untouched in every branch.
|
||||
expect(html).toContain('data-slot="onboarding-ai-keyless"');
|
||||
expect(html).toContain('href="/settings/ai"');
|
||||
});
|
||||
|
||||
it("claims nothing either way when nobody has measured the provider", () => {
|
||||
// `unknown` is an empty `provider_health` table — the state of every
|
||||
// instance until its first AI call, and the most common way through this
|
||||
// screen. Saying "not answering" there is a claim about something nobody
|
||||
// measured, which is the one thing this panel exists not to do.
|
||||
aiProviderState.data = {
|
||||
managedBy: "server",
|
||||
serverProviderHealth: "unknown",
|
||||
serverProviderOffer: false,
|
||||
serverProviderConsent: false,
|
||||
};
|
||||
const html = render();
|
||||
expect(html).not.toContain('data-slot="onboarding-ai-unavailable"');
|
||||
expect(html).not.toContain('data-slot="onboarding-ai-offer-grant"');
|
||||
expect(html).not.toContain('data-slot="onboarding-ai-shared-key"');
|
||||
// The neutral panel is the whole panel, minus any claim about a provider.
|
||||
expect(html).toContain('data-slot="onboarding-ai-keyless"');
|
||||
expect(html).toContain('href="/settings/ai"');
|
||||
});
|
||||
|
||||
it("keeps the shared-key note for the one state it was ever true of", () => {
|
||||
aiProviderState.data = {
|
||||
|
||||
@@ -35,8 +35,9 @@ interface AiProviderStatus {
|
||||
/** Origin of the provider that would serve this user, if any. */
|
||||
managedBy?: "user" | "local" | "server" | null;
|
||||
/**
|
||||
* v1.38.19 (wave D) — the instance-wide tri-state. Fail closed: `unknown`
|
||||
* is treated exactly like `unhealthy`, here and everywhere.
|
||||
* v1.38.19 (wave D) — the instance-wide tri-state. Fail closed for the
|
||||
* offer: neither `unhealthy` nor `unknown` earns one. Only `unhealthy` is
|
||||
* a verdict this screen may state out loud.
|
||||
*/
|
||||
serverProviderHealth?: "healthy" | "unhealthy" | "unknown";
|
||||
/** Whether the shared provider may honestly be offered in one tap. */
|
||||
@@ -174,17 +175,21 @@ export function DoneScreen({ state }: { state: OnboardingStateDto }) {
|
||||
operatorProvides &&
|
||||
!consentOnFile &&
|
||||
aiProvider?.serverProviderOffer === true;
|
||||
// Only a health verdict paints the "not answering" card. When the offer is
|
||||
// refused for a reason that is not health — the operator switched the
|
||||
// assistant surfaces off, or this is a managed profile whose provider is
|
||||
// the operator's by definition — the panel falls back to its neutral form
|
||||
// and claims nothing either way.
|
||||
// Only a health verdict somebody EARNED paints the "not answering" card —
|
||||
// `unhealthy`, which means the provider was tried and the last thing that
|
||||
// happened was a failure. `unknown` is not that verdict: it is an empty
|
||||
// ledger, which is the state of every instance until its first AI call and
|
||||
// therefore the most common way through this screen. It falls to the
|
||||
// neutral panel, as does every other non-health reason the offer is
|
||||
// withheld (the assistant surfaces switched off, a managed profile whose
|
||||
// provider is the operator's by definition). The neutral panel claims
|
||||
// nothing either way, which is the only honest thing to say about a state
|
||||
// nobody measured.
|
||||
const sharedUnavailable =
|
||||
operatorProvides &&
|
||||
!consentOnFile &&
|
||||
!offerShared &&
|
||||
(aiProvider?.serverProviderHealth === "unhealthy" ||
|
||||
aiProvider?.serverProviderHealth === "unknown");
|
||||
aiProvider?.serverProviderHealth === "unhealthy";
|
||||
|
||||
return (
|
||||
<section
|
||||
|
||||
Reference in new issue
Block a user