Lexiverse
Lexiverse - 영어 학습자를 위한 AI 기반 문맥 독해 엔진
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Render standard = the OpenUI Lang plan. Runbook: brain
projects/calc/_files/sprint-render-v2/SPRINT.md(plan:projects/calc/topics/openui-render-arch-plan-2026-10-18.md). No new view/widget format.
A code-gated STEM drill site (CALC1 / PHYS / CSCI26). It's a static page plus a small Python server, serve.py.
problems.json. Every problem, answer, wrong answer and hint. Format: SCHEMA.md (the only schema).
serve.py reads it, sends the browser only the public part of the problem whose code was typed (GET /p/<CODE>.json) and grades on POST /check. Edit or replace the file: live on the next request.problems.json and loads every problem in it.banks/BANK_XXX.json (same format). Type BANK_XXX in the code box: its questions become the list, and the server keeps each browser's progress and place (design/BANK.md). Ships empty.BANK_ + 3–6 capital letters or digits, e.g. BANK_CE3.json. Any other name is skipped (the server prints one line saying so). ce3-traps.json → rename to BANK_CE3.json.banks/ next to serve.py on the server (with deploy.sh: ~/stem-stuff-site/banks/). No restart: it's live on the next request.BANK_CE3 in the code box (bank ce3 works too), orhttp://<host>:<port>/#BANK_CE3 (e.g. http://csci4x.com:5567/#BANK_CE3).BANK_CE3. The server remembers each browser's progress and last question (cookie), so next visit reopens the same bank where they left off./banks/ is never served. Uploading a file instead (⬆ button) switches that browser to the file until they open a bank again.deploy.sh installs sympy if missing).tests/run-all.sh runs everything (unittest, npm test, every tests/*.pw.mjs with its server) and exits 1 on any failure; SHARD=1/2 / SHARD=2/2 (or --shard N/2) runs one half so the two can run at the same time (CI does), --list prints what a run would run; --quick = unittest + npm test (~10 s; as a pre-push hook: printf '#!/bin/sh\ntests/run-all.sh --quick\n' > .git/hooks/pre-push && chmod +x .git/hooks/pre-push). GitHub Actions runs the full set as two parallel shards (plus a required all check that needs both) on every PR (.github/workflows/tests.yml). First time: cd tests && npm ci../deploy.sh [dir] [port] [branch] (defaults ~/stem-stuff-site, 5567, main). Fresh → clone + serve. Existing install → shows incoming commits, asks "update the live site now? [Y/n]". Every run force-stops the old server (pid file, then anything still on the port) and starts a new one. Non-empty other dir → Exit (default) / wipe + install (type folder name to confirm).sw.js runs and the site works offline after one visit):
npm i -g vercel, vercel login, vercel link in the repo. In the Vercel project: Storage → add Upstash Redis (sets KV_REST_API_URL / KV_REST_API_TOKEN; tries live there, key stem:tries). Domains → add csci4x.com, set the DNS records it shows. Leave Git auto-deploy off.banks/: tools/ship.sh demo (prints a private preview URL) → check → tools/ship.sh live. It deploys origin/main plus your banks, never your working tree. Banks come from the brain first (newest commit wins; a stale copy in your checkout never beats it): a local brain clone, else the bank hatch (GitLab API, bank files only, BRAIN_TOKEN with read_api in .env; .env.example). --print-banks shows which, --no-brain skips; design/DEPLOY.md. A demo you liked → Vercel dashboard → Deployments → ⋯ → Promote (no rebuild).vercel.json builds public/ with tools/build_public.py (the client only) and sends /p/, /state/, /b/, /check to api/index.py, which bundles serve.py, problems.json and banks/. Answers never reach static files.STEM_REDIRECT=https://stem-stuff.vercel.app ./deploy.sh (same dir/port). It then sends every request there (302; POST /check 307), keeping the path and the #CODE. Run ./deploy.sh without it to bring the old site back.problems.json change is a redeploy (no hot reload). Reset a browser's tries: edit the stem:tries key in the Upstash console. The first check after idle is ~1–2 s slower (sympy loads).Every failure the app can catch gets one code, and the student sees it after the usual failure words: "Didn't load. Tap Try again. Send Tony this code: E-7K3QX". Never the reason (Tony, Oct 19).
Code: E- (made in the page, incident.mjs) or S- (made by serve.py, sent back in the X-Incident: S-XXXXX reason <trace id> header when Cluck failed before any text, so the page shows the server's code) + 5 characters from 23456789ABCDEFGHJKLMNPQRSTUVWXYZ (no 0/O/1/I).
Where to look: python3 tools/incident.py E-7K3QX prints the incident line(s), the page's snapshot as tables and every server line of the same trace, from Vercel's log search (VERCEL_KEY; runtime logs are kept a short time only), PostHog (POSTHOG_KEY, a personal phx_ key; months) or a saved log (--paste FILE, - = stdin). No KV or other paid store. By hand: Vercel → the project → Logs → search the code (one JSON line per incident; src = server or client). PostHog → event incident, filter code.
Line (OpenTelemetry names where one exists, no SDK): {"incident":"S-7K3QX","src":"server","trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","where":"note","reason":"budget_cap","http.response.status_code":402,"gen_ai.request.model":"deepseek/deepseek-v4.1-flash","gen_ai.system":"openrouter","server.address":"openrouter.ai","exception.message":"HTTPError: HTTP Error 402: Payment Required","error.type":"HTTPError","q":"CALC1_XP1","t":"2026-10-19T17:02:11Z"}. The page's report (POST /log/incident) logs src: "client" with ua (browser family) and ua_raw (the full User-Agent). Every fetch sends a W3C traceparent; the same trace_id is on the page's incident and on every server line of that request, so an E- code and an S- code link.
Fields: where (explain, chat, mini, check, bank, problem, window, note, cluck, api, sw), reason (http, network, timeout, timeout_x2 = /check timed out twice in a row, parse, empty, upstream, budget_cap, exception, error, rejection, leak = a Cluck or mini-Cluck reply said a prompt's own word (the KEY, a prompt label: serve.leak_scan); err holds the pattern id only, never the text, sw_loop = a 3rd service-worker reload of one tab inside 60 s: it is skipped and the update bar shows instead), status / http.response.status_code, phase (headers = before the reply, stream = the reply broke, parse, timeout, script), (since the request started), (received before a break), / , / (the whole message, e-mails and long digit runs blanked), (question code), (deploy sha), , ; server only: , , . Never answers, notes or chat text. The PostHog event carries the page's names (, , , , ...).
An hourly GitHub Actions job (.github/workflows/spend.yml → tools/anthropic_spend.py) reads the Anthropic Admin API cost report and writes one KV key, stem:anthropic:spend = {"cents_month", "cents_since", "since", "at", "ok": true} (USD cents; month = current UTC calendar month). A failed run writes stem:anthropic:spend_err = {"ok": false, "err", "at"} instead and leaves the last good value alone (the err key is deleted on the next good run). The app estimates the balance as PREPAID_CENTS − cents_since.
The Admin API key must NEVER go into Vercel (or .env, or any file in the repo). It can read and manage the whole organization; it lives only in GitHub Actions secrets. The app only ever reads the KV key.
One-time setup (Tony):
sk-ant-admin01-…; org admins only).ANTHROPIC_ADMIN_KEY (step 1), KV_REST_API_URL and KV_REST_API_TOKEN (same values as the Vercel project's Upstash env vars).SPEND_SINCE = the date the prepaid credit was bought, YYYY-MM-DD (unset = 1st of this month).PREPAID_CENTS = credit bought on SPEND_SINCE, in cents (e.g. 5000 for $50). Not the Admin key.ANTHROPIC_ADMIN_KEY=… tools/anthropic_spend.py --dry-run.msbytespathurl.patherrexception.messageqvtrace_iduagen_ai.*server.addresshttp.request.methodincidentcodestatuspatherrbudget_cap: an AI provider said out of credit (OpenRouter 402 / key limit, Anthropic billing, our CLAUDE_BUDGET_USD cap). That request skips the provider at once; when every provider is out, /explain and /chat answer 503 with the code right away.
Debug mode (Tony only): open the site once with ?debug=1 (saved as stem-debug in this browser): codes show their reason too ("E-7K3QX network"). ?debug=0 turns it off. No switch on any page. Debug mode also shows the error card's diag line: v#197 · explain 504 · 20.1 s · AI: cap|err|ok|?.
Flight recorder (flight.js): the page keeps its last 256 steps in memory (nav, stage, pick, net start/headers/first byte/end/error with status, ms, bytes, rid = x-vercel-id + trace id, cluck, vis, conn, err, sw, layout: vp, scroll, cls, box, mq, load, ro; perf = long tasks folded into one entry per 30 s window: count, total ms, longest ms, JS heap MB on Chrome) plus 32 keyboard/focus events as classes (kd/ku/in/cs/ce/fi/fo/vv, and paste/cut/copy/submit as the event only, never the clipboard: target id, char or a named key, inputType, lengths, 100 ms times; never a value). An incident report carries fr = the last 64 steps, the last 20 keyboard events, page.at (the app's state: question, tries, Cluck, viewport, control boxes; no text) and page.before (the same, re-read in idle time 250 ms after each stage/nav/cluck/sw/vis step, with its age_ms: the page just before the error line), the diag line and /health's answer: ≤ 16 KB, ~5 KB typical; 3 per 10 minutes, the same failure within 60 s sends {dup, n} only. Look at it in the client line's fr (Vercel) or the PostHog incident event.
Server lines: every stderr line ends rid=<x-vercel-id> trace=<trace id>; a provider failure's incident line adds etype + emsg (the provider's own error type and message, 120 chars) and is kept as KV stem:err:<claude|openrouter> for an hour. GET /health answers {"claude","or": ok|over|err|null} only; the numbers (spend, budget, prepaid balance from stem:anthropic:spend with PREPAID_CENTS, last errors, or_key = OpenRouter's GET /auth/key limit / usage / limit_remaining in $) go in its health {...} log line. OpenRouter is asked at most once a minute per instance (kept in memory, a failure too; no KV), so /health (no rate limit) cannot hammer it; limit_remaining <= 0 or a 402 = over, 401/403 = err.
Trace line (one per /explain / /chat request, and per other API request that is not 2xx or takes > 2 s): {"trace":1,"trace_id","rid","path","status","ms","q","sid":sha256(sid)[:8],"v4","arm","fallback","fb_ms" (first byte),"bytes","done" (false = the client left mid-reply),"inc":[S- codes],"phases":[{"p":"note"|"or"|"inc","at" (ms since the request began),"model","status","ms" | "err"; inc: "code","reason","provider","etype","emsg"}],"t"}. Never the prompt, answers, notes or chat. Search the trace id or an S- code in Vercel → Logs (or tools/incident.py, which prints every line of a trace).
PostHog tile (paste it yourself: PostHog → Product analytics → New insight → SQL; nothing is created by code). Incidents by day, kind and reason, with their codes:
SELECT toDate(timestamp) AS day, properties.where AS kind, properties.reason AS reason, count() AS n,
uniq(properties.code) AS codes_n, arraySlice(groupUniqArray(properties.code), 1, 10) AS codes
FROM events
WHERE event = 'incident' AND timestamp > now() - INTERVAL 30 DAY
GROUP BY day, kind, reason
ORDER BY day DESC, n DESC
(DNT / GPC / opted-out devices send no incident event, so this undercounts them; server-only S- codes are in Vercel's logs only.)
Do Not Track / Global Privacy Control / privacy.html "Don't record me": no report is sent; the code still shows, with "Tracking is off on this device, so Tony gets less to go on."