From 6772ca67d3ea0f026e9ad925134cf6919a19fd1c Mon Sep 17 00:00:00 2001 From: richblack Date: Fri, 21 Aug 2026 01:25:12 +0800 Subject: [PATCH] =?UTF-8?q?=E6=AF=8F=E5=80=8B=E9=87=8C=E7=A8=8B=E7=A2=91?= =?UTF-8?q?=E9=83=BD=E8=A6=81=E6=9C=89=E7=9C=9F=E7=9A=84=E6=9C=9F=E9=99=90?= =?UTF-8?q?=EF=BC=8C9999=20=E4=B9=9F=E6=93=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit leo 2026-08-21:「以後所有的 milestone 限制時間」「你根本沒有時間概念,浪費一整天」 實查七個 open milestone:六個期限是 9999-01-01、一個空白。 9999 比空白更糟——盤點時每一格看起來都有值, 於是沒有人發現這裡從來沒有時間壓力。七個已全部改成真日期。 新增 hooks/milestone-due-guard.sh,四向實測: 無 due_on → exit 2 due_on 帶 9999 → exit 2 真期限 → exit 0 只是讀 milestone → exit 0 規範補 M4.8(怎麼定期限、過期只對帳不自動關)。 Co-Authored-By: Claude Opus 5 --- .claude-plugin/plugin.json | 2 +- docs/governance/sdd-gitea-governance.md | 25 +++++++++++++++ hooks/hooks.json | 6 +++- hooks/milestone-due-guard.sh | 42 +++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 2 deletions(-) create mode 100755 hooks/milestone-due-guard.sh diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 461bf82..398b6be 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "isep", "description": "InkStone Environment Plugin —— leo 的 Claude Code 環境唯一真相源:43 支機械閘(53 條註冊,白話盤點見 docs/hooks-inventory.md)、7 支 slash command、2 支 skill、27 支腳本,外加治理規範與標籤真相源。本機與雲端裝同一份,沒有子集。", - "version": "0.3.2", + "version": "0.3.3", "keywords": [ "inkstone", "guardrails", diff --git a/docs/governance/sdd-gitea-governance.md b/docs/governance/sdd-gitea-governance.md index bf3e87d..b72cb39 100644 --- a/docs/governance/sdd-gitea-governance.md +++ b/docs/governance/sdd-gitea-governance.md @@ -677,3 +677,28 @@ Gitea 的 milestone 只管得到同一個 repo,所以六個群在 `inkstone/IS 結構(正交) hub ← tracking issue 標記 封存不刪 duplicate ← 由 close/duplicate 取代,保留在歷史票上 ``` + +### M4.8 每個里程碑都要有真的期限(leo 2026-08-21 立) + +> 「**以後所有的 milestone 限制時間**」/「**你根本沒有時間概念,浪費一整天**」 + +🔴 **`9999-01-01` 不算期限。** 立這條的當下實查七個 open milestone, +**六個的期限是 `9999-01-01`**——那是「沒有期限」穿了一件期限的衣服, +比空白更糟:盤點時每一格看起來都有值,於是沒有人發現這裡從來沒有時間壓力。 + +**怎麼定**:里程碑的 deliverable 是**一個可測的版本**(M4.0)。 +問一句「**這個版本幾號要能給 leo 打開?**」,那天就是期限。 + +| 剩幾張未結 | 期限 | +|---|---| +| 本週要收 | 三天 | +| 1–3 張 | 一週 | +| 4 張以上 | 兩週 | + +**過期了怎麼辦**:不自動關、不自動打 tag(M4.3 已否決那條)。 +過期只做兩件事——**對帳**(哪幾張沒動)與**通知**。 +期限的用途是製造節奏,不是製造假完成。 + +**機械閘**=`hooks/milestone-due-guard.sh`(PreToolUse `Bash`): +建 milestone 沒有 `due_on`、或 `due_on` 帶 `9999` → 擋。 +四向實測:無 due_on 擋/9999 擋/真期限放行/只是讀 milestone 放行。 diff --git a/hooks/hooks.json b/hooks/hooks.json index bf5d596..a187fae 100644 --- a/hooks/hooks.json +++ b/hooks/hooks.json @@ -40,6 +40,10 @@ { "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/ticket-api-bypass-guard.sh" + }, + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/milestone-due-guard.sh" } ] }, @@ -323,4 +327,4 @@ } ] } -} +} \ No newline at end of file diff --git a/hooks/milestone-due-guard.sh b/hooks/milestone-due-guard.sh new file mode 100755 index 0000000..83a5df1 --- /dev/null +++ b/hooks/milestone-due-guard.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# milestone-due-guard.sh — 建 milestone 一定要有真的期限(leo 2026-08-21 立) +# +# leo 原話:「**以後所有的 milestone 限制時間**」 +# 「**你根本沒有時間概念,浪費一整天**」 +# +# 🔴 為什麼連 9999 也要擋:立這條的當下實查七個 open milestone, +# 六個的期限是 `9999-01-01`——那是「沒有期限」穿了一件期限的衣服, +# 比空白更糟,因為它讓盤點時看起來每一格都有值。 +set -uo pipefail +INPUT=$(cat) +CMD=$(printf '%s' "$INPUT" | python3 -c "import json,sys;print(json.load(sys.stdin).get('tool_input',{}).get('command',''))" 2>/dev/null || echo "") +[ -z "$CMD" ] && exit 0 + +# 只管「建 milestone」這個動作 +printf '%s' "$CMD" | grep -qE 'milestones' || exit 0 +printf '%s' "$CMD" | grep -qE '\-X *POST|--request *POST' || exit 0 + +DUE=$(printf '%s' "$CMD" | grep -oE '"due_on"[^,}]*' | head -1) +BAD="" +[ -z "$DUE" ] && BAD="沒有 due_on" +printf '%s' "$DUE" | grep -q '9999' && BAD="due_on 是 9999(等於沒有期限)" + +[ -z "$BAD" ] && exit 0 + +cat >&2 <