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,74 @@
---
type: object
cluster: content
universe: live
status: verified
entity: app/models/announcement.rb
---
# Announcement
A site-wide notice written by an admin, with rich text. One may be "featured" at a time.
Verified 2026-08-16 against commit `63732df`.
## Why this shape
**Content is Action Text, not a column.** `has_rich_text :content`
(`app/models/announcement.rb:4`) stores the body in `action_text_rich_texts`
(`db/schema.rb:17-25`) as a polymorphic association. So `content` is a record, not a
string: it is not selectable, not sortable, and not searchable with a plain `WHERE` on
this table.
**The `body` column is a ghost.** `announcements.body` exists (`db/schema.rb:56`) but is
never read, written, validated, or permitted — `announcement_params` allows only
`title`, `content`, `featured` (`app/controllers/admin/announcements_controller.rb:79-81`).
It is the pre-Action-Text column, left behind. Do not write to it expecting it to appear.
**"Only one featured" is a callback, not a constraint.** `before_save
:unfeature_other_announcements` demotes the current holder when a new one is featured
(`:9,27-32`), and `Announcement.current` simply does `find_by(featured: true)`
(`:13-15`). There is no unique index — concurrent writes can leave two featured rows, and
`current` will then return an arbitrary one. The demotion also runs `update` (not
`update!`) on the old record (`:31`), so a failure there is silent.
## Shape
- Table `announcements`, `db/schema.rb:55-62` — `title`, `featured`, `body` (ghost),
timestamps; index on `created_at DESC` (`db/schema.rb:61`)
- `validates :title, presence: true, length: { maximum: 255 }` (`:6`)
- `validates :content, presence: true` (`:7`) — validating the Action Text association
- `scope :latest` — newest first (`:11`)
- `self.current` — the featured one, or `nil` (`:13-15`)
- `excerpt(limit: 150)` — plain-text truncation (`:17-19`)
- `published_at` is an **alias for `created_at`** (`:21-23`); there is no publish workflow
and no draft state
## Connected to
- **owns:** its Action Text record
- **owned-by:** —
- **joins:** —
- **looks-like-but-is-not:** `published_at` is not a publication timestamp — an
announcement is live from the moment it is created. And `content` is not a column.
## If you change this
- **Hits:** `Admin::AnnouncementsController` (full CRUD) and
`AnnouncementsController#show`; the home page and any layout partial calling
`Announcement.current`; Action Text and Active Storage if you touch `content`, since
embedded attachments live there.
- **Does not hit:** anything financial. Announcements touch no portfolio, order, or
gradebook — this is the one object in the map with no path to money.
## Surfaces
| Surface | Role |
|---|---|
| `Admin::AnnouncementsController` | admin CRUD |
| `AnnouncementsController#show` | everyone reads |
| `HomeController#index` | reads the featured one |
## See
- Source: `app/models/announcement.rb`, `db/schema.rb:55-62`