Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ Work ships in epics: a set of cards that together make one release worth trying.

**Done: PERF-1, Telegram-smooth (Chris Sep 25; shipped in 0.3.0, build 138, Sep 26).** "Optimize for pure user experience... very efficient like Telegram." The design stays as it is; the app gets fast. Measure first, so every fix has a before and after, and Yui watches the numbers from then on.

- YUI-98: speed reporting. Step 1 shipped Sep 25: the speed budget ([Speed budget](/developers/perf)), seven named intervals with p50 and p95 targets, hang, hitch and memory budgets, what the phone sends (numbers only) and the proposed `yui_perf` table, the daily report and the rule for the briefing line, and the war room Speed panel in the playground (`/playground?demo=speed`). Step 2 (YUI-102, shipped in 0.3.0, build 138) built Sep 25, app main 9385fc2: signposts and a frame clock on the nine intervals, MetricKit metrics and diagnostics, memory every 30 s, one batch an hour and on background, the `yui_perf` table on PROOF (numbers only, owner only, 500 a day, 90 days), `yui_perf_report.py` and the Speed panel under Builds in the war room, and the briefing line only when a number got worse. Dev builds get a Speed switch. As built: PERF.md section 10. First real numbers Sep 26, from Chris's phone on TestFlight 135 (before YUI-99 and YUI-101): typing p95 70 ms on 52 keystrokes (budget 33), memory peak 328 MB (budget 300); 0.3.0 (138) is the first build to compare against. The same rows showed one batch sent twice across a relaunch: the report drops repeats, YUI-107 fixes the phone.
- YUI-98: speed reporting. Step 1 shipped Sep 25: the speed budget ([Speed budget](/developers/perf)), seven named intervals with p50 and p95 targets, hang, hitch and memory budgets, what the phone sends (numbers only) and the proposed `yui_perf` table, the daily report and the rule for the briefing line, and the war room Speed panel in the playground (`/playground?demo=speed`). Step 2 (YUI-102, shipped in 0.3.0, build 138) built Sep 25, app main 9385fc2: signposts and a frame clock on the nine intervals, MetricKit metrics and diagnostics, memory every 30 s, one batch an hour and on background, the `yui_perf` table on yuigui (numbers only, owner only, 500 a day, 90 days), `yui_perf_report.py` and the Speed panel under Builds in the war room, and the briefing line only when a number got worse. Dev builds get a Speed switch. As built: PERF.md section 10. First real numbers Sep 26, from Chris's phone on TestFlight 135 (before YUI-99 and YUI-101): typing p95 70 ms on 52 keystrokes (budget 33), memory peak 328 MB (budget 300); 0.3.0 (138) is the first build to compare against. The same rows showed one batch sent twice across a relaunch: the report drops repeats, YUI-107 fixes the phone.
- YUI-99 (shipped in 0.3.0, build 138): typing keeps up. Holding backspace and double-space for a period run as fast as in Notes, even on a long thread.
- YUI-100 (shipped in build 155, Sep 26): memory audit. On a scripted simulator session (5 agents, 486-row threads, decks, galleries, games, screens 2-12, idle) the inline deck pager leaked about 1,550 objects; it pages with a ScrollView now and the session ends with 0 new leaks. Pictures decode at display size into one 48 MB cache emptied on a memory warning, videos drop their player, the thread window trims back at the bottom. Peak 243 to 203 MB, launch 137 to 124 MB. `scripts/memory.sh` runs before every ship and fails on over 100 new leaks, 15 MB growth or a 300 MB peak.
- YUI-101 (shipped in 0.3.0, build 138): taps answer on the same frame, 120 fps scroll, fast thread open. On the simulator a long thread opens in about 0.3 s instead of 3, taps answer in under 100 ms, and scroll hitches fell from 63 to 2 ms a second.
Expand Down Expand Up @@ -283,7 +283,7 @@ Deliverables:
- DONE Sep 23: hub site (roadmap, progress log, business plan draft, deck outline, mockups), source on GitHub at postscarcityai/yuigui.
- DONE Sep 23: Yui Lines v0 (spec, 12 presets, JS parser, web playground, token benchmark). Replaces the JSON protocol from draft 1.
- DONE Sep 23: yuigui.com live, built in public (progress log per ship, weekly update Fridays).
- DONE Sep 23: waitlist on yuigui.com (replaced by Request an invite on Sep 24, SITE-26), stored in `yui_waitlist` in the existing PostScarcity AI Supabase project (PROOF). All Yui tables use the `yui_` prefix there; no new Supabase instance.
- DONE Sep 23: waitlist on yuigui.com (replaced by Request an invite on Sep 24, SITE-26), stored in `yui_waitlist` in the existing PostScarcity AI Supabase project (PROOF). Yui moved to its own project, yuigui, on Sep 27. All Yui tables use the `yui_` prefix there; no new Supabase instance.
- DONE Sep 23: Apple Developer account (Chris, individual enrollment, no D-U-N-S).
- DONE Sep 23: the Yui Lines conformance suite, and the Swift parser that passes it (YUI-3). Events back to the agent (tap, submit) are in the spec from day one; voice events wait for YUI-14.
- DONE Sep 23: Xcode on the Mac mini and a TestFlight pipeline on the App Store Connect API. The first build went out the same day.
Expand Down
2 changes: 1 addition & 1 deletion docs/business/BIZ-4-content-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ Clips need to be cheap or they will not happen. Proposal: a `YuiDemo` UI test in

