11 KiB
11 KiB
scholarr Agent Handbook
This file is the consolidated source of truth for project scope, constraints, scrape contract, and UI implementation flow.
1. Product Objective
- Build a self-hosted scholar tracking system ("scholarr") with reliable, low-cost scraping and clear, actionable UI.
- Keep the MVP scope intentionally small.
- Prioritize reliability and ease of use over advanced/fancy processing.
2. Locked Constraints and Preferences
- Multi-user system with strict tenant isolation.
- Users are admin-created only.
- Users can change their own password.
- Admin features are shown only to admin users.
- Same-origin cookie session model with CSRF protection.
- Container-first development workflow (
docker compose,uv). - Test while developing; avoid shipping untested flows.
- Keep backend modular and DRY.
- UI is being rebuilt from scratch against API contracts.
3. Current Backend Architecture
- Runtime: Python + FastAPI.
- DB: PostgreSQL + SQLAlchemy + Alembic migrations.
- Scheduler: background scheduler + continuation queue processing.
- Auth:
- Argon2 password hashing.
- Session cookie (
HttpOnly,SameSite=Lax, secure flag configurable). - CSRF required for unsafe methods via
X-CSRF-Token.
- Logging:
- structured request logging with request IDs.
- redaction controls for sensitive fields.
4. Scraping Contract (Probe-Based)
Status: sufficient for MVP implementation with graceful degradation.
Target endpoint:
https://scholar.google.com/citations?hl=en&user=<scholar_id>
Primary selectors:
- Row:
tr.gsc_a_tr - Title/link:
a.gsc_a_at - Citation count:
a.gsc_a_ac - Year:
span.gsc_a_h(fallback regex on year cell text) - Metadata lines: first/second
div.gs_gray-> authors/venue - Cluster ID: from
citation_for_view=<user>:<cluster_id>in title URL
Required parser states:
okno_resultsblocked_or_captchalayout_changednetwork_error
Required page flags:
has_show_more_buttonarticles_range
Quality assumptions from probe:
- title/cluster_id/citation/authors coverage observed near 100%
- year and venue may be missing and must remain nullable
Guardrails:
- Never crash the full run when one scholar fails.
- Persist structured failure/debug information for diagnosis.
- Handle inaccessible/redirected IDs as states, not exceptions.
5. Current API Scope
Base path: /api/v1
Envelope model:
- success:
{"data": ..., "meta": {"request_id": "..."}} - error:
{"error": {"code": "...", "message": "...", "details": ...}, "meta": {"request_id": "..."}}
Auth/session:
GET /api/v1/auth/csrfPOST /api/v1/auth/loginGET /api/v1/auth/mePOST /api/v1/auth/change-passwordPOST /api/v1/auth/logout
Admin users:
GET /api/v1/admin/usersPOST /api/v1/admin/usersPATCH /api/v1/admin/users/{id}/activePOST /api/v1/admin/users/{id}/reset-password
Scholars:
GET /api/v1/scholarsPOST /api/v1/scholarsGET /api/v1/scholars/searchPATCH /api/v1/scholars/{id}/togglePUT /api/v1/scholars/{id}/image/urlPOST /api/v1/scholars/{id}/image/uploadGET /api/v1/scholars/{id}/image/uploadDELETE /api/v1/scholars/{id}/imageDELETE /api/v1/scholars/{id}
Settings:
GET /api/v1/settingsPUT /api/v1/settings
Runs/diagnostics/queue:
GET /api/v1/runsGET /api/v1/runs/{id}POST /api/v1/runs/manual(Idempotency-Keysupported)GET /api/v1/runs/queue/itemsPOST /api/v1/runs/queue/{id}/retryPOST /api/v1/runs/queue/{id}/dropDELETE /api/v1/runs/queue/{id}
Publications:
GET /api/v1/publicationsPOST /api/v1/publications/mark-readPOST /api/v1/publications/mark-all-read
Ops:
GET /healthz
5.1 Runtime Semantics (Locked)
Manual run idempotency:
Idempotency-Keyis optional onPOST /api/v1/runs/manual.- Idempotency scope is
(user_id, idempotency_key). - Replaying the same key for the same user returns the same run payload after completion.
- If the keyed run is still in progress, respond with
409 run_in_progressand includerun_id. - Enforcement is database-backed, not best-effort in-memory logic.
- Frontend triggers generate keys internally; key input is not user-facing.
Continuation queue attempt policy:
attempt_countis a failed-attempt budget, not a total execution counter.- Increment attempts only when a continuation execution fails to make progress.
- Reset attempts to
0after successful progress (success or resumable partial progress). - Drop queue items only after failed-attempt threshold is reached.
Scholar no-change skip policy:
- Persist first-page fingerprint snapshots per scholar.
- If the first-page fingerprint is unchanged for a normal run (
cstart=0), skip deep pagination/upsert work. - Record run scholar result
state_reasonasno_change_initial_page_signature.
Scholar create naming policy:
- Do not accept custom display names during scholar create.
- Hydrate names from scraped profile metadata.
Scholar name-search safety policy:
- Treat name search as best-effort only.
- Apply single-flight execution, pacing/jitter, cache, and cooldown circuit breaker after repeated blocked responses.
- Prefer Scholar URL/ID adds for reliable ingestion onboarding.
6. Required UI Behavior (MVP)
Core UX entities:
- scholars
- publications (
allandnew) - runs + run diagnostics
- queue visibility/actions
- settings/account
- admin users (role-gated)
Critical semantics:
- Keep
is_new_in_latest_runseparate fromis_read. - First-time ingestion should not imply user read-state.
- Surface partial runs and warnings clearly.
- Show loading, empty, and error states explicitly.
- Support both light and dark modes with consistent status semantics.
7. UI Information Architecture
Public:
- Login
Authenticated:
- Dashboard
- Scholars
- Publications
- Settings
- Admin Users (admin only)
8. End-to-End UI Flow Plan
8.1 Session bootstrap
- On load call
GET /api/v1/auth/csrf. - Call
GET /api/v1/auth/me. - Route to login or dashboard based on auth state.
8.2 Login/logout
- Login form submits
POST /api/v1/auth/loginwithX-CSRF-Token. - Store CSRF token from response for future unsafe requests.
- Logout calls
POST /api/v1/auth/logout.
8.3 Dashboard
- Fetch latest publications summary from
GET /api/v1/publications?mode=new&limit=20. - Fetch recent runs from
GET /api/v1/runs?limit=5. - Fetch queue status from
GET /api/v1/runs/queue/items?limit=200(aggregate in UI). - Show:
- new publications count (
new_count) - total tracked publications (
total_count) - latest run status/started time
- queue badges (
queued,retrying,dropped)
- new publications count (
- Admin users additionally see a diagnostics shortcut block to full runs/queue page.
- Every dashboard error panel must display request-id for support.
8.4 Scholars
- List via
GET /api/v1/scholars. - Add/toggle/delete actions mapped to scholar endpoints.
- Search by name via
GET /api/v1/scholars/searchwith candidate add actions. - Profile image controls:
- scraped image fallback from Scholar metadata
- custom URL override
- image upload + reset to scraped/default
- Per-row quick links:
- all publications filtered by scholar
- new publications filtered by scholar
8.5 Publications
- Default view mode is
new(GET /api/v1/publications?mode=new). - Support mode toggle:
new: scoped to latest completed runall: full history
- Support scholar filter via
scholar_profile_idquery param. - Render both badges independently:
is_new_in_latest_runis_read
- "Mark all read" action calls
POST /api/v1/publications/mark-all-readwith CSRF. - Show explicit empty states:
- no publications tracked
- no new publications in latest run
- filtered scholar has no items
- Surface run recency context when mode is
new(latest completed run semantics).
8.6 Runs and diagnostics (admin)
- List runs from
GET /api/v1/runs. - Run details from
GET /api/v1/runs/{id}:- scholar result state/reason
- warnings
- page logs + attempt logs
- debug metadata
- Queue operations from queue endpoints (
retry,drop,clear).
8.7 Settings + account
- User ingest settings from
/api/v1/settings. - Password change via
/api/v1/auth/change-password.
8.8 Admin user management
- Show section only when
current_user.is_admin=true. - Use admin user endpoints for create/activate/deactivate/reset password.
9. Development Method
- API-contract-first frontend.
- Vertical slices (complete flow before moving on).
- Shared API client module:
- always send credentials
- inject CSRF header for unsafe methods
- normalize envelope parsing and error handling
- Keep frontend modules separate (
auth,scholars,publications,runs,settings,admin). - Write tests while implementing each slice.
10. Definition of Done (UI Readiness)
- All main flows above implemented and mapped to existing API endpoints.
- Role-gated admin experience enforced in UI.
- Distinct new-vs-read publication semantics visible.
- Run/queue diagnostics visible and actionable.
- Reliable loading/empty/error states with request-id surfaced for support.
- No dependence on removed server-rendered UI layer.
11. Open Items (Known Future Work)
- API contract freeze/changelog discipline for external UI clients.
- Additional API-first smoke coverage for end-to-end UI critical flows.
- Optional background image enrichment/refresh jobs for already-tracked scholars.
12. UI Rebuild Program Artifacts
- Phase 0 through Phase 3 decisions are consolidated into this handbook and
README.md. - Local scratch notes, probes, and temporary planning artifacts are intentionally not tracked in git.
13. Frontend Scaffold Status (Implemented)
- Frontend scaffold path:
frontend/ - Implemented core:
- App shell + routing + auth/role route guards
- Shared API client with envelope parsing, credentials, and CSRF header injection
- Pinia stores for auth, theme, and global UI errors
- Tailwind-first design foundation with light/dark themes and pre-paint theme initialization
- Page shells for login, dashboard, scholars, publications, runs, run detail, settings, and admin users
- Initial feature modules under
frontend/src/features/*mapped to current API endpoints
- Deployment boundary:
- No in-repo reverse-proxy implementation
- Frontend edge/proxy routing is managed externally (for example, Caddy)
14. Frontend Quality Gates (Phase 3 Implemented)
- CI now includes frontend quality gates in
.github/workflows/ci.yml:- API contract drift check (
scripts/check_frontend_api_contract.py) - frontend typecheck (
npm run typecheck) - frontend unit tests (
npm run test:run) - frontend build (
npm run build)
- API contract drift check (
- Frontend test harness:
vitestconfigured infrontend/vitest.config.ts- baseline tests in
frontend/src/lib/api/envelope.test.tsandfrontend/src/lib/api/errors.test.ts