Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c1df3c7a08 | |||
| 544beefc5e | |||
| c0eec76b52 |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "isep",
|
"name": "isep",
|
||||||
"description": "InkStone Environment Plugin —— leo 的 Claude Code 環境唯一真相源:62 支機械閘(87 條註冊,白話盤點見 docs/hooks-inventory.md)、7 支 slash command、2 支 skill、7 位有名字的工人(agents/,見 docs/governance/worker-roster.md)、62 支腳本,外加治理規範與標籤真相源。本機與雲端裝同一份,沒有子集。",
|
"description": "InkStone Environment Plugin —— leo 的 Claude Code 環境唯一真相源:62 支機械閘(87 條註冊,白話盤點見 docs/hooks-inventory.md)、7 支 slash command、2 支 skill、7 位有名字的工人(agents/,見 docs/governance/worker-roster.md)、62 支腳本,外加治理規範與標籤真相源。本機與雲端裝同一份,沒有子集。",
|
||||||
"version": "0.30.0",
|
"version": "0.30.1",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"inkstone",
|
"inkstone",
|
||||||
"guardrails",
|
"guardrails",
|
||||||
|
|||||||
@@ -43,6 +43,12 @@
|
|||||||
> **62 支、87 條、62 支腳本是在這棵樹上當場數出來的**(`ls hooks/*.sh | wc -l`=62、
|
> **62 支、87 條、62 支腳本是在這棵樹上當場數出來的**(`ls hooks/*.sh | wc -l`=62、
|
||||||
> `grep -c '"command":' hooks/hooks.json`=87、`ls -p scripts | grep -v / | wc -l`=62)。
|
> `grep -c '"command":' hooks/hooks.json`=87、`ls -p scripts | grep -v / | wc -l`=62)。
|
||||||
|
|
||||||
|
> 📌 **`0.30.1`(`inkstone/ISEP#130`,2026-09-13)+0 支、+0 條**:修補 0.30.0 的抓票入口——
|
||||||
|
> `mainline set/refresh/adopt` 讀不到任何一條 `/dependencies` 時,名單標成不完整、`set` 離開碼 3 不印 ✅、
|
||||||
|
> 保留舊快取;否則雲端工人會拿殘缺名單 `pick` 到「抓盡」而收工(09-13 實撞:hub 票底下三張整批漏收)。
|
||||||
|
> **62 支、87 條、62 支腳本是在這棵樹上當場數出來的**(`ls hooks/*.sh | wc -l`=62、
|
||||||
|
> `grep -c '"command":' hooks/hooks.json`=87、`ls -p scripts | grep -v / | wc -l`=62)。
|
||||||
|
|
||||||
> 📌 **`inkstone/ISEP#147`(2026-09-07)+0 支、+1 條**:`line-needs-own-worktree.sh` 多掛 PostToolUse `Agent|Task`
|
> 📌 **`inkstone/ISEP#147`(2026-09-07)+0 支、+1 條**:`line-needs-own-worktree.sh` 多掛 PostToolUse `Agent|Task`
|
||||||
> ——教你開分身的那支閘,在線收工那一刻替你收(分支推了就 remove;沒推就點名分支與路徑,不靜默、不 force)。
|
> ——教你開分身的那支閘,在線收工那一刻替你收(分支推了就 remove;沒推就點名分支與路徑,不靜默、不 force)。
|
||||||
> 分身的家從「repo 旁邊」改成「repo 裡面的 `.worktrees/`」:leo 在 Finder 看到一排 `ISEP-wt-117`/`InkStoneCo-wt-112`
|
> 分身的家從「repo 旁邊」改成「repo 裡面的 `.worktrees/`」:leo 在 Finder 看到一排 `ISEP-wt-117`/`InkStoneCo-wt-112`
|
||||||
|
|||||||
+31
-5
@@ -364,18 +364,42 @@ def collect_members(api, ms, cap=MEMBER_WALK_CAP):
|
|||||||
回 (members, info):
|
回 (members, info):
|
||||||
members 排好序的 `owner/repo#N` 清單(含已關的——進度與 `has` 都要看得到它們)
|
members 排好序的 `owner/repo#N` 清單(含已關的——進度與 `has` 都要看得到它們)
|
||||||
info {"milestone": [...], "via_deps": [...], "hub_ticket": ref|None,
|
info {"milestone": [...], "via_deps": [...], "hub_ticket": ref|None,
|
||||||
"reached": bool, "capped": bool}
|
"reached": bool, "capped": bool, "unread": [...], "complete": bool}
|
||||||
🔴 `reached=False` = 連 hub 里程碑那一頁都讀不到 ⇒ 呼叫端**不要拿空清單蓋掉舊快取**
|
🔴 `reached=False` = 連 hub 里程碑那一頁都讀不到 ⇒ 呼叫端**不要拿空清單蓋掉舊快取**
|
||||||
(讀不到 ≠ 沒有成員)。
|
(讀不到 ≠ 沒有成員)。
|
||||||
|
🔴 `unread` = 讀不到 `/dependencies` 的票(重讀過仍失敗)。**只要有一張,名單就不完整**:
|
||||||
|
那張底下的前置全部沒收到(inkstone/ISEP#130 c6973:Gitea 握手逾時,hub 票底下三張全漏,
|
||||||
|
`set` 卻印 ✅)。`complete`=reached 且沒有 unread 且沒被上限截斷——呼叫端只准在它為真時
|
||||||
|
把名單當成完整的寫出去。
|
||||||
判準只有兩個 Gitea 事實:票的 `milestone.id` 等於錨的 id、以及相依邊。標題只拿來查詢。
|
判準只有兩個 Gitea 事實:票的 `milestone.id` 等於錨的 id、以及相依邊。標題只拿來查詢。
|
||||||
"""
|
"""
|
||||||
|
import time
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
try:
|
||||||
|
retries = max(0, int(os.environ.get("ISEP_MEMBER_RETRIES", "2")))
|
||||||
|
except ValueError:
|
||||||
|
retries = 2
|
||||||
|
try:
|
||||||
|
backoff = max(0.0, float(os.environ.get("ISEP_MEMBER_BACKOFF", "2")))
|
||||||
|
except ValueError:
|
||||||
|
backoff = 2.0
|
||||||
|
|
||||||
|
def read_list(path):
|
||||||
|
"""讀一頁清單;拿不到 list 就重讀(握手逾時常常下一次就好)。仍拿不到回 None。"""
|
||||||
|
for n in range(retries + 1):
|
||||||
|
got = api(path)
|
||||||
|
if isinstance(got, list):
|
||||||
|
return got
|
||||||
|
if n < retries and backoff:
|
||||||
|
time.sleep(backoff * (n + 1))
|
||||||
|
return None
|
||||||
|
|
||||||
owner, repo, mid = ms.get("owner"), ms.get("repo"), ms.get("id")
|
owner, repo, mid = ms.get("owner"), ms.get("repo"), ms.get("id")
|
||||||
title = ms.get("title") or ""
|
title = ms.get("title") or ""
|
||||||
info = {"milestone": [], "via_deps": [], "hub_ticket": hub_ticket(ms),
|
info = {"milestone": [], "via_deps": [], "hub_ticket": hub_ticket(ms),
|
||||||
"reached": False, "capped": False}
|
"reached": False, "capped": False, "unread": [], "complete": False}
|
||||||
q = urllib.parse.urlencode({"state": "all", "type": "issues", "milestones": title, "limit": 100})
|
q = urllib.parse.urlencode({"state": "all", "type": "issues", "milestones": title, "limit": 100})
|
||||||
rows = api("/repos/%s/%s/issues?%s" % (owner, repo, q))
|
rows = read_list("/repos/%s/%s/issues?%s" % (owner, repo, q))
|
||||||
if not isinstance(rows, list):
|
if not isinstance(rows, list):
|
||||||
return [], info
|
return [], info
|
||||||
info["reached"] = True
|
info["reached"] = True
|
||||||
@@ -400,9 +424,10 @@ def collect_members(api, ms, cap=MEMBER_WALK_CAP):
|
|||||||
parts = split_ref(cur)
|
parts = split_ref(cur)
|
||||||
if not parts:
|
if not parts:
|
||||||
continue
|
continue
|
||||||
deps = api("/repos/%s/%s/issues/%d/dependencies?limit=100" % parts)
|
deps = read_list("/repos/%s/%s/issues/%d/dependencies?limit=100" % parts)
|
||||||
if not isinstance(deps, list):
|
if not isinstance(deps, list):
|
||||||
continue # 這一張讀不到:跳過它,不當成「沒有相依」以外的事
|
info["unread"].append(cur) # 讀不到 ≠ 沒有相依:記下來,名單就不算完整
|
||||||
|
continue
|
||||||
for d in deps:
|
for d in deps:
|
||||||
r = issue_ref(d)
|
r = issue_ref(d)
|
||||||
if not r or r in seen:
|
if not r or r in seen:
|
||||||
@@ -412,6 +437,7 @@ def collect_members(api, ms, cap=MEMBER_WALK_CAP):
|
|||||||
break
|
break
|
||||||
seen.add(r); out.append(r); queue.append(r)
|
seen.add(r); out.append(r); queue.append(r)
|
||||||
info["via_deps"].append(r)
|
info["via_deps"].append(r)
|
||||||
|
info["complete"] = not info["unread"] and not info["capped"]
|
||||||
return sorted(set(out)), info
|
return sorted(set(out)), info
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,14 @@ class H(BaseHTTPRequestHandler):
|
|||||||
r = "%s/%s#%s" % m.groups()[:3]
|
r = "%s/%s#%s" % m.groups()[:3]
|
||||||
if r not in w["issues"]: return self._send(404, {"message": "not found"})
|
if r not in w["issues"]: return self._send(404, {"message": "not found"})
|
||||||
if not m.group(4): return self._send(200, issue_obj(w, r))
|
if not m.group(4): return self._send(200, issue_obj(w, r))
|
||||||
if m.group(4) == "/dependencies": return self._send(200, [issue_obj(w, c) for c in w["deps"].get(r, [])])
|
if m.group(4) == "/dependencies":
|
||||||
|
# inkstone/ISEP#130 c6973:Gitea 握手逾時時,票本身讀得到、它的 /dependencies 讀不到
|
||||||
|
fo = os.path.join(TMP, "depsfail-once"); ff = os.path.join(TMP, "depsfail")
|
||||||
|
if os.path.exists(fo) and r in open(fo).read().split():
|
||||||
|
os.remove(fo); return self._send(500, {"message": "handshake timed out (test, once)"})
|
||||||
|
if os.path.exists(ff) and r in open(ff).read().split():
|
||||||
|
return self._send(500, {"message": "handshake timed out (test)"})
|
||||||
|
return self._send(200, [issue_obj(w, c) for c in w["deps"].get(r, [])])
|
||||||
return self._send(200, [issue_obj(w, pr) for pr, cs in w["deps"].items() if r in cs])
|
return self._send(200, [issue_obj(w, pr) for pr, cs in w["deps"].items() if r in cs])
|
||||||
return self._send(404, {"message": "not found", "path": p})
|
return self._send(404, {"message": "not found", "path": p})
|
||||||
def do_POST(self):
|
def do_POST(self):
|
||||||
@@ -299,6 +306,38 @@ OUT=$(ml refresh --members); RC=$?
|
|||||||
[ "$RC" = 0 ] && [ "$(members)" = "$BEFORE" ] && ok "㊻ Gitea 回 500 ⇒ refresh --members 不炸、成員一張不少(讀不到 ≠ 沒有成員)" || no "㊻ 成員被清掉或炸了" "$RC $OUT / $(members)"
|
[ "$RC" = 0 ] && [ "$(members)" = "$BEFORE" ] && ok "㊻ Gitea 回 500 ⇒ refresh --members 不炸、成員一張不少(讀不到 ≠ 沒有成員)" || no "㊻ 成員被清掉或炸了" "$RC $OUT / $(members)"
|
||||||
rm -f "$TMP/down"
|
rm -f "$TMP/down"
|
||||||
|
|
||||||
|
echo "── F. 讀不到任何一條 /dependencies ⇒ 不准寫出一份看起來完整的名單(inkstone/ISEP#130 → c6973)──"
|
||||||
|
jget(){ python3 -c "import json,sys; d=json.load(open(sys.argv[1])); v=d.get(sys.argv[2]); print(v if not isinstance(v,list) else ' '.join(v))" "$ISEP_COUNTDOWN_STATE_DIR/mainline.json" "$1"; }
|
||||||
|
export ISEP_MEMBER_BACKOFF=0
|
||||||
|
printf 'inkstone/InkStoneCo#44\n' > "$TMP/depsfail"
|
||||||
|
OUT=$(ml set inkstone/Arcrun#48 --hub inkstone/InkStoneCo#44); RC=$?
|
||||||
|
[ "$RC" != 0 ] && ok "F1 hub 票的 /dependencies 讀不到 ⇒ set 離開碼非 0(實際 $RC)" || no "F1 set 當成成功" "$RC $OUT"
|
||||||
|
printf '%s' "$OUT" | grep -q '✅' && no "F2 名單不完整卻印 ✅(c6973 重現的就是這個)" "$OUT" || ok "F2 名單不完整時不印 ✅"
|
||||||
|
case "$OUT" in *"成員不完整"*"inkstone/InkStoneCo#44"*) ok "F3 訊息講出「成員不完整」並點名讀不到的是哪一張" ;; *) no "F3 沒講不完整/沒點名" "$OUT" ;; esac
|
||||||
|
[ "$(jget members_complete)" = "False" ] && ok "F4 寫出去的主線檔標 members_complete=false(讀檔的人也看得出)" || no "F4 檔案沒標不完整" "$(cat "$ISEP_COUNTDOWN_STATE_DIR/mainline.json")"
|
||||||
|
case " $(jget members_unread) " in *" inkstone/InkStoneCo#44 "*) ok "F5 members_unread 列出讀不到的票" ;; *) no "F5 沒列" "$(jget members_unread)" ;; esac
|
||||||
|
[ "$(jget ref)" = "inkstone/Arcrun#48" ] && ok "F6 主線本身照樣標定(壞的只有成員那一格)" || no "F6 主線沒標上" "$(jget ref)"
|
||||||
|
OUT=$(python3 "$TICKET" pick --mainline "$ISEP_COUNTDOWN_STATE_DIR/mainline.json" 2>&1); RC=$?
|
||||||
|
[ "$RC" != 1 ] && ok "F7 同樣讀不到時 ticket pick 不回離開碼 1(不會讓 queue-drain-guard 當成抓盡;實際 $RC)" || no "F7 pick 回了「抓盡」" "$OUT"
|
||||||
|
rm -f "$TMP/depsfail"
|
||||||
|
|
||||||
|
printf 'inkstone/InkStoneCo#44\n' > "$TMP/depsfail-once"
|
||||||
|
OUT=$(ml set inkstone/Arcrun#48 --hub inkstone/InkStoneCo#44); RC=$?
|
||||||
|
[ "$RC" = 0 ] && printf '%s' "$OUT" | grep -q '✅' && ok "F8 只逾時一次 ⇒ 重讀一次就完整,set 照常 ✅" || no "F8 沒重試" "$RC $OUT"
|
||||||
|
case " $(members) " in *" inkstone/arcrun-rag#104 "*) ok "F9 重讀後 hub 票底下的 arcrun-rag#104 收到了" ;; *) no "F9 還是漏收" "$(members)" ;; esac
|
||||||
|
[ "$(jget members_complete)" = "True" ] && ok "F10 完整時 members_complete=true" || no "F10 完整卻沒標" "$(jget members_complete)"
|
||||||
|
|
||||||
|
OUT=$(ml refresh --members); RC=$?
|
||||||
|
case "$OUT" in *"成員"*"張"*) ok "F11 refresh --members 讀得到時會印出成員張數(c6973:原本零輸出)" ;; *) no "F11 refresh --members 還是零輸出" "$OUT" ;; esac
|
||||||
|
BEFORE=$(members)
|
||||||
|
printf 'inkstone/InkStoneCo#44\n' > "$TMP/depsfail"
|
||||||
|
OUT=$(ml refresh --members); RC=$?
|
||||||
|
[ "$RC" = 0 ] && ok "F12 refresh --members 讀不到也不炸(SessionStart 那條不准炸)" || no "F12 炸了" "$RC $OUT"
|
||||||
|
[ "$(members)" = "$BEFORE" ] && ok "F13 不完整時保留舊快取,不拿殘缺名單蓋掉" || no "F13 快取被蓋掉" "$BEFORE → $(members)"
|
||||||
|
case "$OUT" in *"成員不完整"*) ok "F14 refresh --members 講出成員不完整" ;; *) no "F14 靜默" "$OUT" ;; esac
|
||||||
|
rm -f "$TMP/depsfail"
|
||||||
|
unset ISEP_MEMBER_BACKOFF
|
||||||
|
|
||||||
echo "── E. milestone-account 的帳本落在會被 clone 走的地方(票上驗法第 3 條)──"
|
echo "── E. milestone-account 的帳本落在會被 clone 走的地方(票上驗法第 3 條)──"
|
||||||
OUT=$(CLAUDE_PROJECT_DIR="$SHELL_ROOT" python3 "$ACCOUNT" ledger-path 2>&1)
|
OUT=$(CLAUDE_PROJECT_DIR="$SHELL_ROOT" python3 "$ACCOUNT" ledger-path 2>&1)
|
||||||
[ "$OUT" = "$HUB/system-dev/estimates/ledger.jsonl" ] && ok "㊼ 雲端薄殼形狀(\$CLAUDE_PROJECT_DIR 是薄殼根、真身在 InkStoneCo/)⇒ 帳本在 InkStoneCo/system-dev/estimates/" || no "㊼ 帳本路徑不對" "$OUT"
|
[ "$OUT" = "$HUB/system-dev/estimates/ledger.jsonl" ] && ok "㊼ 雲端薄殼形狀(\$CLAUDE_PROJECT_DIR 是薄殼根、真身在 InkStoneCo/)⇒ 帳本在 InkStoneCo/system-dev/estimates/" || no "㊼ 帳本路徑不對" "$OUT"
|
||||||
|
|||||||
+51
-7
@@ -252,21 +252,50 @@ def cmd_set(argv):
|
|||||||
"members": [],
|
"members": [],
|
||||||
}
|
}
|
||||||
refs, info = collect_members(ms)
|
refs, info = collect_members(ms)
|
||||||
|
complete = bool(info.get("reached") and info.get("complete"))
|
||||||
|
if not complete:
|
||||||
|
# 名單不完整:同一條線重標時,舊快取裡有的成員先留著(讀不到 ≠ 沒有)
|
||||||
|
prev = ML.load() or {}
|
||||||
|
if prev.get("ref") == ms["ref"]:
|
||||||
|
refs = sorted(set(refs) | set(prev.get("members") or []))
|
||||||
ms["members"] = refs
|
ms["members"] = refs
|
||||||
|
ms["members_complete"] = complete
|
||||||
|
ms["members_unread"] = incomplete_what(info)
|
||||||
now = datetime.now(timezone.utc).astimezone().strftime("%Y-%m-%d %H:%M")
|
now = datetime.now(timezone.utc).astimezone().strftime("%Y-%m-%d %H:%M")
|
||||||
ms["set_at"] = ms["refreshed_at"] = now
|
ms["set_at"] = ms["refreshed_at"] = now
|
||||||
if not ML.save(ms, mirror=True):
|
if not ML.save(ms, mirror=True):
|
||||||
die("🔴 寫不進 %s——主線沒有被標上。" % ML.path())
|
die("🔴 寫不進 %s——主線沒有被標上。" % ML.path())
|
||||||
print("✅ 主線已標定:%s/%s#%d「%s」" % (owner, repo, mid, title))
|
# 🔴 inkstone/ISEP#130 c6973:名單不完整時不准印 ✅——雲端工人靠這份名單抓票,
|
||||||
|
# 漏收=以為抓盡=收工。主線本身照樣標定,壞的只有成員那一格,所以講清楚是哪一格。
|
||||||
|
print("%s 主線已標定:%s/%s#%d「%s」%s" % (
|
||||||
|
"✅" if complete else "⚠️", owner, repo, mid, title, "" if complete else "(🔴 成員不完整)"))
|
||||||
if hub:
|
if hub:
|
||||||
print(" hub 票:%s" % hub)
|
print(" hub 票:%s" % hub)
|
||||||
print(members_line(refs, info) if info.get("reached")
|
print(members_line(refs, info) if info.get("reached")
|
||||||
else " ⚠️ 讀不到 hub 里程碑裡的票,成員先留空(讀不到 ≠ 沒有)")
|
else " ⚠️ 讀不到 hub 里程碑裡的票,成員先留空(讀不到 ≠ 沒有)")
|
||||||
if info.get("capped"):
|
if not complete:
|
||||||
print(" ⚠️ 相依走到 %d 張就停了(上限),剩下的沒收" % ML.MEMBER_WALK_CAP)
|
print(incomplete_line(info))
|
||||||
print(mirror_line())
|
print(mirror_line())
|
||||||
print()
|
print()
|
||||||
cmd_show([])
|
cmd_show([])
|
||||||
|
if not complete:
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
|
|
||||||
|
def incomplete_what(info):
|
||||||
|
"""名單不完整的原因,寫進主線檔(讀檔的人不必重跑也看得出)。完整時回 []。"""
|
||||||
|
what = list(info.get("unread") or [])
|
||||||
|
if not info.get("reached"):
|
||||||
|
what.insert(0, "(hub 里程碑那一頁)")
|
||||||
|
if info.get("capped"):
|
||||||
|
what.append("(相依走到 %d 張上限)" % ML.MEMBER_WALK_CAP)
|
||||||
|
return what
|
||||||
|
|
||||||
|
|
||||||
|
def incomplete_line(info):
|
||||||
|
return (" 🔴 成員不完整:讀不到 %s——那底下的前置沒收到。\n"
|
||||||
|
" 雲端工人靠這份名單抓票;Gitea 恢復後重跑 `mainline refresh --members`(或同一行 set)補齊。"
|
||||||
|
% "、".join(incomplete_what(info)))
|
||||||
|
|
||||||
|
|
||||||
def mirror_line():
|
def mirror_line():
|
||||||
@@ -328,6 +357,9 @@ def cmd_refresh(argv):
|
|||||||
except SystemExit:
|
except SystemExit:
|
||||||
return # 拿不到 token:主線已經在了,進度晚點再更新,不炸
|
return # 拿不到 token:主線已經在了,進度晚點再更新,不炸
|
||||||
if not isinstance(d, dict) or not d.get("title"):
|
if not isinstance(d, dict) or not d.get("title"):
|
||||||
|
if "--members" in argv: # 明講要成員的人要看得到結果(c6973:原本零輸出)
|
||||||
|
print(" ⚠️ 讀不到主線里程碑 %s——成員沒更新,沿用舊快取 %d 張"
|
||||||
|
% (ML.ref_of(ms), len(ms.get("members") or [])))
|
||||||
return # 讀不到 ≠ 主線不存在
|
return # 讀不到 ≠ 主線不存在
|
||||||
ms.update({
|
ms.update({
|
||||||
"title": d.get("title") or ms["title"],
|
"title": d.get("title") or ms["title"],
|
||||||
@@ -342,12 +374,19 @@ def cmd_refresh(argv):
|
|||||||
# 真 Gitea 實測 25 張成員要 19 秒 ⇒ SessionStart 那條不帶 --members(成本不變);
|
# 真 Gitea 實測 25 張成員要 19 秒 ⇒ SessionStart 那條不帶 --members(成本不變);
|
||||||
# `set`/`adopt` 會重收,`pick`/`has`/focus guard 本來就每次現問 Gitea,快取只餵 `show`。
|
# `set`/`adopt` 會重收,`pick`/`has`/focus guard 本來就每次現問 Gitea,快取只餵 `show`。
|
||||||
# 讀不到(reached=False)就保留舊快取——寧可資料舊,不要把成員弄丟。
|
# 讀不到(reached=False)就保留舊快取——寧可資料舊,不要把成員弄丟。
|
||||||
|
# 🔴 inkstone/ISEP#130 c6973:只有**完整**的名單才准蓋掉快取;不完整就留舊的並講出來。
|
||||||
try:
|
try:
|
||||||
refs, info = collect_members(ms)
|
refs, info = collect_members(ms)
|
||||||
except SystemExit:
|
except SystemExit:
|
||||||
refs, info = [], {"reached": False}
|
refs, info = [], {"reached": False}
|
||||||
if info.get("reached"):
|
if info.get("reached") and info.get("complete"):
|
||||||
ms["members"] = refs
|
ms["members"] = refs
|
||||||
|
ms["members_complete"], ms["members_unread"] = True, []
|
||||||
|
print(members_line(refs, info))
|
||||||
|
else:
|
||||||
|
ms["members_complete"], ms["members_unread"] = False, incomplete_what(info)
|
||||||
|
print(" ⚠️ 成員不完整,沿用舊快取 %d 張(沒有拿殘缺名單蓋掉)" % len(ms.get("members") or []))
|
||||||
|
print(incomplete_line(info))
|
||||||
ML.save(ms)
|
ML.save(ms)
|
||||||
|
|
||||||
|
|
||||||
@@ -391,13 +430,18 @@ hub 里程碑裡現在有:%s
|
|||||||
T.add_dependency(vparts, (owner, repo, num))
|
T.add_dependency(vparts, (owner, repo, num))
|
||||||
how = "成為 hub 票 %s 的相依(%s/%s 沒有多出任何里程碑)" % (via, owner, repo)
|
how = "成為 hub 票 %s 的相依(%s/%s 沒有多出任何里程碑)" % (via, owner, repo)
|
||||||
refs, info = collect_members(ms)
|
refs, info = collect_members(ms)
|
||||||
if info.get("reached"):
|
complete = bool(info.get("reached") and info.get("complete"))
|
||||||
|
if complete:
|
||||||
ms["members"] = sorted(set(refs) | {ref})
|
ms["members"] = sorted(set(refs) | {ref})
|
||||||
else:
|
else:
|
||||||
ms["members"] = sorted(set(ms.get("members") or []) | {ref})
|
# 不完整(c6973):新收到的與舊快取取聯集,不拿殘缺名單蓋掉
|
||||||
|
ms["members"] = sorted(set(ms.get("members") or []) | set(refs) | {ref})
|
||||||
|
ms["members_complete"], ms["members_unread"] = complete, incomplete_what(info)
|
||||||
ML.save(ms, mirror=True)
|
ML.save(ms, mirror=True)
|
||||||
print("✅ 補收:%s → %s" % (ref, how))
|
print("✅ 補收:%s → %s" % (ref, how))
|
||||||
print(members_line(ms["members"], info) if info.get("reached") else " (成員清單沿用舊快取+這一張)")
|
print(members_line(ms["members"], info) if complete else " (成員清單=舊快取+這次讀得到的+這一張)")
|
||||||
|
if not complete:
|
||||||
|
print(incomplete_line(info))
|
||||||
print(mirror_line())
|
print(mirror_line())
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user