agentskills Standard · Phase 2: Deterministic Tooling

Day 5: The scripts/ Directory & CLI Execution

Developing standalone bash/python validation runners executed by Host Agents (GitHub Copilot, Cursor, Claude Code)

🗺️ 1. System Blueprint & Daily Integration

Where are we? ใน Phase 1 (Day 1-4) เราได้สร้าง Frontmatter Metadata, Semantic Router Description, Negative Boundaries (RFC 2119), และ Few-shot Exemplars ภายใน SKILL.md สำเร็จ ทำให้ LLM เข้าใจขอบเขตของภาษาและโครงสร้างโค้ดโดยสมบูรณ์

What is missing? แม้ LLM จะมี In-context Constraints แต่ตัวมันคือ Probabilistic Engine ซึ่งไม่สามารถรัน Compiler, ไม่สามารถตรวจ Abstract Syntax Tree จริง, และไม่สามารถการันตีความถูกต้อง 100% ได้ด้วยตัวอักษรเพียงอย่างเดียว การพึ่งพา "Prompting เพื่อตรวจโค้ด" คือช่องโหว่ความน่าเชื่อถือ วันนี้เราจึงก้าวเข้าสู่ Phase 2 โดยสร้าง Execution Layer ผ่านไดเรกทอรี scripts/ ซึ่งประกอบด้วย Deterministic Verification Runners ที่ Host Agent สามารถเรียกทำงานผ่าน CLI สั่ง Compile และตรวจสอบโค้ดก่อนส่งมอบ

