# ผู้สอน: Assessment, เฉลย และ rubric capstone

## วิธีใช้ assessment

ข้อ 1–12 เป็นคลัง assessment เต็ม สำหรับ workshop 2 วัน ให้ผู้สอนเลือก **4 ข้อ** ทำ diagnostic 10–15 นาทีต้นวัน 1 และเลือก 4 ข้อคู่ขนานทำ posttest 10 นาทีท้ายวัน 2 โดยสลับชื่อธุรกิจ/ตัวเลข/route ที่ไม่เปลี่ยน principle. หากใช้ครบ 12 ข้อเป็นงานศึกษาต่อ ให้เวลา 20–25 นาที, ไม่เปิดเอกสาร, 1 คะแนนต่อคำตอบหลัก + 1 คะแนนสำหรับเหตุผล/หลักฐาน (24 คะแนน). เป้าหมายไม่ใช่ท่อง Cloudflare แต่ตัดสินใจได้ว่าควรหยุด, ตรวจ, หรือทำต่ออย่างไร.

## ข้อสอบสถานการณ์ (พร้อมเฉลยและเหตุผล)

1. **Prompt:** ผู้เรียนพิมพ์ “ทำเว็บให้เช่ากล้องสวย ๆ พร้อมจ่ายเงินวันนี้” โดยไม่มีราคา/เงื่อนไข/ผู้อนุมัติ. คำตอบแรกของ agent ควรเป็นอะไร?

   **เฉลย:** ให้ agent ทำ plan-only และถาม/บันทึก facts ที่ขาด: audience, content owner, pricing source, payment scope, form destination, acceptance criteria; ใช้ `[TODO]` แทนการแต่งข้อมูล. **เหตุผล:** ราคาและ payment เป็น business/security decisions; UI ที่ดูดีไม่พิสูจน์ความจริงหรือความพร้อม production.

2. **Context:** กติกา `npm run build`, “ห้ามส่ง secret”, และ “deploy preview only” ถูกใช้ทุก task. ควรเก็บที่ใด?

   **เฉลย:** `AGENTS.md` ของ repository แบบสั้น. **เหตุผล:** เป็น context ถาวร. คู่มือ deploy ยาวหรือ workflow review ที่ใช้บางครั้งควรเป็น skill/reference เพื่อไม่บวมทุก session.

3. **Skill:** ต้องการ procedure ตรวจ marketing page ก่อนส่ง preview โดยมี checklist ยาวและ brand guide. ควรวาง brand guide ทุกบรรทัดใน `AGENTS.md` ไหม?

   **เฉลย:** ไม่ควร; สร้าง skill ที่มี `name`/`description` ชัดและให้ body ลิงก์ `references/brand-guide.md` เมื่อจำเป็น. **เหตุผล:** progressive disclosure โหลดเฉพาะเมื่อ task match. ตรวจ syntax invocation ตาม host; portable core ไม่รับประกัน `$skill` หรือ `/skill` ทุกเครื่อง.

4. **API:** Frontend cart ส่ง `{ productId, quantity, totalPrice: 1 }` แต่สินค้าแท้ราคา 459000. Worker ควรทำอย่างไร?

   **เฉลย:** ignore/reject client `totalPrice`, validate product/quantity แล้วใช้ catalog server-side คำนวณใหม่. ใน repo นี้ `POST /api/quotes` รับ `items` (1–10 unique) และ Worker สร้าง `totalPrice`. **เหตุผล:** browser state เป็น UX state ไม่ใช่ authority ของเงิน.

5. **Rental:** ผู้ใช้ A และ B เห็น `available: true` จาก GET พร้อมกัน. ทั้งคู่กดจองกล้องที่เหลือชุดสุดท้าย. ทำไม GET check อย่างเดียวพอไม่ได้ และ repository ป้องกันอย่างไร?

   **เฉลย:** เกิด race ระหว่าง read และ write. `POST /api/rentals` insert `rentals`/`rental_days` เป็น batch และ D1 trigger `rental_days_capacity_before_insert` abort เมื่อเกิน capacity; Worker แปลงเป็น 409 `UNAVAILABLE`. **เหตุผล:** constraint ต้องตรวจที่ write point.

