feat(bio): Phase 4 — bio self-edit UI at /[lang]/my-bio (closes #2)#174
Conversation
Final phase of the cdcf-bio-edit-zitadel plan. Adds the TipTap-based
editor backed by the Phase 3 REST endpoints. Authenticated team
members can now click "Edit my bio" in the header dropdown, land on a
language-switchable form, write in any of their available locales,
and trigger the re-translation fan-out via Save.
New surface
-----------
- app/[lang]/my-bio/page.tsx Server component. requireSession() →
fetchMyTeamMember() (discovery) →
pick initial lang (Zitadel locale →
URL locale → first available) →
fetchTeamMemberPost() → render the
editor. Friendly fallbacks for the
no-link / no-langs / load-error
paths surface localized copy
instead of internal errors.
- components/BioEditor.tsx Client. TipTap StarterKit + Link
(rel=noopener, target=_blank, no
autolink). Editable: post_content,
member_title, member_linkedin_url,
member_github_url. Read-only:
post_title (the team member's name —
admin-managed). Language <select>
triggers /api/my-bio/load/{lang}
with an unsaved-changes
window.confirm() guard. Save is
disabled until isDirty AND wired up
so editing again clears the prior
status banner via a shared
markDirty() callback (no
setState-in-effect cascade).
- app/api/my-bio/check/route.ts GET → {linked, available_languages}.
Used by AuthButton to decide whether
to render the "Edit my bio" entry.
Anon / not-linked / 403 from WP all
collapse to {linked: false} so the
dropdown never blinks an error.
- app/api/my-bio/load/[lang]/ GET → editable post content for the
route.ts language. Resolves post_id
server-side from the discovery
payload (the client never gets to
pick which post it loads).
- app/api/my-bio/save/route.ts POST → forwards the PATCH to WP
with the bearer attached server-
side. The access token never reaches
the browser. Surfaces BioApiError
shape back as JSON.
- lib/bio-api.ts Server-only helpers reused by the
page + all three route handlers:
fetchMyTeamMember,
fetchTeamMemberPost (context=edit,
raw → rendered fallback), and
saveMyTeamMember. BioApiError class
carries WP status + code into the
Next.js error path. `server-only`
guard preserved for the bundler-
level safety net.
Header dropdown
---------------
- components/AuthButton.tsx Adds a one-shot fire-and-forget
/api/my-bio/check on transitioning
to authenticated. Stale state never
leaks because the menu is gated by
session.user; on sign-out the menu
disappears entirely so a stale
hasBioLink=true is invisible.
i18n
----
- messages/{en,it,es,fr,pt,de}.json
+ Auth.editMyBio (header dropdown entry)
+ MyBio.* (17 keys covering page chrome, form labels, save status,
unsaved-changes confirm, and three error fallbacks)
- All edits applied via append-only Python splice (no whole-file
reformatting) to keep diffs minimal.
Vitest setup
------------
- vitest.config.ts adds a `server-only` alias to a no-op stub plus an
`@/` resolver so library tests can import server modules without the
real `server-only` package's runtime throw.
- tests/stubs/server-only.ts is the stub.
Tests added (11 new in lib/bio-api.test.ts; 145 total vitest)
- fetchMyTeamMember: attaches bearer + parses payload; 401 when no
access token on session; surfaces WP 403; throws 500 when
WP_REST_URL unset.
- fetchTeamMemberPost: hits /wp/v2 with context=edit, normalises
raw → rendered, surfaces 404.
- saveMyTeamMember: PATCH method + body shape; rejects malformed lang
before network; surfaces WP rest_invalid_url 400 + rest_forbidden
403.
Deliberately out of scope (consistent with the locked plan)
-----------------------------------------------------------
- Featured image edits (separate issue per locked decision #4).
- Server-side role gating beyond presence of the
author_team_member link — the Zitadel `team_member` role is
available but not yet enforced; the link itself is the canonical
ownership signal.
- Optimistic UI / persistent toasts beyond the inline status banner.
Verification
- next build: clean, 4 new routes registered
- npm run lint: clean (no setState-in-effect cascades)
- npm test: 145 / 145 (was 134 — +11 bio-api)
- npm run lint:md: 0 errors
- prettier check: clean
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
More reviews will be available in 33 minutes and 22 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR adds a complete "bio self-edit" feature enabling team members to edit their multilingual profiles. It introduces server-side API helpers for WordPress REST integration, three HTTP routes exposing those helpers, a server page to render the editor, a Tiptap-powered client editor component, auth dropdown integration, and full multilingual support with tests. ChangesTeam Member Bio Self-Edit Feature
Sequence DiagramsequenceDiagram
participant User
participant MyBioPage
participant BioEditor
participant LoadAPI as /api/my-bio/load/{lang}
participant SaveAPI as /api/my-bio/save
participant WordPress as WordPressREST
User->>MyBioPage: open /[lang]/my-bio (server-page)
MyBioPage->>LoadAPI: resolve discovery -> post_id -> fetch initial post
LoadAPI->>WordPress: GET /wp/v2/team_member/{postId}?context=edit
WordPress-->>LoadAPI: Post JSON
MyBioPage-->>User: render BioEditor(initialPost, availableLanguages)
User->>BioEditor: change language
BioEditor->>LoadAPI: GET selected lang
LoadAPI->>WordPress: GET post
WordPress-->>LoadAPI: Post JSON
LoadAPI-->>BioEditor: BioPostContent
User->>BioEditor: click Save
BioEditor->>SaveAPI: POST {lang, content, member_*}
SaveAPI->>WordPress: PATCH /cdcf/v1/my-team-member/{lang}
WordPress-->>SaveAPI: Save response / errors
SaveAPI-->>BioEditor: queued or error JSON
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 142 |
| Duplication | 6 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
package.json (1)
23-26: ⚡ Quick winTiptap versions/security look OK; consider dropping
@tiptap/pmif unused
@tiptap/extension-link,@tiptap/pm,@tiptap/react, and@tiptap/starter-kitall exist at3.26.0on npm (as pinned inpackage.jsonlines 23–26).- GitHub security advisories show no issues for
@tiptap/pm,@tiptap/react, or@tiptap/starter-kit; the@tiptap/extension-linkXSS advisory applies to< 2.10.4, so3.26.0is outside the vulnerable range.- Repo-wide search finds no direct imports/require of
@tiptap/pm(only the dependency entry inpackage.json), so it can likely be removed to simplify dependency management if nothing depends on it transitively.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@package.json` around lines 23 - 26, package.json declares "`@tiptap/pm`" at version "^3.26.0" but the repo search shows no imports/requires of that package; remove the "`@tiptap/pm`" dependency entry from package.json (the unique symbol to locate is the "`@tiptap/pm`" key), then run npm/yarn install and the test suite and optionally npm audit / npm ls `@tiptap/pm` to ensure nothing else depends on it transitively; if any consumer requires it, restore or add a comment explaining why it must remain.components/BioEditor.tsx (1)
176-180: ⚡ Quick winReplace
<label>with<div>for read-only display.The
<label>element should only be used for form controls. Since the post title is read-only text (not an input), use a<div>or<span>instead.♻️ Proposed fix
<div> - <label className="block text-xs font-medium uppercase tracking-wide text-gray-500"> + <div className="block text-xs font-medium uppercase tracking-wide text-gray-500"> {t('postTitleLabel')} - </label> + </div> <p className="mt-1 text-base">{currentTitle}</p> </div>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/BioEditor.tsx` around lines 176 - 180, In the BioEditor component replace the semantically incorrect <label> used for read-only text with a non-form container: change the <label className="block text-xs font-medium uppercase tracking-wide text-gray-500"> that renders {t('postTitleLabel')} to a <div> (or <span>) preserving the same className and text; ensure the surrounding structure that displays {currentTitle} remains unchanged so styling and layout are preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/`[lang]/my-bio/page.tsx:
- Line 32: The variable `discovery` is declared without a type; replace `let
discovery` with an explicit annotation such as `let discovery:
Awaited<ReturnType<typeof getDiscovery>> | undefined` (or `Discovery |
undefined` if you have a `Discovery` interface/type) — if you use a different
fetch helper, substitute `getDiscovery` with that function so the type is
inferred from its return type and avoids `any`.
In `@app/api/my-bio/save/route.ts`:
- Around line 25-30: The handler currently spreads body into payload without
runtime validation (symbols: lang, payload, BioSavePayload) before calling
saveMyTeamMember; add explicit runtime validation: ensure lang is string
(already done), then whitelist expected payload keys (e.g., content and any
other allowed fields) and check each required/optional field has the correct
type (e.g., typeof content === 'string'), reject requests with extra keys or
wrong types by returning a 400 JSON error, and only pass the sanitized object to
saveMyTeamMember.
---
Nitpick comments:
In `@components/BioEditor.tsx`:
- Around line 176-180: In the BioEditor component replace the semantically
incorrect <label> used for read-only text with a non-form container: change the
<label className="block text-xs font-medium uppercase tracking-wide
text-gray-500"> that renders {t('postTitleLabel')} to a <div> (or <span>)
preserving the same className and text; ensure the surrounding structure that
displays {currentTitle} remains unchanged so styling and layout are preserved.
In `@package.json`:
- Around line 23-26: package.json declares "`@tiptap/pm`" at version "^3.26.0" but
the repo search shows no imports/requires of that package; remove the
"`@tiptap/pm`" dependency entry from package.json (the unique symbol to locate is
the "`@tiptap/pm`" key), then run npm/yarn install and the test suite and
optionally npm audit / npm ls `@tiptap/pm` to ensure nothing else depends on it
transitively; if any consumer requires it, restore or add a comment explaining
why it must remain.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a4369129-dc9c-4fb2-a057-7ff155d5c780
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (17)
app/[lang]/my-bio/page.tsxapp/api/my-bio/check/route.tsapp/api/my-bio/load/[lang]/route.tsapp/api/my-bio/save/route.tscomponents/AuthButton.tsxcomponents/BioEditor.tsxlib/bio-api.test.tslib/bio-api.tsmessages/de.jsonmessages/en.jsonmessages/es.jsonmessages/fr.jsonmessages/it.jsonmessages/pt.jsonpackage.jsontests/stubs/server-only.tsvitest.config.ts
Three findings verified valid, one skipped with reason.
[HIGH, no-implicit-any] app/[lang]/my-bio/page.tsx
`let discovery` was inferred as `any`. Added the BioDiscovery type
import and an explicit annotation `let discovery: BioDiscovery`.
[HIGH, jsx-a11y/label-has-associated-control] components/BioEditor.tsx
The "Name" label for the read-only display had no associated input
(the team member's display name is admin-managed, so there's no
control to label). Swapped <label> for <span> with the same
className and a comment explaining the choice.
[best practice, input validation] app/api/my-bio/save/route.ts
Previous handler spread the JSON body into `payload` via `{lang,
...payload}` and trusted the TS cast. Replaced with an
ALLOWED_PAYLOAD_FIELDS allow-list constant + a per-field type check:
unknown keys are silently dropped (forward-compat friendly), allow-
listed keys present with a non-string value reject the whole
request with HTTP 400 + {error: 'invalid_field', field}. WP-side
sanitize_callbacks still run downstream — this is the first
validation checkpoint.
[skipped, package.json — @tiptap/pm unused]
CodeRabbit flagged @tiptap/pm as unused because no source file
imports it directly. The package IS a required peer dependency of
@tiptap/react, @tiptap/starter-kit, @tiptap/core, and
@tiptap/extension-link (verified via the peerDependencies blocks in
each package.json). Removing it would trigger peer-dep warnings and
break runtime ProseMirror plugin resolution. Keeping the explicit
install entry is the documented TipTap pattern. No change.
Verification
- next build: clean, 4 routes registered
- npm run lint: clean
- npm test: 145 / 145
- npm run lint:md: 0 errors
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Codacy + CodeRabbit findings addressed in
Note on the stale TS diagnostics: the system reminder also listed several Verification: |
|
@coderabbitai review |
✅ Action performedReview finished.
|
…ssenger bind-address leak Sign-in on staging produced an OIDC redirect_uri of https://0.0.0.0:3000/api/auth/callback/zitadel, which Zitadel rejected: "invalid_request: requested redirect_uri is missing in the client configuration." Same root cause as the existing locale-redirect Location-header leak (memory project_plesk_passenger_port_leak): Next standalone under Phusion Passenger surfaces its bind address (0.0.0.0:3000) to the app — and unlike Express et al., Next.js does NOT honor X-Forwarded-Host / X-Forwarded-Port here, so trustHost:true alone isn't enough to fix Auth.js's request.url-derived callback URL. Auth.js v5 reads process.env.AUTH_URL first when constructing the callback. We already configure NEXT_PUBLIC_SITE_URL per-environment at build time AND it's available at runtime in Plesk's app env (per-domain Settings). Promote it to AUTH_URL at lib/auth.ts module load time so the operator doesn't have to set a second env var: if (!process.env.AUTH_URL && process.env.NEXT_PUBLIC_SITE_URL) { process.env.AUTH_URL = process.env.NEXT_PUBLIC_SITE_URL } This runs BEFORE the NextAuth({}) call, so Auth.js picks up the value on first request. Doesn't override an explicit AUTH_URL if one is deliberately set (e.g. behind multiple proxies where SITE_URL and the public origin differ). .env.local.example gets a commented-out AUTH_URL stub + explanation of the auto-promotion so deployers know the fallback exists. Memory update: project_plesk_passenger_port_leak.md gains the sibling- symptom section so the next person hitting the OIDC version of this finds the connection back to the locale-redirect case in proxy.ts. Verification - next build: clean - npm run lint: clean - npm test: 145 / 145 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…_GRAPHQL_URL
Staging surfaced a 500 from /api/my-bio/check after a user signed in
but wasn't yet linked to a team_member post. Two issues compounded:
1. The check route only collapsed BioApiError 401/403 to
{linked: false}; any other status (500 from a missing env var,
network errors that escape BioApiError entirely) propagated as a
500 to the browser console. This is a UI-decoration endpoint, not
a security boundary — the dropdown's "Edit my bio" entry should
silently disappear on any failure, never blink an error.
2. The bio-api helpers required WP_REST_URL explicitly, but the
historical default on these deployments is WP_GRAPHQL_URL (set in
deploy.yml via vars.WP_GRAPHQL_URL). WP_REST_URL was added later
for the Python CLI's host-perspective default and isn't reliably
set at the Next.js runtime in Plesk's per-domain env. With only
WP_GRAPHQL_URL set, every helper call threw
BioApiError(500, 'config_missing') — that 500 plus the unconditional
re-throw in /check is exactly what produced the console error.
Changes:
- lib/bio-api.ts::getWpRestUrl() now falls back to
WP_GRAPHQL_URL?.replace(/\/graphql\/?$/, '/wp-json') when
WP_REST_URL is unset. Both vars resolve to the same WP origin in
every production-shaped deploy; same trick as the deferred
feature/zitadel-integration branch's WP sync logic. Only when BOTH
are unset do we throw the config_missing error.
- app/api/my-bio/check/route.ts replaces the narrow 401/403 catch
with a broad catch-all that returns {linked: false} for every
failure and console.error's the underlying error for server-side
diagnosis. Anonymous / unlinked / expired token / unreachable WP /
missing env var all collapse to the same HTTP 200 payload. The
/[lang]/my-bio page still surfaces real errors as localized copy
via its own per-status handling, so this fail-soft only affects
the dropdown-decoration endpoint.
Tests
- Renamed the WP_REST_URL-unset test to make the "both unset"
precondition explicit.
- Added a new fallback test confirming `https://cms.example.org/graphql`
→ `https://cms.example.org/wp-json/cdcf/v1/my-team-member`.
- bio-api.test.ts beforeEach now clears WP_GRAPHQL_URL too, so
fallback-branch tests start from a clean slate.
Verification
- npm test: 146 / 146 (was 145, +1 for the fallback test)
- npm run lint: clean
- next build: clean
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
app/api/my-bio/check/route.ts (1)
24-24: ⚡ Quick winConsider adding an explicit type annotation for clarity.
While TypeScript infers the type from
fetchMyTeamMember, an explicit annotation would improve readability and align with the pattern from commit 902bf71 where similar type annotations were added.📝 Suggested change
- const discovery = await fetchMyTeamMember(session) + const discovery: BioDiscovery = await fetchMyTeamMember(session)Add the import if not present:
-import { fetchMyTeamMember } from '`@/lib/bio-api`' +import { fetchMyTeamMember, type BioDiscovery } from '`@/lib/bio-api`'🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/api/my-bio/check/route.ts` at line 24, Add an explicit type annotation for the variable `discovery` returned by `fetchMyTeamMember` in the `route.ts` handler: import the appropriate type (the same interface/type used elsewhere for team member discovery) and annotate `discovery` (e.g., const discovery: <TypeName> = await fetchMyTeamMember(session)). Update the import list to include that type if missing and ensure the annotation matches the return type of `fetchMyTeamMember` for consistency with commit 902bf71.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@app/api/my-bio/check/route.ts`:
- Line 24: Add an explicit type annotation for the variable `discovery` returned
by `fetchMyTeamMember` in the `route.ts` handler: import the appropriate type
(the same interface/type used elsewhere for team member discovery) and annotate
`discovery` (e.g., const discovery: <TypeName> = await
fetchMyTeamMember(session)). Update the import list to include that type if
missing and ensure the annotation matches the return type of `fetchMyTeamMember`
for consistency with commit 902bf71.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8e03941d-c6c2-42db-a2fe-8b001fd8e1e7
📒 Files selected for processing (8)
.env.local.exampleapp/[lang]/my-bio/page.tsxapp/api/my-bio/check/route.tsapp/api/my-bio/save/route.tscomponents/BioEditor.tsxlib/auth.tslib/bio-api.test.tslib/bio-api.ts
✅ Files skipped from review due to trivial changes (1)
- .env.local.example
🚧 Files skipped from review as they are similar to previous changes (5)
- app/api/my-bio/save/route.ts
- lib/bio-api.test.ts
- app/[lang]/my-bio/page.tsx
- components/BioEditor.tsx
- lib/bio-api.ts
…(CodeRabbit nitpick) Mirror the same fix applied in 902bf71 for app/[lang]/my-bio/page.tsx. The discovery variable in app/api/my-bio/check/route.ts was missed at the time and inferred as `any` once it reaches the destructuring. Add the BioDiscovery type import + explicit annotation so the const is typed end-to-end. Verification - tsc --noEmit: clean - npm run lint: clean - npm test: 146 / 146 - next build: clean Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
CodeRabbit nitpick addressed in
Verification: |
Summary
Final phase of issue #2. Adds the TipTap-based bio editor UI on top of the bearer validator + Auth.js wiring + REST endpoints from #172. Closes the issue.
What lands
Pages + routes
app/[lang]/my-bio/page.tsxrequireSession()→ fetch discovery → pick initial language (Zitadel locale → URL locale → first available) → fetch current content → render editor. Friendly localized fallbacks for no-link / no-langs / load-error.app/api/my-bio/check/route.tsGET → {linked, available_languages}. Used by AuthButton; collapses all "not really linked" paths (anon / WP 403 / no link) to{linked: false}so the dropdown never blinks an error.app/api/my-bio/load/[lang]/route.tsGET → editable content for the lang. Resolvespost_idserver-side from discovery — client never picks which post it loads.app/api/my-bio/save/route.tsPOST → forwards PATCH to WP with bearer attached server-side. Access token never reaches the browser.Components
components/BioEditor.tsx— Client component. TipTapStarterKit+Link(rel=noopener, target=_blank, no autolink). Editable:post_content,member_title,member_linkedin_url,member_github_url. Read-only:post_title(the team member's name — admin-managed). Language<select>triggers/api/my-bio/load/{lang}with an unsaved-changesconfirm()guard. Save disabled untilisDirty.components/AuthButton.tsx— adds a one-shot fire-and-forget/api/my-bio/checkon transitioning toauthenticated. Conditional "Edit my bio" entry rendered between the email block and the sign-out button.Shared lib
lib/bio-api.ts— server-only helpers (fetchMyTeamMember,fetchTeamMemberPost,saveMyTeamMember) reused by the server page + all three route handlers.BioApiErrorclass carries WP status + code through to the Next.js error path.import 'server-only'preserved for the bundler-level safety net (vitest stubs it).i18n
messages/{en,it,es,fr,pt,de}.json:Auth.editMyBio(one new key for the header dropdown)MyBio.*(17 new keys: page chrome, form labels, save status, unsaved-changes confirm, three error fallbacks)All edits applied via append-only Python splice to avoid whole-file reformatting.
Tests
11 new vitest tests in
lib/bio-api.test.tscovering:/wp/v2/team_member/{id}?context=editURL + raw → rendered normalisationrest_invalid_url400 andrest_forbidden403 surfacedVitest now also resolves
server-onlyto a stub + the@/alias (config tweak invitest.config.ts).Verification:
Deliberately out of scope
Locked decisions from the plan held:
author_team_memberlink presence — the Zitadelteam_memberrole is wired but not yet enforced; the link itself is the canonical ownership signal that's already on by default for every linked user.Deploy notes
gh workflow run deploy.ymlwith default staging is sufficient. Production deploy happens via the next release.AUTH_*env vars (on the frontend) andCDCF_ZITADEL_EXPECTED_AUD(inwp-config.phpon the shared WP backend) must be configured per cdcf-infra handoff before the editor will actually authenticate. Without them, sign-in either fails at the OIDC redirect (frontend) or every PATCH 403s at WP's bearer validator.Test plan
author_team_memberlink → "Edit my bio" appears in dropdown/my-biopage redirects unauthenticated users to/api/auth/signinhttps://gitlab.com/mein GitHub field) → 400 surfaced inlineSummary by CodeRabbit
New Features
UI
API / Backend
Localization
Tests
Bug Fix