đ ãå¤ąæããããã1åãã¯åŖãããĒããŠã¤ââåž ã¤ãģčĢĻãããģæģããŽ3į¯ã§æ¸ãį´ã
æå120ä¸ãŽčĒååį°åĸããããæ5æŦåæãĢæĸãžãŖãĻããžããããã°ãéãããå ¨é¨ãĢåãčĄãä¸Ļãã§ããããå¤ąæãããŽã§ãã1ååŽčĄããžãããããã1åãå¤ąæããã1åãå¤ąæãæåãŽå¤ąæãã63åãįĩãŖãĻãæŽãŖãĻãããŽã¯æåãŽå¤ąæã¨ãžãŖããåãįĩæã§ããã
ãĒãããŽäģįĩãŋãåšããŽã
ãšã¯ãĒãããã¯ãããĻæ¸ããã¨ãããĒããŠã¤ã¯ããããåŊĸãããĻããã¨æãŖãĻããžããã
for attempt in range(3):
try:
result = do_something()
break
except Exception as e:
time.sleep(2)
ããã§åãŖãĻããžãããããã¯ãŧã¯ãä¸įŦåããããĩãŧããŧã䏿įãĢéããĒãŖããããããå¤ąæãĒãįĸēããĢããã§į´ããåéĄã¯ãæŦĄãŽčŠĻčĄãæåããã¯ãã ãã¨ããåæããããšãĻãŽå¤ąæãĢåããåŊãĻã¯ããĻããã¨ããã§ãã
į¨ŧåããĻããã¸ã§ããåĸããĻããã¨ãããŽåæãå´Šããå¤ąæãåŋ ãįžããžãã
ããããéį¨ããĻããSNSčĒååį°åĸã§ã2026åš´8æ8æĨãã9æĨãĢãããĻãŽ2æĨéã§ã5æŦãŽãŦãŧãŗããåæãĢãĒããŠã¤äŋŽæŖãä¸ããŖãĻããžãããįįļã¯ãŠããäŧŧãĻããžããããåå ã¯ããŠããŠã§ããã
note-autolike ãŽãĒãŠã¤ãã3æĨéãŧãäģļã§ããã æįĩæå㯠2026-08-06T09:03:20Zãč¨äēãŋã¤ããĢãĢ58åä¸éãŽããĒããŧãˇã§ãŗããããŽãĢãįæãããŗãããĢã¯åæ°åļéãä¸č¨ãæ¸ãããĻããĒããŖããŽã§ããĸããĢã¯æ¯å63åãŽãŋã¤ããĢãčŋããĻããã3åæããĻ3åã¨ã63åã4åįŽã¯čŠĻããããã¨ãĒãčĢĻãåĻįãĢå Ĩããžãã
ai-portraits ãŽįģåįæã63åįŠēåãããĻããžããã ãĸããŦãŧãˇã§ãŗã§ãĒã¯ã¨ãšããåŧžãããã¨ãããããŗãããæŽĩéįãĢįŠåŊãĢããĻåčŠĻčĄããããŠãŧãĢããã¯ã3æŽĩåŽčŖ ããĻããžããã1æĻåŋĩãããæå¤§21åã3æĻåŋĩãĒãæå¤§63åãåŽæ¸Ŧã§ã¯ãå¯žčąĄãŗãŗããŗãčĒäŊããĸããŦãŧãˇã§ãŗãŽåŧãŖãããåå ãŽã¨ããTierãä¸ããĻãåŖã¯åããžããã3æŽĩå ¨é¨ãčŠĻããã¨ããã§įĩæã¯1æŽĩįŽã¨åãã§ããã
browser-slot.sh ã¯ã°ããŧããĢæ 3æŦãĢ寞ããĻ1æéããã18ã34æŦãčĩˇåãčŠĻãŋãžãã 1 runã¯14ã62åããããŽã§æ ã¯å¸¸ãĢåãžãŖãĻãããäŋŽæŖåãŽåŽčŖ
ã¯ãacquire ãĢå¤ąæããįŦéãĢ SKIP: global limit reached ãåēããĻ exit 0 ããĻããžããããã°ãĢæŽãŖãĻããæ°åã¯ã09æå°9äģļãģ11æå°8äģļãģ12æå°12äģļãčĩˇåããããĻããĒããŖãã¨ããããŽã§ããããããĸãĢãĻãŗãã¯åŊæĨ2åã¨ãskipã§ãããããããŠããŧã0ã§ããã
3ã¤ã¨ãããã1åããåŽčŖ ãããĻããžãããããŠããį´ãŖãĻããžããã
å¤ąæãŽį¨ŽéĄãéãããã§ãã63åãŽãŋã¤ããĢã¯ãåããããŗãããåããĸããĢãĢæããã°åã63åãčŋããžããæąēåŽčĢįãĒå¤ąæã§ãããĸããŦãŧãˇã§ãŗãããã¯ãããŗãŗããŗããåå ãĒãæĄäģļãå¤ããĒããã°äŊæŽĩčŠĻããĻãå¤ãããĒããæ ãŽä¸čļŗã¯ãčǰããæ ãįŠēãããžã§åž ã¤ãããĒãã
ããŽåéĄããĒããĒããŠã¤åŽčŖ ã§ã¯ã3į¨ŽéĄãŽå¤ąæãããšãĻã3åãĢãŧãâįĩäēãã¨ããåä¸ãŽåĻįãĢå¸ãčžŧãžããžããįĩæãį´ããå¤ąæãį´ããĒãå¤ąæãåæ°ãĢãĻãŗããããĻãæįĩįãĢåãåēåŖããåēãĻããžãã
éå´ãŽåŖãæšããããžãããå¤ąæãããã¨ãã°ãĢæŽãŖãĻãããŽãĢãåŽéãĢã¯ä¸éæ§ãŽéåŽŗã ãŖããąãŧãšã§ããããã㎠watch-arb ãšã¯ãĒããã¯ãååããŧã¸ãŽååžãĢå¤ąæãã㨠extract_failed ã¨ã ãč¨é˛ããĻããžãããįŋæįģéĸãčĻãã¨ãååžã§ããĒãåēã16äģļãããã¨čĻãããåŽéãĢ㯠HTTP 429 ãčŋãŖãĻããã ãã§ãåãåēã1äģļãã¤ããŖããåŠããããŧãã§ã¯æŖããåããĻããžãããåæéä¸ãĢįĸēčĒããéĄäŧŧäē蹥ãä¸Ļãšãã¨ãåã429ããã°ä¸ã§7ã¤ãŽåĨãŽååã§æŽãŖãĻããžããã
| ãŦãŧãŗ | ãã°ãĢæŽãŖãĻããåå | åŽéãŽįå |
|---|---|---|
| watch-arb ååããŧ㸠| extract_failed | HTTP 429 |
| watch-arb bootstrap | FATAL | undici ãŽ10į§ ConnectTimeout |
| social-autolike ig-3 | coverage 0/11 â å
¨ãŊãŧãšååžå¤ąæ â exit 1 | IG private API ㎠429 |
| social-autolike ig-sug | following mapååžå¤ąæ â runåæĸ | åã 429 |
| outreach-multi enrich | å
¨äģļ error â qualify å
¨äģļ drop â éįŽ0é | web_profile_info ã常æ 429 |
| outreach-multi igJson | unparsable json | ãŦãšããŗãšã512,558æåãããŽãĢ400,000æåã§slice |
extract_failed ã¨č¨é˛ãããĻããã°ãããŽåēãŽæ§é ãįšæŽãã¨čĒãŋãžããåŽéã¯ãéãåŠãããããã¨ããåéĄã§ãįŋæĨããŖããåŠãã°åãããååãééãŖãĻãããŽã§ãäŋŽæŖãŽæšåãééãįļããĻããžããã
ãããæ´įããã¨ãæŖãããĒããŠã¤č¨č¨ã¯3ã¤ãŽåããĢįãããã¨ãĢåæããžãã
(1) åž ãĻã°į´ããã (2) äŊåã§čĻåããã (3) æŦĄãŽčŠĻčĄãĢäŊãå¤ããĻæ¸Ąããã
ãå¤ąæããããã1åãã¯ããŽ3ã¤ãããšãĻįįĨããåŽčŖ ã§ããä¸ãŽ5æŦãåæãĢæĸãžãŖãĻããæã¯ã3ã¤ã¨ãįããæ¸ãããĻããžããã§ããã
æå120ä¸ãŽčĒååį°åĸã¯ãæ¯æĨãŗãŧããæ¸ããĻããããåããĻãããŽã§ã¯ãããžãããčĒåãå¯ãĻããéãĢį°åĸãčĒåˇąäŋŽæŖããĻåãįļããããæįĢããĻããžããããŽåæãĢįĢã¤ã¨ããĒããŠã¤č¨č¨ãŽčĻäģļãå¤ãããžãããå¤ąæããĻãæŦĄã¯į´ããã§ã¯ãĒãããå¤ąæãŽį¨ŽéĄãã¨ãĢæŦĄãŽčĄåãæąēããããã¨ãčĻäģļãĢãĒããžããããŽč¨č¨ãããåŽčŖ ã¨įĄãåŽčŖ ã§ã¯ãéåŽŗãŽåžŠæ§ãĢčĒåãéĸä¸ããåŋ čĻãããããŠãããæ šæŦįãĢå¤ãããžãã
å ¨äŊãŽæĩã
3ã¤ãŽåããã˛ã¨ã¤ãŽåŗã§æ´įããžãã
å¤ąæãįēįãã
â
âŧ
âââââââââââââââââââââââââââââââââââââââ
â (1) åž
ãĻã°į´ããīŧ â
â â
â æ ä¸čļŗ â įŠēããžã§åž
㤠â
â ãããã¯ãŧã¯æ â ããã¯ãĒãããĻåčŠĻčĄ â
â 429 â Retry-After åĒå
ã§åž
㤠â
â ãã°ã¤ãŗåã â åž
ãŖãĻãį´ããĒã â
â ããĒããŧãˇã§ãŗéå â åž
ãŖãĻãį´ããĒã â
â ãĸããŦãŧãˇã§ãŗ â æŽĩãĢããīŧåžčŋ°īŧ â
âââââââââââââââââââââââââââââââââââââââ
â åž
ãŖãĻãį´ããĒã
âŧ
âââââââââââââââââââââââââââââââââââââââ
â (2) äŊåã§čĻåããīŧ â
â â
â åæ°ä¸é or åäģãã3æéčļ
â æãĄåã â
â ãžã čŠĻãã â (3)㸠â
âââââââââââââââââââââââââââââââââââââââ
â ãžã čŠĻãã
âŧ
âââââââââââââââââââââââââââââââââââââââ
â (3) äŊãå¤ããĻæ¸Ąããīŧ â
â â
â åãå
åŽšãæ¸Ąã â æąēåŽčĢįå¤ąæãĒãåãįĩæ â
â ã¨ãŠãŧå
åŽšãæˇģããĻæ¸Ąã â äŋŽæŖã§ããå¤ąæãĒãå垊 â
â Tieréæ ŧīŧį¸Ļīŧâ æĄäģļãįŠåŊãĢããĻåčŠĻčĄ â
â åĨčǞéĄãĢåˇŽãæŋãīŧæ¨Ēīŧâ čǞéĄãã¨å¤ãã â
âââââââââââââââââââââââââââââââââââââââ
3į¯ã¯é įĒãĢåĻįãããŽã§ã¯ãĒããå¤ąæãŽį¨ŽéĄãĢããŖãĻéŠį¨ããįĩãŋåãããå¤ãããžãããããã¯ãŧã¯æãĒã(1)ã ãã§ååã§ããæąēåŽčĢįãĒããĒããŧãˇã§ãŗéåã¯(1)ããšãããããĻ(3)ããå ĨããžãããĸããŦãŧãˇã§ãŗãããã¯ã¯(1)ãŽãšããããģ(2)ãŽéžå¤ãģ(3)ãŽå¤ãæšãå ¨é¨éĸäŋããžãã
(1) åž ã¤ â åŽčŖ ãŽéǍæ ŧ
browser-slot.sh ãŽäŋŽæŖã§å ãããŗãĸã¯äģĨä¸ãŽåŊĸã§ãã
BROWSER_SLOT_WAIT_SEC="${BROWSER_SLOT_WAIT_SEC:-600}"
wait_for_slot() {
local waited=0
while ! try_acquire_slot; do
local jitter=$(( RANDOM % 31 + 15 )) # 15ã45į§ãŠãŗãã
sleep "$jitter"
waited=$(( waited + jitter ))
if [[ $waited -ge $BROWSER_SLOT_WAIT_SEC ]]; then
log "RESULT waited=${waited}s SKIP: slot timeout"
return 1
fi
done
return 0
}
åčŠĻčĄééã15ã45į§ãŽãŠãŗãã ãĢããžãã åēåŽééãĢããã¨ãæ ãåž ãŖãĻããå ¨ã¸ã§ããåãįŦéãĢæŽēå°ããžãīŧthundering herdīŧããŠãŗãã ã¸ããŋãå Ĩãããã¨ã§ãã¸ã§ããäēããĢãããĻčĒįļãĢåæŖããžããåŽæ¸Ŧã§ãåēåŽééã§ã¯å ¨ã¸ã§ããį´ååããĻãããŖãĻ芰ãžããã¨ãįĸēčĒããĻãã夿´ããžããã
åž æŠä¸ã¯ãŦãŧããæ´ãŋãŖãąãĒããĢããžããã ååžå¤åŽãŽä¸įŦã ãããã¯ãæãŖãĻãå¤åŽåžã¯åŋ ãéĸããžãããããééããã¨åž æŠä¸ãŽã¸ã§ããå ¨é¨ããã¯ãæãĄåãŖãĻå ¨é¨æĸãžããžãã
ãã°ãŽ RESULT čĄãĢ waited=600s ãæŽããžãã ã600į§åž
ãŖãããã§čĢĻãããã¨ãåŗæ¨ãĻããã¯åĨãŽäē蹥ã§ãåēåĨã§ããĒãã¨åžããčŠäžĄã§ããžããã
BROWSER_SLOT_WAIT_SEC=0 ã§äŋŽæŖåãŽåŗskipåäŊã¨åŽå
¨ä¸č´ããžãã30æŦäģĨä¸ãŽlaunchdã¸ã§ããããŽãšã¯ãĒãããå
ąæããĻãããŽã§ãåžæšäēæãįļæããĒããæŽĩéįãĢåãæŋãããããããĢããžããã
䏿šãä¸éæ§ãĢčĻããĻåŽã¯åŗåčŠĻčĄã§į´ããąãŧãšããããžããCloudflare ㎠Authentication error [code: 10000] ã§3æĨéŖįļã¸ã§ããå¤ąæããĻããæĄäģļãããããOAuth ãå¤ąåšãã â äēēéãŽæäŊãčĻãããĢčĻããĻããžãããã¨ããããˇã§ãĢ㨠launchd æå°į°åĸãŽä¸Ąæšã§ãããŧã5æŦãåŽčĩ°ããã¨å
¨ãĻ exit 0ãå¤ąææĨãŽãã°ãčĒãŋį´ãã¨ããĸã¯ãģãšããŧã¯ãŗåįēčĄį´åžãŽ0.5į§åžãĢåä¸ããŧã¯ãŗã§åĨã¨ãŗããã¤ãŗãã 200 ãčŋããĻãããããŧã¯ãŗåįēčĄį´åžãŽä¸éæ§ 401 ã§ããĒããŠã¤ã1åãįĄããŽãįå ã§ãããå¤ąæį㯠3/9æĨīŧ33%īŧããčĒč¨ŧã¨ãŠãŧã ããäēēéæĄäģļãã¨åŗæããã¨ãæŠæĸ°ã§į´ããããŽãäēēéãŽåž
ãĄčĄåãĢįŠãŋãžãã
(2) čĢĻãã â įĩį̝ãåŋ ãįŊŽã
ai-portraits ㎠Tier ãŠããŧãäžãĢããžãããĸããŦãŧãˇã§ãŗãããã¯ãŽã¨ãŠãŧãĢ㯠moderation_stage ã¨ããããŖãŧãĢãããããžããoutput ã§æĸãžãŖãããŽã¯Tieréæ ŧã§éãåŽį¸žããããinput ããå§ãžãŖãããŽã¯ãåæéä¸9čŠĻčĄå
¨æģ
ã§ããã
for tier in [1, 2, 3]:
result = generate(concept, tier=tier)
if result.get("moderation_stage") == "input":
# inputæŽĩ â Tierãå¤ããĻãåŖã¯åããĒããæŽããbreak
log(f"input-stage block, skip remaining tiers for this concept")
break
if result.get("moderation_blocked"):
# outputæŽĩ â Tieréæ ŧã§éãå¯čŊæ§ããã
continue
if result.get("success"):
return result
æĸ¯åãéãã䞥å¤ããããã1æŽĩįŽãŽã¨ãŠãŧæŦæã§å¤åŽããžãã moderation_stage == "input" ã§ break ããã ãã§ã63åãŽįŠēčĩ°ãã0åãĢãĒããžããåæ§ãĢãgsheets_retry.py ã§ã¯ 401/403/404 ã¯åŗ raise ãããããĢč¨č¨ãããĻããžãã
RETRYABLE_HTTP = {429, 500, 502, 503, 504}
def sheets_call_with_retry(fn, max_attempts=3):
delay = 2
for attempt in range(max_attempts):
try:
return fn()
except HttpError as e:
if e.resp.status not in RETRYABLE_HTTP:
raise # 401/403/404 ã¯åŗ raise â ãĒããŠã¤ã§é ããĒã
if attempt == max_attempts - 1:
raise
time.sleep(delay)
delay *= 3 # 2į§ â 6į§īŧå荿大8į§īŧ
except (socket.timeout, ConnectionError, TimeoutError):
if attempt == max_attempts - 1:
raise
time.sleep(delay)
delay *= 3
ãå荿大8į§ãįĩļ寞ãĢčļ ããĒãããå ãĢæąēããžãã ææ°ããã¯ãĒããåæ°ã§æąēããã¨ãåŧãŗåēãå´ãŽãšãããæéãéããĢéŖããžããį§æ°ã§ä¸éãåēåŽããĻããåæ°ãéįŽããé įĒãåŽå ¨ã§ãã
åæ°ã ãã§ãčĢĻããããåŽčŖ
ããã¨ãæåģčģ¸ãŽéåŽŗãĢ寞åŋã§ããžãããkotonoha ãŽã¸ã§ãããĨãŧã§ 39æé pending ãĢãĒãäēæ
ããããžãããä¸é6åã§ä¸æããĻã requeue ãæåãããŽã§å¤ąæãä¸åēĻãįĢãããįŖčĻãéŗ´ãããåŠį¨č
ãŽįģéĸã¯ãäŊæä¸ããŽãžãžã§ããã
äŋŽæŖåžãŽå¤åŽåŧã¯ãããĒããžãã
function shouldGiveUpForQuota({ quotaHits, createdAtMs, resumeAtMs }) {
const THREE_HOURS_MS = 3 * 60 * 60 * 1000;
return (
quotaHits >= 2 || // 2åįŽãŽä¸é
resumeAtMs > createdAtMs + THREE_HOURS_MS // åäģãã3æéčļ
);
}
ãåæ°ãã¨ãåäģãããŽįĩļ寞æåģããŽäēéã§æãĄæĸããįŊŽããžãã äēæ
åŊæĨãŽåŽå¤īŧåäģ 2026-08-19T00:36:57Zãģåé 06:37:00Zīŧãåĸįãąãŧãšã¨ããĻããšããĢå
Ĩããã¨ãåãäēæ
ã¯äēåēĻã¨éããĒããã¨ãæŠæĸ°ã§äŋč¨ŧãããžãã
(3) æģã â æŦĄãŽčŠĻčĄãĢå¤ããå åŽšãæ¸Ąã
note-autolike ãŽäžã§ãããĒãŠã¤ãã3æĨéãŧãäģļã ãŖãåå ã¯ãåä¸ãããŗããã3åæãį´ãã ããŽåŽčŖ
ã§ãããã¨ãŠãŧãæŠæĸ°å¯čĒãĢããĻã2åįŽäģĨéãŽčŠĻčĄãĢã¯äŋŽæŖæį¤ēãäģããĻæ¸Ąããžãã
// ã¨ãŠãŧãĢ code / actual / limit ãčŧãã
throw new ValidationError(`ãŋã¤ããĢã¯1ã58åãĢããĻãã ããīŧåŽé: ${actual}åīŧ`, {
code: "title-too-long",
actual,
limit: 58,
});
// 2åįŽäģĨéã¯äŋŽæŖæį¤ēãäģããĻæãã
function rewriteRetryInstruction(error) {
if (error.code === "title-too-long") {
return [
`ååãŽåēåã¯æŦĄãŽįįąã§ä¸æĄį¨ã§ããīŧ`,
`ãŋã¤ããĢã${error.actual}åãããä¸éãŽ${error.limit}åãčļ
ããĻããžãã`,
`åãå
厚ãģåãæ§æãŽãžãžãããŽįšã ããæēããåŊĸã¸į´ããĻãįJSONãŽãŋãååēåããĻãã ããã`,
`ãŋã¤ããĢã¯įĩĩæåčžŧãŋã§${error.limit}åäģĨå
īŧååã¯${error.actual}åīŧãĢåŋ
ãåããĻãã ããã`,
].join("\n");
}
}
async function askClaudeWithRetry(prompt, retries = 3) {
let currentPrompt = prompt;
for (let i = 0; i < retries; i++) {
try {
const result = await callClaude(currentPrompt);
validateRewrite(result);
return result;
} catch (e) {
if (e instanceof ValidationError) {
// æąēåŽčĢįãĒå¤ąæ â ãšãĒãŧããĒããäŋŽæŖæį¤ēãäģããĻåŗåčŠĻčĄ
currentPrompt = prompt + "\n\n" + rewriteRetryInstruction(e);
} else {
// ãããģãšįŗģãŽå¤ąæ â 30į§åž
ã¤
await sleep(30_000);
}
}
}
}
æąēåŽčĢįãĒå¤ąæã¯ãšãĒãŧããæãžãåŗåčŠĻčĄããžãã ããĒããŧãˇã§ãŗéåãĢ2į§åž ã¤ãŽã¯ã2į§ãæ¨ãĻãĻããã ãã§ãããåž ã¤ãã¨ãį´ããĻæãį´ãããåããĒããŠã¤ã§æˇˇããã¨ãį´ããå¤ąæãĢãåž ãĄæéãæããžããå¤ąæãŽį¨ŽéĄã§åå˛ããĻããšãĒãŧããå Ĩããå ´æãé¸ãŗãžãã
LLMã¸čŋãäŋŽæŖæį¤ēã¯ãæŠæĸ°ãŽčåĨåãã§ã¯ãĒããäēēéčĒãŽæį¤ēīŧįžįŠãŽæååãã§æ¸Ąããžãã"title-too-long" ãããŽãžãžæ¸Ąãã¨3éŖæããžããããŋã¤ããĢã63åããä¸éãŽ58åãčļ
ããĻããžãã58åäģĨå
ãĢåããĻãã ãããã¨æ¸Ąãã¨å垊ããžããããã¯åĨãŽãšã¯ãĒããã§å
ãĢåŽč¨ŧæ¸ãŋãŽäēåŽã§ãã
3į¯ãŽå ¨åŽšã¯ããã§ä¸æĻæĸããžããæŦĄã¯åį¯ãŽãåŖãæšãã¨ãį´ãæšããã5ãŦãŧãŗåæéåŽŗããæžãŖããąãŧãšåĨãĢåč§ŖããĻãããžãã
åŽčŖ ãŽčŠŗį´°
ããĒããŠã¤ãæ¸ãããã¯åŽäēã§ã¯ãĒã ââ ããŠãĢãæŗ¨å ĨãåããŗããããĢå Ĩãã
ååã§č¨č¨ãã3į¯ã¯ãæ¸ããæįšã§ã¯ãåãã¯ããŽãŗãŧããã§ãããããžãããéæŦ éåŽŗã¯åįžææŽĩããĒããŽã§ããĒããŠã¤ãããŽįĩ莝ãåŽéãĢéãããŠãããįĸēãããæšæŗããããŠãĢãã§ã¯åå¨ããžãããæåãŽ4æŦãŽåŽčŖ
ã¯ããã§įĩããŖãĻããžãããgit diff ãčĻãĻįĸēčĒããæ°ãĢãĒãŖãĻãåŽäēãĢããĻããžããã
åŽéãĢåšãããŽã¯ãããŠãĢãæŗ¨å ĨãåŽčŖ æŦäŊã¨åããŗããããĢåæĸąãããã¨ã§ããã
// watch-arb/collector/lib/api.mjs
let _failOnceRemaining = parseInt(process.env.WA_API_FAIL_ONCE ?? '0', 10);
async function req(path, opts = {}) {
if (_failOnceRemaining > 0) {
_failOnceRemaining--;
throw new TypeError('fetch failed'); // åæãŽéåŽŗãæŦįĒã§ã¯įĩļ寞ãĢįēįĢããĒã
}
// ... æŦæĨãŽfetchåĻį
}
ãĢãĻãŗãŋã¯ãĸã¸ãĨãŧãĢãšãŗãŧãã§å
¨ req åŧãŗåēãå
ąéã§ãããããģãščĩˇååžãŽæåãŽNåã ããfetch ãåŧãļåãĢåæãŽ TypeError ãæããžãã
åŽäēæĄäģļã¯2æŦãģããã§åŋ ã严æšįĸēčĒããžãã
- â
WA_API_FAIL_ONCE=2ã§čĩˇåããĻapi retry attempt=1/3ã¨2/3ãåēãããã§ FATAL ãĒã exit 0 ã§åŽčĩ°ãã - ⥠į°åĸ夿°æĒč¨åŽãĒã retry ãã°ã1čĄãåēãĒã
âĄããããã¨ã§ãããŠãĢãæŗ¨å ĨãæŦįĒãĢæŧããĻããĒãããã¨ãåããŗããŗãã§į¤ēããžããåŽæ¸Ŧã§åēããã°ã¯ãããĒããžããã
[INFO] api retry attempt=1/3 path=/api/collector/shops reason=fetch failed
[INFO] api retry attempt=2/3 path=/api/collector/shops reason=fetch failed
[INFO] 32äģļã ingest ããĻ exit 0
ããŽå㯠watch-arb ãã zaiko-radarãģlily-line-funnelãģsocial-autolike ã¨æ¨ĒåąéããĻ4æŦãĢãĒããžãããããĒããŠã¤ãæ¸ãããã¯ãããŠãĢãæŗ¨å
Ĩčžŧãŋã§čĩ°ãŖãã¨ãããčĻããã§ãĒããã°åŽäēãĢããĒããã¨ãããĢãŧãĢãĢå¤ãããžããã
åčŠĻčĄããå¯žčąĄã1éĸæ°ãĢéãã
ãĒããŠã¤ãæ¸ãã¨ããæåãĢæąēãããã¨ã¯ãäŊåčŠĻãããã§ã¯ãĒããäŊãåčŠĻčĄããããã§ãã
// å¤åŽã1éĸæ°ãĢéį´ãã
function isTransientStatus(status) {
return status === 429 || (status >= 500 && status <= 504);
}
async function req(path, opts = {}) {
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
try {
const res = await fetchWithTimeout(path, opts);
if (!res.ok) {
if (!isTransientStatus(res.status)) throw new HttpError(res.status);
if (attempt === MAX_ATTEMPTS) throw new HttpError(res.status);
await sleep(backoff(attempt));
continue;
}
return await res.json();
} catch (e) {
if (!isNetworkError(e)) throw e; // ãããã¯ãŧã¯äžå¤äģĨå¤ã¯åŗthrow
if (attempt === MAX_ATTEMPTS) throw e;
await sleep(backoff(attempt));
}
}
}
401/403/404 ãåŗ throw ãããŽã¯įååã§ã¯ãĒããåĨãŽäēæ ãé˛ããããŽč¨č¨ã§ããæ¨Šéã¨ãŠãŧããĒããŠã¤ã§å ãã¨ãåããĒã¯ã¨ãšãã3åæããĻ3åã¨ãåãå¤ąæãčŋããžãããã°ãĢã¯ã3åå¤ąæãããã¨ã ãæŽãããæ¨ŠéãįĄããã¨ããįå ã¯åãããžãã
transient-failure-recorded-as-permanent.md ã§č¨é˛ããã7ã¤ãŽååã§æŽãŖã429ãã¨éåããŽäēæ
ã§ãã429ã extract_failed ãĢåãããŽã¨åãæ§é ã§ã401/403 ããããžãĢé
ãããĢåããžãã
isTransientStatus ã1éĸæ°ãĢåēãįįąã¯ã夿ã褿°ãŽåŧãŗåēãįŽæãĢæŖãã¨å ´æãã¨ãĢ垎åĻãĢéãåŽįžŠãįãžããããã§ããgsheets_retry.py ㎠RETRYABLE_HTTP = {429, 500, 502, 503, 504} ã¨ããåŽæ°ãģãããåãįēæŗã§ãããŠãŽãšããŧãŋãšããĒããŠã¤å¯žčąĄããã¨ããįĨčã1įŽæãĢéããĻããžãã
ãĒããŠã¤ãå Ĩããã¨äžå¤ãŽäŧããæšãå¤ãã
kotonoha ãĢ withRetry ãåŽčŖ
ããįŋãŗãããã§ãå¯äŊį¨ãŽäŋŽæŖã3äģļå
ĨããžããããæŠčŊãčŋŊå ãããã¨æãŖãĻããžããããåŽéãĢã¯ãäžå¤ãŽäŧããæšãå¤ããã夿´ã§ããã
夿´åãpurge() 㯠5xx ãčŋãŖãĻãåžįļãŽãæå
ãŽäŊæĨããŖãŦã¯ããĒ30æĨæé¤ããžã§åĻįãé˛ãã§ããžãããres.ok ã false ã§ãæŦĄãŽčĄã¸é˛ãåŽčŖ
ãĢãĒãŖãĻããããã§ããwithRetry ãå
ĨããĻ5xx ãŽįŦéãĢ throw ãããããĢãĒãã¨ãæé¤ãŽãŗãŧããããã§å°éä¸čŊãĢãĒããžããåŽåŽŗã¯ãäŊæĨããŖãŦã¯ããĒã30æĨåčįŠãããã¨ãããåŽæįãĢæåæé¤ããĒããã°æ°ãĨããĒãį¨ŽéĄãŽéããĒåŖåã§ããã
async function purge(jobId) {
try {
await uploadResult(jobId);
} catch (e) {
log(`purge upload failed: ${e.message}`);
// ããã§ throw ããĒã â įŊŽãå ´ãŽå¤ąæã¯č¨é˛ããĻįļčĄãã
}
await cleanWorkDir(); // äžå¤ãŽæįĄãĢãããããåŋ
ãčĩ°ããã
}
ãã1äģļ㯠setContentSafely ã§ã1åįŽã¨2åįŽãŽåčŠĻčĄãŽéã0į§ã§ããããĒãŊãŧãšéŧčŋĢã§čŊãĄãį´åžãĢåæĄäģļã§åŗåčŠĻčĄããĻããå§åãæŽãŖãĻããéã¯åãįĩæãĢãĒããžããååã§ãæąēåŽčĢįãĒå¤ąæã¯ãšãĒãŧããæãžãåŗåčŠĻčĄãã¨æ¸ããžããããããŽéå´ã§ããčŗæēéŧčŋĢãåå ãŽå¤ąæã¯ãåž
ããĒãã¨ãĒããŠã¤ãĢæåŗããããžããã
čŠĻčĄåæ°ã¯ãå¤ąæããã¨ãäŊãčĩˇããããã§éå¯žį§°ãĢč¨åŽããĻããžããreport()īŧįĩæå ąåīŧã¯å¤ąæããã¨åŠį¨č
ãŽįģéĸããåŽčĄä¸ããŽãžãžæ°¸äš
ãĢæĸãžããŽã§ attempts=5ãclaim()īŧäēį´ååžīŧ㯠attempts=3ãä¸åžãŽåæ°ã¯ãŗãšãã¨čĸĢåŽŗãŽéå¯žį§°ãįĄčĻããžãããŋã¤ã ãĸãĻããæäŊãŽéãã§åããĻããĻãputObject 㨠getStream ã¯120į§ãgetText 㨠listKeys ã¯30į§ã§ãããŋã¤ã ãĸãĻãč¨åŽãŽãĒã fetch ã¯ãæ°¸äš
ãĢåããããã¨ããææĒãŽãĒããŠã¤ä¸čŊįļæ
ãäŊããžãã
ãšããĒãŧã ãæ¸Ąãã¨ãĒããŠã¤ã§ããĒã
åŽčŖ ãããžã§æčããĻããĒããŖãåļį´ããããžãããĒããŠã¤å¯čŊãĢãããĢã¯ãå ĨåããååŽčĄå¯čŊãã§ãĒããã°ãĒããžããã
// NG: ãšããĒãŧã ã¯ä¸åēĻæļč˛ģãããåįã§ããĒã
async function retryWithStream(stream) {
for (let i = 0; i < 3; i++) {
await fetch(url, { body: stream }); // 2åįŽã¯ãšããĒãŧã ãįŠē
}
}
// OK: Buffer ã string ãĢéåŽãã
async function retryWithBuffer(buffer) {
for (let i = 0; i < 3; i++) {
await fetch(url, { body: buffer }); // äŊåēĻã§ãåãããŧãŋãéãã
}
}
putObject ãĢæ¸Ąã body ã Buffer ã string ãĢéåŽããžãããååļį´ããŗãĄãŗãã§æŽããĻãããĒãã¨ãåžããåŧãŗåēãå´ããšããĒãŧã ãæ¸ĄããĻããĒããŠã¤ãåããĻãããŽãĢæ¯åå¤ąæããããã°ãĢãĒããžãããšããĒãŧã ããĒããŠã¤ãĢæ¸Ąãã¨2åįŽäģĨéã¯įŠēãŽãããŖãéãįļããžããã¨ãŠãŧãåēããĢæ¯åå¤ąæããžãã
į§ã芰ãžãŖã芹
é˛åžĄãŽãããĢįŊŽããåŽæ°ããæåį0%ãŽåŖãĢãĒãŖãĻãã
outreach-multi ㎠DM åäŋĄįŽąããŧãšåĻįã§ãunparsable json ã¨ããã¨ãŠãŧãæ¯æĨåēãĻããžããããģãŦã¯ãŋãåŖããããJSON ãŽæ§é ãå¤ããŖããã¨įãŖãĻ3æĨéäŋŽæŖãįļããžããã
åŽæ
ã¯ãåäŋĄįŽąååžãŽãŗãŧããĢ text.slice(0, 400_000) ã¨ããčĄããããžããã厍大ãĒãŦãšããŗãšãæĨãã¨ããĢãĄãĸãĒãäŋčˇãããããĢæ¸ãããä¸éã§ãããIG ㎠DM åäŋĄįŽąãŽåŽãĩã¤ãēã荿¸Ŧããã 512,558æå ã§ãããä¸éã 400,000 ãĒãŽã§ãæ¯ååŋ
ãéä¸ã§åããžããåãã JSON 㯠JSON.parse ã§æ¯ååŋ
ãå¤ąæããžããæåįã¯0%ã§ãã
3æĨéåãå¤ąæãčĻįļããĻãããŽã¯ãtext.slice ãŽčĄãåŋããĻãããŧãšãĢå¤ąæããĻãã â æ§é ãåŖãããã¨čĒãã§ããããã§ããįįļãåēå§ãããŽããäģæĨãããã§ã¯ãããžããã§ãããåäŋĄįŽąãŽãĩã¤ãēã 400,000åãčļ
ããæĨããéããĢå¤ąæãå§ããĻãããæĨæ°ãĨãããéįŽ0äģļãĢãĒãŖãĻããåŊĸã§ãã
äŋŽæŖã¯ page.evaluate ãŽä¸ã§ JSON.parse ãžã§åŽäēãããå¤ąæããã¨ãã ãå
é 300åãč¨ēæį¨ãĢčŋãåŊĸãĢå¤ããžããã
ããŽäģļããåžãč¨č¨ãĢãŧãĢã¯ãä¸éãįŊŽããĒããä¸éãĢåŊããŖããã¨čĒäŊããã°ãĢåēããã§ããtext.slice ã¯ãĩã¤ãŦãŗããĢåãčŊã¨ãã ãã§ãåãããã¨ãčǰãĢãįĨãããžãããé˛åžĄãŽåŽæ°ãããŽåŽæ°ãčļ
ããįŦéããæäš
įãĒå¤ąæãĢå¤ããŖãĻããĻãããã°ãĢã¯äŊãæŽããžããã
quarantine ãæ°¸äš ãĢãŧããĢãĒãŖãĻãã
unfollow-core.js ãŽãĸãŗããŠããŧåĻįãĢã3åå¤ąæããã¨7æĨéãŽäŋįīŧquarantineīŧãĢå
Ĩãäģįĩãŋããããžããã
åéĄã¯ãquarantine ãã垊æ´ģããããŠããĒãããã§ãã垊æ´ģãããĸãĢãĻãŗãã¯åčŖãĒãšããĢæģããžããååēĻ3åå¤ąæããã¨ããžã7æĨéãŽäŋįãĢå ĨããžãããããįĄéãĢåããžãã
åčŖã 2,605äģļããŖãĻãscanned: 8 / unfollowed: 2 ã¨ããæ°åãčĻãĻ2éąéæŠãŋãžãããåčŖããģãŧåĻįãããĻããĒããããĒãé˛ãžãĒããŽãããčĒŋãšãããåããĸãĢãĻãŗãã quarantine ãĢå
ĨãŖãĻã¯åēããįš°ãčŋããĻããĻãåãĢé˛ãã§ããĒããã§ããã
ãã°ãĢ㯠circuit-break ã¨åēãĻããžãããcircuit-break ãæŠčŊããĻãããŽãĢåŽčĄæ°ãåĸããĒããŽã¯įįžããžããããŖã¨æŠãæ°ãĨããã¯ãã§ãããããcircuit-break ãåããĻãã = äŋčˇãããĻãããã¨čĒãã§ãããŽã§įããžããã§ããã
äŋŽæŖã¯ãį´¯č¨å¤ąæåæ°īŧUNFOLLOW_GIVEUP_FAILS = 6ãquarantine å¤ąæéžå¤ãŽ2åīŧãĢéãããĸãĢãĻãŗããå°å¸ŗããįŠįåé¤ãããã¨ã§ããã
ãäŋįãã¯æēãžãã ãã§æ¸ããžããã æ¨ãĻãįĩį̝ãåŋ
ãįŊŽããĒããã°ãå°å¸ŗã¯åĻįã§ããĒããĸãĢãĻãŗãã§åãžãįļããžãããã ãæ¨ãĻãåãĢãčĒåãŽåĻįãæŖå¸¸ãĢåããĻãããããŽįĸēčĒãå
ã§ãããģããˇã§ãŗãåããĻããã ãã§ãæŖå¸¸ãĒãĸãĢãĻãŗããå
¨é¨æ¨ãĻãĻããžãéäēæ
ãããåžãžããkotonoha ãŽããŧã¸ãĢæ¸ãããæ¨ãĻãåãĢčĒåãŽæŖå¸¸æ§ãįĸēčĒãããã¨ããååããããĢãéŠį¨ãããžãã
įŖčĻã39æééŗ´ããĒããŖã
kotonoha ãŽã¸ã§ãã39æéããŖã¨ãäŊæä¸ããŽãžãžæĸãžãŖãĻããžãããįŖčĻã¯ä¸åēĻãéŗ´ããžããã§ãããã¸ã§ãã¯å¤ąæã¨ããĻč¨é˛ãããĻããžããã§ããã
åå ã¯ãåæ°ãįļæ ãĢæãŖãĻããĒããĒããŠã¤ãĢã¯ãæãĄæĸããįŊŽããĒããã§ããã
ã¸ã§ããã¯ãŠãŧãŋä¸éãĢåŊãã㨠requeue ã§ pending ãĢæģããžããæģããã¸ã§ãã¯ãäŊåįŽããã¨ãããĢãĻãŗãŋãæãŖãĻããžããã§ããã7åįŽãŽã¸ã§ãã1åįŽãŽã¸ã§ããåŽčŖ
ããåēåĨã§ããžãããrequeue ãæåãããŽã§å¤ąæãįĢãĄãžãããįŖčĻãŽéžå¤ãĢãåŊãããžãããåŠį¨č
ãŽįģéĸãĢã¯ãäŊæä¸ããåģļã
ã¨čĄ¨į¤ēããįļããžãã
äŋŽæŖã¯2æŽĩéã§ããããžããåæ°ãįļæ
ãĢæãããããæŦĄãĢãåæ°ã¨åäģãããŽįĩļ寞æåģãŽäēéã§æãĄæĸããįŊŽãããååã§į¤ēãã shouldGiveUpForQuota éĸæ°ãåĸįãąãŧãšäģããŽããšããĢéãã¨ããäēæ
åŊæĨãŽåŽå¤īŧåäģ 2026-08-19T00:36:57ZãģåéäēåŽ 06:37:00Zīŧã1ãąãŧãšå
Ĩããžãããåãäēæ
ã¯äēåēĻã¨éããĒããã¨ãæŠæĸ°ã§äŋč¨ŧãããžãã
åæ°ã ãã§æãĄæĸããįŊŽããĻããã¨ã1åįŽã ãåéã12æéå ããéããĻããžããžããæåģã ãã ã¨ãåéã¯įããä¸éãéŖįļããĻããããéããĻããžããžããäģåãŽäēæ ã¯æåģæĄäģļãŽå´ã§1åįŽãĢæãžãåŊĸã§ãããäēéãĢããæåŗãåŽããŧãŋã§æ¸Ŧããäģļã§ãã
ãã¯ãŧãĢãŧãåž ã¤ãã¨ãããŽã¸ã§ããčĢĻãããã¯åĨãŽå¤æã§ããã¯ãŠãŧãŋã¯åž ãĻã°į´ãå¤ąæãĒãŽã§ã¯ãŧãĢãŧã¯åž ãĄãžããã§ãåŠį¨č ã3æéäģĨä¸åž ãããæ šæ ãĢã¯ãĒããžããã
ãčĒč¨ŧã¨ãŠãŧãã¨åŗæããĻ3æĨéäēēéåž ãĄãĢããĻãã
com.lily.line-pdca ã Cloudflare ㎠Authentication error [code: 10000] ã§3æĨéŖįļæĸãžããžãããčĒč¨ŧã¨ãŠãŧãĒãŽã§ãOAuth ãå¤ąåšãããæåã§ wrangler login ãčĻããã¨å¤æããžãããį°åĸ夿°ãŽč¨åŽæŧããįããžããã
įĸēãããåãĢãäēēéãŽGUIæäŊãčĻãæĄäģļãã¨ããĻåéĄãããŽãééãã§ããã
åŽéãĢ shell 㨠launchd æå°į°åĸãŽä¸Ąæšã§ wrangler whoamiãģkv key listãģdeploy --dry-run ãåŽčĩ°ããã¨å
¨ãĻ exit 0 ã§ãããåēåãã¤ãæ°ãžã§åä¸īŧ1,516 bytesīŧãå¤ąææĨãŽãã°ãčĒãŋį´ãã¨ããĸã¯ãģãšããŧã¯ãŗãåįēčĄãã 0.5į§åž ãĢ KV keys ã 401 ãčŋããĻããžãããããŽåãããŧã¯ãŗã§0.5į§åžãŽ /user 㯠200 ãčŋããĻããžãããããŧã¯ãŗåįēčĄį´åžãŽä¸éæ§ 401 ã§ããĒããŠã¤ã1åããĒããŽãįå ã§ããã
å¤ąæį㯠3/9æĨīŧį´33%īŧã§ãããæ¯æĨåēãĒããã§ã3æĨãĢ1åã¯åēããããŽé ģåēĻããäēēéæĄäģļãĢčĻãããåå ã§ããããžããã
夿ãå¤ããåēæēã¯ãåãæé ãäģããä¸åēĻåŠãããéããããåŽéãĢåŠããĻįĸēããããã¨ã§ãããã¨ãŠãŧãŽį¨ŽéĄīŧ401ãģAuthentication errorīŧã ãã§ãįĩļ寞ãĢį´ããĒããã¨åéĄããĒããtransient-failure-recorded-as-permanent.md ãĢč¨é˛ããã429ã7ã¤ãŽååã§æŽãŖãĻãããã¨åãåã§ãäģåēĻã¯ãčĒč¨ŧã¨ãŠãŧãã¨ããååãä¸éæ§ãŽå¤ąæãäēēéæĄäģļãĢčĻããĻããžããã
ããŽäģļäģĨéãčĒč¨ŧã¨ãŠãŧãåēãããããŧãã1æŦčĩ°ãããĻãã夿ãããĢãŧãĢãĢå¤ãããžããããæŠæĸ°ã§į´ããããŽãäēēéãŽäŊæĨåž ãĄããĨãŧãĢįŠãžãĒãããŽåæã¯ãæŠæĸ°ã§åŠããĻįĸēããããã§ããįĸēãããĒããžãžåŗæãããŽã¯ãåŠãããĢãį´ããĒããã¨æąēãã¤ããĻãããŽã¨å¤ãããžããã
ã¤ãžãããã¤ãŗã
åæŽĩã§åãä¸ããååĨäēäžãŽčŖãĢããæ§é įãĒããŋãŧãŗããžã¨ããžããã芰ãžãŖãå ´æãŽååããéãã ãã§ãæ šãŖãã¯åãåã§ãã
å¤ąæããã˛ã¨ã¤ãŽååããĢå§į¸ŽããĻč¨é˛ãã
watch-arbãŽååããŧã¸ã¯extract_failedãbootstrapåŽčĄã¯FATALãDMåäŋĄįŽąã¯unparsable jsonã¨č¨é˛ãããĻããžããã3ã¤ã¨ãåŽéãŽįå ã¯HTTP 429ã§ããtransient-failure-recorded-as-permanent.mdãĢč¨é˛ããããŽäēčąĄãæ´įããã¨ãåæéä¸ã§åã429ã7į¨ŽéĄãŽåĨåã§æŽãŖãĻããžãããååãŽæįšã§HTTPãšããŧãŋãšãæļããã¨ããåž
ãĻã°į´ãããĒãŽããæ§é ãį´ãåŋ
čĻãããããŽããŽå¤æãã§ããĒããĒããžããäŋŽæŖã¯1čĄã§ããč¨é˛ããåãĢ429/503 â rate_limited / 401/403 â auth_error / ããäģĨå¤ â extract_failedã¨åéĄããžããããŽ3åå˛ããåž
ã¤ãģčĢĻãããģæģãããŽåå˛ãŽæåãĢããåææĄäģļã§ãã
ã¨ãŠãŧæŦæãæ¨ãĻãĻãã
ai-portraitsãŽgenerate.pyãcodex execãŽstdout/stderrãæ¨ãĻãĻãããããįģåãåēãĒããŖãįįąãmissingã¨ããæŽããžããã§ãããmoderation_blockedãsafety_violations: ["sexual"]ãčĻããĒããã°ãTieréæ ŧã§å垊ãããŽãinputæŽĩã§åŗæãĄåããšãããŽå¤æãįĢãĄãžãããã¨ãŠãŧãäŋæããĒãåŽčŖ
ã§ã¯ãåž
ã¤/čĢĻãã/æģãããŽ3į¯ãããããæįĢããĒããŽã§ãã¨ãŠãŧäŋæã¯č¨č¨ãŽåæã§ããŖãĻæŠčŊčŋŊå ã§ã¯ãããžããã
moderation_stageãįĸēčĒãããĢå
¨TierãčŠĻã
moderation_stage: inputã§æĸãžãŖãæĻåŋĩã¯ãT1ããT3ãžã§å
¨æŽĩčŠĻããĻ9čŠĻčĄå
¨æģ
ã§ãããmoderation_stage: outputã§æĸãžãŖãããŽã¯Tieréæ ŧã§éãåŽį¸žããããžãīŧpartial 01_feet_up_windowã¯T2ã03_legs_on_bed_topdownã¯T3īŧãT1ãŽmoderation_stageããŖãŧãĢã1čĒã§ãæĸ¯åãéãã䞥å¤ããããããæąēãžããŽãĢãããŖãŧãĢããčĒãžããĢå
¨æŽĩãčŠĻãã¨1æĻåŋĩãããæå¤§21åã3æĻåŋĩã§æå¤§63åãįĄé§ãĢãĒããžãããåŽå
¨å´ããŠãŧãĢããã¯ãäŊæŽĩããããããã1æŽĩįŽãŽã¨ãŠãŧæŦæã§éãã䞥å¤ããããããå
ãĢå¤åŽããžãã
éãå ãŽåŽšéãæ¸ŦãããĢé įˇãã
social-autolikeãŽfollow-source-followersã§ããŠãŧãĢããã¯ãčŋŊå ããåãĢãUIãĸãŧããĢãŽfollowingå´ã408äģļ/63į§ã§äŧ¸ãŗããŽãĢ寞ããĻfollowerså´ã12äģļã§æĸãžããã¨ãå
ãĢåŽæ¸Ŧããžããã厚éãããå´ã ããããŠãŧãĢããã¯ãĢæĄį¨ãããŽãįįąã§ããæ¸ŦãããĢé
įˇããã¨ã厚éãŽãĒãéãå
ãĢå
¨ããŠããŖãã¯ãæĩããĻãéãå
ãčŋŊå ãããŽãĢæåįãå¤ãããĒããã¨ããįļæŗãĢãĒããžããoutreach-multiãŽpkč§ŖæąēããIGã70äģļååžã§įĩãã¨ããæĸįĨãŽåŖãįšãčžŧãã§1run 30äģļåļéãĢããĻããžãã
æäš įãĒå¤ąæãããã¨1åããŽæ ãĢæ°ãã
outreach-multiãŽDMéäŋĄã§no ãĄããģãŧ㸠buttonīŧDMä¸å¯ãŽč¨åŽīŧãčŋãã¨ãexhausted after 3 attemptsãĢäšãŖãĻéäŋĄæ ã1äģļããã3ååæļč˛ģããĻããžããã᏿ãŽãĸãĢãĻãŗãč¨åŽã¯ããĄãããĒããŠã¤ããĻãå¤ãããžãããsent.jsonlãŽreasonããŖãŧãĢããčĒãã§ãno ãĄããģãŧ㸠button / profile gone / ããŽããŧã¸ã¯åå¨ããžãããåĢãåŽå
ã¯permanentlyFailed SetãĢå
ĨããshouldSkip()ã§exhaustedãŽæåãĢå¤åŽãįŊŽããžããããã¯åæŽĩã§č§Ļãããä¸éæ§ãæäš
ã¨ããĻč¨é˛ããéåããŽäēæ
ãã¨åãæ šã§ãå¤ąæãŽåéĄãŽč¨č¨ããĒããŠã¤åæ°ãŽč¨č¨ããå
ãĢčĻããžãã
429ãŽãã¨ãåãéåēĻã§åŠãįļãã
social-autolikeãŽig-3ã¯0.3ã0.5į§ééã§17ãŊãŧãšãæ°į§ã§æãĄčžŧãã§14æéãŧãææã§ããã429ãåããåžãééãå¤ããĒããã°ãåļéä¸ãĢåããĒã¯ã¨ãšããįŠãŋä¸ããĻčĒåã§ãšããããĒãŗã°ãåģļéˇããžããretry-and-giveup-design.mdãĢč¨é˛ããäŋŽæŖã§ã¯ã3éŖįļ429ã§ããŽãããģãšãŽAPIåŧãŗåēããæãĄåããŠããã¨ã429ã1åēĻã§ãčĻãåēãŽconcurrencyã1ãĢčŊã¨ãč¨č¨ãå
ĨããĻããžããåž
ãŖãĻããåčŠĻčĄããã ãã§ãĒããéåēĻããŽããŽãčŊã¨ããã¨ããģããã§ãã
ãĒããŠã¤ã2åą¤ãĢéãĒãŖãĻãã
watch-arbãŽingestShopãįŦčĒãĢ3åãĢãŧããæãĄãreq()å´ãĢã3åãĢãŧãããããžãããææĒ3Ã3=9åãĢãĒãããã°ãĢãretry attempt 3/3ãã2åēĻä¸Ļãã§ãä¸éããŠããĢãããŽããäģäŊåįŽãĒãŽããčĒããžãããããŠãĢãæŗ¨å
Ĩãå
ĨããĻãããŠãĄããŽåą¤ãåããĻãããããåãããĒããĒããžããäŋŽæŖã¯ingestShopå´ãŽįŦčĒãĢãŧããåé¤ããĻreq()ãŽ1åą¤ãĢ䏿Ŧåããžãããåŧãŗåēãå´ã¯åŗthrowããã ããĢããžãã
ããŠãŧãĢããã¯ãŽæåãæãĄåããĢãĻãŗãŋãĢæ°ãã
ã3éŖįļ429ã§APIåŧãŗåēããæãĄåãããŠããããUIããŠãŧãĢããã¯ãæåããäģļããĢãĻãŗããĢåĢããĻããžãããéãéãäŊãŖãĻã3äģļã§æĸãžãŖãĻããžãããæãĄåããĢãĻãŗãŋã¯ãå ¨įĩ莝ãå¤ąæããã¨ããã ãåĸãããžããããŠãŧãĢããã¯å ã§æåããäģļã¯ããĄã¤ãŗãŽå¤ąæãĢãĻãŗããĢčŧããžããã
ãåããĒããŖãīŧį°å¸¸īŧãã¨ãåããĢčĄãæéããĒããŖãīŧæŖå¸¸ãĒæãĄåãīŧããåããŧãäģļã§å¤åŽãã
follow-source-followersã¯ãŊãŧãšååžã ãã§æéäēįŽãäŊŋãåãŖãĻåčŖãŧãäģļãĢãĒãŖãå ´åãæĸåãŽãå
¨ãŊãŧãšååžå¤ąæâčǤæäŊ鞿ĸã§åæĸãįĩ莝ãĢå
ĨãŖãĻããžããžããåããĒããŖããŽãæéããĒããŖãã ããĒãŽãã¯åĨãŽäē蹥ã§ããåããŧãäģļã§å¤åŽããã¨æ¯æĨčĒ¤å ąãéŗ´ããæŦįŠãŽå¤ąæãčĻåããããĒããĒããžããtime-budgetã¨ããåĨåēåŖã፿ããĻåēåĨããžãã
čĢĻããå¤åŽåŧãcatchãããã¯ãĢããŋæ¸ããã
æãĄæĸããŽæĄäģļãcatchãŽä¸ãĢæ¸ãããĻããã¨ãåĸįãąãŧãšãŽããšããĢčŧããããžãããį´į˛éĸæ°ãĢåēãã¨äēæ
åŊæĨãŽåŽå¤ã1ãąãŧãšå
ĨããããåãæĄäģļãäēåēĻã¨éããĒããã¨ãæŠæĸ°ã§äŋč¨ŧã§ããžãããã¸ãã¯ãcatchãŽä¸ãĢããéããããšãã¯ãããžããžãąãŧãšãéããĒããŖããįļæ
ã§åæ ŧããžãã
ã¨ãŠãŧãĄããģãŧã¸ã̿пĸ°å¯čĒãĒæ å ąãčŧãŖãĻããĒã
ValidationErrorãæååã ãã§æããã¨ãåčŠĻčĄæãĢãäŊãæĒããŖãããããŗãŧãããåãåēããžããã2åįŽäģĨéãĢåããããŗãããæããã¨åãįĩæãĢãĒããŽã¯ãæģãããåŽčŖ
ãããĻããĒãããã§ãcode: "title-too-long", actual: 63, limit: 58ãŽãããĒæ§é ãčŧããã¨äŋŽæŖæį¤ēãåįãĢįĩãŋįĢãĻãããžããLLMã¸čŋãã¨ãã¯čåĨåīŧ"title-too-long"īŧã§ãĒãäēēéčĒãŽæį¤ēæã¨åŽæ¸Ŧå¤ãæ¸ĄããžããčåĨåãããŽãžãžæ¸Ąãã¨3éŖæããžãã
ããšãããŠã¯ããŖãš
5ãŦãŧãŗåæéåŽŗããč¸įãããĢãŧãĢãåæããžããæ°ãå¤ãã§ãããå ¨é¨ãåŽéãĢ芰ãžãŖãããå ĨãããããŽã§ãã
â å¤ąæã¯č¨é˛ããåãĢHTTPãšããŧãŋãšã¨äžå¤ãŽåã§åéĄãã
429/503 â rate_limited / 401/403 â auth_error / ããäģĨå¤ â åĻįįŗģã¨ãŠãŧãããŽ3åå˛ãå
ãĢįŊŽãã ãã§ãįģéĸãĢåēããå¤ąæããĻããåēãŽä¸čϧããŽæåŗãå¤ãããžããwatch-arbã¯ããŽåéĄãå ããåžãshops.last_errorãŽæč¨ãããŦãŧãåļéã§ååžã§ãããã¨ãæ§é ãåããĒãããĢåããžããã
⥠ãĒããŠã¤ãæ¸ããããããŠãĢãæŗ¨å ĨãåããŗããããĢå Ĩãã
let _failOnceRemaining = parseInt(process.env.WA_API_FAIL_ONCE ?? '0', 10);
// čĩˇååžãŽæåãŽNåã ãåæãŽã¨ãŠãŧãæãã
åŽäēæĄäģļã¯2æŦãģããã§ãã=2ã§retry attempt=1/3ã¨2/3ãåēãĻexit 0ã§åŽčĩ°ãããã¨ãæĒč¨åŽãĒã retry ãã°ã1čĄãåēãĒããã¨ãâĄããĒãã¨ãããŠãĢãæŗ¨å
ĨãæŦįĒãĢæŧããĻããĒãããã¨ãåããŗããŗãã§įĸēčĒã§ããžããã
âĸ æãĄæĸãããåæ°ãã¨ãåäģãããŽįĩļ寞æåģããŽäēéã§įŊŽã
åæ°ã ãã ã¨ã1åįŽã ãåéã12æéå
ããéããžããįĩļ寞æåģã ãã ã¨ãåéã¯įããä¸éãéŖįļããĻããããéããžããkotonohaãŽäēæ
ã¯æåģæĄäģļãŽå´ã§1åįŽãĢæãžãåŊĸã§ããīŧåäģ2026-08-19T00:36:57Zãģåé06:37:00Zãåäģ+3æéãŽå¤īŧãäēéãĢããæåŗãåŽããŧãŋã§æ¸Ŧããäģļã§ãã
âŖ åž ãĄãŽåč¨ä¸éãį§ã§å ãĢæąēããåæ°ãéįŽãã
gsheets_retry.pyã¯ãå荿大8į§ãįĩļ寞ãĢčļ
ããĒãããäžé ŧæãĢåēåŽããĻããã2į§â6į§īŧ3åīŧãéįŽããžãããææ°ããã¯ãĒããåæ°ã§å
ãĢæąēããã¨ãåŧãŗåēãå´ãŽãšãããæéãéããĢäžĩéŖãããžãã
⤠401/403/404ã¯åŗthrowããĻãĒããŠã¤ã§é ããĒã
if e.resp.status not in RETRYABLE_HTTP:
raise # 樊éã¨ãŠãŧã¯åŗ raise
樊éã¨ãŠãŧããĒããŠã¤ã§å
ãã¨ã3åå¤ąæãããã¨ã ãæŽãããæ¨ŠéããĒããã¨ããįå ãåãããžããtransient-failure-recorded-as-permanent.mdã§č¨é˛ããã429ãextract_failedãĢåãããã¨åãæ§é ã§ãäģåēĻã¯401ããããžãĢé
ãããĢåããžãã
âĨ moderation_stageã§æĸ¯åãéãã䞥å¤ããããå¤åŽããĻããTierãčŠĻã
if result.get("moderation_stage") == "input":
log("inputæŽĩãŽæåĻãæŽãTierãbreak")
break
1čĄå ããã ãã§63åãŽįŠēčĩ°ãã0åãĢãĒããžããoutputã§æĸãžãŖãããŽã¯ãŠããŧãåã䞥å¤ããããžããinputã§æĸãžãŖãããŽã¯æĻåŋĩãã¨æ¨ãĻãĻæŦĄãĢé˛ãŋãžãã
âĻ ãĒããŠã¤ãŽåą¤ã1įŽæãĢéį´ãã
åŧãŗåēãå´ãįŦčĒãĢãŧããæãĄãŠã¤ããŠãĒå´ãĢãåčŠĻčĄãããã¨ææĒNÃMåãĢãĒããžããéč¤ãčĻã¤ãããå¤å´ãŽãĢãŧããåé¤ããĻreq()ãŽ1åą¤ãĢ䏿Ŧåããžãã
â§ éãå ãŽåŽšéãæ¸ŦãŖãĻããé įˇãã
ããŠãŧãĢããã¯ãčŋŊå ããåãĢãåŽæ¸Ŧã§ãããŽéãå ãäŊäģļãžã§čŋãããããŠãã§čаãžããããįĸēčĒããžãã厚éãŽãĒãéãå ãĢå ¨ããŠããŖãã¯ãæĩããĻãæåįã¯å¤ãããžããã
⨠æäš įãĒå¤ąæã¯æ°¸įļãĒãšããĢ1åã§č¨é˛ããĻé¤å¤ãã
no ãĄããģãŧ㸠button / profile goneãŽãããĒ᏿å´ãŽč¨åŽãĢčĩˇå ããå¤ąæã¯ããĒããŠã¤åæ°ãæļč˛ģããåãĢé¤å¤ããžããshouldSkip()ãŽå¤åŽãexhaustedãŽæåãĢįŊŽããžãã
⊠ãäģããä¸åēĻåŠãããéããããåŽéãĢåŠããĻããäēēéæĄäģļãĢåéĄãã
Cloudflare Authentication errorããOAuthãå¤ąåšãããã¨åŗæããĻ3æĨéäēēéãŽåž
ãĄčĄåãĢįŠãã§ããžãããshell 㨠launchd æå°į°åĸãŽä¸Ąæšã§ãããŧããåŽčĩ°ããã¨å
¨ãĻexit 0ã§ãããã¨ãŠãŧãŽååã ãã§ãįĩļ寞ãĢį´ããĒããã¨åéĄããĒããįĸēãããĻãã夿ããžãã
âĒ ä¸éåŽæ°ãåŊããŖããã¨čĒäŊããã°ãĢåēã
text.slice(0, 400_000)ã¯ãĩã¤ãŦãŗããĢåãčŊã¨ãã ãã§ãããé˛åžĄãŽåŽæ°ãæäš
įãĒæ
éãŽåŧãéãĢãĒãŖãĻããĻãããã°ãĢã¯äŊãæŽããžãããJSON.parseãžã§å
é¨ã§åŽäēãããå¤ąæããã¨ãã ãč¨ēæį¨ãĢå
é 300åãčŋãåŊĸãĢããã¨ããåãããã¨ããäēåŽãč¨é˛ãĢæŽããžãã
âĢ ãĒããŠã¤å°å Ĩåžã¯äžå¤ãŽäŧæį¯å˛ãŽå¤åãįĸēčĒãã
withRetryãå
Ĩããã¨ãæĄãã¤ãļãâthrowããĢå¤ããŖãįŽæãŽä¸æĩãĢããåŋ
é åĻįãå°éä¸čŊãĢãĒããã¨ããããžãããĒããŠã¤ã¯æŠčŊãŽčŋŊå ã§ã¯ãĒããäžå¤ãŽäŧããæšãå¤ãã夿´ã§ãã䏿ĩãĢãå¤ąæããĻãåŋ
ããããšããã¨ããããįŽæã¯ååĨãĢå˛ãŋį´ããžãã
âŦ čŗæēéŧčŋĢãåå ãŽå¤ąæã¯ãåž ãŖãĻããåčŠĻčĄãã
setContentSafelyãŽåčŠĻčĄééããŧãį§ã§ã¯ãéĢč˛ čˇã§čŊãĄãį´åžãĢåæĄäģļã§ããä¸åēĻčŊãĄãžããæąēåŽčĢįãĒå¤ąæã¯ãšãĒãŧããĒãåŗåčŠĻčĄãæŖč§Ŗã§ãããããŽéå´ã¨ããĻãčŗæēãåž
ããĒãã¨ãĒããŠã¤ãĢæåŗããããžãããå¤ąæãŽį¨ŽéĄã§ãšãĒãŧããå
Ĩããå ´æãé¸ãŗãžãã
â čĢĻããå¤åŽåŧãį´į˛éĸæ°ãĢåēããĻããšããĢäēæ åŊæĨãŽåŽå¤ãå Ĩãã
shouldGiveUpForQuota({quotaHits, createdAtMs, resumeAtMs})ã¸åãåēããĻãåäģ2026-08-19T00:36:57Zãģåé06:37:00Zã1ãąãŧãšã¨ããĻå
Ĩããã¨ãåãäēæ
ã¯äēåēĻã¨éããžãããcatchãããã¯ãĢããŋæ¸ããããå¤åŽã¯åĸįãąãŧãšãããšãã§ããžããã
âŽ åž ãŖãäēåŽãģčĢĻããäēåŽãģæãĄåãŖãäēåŽããã°ãŽ1čĄãĢæŽã
waited=600s SKIP: slot timeoutã¨SKIP: global limit reachedīŧåŗæ¨ãĻīŧã¯åĨãŽäē蹥ã§ãããŠãĄããåããSKIPããĢčĻããã¨ããæ ãŽįĢļåãåĸããããĒãŽãããã¨ãã¨åŗæ¨ãĻããĻããããĒãŽããåžããåēåĨã§ããžãããæčŗããåž
ãĄæéã¯æ°å¤ã§ãã°ãĢåēããžãã
ãžã¨ã
ãå¤ąæããããã1åãã¨æ¸ããã¨ããããŽãŗãŧããĢã¯3ã¤ãŽåããŽįãããããžãããåž ãĻã°į´ãããäŊåã§čĻåãããæŦĄãŽčŠĻčĄãĢäŊãå¤ããĻæ¸ĄãããããŽ3ã¤ãæãŖãĻããĒãåŽčŖ ã¯ãæąēåŽčĢįãĒå¤ąæãŽåã§čŗæēãįŧãįļããĻæĸãžããžãã
5ãŦãŧãŗãåæãĢæĸãžãŖãæãĢä¸ĻãšãĻæ°ãĨãããŽã¯ãéåŽŗãŽåžŠæ§ãĢčĒåãéĸä¸ããåŋ čĻãããããŠããããããŽč¨č¨ãŽæįĄã§ãģãŧæąēãžãã¨ããįšã§ããč¨č¨ãããã°ããšã¯ãĒããã¯å¤ąæãŽį¨ŽéĄãã¨ãĢæŦĄãŽčĄåãčĒåã§æąēããžããč¨č¨ããĒããã°ãåãå¤ąæã3åč¨é˛ãããã ãã§ãįŋæãåãå ´æãĢ芰ãžããžãã
æå120ä¸ãŽčĒååį°åĸãæ¯æĨåããĻãããŽã¯ããŗãŧããæ¯æĨæ¸ããĻããããã§ã¯ãããžãããå¤ąæãæŖããåéĄãããĻãæŠæĸ°ãčĒåã§å垊ããããæįĢããĻããžããããŽåæãĢįĢã¤ã¨ããĒããŠã¤č¨č¨ã¯ãåžããå ããæŠčŊãã§ã¯ãĒããæåãŽåŽčŖ ãĢåĢãžããĻãããšãããŽã§ãã
ããĒããŠã¤ãæ¸ãããã¯ããããŠãĢãæŗ¨å Ĩčžŧãŋã§čĩ°ãŖãã¨ãããčĻããã§åŽäēãĢããžãã
äģįĩãŋãŽå ¨äŊåãģæ120ä¸ãŽå 荺ãģ30æĨæé ã¯æænoteãĢãžã¨ããĻããžãã
đ Claude CodečĒåžį°åĸã§ãåŽéãŠãį¨ŧãã â äģįĩãŋãģåŽäžãģå§ãæšãģãĩããŧã
Lilyīŧ@bokuwalilyīŧâ åäēēéįēč ãClaude Code ã§čĒåååēį¤ãįĩãŋãĒãããiOSãĸããĒãWebãĩãŧããšãéįŖããĻããžã
- AIã§ãå¯ãĻãĻãåãäģįĩãŋããäŊãŖãĻæ120ä¸ãĢããčŠąã¯ noteãŽææč¨äē ãĢđ°
- OSS: github.com/bokuwalily đ
- ææ°æ å ąãģãåãåãã㯠X @bokuwalily ã¸đ
- AIå°å ĨãģčĒååãŽį¸čĢã¨åŽčŖ ããŗããŦ7æŦãŽé å¸ã¯ å ŦåŧLINE ããđŦ
įãã㎠â¤ī¸ ããˇã§ãĸãåąãŋãĢãĒããžãīŧ