studio-engine 0.74.12 → 0.76.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c4fe6cb6d0167ad680d5d6e9dc577a623c87e83d839847be79950877b922d576
4
- data.tar.gz: 07d63386f006d3bfba60556910097f0f0797e52f28f6bcde41e38c37e4af9326
3
+ metadata.gz: 07421317c32cb9dd4b3bb4c4db45f3877996d67304d6162f38a7ba9e93e59ff0
4
+ data.tar.gz: 2a913fa2583f81c901cca276050dffe751f70ddbdd856c6abf4b2270167e1f9a
5
5
  SHA512:
6
- metadata.gz: a46c78e389a2776d874522186127a1a16ae5bca04429ee3879a3bd0b1a8ce4e669d382803d94a2f3becb127c8735cd40255f62ef848cdfec3a76e912997d347d
7
- data.tar.gz: caab23ce4c8cd6788ad50bc28391302b9b388015dd33e19a80855ba708f335f38653a449af6afd3b4afe43dd92a7cd87233ed51874f831c7cf175d6109ebe76e
6
+ metadata.gz: 5ed8a3771ddd488e56740a892d6b0b01f1b2e899e7916718975a45f1793b157984df97ee1645d179624ee3c0766126ed9db7e47366a250d159c1aed7196e4df6
7
+ data.tar.gz: 8ab8227c9695fdc3f1efc446151af57b32c19a88f915556268426e99027b2accf08496c408d73d0b82b201c3ba2b312905fd33b2fe11b5396705d6fc702ac398
data/CHANGELOG.md CHANGED
@@ -4,6 +4,85 @@ The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This pro
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.76.0 — 2026-09-17
8
+
9
+ ## 0.75.0 — 2026-09-17
10
+
11
+ ### Added
12
+
13
+ - **Session drift: every page now knows when the browser's session changed
14
+ underneath it.** Another tab signs in or out, the session expires, or the
15
+ server revokes it; the page notices, repairs itself in place when the host
16
+ allows, and tells a deliberate switch from an accidental one. Generic by
17
+ design: it knows sessions, accounts and fingerprints, and an app or companion
18
+ gem binds anything else to the session through an identity-source plug-in.
19
+ Contract: [`docs/SESSION_DRIFT.md`](./docs/SESSION_DRIFT.md).
20
+ - **The stamp, on every page, with no wiring.** `layouts/studio/_head` renders
21
+ `studio/_session_stamp`: `<meta name="studio-session">` carrying the
22
+ session's state, fingerprint, issue and expiry times, rehydrate URL and bound
23
+ identities, plus the store script. It renders only for a controller that
24
+ includes `Studio::ErrorHandling` (every consumer's `ApplicationController`);
25
+ any other head is byte-identical to before.
26
+ - **`Studio::SessionState`**: `STATES` (anonymous, authenticated, stale,
27
+ changed, rehydrated, signed_out), `SERVER_STATES`, `#state`, `#anonymous?`,
28
+ `#authenticated?`, `#fingerprint` and `#to_stamp`, built from the viewer
29
+ alone. `SessionContext` exposes the same by delegation; its constructor and
30
+ `#to_h` are unchanged.
31
+ - **`Studio::SessionFingerprint`**: an HMAC of the user's id, `session_token`
32
+ and bound identities, keyed from `secret_key_base`. Rotating the token or
33
+ re-binding an identity changes it; it never carries either readably.
34
+ - **`Studio::SessionDrift`**, included by `Studio::ErrorHandling`: helpers
35
+ `studio_session_stamp`, `studio_session_page_stamp` (logs to `ErrorLog` and
36
+ omits the stamp in production rather than failing a page),
37
+ `studio_session_state`, `studio_session_rehydrate_url`, and the host hook
38
+ `studio_session_identities`.
39
+ - **`GET /session/state`** (`Studio::SessionStatesController`), OFF by default:
40
+ `config.draw_session_routes = true`. Returns the stamp, the host's
41
+ `client_session_payload` and a fresh CSRF token. Anonymous gets a 200; a host
42
+ filter's 401 reads as revoked.
43
+ - **`window.StudioSession`** (`studio/session.js`, precompiled): `current`,
44
+ `subscribe`, `refresh`, `expectChange(scope)`, `registerIdentitySource`,
45
+ `observed`, `configure`; `session:changed` and `session:mismatch` events on
46
+ `document`; `Alpine.store('studioSession')`. Ships built-in sources for other
47
+ tabs (BroadcastChannel `studio-session`), expiry (after a grace, so probing
48
+ never renews a sliding session), and a server probe on returning to a hidden
49
+ tab or a bfcache restore. Back to a cached page of the same session keeps
50
+ what the tab already learned. Holds are capped at 10 minutes. It never
51
+ touches a host's own stores.
52
+ - Tested by executing the store under node, one vm context per tab, every
53
+ scenario asserted by name (`test/views/studio_session_store_test.rb`); two
54
+ browser-lane specs for real delivery and real cross-tab messaging
55
+ (`e2e/session_drift.spec.js`, lane contract 138 -> 140); a dummy-app
56
+ integration suite (`test/integration/session_drift_test.rb`); unit suites for
57
+ the state, the context delegation and the fingerprint; and a vocabulary guard
58
+ over the primitive's code, docs, tests and this entry
59
+ (`test/lib/session_drift_vocabulary_test.rb`).
60
+ - **`docs/USER_CONTRACT.md` documents two methods the engine already called
61
+ without listing:** `#session_token` and `#regenerate_session_token!`.
62
+
63
+ ### Fixed
64
+
65
+ - **The toast halo and the success card's explorer link now paint their
66
+ tints.** Both used the legacy `rgba(var(--color-primary-500-rgb), A)` form.
67
+ `Studio::ThemeResolver` emits every `-rgb` var as a space-separated list, and
68
+ that list makes the comma form invalid, so browsers dropped each declaration
69
+ without an error. `layouts/studio/_flash`'s `.dark .toast-blur-glow` (the
70
+ `blurShadow: true` halo) painted no halo in dark mode. The Solana explorer
71
+ link in `blocks/_success_card` (`tx_solana: true`) lost its tinted backing in
72
+ both themes. Both now use the slash form, `rgb(var(--x-rgb) / A)`.
73
+ - Measured in headless Chromium on a page built from the engine's
74
+ consumer-style Tailwind build and the resolver's real theme vars. Before,
75
+ both elements computed `rgba(0, 0, 0, 0)`. After, the dark halo computed
76
+ `rgba(142, 130, 254, 0.12)` and the link `rgba(142, 130, 254, 0.06)`.
77
+ - Guarded by `test/views/legacy_rgba_var_guard_test.rb`. It refuses the
78
+ legacy form in every code file the gemspec packages, in the
79
+ `Studio::UiPrimitives` CSS strings, and in a compiled consumer build. It
80
+ also pins the premise that every `-rgb` triple the engine defines is a
81
+ space-separated list.
82
+ - `RUNBOOK.md` gains the symptom and its fix. Its palette entry no longer
83
+ says a nil or blank primary skips palette generation; on the
84
+ `studio_theme_css_tag` path, both fall back to a default.
85
+
7
86
  ## 0.74.12 — 2026-09-16
