Speak the suite's sign-in door copy in Memoria
The door's button reads 'Sign in'; sent and signed-out pages come from estilo; titles read '<Page> · Memoria' (was 'Sign in — Memoria'); the email subject is 'Sign in to Memoria'. Reviewed by GPT-6-Astra via Codex (#22). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Squashed from chore/door-copy into main.
9 files changed,
+36
−41
go.mod+1 −1go.sum+2 −2internal/memoria/auth.go+1 −1internal/memoria/handlers.go+13 −23internal/memoria/home_render.go+0 −2internal/memoria/home_router_test.go+3 −2internal/memoria/return.go+6 −9internal/memoriatest/signin_door_test.go+4 −1internal/memoriatest/signinoutcome_test.go+6 −0
CI passed — run details
CI log
=== vet === go vet ./... === fmt === === test === go test ./... ? amadan.net/oficina/memoria/cmd/memoria [no test files] ok amadan.net/oficina/memoria/internal/memoria 3.706s ok amadan.net/oficina/memoria/internal/memoria/chunk (cached) ok amadan.net/oficina/memoria/internal/memoria/fact (cached) ? amadan.net/oficina/memoria/internal/memoria/icons [no test files] ok amadan.net/oficina/memoria/internal/memoria/index (cached) ok amadan.net/oficina/memoria/internal/memoria/notion (cached) ok amadan.net/oficina/memoria/internal/memoria/provider (cached) ok amadan.net/oficina/memoria/internal/memoria/query (cached) ok amadan.net/oficina/memoria/internal/memoria/seal (cached) ok amadan.net/oficina/memoria/internal/memoria/sibling (cached) ok amadan.net/oficina/memoria/internal/memoriatest 1.814s ? amadan.net/oficina/memoria/scripts/writeestilo [no test files] ? amadan.net/oficina/memoria/tools/bench [no test files] ? amadan.net/oficina/memoria/tools/drive [no test files] ? amadan.net/oficina/memoria/tools/seed [no test files] ? amadan.net/oficina/memoria/tools/vendorassets [no test files] === migration-check === go run github.com/carlosframework/rastrillo/cmd/rastrillo migration check rastrillo migration check: models and migrations agree
diff --git a/go.mod b/go.mod| index 0dfacdb..326bdca 100644 |
| --- a/go.mod |
| +++ b/go.mod |
| @@ -5,7 +5,7 @@ go 1.26 |
| toolchain go1.26.5 |
| require ( |
| - amadan.net/oficina/estilo.git v0.0.0-20260929231226-cce11010ece0 |
| + amadan.net/oficina/estilo.git v0.0.0-20261001084202-0b4d92c3dc16 |
| amadan.net/oficina/home/connect v0.0.0-20260923190447-01166fcfc0a9 |
| amadan.net/oficina/home/handoff v0.0.0-20260908102137-481d2d57f489 |
| amadan.net/oficina/suite v0.0.0-20260905215529-60b01a8a0d6d |
diff --git a/go.sum b/go.sum| index 945bb9f..b33184b 100644 |
| --- a/go.sum |
| +++ b/go.sum |
| @@ -1,5 +1,5 @@ |
| -amadan.net/oficina/estilo.git v0.0.0-20260929231226-cce11010ece0 h1:Qww+xGIYJaj+6TIX4Knr71ghxeYJjtgFm6WukX6NkGg= |
| -amadan.net/oficina/estilo.git v0.0.0-20260929231226-cce11010ece0/go.mod h1:4u0jNBtIdMLOsJyrScfqoEuYDJF7BBM3qM9YWbzt6aw= |
| +amadan.net/oficina/estilo.git v0.0.0-20261001084202-0b4d92c3dc16 h1:F5v3x25dEj1rJNnBm71G7shN+fM+T2actUlaGPvoeH8= |
| +amadan.net/oficina/estilo.git v0.0.0-20261001084202-0b4d92c3dc16/go.mod h1:4u0jNBtIdMLOsJyrScfqoEuYDJF7BBM3qM9YWbzt6aw= |
| amadan.net/oficina/home/connect v0.0.0-20260923190447-01166fcfc0a9 h1:K/PaOVdQqRMwifPD6ipiwLeGR5nbFSxm9FxjrDJlZcg= |
| amadan.net/oficina/home/connect v0.0.0-20260923190447-01166fcfc0a9/go.mod h1:YroavXe43Wietec+po8mtSk4a9Bir2h31Q3iAf4MWxI= |
| amadan.net/oficina/home/handoff v0.0.0-20260908102137-481d2d57f489 h1:RPKtyry/71i06o8L+gjAaUyexbmG56kO1YPs601URAM= |
diff --git a/internal/memoria/auth.go b/internal/memoria/auth.go| index 453b0dd..b09f2eb 100644 |
| --- a/internal/memoria/auth.go |
| +++ b/internal/memoria/auth.go |
| @@ -216,7 +216,7 @@ func NewAuth(sqlDB *sql.DB, env AuthEnv, logger *slog.Logger, authorize func(str |
| Authorize: authorize, |
| SigninPath: signinPath, |
| SignedInPath: landing, |
| - Subject: "Sign in to Memoria", |
| + Subject: chrome.EmailSubject("Memoria"), |
| Logger: logger, |
| }) |
| if err != nil { |
diff --git a/internal/memoria/handlers.go b/internal/memoria/handlers.go| index d8a7d10..408d4d2 100644 |
| --- a/internal/memoria/handlers.go |
| +++ b/internal/memoria/handlers.go |
| @@ -1170,14 +1170,6 @@ type signinPage struct { |
| SignedOut *chrome.SignInNotice |
| } |
| -// The door's words (spec §9.2). Local mode has no lede: "to continue to |
| -// Memoria" says nothing on Memoria's own page. "Memoria holds your team's |
| -// work…" is retired with it. |
| -const ( |
| - signinHelp = "We’ll email you a link that signs you in. It works once and expires in 15 minutes." |
| - signinSentBody = "If that address can sign in here, we’ve sent it a link. It works once and expires in 15 minutes." |
| -) |
| - |
| // localSignin serves the sign-in page: GET /signin standalone, and in Home |
| // mode the local-address form (/signin/local, and /signin whenever |
| // connect hands an outcome back: ?sent, ?err, ?force). |
| @@ -1248,25 +1240,21 @@ func (a *app) localSignin(w http.ResponseWriter, r *http.Request) { |
| if remembered, ok := a.rememberedReturn(r); ok { |
| again += "?return_to=" + url.QueryEscape(remembered) |
| } |
| + // The door's words; again is where "Use a different address" goes. |
| + sent := chrome.SignInNotice{Icon: icon, App: appName, Sky: view}.Sent(again) |
| render(w, "signin", signinPage{ |
| - page: page{Frame: a.frameAs(r, viewer{}, frame{parts: []string{"Check your email"}, signinPage: true}), ModelOn: a.modelOn()}, |
| - Sent: &chrome.SignInNotice{ |
| - Icon: icon, App: appName, Sky: view, |
| - Heading: "Check your email", Body: signinSentBody, |
| - Link: &chrome.Link{Href: again, Label: "Use a different address"}, |
| - }, |
| + page: page{Frame: a.frameAs(r, viewer{}, frame{parts: []string{sent.Page()}, signinPage: true}), ModelOn: a.modelOn()}, |
| + Sent: &sent, |
| }) |
| return |
| } |
| + // The door supplies its own heading, label, help and submit; a plain |
| + // visit carries no lede. |
| door := &chrome.SignIn{ |
| Icon: icon, App: appName, Sky: view, |
| - Heading: "Sign in", |
| - Action: "/auth/begin", |
| - AddressLabel: "Email address", |
| - Help: signinHelp, |
| - Submit: "Sign in", // Paul, 2026-10-01: the door says "Sign in" |
| - OpenSource: &chrome.Link{Href: "/help/open-source", Label: "Open source"}, |
| + Action: "/auth/begin", |
| + OpenSource: &chrome.Link{Href: "/help/open-source", Label: "Open source"}, |
| } |
| if hasReturn { |
| door.Return = ret |
| @@ -1277,8 +1265,9 @@ func (a *app) localSignin(w http.ResponseWriter, r *http.Request) { |
| switch kind { |
| case "": |
| case "warn": |
| - // ?reauth=1. Not an error, so not the door's negative callout: it |
| - // stands where the lede would, and says why the form is back. |
| + // ?reauth=1. Not an error, so not the door's negative callout: |
| + // it stands in the lede, and says why the form is back. The one |
| + // lede this door carries: a state, never an introduction. |
| door.Lede = msg |
| case "field": |
| // An address that is not one belongs to the field, not a banner. |
| @@ -1308,7 +1297,8 @@ func (a *app) localSignin(w http.ResponseWriter, r *http.Request) { |
| // session was just ended, so it tells nobody else anything.) |
| func signinOutcome(q url.Values) (msg, kind string) { |
| if q.Get("sent") != "" { |
| - return signinSentBody, "good" |
| + // No sentence: the door's check-your-email state says it. |
| + return "", "good" |
| } |
| switch q.Get("err") { |
| case "": |
diff --git a/internal/memoria/home_render.go b/internal/memoria/home_render.go| index 6376512..8867326 100644 |
| --- a/internal/memoria/home_render.go |
| +++ b/internal/memoria/home_render.go |
| @@ -49,8 +49,6 @@ var homeCopy = func() map[string]string { |
| out["unknown_title"] = "Oficina account not connected" |
| out["unknown_body"] = "Sign in to your existing account, then connect your Oficina account from the account menu." |
| out["success_body"] = "You can now use your Oficina account to sign in to this account." |
| - out["signedout_body"] = "You’ve signed out of Memoria on this device." |
| - out["signedout_action"] = "Sign in" |
| out["documents_action"] = "Back to Memoria" |
| return out |
| }() |
diff --git a/internal/memoria/home_router_test.go b/internal/memoria/home_router_test.go| index 8c9e16b..9fdcfbf 100644 |
| --- a/internal/memoria/home_router_test.go |
| +++ b/internal/memoria/home_router_test.go |
| @@ -424,13 +424,14 @@ func TestSignedOutIsTheDoor(t *testing.T) { |
| for _, want := range []string{ |
| "<div estilo-door", "estilo-sky-layer", "<section estilo-signin", "<span>Memoria</span>", |
| "/static/chrome.", "/static/sky.", "<title>Signed out · Memoria</title>", |
| - "You’re signed out", "You’ve signed out of Memoria on this device.", `href="/signin"`, ">Sign in<", |
| + "<h1>You’re signed out</h1>", "You’ve signed out of Memoria on this device.", `href="/signin"`, ">Sign in<", |
| } { |
| if !strings.Contains(b, want) { |
| t.Errorf("/signed-out lacks %q", want) |
| } |
| } |
| + // Home is never named to people (Paul, 2026-09-23). |
| if strings.Contains(b, "Home") { |
| - t.Error("/signed-out names Home") |
| + t.Errorf("/signed-out names Home:\n%s", b) |
| } |
| } |
diff --git a/internal/memoria/return.go b/internal/memoria/return.go| index b4d3b39..889dfc9 100644 |
| --- a/internal/memoria/return.go |
| +++ b/internal/memoria/return.go |
| @@ -221,17 +221,14 @@ func signedOutAfter(signout http.HandlerFunc) http.HandlerFunc { |
| } |
| } |
| -// signedOut serves GET /signed-out: "You’re signed out", and a primary |
| -// Sign in. The same page standalone and with Home. |
| +// signedOut serves GET /signed-out: the door's signed-out page ("You’re |
| +// signed out", and a primary Sign in), in the door's words. The same page |
| +// standalone and with Home, which is never named. |
| func (a *app) signedOut(w http.ResponseWriter, r *http.Request) { |
| w.Header().Set("Cache-Control", "no-store") |
| + out := chrome.SignInNotice{Icon: assets.Path("static/brand/icon.svg"), App: appName, Sky: sky.For(r.Host)}.SignedOut(signinPath) |
| render(w, "signin", signinPage{ |
| - page: page{Frame: a.frameAs(r, viewer{}, frame{parts: []string{"Signed out"}, signinPage: true}), ModelOn: a.modelOn()}, |
| - SignedOut: &chrome.SignInNotice{ |
| - Icon: assets.Path("static/brand/icon.svg"), App: appName, Sky: sky.For(r.Host), |
| - Heading: "You’re signed out", |
| - Body: "You’ve signed out of " + appName + " on this device.", |
| - Link: &chrome.Link{Href: signinPath, Label: "Sign in"}, |
| - }, |
| + page: page{Frame: a.frameAs(r, viewer{}, frame{parts: []string{out.Page()}, signinPage: true}), ModelOn: a.modelOn()}, |
| + SignedOut: &out, |
| }) |
| } |
diff --git a/internal/memoriatest/signin_door_test.go b/internal/memoriatest/signin_door_test.go| index 1aceb81..3fdbb10 100644 |
| --- a/internal/memoriatest/signin_door_test.go |
| +++ b/internal/memoriatest/signin_door_test.go |
| @@ -47,6 +47,8 @@ func TestSigninPageIsTheDoor(t *testing.T) { |
| `action="/auth/begin"`, |
| // Paul, 2026-10-01: the door's button says "Sign in". |
| `>Sign in</button>`, |
| + // The door's tab title (estilo chrome.Title). |
| + `<title>Sign in · Memoria</title>`, |
| } { |
| if !strings.Contains(html.UnescapeString(body), html.UnescapeString(want)) { |
| t.Errorf("the sign-in page lacks %q", want) |
| @@ -56,7 +58,8 @@ func TestSigninPageIsTheDoor(t *testing.T) { |
| // "Memoria holds your team's work…", and the |
| // footer's egress claim, which is about searching and is said only to |
| // somebody signed in. |
| - for _, gone := range []string{"Email me a link", "holds your team", "<footer", "leaves this instance"} { |
| + // And the old title, "Sign in — Memoria". |
| + for _, gone := range []string{"Email me a link", "holds your team", "<footer", "leaves this instance", "Sign in — Memoria"} { |
| if strings.Contains(body, gone) { |
| t.Errorf("the sign-in page still carries %q", gone) |
| } |
diff --git a/internal/memoriatest/signinoutcome_test.go b/internal/memoriatest/signinoutcome_test.go| index d655034..3676db5 100644 |
| --- a/internal/memoriatest/signinoutcome_test.go |
| +++ b/internal/memoriatest/signinoutcome_test.go |
| @@ -82,6 +82,12 @@ func TestSigninPageSaysWhatHappened(t *testing.T) { |
| if !strings.Contains(body, "<h1>Check your email</h1>") { |
| t.Error("the sent notice does not say check your email") |
| } |
| + if !strings.Contains(body, "<title>Check your email · Memoria</title>") { |
| + t.Error("the sent page's tab title is not \"Check your email · Memoria\"") |
| + } |
| + if !strings.Contains(body, `<a href="/signin">Use a different address</a>`) { |
| + t.Errorf("the sent notice does not link back to the form as \"Use a different address\":\n%s", body) |
| + } |
| if strings.Contains(body, `action="/auth/begin"`) { |
| t.Error("the sign-in form is still on the page after a link was sent") |
| } |