6. **Idempotency:** network timeout หลัง browser ส่ง `/api/contact`, ผู้ใช้กดซ้ำ. Frontend ใน repository ส่ง header ใด และ server behaviour ที่ถูกต้องคืออะไร?

   **เฉลย:** `Idempotency-Key`; same key + same payload ต้อง replay response เดิม (201 และ `Idempotent-Replayed: true` ใน implementation), key เดิมกับ payload ต่างกันตอบ 409 `IDEMPOTENCY_CONFLICT`. **เหตุผล:** ป้องกัน mutation ซ้ำโดยไม่ต้องอาศัย UI button disabled อย่างเดียว.

7. **Accessibility/motion:** reduced motion เปิดอยู่ แต่ animation `gsap.from({ opacity: 0 })` ยังคง execute แล้ว animation ถูกยกเลิก. เกิดอะไรและแก้อย่างไร?

   **เฉลย:** content อาจค้าง invisible. ให้ DOM/CSS เริ่ม final readable state และสร้าง GSAP tween เฉพาะ `prefers-reduced-motion: no-preference`; ใน store example ใช้ `gsap.matchMedia()` ตาม pattern นี้. **เหตุผล:** reduced motion ต้องเป็น usable experience ไม่ใช่ “animation เร็วมาก”.

8. **Form:** learner เพิ่ม field `phone` ใน form แต่ API `allowOnlyKeys` ไม่อนุญาต. ควรคาดหวังอะไรและทำอย่างไร?

   **เฉลย:** HTTP 400 `UNKNOWN_FIELD`; ตัดสิน privacy/data contract ก่อน แล้วจึงเปลี่ยน form + Worker validation + schema/migration + evidence พร้อมกันหากอนุมัติ. **เหตุผล:** strict allow-list ตั้งใจป้องกัน field ที่ frontend แอบเพิ่มหรือ typo.

9. **Deploy:** `npm run deploy` ใช้ได้บนเครื่องผู้สอน แต่ user ยังไม่ review preview และ D1 migration. action ที่ถูกต้องถัดไปคือ?

   **เฉลย:** ไม่ deploy production. รัน/อ่านผล `npm run deploy:check`, ระบุ environment, review migration, deploy preview/staging ตาม workflow ที่อนุมัติ, smoke test URL, สร้าง release packet และรอ human sign-off. **เหตุผล:** command สำเร็จไม่พิสูจน์ business correctness หรือ rollback readiness.

10. **Testing claim:** agent พูด “tests passed” แต่ report ไม่มี command/output และ `tests/` อาจไม่มีไฟล์. reviewer ทำอย่างไร?

    **เฉลย:** label เป็น `not verified`, run/inspect commands ที่ package ประกาศก่อนจึงสรุป. `npm run test` มี script แต่ไม่ใช่หลักฐานว่ามี/ผ่าน test ใน environment ปัจจุบัน. **เหตุผล:** evidence ต้องผูก command, output, scope และเวลา.

11. **Security:** ผู้เรียนจะ paste Cloudflare token ลง `SKILL.md` เพื่อให้ deploy ง่าย. คำตอบที่ถูกต้องคือ?

    **เฉลย:** ห้าม; remove token, rotate หากเคยเผย, ใช้ Cloudflare secret/environment binding หรือ credential store และอ้างเฉพาะชื่อ binding ใน docs. review scripts/MCP/plugin ก่อน execute และใช้ preview least privilege. **เหตุผล:** skill เป็น instruction/package ไม่ใช่ secret manager; Git/history/prompt อาจเผย credential.

12. **Endpoint mapping:** ผู้เรียนใช้ `POST /api/rental-requests` ตาม blog ข้างนอก แล้วได้ 404. ให้อธิบาย correction โดยใช้ repo เป็น source of truth.

    **เฉลย:** อ่าน `src/worker.ts`: routes จริงคือ `GET /api/health`, `GET /api/catalog`, `GET /api/availability`, `POST /api/contact`, `POST /api/rentals`, `POST /api/quotes`. Rental ต้องส่ง fields ที่ Worker allow-list ไม่ใช่ route/shape จาก tutorial. **เหตุผล:** external example เป็น reference ได้ แต่ implementation contract ของ repo เป็น authority.

### เกณฑ์แปลผล

