Notechondria

Version: 0.1.26 Build Date: 2026-04-18T04:00

What's Changed

Offline-first sidebar parity (planner + portal)

  • Planner and portal sidebars now hide cloud categories while the user is signed out, matching the editor behavior landed in 0.1.25. planner_app/lib/app_shell.dart and portal_app/lib/app_shell.dart each gate their courses: parameter on _token == null || _token!.isEmpty at the single call site where _LearnerPage composes _localCourses + _courses. When signed out the learner view sees only local categories; cached cloud note content stays addressable by UUID.

§1.7 migration: Editor.Auth round

  • editor_app/lib/app_shell.dart auth methods migrated to the canonical "<consequence>: Editor.Auth/<process> \u2014 <cause>" shape (as documented in docs/AGENTS.md):
    • _register \u2192 Editor.Auth/register
    • _verify \u2192 Editor.Auth/verify
    • _resendVerification \u2192 Editor.Auth/resend_verification
    • _login \u2192 Editor.Auth/login
    • _requestPasswordReset \u2192 Editor.Auth/password.reset.request
    • _confirmPasswordReset \u2192 Editor.Auth/password.reset.confirm
    • _applyAuthPayload settings-bootstrap fallback \u2192 Editor.Sync.Settings/bootstrap
    • _applyAuthPayload success \u2192 Editor.Auth/applyAuthPayload
    • _logout \u2192 Editor.Auth/logout
  • Legacy _appendUiLog(String) wrapper preserved so other untouched call sites still compile; new code paths on this round call the richer _log(...) form.
  • Preserved substring sentinels verified: "invalid token", "token_not_valid", "authentication credentials were not provided", "not_registered", and "No account found" all still appear in the pre- or post-prefix text paths where the session-rejection and OAuth-registration detectors look for them.

§1.7 migration: Backend.Creators.Auth/bind.* phased detail

(task 5a)

  • BindGoogleApiView.post now fails with a distinct detail and appropriate HTTP status for each phase:
    • bind.google.config_lookup \u2192 503 when GOOGLE_OAUTH_CLIENT_ID or GOOGLE_OAUTH_CLIENT_SECRET is missing from server settings.
    • bind.google.token_exchange \u2192 502 on network errors reaching Google's token endpoint, 400 when Google rejects the code or returns no id_token.
    • bind.google.token_verify \u2192 502 on network errors, 400 when tokeninfo rejects the id_token or the audience mismatches.
    • bind.google.db_write \u2192 500 on an unexpected persistence failure, 409 on the pre-existing "already linked to another user" conflict path.
  • BindGithubApiView.post mirrors the same phased layout:
    • bind.github.config_lookup \u2192 503 on missing GitHub OAuth credentials.
    • bind.github.token_exchange \u2192 502 network errors, 400 when GitHub rejects code or returns no access token.
    • bind.github.profile_fetch \u2192 502 network errors, 400 when /user returns a non-200.
    • bind.github.db_write \u2192 same shared path via _BindOAuthMixin.
  • _BindOAuthMixin._bind_social_account wraps the SocialAccount.update_or_create in a try / except so a DB-side failure (uniqueness race, transient connectivity, etc.) is no longer surfaced as a generic 500 Internal Server Error but as Account linking failed: Backend.Creators.Auth/bind.<provider>.db_write \u2014 <cause>.
  • Existing creators.tests.OAuthBindRejectionTests still pass: the public bind substring sentinel used by test_google_public_endpoint_rejects_bind_intent and test_github_public_endpoint_rejects_bind_intent is untouched.

Files Changed

New

  • docs/versions/0.1.26.md (this file).

Modified

  • VERSION: 0.1.25 \u2192 0.1.26.
  • docs/TODO.md: 4b-extension item removed; 5b OAuth-callback-loading item removed (already landed in 0.1.25); \u00a71.7 migration checklist updated with Editor.Auth and Backend.Creators.Auth/bind.* marked done.
  • frontend/editor_app/lib/app_shell.dart: 8 auth-related messages rewritten to the \u00a71.7 shape; _applyAuthPayload's remote-settings-unavailable path now logs via _log at warning level with Editor.Sync.Settings/bootstrap source instead of concatenating a free-form _appendUiLog string.
  • frontend/planner_app/lib/app_shell.dart: _LearnerPage.courses expression gated on _token presence.
  • frontend/portal_app/lib/app_shell.dart: same gate.
  • backend/creators/api.py: _BindOAuthMixin, BindGoogleApiView.post, BindGithubApiView.post restructured for per-phase error responses with network-error wrapping.

Verification

  • flutter analyze on editor / planner / portal \u2014 issue count unchanged vs 0.1.25 (same pre-existing infos).
  • flutter test test/smoke_test.dart -r compact on all three apps \u2014 pass.
  • DJANGO_SETTINGS_MODULE=notechondria.settings_test python manage.py test creators -v 1 \u2014 29 tests, all pass, including the OAuthBindRejectionTests suite that asserts the bind substring sentinel survives.

Notes / follow-ups

  • Editor.Sync.*, Editor.LocalStore, Editor.UI rounds remain open in docs/TODO.md \u00a7"\u00a71.7 message-compliance migration". Do not bundle them with unrelated changes; each module ships as its own commit.
  • Planner / portal Auth rounds still need the same Editor.Auth-style migration. Defer until their auth code paths are next touched.
  • The Flutter host code that catches bind failures already surfaces the server's detail verbatim via SnackBar, so no frontend code change was needed to use the new phased messages \u2014 the richer detail simply flows through.