Skip to content

2026-08-10 — ชั้นปี becomes one derived fact, and v4.6.0 ships ​

Migrations 0145–0146. One VM deploy. The invariants this produced live in STATE.md's NEXT-SESSION PROMPT; this file is the reasoning.

What the owner reported ​

Five things in one message. The first was three symptoms of one cause:

when i change ชั้นปี in the main web, nothing happens, and when i change รหัสนักศึกษา the รุ่น does change, but ปี doesn't change … i've test changing my student id to 603070316-0 it shows ชั้นปี 5 on main web, it shows จบแล้ว in ระบบบ้าน(ปี10), it shows ปี5 in teamsamo. the data become not syncing

…plus: fold คำขอแก้ไขของฉัน, fold อาจารย์ในบ้านเดียวกัน, design a real workflow for adding a นักศึกษา who already exists (raw 23505 on screen), and "scan for bugs, update the release, push to prod".

0145 — the ชั้นปี model ​

Two independent defects, both reproduced from the authority before anything was written. Full write-up: docs/mistakes/postgres-schema.md.

  1. person_mirror_down pushed people.year down; nothing carried it up. So update_my_identity's own last statement (an unrelated update people) reverted the edit the card had just made. Proved in a rollback transaction: tm=5 → tm=3 → tm=5.
  2. ระบบบ้าน derived ชั้นปี, ทีม SAMO stored it. house/fields.js had predicted this in a comment since 0131 and it prevented nothing: 9 of 400 members were a year behind, 13 more had a ชั้นปี and no รหัส, 3 had a รหัส and no ชั้นปี.

The fix moved the rule to src/js/study-year.js (a rule two systems need does not belong inside one of them) and moved the INGREDIENTS to the placements: team_members gained cohort_year + year_offset, mirrored down from public.people, with people_fill_cohort giving the registry 0128's re-derivation rule.

Three ordering/safety details that were each nearly wrong — see the mistakes entry: the backfill must run AFTER the new trigger body (or it blanks 109 postings for the still-served bundle); the backfill CONVERTS a stored ชั้นปี into a ปีที่เข้า rather than discarding it; and person_mirror_down must save/restore app.team_sync rather than blank it, or a member with two ตำแหน่ง cannot save.

The owner's question — "should changing รหัสนักศึกษา change ชั้นปี, i think it shouldn't" — was answered yes, with the reasoning that their instinct is already satisfied by year_offset being a DIFFERENCE. Written into study-year.js's header and pinned by two tests.

Result, measured live afterwards ​

beforeafter
distinct ชั้นปี answers for the reported person31
postings disagreeing with the registry—0
members with no ปีที่เข้า163 (they have no ชั้นปี either)

0146 — the crest refcount ​

Carried over from the 2026-08-09 handoff. photo_reference_count() answered 0 for every house crest because it counted five photo_url columns and the crest is icon_url. The guard test written for exactly this hazard scanned for photo_url and reported green. Write-up: docs/mistakes/integrations.md.

The three UI pieces ​

  • Folds (my-house.js): native <details>, because this card has already shipped the hand-rolled version and it opened only on odd-numbered paints. A PENDING request is never folded; the newest decision stays visible; the rest fold with the count on the summary.
  • "This person already exists" (house/index.js): three states, not one error. Write-up: docs/mistakes/frontend-ui.md.
  • The ทีม SAMO admin ชั้นปี box became read-only and computed, repainting as the รหัส is typed.

v4.6.0 ​

86 commits since v4.5.0 → MINOR. All 46 notes came from PENDING; the 48 TODO lines tools/release.mjs emitted were deleted rather than rewritten, because every one was already covered by a note written at ship time. That is what the PENDING mechanism is for.

Verification ​

Deployed and checked from the SERVED artifacts, never the local files: /build.json → 4.6.0; six new Thai string literals present; a control string that must be absent returns 0; headless drive of /, /admin/, /updates, /vssound → zero console errors.

⚠️ studyYearLabel landed in the SHARED chunk analytics-*.js, which BOTH entries import — grepping only the entry bundles for it returns 0 on a good deploy.

Working docs. STATE.md is the status file and lives at the repo root, not here.