Template-carrying login links
Status#
Implemented and live-verified on preview-17 (all CI green): both hints
survive os login -> authorize -> signed /login redirect; a project created as
pr2477-template-waiter-chef was born from PR 2477's configs/waiter-chef
(MENU.md and waiter prompts in its repo), and the existence gate correctly
downgraded a smoke-template-waiter-chef (no pr prefix -> main ref, template
not merged) to a stock project. Everything below the Decisions section
is best-guess fleshing-out of a brief prompt — assumptions are called out.
Ask (from Misha, verbatim-ish)#
allow query params that let me choose the project config too. Example PR with written instructions to create a project: https://github.com/iterate/iterate/pull/2477 — but it'd be good if it could be encoded in the URL somehow. How about: if the project slug ends with
-template-mytemplateandmytemplateis a config template that exists, the project when created in OS will be created withmytemplate. And the auth app just needs aprojectHintquery param similar tologinHintthat it respects and uses instead of the derived-from-email algorithm that always ends up withnustom. So the projectHint could bepr1234-template-mytemplate. Then, as long as this is documented, agents can add a maximally useful login link to PR bodies.
The end-to-end recipe (what this enables)#
An agent opening a config-template PR (e.g. #2477, template
configs/waiter-chef on branch waiter-chef) adds to the PR body:
https://os.iterate-preview-N.com/api/iterate-auth/login?login_hint=pr2477%2Btest%40nustom.com&project_hint=pr2477-template-waiter-chef
Click → "Continue as pr2477+test@nustom.com" (fixed OTP) → first-run setup
prefilled with project slug pr2477-template-waiter-chef → Get started →
OS creates the project from that PR's configs/waiter-chef.
Decisions (assumptions delineated)#
- Slug convention (os side): the first
-template-splits the slug:<prefix>-template-<name>→ template folderconfigs/<name>ingithub:iterate/iterate. Assumption: the canonical template repo is hardcoded to iterate/iterate — same default the create form's placeholder advertises. - Ref from the prefix:
pr<digits>prefix → git refpull/<N>/head(verified: the downloader'scommits/{ref}GitHub endpoint resolves it, and the existing ref validator accepts it — no GitHub PR-API lookup needed). Any other prefix → no ref (downloader defaults to HEAD = default branch). So merged templates work aswhatever-template-<name>, and in-flight PR templates aspr<N>-template-<name>. - Existence gate: before recording the derived template in the creation
event, one GitHub
contents/<path>?ref=check. Missing/failed → create proceeds WITHOUT a template (per "and mytemplate is a config template that exists"), with a loud server log. Assumption: graceful downgrade beats blocking project birth on GitHub availability; explicitconfigRepoTemplateargs are never second-guessed and skip all of this. - Derivation point:
ProjectRpcTarget.create()— applies to both the prospective-slug lane (os create form) and the existing-handle lane (the welcome page's?ensureBirthafter an auth-app create — Misha's flow). - Param name (auth side):
project_hint, snake_case sibling oflogin_hint— same "non-binding suggestion" semantics, same forwarding path. Validated against the project-slug shape (lowercase kebab, ≤50 chars) at every hop; anything else is dropped. - Plumbing: RP
/loginforwards it onto the authorize URL; the @better-auth/oauth-provider patch gainsproject_hint: z.string().optional()in the authorize endpoint schema (exactly howlogin_hintsurvives the signed /login redirect and post-login authorize re-entry);/project-accessreads it and uses it as the initial project slug in both the first-run (org+project) and project-only forms, beating the derived-from-email suggestion. - Docs: recipe documented in
docs/dev-environments.mdnext to the test-email/OTP section. The preview PR comment'sLogin ↗link (#2475) stays hint-less — agents opt in per-PR when they have a template to demo.
Checklist#
- os:
configRepoTemplateFromSlughelper (pure parse) + existence check with injectable fetch; unit tests - os: derive in
ProjectRpcTarget.create()whenconfigRepoTemplatearg is absent - auth: extend oauth-provider patch with
project_hint - auth: RP
/loginforwards validproject_hint(+ forwarding matrix test in relying-party-behavior.test.ts) - auth:
/project-accessprefills project slug from the hint - auth e2e:
project_hintsurvives into the signed /login redirect (sibling of the login_hint case in oauth-code-exchange.e2e.test.ts) - docs: login-link recipe in docs/dev-environments.md
Known limitation (found in live verification)#
Template downloads use unauthenticated api.github.com from the worker; under
GitHub's shared-egress-IP rate limits the repo seed can fail with
Config repo creation failed: GitHub returned HTTP 403 (observed on
preview-14; preview-17 sailed through minutes earlier). This is pre-existing
for every configRepoTemplate create — the form lane included — not specific
to the slug convention; the convention adds one contents/ existence call
(which itself degrades safely to a stock project when rate-limited).
Follow-up: authenticate the template downloader (platform GitHub token or
app installation) so template births stop depending on shared-IP quota.