AI Career Pulse is a personal, single-user AI/ML job intelligence instrument. It ingests AI/ML job postings from the IndianAPI jobs endpoint, enriches each posting with structured decisions from TypeSafe Jev, ranks every job against the owner's own profile with a transparent 0–100 fit score, exposes skill gaps versus observed market demand with a grounded learning plan, generates JD-specific interview preparation, tracks applications through a funnel, and renders a stock-style market trends dashboard that is explicitly labeled with its source limits.
The product is built in phases. After each phase the builder runs tests, summarizes results, lists open issues, and stops for the owner's review. Unknowns and assumptions are logged in NOTES.md and raised as questions; endpoints, parameters, fields, and data are never invented.
The audience is a single technical power user — an AI/ML candidate who reads tables, scores, and charts all day — operating the tool for their own job search. The interface is a warm graphite instrument panel, not a marketing surface.
AI Career Pulse runs in personal mode by default: one user, APScheduler for scheduling, PostgreSQL 16 with pgvector for storage and embeddings. There is no Celery, no Redis, and no SSE. Interfaces are kept clean so that a later product mode (auth, Celery, SSE) is additive rather than a rewrite.
Current actors:
NOTES.md; and reviewing each phase's test results, summary, and open issues.External and system actors (not personas): the IndianAPI jobs service (job data), TypeSafe Jev (decisions only, generates no text), the configured LLM provider (text only), prep-resource providers (YouTube Data API, Dev.to, HN Algolia, arXiv, GitHub search, RSS), the email or Telegram delivery channel for the daily digest, and the internal APScheduler-driven pipeline.
Accepted behavior spans five goals in priority order: (1) rank AI/ML jobs by fit to the owner's profile with a transparent score; (2) show skill gaps versus demand plus a learning plan; (3) JD-specific interview prep; (4) track applications; (5) a stock-style market trends dashboard labeled with source limits.
Narrow exclusions and boundaries:
Delivery ownership. AI Career Pulse is a first-party application with its own backend (FastAPI, /api/v1) and its own Next.js frontend. All job ranking, fit scoring, gap analysis, prep ranking, application tracking, and market metrics are computed and rendered by the application itself. Job data is fetched from the IndianAPI jobs endpoint under the owner's key; structured decisions come from TypeSafe Jev; free text (learning plans, interview questions) comes from the configured LLM provider. Prep resources are discovered through the listed providers and stored as metadata plus a short summary written in our own words — never as full copyrighted text.
Access ownership. The application owns identity for its own durable state. Because the profile, fit history, applications, review labels, and operator functions must remain bound to the correct participant and be resumable, the application provides self-service enrollment and returning verification. The public entry surface (/) is anonymously reachable and explains the product before any protected work; the protected destinations (/profile, /jobs, /jobs/[id], /growth, /applications, /market, /skills/[name], /prep, /admin) require an established identity. Operator functions (review queue, health, runs, API usage, phase probe reporting) require operator authorization. Application identity and session continuity do not by themselves establish differentiated permissions beyond the operator boundary described here.
Current vs future boundary. Current scope is personal mode: single user, APScheduler, PostgreSQL, no Celery/Redis/SSE. A later product mode (auth, Celery, SSE) is explicitly a future horizon and is not part of current pages or acceptance. The daily digest is delivered through an external email or Telegram channel; delivery configuration is required for that capability, and delivery itself remains provider/external-channel owned.
Source limits. Market trends reflect only the IndianAPI source. Volume swings greater than 50% are flagged as "source anomaly" and excluded from deltas. Charts show "Insufficient history" until seven days of data exist. A Jev model change is marked on the chart.
Prep resources are collected from the following content sources. For each resource the application stores title, URL, date, metadata, and a short summary written in our own words; full copyrighted text is never stored.
YOUTUBE_API_KEY.GITHUB_TOKEN.Job content is sourced from the IndianAPI jobs endpoint with the documented response fields: id, title, company, about_company, job_description, job_title, job_type, location, experience, role_and_responsibility, education_and_skills, apply_link, posted_date (ISO 8601). There is no salary field.
/./.skill_match, experience_match, domain_match, growth_potential, red_flags.q; sort by fit or date. Each row shows company, role, city, posted date, source, apply link, and the fit numeral right-aligned in a fixed-width column with a 3px probability bar beneath it./growth; read the source-limits strip.0.5*cosine + 0.5*skill overlap and the Jev-reranked top 10 per job, with rerank scores (not/somewhat/very useful).Each requirement is a distinct story point with provenance, lifecycle facts, and observable acceptance.
FR-1 — Phased build with review stops (explicit) As the Operator / Reviewer, I should have the build proceed in phases (P0 probes; P1 skeleton, DB, configs; P2 IndianAPI connector, dedup, scheduler; P3 Jev enrichment, gating, review queue; P4 fit + alerts; P5 prep + rerank + questions; P6 gap + plans + tracker; P7 market metrics; P8 frontend; P9 eval, CI, README), so that after each phase tests run, results are summarized, open issues are listed, and the build stops for my review.
FR-2 — No invented endpoints, params, fields, or data (explicit)
As the Operator / Reviewer, I should have unknowns and assumptions logged in NOTES.md and raised as questions, so that no endpoint, parameter, field, or data value is invented.
NOTES.md and a question. Failure/recovery: unresolved unknowns block the affected work rather than being guessed. Continuation: work resumes after an answer.FR-3 — Rank AI/ML jobs by fit (explicit) As the Job Seeker, I should see AI/ML jobs ranked by fit to my profile with a transparent 0–100 score, so that I can prioritize the best-matched roles.
FR-4 — Skill gaps versus demand plus learning plan (explicit) As the Job Seeker, I should see my skill gaps versus observed demand plus a learning plan, so that I can close the highest-value gaps.
FR-5 — JD-specific interview prep (explicit) As the Job Seeker, I should get JD-specific interview preparation, so that I can prepare for a specific role.
FR-6 — Track applications (explicit) As the Job Seeker, I should track applications through saved, applied, interview, offer, rejected, and withdrawn with dates, notes, and next action, so that I can manage my pipeline.
FR-7 — Market trends dashboard (explicit) As the Job Seeker, I should see a stock-style market trends dashboard labeled with source limits, so that I can read market direction without over-trusting the source.
FR-8 — Target roles (explicit) As the Job Seeker, I should have jobs classified into AI Engineer, AI/ML Engineer, ML Engineer, LLM/GenAI Engineer, Data Scientist (ML), MLOps, Computer Vision, and NLP Engineer, so that ranking matches my target roles.
roles.yaml plus other. Failure/recovery: low confidence yields NULL and needs_review. Continuation: re-enrichment on model or question-set change.FR-9 — Personal mode (explicit) As the Operator / Reviewer, I should run in personal mode (single user, APScheduler, PostgreSQL, no Celery/Redis/SSE) with clean interfaces, so that a later product mode (auth, Celery, SSE) is additive.
FR-10 — Key handling (explicit)
As the Operator / Reviewer, I should keep keys in backend .env only — never in frontend, logs, fixtures, or git — mask them in logs, and ship .env.example, so that credentials stay safe.
INDIANAPI_JOBS_KEY, TYPESAFE_API_KEY, LLM_API_KEY with LLM_PROVIDER=openai|anthropic, optional YOUTUBE_API_KEY, GITHUB_TOKEN, and alert credentials..env.example present. Failure/recovery: missing key skips the service with a log. Continuation: service resumes when the key is provided.FR-11 — IndianAPI job data (explicit)
As the Job Seeker, I should have job data fetched from GET https://jobs.indianapi.in/jobs with header X-Api-Key, so that postings are current and sourced.
limit, sent as a STRING (e.g. "50"); an int gives 422. Unconfirmed: title, location, company, experience, job_type, pagination. Response array fields: id, title, company, about_company, job_description, job_title, job_type, location, experience, role_and_responsibility, education_and_skills, apply_link, posted_date (ISO 8601). No salary field: show "Not available from source", never estimate. Rate limits unknown: limiter default 1 req/2s, INDIANAPI_DAILY_CAP, retry 429/5xx max 3 with backoff+jitter, honor Retry-After.FR-12 — TypeSafe Jev decisions (explicit)
As the Job Seeker, I should have structured decisions produced by TypeSafe Jev at POST https://api.typesafe.ai/v1/systemone with Bearer auth, so that enrichment and fit use typed decisions rather than free text.
typesafe-sdk (AsyncTypeSafeClient, Choice/Score/Noul, RetryPolicy). Body {state, model, questions}. choice → choice + probabilities + confidence; score → score + probabilities + confidence; noul → probability 0–1. Pin JEV_MODEL to an exact id via SDK list-models (not jev-latest). Store model version per record. First-install skill: claude plugin marketplace add typesafe-ai/skills && claude plugin install typesafe@typesafe-ai. Read docs.typesafe.ai primitives, confidence, patterns (fan-out, confidence routing, composite scoring), cookbooks (rerank, entity alignment, pre-parsed extraction), and jev-1.13 jaggedness; summarize limits in NOTES.md.FR-13 — LLM text (explicit) As the Job Seeker, I should have text generated by a provider-agnostic LLM at temperature 0.2, JSON-schema-validated and cached, so that plans and questions are consistent and cheap to regenerate.
FR-14 — Prep resources (explicit) As the Job Seeker, I should have prep resources collected from YouTube Data API, Dev.to, HN Algolia, arXiv, GitHub search, and RSS, storing title, URL, date, metadata, and a short summary in our own words, so that prep is grounded and legally safe.
FR-15 — Hard rules (explicit) As the Operator / Reviewer, I should have the hard rules enforced: no scraping any website (no LinkedIn/Indeed/Naukri/Glassdoor); missing field = NULL; no fake data outside fixtures; every job shows source + apply_link; low-confidence decisions → "needs review", never silently accepted; market pages show the banner "Source: IndianAPI only; trends reflect this source, not the full market."
FR-16 — Stack (explicit) As the Operator / Reviewer, I should have the specified stack used: Python 3.11, FastAPI, SQLAlchemy 2, Alembic, httpx, Pydantic v2, APScheduler, PostgreSQL 16 + pgvector, rapidfuzz, typesafe-sdk; Next.js 14, TypeScript, Tailwind, TanStack Query, lightweight-charts, Recharts; Docker Compose, pytest, ruff, mypy, GitHub Actions.
FR-17 — Config (explicit)
As the Operator / Reviewer, I should have config/*.yaml hold profile (skills {name, level 1-5}, years, domains, cities, remote pref, target roles, min seniority, deal-breakers, resume_text), roles (canonical + description + synonyms + regex), skills (id, name, description), locations (aliases, e.g. Bengaluru → Bangalore), thresholds, fit_weights, schedule, and sources, so that behavior is configurable without code changes.
FR-18 — Phase 0 probes (explicit)
As the Operator / Reviewer, I should have probe_indianapi.py run limit="5", then test each unconfirmed param and pagination, recording status, count, whether results truly filtered, posted_date freshness, id stability, and max limit, saving to tests/fixtures/indianapi/; and probe_jev.py run 10 fixture JDs through the enrichment questions, measuring latency, tokens, confidence spread, batched vs split calls, and state-length limit, saving to tests/fixtures/jev/; with NOTES.md recording results, query plan, and batch size.
NOTES.md entries. Failure/recovery: probe failures are reported, not guessed. Continuation: stop and report.FR-19 — Pipeline (explicit) As the Job Seeker, I should have the pipeline run fetch → normalize → loose regex pre-filter → dedup → Jev enrich → gate → upsert → embed → fit → metrics → alerts, so that jobs flow from source to ranked, tracked state.
source_job_id=id; title=job_title or title; keep JD sections separate; location → city/state/country; posted_at=parse(posted_date); raw JSON in jobs.raw. Dedup: sha256(norm company+title+city); rapidfuzz title ≥85, same company, 30 days → Jev same-posting score 0/1/2: 2 merge (keep earliest posted_at, all links), 1 review. Unseen in 3 full syncs → inactive.FR-20 — Jev enrichment (explicit) As the Job Seeker, I should have each job enriched with batched Jev questions using state = title, company, location, experience, job_type, JD sections (truncated per Phase 0), so that structured attributes are attached.
is_ai_role (noul) — primary work is AI/ML/DS/LLM/CV/NLP/MLOps engineering; role (choice) — roles.yaml + other; seniority (score) — fresher, junior 0-2y, mid 2-5y, senior 5-8y, lead 8y+; work_mode (choice) — onsite|hybrid|remote|unspecified; skill_<id> (noul each, chunked) — job requires/prefers <skill>; experience (choice) — regex-extracted spans + none, code converts to min/max; domain (choice) — fintech, real estate, hospitality, healthcare, e-commerce, HR tech, document AI, manufacturing, SaaS, other.is_ai_role ≥0.7 keep, 0.4–0.7 review, <0.4 drop (logged). Skill ≥0.6 attach with prob. Low confidence → NULL + needs_review (or LLM fallback if enabled). Cache by sha256(state+question_set_version+model). API failure → status failed, retry next run, never block ingestion.FR-21 — Fit scoring (explicit)
As the Job Seeker, I should have one Jev call per job with state = JD + profile summary producing skill_match score 0–3, experience_match score 0–2 (below/match/above), domain_match noul, growth_potential score 0–2 (scope beyond my level), and red_flags noul (unrealistic/vague/mislabeled); with code applying location/remote prefs, deal-breakers (hard filter), and min seniority; fit 0–100 = weighted sum (fit_weights); components and confidence stored; UI shows "why this score"; recompute on profile change.
FR-22 — Skill gap (explicit)
As the Job Seeker, I should have per-skill 30/90-day trend, senior-vs-mid share, share in my top-fit jobs, and my level; gap = high on these AND my level ≤2; /growth shows top 5 gaps, ranked resources, and a 4-week plan (LLM, grounded only in matched resources, cite URLs).
FR-23 — Prep per job (explicit)
As the Job Seeker, I should have a shortlist of top 30 by 0.5*cosine + 0.5*skill overlap, a Jev rerank score per (JD, resource) pair (not/somewhat/very useful), and the top 10 kept; POST /jobs/{id}/interview-questions uses the LLM to produce 15 questions (technical, ML system design, behavioral), tagged with my gap skills, cached.
FR-24 — Tracker (explicit) As the Job Seeker, I should have statuses saved, applied, interview, offer, rejected, withdrawn with dates, notes, and next action, plus a funnel chart.
FR-25 — Alerts (explicit)
As the Job Seeker, I should receive a daily digest (email|telegram) of new jobs with fit ≥ ALERT_MIN_FIT plus the top 3 rising gap skills.
FR-26 — Market metrics (explicit) As the Job Seeker, I should see per skill/role/city: active, new today, delta% vs yesterday and 7-day average; AI Job Index = active AI jobs, base 100 day 1; volume swing >50% → "source anomaly", excluded from deltas; Jev model change → chart marker; "Insufficient history" until 7 days.
FR-27 — Schedule (explicit)
As the Operator / Reviewer, I should have the schedule run in Asia/Kolkata: poll every 2h 08:00–22:00; full sync 02:00; prep 03:00; metrics+fit+gap 04:00 and after runs; digest 08:30; with ingestion_runs and api_usage (service, calls, tokens) logged.
FR-28 — Data model (explicit) As the Operator / Reviewer, I should have the specified tables: companies, jobs (role, seniority, work_mode, domain, ai_role_prob, exp_min/max, confs, needs_review, model_version, raw, embedding, is_active), job_sources, skills, job_skills(prob), job_fit(components JSONB), skill_gap_daily, prep_resources, resource_skills, job_prep_rank, interview_question_sets, applications, review_queue, daily_metrics, ingestion_runs, api_usage, decision_cache, alerts_sent; unique (source, source_job_id); indexes on posted_at, role, city, is_active, dedup_hash, fit; pgvector.
FR-29 — API (explicit)
As the Job Seeker, I should have /api/v1 endpoints: GET /jobs (filters role, skill, city, work_mode, seniority, min_fit, posted_within_days, q; sort fit|date), GET /jobs/{id}, GET /jobs/{id}/prep, POST /jobs/{id}/interview-questions, GET/PUT /profile, GET /growth/gaps, GET /growth/plan/{skill}, GET/POST/PATCH /applications, GET /market/{index,ticker,skills,locations}, GET /prep, GET /admin/{health,runs,api-usage,review-queue}, POST /admin/review/{id} (saved as labels).
FR-30 — Frontend (explicit)
As the Job Seeker, I should have the routes / (my top-fit new jobs + gaps), /jobs, /jobs/[id] (JD, fit breakdown, matched/missing skills, apply, prep, questions, track), /growth, /applications, /market (ticker tape, index chart, gainers/decliners, banner), /skills/[name], /prep, /profile, /admin; responsive, dark mode, with loading/empty/error states.
FR-31 — Tests (explicit) As the Operator / Reviewer, I should have tests using fixtures only with no live calls in CI, covering normalize, string limit, error codes, Jev parsing/gating/cache/fallback, dedup, experience regex, fit, gap, metrics, anomaly guard, and alerts; an eval script where I label 50 JDs (is_ai_role, role, skills, fit 1-5) reporting precision/recall per threshold and fit correlation to tune thresholds and weights; coverage ≥80%.
FR-32 — Output rules (explicit) As the Operator / Reviewer, I should have files listed before each phase; a missing key skips the service with a log; and any new service or dependency requires asking first.
FR-33 — Self-service enrollment (required_inference) As the Job Seeker, I should be able to establish my own identity through self-service enrollment, so that my profile, fit history, applications, and review labels remain bound to me and resumable.
FR-34 — Returning verification (required_inference) As the Job Seeker, I should verify my identity on return before accessing protected profile, job, growth, preparation, application, market, or administrative work, so that my durable state stays private and continuous.
FR-35 — Operator authorization (required_inference) As the Operator / Reviewer, I should be authorized before accessing review queues, operational health, ingestion runs, API usage, and phase probe reporting, so that operational functions remain under my control.
/admin. Observable result: authorized access. Failure/recovery: unauthorized access denied. Continuation: normal operation.FR-36 — Digest delivery configuration (required_inference) As the Job Seeker, I should have external email or Telegram delivery configured for the daily digest, with delivery remaining provider/external-channel owned.
Product context. The single personal-mode user of AI Career Pulse. They are an AI/ML candidate who reads tables, scores, and charts all day and will instantly notice a template. They operate the tool for their own job search, not for a demo.
Primary goal. A shortlist of well-matched roles with grounded prep material and an up-to-date application pipeline.
Distinct accepted responsibilities. Maintaining the profile (skills with levels 1–5, years, domains, cities, remote preference, target roles, minimum seniority, deal-breakers, resume text); reviewing fit breakdowns and "why this score" explanations; resolving low-confidence/needs-review items; acting on the daily digest of new high-fit jobs and rising gap skills; preparing for specific job interviews; and tracking applications through the funnel.
Relevant inputs or decisions. Profile fields and skill levels; filter and sort choices on /jobs; decisions to apply, save, or track; decisions to generate interview questions; decisions to open ranked resources and follow the 4-week plan.
Interactions with other accepted participants. The Job Seeker is the sole human participant in the career workflows. Their work is supported by external providers (IndianAPI for job data, TypeSafe Jev for decisions, the LLM for text, prep-resource providers) and by the internal pipeline. The Operator / Reviewer is the same person acting operationally.
Observable success. A ranked job list with transparent fit scores; a top-5 gap list with a grounded 4-week plan; cached interview questions tagged with gap skills; an application funnel that reflects current statuses; and a market dashboard read with its source limits clearly in view.
What makes this role different. The Job Seeker's work is analytical and personal: every number on screen is about them — their level, their fit, their gaps, their pipeline. The interface is an instrument for one person, and the value comes from the transparency of the score and the honesty of the source limits.
Product context. The same personal-mode owner acting in an operational capacity. They run the Phase 0 probes, read NOTES.md unknowns and assumptions, and review each phase's test results, summary, and open issues before the build continues.
Primary goal. A healthy, observable pipeline with no silently accepted low-confidence decisions and no invented endpoints, parameters, fields, or data.
Distinct accepted responsibilities. Monitoring ingestion runs, API usage, health, and the review queue; labeling review-queue items via POST /admin/review/{id} so labels feed threshold and weight tuning; running the Phase 0 probes; reading NOTES.md; and reviewing each phase's test results, summary, and open issues.
Relevant inputs or decisions. Review-queue items and their labels; ingestion-run and API-usage records; probe results; phase test summaries and open issues; decisions to approve or reject new services or dependencies.
Interactions with other accepted participants. The Operator / Reviewer is the same person as the Job Seeker, acting operationally. Their work is supported by the internal pipeline and by the external providers whose usage they monitor.
Observable success. A healthy pipeline with visible runs and API usage; a review queue that is labeled rather than ignored; probe results and NOTES.md entries that record unknowns honestly; and phase reviews that stop the build until issues are addressed.
What makes this role different. The Operator / Reviewer's work is about the system rather than the job search: they watch the machine, label its uncertain outputs, and decide whether the build may continue. Their success is measured in observability and honesty, not in fit scores.
/ and reads the hero band: the headline, the live data line, and the top-three fit rows (or a neutral "no data yet" line if nothing has been ingested)./profile, they enter skills with levels 1–5, years, domains, cities, remote preference, target roles, minimum seniority, deal-breakers, and resume text, and save.skill_match, experience_match, domain_match, growth_potential, and red_flags./jobs to review ranked roles./jobs./growth./skills/[name] to see the 30/90-day trend, senior-versus-mid share, share in their top-fit jobs, and their level./growth./jobs/[id], the Job Seeker reviews the JD and fit breakdown.0.5*cosine + 0.5*skill overlap and the Jev-reranked top 10 with rerank scores (not/somewhat/very useful)./jobs./jobs/[id], the Job Seeker saves or tracks the application./applications, they see the application row with status (saved, applied, interview, offer, rejected, withdrawn), dates, notes, and next action./market.ALERT_MIN_FIT plus the top 3 rising gap skills./jobs/[id] and reviews the fit breakdown./growth to act on a rising gap./admin and reviews health, ingestion runs, API usage (service, calls, tokens), and the review queue.POST /admin/review/{id}; labels are saved and feed threshold and weight tuning./admin.probe_indianapi.py with limit="5", then tests each unconfirmed param and pagination, recording status, count, whether results truly filtered, posted_date freshness, id stability, and max limit, saving to tests/fixtures/indianapi/.probe_jev.py with 10 fixture JDs through the enrichment questions, measuring latency, tokens, confidence spread, batched vs split calls, and state-length limit, saving to tests/fixtures/jev/.NOTES.md.NOTES.md and raised as questions.The creative direction is authoritative for this section. The muse is Rasmus Andersson: systematic product craft with an opinion — tight 4/8-pt scale, tabular numerals as ornament, hairline rules, keyboard-first density, and a palette that deliberately refuses bootstrap blue on white. The headline register is serious craft, quiet confidence, instrument-grade precision, zero marketing gloss.
Mode. Dark mode is the primary mode.
Colour tokens by role (dark mode).
| Role | Token | Hex |
|---|---|---|
| Background | --bg | #141517 |
| Surface (panels, cards, ticker tape) | --surface | #1C1E21 |
| Text (body, numerals) | --text | #F2F0EC |
| Primary action (apply, active nav, index line, selected chip) | --primary | #E8552B |
| Data signal (gainers, rising gap bars, fit ≥80, delta up) | --accent | #C6F24E |
| Muted (labels, metadata, "Not available from source", timestamps, axis text) | --muted | #8A8D93 |
| Hairline rules | --rule | #2A2D31 |
| Decliners and low-confidence flags | --danger | #FF5C5C |
Tangerine #E8552B is the single primary action colour, used on roughly 5% of pixels and never as a fill for large blocks. Acid lime #C6F24E is the data signal. Muted #8A8D93 carries labels and metadata. Hairlines are #2A2D31 at 1px. Red #FF5C5C is used only inside charts and the needs-review pill. No blue anywhere in the system.
Typography.
clamp(3.5rem, 9vw, 8.5rem), flush left, ragged right, with the fit score numeral interleaved at the same optical weight so type and data are one gesture.font-feature-settings: 'tnum' 1, 'ss01' 1./admin.Shape language. Sharp and exact. 6px radius on buttons and inputs, 10px on panels, 0px on the ticker tape, table rows, and chart frames. Hairline 1px borders (#2A2D31) do all the separation — no shadows, no glow, no blur. Focus rings are a 2px #C6F24E outline offset by 2px, keyboard-first. Confidence is shown as a thin horizontal probability bar with a hard right edge, never as a pill or a badge.
Spacing rhythm. 4/8-pt scale; 12-column grid, 24px gutters, 1440px max content, with charts and the ticker tape full-bleed edge to edge. Left rail navigation, 240px, persistent, with section labels in 11px uppercase and the current page marked by a 2px tangerine left rule.
Imagery style. No photography and no illustration. The imagery is the interface itself: the fit-score numeral as the hero object, ruled tables, monospace ticker tape, hairline chart grids, a small SVG sparkline per skill row, and one schematic diagram on /profile showing how the score is composed (weighted bars labelled skill_match, experience_match, domain_match, growth_potential, red_flags). Icons are 16px 1.5px-stroke line icons. Every chart is labelled with its source and its limits inline — the market banner is part of the visual system, not a dismissible toast.
The public entry (/) is a full-bleed graphite band, edge to edge, with a 1px hairline top and bottom. It is not a centred headline with a button.
clamp(3.5rem, 9vw, 8.5rem) reading "YOUR NEXT ROLE, SCORED." with the word SCORED in #E8552B. Immediately under it, a single ruled line of live data in JetBrains Mono at 14px: AI JOB INDEX 100.0 · ACTIVE 1,284 · NEW TODAY 37 · +2.1% vs 7D, with the delta in #C6F24E.#C6F24E with a 3px probability bar beneath it.The concept recomposes only accepted content, states, and controls: the headline, the live data line drawn from daily_metrics, and the top-three fit rows drawn from job_fit. It introduces no new behavior, page, or destination.
Interaction Model: Static Motion Tempo: restrained Hero Dimensionality: flat
Landing Hero Motion Brief. The focal subject is the fit-score numeral and the ruled data line, not an illustration. The input→transformation→outcome thesis: on first paint, the fit numeral counts up once over 400ms with no bounce, and the ruled data line settles in; the outcome is a composed first frame where type and data read as one gesture. The motion vocabulary is restrained and functional — 120–200ms ease-out on everything, filter chips settle in, table rows fade in on data load, chart lines draw in left-to-right once, and the ticker tape scrolls continuously at a slow constant rate and pauses on hover. The only expressive moment is the index line drawing itself on first load of /market. There is no parallax, no hover-lift, no spring, and no scroll-triggered choreography. The reduced-motion state removes the count-up and the ticker scroll, rendering the final numerals and a static ticker line instead.
apply_link. Rationale: traceability.limit parameter must be sent as a STRING; an int gives 422. Rationale: confirmed API behavior.JEV_MODEL to an exact id via SDK list-models (not jev-latest); store model version per record. Rationale: reproducibility and correct role separation.NOTES.md and ask. Rationale: correctness.is_ai_role ≥0.7 keep, 0.4–0.7 review, <0.4 drop (logged); skill ≥0.6 attach with prob. Rationale: precision control.posted_at and all links, 1 review); unseen in 3 full syncs → inactive. Rationale: duplicate control.INDIANAPI_DAILY_CAP, retry 429/5xx max 3 with backoff+jitter, honor Retry-After. Rationale: safe consumption..env only; never in frontend, logs, fixtures, or git; mask in logs; ship .env.example. Rationale: credential safety.#F2F0EC on #141517 yields roughly 13:1 contrast, safe at 14px; focus rings are a 2px #C6F24E outline offset by 2px. Rationale: readable dense data surfaces.Source-specified choices are preserved exactly.
Backend. Python 3.11, FastAPI, SQLAlchemy 2, Alembic, httpx, Pydantic v2, APScheduler, PostgreSQL 16 + pgvector, rapidfuzz, typesafe-sdk.
Frontend. Next.js 14, TypeScript, Tailwind, TanStack Query, lightweight-charts, Recharts.
Tooling and delivery. Docker Compose, pytest, ruff, mypy, GitHub Actions.
External services. IndianAPI jobs endpoint (GET https://jobs.indianapi.in/jobs, header X-Api-Key); TypeSafe Jev (POST https://api.typesafe.ai/v1/systemone, Bearer auth); a provider-agnostic LLM (LLM_PROVIDER=openai|anthropic); prep-resource providers (YouTube Data API, Dev.to, HN Algolia, arXiv, GitHub search, RSS); and an email or Telegram channel for the daily digest.
Configuration. config/*.yaml for profile, roles, skills, locations, thresholds, fit_weights, schedule, and sources.
Assumptions.
limit parameter is sent as a STRING; an int gives 422. All other parameters (title, location, company, experience, job_type, pagination) are unconfirmed until Phase 0 probes report.INDIANAPI_DAILY_CAP and bounded retries.JEV_MODEL is pinned to an exact id obtained via SDK list-models, never jev-latest.LLM_PROVIDER=openai|anthropic; text generation is temperature 0.2, JSON-schema-validated, and cached.Constraints.
.env only; never in frontend, logs, fixtures, or git; mask keys in logs; ship .env.example.apply_link.limit param must be sent as a STRING (int gives 422).JEV_MODEL to an exact id via SDK list-models (not jev-latest); store model version per record.NOTES.md and ask the user.is_ai_role ≥0.7 keep, 0.4–0.7 review, <0.4 drop (logged); skill ≥0.6 attach with prob.posted_at and all links, 1 review); unseen in 3 full syncs → inactive.INDIANAPI_DAILY_CAP, retry 429/5xx max 3 with backoff+jitter, honor Retry-After.GET https://jobs.indianapi.in/jobs, authenticated with header X-Api-Key.POST https://api.typesafe.ai/v1/systemone, used with typesafe-sdk.jev-latest.noul returns a probability 0–1; choice returns a choice with probabilities and confidence; score returns a score with probabilities and confidence.skill_match (0–3), experience_match (0–2), domain_match (noul), growth_potential (0–2), and red_flags (noul), combined with code-applied location/remote preferences, deal-breakers, and minimum seniority.POST /admin/review/{id}.is_ai_role (≥0.7 keep, 0.4–0.7 review, <0.4 drop) and skills (≥0.6 attach with prob).sha256(norm company+title+city) used to detect duplicate postings.posted_at, all links), 1 review.sha256(state+question_set_version+model).
Market delta tape: AI JOB INDEX +2.1%.
Newly ingested AI/ML roles, ordered by the transparent fit score composed from skill match, experience match, domain match, growth potential and red flags.

Market delta tape: AI JOB INDEX +2.1%.
Newly ingested AI/ML roles, ordered by the transparent fit score composed from skill match, experience match, domain match, growth potential and red flags.
No comments yet. Be the first!