Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 43c328d26f | |||
| bcb736ed19 | |||
| 6772ca67d3 | |||
| 1b5551274a |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "isep",
|
"name": "isep",
|
||||||
"description": "InkStone Environment Plugin —— leo 的 Claude Code 環境唯一真相源:43 支機械閘(53 條註冊,白話盤點見 docs/hooks-inventory.md)、7 支 slash command、2 支 skill、27 支腳本,外加治理規範與標籤真相源。本機與雲端裝同一份,沒有子集。",
|
"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": [
|
"keywords": [
|
||||||
"inkstone",
|
"inkstone",
|
||||||
"guardrails",
|
"guardrails",
|
||||||
|
|||||||
@@ -677,3 +677,28 @@ Gitea 的 milestone 只管得到同一個 repo,所以六個群在 `inkstone/IS
|
|||||||
結構(正交) hub ← tracking issue 標記
|
結構(正交) hub ← tracking issue 標記
|
||||||
封存不刪 duplicate ← 由 close/duplicate 取代,保留在歷史票上
|
封存不刪 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 放行。
|
||||||
|
|||||||
+5
-1
@@ -40,6 +40,10 @@
|
|||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/ticket-api-bypass-guard.sh"
|
"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 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Executable
+42
@@ -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 <<MSG
|
||||||
|
⏱️ 里程碑期限閘:$BAD
|
||||||
|
|
||||||
|
【leo 2026-08-21】「**以後所有的 milestone 限制時間**」
|
||||||
|
「**你根本沒有時間概念,浪費一整天**」
|
||||||
|
|
||||||
|
🔴 9999-01-01 也算違規。立這條的當下實查七個 open milestone,
|
||||||
|
六個是 9999——那是「沒有期限」穿了一件期限的衣服,
|
||||||
|
比空白更糟,因為盤點時每一格看起來都有值。
|
||||||
|
|
||||||
|
怎麼定:這個里程碑的 deliverable 是**一個可測的版本**。
|
||||||
|
問「這個版本幾號要能給 leo 打開?」,那天就是期限。
|
||||||
|
剩 1–3 張票 → 一週;4 張以上 → 兩週;本週要收 → 三天。
|
||||||
|
|
||||||
|
加上去再送一次: "due_on": "YYYY-MM-DDT23:59:59Z"
|
||||||
|
MSG
|
||||||
|
exit 2
|
||||||
@@ -119,8 +119,21 @@ if not ok_hits and not ng_hits:
|
|||||||
|
|
||||||
sid = (d.get("session_id") or "nosid")[:8]
|
sid = (d.get("session_id") or "nosid")[:8]
|
||||||
aid = (d.get("agent_id") or d.get("subagent_id") or "")[:10]
|
aid = (d.get("agent_id") or d.get("subagent_id") or "")[:10]
|
||||||
stamp = hashlib.sha1((tp + sid + aid).encode()).hexdigest()[:8]
|
# 🔴 2026-08-21:stamp 原本雜湊「交件路徑」⇒ 同樣的宣稱每回合生一個新檔名,
|
||||||
path = os.path.join(os.environ["DIR"], "claims-%s-%s.md" % (sid, stamp))
|
# 而且不知道總管已經驗過了。實際發作:同兩條 sdd-guard 宣稱連生四張單
|
||||||
|
# (1c97d461/fcb285dc/256de849/394b97ae),驗掉一張下一回合又冒一張。
|
||||||
|
# ⇒ 改成雜湊**宣稱內容本身**:同樣的宣稱=同一個檔名 ⇒ 驗過就不再冒出來。
|
||||||
|
_claims = sorted(set(str(x) for x in (ok_hits + ng_hits)))
|
||||||
|
# 🔴 空清單就退回舊行為 —— 但那等於這支閘沒抓到任何宣稱,本來就會 SKIP,
|
||||||
|
# 所以這個 fallback 實際上不會被用到;留著只是不讓 stamp 變成空字串的雜湊。
|
||||||
|
_basis = "\n".join(_claims) if _claims else (tp + sid + aid)
|
||||||
|
stamp = hashlib.sha1(_basis.encode()).hexdigest()[:8]
|
||||||
|
_name = "claims-%s-%s.md" % (sid, stamp)
|
||||||
|
path = os.path.join(os.environ["DIR"], _name)
|
||||||
|
|
||||||
|
# 已經驗過並移進 verified/ 的,不要再生一次。
|
||||||
|
if os.path.exists(os.path.join(os.environ["DIR"], "verified", _name)):
|
||||||
|
print("SKIP:already-verified"); raise SystemExit
|
||||||
|
|
||||||
def block(title, items, howto, cap):
|
def block(title, items, howto, cap):
|
||||||
if not items:
|
if not items:
|
||||||
|
|||||||
Reference in New Issue
Block a user