removed stocks

This commit is contained in:
2026-09-07 13:42:31 -04:00
parent 70d2a7df10
commit df7eb65930
19 changed files with 0 additions and 6606 deletions

View File

@@ -1,250 +0,0 @@
● Here's the synopsis.
What it is
Stocks in the Future
(rubyforgood/stocks-in-the-future) — a Rails 8.1 /
Ruby 3.4.4 app for a nonprofit that teaches
middle-schoolers financial literacy. Students earn
real-ledger, fake-money by attending class and
getting good grades, then invest that money in a
simulated stock market tracking real prices. Teachers
enter grades; admins run everything.
Architecture
Standard Rails-with-extras, no API layer —
server-rendered Hotwire (Turbo + Stimulus),
Tailwind/shadcn, Propshaft, importmap. Postgres.
Solid Queue for jobs. Devise for auth, Pundit for
authorization, Discard for soft deletes. Deployment
is Capistrano → AWS Lightsail (Terraform-managed),
with SES for mail; Docker for local dev.
Layering is more disciplined than typical:
app/services (business operations), app/policies,
app/presenters, app/facades, plus a PortfolioPosition
PORO aggregate. Models stay reasonably thin.
Domain shape:
School → SchoolYear → Quarter(1-4) → GradeBook →
GradeEntry
↓
Classroom ← TeacherClassroom → Teacher
↓
ClassroomEnrollment → Student ─ Portfolio ─┬
PortfolioTransaction (ledger)
├
PortfolioStock (lots)
└
PortfolioSnapshot (monthly)
Student → Order → Stock
User is STI (User/Student/Teacher) with an admin
boolean orthogonal to type.
Two design decisions worth knowing:
1. Transactions are an append-only ledger. portfolios
deliberately has no cash column — balance is derived
by summing transactions
(app/models/portfolio.rb:88), and it subtracts
pending buy orders plus a pending fee so students
can't double-spend money already committed.
2. Orders are batch-executed, not immediate. Students
queue buy/sell orders; OrderExecutionJob drains
them, then TransactionFeeProcessor charges a flat
$1/student/day regardless of order count. Prices
update once daily from Alpha Vantage, so the price a
student sees is the price they get.
Earnings: $0.20/day attended, $1 perfect-attendance
bonus, $3 for an A / $2 for a B in reading and math,
$2 per grade improvement over the prior quarter.
Admin "finalizes" a gradebook, which is what actually
mints the deposits (DistributeEarnings).
docs/ is unusually good for a volunteer project —
orders-and-transactions.md and gradebook-earnings.md
explain the why.
State of the code
I ran the suite: 675 tests, 1 failure, 2 skips, 0
errors (~18s).
The one failure is not a product bug — it's a time
bomb. test/controllers/admin/teachers_controller_test
.rb:143 hardcodes school year "2025 - 2026", but
Year.current_school_year returns "2026 - 2027" for
any date after June 2026, so the classroom never
renders. It began failing on 2026-07-01 and will keep
failing until the literal is replaced with a
computed current year.
Things I'd flag
Two authorization gaps — I verified both with
throwaway integration tests, then deleted them.
1. IDOR on order updates
(app/controllers/orders_controller.rb:41). set_order
does an unscoped Order.find(params[:id]), and update
never calls authorize. OrderPolicy#update? exists and
is unit-tested, but nothing invokes it — only cancel
does. Confirmed: an unrelated student changed
another student's pending order from 1 share to 42
and got a 302 success. Ownership can't be reassigned
(order_params excludes user_id) and only pending
orders qualify, but one student can still rewrite
another's trade. One-line fix: authorize @order in
update.
2. Teachers aren't scoped to their own classrooms in
StudentsController and
ClassroomEnrollmentsController. Both use
ensure_teacher_or_admin — any teacher — then
Classroom.find(params[:classroom_id]) with no
membership check. Confirmed: a teacher of classroom A
reset the password of a student in classroom B.
Notably
ClassroomsController#check_classroom_eligibility does
check membership, so this is an inconsistency rather
than a deliberate policy.
Neither is remotely exploitable — both require an
authenticated account — and the blast radius is one
school district's students, but #2 crosses a
classroom boundary that the app elsewhere treats as
real.
Lower-priority:
- Admin::PortfolioTransactionsController has six
commented-out authorize calls with TODO: FIX.
Practically fine (BaseController gates admins), but
the ledger doc says transactions are immutable, and
this controller offers edit/update/destroy on them —
that's a stated-invariant violation more than an
authz one.
- docs/scheduling.md has drifted from
config/recurring.yml. The doc says OrderExecutionJob
runs weekdays at 1 AM and auto-triggers the price
update; actually it runs every 15 minutes and the
price job is independently scheduled. It also
references two docs that don't exist.
- Admin::BaseController#apply_sorting interpolates
params[:sort] into reorder. Rails quotes hash keys as
identifiers so it isn't injectable, but an unknown
column produces a 500 rather than a fallback.
- Mid-migration duality: students link to classrooms
via both legacy users.classroom_id and the newer
ClassroomEnrollment. ClassroomFacade#students unions
both. Worth finishing, since some scopes (e.g.
- docs/scheduling.md has drifted from config/recurring.yml. The doc says OrderExecutionJob runs weekdays at 1
AM and auto-triggers the price update; actually it runs every 15 minutes and the price job is independently
scheduled. It also references two docs that don't exist.
- Admin::BaseController#apply_sorting interpolates params[:sort] into reorder. Rails quotes hash keys as
identifiers so it isn't injectable, but an unknown column produces a 500 rather than a fallback.
- Mid-migration duality: students link to classrooms via both legacy users.classroom_id and the newer
ClassroomEnrollment. ClassroomFacade#students unions both. Worth finishing, since some scopes (e.g.
Classroom.order_by_student_count) still read only the legacy column.
- Order#destroy is defined below private, so DELETE /orders/:id routes to a nonexistent action — dead code
plus a phantom route.
$2 per grade improvement over the prior quarter.
Admin "finalizes" a gradebook, which is what actually
mints the deposits (DistributeEarnings).
docs/ is unusually good for a volunteer project —
orders-and-transactions.md and gradebook-earnings.md
explain the why.
State of the code
I ran the suite: 675 tests, 1 failure, 2 skips, 0
errors (~18s).
The one failure is not a product bug — it's a time
bomb. test/controllers/admin/teachers_controller_test
.rb:143 hardcodes school year "2025 - 2026", but
Year.current_school_year returns "2026 - 2027" for
any date after June 2026, so the classroom never
renders. It began failing on 2026-07-01 and will keep
failing until the literal is replaced with a
computed current year.
Things I'd flag
Two authorization gaps — I verified both with
throwaway integration tests, then deleted them.
1. IDOR on order updates
(app/controllers/orders_controller.rb:41). set_order
does an unscoped Order.find(params[:id]), and update
never calls authorize. OrderPolicy#update? exists and
is unit-tested, but nothing invokes it — only cancel
does. Confirmed: an unrelated student changed
another student's pending order from 1 share to 42
and got a 302 success. Ownership can't be reassigned
(order_params excludes user_id) and only pending
orders qualify, but one student can still rewrite
another's trade. One-line fix: authorize @order in
update.
2. Teachers aren't scoped to their own classrooms in
StudentsController and
ClassroomEnrollmentsController. Both use
ensure_teacher_or_admin — any teacher — then
Classroom.find(params[:classroom_id]) with no
membership check. Confirmed: a teacher of classroom A
reset the password of a student in classroom B.
Notably
ClassroomsController#check_classroom_eligibility does
check membership, so this is an inconsistency rather
than a deliberate policy.
Neither is remotely exploitable — both require an
authenticated account — and the blast radius is one
school district's students, but #2 crosses a
classroom boundary that the app elsewhere treats as
real.
Lower-priority:
- Admin::PortfolioTransactionsController has six
commented-out authorize calls with TODO: FIX.
Practically fine (BaseController gates admins), but
the ledger doc says transactions are immutable, and
this controller offers edit/update/destroy on them —
that's a stated-invariant violation more than an
authz one.
- docs/scheduling.md has drifted from
config/recurring.yml. The doc says OrderExecutionJob
runs weekdays at 1 AM and auto-triggers the price
update; actually it runs every 15 minutes and the
price job is independently scheduled. It also
references two docs that don't exist.
- Admin::BaseController#apply_sorting interpolates
params[:sort] into reorder. Rails quotes hash keys as
identifiers so it isn't injectable, but an unknown
column produces a 500 rather than a fallback.
- Mid-migration duality: students link to classrooms
via both legacy users.classroom_id and the newer
ClassroomEnrollment. ClassroomFacade#students unions
both. Worth finishing, since some scopes (e.g.
Classroom.order_by_student_count) still read only the
legacy column.
- Order#destroy is defined below private, so DELETE
/orders/:id routes to a nonexistent action — dead
code plus a phantom route.
- MemorablePasswordGenerator builds passwords from
Faker::Superhero + rand(1..99) — the file's own TODO
acknowledges this. Fine for classroom accounts, low
entropy in principle.
Working tree: a 15 MB GITFOLDER.zip containing a full
.git directory is sitting untracked in the repo
root, and two .DS_Store files show as deleted.
.gitignore covers .DS_Store but not the zip. Probably
a stray artifact from someone's backup — worth
removing before it gets committed.

