convert pdf to md, add OVERVIEW and docs

This commit is contained in:
2026-08-17 22:30:18 +00:00
parent 95d5787868
commit 4df62d2609
44 changed files with 3307 additions and 1 deletions

View File

@@ -0,0 +1,76 @@
---
type: object
cluster: gradebook
universe: live
status: verified
entity: app/models/grade_book.rb
---
# GradeBook
One [classroom](../org/classroom.md)'s grades for one [quarter](../org/quarter.md), and
the object whose status decides whether students get paid.
Verified 2026-08-16 against commit `63732df`.
## Why this shape
The model is tiny — two belongs-to, one has-many, one enum (`app/models/grade_book.rb`) —
but the enum is the payout gate.
`status` has three values: `draft → verified → completed` (`:8-12`). Read literally that
looks like a review workflow. **It is not.** `GradeBooksController#finalize` sets
`verified!` and calls `DistributeEarnings` on the very next line
(`app/controllers/grade_books_controller.rb:30-31`), so `verified` exists for a few
milliseconds. Its real job is to satisfy the service's own guard,
`return unless @grade_book.verified?` (`app/services/distribute_earnings.rb:14`), which
keeps the service safe to call from anywhere else.
**Double-payment is prevented by exactly one check** — the controller's
`if @grade_book.completed?` (`app/controllers/grade_books_controller.rb:26`). There is no
database constraint, no idempotency key on the resulting deposits, and
`DistributeEarnings` itself would happily pay twice if handed a `verified` book. Anything
new that finalizes a gradebook must repeat that check.
Gradebooks are never created by a controller: [classroom](../org/classroom.md) creates one
per quarter on `after_create` (`app/models/classroom.rb:29,112-116`).
## Shape
- Table `grade_books`, `db/schema.rb:98-107`; unique on `[quarter_id, classroom_id]`
(`db/schema.rb:105`) — one book per classroom per quarter
- `status` is a **string** column, default `"draft"`, `null: false` (`db/schema.rb:102`)
- `belongs_to :quarter`, `belongs_to :classroom` (`:4-5`)
- `has_many :grade_entries, dependent: :destroy` (`:6`)
## Connected to
- **owns:** [grade-entry](grade-entry.md)
- **owned-by:** [classroom](../org/classroom.md), [quarter](../org/quarter.md)
- **joins:** —
- **looks-like-but-is-not:** `verified` is not a human review state; see Why.
And a `GradeBook` is not a [grade-level](../org/grade-level.md).
## If you change this
- **Hits:** [portfolio-transaction](../money/portfolio-transaction.md) — finalizing mints
deposits; the [finalize-gradebook-earnings](../../processes/finalize-gradebook-earnings.md)
movement; [grade-entry](grade-entry.md) via `dependent: :destroy`;
`GradeBookPolicy`; the autosave Stimulus controller, which PATCHes entries into the
`update` action.
- **Does not hit:** [order](../trading/order.md) or any holding. Earnings arrive as cash
deposits only — finalizing never buys, sells, or touches
[portfolio-stock](../trading/portfolio-stock.md).
## Surfaces
| Surface | Role |
|---|---|
| `GradeBooksController` (`show`, `update`, `finalize`) | teacher reads/writes |
| `Classroom#create_gradebooks_for_quarters` | writes (creation) |
| `DistributeEarnings` | reads status, writes `completed!` |
## See
- Source: `app/models/grade_book.rb`, `db/schema.rb:98-107`
- As-built: `docs/gradebook-earnings.md`