feat(jobs): jobs describe themselves — description, mutates, repair_description
The admin panel had no repair toggle wired to anything but a hardcoded
name list naming the two refcount tenants, so `thumb_derived_import` and
`thumb_attached_import` could not be run in repair mode from the UI at
all despite supporting it. And nothing in the job list said what any
given job does or whether clicking Run on production writes anything.
Three defaulted methods on `JobHandler` and `RecoverableJobHandler`:
fn description(&self) -> &'static str
fn mutates(&self) -> Mutates // Never | Always | OnRepairOnly
fn repair_description(&self) -> Option<&'static str>
`RecoverableAdapter` forwards them — the registry only holds
`dyn JobHandler`, so a tenant's metadata is invisible otherwise, and
falling back to the defaults would report every recoverable job as
read-only, including the ones that delete files.
Three values rather than a boolean because a job can be read-only by
default and destructive under `?repair=true`; a boolean answers wrongly
for one of its two modes, and `false` on something that unlinks files is
the dangerous direction to be wrong in. `repair_description` returning
`Option` collapses "does it repair" and "what does repair do" into one
method: presence gates the toggle, content is the confirmation text —
which the frontend cannot invent, since correcting a counter and
deleting sidecars are not the same warning.
`OnRepairOnly` with no `repair_description` is rejected at registration:
it claims to mutate only under a flag it does not support.
All 17 registered jobs declare all three. The panel now renders the
description under each name, badges read-only jobs, confirms before a
plain run of a mutating one, and offers the repair variant off the
backend flag instead of the name list.
Descriptions are English in the trait, next to the behaviour: one in
`locales/*.json` rots invisibly the moment a job changes, and a
translator cannot know what `manifests_consistency` reconciles. i18n can
layer on later keyed by job name with these as the fallback.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -113,6 +113,40 @@ jsonpath "$[*].name" contains "consistency_batch"
|
||||
jsonpath "$[*].name" contains "backend_migration"
|
||||
jsonpath "$[*].name" contains "backend_rotate"
|
||||
|
||||
# Job metadata — `description` / `mutates` / `repair_description`.
|
||||
# The admin panel keys the read-only badge and the repair toggle off
|
||||
# these, so a handler that stops declaring them degrades the UI
|
||||
# silently: a mutating job renders as safe to click, and a repair-
|
||||
# capable one loses its toggle entirely. That second failure is the
|
||||
# bug this replaced — a name-based allowlist in the panel that never
|
||||
# grew past the two refcount tenants, leaving the thumbnail imports
|
||||
# unrunnable in repair mode from the UI.
|
||||
#
|
||||
# Per-job pins use scalar equality on a single-match filter — NOT
|
||||
# `count`, which trips Hurl's "filter matched one item → scalar, not
|
||||
# list" quirk. See memory `hurl-jsonpath-filter-empty-result`.
|
||||
#
|
||||
# `mutates` is a closed enum the UI switches on, so all three wire
|
||||
# spellings are pinned; a rename would break the panel silently.
|
||||
jsonpath "$..mutates" contains "never"
|
||||
jsonpath "$..mutates" contains "always"
|
||||
jsonpath "$..mutates" contains "on_repair_only"
|
||||
# Read-only tenant — safe to trigger, earns the read-only badge.
|
||||
jsonpath "$[?(@.name=='files_consistency')].mutates" == "never"
|
||||
# Repairs refcounts under ?repair=true, read-only otherwise.
|
||||
jsonpath "$[?(@.name=='blobs_consistency')].mutates" == "on_repair_only"
|
||||
# Destructive on a plain run AND repair-capable — the combination a
|
||||
# boolean could not express, and the reason `Mutates` has three values
|
||||
# rather than two.
|
||||
jsonpath "$[?(@.name=='thumb_derived_import')].mutates" == "always"
|
||||
# Floors, not totals: every job registered today declares a
|
||||
# description, and five declare a repair arm (both imports, both
|
||||
# refcount tenants, consistency_batch). New tenants only push these
|
||||
# up. Registration itself already rejects `on_repair_only` without a
|
||||
# repair_description, so the contradictory pairing can't reach here.
|
||||
jsonpath "$..description" count >= 10
|
||||
jsonpath "$..repair_description" count >= 5
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 3 — Trigger `trash_cleanup`. Envelope shape:
|
||||
|
||||
Reference in New Issue
Block a user