Skill 寫法圖鑑

delivery-gate

affaan-m | 安全護欄 | 原文 | MIT

Stop hook,磁碟不足或學習紀錄過期時機械式擋住 Claude 收工

中文翻譯

---
name: delivery-gate
description: 一個 Stop hook,在品質檢查通過之前,擋住 Claude 結束工作。會偵測合理化的說法(表面文字的啟發式判斷)、過期的學習紀錄(檔案系統 mtime)和磁碟空間不足。透過機械式地強制執行學習記錄的習慣,來補足 self-audit。當要在品質檢查和學習記錄真的通過之前,機械式地擋住 Claude 宣稱工作完成時使用。
metadata:
  version: 1.1.1
  origin: ECC
---

# Delivery Gate:給 Claude Code 的機械式品質關卡

一個 **Stop hook**,在 Claude 結束 session 之前檢查三件事,只用**確定性的檢查**:檔案修改時間戳、磁碟用量,以及對 transcript 文字做的 regex 比對。沒有 AI 推論。

這跟推理型的關卡(像 `self-audit`)不同:delivery-gate 檢查機器可以驗證的事實;self-audit 則是從四個推理面向檢查輸出品質。兩者合起來就是縱深防禦:
- **delivery-gate**:「今天有動過學習資料庫嗎?磁碟空間安全嗎?」
- **self-audit**:「檔案內容正確、完整而且誠實嗎?」

這跟 CI pipeline 關卡是同一種模式:自動、確定性的檢查,驗證機器可讀的事實,而不是相信自己回報的狀態。

## 它檢查什麼

| 檢查 | 機制 | 命中時 |
|-------|-----------|--------|
| 合理化的說法 | 對 transcript 尾端做 regex | **只警告**(絕不阻擋) |
| 過期的學習資料庫 | 5 個可設定路徑的 mtime | 部分過期就警告;在複雜任務中,>=3 個過期,或 growth-log 過期,就**阻擋** |
| 磁碟空間 < 50GB | `shutil.disk_usage` | 提醒 |
| 磁碟空間 < 30GB | `shutil.disk_usage` | 警告 |
| 磁碟空間 < 15GB | `shutil.disk_usage` | **阻擋**(exit 2),每次 Stop 都阻擋 |

合理化偵測會對「skip tests for now」和「pre-existing bug」這類說法發出警告,它們是思考可能被草草打住的表面訊號。它自己絕不會阻擋,因為 regex 的啟發式判斷可能誤報。會阻擋的條件是:磁碟到了危急程度(任何 session),或者在複雜任務(>=3 次 Edit/Write 呼叫)中,`>=3 個學習資料庫過期`,或 `growth-log` 特別過期。如果專案的記憶目錄不存在,就跳過學習資料庫的檢查:hook 只會警告它無法驗證學習記錄,複雜任務不會被阻擋(磁碟檢查仍然適用)。

hook 對同一次 Stop 最多只阻擋一次。當 Claude Code 帶著 `stop_hook_active: true` 重新執行它(Claude 已經因為這個 hook 繼續過一次),它就 exit 0,所以 agent 修不好的狀況(例如磁碟空間不足)不會把 session 困在無窮迴圈裡。

## 為什麼

Claude Code 內建的檢查涵蓋程式碼品質(build → type → lint → test)。但還有一種不同的失敗模式:agent 產出了可以運作的程式碼,但 **session 的衛生被忽略了**:沒有記錄學習、有被合理化的捷徑、磁碟默默被用光。

在很多次「出貨就忘」的 session 之後,人並沒有成長。這個 hook 強制執行這個習慣:複雜任務 → 必須動到學習資料庫。

## 安裝

從 ECC repository 的根目錄:

```bash
mkdir -p ~/.claude/scripts
cp skills/delivery-gate/hooks/quality-gate.py ~/.claude/scripts/
```

加到 `~/.claude/settings.json`:
```json
{
  "hooks": {
    "Stop": [{
      "hooks": [{
        "type": "command",
        "command": "python3 ~/.claude/scripts/quality-gate.py",
        "timeout": 5
      }]
    }]
  }
}
```

## 學習資料庫

在你專案的記憶目錄(`~/.claude/projects/<encoded-project-path>/memory/`,專案路徑中每個非英數字元都會變成 `-`)裡建立這些檔案。hook 會檢查其中哪些在今天更新過:

