feat(assistant): 一輪算完就寫提醒佇列
助理算得出哪幾筆到期、哪幾筆逾期,但那些結果只留在監控頁上。這一批讓 每一輪把「要送到人面前」的那幾筆寫成一份佇列檔,位置固定在助理狀態目錄 底下,讀的那一端只印、不判。 判定不放在讀的那一邊,理由是漂移:讓讀的人自己拿 due 欄與 next_run 去跟 現在比,就是第二套到期判定,跟這裡那一套遲早對不上,而對不上的那一天 兩邊都說自己是對的。 這樣換來一個新的失效模式,所以檔頭寫足四個值:輪次代號、UTC 時間戳、 連續失敗筆數、待辦總筆數,再加同一刻的 epoch 秒。助理沒在跑的時候佇列 不會更新,而一份舊佇列讀起來跟新的一模一樣——讀的那一端要算得出它多舊, 「沒有提醒」與「沒有人算提醒」不可以長得一樣。epoch 是為了讓讀的那一端 不必自己解 ISO 字串:那等於在每一個讀取端各放一份日期解析。 同一筆待辦既到期又逾期時只列逾期那一行。同一批提醒裡出現兩行會被讀成 兩件事,而「逾期五天」比「排定點過了」講得更清楚。 逾期那幾行的「逾期多久」直接取判定那一支算好的寫法,不在這裡再寫一個 時間長度格式化——同一個秒數在兩個地方就會印出兩種說法。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+2
-1
@@ -1140,7 +1140,8 @@ cmd_scan() {
|
||||
if [ -n "$OVERDUE_LINES" ]; then
|
||||
printf '%s' "$OVERDUE_LINES" | while IFS="$(printf '\t')" read -r _oi _os _od _oa _ot; do
|
||||
[ -n "$_oi" ] || continue
|
||||
printf 'overdue_row=%s state=%s due=%s overdue_secs=%s title=%s\n' "$_oi" "$_os" "$_od" "$_oa" "$_ot"
|
||||
printf 'overdue_row=%s state=%s due=%s overdue_secs=%s human=%s title=%s\n' \
|
||||
"$_oi" "$_os" "$_od" "$_oa" "$(dur_human "$_oa")" "$_ot"
|
||||
done
|
||||
fi
|
||||
printf 'rows_file=%s\n' "$ROWS"
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
# 時,原因只寫在這裡
|
||||
# tasks_total= tasks_failing= 待辦簿筆數與連續失敗筆數,供目錄頁那一個區塊與摘要用
|
||||
# tasks_overdue= 逾期筆數(截止時間已經過了),取自到期判定那一支;判不出來時是減號
|
||||
# reminders= reminders_file= 提醒佇列的筆數與路徑。工作階段開始那一支 hook 讀它
|
||||
# tasks_due= 本輪到期的筆數;到期判定那一支失敗時為空
|
||||
# events_new= 本輪偵測到的新事件種類數;到期判定那一支失敗時為空
|
||||
# due_status= due_rc= 到期判定那一支的結果與結束碼
|
||||
@@ -212,6 +213,8 @@ DUE_TASKS=''
|
||||
DUE_EVENTS=''
|
||||
DUE_OVERDUE=''
|
||||
FAILING_LINES=''
|
||||
REMINDERS=0
|
||||
REMINDERS_FILE=''
|
||||
|
||||
# 這支腳本是不是從 $JSC_HOME/current 那一組路徑被叫起來的。不是就大聲警告,但照跑。
|
||||
# 只警告、不中止是刻意的取捨:從工作樹直接跑腳本是開發時的正當用法,中止會把那條路擋掉;
|
||||
@@ -903,6 +906,63 @@ count_tasks() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# --- 提醒佇列 ---
|
||||
#
|
||||
# 一輪算完之後,把「要送到人面前」的那幾筆寫成一份佇列檔,位置固定在助理狀態目錄底下。
|
||||
# 讀的那一端是工作階段開始那一支 hook:它只印,一個判定都不做。
|
||||
#
|
||||
# 為什麼判定不放在 hook 那一邊:hook 跑在每一個工作階段的開頭,它要快、而且絕對不能擋人。
|
||||
# 更重要的是,讓 hook 自己拿 due 欄與 next_run 去比就是第二套到期判定,跟 due.sh 那一套
|
||||
# 會漂移,而漂移的那一天兩邊都說自己是對的。所以判定只有一套,佇列是它的輸出。
|
||||
#
|
||||
# 這樣換來一個新的失效模式,要正面處理:助理沒在跑的時候,這份佇列不會更新,而一份舊佇列
|
||||
# 讀起來跟新的一模一樣。所以檔頭寫一行 round,帶著這一輪的時間戳,讓讀的那一端算得出
|
||||
# 它有多舊——「沒有提醒」與「沒有人算提醒」不可以長得一樣。
|
||||
write_reminders() {
|
||||
REMINDERS=0
|
||||
REMINDERS_FILE="$STATE_DIR/reminders.tsv"
|
||||
_rt="$REMINDERS_FILE.tmp.$$"
|
||||
{
|
||||
# 檔頭同時寫 ISO 時間與 epoch 秒。ISO 給人看,epoch 給讀的那一端算年紀——讓它自己解
|
||||
# ISO 字串就是在每一個讀取端各放一份日期解析,而 date -d 不是每一台機器都認得那個格式。
|
||||
# 最後那個待辦總筆數是給讀的那一端判「該不該吵」用的:佇列空又過期時,待辦簿有東西
|
||||
# 才代表「有事沒人在算」,零筆就只是助理閒著,那時候安靜才對。
|
||||
printf 'round\t%s\t%s\t%s\t%s\t%s\n' \
|
||||
"$ROUND" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$TASKS_FAILING" "$(date +%s)" "$TASKS_TOTAL"
|
||||
# 逾期那幾筆照抄判定那一支印的行,連「逾期多久」那個給人看的寫法都取它算好的:
|
||||
# 自己再寫一個時間長度格式化,同一個秒數在兩個地方就會印出兩種說法。
|
||||
_ovids="$RD/overdue-ids"
|
||||
: >"$_ovids"
|
||||
if [ -f "$RD/due.out" ]; then
|
||||
sed -n 's/^overdue_row=//p' "$RD/due.out" 2>/dev/null | while IFS= read -r _l; do
|
||||
[ -n "$_l" ] || continue
|
||||
_oi=${_l%% *}
|
||||
_oh=$(printf '%s' "$_l" | sed -n 's/.* human=\(.*\) title=.*/\1/p')
|
||||
_ot=$(printf '%s' "$_l" | sed -n 's/.* title=//p')
|
||||
printf '%s\n' "$_oi" >>"$_ovids"
|
||||
printf 'overdue\t%s\t%s\t%s\n' "$_oi" "${_oh:--}" "${_ot:--}"
|
||||
done
|
||||
fi
|
||||
# 只提醒型而且到期的那幾筆。指令型不進佇列:它們由執行那一支真的跑掉了,人不必接手。
|
||||
# 已經以逾期身分列過的那幾筆不再列第二次:同一筆待辦在同一批提醒裡出現兩行,讀的人會
|
||||
# 當成兩件事,而且「逾期五天」比「排定點過了」講得更清楚——留強的那一行就好。
|
||||
if [ -n "$DUE_ROWS" ] && [ -f "$DUE_ROWS" ]; then
|
||||
awk -F'\t' -v idf="$_ovids" '
|
||||
BEGIN { while ((getline _l < idf) > 0) seen[_l] = 1 }
|
||||
NF >= 12 && $1 != "" && $2 == "due" && $5 == "remind" && !($1 in seen) {
|
||||
printf "remind\t%s\t%s\t%s\n", $1, ($10 == "" ? "-" : $10), ($11 == "" ? "-" : $11)
|
||||
}' "$DUE_ROWS" 2>/dev/null
|
||||
fi
|
||||
} >"$_rt" 2>/dev/null || { add_warn '提醒佇列寫不出來'; rm -f "$_rt"; return 0; }
|
||||
if mv "$_rt" "$REMINDERS_FILE" 2>/dev/null; then
|
||||
REMINDERS=$(awk -F'\t' '$1 == "remind" || $1 == "overdue" { n++ } END { print n + 0 }' "$REMINDERS_FILE")
|
||||
else
|
||||
add_warn '提醒佇列換不上去'
|
||||
rm -f "$_rt"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# --- 待辦簿的事件偵測與到期判定 ---
|
||||
#
|
||||
# 算到期的邏輯不在這一支,也不在 tools/tasks.sh,而在 tools/due.sh,理由寫在那一支的檔頭:
|
||||
@@ -1167,6 +1227,7 @@ case "$CMD" in
|
||||
if [ "$TASKS_FAILING" -gt 0 ]; then
|
||||
add_pending "有 $TASKS_FAILING 筆待辦連續失敗,每一輪都在重試" '待辦簿到期與逾期' '/jsc-assist:assistant status'
|
||||
fi
|
||||
write_reminders
|
||||
tally "$D01_STATUS"; tally "$D04_STATUS"; tally "$D07_STATUS"; tally "$D09_STATUS"
|
||||
tally "$D11_STATUS"
|
||||
|
||||
@@ -1198,6 +1259,8 @@ case "$CMD" in
|
||||
printf 'tasks_failing=%s\n' "$TASKS_FAILING"
|
||||
printf 'tasks_due=%s\n' "$DUE_TASKS"
|
||||
printf 'tasks_overdue=%s\n' "${DUE_OVERDUE:--}"
|
||||
printf 'reminders=%s\n' "$REMINDERS"
|
||||
printf 'reminders_file=%s\n' "$REMINDERS_FILE"
|
||||
printf 'events_new=%s\n' "$DUE_EVENTS"
|
||||
printf 'due_status=%s\n' "$DUE_STATUS"
|
||||
printf 'due_rc=%s\n' "$DUE_RC"
|
||||
|
||||
Reference in New Issue
Block a user