Files
project-work/worker-toolkit-stocks-in-the-future/repo/docs/map/CONTEXT.md

108 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# How to walk this map
One job: tell a cold agent which parts of the app are in force, which are decoration,
and which words mean two things — before it opens a card.
Verified against commit `63732df` (detached HEAD), 2026-08-16.
## The three universes
| Universe | Meaning |
|---|---|
| **live** | In force. Implement and cite against these. |
| **leftover** | Still present and still wired, but no longer the main path. Touch only if that path is in scope. |
| **ghost** | Named or filed, not wired. **Do not implement against these.** |
Everything in `objects/` and `processes/` is `live` unless its frontmatter says otherwise.
### Ghosts — present in the tree, unreachable
- **`SchoolsController` + `app/views/schools/*` (9 files).** No route reaches them.
`resources :schools` appears only inside `namespace :admin` (`config/routes.rb:58`),
which resolves to `Admin::SchoolsController`. Rails scaffold remnant. If you want to
change school admin, edit `app/controllers/admin/schools_controller.rb`.
- **`admin_v2` / `/admin-new`.** Survives only as a comment (`config/routes.rb:42`).
The in-house admin is the live `namespace :admin` at `/admin`.
### Leftovers — wired, but not the path to build on
- **`users.classroom_id` direct membership.** See the trap below — this one is
load-bearing, not dead.
- **`PortfolioTransaction.reason: grade_earnings`** (enum value 3). Marked
`# Deprecated, will be removed in future` at `app/models/portfolio_transaction.rb:13`
and referenced nowhere else. New earnings use `math_earnings` / `reading_earnings`.
- **`docs/old-site/`.** Training material for the predecessor site.
## The traps
Four places where the obvious reading of the code is wrong. Each one has bitten or is
likely to.
### 1. A student's classroom has two rival sources of truth
Both are wired **right now**:
| Path | Where | Used by |
|---|---|---|
| `users.classroom_id` | `db/schema.rb:374`, `app/models/user.rb:20` | `Classroom#students` (`app/models/classroom.rb:20`), `Order.for_teacher` (`app/models/order.rb:40-42`), `ApplicationController` redirects |
| `classroom_enrollments` | `app/models/classroom_enrollment.rb` | `Student#current_classrooms` (`app/models/student.rb:24`), `Classroom#current_students` (`app/models/classroom.rb:74`) |
`Student#primary_classroom` bridges them and falls back to `classroom_id`
(`app/models/student.rb:37-43`, commented "for backward compatibility"). `Student`
creation writes **both**: `create_initial_enrollment` fires only when `classroom_id` is
present (`app/models/student.rb:10,82-86`).
**Consequence:** a student enrolled only via `ClassroomEnrollment` is invisible to
`Classroom#students` and to teacher order scoping. Changing either path without the
other splits the roster. Start at `objects/org/classroom-enrollment.md`.
### 2. Money is integer cents — except in one method
Every column is cents (`amount_cents`, `price_cents`, `worth_cents`). But
`Portfolio#cash_balance` returns **dollars** as a float
(`app/models/portfolio.rb:16-18` → `cash_on_hand` → `/ 100.0` at `app/models/portfolio.rb:67-69`).
Callers must multiply back: `app/models/order.rb:137` does
`(user.portfolio&.cash_balance || 0) * 100`. Any new caller that forgets is off by 100×.
Detail: `objects/money/portfolio.md`.
### 3. Cash is never stored
`portfolios` has exactly three columns — `id`, `user_id`, timestamps
(`db/schema.rb:181-186`). There is no balance column. Every balance is a live SUM over
`portfolio_transactions`, **plus a subtraction for pending orders and their fee**
(`app/models/portfolio.rb:71-100`). Balance is therefore a function of open orders, not
just settled history.
### 4. One API key, two homes, two different failure modes
| Reader | Source | Missing-key behaviour |
|---|---|---|
| `AlphaVantageApiClient` | `ENV["ALPHA_VANTAGE_API_KEY"]`, default `nil` (`app/services/alpha_vantage_api_client.rb:11`) | logs an error and returns `nil` (`:31-36`) |
| `StockAttributeUpdate` | global `API_KEY` (`app/services/stock_attribute_update.rb:75`) from `config/initializers/api_keys.rb:1`, default `"test-api-key"` | silently queries Alpha Vantage with a junk key |
One fact, two homes. If you consolidate, `objects/trading/stock.md` lists what reads it.
## Name collisions, stated once
| Product word | Code name | Note |
|---|---|---|
| SIF dollars | `PortfolioTransaction#amount_cents` | integer cents; no `Money`/`BigDecimal` wrapper |
| balance / cash on hand | `Portfolio#cash_balance` | derived; returns **dollars** |
| grade (5th–8th) | `Grade`, `grades.level` | `app/models/grade.rb` |
| grade (A+…F) | `GradeEntry#math_grade`, `#reading_grade` | `app/models/grade_entry.rb:15` |
| admin | `users.admin` boolean | **not** an STI type (`app/models/user.rb:39,43`) |
| student / teacher | STI on `users.type` | `Student < User`, `Teacher < User` |
| holding / position | `PortfolioStock` rows vs `PortfolioPosition` | rows are append-only lots; the PORO is the aggregate |
| gradebook "finalize" | `GradeBook#verified!` then `completed!` | two statuses, one button |
| Stocks for Good | `StocksInTheFuture` | `docs/README.md:1` vs `config/application.rb:14` |
## Walking order
1. This file — universes and traps.
2. `objects/_index.md` — find the noun.
3. One card. Follow its `See:` link into the app tree.
4. Before editing: `effects/CONTEXT.md`.
Do not read the whole `objects/` folder. The index exists so you do not have to.