View File

@@ -1,56 +0,0 @@
# Branch Survey
**Repo:** `rubyforgood/stocks-in-the-future`
**`main` at:** `63732df` (2026-06-30)
**Surveyed:** 2026-08-14
**Scope:** all 11 local branches other than `main`.
---
## Priority legend
| P | Meaning |
|---|---------|
| 1 | Merge-ready and valuable now. Zero commits behind `main`, so it fast-forwards. `main` is currently *missing* this work. |
| 2 | High value, current, but a large review. Will rot quickly if `main` moves. |
| 3 | Small, self-contained, cheap to land. |
| 4 | Real unmerged work, but far behind `main` — needs a rebase or a product decision before it is worth anything. |
| 5 | Superseded, stale, or already merged. Housekeeping: delete or consciously abandon. |
---
## Branches
| Priority | Name | Description |
|---|---|---|
| 1 | `dependabot/bundler/solid_queue-1.6.0` | Despite the name, not a single dependency bump — this is the de-facto integration branch that `main` has fallen behind. 13 unique commits spanning Jun–Aug 2026: solid_queue 1.4→1.6, **Rails 8.1.3→8.1.3.1** (patch release), csv, simplecov 0.22→1.0.3, rubocop/rubocop-rails, selenium-webdriver, and image_processing 1.14→2.0.2 — the last accompanied by libvips provisioning for staging/production (`config/deploy.rb`, CI workflows, `Dockerfile.dev`) and a new Active Storage image-processing test. Also carries the "show reset password notice once" fix (#1150) and a rotted-test repair. 19 ahead / **0 behind**, so it fast-forwards cleanly. |
| 1 | `pr-1150` | The original home of the "show reset password notice once" fix (removes duplicate flash markup from `classrooms/show`). Now roughly 95% duplicated by the solid_queue branch above, but holds **one commit that branch lacks**: a test-setup fix creating the `teacher_classrooms` join row so the teacher actually passes `ClassroomsController#check_classroom_eligibility` (the factory only set the `belongs_to`, leaving the join table empty, so the teacher was redirected to root and the notice never rendered). Reconcile the two branches rather than merging both. 19 ahead / 0 behind. |
| 2 | `stocksdesign` | A full UI and design-system overhaul, and by far the largest branch: 95 commits, 251 files, +14,028/−3,536. Adds `design.md` (the design system, adapted from the Ruby for Good **CASA** project and re-reconciled for this app), `design-instructions.md` (process), and `design-todo.md` (an automated audit of 117 templates flagging WCAG and design-token violations — hex colours, off-tier breakpoints, faint text, missing `alt`, removed focus outlines, `div`-as-button, `th` without `scope`). Self-hosts the Figtree variable font, unifies buttons/cards/tables/badges onto shared primitives, flattens navigation and adds a mobile drawer, converts copy to sentence case, adds `AdminDashboard`, `EarningsCalculator` and `PopulateGradeBook`, and brings a substantial new system/integration test suite. Most recently active branch (2026-08-04) and **0 behind** `main`. |
| 3 | `increase_rate_limit` | **Name does not match content.** Adds three lines to `config/application.rb` setting `config.solid_queue.recurring_tasks_file` to `config/recurring.yml`, so the recurring-job schedule is loaded explicitly rather than relying on Solid Queue's default lookup. Nothing in the diff concerns rate limiting; the name most likely refers to the job cadence in `recurring.yml` that this change activates. 2 ahead / 268 behind, but the diff is 3 lines and trivially re-appliable. |
| 3 | `script_updates` | Hardens `script/migrate_returning_students.rb`, the one-off production script that imports returning students' prior balances, stock holdings and quarterly grades from a spreadsheet. Replaces hardcoded 0-indexed CSV column positions (`COL = { username: 0, earnings: 6, ... }`) with **named case-sensitive headers**, and replaces the hardcoded `TARGET_CLASSROOM_ID = 1` with a required `--classroom=ID` flag. Net −22 lines but a near-total rewrite of the parsing layer. Only 38 commits behind. |
| 3 | `ah/argument-alignment` | Pure lint. Enables the `Layout/FirstMethodArgumentLineBreak` RuboCop cop in `.rubocop.yml` and reformats the 11 files that then violate it (5 app, 7 test). 558 behind, so the reformatting would conflict, but the branch is regenerable in a minute by enabling the cop and running `rubocop -a`. Value is the decision, not the diff. |
| 4 | `student-grade-import-to-transaction` | First pass at `BulkGradeImportService` — 271 lines of service plus 466 lines of tests, and nothing else. Imports student grades from CSV (school/classroom/quarter/math/reading/absences) so that grade data can flow into gradebook entries and, on finalisation, into earnings transactions. Complements the existing `BulkStudentImportService`, which only creates student accounts. Genuinely useful and absent from `main`, but 693 commits behind and never wired into a controller, route or admin UI. |
| 4 | `feature/kamal-deployment` | Migrates deployment to **Kamal 2**: base/staging/production `config/deploy*.yml`, `.kamal/secrets*` files, secrets documentation, and GitHub Actions deploy workflows for both environments. Additive only (224 insertions, 0 deletions). Appears to be an abandoned alternative direction — `main` stayed on Capistrano + AWS Lightsail and has kept investing there (the P1 branch above adds libvips provisioning to `config/deploy.rb`). Needs a product/infra decision before any rebase is worthwhile. 525 behind. |
| 5 | `feature/multiple-classroom-memberships` | **Superseded.** Lets a student belong to several classrooms simultaneously via a new `Enrollment` join model, three migrations, and updates to `Classroom`/`Student`/`ImportStudentService`. `main` has since shipped the same concept under a different name and a richer design — `ClassroomEnrollment`, with `enrolled_at`/`unenrolled_at`, a `primary` flag, `current`/`historical` scopes, and a dedicated controller. 48 ahead / 338 behind, and the abandoned dual-write approach ("maintain dual relationship") survives in `main` as the legacy `users.classroom_id` / `ClassroomEnrollment` duality. Keep only as historical context. |
| 5 | `resolve-testing-issues-admin-v2` | **Stale — targets code that no longer exists.** Un-skips and repairs tests across the `admin_v2` namespace (base, grades, portfolio_transactions, school_years, students, teachers controllers) plus `SchoolYear` and a form builder. `main` renamed that whole namespace from `admin_v2` (`/admin-new`) to `admin` (`/admin`), so **zero** `admin_v2` paths remain — every one of the 12 files touched is gone. The underlying intent (no skipped admin tests) may still be worth redoing against the current namespace; the diff itself is unusable. 245 behind. |
| 5 | `feature/make-check-box-lable-clickable` | **Already merged — delete.** Made the label clickable on the shared checkbox component (`a81e914`), plus a README touch-up. The branch tip is a direct ancestor of `main`: 0 commits ahead, 1,430 behind. Nothing to recover; it is pure branch-list clutter. (Note the typo in the branch name, `lable`.) |
---
## Cross-cutting observations
**`main` is behind its own development.** `main`'s tip is 2026-06-30, but two branches (`dependabot/bundler/solid_queue-1.6.0`, `pr-1150`) and `stocksdesign` are all **0 commits behind** with work running through early August. The most consequential item sitting unmerged is the **Rails 8.1.3 → 8.1.3.1** patch bump. Landing the P1 branches is the cheapest high-value action available.
**Two branches duplicate each other.** `dependabot/bundler/solid_queue-1.6.0` and `pr-1150` share six commits verbatim and a seventh in squashed form. Merging both would be redundant; the solid_queue branch is very nearly a superset, so the practical move is to merge it and cherry-pick `2783b49` from `pr-1150`.
**Two branch names actively mislead.** `increase_rate_limit` contains no rate-limiting code, and `dependabot/bundler/solid_queue-1.6.0` is not a lone Dependabot bump but the project's real integration branch. Anyone triaging by name alone will mis-rank both.
**Three branches are dead weight** (`feature/make-check-box-lable-clickable`, `resolve-testing-issues-admin-v2`, `feature/multiple-classroom-memberships`) — one already merged, two overtaken by renames or reimplementation in `main`.
**Related to the earlier code review:** `pr-1150`'s unique commit documents that `Classroom#teachers` (the `teacher_classrooms` join) is the real gate for classroom access, while `users.classroom_id` is not. That is the same legacy/enrollment duality flagged in the synopsis, and the same join that `StudentsController` fails to check — the branch confirms the distinction matters in practice.
---
## Method
For each branch: `git rev-list --count` in both directions against `main`; `git log --no-merges main..<branch>` for unique commits; `git diff --stat <merge-base> <branch>` for scope; then targeted reads of the actual diffs, and `git ls-tree`/`git merge-base --is-ancestor` checks against `main` to establish which branches had been superseded or already merged. No branch was checked out and no branch was modified.

View File

@@ -1,69 +0,0 @@
# Task Creation Overview
## Purpose
Create a task package capturing **meaningful failures** of Claude Code in a real repository. A failure must be:
- Recognizable (~80% of senior engineers would flag it)
- Real‑world impactful (security, data, permissions, etc.)
- Free of artificial or contrived setup
## End‑to‑End Workflow
1. **Explore & Identify Failure**
- Use the *Explore* container to locate a genuine defect in a repository.
- Verify the defect’s significance against the “Meaningful Failure” criteria.
2. **Build the Task**
- **instruction.md** – Write a realistic, self‑contained prompt:
* Base it on actual repository state (including any workspace.patch changes).
* Avoid hints, AI commentary, or external dependencies.
* Do not manufacture breakage; use pre‑existing flaws.
- **grader‑guidance‑consolidated.md** – Tailor the grader’s evaluation:
* Provide concise task context & optional business context.
* Supply privileged ground‑truth facts (file:line references, correct fix).
* For each of the 8 grading criteria, describe strong vs. weak responses specific to the task.
* Add heavy penalties only for deal‑breakers, targeting a named criterion and stated behavior.
3. **Generate Reference Runs**
- Run trials with 'harbor-run' (or copy from existing job) to produce recorded attempts.
- Ensure **≥ 4 accepted reference runs** are stored in 'reference-runs/'.
- All runs must use the **same trial agent** (Claude Code or Codex) – do not mix agents.
4. **Run Detectors**
- Execute each detector skill ('/detector‑…') to catch:
* Off‑hinting, broken dev‑env, cross‑task references, fact‑check failures, etc.
- Fix any issues and re‑run detectors until all reports pass.
5. **Validate, Export & Submit**
- Run 'npx tsx scripts/submit-task.ts <slug>' to validate and package the task.
- Export platform state before submitting.
- Upload the generated tarball and fill the feedback‑request field (4‑item format).
- Include the Slack thread URL for reviewer access.
## Key Constraints & Policies
- **Confidentiality**: All task artifacts must stay on your local machine; never post or share publicly.
- **Agent Consistency**: Pick Claude Code **or** Codex and stick with it for the entire task.
- **Scoring Model**:
- Grader uses the **Consolidated Grading Standard** (8 criteria: Integrity, Narrow Correctness, Broader Correctness, Persistence, Communication, Verification & Thoroughness, Common Sense, Thought Partnership).
- Scores are averaged (0‑1) and may be reduced by **qualitative heavy penalties** applied to a named criterion.
- No numeric caps or combined‑criterion weighting; penalties preserve relative ranking.
- **Workspace & Patch**:
- Changes are captured in 'environment/workspace.patch'; avoid editing ignored files, Dockerfiles, or external resources.
- Verify that the patch applies cleanly on rebuilds ('check-workspace-sync.sh' helps).
- **Failure Significance**:
- Must meet the “Meaningful Failure” definition (real impact, engineer consensus, blockable PR, etc.).
- If reference runs all score high, revisit prompt difficulty or ground‑truth discrimination.
## Quick Reference Commands
| Action | Command |
|--------|---------|
| Start exploration container | 'claude' |
| Snapshot current workspace | '/create-snapshot:snapshot' (Claude) |
| Build workspace script | 'bash scripts/build-workspace.sh <slug>' |
| Verify patch sync | 'bash scripts/check-workspace-sync.sh' |
| Run a trial | 'harbor-run' |
| Copy reference runs | 'npx tsx scripts/copy-reference-run.ts harbor-jobs/<job>/<slug>__*' |
| Rerun a stale reference run | '/regrade-reference-run' |
| Submit task | 'npx tsx scripts/submit-task.ts <slug>' |

View File

@@ -1,46 +0,0 @@
# Stocks for Good developer documentation
Welcome to the internal documentation for _Stocks for Good_.
This section of the repository is dedicated to **technical documentation**
intended for developers, contributors, and future maintainers. It covers system
architecture, development practices, and design decisions that influence how the
app is built and evolves.
---
## 📚 Index
- [Architecture Overview](architecture/index.md)
- [Database seeds](seeds.md)
- [Background job scheduling](scheduling.md)
- [Schema](schema.md)
- [Orders And Transactions](orders-and-transactions.md)
- [GradeBook Earnings](gradebook-earnings.md)
- [Old site](old-site/README.md)
---
## Purpose
While the main `README.md` in the root of the repository is designed to help users
and new developers get started quickly, this `/docs` directory provides deeper
insight into:
- **How** the system works under the hood
- **Why** certain frameworks, gems, and patterns were chosen
- **What** trade-offs were considered during development
This documentation serves as both a knowledge base and a decision log.
---
## Writing Guidelines
- Write in plain Markdown (`.md`) so it remains portable and readable on GitHub
and in any editor.
- Prefer **clarity over completeness** — focus on what someone else (or future-you)
would need to understand.
- Include dates and authors on major decisions when helpful.
---

View File

@@ -1,32 +0,0 @@
# GradeBook Earnings
Students earn money in their portfolios through attendance and grades in their classes. This document outlines how these grade book earnings are calculated and recorded in the system.
## Grade Book Structure
- Each classroom has a grade book for each quarter of the school year.
- At the end of each quarter, teachers and/or admins enter attendance and grades for each student in the classroom.
- *Note*: This grade book is only used to calculate earnings. This system is not responsible for individual grade management or report cards.
- The following [entries](../app/models/grade_entry.rb) are supported for each student in the class for the grade book:
- Number of days attended
- Perfect Attendance (boolean)
- Reading Grade
- Math Grade
## Earnings Calculation
- Once grades have been entered, an admin can "finalize" the grade book for the quarter. This action will calculate earnings for each student based on their attendance and grades and create the corresponding transactions in their portfolios.
- The earnings are calculated as follows:
- Attendance:
- $0.20 per day attended
- $1.00 bonus for perfect attendance
- Grades:
- Reading Grade in the A range: $3.00
- Reading Grade in the B range: $2.00
- Math Grade in the A range: $3.00
- Math Grade in the B range: $2.00
- Grade Improvements
- If a student's grade improves from the previous quarter, they receive an additional bonus:
- Improvement in Reading Grade: $2.00
- Improvement in Math Grade: $2.00
- *Note* We currently are not looking at improvements across school years. So there are no improvement bonuses for the first quarter of a school year.
See the [DistributeEarnings](../app/services/distribute_earnings.rb) class for the implementation of this logic.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -1,23 +0,0 @@
#### Old Site Documentation
This folder contains documentation from the previous iteration of the site
### IMPORTANT DIFFERENCES
Our current goals for the new application include
* Reworking the attendence workflow to be much, much simpler
* Skipping the extra step of "verifying" the information after it is entered
Things we are not working on:
* Not implementing quizzes
* Not working on banner messages / message of the day messages
* Not working on links or other external information
If you find yourself starting to work on any of the above, please stop.
We're also not currently working on dividends at the moment. Maybe someday we'll be ready to add this to the system, though.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 251 KiB

View File

@@ -1,40 +0,0 @@
# Orders and Transactions
This document outlines how orders and transactions function in terms of this application.
Note that due to the fundamental way in which this system works, these concepts are different than you might typically expect from a stock trading platform.
## [Orders](../app/models/order.rb)
- Students can place buy or sell orders for stocks. These orders are not executed immediately but are pending until they are processed by the [OrderExecutionJob](../app/jobs/order_execution_job.rb) at the end of each day.
- Currently, this job runs at midnight Eastern Time.
- Students can cancel or update their pending orders at any time before they have been executed.
- At this time, orders can only be placed for whole shares of stock (no fractional shares).
- When placing an order, the price of the stock is a known value. This is because we only update the stock prices once per day, and we execute all pending orders before updating the prices for the next day.
### Transaction Fees
- There is a flat transaction fee of $1.00 per student per day for executing orders, regardless of the number of orders placed.
- Here are some examples:
- A student places a buy order for 2 shares of stock A at $10 each. They will be charged $20 for the shares plus a $1 transaction fee, totaling $21.
- A student places buy orders for 1 share of stock A at $10 and 1 share of stock B at $15 on the same day. They will be charged $10 + $15 + $1 transaction fee, totaling $26.
- A student places a sell order for 3 shares of stock A at $10 each. They will receive $30 from the sale minus the $1 transaction fee, totaling $29.
### Validations
#### Buy Orders
- When placing a buy order, the system checks if the student has sufficient cash in their portfolio to cover the cost of the shares plus the $1 transaction fee.
- Note that the system must correctly consider that multiple buy orders placed on the same day will incur only a single $1 transaction fee.
- The system also considers the total cost of all buy orders placed on that day when validating if the student has enough cash.
#### Sell Orders
- When placing a sell order, the system checks if the student has enough shares of the stock they wish to sell in their portfolio.
## [Transactions](../app/models/portfolio_transaction.rb)
- Transactions are records of cash movements in a student's portfolio.
- Transactions can be of the following types:
- **Deposit** - represents cash added to the portfolio through earnings from gradebook or manual deposits by teachers/admins
- **Withdrawal** - represents cash removed from the portfolio by admins
- **Credit** - represents cash received from selling stocks
- **Debit** - represents cash spent on buying stocks
- **Fee** - represents the $1 transaction fee charged for executing orders
- Transactions can be associated with an order if they are the result of executing a buy or sell order. However not every transaction is linked to an order (e.g., deposits, withdrawals, and fees).
- Transactions are immutable records and should not be edited or deleted after they are created
- Note that the transactions table is designed to be a ledger, thus if you want to calculate the current cash balance of a portfolio, you should sum up all the transactions. This is why the portfolios table does not have a cash balance column.

View File

@@ -1,299 +0,0 @@
# Responsive Design Guidelines
**Target Users:** Students on Chromebooks
**Last Updated:** 2025-10-19
---
## Table of Contents
- [Overview](#overview)
- [Breakpoints](#breakpoints)
- [Touch Targets](#touch-targets)
- [Typography](#typography)
- [Spacing & Layout](#spacing--layout)
- [Tables](#tables)
- [Forms](#forms)
- [Images & Media](#images--media)
- [Navigation](#navigation)
- [Testing Requirements](#testing-requirements)
- [Common Patterns](#common-patterns)
- [Quick Reference](#quick-reference)
- [Checklist](#checklist)
- [Additional Resources](#additional-resources)
---
## Overview
> ✅ Use only `base` and `lg:` responsive tiers.
> Test all UI components at **375px** and **1366px** before submitting PRs.
These guidelines ensure a consistent, accessible responsive design across the **Stocks in the Future** application.
All contributors working on responsive features must follow these standards.
**Core Principles**
- 🎯 **Mobile-first:** Design for the smallest screen and scale up.
- 💻 **Chromebook-focused:** 1366×768 is our main target.
- ✋ **Touch-friendly:** Minimum 44px touch targets.
- 🎨 **Tailwind-only:** No custom CSS.
- ♿ **Accessible:** WCAG AA minimum compliance.
---
## Breakpoints
We only support **two responsive tiers**:
| Mode | Screen Size | Tailwind Prefix | Example Devices | Priority |
|------|--------------|----------------|------------------|-----------|
| **Base (mobile)** | up to 1023px | *(no prefix)* | Phones, tablets | Medium |
| **Chromebook/Desktop** | 1024px+ | `lg:` | Chromebooks, desktops | **CRITICAL** |
### Why Only Two?
- 1366×768 is the **most common Chromebook resolution**.
- Simplifies layout logic and testing.
- Matches real classroom usage.
- Keeps Tailwind classes minimal and maintainable.
### Example
```html
<!-- Mobile-first base styles -->
<div class="px-4">Mobile padding</div>
<!-- Add styles for larger screens -->
<div class="px-4 lg:px-8">Responsive padding</div>
<!-- Show/hide elements -->
<div class="lg:hidden">Mobile only</div>
<div class="hidden lg:block">Desktop only</div>
```
---
## Touch Targets
### Minimum Sizes
| Element | Minimum | Preferred | Notes |
|----------|----------|------------|--------|
| Buttons | 44×44px | 48×48px | Use `min-h-[44px]` or larger |
| Inputs | 44px height | 48px height | Use `py-3` minimum |
| Checkboxes | 24×24px | 32×32px | Make label clickable |
| Icon buttons | 44×44px | 48×48px | Hamburger, close, etc. |
### Spacing
```html
<!-- Minimum 8px spacing between targets -->
<div class="flex gap-2">
<button class="min-h-[44px] px-4">Action</button>
<button class="min-h-[44px] px-4">Action</button>
</div>
<!-- Preferred 16px spacing -->
<div class="flex gap-4">
<button class="min-h-[48px] px-6">Primary</button>
<button class="min-h-[48px] px-6">Secondary</button>
</div>
```
---
## Typography
| Element | Mobile (`base`) | Chromebook (`lg:`) |
|----------|------------------|--------------------|
| H1 | `text-2xl` (24px) | `lg:text-4xl` (36px) |
| H2 | `text-xl` (20px) | `lg:text-3xl` (30px) |
| Body | `text-base` (16px) | `lg:text-lg` (18px) |
| Small | `text-sm` (14px) | `lg:text-base` (16px) |
**Rules**
1. Never go below 14px (`text-sm`) for body text.
2. Use responsive Tailwind typography classes (`text-xl lg:text-3xl`).
3. Maintain heading hierarchy.
**Example**
```html
<h1 class="text-2xl lg:text-4xl font-bold">Welcome to Your Financial Journey</h1>
<p class="text-base lg:text-lg">This is your launchpad to earn, invest, and grow.</p>
```
---
## Spacing & Layout
### Container Padding
```html
<main class="px-4 lg:px-8">
<!-- Content -->
</main>
<div class="p-4 lg:p-8">
<!-- Card content -->
</div>
```
### Grids and Flex Layouts
```html
<!-- Stack on mobile, row on desktop -->
<div class="flex flex-col lg:flex-row gap-4">
<div class="flex-1">Left</div>
<div class="flex-1">Right</div>
</div>
<!-- Stack to grid -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4">
<div>Card 1</div>
<div>Card 2</div>
<div>Card 3</div>
</div>
```
---
## Tables
### Responsive Table Pattern
```html
<div class="overflow-x-auto">
<table class="w-full border-collapse">
<thead>
<tr class="border-b border-black">
<th class="px-4 lg:px-7 py-3 text-left text-sm lg:text-base">Stock</th>
<th class="hidden lg:table-cell px-7 py-3 text-right">Price</th>
<th class="px-4 lg:px-7 py-3 text-right text-sm lg:text-base">Actions</th>
</tr>
</thead>
<tbody>
<tr class="border-b">
<td class="px-4 lg:px-7 py-2 text-sm lg:text-base">AAPL</td>
<td class="hidden lg:table-cell px-7 py-2 text-right">$150.00</td>
<td class="px-4 lg:px-7 py-2 text-right">
<button class="min-h-[44px] px-4">Buy</button>
</td>
</tr>
</tbody>
</table>
</div>
```
---
## Forms
```html
<input
type="text"
class="w-full lg:max-w-md px-4 py-3 text-base border rounded-lg"
placeholder="Enter amount"
/>
<button
type="submit"
class="w-full lg:w-auto px-6 py-3 min-h-[48px] bg-blue-600 text-white rounded-lg"
>
Submit
</button>
```
---
## Images & Media
```html
<img src="piggy-bank.png" class="w-24 lg:w-32 h-auto" alt="Piggy bank">
<div class="overflow-hidden rounded-lg">
<img src="chart.png" class="w-full h-auto" alt="Stock chart">
</div>
```
---
## Navigation
```html
<input type="checkbox" id="menu-toggle" class="hidden peer" />
<label for="menu-toggle" class="lg:hidden flex items-center justify-center w-10 h-10">
<svg class="w-6 h-6">...</svg>
</label>
<nav class="fixed top-0 bottom-0 left-0 w-64 transform -translate-x-full peer-checked:translate-x-0 lg:translate-x-0 transition-transform">
<!-- Nav links -->
</nav>
```
---
## Testing Requirements
### Test at Two Sizes
- ✅ 375px — Mobile (base)
- ✅ 1366px — Chromebook (lg)
### Checklist
- [ ] No horizontal scroll
- [ ] Minimum text size 14px
- [ ] All touch targets ≥44px
- [ ] Forms and buttons are touch-friendly
- [ ] Navigation accessible
- [ ] Layout consistent on Chromebook
---
## Common Patterns
### Card
```html
<div class="bg-white border-2 border-black rounded-[20px] p-4 lg:p-8">
<h2 class="text-xl lg:text-3xl font-bold mb-4">Card Title</h2>
<p class="text-base lg:text-lg">Card content</p>
</div>
```
### Button Group
```html
<div class="flex flex-col lg:flex-row gap-3">
<button class="w-full lg:w-auto px-6 py-3 min-h-[48px] bg-blue-600 text-white rounded-lg">Primary</button>
<button class="w-full lg:w-auto px-6 py-3 min-h-[48px] border-2 border-black rounded-lg">Secondary</button>
</div>
```
---
## Quick Reference
```
(no prefix) = up to 1023px (mobile-first)
lg: = 1024px+ (Chromebook/Desktop)
```
---
## Checklist
- [ ] Uses Tailwind-only classes
- [ ] Works at 375px and 1366px
- [ ] No horizontal scroll
- [ ] All touch targets ≥44px
- [ ] Accessible labels and contrast
---
## Additional Resources
- [Tailwind CSS Responsive Design](https://tailwindcss.com/docs/responsive-design)
- [WCAG Touch Target Guidelines](https://www.w3.org/WAI/WCAG21/Understanding/target-size.html)
- [Mobile-First Design Principles](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Responsive/Mobile_first)
---

View File

@@ -1,275 +0,0 @@
# Job Scheduling with Solid Queue
This document explains how stock-related jobs are scheduled to run automatically using Rails 8's **Solid Queue** system.
## Overview
The application uses **Solid Queue** for both job processing and recurring job scheduling. This provides a unified, database-backed system that works identically across all environments (development, staging, production).
### Scheduled Jobs
1. **OrderExecutionJob** - Executes pending stock orders (both buy and sell) at 1:00 AM ET weekdays only (Monday-Friday)
2. **StockPricesUpdateJob** - Updates stock prices from Alpha Vantage API (automatically triggered after OrderExecutionJob)
3. **MonthlyPortfolioSnapshotJob** - Creates portfolio snapshots on the last day of each month at 11:00 PM ET
## Configuration
### Recurring Jobs Configuration
The scheduling configuration is defined in [`config/recurring.yml`](../config/recurring.yml):
```yaml
development: &default
daily_order_execution:
class: OrderExecutionJob
queue: default
schedule: "0 6 * * 1-5" # Monday-Friday at 6 AM UTC (1 AM EST)
monthly_portfolio_snapshot:
class: MonthlyPortfolioSnapshotJob
queue: default
schedule: "0 23 L * *" # Last day of month at 11 PM UTC
production:
<<: *default
```
### Worker Configuration
Worker settings are configured in [`config/queue.yml`](../config/queue.yml):
```yaml
default: &default
dispatchers:
- polling_interval: 1
batch_size: 500
workers:
- queues: "*"
threads: 3
processes: <%= ENV.fetch("JOB_CONCURRENCY", 1) %>
polling_interval: 0.1
```
## Job Execution Flow
### Daily Stock Processing (1:00 AM ET, Weekdays Only)
1. **OrderExecutionJob** runs first (scheduled via recurring.yml)
- Processes all pending buy/sell orders
- Applies transaction fees
- **Automatically triggers** StockPricesUpdateJob upon completion
- **Only runs Monday through Friday** (weekdays)
2. **StockPricesUpdateJob** runs second (triggered by OrderExecutionJob)
- Updates current stock prices from Alpha Vantage API
- Saves yesterday's prices for historical tracking
- **Only runs on weekdays** (when OrderExecutionJob runs)
### Monthly Portfolio Snapshots (Last Day, 11:00 PM ET)
3. **MonthlyPortfolioSnapshotJob** runs monthly
- Creates portfolio snapshots for all students
- Used for performance tracking and reporting
## Deployment Instructions
### Development Environment
Start the Solid Queue worker to process jobs:
```bash
# Start worker in development
bin/jobs
# Or run in background
bin/jobs &
```
Test job scheduling:
```bash
# Test manual job execution
rails console
OrderExecutionJob.perform_later
SolidQueue::Job.count # Should show queued jobs
```
### Staging Deployment
#### Heroku
```bash
# Deploy code changes
git push heroku main
# Scale up job worker (uses Procfile configuration)
heroku ps:scale job=1 -a your-app-name
# Verify worker is running
heroku ps -a your-app-name
```
#### AWS/Traditional Servers
Create a systemd service for the worker:
```ini
# /etc/systemd/system/stocks-solid-queue.service
[Unit]
Description=Stocks in the Future - Solid Queue Worker
After=network.target
[Service]
Type=simple
User=deploy
WorkingDirectory=/path/to/your/app
ExecStart=/path/to/your/app/bin/jobs
Restart=always
RestartSec=5
Environment=RAILS_ENV=production
Environment=JOB_CONCURRENCY=3
[Install]
WantedBy=multi-user.target
```
```bash
# Enable and start service
sudo systemctl enable stocks-solid-queue
sudo systemctl start stocks-solid-queue
sudo systemctl status stocks-solid-queue
```
## Job Dependencies
### Prerequisites
1. **Database Access**: All job data is stored in PostgreSQL
2. **Active Worker Process**: Must have `bin/jobs` running
3. **API Access**: StockPricesUpdateJob requires Alpha Vantage API access
4. **Environment Variables**: `ALPHA_VANTAGE_API_KEY` must be set
### Database Tables
Solid Queue uses these database tables:
- `solid_queue_jobs` - Main job queue
- `solid_queue_ready_executions` - Jobs ready to run
- `solid_queue_recurring_executions` - Recurring job schedules
- `solid_queue_failed_executions` - Failed jobs for debugging
## Monitoring
### Job Status Monitoring
```bash
# Check job counts
rails console
SolidQueue::Job.count # Total jobs
SolidQueue::Job.where(finished_at: nil).count # Pending jobs
SolidQueue::FailedExecution.count # Failed jobs
# View recent jobs
SolidQueue::Job.last(10).each do |job|
puts "#{job.class_name} - #{job.finished_at ? 'Complete' : 'Pending'}"
end
```
### Heroku Monitoring
```bash
# View job logs
heroku logs --tail -a your-app-name | grep -E "(OrderExecutionJob|StockPricesUpdateJob)"
# Check worker status
heroku ps -a your-app-name
# Check job queue
heroku run rails runner "puts SolidQueue::Job.count" -a your-app-name
```
### Log Files
Monitor execution through:
- Rails logs: `log/production.log` (contains job execution details)
- Worker logs: Output from `bin/jobs` process
- Heroku logs: `heroku logs --tail -a your-app-name`
## Manual Execution
For testing or emergency runs:
```bash
# Rails console
rails console
OrderExecutionJob.perform_later # Queue job
OrderExecutionJob.perform_now # Run immediately
StockPricesUpdateJob.perform_later # Queue job
MonthlyPortfolioSnapshotJob.perform_later # Queue monthly job
# Command line
rails runner "OrderExecutionJob.perform_later"
```
## Troubleshooting
### Common Issues
1. **Jobs not running**:
- Check worker process is running (`bin/jobs`)
- Verify database connection
- Check `solid_queue_jobs` table exists
2. **Jobs failing**:
- Check `SolidQueue::FailedExecution.all` for error details
- Verify API keys are set (`ALPHA_VANTAGE_API_KEY`)
- Check database connectivity
3. **Recurring jobs not scheduling**:
- Verify `config/recurring.yml` syntax
- Check worker is running with recurring job support
- Confirm time zone settings
4. **Performance issues**:
- Adjust `JOB_CONCURRENCY` environment variable
- Monitor database table sizes
- Check API rate limits
### Debugging Commands
```bash
# View failed jobs with errors
rails console
SolidQueue::FailedExecution.all.each do |failed_job|
puts "#{failed_job.job.class_name}: #{failed_job.error}"
end
# Check recurring job schedules
SolidQueue::RecurringTask.all.each do |task|
puts "#{task.class_name}: #{task.schedule}"
end
# Clear all jobs (emergency only)
SolidQueue::Job.delete_all
```
## Time Zone Considerations
- **Recurring jobs use server time zone**
- **Jobs are scheduled in Eastern Time (America/New_York)**
- **Heroku**: Runs in UTC, so 1 AM ET = 6 AM UTC (EST) or 5 AM UTC (EDT)
- **AWS**: Configure server time zone or adjust schedule accordingly
- **Weekday scheduling**: OrderExecutionJob and StockPricesUpdateJob only run Monday-Friday
## Migration from Previous System
This system replaces the previous setup that used:
- ❌ **whenever gem** (removed) - No longer needed
- ❌ **Heroku Scheduler** (removed) - No longer needed
- ❌ **Delayed Job** (removed) - Replaced by Solid Queue
- ❌ **cron jobs** (removed) - No longer needed
### Benefits of New System
- ✅ **Environment independent** - Works same on Heroku and AWS
- ✅ **Database-backed** - More reliable than cron
- ✅ **Built-in monitoring** - Better visibility into job status
- ✅ **Job chaining** - Automatic OrderExecution → StockPrices flow
- ✅ **Rails 8 native** - Optimized performance and integration
## Related Documentation
- [Solid Queue Migration Guide](solid-queue-migration.md) - Technical migration details
- [Heroku Deployment Guide](heroku-deployment-solid-queue.md) - Deployment instructions
- [Orders and Transactions](orders-and-transactions.md) - Business logic overview

View File

@@ -1,99 +0,0 @@
# Schema
- schools
- id
- name
- years
- id
- year
- school_years
- id
- school_id
- year_id
- classrooms
- id
- school_year_id
- name
- grade
- teacher_id
Inheritance?
- users
- id
- type
- student
- teacher
- admin
- username
- password
- email
- user_invites
- id
- invite_code
- type
- student
- teacher
- admin
- date
- stocks
- id
- name
- ticker
- ...company details
- stock_prices
- id
- stock_id
- date
- open
- high
- low
- close
- volume
- adj_close
- stock_dividends
- id
- stock_id
- date
- dividend
!!!! Use Transactions
- portfolio
- id
- user_id
- cash_amount
- portfolio_transactions
- id
- portfolio_id
- actor_id
- date
- amount
- type
- deposit
- withdrawal
- portfolio_stocks
- id
- portfolio_id
- stock_id
- shares
- portfolio_stock_transactions
- id
- portfolio_stock_id
- stock_id
- date
- shares
- price
- type
- buy
- sell
- dividend
- ...other details

View File

@@ -1,39 +0,0 @@
# Seeds
## Objectives
- We want to maintain a rich database seed for development since that lays a good
foundation for easy contributions.
- We want to ensure production data integrity by putting stops in place to ensure
the development seed is never accidentally run in production.
- We want to keep our seeds separated into partials to keep things organized
and easy to maintain.
## Running the seeds
To seed your database, simply execute:
```
rails db:seed
```
In either the Development or Production environments. Seeds are idempotent.
## Split between Production and Development
To ensure the execution of seeds in only the appropriate environment the default
```db/seeds.rb``` file has been edited to check the environment and execute the
appropriate seed. **Do not edit this file to add your seeds.**
In the ```db/seeds``` directory you will find files named for each environment:
- ```development.rb```
- ```production.rb```
- ```test.rb```
These are where you will add your seeds.
## Seed partials
The ```db/seeds/partials``` directory contains partials that are loaded by the
respective environment seeds (see above). These will contain the bulk of our seed
code.
As far as is practical we will split these into one file per model.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff