refactor(filesystem): split into 5 atomic free-function ops + use cases

Replaces the monolithic FileManager class + scattered helpers in
alfred/infrastructure/filesystem with five free functions, each
single-responsibility and pathlib-native:

  list_dir / create_dir / link_file / move_file / move_dir

The infra layer now raises typed exceptions (FilesystemError base
+ SourceNotFound / DestinationExists / NotADirectory / NotAFile /
PermissionDenied / CrossDevice / FilesystemOSError) instead of
returning {status: ok|error} dicts. No more get_memory() reads
from infra.

Application layer mirrors the same split: five free use cases
(<op>_use_case) wrap each infra op, guard inputs against escaping
the new DirectoryRoots VO (downloads / torrents / movies /
tv_shows), catch infra exceptions, and return frozen DTOs. Roots
are injected — no global state.

Legacy files kept on disk with _OLD suffix for reference during
the follow-up rewiring (FileManager, MediaOrganizer,
create_folder/move helpers; CreateSeedLinks/ListFolder/MoveMedia/
ManageSubtitles use cases, resolve_destination). They are no
longer exported from __init__, which intentionally breaks current
agent tool wrappers and downstream tests — re-wiring is the next
chunk of work on the unfuck branch.
This commit is contained in:
2026-05-26 19:22:09 +02:00
parent 28304bb162
commit 2df7843d8b
26 changed files with 951 additions and 199 deletions
+32
View File
@@ -15,6 +15,38 @@ callers).
## [Unreleased]
### Changed
- **`filesystem` infra + application rewritten as 5 atomic free
functions.** On branch `unfuck`. Replaces the monolithic
`FileManager` class + scattered helpers with five small, pure ops in
`alfred/infrastructure/filesystem/`: `list_dir`, `create_dir`,
`link_file`, `move_file`, `move_dir`. Each takes `pathlib.Path`
arguments and raises typed exceptions from a dedicated hierarchy
(`FilesystemError``SourceNotFound` / `DestinationExists` /
`NotADirectory` / `NotAFile` / `PermissionDenied` / `CrossDevice` /
`FilesystemOSError`) — no more `{"status": "ok" | "error"}` dicts at
the infra boundary, no more `get_memory()` reads.
- **`filesystem` application: 5 use cases as free functions.** A
matching `<op>_use_case(path, …, roots: DirectoryRoots)` wraps each
infra op, guards inputs against escaping a new `DirectoryRoots` VO
(downloads / torrents / movies / tv_shows), catches infra exceptions,
and returns a frozen `<Op>Response` DTO. Roots are now injected, not
pulled from the global memory singleton.
### Removed
- `FileManager` / `MediaOrganizer` / `create_folder` / `move` from the
public API of `alfred.infrastructure.filesystem`. Their files remain
on disk renamed with an `_OLD` suffix (e.g. `file_manager_OLD.py`) so
the migration can finish on a follow-up commit without losing
reference material. They are no longer re-exported from `__init__`.
- `CreateSeedLinksUseCase` / `ListFolderUseCase` / `MoveMediaUseCase` /
`ManageSubtitlesUseCase` / `resolve_destination` from the public API
of `alfred.application.filesystem`. Same `_OLD` rename treatment.
This intentionally breaks current tool wrappers and tests downstream
— re-wiring is the next chunk of work on this branch.
### Added
- **`.alfred` v2 — Phase 4: v2-shaped `rescan_show` + new