Overview
Implement the storage migration protocol proposed in #382 . This issue tracks the implementation work required to add migration capabilities to Campus.
Proposed Protocol
See docs/migration-protocol.md for full specification.
Key decisions:
Separate package: Migrations live in campus-admin, not campus
Separate storage module: campus-admin/storage/ (independent module, not under campus.storage)
Separate database: Admin uses campus_admin_db, not campus databases
Manual deployment: Migrations run manually via SSH, NOT auto-run from Campus apps
CLI: admin command, not campus migrate
No Campus deploy utils: Does NOT use campus.common.devops.deploy (Campus namespace only)
Implementation Plan
Phase 1: campus-admin Package Structure
Create new package: campus-admin/
campus-admin/
├── __init__.py
├── main.py # `admin` CLI entry point
├── storage/
│ ├── __init__.py # Separate storage module, independent from campus.storage
│ ├── migrations/
│ │ ├── __init__.py
│ │ ├── state.py
│ │ ├── runner.py
│ │ └── migrations/
│ │ ├── 001_init_assignments.py
│ │ ├── 002_init_submissions.py
│ │ └── ...
│ └── audit/
│ ├── __init__.py
│ ├── log.py # High-volume audit writer
│ └── queries.py # Audit query helpers
├── yapper/
│ ├── __init__.py
│ └── base.py # Event logging for migrations
└── pyproject.toml
Key point: campus-admin/storage/ is its own storage module, completely separate from campus/storage/.
Phase 2: Core Migration Infrastructure
File: campus-admin/storage/migrations/__init__.py
File: campus-admin/storage/migrations/state.py
File: campus-admin/storage/migrations/runner.py
Phase 3: Migration Files
Directory: campus-admin/storage/migrations/migrations/
Phase 4: Admin CLI
File: campus-admin/main.py
File: campus-admin/pyproject.toml
Phase 5: Manual Deployment Process
Deployment workflow (manual, NOT automatic):
# 1. SSH into server
ssh staging.example.com
# 2. Pull latest code
cd campus-admin
git pull main
# 3. Run migrations manually
admin migrate
# 4. Verify success
admin status
No integration with Campus apps:
Campus apps (campus.auth, campus.api) do NOT auto-run migrations
No changes to wsgi.py or app factories
Migrations are operational infrastructure, separate from application code
Phase 6: Audit Logging
File: campus-admin/storage/audit/log.py
File: campus-admin/yapper/base.py
Phase 7: Testing
File: tests/unit/admin/storage/test_migrations.py (new)
File: tests/integration/admin/storage/test_migration_runner.py (new)
Manual Testing
Files to Create
campus-admin/
├── __init__.py
├── main.py
├── storage/
│ ├── __init__.py
│ ├── migrations/
│ │ ├── __init__.py
│ │ ├── state.py
│ │ ├── runner.py
│ │ └── migrations/
│ │ ├── 001_init_assignments.py
│ │ ├── 002_init_submissions.py
│ │ └── ...
│ └── audit/
│ ├── __init__.py
│ ├── log.py
│ └── queries.py
├── yapper/
│ ├── __init__.py
│ └── base.py
└── pyproject.toml
Files NOT Modified (campus)
No changes to Campus app files:
wsgi.py - NOT modified (no auto-run integration)
campus/*/factory.py - NOT modified
Dependencies
Blocked by docs: Storage Migration Protocol (issue #27) #382 merge (protocol documentation)
Creates new: campus-admin package with independent storage
Uses existing: campus.storage, campus.api.resources, campus.auth.resources (via imports, not inheritance)
Definition of Done
References
Overview
Implement the storage migration protocol proposed in #382. This issue tracks the implementation work required to add migration capabilities to Campus.
Proposed Protocol
See docs/migration-protocol.md for full specification.
Key decisions:
campus-admin, notcampuscampus-admin/storage/(independent module, not under campus.storage)campus_admin_db, not campus databasesadmincommand, notcampus migratecampus.common.devops.deploy(Campus namespace only)Implementation Plan
Phase 1: campus-admin Package Structure
Create new package:
campus-admin/Key point:
campus-admin/storage/is its own storage module, completely separate fromcampus/storage/.Phase 2: Core Migration Infrastructure
File:
campus-admin/storage/migrations/__init__.pyMigrationRunner,MigrationStateFile:
campus-admin/storage/migrations/state.pyMigrationStateclass (connects to campus_admin_db)init_state_table()- create_migrationstablerecord_migration()- log applied migration with status="applied"record_failure()- log failed migration with error_messageget_applied_migrations()- return set of applied IDs (filter by status="applied")mark_rollback()- set status="rolled_back"is_applied()- check if status="applied"File:
campus-admin/storage/migrations/runner.pyMigrationRunnerclassdiscover_migrations()- find and parse migration filesupgrade(target)- apply pending migrationsdowngrade(target)- rollback to revisionpre_flight_checks()- validate before runningPhase 3: Migration Files
Directory:
campus-admin/storage/migrations/migrations/001_init_assignments.py- Operates on campus.api.assignments002_init_submissions.py- Operates on campus.api.submissionscampus.api.resourcesfor type-safe accessPhase 4: Admin CLI
File:
campus-admin/main.pymigrate(target)- run forward migrationsrollback(target)- reverse migrationsstatus()- show current migration stateadmincommandFile:
campus-admin/pyproject.toml[project.scripts] admin = "campus_admin.main:cli"Phase 5: Manual Deployment Process
Deployment workflow (manual, NOT automatic):
No integration with Campus apps:
campus.auth,campus.api) do NOT auto-run migrationswsgi.pyor app factoriesPhase 6: Audit Logging
File:
campus-admin/storage/audit/log.pyAuditWriterclass with direct PostgreSQL connectionbulk_insert(rows)- high-volume insert using executemanyFile:
campus-admin/yapper/base.pymigration.applied,migration.rolled_back,migration.failedPhase 7: Testing
File:
tests/unit/admin/storage/test_migrations.py(new)File:
tests/integration/admin/storage/test_migration_runner.py(new)Manual Testing
Files to Create
Files NOT Modified (campus)
No changes to Campus app files:
- NOT modified (no auto-run integration)wsgi.py- NOT modifiedcampus/*/factory.pyDependencies
campus-adminpackage with independent storagecampus.storage,campus.api.resources,campus.auth.resources(via imports, not inheritance)Definition of Done
admin migrateapplies pending migrationsadmin rollback <revision>reverts migrationsadmin statusshows migration state_migrationstable tracks state correctly (in campus_admin_db)References