diff --git a/alembic/versions/030_drop_legacy_fundamentals_tombstones.py b/alembic/versions/030_drop_legacy_fundamentals_tombstones.py
new file mode 100644
index 0000000..10013e9
--- /dev/null
+++ b/alembic/versions/030_drop_legacy_fundamentals_tombstones.py
@@ -0,0 +1,71 @@
+"""Drop the A6 rollback tombstones
+
+Revision ID: 030
+Revises: 029
+Create Date: 2026-08-07 00:00:00.000000
+
+Migration ``029`` kept two SystemSetting rows alive as rollback tombstones,
+pinned to the values a pre-A6 process needed to behave safely. A6 is deployed
+and healthy, and the provider keys are gone from the production ``.env`` — which
+makes the legacy collector inert regardless of any settings row — so the
+tombstones have no remaining job.
+
+Nothing in the current codebase reads either key.
+"""
+from typing import Sequence, Union
+
+from alembic import op
+import sqlalchemy as sa
+
+
+revision: str = "030"
+down_revision: Union[str, None] = "029"
+branch_labels: Union[str, Sequence[str], None] = None
+depends_on: Union[str, Sequence[str], None] = None
+
+
+# The safe values 029 pinned. Kept here so downgrade restores real protection
+# rather than leaving a rolled-back process reading absent rows permissively.
+_TOMBSTONES: dict[str, str] = {
+ "fundamental_data_sec_dolt_cutover_enabled": "true",
+ "job_fundamental_collector_enabled": "false",
+}
+
+_settings = sa.table(
+ "system_settings",
+ sa.column("id", sa.Integer),
+ sa.column("key", sa.String),
+ sa.column("value", sa.Text),
+ sa.column("updated_at", sa.DateTime(timezone=True)),
+)
+
+
+def upgrade() -> None:
+ conn = op.get_bind()
+ for key in _TOMBSTONES:
+ row = conn.execute(
+ sa.select(_settings.c.value).where(_settings.c.key == key)
+ ).fetchone()
+ if row is None:
+ print(f"a6_tombstone_drop {key}: absent", flush=True)
+ continue
+ print(f"a6_tombstone_drop {key}: {row[0]!r}", flush=True)
+ conn.execute(sa.delete(_settings).where(_settings.c.key == key))
+
+
+def downgrade() -> None:
+ """Restore the tombstones at their safe values.
+
+ Unlike 029's no-op downgrade, this one is meaningful: going back past this
+ revision implies going back toward code that still reads these keys.
+ """
+ conn = op.get_bind()
+ now = sa.func.now()
+ for key, pinned in _TOMBSTONES.items():
+ exists = conn.execute(
+ sa.select(_settings.c.id).where(_settings.c.key == key)
+ ).fetchone()
+ if exists is None:
+ conn.execute(
+ sa.insert(_settings).values(key=key, value=pinned, updated_at=now)
+ )
diff --git a/docs/dolt-integration-plan.md b/docs/dolt-integration-plan.md
index 0fed5a6..6d8e859 100644
--- a/docs/dolt-integration-plan.md
+++ b/docs/dolt-integration-plan.md
@@ -432,9 +432,10 @@ workstream B — Alpaca remains the price source throughout.
remaining production action is flipping that switch on and observing it.
- A6. **DONE 2026-08-07.** FMP/Finnhub/Alpha Vantage removed, along with the
weekly `fundamental_collector` job, the A5 cutover toggle (SEC+Dolt is now the
- unconditional path) and the parity report. Migration `029` tombstones the two
- behavior-bearing settings rows for the rollback window; the archived parity
- bundles stay as the A5 evidence trail.
+ unconditional path) and the parity report. Migration `029` tombstoned the two
+ behavior-bearing settings rows for the rollback window and `030` dropped them
+ once the deploy was confirmed healthy; the archived parity bundles stay as the
+ A5 evidence trail.
**Workstream B (independent, start when wanted):**
@@ -514,8 +515,8 @@ observed in production, so the legacy providers, their config/env keys, the week
collector job and the parity report were all removed. Two consequences to carry:
(1) `fundamental_data` now has no provider fallback — recovery is restore-from-backup;
(2) disabling **SEC Fundamentals Import** stops the SEC fetch only, because the local
-cache refresh was deliberately moved outside the job-enable check. Remaining
-follow-up: delete the migration-029 tombstone rows once the rollback window closes.
+cache refresh was deliberately moved outside the job-enable check. No follow-ups
+remain: migration `030` dropped the tombstone rows after the deploy was verified.
**Known caveats to carry (documented in the findings report, not bugs to fix):**
- KLAC-class post-filing splits: P/E wrong until the next 10-Q; undetectable from
diff --git a/docs/fundamentals-deployment.md b/docs/fundamentals-deployment.md
index e20ae29..e64172c 100644
--- a/docs/fundamentals-deployment.md
+++ b/docs/fundamentals-deployment.md
@@ -180,21 +180,6 @@ WHERE dimension = 'fundamental'
GROUP BY dimension, is_stale;
```
-### Retired settings rows (delete later)
-
-Migration `029` pinned two SystemSetting rows as rollback tombstones rather than
-deleting them, because migrations run before the service restarts and a
-rolled-back pre-A6 process reads an absent `job_..._enabled` row as *enabled*:
-
-| key | pinned value |
-|---|---|
-| `fundamental_data_sec_dolt_cutover_enabled` | `true` |
-| `job_fundamental_collector_enabled` | `false` |
-
-Neither controls anything now; Admin -> Settings hides both. Once the A6
-rollback window has closed, delete the rows and the `MANAGED_SETTINGS` filter in
-`frontend/src/components/admin/SettingsForm.tsx`.
-
## Failure and rollback
- **There is no provider fallback any more, and no Admin switch that freezes the
diff --git a/frontend/src/components/admin/SettingsForm.tsx b/frontend/src/components/admin/SettingsForm.tsx
index a4d7165..5e4f49b 100644
--- a/frontend/src/components/admin/SettingsForm.tsx
+++ b/frontend/src/components/admin/SettingsForm.tsx
@@ -3,14 +3,6 @@ import { useSettings, useUpdateSetting } from '../../hooks/useAdmin';
import { SkeletonTable } from '../ui/Skeleton';
import type { SystemSetting } from '../../lib/types';
-// Retired keys kept in the database as rollback tombstones (migration 029).
-// They no longer control anything; hide them so nobody edits a dead switch.
-// Delete both the rows and this filter once the A6 rollback window has closed.
-const MANAGED_SETTINGS = new Set([
- 'fundamental_data_sec_dolt_cutover_enabled',
- 'job_fundamental_collector_enabled',
-]);
-
export function SettingsForm() {
const { data: settings, isLoading, isError, error } = useSettings();
const updateSetting = useUpdateSetting();
@@ -40,11 +32,10 @@ export function SettingsForm() {
if (isLoading) return
{(error as Error)?.message || 'Failed to load settings'}
; if (!settings || settings.length === 0) returnNo settings found.
; - const visibleSettings = settings.filter((setting) => !MANAGED_SETTINGS.has(setting.key)); return (