| คะแนน / 24 | ความหมาย | การสอนต่อ |
|---:|---|---|
| 0–10 | ยังมอง AI เป็นเครื่องสร้าง code โดยไม่เห็น boundary | pair lab, ใช้ prompt plan-only และ evidence log ทุก task |
| 11–17 | เข้าใจหลักบางส่วน แต่ยังสับสน authority/production | review endpoint/race/deploy scenarios ซ้ำ |
| 18–21 | พร้อมทำ capstone ภายใต้ checkpoint | ให้รับบท reviewer เพิ่มหนึ่งรอบ |
| 22–24 | reasoning มีหลักฐานและชี้ risk ได้ | ให้ช่วย peer facilitation โดยไม่แก้แทนเพื่อน |

## Reference-solution checklists สำหรับ lab

### แบบตรวจเสริม: การติดตั้งและ design handoff

กิจกรรมนี้อยู่นอกคลังข้อสอบ 24 คะแนนเดิม ใช้ตรวจ prework และงานศึกษาต่อ:

| สถานการณ์ | คำตอบ/หลักฐานที่คาดหวัง |
| --- | --- |
| เปิด ChatGPT Desktop ได้ แต่ `codex` ไม่พบ | แยกการติดตั้งแอปกับ CLI; ตรวจ channel, PATH และ `codex --version` |
| ลงชื่อเข้าใช้แอปแล้วจึงคิดว่าใช้ Figma ได้ | ต้อง authorize Figma และมีสิทธิ์ file; ลองอ่าน frame จริง |
| ส่ง screenshot แล้ว agent บอกว่า MCP ผ่าน | ระบุว่าเป็น file handoff; MCP ต้องมี tool call/result ที่ตรวจย้อนกลับได้ |
| Figma remote อ่านคนละหน้ากับที่ต้องการ | ใช้ Copy link to selection ของ frame เดียวและเทียบชื่อ/ขนาด |
| มี local MCP default port แต่เครื่องใช้ร่วมกัน | อ่าน shared-infra ตรวจ listener/จองก่อนเปิด; ใช้ remote ถ้าชน |
| แบบสวยใน Dark แต่ Light อ่านปุ่มไม่ออก | ตรวจ semantic colors/contrast ทั้งคู่; Light เป็น default ของ course web |

รับงานเมื่อมี prompt ฉบับเต็ม, แบบที่เลือก, mapping ที่ตรวจได้ และผลตรวจ 390px/1440px/keyboard ตาม [Lab Design Tools/MCP](../labs/design-tools-and-mcp.md) หากไม่มี account ให้รับ file handoff ที่ระบุข้อจำกัดตรงตามจริง ไม่ลดคะแนนเพียงเพราะผู้เรียนไม่ได้สมัครทุกบริการ

สิ่งต่อไปนี้เป็นเฉลยแนวตรวจ ไม่ใช่คำสั่งให้ copy/overwrite code; ก่อนตรวจต้องใช้ working tree และ output ปัจจุบัน

### Lab บริษัท: contact flow

**Expected contract:** เปิด `/examples/company/`; form JS posts `name`, `email`, `message`, boolean `consent`, `website` to `/api/contact`, adds JSON content type and `Idempotency-Key`. Worker returns 201 onlyหลัง validation/DB mutation. `DEMO_MODE: "true"` ใน `wrangler.jsonc` ทำให้ bypass Turnstile และ redacts stored personal fields, แต่ไม่เปลี่ยน rule ที่ต้อง validate consent/honeypot/origin.

**Instructor checks:**

- [ ] `/api/health` แสดง JSON (ไม่ต้องอ้าง storage ready หาก D1 ยังไม่ตั้ง)
- [ ] valid demo submit แสดง message + id หรือบันทึก failure output ที่แท้จริง
- [ ] blank/invalid email/consent missing มี state ที่ user เข้าใจ
- [ ] keyboard focus ไปถึง form/status; ห้ามส่งข้อมูลลูกค้าจริง
- [ ] repeated same request/key เข้าใจว่า idempotency server-side ไม่ใช่ browser magic

### Lab เช่า: availability/reservation

**Expected contract:** `examples/rental/main.js` fetch `/api/catalog`, then request `/api/availability` with `equipmentId,start,end,quantity`; POST `/api/rentals` with `equipmentId,start,end,quantity,name,email,consent,website`. `start`/`end` ใช้ `YYYY-MM-DD`, end ต้องหลัง start 1–30 วันและไม่ย้อนหลัง; valid id เช่น `camera`, `light`, `audio` ไม่ใช่ invented id. `totalPrice` ใน response เป็น satang and UI divides by 100 for Thai formatting.

