| applyTo | ** |
|---|
This is the platform for a grant/hackathon program for students called Fallout, where they can log and submit hours they spend on projects & their engineering process for prizes and and invite to our summit event.
Keep changes low impact, responses concise. No summaries, no testing. Reference the existing codebase for style consistency. Read all context carefully before making changes — code may be manually modified between messages; do not suggest code that has been deleted or is no longer relevant. If asked to change feature requirements, update all previous implementations to match. Always ask questions when needed.
Detailed architecture docs live in agents-docs/. Before working on an unfamiliar area, scan the index and read the relevant doc — they contain gotchas, patterns, and implementation details that prevent common mistakes. The docs are point-in-time snapshots and may be out of date; treat them as a starting overview and verify against the current code before relying on specifics.
!!! IMPORTANT: When you make changes that affect documented architecture (new controllers, models, policies, services, access control changes, new shared props, etc.), YOU MUST update the corresponding doc in agents-docs/ as part of the change.
Ruby 3.4.4, Rails 8.1.3, React 19, Tailwind 4.1.18 via inertia-rails. Only suggest changes applicable to these versions. Prefer CLI-generated boilerplate over manual file creation — you can always modify generated output.
In-house services: HCA is our unified authentication system. Hackatime is the time tracking system, where Lapse is the timelapse tool for Hackatime. HCB is our "bank" (real US dollars).
Inertia bridges Rails and React and allows for SPA behavior with and Inertia Modals All attributes passed to the frontend — even unused ones — are visible in developer tools; for security & access reasons, be careful what you expose. Inertia docs: https://inertia-rails.dev/llms-full.txt
HCB controls money for the program. DO NOT EDIT ANY CODE RELATED TO HCB WITHOUT EXPLICIT WRITTEN APPROVAL. Alert in chat before making any HCB changes. Do not run any tests or console code related to HCB without EXPLICIT WRITTEN APPROVAL.
Pundit policies enforce authorization at a low level and should always be used. This pertains to security — if unsure how to modify a policy, ask for clarification. Follow the principle of least privilege: only grant access necessary for the feature to function. Docs: https://www.rubydoc.info/gems/pundit
Two user types exist: full users (authenticated through HCA, cross-device access, can access non-public data) and trial users (email-based login, device-cookie-scoped, limited access). For privacy and security, multiple trial accounts with the same email cannot access each other's data. Trial users become full users upon completing HCA authentication. Consider both user types when making changes and enforce access controls via Pundit when making changes.
Staff users have one or more roles stored in a PostgreSQL array column: time_auditor, requirements_checker, pass2_reviewer, admin, or hcb. Each reviewer role grants access only to its specific review queue(s) — use user.can_review?(queue) to check. Admins have access to everything except real money movement, which is reserved for the hcb role: only users with user.hcb? true can issue or top up HCB project funding card grants. Regular admins can read grant orders, edit HcbGrantSetting, adjust admin notes, and move orders to pending/on_hold/rejected — but cannot transition an order to fulfilled (which triggers an HCB topup) or mark a pending topup as completed during reconciliation.
PII (email, full name, etc.) must only be exposed to admins. Non-admin reviewers see display names and avatars but never email addresses or other identifying information. When serializing user data for the admin frontend, always check current_user.admin? before including PII fields. The /admin/users pages are already admin-only via require_admin!.
By default, every action requires full HCA authentication, completed onboarding, and Pundit authorization. Only relax defaults when explicitly necessary, for specific actions only. When in doubt, deny access. Assume developers will forget to configure access on new actions — the system must fail closed.
If a developer forgets to list a new action, the result must always be less access, never more:
- Relaxing directives (
skip_after_action :verify_authorized,skip_after_action :verify_policy_scoped,allow_unauthenticated_access,allow_trial_access,skip_onboarding_redirect,skip_before_action): useonly:— a forgotten action keeps the default restriction active. Note: Pundit'sskip_authorization/skip_policy_scopeare instance methods — they cannot be called at the class level withonly:. Useskip_after_actionon the verification callbacks instead. - Controllers without an
indexaction:ApplicationControllerregistersafter_action :verify_authorized, except: :indexandafter_action :verify_policy_scoped, only: :index. Rails 8.1 raisesAbstractController::ActionNotFoundif the action listed inexcept:/only:doesn't exist on the controller. Controllers withoutindexmust use blanketskip_after_action :verify_authorizedandskip_after_action :verify_policy_scoped(noonly:) to avoid this. Still callauthorize/skip_authorizationin each action explicitly. - Restricting directives (
before_actionenforcing checks likerequire_admin!): useexcept:or apply blanket — a forgotten action still gets the check.
Never use except: on a relaxing directive, and never use only: on a restricting before_action — both silently open access when a new action is added. Every access directive must have an inline comment explaining why it is needed. Prefer allow_trial_access over allow_unauthenticated_access unless the endpoint truly needs to be public.
Data must be preservable and reversible. When deleting data, always ask whether it should be soft-deleted or permanently destroyed — never assume either. PII may need true deletion; other data may need soft-deletion for auditability. The developer decides each time.
For values that change over time — especially currencies like koi — store each change as an individual ledger entry (e.g. "+5 koi from ship review", "-10 koi from shop purchase") rather than mutating a running total. Derive the current balance by replaying history. Think of it like git: store the diffs, not the final state.
Use Rails, Inertia, React, and Pundit best practices. Keep code DRY with partials, helpers, and concerns. Follow this ordering for model internals: constants, enums, includes/concerns, associations, validations, callbacks, scopes, class methods, instance methods (public then private). Minimize database queries (use includes, avoid N+1). Use background jobs for long-running tasks. Use caching where appropriate. When adding the private keyword in Rails, verify nothing below is affected — private methods should always be at the bottom of the class.
Maintain existing functionality; do not introduce bugs. Before finishing, run git diff to review changes, then run bin/rubocop -f github, bin/brakeman --no-pager, and npm run format:check. Flag unrelated issues but you don't have to fix them.
Use only the project's existing color palette (Tailwind theme colors like bg-brown, text-light-brown, border-dark-brown, etc.). Do not use hex colors or arbitrary values (e.g. bg-[#ae9578]) unless explicitly told to. Do not use opacity/translucency (e.g. bg-dark-brown/50, text-brown/30) as a way to create color shades — if a shade is needed that doesn't exist in the palette, ask the developer for the correct color.
The admin dashboard (/admin) uses shadcn/ui components, completely separate from the user-facing Fallout theme. When building admin pages, always use shadcn components from @/components/admin/ui/ (Button, Table, Card, Badge, Input, Select, etc.) instead of writing raw HTML or using the shared Fallout components. The shadcn MCP tools are available for discovering and adding new components. Admin-specific components live in @/components/admin/, and all admin pages use AdminLayout.
Do not add comments unless absolutely necessary for clarity — code should be self-describing. No large comment blocks. Exception: code with non-obvious effects beyond its immediate scope — especially security, access control, or authorization — MUST have an inline comment explaining why it exists. Examples: access directives, policy scoping, before_action filters, session/cookie manipulation, and any logic whose removal would silently change access. If someone reading the code in isolation couldn't tell why a line is there, comment it.
For temporary files, like scripts or one-time output data, use the folder in this directory called tmp/ and not the system's /tmp.