```
memory/
├── growth-log/          # Daily learning entries (directory)
├── decisions/log.md     # Decision log
├── output-index.md      # Index of session outputs
├── ratings-tracker.md   # Skill ratings over time
└── tooling_capabilities.md  # Known tools inventory
```

自訂 `LIBS` dict 來符合你自己的檔案結構。

## 設定

編輯 `quality-gate.py`:

| 變數 | 預設值 | 用途 |
|----------|---------|---------|
| `RATIONALIZE` | 4 個模式 | 偵測合理化說法的 regex 模式 |
| `LIBS` | 5 個資料庫 | 要檢查今天是否有更新的檔案/目錄 |
| `COMPLEX_THRESHOLD` | 3 | 判定為複雜任務所需的 Edit/Write 呼叫次數 |
| `DISK_REMIND_GB` | 50 | 低於這個值就提醒 |
| `DISK_WARN_GB` | 30 | 低於這個值就警告 |
| `DISK_CRIT_GB` | 15 | 低於這個值就阻擋 |

## 範例

**簡單的 session,允許:**
```
edit_count=1 (< 3, not complex) → exit 0
```

**複雜任務,有記錄學習,允許:**
```
edit_count=5 (complex) → checks LIBS → growth-log + 2 others updated today (2 stale) → exit 0
```

**複雜任務,沒有學習,阻擋:**
```
edit_count=4 (complex) → checks LIBS → all 5 stale → exit 2
stderr: "Blocked: complex task but >=3 learning libs stale."
```

**磁碟空間不足,阻擋:**
```
disk_free=12GB < 15GB critical → exit 2
stderr: "Blocked: disk space at 12GB (<15GB). Free space before continuing."
```

## 限制

這個 hook 強制執行的是動到學習資料庫的**習慣**,不是所記錄內容的**品質**。在複雜任務中,只有 `growth-log` 今天有更新,而且過期的資料庫最多 2 個,才會通過;光更新 `output-index.md` 仍然會被阻擋。沒有專案記憶目錄的話就沒東西可以檢查,所以只會警告。這是刻意的設計:機械式的關卡檢查的是機器可以驗證的事實。要驗證內容品質,要搭配 `self-audit`。

## 相容性

- Python 3.8+(使用 `from __future__ import annotations`)
- 跨平台:Windows、macOS、Linux
- 除了標準函式庫之外沒有任何相依套件

## 品質

這份程式碼經過 4 輪自動化 code review(CodeRabbit + Greptile),找出並修好了 9 個真正的 bug。

## 另見

- `self-audit`:推理品質關卡(完整性/一致性/有根據/誠實)
- `verification-loop`:程式碼品質檢查(build/type/lint/test)
- `gateguard`:PreToolUse 的安全關卡

英文原文

---
name: delivery-gate
description: Stop hook that blocks Claude from finishing until quality checks pass. Detects rationalization patterns (surface text heuristics), stale learning logs (filesystem mtime), and low disk space. Complements self-audit by mechanically enforcing learning capture habits. Use when Claude should be mechanically blocked from declaring work finished before quality checks and learning capture actually pass.
metadata:
  version: 1.1.1
  origin: ECC
---

# Delivery Gate — Mechanical Quality Gate for Claude Code

A **Stop hook** that checks three things before Claude can finish a session, using only **deterministic checks** — file modification timestamps, disk usage, and regex patterns on the transcript text. No AI inference.

This is distinct from reasoning gates (like `self-audit`): delivery-gate checks machine-verifiable facts; self-audit checks output quality across four reasoning dimensions. Together they form defense in depth:
- **delivery-gate**: "Was the learning library touched today? Is disk space safe?"
- **self-audit**: "Is the file content correct, complete, and honest?"

This is the same pattern as CI pipeline gates — automated, deterministic checks that verify machine-readable facts rather than trusting self-reported status.

## What It Checks

| Check | Mechanism | On Hit |
|-------|-----------|--------|
| Rationalization patterns | Regex on transcript tail | **Warning only** (never blocks) |
| Stale learning libraries | mtime on 5 configurable paths | Warning if some stale; **Block** on a complex task if >=3 stale OR growth-log stale |
| Disk space < 50GB | `shutil.disk_usage` | Reminder |
| Disk space < 30GB | `shutil.disk_usage` | Warning |
| Disk space < 15GB | `shutil.disk_usage` | **Block** (exit 2), on every Stop |

