Skip to content

Harden Hafa Code for the FDMS classroom launch#8

Open
leonshimizu wants to merge 4 commits into
mainfrom
codex/fdms-classroom-hardening
Open

Harden Hafa Code for the FDMS classroom launch#8
leonshimizu wants to merge 4 commits into
mainfrom
codex/fdms-classroom-hardening

Conversation

@leonshimizu

@leonshimizu leonshimizu commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • adds private teacher/student feedback threads with unread and resolution state
  • makes cloud saves durable across switches, reloads, offline recovery, retries, and stale-write conflicts
  • keeps class copies in class and preserves conflicted work as a private Conflict Copy
  • adds bulk invitations, invite-only signup support, durable email jobs, delivery state, and domain controls
  • adds classroom school-year, archive/read-only, export, offboarding, and audit workflows
  • disables classroom external visibility and snapshot sharing by default
  • adds quotas, cleanup jobs, persistent share throttling, accessibility focus handling, and a leaner PWA shell
  • moves CI to the repository root, adds Dependabot, updates dependencies, and adds backend/frontend tests
  • adds the FDMS launch readiness and action plan at docs/FDMS_CLASSROOM_LAUNCH_PLAN.md

Verification

  • Rails: 50 tests, 402 assertions, 0 failures
  • React: 4 files, 10 tests, all passing
  • RuboCop: 73 files, no offenses
  • Brakeman: 0 warnings
  • Bundler Audit: no vulnerabilities
  • npm audit: 0 vulnerabilities
  • frontend lint and production build pass
  • production-mode Rails boot passes with Solid Queue
  • local multi-role API workflow and visible Ruby runner smoke test pass
  • Greptile: completed at 5/5; latest pass reviewed 76 files with 0 new comments

Deployment and school gates

Application code is ready for review. Before student launch, deploy both services and verify Netlify/Render/Clerk production settings, configure Resend and Solid Queue operation, complete FDMS privacy and sharing approval, verify backup restore and monitoring, and run the documented 2–4 student pilot.

Greptile Summary

This PR hardens Hafa Code for a classroom launch across the full stack: teacher/student feedback threads, durable optimistic-lock cloud saves with conflict-copy recovery, bulk invitations with delivery tracking, classroom archive/export/audit workflows, and quota + share-throttle hardening. The changes are well-tested (50 Rails tests, 10 React tests) and internally consistent.

  • Feedback threads: new ProjectComment / ProjectCommentRead models with a project_comments controller; file_path_belongs_to_project is scoped to will_save_change_to_file_path? so resolve operations are never blocked by stale file references.
  • Durable cloud save: per-project pending-sync map in localStorage, exponential retry with three attempts, and a conflict-copy branch that preserves local edits when optimistic locking detects a stale write.
  • Classroom lifecycle: archived_at / school_year on organisations; archive gate applied consistently across projects, checkpoints, invitations, and member management; bulk invite with domain allow-list and 100-address cap.

Confidence Score: 5/5

Safe to merge — no correctness defects in the changed paths; all three inline findings are non-blocking quality notes.

The changes are thoroughly tested and the core flows (conflict resolution, archive gating, feedback auth) are correct. The three findings are style/performance notes: a double membership lookup per comment author largely mitigated by Rails' request-level query cache, a rate-limit off-by-one permitting 61 rather than 60 requests per window, and misleading error messaging when archived-classroom saves retry. None affects data integrity or security.

Files Needing Attention: api/app/controllers/api/v1/project_comments_controller.rb (membership N+1 in comment_author_role) and web/src/App.tsx (cloud-sync retry on archived workspace) are the two spots worth a follow-up before the classroom grows large.

Important Files Changed

