Database and migrations
Model
The EF Core model lives in src/Conderis.Core/Data: entities in Entities.cs, configuration and global query filters in AppDb.cs. All tables use the recall schema.
Production runs on SQL Server, tests on SQLite. At startup the app calls MigrateAsync on SQL Server and EnsureCreated on SQLite, so a deployment applies pending migrations by itself. After migrating, startup runs steps that need code SQL does not have: AgentService.BackfillNormalizedNames and ProjectSlugs.MoveUnderHandles, which moves projects still waiting for an address under their owner's username (with the cased name) or to the placeholder while there is none. Both find nothing after their first run.
Development points at the remote database. DatabaseConnectionRemote is selected in Development, so dotnet run locally migrates production. Use Database__UseSqlite=true for a local database, or the migration tests below.
Adding a migration
dotnet ef migrations add AddSomething --project src/Conderis.Core --startup-project src/Conderis.Core
Read the generated file before committing. It should contain only the change you meant; an unexpected table rebuild usually means the snapshot and the model disagree.
Tests do not run migrations, except SqlServerMigrationTests, which run against the local .\SQLEXPRESS on a database they create and drop (set CONDERIS_TEST_SQLSERVER for another server; without one they report inconclusive). Seed old schemas there with raw SQL in the columns the tables had then, never with the current entities, which carry columns added later.
Query filters hide restricted content
Content awaiting permanent deletion carries PrivacyRestricted = true. Global query filters in AppDb hide it from every normal query: direct reads, history, recall, exports and proposals. This applies to memories, tasks, documentation pages and single revisions.
Code that must see restricted rows, which is only the deletion itself, calls IgnoreQueryFilters() explicitly. Everything else stays unaware of them, so a new feature cannot leak restricted text by forgetting a condition.
Changes that must stay compatible
- Migrations are applied automatically and cannot be rolled back by the app. Prefer adding columns with defaults over renaming or dropping.
- Item numbers are never reused. A deleted item keeps its number with the kind
deleted, so an old#Nnever points at something newer. - Project paths are unique case-insensitively (
NormalizedPath); addresses a project left live inProjectRedirects, blocked people inProjectBlocks.