è§£éããåå¹Žã§æå120äžã«ãªã£ãçç±ãäžèšã§èšããªãããè©°ãŸã£ãŠãèªåã§æ°ã¥ããŠçŽãç°å¢ãäœã£ããããã§ãã
ãªããã®ä»çµã¿ãå¹ãã®ã
ã³ã³ãã³ããèªåçæããä»çµã¿ãäœã£ããšããæåã«çŽé¢ããã®ã¯ãåããŠããããšæã£ãŠãããå®ã¯æ¢ãŸã£ãŠããåé¡ã§ããã
launchdã§æ¯æã¹ã¯ãªãããèµ·åããŠãããããã¯ãŒã¯æªæ¥ç¶ã®ãŸãŸèµ·åããŠç¡èšã§çµäºãããAPIã®ã¬ã¹ãã³ã¹ãè¿ããã¿ã€ã ã¢ãŠãããŠãããã°ã¯ååšãããClaudeã®ããŒã¯ã³äºç®ãæ¯æžããŠããŠãããšã©ãŒã¡ãã»ãŒãžã¯åºåãã¡ã€ã«ã«æµã蟌ãã§æ¬æããŒããã€ãã«ãªãããããã倱æã¯ãã¹ãŠãã¹ã¯ãªããã¯èµ°ã£ãããšããäºå®ã ããæ®ããŠæ¶ããŸãã
äœæ¥ã§ãªãç°å¢ãäœããšã¯ã©ãããããšããç§ã®çãã¯ãå®äºããã¡ã€ã«ã®å®åšã§èšŒæãããããšããèšèšã§ãããã¹ã¯ãªãããäœããã°ã«æžãããã§ã¯ãªãã~/.claude/logs/.article-daily-done-20260710 ãšãããã¡ã€ã«ãååšãããã©ããã ããçå®ãšããŠæ±ãããããdone-markerãã¿ãŒã³ã®æ¬è³ªã§ãã
ãã®çºæ³ã5æ¬ã®ã³ã³ãã³ãã¬ãŒã³å
šäœã«æšªå±éããã®ã content-watchdog.sh ã§ãã1æ¥ã«è€æ°ååŒã°ãããå
šã¬ãŒã³ã®done-markerã確èªããŠãæ¬ ããŠãããã®ã ãåèµ·åããããšããåçŽãªä»äºãããŸããåçŽã ããããå£ããŸããã
èªååãè©°ãŸãçç±ã¯æè¡çãªãã®ãããéçšçãªãã®ãå€ãã§ãããæšæ¥ã¯åããŠãããã仿¥ãåãããšããåæã§èšèšãããšãWifiãç¹ãã£ãŠããªãæãããããªãŒãæ®ã3%ã®æ·±å€ãäºç®ãæ¯æžããææ«ã«éãã«æ¢ãŸããŸãããŠã©ããããã°ãååšããããšã§ãç§ã¯ã仿¥ãåããŠããã¯ãããšããäžå®ããè§£æŸãããŸãããiOSã¢ããª10æ¬ãæãæã¡ã§ç®¡çããªããæå120äžãç¶æã§ããã®ã¯ã確èªã®ããã®äœæ¥æéãéããªããŒãã«è¿ãããã§ãã
å šäœã®æµã
ãŠã©ããããã°ãšåã¬ãŒã³ã¹ã¯ãªããã®é¢ä¿ãå³ã§ç€ºããŸãã
launchd (è€æ°ã¹ããã)
â
âââ content-watchdog.sh (sweep ã¢ãŒã)
â
ââ acquire_lock() # mkdir ç«¶åããã¯
â ââ ~/.claude/locks/content-watchdog.lockd/
â
ââ for lane in article note maker series ameba
â â
â ââ done_lane() # done-marker / .done ãã¡ã€ã«ã確èª
â â
â ââ [æªå®äºãªã] run_capped 1800 bash <script> apply
â â
â ââ åã¬ãŒã³ã¹ã¯ãªãããèªåã®done-markerãç«ãŠã
â äŸ: ~/.claude/logs/.article-daily-done-20260710
â
ââ [å
šã¬ãŒã³å®äºãªã] send_heartbeat_once()
ââ Discordéç¥ + ~/.claude/logs/.content-watchdog-heartbeat-20260710
ã³ãŒãã®å®äœãé ã«èªãã§ãããŸãã
done-markerã®ç¢ºèªããžãã¯
done_lane() 颿°ã¯5ã¬ãŒã³ããããã«ç°ãªã確èªããžãã¯ãæã£ãŠããŸãã
done_lane() {
local lane="$1"
local hit
case "$lane" in
article)
[ -f "$LOG_DIR/.article-daily-done-$TODAY" ]
;;
note)
hit="$(find "$LOG_DIR/note-daily" -maxdepth 1 -name "$TODAY-*.done" -print -quit 2>/dev/null || true)"
[ -n "$hit" ]
;;
maker)
hit="$(find "$LOG_DIR/maker-daily" -maxdepth 1 -name "$TODAY-*.done" -print -quit 2>/dev/null || true)"
[ -n "$hit" ]
;;
series)
[ -f "$LOG_DIR/.series-daily-done-$TODAY" ]
;;
ameba)
local ad td f
ad="$HOME_DIR/Desktop/Article/ameba"
td="$(date +%F)"
grep -rlq "created:.*$td" "$ad" 2>/dev/null && return 0
for f in "$ad"/*.md; do
[ -f "$f" ] || continue
[ "$(stat -f %Sm -t %F "$f" 2>/dev/null)" = "$td" ] && return 0
done
return 1
;;
esac
}
article ãš series ã¯é ããã¡ã€ã«åœ¢åŒïŒ.article-daily-done-20260710ïŒã®åäžãã¡ã€ã«ã§ç®¡çããŸããnote ãš maker 㯠20260710-*.done ãšãããã¿ãŒã³ã§è€æ°ãã¡ã€ã«ãååšããå¯èœæ§ã蚱容ããèšèšã§ããameba ã ããdone-markerãæããã宿æç©ïŒ.md ãã¡ã€ã«ã® created: ã¡ã¿ããŒã¿ãmtimeïŒãçŽæ¥ç¢ºèªããŸããããã¯amebaçšã®ã¹ã¯ãªãããdone-markerã®ä»æ§ãæã£ãŠããªãããã®ãã©ãŒã«ããã¯å®è£
ã§ããèšèšã®å€åŽã«ããæ¢åã¹ã¯ãªããããŠã©ããããã°ã«çµã¿èŸŒãéã®çŸå®çãªåŠ¥åç¹ããšããŠæ©èœããŠããŸãã
done-markerã¯ãã€ç«ã€ã
article-daily-stock.sh ã®å®è£
ãèŠããšãdone-markerãç«ã€ã¿ã€ãã³ã°ãæç¢ºã§ãã
DONE_MARKER="$HOME/.claude/logs/.article-daily-done-${TODAY}"
ã¹ã¯ãªããã®åé ã§ãã¹ã確å®ãããèšäºæ¬æã®çæã»æ€èšŒã»ã¹ããã¯é 眮ã»ç§å¯ã¹ãã£ã³ããã¹ãŠééããããšãgit pushã®åã«ããŒã«ãŒãç«ãŠãŸãïŒã³ãŒã453è¡ç®ïŒã
# çææåïŒãã®æç¹ã§åœæ¥doneã確å®ããã以éã®git pushã¯best-effortïŒå€±æããŠãçæã¯æåæ±ãïŒ
# ãªã®ã§ãgit倱æã§ããŒã«ãŒæªèšå®âç¿ã¹ãããã§éè€çæããšããäºæ
ãé²ãããããã§å
ã«ç«ãŠãã
touch "$DONE_MARKER"
ã³ã¡ã³ãã«ãgit倱æã§ããŒã«ãŒæªèšå®âç¿ã¹ãããã§éè€çæããšããäºæ ãé²ãããããšæžããŠãããŸããpushããã¹ããšãã©ãŒãã§ãã以äžãpushã®æåŠã§doneãå€å®ãããšéè€çæãèµ·ããŸãããçæãšé ä¿¡ã¯ç¬ç«ãã責åããšããèšèšååãããã«è¡šããŠããŸãã
mkdirã«ããç«¶åããã¯
ãŠã©ããããã°èªèº«ãè€æ°ã¹ãããããèµ·åãããããšãæ³å®ããæä»å¶åŸ¡ã« mkdir ã䜿ã£ãŠããŸãã
acquire_lock() {
if mkdir "$LOCKDIR" 2>/dev/null; then
printf '%s\n' "$$" > "$LOCKDIR/pid"
trap release_lock EXIT INT TERM
return 0
fi
local now mod age
now="$(date +%s)"
mod="$(stat -f %m "$LOCKDIR" 2>/dev/null || printf '%s\n' "$now")"
age=$((now - mod))
if [ "$age" -ge 1800 ]; then
log "lock stale age=${age}s; taking over"
rm -rf "$LOCKDIR"
if mkdir "$LOCKDIR" 2>/dev/null; then
printf '%s\n' "$$" > "$LOCKDIR/pid"
trap release_lock EXIT INT TERM
return 0
fi
fi
log "lock held; skip"
exit 0
}
mkdir ã¯POSIXã¬ãã«ã§ã¢ãããã¯ãªæäœã§ãã2ã€ã®ããã»ã¹ãåæã« mkdir ãåŒãã§ããæåããã®ã¯1ã€ã ãã§ããflockäžèŠãLinuxãšmacã®äºææ§åé¡ãªãã倱æãããå³ exit 0 ãšããåçŽãã匷ã¿ã§ãã
ããã¯ã1800ç§ïŒ30åïŒä»¥äžæŸçœ®ãããŠããå Žåã¯ãåã®ããã»ã¹ãç°åžžçµäºããŠããã¯ãæ®ã£ãããšå€æããŠåŒ·å¶åŒãç¶ãããŸããåã¬ãŒã³ã®ã¹ã¯ãªããåŒã³åºãã«ãåã1800ç§ãã¿ã€ã ã¢ãŠããšããŠäœ¿ãããŠãããããæ£åžžãªãŠã©ããããã°ãå®èµ°ããçŽåŸã§ãªããã°æ¬¡ã®ãŠã©ããããã°ãèµ·åããããšã¯ãããŸããã
article-daily-stock.sh ãç¬èªã«åãæ¹åŒã®ããã¯ãæã£ãŠããŸãã
LOCKDIR="$HOME/.claude/locks/article-daily.lock"
if ! /bin/mkdir "$LOCKDIR" 2>/dev/null; then
oldpid=$(cat "$LOCKDIR/pid" 2>/dev/null || true)
if [ -n "${oldpid:-}" ] && kill -0 "$oldpid" 2>/dev/null; then
log "å¥ã€ã³ã¹ã¿ã³ã¹å®è¡äž(pid=$oldpid) â skip"; exit 0
fi
rm -rf "$LOCKDIR"; /bin/mkdir "$LOCKDIR" 2>/dev/null || exit 0
fi
echo $$ > "$LOCKDIR/pid"
trap 'rm -rf "$LOCKDIR"' EXIT INT TERM
ãã¡ãã¯PIDã®çå確èªã远å ããŠããŸããåã®ããã»ã¹ãçããŠããã°æ¬åœã«ã¹ãããããæ»ãã§ããã°ããã¯ã匷å¶è§£æŸããŠèªåãèµ°ããŸãããŠã©ããããã°ã®ããã¯ãšåã¬ãŒã³ã¹ã¯ãªããã®ããã¯ãäºéã«ååšããããšã§ãããŠã©ããããã°ãåèµ·åã詊ã¿ãããšãåã®èšäºçæããã»ã¹ããŸã èµ°ã£ãŠããããå¹²æžããŸããã
Discordéç¥ãšããŒãããŒãã®èšèš
倱æã¯éç¥ããããæåã¯1æ¥1åã ãéç¥ãããšããèšèšã send_heartbeat_once() ã«èŸŒããããŠããŸãã
send_heartbeat_once() {
[ "$MODE" = "sweep" ] || return 0
if [ -f "$HEARTBEAT_MARKER" ]; then
log "heartbeat skip already_sent=1"
return 0
fi
notify alerts "ð content-watchdog heartbeat: å
šã¬ãŒã³åœæ¥çææžã¿"
touch "$HEARTBEAT_MARKER"
log "heartbeat sent"
}
ãŠã©ããããã°ã¯1æ¥ã«äœåºŠãåŒã°ããŸããå
šã¬ãŒã³æ£åžžãªãæåã®1åã ãDiscordã« ð ãé£ã°ãã以éã¯ããŒã«ãŒã§ç絡ããŸãã察ããŠå€±æéç¥ã¯æ¯åé£ã³ãŸãã
if [ -n "$FAILED_LANES" ]; then
log "result=unhealthy lanes=$FAILED_LANES"
if [ "$MODE" = "sweep" ]; then
notify alerts "ðš content忢: $FAILED_LANES åœæ¥æªçæ(èªå·±ä¿®åŸ©äžèœ)"
fi
èªå修埩ã詊ã¿ãŠãïŒrun_lane ã®äžã§åèµ·åãããã®åŸããäžåºŠ done_lane() ã確èªããïŒãªãdone-markerãç«ã£ãŠããªããã°ãæå察å¿ãå¿
èŠãªããšãDiscordã«ç¥ãããŸãã
run_lane ã®å
éšæ§é ãèŠããšããåèµ·åâå確èªãã®ãµã€ã¯ã«ã1颿°ã«åãŸã£ãŠããŸãã
run_lane() {
local lane="$1"
local script rc
script="$(script_for "$lane")"
if done_lane "$lane"; then
log "lane=$lane status=healthy action=skip"
return 0
fi
if [ ! -f "$script" ]; then
log "lane=$lane status=missing script=$script"
return 1
fi
log "lane=$lane status=missing-done action=reinvoke script=$script timeout=1800s"
if [ "$lane" = "ameba" ]; then
run_capped 1800 bash "$script" >> "$LOG" 2>&1
else
run_capped 1800 bash "$script" apply >> "$LOG" 2>&1
fi
rc=$?
log "lane=$lane reinvoke_exit=$rc"
if done_lane "$lane"; then
log "lane=$lane status=healthy-after-reinvoke"
return 0
fi
log "lane=$lane status=failed-after-reinvoke"
return 1
}
done_lane ã§ç¢ºèª â åé¡ãªããã° skip â ããã°åèµ·å â å床 done_lane ã§ç¢ºèªããšããæµãã§ããåèµ·åã®çµäºã³ãŒãïŒrcïŒã¯ãã°ã«æ®ããŸããã倿ã«ã¯äœ¿ããŸããããçµäºã³ãŒã0ã§ãdone-markerãç¡ããã°å€±æããšãã峿 Œãããã¹ã¯ãªãããåºåãã¡ã€ã«ã«ãšã©ãŒãåããŠæ£åžžçµäºããã±ãŒã¹ãææããŸãã
å®è£ ã®è©³çް
çæç©ããäžèº«ã®ããèšäºããã3å±€ã§ç¢ºèªãã
run_lane() ãdone-markerãä¿¡é Œåºæºã«ããããšã¯ååã§èª¬æããŸãããã§ã¯ãdone-markerãç«ãŠãåã«äœããã§ãã¯ããŠããããarticle-daily-stock.sh ã®æ€èšŒå±€ãçãã§ãã
ãŸã article_ok() ããã¡ã€ã«ã®å®åšãšäžèº«ã確èªããŸãã
MIN_ARTICLE_BYTES=1200
article_ok() {
local f="$1"
[ -s "$f" ] || return 1
[ "$(stat -f%z "$f" 2>/dev/null || echo 0)" -ge "$MIN_ARTICLE_BYTES" ] || return 1
grep -qE '^title:' "$f" || return 1
grep -qiE 'request timed out|äžæãªåå|TODO: *æ¬æ|\(çæå€±æ\)' "$f" && return 1
return 0
}
4ã€ã®ã¬ãŒããçŽåã«äžŠãã§ããŸãããã¡ã€ã«ãååšãããã»1200ãã€ã以äžãããã»frontmatterã«titleè¡ããããã»ã¿ã€ã ã¢ãŠãæèšãçæå€±æãã¬ãŒãºãæ··å
¥ããŠããªããã§ãããã®æåŸã® grep -qiE ãåŸã§è©°ãŸã£ã話ã«çŽçµããã®ã§èŠããŠãããŠãã ããã
ãµã ãã®ç¢ºèªã¯å¥é¢æ° thumb_ok() ãæ
ããŸãã
thumb_ok() {
local f="$1" w; [ -s "$f" ] || return 1
w=$(sips -g pixelWidth "$f" 2>/dev/null | awk '/pixelWidth/{print $2}')
[ -n "$w" ] && [ "$w" -ge 2000 ] 2>/dev/null
}
sips ã¯macOSçµã¿èŸŒã¿ã®ç»åããŒã«ã§ããå¹
2000ãã¯ã»ã«ä»¥äžã§ãªããã°ãµã ããšããŠèªããŸããããã¡ã€ã«ãååšããŠããŒããã€ããç ŽæPNGã¯åŒŸããŸãã
ãã®2ã€ã®é¢æ°ã䜿ã£ãŠãæ¯åã®èµ·åã§å
šã¹ããã¯ãå確èªããã®ã audit_repair() ã§ãïŒ312è¡ã®å€§é¢æ°ã§ããèŠç¹ã ãåŒçšããŸãïŒã
audit_repair() {
for f in "$STOCK_ART"/[0-9][0-9]-*.md; do
if [ "$t_ok" = "missing" ] && [ "$a_ok" = "ok" ]; then
if regen_thumb "$slug" "$no" "$f"; then
t_ok=ok; fixed=$((fixed+1)); log "FIX: ãµã ãåçæ $no-$slug"
fi
fi
[ "$a_ok" = "broken" ] && broken+=("$no-$slug")
done
for nb in "${broken[@]:-}"; do
n=$(bump_attempt "$slug")
if [ "$n" -le "$MAX_ATTEMPTS" ]; then
jq --argjson t "$topic" '[$t] + (map(select(.slug != ($t.slug))))' "$QUEUE" > ...
log "REQUEUE: $slug åçæãã¥ãŒãžæå
¥(è©Šè¡ $n/$MAX_ATTEMPTS)"
else
needhuman+=("$nb (åçæ${n}å倱æ)")
fi
done
}
MAX_ATTEMPTS=2 ã宿°ã§å®£èšãããŠããŸãïŒã³ãŒã136è¡ç®ïŒããµã ããå£ããŠããã°èªååçæãæ¬æãå£ããŠããã°æå€§2åãŸã§èªåã§ãã¥ãŒã«æ»ããŠåçæã3åç®ã¯äººéã«éç¥ããŠæ¢ãŸããŸãããè©°ãŸã£ããŸãŸæŸçœ®ããããªãããã®èªå·±ä¿®åŸ©ãããã«ãããŸãã
audit_repair() ã¯èšäºçæã®åã«æ¯åèµ°ããŸãïŒã³ãŒã246è¡ç®ïŒãçææžã¿ãã©ã°ã§æ¬æ¥ã®çæãã¹ãããããŠããç£æ»ã ãã¯èµ°ãç¶ããŸããæ°èŠçæãšã¹ããã¯ã®å質ä¿èšŒãç¬ç«ãããµã€ã¯ã«ã§åããŠããã®ãèšèšã®èã§ãã
audit_repair
if [ "$MODE" = "audit" ] || [ "$SKIP_GEN" = "1" ]; then
log "===== article-daily done(audit$([ "$SKIP_GEN" = "1" ] && echo '+gen-skipped')) ====="
exit 0
fi
èµ·åã¿ã€ãã³ã°ã®çœ ã3ã€ã®ä»æãã§æœ°ã
launchdãæ¯æã¹ã¯ãªãããåŒãã§ããèµ·åçŽåŸã®ç°å¢ã¯æã£ãããäžå®å®ã§ãã3ã€ã®ä»æãããããŸãã
1. caffeinateïŒMacãã¹ãªãŒãããªãããã«ãã
if [ -z "${CAFFEINATED:-}" ]; then
exec /usr/bin/caffeinate -i -s env CAFFEINATED=1 /bin/bash "$0" "$@"
fi
exec ã§èªåèªèº«ã caffeinate ã§ã©ããããŠèµ·åãçŽããŠããŸããç°å¢å€æ° CAFFEINATED=1 ãæž¡ãããšã§äºéexecãé²ããŸãã-i ãããã»ã¹ãçããŠããéã¹ãªãŒããé²ããã©ã°ã-s ãã·ã¹ãã ã¹ãªãŒãã®é²æ¢ã§ããããããªããšãæ·±å€ã®ããããªãŒé§åæã«èšäºçæã®éäžã§Macãç ããŸãã
2. ãããã¯ãŒã¯åŸ ã¡ïŒWifiæ¥ç¶åã«APIãåŒã°ãªã
if [ "$MODE" != "audit" ]; then
for _ in $(seq 1 18); do
/usr/bin/nc -z -G 3 1.1.1.1 443 2>/dev/null && break; sleep 5
done
fi
nc ã§1.1.1.1ã®ããŒã443ã«æ¥ç¶ç¢ºèªããã€ãªãããŸã§æå€§18åïŒ90ç§ïŒç¹°ãè¿ããŸãã-G 3 ã¯3ç§ã®ã³ãã¯ãã¿ã€ã ã¢ãŠãã§ããlaunchdã¯Macãèµ·åããŠããã«ã¹ã¯ãªãããåŒã¶ããšããããããWifiæ¥ç¶ã確ç«ããåã«Claudeã®APIãå©ããŠå
šæ»
ããã±ãŒã¹ãžã®å¯ŸåŠã§ããauditå°çšã¢ãŒãã¯Claudeã䜿ããªãã®ã§ã¹ãããããŸãã
3. äºç®ãã§ãã¯ïŒããŒã¯ã³ãæ¯æžããŠãããªãäœãããªã
BUDGET=$(~/.claude/scripts/token-budget-advisor.sh --short 2>/dev/null || echo "n/a")
log "budget: $BUDGET"
if [ "$MODE" != "audit" ] && echo "$BUDGET" | grep -qE 'ðŽ|critical|cap-near'; then
log "ABORT: budget critical â æ¬¡ã¹ãããã§å詊è¡"; exit 0
fi
token-budget-advisor.sh ãããŒã¯ã³æ¶è²»ç¶æ³ãè¿ããðŽ ã critical ãå«ãŸããŠãããäœãããã«çµäºããŸããææ«ã«äºç®ãæ¯æžããŠããŠããæ¬¡ã®ã¹ãããïŒç¿æãªã»ããåŸïŒã§ãŠã©ããããã°ãèªåã§åèµ·åããã®ã§ãæåã§äœãããå¿
èŠã¯ãããŸããã
gtimeoutã®æç¡ãåžåããrun_cappedãã¿ãŒã³
content-watchdog.sh ã® run_capped() ã¯äžèŠã·ã³ãã«ã§ãããç°å¢å·®åãåžåããéèŠãªã©ãããŒã§ãã
timeout_bin() {
if [ -x /opt/homebrew/bin/gtimeout ]; then
printf '%s\n' /opt/homebrew/bin/gtimeout
else
command -v gtimeout 2>/dev/null || true
fi
}
run_capped() {
local limit="$1"
shift
local tb
tb="$(timeout_bin)"
if [ -n "$tb" ]; then
"$tb" "$limit" "$@"
else
log "gtimeout unavailable; running without timeout: $*"
"$@"
fi
}
homebrewã®çµ¶å¯Ÿãã¹ãå
ã«ç¢ºèªãããªããã° command -v ã§PATHãæ¢ããŸããäž¡æ¹ãªããã°ã¿ã€ã ã¢ãŠããªãã§å®è¡ãã€ã€ãã°ã«æ®ããŸãã|| true ã§ãšã©ãŒã«ããªãç¹ããã€ã³ãã§ããgtimeoutãç¡ãããã§ãŠã©ããããã°å
šäœãèœã¡ãããšããäºæ
ãé²ãã§ããŸããarticle-daily-stock.sh ãåãææ³ã§ run_to ãã«ããæã£ãŠããŸãã
TIMEOUT_BIN="/opt/homebrew/bin/gtimeout"
[ -x "$TIMEOUT_BIN" ] || TIMEOUT_BIN="/opt/homebrew/bin/timeout"
[ -x "$TIMEOUT_BIN" ] || TIMEOUT_BIN=""
run_to() { local s=$1; shift; if [ -n "$TIMEOUT_BIN" ]; then "$TIMEOUT_BIN" --kill-after=30 "$s" "$@"; else "$@"; fi; }
ãã¡ã㯠--kill-after=30 ã远å ããŠããŠãã¿ã€ã ã¢ãŠãåŸã«SIGKILLãéããŸã§30ç§ã®ç¶äºãæãããŠããŸããClaudeã®ããã»ã¹ã¯ SIGTERM ãåãåã£ãŠã峿»ããªãããããã®äºæ®µæ§ããå¿
èŠã§ãã
ãã¥ãŒã空ã«ãªã£ãŠãæ¢ãŸããªãèªåãã¿è£å
èšäºã®ãã¿ã¯ ~/.topic-queue.json ã«æ ŒçŽãããŠããŸãããã®ãã¥ãŒã空ã«ãªã£ããšããã¹ã¯ãªããã¯æ¢ãŸãã®ã§ã¯ãªã claude -p ã§ãã¿ã1æ¬èªåç«æ¡ããŠãã¥ãŒã«è¿œå ããŸãã
QLEN=$(jq 'length' "$QUEUE" 2>/dev/null || echo 0)
if [ "$QLEN" -eq 0 ]; then
log "queue 空 â å®äœæ¥ãããã¿èªåç«æ¡"
# ...é·ãããã³ããçµã¿ç«ãŠ...
TOPIC_JSON=$(run_to 600 "$CLAUDE" -p "$REPLENISH_PROMPT" \
--model "${ARTICLE_MODEL:-claude-sonnet-4-6}" --effort high \
--output-format text --allowedTools "Read,Grep,Glob,Bash" --max-turns 20 2>>"$LOG")
--max-turns 20 ãéèŠã§ãããã¿ç«æ¡ã®ããã«Claudeãå®ãã¡ã€ã«ãReadããŠããåºåããåŸåŸ©ã¿ãŒã³æ°ã®äžéã§ããããããªããšãClaudeã念å
¥ãã«èª¿ã¹ãããŠããŒã¯ã³ãé£ã朰ããŸãã
ç«æ¡ããããã¿ãæ¢åã®slugãšéè€ããŠããªããã確èªããŸãã
if used_slugs | grep -qx "$NEW_SLUG"; then
log "ABORT: ç«æ¡slug '$NEW_SLUG' ãæ¢åº â æ¬¡ã¹ãããå詊è¡"; exit 0
fi
used_slugs() ã¯ããã¥ãŒã»å®äºæžã¿ãªã¹ãã»æ¢åèšäºãã¡ã€ã«ã»coverage.jsonã®4ãœãŒã¹ããslugãéããŠéè€ãã§ãã¯ããŸããããããªããšãå
šãåããã¿ã®èšäºãéç£ãããããšããäºæ
ãèµ·ããŸãïŒå®éã«èµ·ããŸãããåŸè¿°ïŒã
ç§ãè©°ãŸã£ã話
ãã¹ã¯ãªããã¯èµ°ã£ããã®ã«èšäºã186ãã€ãã ã£ã
æåã®å€±æã¯ããã§ããlaunchdã®ãã°ã«ã¯æ£åžžçµäºãèšé²ãããŠããŸããããã¡ã€ã«ãååšããããããäžèº«ãéããšä»¥äžã®å 容ã ãã§ããã
---
title: "Claude Codeã䜿ã£ãèªåå"
emoji: "ð€"
type: "tech"
published: true
---
request timed out
Claude CodeèªåŸã»ãã·ã§ã³ã30ç§ã®ãããã¯ãŒã¯ã¿ã€ã ã¢ãŠãã§åãããã®ãšã©ãŒã¡ãã»ãŒãžãåºåãã¡ã€ã«ã«æµã蟌ãã§ããŸãããã¹ã¯ãªããã®çµäºã³ãŒãã¯0ã[ -s "$ART" ] ã®ç¢ºèªããã¹ããããã®ç¶æ
ããæåããšããŠæ±ã£ãŠãããããç¿æã®ãŠã©ããããã°ããdone-markerãããã¹ãããããšå€æããŠèšäºã¯æ°žé ã«è£ä¿®ãããŸããã§ããã
çŽãæ¹ã¯ article_ok() ã®è¿œå ã§ããgrep -qiE 'request timed out' ãæ€èšŒæ¡ä»¶ã«å ãããã®ãã¬ãŒãºãæ¬æã«å«ãŸããŠãããçæå€±æãšããŠæ±ããã¡ã€ã«ãåé€ããŸããdone-markerãç«ãŠãã«çµäºããã®ã§ã次ã®ã¹ãããã§ãŠã©ããããã°ãåèµ·åããŸãã
æèšïŒçµäºã³ãŒã0ã¯ã¹ã¯ãªãããèµ°ã£ããšãã蚌æã§ãã£ãŠãåºåãæ£ãããšãã蚌æã§ã¯ãªããäžèº«ãèŠãªãéããçæã®æåŠã¯åãããŸããã
done-markerãgit pushã®åŸã«ç«ãŠãŠããæä»£
åœåã®å®è£ ã§ã¯ãgit pushãæåããããšãdone-markerãç«ãŠãæ¡ä»¶ã«ããŠããŸãããè«ççã«èãããŸãããå ¬éãŸã§å«ããŠå®äºããšããèãæ¹ã§ãã
ãããææ«ã«è©°ãŸããŸãããGitHubã®APIã¬ãŒããªãããã«ã²ã£ããããpushãé£ç¶ããŠå€±æããŸããããããšãŠã©ããããã°ã¯æ¯ã¹ããããdone-markerãªããåèµ·åããšå€æããåãæ¥ã«åããã¿ã§3æ¬ã®èšäºãçæãããŸãããå 容ã¯åŸ®åŠã«éãããã¡ã€ã«åã¯çªå·ãé£çªã§ã€ããŠãããæ··ä¹±ããŸããã
ã³ãŒãã®ã³ã¡ã³ãããã®çµç·¯ãèšé²ããŠããŸãïŒ453è¡ç®ïŒã
# çææåïŒãã®æç¹ã§åœæ¥doneã確å®ããã以éã®git pushã¯best-effortïŒå€±æããŠãçæã¯æåæ±ãïŒ
# ãªã®ã§ãgit倱æã§ããŒã«ãŒæªèšå®âç¿ã¹ãããã§éè€çæããšããäºæ
ãé²ãããããã§å
ã«ç«ãŠãã
touch "$DONE_MARKER"
ãã¥ãŒã®popã»done-queueãžã®èšé²ã»ããŒã«ãŒã®èšçœ®ããã¹ãŠgit pushããåã«æžãŸããããã«ãªããŸãããpushã¯ãããŸã§ãZennãªããžã®ãã¹ããšãã©ãŒãé
ä¿¡ãã§ãããèšäºã®çæãšã¹ããã¯é
眮ãšã¯ç¬ç«ãã責åã§ãã念ã®ããgit pushã®çŽåŸã«ã touch "$DONE_MARKER" ãå床åŒãã§ããŸãïŒ468è¡ç®ïŒãã¹ãçãªæäœãªã®ã§ã³ã¹ãã¯ãŒãã§ãã
Macãæ·±å€ã«ã¹ãªãŒãããŠèšäºçæãéäžã§æ»ãã§ãã
MacBook AirãããããªãŒé§åã§äœ¿ã£ãŠããå€ãåå2æã«èšäºçæãèµ°ã£ãŠOSãã¹ãªãŒãããŸãããlaunchdã¯ããã»ã¹ãèµ·åããŸãããããã£ã¹ã¯I/Oãæ¢ãŸããClaudeã®ã»ãã·ã§ã³ãäžæãããèšäºã¯éäžã§çµãããŸããããã¡ã€ã«ã¯ååšããããµã€ãºã500ãã€ããããã§ãæ¬æãéäžã§åããŠãããarticle_ok() ãã1200ãã€ãæªæºãã§åŒŸãã®ã§ç¿æ¥ã«åçæãããŸãããããããŸã§æ°ã¥ããŸããã§ããã
caffeinate ã远å ããåŸã¯ãèšäºçæã®å®è¡äžã¯Macãã¹ãªãŒãããªããªããŸããã-s ãã©ã°ïŒã·ã¹ãã ã¹ãªãŒã鲿¢ïŒãããã®ã§ããµããéããŠãç ããŸããããã®ä»æããªãã«ãæ¯æèªåçæãã¯æç«ããŸããã§ããã
amebaã¬ãŒã³ã ãdone-markerãååšããªãã£ã
amebaçšã® generate.sh ã¯ç§ãæžããã®ã§ã¯ãªããæ¢åã®ãããžã§ã¯ãã«åŸãããŠã©ããããã°ãç¹ãã ãã®ã§ãããã®ã¹ã¯ãªããã«ã¯done-markerã®ä»çµã¿ããããŸããã§ããã
ä»ã®4ã¬ãŒã³ãšåãããã« .ameba-daily-done-20260710 ã確èªããããšããŠãããããªãã¡ã€ã«ã¯æ°žé ã«ååšããŸããããŠã©ããããã°ã¯æ¯åãæªå®äºããšå€æããŠåèµ·åãç¹°ãè¿ããŸããã
仿¹ãªã done_lane() ã®amebaåå²ã ãå¥ã®ç¢ºèªæ¹æ³ã«ããŸããã
ameba)
local ad td f
ad="$HOME_DIR/Desktop/Article/ameba"
td="$(date +%F)"
grep -rlq "created:.*$td" "$ad" 2>/dev/null && return 0
for f in "$ad"/*.md; do
[ -f "$f" ] || continue
[ "$(stat -f %Sm -t %F "$f" 2>/dev/null)" = "$td" ] && return 0
done
return 1
;;
åœæ¥ã® created: ã¡ã¿ããŒã¿ãæã€mdãã¡ã€ã«ãååšããããgrepã§ç¢ºèªãããªããã°mtimeã仿¥ã®ãã¡ã€ã«ãããããstatã§ç¢ºèªããŸãã2段éã®ãã©ãŒã«ããã¯ã§ãããèšèšã®å€åŽã«ããæ¢åã¹ã¯ãªããããŠã©ããããã°ã«åã蟌ãéã®çŸå®çãªåŠ¥åç¹ããšã³ãŒãã®ã³ã¡ã³ãã«æžããŸããããããã¯å®éã«ããã§ãçæ³ã¯ generate.sh åŽã«done-markerã远å ããããšã§ãããã æ¢åã¹ã¯ãªãããžã®æãå ããã³ã¹ããšããã©ãŒã«ããã¯ã§åããã³ã¹ããæ¯ã¹ãŠãã©ãŒã«ããã¯ãéžã³ãŸããã
åããã¿ã§10æ¬ã®èšäºã䞊ãã æ¥
slugéè€ãã§ãã¯ã远å ããåã®è©±ã§ãããã¥ãŒã空ã«ãªããèªåãã¿è£å
ãèµ°ããŸãããClaudeãåºåããslug㯠claude-code-automation ã§ãããããã¯æ¢åã®done-queueã«ãã§ã«ååšããŠããŸãããéè€ãã§ãã¯ããªãã£ãã®ã§ããã¥ãŒã«è¿œå ãããç¿æ¥ãŸãåããã¿ãçæãããŸããã10æ¥åŸã«ç¢ºèªããã 01-claude-code-automation.md ãã 10-claude-code-automation.md ãŸã§äžŠãã§ããŸãããå
å®¹ã¯æ¯å埮åŠã«éããŸãã
used_slugs() 颿°ã¯ããã®äºæ
ã®åŸã«è¿œå ãããŸããã4ãœãŒã¹ïŒãã¥ãŒã»å®äºæžã¿ã»æ¢åãã¡ã€ã«ã»coverage.jsonïŒãçµ±åããããšã§ãã©ã®ã¿ã€ãã³ã°ã§éè€ãã§ãã¯ããŠãæŒãããããŸããã
ããã¯ãæ®éªžã«ãªã£ãŠãã®æ¥ã®èšäºçæãå šéšæ¢ãŸã£ã
ããã»ã¹ã kill -9 ã§åŒ·å¶çµäºããããšãtrap ãçºç«ããã«ããã¯ãã£ã¬ã¯ããªãæ®ããŸããããããããšç¿ã¹ãããã®ãŠã©ããããã°ããlock held; skipããšå€æããŠããã«çµäºããŸãã
2æ¥åŸã«Discordã® ð ãå±ããªãããšã§æ°ã¥ããŸããããã°ã確èªããããlock held; skipãã100è¡ä»¥äžäžŠãã§ããŸããã
acquire_lock() ã®1800ç§staleæ€ç¥ïŒååã§åŒçšããã³ãŒãïŒã¯ããã®äºæ
ãåããŠè¿œå ãããã®ã§ããããã¯ã1800ç§ïŒ30åïŒæŸçœ®ãããŠããã°åŒ·å¶åŒãç¶ãããŸããèšäºçæã®æé·ã¿ã€ã ã¢ãŠãã1800ç§ãªã®ã§ãæ£åžžãªããã»ã¹ãèµ°ã£ãŠããã°å¿
ããã®æéå
ã«ããã¯ãè§£æŸããŸããstaleå€å®ãããå Žåã ãåŒãç¶ããèµ·ããŸãã
ãã®å¯ŸåŠãå
¥ããŠããããDiscordã® ð ãæ¥ãªãæ¥ãã¯ãŒãã«ãªããŸããããŠã©ããããã°ãè©°ãŸã£ãŠãDiscordã« ðš ãé£ãã§ããã®ã§ãæã®ã³ãŒããŒã飲ã¿ãªããéç¥ã確èªããã ãã§å
š5ã¬ãŒã³ã®å¥å
šæ§ãææ¡ã§ããŸããiOSã¢ããª10æ¬ã®éçºãšæãæã¡ããªããæå120äžãç¶æã§ããŠããçç±ã®äžã€ã¯ããã®ã確èªããªããŠããèšèšãã«ãããŸãã
ã€ãŸãããã€ã³ã
ååãšäžæ®µã§ãå®éã«è©°ãŸã£ã話ãã6æ¬åãäžããŸãããããã§ã¯å®è£ ãèŠçŽãäžã§æ°ã¥ããããã现ããèœãšã穎ãç®æ¡æžãã§ãŸãšããŸãããããã¯ãåããŠããããã«èŠããã®ã«ãæ°ã¥ãããå£ããŠãããç³»ã®ãã®ã°ããã§ãã
launchdã¯PATHãåŒãç¶ããªã
content-watchdog.sh ã®åé 4è¡ç®ã«ããæžããŠãããŸãã
export PATH="~/.nvm/versions/node/v24.13.0/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin"
ãããç¡ããš gtimeout ã claude ã jq ãèŠã€ãããŸãããlaunchdã§èµ·åããããã»ã¹ã® PATH 㯠/usr/bin:/bin:/usr/sbin:/sbin ã ãã§ããã¿ãŒããã«ã§åãã®ã«launchdã§åããªããšããçç¶ã®9å²ã¯ããã§ããarticle-daily-stock.sh åŽã¯å¥ã®ã¢ãããŒãã§è§£æ±ºããŠããŸãã
NODE_BIN=$(ls -d "$HOME"/.nvm/versions/node/*/bin 2>/dev/null | sort -V | tail -1)
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$PATH"
[ -n "$NODE_BIN" ] && export PATH="${NODE_BIN}:$PATH"
sort -V ã§ããŒãžã§ã³é ã«ãœãŒãããŠææ°ã®nvmãã€ããªãåçã«çºèŠããŸããclaudeã®ãã€ããªããã®çŽåŸã«3段ãã©ãŒã«ããã¯ã§è§£æ±ºããŠããŸãïŒcommand -v â ~/.local/bin â nvmé
äžã ls -t ã§æ¢çŽ¢ïŒãã©ããã§èŠã€ãããªããã° ABORT: claude binary not found ãšèšé²ã㊠exit 0 ããŸããexit 1 ã§ãªã exit 0 ãªã®ã¯ãlaunchdã«ã倱æããšããŠèšé²ãããŠãªãã©ã€ã«ãŒãã«å
¥ããããšå°ãããã§ãã
note/makerã¬ãŒã³ã®done-markerã¯globæ€çŽ¢
article ãš series ã¯ãã¡ã€ã«åãåºå®ã®é ããã¡ã€ã«ïŒ.article-daily-done-20260710ïŒã§ãããnote ãš maker ã¯éããŸãã
note)
hit="$(find "$LOG_DIR/note-daily" -maxdepth 1 -name "$TODAY-*.done" -print -quit 2>/dev/null || true)"
[ -n "$hit" ]
;;
20260710-*.done ãšããã¯ã€ã«ãã«ãŒããã¿ãŒã³ã§ãããªãããšãããšãnoteãš maker ã¯1æ¥ã«è€æ°ãããã¯ãçæãããããšãããããããã¡ã€ã«åã«ãããã¯ã®slugãå
¥ããŸããåºå®åã®done-markerã ãšãã©ã®ãããã¯ãå®äºãããããåãããªããªããŸãã-print -quit ã§1ä»¶èŠã€ãã£ããããçµäºããã®ã¯ããã¡ã€ã«æ°ãå¢ãããšãã« find ãå
šä»¶èµ°æ»ããã®ãé²ãããã§ãã
å®è£ æã«ãããå šã¬ãŒã³åäžãã¿ãŒã³ã§æžããŠããŸããšãnoteã¬ãŒã³ã®done-markerãæ°žé ã«ãèŠã€ãããªããç¶æ ã«ãªããŸããã¬ãŒã³ããšã«ç¢ºèªããžãã¯ãéãããšãæåããææ¡ããŠããå¿ èŠããããŸãã
gtimeoutã®SIGTERMã§æ¢ãŸããªãããã»ã¹ãžã®å¯ŸåŠ
Claudeã®ããã»ã¹ã¯ SIGTERM ãåãåã£ãŠãå³åº§ã«çµäºããŸãããäŒè©±ã®éäžã§ããŒã¯ã³ãåŠçããŠããæäžã« SIGTERM ãæ¥ããšãæ°ç§ããæ°åç§ãããŠåŠçãçµããŠããçµäºããŸããarticle-daily-stock.sh ã® run_to ãã«ããŒã --kill-after=30 ãä»ããŠããã®ã¯ãã®ããã§ãã
run_to() { local s=$1; shift; if [ -n "$TIMEOUT_BIN" ]; then "$TIMEOUT_BIN" --kill-after=30 "$s" "$@"; else "$@"; fi; }
gtimeout ã® --kill-after=30 ã¯ãã¿ã€ã ã¢ãŠãåŸã«SIGTERMãéãã30ç§åŸ
ã£ãŠããŸã çããŠããã°SIGKILLãéãããšãã2段æ§ãã§ããããããªããšãã¿ã€ã ã¢ãŠãåŸã®ããã»ã¹ããŸã³ãã®ããã«æ®ãç¶ããæ¬¡ã®ã¹ãããã§ãŠã©ããããã°ãèµ·åããéã«äºéå®è¡ããã¯ã«ã²ã£ããããŸãã
jqãèŠã€ãããªããšåŸç¶ã®å šåŠçããµã€ã¬ã³ã倱æãã
ãã¥ãŒã®åŠçã»coverage.jsonã®æŽæ°ã»used_slugs()ã®éè€ãã§ãã¯ã¯ãã¹ãŠ jq ã䜿ããŸããarticle-daily-stock.sh ã«ã¯æç€ºçãªãã§ãã¯ããããŸãã
command -v jq >/dev/null 2>&1 || { log "ABORT: jq not found"; exit 0; }
ãããå
¥ããåã¯ãjq ãç¡ãç°å¢ã§ã¹ã¯ãªãããèµ°ãããéã« jq: command not found ããã°ã®å¥¥æ·±ãã«åãŸãããã¥ãŒãæ£ããèªããŠããããã«èŠããŠå®ã¯ç©ºãšããŠæ±ãããæ¯åãã¿èªåè£å
ãèµ°ããšããçŸè±¡ãèµ·ããŸããã
ãã°ãèšãã¿ç¶ããŠãã£ã¹ã¯ãé£ã
ãŠã©ããããã°ã¯1æ¥ã«è€æ°åèµ·åãããŸããèšäºçæã®ãã³ã«Claudeã®åºåãå
šéšãã°ã«æµã蟌ããããäœãããªããšæ°æ¥ã§ãã°ãã¡ã€ã«ã巚倧ã«ãªããŸããarticle-daily-stock.sh ã¯5MBãè¶
ããæç¹ã§ããŒããŒãããŸãïŒã³ãŒã78è¡ç®ïŒã
[ -f "$LOG" ] && [ "$(stat -f%z "$LOG" 2>/dev/null || echo 0)" -gt 5242880 ] && mv "$LOG" "$LOG.old"
content-watchdog.log åŽã«ã¯ããŒããŒããå®è£
ãããŠããªããããé·æéçšã§ã¯æåã§ç¢ºèªããå¿
èŠããããŸããããã¯ä»ã®éçšã§ã®æªè§£æ±ºäºé
ã®ã²ãšã€ã§ãã
ãç£æ»ã¯æ¯åèµ°ããçæã¯1æ¥1åããèµ°ããªãããšããåé¢
article-daily-stock.sh ã®èµ·åçŽåŸã« SKIP_GEN ãã©ã°ãèšå®ãããŸãã
SKIP_GEN=0
[ "$MODE" = "apply" ] && [ -f "$DONE_MARKER" ] && SKIP_GEN=1
ãã®åŸ audit_repair() ãå¿
ãå®è¡ãããŸããSKIP_GEN=1 ã§ãç£æ»ã¯æ¢ãŸããŸããïŒã³ãŒã246è¡ç®ïŒã
audit_repair
if [ "$MODE" = "audit" ] || [ "$SKIP_GEN" = "1" ]; then
log "===== article-daily done(audit$([ "$SKIP_GEN" = "1" ] && echo '+gen-skipped')) ====="
exit 0
fi
ã€ãŸããŠã©ããããã°ã1æ¥ã«è€æ°åã¹ã¯ãªãããåŒãã§ãã2åç®ä»¥éã¯ãæ¢åã¹ããã¯ã®ç£æ»ã»èªå·±ä¿®åŸ©ãã ããå®è¡ããŠçµãããŸããçæã®äºéå®è¡ãšã¹ããã¯ã®å質å£åãç¬ç«ããŠå¶åŸ¡ãããŠããŸãããã®åé¢ãçè§£ããŠããªããšããªããŠã©ããããã°ãæ¯åã¹ã¯ãªãããåŒã¶ã®ã«çæã¯1åãªã®ãããåãããªããªããŸãã
ãã¹ããã©ã¯ãã£ã¹
å®éã«å£ããŠçŽããçµéšããããæåããããããŠããã°ããã£ãããšããèšèšå€æã10å以äžãŸãšããŸãã
1. å®äºã®èšŒæã¯ãã¡ã€ã«ã®å®åšã«ãã
ãã°ã®å
容ã»ããã»ã¹ã®çµäºã³ãŒãã»printfã®åºåã¯ãã¹ã¯ãªãããèµ°ã£ãäºå®ããã蚌æããŸããã~/.claude/logs/.article-daily-done-20260710 ãšãããã¡ã€ã«ãååšãããã©ããã ããçå®ãšããŠæ±ãèšèšã«ãããšãã©ããªå€±æãã¿ãŒã³ã§ã次ã®ã¹ãããã§åèµ·åãããŸãã
2. çæãšé ä¿¡ã¯ç¬ç«ãã責åã«ãã
done-markerãgit pushã®æåŠã§å€å®ãããšããããã¯ãŒã¯é害ãAPIã¬ãŒããªããããçæã®å€±æã«èŠããŠããŸããŸããarticle-daily-stock.sh ã®453è¡ç®ã®ã³ã¡ã³ãã瀺ããšãããçææåã®æç¹ã§done-markerãç«ãŠãpushã¯ãã¹ããšãã©ãŒãã«ããŸããtouch "$DONE_MARKER" ã¯pushåŸã«ãå床åŒã°ããŸããïŒ468è¡ç®ïŒãã¹ãçãªæäœãªã®ã§ã³ã¹ãã¯ãŒãã§ãã
3. äžèº«ãæ€èšŒããŠããdone-markerãç«ãŠã
article_ok() ã®4ã€ã®ã¬ãŒããééããªãéããdone-markerã¯ç«ã¡ãŸããã
article_ok() {
local f="$1"
[ -s "$f" ] || return 1
[ "$(stat -f%z "$f" 2>/dev/null || echo 0)" -ge "$MIN_ARTICLE_BYTES" ] || return 1
grep -qE '^title:' "$f" || return 1
grep -qiE 'request timed out|äžæãªåå|TODO: *æ¬æ|\(çæå€±æ\)' "$f" && return 1
return 0
}
1200ãã€ãã»frontmatterã®titleè¡ã»ã¿ã€ã ã¢ãŠãæèšã®äžåšãšãã3ç¹ãã¯ãªã¢ããŠåããŠãèšäºãšããŠæç«ããŠããããšå€æããŸããçµäºã³ãŒã0ã¯äœã蚌æããªãããšããååã®å®è£ ã§ãã
4. èªå修埩ã®è©Šè¡åæ°ã«äžéãèšãã
MAX_ATTEMPTS=2
ãã®å®æ°ã¯ article-daily-stock.sh ã®136è¡ç®ã«ãããŸããå£ããèšäºã®åçæã2åãŸã§èªåã§è©Šã¿ã3åç®ã¯ needhuman ãªã¹ãã«ç©ãã§éç¥ããŸããç¡éã«åçæãç¹°ãè¿ããšããã¥ãŒãç ŽæããŠããããClaudeãããçš®ã®å
¥åãåŠçã§ããªãå Žåã«ããŒã¯ã³ãç¡é§ã«æ¶è²»ããŸãã
5. éè€ãã§ãã¯ã¯4ãœãŒã¹ãã
used_slugs() ã¯4ãœãŒã¹ãçµ±åããŸãã
used_slugs() {
{ jq -r '.[].slug' "$QUEUE" 2>/dev/null
jq -r '.[].slug' "$DONEQ" 2>/dev/null
ls "$ARTICLES" 2>/dev/null | sed 's/\.md$//'
jq -r '.[].slug' "$COVERAGE" 2>/dev/null
} | sort -u
}
ãã¥ãŒã»å®äºæžã¿ã»æ¢åãã¡ã€ã«ã»coverage.jsonã®ã©ããã²ãšã€ã§ãæ¬ ãããšãéè€ãã¿ãçæãããŸãããåããã¿ã§10æ¬äžŠãã ããšããäºæ ãèµ·ããã®ã¯coverageã®ç¢ºèªã ããæããŠããããã§ãã
6. mkdirã§ç«¶åããã¯ãåã
mkdir ã¯POSIXã¬ãã«ã§ã¢ãããã¯ã§ããflock 㯠Linux/macOS ã§æåã埮åŠã«ç°ãªããŸãããmkdir ã®ååæ§ã¯ã©ã¡ãã§ãä¿èšŒãããŠããŸããcontent-watchdog.sh ã®ããã¯ãã£ã¬ã¯ããªåã .lockdïŒæ«å°ŸdïŒã«ãªã£ãŠããã®ã¯directoryã§ããããšãæç€ºããããã§ãã
7. stale lockã1800ç§ã§èªååŒãç¶ããã
åã¬ãŒã³ã®ã¿ã€ã ã¢ãŠãã1800ç§ãªã®ã§ãæ£åžžãªããã»ã¹ãèµ°ã£ãŠããã°å¿ ããã®æéå ã«ããã¯ãè§£æŸããŸãã
if [ "$age" -ge 1800 ]; then
log "lock stale age=${age}s; taking over"
rm -rf "$LOCKDIR"
...
ãã®å€ã¯ãäœç§ã§åŒãç¶ããããšãã¬ãŒã³ã¹ã¯ãªããã®ã¿ã€ã ã¢ãŠãããäžèŽãããããšã§ã誀åŒãç¶ããé²ãã§ããŸããæ°åã«äžè²«æ§ãæãããããšããã€ã³ãã§ãã
8. çµäºã³ãŒãã§ãªãdone-markerã§åèµ·åã®èŠåŠã倿ãã
run_lane() ã¯åèµ·ååŸã®çµäºã³ãŒãïŒrcïŒããã°ã«æ®ããŸããã倿ã«ã¯äœ¿ããŸããã
rc=$?
log "lane=$lane reinvoke_exit=$rc"
if done_lane "$lane"; then
log "lane=$lane status=healthy-after-reinvoke"
return 0
fi
çµäºã³ãŒã0ã§ãdone-markerãç¡ããã°åèµ·åæ±ããçµäºã³ãŒã1ã§ãdone-markerãããã°æåæ±ããã¹ã¯ãªãããåºåãã¡ã€ã«ã«ãšã©ãŒãæµã蟌ãã§æ£åžžçµäºããã±ãŒã¹ïŒã¿ã€ã ã¢ãŠãæèšæ··å ¥ïŒãæ£ç¢ºã«ææã§ããŸãã
9. caffeinate execã§æ·±å€ã¹ãªãŒããé²ã
ããããªãŒé§åã®Macã§æ·±å€ã«èšäºçæãèµ°ããšOSãã¹ãªãŒãããŸãã
if [ -z "${CAFFEINATED:-}" ]; then
exec /usr/bin/caffeinate -i -s env CAFFEINATED=1 /bin/bash "$0" "$@"
fi
exec ã§èªåèªèº«ã caffeinate ã§ã©ããããŠèµ·åãçŽããŸãã-s ãã©ã°ã¯ã·ã¹ãã ã¹ãªãŒãã®é²æ¢ã§ããµããéããŠãç ããŸãããCAFFEINATED=1 ãç°å¢å€æ°ã§æž¡ããŠäºéexecãé²ããŸãã
10. ãããã¯ãŒã¯åŸ ã¡ãå¿ ãå®è£ ãã
launchdã¯Macèµ·åçŽåŸã«ããã»ã¹ãåŒã¶ããšããããŸããWifiæ¥ç¶ã確ç«ããåã«Claudeã®APIãå©ããšå šã¬ãŒã³ã倱æããŸãã
for _ in $(seq 1 18); do
/usr/bin/nc -z -G 3 1.1.1.1 443 2>/dev/null && break; sleep 5
done
æå€§18åÃ5ç§ïŒ90ç§åŸ
ã¡ãŸãã-G 3 ã§ã³ãã¯ãã¿ã€ã ã¢ãŠãã3ç§ã«æããããšã§ãWifiãã€ãªãã£ãŠããã°æ°ç§ã§æããŸããaudit ã¢ãŒãã¯Claudeã䜿ããªãã®ã§ãã®ã«ãŒããã¹ãããããŸãïŒã³ãŒã87è¡ç®ïŒã
11. äºç®ãã§ãã¯ãAPIãå©ãåã«æžãŸãã
BUDGET=$(~/.claude/scripts/token-budget-advisor.sh --short 2>/dev/null || echo "n/a")
if echo "$BUDGET" | grep -qE 'ðŽ|critical|cap-near'; then
log "ABORT: budget critical â æ¬¡ã¹ãããã§å詊è¡"; exit 0
fi
ææ«ã«äºç®ãæ¯æžããŠããŠããç¿æãªã»ããåŸã®ã¹ãããã§èªåçã«ãŠã©ããããã°ãåèµ·åããŸãããææ«ã«å šéšæ¢ãŸã£ãŠæåã§åéããããšããäœéšãããŠãã远å ãã仿ãã§ãã
12. ããŒãããŒãã¯1æ¥1åã倱æéç¥ã¯æ¯å
æåãDiscordã§æ¯åéç¥ãããšéç¥ç²ããèµ·ããŸããsend_heartbeat_once() 㯠HEARTBEAT_MARKER ã§ååã ãã«çµããŸãã倱æéç¥ïŒðšïŒã¯æ¯åé£ã°ããŸãã
notify alerts "ð content-watchdog heartbeat: å
šã¬ãŒã³åœæ¥çææžã¿"
touch "$HEARTBEAT_MARKER"
察ããŠå€±æéç¥ã¯ if [ -n "$FAILED_LANES" ] ã§æ¯åæ¡ä»¶ãè©äŸ¡ããŸãããéããªæåã»ãããã倱æããšããèšèšã§ããæã«Discordãéã㊠ð ãããã°å
šã¬ãŒã³æ£åžžãðš ãããã°æå察å¿ãå¿
èŠã ãšåãããŸãã
13. amebaçã®æ¢åã¹ã¯ãªããã¯done-markerãã宿æç©ã確èªãã
æ¢åã¹ã¯ãªããã«åŸãããŠã©ããããã°ãç¹ãéãã¹ã¯ãªããã«done-markerãè¿œå æ¹ä¿®ããã³ã¹ããé«ãå ŽåããããŸããameba ã¬ãŒã³ã®ãã©ãŒã«ããã¯å®è£
ïŒcreated: ã¡ã¿ããŒã¿ãšmtimeã®2段é確èªïŒã¯ãã®çŸå®çãªåŠ¥åç¹ã§ããçæ³ã¯ã¹ã¯ãªããåŽã«done-markerã远å ããããšã§ããããåããããšãå®ç§ã«ãããã®ã©ã¡ããåªå
ãããã¯ç¶æ³æ¬¡ç¬¬ã§ãã
ãŸãšã
ãã®èšäºã§ã¯ content-watchdog.sh ãš article-daily-stock.sh ã®å®ã³ãŒããèªã¿ãªããããè©°ãŸã£ãŠãèªåã§çŽããèšèšã®æ žå¿ã確èªããŠããŸããã
äžå¿ã«ããã®ã¯3ã€ã®ã¢ã€ãã¢ã§ãã
å®äºã¯ãã¡ã€ã«ã®å®åšã§èšŒæããã ãã°ã®æååã§ãçµäºã³ãŒãã§ããªãã~/.claude/logs/.article-daily-done-20260710 ãååšãããã©ããã ããçå®ãšããŠæ±ããŸããããã«ããããã¹ã¯ãªããã¯èµ°ã£ããäžèº«ããªãããšãã倱æãã¿ãŒã³ãå
šéšææã§ããŸãã
çæãšé
ä¿¡ã¯ç¬ç«ãã責åã«ããã git pushã®æåŠãdone-markerã®æ¡ä»¶ã«ãããšããããã¯ãŒã¯é害ãçæã®å€±æã«èŠããŸããèšäºã ~/Desktop/Article ã«æžããæç¹ãæåãšããé
ä¿¡ã¯ãã¹ããšãã©ãŒãã«ããŸããææ«ã®APIã¬ãŒããªããããåœæ¥åã®èšäºçæã«åœ±é¿ããªããªããŸããã
ç£æ»ã¯æ¯åèµ°ãããçæã¯1æ¥1åã ãèµ°ãããã audit_repair() 㯠SKIP_GEN=1 ã§ãå®è¡ãããŸããæ°èŠçæãã¹ãããããæ¥ã§ããæ¢åã¹ããã¯ã®å質確èªãšå£ããèšäºã®åçæãéãã«åãç¶ããŸãããã®åé¢ããªããã°ããçææžã¿ãã©ã°ãããæ¥ã«æ¢åèšäºãå£ããŠããŠãèª°ãæ°ã¥ããªãããšããç©Žãæ®ããŸãã
è§£éããå幎ã§iOSã¢ããª10æ¬ãæãæã¡ããªããæå120äžãç¶æã§ããŠããã®ã¯ããã®ã確èªããªããŠããèšèšããããããã§ããæ¯æDiscordã« ð ãæ¥ãããšã確èªããã ãã§ã5æ¬ã®ã³ã³ãã³ãã¬ãŒã³ãæ£åžžã§ããããšãåãããŸãããŠã©ããããã°ãååšããåã¯ãã仿¥ãåããŠããã¯ãããšããäžå®ãæ±ããªããæåã§ãã°ã確èªããŠããŸããããã®æéãšäžå®ã³ã¹ãããŒãã«ãªã£ãããšããä»ã®éçºã«éäžã§ããäœçœãçãã§ããŸãã
ä»çµã¿ã®å šäœåã»æ120äžã®å èš³ã»30æ¥æé ã¯æænoteã«ãŸãšããŠããŸãã
ð Claude CodeèªåŸç°å¢ã§ãå®éã©ã皌ãã â ä»çµã¿ã»å®äŸã»å§ãæ¹ã»ãµããŒã
LilyïŒ@bokuwalilyïŒâ å人éçºè ãClaude Code ã§èªåååºç€ãçµã¿ãªãããiOSã¢ããªãWebãµãŒãã¹ãéç£ããŠããŸã
- å¶äœç©ã»èšäºã¯ bokuwalily.com ã«ãŸãšããŠããŸãð¥ïž
- AIã§ãå¯ãŠãŠãåãä»çµã¿ããäœã£ãŠæ120äžã«ãã話㯠noteã®ææèšäº ã«ð°
- OSS: github.com/bokuwalily ð
- ææ°æ å ±ã»ãåãåãã㯠X @bokuwalily ãžð
çããã® â€ïž ãã·ã§ã¢ãå±ã¿ã«ãªããŸãïŒ