DimitriGilbert / DimitriGilbert/FeedElity

Test & quality alignment plan

Open
#3 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
2
Forks
0
Avg merge
1d 2h
Merged PRs (30d)
2

Description

# Test & Quality Alignment Plan — FeedElity

_Revised 2026-09-05: CI removed by owner decision — quality gates are local commands run by the owner/agents; no GitHub Actions._

- **Project**: FeedElity (`/home/didi/workspace/FeedElity`, GH `DimitriGilbert/FeedElity`)
- **Date**: 2026-09-05
- **Generated by the fleet test-alignment initiative** (28-repo audit → synthesis → per-project plans)
- **Audit report**: [`../.test-alignment-audit/reports/FeedElity.md`](../.test-alignment-audit/reports/FeedElity.md)
- **Fleet synthesis & decisions**: [`../.test-alignment-audit/synthesis-decisions.md`](../.test-alignment-audit/synthesis-decisions.md)
- **Execution methodology**: `subagent-orchestration` — each phase is executed by an **implementer** subagent (which must run the phase's gatekeeping commands itself before reporting done), then reviewed by a **separate validator** subagent (which must actually read the code, not just run commands). Validator failures go to a **fixer** subagent with all errors at once, then re-validation; max 3 fix attempts per phase. Every dispatch carries the NO-SLOP policy (no `any`, no TODO/FIXME placeholders, no unused imports/variables, `import type` for type-only imports, external-then-local import order, no dev servers).

**Stack reality this plan is built on** (verified in-repo, not assumed): Bun-native turborepo (`bun.lock`, `packageManager: bun@1.3.0`, `workspaces.catalog` in root `package.json`), SolidJS/Hono/oRPC/Drizzle-SQLite/Electrobun, `bun:test` with zero test config, 42 test files / ~483 cases through the public oRPC surface, per-test `:memory:` libSQL databases, zero mocks, no CI, no lint, no coverage.

---

## 1. Adopted decisions (D1–D7, adapted to the Bun stack)

| Decision | Adoption here | Notes / deviations |
|---|---|---|
| **D1 — Runner & layout** | **`bun:test` stays** (synthesis explicitly keeps it for the two Bun repos). Tests stay colocated as `*.test.ts`; new tests colocate. Root headless entry already exists: `bun run test` → `turbo test`. No vitest, no jsdom. | Sanctioned Bun branch of D1, not a deviation. Environment policy resolved to `node`-equivalent: Bun's built-in runtime; web tests keep their existing `--conditions browser` flag. |
| **D2 — Coverage** | **Measured, report-only, no thresholds.** Add per-package `test:coverage` scripts (`bun test --coverage`), a turbo `test:coverage` task, and record the measured baseline per package in AGENTS.md. **Deviations (explicit):** (a) Bun's coverage reporter is experimental — synthesis D2 says Bun repos are report-only in v1 and must not be gated on numbers Bun may misreport, so the "threshold = measured − 5, ratchet +5" rule of D2 state 2 is **deferred until Bun coverage is threshold-grade**; (b) provider is Bun's native `--coverage` (text + lcov), **not** `@vitest/coverage-v8`; no HTML reporter guaranteed. |
| **D3 — Lint & format** | **ESLint 9 flat config + typescript-eslint ^8**, one root `eslint.config.ts`, `projectService` type-checked where feasible, `import/order` enabled per D3. Root `lint` script; the **dead `lint` task in `turbo.json` is deleted** (lint is repo-wide from root — one config, one source of truth; per-package lint scripts intentionally not added). **Deviations:** (a) `eslint-plugin-react-hooks` omitted — this repo is SolidJS, not React (D3 scopes that plugin to React repos); (b) **Prettier deferred** (D3 permits optional-in-v1 with explicit statement): zero formatting config exists today, a whole-repo format churn PR is out of scope; the deferral is stated in AGENTS.md (Phase 6). |
| **D4 — Quality metrics** | **jscpd report-only** (`minLines: 8, minTokens: 80`, launch-mommy config as canonical, `exitCode` untouched/report-only) + **knip recommended** (repo publishes no packages, so knip is optional-tier) as an on-demand local report script (D5). **Deviation:** **CRAP/crap-score is not adoptable here** — it consumes istanbul/v8 `coverage-final.json`, which Bun's coverage cannot emit. D4 is therefore partially adopted (jscpd + knip only). jscpd is expected to go near-silent after Phase 1 removes the 20× harness duplication — the audit named this repo the fleet's best jscpd candidate precisely for that duplication. |
| **D5 — no CI by design (owner decision 2026-09-05)** | **No GitHub Actions, no reusable-workflow caller.** The earlier "thin caller to a central Bun reusable workflow in `DimitriGilbert/.github`" plan is dropped entirely. Enforcement = the root-level headless verification entrypoint (`bun run verify`: check-types → lint → test, composed in Phase 4) run by the owner and by agents; the plan's phase validation gates are run by implementer/validator agents before any phase is declared done. |
| **D6 — Shared config** | **Deferred, conditionally.** `@FeedElity/config` keeps its `tsconfig.base.json` (already byte-similar to the fleet strict-flag set; every workspace extends it). Adoption of `@dg/config` (ESLint preset) happens when `@dg/config` is published and verified Bun-compatible — this repo touches lint in Phase 2, so it is the natural adopter afterwards. Not a v1 deliverable; never a config-churn PR for its own sake. |
| **D7 — Hooks & gates** | **The local gate commands run by the owner/agents are the only authoritative gate.** No lefthook, no hooks (a repo with no hooks and a green local `bun run verify` is compliant per D7). AGENTS.md gains the explicit statement "the headless local verification command is the quality gate, run by the owner/agents; hooks are local convenience" and the full Testing section rewrite (Phase 6). |

**Per-project delta from synthesis §4** and its reality-checked resolution:

| Synthesis delta item | Resolution |
|---|---|
| Local gate (verify), lint, coverage (bun) | Phases 4, 2, 3. |
| Extract `createTestDatabase` (20 copies) into `@FeedElity/db` test-utils | Phase 1 — early refactor, as required. |
| Delete the stale test copy inside `apps/desktop/build/` | **Reality deviation:** `apps/desktop/build/` is **untracked** (`git ls-files apps/desktop/build` → empty; `.gitignore` already covers `build`). The stale `catalog-data-migrations.test.ts` is local build-output noise only — nothing to fix in git. Phase 0 deletes the local artifact dir as hygiene and the tooling scopes in Phases 2/3/5 exclude `build/` so it can never pollute results again. |
| Untested `packages/auth` + `apps/docs` need smoke tests or explicit exclusion | Phase 5: `packages/auth` gets a real smoke test (boot better-auth against the shared `:memory:` test DB — it depends on `@FeedElity/db`, so the Phase 1 helper unblocks it); `apps/docs` gets a **documented explicit exclusion** (static marketing/docs site, zero logic-bearing source; `check-types`+build remain its gates). |

---

## 2. Phased plan

Phase ordering rationale: freeze a green baseline first (P0); kill the largest duplication before adding tools that would just measure it (P1 before P2/P3/P5); lint before gate composition so the composed gate is green on arrival (P2 before P4).

### Phase 0 — Baseline freeze & artifact hygiene (S)

**Goal:** Prove the current suite is green before anything changes, and clear local-artifact noise.

**Tasks (implementer):**
1. Run `bun install`, then `bun run check-types` and `bun run test` (turbo) from repo root; record pass/fail counts (42 files / ~483 cases expected) in the phase report.
2. Delete local build output `apps/desktop/build/` (untracked, gitignored — verified; contains a stale copy of `catalog-data-migrations.test.ts`). Do **not** touch the untracked `*.db` / `*.db.backup-*` / `*-db-*.db` files at repo root — they are data, not artifacts (see Risks).
3. Record the exact Bun version (`bun --version`) and confirm `bun test --coverage --coverage-reporter=text --coverage-reporter=lcov --coverage-dir=coverage` flags are accepted by it (needed by Phase 3); report which reporters the installed Bun supports.

**Validation gate (validator):** `bun run check-types` exit 0; `bun run test` exit 0; `apps/desktop/build` absent; report states supported coverage flags.

**Role:** implementer → validator (fix loop ≤3).

### Phase 1 — Extract the shared test-DB harness (M) — *early refactor, 3 sub-phases + phase-wide validation*

**Goal:** One canonical `createTestDatabase` + user/context factories instead of 20 drifting copies, each of which re-declares its own hand-written `schemaStatements` DDL (the `playlist-api.test.ts` copy creates a `user` table the `refresh.test.ts` copy does not — real drift already).

**Design (binding for all sub-phases):**
- Create `packages/db/src/test-utils.ts` exporting:
- `createTestDatabase(): Promise` returning `{ client, db }` (same shape all 20 copies return, to minimize call-site churn). Schema application uses the **canonical drizzle migrations** at `packages/db/src/migrations/` via `drizzle-orm/libsql/migrator` against a fresh `:memory:` libSQL client with `PRAGMA foreign_keys = ON` — this permanently eliminates DDL drift. (Exception: `packages/api/src/migration/run-migration.test.ts` tests the migrator itself and keeps its own mechanism.)
- `insertUser(...)` and `authenticatedContext(...)`: the implementer must first diff all 14 `insertUser` and 12 `authenticatedContext` copies, unify them to the superset behavior, and document the chosen contract in the helper's TSDoc.
- Add export map entry `"./test-utils"` to `packages/db/package.json` `exports` (package already uses the `"./*"` fallback; make it explicit).
- Test files import from `@FeedElity/db/test-utils`; the inline `TestDatabase` type, `schemaStatements` arrays, and factory functions are deleted from every migrated file. No behavior of any assertion changes — this is a pure extraction.

**Sub-phase 1a — helper + pilot (implementer → validator):**
- Create `packages/db/src/test-utils.ts` + export map entry.
- Migrate the `packages/db` in-memory test that hand-rolls a client, plus pilot `packages/api/src/routers/playlist-api.test.ts` (the audit's security-assertion showcase) and one migration-dir test that is *not* `run-migration.test.ts` (e.g. `user-data-export.test.ts`).
- Gate: `bun run test --filter=@FeedElity/db --filter=@FeedElity/api` green; `bun run check-types` green.

**Sub-phase 1b — routers (implementer → validator):** migrate the remaining `packages/api/src/routers/*.test.ts` (~10 files: `auth-access`, `bulk-unsubscribe-api`, `catalog-browsing`, `collection-api`, `feed-health-api`, `ingestion-api`, `playback-api`, `settings-api`, `unread-api`, `user-data-api`). Gate: same commands, full `@FeedElity/api` suite green.

**Sub-phase 1c — migration/services/repositories (implementer → validator):** migrate `packages/api/src/migration/*.test.ts` (4), `packages/api/src/services/*.test.ts` (3), `packages/api/src/repositories/repositories.test.ts` (1). Gate: same commands.

**Phase-wide validation (validator, after 1a–1c pass individually):** read the helper and a sample from each sub-phase together; verify: exactly **one** `function createTestDatabase` definition remains in the repo (`grep -rn "function createTestDatabase" --include="*.test.ts" apps packages` → only `packages/db/src/test-utils.ts` — note `run-migration.test.ts` may keep a differently-named harness; it must not define `createTestDatabase`); zero remaining inline `schemaStatements` outside `run-migration.test.ts`; no assertion was weakened (spot-check `playlist-api.test.ts` cross-user assertions survived verbatim); `bun run check-types` && `bun run test` green from root.

**Role:** per sub-phase: implementer → validator → fixer loop (≤3); then phase-wide validator.

### Phase 2 — Lint: ESLint 9 flat + typescript-eslint; kill the dead turbo lint task (M)

**Goal:** Language-appropriate lint at zero errors, enforced by the root gate afterwards; the dangling `lint` turbo task resolved.

**Tasks (implementer):**
1. `bun add -d eslint @eslint/js typescript-eslint eslint-plugin-import globals` (root; versions within the synthesis-pinned families: ESLint ^9, typescript-eslint ^8, eslint-plugin-import ^2, globals ^16).
2. Create root `eslint.config.ts`: `@eslint/js` recommended + `typescript-eslint` with `projectService: true` (`tsconfigRootDir: import.meta.dirname`) aiming at `recommendedTypeChecked`; `import/order` enabled; `globals` for browser/Solid files under `apps/web/src`. `ignores`: `**/node_modules/**`, `**/dist/**`, `**/build/**` (desktop build output), `apps/web/src/routeTree.gen.ts` (generated), `**/*.gen.ts`, `**/coverage/**`, `**/*.db`, `scripts/classify/data/**`, `scripts/db-repair/data/**`. If TS-config loading errors under the installed ESLint/Bun combo, fall back to an identical `eslint.config.mjs` and note it in the report (documented fallback, not a silent downgrade).
3. Add root `"lint": "eslint ."` script to `package.json`.
4. Delete the dead `"lint"` task from `turbo.json` (no package defines `lint`; repo-wide lint from root is the single source of truth). Record the resolution in the report.
5. Fix **every** reported lint error by fixing causes (the repo has zero lint history; expect a real cleanup pass). Never blanket-disable a rule. If type-checked volume is genuinely unmanageable in one pass, land `recommended` (non-type-checked) at zero errors and list `recommendedTypeChecked` promotion as an explicit follow-up in the phase report + AGENTS.md (Phase 6) — a documented downgrade, never `/* eslint-disable */` noise.

**Validation gate (validator):** `bun run lint` exit 0 from root; `grep -n '"lint"' turbo.json` → no task; `bun run check-types` exit 0; `bun run test` exit 0; validator reads `eslint.config.ts` and confirms the ignore globs (especially `routeTree.gen.ts` and `build/`) and that no rule is disabled without a written, scoped justification comment.

**Role:** implementer → validator (fix loop ≤3).

### Phase 3 — Coverage: measured, report-only, baseline recorded (S)

**Goal:** The ~40% test-LOC ratio becomes a measured number; no thresholds (Bun deviation, D2).

**Tasks (implementer):**
1. Add per-package scripts (flags per Phase 0 findings; keep each package's existing test invocation intact):
- `packages/api`, `packages/db`, `packages/env`, `apps/server`: `"test:coverage": "bun test --coverage --coverage-reporter=text --coverage-reporter=lcov --coverage-dir=coverage"`
- `apps/web`: `"test:coverage": "bun test --conditions browser --coverage --coverage-reporter=text --coverage-reporter=lcov --coverage-dir=coverage"`
- `apps/desktop`: `"test:coverage": "bun test src --coverage --coverage-reporter=text --coverage-reporter=lcov --coverage-dir=coverage"`
2. Root `package.json`: `"test:coverage": "turbo test:coverage"`. `turbo.json`: add task `"test:coverage": { "dependsOn": ["^build"], "outputs": ["coverage/**"] }`. Packages without the script (`apps/docs`, `packages/auth` until Phase 5, `packages/config`) are simply not matched by the task — no `passWithNoTests`-style fakery anywhere.
3. `.gitignore` already contains `coverage` — verify, no change expected.
4. Run `bun run test:coverage`; record per-package measured line/branch coverage in the phase report; this becomes the AGENTS.md baseline in Phase 6.

**Validation gate (validator):** `bun run test:coverage` exit 0; `coverage/` dirs appear and are gitignored (`git status --porcelain` stays clean); baseline numbers recorded; **no threshold configured anywhere** (`grep -rn "coverageThreshold\|fail_on_threshold" --include="bunfig.toml" --include="*.json"` apps packages → empty, or bunfig absent entirely).

**Role:** implementer → validator (fix loop ≤3).

### Phase 4 — Local gate composition + Renovate (M)

**Goal:** The 483-case suite is gated by one headless command (fleet's single biggest gap), with the dependency-hygiene one-liner.

**Tasks (implementer):**
1. Root `package.json`: add `"verify": "bun run check-types && bun run lint && bun run test"` — the headless verification entrypoint (D5). Turbo's `test` task `dependsOn: ["^build"]` is honored inside the chain, so deps are built before tests with no extra step. Document in the phase report what `verify` gates and its expected runtime, so agents and the owner can budget a full run.
2. Create root `renovate.json`: `{ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["local>DimitriGilbert"] }` (DoD item 7; the org preset carries the real config — a Renovate preset is a non-workflow artifact, unaffected by the no-CI decision).
3. Confirm no `"latest"` deps exist (`grep -rn '"latest"' apps/*/package.json packages/*/package.json` → expected empty; deps are catalog/workspace/range-pinned). Report the result.

**Validation gate (validator):** `bun run verify` exits 0 from root and every sub-command exists in `package.json` scripts; fail-fast proven once (validator introduces a failing assertion in a scratch copy and confirms `verify` exits non-zero at the failing step — never on the real tree); renovate JSON valid (`jq . renovate.json`); the implementer/validator agents record the green `verify` output in the phase report before the phase is declared done (D5); no workflow file was created anywhere.

**Role:** implementer → validator (fix loop ≤3).

### Phase 5 — Quality report (report-only) + untested-package decisions (S)

**Goal:** D4/D5 metric plumbing without gates; close the `packages/auth` / `apps/docs` hole the audit flagged.

**Tasks (implementer):**
1. `bun add -d jscpd knip` (root; jscpd ^4.2 per D4 family; knip optional-tier — repo publishes nothing).
2. Create root `jscpd.json` with the canonical launch-mommy config: `minLines: 8, minTokens: 80`, `reporters: ["html", "consoleFull"]`, output `reports/jscpd/`, **report-only (no failure threshold)**, ignores: `**/__tests__/**`, `**/*.test.ts`, `**/node_modules/**`, `**/dist/**`, `**/build/**`, `**/coverage/**`, `**/routeTree.gen.ts`, `**/*.db`. Add root script `"quality:duplication": "jscpd --config jscpd.json ."` — expected near-silent after Phase 1.
3. Optional `knip.json` (products are app-internal; exports map in `packages/*` is hand-maintained) — if included, report-only, ignoring `apps/desktop/build`.
4. `packages/auth`: add `"test": "bun test"` script + `packages/auth/src/auth.smoke.test.ts`: boots the better-auth instance from `src/index.ts` against a `createTestDatabase()` from `@FeedElity/db/test-utils` and asserts instance construction + a benign call (e.g. session lookup returns null for an unknown token). Real assertions, no mocks (repo convention).
5. `apps/docs`: **explicit documented exclusion** — no test script; exclusion rationale goes into AGENTS.md in Phase 6 (static site, zero logic; `check-types` + `build` remain its gates).
6. Add root script `"quality:report"`: runs `bun run quality:duplication` (and knip if included), writing reports into the git-ignored `reports/` directory. Run on demand by the owner or during a periodic agent pass — no workflow, no schedule, no artifacts (D5).

**Validation gate (validator):** `bun run quality:duplication` exits 0 and reports duplication; `bun run quality:report` produces the local report; `bun run test` green **including the new auth smoke test** (`bun test packages/auth` shows 1+ passing case); no metric gate configured (`jscpd.json` has no failing `exitCode`/threshold; knip report-only); no workflow file was created and nothing wires the quality scripts into `bun run verify`.

**Role:** implementer → validator (fix loop ≤3).

### Phase 6 — AGENTS.md alignment + DoD final audit (S)

**Goal:** Documentation matches reality (DoD item 5) and the full Definition of Done is verified item by item.

**Tasks (implementer):** Rewrite/extend `AGENTS.md`:
1. **Testing section**: runner (`bun:test`, colocated `*.test.ts`), commands (`bun run test`, `bun run test:coverage`, `bun run lint`, `bun run quality:duplication`), locations, the shared harness contract (`@FeedElity/db/test-utils` — new tests must use it, never paste factories), measured coverage baselines from Phase 3, explicit statement that coverage is **report-only** and thresholds are deferred until Bun's coverage tooling is threshold-grade, `packages/auth` smoke scope, `apps/docs` explicit exclusion.
2. **Gate statement** (D7): "the headless local verification command (`bun run verify`) is the quality gate, run by the owner/agents; hooks are local convenience" — no hooks in this repo by design.
3. **Deferral statements**: Prettier deferred (D3 optional-in-v1); rendered-UI/browser E2E for the Solid app remains a non-goal (P3); `recommendedTypeChecked` promotion if Phase 2 landed at `recommended`.
4. Update the stale statement `bun run test` "runs every suite through Turbo" only if it is inaccurate after the turbo `lint` task deletion; keep all build/test commands list truthful.

**Validation gate (validator):** every claim in the Testing section is executed and true (validator runs each documented command); no phantom scripts documented (cross-check `package.json` scripts against AGENTS.md line by line); final DoD checklist (§3 below) re-scored with evidence per item; `bun run check-types` && `bun run test` green from root.

**Role:** implementer → validator (fix loop ≤3).

### Effort summary

| Phase | Scope | Effort |
|---|---|---|
| 0 — Baseline & hygiene | green baseline, local artifact cleanup, Bun flag probe | **S** |
| 1 — Test-DB harness extraction | 1 helper + ~21 files migrated, 3 sub-phases + phase-wide validation | **M** |
| 2 — ESLint + dead lint task | 1 config + root script + whole-repo zero-error pass | **M** |
| 3 — Coverage report-only | 6 scripts + turbo task + baseline | **S** |
| 4 — Local gate + Renovate | `verify` entrypoint + renovate.json, green run recorded | **M** |
| 5 — Quality report + auth/docs | jscpd/knip config, auth smoke test, quality report script | **S** |
| 6 — Docs + DoD audit | AGENTS.md rewrite + final verification | **S** |

---

## 3. Alignment Definition of Done — current status per item

Fleet DoD from synthesis §3, scored against this repo today (2026-09-05):

| # | Item | Status | Evidence / plan |
|---|---|---|---|
| 1 | Root-level headless test command, documented in AGENTS.md | **MET** | `bun run test` → `turbo test`; AGENTS.md §Build/Test Commands lists it |
| 2 | Headless verification entrypoint running typecheck + test + lint in one local command; agent records green before a phase is done | **MISSING** | no root aggregate command → Phase 4 |
| 3 | Coverage measured & reported; baseline recorded; thresholds only where D2 applies; never lowered | **MISSING** | zero coverage config → Phase 3 (report-only per Bun deviation; threshold ratchet deferred) |
| 4 | Language-appropriate lint, zero errors, wired into the headless entrypoint; dead turbo `lint` task implemented or deleted | **MISSING** | no eslint/biome/prettier anywhere; `turbo.json` `lint` task dead → Phase 2 (+ Phase 4 composition) |
| 5 | AGENTS.md "Testing" section matches reality | **PARTIAL** | testing conventions are accurate and followed (fixture-backed, public interfaces, failure paths); runner/coverage/gate facts absent → Phase 6 |
| 6 | Dead scaffold test deps removed or used | **MET / N-A** | Bun repo — no unused RTL/jsdom scaffold deps exist. (The synthesis delta's "stale test copy in `apps/desktop/build/`" is **untracked local build output**, not a repo file — reality deviation, handled in Phase 0.) |
| 7 | Renovate one-liner present; no `"latest"` deps | **PARTIAL** | no `"latest"` deps observed (catalog/workspace/ranges only); no `renovate.json` → Phase 4 |
| 8 | Quality-report script (knip + jscpd; CRAP where coverage exists) runnable locally, report-only | **MISSING** | nothing configured; CRAP not adoptable under Bun (D4 deviation) → Phase 5 |
| 9 | No fake passes | **MET** | 483 real integration cases through the public oRPC surface; no `passWithNoTests`; zero mocks; audit found no tautological assertions — Phase 1 must not weaken any of them |

---

## 4. Risks & repo-specific notes

1. **Renovate preset convergence (Phase 4).** `local>DimitriGilbert` resolves against the fleet owner's org preset; if it does not exist yet, Renovate logs a warning and applies defaults — harmless, converge in a one-line change later. No CI dependency exists anymore: the gate is the local `bun run verify` (D5).
2. **Bun coverage is experimental.** Numbers may be imprecise or reporter-limited (text/lcov; HTML not guaranteed). Hence report-only, no thresholds, no ratchet — per synthesis §5 ("do not gate Bun repos on numbers Bun's tooling may misreport"). Revisit when Bun coverage stabilizes.
3. **Migrator-based test helper performance.** Phase 1 swaps per-file hand DDL for the real drizzle migrator on a fresh `:memory:` DB per test (~483 cases). If suite wall-time regresses materially (>~2×), the fallback is a **generated** canonical DDL snapshot (`drizzle-kit generate` output committed once, produced by a script, never hand-maintained) — not a return to 20 copies.
4. **First lint pass volume.** The repo has zero lint history; `recommendedTypeChecked` over ~27k src LOC may surface a large cleanup. Phase 2's documented downgrade path (land `recommended` at zero errors, promote later) exists for this — but no rule is ever blanket-disabled.
5. **ESLint TS config under Bun.** `eslint.config.ts` loading depends on the installed ESLint version; identical `.mjs` fallback is pre-authorized (documented in the phase report).
6. **Do not touch the root `*.db` / backup files.** ~6 large untracked SQLite databases and snapshots sit at repo root, gitignored. They are runtime data. Tooling (jscpd/knip/lint/coverage) must exclude `**/*.db`; nothing deletes or reads them.
7. **Desktop build output.** `apps/desktop/build/` is untracked but present with heavy contents (including the stale test copy). All tool scopes exclude `**/build/**`; Phase 0 deletes the local dir; no tool or gate ever reads it.
8. **turbo `test` depends on `^build`.** Tests require built deps (existing design). The `verify` chain builds before testing via that turbo dependency; do not "optimize" this away.
9. **Solid ≠ React.** No `eslint-plugin-react-hooks`, no `@solidjs/testing-library`, no jsdom/happy-dom — Bun's missing DOM classes are already documented in-repo (`keyboard-shortcuts.test.ts`); rendered-UI testing stays a non-goal (P3) unless the owner asks.
10. **No publishing steps.** FeedElity publishes no npm packages (desktop releases go through `scripts/publish-desktop-release.sh`, out of scope); no `release.yml` needed; knip is optional-tier accordingly.
11. **D6 (`@dg/config`) deferral** is deliberate: the repo's `tsconfig.base.json` already matches the fleet flag set, and Bun compatibility of `@dg/config` must be verified before adoption. Revisit at the next lint/config touchpoint.

*End of plan.*

Contributor guide

No contributing guide indexed for this repository

Research direction

Start at the repository root with bun install, bun run check-types, and bun run test to establish the Phase 0 baseline. Then read packages/db/src/migrations/ and the listed test files, especially packages/api/src/routers/playlist-api.test.ts, before assessing the shared test-utils extraction. Done means the phase gates pass, duplicated harnesses are removed as specified, and the full local verification commands remain green.

Written by the indexing model from the issue text.

Assessment

Tech stack
bun, eslint, typescript
Domain
build-system, testing, tooling
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.