removed stocks
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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>' |
|
||||
|
||||
@@ -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.
|
||||
|
||||
---
|
||||
@@ -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 |
@@ -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.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 251 KiB |
@@ -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.
|
||||
@@ -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)
|
||||
|
||||
---
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user