This system was built for a single operator. This document examines what would need to change to support multiple users, collaborative workflows, and production deployment. Organized by the boundaries that would shift.
The following assumptions are baked into the current design:
- One resume —
data/resume.mdis a single file - One set of preferences — archetypes, rubric, and weights reflect one person's priorities
- One ChromaDB instance — embedded (SQLite), local, single-writer
- One decision history — verdicts are from one reviewer's perspective
- One machine — Ollama, ChromaDB, and Playwright all run locally
- No concurrent writes — no locking or conflict resolution
- JSONL audit logs — append-only, no indexing or querying beyond
grep
The embedded ChromaDB (SQLite file) cannot support concurrent writers. With a team:
- Upgrade to ChromaDB server mode — run ChromaDB as a separate service with an HTTP API
- Or replace with a managed vector store (Pinecone, Weaviate, PGVector)
- Connection string replaces
persist_dirinChromaConfig - Multi-tenancy — each user's decisions go into partitioned sub-collections or use metadata filtering. Shared collections (resume, archetypes, rubric) remain common.
The append-only JSONL files work for audit but don't support queries like "show me all team decisions this week" or "which roles did Alice approve that Bob rejected?"
- Decision store → PostgreSQL or similar with columns for
user_id,verdict,reason,timestamp,job_id - Session logs → time-series database or structured logging service (Loki, CloudWatch)
- Eval history → same database, enabling cross-user comparison
| Config | Current | Team |
|---|---|---|
settings.toml (boards, Ollama) |
Shared | Shared — infrastructure config |
role_archetypes.toml |
Single-user | Per-user or per-team profiles |
global_rubric.toml |
Single-user | Base rubric shared; per-user overrides |
| Scoring weights | Single-user | Per-user profiles |
resume.md |
Single-user | Per-user (obviously) |
base_salary |
Per-user | Per-user |
The config system would need to support profiles:
[profiles.alice]
resume_path = "data/resumes/alice.md"
archetypes_path = "config/archetypes/alice.toml"
base_salary = 220000
[profiles.bob]
resume_path = "data/resumes/bob.md"
archetypes_path = "config/archetypes/bob.toml"
base_salary = 180000ChromaDB collections would need user prefixes or metadata:
resume_alice,resume_bob— or a singleresumecollection withuser_idmetadata filteringdecisionscollection would needuser_idmetadata on every document- Negative signals and global rubric might stay shared (team values) with per-user overrides
Each user's fit_score queries their own resume collection. The scorer
needs to accept a user context:
async def score(jd_text: str, user_id: str) -> ScoreResult:
# Query user-specific resume collection
# Query shared archetype collection (or user-specific)
# Query user-specific decisions
# ...Currently, history_score reflects one person's preferences. With a team:
- Option A: Per-user history — each person's "yes" verdicts only influence their own scores. Most isolated.
- Option B: Team history — all "yes" verdicts feed one collection. Creates a team consensus signal.
- Option C: Weighted team history — weight your own verdicts higher than teammates'. Most complex but most useful.
Currently, past "no" reasons are injected into the disqualifier prompt from one person's decisions. With a team, the prompt would need to select relevant rejection reasons from the current user's history, not everyone's.
Running Ollama locally per user is wasteful. With a team:
- Shared Ollama server on a GPU box or cloud instance
- Or switch to API-based inference (OpenAI, Anthropic) with appropriate key management and cost allocation
- The
OllamaConfig.base_urlalready supports pointing to a remote server - Rate limiting becomes necessary — multiple users embedding concurrently
Playwright running on each user's machine doesn't scale:
- Centralized scraping service — one instance per board, shared results
- Job board APIs where available (Indeed, LinkedIn APIs have access tiers)
- Proxy rotation — shared IP pool to avoid per-user rate limiting
- Session cookies become per-user secrets requiring secure storage
- Centralized logging — session logs aggregate across users
- Dashboards — scoring quality metrics per user, per board, over time
- Alerting — detect when a board's adapter starts failing (selector changes)
The CLI dispatch model (run → exit) doesn't support:
- Concurrent users
- Background scheduling
- Webhook notifications
The pipeline would split into:
| Component | Current | Team |
|---|---|---|
| Search trigger | jobsearch-rag search (CLI) |
Scheduled worker or API endpoint |
| Scoring | In-process, synchronous | Queue-based worker (Celery, Temporal) |
| Review | Interactive CLI session | Web UI |
| Decision recording | CLI → direct write | API endpoint → database |
| Export | Write to local output/ |
Serve via web UI or push to Slack/email |
The adapter layer is already the cleanest boundary for team scaling:
- Adapters produce
JobListingobjects — this contract doesn't change - A centralized search service would run adapters on a schedule and store raw listings in a database
- Users would score and review from the shared listing pool
A realistic migration path for a team adoption:
- Shared Ollama server — lowest friction, change
base_urlonly - Shared ChromaDB server — move from embedded to server mode
- Per-user profiles — config system supports multiple users
- Centralized scraping — one search service, shared listing pool
- Web UI — replace CLI review with browser-based interface
- API layer — RESTful API over the pipeline for external integrations
The core abstractions hold up under team scaling:
JobListingdata contract — board-agnostic, user-agnosticScoreResultstructure — same six components, same fusion formula- Adapter ABC + Registry — board-specific logic stays encapsulated
ActionableErrorhierarchy — error design works for operators, UIs, and AI agents- Score fusion formula — weights may differ per user but the formula is universal
- Deduplication — exact + near-dedup logic works regardless of who's reviewing
- Eval metrics — precision, recall, Spearman ρ are per-user measurements that compose into team dashboards
The architecture was designed around data contracts and clear boundaries. Most team-scaling changes are at the edges (persistence, config, transport), not in the scoring core.