* feat(PBI-76): one-shot localStorage→user-settings migration helper Reads all legacy keys (sprint_pb_*, pbi_*, story_sort, debug-mode, and dynamic *_filter_kind/*_filter_status for jobs columns) and returns a typed UserSettings patch plus the keys to clear. Idempotent via scrum4me:settings_migrated=v1 marker. Skips invalid values silently so existing corrupt entries do not block migration. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(PBI-76): bridge runs one-shot localStorage migration After hydrate, scans legacy localStorage keys via buildMigrationPatch and, if any data is found, pushes one bulk patch to the server, applies it locally, then removes the legacy keys. Demo accounts skip the migration entirely. Cancellable on unmount to avoid setState on unmounted component. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(PBI-76): migrate sprint-backlog to user-settings store Replaces six useState+useEffect+localStorage flows with selectors from useUserSettingsStore. Defaults are applied at the selector level (filterStatus 'OPEN', sort 'code', etc) so the component matches its previous behaviour. The collapsed Set is derived from the persisted array, falling back to auto-collapse-DONE when no preference exists yet. setPref calls are fire-and-forget — the optimistic flow handles the local state update. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(PBI-76): migrate pbi-list to user-settings store Same pattern as sprint-backlog: replaces local useState + localStorage hydration/persist with selectors from useUserSettingsStore. filterPopoverOpen blijft lokaal — die was nooit gepersisteerd in pbi-list. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(PBI-76): migrate story-panel sort to user-settings store Single pref (sortMode) — replaces sync localStorage useState initializer with a selector. Default 'priority' applied at the read site. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(PBI-76): migrate jobs-column to user-settings store Per-instance filter state (kinds + statuses) now lives under views.jobsColumns[storageKeyPrefix] in user-settings. Removes the local CSV-encoding helpers — store keeps arrays natively. A single persist() call writes both fields together so the two arrays cannot drift in optimistic mid-flight updates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(PBI-76): migrate debug-mode to user-settings store DebugToggle reads debugMode from user-settings.devTools and toggles via setPref. Removes the standalone stores/debug-store.ts (no consumers left). Body classlist update only fires after the store is hydrated to avoid a flash on initial paint. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(PBI-76): remove unused readLocalStoragePref helper No consumers left after migrating sprint-backlog, pbi-list, story-panel, jobs-column, and debug-store to user-settings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(PBI-76): mock user-settings action in backlog integration test PbiList now imports the user-settings store, which transitively loads actions/user-settings.ts → lib/prisma. The vitest jsdom environment has no DATABASE_URL, so we add a mock alongside the existing action mocks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(docs): allow balanced parens in markdown link URLs Previously the link-checker regex stopped at the first ')', breaking on Next.js route-group paths like `app/(app)/...`. The new regex matches one level of balanced parens inside the URL. Caught by CI on PR #188 — pre-existing breakage from PBI-78 plan doc that was already merged on main. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| build-manual.mjs | ||
| check-doc-links.mjs | ||
| create-admin.ts | ||
| generate-docs-index.mjs | ||
| insert-milestone.ts | ||
| README.md | ||
| realtime-mutate.ts | ||
| sync-model-prices.ts | ||
| test-api.sh | ||
Scripts
sync-model-prices.ts
Wekelijks handmatig draaibaar script dat de tabel model_prices synchroniseert. Haalt de actuele Claude 4.x modellijst op via GET /v1/models (Anthropic API) en upsert de prijzen vanuit een hardcoded PRICE_TABLE in het script. Anthropic biedt geen prijs-API; bij elke prijswijziging update je de tabel in scripts/sync-model-prices.ts.
Prerequisites
| What | How |
|---|---|
ANTHROPIC_API_KEY in .env.local |
Genereer op console.anthropic.com → API Keys. Free Evaluation tier is voldoende — /v1/models is een gratis metadata-call. |
DATABASE_URL in .env.local |
Standaard Scrum4Me-setup. |
Gebruik
# Eerst droog draaien — toont wat er zou gebeuren, schrijft niets
npm run db:sync-model-prices -- --dry-run
# Echt synchroniseren
npm run db:sync-model-prices
Output
Fetching /v1/models from Anthropic API...
→ 12 models received, 4 match Claude 4.x filter
Syncing prices:
✓ claude-opus-4-7 (unchanged)
✓ claude-sonnet-4-6 (unchanged)
✓ claude-haiku-4-5-20251001 (unchanged)
⚠ claude-sonnet-4-9 (geen prijs in PRICE_TABLE — ...)
Result: 0 created, 0 updated, 3 unchanged, 1 skipped
Bij een nieuw model (⚠ skipped)
- Open de Anthropic pricing-pagina.
- Voeg het model toe aan
PRICE_TABLEinscripts/sync-model-prices.ts:'claude-sonnet-4-9': { input: 3.0, output: 15.0 }, - Draai het script opnieuw.
Edge cases
- API geeft 401: controleer
ANTHROPIC_API_KEY. - API geeft 5xx: script doet 1× retry met 2s delay, daarna falen.
- Model in DB maar niet meer in API: wordt niet verwijderd — alleen gelogd, zodat oude
claude_jobsrijen kostberekening blijven hebben.
test-api.sh
Runs curl-based tests against all 7 Scrum4Me API endpoints. Covers happy paths, auth (401), demo-block (403), and input validation (400).
Prerequisites
| What | How |
|---|---|
| Database seeded | npx prisma db seed |
| Dev server running | npm run dev |
curl installed |
curl --version |
Step 1 — Get a token for lars
- Open
http://localhost:3000in your browser - Log in as
lars/scrum4me123 - Go to Settings → API Tokens
- Click New token, give it any label
- Copy the token — it is shown only once
Open scripts/test-api.sh and paste it into TOKEN="".
Step 2 — Find your IDs
You need four IDs. The easiest way is to use the API itself:
# 1. Get PRODUCT_ID — run this after setting TOKEN
curl -s -H "Authorization: Bearer <TOKEN>" http://localhost:3000/api/products | grep -o '"id":"[^"]*"' | head -1
# 2. SPRINT_ID — look in the database or the UI (Sprint → copy ID from the URL)
# 3. STORY_ID — open a story in the Sprint Board, copy the ID from the URL or the activity log
# 4. TASK_ID — open a task, copy its ID
Or query the database directly:
# With psql / Neon CLI:
SELECT id FROM "Sprint" WHERE status = 'ACTIVE' LIMIT 1;
SELECT id FROM "Story" WHERE status = 'IN_SPRINT' LIMIT 1;
SELECT id FROM "Task" WHERE story_id = '<story-id>' LIMIT 1;
Paste the four values into the variables at the top of test-api.sh.
Step 3 — (Optional) Get a demo token for 403 tests
The 403 demo-block tests are skipped unless you set DEMO_TOKEN.
- Log out, log in as
demo/demo1234 - Go to Settings → API Tokens, create a token
- Paste it into
DEMO_TOKEN=""intest-api.sh
Step 4 — Run
bash scripts/test-api.sh
Expected output when all IDs are set and a demo token is provided:
============================================================
Scrum4Me API Test Suite
Base URL : http://localhost:3000
Token : scrum4me... (lars)
Demo : demo1234... (403 tests active)
============================================================
── GET /api/products ──────────────────────────────────────────────
PASS TC-P-09 happy path (lars)
PASS TC-P-01 no token → 401
PASS TC-P-02 invalid token → 401
── GET /api/products/:id/next-story ───────────────────────────────
PASS TC-NS-08 happy path (lars, 200 or 404 both valid)
PASS TC-NS-01 no token → 401
...
============================================================
Results: 30 passed, 0 failed
============================================================
Notes
- product_id is required for
POST /api/todos. The Zod schema enforcesz.string().min(1). Sending a todo without a product_id returns 400. - TC-NS-08 accepts both 200 and 404. After a fresh seed lars has no active sprint, so 404 is the expected response unless you create a sprint and add stories to it.
- Reorder test (TC-RO-10) uses a single task ID. A reorder with one element is valid (sort_order is updated to 1).
- The todos happy-path test appends
$(date +%s)to the title to avoid duplicate-title issues on repeated runs.