CluPilotCloud/app/Console/Commands
nexxo a43be4186a Say what the sweep did not check, before telling anyone to archive
stripe:sweep-orphan-prices described itself as listing "what AdoptStripePrice
will not take over". It computes nothing of the sort: it lists every active
Price at one of our Products that no register row knows, adoptable or not — the
only question it asks is whether a row holds the id. And then it invited the
destructive path, with no word anywhere about running stripe:sync-catalogue
first.

Following that advice can make a plan unsellable. Archive an orphan the next
sync would have adopted and the sync can no longer see it: activePricesFor()
asks Stripe for active prices only, so adoption returns null and createPrice()
fires under the same key the crashed run used. For twenty-four hours Stripe
answers a repeated key by replaying the stored response — the archived Price's
id, in the body it had while it was live. The register writes it down as live,
and the checkout is refused. This is the same class of defect the branch exists
against: a comment that was eloquent and wrong.

- The docblock now says what the command lists, names the twenty-four-hour
  replay so nobody has to rediscover it, and the ordering requirement is in
  $description and printed on every path that found something, --archive
  included. The reassurance that archiving is harmless is now scoped to what
  THIS codebase sells; a payment link an operator built by hand against one of
  our Products is withdrawn all the same, and says so.
- stripe:sync-catalogue --dry-run says "created or adopted" again. A dry run
  counts intents without calling ensure(), so it cannot know which Stripe
  already holds — and a dry run is the first thing an operator runs after an
  interrupted one. The live line keeps both figures, where they are knowable.
- The sweep's plan side gets a test: an orphan at a family Product is reported
  and a known plan Price never is. Deleting either half of the lookup turns it
  red; the five tests it joins all stayed green for the products() half.
- The throttle test pinned "once" and neither "per price" nor "per day". A key
  mutated to a constant would have silenced every orphan after the first and
  kept it green — the silent no-op the branch is built against. It now plants a
  second orphan, and travels a day to hear the first one again.
- Two of my own documents corrected. The spec sentence claiming an adoptable
  orphan never appears in this list is what the implementation faithfully built;
  it holds only if a sync has run since the orphan appeared, which nothing
  enforced. And the two adoption classes do not carry the same run log:
  $adoptions on the Price side, $duplicates on the Product side.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 19:12:57 +02:00
..
ApplyDuePlanChanges.php Book a downgrade for a date that cannot move, and sell a module once 2026-07-29 17:51:37 +02:00
ApplyStorageQuotas.php Fix nine defects in the provisioning pipelines 2026-07-30 01:34:55 +02:00
ArchiveUnexportedInvoices.php Let the export have as many destinations as somebody wants 2026-07-29 02:43:15 +02:00
AutoUpdate.php Update on a schedule if the owner wants one, and say where incidents go 2026-07-29 15:01:54 +02:00
CheckDnsZone.php Put customer instances on their own domain, and say which domains are ours 2026-07-29 16:03:13 +02:00
CheckPlanCatalogue.php feat(billing): the plan catalogue becomes three tables, and config stops selling 2026-07-26 12:05:56 +02:00
ConsoleAccess.php Make the private hostnames look like nothing is there, and close the way past the proxy 2026-07-27 11:26:48 +02:00
CreateAdmin.php Check users directly for the reverse operator-identity collision, not just customers 2026-07-28 15:36:29 +02:00
EndCancelledAddons.php Bill a booked module every month, and put it on the invoice 2026-07-29 20:22:02 +02:00
EndDueServices.php Restart a machine, enforce the quota that was sold, end a route that ended 2026-07-29 18:28:28 +02:00
FetchInboundMail.php Read the support mailbox into the console 2026-07-29 21:41:29 +02:00
PruneDormantAccounts.php Accept terms instead of a start date, and fix the sender no server accepts 2026-07-30 15:22:35 +02:00
PruneExportFolders.php Let a destination say how it is laid out and how long anything stays 2026-07-29 03:08:27 +02:00
PruneHostDns.php Move host DNS names off the public zone into vpn-dns's internal hostsdir 2026-07-28 23:44:31 +02:00
PruneUnverifiedAccounts.php Discard an unconfirmed registration, and sweep the abandoned ones 2026-07-30 14:08:02 +02:00
RepriceStripeSubscriptions.php Stop charging VAT to the customers who owe us none 2026-07-30 02:15:41 +02:00
SampleServiceStatus.php Rebuild the status page as a status page 2026-07-29 12:45:18 +02:00
SweepOrphanStripePrices.php Say what the sweep did not check, before telling anyone to archive 2026-07-30 19:12:57 +02:00
SyncStripeCatalogue.php Say what the sweep did not check, before telling anyone to archive 2026-07-30 19:12:57 +02:00
SyncStripeSubscriptions.php Bill a booked module every month, and put it on the invoice 2026-07-29 20:22:02 +02:00
VerifyCustomDomains.php Serve the custom domain, not just announce it 2026-07-29 16:44:52 +02:00
VerifyVatIds.php Stop charging for a service that has ended 2026-07-30 00:52:19 +02:00