fix(usage): let an operator clear a participation the collector never accepted
Probing the live collector to settle the delete-sequence question turned up something else: usage.picpeak.app answers a valid usage.v2 registration with INVALID_PACKET while the identical v1 flow is accepted. It does not speak v2 yet — which the deployment notes already require, but the consequence of getting that order wrong was worse than "reports do not send". Opting in to v2 against a v1-only collector left the installation stuck. Registration was refused, so nothing existed at the collector at all; the row sat in activation_pending, disable moved it to deletion_pending, retry was futile forever, and enable refused because the row was not `disabled`. The abandon hatch added earlier did not apply: it was gated on SIGNING_KEY_UNREADABLE. So the most harmless possible failure — nothing registered anywhere — was the one an operator could not clear. The gate is now the property that actually matters: a participation the collector has provably never accepted (sequence 0, no receipt) with a failing delivery can be discarded, from activation_pending as well as deletion_pending. Its receipt records `never-registered` rather than an unconfirmed deletion, because nothing remote exists to be unsure about. A participation the collector *did* accept keeps the old narrow gate and its explicit warning — clearing local state while the collector still holds reports must stay a deliberate, warned-about act. A collector that rejects a registration or a deletion outright now reports SCHEMA_NOT_ACCEPTED instead of DELIVERY_FAILED, and the settings page says the collector does not accept this report version yet. Retrying cannot fix that, and sending the operator to look for a network fault they do not have was wrong. Verified end to end against the live collector: v2 opt-in reports SCHEMA_NOT_ACCEPTED, the exit is offered immediately, the receipt says never-registered, and joining again on v1 registers, reports and withdraws with a collector-confirmed deletion.
This commit is contained in:
@@ -278,7 +278,9 @@ export default function ProductUsageTab() {
|
||||
{t(
|
||||
data.last_error === 'SIGNING_KEY_UNREADABLE'
|
||||
? 'productUsage.signingKeyUnreadable'
|
||||
: 'productUsage.deliveryProblem'
|
||||
: data.last_error === 'SCHEMA_NOT_ACCEPTED'
|
||||
? 'productUsage.schemaNotAccepted'
|
||||
: 'productUsage.deliveryProblem'
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
@@ -296,7 +298,13 @@ export default function ProductUsageTab() {
|
||||
// The one dead end the operator cannot retry out of. Offered only
|
||||
// here, and worded so nobody mistakes it for a confirmed deletion.
|
||||
<div className="rounded border border-amber-300 dark:border-amber-700 p-3 space-y-2">
|
||||
<p>{t('productUsage.abandonExplanation')}</p>
|
||||
<p>
|
||||
{t(
|
||||
data.abandon_never_registered
|
||||
? 'productUsage.abandonExplanationUnregistered'
|
||||
: 'productUsage.abandonExplanation'
|
||||
)}
|
||||
</p>
|
||||
<Button
|
||||
variant="outline"
|
||||
className={WRAPPING_BUTTON}
|
||||
@@ -305,7 +313,11 @@ export default function ProductUsageTab() {
|
||||
if (
|
||||
await confirm({
|
||||
title: t('productUsage.abandon'),
|
||||
message: t('productUsage.abandonConfirm'),
|
||||
message: t(
|
||||
data.abandon_never_registered
|
||||
? 'productUsage.abandonConfirmUnregistered'
|
||||
: 'productUsage.abandonConfirm'
|
||||
),
|
||||
confirmLabel: t('productUsage.abandon'),
|
||||
variant: 'danger'
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user