Dev gate
Skill shode666/claude-skill-shode-house/skills/workflow/dev-gate
[WHAT] บังคับ TDD cycle (red → green → refactor) + quality gate (format/lint/type/complexity/naming/test/doc) ก่อน hand-off production code. [AUDIENCE] Dave (implement) + Chris (verify) — sole owners. [WHEN] Phase 2 implement / refactor / bug-fix; ก่อน push หรือ open PR; หลัง spec ครบจาก Phase 1. [TRIGGER] /shode-house:dev-gate, "TDD", "test first", "red-green-refactor", "clean code", "เน้น quality", "production code", "refactor", "เริ่มเขียน code".From its SKILL.md
npx -y skills add shode666/claude-skill-shode-house --skill dev-gateAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
20.5 KB, as published. Nobody here has run it
Dev Gate (TDD + Quality Gates) — v3.0 merged
Owner: Dave (implement) + Chris (verify). เปิด skill นี้ตอนเขียน production code Merged from v2 skills:
tdd+code-quality— ลด context, รวม dev-time discipline
When NOT to use
- Spike / prototype / throwaway script — TDD overhead ไม่คุ้ม. ใช้
diagnoseแทนถ้าเป็น exploration - Generated code (codegen, OpenAPI client, ORM model) — gen tool ดูแล quality, dev-gate ไม่ฟิต
- Pure config change (yaml/json/env tweak) — review + smoke test พอ ไม่ต้อง TDD
- Production hot-fix P0/P1 — ใช้
incidentskill ก่อน; dev-gate ตามมาตอน follow-up fix
Required inputs — refuse without
ก่อน hand-off Phase 2 → 3, confirm ทุก checklist. ถ้าขาด list สิ่งที่ขาด แล้วหยุด — ห้าม claim "done":
- Spec ครบ จาก Phase 1 (BRD + AC G-W-T + ADR ที่กระทบ)
- Test runs locally (red phase): ทุก new behavior มี failing test ก่อน implement
- Test pass (green): ทุก unit/integration test green; ไม่ skip/disable
- Quality gate ผ่านครบ 11 (Gate 0-10, ดู Part 2): YAGNI · format · lint · type · complexity ≤10 · naming · test · coverage ≥ threshold · doc/comment "why" · security · observability
- Evidence paste: command + output ใน hand-off (ห้าม "should work")
หลักการ (🔴)
- No production code without failing test first (TDD core)
- Quality at dev time, not after — fix early ถูกกว่า 100x
- Best code = code you never wrote — ผ่าน YAGNI ladder (Step 0) ก่อนทุกครั้ง
Lazy ≠ Negligent — ห้ามตัด (carve-out)
YAGNI/compression ตัดได้เฉพาะ "ความซับซ้อนที่ยังไม่ต้องใช้" — ห้ามแตะ:
- Trust-boundary validation (input/HTTP/queue/env — Zod/Pydantic)
- Data-loss handling (transaction, idempotency, money R0)
- Security control (auth, crypto, secret, injection guard)
- Accessibility (WCAG — Uma's gate)
- Regulation/compliance (Felix BOT/PCI · Iris OIC · domain rule)
ตัดของเหล่านี้ = Philosophy violation, ไม่ใช่ "lazy"
Part 1: TDD Cycle (red → green → refactor)
0. ⛔ YAGNI ladder — หยุดก่อนเขียน (จาก ponytail)
ก่อนเขียน production code ใด ๆ ตอบไล่จากบนลงล่าง หยุดที่ข้อแรกที่ "ใช่":
| ขั้น | ถาม | ถ้าใช่ |
|---|---|---|
| 1 | feature นี้ต้องมีจริงไหม? | ไม่ → skip (YAGNI) + log เป็น bd discovered |
| 2 | stdlib ทำได้ไหม? | ใช้ stdlib |
| 3 | native platform feature? (<input type=date>, crypto, ...) | ใช้ native |
| 4 | dep ที่ลงแล้วทำได้? | ใช้ของเดิม ห้ามลง dep ใหม่ |
| 5 | one-liner พอไหม? | เขียนบรรทัดเดียว |
| 6 | ถ้าผ่านทั้งหมด | เขียน "ขั้นต่ำที่ work" เท่านั้น |
ทุกครั้งที่ตัด (ขั้น 1) หรือใช้ทางลัด → mark ด้วย
shortcut(bd:N):comment (ดู Gate 3) เพื่อให้ debt harvest เก็บได้ เพดานความขี้เกียจ = carve-out ด้านบน (validation/security/a11y/regulation ห้ามตัด)
1. 🔴 Red — เขียน test ที่ fail ก่อน
- Test เล็กที่สุด — 1 behavior
- Naming:
should_<behavior>_when_<condition>หรือ G-W-T - Run → ต้อง fail (assertion fail; compile error ไม่นับ)
def test_calculate_total_with_vat_includes_7_percent():
cart = Cart(items=[Item(price=Decimal("100"))])
total = cart.total_with_vat(rate=Decimal("0.07"))
assert total == Decimal("107.00")
2. 🟢 Green — เขียน code น้อยที่สุดที่ pass
- Simplest thing that works (YAGNI)
- ห้าม anticipate future requirement
- ห้าม refactor ตอนนี้
3. 🔵 Refactor — clean up (test ยัง pass)
- Remove duplication, improve naming, extract function
- ห้ามเพิ่ม behavior ใหม่ในรอบ refactor
TDD scope
✅ เหมาะ: business logic, calc, validation, state machine, parser, algorithm, bug-fix regression ❌ ไม่เหมาะ: UI prototype, spike, pure framework integration
Part 2: Quality Gates (🔴 11 gates — รัน local + pre-commit + CI)
เปลี่ยน v3.1.1: เพิ่ม Gate 0 Architecture self-check (SOLID/cohesion/readable) + เพิ่ม Gate 9 Security Lint. แตก Format เป็น Format / Imports / Remove-Unused เพราะ 3 หมวดใช้ tool ต่างกัน
| # | Gate | Read/Write | ตรวจอะไร | บล็อก hand-off ถ้า fail |
|---|---|---|---|---|
| 0 | Architecture self-check | Read (judgment) | SOLID + cohesion + human-readable | 🟠 (Dave judge; Chris verify) |
| 1 | Format | Write (rewrite style) | indent/space/quote/line-length | 🔴 |
| 2 | Organize Imports | Write (sort+group) | stdlib/3rd-party/local + alpha sort | 🔴 |
| 3 | Remove Unused | Write (delete) | unused import/var/function/parameter | 🔴 |
| 4 | Lint | Read (diagnose) | code smell + rule violation | 🔴 |
| 5 | Type Check | Read (diagnose types) | --strict, no Any/any escape | 🔴 |
| 6 | Complexity | Read (measure) | cyclomatic ≤ 10, file ≤ 500, fn ≤ 50 | 🔴 |
| 7 | Naming | Read (convention check) | noun/verb/bool/const pattern | 🟠 |
| 8 | Test | Run | coverage ≥ 80%, no skipped test | 🔴 |
| 9 | Security Lint | Read (SAST) | injection/secret/insecure dep | 🔴 |
| 10 | Doc | Read (existence) | docstring on public API + "why" comment | 🟠 |
Gate 0: Architecture self-check (🔴 v3.1.1 added — Dave's judgment ก่อน hand-off)
Tool ตรวจ Gate 1-10 ได้ครบ แต่ SOLID/cohesion/readable ต้องคนตัดสิน. Dave self-check ก่อน hand-off ลด round-trip กับ Chris
SOLID (5 ข้อ — ตอบ "yes" ทุกข้อก่อน hand-off):
- SRP — class/function ทำสิ่งเดียว. "และ" ใน description = แตก
- OCP — extend ได้ผ่าน interface/composition โดยไม่ต้องแก้ class เดิม
- LSP — subclass แทน parent ได้ทุก context (ไม่ throw, ไม่ break invariant)
- ISP — interface เล็ก. ห้าม "fat interface" ที่ implementor ต้อง stub method ที่ไม่ใช้
- DIP — depend on abstraction (interface/protocol) ไม่ใช่ concrete class. business logic ห้าม import framework โดยตรง
Cohesion + Coupling:
- High cohesion — code ในไฟล์เดียวกันแก้ปัญหาเดียว. ถ้า "และ" ปะปน → แตก module
- Low coupling — module A ไม่ควรรู้ internal ของ module B. ผ่าน interface/event/DTO
- Stable dependency — depend ไปทาง stable (lower layer). ห้าม domain → infra direct
Human readability (ก่อน push อ่าน diff ตัวเอง 1 รอบ):
- Intent revealing — ชื่อ + structure บอกเจตนาได้โดยไม่ต้องอ่าน implementation
- Linear flow — อ่าน top-to-bottom เข้าใจได้. ห้ามกระโดดข้าม helper > 3 hop
- Comment "why" — code อ่านได้แล้ว; comment เฉพาะตอน trade-off / business rule / quirk
- Symmetry — pattern ซ้ำในไฟล์ใช้ shape เดียวกัน (อ่าน 1 รอบเข้าใจ 10)
- Abstraction level — function เดียวอย่าผสม high-level + low-level (e.g., business logic + bit twiddling)
Self-check format ตอน hand-off:
Dave ▸ Chris : impl bd-42 (dev-gate passed 1-10)
- Gate 0 self-check:
- SOLID: SRP ✓ OCP ✓ LSP ✓ ISP ✓ DIP ✓
- Cohesion: high (1 module = 1 concern)
- Readable: diff อ่านแล้ว linear; no surprise
- Trade-off documented: <link to comment line:N> (if any)
ถ้า self-check fail → refactor ก่อน hand-off ห้าม "Chris จะ review ให้". Chris จะ reject + bd revision รอบใหม่
Per-language tool matrix (🔴 บังคับ use ตาม stack)
| Stack | 1 Format | 2 Imports | 3 Unused | 4 Lint | 5 Type | 6 Complexity | 9 Security |
|---|---|---|---|---|---|---|---|
| Python | ruff format / black | ruff --fix I / isort | ruff --fix F401,F841 | ruff E,F,B,N,UP,S,A,C90,SIM,RET | mypy --strict | radon cc -nb | bandit / semgrep |
| TypeScript/JS | biome format / prettier | biome check --apply / eslint-plugin-import/order | eslint --fix no-unused-vars,no-unused-imports / ts-unused-exports | biome check / eslint @typescript-eslint/strict | tsc --strict --noUncheckedIndexedAccess | eslint complexity | npm audit / semgrep |
| Go | gofmt / goimports | goimports (built-in) | golangci-lint unused | golangci-lint (govet,staticcheck,ineffassign,unused,gosec) | (compile = check) | gocyclo -over 10 | gosec (in golangci) |
| Java | google-java-format / spotless | spotless (importOrder) | error-prone UnusedVariable | spotbugs + checkstyle | @Nullable/@NonNull strict | pmd CyclomaticComplexity | spotbugs (FindSecBugs) |
| Kotlin | ktfmt / ktlint | ktlint (import-ordering) | detekt UnusedImports | detekt | (compile + -Werror) | detekt complexity rules | detekt (detekt-formatting) |
| Rust | cargo fmt (rustfmt) | rustfmt (built-in) | cargo +nightly udeps | cargo clippy -- -D warnings | (compile = check) | clippy cognitive_complexity | cargo audit |
| Vue/Nuxt | prettier + biome | biome | eslint-plugin-vue + ts-unused-exports | eslint-plugin-vue + biome | tsc + vue-tsc | eslint complexity | npm audit |
| PHP | php-cs-fixer / pint | php-cs-fixer (ordered_imports) | rector dead-code | phpstan / psalm | phpstan strict | phpmd | composer audit |
Rule: ใช้ all-in-one tool (Ruff / Biome / golangci-lint) ดีกว่า stitch หลาย tool — boundary error น้อย, config เดียว
Gate 1: Format
- Auto-format on save (IDE) + pre-commit hook + CI gate (3 จุด)
- ห้าม: manual format / "ไม่ตรง project standard but readable"
Gate 2: Organize Imports (🔴 v3.1 split out)
- Sort + group: stdlib → 3rd-party → local
- Alpha-sort within group
- ห้าม wildcard import (
from x import */import *) - ห้าม relative
..import เกิน 1 level
Gate 3: Remove Unused (🔴 v3.1 split out)
- Unused import → ลบ (F401 / no-unused-vars)
- Unused local variable → ลบ (F841)
- Unused function parameter → ลบ หรือ prefix
_ถ้าจำเป็นต้องเก็บ signature - Unused function/class export → ลบหรือ mark internal (ts-unused-exports / vulture)
- ห้าม
# noqa/// eslint-disableโดยไม่ comment "why" + bd track
Deferred-shortcut convention (จาก ponytail — ทางลัดที่ YAGNI ladder ตัดไว้):
- รูปแบบบังคับ:
shortcut(bd:<id>): <reason>; upgrade → <path> - ตัวอย่าง:
# shortcut(bd:42): in-memory dict; upgrade → Redis เมื่อ >10k key grep -rn 'shortcut(bd' .//review --debtรวบเป็น ledger → "later" ไม่กลาย "never"- ห้าม shortcut โดยไม่มี bd id (ต้อง track ได้)
Gate 4: Lint (strict — diagnose)
- ดู per-language matrix; เปิด strict rule set ทั้งหมด ไม่ใช่ default ที่อ่อน
- Lint warning = bd issue (track หรือ fix); ห้าม ignore
Gate 5: Type Check (🔴 strict)
- Py:
mypy --strict— ห้ามAnyเลี่ยงได้ - TS:
tsc --strict --noUncheckedIndexedAccess— ห้ามany, ใช้unknown+ narrow - Java: explicit null annotation (
@Nullable/@NonNull) - ห้าม
# type: ignore/// @ts-ignore/@SuppressWarningsโดยไม่ ticket
Gate 6: Complexity
- Cyclomatic ≤ 10 / function
- Cognitive ≤ 15 / function
- Function ≤ 50 บรรทัด (≤ 30 ดีกว่า)
- File ≤ 500 บรรทัด
- Nesting ≤ 3 (early return / guard clause แทน)
Gate 7: Naming
- Variable:
nounที่บอก what (ไม่ใช่data/info/temp) - Function:
verb_noun(calculate_total) - Boolean:
is_*/has_*/should_*/can_* - Constant:
UPPER_SNAKE - ห้าม abbreviation ที่ไม่เป็นมาตรฐาน
- ห้าม magic number/string → constant + comment "why"
Gate 8: Test
- Unit coverage ≥ 80% business logic (Chris's responsibility; Dave smoke ก่อน hand-off)
- AAA pattern + G-W-T naming
- Edge case + error path
- ห้าม skipped/disabled test ไม่มี bd track
Gate 9: Security Lint (🔴 v3.1 added)
- SAST per language (ดู matrix Gate 9 column)
- Secret scan (gitleaks /
git-secrets) — block commit ที่มี API key / password / cert - Dependency audit (
npm audit/pip-audit/cargo audit) — block critical/high vulns - ห้าม: ignore security warning โดยไม่ Sentinel approve
Gate 10: Doc
- Docstring/JSDoc สำหรับ public API (signature + example + edge case)
- Inline comment เฉพาะ "why" ไม่ใช่ "what"
- README update ถ้า API change
Pre-commit hook (🔴 v3.1 — บังคับทุก repo)
ติดตั้ง pre-commit (pre-commit.com) + .pre-commit-config.yaml:
# .pre-commit-config.yaml — Python project example
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.0
hooks:
- id: ruff-format # Gate 1
- id: ruff # Gate 2+3+4 (--fix organize+unused+lint)
args: [--fix, --exit-non-zero-on-fix]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.0
hooks:
- id: mypy # Gate 5
args: [--strict]
- repo: https://github.com/pycqa/bandit
rev: 1.7.9
hooks:
- id: bandit # Gate 9
args: [-c, pyproject.toml]
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.4
hooks:
- id: gitleaks # Gate 9 secret scan
# .pre-commit-config.yaml — TS/JS project example
repos:
- repo: https://github.com/biomejs/pre-commit
rev: v0.5.0
hooks:
- id: biome-check # Gate 1+2+3+4 (format + imports + unused + lint)
args: [--apply]
- repo: local
hooks:
- id: tsc
name: TypeScript strict
entry: npx tsc --noEmit --strict
language: system
types: [ts]
- id: ts-unused-exports
name: Unused exports
entry: npx ts-unused-exports tsconfig.json
language: system
pass_filenames: false
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.4
hooks:
- id: gitleaks
Setup steps:
pip install pre-commit # หรือ brew install pre-commit
pre-commit install # ติด git hook
pre-commit run --all-files # รัน lint ทั้ง repo ครั้งแรก
ทุก git commit จะถูก block ถ้า gate ใด fail. ห้าม --no-verify (bypass) ใน production code
Quality Smells (🚫 reject)
| Smell | Why bad | Fix |
|---|---|---|
| God class (> 500 lines) | hard to test/change | extract module |
| Long parameter list (> 4) | hard to read | parameter object |
| Duplicate code | DRY violation | extract function |
| Deep nesting (> 3) | cognitive load | early return / guard clause |
| Magic number/string | meaning unclear | named constant |
| Comment explaining hack | code smell | refactor + remove comment |
try ... pass | swallow error | log + re-raise / handle |
| Unused import / variable | dead code | ลบ ห้าม keep "for later" |
Wildcard import (from x import *) | namespace pollution | explicit import |
Pre-Push Checklist (🔴 v3.1.1 — all 11 gates)
# Gate 0 — Architecture self-check (Dave answers each checkbox above ก่อนรัน mechanical gates)
make fmt # Gate 1 — auto-format
make imports # Gate 2 — organize imports
make clean-unused # Gate 3 — remove unused (CI auto-fail if anything found)
make lint # Gate 4 — strict lint
make typecheck # Gate 5 — strict types (no Any/any)
make complexity # Gate 6 — cyclomatic + size limits
make test # Gate 8 — unit + coverage
make security # Gate 9 — SAST + secret scan + dep audit
make doc-check # Gate 10 — public API docstring present
หรือเรียก make pre-push ที่รวมทุก mechanical gate ในคำสั่งเดียว:
# Makefile snippet
pre-push: fmt imports clean-unused lint typecheck complexity test security doc-check
@echo "✅ Gates 1-10 pass — Dave: confirm Gate 0 self-check before push"
ทุก check ผ่าน → ค่อย push. CI ก็ต้องรันชุดเดียวกัน (pre-commit + GitHub Actions / GitLab CI / CircleCI)
Gate 0 ไม่อยู่ใน Makefile — ตั้งใจให้ Dave หยุดคิด 30 วินาทีก่อน push, ไม่ใช่ auto-pass. มันคือ judgment ไม่ใช่ tool
Hand-off
Dave ▸ Chris : impl + smoke (dev-gate passed)
Chris ▸ Quinn : 7-dim + unit ≥ 80% + mutation ≥ 70%
ห้าม
- ห้ามเขียน code ก่อน test (TDD core)
- ห้าม commit test ที่ยัง fail (ใช้ skip/xfail + bd issue)
- ห้าม refactor พร้อม add behavior — แยก commit
- ห้าม mock business logic — mock เฉพาะ external (DB/API/clock)
- ห้ามใช้
time.sleepใน test → fake time/freeze - ห้าม disable lint rule โดยไม่ comment + bd track
- ห้าม
// @ts-ignore/# type: ignoreโดยไม่ ticket - ห้าม commit
console.log/printdebug - ห้ามใช้
any(TS) /Any(Py) เป็นทางลัด - ห้าม PR ที่ลด coverage (CI ตั้ง gate, ratchet)
- ห้าม merge code ที่ build แดง
Skill composition (where to go next)
| Situation | Next skill | Reason |
|---|---|---|
| Test pass แต่ยังไม่มี CI gate | → automate-test | Pyramid ratio + CI threshold + contract test (dev-gate = per-task; automate-test = project-wide) |
| Code touches frontend | → ui-test | E2E + visual + a11y automation (dev-gate ไม่ครอบ visual) |
| Frontend public-facing (perf/SEO/security) | → web-q | CWV + Lighthouse + security headers budget |
| Touch security control (auth/crypto/PII) | → secure | Sentinel threat model + abuse case (dev-gate ไม่ classify threat) |
| Hand-off Phase 2 → 3b review | → review-checklist skill | Chris 7-dim + Quinn integration matrix (used by /implement Phase 3b + /review) |