**Instructor checks:**

- [ ] unknown query field / id / invalid date ถูก reject ตาม API response
- [ ] `/api/availability` เป็น preview; POST เป็น authority ที่มี D1 capacity trigger
- [ ] UI สามารถสื่อ 409 ไม่ใช่บอก “จองสำเร็จ”
- [ ] request field ไม่เกิน allow-list และ form applies idempotency key
- [ ] learner รู้ว่า `reserved-demo` ไม่มีผลทางการค้า

### Lab store: motion/cart/quote

**Expected contract:** `examples/store/main.js` imports `gsap` and `ScrollTrigger`, creates animation only for no-preference media query, and cart uses `Map`. Cart submit sends `items` as `{productId,quantity}` with contact/consent/honeypot, no price/total. Worker validates and returns `quote-demo`, not paid/order confirmed.

**Instructor checks:**

- [ ] reduced motion leaves hero/story/product content readable
- [ ] Escape/close control works and focus treatment is reviewed
- [ ] empty cart prevents quote submit; max quantity UI aligns with API limit 10
- [ ] modifying browser total cannot alter Worker computed total (show API response)
- [ ] copy says quote demo, no credit-card/payment claim

## Rubric capstone (100 คะแนน)

ให้คะแนนจาก artifact, live demo และ evidence record. “หน้าสวย” ไม่มีคะแนนแทน criterion ที่ขาด. หากมี hard-stop ด้านล่าง ให้ cap final score แม้คะแนนส่วนอื่นสูง.

![แผนภาพ release gates จาก lint และ typecheck ผ่าน API tests, browser และ accessibility tests, build dry-run, deploy และ production smoke](/diagrams/release-gates.svg)

[เปิดแผนภาพ release gates แบบเต็มหน้า](/diagrams/release-gates.html) แล้วให้ผู้เรียนชี้ว่า evidence ของตนหยุดอยู่ที่ gate ใดก่อนให้คะแนนหมวด Preview/release readiness ตาราง rubric ด้านล่างยังเหมาะกับการให้คะแนนหลายมิติ จึงคงเป็นตาราง ไม่แปลงเป็นรูป

| หมวด | คะแนน | หลักฐานที่ต้องเห็น |
|---|---:|---|
| Problem, brief และ content truth | 12 | audience/action, approved facts/assets, TODO owner, explicit out-of-scope |
| Spec/prompt discipline | 10 | `SPEC.md`, assumption log, plan-first prompt, small slice history |
| Information architecture และ UX | 12 | routes/sections/empty-error-success states, responsive demo 320px+desktop, CTA truthfully describes effect |
| Accessibility | 12 | semantic/labels/error/status, keyboard/focus, alt rationale, reduced-motion evidence where motion exists |
| Implementation quality | 10 | small coherent diff, no needless dependency, readable data/state boundary, no obvious code copied blindly |
| Server/data authority (as applicable) | 12 | form/catalog contract; server validates/reprices; D1 migration or explicit static-only rationale; no client trusted price |
| Security/privacy | 10 | no secret/real PII, allow-list/validation/origin boundary where API used, preview-only credentials and human gates |
| Validation and quality evidence | 10 | exact commands/output, manual smoke flow, not-run gaps stated; no unsupported “passed” claim |
| Preview/release readiness | 6 | preview URL or documented blocker, environment/commit, deployment check, rollback/known gaps |
| Communication/demo | 6 | 5–7 minute demo explains user outcome, one technical boundary, one risk and next action |

### Hard stops / score caps

| Condition | Maximum total until fixed |
|---|---:|
| Secret/token committed or pasted into deliverable | 0 and incident/rotation exercise required |
| Claims payment, reservation, testimonial, price, or compliance that is not true | 59 |
| Deploys/changes production without explicit authorized checkpoint | 59 |
| No working artifact or no evidence record | 49 |
| API accepts client-authoritative price/total for quote/commerce flow | 69 |

## Capstone solution outline

A passing company capstone can remain static plus contact demo: cite source of copy, make CTA/form honest, show form states and evidence. A rental capstone must not say “confirmed booking” unless it implements real staff/payment workflow; use request/`reserved-demo` language and derive price/capacity server-side. A storefront capstone can use GSAP only as progressive enhancement and must call the flow a quote request. For all tracks, the instructor awards full deployment points only for the actual preview evidence or a correctly documented blocker—not for a fabricated URL.
