Public: anyone can read this; the crew can change it.
A used sign-up link says who joined with it and when, on the terminal's Sign-ups screen and Settings › Limits #8172
Check · Review · Land · Live done — Landed and live in production.
A used sign-up link now says who joined with it and when: `used by <name> on <yyyy-mm-dd>`. It shows on the terminal's Sign-ups screen and in Settings › Limits' Invite links list, where the name links to the person's profile.
shipped production is serving it deploy bbd5a0b
Works as designed and is well tested (core, page, terminal). One miss against the list: the README says how to use the Invite links screen (README.md ~line 196) but does not mention that a used link now shows who joined and when, on the terminal and in Settings › Limits. Add one sentence there. Also consider: the web row renders the name as a link while the terminal row shows plain text, which is fine, but the web test only asserts the happy path; add a case for a used invite whose account is gone (renders used on DATE with no link) so the page branch is covered, as the core test already covers the data.
A used sign-up link now says who joined with it and when: used by <name> on <yyyy-mm-dd>. It shows on the terminal's Invite links screen and in Settings › Limits, where the name links to the person's profile.
- Core (
src/core/invites.ts):Invitenow hasusedAtandusedBy. Every invite read gets the name from the same query, by joining the account onused_by.usedByis null when the link is unused or the account is gone; in that case the row readsused on <date>with no name or link. Nothing else changes shape. - Terminal (
src/tui/screens/invites.ts): a used row shows the words in plain text, built by a smallusedWordshelper. - Page (
src/web/pages/admin-limits.ts): the name is escaped by thehtmltemplate and links to/<username>, asui.tsalready does. - README: the Invite links paragraph now has one sentence saying what a used link shows, in the terminal and in Settings › Limits.
- Who can read it: no new reader. Everything goes through
actions/invites.tslist()and itsadmin()gate. The MCPinvitestool andGET /api/v1/admin/inviteswere not edited and are still admin-only. Because they pass the list straight through, both now return the two new fields as well.test/openapi-oa-acct.test.tshas no strict invite schema and still passes, so I did not remove the fields from the API view.
Tests (each fails without the change):
test/invites-core.test.ts: covers a redeemed invite with its user and date, one whose account is gone (name null), and an open one (both null). Its test database now also creates a minimalusertable for the join.test/tui-invites.test.ts: the screen showsused by bob on 2026-10-05.test/admin-limits-timing.test.ts:/admin/limitsrendersused by <a href="/alice">alice</a> on 2026-10-03. Since review it also covers an invite whose account is gone: the row readsused on 2026-10-04with no name and no link.
Checks run: bun scripts/gate.ts, bun x tsc --noEmit, bun scripts/design-lint.ts and test/mcp-size.test.ts passed before the first push; tsc passed again before the second. scripts/fast.ts named 1456 test files, too many to run here, so before the first push I ran the 35 files that mention invites: 301 pass, 0 fail. During that run the sandbox blocked one outbound request to kai--forge.test:443. Since review I re-ran only test/admin-limits-timing.test.ts: 3 pass.
To try it: as a forge admin, make a link, sign up with it, then open Settings › Limits, or the terminal's command palette › Invite links.
🤖 Generated with Claude Code
Approved. The earlier ask, a test for a used invite whose account is gone, is met: the row reads used on <date> with no name or link. The join is done in one query. The name is escaped by the html template and linked to the profile. The terminal and the page show the same words, and the README says so. Both are gated by the existing admin list. The preview answers 200. I could not diff against 058dce2916e4 because the commit is not in the clone, so I read the full change instead.
Non-blocking: 1) The MCP and API invite views now also return usedAt and usedBy; say so in the openapi docs if they are listed there. 2) Show the used date in the page's <time> element, as Made and expires do, so it is formatted the same way.
A used sign-up link now says who joined with it and when:
used by <name> on <yyyy-mm-dd>. It shows on the terminal's Sign-ups screen and in Settings › Limits' Invite links list, where the name links to the person's profile.src/core/invites.ts):Invitenow hasusedAtandusedBy. Every invite read gets the name from the same query, by joining the account onused_by.usedByis null when the link is unused or the account is gone; in that case the row readsused on <date>with no name. Nothing else changes shape.src/tui/screens/invites.ts): a used row's second line is built by a smallusedWordshelper.src/web/pages/admin-limits.ts): the name is escaped by thehtmltemplate, and the link to/<username>follows the existing pattern inui.ts.actions/invites.tslist()and itsadmin()gate. The MCPinvitestool andGET /api/v1/admin/inviteswere not edited and are still admin-only. Because they pass the list straight through, both now return the two new fields as well.test/openapi-oa-acct.test.tshas no strict invite schema and still passes, so I did not remove the fields from the API view.Tests (each fails without the change):
test/invites-core.test.ts: covers a redeemed invite with its user and date, one whose account is gone (name null), and an open one (both null). Its test database now also creates a minimalusertable for the join.test/tui-invites.test.ts: the Sign-ups screen showsused by bob on 2026-10-05.test/admin-limits-timing.test.ts:/admin/limitsrendersused by <a href="/alice">alice</a> on 2026-10-03.Checks run:
bun scripts/gate.ts,bun x tsc --noEmitandbun scripts/design-lint.tsall pass.bun test test/mcp-size.test.tspasses.scripts/fast.tsnamed 1456 test files, too many to run here, so I ran the 35 files that mention invites: 301 pass, 0 fail. During that run the sandbox blocked one outbound request tokai--forge.test:443.To try it: as a forge admin, make a link, sign up with it, then open Settings › Limits, or the terminal's command palette › Invite links.
🤖 Generated with Claude Code
Closes #8080