ธีม
Archived from STATE.md — 2026-08-05
Both SHIPPED to prod and stable. Moved out of STATE.md to keep it a "what is true right now" document; git log --oneline is the chronology.
Org chart collapse + "ตำแหน่งของฉัน" (2026-08-05 — SHIPPED to prod)
LIVE. main at 12f93e3 (+ a follow-up commit for the proof scripts), pushed; KKU VM deployed and verified against the SERVED bundle (buildId ff617f917f9b). Behaviour re-run against https://samo.md.kku.ac.th/team in headless Chrome — identical to local. 279 tests green; every proof script in tools/ green.
Rendered and driven in a real browser this time (the previous session could not). No Chrome extension — the extension was not connected — so it was done with headless Chrome + CDP over a raw WebSocket: /private/tmp/.../scratchpad/cdp.mjs in that session. Node 22 has a global WebSocket, so a ~40-line driver gets navigate / evaluate / screenshot with no dependency. Worth rebuilding if you need to SEE a change; --dump-dom alone cannot click.
- โครงสร้างองค์กร is now an accordion. It rendered all 279 ตำแหน่ง / 402 people at once — 63,912px measured; collapsed it lands at ~3,000px. Each root ฝ่าย is a card showing its subtree counts ("65 ตำแหน่ง · 81 คน"). ARIA accordion pattern (heading wraps button). Toggling mutates the DOM, it does not re-render — a repaint would drop the scroll position. Collapsed bodies use
hidden, deliberately NOT a0frheight animation: that is what stops 400 lazy portraits being fetched for branches nobody opened. A ตำแหน่ง with ≤3 people and no children stays inline (106 hold exactly one). Search results are always fully expanded with no toggles. Station rows are a named CSS grid — flex-wrap stranded the count pill and the chevron on their own lines at 390px. - ตำแหน่งของฉันในทีม SAMO — new card under the home greeting and in the โปรไฟล์ modal. A ทีม SAMO grant used to be invisible to the person holding it. Fed by
public.get_my_team_seat()(migration 0109, applied): definer, takes NO argument so identity comes fromauth.uid()and it cannot be aimed at anyone else; hand-built jsonb allow-list, neverreturns setof. Proof:tools/seat0109-my-seat.mjs(17 checks, incl. anon refused over real HTTPS and "the payload carries no other person's kkumail / รหัส"). The CTA respects the door it opens: apassport-only grantee is sent to/passport/, not/admin/, which would bounce them (ADMIN_FEATURES). src/js/team-vocab.jsis new — PERM_CATALOG / VS_DEPTS / PROJECT_SEATS / ADMIN_FEATURES moved out of the admin-onlyteam/index.js+admin-main.jsso the public card names things the same way. Behaviour unchanged on both sides; the user asked for no changes to the admin ทีม SAMO UI and there are none.
Two bugs found by the scan, both now in .claude/rules/mistakes.md:
revoke ... from publicdid NOT strip theauthenticatedgrant that this database's DEFAULT PRIVILEGES hand every new function — in thepublicschema, not justpassport.team_node_pathshipped world-callable on the first apply. Verify ACLs frompg_proc.proacl, never from the migration text.- Two proof scripts were failing/mis-reporting for CORRECT reasons (
prof0095assumed the probe account is never a namedprof_id;seat0109substring-matched a placeholderkkumail = '-'against uuids). Both fixed.
Known, NOT fixed — needs the user's call:
ฝ่ายเอิงtestis live on the public org chart (root ฝ่าย, 7 ตำแหน่ง, 5 people, one ตำแหน่ง literally namedhi). It is test data visible to the world at/team. Deleting it is a data change in ทีม SAMO, so it was left alone — ask before removing.- One
team_membersrow carrieskkumail = '-'(ชญาภา เลาหะตานนท์). Harmless today; ตรวจสอบข้อมูล should be showing it.
Release notes + versioning + the IT panel (2026-08-04 — SHIPPED to prod)
LIVE. main at 28fa020, pushed; tag v4.4.0 pushed; KKU VM deployed and verified against the SERVED bundle (buildId 9f65ec53b172, /build.json now reports {"buildId":…,"version":"4.4.0"}, /updates → 200). 265 tests green.
STILL NEVER RENDERED IN A BROWSER BY AN AGENT — the Chrome extension was not connected for this whole session, so every layout/animation decision was reasoned about and unit-tested, never seen. The user reviewed it by screenshot and caught one thing tests cannot (the sticky bar reading as a cut-off rectangle). If anything looks wrong on / or /updates, that is why.
/updates— the public changelog. Content issrc/data/changelog.js(22 curated releases, 2026-04-30 → 2026-08-01, condensed before July because that stretch ran ~13 commits/day). Reached from the footer's เกี่ยวกับเรา column and from the version chip in the footer bar; off-tablist tab likepills-article-tab, path route/updatesinPATH_ROUTES.- A real version system —
docs/VERSIONING.mdis the policy, read it first.MAJOR.MINOR.PATCHwith MAJOR redefined as "the portal's SCOPE changed" (SemVer's "breaking API change" can never fire on a website, so it would pin us at 1.x forever). 4 majors / 18 minors → current v4.4.0, assigned retroactively.npm run releasederives the bump from Conventional Commits, drafts the changelog stub, and optionally tags; it never pushes.v4.4.0is tagged LOCALLY and not pushed — push it when you next push.package.jsonis the single source of truth;/build.jsonnow carries{buildId, version}and__APP_VERSION__is defined at build time. Tests enforce that each bump matches its tier and that package.json agrees. - "เบื้องหลังการพัฒนา" on the landing page (
#devActivity) — THREE tiles (7 ระบบ · 22 เวอร์ชัน · 14 สัปดาห์) over a timeline of when each system opened (SYSTEMSinsrc/data/changelog.js). A fourth tile ("91 รายการที่อัปเดต") was removed: the user twice said it communicated nothing, and they were right — a count of changelog bullet points is a number only we can judge. The version and last-update date moved into the lead sentence instead. Home order is deliberate and was set by the user: banner → sign-in → ประกาศ → เบื้องหลังการพัฒนา → สถิติการใช้งาน → quick actions. - Two claims are BANNED from the panel and both have guard tests. (1) No "100% built in-house / ไม่ได้จ้าง" — this project is built with AI assistance and the claim overstated it; the user asked for it gone. (2) No cadence promise ("ทุกสัปดาห์") — real gaps run to weeks. Credit line reads "ดูแลโดย IT SAMO'69", not individual names.
- Thai copy — four rules the user gave, learned the slow way over ~5 rounds. (1) No literal translations of English idiom: "SAMO Portal ในตัวเลข" (from "by the numbers") and "ชุมชน…ที่กำลังเติบโตและให้บริการทุกวัน" both read as AI output. (2) Professional register, not casual — "เว็บนี้ยังพัฒนาต่อเรื่อย ๆ" was rejected; think professional web agency. (3) Do not mix languages inside one group — a row reading "22 เวอร์ชันทั้งหมด / 4 Major release" is the complaint; the changelog hero is now all-English (Releases · Major releases · Changes · Weeks) because its eyebrow already says "Release notes", while the landing panel stays all-Thai. (4)
LEVELSlabels stay English (Major/Minor/Patch) — "รุ่นใหญ่/รุ่นย่อย" is a translation nobody says. I cannot reliably judge natural Thai — get the user to read new copy. Every string I own is listed in the git log for this session's final commit. npm run check:iconsis new — run it before using a Bootstrap icon.bi-passport/bi-passport-fill/bi-envelope-arrow-upwere all added in bootstrap-icons 1.11 and both entries pin 1.10.5, so they rendered as empty boxes — silently, for months, in the ทีม SAMO permission modal and the profile "รอยืนยัน" badge. A missing glyph is not a 404 and not a console error. Full write-up in.claude/rules/mistakes.md. Passport now uses a plane, which is both correct and on-theme ("Life is a Journey").SYSTEMSdates are LAUNCH dates. SAMO Passport was first dated 2026-07-22 — the day its DATABASE merged into this project, which no student experienced. Its real launch is 2026-05-12, in its own repo (samomdkku/samomdkkupassport, cloned at~/development/samodevmdkku69/passport). Every other entry was verified withgit log --diff-filter=Aon the module or migration that introduced it. Known gap: Passport's launch has no release entry inchangelog.js— adding one means renumbering every version after it, so it was left for a deliberate pass.- The sticky filter bar on
/updatesis a FLOATING ROUNDED bar, matching.samo-navbar. A plain white rectangle inside the 900px column reads as "a rectangle that got cut off" (the user's words) because its hard edges stop mid-page against the body gradient. Full-bleed was the other fix and was REJECTED: it needsoverflow-x: clipto contain the width, which older iOS Safari does not support and would degrade to a horizontal scrollbar. It is also opaque, not frosted — blur over the green spine went muddy. npm run gen:activityregeneratessrc/data/dev-activity.jsonfrom git. Not wired intobuildon purpose.--checkfails when stale. It publishes no email addresses (repo is public, JSON is bundled) — a test asserts it.
The first version of the panel was wrong and was rebuilt — do not put it back. It showed commits (549), active days, lines added/deleted, longest streak and a GitHub-style commit heatmap. The user's objection was correct and is the general rule: those measure EFFORT, not outcome; lines-of-code and commit counts are discredited even inside engineering; and to a SAMO member a dense heatmap of nights and weekends reads as grinding, not competence. The panel now measures what exists that did not exist before. changelog.test.js has a guard test ("publishes no effort metrics") that fails if any of it creeps back. The heatmap data is still generated (a few KB, the honest record) and the validated 5-step green ramp is preserved in git history if it is ever wanted for an internal-only page.
Still needs a human, same reason as the item below: the Chrome extension was not connected, so none of this has been rendered. Check on / and /updates: the launch timeline flips from a vertical spine to a horizontal track at 768px and the connecting line lands on the nodes in both; the changelog hero aurora does not bleed sideways (it is margin-negative to full-bleed past .container-fluid px-4); the sticky filter bar's sliding pill sits under the active button after a resize; and the per-release spine segments join up rather than leaving gaps.