new-term-radar
Daily radar for new AI terms: names of new models, products and repos, caught on X, Hugging Face, GitHub and sitemaps before keyword tools list them.
Check sales and monthly progress on a phone or desktop without spreadsheets
Translation pending
AI summary · README
MarketingJavaScript · HTMLGitHub @jorgeasenriveraOnlineOwner 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 dealership runs on two questions nobody has time to answer: who is up, and how
is the month going. Sage answers both on a phone in the five seconds a
salesperson has between customers, and on a desk screen a manager glances at
between calls. It reads the reports the store already emails itself, keeps the
floor's rotation honest, and says who is short and by how much, in words rather
than in a spreadsheet.Read the full README ↗A dealership runs on two questions nobody has time to answer: who is up, and how is the month going. Sage answers both on a phone in the five seconds a salesperson has between customers, and on a desk screen a manager glances at between calls. It reads the reports the store already emails itself, keeps the floor's rotation honest, and says who is short and by how much, in words rather than in a spreadsheet.
This file is the map. AGENTS.md is the short list of rules that hold whichever
agent is working, and it is the one to read every session. This one is worth
reading once, properly, before the first change.
A React 18 single page app built by Vite, a handful of serverless functions on Vercel, and Postgres at Supabase. No framework beyond that, no state library, no component library. Two files carry nearly all of the app:
| file | lines | what it is |
|---|---|---|
src/LeadPerformanceCalculator.jsx | ~17,000 | the core: the salesperson's phone, both rooms, sign-in, the shared helpers, the design tokens, and every stylesheet in the app |
src/Manager.jsx | ~28,000 | the manager's surface, lazily loaded: the boards, the desk, People, Import, the reports, the printed sheets |
Their size is the single most important fact about working here. See section 8.
Three rooms. Live Floor is the walk-in rotation, Phone Line is the phone opportunity queue, and Online exists but has never been used by any store and currently shows an under construction page.
Two audiences, one truth. A salesperson sees their own corner: where they are in the line, their day, their month. A manager sees the store. Both read the same rows, so they cannot disagree.
npm install
npm run dev # http://localhost:5173 against whatever .env.local points at
.env.local carries VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY. It is
gitignored and stays that way. Never commit one.
scripts/mock-supabase.mjs is a small stand-in for Supabase, seeded from
scripts/demo-seed.mjs with a whole demo store: fourteen people, a month of
figures, a floor with a line on it. It needs no network and no credentials.
printf 'VITE_SUPABASE_URL=http://127.0.0.1:5433\nVITE_SUPABASE_ANON_KEY=mock-anon-key\n' > .env.local
SALESPERSON=1 node scripts/mock-supabase.mjs & # port 5433
npm run build && npx vite preview --port 5178 --host 127.0.0.1 &
SALESPERSON=1 seeds the salesperson's own screens as well, and the feel harness
refuses to run without it. Sign in as [email protected] with any password.
To stop the mock, match the exact command so you do not kill your own shell:
for pid in $(pgrep -f '^node scripts/mock-supabase.mjs$'); do kill $pid; done
Three checks reached production in one week once, all found by somebody noticing a wrong number on a screen days later. Every one was catchable in milliseconds. That is why these exist, and why none of them is optional.
npm test # every test under test/, Node's own runner, no network, under a second
npm run build # a build that fails is as broken as a test that fails
npm run check # both of the above
npm run feel # the phone, measured in a real browser
npm run shots # the phone, pictured: every screen at three text sizes, into shots/
npm testRoughly forty files under test/. Almost every case is a fault that reached
production first, kept so the second time is free. test/README.md says which
fault each one holds the line on. Two are worth knowing about up front:
test/feel.test.mjs holds the phone's feel in the source: no control
greyed out for a round trip, taps committed before the server answers, rails
that spring on transform, the motion tokens used rather than raw numbers, and
the shape of the recent cut list work. It is the fastest way to catch a change
that quietly undoes a decision.test/no-duplicates.test.mjs is structural, and test/README.md explains it.npm run shotsThe phone, pictured rather than measured. scripts/shots.mjs signs in on the
mock, walks Home, Home's foot, the frame halfway through a swipe to the floor,
Live Floor and Phone Line at Normal, Large and Largest, and the You're up
takeover, and writes each as a PNG under shots/. On every pull request the
checks workflow runs it in WebKit and links the pictures from the pull request,
so the phone view of a change is there before the phone is picked up.
FEEL_BROWSER=webkit runs it in WebKit locally; SHOTS_SIZES=1 is Normal only.
The sign-in, the floor's shape, a phone-sized browser and a thumb live in
scripts/probe-kit.mjs, so the next probe anybody writes starts from them
rather than from a scratchpad. The feel harness keeps its own copy of the floor
prep on purpose: it is the measured thing and its guards pin its text.
npm run feelThe feel harness drives the real app in a real browser against the mock, with a dealership's 400 ms added to every data request, and fails over the bar:
| bar | what it means |
|---|---|
| a tab under 150 ms | the other room is the screen the phone already had |
| a tap drawn under 100 ms | drawn in the frame it landed in, not after the server answers |
| two taps in one round trip | the second wins, on the screen and on the server |
| a press that gives, with one tick | the control moves under the finger and buzzes once |
| a return sign-in in about a second | signing in again the same day takes the short way |
Setup, in this session's environment:
FEEL_URL=http://127.0.0.1:5178/ \
FEEL_PLAYWRIGHT=/opt/node22/lib/node_modules/playwright \
PLAYWRIGHT_BROWSERS_PATH=/opt/pw-browsers \
npm run feel
FEEL_LAG sets the added delay per request (default 400). FEEL_CPU=4 throttles
the processor four times over, which is roughly a three year old phone on a hot
afternoon. FEEL_DEBUG=1 narrates.
FEEL_BROWSER=webkit runs it in Playwright's WebKit instead of Chromium. WebKit
is what the app's WebView is, and Chromium at phone width is not the phone:
the glitches that reach Jorge from the lot are the kind only WebKit shows. CI
runs both; the WebKit run reports without blocking until it has been green on
three merges, then it blocks like the other. This session's container has no
WebKit, so here it is the Chromium run and CI is the WebKit one.
Two things the harness has taught, both the hard way, both now written into it:
.sf-seg-btn click lands on a
display:none button and does nothing. Use the ROOM constant..github/workflows/checks.yml runs both jobs on every push and every pull
request, the feel job inside Playwright's own image so the numbers come from the
same machine every time. On a pull request the table is posted as a comment, so
every diff says what it did to the phone.
.github/workflows/sage-app.yml builds the phone app and hands it to TestFlight
or Google Play, from a browser, on demand.
Do not merge red. If a check fails for a reason you believe is not yours, prove it in the pull request and fix the measurement. Do not loosen a bar and do not add a retry.
src/
LeadPerformanceCalculator.jsx the core, the phone, both rooms, the styles
Manager.jsx the manager's surface, lazily loaded
SageMark.jsx the identity, one drawing on the 9x9 PixIcon grid
FenceEditor.jsx the lot boundary editor
report.js what the phone reports about itself: errors, web vitals
sw.js the worker that keeps the fonts on a phone with no signal
api/ Vercel functions. _underscored files are shared modules,
not endpoints, and are where the testable logic lives
scripts/
mock-supabase.mjs the local stand-in for Supabase
demo-seed.mjs the demo store it is seeded from
feel.mjs the feel harness
test/ the checks, and test/README.md on why each exists
supabase/migrations/ versioned schema changes, not a fresh-database bootstrap
supabase/reference/ historical schema snapshot, outside automatic migrations
workers/lpc-mail.js the Cloudflare email worker that catches the store's reports
native/ the Expo shell around the site, and its own README
docs/routines.md the four scheduled jobs that look after Sage unattended, and the deploy watch
docs/in-flight.md the board: who is working on what
docs/handoff.md messages between the two agents
NOTES.md asked for, not built yet, and the decisions behind it
HANDOFF_3.md, HANDOFF_4.md what a past session knew
The api/_*.mjs modules matter more than they look. Anything worth testing lives
in one of them so a test can reach it without a browser: _people-status.mjs
(who works here and who two spellings belong to), _stations.mjs (the desks),
_store-merge.mjs (what happens when two tabs save the same store),
_report-parsers.mjs (reading the store's emailed reports).
Supabase project dydfevdnpppvxgdptiwv. The tables that matter:
| table | what it holds |
|---|---|
app_data | the store document and everything keyed off it: config, daily rows, backups, boards |
floor_public | one row per store per day, the Live Floor: the line, the assists, the history |
queue_public | the same for the Phone Line |
floor_people | the link between an account and a person on the roster |
profiles | accounts |
deal_events | the read-only feed from DriveCentric that flips a salesperson to "with a guest" without anybody remembering to |
app_errors | every crash on a phone, failed write and server fault, grouped by fingerprint |
app_vitals | INP, LCP and CLS from real phones, with the build, the room and the store |
queue_identity | dead since #326. Six rows. Nothing reads it. See docs/in-flight.md item C3 |
Row level security is the only boundary, so treat every policy change as a
security change. Schema changes are migrations under supabase/migrations/,
never ad hoc SQL against the live project.
The reports arrive by email: the store forwards them to an address that a
Cloudflare worker catches and posts to /api/ingest, which parses and files them.
test/parsers.test.mjs and test/ingest-routing.test.mjs are what stand between
that and a fortnight of one store showing another store's salespeople.
The site is on Vercel and deploys from main. api/*.mjs are its functions.
A commit that touches only docs/ and markdown does not deploy: vercel.json
carries an ignoreCommand for it, because every board claim and release commit
used to redeploy production and the Board's wall reloaded for each one. The
reason lives here and not in vercel.json, which is checked against a schema
and refuses a comment key.
Supabase holds the data. The phone app is an Expo shell around the site, built by
.github/workflows/sage-app.yml on demand, never on anybody's laptop.
Four scheduled jobs look after Sage unattended, documented word for word in
docs/routines.md: an hourly error watch that reads app_errors and can open
(and in narrow, stated conditions merge) a fix; a morning brief; a nightly
demo store reset; and a morning collector that puts TestFlight feedback on
the board as one row. A fifth thing on that page is not a routine: the deploy
watch, a workflow that opens an issue when Vercel reports a failed deployment. If you change src/report.js, api/_report.mjs or the error
schema, read that file, because a routine depends on it.
No em dashes. Anywhere a person reads: the app, commit messages, pull requests, comments. A comma, a colon or a full stop does the job.
Write for five seconds. The reader has a customer walking in and may not be technical. Show rather than describe. One verdict per row, said in a colour, in a glyph and in a word, so it survives a mono laser printer, a bright lot and a colour blind reader.
Comments explain why. Look at any block of this codebase: the comment says what was tried, what broke, and what it cost. That is deliberate. Match it. A comment that restates the code is noise; a comment that records the reason is the only copy of that reason there will ever be.
Motion is tokens, not numbers. --t-press, --t-release, --t-exit,
--t-swap, --t-settle, --t-wipe, and MOTION in the script. A new surface
uses these. test/feel.test.mjs holds the two copies together.
Type and iconography come off one ruler. Geist, Geist Mono and Space Grotesk
are served from public/fonts and cached by the worker, so a lot with no signal
still draws Sage in Sage's faces. PixIcon and the Sage mark are one 9x9 grid.
Nothing visual ships without approval. Jorge approves visual changes in advance, on a published proposal page with a decision per item: Approve, Adjust, or No. A pull request description is not a substitute. Copy, layout, colour, motion, a new control, a removed control: all of it. If your change leaves the pixels identical, say so plainly in the pull request.
Two agents work on Sage: Claude Code and Codex. The point of two is not twice the branches. It is that each reviews what the other could not see. Everything here exists to get that benefit without the two of you fighting over the same file.
src/LeadPerformanceCalculator.jsx is ~17,000 lines and src/Manager.jsx is
~28,000. Almost every feature lands in one of them. Two open branches that both
touch one of these will conflict, and resolving a conflict in a file that size
is worse than doing the work twice. Every rule below follows from that.
docs/in-flight.md. One row per item: what, who, which branch, which files.
Alongside it, docs/handoff.md carries messages between the agents. The board
says who owns what; the handoff says what one agent needs the other to know. It
is the only channel they have, since one runs in a web session and the other on
the desktop, so anything urgent still goes through Jorge. Each agent writes only
in its own section, which is what stops two writers landing on the same line.
Claiming is a commit to main before the work starts:
git fetch origin main && git checkout -B main origin/main
# fill in owner, branch, files, status "in progress" on your row
git commit -am "Claim: <item>" && git push origin main
That commit is the lock. It needs no shared runtime, works across two different
tools, and survives either agent losing its session. If two claims race, the one
already on main wins and the loser picks something else. The claim is released
in the same pull request that finishes the work.
At most one agent may have an open branch touching either big file. The board says who. This is the rule that actually prevents the pain, and it is worth more than any merge strategy.
If the hot files are taken, there is plenty that does not collide:
api/ and its _*.mjs modules, where the testable logic livestest/, and the harness in scripts/supabase/migrations/workers/, native/, docs/And the corollary: no wholesale reformatting. No import reordering, no prettier pass, no comment reflow, no rename across a file. One whitespace sweep through either file turns every open branch into a conflict.
claude/<slug> and codex/<slug>, so git log says who did what.main before every push. A week-old branch costs a day.Each agent already works in its own container or worktree, so filesystem
isolation is free and neither of you should ever be on the other's branch. If you
ever do run two agents on one machine, give each its own git worktree. Never two
agents in one working directory: the index lock and the checkouts will collide.
When the other agent opens a pull request, read it before it merges. Look for the thing a second reader catches: a claim in the description that the diff does not support, a cost the author did not notice, a simpler shape, a test that asserts the implementation rather than the behaviour. Say it in a review comment, with the evidence.
Three of sixteen findings in the September audit were wrong on a closer read of the code. A second reader would have caught them sooner, and that is the whole argument for this arrangement.
Rough guide, not a rule:
The one thing neither should do is start a second branch on a hot file because the first one is taking a while.
Codex picks up item C6, moving the restore point out of the store row. The C
says Claude wrote that row; the Owner column is what says who has it now.
AGENTS.md, then this file, then docs/in-flight.md, then
docs/handoff.md.src/LeadPerformanceCalculator.jsx. The
board shows no open branch on it, so the item is takeable. If Claude had one
open, Codex would take C3 (a migration) or review instead.codex/restore-point-key,
files, status in progress. Commit to main, push. The lock is live.main commit that carries the claim.npm test, npm run build, and npm run feel because the core
file is in the diff.display:none control and conclude the app lost your tap.saveShared(key, value, quiet). The third argument suppresses the buzz.
Housekeeping writes must pass it, or a tidy-up that clears three hundred rows
buzzes somebody's phone three hundred times. That happened.scripts/demo-seed.mjs is what
the mock and the harness are built on. Changing it can move a bar.npm ci before CI-shaped work. The lockfile is committed; if you change a
dependency, say so loudly, because it is the one file both agents always touch.