Change editor rule

This commit is contained in:
2026-09-24 13:24:43 +02:00
parent 16478678b5
commit 73e37dbecd
401 changed files with 14735 additions and 12995 deletions
+235
View File
@@ -0,0 +1,235 @@
# 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` (base `master` at `87c3f97`). 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 check` in `web-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)
- `IOwnedEntity` lives in `Domain/Abstractions` and has no `Id`; `IHasLifeCycle` provides it.
`FindOwnedAsync` requires both and treats non-active entities as NotFound.
- List query records keep `int PageNumber, int PageSize`; the handler builds a `PageRequest`.
The API binds them through `Api/Models/PageQuery` (camelCase names) + `PageQueryValidator`.
- Events and `Publish*Arguments` carry `ImageSizes Sizes`.
- Cover endpoint helper `CoverEndpoint.Stream` already 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)
1. **Language of the conversation: Spanish.** Talk to the user in Spanish.
2. **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 file `feedback_commit_messages.md`.)
3. **Never include the plan documents in commits.** `backend/docs/backend-refactor-plan.md`
and `backend/docs/backend-refactor-handoff.md` stay untracked. Also never commit the
temporary test `backend/Tests/Musify.Api.Tests/Endpoints/ZzOpenApiDumpTests.cs`.
Stage with `git add backend web-player` and then
`git reset -q backend/docs backend/Tests/Musify.Api.Tests/Endpoints/ZzOpenApiDumpTests.cs`.
4. **Do the plan in parts, one part per commit, in order.** Do not mix parts. Never mix
formatting with behaviour.
5. **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.
6. 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 `TestDatabase` kept in sync; a MassTransit message change must be
noted in the Log ("drain queues before deploying").
7. Pronouns: use they/them for anyone whose pronouns are not stated.
8. 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.
9. 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 (`.editorconfig` enforces it; the build treats
warnings as errors, including unused usings IDE0005).
- Private fields camelCase without underscore; constants PascalCase; explicit accessibility.
- Application: `<Feature>/<UseCase>.cs` holds record + handler; responses in `Responses/`.
- Errors via `AppErrors` (`NotFound`, `Forbidden` = 403 for "not owner", `Conflict`);
`Unauthorized` (401) only for "not signed in". Success value is `Result.Success`.
- No try/catch around `SaveChangesAsync`/`PublishAsync` in 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 `Id` tiebreak,
public reads return only `LifeCycleStatus.Active`.
- Text: trim stored names; normalized columns use `TextNormalizer.Normalize`.
- `ArgumentNullException.ThrowIfNull(x)` without `nameof`.
- User-facing strings in English (the web-player owns translation).
- Naming: `PlayList` everywhere; config POCOs end in `Configuration`; 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
```bash
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 info` fails, start
`C:\Users\XShin\AppData\Local\Programs\DockerDesktop\Docker Desktop.exe` and wait.
- Unused-using errors (IDE0005) are common after edits. A helper script removes them:
`python <scratchpad>/fixusings.py` (loops `dotnet build`, deletes the flagged using lines).
If the scratchpad is gone, do it by hand or with `dotnet 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 run `python 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`) on `git add/commit`. Python edit helpers
should read/write with `newline=''` and preserve CRLF if present.
### EF migrations
```bash
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 remove` needs a DB connection and fails offline: delete the migration
files by hand and `git checkout` the snapshot, then re-add.
- To verify a model refactor keeps the schema: add a migration named `Check`, confirm `Up`
is 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.cs` mirrors part of `Database.OnModelCreating`
(P14.3 will extract `ModelConfiguration.Apply`).
### Web-player contract regeneration (when an HTTP contract changes)
1. `web-player/node_modules` is a junction to the main checkout's `node_modules`
(git-ignored). If missing:
`cmd //c "mklink /J web-player\node_modules ..\..\..\web-player\node_modules"` from the worktree root.
2. The OpenAPI document is dumped by the temporary test `ZzOpenApiDumpTests` (untracked; it
returns early unless env `OPENAPI_OUT` is set and runs the API in Development). Script used:
```bash
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)
1. `Program.cs`: `WebApplication.CreateBuilder(args)`, sorted usings, CORS constant at top.
2. Cover endpoints: **already done** with `CoverEndpoint.Stream`.
3. Routes: resource id is `{id}`, sub-resources `{trackId}`; Guid routes use `:guid`,
user ids `:long`. Playlist routes keep `/playlists`. (Contract change: regenerate.)
4. Every endpoint returns `IResult` through `ToHttpResult` (some still return raw types,
e.g. `GetListeningHistory`, `IsFollowing`); use `Results.Ok` where the handler cannot fail.
5. DTOs: drop the `New...` prefix (`UpdateAlbumRequest(Title, Description, ReleaseYear,
PictureIntentId)`; same for playlists). Decision recommended in the plan: rename.
6. Validators: merge the album/playlist picture upload DTOs into one
`PictureUploadRequest` and one validator (the responses are already merged).
7. `OpenApiOptionsSetup` and `ScalarOptionsSetup` share the `SecuritySchemeId` constant.
8. `.WithSummary(...)` strings in sentence case, no trailing period. (Current ones are Title Case.)
9. `JwtBearerOptionsSetup`: single-line guard without braces.
10. `Validators/Limits.cs` with 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
1. Worker `Program.cs`: call `AddApplication(configuration)` instead of registering Application
options by hand (Album config is missing today), add `AddObservability`, remove
`ValidateOnBuild = false` and make the container validate. Register the extra POCO configs
the handlers need (stream tickets, gateway config) rather than disabling validation.
Then `AddUploadIntentConfiguration` (kept only for the Worker) can go.
2. Worker `appsettings.json`: complete and consistent with the Api for shared sections
(add `Album`; keys were renamed in P5), remove stale ones.
3. Gateway: keep option registration local and self-contained; field naming without
underscores (`TicketValidator` uses `_options`, `_rsa`, ...; the middleware field that
shadowed a constructor parameter is already gone); replace the default bucket in the
YARP transform of `appsettings.json` with a placeholder like `/buckets/CHANGE_ME/{**rest}`
(compose overrides it) or document it in `docs/streaming.md`.
Check `deploy/` when renaming config keys.
### P14: Tests
1. 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 use
`OnMissingInstance(Discard)`), `RecurringJobsRegistrar.EveryCron` (internal, may need
`InternalsVisibleTo`).
2. Endpoint tests for visibility and ownership if not already covered (P1, P3 added several).
3. Extract the owned-type and index configuration of `Database` into
`ModelConfiguration.Apply(ModelBuilder)` in Infrastructure and call it from `Database` and
`TestDatabase` (`Application.Tests` then references Infrastructure; note it in the Log).
### P15: Final sweep and documentation
1. Recompute the metrics of section A.5 (script in the plan) and fill D.1 (baseline was
12,687 non-migration, non-test `.cs` lines; targets in A.5). Also record the MassTransit
line count (3,547 to about 2,530) and the Application line count.
2. Grep for leftovers: `Error.Unauthorized(`, `StaticPagedList`, `new ImageSize(` (only in
`PictureSizes.ToImageSizes`), `ThrowIfNull(.*nameof`, `#pragma warning disable CS8618`,
try blocks around `SaveChangesAsync` in `Application/`, `serviceDescriptors`,
`storageHandler`, `PlaylistVisibility`, log-and-rethrow in `MassTransit/Activities/Files`.
3. The analyzer rules downgraded to `suggestion` in `.editorconfig` in 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.
4. Run `dotnet format Musify.slnx` and check the diff is whitespace only (some test files have a
stray blank line before the closing brace).
5. Update `docs/architecture.md`, `docs/media-processing.md`, `docs/projects.md`,
`docs/storage.md`, `docs/streaming.md` with the new names (jobs, entities, routes, config
keys such as `ParentFolder`, `Temp*`, activity/queue names, `MixKind`).
6. Add a short `backend/CLAUDE.md` with the conventions of section 3 above (this one **is**
committed).
7. Delete `ZzOpenApiDumpTests.cs`, keep the plan (section E rows should all point to a checked
part in D), and remove the `web-player/node_modules` junction 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
1. `git log --oneline -14` and `git status --short` (expect only the two untracked docs and
`ZzOpenApiDumpTests.cs`).
2. Run build + tests to confirm the green baseline.
3. Start P12, commit, update the Log, then continue only if the user has not asked to stop.