fix(6229): remove the Profile settings page — nothing read it
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m9s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 43s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m9s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 43s
The Profile tab said an assistant used it to personalise chat and a daily journal. Neither exists, and nothing read the profile: the page saved to user_profiles and the backup carried it. Removed the tab, the client calls, /api/profile, the service and the model, and migration 0123 drops the table (downgrade recreates it empty). Backup format v27 stops writing user_profiles; an older archive carrying the section still restores, and the file-restore integration test now proves it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"""Real-Postgres proof that the scheduled backup's FILE restores (milestone
|
||||
523): a payload written as the tar, read back through `scribe.restore`'s
|
||||
loader, and fed to the real `restore_full_backup`.
|
||||
"""Real-Postgres proof that the scheduled backup's FILE restores: a payload
|
||||
written as the tar, read back through `scribe.restore`'s loader, and fed to
|
||||
the real `restore_full_backup`.
|
||||
|
||||
The unit lane already shows the tar hands back the dict it was given. What
|
||||
only this lane shows is that the dict it hands back is one the restore
|
||||
@@ -16,7 +16,6 @@ from scribe.models.milestone import Milestone
|
||||
from scribe.models.note import Note
|
||||
from scribe.models.project import Project
|
||||
from scribe.models.user import User
|
||||
from scribe.models.user_profile import UserProfile
|
||||
from scribe.restore import load_backup, run
|
||||
from scribe.services import backup
|
||||
from scribe.services.backup_file import write_archive
|
||||
@@ -52,7 +51,8 @@ async def _no_leftovers():
|
||||
async def written(tmp_path):
|
||||
"""A project with a milestone and a task in it, exported with the real
|
||||
row builders, renamed so the restore mints a separate user, and written
|
||||
as the backup tar."""
|
||||
as the backup tar. It also carries a `user_profiles` section, as an
|
||||
archive from before v27 does."""
|
||||
async with async_session() as s:
|
||||
user = await ensure_user(s, OWNER)
|
||||
project = Project(user_id=user.id, title="Restore drill")
|
||||
@@ -65,10 +65,6 @@ async def written(tmp_path):
|
||||
title="Prove the restore", body="From the tar, not the JSON.",
|
||||
status="todo", note_type="note", task_kind="work")
|
||||
s.add(note)
|
||||
profile = UserProfile(user_id=user.id, display_name="Drill Owner",
|
||||
tone="technical", interests=["backups"],
|
||||
work_schedule={"start": "09:00"})
|
||||
s.add(profile)
|
||||
await s.commit()
|
||||
payload = {
|
||||
"version": backup.BACKUP_VERSION,
|
||||
@@ -77,7 +73,8 @@ async def written(tmp_path):
|
||||
"projects": backup._project_rows([project]),
|
||||
"milestones": backup._milestone_rows([milestone]),
|
||||
"notes": backup._note_rows([note]),
|
||||
"user_profiles": backup._user_profile_rows([profile]),
|
||||
"user_profiles": [{"user_id": user.id, "display_name": "Drill Owner",
|
||||
"tone": "technical", "interests": ["backups"]}],
|
||||
}
|
||||
payload["users"][0]["username"] = RESTORED
|
||||
from datetime import datetime, timezone
|
||||
@@ -94,19 +91,10 @@ async def test_the_tar_restores_with_every_id_remapped(written):
|
||||
project = (await s.execute(select(Project).where(Project.user_id == user.id))).scalars().one()
|
||||
milestone = (await s.execute(select(Milestone).where(Milestone.user_id == user.id))).scalars().one()
|
||||
note = (await s.execute(select(Note).where(Note.user_id == user.id))).scalars().one()
|
||||
profile = (await s.execute(
|
||||
select(UserProfile).where(UserProfile.user_id == user.id)
|
||||
)).scalars().one()
|
||||
assert project.title == "Restore drill"
|
||||
assert (note.project_id, note.milestone_id) == (project.id, milestone.id)
|
||||
assert note.body == "From the tar, not the JSON."
|
||||
assert note.status == "todo"
|
||||
# The Profile page travels with its user; a field left blank stays
|
||||
# blank rather than restoring as the page's default.
|
||||
assert (profile.display_name, profile.tone) == ("Drill Owner", "technical")
|
||||
assert profile.interests == ["backups"]
|
||||
assert profile.work_schedule == {"start": "09:00"}
|
||||
assert profile.job_title is None
|
||||
|
||||
|
||||
async def test_the_command_refuses_a_database_with_accounts(written):
|
||||
|
||||
@@ -21,11 +21,8 @@ from scribe.services import backup
|
||||
def test_backup_version_is_current():
|
||||
"""The bump is the point of the test — a payload section added without
|
||||
moving the version produces backups that are structurally different and
|
||||
indistinguishable by inspection.
|
||||
|
||||
(Named for the number it asserted until v10, which is exactly the drift a
|
||||
name-carrying-a-value invites; it now says what it checks.)"""
|
||||
assert backup.BACKUP_VERSION == 26
|
||||
indistinguishable by inspection."""
|
||||
assert backup.BACKUP_VERSION == 27
|
||||
|
||||
|
||||
def _exportable_note(**over):
|
||||
@@ -164,7 +161,6 @@ def _column_guard_targets():
|
||||
from scribe.models.system import RecordSystem, System
|
||||
from scribe.models.task_log import TaskLog
|
||||
from scribe.models.user import User
|
||||
from scribe.models.user_profile import UserProfile
|
||||
|
||||
return {
|
||||
"users": (User, backup._user_rows),
|
||||
@@ -212,7 +208,6 @@ def _column_guard_targets():
|
||||
"family_idea_references": (FamilyIdeaReference, backup._family_idea_reference_rows),
|
||||
"family_adoptions": (FamilyAdoption, backup._family_adoption_rows),
|
||||
"family_decisions": (FamilyDecision, backup._family_decision_rows),
|
||||
"user_profiles": (UserProfile, backup._user_profile_rows),
|
||||
}
|
||||
|
||||
|
||||
@@ -324,7 +319,6 @@ def _import_guard_targets():
|
||||
"system_usage_events": backup._build_system_usage_event,
|
||||
"retrieval_tuning_events": backup._build_retrieval_tuning_event,
|
||||
"moment_mappings": backup._build_moment_mapping,
|
||||
"user_profiles": backup._build_user_profile,
|
||||
"design_systems": backup._build_design_system,
|
||||
"design_tokens": backup._build_design_token,
|
||||
"repo_bindings": backup._build_repo_binding,
|
||||
@@ -593,9 +587,7 @@ async def test_export_full_backup_contains_every_declared_section():
|
||||
# v25: family canon.
|
||||
"platforms", "project_platforms", "family_ideas",
|
||||
"family_idea_platforms", "family_idea_references",
|
||||
"family_adoptions", "family_decisions",
|
||||
# v26: the Profile page.
|
||||
"user_profiles"):
|
||||
"family_adoptions", "family_decisions"):
|
||||
assert key in out, f"missing export section: {key}"
|
||||
assert out[key] == []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user