Complete preview catalog and record bounded navigation exception
3 files changed,
+17
−2
docs/superpowers/specs/2026-08-30-home-design.md+13 −0internal/home/directory_catalog.go+2 −0internal/home/directory_catalog_test.go+2 −2
CI passed — run details
diff --git a/docs/superpowers/specs/2026-08-30-home-design.md b/docs/superpowers/specs/2026-08-30-home-design.md| index bd7b00f..40265f7 100644 |
| --- a/docs/superpowers/specs/2026-08-30-home-design.md |
| +++ b/docs/superpowers/specs/2026-08-30-home-design.md |
| @@ -1342,6 +1342,19 @@ registry is not invented for this screen — §4's `<team>.<app>.oficina.works` |
| has to resolve per app and §7 has to know what can be provisioned, and |
| without it that knowledge is scattered across templates. |
| +**Preview navigation exception, 2026-09-09 — approved by Paul for the |
| +current preview connection.** The directory may read the operator-only |
| +`HOME_DOCS_PREVIEW_ORIGIN` setting and link the Docs catalog entry to that |
| +canonical HTTPS origin's fixed `/signin` path, labelled **Open preview**. |
| +An empty setting keeps the existing `/apps/docs` team chooser. This is an |
| +opt-in preview launcher, not a team instance or a change to the canonical |
| +`<team>.<app>.oficina.works` route policy. It does not register a consumer |
| +or grant access; the destination must already implement its own Home |
| +handshake. The setting is rejected outside the directory role, for a |
| +noncanonical origin, or for the credential/directory origins themselves. |
| +The current catalog is a staged list with explicit unavailable states; |
| +the full team-scoped grid and its switcher remain to be implemented. |
| + |
| **Mail and Files are `integrated: false`** — Keymail and Slopbox are |
| separate products in separate repositories with their own auth. They render |
| as plain outbound links, never as team-scoped tiles, until each verifies |
diff --git a/internal/home/directory_catalog.go b/internal/home/directory_catalog.go| index 8d95eab..a4f6006 100644 |
| --- a/internal/home/directory_catalog.go |
| +++ b/internal/home/directory_catalog.go |
| @@ -18,6 +18,8 @@ func directoryCatalog(previewOrigin string) []catalogApp { |
| {Name: "Slides", Status: "Not connected to Home yet"}, |
| {Name: "Calendar", Status: "Not connected to Home yet"}, |
| {Name: "Meet", Status: "Coming soon"}, |
| + {Name: "Mail", Status: "Separate sign-in"}, |
| + {Name: "Files", Status: "Separate sign-in"}, |
| } |
| } |
diff --git a/internal/home/directory_catalog_test.go b/internal/home/directory_catalog_test.go| index 71bb0fc..f11d7b9 100644 |
| --- a/internal/home/directory_catalog_test.go |
| +++ b/internal/home/directory_catalog_test.go |
| @@ -18,7 +18,7 @@ func TestDirectoryCatalogRequiresSigninAndDoesNotInventLaunchDestinations(t *tes |
| } |
| noStore(t, page) |
| body := page.Body.String() |
| - for _, name := range []string{"Docs", "Sheets", "Slides", "Calendar", "Meet"} { |
| + for _, name := range []string{"Docs", "Sheets", "Slides", "Calendar", "Meet", "Mail", "Files"} { |
| if !strings.Contains(body, ">"+name+"<") { |
| t.Fatal("missing app", name) |
| } |
| @@ -26,7 +26,7 @@ func TestDirectoryCatalogRequiresSigninAndDoesNotInventLaunchDestinations(t *tes |
| if strings.Count(body, "<a ") != 1 || !strings.Contains(body, `href="/apps/docs"`) || !strings.Contains(body, "Choose a team") || strings.Contains(body, "<script") { |
| t.Fatal("catalog advertised unsupported launch", body) |
| } |
| - if strings.Count(body, "Not connected to Home yet") != 3 || strings.Count(body, "Coming soon") != 1 { |
| + if strings.Count(body, "Not connected to Home yet") != 3 || strings.Count(body, "Coming soon") != 1 || strings.Count(body, "Separate sign-in") != 2 { |
| t.Fatal("catalog readiness missing", body) |
| } |
| entry := f.request("GET", docsEntryPath, "", "", cookie) |