8
87
 
9
88
  ### Fixed
data/README.md CHANGED
@@ -31,6 +31,7 @@ resolved.
31
31
 
32
32
  - **Authentication**: Passwordless magic-link auth, optional password auth, Google OAuth via OmniAuth, Solana wallet sign-in, and optional one-way SSO patterns
33
33
  - **Error handling**: `Studio::ErrorHandling` concern with `rescue_and_log`, `ErrorLog` model with `capture!`, error log viewer at `/error_logs`
34
+ - **Session drift**: every page carries a session stamp and loads `window.StudioSession`, which notices when another tab signs in or out, the session expires, or the server revokes it; `session:changed` / `session:mismatch` events, cross-tab sync, an identity-source plug-in interface, and `expectChange` holds for deliberate switches. The rehydrate endpoint (`GET /session/state`) is opt-in. See [`docs/SESSION_DRIFT.md`](docs/SESSION_DRIFT.md).
34
35
  - **Theme system**: Dynamic CSS custom properties generated from 7 role colors (primary, dark, light, success, accent, warning, danger). Dark/light mode toggle. Admin theme editor at `/admin/theme`.
35
36
  - **UI primitives**: Shared component partials and CSS primitives such as `components/emoji_swap` for nav/sidebar emoji hover transitions.
36
37
  - **Operator tooling**: Shared `studio/banners/environment` banner with Dev Mode + email connector controls, `studio/banners/impersonation`, and an opt-in `Studio::Impersonation` concern for Act As session conventions.
@@ -120,6 +121,8 @@ end
120
121
 
121
122
  This draws the enabled auth routes (`/login`, `/signup`, `/logout`, `POST /magic_link` to request a link, `GET`/`POST /l/:token` for the link itself, Solana routes), OAuth callbacks, optional SSO routes, `/error_logs`, and `/admin/theme`. Set `Studio.draw_geo_routes = true` to add the geo manager (`/admin/geo`) and its public probe (`/geo/check`) — off by default because turf-monster owns those helper names until its adoption lands. Magic-link emails point at the inert `GET /l/:token` confirmation page; the single-use token is burned only by the CSRF-protected `POST` to `link_consume_path`.
122
123
 
124
+ Set `Studio.draw_session_routes = true` to add the session-drift rehydrate endpoint (`GET /session/state`) — off by default because it inherits the host's filters, which an app should check first ([`docs/SESSION_DRIFT.md`](docs/SESSION_DRIFT.md)).
125
+
123
126
  **Magic links need the `studio_links` table.** Install it with `bin/rails studio_engine:install:migrations && bin/rails db:migrate` (install all of them) before enabling `:magic_link` — never by hand-copying the migration, which collides with the task's own copy on `class CreateStudioLinks`. Without the table, the first sign-in raises `Studio::Link::MissingTable`.
124
127
 
125
128
  In non-production local requests, this also draws `/_studio/local_emails`, a local email inbox for agent/worktree proof flows. Set `LOCAL_EMAIL_CAPTURE=1` or run with `AGENT_WORKTREE=1` to record outbox rows without sending real email.