單元測試與代碼提交前觸發。掃描並驗證方法的 pre-conditions、post-conditions 與 invariants,透過契約式設計減少 AI 幻覺。
在编写单元测试或提交代码前,自动扫描并验证方法的前置条件、后置条件和不变量,通过契约式设计来减少AI生成代码时的幻觉问题。
/plugin marketplace add knowlet/skills/plugin install knowlet-skills@knowlet/skillsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
analyze-frame 產出的規格時透過 Design by Contract 明確定義每個方法的邊界條件,極大化減少 AI 幻覺。
/**
* 建立新訂單
*
* @param input 建立訂單的輸入參數
* @return 建立成功的訂單資訊
*
* @pre input != null
* @pre input.getCustomerId() != null
* @pre input.getItems() != null && !input.getItems().isEmpty()
* @pre 所有 items 的 quantity > 0
* @pre 所有 items 的 productId 對應的商品存在
*
* @post result != null
* @post result.getOrderId() != null
* @post result.getStatus() == OrderStatus.CREATED
* @post 訂單已持久化到資料庫
* @post OrderCreatedEvent 已發布
*
* @throws CustomerNotFoundException 當 customerId 對應的客戶不存在
* @throws ProductNotFoundException 當 productId 對應的商品不存在
* @throws InsufficientInventoryException 當庫存不足
*/
public Output execute(Input input) {
// 實作
}
public Output execute(Input input) {
// ===== Pre-conditions =====
Objects.requireNonNull(input, "input must not be null");
Objects.requireNonNull(input.getCustomerId(), "customerId must not be null");
if (input.getItems() == null || input.getItems().isEmpty()) {
throw new IllegalArgumentException("items must not be empty");
}
for (OrderItemRequest item : input.getItems()) {
if (item.getQuantity() <= 0) {
throw new IllegalArgumentException(
"quantity must be positive, got: " + item.getQuantity()
);
}
}
// ===== 主要邏輯 =====
// ...
// ===== Post-conditions (assert in development) =====
assert result != null : "result must not be null";
assert result.getOrderId() != null : "orderId must not be null";
return result;
}
public class Order {
private OrderId id;
private CustomerId customerId;
private List<OrderItem> items;
private OrderStatus status;
private Money totalAmount;
/**
* Order 的不變量:
* @invariant id != null
* @invariant customerId != null
* @invariant items != null && !items.isEmpty()
* @invariant totalAmount != null && totalAmount.isPositive()
* @invariant status != null
* @invariant 當 status == CANCELLED 時,不能再修改訂單內容
*/
// 建構子必須建立有效狀態
public Order(OrderId id, CustomerId customerId, List<OrderItem> items) {
// Pre-conditions
Objects.requireNonNull(id, "id must not be null");
Objects.requireNonNull(customerId, "customerId must not be null");
if (items == null || items.isEmpty()) {
throw new IllegalArgumentException("items must not be empty");
}
this.id = id;
this.customerId = customerId;
this.items = new ArrayList<>(items);
this.status = OrderStatus.CREATED;
this.totalAmount = calculateTotal();
// 驗證 invariants
assertInvariants();
}
public void addItem(OrderItem item) {
// Pre-conditions
Objects.requireNonNull(item, "item must not be null");
if (this.status == OrderStatus.CANCELLED) {
throw new IllegalStateException("Cannot modify cancelled order");
}
// 執行變更
this.items.add(item);
this.totalAmount = calculateTotal();
// Post-conditions & Invariants
assertInvariants();
}
public void cancel() {
// Pre-conditions
if (this.status == OrderStatus.SHIPPED) {
throw new IllegalStateException("Cannot cancel shipped order");
}
// 執行變更
this.status = OrderStatus.CANCELLED;
// Invariants
assertInvariants();
}
private void assertInvariants() {
assert id != null : "Invariant violated: id is null";
assert customerId != null : "Invariant violated: customerId is null";
assert items != null && !items.isEmpty() : "Invariant violated: items is empty";
assert totalAmount != null && totalAmount.isPositive() :
"Invariant violated: totalAmount is invalid";
assert status != null : "Invariant violated: status is null";
}
}
| 項目 | 描述 | 嚴重度 |
|---|---|---|
| Null Check | 所有物件參數是否有 null 檢查 | 🔴 嚴重 |
| Empty Collection | 集合參數是否檢查 empty | 🟡 中度 |
| Positive Numbers | 數量、金額等是否檢查正數 | 🟡 中度 |
| Valid State | 狀態轉換是否合法 | 🔴 嚴重 |
| Return Value | 回傳值是否可能為 null | 🟡 中度 |
contract_rules:
pre_conditions:
- rule: null_check_for_objects
description: "物件型別參數必須有 null 檢查"
pattern: "public.*\\(.*[A-Z]\\w+\\s+\\w+"
check: "Objects.requireNonNull|!= null"
- rule: empty_check_for_collections
description: "集合型別必須檢查是否為空"
applies_to: ["List", "Set", "Collection"]
check: "isEmpty()|!.*\\.isEmpty()"
- rule: positive_check_for_quantities
description: "數量類型必須檢查大於零"
applies_to: ["quantity", "amount", "count", "size"]
check: "> 0|>= 1|isPositive"
post_conditions:
- rule: non_null_return
description: "標註 @NonNull 的回傳值必須確保不為 null"
- rule: state_consistency
description: "狀態變更後 invariants 必須成立"
invariants:
- rule: aggregate_validity
description: "Aggregate 必須定義 assertInvariants() 方法"
applies_to: "Aggregate"
class CreateOrderUseCaseTest {
// ===== Pre-condition 測試 =====
@Test
@DisplayName("當 input 為 null 時,應拋出 NullPointerException")
void should_throw_when_input_is_null() {
// Given
CreateOrderUseCase useCase = createUseCase();
// When & Then
assertThrows(NullPointerException.class, () -> {
useCase.execute(null);
});
}
@Test
@DisplayName("當 items 為空時,應拋出 IllegalArgumentException")
void should_throw_when_items_is_empty() {
// Given
Input input = new Input(customerId, Collections.emptyList(), address);
// When & Then
assertThrows(IllegalArgumentException.class, () -> {
useCase.execute(input);
});
}
// ===== Post-condition 測試 =====
@Test
@DisplayName("成功建立訂單後,應回傳有效的 OrderId")
void should_return_valid_orderId_on_success() {
// Given
Input input = createValidInput();
// When
Output output = useCase.execute(input);
// Then - 驗證 post-conditions
assertNotNull(output);
assertNotNull(output.getOrderId());
assertEquals(OrderStatus.CREATED, output.getStatus());
}
@Test
@DisplayName("成功建立訂單後,應發布 OrderCreatedEvent")
void should_publish_event_on_success() {
// Given
Input input = createValidInput();
// When
useCase.execute(input);
// Then - 驗證 post-condition
verify(eventPublisher).publish(any(OrderCreatedEvent.class));
}
}
透過契約式設計,可以有效減少 AI 幻覺:
契約完整度 ∝ 1 / AI 幻覺發生率
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.