Replace the ALLOWED_USER_IDS sub-allowlist with a Zitadel project role
check: tokens must carry the role from REQUIRED_ROLE (default "user")
in the urn:zitadel:iam:org:project[:id]:roles claim. Roles are granted
per account in Zitadel (project ClaudeDo), where access is now managed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Tasks as dense divided rows: title clamps to 2 lines, note to 1;
tap a row to expand the full text (was: full-height cards → heavy scroll)
- List switcher moved from top chip row into the thumb zone: a "List" pill
in the dock opens a bottom sheet with all lists (52px rows, active check)
- Masthead title now shows the selected list; compacted header spacing
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The desktop pushes its full Idle backlog as a JSON array to /tasks/mirror, not per-task.
Previously /tasks/mirror matched tasks/[id].put.ts (id=mirror) and rejected the array with
400. New static route validates per-element, accepts empty arrays, upserts each as
consumed=true (desktop-owned), deletes consumed=true rows not in the array, and leaves
web-created consumed=false rows untouched. Mirrors PUT /lists.
Root cause: the router-guard adapter let index.vue mount and call the API before
auth resolved, so auth.fetch returned a synthetic 401 (the banner) and the package's
redirect-loop guard could strand the user. Now use the core ZitadelAuth and gate in an
async plugin (Nuxt awaits it before mount), mirroring the working krypto-kuns app.