diff --git a/.claude/skills/closing-an-issue/SKILL.md b/.claude/skills/closing-an-issue/SKILL.md new file mode 100644 index 000000000..308e90b4f --- /dev/null +++ b/.claude/skills/closing-an-issue/SKILL.md @@ -0,0 +1,37 @@ +--- +name: closing-an-issue +description: The ersatztv task-completion protocol — the mandatory steps and the `## Closing record` comment template for closing a Gitea issue. Use when finishing a task that closes an issue, or when writing a closing comment. The `/done` command runs this automatically. +--- + +# Task Completion Protocol + +Every task that closes a Gitea issue MUST complete ALL of these before it is considered done. +Use `/done ` to run through this automatically. + +Merge consent is a separate, hook-enforced concern — see the `## Done-when` convention in the +root `CLAUDE.md`, which stays always-loaded. + +1. **Root cause** (bug fixes / incidents only): Document WHY the problem existed, not just what was changed. If root cause is unknown, say so explicitly and open a follow-up investigation issue. Fixing symptoms without understanding causes creates recurring problems. +2. **Comment on issues** as you work — what you found, what approach you're taking, any deviations from the suggested fix. +3. **Push changes**: `git push` all commits before closing. Use `fixes #N` in commit messages to auto-close where appropriate. +4. **Close comment**: Add a structured `## Closing record` comment on the issue (template below). +5. **Close the issue** via API or `fixes #N` commit. Leave open with a comment only if partially addressed. +6. **Update docs**: If the change affects operational behavior, update the relevant Obsidian docs (`~/homelab-docs/`), MEMORY.md, or CLAUDE.md inline — not as a follow-up. +7. **Reply to reviewer** (if from adversarial review): Summary of done/deferred/questions. This triggers the next review cycle. + +## `## Closing record` template + +Step 4 — this is both the human-readable summary and the per-issue unit MemPalace mines for +retrieval; see `docs/handoffs/chicorytv-issue-queue.md` → "Knowledge retrieval" for the retrieval +contract this feeds. + +```markdown +## Closing record +**Outcome:** +**Root cause:** +**Decisions/conventions changed:** +**Reusable knowledge:** +**Verification:** +**Deferred:** +**Docs updated:** +``` diff --git a/CLAUDE.md b/CLAUDE.md index 93b08c643..2e30a062b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,25 +4,9 @@ Custom IPTV channel server for Jellyfin. Forked from [ErsatzTV/ErsatzTV](https:/ ## Architecture -- **Language**: C# / .NET 10 - **UI**: ChicoryTV React SPA (`web/`, Vite, served at `/app`) over the REST API — the ONLY UI. The legacy Blazor Server UI (MudBlazor) was removed in #91 phase (b); root `/` and every legacy route now 302 to `/app`, either via an explicit redirect in `ErsatzTV/LegacyUiRedirects.cs` or the Startup catch-all fallback (any unmatched non-`/api`/`/artwork`/`/docs`/`/openapi` path → `/app`). Historical parity work: media detail pages + image folder browser landed via #141 (PR #183); scheduling parity #144/#162, #141/#158/#161/#180, #145, #151/#152/#153/#155, and the media-source write API/SPA #202 are all DONE. - **Pattern**: CQRS via MediatR — queries/commands in `ErsatzTV.Application/` - **Database**: EF Core (SQLite default, MySQL optional) — context in `ErsatzTV.Infrastructure/Data/TvContext.cs` -- **Media**: FFmpeg via CliWrap, SkiaSharp for logo generation -- **Functional C#**: Language Ext (Option, Either monads throughout) - -### Project Layout - -| Project | Role | -|---------|------| -| `ErsatzTV/` | ASP.NET Core host, API controllers, SPA static hosting, DI setup | -| `web/` | ChicoryTV React SPA (Vite + TypeScript; builds into `ErsatzTV/wwwroot/app`) | -| `ErsatzTV.Application/` | MediatR handlers (business logic) | -| `ErsatzTV.Core/` | Domain entities, interfaces, no infrastructure deps | -| `ErsatzTV.Infrastructure/` | EF Core repos, data access | -| `ErsatzTV.Infrastructure.Sqlite/` | SQLite-specific implementations | -| `ErsatzTV.FFmpeg/` | FFmpeg process wrapper | -| `ErsatzTV.Scanner/` | Media library scanning | ### Key Files @@ -43,12 +27,6 @@ Custom IPTV channel server for Jellyfin. Forked from [ErsatzTV/ErsatzTV](https:/ ## Development ```bash -# Build -dotnet build ErsatzTV.sln - -# Run locally (needs FFmpeg in PATH) -dotnet run --project ErsatzTV - # Docker build docker build -f docker/Dockerfile -t ersatztv:dev . ``` @@ -88,26 +66,9 @@ Every task that closes a Gitea issue MUST complete ALL of these before it is con Both need Gitea read creds in the env to enforce (**`ETV_GITEA_BASICAUTH=user:pass`** or `ETV_GITEA_TOKEN`; `ETV_GITEA_URL` overrides the base). Without them the merge hook asks and the push backstop is a no-op — the gate degrades to today's manual confirmation, never a silent pass. Docs-only PRs/pushes are exempt. -1. **Root cause** (bug fixes / incidents only): Document WHY the problem existed, not just what was changed. If root cause is unknown, say so explicitly and open a follow-up investigation issue. Fixing symptoms without understanding causes creates recurring problems. -2. **Comment on issues** as you work — what you found, what approach you're taking, any deviations from the suggested fix. -3. **Push changes**: `git push` all commits before closing. Use `fixes #N` in commit messages to auto-close where appropriate. -4. **Close comment**: Add a structured `## Closing record` comment on the issue (template below). -5. **Close the issue** via API or `fixes #N` commit. Leave open with a comment only if partially addressed. -6. **Update docs**: If the change affects operational behavior, update the relevant Obsidian docs (`~/homelab-docs/`), MEMORY.md, or CLAUDE.md inline — not as a follow-up. -7. **Reply to reviewer** (if from adversarial review): Summary of done/deferred/questions. This triggers the next review cycle. - -**`## Closing record` template** (step 4 — this is both the human-readable summary and the per-issue unit MemPalace mines for retrieval; see `docs/handoffs/chicorytv-issue-queue.md` → "Knowledge retrieval" for the retrieval contract this feeds): - -```markdown -## Closing record -**Outcome:** -**Root cause:** -**Decisions/conventions changed:** -**Reusable knowledge:** -**Verification:** -**Deferred:** -**Docs updated:** -``` +**The 7 mandatory completion steps and the `## Closing record` comment template** live in the +`closing-an-issue` skill (`.claude/skills/closing-an-issue/SKILL.md`) — invoke it (or `/done`) +when finishing a task that closes an issue. ## Project Boundaries