diff --git a/AGENTS.md b/AGENTS.md
index 858175ccb..393aed77d 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -208,6 +208,11 @@ A run-bearing analysis-run registry empties only after an unrevoked
(ADR 0020 / v0.87.0). The documented phrase is not a secret. Do not
expose purge on a public HTTP route.
+Unauthenticated **Log in** must call `returnUrlFromLocation()` then
+`rememberOidcReturnUrl` before `signinRedirect` (ADR 0109) so a
+shared `/?post=` link still opens that post. Do not mount tenant admin
+settings on the signed-out login shell.
+
`POST /api/analysis-runs` records Pending lineage only (ADR 0017 /
v2.7.1). TEPP and period-report kinds 422 before any snapshot write.
`POST /api/analysis-runs/{id}/start` reconstructs a Pending lineage
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index d4ff2e159..4cb521e8a 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -301,7 +301,9 @@ config baked in at build time from the same `.env` ports every other
service uses (Vite embeds `import.meta.env.VITE_*` at build time, not
runtime, so these are Docker build args, not container env vars).
`src/App.test.tsx` mocks `react-oidc-context`'s `useAuth` to test the
-component's own render logic (login button -> `signinRedirect()`; the
+component's own render logic (login button stores a safe return path
+then `signinRedirect()`; the signed-out shell never mounts tenant
+admin settings; the
A-100 fork DAG shows a branch point and rec-006 as its own root;
`post_admin` can rebuild; fetch posts with the token -> render list ->
click -> popup shows the fetched body and every panel; ask a chat
diff --git a/CHANGELOG.d/2.12.19-oidc-login-return-remember.md b/CHANGELOG.d/2.12.19-oidc-login-return-remember.md
new file mode 100644
index 000000000..1d590260f
--- /dev/null
+++ b/CHANGELOG.d/2.12.19-oidc-login-return-remember.md
@@ -0,0 +1,6 @@
+## 2.12.19 — Remember the login return path
+
+- Log in now stores a validated same-origin return path (ADR 0109)
+ before the OIDC redirect, so a shared `/?post=` link still opens that
+ post after callback. Tenant admin settings stay off the signed-out
+ login shell so the production frontend build type-checks.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 52390dabc..6959d29b5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -203,6 +203,16 @@ All notable changes to this project are documented here. Format follows
- The static SQL review contract now counts the Customer Master evidence query
that uses closed schema fragments and bound entity ids.
+## [2.12.19] - 2026-08-24
+
+### Fixed
+
+- Log in remembers a validated same-origin return path (ADR 0109) before
+ the OIDC redirect, so a shared `/?post=` link still opens that post
+ after callback. Tenant admin settings stay off the signed-out login
+ shell. The production frontend build type-checks again.
+
+
## [2.12.6] - 2026-08-20
### Added
diff --git a/docs/adr/0109-oidc-deep-link-state-recovery.md b/docs/adr/0109-oidc-deep-link-state-recovery.md
index 808f12aff..832a4b70a 100644
--- a/docs/adr/0109-oidc-deep-link-state-recovery.md
+++ b/docs/adr/0109-oidc-deep-link-state-recovery.md
@@ -21,6 +21,11 @@ when the member's OIDC session is otherwise valid.
- Persist the same validated same-origin path in both `sessionStorage` and
`localStorage` before redirecting to OIDC. `localStorage` is only a bounded
recovery fallback, not an authentication or authorization store.
+- The signed-out **Log in** control computes that path with
+ `returnUrlFromLocation()`, persists it with `rememberOidcReturnUrl`, and
+ passes the same value in `signinRedirect` state.
+- Tenant admin settings mount only after authentication provides an access
+ token; the signed-out login shell never renders `AdminPanel`.
- On callback, remove the key from both stores and use session storage before
local storage. Reject external and protocol-relative URLs.
- Keep member language preference account-scoped in
diff --git a/frontend/package.json b/frontend/package.json
index 56b813d28..5261a4354 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,7 +1,7 @@
{
"name": "frontend",
"private": true,
- "version": "2.12.6",
+ "version": "2.12.19",
"type": "module",
"scripts": {
"dev": "vite",
diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx
index 4a9ad9ec9..bfde3b7b0 100644
--- a/frontend/src/App.test.tsx
+++ b/frontend/src/App.test.tsx
@@ -32,6 +32,8 @@ beforeEach(() => {
afterEach(() => {
vi.unstubAllGlobals();
+ window.sessionStorage.clear();
+ window.localStorage.clear();
});
describe("App, unauthenticated", () => {
@@ -55,6 +57,27 @@ describe("App, unauthenticated", () => {
);
});
+ it("remembers a same-origin post deep link before the OIDC redirect", async () => {
+ window.history.replaceState({}, "", "/?post=synthetic-post-ada");
+ render(