mikebridge commented on PR #41550:
URL: https://github.com/apache/superset/pull/41550#issuecomment-5142201629
## Capstone review — the complete soft-delete feature, with resolutions
Ahead of human review, a **10-lens capstone panel** was run over the
*entire* deletion feature as one system — the merged halves (soft-delete core,
restore, the #41549 retention engine) together with this PR — at `63419d4268`.
Lenses: clean-code, continuous-delivery, css, domain-driven-design, preset,
python, react, sqlalchemy, superset-committer, tidy-first. Seven of ten
verified claims empirically (test runs, migration round-trips, live probes).
The scope was deliberately cross-seam: this feature's worst historical bugs
lived in the composition between PRs, not in any single file.
**106 raw findings → ~70 after dedup: 1 BLOCKER, 2 HIGH, ~19 MEDIUM,
remainder LOW.** No lens found a blocker in the backend core — the locked-claim
purge design, write-ahead audit, and migration set were repeatedly verified
sound. The findings concentrated in the seams: frontend↔backend clock
conventions, merged↔unmerged caller assumptions, dialect↔dialect constraint
divergence, and copy↔behaviour honesty.
⚑N = found independently by N lenses. **Every "fixed" entry below carries a
verified control**: the regression test was confirmed to *fail* against the
pre-fix source and pass with the fix — a discipline adopted after earlier
rounds produced tests that passed either way.
### BLOCKER
| Finding | Resolution |
|---|---|
| **`allowHtml` silently dropped by `addToast`** — the restore toast's "View
chart →" link rendered as dead text in production; every test was green because
every test injected the flag downstream of the drop (react; verified
line-by-line) | **Fixed** `6ddca1349a` — flag carried through the payload + an
integration-shaped test through the real action creator. This also vindicates
an earlier external bug report of the dead link that a previous investigation
on this branch had wrongly refuted (it tested Interweave's attribute handling,
one layer below the drop). |
### HIGH
| Finding | Resolution |
|---|---|
| **⚑5 Timezone seam**: `deleted_at` is stamped naive-server-local
(deliberately, matching every audit column); the archive UI parsed it as UTC
and filtered with client-UTC cutoffs — ages and time-range windows shifted by
the server offset on any non-UTC deployment, invisible to UTC-based CI (CD,
clean-code, preset, ddd, committer) | **Fixed** `ec97bd4e04` — display is
server-humanized (`deleted_at_delta_humanized`, same pattern as
`changed_on_delta_humanized`); presets send day counts through new per-model
`<type>_deleted_recency` filters resolved with the server's clock. Also retires
two related LOWs: cutoffs frozen at page-mount, and absolute timestamps
persisted into `?filters=` URLs. |
| **PR process gate**: stale draft-tracker body, no template sections,
superseded title (committer, preset) | **Fixed** — body rewritten to the
template, retitled. Screenshots to be attached by the author. |
### MEDIUM — fixed on this branch
| Finding | Resolution |
|---|---|
| **Bulk "Archive" hard-deleted semantic views** behind "you can recover
them there" copy, friction removed, toast counting them as archived (react;
endpoint verified to have no soft-delete) | **Fixed** `20cde5a05d` — mixed
selections keep the full danger treatment (type-DELETE gate) and say plainly
which items die permanently; the toast counts the two fates separately. |
| **⚑2 Welcome-dashboard purge blocker was FK-dependent**: on Postgres/MySQL
an IntegrityError misreported as a policy block and retried forever; on SQLite
(FKs off) the dashboard purged *successfully*, stranding a broken homepage
pointer while the audit said `confirmed` (tidy-first, preset) | **Fixed**
`794dbda1d8` — explicit guard in `_validate_deletion_allowed` beside the
ReportSchedule check; dialect-independent, with a nameable reason. Control:
with the guard stripped, the SQLite test shows the dashboard genuinely
destroyed. |
| **⚑2 IntegrityError conflation**: raw driver text (SQL + bind parameters)
reached the client toast as the 422 reason, and a genuine cascade-coverage bug
was indistinguishable from an intentional block at INFO (preset, python) |
**Fixed** `794dbda1d8` — curated client message ("blocked by database
references"); constraint detail to the log at WARNING where a coverage gap is
diagnosable. |
| **Fail-open audience default**: `_scope_to_restore_audience` returned the
query unchanged for models without an `editors` relationship — latent, but the
next `SoftDeleteMixin` adopter would expose its archive to anyone with list
access (python) | **Fixed** `c91e08f5c7` — non-admins fail closed. The
docstring also stops overclaiming "mirrors `raise_for_editorship`":
`EXTRA_EDITORS_RESOLVER` and guest-role editorship have no SQL form, so the
enumeration is documented as narrower than the true audience, never wider. |
| **Unaudited REST purge**: the command's fail-open on audit failure was
licensed by "an operator is present at a shell", but this PR routes
`@protect()`-ed REST users through it without checking `record_id` (ddd) |
**Fixed** `c91e08f5c7` — `require_audit` flag, set by the REST caller: an
unwritable audit answers 422 with the entity untouched. The CLI keeps its
operator-trust fail-open. |
| **Chart filter duplicated the restore-audience rule** — a second,
semantically identical editors-EXISTS stacked on the base's; a future audience
change would land in one and charts would enforce the intersection (ddd) |
**Fixed** `c91e08f5c7` — override deleted; the existing editor-visibility tests
pass on the base rule alone, which is the proof the copy added nothing. |
| **sd_acl test fixtures leaked past a failed setup** — observed
empirically: one leaked slice made 28 unrelated chart tests fail (python;
introduced by this branch's own visibility tests) | **Fixed** `a3441c77c9` —
creation moved inside try/finally with None-seeded cleanup, plus a permanent
probe test that injects a failure at the exact old boundary and asserts nothing
survives. |
| **⚑4 Visibility-filter docstring contradicted its own gate** — claimed
relationship loads are skipped while the predicate 20 lines up deliberately
includes them; a "fix" toward the wrong docstring would reintroduce a lazy-load
leak (clean-code, preset, ddd, tidy-first) | **Fixed** `4eca699bd1` — the
listener's docstring now defers to the predicate. |
| **⚑2 Translation catalogue not regenerated** — none of the feature's ~30
frontend strings were in `messages.pot`; the archive surface shipped
untranslatable (committer, confirmed by grep) | **Fixed** `8c6dfca5f1` — real
`babel_update.sh` run; all strings extracted into the pot and every language
catalogue. Noted in the commit: `lazy_gettext` FAB filter names are outside the
pipeline's keyword list (long-standing, applies to pre-existing siblings too),
and the script's `msgcat` step silently no-ops on GNU gettext. |
| **No UPDATING.md entry** for the purge API and modal changes; the datasets
entry's "until a purge capability lands" now answerable (committer) | **Fixed**
`e3cb4e18ca` — full entry incl. the status contract, the semantic-view
carve-out, the database-deletion resolution pointer, and `purge_audit_log`'s
never-pruned-by-design posture. |
### MEDIUM — deliberate follow-ups (not blocking this PR)
- **⚑4 Audit growth / blocked-entity churn** (python, sqlalchemy, CD,
preset): blocked entities re-attempted every run, one immutable audit row per
day forever, no backoff, no audit-retention story. Wants a
skip-if-latest-blocked rule + a retention policy.
- **⚑3 Slug-reuse 500 on MariaDB / MySQL <8.0.13 / SQLite** (ddd,
sqlalchemy, committer): dialects keeping the full unique constraint let
validation (which sees only active rows) pass and die at flush.
sqlalchemy-review's sharpening: MariaDB is a supported production metadata DB,
so this deserves a visibility-bypassed pre-check → clean 422.
- **Missing beat-schedule startup warning** (CD): custom `CELERY_CONFIG`
deployments silently never purge; the sibling versioning task already ships the
exact warning pattern to generalize.
- **⚑2 Dry-run honesty** (CD, ddd): docs and the confirm-modal quote a
retention deadline that `SOFT_DELETE_PURGE_DRY_RUN=True` (the shipped default)
never enforces.
- **Flag ON→OFF slug-twin hazard** (CD): a new dashboard claims an archived
slug; flag-off resurrects the twin — two visible dashboards, one slug.
- **Audit-orchestration duplication** (clean-code): the ~60-line write-ahead
protocol is copied between the scheduled and force purge paths; extracting it
is also where `require_audit` naturally lands.
- **css accessibility cluster**: `:focus-within` reveal wins only by emotion
injection order; the keyboard-invisibility fix should move into TableCollection
(one line fixes every list page); sub-24px touch targets beside a no-confirm
Recover; hover-only reveal leaves this page's only affordances invisible on
touch devices.
### LOW (grouped; tracked as follow-ups)
**⚑6 bypass-release family** (session-scoped visibility bypass released only
on the happy path; no refcounting against the context-manager owner) · **⚑3
reconcile-semantics family** (>1h in-flight purge finalized `failed`
permanently; attribution edges) · **⚑3 favstar rows survive purge** (with a
SQLite recycled-rowid twist) · **orphan-sweep candidate asymmetry**
(`end_transaction_id` never considered for sweeping — a leak, not a correctness
break) · legacy pre-subjects join tables cleaned only by DB cascade · **⚑3
softDelete copy triplication** across the three list pages + `'%s item(s)'`
pluralization · window-default duplication · deprecated `utcnow()` in test
helpers · "Archived by" is `changed_by` (background maintenance can overwrite
it) · chart "Name" search is all-text · menu/header casing drift · assorted
per-lens LOWs.
### Validation of prior rounds
The lenses also re-covered ground fixed in the two earlier review rounds
posted above — the rison type-switch crash, the discarded 422 reason, the
cross-model UUID confusion, the restore race (whose identity+eligibility
predicate design was repeatedly *praised* rather than re-flagged), the CLI
`--type` handling — and none re-found them broken.
### Method notes, for calibration
- css and ddd could not load their skill definitions in subagents and
reviewed directly with the named discipline; python and sqlalchemy loaded
theirs manually. The remaining six ran as their named skills.
- The empirical runs were done against clean, freshly-migrated databases
after one lens traced local flakiness to a stale shared fixture DB (and another
traced *that* to the sd_acl leak fixed above).
- Backend suites at head: retention + soft-delete integration and unit
suites green; frontend: ArchivedList/DatasetList/MessageToasts suites green.
_This review and the fixes were generated by Claude (AI) on behalf of
@mikebridge._
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]