Rationalization detection warns about patterns like "skip tests for now" and "pre-existing bug" — surface signals that thinking may have been cut short. It never blocks on its own, because regex heuristics can false-positive. The blocking conditions are: disk critical (any session), or, on a complex task (>=3 Edit/Write calls), `>=3 learning libs stale` OR `growth-log` specifically stale. If the project memory directory does not exist, the learning-library checks are skipped: the hook only warns that it cannot verify learning capture, and a complex task is not blocked (the disk checks still apply).

The hook blocks a given Stop at most once. When Claude Code re-runs it with `stop_hook_active: true` (Claude already continued once because of this hook), it exits 0, so a condition the agent cannot fix (such as low disk space) cannot trap the session in an endless loop.

## Why

Claude Code's built-in checks cover code quality (build → type → lint → test). But there's a different failure mode: the agent produces working code while the **session hygiene was neglected** — learning not captured, rationalized shortcuts, disk running out silently.

Over many sessions of "ship and forget," the human hasn't grown. This hook enforces the habit: complex task → must touch learning libraries.

## Install

From the ECC repository root:

```bash
mkdir -p ~/.claude/scripts
cp skills/delivery-gate/hooks/quality-gate.py ~/.claude/scripts/
```

Add to `~/.claude/settings.json`:
```json
{
  "hooks": {
    "Stop": [{
      "hooks": [{
        "type": "command",
        "command": "python3 ~/.claude/scripts/quality-gate.py",
        "timeout": 5
      }]
    }]
  }
}
```

## Learning Libraries

Create these files in your project's memory directory (`~/.claude/projects/<encoded-project-path>/memory/`, where every non-alphanumeric character of the project path becomes `-`). The hook checks which of them were updated today:

```
memory/
├── growth-log/          # Daily learning entries (directory)
├── decisions/log.md     # Decision log
├── output-index.md      # Index of session outputs
├── ratings-tracker.md   # Skill ratings over time
└── tooling_capabilities.md  # Known tools inventory
```

Customize the `LIBS` dict to match your own file structure.

## Configuration

Edit `quality-gate.py`:

| Variable | Default | Purpose |
|----------|---------|---------|
| `RATIONALIZE` | 4 patterns | Regex patterns for rationalization detection |
| `LIBS` | 5 libraries | Files/dirs to check for today's updates |
| `COMPLEX_THRESHOLD` | 3 | Edit/Write calls to classify as complex |
| `DISK_REMIND_GB` | 50 | Remind below this |
| `DISK_WARN_GB` | 30 | Warn below this |
| `DISK_CRIT_GB` | 15 | Block below this |

## Examples

**Simple session — allowed:**
```
edit_count=1 (< 3, not complex) → exit 0
```

**Complex task, learning captured — allowed:**
```
edit_count=5 (complex) → checks LIBS → growth-log + 2 others updated today (2 stale) → exit 0
```

**Complex task, no learning — BLOCKED:**
```
edit_count=4 (complex) → checks LIBS → all 5 stale → exit 2
stderr: "Blocked: complex task but >=3 learning libs stale."
```

**Low disk space — BLOCKED:**
```
disk_free=12GB < 15GB critical → exit 2
stderr: "Blocked: disk space at 12GB (<15GB). Free space before continuing."
```

## Limitations

The hook enforces the **habit** of touching learning libraries, not the **quality** of what was recorded. On a complex task it passes only when `growth-log` was updated today and at most 2 libraries are stale; updating `output-index.md` alone still blocks. Without a project memory directory there is nothing to check, so it only warns. This is by design: mechanical gates check machine-verifiable facts. For content quality verification, pair with `self-audit`.

## Compatibility

- Python 3.8+ (uses `from __future__ import annotations`)
- Cross-platform: Windows, macOS, Linux
- Zero dependencies beyond stdlib

## Quality

This code went through 4 rounds of automated code review (CodeRabbit + Greptile) with 9 real bugs found and fixed.

## See Also

- `self-audit` — Reasoning quality gate (completeness/consistency/groundedness/honesty)
- `verification-loop` — Code quality checks (build/type/lint/test)
- `gateguard` — PreToolUse safety gate