.github/skills/spring-boot-unit-tester/ ├── SKILL.md <- Metadata, Constraints, Exemplars (Phase 1) ├── scripts/ <- [TODAY'S INTEGRATION: DETERMINISTIC TOOLING] │ ├── run-unit-tests.sh <- Host CLI runner: maven verification & mock check │ └── verify-spring-context-leak.py <- Fast AST/Token linter to halt heavy context boot ├── references/ <- DTO schemas & contracts (Phase 3) └── assets/ <- Diagrams & templates [Host Agent Lifecycle Flow] User Request ──> Semantic Match (SKILL.md) ──> LLM Generates Test ▲ │ │ [Fail: Non-Zero Exit] ▼ └────── Stderr Feedback Pipeline ◄────── CLI: scripts/run-unit-tests.sh │ ▼ [Pass: Exit 0] Return Code to User

⚙️ 2. Architectural Deep Dive: Concept & Contracts

ตามมาตรฐาน agentskills Standard สคริปต์ในไดเรกทอรี scripts/ ไม่ใช่เพียง helper สุ่มๆ ในโปรเจกต์ แต่คือ Deterministic Verifiers ที่ทำหน้าที่เป็น Pre-commit Gate ให้กับ Host Agent การออกแบบสคริปต์ระดับ Enterprise จะต้องปฏิบัติตามสัญญาการทำงาน (Execution Contracts) ที่เคร่งครัด

Core Analogy: The Hardware Watchdog:
หาก LLM เปรียบเสมือน Software Logic ที่มีความยืดหยุ่นสูง CLI Script ใน scripts/ ก็คือ Hardware Watchdog Timer ที่ไม่สนใจข้ออ้างหรือคำอธิบาย หากโปรแกรมละเมิด Invariant หรือ Compile ไม่ผ่าน สคริปต์จะตัดวงจร (Exit code != 0) และโยน Stderr กลับเข้าไปใน Context ทันที

1. Execution Environment & Sandboxing Contracts

2. Production Trade-offs: In-Context Prompting vs. CLI Scripts

มิติการประเมิน In-Context Prompting (LLM Self-Check) Deterministic Scripts (scripts/)
Deterministic Guarantee ต่ำ (Probabilistic: มีโอกาสตอบว่าถูกทั้งที่ syntax ผิด) 100% (Binary: Compile ผ่านหรือไม่ผ่าน)
Token Consumption สูงมาก (ต้องป้อนโค้ดกลับเข้าไปถามซ้ำ สิ้นเปลือง Context Window) ต่ำมาก (กิน Token เฉพาะ stdout/stderr ที่ส่งกลับมา)
Execution Speed ช้า (ขึ้นกับ Token generation rate ของโมเดล) เร็วมาก (Native process runtime: Bash/Native AST)
Feedback Accuracy คลุมเครือ ("I think this looks correct...") ชัดเจน (Stack Trace, Line Number, Compilation Error)

🧩 3. End-to-End Code Blueprint

1. Host Contract Registration: .github/skills/spring-boot-unit-tester/SKILL.md

อัปเดต SKILL.md เพื่อแจ้ง Host Agent ว่ามี Deterministic Tool ให้เรียกใช้งาน

---
name: spring-boot-unit-tester
description: Generates isolated Spring Boot unit tests using Kotlin, MockK, and AssertJ. MUST be used for unit testing service layers.
version: 1.2.0
tools:
  - name: run-unit-tests
    description: Executes local project verification script to validate test compilation, MockK conventions, and isolation.
    file: scripts/run-unit-tests.sh
---

## Tool Execution Rules
1. After generating or refactoring any unit test, the Host Agent MUST execute the validation script:
   `bash .github/skills/spring-boot-unit-tester/scripts/run-unit-tests.sh `
2. If the script exits with status code != 0, inspect stderr, fix the violations, and re-run until it passes.
3. NEVER return a test implementation to the user without a successful (exit code 0) verification pass.

2. Standalone Deterministic Runner: .github/skills/spring-boot-unit-tester/scripts/run-unit-tests.sh

#!/usr/bin/env bash
# ==============================================================================
# Script: run-unit-tests.sh
# Standard: agentskills Standard v1.0 (Phase 2 Deterministic Tooling)
# Purpose: Validates MockK syntax, checks context pollution, runs Maven test.
# Exit Codes:
#   0: Success - All checks passed
#   1: Missing/Invalid Input Arguments
#   2: Architectural Constraint Violation (AST/Regex Check)
#   3: Build/Compilation Failure (Maven)
# ==============================================================================

set -euo pipefail

# 1. Parameter Validation
TARGET_FILE="${1:-}"

if [[ -z "${TARGET_FILE}" ]]; then
    echo "[ERROR] Missing target test file argument." >&2
    echo "Usage: $0 <path/to/TargetTest.kt>" >&2
    exit 1
fi

if [[ ! -f "${TARGET_FILE}" ]]; then
    echo "[ERROR] Target file '${TARGET_FILE}' does not exist." >&2
    exit 1
fi

echo "[INFO] Commencing deterministic inspection for: ${TARGET_FILE}"

# 2. Fast Static Invariant Linter (Negative Constraints)
# // [TODAY'S LESSON PLUGS IN HERE - SHELL VALIDATION GATES]
VIOLATIONS_FOUND=0

# Rule A: Check for forbidden Mockito imports
if grep -En "org\.mockito" "${TARGET_FILE}" >&2; then
    echo "[VIOLATION] Forbidden Mockito import detected. Use io.mockk instead." >&2
    VIOLATIONS_FOUND=$((VIOLATIONS_FOUND + 1))
fi

# Rule B: Check for forbidden Spring Boot Context Loading in pure Unit Tests
if grep -En "@SpringBootTest|@DataJpaTest|@WebMvcTest" "${TARGET_FILE}" >&2; then
    echo "[VIOLATION] Spring ApplicationContext runner detected. Pure unit tests MUST NOT use Spring test runners." >&2
    VIOLATIONS_FOUND=$((VIOLATIONS_FOUND + 1))
fi

# Rule C: Enforce confirmVerified on MockK dependencies
if ! grep -q "confirmVerified(" "${TARGET_FILE}"; then
    echo "[WARNING] No 'confirmVerified(...)' statement found. Host should verify all mocks." >&2
fi

if [[ ${VIOLATIONS_FOUND} -gt 0 ]]; then
    echo "[FAILED] Static validation gates failed with ${VIOLATIONS_FOUND} critical violation(s)." >&2
    exit 2
fi

echo "[SUCCESS] Static architectural invariant checks passed."

# 3. Dynamic Compilation & Targeted Test Execution
# Extract Class Name for Maven targeted runner
CLASS_NAME=$(basename "${TARGET_FILE}" .kt)

echo "[INFO] Running Maven targeted compilation for class: ${CLASS_NAME}"

# Execute Maven in non-interactive batch mode, suppressing progress noise
if ! mvn test -B --no-transfer-progress -Dtest="${CLASS_NAME}" > /tmp/mvn_test_output.log 2>&1; then
    echo "[FAILED] Maven compilation or test execution failed." >&2
    echo "---------------- Maven Output Diagnostics ----------------" >&2
    # Filter error messages to conserve agent context window
    grep -E "ERROR|FAILURE|Compilation failure|\[ERROR\].*:[0-9]+" /tmp/mvn_test_output.log >&2 || cat /tmp/mvn_test_output.log | tail -n 30 >&2
    exit 3
fi

echo "[PASSED] Test compiled and passed successfully under standard Maven build."
exit 0

🛠️ 4. Hands-on Scenario & Lab

Enterprise Problem Statement:
Host Agent (เช่น Cursor หรือ GitHub Copilot) ถูกสั่งให้สร้าง Unit Test สำหรับ OrderSettlementService.kt แต่ Agent เผลอใส่ @SpringBootTest และนำเข้า org.mockito.Mockito.when ติดมาด้วย ส่งผลให้การรัน CI/CD ช้าลง 20 เท่า และเกิด Flaky Test ใน Pipeline

Task Requirements:

  1. สร้างไฟล์ OrderSettlementServiceTest.kt ที่จำลองข้อผิดพลาดดังกล่าว
  2. รันสคริปต์ตรวจสอบ scripts/run-unit-tests.sh เพื่อยืนยันว่าสคริปต์สามารถ Reject โค้ดที่ผิดพลาดด้วย Exit Code 2 ได้จริง
  3. Refactor โค้ดให้เป็น Pure MockK ตามข้อกำหนด แล้วรันซ้ำเพื่อรับ Exit Code 0

1. Input: Faulty Test File (src/test/kotlin/OrderSettlementServiceTest.kt)

package com.enterprise.trading.service

import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest // VIOLATION
import org.mockito.Mockito // VIOLATION

@SpringBootTest
class OrderSettlementServiceTest {
    @Test
    fun `should settle order successfully`() {
        val mockRepo = Mockito.mock(OrderRepository::class.java)
        // ...
    }
}

2. Execution & Verification Output

$ bash .github/skills/spring-boot-unit-tester/scripts/run-unit-tests.sh src/test/kotlin/OrderSettlementServiceTest.kt

[INFO] Commencing deterministic inspection for: src/test/kotlin/OrderSettlementServiceTest.kt
4:import org.mockito.Mockito // VIOLATION
[VIOLATION] Forbidden Mockito import detected. Use io.mockk instead.
3:@SpringBootTest
[VIOLATION] Spring ApplicationContext runner detected. Pure unit tests MUST NOT use Spring test runners.
[FAILED] Static validation gates failed with 2 critical violation(s).
$echo$?
2

3. Remediated Output (Passes Static & Dynamic Gates)

package com.enterprise.trading.service

import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import io.mockk.confirmVerified
import org.junit.jupiter.api.Test
import org.assertj.core.api.Assertions.assertThat

class OrderSettlementServiceTest {

    private val repository = mockk<OrderRepository>()
    private val service = OrderSettlementService(repository)

    @Test
    fun `should settle order successfully`() {
        // Given
        val orderId = "ORD-9921"
        every { repository.updateStatus(orderId, "SETTLED") } returns true

        // When
        val result = service.settle(orderId)

        // Then
        assertThat(result).isTrue()
        verify(exactly = 1) { repository.updateStatus(orderId, "SETTLED") }
        confirmVerified(repository)
    }
}

🎯 5. Interactive Architecture & Verification Quiz

ทดสอบความเข้าใจเชิงสถาปัตยกรรมด้าน Deterministic Tooling และ CLI Execution สัญญาณตรวจคำตอบจะทำงานทันทีที่ท่านเลือกตัวเลือก

Progress: 0 / 12 Solved
Score: 0 / 12