Captures the owner's requirements and decisions from the planning conversation, current branch/worktree state, open tasks 17-19, pending owner decisions and the controller's rulings so a new agent can resume. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
182 lines
11 KiB
Markdown
182 lines
11 KiB
Markdown
---
|
||
status: in-progress
|
||
---
|
||
|
||
# Phase 1 handoff (state as of 2026-11-02)
|
||
|
||
Read this first if you are picking up STTracker Phase 1. It is the bridge between the original conversation with the owner and the repo. Everything the owner decided is in the spec. Everything the controller decided while executing is in `docs/plans/001-phase1-sdd/ledger.md` (search for "Ruling").
|
||
|
||
Read in this order:
|
||
1. `AGENTS.md`
|
||
2. this file
|
||
3. `docs/specs/001-sttracker-v1-design.md`
|
||
4. `docs/plans/001-phase1-own-your-data.md`
|
||
5. `docs/plans/001-phase1-sdd/ledger.md`
|
||
|
||
## 1. What the owner asked for, and decided
|
||
|
||
**The original request.** A self-hosted "meta" entertainment tracker.
|
||
- Users connect their accounts on other platforms and get everything in one place.
|
||
- They can create their own entries when no platform has the item.
|
||
- Required platforms: Hardcover, Backloggd, Trakt and Last.fm.
|
||
- Stack: Bun with SQLite or Postgres.
|
||
- After the research, prepare the repo with the "preparing repos for agents" skill and push it to `https://gitea.lucaswinther.info/lucasw89/STTracker.git`.
|
||
|
||
**Why the owner wants it** (their own words, summarised):
|
||
- Detect entries that were deleted or merged without their knowledge.
|
||
- Get backups that platforms don't offer.
|
||
- Move data between services that have no link to each other.
|
||
- Let an AI help convert data between services.
|
||
- Log things quickly without needing a "real" ID such as an ISBN or a TMDB id, and fix them later.
|
||
- Have the same features for every kind of media, instead of a separate "Trakt for X" per media type.
|
||
- Get cross-media recommendations and lists, e.g. Leitner → Magnus Archives → Voices of the Void → Twilight Zone.
|
||
- Have a personal, shareable page.
|
||
- **The app must put the user in control.** Users who keep a spreadsheet or Obsidian/Joplin notes must not have to conform to the data. Examples they gave:
|
||
- screenshots on games;
|
||
- where they bought something;
|
||
- rating a whole series as well as each book.
|
||
|
||
**Owner decisions** (all are already in the spec):
|
||
- **Audience:** the owner first, then a few friends, maybe a small public instance later. Social features are a nice-to-have, not now.
|
||
- **Backloggd:** wait for its official CSV export. Do not scrape it (it has no API and sits behind Anubis bot protection).
|
||
- **Music:** ListenBrainz is primary, with Libre.fm as a mirror and Last.fm as a read-only archive.
|
||
- **Sources are plugins,** so they can be fixed or added without big core changes.
|
||
- **Owned library:** a private "owned library" (Steam, Jellyfin and so on) that never shows on the profile.
|
||
- **Other apps plug in later:** Jellyfin, Kavita and Calibre-Web, through STTracker's own API, app tokens and OAuth. Phase 3 ingest: webhooks, a ListenBrainz-compatible endpoint and kosync.
|
||
- **Stack:** React plus Docker.
|
||
- **Look:**
|
||
- The app is "Hardcover-style".
|
||
- The public page is "scrapbook + river".
|
||
- "Shelf" and "card catalogue" are parked as future themes.
|
||
- Mockups: `docs/design/mockups/`.
|
||
- **Conflicts:** when two sources disagree, ask the user via the Changes inbox. The same play from two sources is merged.
|
||
- **Write-back:** Phase 1 is read-only. Write-back is opt-in per connection, off by default, and comes in Phase 2.
|
||
- **Execution:** "Use subagent drive, and use multiple agents at once."
|
||
- **The owner's global preference** (`~/.claude/CLAUDE.md`): once a design is approved, carry it through spec → plan → execution with no extra review stops, and report decisions at the end.
|
||
|
||
## 2. Where the code is
|
||
|
||
- **`main`** (on Gitea): only the bootstrap commit (560c27c). Nothing else has been pushed.
|
||
- **`feat/phase1`** (local only, **not pushed**) @ 42fea7d contains Tasks 1–16, all reviewed, fixed and merged:
|
||
- db layer, schema, migrations 0001–0003;
|
||
- plugin SDK;
|
||
- auth;
|
||
- works and matching;
|
||
- user layer (entries, engagements, history, lists, copies, custom fields);
|
||
- attachments and crypto;
|
||
- all six plugins (Trakt, Hardcover, MangaBaka, CSV/JSON, ListenBrainz, Last.fm);
|
||
- sync engine and Changes inbox;
|
||
- jobs, scheduler, registry and connections;
|
||
- export, restore and backups;
|
||
- the server API.
|
||
- **Last full verification on `feat/phase1`:** 520 tests pass on SQLite+Postgres, and typecheck is clean.
|
||
|
||
**Open work** (each item is a branch with a worktree under `.worktrees/`):
|
||
|
||
| Task | Branch @ head | State | Next step |
|
||
|---|---|---|---|
|
||
| 17 web shell, library, entry detail | `task/17` @ 9546fa1 | Fix round 3 done. Its re-review died on a usage limit before finishing. | Re-run the scoped re-review of 4608c97..9546fa1 (review-draft sync; see ledger). If clean, merge into feat/phase1. |
|
||
| 18 Changes, Sources, Log, Lists, Owned, Settings, About | `task/18` @ bbd04a5 | Partly done. Committed: core/server log feed, change titles, copy work titles, the plugins device flag, the library cache fix, and the Changes, Sources, Log and Lists screens. **Not done:** Owned, Settings (prefs, custom fields, tokens, invites, reload, export/import), About, the `describeChange`/`scheduleLabel` tests, the CDP check and docs. The implementer stopped on a usage limit. | Dispatch a fresh implementer with `task-18` brief + this table + "continue from bbd04a5". Then review it. It also must `git merge task/17` again after Task 17 is final. |
|
||
| 19 Docker, compose, CI, docs | `task/19` @ 0a54020 | Barely started. Only an **uncommitted** DECISIONS.md reorder exists in `.worktrees/t19`. | Dispatch a fresh implementer (brief + the Task 19 rulings in the ledger). Base it on feat/phase1 after 17/18 land, or merge them in later. The uncommitted DECISIONS edit can be kept or redone. |
|
||
|
||
After 17, 18 and 19 are merged, follow the plan's Completion section:
|
||
1. Full suite on both DBs, plus typecheck.
|
||
2. A whole-branch final review on the most capable model, triaging the deferred minors in the ledger.
|
||
3. `git rm` the plan, this handoff and `docs/plans/001-phase1-sdd/`.
|
||
4. Mark the spec `implemented`.
|
||
5. Merge to `main` via superpowers:finishing-a-development-branch.
|
||
6. Push only with owner approval.
|
||
|
||
Branches `task/4`…`task/16` are merged and can be deleted. The `.worktrees/t8, t9, t15, t16` worktrees are merged too and can be removed with `git worktree remove`.
|
||
|
||
## 3. How the work is being run (for the next controller)
|
||
|
||
- **Skill:** superpowers:subagent-driven-development, with **parallel** implementers. The owner asked for this, overriding the skill's one-at-a-time default.
|
||
- **Worktrees:** the controller creates them itself:
|
||
|
||
```bash
|
||
git worktree add .worktrees/tN -b task/N feat/phase1
|
||
cd .worktrees/tN && bun install --frozen-lockfile
|
||
```
|
||
|
||
**Do not** use the Agent tool's `isolation: "worktree"`. It branches from `main`, not `feat/phase1`.
|
||
- **Workspace scripts:**
|
||
- SDD workspace and briefs: `bash <superpowers>/skills/subagent-driven-development/scripts/{sdd-workspace,task-brief,review-package}`.
|
||
- The ignored workspace is `.superpowers/sdd/001-phase1-own-your-data/`. A copy of the ledger, rulings, fix findings and implementer reports is in `docs/plans/001-phase1-sdd/`, because `.superpowers/` is gitignored.
|
||
- **Dispatch pattern:**
|
||
- Implementers read `docs/plans/001-phase1-sdd/common-implementer.md`, their brief and any `task-N-rulings.md`.
|
||
- Reviewers read `common-reviewer.md`.
|
||
- Every fix round gets a scoped re-review.
|
||
- Merge with `git merge --no-ff task/N`, then run the full suite on both DBs.
|
||
- **The owner has interrupted merge commands twice, then replied "Approved."** Expect a permission prompt on merges into `feat/phase1`.
|
||
- **Test Postgres:** the docker container `sttracker-pg-test` on `127.0.0.1:55432` (`docker start sttracker-pg-test` if it is stopped).
|
||
|
||
```bash
|
||
TEST_POSTGRES_URL=postgres://postgres:postgres@127.0.0.1:55432/postgres bun test
|
||
```
|
||
|
||
- **Model choice that worked:**
|
||
- Opus for core, engine, server and web implementers and their reviewers.
|
||
- Sonnet for plugins and small fixes.
|
||
- Haiku only for tiny re-reviews.
|
||
|
||
## 4. Pending owner decisions (surface these, don't work around them)
|
||
|
||
1. **Agent permission files.** Task 16's implementer tried to add verified commands to `.claude/settings.json` and `.gemini/settings.json`, and the harness **denied** it as agent self-modification. Nobody has worked around this.
|
||
- The wanted change is in `docs/TODO.md`: allow `bun run typecheck`, `bun apps/server/src/main.ts` and `bun --hot apps/server/src/main.ts`.
|
||
- `docs/PERMISSIONS.md` and the AGENTS.md Commands section are waiting on the same decision.
|
||
- The owner should either make the edit, or approve an agent making it.
|
||
2. **Pushing `feat/phase1`** to Gitea, and the final merge to `main`.
|
||
|
||
## 5. Key technical decisions made during execution
|
||
|
||
The full list is in the ledger and `docs/DECISIONS.md`. The ones a newcomer would otherwise trip on:
|
||
|
||
- **Database access:**
|
||
- A single Kysely dialect over `Bun.SQL` serves both `sqlite://` and `postgres://`.
|
||
- SQLite runs on one connection behind a mutex, so **inside a transaction always use `tx`, never `db.k`**, or it deadlocks.
|
||
- IDs are UUIDv7. JSON is stored as text.
|
||
- **External-ID namespaces are type-specific** (`trakt_movie`, `tvdb_episode`, `anilist_manga`, …). Private works can't carry external IDs.
|
||
- **`field_sources`:**
|
||
- It records who owns each field. Any non-`plugin:` actor is stored as `"user"`; history keeps the full actor (`token:<name>`).
|
||
- A sync only applies a field it owns. Otherwise it files a pending `changed` or `conflict` event.
|
||
- Stale pending events are superseded.
|
||
- Plugin actors never revive deletes.
|
||
- **Hierarchy:**
|
||
- Creating a child entry also ensures the root ancestor has an entry.
|
||
- Rollups and drill-down work to depth 3, skipping untracked intermediates.
|
||
- **Lists** from sources are applied. A list becomes user-owned after a user edit.
|
||
- **Secrets:**
|
||
- Secret config is stored only in encrypted `credentials_enc` (AES-GCM, key from `STTRACKER_SECRET`).
|
||
- Rate limits are declared on the plugin manifest (`rateLimit`).
|
||
- **Jobs:**
|
||
- The queue lives in the DB, with an advisory-locked enqueue, an atomic claim, a heartbeat and a stale-reaper.
|
||
- Pre-run failures (bad secret, missing plugin) are non-retryable and visible.
|
||
- **API:**
|
||
- Cookie sessions plus app tokens.
|
||
- CSRF uses the `X-Requested-With: sttracker` header on every non-Bearer write, including login.
|
||
- JSON bodies require `content-type: application/json`.
|
||
- **Export:**
|
||
- The format is `sttracker-export/v1`: a per-user zip with JSONL files, including deleted rows, aliases and tombstones, and no secrets.
|
||
- Restore goes into an empty account only.
|
||
- Backups are daily, with 0600 files.
|
||
|
||
## 6. Not in Phase 1 (from the spec)
|
||
|
||
The following come later:
|
||
- the public page (scrapbook + river);
|
||
- Markdown vault sync;
|
||
- Steam and Jellyfin owned-library connectors;
|
||
- IGDB, TMDB and OpenLibrary lookups;
|
||
- Libre.fm;
|
||
- write-back and migration;
|
||
- the ingest endpoints;
|
||
- the Kavita, Audiobookshelf, Komga and Calibre-Web connectors;
|
||
- Backloggd CSV import;
|
||
- an MCP server;
|
||
- themes;
|
||
- an OAuth provider;
|
||
- social features.
|
||
|
||
Deferred minors from every task review are in the ledger, with "minor (deferred)" lines, and in `docs/TODO.md`.
|