factcircuit
FactCircuit: an auditable claim-and-evidence verification loop with immutable provenance, staged checks, bounded retrieval, and reproducible evaluation.
Visualize AI's impact on jobs with 3D terrain, global maps, and live labor data
AI summary · README
DataHTML · TypeScriptGitHub @abhishek-sutariaOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
A futuristic dashboard visualizing the impact of AI on the workforce. This application combines 3D visualization, live economic data, and generative AI to provide a comprehensive look at how jobs are evolving.Read the full README ↗A futuristic dashboard visualizing the impact of AI on the workforce. This application combines 3D visualization, live economic data, and generative AI to provide a comprehensive look at how jobs are evolving.
Install Dependencies
npm install
Environment Configuration
Create a .env file in the root directory:
ANTHROPIC_API_KEY=your_server_default_claude_key_here
VITE_BLS_API_KEY=your_bls_api_key_here
VITE_SUPABASE_URL=your_supabase_project_url_here
VITE_SUPABASE_ANON_KEY=your_supabase_anon_key_here
Notes:
ANTHROPIC_API_KEY is used server-side by the proxy as the default Claude key.VITE_SUPABASE_URL / VITE_SUPABASE_ANON_KEY are optional. Omit them and
the app runs exactly as before — no account button, no persistence, nothing
broken. See Individual User Activity to set
these up. The anon key is a publishable identifier, not a secret — see that
section for why it's safe to ship in the client bundle.Run Locally
npm run dev
This app is a Vite SPA and uses server-side proxy endpoints in production.
The local Vite proxy in vite.config.ts is dev-only and does not run on Vercel.
Set these in Vercel Project Settings -> Environment Variables:
ANTHROPIC_API_KEY=your_server_default_claude_key_here
VITE_BLS_API_KEY=your_bls_api_key_here
VITE_SUPABASE_URL=your_supabase_project_url_here
VITE_SUPABASE_ANON_KEY=your_supabase_anon_key_here
Notes:
ANTHROPIC_API_KEY enables default Claude key mode.VITE_BLS_API_KEY is optional but recommended for higher BLS rate limits.VITE_SUPABASE_* are optional — omit to deploy without accounts/persistence.From the repo root:
npx vercel
Follow prompts to create/link the project, then add env vars above.
After env vars are configured:
npx vercel --prod
Production uses Vercel serverless functions under:
/api/claude/messages -> Anthropic proxy/api/bls and /api/bls/:seriesId -> BLS proxyThese endpoints preserve the same runtime behavior expected by the frontend:
Landscape.tsx: Main 3D scene controller.Terrain.tsx: Custom shader-based terrain generation using BLS data.MapView.tsx: Interactive SVG/Dom-based world map for geographic insights.UI.tsx: The main "Glassmorphism" overlay dashboard.Employment/headcount data comes from BLS's Occupational Employment and Wage Statistics program, in two separate extracts refreshed independently — check each one's own source note before assuming a number is current, since they can drift apart between refreshes:
| Extract | File | Drives | Current vintage |
|---|---|---|---|
| National, per role | src/data.ts | 3D terrain height, the Workers stat, the "OES" badge | May 2025 |
| State-level | src/data/geo_real.json | 2D US map employment + Location Quotient | May 2025 |
# 1. Get a free BLS registration key (unregistered tier is 25 req/day, not
# enough to cover the ~5,000 series a full refresh needs):
# https://data.bls.gov/registrationEngine/
export VITE_BLS_API_KEY=your_key_here
# 2. Preview the change with no files written:
node scripts/refresh_oes_data.mjs --dry-run
# 3. Apply it (both national and state-level; add --national-only for just
# the smaller national pass, e.g. while iterating without spending quota
# on the full state-level fetch):
node scripts/refresh_oes_data.mjs
This pulls directly from api.bls.gov — www.bls.gov/download.bls.gov block
automated downloads (Akamai bot protection), so the classic "download the OEWS
workbook" path (scripts/extract_bls_data.py) can no longer be refreshed
programmatically for a new vintage. scripts/refresh_oes_data.mjs rewrites, in
one pass: src/data.ts employment values, all 50 dataSources literals in
that file, DATA_SOURCES.BLS_OES in src/config/constants.ts,
src/data/national_employment.json, and (unless --national-only)
src/data/geo_real.json. src/__tests__/oesVintage.test.ts and
scripts/audit.mjs (T65–T67) both guard
that DATA_SOURCES.BLS_OES and the literals in data.ts never drift apart.
src/utils/onet.ts's MAP_TITLE_TO_SOC is the single source of truth for
which SOC code each role uses, for both extracts. If BLS retires or
consolidates a SOC code (as happened to 13-1022/13-1023, now the broad
code 13-1020), update that map first — the refresh script and
scripts/audit.mjs T64/T57/T58 both key off it.
Signed-in users (anonymous by default, upgradeable to email) get their saved roles, view history, upskill progress, and saved AI reports persisted and kept private to them. This is entirely optional infrastructure — without it configured, the app behaves exactly as a stateless, anonymous SPA.
api/*.ts serverless functions do
not run under npm run dev (see AGENTS.md).localStorage['foj_user_claude_key'], sent as an x-user-api-key header)
is untouched by any of this. Signing out does not clear it; it is never
written to a user record; auth uses a completely different transport
(Supabase's own session bearer token, to a different origin).resumeCacheKey in src/lib/userData.ts) so a
saved report can be found again without ever storing the source text.supabase/migrations/, in
order — read each file's own header comment for what it does before running
it:
0001_user_activity.sql — creates the tables and RLS policies.0002_grant_authenticated_privileges.sql — required. RLS decides
which rows a role may touch, but Postgres needs a separate, more basic
GRANT before a role may query a table at all. Skipping this leaves every
request from a signed-in (including anonymous) user failing with a 403
before RLS is ever evaluated — the app looks completely broken while the
policies themselves are perfectly correct.http://localhost:3000. Set it to your deployed
origin (e.g. https://your-app.vercel.app), or every emailed link points
at a dev server that isn't running.emailRedirectTo and
falls back to the Site URL. Add your origins with a wildcard so any path
(the app requests /dashboard) is permitted:
https://your-app.vercel.app/**
http://localhost:5173/**
VITE_SUPABASE_URL / VITE_SUPABASE_ANON_KEY — locally in .env.local
and in Vercel Project Settings → Environment Variables for production.
The anon key is meant to be public: it carries no privileges of its own,
and every table denies all access until RLS explicitly grants it.VITE_SUPABASE_URL=... VITE_SUPABASE_ANON_KEY=... node scripts/verify_rls.mjs
This creates two real anonymous users and asserts neither can read, write,
update, or delete the other's rows — the thing a mocked-client unit test
cannot prove, since RLS is enforced by Postgres itself.