Filename Overview
api/app/controllers/api/v1/projects_controller.rb Adds pagination, optimistic-lock conflict handling, archived-classroom guard, audit events, and a duplicate guard — all well-structured and matching the new schema.
api/app/controllers/api/v1/project_comments_controller.rb New feedback CRUD with auth guard, resolve toggle, and mark-read tracking; comment_author_role performs up to two membership DB queries per unique comment author (partially mitigated by query cache).
api/app/controllers/api/v1/organizations_controller.rb Large addition: archive/unarchive, export, audit-events endpoint, bulk invite, domain allow-list, and pagination — logic is sound and the archived guard is consistently applied.
api/app/models/api_rate_limit.rb DB-backed rate limiter with savepoint retry; effective window limit is limit+1 due to an off-by-one in the increment-then-compare logic.
web/src/App.tsx Major cloud-sync overhaul with per-project status tracking, conflict resolution, and retry backoff; pending syncs will retry (and misleadingly fail) when a classroom is archived mid-session.
api/app/models/project_comment.rb New comment model with body length cap, optional file/line anchoring, and file-path validation scoped to changes only — the will_save_change_to_file_path? guard correctly avoids blocking resolve operations.
web/src/lib/cloudSyncStorage.ts Clean localStorage-backed pending-sync map with safe JSON parsing and atomic replace — no issues found.
api/app/jobs/organization_invite_email_job.rb Durable email job with row-level locking for delivery state updates, polynomial retry, and proper discard-on-not-found — looks correct.
api/db/migrate/20260725000002_harden_organization_invitations.rb Deduplicates pending invitations, normalises emails, swaps the old 3-column unique index for a partial unique index on pending rows — migration is safely reversible.
api/app/models/organization_invitation.rb Adds delivery-state columns, pending-email uniqueness constraint, and a renew! method that resets the token — correct and consistent with the migration.

Sequence Diagram

sequenceDiagram
    participant FE as Frontend (React)
    participant SS as cloudSyncStorage (localStorage)
    participant API as Rails API
    participant DB as Database

    Note over FE,DB: Cloud save with optimistic locking
    FE->>SS: markProjectPendingCloudSync(id, updatedAt)
    FE->>API: "PATCH /projects/:id {lock_version}"
    alt lock_version matches DB
        API->>DB: "UPDATE projects SET lock_version = lock_version + 1"
        API-->>FE: "200 {project, lock_version: N+1}"
        FE->>SS: clearProjectPendingCloudSync(id)
    else lock_version mismatch (StaleObjectError)
        API-->>FE: "409 {code: project_conflict, project: serverVersion}"
        FE->>FE: createConflictCopy(localProject)
        FE->>SS: markProjectPendingCloudSync(conflictCopy.id)
        FE->>API: "POST /projects {conflictCopy}"
        API-->>FE: "201 {project: savedConflictCopy}"
    end

    Note over FE,DB: Teacher feedback thread
    FE->>API: GET /projects/:id/comments
    API->>DB: SELECT project_comments + users + resolved_by
    API-->>FE: "{comments, unread_count}"
    FE->>API: POST /projects/:id/comments/mark_read
    API->>DB: "UPSERT project_comment_reads SET read_at = now"

    Note over FE,DB: Classroom archive
    FE->>API: PATCH /organizations/:id/archive
    API->>DB: "UPDATE organizations SET archived_at = now"
    API->>DB: INSERT audit_events
    API-->>FE: "{organization: {archived_at: ...}}"
    FE->>FE: "workspaceArchived = true, canEditProject = false"
Loading

Reviews (4): Last reviewed commit: "Address Greptile classroom edge cases" | Re-trigger Greptile

@netlify

netlify Bot commented Jul 25, 2026

Copy link
Copy Markdown

Deploy Preview for hafa-code ready!

Name Link
🔨 Latest commit 2f90497
🔍 Latest deploy log https://app.netlify.com/projects/hafa-code/deploys/6a64257de9c5fe00087a7392
😎 Deploy Preview https://deploy-preview-8--hafa-code.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0236972c-0c46-4919-9410-8ef6d582a116

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 codex/fdms-classroom-hardening

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.

Comment thread api/app/models/project_comment.rb
Comment thread web/src/App.tsx
Comment thread api/app/controllers/api/v1/project_shares_controller.rb
@leonshimizu
leonshimizu marked this pull request as ready for review July 25, 2026 11:17
Sign up for free to 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.

1 participant