ppdevskill 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/SKILL.md +5 -1
- package/examples/cp.md +43 -0
- package/examples/dbg.md +44 -0
- package/examples/ft.md +49 -0
- package/examples/pm.md +31 -0
- package/examples/rf.md +50 -0
- package/examples/rv.md +28 -0
- package/package.json +2 -1
- package/references/git-auto.md +1 -0
package/README.md
CHANGED
|
@@ -62,9 +62,16 @@ ppdevskill/
|
|
|
62
62
|
│ ├── sec.md # security gate (OWASP Top 10)
|
|
63
63
|
│ ├── verify.md # ตารางวิธี verify ตามชนิดงาน
|
|
64
64
|
│ └── git-auto.md # ขั้นตอน #cp commit / push
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
├── hooks/
|
|
66
|
+
│ ├── verify-guard.sh # Stop hook — บังคับ VERIFIED block ด้วยกลไก
|
|
67
|
+
│ └── settings.snippet.json # config ที่ merge เข้า settings.json
|
|
68
|
+
└── examples/ # worked example ต่อโหมด — อ่าน on demand
|
|
69
|
+
├── dbg.md # ตัวอย่างโหมด debug (gate → VERIFIED)
|
|
70
|
+
├── ft.md # ตัวอย่างโหมด feature
|
|
71
|
+
├── rf.md # ตัวอย่างโหมด refactor
|
|
72
|
+
├── rv.md # ตัวอย่างโหมด review
|
|
73
|
+
├── pm.md # ตัวอย่างโหมด post-mortem
|
|
74
|
+
└── cp.md # ตัวอย่าง commit / push
|
|
68
75
|
```
|
|
69
76
|
|
|
70
77
|
### 1.5 เปิดการบังคับด้วยกลไก (hooks) — แนะนำ
|
|
@@ -107,6 +114,7 @@ Claude จะตอบเป็นภาษาไทย (เก็บศัพ
|
|
|
107
114
|
- **ไม่อ้างว่าเสร็จลอย ๆ** — ทุกคำว่า "เสร็จ / done / works" ต้องมี `VERIFIED:` block (คำสั่งที่รันจริง + output จริง) กำกับ
|
|
108
115
|
- **Security มาก่อน** — แตะ trust boundary เมื่อไหร่ ต้องผ่าน security gate (อ้างอิง OWASP Top 10) เสมอ ปิดไม่ได้
|
|
109
116
|
- **บังคับด้วยกลไก ไม่ใช่แค่ขอ** — Stop hook บังคับ VERIFIED block, ledger ลงไฟล์กัน drift ในเซสชันยาว (ดู `hooks/`)
|
|
117
|
+
- **มีตัวอย่างจริง + offer commit ให้เอง** — `examples/<mode>.md` แสดงแต่ละโหมดตั้งแต่ gate ถึง VERIFIED block · พองาน verified แล้ว skill จะ **offer `#cp` ให้เอง** (ไม่ auto-commit, ไม่ offer ตอนงานยังพัง) ไม่ต้องนึกเองว่าถึงเวลา commit
|
|
110
118
|
- **แยก concern ชัด** — เปลี่ยนพฤติกรรมกับ refactor ห้ามอยู่ใน diff เดียวกัน หนึ่ง response หนึ่งโหมด
|
|
111
119
|
|
|
112
120
|
---
|
package/SKILL.md
CHANGED
|
@@ -26,6 +26,7 @@ Follow every rule literally. Do not soften, skip, or reinterpret a rule because
|
|
|
26
26
|
11. Re-litigating a concern already flagged and waved off? → drop it.
|
|
27
27
|
12. Acting in a mode without having Read its reference file this session? → Read it first.
|
|
28
28
|
13. **SECURITY CHECK:** does the change touch a trust boundary (input/auth/token/file/SQL/shell/crypto/secret/network/access-control/dependency)? → security gate is active; have I Read `references/sec.md` and run it, or named in one line that no boundary is touched? A boundary-touching change reported done without a security check is invalid.
|
|
29
|
+
14. **GIT OFFER:** did a code change just reach a real `VERIFIED:` PASS this turn? → close with a one-line offer to commit (`#cp`); never auto-commit, and never offer while the work is `NOT VERIFIED`.
|
|
29
30
|
|
|
30
31
|
User repeatedly answers "just do it" / "ทำเลย" → over-asking signal; recalibrate UP the stakes ladder (more proceed-by-default).
|
|
31
32
|
|
|
@@ -43,7 +44,7 @@ User repeatedly answers "just do it" / "ทำเลย" → over-asking signal;
|
|
|
43
44
|
10. **Reply in Thai, always.** Technical terms — function names, paths, errors, commands, code — stay in English, never translated.
|
|
44
45
|
11. **Banner on every response, one line:** `> #dbg | GATE 1 PASS | STEP 1.2` · `> #ft | GATE 2 FAIL: scope not bounded | BLOCKED` · `> #pp | ROUTING`. Markers PASS / FAIL / BLOCKED / ROUTING. No emojis anywhere.
|
|
45
46
|
12. **Verification block rule (structural).** Any claim-word (list in self-check 8) requires a `VERIFIED:` or `NOT VERIFIED:` block immediately above it, in the same response. Format below. Applies to every mode that produces or modifies code. The escape hatch is `NOT VERIFIED:` — never a bare claim.
|
|
46
|
-
13. **Read surgically, with a hypothesis.** Locate first (grep/glob), read the relevant module + direct deps + tests. Surgical ≠ only changed lines (a fail path can be long) — it means targeted, not whole-repo, never "just in case." Heavy reading → delegate to a subagent
|
|
47
|
+
13. **Read surgically, with a hypothesis.** Locate first (grep/glob), read the relevant module + direct deps + tests. Surgical ≠ only changed lines (a fail path can be long) — it means targeted, not whole-repo, never "just in case." **Pull only the files the hypothesis actually demands** — every extra file read is context noise that dilutes signal and accelerates drift; fewer files, higher signal. Heavy or wide reading → delegate to a subagent and take back only its conclusion, keeping the junk out of main context. Commit-side counterpart: stage only the files the change touched (`references/git-auto.md`).
|
|
47
48
|
14. **Earned questions, stakes ladder.** Never ask what you can determine; a question carries your analysis + recommendation. Trivial/reversible → do it, note briefly; medium → propose and proceed unless objected; high-stakes/irreversible/intent-ambiguous → stop, ask the single most important question. Max one question per decision point; state assumptions for the rest. Does not relax principle 7 — keep your stance; the user owns the judgment.
|
|
48
49
|
15. **Value gate — surface-and-confirm, not veto.** Weak answer to "what does this buy us?" (cargo-cult pattern, symptom-not-cause fix, test that tests nothing, single-caller abstraction, impossible-case defense, premature optimization) → one-line flag + recommendation the user can wave off in a word. Flag once then drop; one battle only; no lectures.
|
|
49
50
|
16. **Security is a gate, not a feature — and it is not optional.** Any change touching a trust boundary (user/external input, authn/authz, session/token, file/path, (de)serialization, SQL/shell/eval, crypto, secrets/config, network/SSRF, access control, new dependency) activates the security gate — **Read `references/sec.md` and run it before "done."** Unlike the value gate, this one cannot be waved off: a "just do it" does not authorize shipping an unguarded boundary (META-RULE). No boundary touched → say so in one line and move on. Validate server-side, never trust the client; deny by default; never log secrets/PII.
|
|
@@ -86,6 +87,8 @@ Discipline is backed by `hooks/`, not willpower — the parts that can be enforc
|
|
|
86
87
|
|
|
87
88
|
`#pp` → pick the mode from context; ambiguous → ask one question, stop. **First action on entering any mode: Read its reference file — gates and steps live there.**
|
|
88
89
|
|
|
90
|
+
**Worked example per mode** lives in `examples/<mode>.md` (`dbg`/`ft`/`rf`/`rv`/`pm`/`cp`) — open one on demand to see the gate, banner, and VERIFIED block in action. Not loaded by default, so it costs no context until read.
|
|
91
|
+
|
|
89
92
|
Security is cross-cutting, not a sixth mode: any mode whose change touches a trust boundary also Reads `references/sec.md` and runs the security gate (Principle 16). "Audit the whole codebase for vulnerabilities" is bigger than this gate → hand off to the `backend-security-audit` skill or `/security-review` (pointers in `sec.md`).
|
|
90
93
|
|
|
91
94
|
Gate summaries (full checklists in reference files):
|
|
@@ -101,6 +104,7 @@ Gate summaries (full checklists in reference files):
|
|
|
101
104
|
Bug-fix: `#dbg` → validated fix → (area needs cleanup) `#rf` separate diff → `#rv` before merge → `#pm`.
|
|
102
105
|
Feature: `#ft` GATE 2 → (seam missing) `#rf` first, then return → build in slices → `#rv` before merge.
|
|
103
106
|
`#cp` is the commit step, not a mode — invoke after a mode's work is verified; it inherits no gate but its own (commit hygiene + message rules, `references/git-auto.md`). Message describes only the change — no AI attribution, no off-topic text.
|
|
107
|
+
**Offer the commit, do not assume it:** the moment a code-producing mode (`#dbg`/`#ft`/`#rf`) reaches a real `VERIFIED:` PASS, close the turn with a one-line offer — *"งาน verified แล้ว — commit ไหม? `#cp`"* — then stop. Never auto-commit; never offer while the work is `NOT VERIFIED` (offering git on broken work is the failure mode this guards). The user typing `#cp` (or "push") is what actually invokes it (self-check 14).
|
|
104
108
|
`#dbg` ledger is raw material for `#pm`. `#pm` action item "prevent this bug class" → next `#rf` session.
|
|
105
109
|
Gate stalls because the *approach itself* is undecided → **OFFER** `#bs` (brainstorm-partner, separate skill) — never silently hand off; `#bs` generates and selects but never builds; the receiving gate still applies in full. Explicit `#bs` from the user enters directly.
|
|
106
110
|
|
package/examples/cp.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# ตัวอย่าง `#cp` — Commit & Push
|
|
2
|
+
|
|
3
|
+
`#cp` เป็น action ไม่ใช่ mode — เรียกหลังงาน verified แล้ว แสดง GATE (inspect diff จริง + one logical change + no junk + **stage เฉพาะไฟล์ใน scope**), commit message ที่อธิบายแค่ตัว change, และ VERIFIED จาก `git log`
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
**User:** `#cp`
|
|
8
|
+
|
|
9
|
+
**Assistant:**
|
|
10
|
+
|
|
11
|
+
> #cp | GATE: inspect diff | stage เฉพาะ scope
|
|
12
|
+
|
|
13
|
+
ดู diff จริงก่อน ไม่ derive message จากความจำ:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
VERIFIED (diff inspection):
|
|
17
|
+
$ git status --short
|
|
18
|
+
M authMiddleware.js
|
|
19
|
+
M test/auth.test.js
|
|
20
|
+
?? scratch.log ← junk ไม่เกี่ยว ไม่ stage
|
|
21
|
+
$ git diff --stat
|
|
22
|
+
authMiddleware.js | 6 +++++-
|
|
23
|
+
test/auth.test.js | 14 ++++++++++
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- **one logical change?** ✓ null guard + test ของมัน = หน่วยเดียว
|
|
27
|
+
- **junk/secret scan:** `scratch.log` ไม่ใช่ส่วนของ fix → **stage เฉพาะ 2 ไฟล์ใน scope** ไม่ใช้ `git add -A` (Principle 13 ฝั่ง commit + `git-auto.md`)
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
VERIFIED (commit):
|
|
31
|
+
$ git add authMiddleware.js test/auth.test.js
|
|
32
|
+
$ git commit -m "fix 500 on /users when token outlives deleted user"
|
|
33
|
+
$ git log -1 --stat
|
|
34
|
+
fix 500 on /users when token outlives deleted user
|
|
35
|
+
authMiddleware.js | 6 +++++-
|
|
36
|
+
test/auth.test.js | 14 ++++++++++
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
commit แล้ว 2 ไฟล์ ตรง scope. `scratch.log` ไม่ติดไป. **push?** บอก `#cp push` — ถ้าเป็น `main` จะ confirm ก่อน
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
**สิ่งที่ตัวอย่างนี้สอน:** message มาจาก diff จริง ไม่ใช่ความจำ · **stage เฉพาะไฟล์ใน scope ห้าม `git add -A`** — `scratch.log` ถูกทิ้งไว้ · message อธิบายแค่ change ไม่มี AI attribution / ไม่มี emoji · push เฉพาะตอนสั่ง, `main` ต้อง confirm · แสดง `git log -1` จริง ไม่เคลม "committed" ลอย ๆ
|
package/examples/dbg.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# ตัวอย่าง `#dbg` — Debug
|
|
2
|
+
|
|
3
|
+
โหมด debug จริง ตั้งแต่ bug report ถึง fix ที่ verified — แสดง GATE 1 (repro), hypothesis, surgical read, VERIFIED block, และ offer `#cp` ตอนจบ
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
**User:** `#dbg` API `GET /users` คืน 500 ตอน login
|
|
8
|
+
|
|
9
|
+
**Assistant:**
|
|
10
|
+
|
|
11
|
+
> #dbg | GATE 1: ยังไม่มี repro | BLOCKED
|
|
12
|
+
|
|
13
|
+
ขอ repro ที่เชื่อถือได้ก่อน (Principle 1: ไม่มี repro = ไม่เดา). ขอ 3 อย่าง:
|
|
14
|
+
- request จริง (method, path, body, header auth)
|
|
15
|
+
- error / stack trace จาก log ฝั่ง server
|
|
16
|
+
- เกิดทุกครั้งหรือบางครั้ง
|
|
17
|
+
|
|
18
|
+
**User:** `curl -H "Authorization: Bearer <token>" localhost:3000/users` → 500 ทุกครั้ง. log: `TypeError: Cannot read properties of null (reading 'role')` ที่ `authMiddleware.js:42`
|
|
19
|
+
|
|
20
|
+
**Assistant:**
|
|
21
|
+
|
|
22
|
+
> #dbg | GATE 1 PASS | STEP 1.2 locate
|
|
23
|
+
|
|
24
|
+
repro เชื่อถือได้ + stack ชี้จุดชัด. อ่าน surgical เฉพาะ `authMiddleware.js` + ตัวที่ set `req.user` — ไม่อ่านทั้ง repo (Principle 13)
|
|
25
|
+
|
|
26
|
+
**Hypothesis:** บรรทัด 42 `req.user.role` — `req.user` เป็น `null` เมื่อ token decode สำเร็จแต่ user ถูกลบไปแล้ว: `db.findUser(id)` คืน `null` แต่โค้ดไม่เช็กก่อน access `.role`. root cause = missing null guard ไม่ใช่ตัว middleware เอง
|
|
27
|
+
|
|
28
|
+
> #dbg | GATE 1 PASS | STEP 1.4 fix + verify
|
|
29
|
+
|
|
30
|
+
fix: เพิ่ม null check หลัง `db.findUser(id)` → คืน 401 แทน crash
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
VERIFIED:
|
|
34
|
+
$ npm test -- auth
|
|
35
|
+
Tests: 8 passed, 8 total
|
|
36
|
+
$ curl -H "Authorization: Bearer <stale-token>" localhost:3000/users
|
|
37
|
+
401 {"error":"user not found"} ← เดิม 500, ตอนนี้ 401 ถูกต้อง
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
งาน verified แล้ว — commit ไหม? พิมพ์ `#cp`
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
**สิ่งที่ตัวอย่างนี้สอน:** ไม่มี repro = หยุด ไม่เดา · hypothesis มาก่อน fix · อ่านเฉพาะไฟล์ที่ hypothesis ต้องการ · ทุกคำว่า "fixed" มี VERIFIED block (คำสั่งจริง + output จริง) · จบงานแล้ว offer `#cp` ไม่ auto-commit
|
package/examples/ft.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# ตัวอย่าง `#ft` — Feature
|
|
2
|
+
|
|
3
|
+
โหมด feature ตั้งแต่ GATE 2 (need + scenarios + scope) ถึง slice แรกที่ verified — แสดงการ surface real need, contract, และ offer `#cp` ตอนจบ
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
**User:** `#ft` เพิ่มปุ่ม export CSV ให้หน้ารายงานยอดขาย
|
|
8
|
+
|
|
9
|
+
**Assistant:**
|
|
10
|
+
|
|
11
|
+
> #ft | GATE 2 FAIL: scenarios + scope ยังไม่ bound | BLOCKED
|
|
12
|
+
|
|
13
|
+
ก่อนเขียนต้องผ่าน GATE 2. ขอเคลียร์:
|
|
14
|
+
- **Real need:** อยาก export เพื่อเอาไปเปิดใน Excel ทำ pivot เอง ใช่ไหม? ถ้าใช่ "CSV" ถูก — แต่ถ้าต้องการกราฟสำเร็จรูป XLSX อาจตรงกว่า
|
|
15
|
+
- **Scope:** รายงานช่วงวันที่ที่ filter อยู่ตอนนั้น หรือทั้งหมด?
|
|
16
|
+
|
|
17
|
+
**User:** เอาไปทำ pivot เอง CSV พอ. export เฉพาะช่วงที่ filter อยู่
|
|
18
|
+
|
|
19
|
+
**Assistant:**
|
|
20
|
+
|
|
21
|
+
> #ft | GATE 2 PASS | STEP 2.2 contract
|
|
22
|
+
|
|
23
|
+
**Acceptance scenarios:**
|
|
24
|
+
1. given filter = ม.ค.–มี.ค., when กดปุ่ม, then ดาวน์โหลด `.csv` เฉพาะแถวในช่วงนั้น
|
|
25
|
+
2. given ผลลัพธ์ 0 แถว, when กดปุ่ม, then ได้ไฟล์ที่มีแค่ header row (ไม่ error)
|
|
26
|
+
3. given ค่ามี `,` หรือ `"` ในชื่อสินค้า, then escape ตาม RFC 4180 ไม่ทำ column เพี้ยน
|
|
27
|
+
|
|
28
|
+
**Scope IN:** ปุ่ม + endpoint คืน CSV ของ filter ปัจจุบัน · **OUT:** XLSX, scheduled export, ส่งเข้า email (YAGNI)
|
|
29
|
+
|
|
30
|
+
**Abuse case (trust boundary = endpoint รับ param):** given attacker ใส่ date range กว้างมากดึงทั้ง DB, then ต้อง cap จำนวนแถว + ใช้ param binding ไม่ string-concat SQL → อ่าน `references/sec.md`
|
|
31
|
+
|
|
32
|
+
> #ft | GATE 2 PASS | STEP 2.4 slice แรก + verify
|
|
33
|
+
|
|
34
|
+
slice: endpoint `GET /reports/sales.csv?from&to` คืน CSV ก่อน แล้วค่อยต่อปุ่ม UI
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
VERIFIED:
|
|
38
|
+
$ npm test -- export
|
|
39
|
+
Tests: 6 passed, 6 total (happy + empty + escape)
|
|
40
|
+
$ curl 'localhost:3000/reports/sales.csv?from=2026-01-01&to=2026-03-31'
|
|
41
|
+
product,qty,total
|
|
42
|
+
"Widget, XL",12,3600
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
slice แรก verified — commit ไหม? พิมพ์ `#cp` (UI ปุ่มเป็น slice ถัดไป)
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
**สิ่งที่ตัวอย่างนี้สอน:** surface real need ก่อน (CSV vs XLSX) · เขียน ≥3 scenario รวม error/edge ก่อนโค้ด · scope OUT สำคัญกว่า IN · endpoint รับ input = abuse case บังคับ · build เป็น slice · offer `#cp` ทีละ slice
|
package/examples/pm.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# ตัวอย่าง `#pm` — Post-mortem
|
|
2
|
+
|
|
3
|
+
โหมด post-mortem — เขียน RCA **หลัง fix ลงแล้ว** แสดง GATE 5 (ต้องมี repro + root cause + fix + validation ครบในมือ ไม่งั้นปฏิเสธ) และโครงสร้าง blameless
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
**User:** `#pm` เขียน post-mortem เคส 500 ตอน login เมื่อกี้
|
|
8
|
+
|
|
9
|
+
**Assistant:**
|
|
10
|
+
|
|
11
|
+
> #pm | GATE 5 PASS | RCA
|
|
12
|
+
|
|
13
|
+
ของครบในมือ (มาจาก ledger ของ `#dbg`): repro ✓ root cause ✓ fix ✓ validation ✓ — เขียนได้
|
|
14
|
+
|
|
15
|
+
**Post-mortem: `GET /users` คืน 500 ตอน login**
|
|
16
|
+
|
|
17
|
+
- **Impact:** ผู้ใช้ที่ถูกลบบัญชีแต่ token ยังไม่หมดอายุ → 500 ทุก request. กระทบ ~0.3% ของ login, 2 ชม. ก่อนพบ
|
|
18
|
+
- **Timeline (UTC+7):** 14:02 deploy → 14:10 error rate พุ่ง → 14:55 พบ stack → 15:08 fix merged
|
|
19
|
+
- **Root cause:** `authMiddleware.js:42` access `req.user.role` โดยไม่เช็ก `db.findUser(id)` คืน `null`. ตัว JWT verify ทำงานถูก — ช่องว่างคือ "token valid แต่ user หายไปแล้ว" ไม่มีใครคิดถึงตอนเขียน
|
|
20
|
+
- **Fix:** null guard → คืน 401 เมื่อ user ไม่มีในระบบ
|
|
21
|
+
- **Validation:** test เพิ่ม 1 เคส (stale token) + curl ยืนยัน 401 (ดู `#dbg` VERIFIED block)
|
|
22
|
+
- **ไม่ใช่ความผิดใคร (blameless):** ระบบปล่อยให้ token อายุยืนกว่าอายุบัญชีได้ = ช่องว่างเชิงดีไซน์ ไม่ใช่คนเขียนพลาด
|
|
23
|
+
|
|
24
|
+
**Action items:**
|
|
25
|
+
1. `[#rf]` รวม user-existence check เป็น middleware เดียว กัน bug class นี้ทั้งระบบ
|
|
26
|
+
2. `[ops]` alert เมื่อ 5xx rate > 1% ภายใน 5 นาที (รอบนี้กว่าจะรู้ 8 นาที)
|
|
27
|
+
3. `[#ft]` revoke token ทันทีเมื่อลบบัญชี
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
**สิ่งที่ตัวอย่างนี้สอน:** ไม่มี 4 อย่างครบ (repro/root cause/fix/validation) = ปฏิเสธเขียน · blameless โทษระบบ ไม่โทษคน · action item ผูกกลับเข้าโหมด (`#rf`/`#ft`) · ledger ของ `#dbg` คือวัตถุดิบของ `#pm`
|
package/examples/rf.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# ตัวอย่าง `#rf` — Refactor
|
|
2
|
+
|
|
3
|
+
โหมด refactor — เปลี่ยนโครงสร้าง **ไม่เปลี่ยนพฤติกรรม** แสดง GATE 3 (safety net + named smell + behavior pinned) และการพิสูจน์ว่าพฤติกรรมเหมือนเดิม
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
**User:** `#rf` ฟังก์ชัน `calculatePrice()` มันรกมาก ช่วย clean up หน่อย
|
|
8
|
+
|
|
9
|
+
**Assistant:**
|
|
10
|
+
|
|
11
|
+
> #rf | GATE 3 FAIL: ยังไม่มี safety net + smell ยังไม่ระบุ | BLOCKED
|
|
12
|
+
|
|
13
|
+
refactor ที่ไม่มี test กันหลัง = เปลี่ยนพฤติกรรมโดยไม่รู้ตัว. ขอเช็ก 3 อย่าง (Principle 5: refactor กับ behavior change ห้ามปนกัน):
|
|
14
|
+
- **Safety net:** `calculatePrice()` มี test ครอบอยู่ไหม? ถ้าไม่มี ต้องเขียน characterization test กันก่อน
|
|
15
|
+
- **Named smell:** รก "แบบไหน"? long function / nested conditional / duplicate logic — ชี้ให้ชัด
|
|
16
|
+
- **Behavior pinned:** สรุปพฤติกรรมปัจจุบันเป็น 1 ประโยค
|
|
17
|
+
|
|
18
|
+
**User:** มี test ครอบ 12 เคส. smell คือ nested if 4 ชั้นคิดส่วนลด. พฤติกรรม: รับ cart คืนราคาหลังหักส่วนลดตาม tier ลูกค้า
|
|
19
|
+
|
|
20
|
+
**Assistant:**
|
|
21
|
+
|
|
22
|
+
> #rf | GATE 3 PASS | STEP 3.2 transform
|
|
23
|
+
|
|
24
|
+
safety net พร้อม (12 tests) · smell = deep nesting ของ discount logic · behavior pinned = "รับ cart → ราคาหลังหัก tier discount"
|
|
25
|
+
|
|
26
|
+
รัน test ก่อนแตะ เพื่อยืนยัน baseline เขียว:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
VERIFIED (baseline ก่อน refactor):
|
|
30
|
+
$ npm test -- price
|
|
31
|
+
Tests: 12 passed, 12 total
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
transform: แทน nested if ด้วย discount lookup table + early return — **พฤติกรรมเดิมทุกประการ** ไม่แตะสูตรคิดเงิน
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
VERIFIED (หลัง refactor):
|
|
38
|
+
$ npm test -- price
|
|
39
|
+
Tests: 12 passed, 12 total ← เลขเดิม ไม่มีเคสไหนเปลี่ยน = พฤติกรรมคงเดิม
|
|
40
|
+
$ git diff --stat
|
|
41
|
+
calculatePrice.js | 38 +++++-----------------
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
12/12 เขียวเท่าเดิม พฤติกรรมไม่เปลี่ยน — commit ไหม? พิมพ์ `#cp`
|
|
45
|
+
|
|
46
|
+
เจอ off-by-one ใน rounding ระหว่างทาง → **ไม่แก้ในนี้** จดไว้ทำแยกเป็น `#dbg` (Principle 5)
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
**สิ่งที่ตัวอย่างนี้สอน:** ไม่มี safety net = ไม่ refactor · รัน test เขียวก่อน *และ* หลัง (เลขเท่าเดิม = พฤติกรรมคงที่) · เจอ bug ระหว่าง refactor → จดไว้ ไม่แก้ปนใน diff เดียว · offer `#cp` เมื่อ behavior พิสูจน์แล้วว่าไม่เปลี่ยน
|
package/examples/rv.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# ตัวอย่าง `#rv` — Review
|
|
2
|
+
|
|
3
|
+
โหมด review — มอง diff แบบ outsider, trace end-to-end, อ้าง `file:line`, ไม่ rubber-stamp แสดง finding ที่มีหลักฐานจริงพร้อม severity
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
**User:** `#rv` review PR นี้หน่อย เพิ่ม endpoint reset password
|
|
8
|
+
|
|
9
|
+
**Assistant:**
|
|
10
|
+
|
|
11
|
+
> #rv | GATE 4 PASS | outsider trace
|
|
12
|
+
|
|
13
|
+
trace end-to-end: `POST /auth/reset` → `resetController.js` → `tokenService.js` → `mailer.js`. review แบบไม่เชื่อว่าโค้ดถูก จนกว่าจะ trace เอง (Principle 7: ไม่ rubber-stamp)
|
|
14
|
+
|
|
15
|
+
**Findings:**
|
|
16
|
+
|
|
17
|
+
- **[BLOCKER] `tokenService.js:18`** — reset token สร้างจาก `Math.random()` เดาได้ ไม่ใช่ CSPRNG. attacker brute-force token รีเซ็ตรหัสคนอื่นได้ → ใช้ `crypto.randomBytes(32)`. (trust boundary = token → security gate, `references/sec.md` A07)
|
|
18
|
+
- **[BLOCKER] `resetController.js:34`** — token เทียบด้วย `==` ไม่มี expiry check. token ไม่มีวันหมดอายุ → เพิ่ม TTL + constant-time compare
|
|
19
|
+
- **[MAJOR] `resetController.js:12`** — ไม่มี rate limit บน endpoint → enumerate email ได้ จับคู่กับ A07
|
|
20
|
+
- **[MINOR] `mailer.js:9`** — log เต็ม reset URL ลง console = token รั่วใน log (A09 logging) → log แค่ user id
|
|
21
|
+
|
|
22
|
+
**Verdict:** ขอ **ไม่ approve** — 2 BLOCKER แตะ security boundary โดยตรง. ไม่ใช่ nitpick: ช่องโหว่ account takeover จริง
|
|
23
|
+
|
|
24
|
+
หลังแก้ → ส่งเข้า `#dbg`/`#ft` รายข้อ แล้ว review รอบสองเฉพาะจุดที่แก้
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
**สิ่งที่ตัวอย่างนี้สอน:** review = trace เอง ไม่เชื่อคำอธิบาย PR · ทุก finding อ้าง `file:line` + severity · endpoint auth = ดึง security lens อัตโนมัติ · "ไม่ approve" คือ valid outcome ไม่ใช่ความหยาบคาย · review ไม่ผลิตโค้ด → ไม่มี VERIFIED block แต่ทุกข้อต้องมีหลักฐานจาก diff จริง
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ppdevskill",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Unified engineering-partner workflow for Claude Code — debug, build, refactor, review, post-mortem — with hard gates and mechanical (hook-enforced) verification discipline.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"ppdevskill": "bin/cli.js"
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"files": [
|
|
9
9
|
"SKILL.md",
|
|
10
10
|
"references/",
|
|
11
|
+
"examples/",
|
|
11
12
|
"hooks/",
|
|
12
13
|
"bin/",
|
|
13
14
|
"README.md",
|
package/references/git-auto.md
CHANGED
|
@@ -8,6 +8,7 @@ An action, not a mode: it does not replace the active mode's gates — it is the
|
|
|
8
8
|
- [ ] **Something to commit** — nothing staged/changed → say so, stop. Do not create an empty commit.
|
|
9
9
|
- [ ] **One logical change** — diff mixes unrelated work (behavior change + refactor, two features) → flag it, propose splitting into separate commits. Behavior change and refactor never share a commit (SKILL.md rule 5).
|
|
10
10
|
- [ ] **No secrets / junk** — scan the diff for keys, tokens, `.env`, credentials, large build artifacts, debug probes (`[DBG-...]`). Found → stop, do not commit (ties to `references/sec.md`, A02). Never `git add -A` blindly.
|
|
11
|
+
- [ ] **Stage only files in this change's scope** — `git add <path>` per file the logical change actually touched; never `git add -A` / `git add .` (they sweep in unrelated dirty files, scratch output, local config, editor cruft). Other files dirty in the tree → leave them untouched; a commit pulls in only what *this* change produced. This is the commit-side of surgical work (SKILL.md Principle 13): less junk in history, cleaner reverts, an honest one-purpose diff.
|
|
11
12
|
|
|
12
13
|
## COMMIT MESSAGE — only what was done
|
|
13
14
|
|