Promote staging → production: design-system, agents, calendar rewire, storage lockdown - #305

Merged
AndresL230 merged 259 commits into
productionfrom
main
Jul 2, 2026
Merged

Promote staging → production: design-system, agents, calendar rewire, storage lockdown#305
AndresL230 merged 259 commits into
productionfrom
main

Conversation

@AndresL230

Copy link
Copy Markdown
Collaborator

Promote main (staging) → production. Routine promotion since the last one (PR #241, 2026-06-27). The DB modular redesign already shipped in #241; this batch is 64 real commits (the other 188 are #147 no-op checkpoints).

What ships

Frontend — design-system / token unification (#286, #294)

  • Shared Button / Toggle / Chip / Badge primitives + FilterPills; migrated high-impact buttons, selectors, and filters across Learn / Gradebook / Calendar / Social / Library / Study / Tree.
  • Single token namespace (removed aliases), (public) route group + .public-surface marketing layer, pre-auth token de-shadowing, a11y fixes (aria-pressed, neutral Badge text).

Backend — one-shot LLM → Pydantic AI agents (#147, #296)

  • New study_guide_agent, social_summary_agent, health_probe_agent on the shared provider + run_agent_sync bridge.
  • /api/gemini-test, study-guide generation, and social group summaries routed through agents (502 on failure); failure-contract tests added.

Calendar — enrollment rewire (#283)

  • Enrollment-scoped Google sync/export/update/delete and study-block reads; assignments written via resolved enrollment_id + source tag; syllabus saves tagged.

Security — storage lockdown (#231, #304)

  • Makes issues-media-files private, caps size/mime, drops the last anon storage policies. Requires migration 0029 on the prod DB (see below).

Fixes

  • Onboarding completion persistence (same-origin helper), OAuth popup COOP (same-origin-allow-popups), graph unexplored-node labels, local-mode mock data for gradebook detail + chatroom.

Prod-side steps (do around merge)

  1. Apply migration 0029 to proddotenv -f .env.production run -- python -m db.migrate (idempotent; storage hardening only, header notes it was verified read-only against prod).
  2. Merge → triggers Railway (production) + Cloudflare Workers Builds (frontend worker) prod deploys.
  3. Smoke test prod: sign-in, dashboard API calls, doc upload → graph, issue-report screenshot upload (service-role path).

Verified before opening

  • mainproduction merges clean (no conflicts).
  • CI green on main; no new backend env vars vs. last promotion.
  • Only new prod DB migration is 0029.

🤖 Generated with Claude Code

AndresL230and others added 30 commits June 28, 2026 01:56
Rewires sync_to_google and export_to_google onto the enrollment-keyed
schema: select/write-back scoped by enrollment_id membership instead of
the removed user_id column; drops courses!left embed in favour of
_course_meta_cached. Updates test_calendar_export_idor.py and
test_calendar_sibling_write_scoping.py to assert the new enrollment_id
boundary (same IDOR guarantee, new key).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ment schema
- routes/documents.py: pass source="syllabus" at both save_assignments_to_db
call sites (_save_orchestrator_syllabus and the legacy call_gemini_json path)
- tests/test_calendar_routes.py: rewire TestSaveAssignments to include course_id
in fixtures and mock enrollment_id_for/user_enrollment_ids; rewire
TestGetUpcoming.test_returns_assignments_from_db to the enrollment-keyed row
shape (enrollment_id, no user_id/course_id/courses columns); add _tbl helper
- tests/test_assignment_notes_encryption.py: supply course_id to test fixtures
and mock academics so insert_new_assignments reaches the encryption boundary
- tests/test_documents_routes.py: update assert_called_once_with to include
source='syllabus' to match the new tagged call
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nrollment-rewire
fix(calendar): rewire assignments to the enrollment-keyed schema (dashboard 500)
…legend
- Remove r > 10 gate in KnowledgeGraph2D so every concept node shows its
name regardless of mastery score (unexplored nodes have r=8, below old threshold)
- Capitalize legend labels: mastered/learning/struggling/unexplored → title case
- Add react-force-graph-3d (missing from node_modules, caused build error)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…g sticks
handleOnboardingComplete POSTed /api/onboarding/profile to the cross-origin
NEXT_PUBLIC_API_URL without credentials:'include', so the browser dropped the
sapling_session cookie and the backend's require_self returned 401. The
onboarding_completed flag never flipped to True — and because the code never
checked res.ok, the 401 was swallowed and users were routed to /dashboard as if
it worked, then bounced back to "Get Started" on every subsequent sign-in.
Switch to the existing submitOnboardingProfile() helper, which goes through the
same-origin lib/api.ts fetchJSON path (API_URL='' proxied via the /api/* rewrite,
credentials:'include', and an res.ok check that surfaces failures instead of
hiding them).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ersistence
fix(onboarding): persist completion so users aren't trapped in the Get Started loop
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ate states correct)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…plicate namespace
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rarity + glass-input
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng-layer host
Moves landing + about/careers/privacy/terms under app/(public)/ (URLs unchanged).
The layout applies a token-only .public-surface scope so content pages keep the
warm paper canvas; the landing retains .landing-page for its mesh visual.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(public)/page.tsx, HowItWorks.tsx, Dialog.tsx: --brand-text1/2 -> --text/--text-dim,
--radius-* -> --r-*, --ease-out/in-out -> --ease. Mechanical identifier renames only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ngle namespace
Migrates the 3 remaining globals.css --ease-out consumers to --ease, then deletes
the temporary :root brand aliases and the .public-surface/.landing-page namespace
aliases. Zero retired-token references remain tree-wide.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… + dir hygiene)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/page.tsx
The route move shifted the landing page out from under its path-keyed
suppression entry, un-suppressing 4 pre-existing grandfathered errors
(no-html-link-for-pages, prefer-const x2, react-hooks/immutability).
Re-homes the entry to the new path; no code behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rison
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Retire sage --accent -> brighter forest (--brand-forest-bright) for highlight/focus;
merge --state-mastery + --grade-a into one --positive status green; add .btn--lg
hero size for de-pilled CTAs. Three forest-family greens by role: forest(action) /
accent(highlight) / positive(status).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Thin wrappers over the canonical .btn/.chip classes. Button enforces one shape
(6px) with variant + size (incl. lg hero size); Toggle is the one segmented
control; Chip/Badge collapse the pill/badge zoo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ives
- Landing CTAs (Get Started x2, Sign up for Beta) -> <Button size=lg>, de-pilled
to sharp 6px, infinite glow removed.
- 5 Gradebook modal save/submit buttons -> <Button> (kills hard-coded borderRadius:6).
- Study flashcard ratings 10px -> 6px (kept color-coding).
- Tokenize orphan #1a5c2a wordmark -> var(--brand-forest) across 8 files.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add optional per-option title to <Toggle>; replace the Learn setup mode pills with
it. ModelToggle (Fast/Smart) intentionally left as-is — it has a sliding animation,
per-option color semantics, and a tooltip that the generic Toggle would degrade.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Flair
Badge carries the hue on border + soft bg, keeps text neutral (colored text fails
4.5:1 on several rarity tiers). TitleFlair now wraps Badge instead of inline styles.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…arted CTA
De-pilled CTAs hug the text now (lg 13/26 -> 9/18). The closing 'Get Started'
under the 'Ready to Start Growing?' hero uses a new xl size so it stays a
prominent central focal point; navbar + hero beta stay tight.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…bright accent
The header button hard-coded background:var(--accent); after accent shifted to the
brighter forest, it rendered brighter than every other primary button. Now a proper
<Button variant=primary> (--brand-forest), matching the term pills and app buttons.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Family (b) control: a wrapping row of selectable pills for 'All' + N
dynamic filters, wrapping the existing <Pill> (with per-option color/icon).
Complements <Toggle> (family (a): fixed connected segmented control).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the custom accent-filled pill buttons with the shared <Toggle>
(forest-filled active). Keeps SemesterChips' public API; also fixes the
prior --accent button fill.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Overview/Chat/Study match/Activity tab row now uses the shared <Toggle>.
setTab wiring unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Jose-Gael-Cruz-Lopezand others added 21 commits July 1, 2026 04:38
#147)
Closes an acceptance-#5 gap found in review: the probe-failure branch in
gemini_test (except → {ok:false,error}, still 200) was untested. Adds a case
that raises from health_probe_agent.run and asserts the failure contract.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(agents): migrate remaining one-shot LLM calls to Pydantic AI agents (#147)
…mini fallback (#144)
calendar_service already extracted via syllabus_extraction_agent as the
primary path; the raw-Gemini `parse_syllabus`/`call_gemini_json` fallback was
the only remaining seam. Remove it and degrade gracefully instead:
- Delete parse_syllabus, the call_gemini_json import, and the now-unused
PROMPT_PATH. On agent guardrail-trip or unexpected failure,
extract_assignments_from_file now returns _degraded_result (empty
assignments + a user-facing warning, no second LLM call) — resilience
without a raw Gemini call.
- Response contract preserved: {assignments, warnings, raw_text} on every
path (success / degrade / empty-text).
- notes stay encrypted at the write boundary (insert_new_assignments,
encrypt_if_present) per #126 — added a regression test.
Tests: rewrote the parse_syllabus-based fallback tests in test_ocr_pipeline.py
to assert the graceful degrade; repointed the live fixtures off parse_syllabus
onto the agent; added a notes-encryption test. Full suite green (2 pre-existing
storage-env failures unrelated). ruff clean.
Spec: specs/144-calendar-agent.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…uiz-gen fallback (#145)
Removes the last raw call_gemini* seams from the quiz surface:
- New agents/quiz_context.py (QuizContext: weak_areas, common_mistakes,
questions_seen_summary, recommended_difficulty, notes — mirrors
quiz_context_update.txt). routes/quiz.py's post-submit background task runs it
via run_agent_sync and saves .model_dump() (unchanged context_json shape).
- New agents/course_summary.py (CourseSummary.summary). course_context_service's
_generate_summary_with_gemini runs it via run_agent_sync and keeps the
deterministic template fallback on agent failure (no second LLM call).
- Removed _legacy_generate_quiz + the MODEL_LITE/MODEL_SMART/call_gemini_json
import. On agent guardrail-trip or failure, generate_quiz now returns 502
instead of a raw-Gemini fallback; the pre-agent 404 (unknown node) is
unchanged. _providers registers course_summary (flash) + quiz_context (lite).
Coordination: does NOT touch scoring / apply_graph_update / quiz_attempts writes
(#128/#129 territory) — only the LLM seams.
Tests: rewrote the legacy-fallback quiz tests to degrade-to-502; repointed the
submit context-update patches onto quiz_context_agent; removed the obsolete
legacy prompt-augmentation + legacy-model tests; added quiz-context save,
course-summary success/fallback tests. Full suite 826 passed (2 pre-existing
storage-env failures). ruff clean.
Spec: specs/145-quiz-course-context-agents.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…_context in submit mocks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-files + drop anon policies (#304)
Closes the last anon storage surface from #231. The rest already shipped:
application_resumes is private (backend/service-key via careers.py), issue-report
screenshots upload through the auth-gated POST /api/issue-reports/screenshot
(service role, size+mime validated; feedback.py), and ReportIssueFlow.tsx no
longer uses the anon storage client.
Migration 0029 (verified against prod, read-only, before writing):
- makes issues-media-files private (application_resumes already private → no-op),
- caps it at 5 MB + a mime allowlist matching the upload endpoint,
- drops the two anon storage.objects policies ("Allow uploads" INSERT / "Allow
public read" SELECT) the app no longer relies on.
Safe: uploads use the service role (bypasses storage RLS) and review is via the
dashboard / signed URLs, so nothing reads issues-media-files via a public URL.
Idempotent across environments (WHERE no-ops on absent buckets; DROP ... IF EXISTS).
NOT yet applied to prod — apply via `python -m db.migrate` (see the migration's
privilege note re: the storage schema).
Also updates docs/security/storage-hardening-plan.md to reflect the shipped state
(résumé-PII exposure already closed; only this lockdown remained).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses CodeRabbit review on #286:
- Toggle: flexWrap so SemesterChips' dynamic list wraps instead of
overflowing horizontally (no effect on fixed-option callers).
- Pill: type="button" (stops enclosing-form submit) and aria-pressed
to expose active state to assistive tech.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
refactor(frontend): unify CSS token layer + add (public) route group (Phase 1)
The direct-run harness (python3 tests/test_ocr_pipeline.py, documented in
the module docstring) had lost its sys.exit(1) and traceback on failure,
so a failed run exited 0 and silently reported success. Restore both.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Jul 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 27b74f9f-1588-4381-9a18-67c3da8bae5b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch main

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 2, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingf472300Jul 02 2026, 06:14 AM

feat(calendar): syllabus extraction via agent, retire raw-Gemini fallback (#144)
@railway-app
railway-appBottemporarily deployed to Sapling / staging July 2, 2026 05:45 Inactive
AndresL230and others added 2 commits July 2, 2026 02:08
The all-questions-drift branch's comment still described routing to the
legacy fallback, which #145 deleted. The RuntimeError is now caught by
generate_quiz's bare-except and returned as HTTP 502 (per
test_degrades_when_all_questions_drift). Comment-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145)
@railway-app
railway-appBottemporarily deployed to Sapling / staging July 2, 2026 06:12 Inactive
@AndresL230
AndresL230 merged commit 4cf87f1 into productionJul 2, 2026
9 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@AndresL230@Darkest-Teddy@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Promote staging → production: design-system, agents, calendar rewire, storage lockdown - #305

Merged
AndresL230 merged 259 commits into
productionfrom
main
Jul 2, 2026
Merged

Promote staging → production: design-system, agents, calendar rewire, storage lockdown#305
AndresL230 merged 259 commits into
productionfrom
main

Conversation

@AndresL230

Copy link
Copy Markdown
Collaborator

Promote main (staging) → production. Routine promotion since the last one (PR #241, 2026-06-27). The DB modular redesign already shipped in #241; this batch is 64 real commits (the other 188 are #147 no-op checkpoints).

What ships

Frontend — design-system / token unification (#286, #294)

  • Shared Button / Toggle / Chip / Badge primitives + FilterPills; migrated high-impact buttons, selectors, and filters across Learn / Gradebook / Calendar / Social / Library / Study / Tree.
  • Single token namespace (removed aliases), (public) route group + .public-surface marketing layer, pre-auth token de-shadowing, a11y fixes (aria-pressed, neutral Badge text).

Backend — one-shot LLM → Pydantic AI agents (#147, #296)

  • New study_guide_agent, social_summary_agent, health_probe_agent on the shared provider + run_agent_sync bridge.
  • /api/gemini-test, study-guide generation, and social group summaries routed through agents (502 on failure); failure-contract tests added.

Calendar — enrollment rewire (#283)

  • Enrollment-scoped Google sync/export/update/delete and study-block reads; assignments written via resolved enrollment_id + source tag; syllabus saves tagged.

Security — storage lockdown (#231, #304)

  • Makes issues-media-files private, caps size/mime, drops the last anon storage policies. Requires migration 0029 on the prod DB (see below).

Fixes

  • Onboarding completion persistence (same-origin helper), OAuth popup COOP (same-origin-allow-popups), graph unexplored-node labels, local-mode mock data for gradebook detail + chatroom.

Prod-side steps (do around merge)

  1. Apply migration 0029 to proddotenv -f .env.production run -- python -m db.migrate (idempotent; storage hardening only, header notes it was verified read-only against prod).
  2. Merge → triggers Railway (production) + Cloudflare Workers Builds (frontend worker) prod deploys.
  3. Smoke test prod: sign-in, dashboard API calls, doc upload → graph, issue-report screenshot upload (service-role path).

Verified before opening

  • mainproduction merges clean (no conflicts).
  • CI green on main; no new backend env vars vs. last promotion.
  • Only new prod DB migration is 0029.

🤖 Generated with Claude Code

AndresL230and others added 30 commits June 28, 2026 01:56
Rewires sync_to_google and export_to_google onto the enrollment-keyed
schema: select/write-back scoped by enrollment_id membership instead of
the removed user_id column; drops courses!left embed in favour of
_course_meta_cached. Updates test_calendar_export_idor.py and
test_calendar_sibling_write_scoping.py to assert the new enrollment_id
boundary (same IDOR guarantee, new key).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ment schema
- routes/documents.py: pass source="syllabus" at both save_assignments_to_db
call sites (_save_orchestrator_syllabus and the legacy call_gemini_json path)
- tests/test_calendar_routes.py: rewire TestSaveAssignments to include course_id
in fixtures and mock enrollment_id_for/user_enrollment_ids; rewire
TestGetUpcoming.test_returns_assignments_from_db to the enrollment-keyed row
shape (enrollment_id, no user_id/course_id/courses columns); add _tbl helper
- tests/test_assignment_notes_encryption.py: supply course_id to test fixtures
and mock academics so insert_new_assignments reaches the encryption boundary
- tests/test_documents_routes.py: update assert_called_once_with to include
source='syllabus' to match the new tagged call
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nrollment-rewire
fix(calendar): rewire assignments to the enrollment-keyed schema (dashboard 500)
…legend
- Remove r > 10 gate in KnowledgeGraph2D so every concept node shows its
name regardless of mastery score (unexplored nodes have r=8, below old threshold)
- Capitalize legend labels: mastered/learning/struggling/unexplored → title case
- Add react-force-graph-3d (missing from node_modules, caused build error)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…g sticks
handleOnboardingComplete POSTed /api/onboarding/profile to the cross-origin
NEXT_PUBLIC_API_URL without credentials:'include', so the browser dropped the
sapling_session cookie and the backend's require_self returned 401. The
onboarding_completed flag never flipped to True — and because the code never
checked res.ok, the 401 was swallowed and users were routed to /dashboard as if
it worked, then bounced back to "Get Started" on every subsequent sign-in.
Switch to the existing submitOnboardingProfile() helper, which goes through the
same-origin lib/api.ts fetchJSON path (API_URL='' proxied via the /api/* rewrite,
credentials:'include', and an res.ok check that surfaces failures instead of
hiding them).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ersistence
fix(onboarding): persist completion so users aren't trapped in the Get Started loop
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ate states correct)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…plicate namespace
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rarity + glass-input
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng-layer host
Moves landing + about/careers/privacy/terms under app/(public)/ (URLs unchanged).
The layout applies a token-only .public-surface scope so content pages keep the
warm paper canvas; the landing retains .landing-page for its mesh visual.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(public)/page.tsx, HowItWorks.tsx, Dialog.tsx: --brand-text1/2 -> --text/--text-dim,
--radius-* -> --r-*, --ease-out/in-out -> --ease. Mechanical identifier renames only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ngle namespace
Migrates the 3 remaining globals.css --ease-out consumers to --ease, then deletes
the temporary :root brand aliases and the .public-surface/.landing-page namespace
aliases. Zero retired-token references remain tree-wide.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… + dir hygiene)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/page.tsx
The route move shifted the landing page out from under its path-keyed
suppression entry, un-suppressing 4 pre-existing grandfathered errors
(no-html-link-for-pages, prefer-const x2, react-hooks/immutability).
Re-homes the entry to the new path; no code behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rison
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Retire sage --accent -> brighter forest (--brand-forest-bright) for highlight/focus;
merge --state-mastery + --grade-a into one --positive status green; add .btn--lg
hero size for de-pilled CTAs. Three forest-family greens by role: forest(action) /
accent(highlight) / positive(status).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Thin wrappers over the canonical .btn/.chip classes. Button enforces one shape
(6px) with variant + size (incl. lg hero size); Toggle is the one segmented
control; Chip/Badge collapse the pill/badge zoo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ives
- Landing CTAs (Get Started x2, Sign up for Beta) -> <Button size=lg>, de-pilled
to sharp 6px, infinite glow removed.
- 5 Gradebook modal save/submit buttons -> <Button> (kills hard-coded borderRadius:6).
- Study flashcard ratings 10px -> 6px (kept color-coding).
- Tokenize orphan #1a5c2a wordmark -> var(--brand-forest) across 8 files.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add optional per-option title to <Toggle>; replace the Learn setup mode pills with
it. ModelToggle (Fast/Smart) intentionally left as-is — it has a sliding animation,
per-option color semantics, and a tooltip that the generic Toggle would degrade.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Flair
Badge carries the hue on border + soft bg, keeps text neutral (colored text fails
4.5:1 on several rarity tiers). TitleFlair now wraps Badge instead of inline styles.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…arted CTA
De-pilled CTAs hug the text now (lg 13/26 -> 9/18). The closing 'Get Started'
under the 'Ready to Start Growing?' hero uses a new xl size so it stays a
prominent central focal point; navbar + hero beta stay tight.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…bright accent
The header button hard-coded background:var(--accent); after accent shifted to the
brighter forest, it rendered brighter than every other primary button. Now a proper
<Button variant=primary> (--brand-forest), matching the term pills and app buttons.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Family (b) control: a wrapping row of selectable pills for 'All' + N
dynamic filters, wrapping the existing <Pill> (with per-option color/icon).
Complements <Toggle> (family (a): fixed connected segmented control).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the custom accent-filled pill buttons with the shared <Toggle>
(forest-filled active). Keeps SemesterChips' public API; also fixes the
prior --accent button fill.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Overview/Chat/Study match/Activity tab row now uses the shared <Toggle>.
setTab wiring unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Jose-Gael-Cruz-Lopezand others added 21 commits July 1, 2026 04:38
#147)
Closes an acceptance-#5 gap found in review: the probe-failure branch in
gemini_test (except → {ok:false,error}, still 200) was untested. Adds a case
that raises from health_probe_agent.run and asserts the failure contract.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(agents): migrate remaining one-shot LLM calls to Pydantic AI agents (#147)
…mini fallback (#144)
calendar_service already extracted via syllabus_extraction_agent as the
primary path; the raw-Gemini `parse_syllabus`/`call_gemini_json` fallback was
the only remaining seam. Remove it and degrade gracefully instead:
- Delete parse_syllabus, the call_gemini_json import, and the now-unused
PROMPT_PATH. On agent guardrail-trip or unexpected failure,
extract_assignments_from_file now returns _degraded_result (empty
assignments + a user-facing warning, no second LLM call) — resilience
without a raw Gemini call.
- Response contract preserved: {assignments, warnings, raw_text} on every
path (success / degrade / empty-text).
- notes stay encrypted at the write boundary (insert_new_assignments,
encrypt_if_present) per #126 — added a regression test.
Tests: rewrote the parse_syllabus-based fallback tests in test_ocr_pipeline.py
to assert the graceful degrade; repointed the live fixtures off parse_syllabus
onto the agent; added a notes-encryption test. Full suite green (2 pre-existing
storage-env failures unrelated). ruff clean.
Spec: specs/144-calendar-agent.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…uiz-gen fallback (#145)
Removes the last raw call_gemini* seams from the quiz surface:
- New agents/quiz_context.py (QuizContext: weak_areas, common_mistakes,
questions_seen_summary, recommended_difficulty, notes — mirrors
quiz_context_update.txt). routes/quiz.py's post-submit background task runs it
via run_agent_sync and saves .model_dump() (unchanged context_json shape).
- New agents/course_summary.py (CourseSummary.summary). course_context_service's
_generate_summary_with_gemini runs it via run_agent_sync and keeps the
deterministic template fallback on agent failure (no second LLM call).
- Removed _legacy_generate_quiz + the MODEL_LITE/MODEL_SMART/call_gemini_json
import. On agent guardrail-trip or failure, generate_quiz now returns 502
instead of a raw-Gemini fallback; the pre-agent 404 (unknown node) is
unchanged. _providers registers course_summary (flash) + quiz_context (lite).
Coordination: does NOT touch scoring / apply_graph_update / quiz_attempts writes
(#128/#129 territory) — only the LLM seams.
Tests: rewrote the legacy-fallback quiz tests to degrade-to-502; repointed the
submit context-update patches onto quiz_context_agent; removed the obsolete
legacy prompt-augmentation + legacy-model tests; added quiz-context save,
course-summary success/fallback tests. Full suite 826 passed (2 pre-existing
storage-env failures). ruff clean.
Spec: specs/145-quiz-course-context-agents.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…_context in submit mocks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-files + drop anon policies (#304)
Closes the last anon storage surface from #231. The rest already shipped:
application_resumes is private (backend/service-key via careers.py), issue-report
screenshots upload through the auth-gated POST /api/issue-reports/screenshot
(service role, size+mime validated; feedback.py), and ReportIssueFlow.tsx no
longer uses the anon storage client.
Migration 0029 (verified against prod, read-only, before writing):
- makes issues-media-files private (application_resumes already private → no-op),
- caps it at 5 MB + a mime allowlist matching the upload endpoint,
- drops the two anon storage.objects policies ("Allow uploads" INSERT / "Allow
public read" SELECT) the app no longer relies on.
Safe: uploads use the service role (bypasses storage RLS) and review is via the
dashboard / signed URLs, so nothing reads issues-media-files via a public URL.
Idempotent across environments (WHERE no-ops on absent buckets; DROP ... IF EXISTS).
NOT yet applied to prod — apply via `python -m db.migrate` (see the migration's
privilege note re: the storage schema).
Also updates docs/security/storage-hardening-plan.md to reflect the shipped state
(résumé-PII exposure already closed; only this lockdown remained).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses CodeRabbit review on #286:
- Toggle: flexWrap so SemesterChips' dynamic list wraps instead of
overflowing horizontally (no effect on fixed-option callers).
- Pill: type="button" (stops enclosing-form submit) and aria-pressed
to expose active state to assistive tech.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
refactor(frontend): unify CSS token layer + add (public) route group (Phase 1)
The direct-run harness (python3 tests/test_ocr_pipeline.py, documented in
the module docstring) had lost its sys.exit(1) and traceback on failure,
so a failed run exited 0 and silently reported success. Restore both.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Jul 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 27b74f9f-1588-4381-9a18-67c3da8bae5b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch main

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 2, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingf472300Jul 02 2026, 06:14 AM

feat(calendar): syllabus extraction via agent, retire raw-Gemini fallback (#144)
@railway-app
railway-appBottemporarily deployed to Sapling / staging July 2, 2026 05:45 Inactive
AndresL230and others added 2 commits July 2, 2026 02:08
The all-questions-drift branch's comment still described routing to the
legacy fallback, which #145 deleted. The RuntimeError is now caught by
generate_quiz's bare-except and returned as HTTP 502 (per
test_degrades_when_all_questions_drift). Comment-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145)
@railway-app
railway-appBottemporarily deployed to Sapling / staging July 2, 2026 06:12 Inactive
@AndresL230
AndresL230 merged commit 4cf87f1 into productionJul 2, 2026
9 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@AndresL230@Darkest-Teddy@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Promote staging → production: design-system, agents, calendar rewire, storage lockdown - #305

Merged
AndresL230 merged 259 commits into
productionfrom
main
Jul 2, 2026
Merged

Promote staging → production: design-system, agents, calendar rewire, storage lockdown#305
AndresL230 merged 259 commits into
productionfrom
main

Conversation

@AndresL230

Copy link
Copy Markdown
Collaborator

Promote main (staging) → production. Routine promotion since the last one (PR #241, 2026-06-27). The DB modular redesign already shipped in #241; this batch is 64 real commits (the other 188 are #147 no-op checkpoints).

What ships

Frontend — design-system / token unification (#286, #294)

  • Shared Button / Toggle / Chip / Badge primitives + FilterPills; migrated high-impact buttons, selectors, and filters across Learn / Gradebook / Calendar / Social / Library / Study / Tree.
  • Single token namespace (removed aliases), (public) route group + .public-surface marketing layer, pre-auth token de-shadowing, a11y fixes (aria-pressed, neutral Badge text).

Backend — one-shot LLM → Pydantic AI agents (#147, #296)

  • New study_guide_agent, social_summary_agent, health_probe_agent on the shared provider + run_agent_sync bridge.
  • /api/gemini-test, study-guide generation, and social group summaries routed through agents (502 on failure); failure-contract tests added.

Calendar — enrollment rewire (#283)

  • Enrollment-scoped Google sync/export/update/delete and study-block reads; assignments written via resolved enrollment_id + source tag; syllabus saves tagged.

Security — storage lockdown (#231, #304)

  • Makes issues-media-files private, caps size/mime, drops the last anon storage policies. Requires migration 0029 on the prod DB (see below).

Fixes

  • Onboarding completion persistence (same-origin helper), OAuth popup COOP (same-origin-allow-popups), graph unexplored-node labels, local-mode mock data for gradebook detail + chatroom.

Prod-side steps (do around merge)

  1. Apply migration 0029 to proddotenv -f .env.production run -- python -m db.migrate (idempotent; storage hardening only, header notes it was verified read-only against prod).
  2. Merge → triggers Railway (production) + Cloudflare Workers Builds (frontend worker) prod deploys.
  3. Smoke test prod: sign-in, dashboard API calls, doc upload → graph, issue-report screenshot upload (service-role path).

Verified before opening

  • mainproduction merges clean (no conflicts).
  • CI green on main; no new backend env vars vs. last promotion.
  • Only new prod DB migration is 0029.

🤖 Generated with Claude Code

AndresL230and others added 30 commits June 28, 2026 01:56
Rewires sync_to_google and export_to_google onto the enrollment-keyed
schema: select/write-back scoped by enrollment_id membership instead of
the removed user_id column; drops courses!left embed in favour of
_course_meta_cached. Updates test_calendar_export_idor.py and
test_calendar_sibling_write_scoping.py to assert the new enrollment_id
boundary (same IDOR guarantee, new key).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ment schema
- routes/documents.py: pass source="syllabus" at both save_assignments_to_db
call sites (_save_orchestrator_syllabus and the legacy call_gemini_json path)
- tests/test_calendar_routes.py: rewire TestSaveAssignments to include course_id
in fixtures and mock enrollment_id_for/user_enrollment_ids; rewire
TestGetUpcoming.test_returns_assignments_from_db to the enrollment-keyed row
shape (enrollment_id, no user_id/course_id/courses columns); add _tbl helper
- tests/test_assignment_notes_encryption.py: supply course_id to test fixtures
and mock academics so insert_new_assignments reaches the encryption boundary
- tests/test_documents_routes.py: update assert_called_once_with to include
source='syllabus' to match the new tagged call
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nrollment-rewire
fix(calendar): rewire assignments to the enrollment-keyed schema (dashboard 500)
…legend
- Remove r > 10 gate in KnowledgeGraph2D so every concept node shows its
name regardless of mastery score (unexplored nodes have r=8, below old threshold)
- Capitalize legend labels: mastered/learning/struggling/unexplored → title case
- Add react-force-graph-3d (missing from node_modules, caused build error)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…g sticks
handleOnboardingComplete POSTed /api/onboarding/profile to the cross-origin
NEXT_PUBLIC_API_URL without credentials:'include', so the browser dropped the
sapling_session cookie and the backend's require_self returned 401. The
onboarding_completed flag never flipped to True — and because the code never
checked res.ok, the 401 was swallowed and users were routed to /dashboard as if
it worked, then bounced back to "Get Started" on every subsequent sign-in.
Switch to the existing submitOnboardingProfile() helper, which goes through the
same-origin lib/api.ts fetchJSON path (API_URL='' proxied via the /api/* rewrite,
credentials:'include', and an res.ok check that surfaces failures instead of
hiding them).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ersistence
fix(onboarding): persist completion so users aren't trapped in the Get Started loop
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ate states correct)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…plicate namespace
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rarity + glass-input
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng-layer host
Moves landing + about/careers/privacy/terms under app/(public)/ (URLs unchanged).
The layout applies a token-only .public-surface scope so content pages keep the
warm paper canvas; the landing retains .landing-page for its mesh visual.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(public)/page.tsx, HowItWorks.tsx, Dialog.tsx: --brand-text1/2 -> --text/--text-dim,
--radius-* -> --r-*, --ease-out/in-out -> --ease. Mechanical identifier renames only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ngle namespace
Migrates the 3 remaining globals.css --ease-out consumers to --ease, then deletes
the temporary :root brand aliases and the .public-surface/.landing-page namespace
aliases. Zero retired-token references remain tree-wide.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… + dir hygiene)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/page.tsx
The route move shifted the landing page out from under its path-keyed
suppression entry, un-suppressing 4 pre-existing grandfathered errors
(no-html-link-for-pages, prefer-const x2, react-hooks/immutability).
Re-homes the entry to the new path; no code behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rison
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Retire sage --accent -> brighter forest (--brand-forest-bright) for highlight/focus;
merge --state-mastery + --grade-a into one --positive status green; add .btn--lg
hero size for de-pilled CTAs. Three forest-family greens by role: forest(action) /
accent(highlight) / positive(status).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Thin wrappers over the canonical .btn/.chip classes. Button enforces one shape
(6px) with variant + size (incl. lg hero size); Toggle is the one segmented
control; Chip/Badge collapse the pill/badge zoo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ives
- Landing CTAs (Get Started x2, Sign up for Beta) -> <Button size=lg>, de-pilled
to sharp 6px, infinite glow removed.
- 5 Gradebook modal save/submit buttons -> <Button> (kills hard-coded borderRadius:6).
- Study flashcard ratings 10px -> 6px (kept color-coding).
- Tokenize orphan #1a5c2a wordmark -> var(--brand-forest) across 8 files.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add optional per-option title to <Toggle>; replace the Learn setup mode pills with
it. ModelToggle (Fast/Smart) intentionally left as-is — it has a sliding animation,
per-option color semantics, and a tooltip that the generic Toggle would degrade.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Flair
Badge carries the hue on border + soft bg, keeps text neutral (colored text fails
4.5:1 on several rarity tiers). TitleFlair now wraps Badge instead of inline styles.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…arted CTA
De-pilled CTAs hug the text now (lg 13/26 -> 9/18). The closing 'Get Started'
under the 'Ready to Start Growing?' hero uses a new xl size so it stays a
prominent central focal point; navbar + hero beta stay tight.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…bright accent
The header button hard-coded background:var(--accent); after accent shifted to the
brighter forest, it rendered brighter than every other primary button. Now a proper
<Button variant=primary> (--brand-forest), matching the term pills and app buttons.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Family (b) control: a wrapping row of selectable pills for 'All' + N
dynamic filters, wrapping the existing <Pill> (with per-option color/icon).
Complements <Toggle> (family (a): fixed connected segmented control).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the custom accent-filled pill buttons with the shared <Toggle>
(forest-filled active). Keeps SemesterChips' public API; also fixes the
prior --accent button fill.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Overview/Chat/Study match/Activity tab row now uses the shared <Toggle>.
setTab wiring unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Jose-Gael-Cruz-Lopezand others added 21 commits July 1, 2026 04:38
#147)
Closes an acceptance-#5 gap found in review: the probe-failure branch in
gemini_test (except → {ok:false,error}, still 200) was untested. Adds a case
that raises from health_probe_agent.run and asserts the failure contract.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(agents): migrate remaining one-shot LLM calls to Pydantic AI agents (#147)
…mini fallback (#144)
calendar_service already extracted via syllabus_extraction_agent as the
primary path; the raw-Gemini `parse_syllabus`/`call_gemini_json` fallback was
the only remaining seam. Remove it and degrade gracefully instead:
- Delete parse_syllabus, the call_gemini_json import, and the now-unused
PROMPT_PATH. On agent guardrail-trip or unexpected failure,
extract_assignments_from_file now returns _degraded_result (empty
assignments + a user-facing warning, no second LLM call) — resilience
without a raw Gemini call.
- Response contract preserved: {assignments, warnings, raw_text} on every
path (success / degrade / empty-text).
- notes stay encrypted at the write boundary (insert_new_assignments,
encrypt_if_present) per #126 — added a regression test.
Tests: rewrote the parse_syllabus-based fallback tests in test_ocr_pipeline.py
to assert the graceful degrade; repointed the live fixtures off parse_syllabus
onto the agent; added a notes-encryption test. Full suite green (2 pre-existing
storage-env failures unrelated). ruff clean.
Spec: specs/144-calendar-agent.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…uiz-gen fallback (#145)
Removes the last raw call_gemini* seams from the quiz surface:
- New agents/quiz_context.py (QuizContext: weak_areas, common_mistakes,
questions_seen_summary, recommended_difficulty, notes — mirrors
quiz_context_update.txt). routes/quiz.py's post-submit background task runs it
via run_agent_sync and saves .model_dump() (unchanged context_json shape).
- New agents/course_summary.py (CourseSummary.summary). course_context_service's
_generate_summary_with_gemini runs it via run_agent_sync and keeps the
deterministic template fallback on agent failure (no second LLM call).
- Removed _legacy_generate_quiz + the MODEL_LITE/MODEL_SMART/call_gemini_json
import. On agent guardrail-trip or failure, generate_quiz now returns 502
instead of a raw-Gemini fallback; the pre-agent 404 (unknown node) is
unchanged. _providers registers course_summary (flash) + quiz_context (lite).
Coordination: does NOT touch scoring / apply_graph_update / quiz_attempts writes
(#128/#129 territory) — only the LLM seams.
Tests: rewrote the legacy-fallback quiz tests to degrade-to-502; repointed the
submit context-update patches onto quiz_context_agent; removed the obsolete
legacy prompt-augmentation + legacy-model tests; added quiz-context save,
course-summary success/fallback tests. Full suite 826 passed (2 pre-existing
storage-env failures). ruff clean.
Spec: specs/145-quiz-course-context-agents.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…_context in submit mocks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-files + drop anon policies (#304)
Closes the last anon storage surface from #231. The rest already shipped:
application_resumes is private (backend/service-key via careers.py), issue-report
screenshots upload through the auth-gated POST /api/issue-reports/screenshot
(service role, size+mime validated; feedback.py), and ReportIssueFlow.tsx no
longer uses the anon storage client.
Migration 0029 (verified against prod, read-only, before writing):
- makes issues-media-files private (application_resumes already private → no-op),
- caps it at 5 MB + a mime allowlist matching the upload endpoint,
- drops the two anon storage.objects policies ("Allow uploads" INSERT / "Allow
public read" SELECT) the app no longer relies on.
Safe: uploads use the service role (bypasses storage RLS) and review is via the
dashboard / signed URLs, so nothing reads issues-media-files via a public URL.
Idempotent across environments (WHERE no-ops on absent buckets; DROP ... IF EXISTS).
NOT yet applied to prod — apply via `python -m db.migrate` (see the migration's
privilege note re: the storage schema).
Also updates docs/security/storage-hardening-plan.md to reflect the shipped state
(résumé-PII exposure already closed; only this lockdown remained).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses CodeRabbit review on #286:
- Toggle: flexWrap so SemesterChips' dynamic list wraps instead of
overflowing horizontally (no effect on fixed-option callers).
- Pill: type="button" (stops enclosing-form submit) and aria-pressed
to expose active state to assistive tech.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
refactor(frontend): unify CSS token layer + add (public) route group (Phase 1)
The direct-run harness (python3 tests/test_ocr_pipeline.py, documented in
the module docstring) had lost its sys.exit(1) and traceback on failure,
so a failed run exited 0 and silently reported success. Restore both.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Jul 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 27b74f9f-1588-4381-9a18-67c3da8bae5b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch main

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 2, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingf472300Jul 02 2026, 06:14 AM

feat(calendar): syllabus extraction via agent, retire raw-Gemini fallback (#144)
@railway-app
railway-appBottemporarily deployed to Sapling / staging July 2, 2026 05:45 Inactive
AndresL230and others added 2 commits July 2, 2026 02:08
The all-questions-drift branch's comment still described routing to the
legacy fallback, which #145 deleted. The RuntimeError is now caught by
generate_quiz's bare-except and returned as HTTP 502 (per
test_degrades_when_all_questions_drift). Comment-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145)
@railway-app
railway-appBottemporarily deployed to Sapling / staging July 2, 2026 06:12 Inactive
@AndresL230
AndresL230 merged commit 4cf87f1 into productionJul 2, 2026
9 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@AndresL230@Darkest-Teddy@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Promote staging → production: design-system, agents, calendar rewire, storage lockdown - #305

Merged
AndresL230 merged 259 commits into
productionfrom
main
Jul 2, 2026
Merged

Promote staging → production: design-system, agents, calendar rewire, storage lockdown#305
AndresL230 merged 259 commits into
productionfrom
main

Conversation

@AndresL230

Copy link
Copy Markdown
Collaborator

Promote main (staging) → production. Routine promotion since the last one (PR #241, 2026-06-27). The DB modular redesign already shipped in #241; this batch is 64 real commits (the other 188 are #147 no-op checkpoints).

What ships

Frontend — design-system / token unification (#286, #294)

  • Shared Button / Toggle / Chip / Badge primitives + FilterPills; migrated high-impact buttons, selectors, and filters across Learn / Gradebook / Calendar / Social / Library / Study / Tree.
  • Single token namespace (removed aliases), (public) route group + .public-surface marketing layer, pre-auth token de-shadowing, a11y fixes (aria-pressed, neutral Badge text).

Backend — one-shot LLM → Pydantic AI agents (#147, #296)

  • New study_guide_agent, social_summary_agent, health_probe_agent on the shared provider + run_agent_sync bridge.
  • /api/gemini-test, study-guide generation, and social group summaries routed through agents (502 on failure); failure-contract tests added.

Calendar — enrollment rewire (#283)

  • Enrollment-scoped Google sync/export/update/delete and study-block reads; assignments written via resolved enrollment_id + source tag; syllabus saves tagged.

Security — storage lockdown (#231, #304)

  • Makes issues-media-files private, caps size/mime, drops the last anon storage policies. Requires migration 0029 on the prod DB (see below).

Fixes

  • Onboarding completion persistence (same-origin helper), OAuth popup COOP (same-origin-allow-popups), graph unexplored-node labels, local-mode mock data for gradebook detail + chatroom.

Prod-side steps (do around merge)

  1. Apply migration 0029 to proddotenv -f .env.production run -- python -m db.migrate (idempotent; storage hardening only, header notes it was verified read-only against prod).
  2. Merge → triggers Railway (production) + Cloudflare Workers Builds (frontend worker) prod deploys.
  3. Smoke test prod: sign-in, dashboard API calls, doc upload → graph, issue-report screenshot upload (service-role path).

Verified before opening

  • mainproduction merges clean (no conflicts).
  • CI green on main; no new backend env vars vs. last promotion.
  • Only new prod DB migration is 0029.

🤖 Generated with Claude Code

AndresL230and others added 30 commits June 28, 2026 01:56
Rewires sync_to_google and export_to_google onto the enrollment-keyed
schema: select/write-back scoped by enrollment_id membership instead of
the removed user_id column; drops courses!left embed in favour of
_course_meta_cached. Updates test_calendar_export_idor.py and
test_calendar_sibling_write_scoping.py to assert the new enrollment_id
boundary (same IDOR guarantee, new key).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ment schema
- routes/documents.py: pass source="syllabus" at both save_assignments_to_db
call sites (_save_orchestrator_syllabus and the legacy call_gemini_json path)
- tests/test_calendar_routes.py: rewire TestSaveAssignments to include course_id
in fixtures and mock enrollment_id_for/user_enrollment_ids; rewire
TestGetUpcoming.test_returns_assignments_from_db to the enrollment-keyed row
shape (enrollment_id, no user_id/course_id/courses columns); add _tbl helper
- tests/test_assignment_notes_encryption.py: supply course_id to test fixtures
and mock academics so insert_new_assignments reaches the encryption boundary
- tests/test_documents_routes.py: update assert_called_once_with to include
source='syllabus' to match the new tagged call
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nrollment-rewire
fix(calendar): rewire assignments to the enrollment-keyed schema (dashboard 500)
…legend
- Remove r > 10 gate in KnowledgeGraph2D so every concept node shows its
name regardless of mastery score (unexplored nodes have r=8, below old threshold)
- Capitalize legend labels: mastered/learning/struggling/unexplored → title case
- Add react-force-graph-3d (missing from node_modules, caused build error)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…g sticks
handleOnboardingComplete POSTed /api/onboarding/profile to the cross-origin
NEXT_PUBLIC_API_URL without credentials:'include', so the browser dropped the
sapling_session cookie and the backend's require_self returned 401. The
onboarding_completed flag never flipped to True — and because the code never
checked res.ok, the 401 was swallowed and users were routed to /dashboard as if
it worked, then bounced back to "Get Started" on every subsequent sign-in.
Switch to the existing submitOnboardingProfile() helper, which goes through the
same-origin lib/api.ts fetchJSON path (API_URL='' proxied via the /api/* rewrite,
credentials:'include', and an res.ok check that surfaces failures instead of
hiding them).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ersistence
fix(onboarding): persist completion so users aren't trapped in the Get Started loop
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ate states correct)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…plicate namespace
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rarity + glass-input
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng-layer host
Moves landing + about/careers/privacy/terms under app/(public)/ (URLs unchanged).
The layout applies a token-only .public-surface scope so content pages keep the
warm paper canvas; the landing retains .landing-page for its mesh visual.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(public)/page.tsx, HowItWorks.tsx, Dialog.tsx: --brand-text1/2 -> --text/--text-dim,
--radius-* -> --r-*, --ease-out/in-out -> --ease. Mechanical identifier renames only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ngle namespace
Migrates the 3 remaining globals.css --ease-out consumers to --ease, then deletes
the temporary :root brand aliases and the .public-surface/.landing-page namespace
aliases. Zero retired-token references remain tree-wide.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… + dir hygiene)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/page.tsx
The route move shifted the landing page out from under its path-keyed
suppression entry, un-suppressing 4 pre-existing grandfathered errors
(no-html-link-for-pages, prefer-const x2, react-hooks/immutability).
Re-homes the entry to the new path; no code behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rison
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Retire sage --accent -> brighter forest (--brand-forest-bright) for highlight/focus;
merge --state-mastery + --grade-a into one --positive status green; add .btn--lg
hero size for de-pilled CTAs. Three forest-family greens by role: forest(action) /
accent(highlight) / positive(status).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Thin wrappers over the canonical .btn/.chip classes. Button enforces one shape
(6px) with variant + size (incl. lg hero size); Toggle is the one segmented
control; Chip/Badge collapse the pill/badge zoo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ives
- Landing CTAs (Get Started x2, Sign up for Beta) -> <Button size=lg>, de-pilled
to sharp 6px, infinite glow removed.
- 5 Gradebook modal save/submit buttons -> <Button> (kills hard-coded borderRadius:6).
- Study flashcard ratings 10px -> 6px (kept color-coding).
- Tokenize orphan #1a5c2a wordmark -> var(--brand-forest) across 8 files.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add optional per-option title to <Toggle>; replace the Learn setup mode pills with
it. ModelToggle (Fast/Smart) intentionally left as-is — it has a sliding animation,
per-option color semantics, and a tooltip that the generic Toggle would degrade.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Flair
Badge carries the hue on border + soft bg, keeps text neutral (colored text fails
4.5:1 on several rarity tiers). TitleFlair now wraps Badge instead of inline styles.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…arted CTA
De-pilled CTAs hug the text now (lg 13/26 -> 9/18). The closing 'Get Started'
under the 'Ready to Start Growing?' hero uses a new xl size so it stays a
prominent central focal point; navbar + hero beta stay tight.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…bright accent
The header button hard-coded background:var(--accent); after accent shifted to the
brighter forest, it rendered brighter than every other primary button. Now a proper
<Button variant=primary> (--brand-forest), matching the term pills and app buttons.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Family (b) control: a wrapping row of selectable pills for 'All' + N
dynamic filters, wrapping the existing <Pill> (with per-option color/icon).
Complements <Toggle> (family (a): fixed connected segmented control).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the custom accent-filled pill buttons with the shared <Toggle>
(forest-filled active). Keeps SemesterChips' public API; also fixes the
prior --accent button fill.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Overview/Chat/Study match/Activity tab row now uses the shared <Toggle>.
setTab wiring unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Jose-Gael-Cruz-Lopezand others added 21 commits July 1, 2026 04:38
#147)
Closes an acceptance-#5 gap found in review: the probe-failure branch in
gemini_test (except → {ok:false,error}, still 200) was untested. Adds a case
that raises from health_probe_agent.run and asserts the failure contract.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(agents): migrate remaining one-shot LLM calls to Pydantic AI agents (#147)
…mini fallback (#144)
calendar_service already extracted via syllabus_extraction_agent as the
primary path; the raw-Gemini `parse_syllabus`/`call_gemini_json` fallback was
the only remaining seam. Remove it and degrade gracefully instead:
- Delete parse_syllabus, the call_gemini_json import, and the now-unused
PROMPT_PATH. On agent guardrail-trip or unexpected failure,
extract_assignments_from_file now returns _degraded_result (empty
assignments + a user-facing warning, no second LLM call) — resilience
without a raw Gemini call.
- Response contract preserved: {assignments, warnings, raw_text} on every
path (success / degrade / empty-text).
- notes stay encrypted at the write boundary (insert_new_assignments,
encrypt_if_present) per #126 — added a regression test.
Tests: rewrote the parse_syllabus-based fallback tests in test_ocr_pipeline.py
to assert the graceful degrade; repointed the live fixtures off parse_syllabus
onto the agent; added a notes-encryption test. Full suite green (2 pre-existing
storage-env failures unrelated). ruff clean.
Spec: specs/144-calendar-agent.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…uiz-gen fallback (#145)
Removes the last raw call_gemini* seams from the quiz surface:
- New agents/quiz_context.py (QuizContext: weak_areas, common_mistakes,
questions_seen_summary, recommended_difficulty, notes — mirrors
quiz_context_update.txt). routes/quiz.py's post-submit background task runs it
via run_agent_sync and saves .model_dump() (unchanged context_json shape).
- New agents/course_summary.py (CourseSummary.summary). course_context_service's
_generate_summary_with_gemini runs it via run_agent_sync and keeps the
deterministic template fallback on agent failure (no second LLM call).
- Removed _legacy_generate_quiz + the MODEL_LITE/MODEL_SMART/call_gemini_json
import. On agent guardrail-trip or failure, generate_quiz now returns 502
instead of a raw-Gemini fallback; the pre-agent 404 (unknown node) is
unchanged. _providers registers course_summary (flash) + quiz_context (lite).
Coordination: does NOT touch scoring / apply_graph_update / quiz_attempts writes
(#128/#129 territory) — only the LLM seams.
Tests: rewrote the legacy-fallback quiz tests to degrade-to-502; repointed the
submit context-update patches onto quiz_context_agent; removed the obsolete
legacy prompt-augmentation + legacy-model tests; added quiz-context save,
course-summary success/fallback tests. Full suite 826 passed (2 pre-existing
storage-env failures). ruff clean.
Spec: specs/145-quiz-course-context-agents.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…_context in submit mocks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-files + drop anon policies (#304)
Closes the last anon storage surface from #231. The rest already shipped:
application_resumes is private (backend/service-key via careers.py), issue-report
screenshots upload through the auth-gated POST /api/issue-reports/screenshot
(service role, size+mime validated; feedback.py), and ReportIssueFlow.tsx no
longer uses the anon storage client.
Migration 0029 (verified against prod, read-only, before writing):
- makes issues-media-files private (application_resumes already private → no-op),
- caps it at 5 MB + a mime allowlist matching the upload endpoint,
- drops the two anon storage.objects policies ("Allow uploads" INSERT / "Allow
public read" SELECT) the app no longer relies on.
Safe: uploads use the service role (bypasses storage RLS) and review is via the
dashboard / signed URLs, so nothing reads issues-media-files via a public URL.
Idempotent across environments (WHERE no-ops on absent buckets; DROP ... IF EXISTS).
NOT yet applied to prod — apply via `python -m db.migrate` (see the migration's
privilege note re: the storage schema).
Also updates docs/security/storage-hardening-plan.md to reflect the shipped state
(résumé-PII exposure already closed; only this lockdown remained).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses CodeRabbit review on #286:
- Toggle: flexWrap so SemesterChips' dynamic list wraps instead of
overflowing horizontally (no effect on fixed-option callers).
- Pill: type="button" (stops enclosing-form submit) and aria-pressed
to expose active state to assistive tech.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
refactor(frontend): unify CSS token layer + add (public) route group (Phase 1)
The direct-run harness (python3 tests/test_ocr_pipeline.py, documented in
the module docstring) had lost its sys.exit(1) and traceback on failure,
so a failed run exited 0 and silently reported success. Restore both.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Jul 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 27b74f9f-1588-4381-9a18-67c3da8bae5b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch main

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 2, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingf472300Jul 02 2026, 06:14 AM

feat(calendar): syllabus extraction via agent, retire raw-Gemini fallback (#144)
@railway-app
railway-appBottemporarily deployed to Sapling / staging July 2, 2026 05:45 Inactive
AndresL230and others added 2 commits July 2, 2026 02:08
The all-questions-drift branch's comment still described routing to the
legacy fallback, which #145 deleted. The RuntimeError is now caught by
generate_quiz's bare-except and returned as HTTP 502 (per
test_degrades_when_all_questions_drift). Comment-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145)
@railway-app
railway-appBottemporarily deployed to Sapling / staging July 2, 2026 06:12 Inactive
@AndresL230
AndresL230 merged commit 4cf87f1 into productionJul 2, 2026
9 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@AndresL230@Darkest-Teddy@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Promote staging → production: design-system, agents, calendar rewire, storage lockdown - #305

Merged
AndresL230 merged 259 commits into
productionfrom
main
Jul 2, 2026
Merged

Promote staging → production: design-system, agents, calendar rewire, storage lockdown#305
AndresL230 merged 259 commits into
productionfrom
main

Conversation

@AndresL230

Copy link
Copy Markdown
Collaborator

Promote main (staging) → production. Routine promotion since the last one (PR #241, 2026-06-27). The DB modular redesign already shipped in #241; this batch is 64 real commits (the other 188 are #147 no-op checkpoints).

What ships

Frontend — design-system / token unification (#286, #294)

  • Shared Button / Toggle / Chip / Badge primitives + FilterPills; migrated high-impact buttons, selectors, and filters across Learn / Gradebook / Calendar / Social / Library / Study / Tree.
  • Single token namespace (removed aliases), (public) route group + .public-surface marketing layer, pre-auth token de-shadowing, a11y fixes (aria-pressed, neutral Badge text).

Backend — one-shot LLM → Pydantic AI agents (#147, #296)

  • New study_guide_agent, social_summary_agent, health_probe_agent on the shared provider + run_agent_sync bridge.
  • /api/gemini-test, study-guide generation, and social group summaries routed through agents (502 on failure); failure-contract tests added.

Calendar — enrollment rewire (#283)

  • Enrollment-scoped Google sync/export/update/delete and study-block reads; assignments written via resolved enrollment_id + source tag; syllabus saves tagged.

Security — storage lockdown (#231, #304)

  • Makes issues-media-files private, caps size/mime, drops the last anon storage policies. Requires migration 0029 on the prod DB (see below).

Fixes

  • Onboarding completion persistence (same-origin helper), OAuth popup COOP (same-origin-allow-popups), graph unexplored-node labels, local-mode mock data for gradebook detail + chatroom.

Prod-side steps (do around merge)

  1. Apply migration 0029 to proddotenv -f .env.production run -- python -m db.migrate (idempotent; storage hardening only, header notes it was verified read-only against prod).
  2. Merge → triggers Railway (production) + Cloudflare Workers Builds (frontend worker) prod deploys.
  3. Smoke test prod: sign-in, dashboard API calls, doc upload → graph, issue-report screenshot upload (service-role path).

Verified before opening

  • mainproduction merges clean (no conflicts).
  • CI green on main; no new backend env vars vs. last promotion.
  • Only new prod DB migration is 0029.

🤖 Generated with Claude Code

AndresL230and others added 30 commits June 28, 2026 01:56
Rewires sync_to_google and export_to_google onto the enrollment-keyed
schema: select/write-back scoped by enrollment_id membership instead of
the removed user_id column; drops courses!left embed in favour of
_course_meta_cached. Updates test_calendar_export_idor.py and
test_calendar_sibling_write_scoping.py to assert the new enrollment_id
boundary (same IDOR guarantee, new key).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ment schema
- routes/documents.py: pass source="syllabus" at both save_assignments_to_db
call sites (_save_orchestrator_syllabus and the legacy call_gemini_json path)
- tests/test_calendar_routes.py: rewire TestSaveAssignments to include course_id
in fixtures and mock enrollment_id_for/user_enrollment_ids; rewire
TestGetUpcoming.test_returns_assignments_from_db to the enrollment-keyed row
shape (enrollment_id, no user_id/course_id/courses columns); add _tbl helper
- tests/test_assignment_notes_encryption.py: supply course_id to test fixtures
and mock academics so insert_new_assignments reaches the encryption boundary
- tests/test_documents_routes.py: update assert_called_once_with to include
source='syllabus' to match the new tagged call
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nrollment-rewire
fix(calendar): rewire assignments to the enrollment-keyed schema (dashboard 500)
…legend
- Remove r > 10 gate in KnowledgeGraph2D so every concept node shows its
name regardless of mastery score (unexplored nodes have r=8, below old threshold)
- Capitalize legend labels: mastered/learning/struggling/unexplored → title case
- Add react-force-graph-3d (missing from node_modules, caused build error)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…g sticks
handleOnboardingComplete POSTed /api/onboarding/profile to the cross-origin
NEXT_PUBLIC_API_URL without credentials:'include', so the browser dropped the
sapling_session cookie and the backend's require_self returned 401. The
onboarding_completed flag never flipped to True — and because the code never
checked res.ok, the 401 was swallowed and users were routed to /dashboard as if
it worked, then bounced back to "Get Started" on every subsequent sign-in.
Switch to the existing submitOnboardingProfile() helper, which goes through the
same-origin lib/api.ts fetchJSON path (API_URL='' proxied via the /api/* rewrite,
credentials:'include', and an res.ok check that surfaces failures instead of
hiding them).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ersistence
fix(onboarding): persist completion so users aren't trapped in the Get Started loop
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ate states correct)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…plicate namespace
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rarity + glass-input
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng-layer host
Moves landing + about/careers/privacy/terms under app/(public)/ (URLs unchanged).
The layout applies a token-only .public-surface scope so content pages keep the
warm paper canvas; the landing retains .landing-page for its mesh visual.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(public)/page.tsx, HowItWorks.tsx, Dialog.tsx: --brand-text1/2 -> --text/--text-dim,
--radius-* -> --r-*, --ease-out/in-out -> --ease. Mechanical identifier renames only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ngle namespace
Migrates the 3 remaining globals.css --ease-out consumers to --ease, then deletes
the temporary :root brand aliases and the .public-surface/.landing-page namespace
aliases. Zero retired-token references remain tree-wide.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… + dir hygiene)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/page.tsx
The route move shifted the landing page out from under its path-keyed
suppression entry, un-suppressing 4 pre-existing grandfathered errors
(no-html-link-for-pages, prefer-const x2, react-hooks/immutability).
Re-homes the entry to the new path; no code behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rison
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Retire sage --accent -> brighter forest (--brand-forest-bright) for highlight/focus;
merge --state-mastery + --grade-a into one --positive status green; add .btn--lg
hero size for de-pilled CTAs. Three forest-family greens by role: forest(action) /
accent(highlight) / positive(status).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Thin wrappers over the canonical .btn/.chip classes. Button enforces one shape
(6px) with variant + size (incl. lg hero size); Toggle is the one segmented
control; Chip/Badge collapse the pill/badge zoo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ives
- Landing CTAs (Get Started x2, Sign up for Beta) -> <Button size=lg>, de-pilled
to sharp 6px, infinite glow removed.
- 5 Gradebook modal save/submit buttons -> <Button> (kills hard-coded borderRadius:6).
- Study flashcard ratings 10px -> 6px (kept color-coding).
- Tokenize orphan #1a5c2a wordmark -> var(--brand-forest) across 8 files.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add optional per-option title to <Toggle>; replace the Learn setup mode pills with
it. ModelToggle (Fast/Smart) intentionally left as-is — it has a sliding animation,
per-option color semantics, and a tooltip that the generic Toggle would degrade.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Flair
Badge carries the hue on border + soft bg, keeps text neutral (colored text fails
4.5:1 on several rarity tiers). TitleFlair now wraps Badge instead of inline styles.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…arted CTA
De-pilled CTAs hug the text now (lg 13/26 -> 9/18). The closing 'Get Started'
under the 'Ready to Start Growing?' hero uses a new xl size so it stays a
prominent central focal point; navbar + hero beta stay tight.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…bright accent
The header button hard-coded background:var(--accent); after accent shifted to the
brighter forest, it rendered brighter than every other primary button. Now a proper
<Button variant=primary> (--brand-forest), matching the term pills and app buttons.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Family (b) control: a wrapping row of selectable pills for 'All' + N
dynamic filters, wrapping the existing <Pill> (with per-option color/icon).
Complements <Toggle> (family (a): fixed connected segmented control).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the custom accent-filled pill buttons with the shared <Toggle>
(forest-filled active). Keeps SemesterChips' public API; also fixes the
prior --accent button fill.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Overview/Chat/Study match/Activity tab row now uses the shared <Toggle>.
setTab wiring unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Jose-Gael-Cruz-Lopezand others added 21 commits July 1, 2026 04:38
#147)
Closes an acceptance-#5 gap found in review: the probe-failure branch in
gemini_test (except → {ok:false,error}, still 200) was untested. Adds a case
that raises from health_probe_agent.run and asserts the failure contract.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(agents): migrate remaining one-shot LLM calls to Pydantic AI agents (#147)
…mini fallback (#144)
calendar_service already extracted via syllabus_extraction_agent as the
primary path; the raw-Gemini `parse_syllabus`/`call_gemini_json` fallback was
the only remaining seam. Remove it and degrade gracefully instead:
- Delete parse_syllabus, the call_gemini_json import, and the now-unused
PROMPT_PATH. On agent guardrail-trip or unexpected failure,
extract_assignments_from_file now returns _degraded_result (empty
assignments + a user-facing warning, no second LLM call) — resilience
without a raw Gemini call.
- Response contract preserved: {assignments, warnings, raw_text} on every
path (success / degrade / empty-text).
- notes stay encrypted at the write boundary (insert_new_assignments,
encrypt_if_present) per #126 — added a regression test.
Tests: rewrote the parse_syllabus-based fallback tests in test_ocr_pipeline.py
to assert the graceful degrade; repointed the live fixtures off parse_syllabus
onto the agent; added a notes-encryption test. Full suite green (2 pre-existing
storage-env failures unrelated). ruff clean.
Spec: specs/144-calendar-agent.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…uiz-gen fallback (#145)
Removes the last raw call_gemini* seams from the quiz surface:
- New agents/quiz_context.py (QuizContext: weak_areas, common_mistakes,
questions_seen_summary, recommended_difficulty, notes — mirrors
quiz_context_update.txt). routes/quiz.py's post-submit background task runs it
via run_agent_sync and saves .model_dump() (unchanged context_json shape).
- New agents/course_summary.py (CourseSummary.summary). course_context_service's
_generate_summary_with_gemini runs it via run_agent_sync and keeps the
deterministic template fallback on agent failure (no second LLM call).
- Removed _legacy_generate_quiz + the MODEL_LITE/MODEL_SMART/call_gemini_json
import. On agent guardrail-trip or failure, generate_quiz now returns 502
instead of a raw-Gemini fallback; the pre-agent 404 (unknown node) is
unchanged. _providers registers course_summary (flash) + quiz_context (lite).
Coordination: does NOT touch scoring / apply_graph_update / quiz_attempts writes
(#128/#129 territory) — only the LLM seams.
Tests: rewrote the legacy-fallback quiz tests to degrade-to-502; repointed the
submit context-update patches onto quiz_context_agent; removed the obsolete
legacy prompt-augmentation + legacy-model tests; added quiz-context save,
course-summary success/fallback tests. Full suite 826 passed (2 pre-existing
storage-env failures). ruff clean.
Spec: specs/145-quiz-course-context-agents.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…_context in submit mocks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-files + drop anon policies (#304)
Closes the last anon storage surface from #231. The rest already shipped:
application_resumes is private (backend/service-key via careers.py), issue-report
screenshots upload through the auth-gated POST /api/issue-reports/screenshot
(service role, size+mime validated; feedback.py), and ReportIssueFlow.tsx no
longer uses the anon storage client.
Migration 0029 (verified against prod, read-only, before writing):
- makes issues-media-files private (application_resumes already private → no-op),
- caps it at 5 MB + a mime allowlist matching the upload endpoint,
- drops the two anon storage.objects policies ("Allow uploads" INSERT / "Allow
public read" SELECT) the app no longer relies on.
Safe: uploads use the service role (bypasses storage RLS) and review is via the
dashboard / signed URLs, so nothing reads issues-media-files via a public URL.
Idempotent across environments (WHERE no-ops on absent buckets; DROP ... IF EXISTS).
NOT yet applied to prod — apply via `python -m db.migrate` (see the migration's
privilege note re: the storage schema).
Also updates docs/security/storage-hardening-plan.md to reflect the shipped state
(résumé-PII exposure already closed; only this lockdown remained).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses CodeRabbit review on #286:
- Toggle: flexWrap so SemesterChips' dynamic list wraps instead of
overflowing horizontally (no effect on fixed-option callers).
- Pill: type="button" (stops enclosing-form submit) and aria-pressed
to expose active state to assistive tech.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
refactor(frontend): unify CSS token layer + add (public) route group (Phase 1)
The direct-run harness (python3 tests/test_ocr_pipeline.py, documented in
the module docstring) had lost its sys.exit(1) and traceback on failure,
so a failed run exited 0 and silently reported success. Restore both.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Jul 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 27b74f9f-1588-4381-9a18-67c3da8bae5b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch main

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 2, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingf472300Jul 02 2026, 06:14 AM

feat(calendar): syllabus extraction via agent, retire raw-Gemini fallback (#144)
@railway-app
railway-appBottemporarily deployed to Sapling / staging July 2, 2026 05:45 Inactive
AndresL230and others added 2 commits July 2, 2026 02:08
The all-questions-drift branch's comment still described routing to the
legacy fallback, which #145 deleted. The RuntimeError is now caught by
generate_quiz's bare-except and returned as HTTP 502 (per
test_degrades_when_all_questions_drift). Comment-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145)
@railway-app
railway-appBottemporarily deployed to Sapling / staging July 2, 2026 06:12 Inactive
@AndresL230
AndresL230 merged commit 4cf87f1 into productionJul 2, 2026
9 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@AndresL230@Darkest-Teddy@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Promote staging → production: design-system, agents, calendar rewire, storage lockdown - #305

Merged
AndresL230 merged 259 commits into
productionfrom
main
Jul 2, 2026
Merged

Promote staging → production: design-system, agents, calendar rewire, storage lockdown#305
AndresL230 merged 259 commits into
productionfrom
main

Conversation

@AndresL230

Copy link
Copy Markdown
Collaborator

Promote main (staging) → production. Routine promotion since the last one (PR #241, 2026-06-27). The DB modular redesign already shipped in #241; this batch is 64 real commits (the other 188 are #147 no-op checkpoints).

What ships

Frontend — design-system / token unification (#286, #294)

  • Shared Button / Toggle / Chip / Badge primitives + FilterPills; migrated high-impact buttons, selectors, and filters across Learn / Gradebook / Calendar / Social / Library / Study / Tree.
  • Single token namespace (removed aliases), (public) route group + .public-surface marketing layer, pre-auth token de-shadowing, a11y fixes (aria-pressed, neutral Badge text).

Backend — one-shot LLM → Pydantic AI agents (#147, #296)

  • New study_guide_agent, social_summary_agent, health_probe_agent on the shared provider + run_agent_sync bridge.
  • /api/gemini-test, study-guide generation, and social group summaries routed through agents (502 on failure); failure-contract tests added.

Calendar — enrollment rewire (#283)

  • Enrollment-scoped Google sync/export/update/delete and study-block reads; assignments written via resolved enrollment_id + source tag; syllabus saves tagged.

Security — storage lockdown (#231, #304)

  • Makes issues-media-files private, caps size/mime, drops the last anon storage policies. Requires migration 0029 on the prod DB (see below).

Fixes

  • Onboarding completion persistence (same-origin helper), OAuth popup COOP (same-origin-allow-popups), graph unexplored-node labels, local-mode mock data for gradebook detail + chatroom.

Prod-side steps (do around merge)

  1. Apply migration 0029 to proddotenv -f .env.production run -- python -m db.migrate (idempotent; storage hardening only, header notes it was verified read-only against prod).
  2. Merge → triggers Railway (production) + Cloudflare Workers Builds (frontend worker) prod deploys.
  3. Smoke test prod: sign-in, dashboard API calls, doc upload → graph, issue-report screenshot upload (service-role path).

Verified before opening

  • mainproduction merges clean (no conflicts).
  • CI green on main; no new backend env vars vs. last promotion.
  • Only new prod DB migration is 0029.

🤖 Generated with Claude Code

AndresL230and others added 30 commits June 28, 2026 01:56
Rewires sync_to_google and export_to_google onto the enrollment-keyed
schema: select/write-back scoped by enrollment_id membership instead of
the removed user_id column; drops courses!left embed in favour of
_course_meta_cached. Updates test_calendar_export_idor.py and
test_calendar_sibling_write_scoping.py to assert the new enrollment_id
boundary (same IDOR guarantee, new key).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ment schema
- routes/documents.py: pass source="syllabus" at both save_assignments_to_db
call sites (_save_orchestrator_syllabus and the legacy call_gemini_json path)
- tests/test_calendar_routes.py: rewire TestSaveAssignments to include course_id
in fixtures and mock enrollment_id_for/user_enrollment_ids; rewire
TestGetUpcoming.test_returns_assignments_from_db to the enrollment-keyed row
shape (enrollment_id, no user_id/course_id/courses columns); add _tbl helper
- tests/test_assignment_notes_encryption.py: supply course_id to test fixtures
and mock academics so insert_new_assignments reaches the encryption boundary
- tests/test_documents_routes.py: update assert_called_once_with to include
source='syllabus' to match the new tagged call
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nrollment-rewire
fix(calendar): rewire assignments to the enrollment-keyed schema (dashboard 500)
…legend
- Remove r > 10 gate in KnowledgeGraph2D so every concept node shows its
name regardless of mastery score (unexplored nodes have r=8, below old threshold)
- Capitalize legend labels: mastered/learning/struggling/unexplored → title case
- Add react-force-graph-3d (missing from node_modules, caused build error)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…g sticks
handleOnboardingComplete POSTed /api/onboarding/profile to the cross-origin
NEXT_PUBLIC_API_URL without credentials:'include', so the browser dropped the
sapling_session cookie and the backend's require_self returned 401. The
onboarding_completed flag never flipped to True — and because the code never
checked res.ok, the 401 was swallowed and users were routed to /dashboard as if
it worked, then bounced back to "Get Started" on every subsequent sign-in.
Switch to the existing submitOnboardingProfile() helper, which goes through the
same-origin lib/api.ts fetchJSON path (API_URL='' proxied via the /api/* rewrite,
credentials:'include', and an res.ok check that surfaces failures instead of
hiding them).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ersistence
fix(onboarding): persist completion so users aren't trapped in the Get Started loop
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ate states correct)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…plicate namespace
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rarity + glass-input
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng-layer host
Moves landing + about/careers/privacy/terms under app/(public)/ (URLs unchanged).
The layout applies a token-only .public-surface scope so content pages keep the
warm paper canvas; the landing retains .landing-page for its mesh visual.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(public)/page.tsx, HowItWorks.tsx, Dialog.tsx: --brand-text1/2 -> --text/--text-dim,
--radius-* -> --r-*, --ease-out/in-out -> --ease. Mechanical identifier renames only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ngle namespace
Migrates the 3 remaining globals.css --ease-out consumers to --ease, then deletes
the temporary :root brand aliases and the .public-surface/.landing-page namespace
aliases. Zero retired-token references remain tree-wide.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… + dir hygiene)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/page.tsx
The route move shifted the landing page out from under its path-keyed
suppression entry, un-suppressing 4 pre-existing grandfathered errors
(no-html-link-for-pages, prefer-const x2, react-hooks/immutability).
Re-homes the entry to the new path; no code behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rison
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Retire sage --accent -> brighter forest (--brand-forest-bright) for highlight/focus;
merge --state-mastery + --grade-a into one --positive status green; add .btn--lg
hero size for de-pilled CTAs. Three forest-family greens by role: forest(action) /
accent(highlight) / positive(status).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Thin wrappers over the canonical .btn/.chip classes. Button enforces one shape
(6px) with variant + size (incl. lg hero size); Toggle is the one segmented
control; Chip/Badge collapse the pill/badge zoo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ives
- Landing CTAs (Get Started x2, Sign up for Beta) -> <Button size=lg>, de-pilled
to sharp 6px, infinite glow removed.
- 5 Gradebook modal save/submit buttons -> <Button> (kills hard-coded borderRadius:6).
- Study flashcard ratings 10px -> 6px (kept color-coding).
- Tokenize orphan #1a5c2a wordmark -> var(--brand-forest) across 8 files.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add optional per-option title to <Toggle>; replace the Learn setup mode pills with
it. ModelToggle (Fast/Smart) intentionally left as-is — it has a sliding animation,
per-option color semantics, and a tooltip that the generic Toggle would degrade.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Flair
Badge carries the hue on border + soft bg, keeps text neutral (colored text fails
4.5:1 on several rarity tiers). TitleFlair now wraps Badge instead of inline styles.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…arted CTA
De-pilled CTAs hug the text now (lg 13/26 -> 9/18). The closing 'Get Started'
under the 'Ready to Start Growing?' hero uses a new xl size so it stays a
prominent central focal point; navbar + hero beta stay tight.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…bright accent
The header button hard-coded background:var(--accent); after accent shifted to the
brighter forest, it rendered brighter than every other primary button. Now a proper
<Button variant=primary> (--brand-forest), matching the term pills and app buttons.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Family (b) control: a wrapping row of selectable pills for 'All' + N
dynamic filters, wrapping the existing <Pill> (with per-option color/icon).
Complements <Toggle> (family (a): fixed connected segmented control).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the custom accent-filled pill buttons with the shared <Toggle>
(forest-filled active). Keeps SemesterChips' public API; also fixes the
prior --accent button fill.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Overview/Chat/Study match/Activity tab row now uses the shared <Toggle>.
setTab wiring unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Jose-Gael-Cruz-Lopezand others added 21 commits July 1, 2026 04:38
#147)
Closes an acceptance-#5 gap found in review: the probe-failure branch in
gemini_test (except → {ok:false,error}, still 200) was untested. Adds a case
that raises from health_probe_agent.run and asserts the failure contract.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(agents): migrate remaining one-shot LLM calls to Pydantic AI agents (#147)
…mini fallback (#144)
calendar_service already extracted via syllabus_extraction_agent as the
primary path; the raw-Gemini `parse_syllabus`/`call_gemini_json` fallback was
the only remaining seam. Remove it and degrade gracefully instead:
- Delete parse_syllabus, the call_gemini_json import, and the now-unused
PROMPT_PATH. On agent guardrail-trip or unexpected failure,
extract_assignments_from_file now returns _degraded_result (empty
assignments + a user-facing warning, no second LLM call) — resilience
without a raw Gemini call.
- Response contract preserved: {assignments, warnings, raw_text} on every
path (success / degrade / empty-text).
- notes stay encrypted at the write boundary (insert_new_assignments,
encrypt_if_present) per #126 — added a regression test.
Tests: rewrote the parse_syllabus-based fallback tests in test_ocr_pipeline.py
to assert the graceful degrade; repointed the live fixtures off parse_syllabus
onto the agent; added a notes-encryption test. Full suite green (2 pre-existing
storage-env failures unrelated). ruff clean.
Spec: specs/144-calendar-agent.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…uiz-gen fallback (#145)
Removes the last raw call_gemini* seams from the quiz surface:
- New agents/quiz_context.py (QuizContext: weak_areas, common_mistakes,
questions_seen_summary, recommended_difficulty, notes — mirrors
quiz_context_update.txt). routes/quiz.py's post-submit background task runs it
via run_agent_sync and saves .model_dump() (unchanged context_json shape).
- New agents/course_summary.py (CourseSummary.summary). course_context_service's
_generate_summary_with_gemini runs it via run_agent_sync and keeps the
deterministic template fallback on agent failure (no second LLM call).
- Removed _legacy_generate_quiz + the MODEL_LITE/MODEL_SMART/call_gemini_json
import. On agent guardrail-trip or failure, generate_quiz now returns 502
instead of a raw-Gemini fallback; the pre-agent 404 (unknown node) is
unchanged. _providers registers course_summary (flash) + quiz_context (lite).
Coordination: does NOT touch scoring / apply_graph_update / quiz_attempts writes
(#128/#129 territory) — only the LLM seams.
Tests: rewrote the legacy-fallback quiz tests to degrade-to-502; repointed the
submit context-update patches onto quiz_context_agent; removed the obsolete
legacy prompt-augmentation + legacy-model tests; added quiz-context save,
course-summary success/fallback tests. Full suite 826 passed (2 pre-existing
storage-env failures). ruff clean.
Spec: specs/145-quiz-course-context-agents.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…_context in submit mocks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-files + drop anon policies (#304)
Closes the last anon storage surface from #231. The rest already shipped:
application_resumes is private (backend/service-key via careers.py), issue-report
screenshots upload through the auth-gated POST /api/issue-reports/screenshot
(service role, size+mime validated; feedback.py), and ReportIssueFlow.tsx no
longer uses the anon storage client.
Migration 0029 (verified against prod, read-only, before writing):
- makes issues-media-files private (application_resumes already private → no-op),
- caps it at 5 MB + a mime allowlist matching the upload endpoint,
- drops the two anon storage.objects policies ("Allow uploads" INSERT / "Allow
public read" SELECT) the app no longer relies on.
Safe: uploads use the service role (bypasses storage RLS) and review is via the
dashboard / signed URLs, so nothing reads issues-media-files via a public URL.
Idempotent across environments (WHERE no-ops on absent buckets; DROP ... IF EXISTS).
NOT yet applied to prod — apply via `python -m db.migrate` (see the migration's
privilege note re: the storage schema).
Also updates docs/security/storage-hardening-plan.md to reflect the shipped state
(résumé-PII exposure already closed; only this lockdown remained).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses CodeRabbit review on #286:
- Toggle: flexWrap so SemesterChips' dynamic list wraps instead of
overflowing horizontally (no effect on fixed-option callers).
- Pill: type="button" (stops enclosing-form submit) and aria-pressed
to expose active state to assistive tech.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
refactor(frontend): unify CSS token layer + add (public) route group (Phase 1)
The direct-run harness (python3 tests/test_ocr_pipeline.py, documented in
the module docstring) had lost its sys.exit(1) and traceback on failure,
so a failed run exited 0 and silently reported success. Restore both.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Jul 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 27b74f9f-1588-4381-9a18-67c3da8bae5b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch main

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 2, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingf472300Jul 02 2026, 06:14 AM

feat(calendar): syllabus extraction via agent, retire raw-Gemini fallback (#144)
@railway-app
railway-appBottemporarily deployed to Sapling / staging July 2, 2026 05:45 Inactive
AndresL230and others added 2 commits July 2, 2026 02:08
The all-questions-drift branch's comment still described routing to the
legacy fallback, which #145 deleted. The RuntimeError is now caught by
generate_quiz's bare-except and returned as HTTP 502 (per
test_degrades_when_all_questions_drift). Comment-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145)
@railway-app
railway-appBottemporarily deployed to Sapling / staging July 2, 2026 06:12 Inactive
@AndresL230
AndresL230 merged commit 4cf87f1 into productionJul 2, 2026
9 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@AndresL230@Darkest-Teddy@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Promote staging → production: design-system, agents, calendar rewire, storage lockdown - #305

Merged
AndresL230 merged 259 commits into
productionfrom
main
Jul 2, 2026
Merged

Promote staging → production: design-system, agents, calendar rewire, storage lockdown#305
AndresL230 merged 259 commits into
productionfrom
main

Conversation

@AndresL230

Copy link
Copy Markdown
Collaborator

Promote main (staging) → production. Routine promotion since the last one (PR #241, 2026-06-27). The DB modular redesign already shipped in #241; this batch is 64 real commits (the other 188 are #147 no-op checkpoints).

What ships

Frontend — design-system / token unification (#286, #294)

  • Shared Button / Toggle / Chip / Badge primitives + FilterPills; migrated high-impact buttons, selectors, and filters across Learn / Gradebook / Calendar / Social / Library / Study / Tree.
  • Single token namespace (removed aliases), (public) route group + .public-surface marketing layer, pre-auth token de-shadowing, a11y fixes (aria-pressed, neutral Badge text).

Backend — one-shot LLM → Pydantic AI agents (#147, #296)

  • New study_guide_agent, social_summary_agent, health_probe_agent on the shared provider + run_agent_sync bridge.
  • /api/gemini-test, study-guide generation, and social group summaries routed through agents (502 on failure); failure-contract tests added.

Calendar — enrollment rewire (#283)

  • Enrollment-scoped Google sync/export/update/delete and study-block reads; assignments written via resolved enrollment_id + source tag; syllabus saves tagged.

Security — storage lockdown (#231, #304)

  • Makes issues-media-files private, caps size/mime, drops the last anon storage policies. Requires migration 0029 on the prod DB (see below).

Fixes

  • Onboarding completion persistence (same-origin helper), OAuth popup COOP (same-origin-allow-popups), graph unexplored-node labels, local-mode mock data for gradebook detail + chatroom.

Prod-side steps (do around merge)

  1. Apply migration 0029 to proddotenv -f .env.production run -- python -m db.migrate (idempotent; storage hardening only, header notes it was verified read-only against prod).
  2. Merge → triggers Railway (production) + Cloudflare Workers Builds (frontend worker) prod deploys.
  3. Smoke test prod: sign-in, dashboard API calls, doc upload → graph, issue-report screenshot upload (service-role path).

Verified before opening

  • mainproduction merges clean (no conflicts).
  • CI green on main; no new backend env vars vs. last promotion.
  • Only new prod DB migration is 0029.

🤖 Generated with Claude Code

AndresL230and others added 30 commits June 28, 2026 01:56
Rewires sync_to_google and export_to_google onto the enrollment-keyed
schema: select/write-back scoped by enrollment_id membership instead of
the removed user_id column; drops courses!left embed in favour of
_course_meta_cached. Updates test_calendar_export_idor.py and
test_calendar_sibling_write_scoping.py to assert the new enrollment_id
boundary (same IDOR guarantee, new key).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ment schema
- routes/documents.py: pass source="syllabus" at both save_assignments_to_db
call sites (_save_orchestrator_syllabus and the legacy call_gemini_json path)
- tests/test_calendar_routes.py: rewire TestSaveAssignments to include course_id
in fixtures and mock enrollment_id_for/user_enrollment_ids; rewire
TestGetUpcoming.test_returns_assignments_from_db to the enrollment-keyed row
shape (enrollment_id, no user_id/course_id/courses columns); add _tbl helper
- tests/test_assignment_notes_encryption.py: supply course_id to test fixtures
and mock academics so insert_new_assignments reaches the encryption boundary
- tests/test_documents_routes.py: update assert_called_once_with to include
source='syllabus' to match the new tagged call
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nrollment-rewire
fix(calendar): rewire assignments to the enrollment-keyed schema (dashboard 500)
…legend
- Remove r > 10 gate in KnowledgeGraph2D so every concept node shows its
name regardless of mastery score (unexplored nodes have r=8, below old threshold)
- Capitalize legend labels: mastered/learning/struggling/unexplored → title case
- Add react-force-graph-3d (missing from node_modules, caused build error)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…g sticks
handleOnboardingComplete POSTed /api/onboarding/profile to the cross-origin
NEXT_PUBLIC_API_URL without credentials:'include', so the browser dropped the
sapling_session cookie and the backend's require_self returned 401. The
onboarding_completed flag never flipped to True — and because the code never
checked res.ok, the 401 was swallowed and users were routed to /dashboard as if
it worked, then bounced back to "Get Started" on every subsequent sign-in.
Switch to the existing submitOnboardingProfile() helper, which goes through the
same-origin lib/api.ts fetchJSON path (API_URL='' proxied via the /api/* rewrite,
credentials:'include', and an res.ok check that surfaces failures instead of
hiding them).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ersistence
fix(onboarding): persist completion so users aren't trapped in the Get Started loop
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ate states correct)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…plicate namespace
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rarity + glass-input
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng-layer host
Moves landing + about/careers/privacy/terms under app/(public)/ (URLs unchanged).
The layout applies a token-only .public-surface scope so content pages keep the
warm paper canvas; the landing retains .landing-page for its mesh visual.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(public)/page.tsx, HowItWorks.tsx, Dialog.tsx: --brand-text1/2 -> --text/--text-dim,
--radius-* -> --r-*, --ease-out/in-out -> --ease. Mechanical identifier renames only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ngle namespace
Migrates the 3 remaining globals.css --ease-out consumers to --ease, then deletes
the temporary :root brand aliases and the .public-surface/.landing-page namespace
aliases. Zero retired-token references remain tree-wide.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… + dir hygiene)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/page.tsx
The route move shifted the landing page out from under its path-keyed
suppression entry, un-suppressing 4 pre-existing grandfathered errors
(no-html-link-for-pages, prefer-const x2, react-hooks/immutability).
Re-homes the entry to the new path; no code behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rison
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Retire sage --accent -> brighter forest (--brand-forest-bright) for highlight/focus;
merge --state-mastery + --grade-a into one --positive status green; add .btn--lg
hero size for de-pilled CTAs. Three forest-family greens by role: forest(action) /
accent(highlight) / positive(status).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Thin wrappers over the canonical .btn/.chip classes. Button enforces one shape
(6px) with variant + size (incl. lg hero size); Toggle is the one segmented
control; Chip/Badge collapse the pill/badge zoo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ives
- Landing CTAs (Get Started x2, Sign up for Beta) -> <Button size=lg>, de-pilled
to sharp 6px, infinite glow removed.
- 5 Gradebook modal save/submit buttons -> <Button> (kills hard-coded borderRadius:6).
- Study flashcard ratings 10px -> 6px (kept color-coding).
- Tokenize orphan #1a5c2a wordmark -> var(--brand-forest) across 8 files.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add optional per-option title to <Toggle>; replace the Learn setup mode pills with
it. ModelToggle (Fast/Smart) intentionally left as-is — it has a sliding animation,
per-option color semantics, and a tooltip that the generic Toggle would degrade.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Flair
Badge carries the hue on border + soft bg, keeps text neutral (colored text fails
4.5:1 on several rarity tiers). TitleFlair now wraps Badge instead of inline styles.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…arted CTA
De-pilled CTAs hug the text now (lg 13/26 -> 9/18). The closing 'Get Started'
under the 'Ready to Start Growing?' hero uses a new xl size so it stays a
prominent central focal point; navbar + hero beta stay tight.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…bright accent
The header button hard-coded background:var(--accent); after accent shifted to the
brighter forest, it rendered brighter than every other primary button. Now a proper
<Button variant=primary> (--brand-forest), matching the term pills and app buttons.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Family (b) control: a wrapping row of selectable pills for 'All' + N
dynamic filters, wrapping the existing <Pill> (with per-option color/icon).
Complements <Toggle> (family (a): fixed connected segmented control).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the custom accent-filled pill buttons with the shared <Toggle>
(forest-filled active). Keeps SemesterChips' public API; also fixes the
prior --accent button fill.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Overview/Chat/Study match/Activity tab row now uses the shared <Toggle>.
setTab wiring unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Jose-Gael-Cruz-Lopezand others added 21 commits July 1, 2026 04:38
#147)
Closes an acceptance-#5 gap found in review: the probe-failure branch in
gemini_test (except → {ok:false,error}, still 200) was untested. Adds a case
that raises from health_probe_agent.run and asserts the failure contract.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(agents): migrate remaining one-shot LLM calls to Pydantic AI agents (#147)
…mini fallback (#144)
calendar_service already extracted via syllabus_extraction_agent as the
primary path; the raw-Gemini `parse_syllabus`/`call_gemini_json` fallback was
the only remaining seam. Remove it and degrade gracefully instead:
- Delete parse_syllabus, the call_gemini_json import, and the now-unused
PROMPT_PATH. On agent guardrail-trip or unexpected failure,
extract_assignments_from_file now returns _degraded_result (empty
assignments + a user-facing warning, no second LLM call) — resilience
without a raw Gemini call.
- Response contract preserved: {assignments, warnings, raw_text} on every
path (success / degrade / empty-text).
- notes stay encrypted at the write boundary (insert_new_assignments,
encrypt_if_present) per #126 — added a regression test.
Tests: rewrote the parse_syllabus-based fallback tests in test_ocr_pipeline.py
to assert the graceful degrade; repointed the live fixtures off parse_syllabus
onto the agent; added a notes-encryption test. Full suite green (2 pre-existing
storage-env failures unrelated). ruff clean.
Spec: specs/144-calendar-agent.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…uiz-gen fallback (#145)
Removes the last raw call_gemini* seams from the quiz surface:
- New agents/quiz_context.py (QuizContext: weak_areas, common_mistakes,
questions_seen_summary, recommended_difficulty, notes — mirrors
quiz_context_update.txt). routes/quiz.py's post-submit background task runs it
via run_agent_sync and saves .model_dump() (unchanged context_json shape).
- New agents/course_summary.py (CourseSummary.summary). course_context_service's
_generate_summary_with_gemini runs it via run_agent_sync and keeps the
deterministic template fallback on agent failure (no second LLM call).
- Removed _legacy_generate_quiz + the MODEL_LITE/MODEL_SMART/call_gemini_json
import. On agent guardrail-trip or failure, generate_quiz now returns 502
instead of a raw-Gemini fallback; the pre-agent 404 (unknown node) is
unchanged. _providers registers course_summary (flash) + quiz_context (lite).
Coordination: does NOT touch scoring / apply_graph_update / quiz_attempts writes
(#128/#129 territory) — only the LLM seams.
Tests: rewrote the legacy-fallback quiz tests to degrade-to-502; repointed the
submit context-update patches onto quiz_context_agent; removed the obsolete
legacy prompt-augmentation + legacy-model tests; added quiz-context save,
course-summary success/fallback tests. Full suite 826 passed (2 pre-existing
storage-env failures). ruff clean.
Spec: specs/145-quiz-course-context-agents.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…_context in submit mocks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-files + drop anon policies (#304)
Closes the last anon storage surface from #231. The rest already shipped:
application_resumes is private (backend/service-key via careers.py), issue-report
screenshots upload through the auth-gated POST /api/issue-reports/screenshot
(service role, size+mime validated; feedback.py), and ReportIssueFlow.tsx no
longer uses the anon storage client.
Migration 0029 (verified against prod, read-only, before writing):
- makes issues-media-files private (application_resumes already private → no-op),
- caps it at 5 MB + a mime allowlist matching the upload endpoint,
- drops the two anon storage.objects policies ("Allow uploads" INSERT / "Allow
public read" SELECT) the app no longer relies on.
Safe: uploads use the service role (bypasses storage RLS) and review is via the
dashboard / signed URLs, so nothing reads issues-media-files via a public URL.
Idempotent across environments (WHERE no-ops on absent buckets; DROP ... IF EXISTS).
NOT yet applied to prod — apply via `python -m db.migrate` (see the migration's
privilege note re: the storage schema).
Also updates docs/security/storage-hardening-plan.md to reflect the shipped state
(résumé-PII exposure already closed; only this lockdown remained).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses CodeRabbit review on #286:
- Toggle: flexWrap so SemesterChips' dynamic list wraps instead of
overflowing horizontally (no effect on fixed-option callers).
- Pill: type="button" (stops enclosing-form submit) and aria-pressed
to expose active state to assistive tech.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
refactor(frontend): unify CSS token layer + add (public) route group (Phase 1)
The direct-run harness (python3 tests/test_ocr_pipeline.py, documented in
the module docstring) had lost its sys.exit(1) and traceback on failure,
so a failed run exited 0 and silently reported success. Restore both.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Jul 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 27b74f9f-1588-4381-9a18-67c3da8bae5b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch main

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 2, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingf472300Jul 02 2026, 06:14 AM

feat(calendar): syllabus extraction via agent, retire raw-Gemini fallback (#144)
@railway-app
railway-appBottemporarily deployed to Sapling / staging July 2, 2026 05:45 Inactive
AndresL230and others added 2 commits July 2, 2026 02:08
The all-questions-drift branch's comment still described routing to the
legacy fallback, which #145 deleted. The RuntimeError is now caught by
generate_quiz's bare-except and returned as HTTP 502 (per
test_degrades_when_all_questions_drift). Comment-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145)
@railway-app
railway-appBottemporarily deployed to Sapling / staging July 2, 2026 06:12 Inactive
@AndresL230
AndresL230 merged commit 4cf87f1 into productionJul 2, 2026
9 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@AndresL230@Darkest-Teddy@Jose-Gael-Cruz-Lopez
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Promote staging → production: design-system, agents, calendar rewire, storage lockdown - #305

Merged
AndresL230 merged 259 commits into
productionfrom
main
Jul 2, 2026
Merged

Promote staging → production: design-system, agents, calendar rewire, storage lockdown#305
AndresL230 merged 259 commits into
productionfrom
main

Conversation

@AndresL230

Copy link
Copy Markdown
Collaborator

Promote main (staging) → production. Routine promotion since the last one (PR #241, 2026-06-27). The DB modular redesign already shipped in #241; this batch is 64 real commits (the other 188 are #147 no-op checkpoints).

What ships

Frontend — design-system / token unification (#286, #294)

  • Shared Button / Toggle / Chip / Badge primitives + FilterPills; migrated high-impact buttons, selectors, and filters across Learn / Gradebook / Calendar / Social / Library / Study / Tree.
  • Single token namespace (removed aliases), (public) route group + .public-surface marketing layer, pre-auth token de-shadowing, a11y fixes (aria-pressed, neutral Badge text).

Backend — one-shot LLM → Pydantic AI agents (#147, #296)

  • New study_guide_agent, social_summary_agent, health_probe_agent on the shared provider + run_agent_sync bridge.
  • /api/gemini-test, study-guide generation, and social group summaries routed through agents (502 on failure); failure-contract tests added.

Calendar — enrollment rewire (#283)

  • Enrollment-scoped Google sync/export/update/delete and study-block reads; assignments written via resolved enrollment_id + source tag; syllabus saves tagged.

Security — storage lockdown (#231, #304)

  • Makes issues-media-files private, caps size/mime, drops the last anon storage policies. Requires migration 0029 on the prod DB (see below).

Fixes

  • Onboarding completion persistence (same-origin helper), OAuth popup COOP (same-origin-allow-popups), graph unexplored-node labels, local-mode mock data for gradebook detail + chatroom.

Prod-side steps (do around merge)

  1. Apply migration 0029 to proddotenv -f .env.production run -- python -m db.migrate (idempotent; storage hardening only, header notes it was verified read-only against prod).
  2. Merge → triggers Railway (production) + Cloudflare Workers Builds (frontend worker) prod deploys.
  3. Smoke test prod: sign-in, dashboard API calls, doc upload → graph, issue-report screenshot upload (service-role path).

Verified before opening

  • mainproduction merges clean (no conflicts).
  • CI green on main; no new backend env vars vs. last promotion.
  • Only new prod DB migration is 0029.

🤖 Generated with Claude Code

AndresL230and others added 30 commits June 28, 2026 01:56
Rewires sync_to_google and export_to_google onto the enrollment-keyed
schema: select/write-back scoped by enrollment_id membership instead of
the removed user_id column; drops courses!left embed in favour of
_course_meta_cached. Updates test_calendar_export_idor.py and
test_calendar_sibling_write_scoping.py to assert the new enrollment_id
boundary (same IDOR guarantee, new key).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ment schema
- routes/documents.py: pass source="syllabus" at both save_assignments_to_db
call sites (_save_orchestrator_syllabus and the legacy call_gemini_json path)
- tests/test_calendar_routes.py: rewire TestSaveAssignments to include course_id
in fixtures and mock enrollment_id_for/user_enrollment_ids; rewire
TestGetUpcoming.test_returns_assignments_from_db to the enrollment-keyed row
shape (enrollment_id, no user_id/course_id/courses columns); add _tbl helper
- tests/test_assignment_notes_encryption.py: supply course_id to test fixtures
and mock academics so insert_new_assignments reaches the encryption boundary
- tests/test_documents_routes.py: update assert_called_once_with to include
source='syllabus' to match the new tagged call
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nrollment-rewire
fix(calendar): rewire assignments to the enrollment-keyed schema (dashboard 500)
…legend
- Remove r > 10 gate in KnowledgeGraph2D so every concept node shows its
name regardless of mastery score (unexplored nodes have r=8, below old threshold)
- Capitalize legend labels: mastered/learning/struggling/unexplored → title case
- Add react-force-graph-3d (missing from node_modules, caused build error)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…g sticks
handleOnboardingComplete POSTed /api/onboarding/profile to the cross-origin
NEXT_PUBLIC_API_URL without credentials:'include', so the browser dropped the
sapling_session cookie and the backend's require_self returned 401. The
onboarding_completed flag never flipped to True — and because the code never
checked res.ok, the 401 was swallowed and users were routed to /dashboard as if
it worked, then bounced back to "Get Started" on every subsequent sign-in.
Switch to the existing submitOnboardingProfile() helper, which goes through the
same-origin lib/api.ts fetchJSON path (API_URL='' proxied via the /api/* rewrite,
credentials:'include', and an res.ok check that surfaces failures instead of
hiding them).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ersistence
fix(onboarding): persist completion so users aren't trapped in the Get Started loop
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ate states correct)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…plicate namespace
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rarity + glass-input
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng-layer host
Moves landing + about/careers/privacy/terms under app/(public)/ (URLs unchanged).
The layout applies a token-only .public-surface scope so content pages keep the
warm paper canvas; the landing retains .landing-page for its mesh visual.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(public)/page.tsx, HowItWorks.tsx, Dialog.tsx: --brand-text1/2 -> --text/--text-dim,
--radius-* -> --r-*, --ease-out/in-out -> --ease. Mechanical identifier renames only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ngle namespace
Migrates the 3 remaining globals.css --ease-out consumers to --ease, then deletes
the temporary :root brand aliases and the .public-surface/.landing-page namespace
aliases. Zero retired-token references remain tree-wide.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… + dir hygiene)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/page.tsx
The route move shifted the landing page out from under its path-keyed
suppression entry, un-suppressing 4 pre-existing grandfathered errors
(no-html-link-for-pages, prefer-const x2, react-hooks/immutability).
Re-homes the entry to the new path; no code behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rison
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Retire sage --accent -> brighter forest (--brand-forest-bright) for highlight/focus;
merge --state-mastery + --grade-a into one --positive status green; add .btn--lg
hero size for de-pilled CTAs. Three forest-family greens by role: forest(action) /
accent(highlight) / positive(status).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Thin wrappers over the canonical .btn/.chip classes. Button enforces one shape
(6px) with variant + size (incl. lg hero size); Toggle is the one segmented
control; Chip/Badge collapse the pill/badge zoo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ives
- Landing CTAs (Get Started x2, Sign up for Beta) -> <Button size=lg>, de-pilled
to sharp 6px, infinite glow removed.
- 5 Gradebook modal save/submit buttons -> <Button> (kills hard-coded borderRadius:6).
- Study flashcard ratings 10px -> 6px (kept color-coding).
- Tokenize orphan #1a5c2a wordmark -> var(--brand-forest) across 8 files.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add optional per-option title to <Toggle>; replace the Learn setup mode pills with
it. ModelToggle (Fast/Smart) intentionally left as-is — it has a sliding animation,
per-option color semantics, and a tooltip that the generic Toggle would degrade.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Flair
Badge carries the hue on border + soft bg, keeps text neutral (colored text fails
4.5:1 on several rarity tiers). TitleFlair now wraps Badge instead of inline styles.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…arted CTA
De-pilled CTAs hug the text now (lg 13/26 -> 9/18). The closing 'Get Started'
under the 'Ready to Start Growing?' hero uses a new xl size so it stays a
prominent central focal point; navbar + hero beta stay tight.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…bright accent
The header button hard-coded background:var(--accent); after accent shifted to the
brighter forest, it rendered brighter than every other primary button. Now a proper
<Button variant=primary> (--brand-forest), matching the term pills and app buttons.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Family (b) control: a wrapping row of selectable pills for 'All' + N
dynamic filters, wrapping the existing <Pill> (with per-option color/icon).
Complements <Toggle> (family (a): fixed connected segmented control).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the custom accent-filled pill buttons with the shared <Toggle>
(forest-filled active). Keeps SemesterChips' public API; also fixes the
prior --accent button fill.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Overview/Chat/Study match/Activity tab row now uses the shared <Toggle>.
setTab wiring unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Jose-Gael-Cruz-Lopezand others added 21 commits July 1, 2026 04:38
#147)
Closes an acceptance-#5 gap found in review: the probe-failure branch in
gemini_test (except → {ok:false,error}, still 200) was untested. Adds a case
that raises from health_probe_agent.run and asserts the failure contract.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(agents): migrate remaining one-shot LLM calls to Pydantic AI agents (#147)
…mini fallback (#144)
calendar_service already extracted via syllabus_extraction_agent as the
primary path; the raw-Gemini `parse_syllabus`/`call_gemini_json` fallback was
the only remaining seam. Remove it and degrade gracefully instead:
- Delete parse_syllabus, the call_gemini_json import, and the now-unused
PROMPT_PATH. On agent guardrail-trip or unexpected failure,
extract_assignments_from_file now returns _degraded_result (empty
assignments + a user-facing warning, no second LLM call) — resilience
without a raw Gemini call.
- Response contract preserved: {assignments, warnings, raw_text} on every
path (success / degrade / empty-text).
- notes stay encrypted at the write boundary (insert_new_assignments,
encrypt_if_present) per #126 — added a regression test.
Tests: rewrote the parse_syllabus-based fallback tests in test_ocr_pipeline.py
to assert the graceful degrade; repointed the live fixtures off parse_syllabus
onto the agent; added a notes-encryption test. Full suite green (2 pre-existing
storage-env failures unrelated). ruff clean.
Spec: specs/144-calendar-agent.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…uiz-gen fallback (#145)
Removes the last raw call_gemini* seams from the quiz surface:
- New agents/quiz_context.py (QuizContext: weak_areas, common_mistakes,
questions_seen_summary, recommended_difficulty, notes — mirrors
quiz_context_update.txt). routes/quiz.py's post-submit background task runs it
via run_agent_sync and saves .model_dump() (unchanged context_json shape).
- New agents/course_summary.py (CourseSummary.summary). course_context_service's
_generate_summary_with_gemini runs it via run_agent_sync and keeps the
deterministic template fallback on agent failure (no second LLM call).
- Removed _legacy_generate_quiz + the MODEL_LITE/MODEL_SMART/call_gemini_json
import. On agent guardrail-trip or failure, generate_quiz now returns 502
instead of a raw-Gemini fallback; the pre-agent 404 (unknown node) is
unchanged. _providers registers course_summary (flash) + quiz_context (lite).
Coordination: does NOT touch scoring / apply_graph_update / quiz_attempts writes
(#128/#129 territory) — only the LLM seams.
Tests: rewrote the legacy-fallback quiz tests to degrade-to-502; repointed the
submit context-update patches onto quiz_context_agent; removed the obsolete
legacy prompt-augmentation + legacy-model tests; added quiz-context save,
course-summary success/fallback tests. Full suite 826 passed (2 pre-existing
storage-env failures). ruff clean.
Spec: specs/145-quiz-course-context-agents.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…_context in submit mocks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-files + drop anon policies (#304)
Closes the last anon storage surface from #231. The rest already shipped:
application_resumes is private (backend/service-key via careers.py), issue-report
screenshots upload through the auth-gated POST /api/issue-reports/screenshot
(service role, size+mime validated; feedback.py), and ReportIssueFlow.tsx no
longer uses the anon storage client.
Migration 0029 (verified against prod, read-only, before writing):
- makes issues-media-files private (application_resumes already private → no-op),
- caps it at 5 MB + a mime allowlist matching the upload endpoint,
- drops the two anon storage.objects policies ("Allow uploads" INSERT / "Allow
public read" SELECT) the app no longer relies on.
Safe: uploads use the service role (bypasses storage RLS) and review is via the
dashboard / signed URLs, so nothing reads issues-media-files via a public URL.
Idempotent across environments (WHERE no-ops on absent buckets; DROP ... IF EXISTS).
NOT yet applied to prod — apply via `python -m db.migrate` (see the migration's
privilege note re: the storage schema).
Also updates docs/security/storage-hardening-plan.md to reflect the shipped state
(résumé-PII exposure already closed; only this lockdown remained).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses CodeRabbit review on #286:
- Toggle: flexWrap so SemesterChips' dynamic list wraps instead of
overflowing horizontally (no effect on fixed-option callers).
- Pill: type="button" (stops enclosing-form submit) and aria-pressed
to expose active state to assistive tech.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
refactor(frontend): unify CSS token layer + add (public) route group (Phase 1)
The direct-run harness (python3 tests/test_ocr_pipeline.py, documented in
the module docstring) had lost its sys.exit(1) and traceback on failure,
so a failed run exited 0 and silently reported success. Restore both.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Jul 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 27b74f9f-1588-4381-9a18-67c3da8bae5b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch main

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 2, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingf472300Jul 02 2026, 06:14 AM

feat(calendar): syllabus extraction via agent, retire raw-Gemini fallback (#144)
@railway-app
railway-appBottemporarily deployed to Sapling / staging July 2, 2026 05:45 Inactive
AndresL230and others added 2 commits July 2, 2026 02:08
The all-questions-drift branch's comment still described routing to the
legacy fallback, which #145 deleted. The RuntimeError is now caught by
generate_quiz's bare-except and returned as HTTP 502 (per
test_degrades_when_all_questions_drift). Comment-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145)
@railway-app
railway-appBottemporarily deployed to Sapling / staging July 2, 2026 06:12 Inactive
@AndresL230
AndresL230 merged commit 4cf87f1 into productionJul 2, 2026
9 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@AndresL230@Darkest-Teddy@Jose-Gael-Cruz-Lopez