14 KiB
Handoff: backend refactor, parts P12 to P15
For the next AI. Read this file first, then
backend/docs/backend-refactor-plan.md(section A, B, and the parts P12–P15, plus the Log in section D). Both files are untracked on purpose: never commit them.
1. Where things stand
- Worktree:
C:\Users\XShin\OneDrive\Documentos\Musify\MusifyM\.claude\worktrees\backend-refactor-plan-9b4465 - Branch:
claude/backend-refactor-plan-9b4465(basemasterat87c3f97). Nothing is pushed. - Done and committed: P0 to P11 (one commit per part, see
git log --oneline). - Left: P12 (API layer), P13 (Worker and Gateway hosts), P14 (tests), P15 (final sweep and docs).
- State at the last commit (
405df08): build with 0 warnings, 365 tests green (Infrastructure tests need Docker),npm run checkinweb-player/clean. - The Log (section D of the plan) already has notes for P0–P11 and is the source of truth for decisions taken. Update it (checkbox, date, notes) for every part you finish.
Deviations from the plan already taken (do not undo)
IOwnedEntitylives inDomain/Abstractionsand has noId;IHasLifeCycleprovides it.FindOwnedAsyncrequires both and treats non-active entities as NotFound.- List query records keep
int PageNumber, int PageSize; the handler builds aPageRequest. The API binds them throughApi/Models/PageQuery(camelCase names) +PageQueryValidator. - Events and
Publish*ArgumentscarryImageSizes Sizes. - Cover endpoint helper
CoverEndpoint.Streamalready exists (this was P12.2). - Mix titles were replaced by
MixKind; the web-player renders the text (src/lib/data/mixes.ts). - The two migrations planned in P4 were merged into
DropUploadTableAndTrackAudioRetryColumns. - Adding a duplicate track to a playlist or album is now a silent success (bulk endpoints).
- Hangfire cron has 1-minute resolution, so job intervals in seconds are rounded up to minutes.
- Pending inside P11: log-and-rethrow still present in
MassTransit/Activities/Files/*(Copy, Download, Remove, Transfer, Upload). Clean it in P15 or when touching them.
2. Rules the user gave (follow them exactly)
- Language of the conversation: Spanish. Talk to the user in Spanish.
- Commit messages: English, only about the code that changed. Never mention the plan,
part numbers (P0, P1...), the Log, or "step X". One commit per part, imperative mood,
e.g.
Simplify infrastructure services and move all recurring jobs to Hangfire. (This is also saved in the memory filefeedback_commit_messages.md.) - Never include the plan documents in commits.
backend/docs/backend-refactor-plan.mdandbackend/docs/backend-refactor-handoff.mdstay untracked. Also never commit the temporary testbackend/Tests/Musify.Api.Tests/Endpoints/ZzOpenApiDumpTests.cs. Stage withgit add backend web-playerand thengit reset -q backend/docs backend/Tests/Musify.Api.Tests/Endpoints/ZzOpenApiDumpTests.cs. - Do the plan in parts, one part per commit, in order. Do not mix parts. Never mix formatting with behaviour.
- When the user says to stop at a point, stop there (the last message was "para en esta parte cuando termines", so work stopped after P11). Only continue when asked.
- Follow the plan's own rules: read section A first; create a shared helper from section B
yourself if it does not exist yet; keep the Log and the findings index (section E) up to date;
a contract change needs the web-player regeneration (section 4 below); an EF model change
needs a migration and
TestDatabasekept in sync; a MassTransit message change must be noted in the Log ("drain queues before deploying"). - Pronouns: use they/them for anyone whose pronouns are not stated.
- Outward-facing or hard-to-reverse actions (push, PR, deleting things) need explicit approval. Nothing has been pushed; do not push or open a PR unless asked.
- Report faithfully: if a test fails or something was skipped or unverifiable (e.g. manual end-to-end runs), say so in the Log and in the final message.
3. Coding conventions (from the plan, section B.2, keep applying them)
- File-scoped namespaces, usings outside, sorted (
.editorconfigenforces it; the build treats warnings as errors, including unused usings IDE0005). - Private fields camelCase without underscore; constants PascalCase; explicit accessibility.
- Application:
<Feature>/<UseCase>.csholds record + handler; responses inResponses/. - Errors via
AppErrors(NotFound,Forbidden= 403 for "not owner",Conflict);Unauthorized(401) only for "not signed in". Success value isResult.Success. - No try/catch around
SaveChangesAsync/PublishAsyncin handlers (outbox: publish then save is atomic). Infrastructure either handles or rethrows, never log-and-rethrow. - Handlers that cannot fail return plain types; lists are paginated, ordered with
Idtiebreak, public reads return onlyLifeCycleStatus.Active. - Text: trim stored names; normalized columns use
TextNormalizer.Normalize. ArgumentNullException.ThrowIfNull(x)withoutnameof.- User-facing strings in English (the web-player owns translation).
- Naming:
PlayListeverywhere; config POCOs end inConfiguration; injected services are named after their type (storageService).
4. Practical procedures (learned the hard way)
Run everything from backend/ unless stated.
Build, format fixes, tests
dotnet build Musify.slnx
dotnet test Musify.slnx
- Do not chain
dotnet test ... | grep && commit: a failing run can look successful. Read the totals (total:,succeeded:) before committing. - Infrastructure and Api tests need Docker Desktop running. If
docker infofails, startC:\Users\XShin\AppData\Local\Programs\DockerDesktop\Docker Desktop.exeand wait. - Unused-using errors (IDE0005) are common after edits. A helper script removes them:
python <scratchpad>/fixusings.py(loopsdotnet build, deletes the flagged using lines). If the scratchpad is gone, do it by hand or withdotnet format.
Editing files from the shell (important)
- The Bash tool fails to parse many commands that combine several heredocs, inline
python - <<'EOF'with backslashes/quotes, or big C# blocks ("unexpected EOF while looking for matching'"). When that happens nothing runs. Use the Write tool for files and put Python edit scripts in a file (in the scratchpad) and runpython that_file.py. - Working tree files use LF while git has autocrlf on; warnings like "LF will be replaced by
CRLF" are noise. Redirect stderr (
2>/dev/null) ongit add/commit. Python edit helpers should read/write withnewline=''and preserve CRLF if present.
EF migrations
export Database__ConnectionString="Host=localhost;Database=x;Username=x;Password=x"
dotnet ef migrations add <Name> --project Core/Musify.Infrastructure --startup-project Core/Musify.Infrastructure
dotnet ef migrations removeneeds a DB connection and fails offline: delete the migration files by hand andgit checkoutthe snapshot, then re-add.- To verify a model refactor keeps the schema: add a migration named
Check, confirmUpis empty, delete its two files (keep snapshot changes if they only rename types). - Hand-edit migrations when data must be preserved (see
MixKind,PlayListTrackUnique). Application.Tests/TestSupport/TestDatabase.csmirrors part ofDatabase.OnModelCreating(P14.3 will extractModelConfiguration.Apply).
Web-player contract regeneration (when an HTTP contract changes)
web-player/node_modulesis a junction to the main checkout'snode_modules(git-ignored). If missing:cmd //c "mklink /J web-player\node_modules ..\..\..\web-player\node_modules"from the worktree root.- The OpenAPI document is dumped by the temporary test
ZzOpenApiDumpTests(untracked; it returns early unless envOPENAPI_OUTis set and runs the API in Development). Script used:
cd backend
export ASPNETCORE_ENVIRONMENT=Development OPENAPI_OUT="<scratchpad>/openapi.json"
dotnet test Tests/Musify.Api.Tests --filter "FullyQualifiedName~ZzOpenApiDump"
cd ../web-player
npx -y openapi-typescript@7 "<scratchpad>/openapi.json" -o src/lib/api/schema.d.ts
npm run check
If the test file is gone, recreate it: it fetches /openapi/v1.json through
fixture.CreateAnonymousClient() and writes it to OPENAPI_OUT.
P12.1 changes WebApplication.CreateBuilder(args); keep the dump working afterwards.
3. Fix web-player call sites, run npx prettier --write on touched files, then npm run check.
4. Delete ZzOpenApiDumpTests.cs at the very end (P15).
5. What is left, part by part
P12: API layer (plan section C, P12)
Program.cs:WebApplication.CreateBuilder(args), sorted usings, CORS constant at top.- Cover endpoints: already done with
CoverEndpoint.Stream. - Routes: resource id is
{id}, sub-resources{trackId}; Guid routes use:guid, user ids:long. Playlist routes keep/playlists. (Contract change: regenerate.) - Every endpoint returns
IResultthroughToHttpResult(some still return raw types, e.g.GetListeningHistory,IsFollowing); useResults.Okwhere the handler cannot fail. - DTOs: drop the
New...prefix (UpdateAlbumRequest(Title, Description, ReleaseYear, PictureIntentId); same for playlists). Decision recommended in the plan: rename. - Validators: merge the album/playlist picture upload DTOs into one
PictureUploadRequestand one validator (the responses are already merged). OpenApiOptionsSetupandScalarOptionsSetupshare theSecuritySchemeIdconstant..WithSummary(...)strings in sentence case, no trailing period. (Current ones are Title Case.)JwtBearerOptionsSetup: single-line guard without braces.Validators/Limits.cswith every validation limit (playlist name 50, descriptions 256, titles 200, user name 48, release year range). List them in the Log for the web-player plan (P7.1). Database column length wins on disagreement.
Coordination table (section A.4): P3.1 and P8.1 of the web-player plan are unblocked by P7;
P8.4 (bulk add) by P9; P7.1 (shared limits) by P12.10. Tick them in the web-player plan's Log too
(web-player/docs/frontend-audit-plan.md, also untracked).
P13: Worker and Gateway hosts
- Worker
Program.cs: callAddApplication(configuration)instead of registering Application options by hand (Album config is missing today), addAddObservability, removeValidateOnBuild = falseand make the container validate. Register the extra POCO configs the handlers need (stream tickets, gateway config) rather than disabling validation. ThenAddUploadIntentConfiguration(kept only for the Worker) can go. - Worker
appsettings.json: complete and consistent with the Api for shared sections (addAlbum; keys were renamed in P5), remove stale ones. - Gateway: keep option registration local and self-contained; field naming without
underscores (
TicketValidatoruses_options,_rsa, ...; the middleware field that shadowed a constructor parameter is already gone); replace the default bucket in the YARP transform ofappsettings.jsonwith a placeholder like/buckets/CHANGE_ME/{**rest}(compose overrides it) or document it indocs/streaming.md. Checkdeploy/when renaming config keys.
P14: Tests
- Add tests for what has none: MassTransit activities (generic lifecycle and picture-update
ones with the SQLite
TestDatabase),ProcessingSlipFaultConsumer(every process kind), routing slip builders (activity order and arguments), saga state machines with MassTransit's test harness (happy path, failure, missing instance on update; sagas already useOnMissingInstance(Discard)),RecurringJobsRegistrar.EveryCron(internal, may needInternalsVisibleTo). - Endpoint tests for visibility and ownership if not already covered (P1, P3 added several).
- Extract the owned-type and index configuration of
DatabaseintoModelConfiguration.Apply(ModelBuilder)in Infrastructure and call it fromDatabaseandTestDatabase(Application.Teststhen references Infrastructure; note it in the Log).
P15: Final sweep and documentation
- Recompute the metrics of section A.5 (script in the plan) and fill D.1 (baseline was
12,687 non-migration, non-test
.cslines; targets in A.5). Also record the MassTransit line count (3,547 to about 2,530) and the Application line count. - Grep for leftovers:
Error.Unauthorized(,StaticPagedList,new ImageSize((only inPictureSizes.ToImageSizes),ThrowIfNull(.*nameof,#pragma warning disable CS8618, try blocks aroundSaveChangesAsyncinApplication/,serviceDescriptors,storageHandler,PlaylistVisibility, log-and-rethrow inMassTransit/Activities/Files. - The analyzer rules downgraded to
suggestionin.editorconfigin P0 (CA1848, CA1873, CA1305, CA1707, CA1859, CA1716, CA1000, CA1725, CA1816, CA1822, CA1852, CA1068, CA1711, CA1861) should be revisited: fix the cheap ones and remove their overrides. - Run
dotnet format Musify.slnxand check the diff is whitespace only (some test files have a stray blank line before the closing brace). - Update
docs/architecture.md,docs/media-processing.md,docs/projects.md,docs/storage.md,docs/streaming.mdwith the new names (jobs, entities, routes, config keys such asParentFolder,Temp*, activity/queue names,MixKind). - Add a short
backend/CLAUDE.mdwith the conventions of section 3 above (this one is committed). - Delete
ZzOpenApiDumpTests.cs, keep the plan (section E rows should all point to a checked part in D), and remove theweb-player/node_modulesjunction only if desired (it is ignored).
6. Deployment notes to keep in the Log
Queues must be drained before deploying because these message contracts changed:
processing-slip process kinds and variables (P2), ImageSizes in events and arguments (P8),
new lifecycle/delete/update-picture activities and arguments (P11), DeleteAlbumEvent (P3).
Migrations added so far: PlayListTrackUnique, DropUploadTableAndTrackAudioRetryColumns,
DropUserHasTrack, MixKind. Old bucket objects under the temporal/ prefix (from before the
Temp* config fix) may need a one-off clean-up.
7. Suggested first steps for the next session
git log --oneline -14andgit status --short(expect only the two untracked docs andZzOpenApiDumpTests.cs).- Run build + tests to confirm the green baseline.
- Start P12, commit, update the Log, then continue only if the user has not asked to stop.