Feat v0.9.0 multi-surface packages (#73)
All checks were successful
CI/CD / detect-changes (pull_request) Successful in 4s
CI/CD / test-runners (pull_request) Has been skipped
CI/CD / e2e-smoke (pull_request) Has been skipped
CI/CD / test-frontend (pull_request) Successful in 5s
CI/CD / test-go-pg (pull_request) Successful in 2m48s
CI/CD / test-sqlite (pull_request) Successful in 2m57s
CI/CD / build-and-deploy (pull_request) Successful in 1m23s

Packages can declare a `surfaces` array with per-path access controls,
titles, and layouts. A single package can serve a public form, an
authenticated dashboard, and an admin page — each with independent
access enforcement.

Kernel:
- Manifest validation for surfaces array (path, access, duplicates)
- Auto-synthesis from legacy auth/layout for existing packages
- Unified /s/:slug route tree (RegisterExtensionRoutes) dispatches
  between surface rendering and ext API calls
- matchSurface() with Gin-style :param support and specificity ordering
- evaluateAccess() for per-surface access checks
- Nav filters out pending_review packages (pre-existing bug fix)

Frontend:
- __SURFACE_PATH__ and __SURFACE_PARAMS__ template injection
- sw.navigate(path, params) for SPA-style intra-package routing
- surface.navigate event + popstate handling
- SDK version bumped to 0.9.0

Docs:
- MULTI-SURFACE-GUIDE.md — full developer guide
- PACKAGE-FORMAT.md — surfaces field reference
- CHANGELOG.md, ROADMAP.md updated

22 new tests (11 manifest validation, 11 route matching/nav).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-03 12:34:38 +00:00
parent 98fd3eb3e6
commit c2473efee2
12 changed files with 1075 additions and 62 deletions

View File

@@ -2,6 +2,78 @@
All notable changes to Armature are documented here.
## v0.9.0 — Multi-Surface Packages
Packages can now declare multiple surfaces — each with its own path, access
level, title, and layout. A single package can serve a public submission
form, an authenticated dashboard, and an admin settings page. The kernel
resolves incoming requests to the correct surface and enforces access
server-side.
**Manifest: `surfaces` array**
- Packages declare a `surfaces` array with entries like
`{ "path": "/submit", "access": "public", "title": "Report a Bug" }`.
- Each surface has independent `path`, `access`, `title`, `layout`, and
`nav` fields. Package-level `auth` and `layout` serve as defaults.
- Supported access levels: `public`, `authenticated`, `admin`, `group:{name}`.
- Packages without `surfaces` get auto-synthesis from legacy `auth`/`layout`
fields — no existing packages break.
**Kernel: unified route tree**
- Surface handler and ext API handler share a single `/s/:slug` route tree
via `RegisterExtensionRoutes`. The dispatcher checks the path prefix:
`/api/*` → ext API handler with JWT auth; everything else → surface
handler with per-surface access checks.
- `matchSurface()` resolves request paths against surface patterns with
Gin-style `:param` support. Static segments preferred over params.
- `evaluateAccess()` checks access requirements per-surface, with login
redirect for unauthenticated users and 403 for insufficient permissions.
**Frontend: `__SURFACE_PATH__` + `sw.navigate()`**
- `window.__SURFACE_PATH__` and `window.__SURFACE_PARAMS__` injected into
every extension surface page. Packages use these to decide which view to
render.
- `sw.navigate(path, params)` for SPA-style intra-package routing via
`pushState`. Emits `surface.navigate` events. Handles back/forward via
`popstate`.
- SDK version bumped to `0.9.0`.
**Navigation**
- `extensionNavItems` reads the `surfaces` array to find the nav entry
(first `nav: true`, or root `/`).
- Fix: packages with `status: pending_review` no longer appear in the
sidebar navigation.
**Validation**
- `ValidateManifest` validates `surfaces` entries: path required, must
start with `/`, no duplicates, access level must be recognized.
- Empty `surfaces` array rejected. Non-array `surfaces` rejected.
**Tests: 22 new**
- 11 manifest validation tests (surfaces valid/invalid, auto-synthesis,
group access, duplicate paths).
- 11 route matching tests (static paths, param extraction, specificity
ordering, nav resolution).
**Modified files:**
- `server/handlers/package_validate.go` — surfaces validation + auto-synthesis
- `server/handlers/package_validate_test.go` — 11 new tests
- `server/main.go` — unified extension route registration
- `server/pages/pages.go` — matchSurface, evaluateAccess, findNavSurface,
RegisterExtensionRoutes, nav status filter
- `server/pages/pages_surface_match_test.go` — 11 new tests
- `server/pages/templates/base.html` — surface path/params injection
- `src/js/sw/sdk/index.js` — sw.navigate, popstate, version bump
- `docs/PACKAGE-FORMAT.md` — surfaces field documentation
- `docs/MULTI-SURFACE-GUIDE.md` — developer guide
## v0.8.5 — Extension Composability
Extensions can now compose with each other through declared slots, UI