### 5.4 The scheduler (reuse Vibe Jam's)

Vibe Jam already has a working queue: a posts table, an admin page that shows a week ahead, and a Vercel cron that posts to Instagram and X, with the double-post and token-refresh bugs fixed on Sep 23. Port that code into the yuigui.com site with a `yui_social_posts` table in the PostScarcity AI Supabase project (PROOF, every table prefixed `yui_`). Approved drafts move from `social/queue/` into the table with their slot. Until the handles exist and Chris approves the first batch, this stays unbuilt and nothing posts.
Vibe Jam already has a working queue: a posts table, an admin page that shows a week ahead, and a Vercel cron that posts to Instagram and X, with the double-post and token-refresh bugs fixed on Sep 23. Port that code into the yuigui.com site with a `yui_social_posts` table in Yui's own Supabase project (yuigui, every table prefixed `yui_`). Approved drafts move from `social/queue/` into the table with their slot. Until the handles exist and Chris approves the first batch, this stays unbuilt and nothing posts.

### 5.5 Long-form route

Expand Down
2 changes: 1 addition & 1 deletion ledger/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

The private ledger behind use to earn (OSS-7). The spec is [spec/LEDGER.md](../spec/LEDGER.md), on the site at [yuigui.com/developers/ledger](https://www.yuigui.com/developers/ledger). The thinking is [Use to earn](https://www.yuigui.com/business/use-to-earn).

- `yui_ledger.sql`: the table and two functions. Apply by hand in the SQL editor of the shared Supabase project. Never `supabase db push`.
- `yui_ledger.sql`: the table and two functions. Apply by hand in the SQL editor of the yuigui Supabase project. Never `supabase db push`.
- `record.mjs`: the daily recorder. Node 22, no dependencies.

```
Expand Down
2 changes: 1 addition & 1 deletion ledger/yui_ledger.sql
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
-- The private ledger (OSS-7). Spec: spec/LEDGER.md, rendered at yuigui.com/developers/ledger.
-- Apply by hand in the SQL editor of the shared Supabase project. Never `supabase db push`:
-- Apply by hand in the SQL editor of the yuigui Supabase project. Never `supabase db push`:
-- the project is shared with other apps.
--
-- Facts about use and contribution, never what anyone said. Rows are only ever added.
Expand Down
2 changes: 1 addition & 1 deletion site/app/api/invite/route.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Invite requests (SITE-26). Writes a row to yui_invites in the PostScarcity AI Supabase project (PROOF)
// Invite requests (SITE-26). Writes a row to yui_invites in Yui's own Supabase project (yuigui)
// with the service role key; the table has RLS on and no grants, so anon can never read or write it.
// The row starts as status=requested. Approving it (supabase/scripts/invite.py in the app repo) is Chris's call.
const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;
Expand Down
2 changes: 1 addition & 1 deletion site/app/components/ConnectFlow.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { useCallback, useEffect, useRef, useState } from "react";
import QRCode from "qrcode";

const OAUTH = "https://ewzzaoperdpxqxkshynx.supabase.co/functions/v1/yui-oauth";
const OAUTH = "https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-oauth";
const POLL_MS = 2000;

async function call(body) {
Expand Down
2 changes: 1 addition & 1 deletion site/app/start/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Cmd from "../components/Cmd";
import Shots from "../components/Shots";
import Films from "../components/Films";

const MCP_URL = "https://ewzzaoperdpxqxkshynx.supabase.co/functions/v1/yui-mcp";
const MCP_URL = "https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp";

// Shots from build 162 on the simulator, demo account, the pairing flow of YuiPromoTests.testPromoPair (SITE-46).
const PAIR_SHOTS = [
Expand Down
2 changes: 1 addition & 1 deletion spec/ADMIN.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ The web comes later, only if a support team with laptops needs bulk work (long t

- **No new login.** The person signs in with Apple as usual. `yui-auth` keeps minting the `yui_user` token it mints today. The role is never inside the token.
- **One admin API.** A new edge function, `yui-admin`, takes every admin call. It checks the caller's token, then reads their role row live on each request and refuses anything the role does not allow. It is the only code that holds admin power. Every call writes one audit row before it answers.
- **Yui roles only.** Admin power is a row in a Yui table, checked by Yui code. It never uses PROOF Auth, never the shared `authenticated` role, never a Postgres superuser from the phone.
- **Yui roles only.** Admin power is a row in a Yui table, checked by Yui code. It never uses Supabase Auth, never the shared `authenticated` role, never a Postgres superuser from the phone.
- **The app only shows.** The Admin section is hidden without a role, but hiding is a courtesy. The API is the lock.
- **Asking again for destructive actions.** Stop, delete, whole-app stop, limit changes, role grants and flags for everyone need a step-up: Face ID on the phone and a fresh Sign in with Apple, both bound to the action (the Apple nonce carries a hash of the exact request). A step-up lasts for that one action, never a session.
- **Two people for the biggest ones.** Once there are two super users, the whole-app stop, account delete and granting super user wait for a second person's tap. Until then the owner alone, and the log says so.
Expand Down
4 changes: 2 additions & 2 deletions spec/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Name and color are optional. They default from the profile name (`coach` becomes

## Data model

All tables live in PROOF, `public` schema, and cascade from `yui_users` (`ON DELETE CASCADE`). Deleting the account leaves zero rows (tested).
All tables live in the yuigui Supabase project, `public` schema, and cascade from `yui_users` (`ON DELETE CASCADE`). Deleting the account leaves zero rows (tested).

### yui_connectors: one per agent host

Expand Down Expand Up @@ -107,7 +107,7 @@ Adding a profile that is already registered returns the existing agent. The app'
3. The agent calls the API:

```
POST https://ewzzaoperdpxqxkshynx.supabase.co/functions/v1/yui-agents
POST https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-agents
apikey: <publishable key>
Authorization: Bearer yui_mt_...

Expand Down
2 changes: 1 addition & 1 deletion spec/LEDGER.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Tested Sep 26 on Postgres 16 with stand-in `yui_users` and `yui_messages` tables
## 6. Switching it on

1. Read this spec and `ledger/yui_ledger.sql`.
2. Apply the SQL by hand in the SQL editor of the shared Supabase project. Never `supabase db push`: the project is shared with other apps.
2. Apply the SQL by hand in the SQL editor of the yuigui Supabase project. Never `supabase db push`.
3. Fill in from day one: `node ledger/record.mjs --from 2026-09-23 --prs`.
4. Run `node ledger/record.mjs --prs` once a day, beside the media sweep.
5. Publish the privacy paragraph (section 3), and change the home page line from "we are starting a private ledger" to "a private ledger counts".
Expand Down
22 changes: 11 additions & 11 deletions spec/MCP.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Any AI app that speaks MCP can put a screen on your phone. You keep talking to C
<--taps-------- <-- <-- a tap
```

- **Endpoint:** `https://ewzzaoperdpxqxkshynx.supabase.co/functions/v1/yui-mcp`
- **Endpoint:** `https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp`
- **Transport:** MCP streamable HTTP, stateless. Every request is one POST with a JSON-RPC message (or a batch) and gets `application/json` back. No session id, no SSE stream: GET answers 405.
- **Auth:** either OAuth 2.1 (paste the URL, sign in, approve it in Yui: [below](#oauth)) or `Authorization: Bearer yui_ct_...`, a connection token you get by pairing (the three steps below). The Claude and ChatGPT apps' custom connectors only do OAuth; Claude Code, Cursor and n8n ([below](#n8n)) can use either.

Expand All @@ -22,7 +22,7 @@ Every Claude surface reaches Yui through this server. Pick the one you use.
Needs a plan that allows custom connectors. Nothing here lists Yui in Claude's directory: it is your own connector, visible only to you.

1. In Claude, open **Settings > Connectors > Add custom connector**.
2. Name it **Yui**. URL: `https://ewzzaoperdpxqxkshynx.supabase.co/functions/v1/yui-mcp`. Leave the OAuth fields empty.
2. Name it **Yui**. URL: `https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp`. Leave the OAuth fields empty.
3. Tap **Connect**. Claude opens www.yuigui.com/connect; approve it in the Yui app ([OAuth](#oauth) below).
4. In a chat, turn Yui on under the tools menu and ask: "Put a 5 minute focus timer on my phone."

Expand All @@ -31,7 +31,7 @@ A connector added on the web also shows up in the desktop and phone apps. Where
### Claude Code

```
claude mcp add --transport http yui https://ewzzaoperdpxqxkshynx.supabase.co/functions/v1/yui-mcp
claude mcp add --transport http yui https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp
claude mcp get yui # "Needs authentication"
claude mcp login yui # opens the browser; approve in Yui
```
Expand All @@ -56,7 +56,7 @@ for await (const m of query({
mcpServers: {
yui: {
type: "http",
url: "https://ewzzaoperdpxqxkshynx.supabase.co/functions/v1/yui-mcp",
url: "https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp",
headers: { Authorization: `Bearer ${process.env.YUI_TOKEN}` }, // yui_ct_... from pairing
},
},
Expand Down Expand Up @@ -84,7 +84,7 @@ Needs an account whose plan and workspace allow developer mode. Do it on chatgpt

1. **Settings > Security and login**, turn on **Developer mode**.
2. Open [chatgpt.com/plugins](https://chatgpt.com/plugins) and press **+**.
3. Name **Yui**, description "Screens on my phone". Public endpoint, URL: `https://ewzzaoperdpxqxkshynx.supabase.co/functions/v1/yui-mcp`. Authentication: OAuth. Create.
3. Name **Yui**, description "Screens on my phone". Public endpoint, URL: `https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp`. Authentication: OAuth. Create.
4. ChatGPT opens www.yuigui.com/connect. Scan its QR with your iPhone (or type an Add agent code from the app) and tap **Allow** in Yui ([OAuth](#oauth) below). A new agent named ChatGPT shows up in Yui.
5. Start a new chat, add Yui from the tools menu, and ask: "Ask me on my phone what we are having for lunch: Salad, Soup or Tacos."

Expand Down Expand Up @@ -125,7 +125,7 @@ curl -s https://api.x.ai/v1/responses \
],
"tools": [{
"type": "mcp",
"server_url": "https://ewzzaoperdpxqxkshynx.supabase.co/functions/v1/yui-mcp",
"server_url": "https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp",
"server_label": "yui",
"server_description": "Screens on my phone",
"allowed_tools": ["yui_show", "yui_answers", "yui_say", "yui_threads"],
Expand Down Expand Up @@ -169,7 +169,7 @@ It calls `yui_show`, then `yui_answers(wait=25)` in a loop up to its Timeout (30
### MCP Client Tool

1. An **AI Agent** node with any chat model.
2. Its tool: **MCP Client Tool**. Endpoint `https://ewzzaoperdpxqxkshynx.supabase.co/functions/v1/yui-mcp`, Server Transport **HTTP Streamable**, Authentication **Bearer Auth** with the token (or **MCP OAuth2**, [OAuth](#oauth)).
2. Its tool: **MCP Client Tool**. Endpoint `https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp`, Server Transport **HTTP Streamable**, Authentication **Bearer Auth** with the token (or **MCP OAuth2**, [OAuth](#oauth)).
3. Tools to Include: `yui_show`, `yui_answers`, `yui_say`. Leave out `yui_tap`, it is app-only.
4. Options, Timeout: 40000 ms or more. `yui_answers` holds a call up to 25 s and n8n's default is 60 s, which is fine for one call, but leave room.
5. The system message: the tool descriptions carry a short guide, and n8n does not read MCP prompts, so spell out the steps. This one got a 7B local model (qwen2.5:7b on Ollama) through it:
Expand Down Expand Up @@ -208,7 +208,7 @@ What a client needs, for the curious:
| | |
| --- | --- |
| Protected resource metadata (RFC 9728) | `GET .../yui-mcp/.well-known/oauth-protected-resource`, also named in the 401's `WWW-Authenticate: Bearer resource_metadata="..."` |
| Authorization server | `https://ewzzaoperdpxqxkshynx.supabase.co/functions/v1/yui-oauth` |
| Authorization server | `https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-oauth` |
| Metadata (RFC 8414 / OIDC discovery) | `.../yui-oauth/.well-known/oauth-authorization-server` and `.../yui-oauth/.well-known/openid-configuration` (`yui-mcp` serves the same at its own `.well-known` paths) |
| Registration (RFC 7591) | `POST .../yui-oauth/register`. Public clients (`none`, PKCE) or `client_secret_post` / `client_secret_basic`. Redirect URIs: https, http only on localhost, or an app scheme |
| Authorize | `GET .../yui-oauth/authorize`: `response_type=code`, PKCE `S256` required, `resource` must be the endpoint above, scope `yui` |
Expand All @@ -224,19 +224,19 @@ Each approval is a connection like a paired computer: the same limits, the same
1. In the Yui app: **Agents > Add agent**. Name it for the app you will connect ("Claude"). It shows a 6-digit code.
2. Trade the code for a token. The code works once and lasts ten minutes:
```
curl -s https://ewzzaoperdpxqxkshynx.supabase.co/functions/v1/yui-connect \
curl -s https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-connect \
-H 'content-type: application/json' \
-d '{"action":"pair","code":"123456","remote_ref":"claude","kind":"mcp","host_name":"Claude Code"}'
```
The answer holds `connector_token` (`yui_ct_...`). It is shown once and stored only as a hash. Treat it like a password.
3. Add the server to your client. Claude Code:
```
claude mcp add --transport http yui https://ewzzaoperdpxqxkshynx.supabase.co/functions/v1/yui-mcp \
claude mcp add --transport http yui https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp \
--header "Authorization: Bearer yui_ct_..."
```
Cursor (`~/.cursor/mcp.json`) and any client that takes a URL and headers:
```
{"mcpServers": {"yui": {"url": "https://ewzzaoperdpxqxkshynx.supabase.co/functions/v1/yui-mcp",
{"mcpServers": {"yui": {"url": "https://txuibjxyfpalzvpneqgp.supabase.co/functions/v1/yui-mcp",
"headers": {"Authorization": "Bearer yui_ct_..."}}}}
```

Expand Down
2 changes: 1 addition & 1 deletion spec/PERF.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ create policy yui_perf_connector_read on public.yui_perf for select to yui_conne
```

- **Only numbers fit.** `name` is a snake_case identifier, `stack` holds frames only, and there is no free-text column. A before-insert trigger drops rows over 500 per account per day (the same pattern as the limits in `yui_limits`), so a stuck loop cannot flood the table.
- **Owner only.** A person's rows are theirs. The phone writes as `yui_user`; the owner's agents read through `yui_connector`, so Yui (the agent) sees its owner's phones and a shared agent never sees a client's. No role can update or delete a row, and PROOF Auth and the `authenticated` role get nothing.
- **Owner only.** A person's rows are theirs. The phone writes as `yui_user`; the owner's agents read through `yui_connector`, so Yui (the agent) sees its owner's phones and a shared agent never sees a client's. No role can update or delete a row, and Supabase Auth and the `authenticated` role get nothing.
- **Retention: 90 days.** A daily job deletes rows older than 90 days; the report keeps its own summary per build (below), so older builds stay comparable after their rows are gone. Deleting the account deletes the rows (`on delete cascade`).

## 6. The daily report
Expand Down
Loading
Loading