전체 글

완성된 글이 나흘 동안 작업 트리에 잠겨 있었다 — 발행 파이프라인 잔여물을 이어받는 구조

xXfacebook페이스북

발행 직전까지 끝난 글 세 편이 작업 트리 안에서 96시간을 보냈습니다. 2026-09-04 낮에 politics-right·note·dev 세 사이트의 초안이 본문과 삽화까지 완성된 채 커밋 없이 남았고 그날의 자동 발행 실행은 wave2 를 발행하지 못한 채 끝났습니다. 왜, 언제 끝났는지는 기록에 남아 있지 않습니다. 남아 있는 것은 결과뿐입니다. 그 뒤 09-05 부터 09-08 아침까지 네 번의 정기 실행이 이 트리 셋을 매번 "건너뛴다"로 처리했고 세 사이트의 발행은 09-04 부터 09-07 까지 넉 날 연속 0편이었습니다. 잠긴 세 편이 풀린 것은 09-08 낮 12시 34분부터 13시 36분 사이였고 09-07 새벽에 잠긴 politics-left 초안은 같은 날 오전 11시 9분에 먼저 나왔습니다.

기술적 결함이 하나 더 있었습니다. 스킵을 알리는 통지는 보드(Paperclip) 한 곳에만 걸리게 돼 있었는데 그 보드가 같은 기간 404로 죽어 있었고 스킵을 알 수 있는 다른 경로인 디스코드에는 무엇을 못 적었는지가 아니라 "보드 기록 실패 6건"이라는 숫자만 갔습니다(문서에 남은 기록 기준). 스킵도 침묵하고 그 침묵을 알릴 통로도 침묵한 셈이라 이 구간의 설계를 고친 커밋 245f472는 "조용한 정지"라고 부릅니다. 이 글은 그 96시간을 만든 두 설계 — 잔여물을 건너뛰던 설계와, 건수만 옮기던 알림 설계 — 를 각각 무엇으로 바꿨는지의 기록입니다.

결론부터

바뀐 것은 두 갈래입니다. 트리를 여는 쪽과, 실패를 알리는 쪽입니다.

상황 예전 (2026-09-04~08 당시) 지금 (09-08·09-11 수정 후)
작업 트리에 잔여물이 있으면 종료 코드 2 — 그 사이트를 건너뛴다 종료 코드 3 — 이어받고, 남은 목록을 에이전트 프롬프트에 넣는다
보드 기록이 실패하면 디스코드에 "실패 N건" 숫자만 간다 못 남긴 내용 자체를 디스코드로 옮긴다 (앞의 3건)
실행 카드를 아예 못 잡으면 옮기는 함수가 입구에서 조용히 돌아간다 BOARD_DOWN — 이 경우에도 내용을 옮긴다

첫 번째 행의 변경이 96시간 감금의 재발을 막는 쪽이고(pipeline/worktree.sh:52-58) 둘째·셋째 행의 변경이 "고쳤는데도 두 번 더 조용했던" 09-09~11 의 재발을 막는 쪽입니다(pipeline/report.sh:28-101, FLOW.md:205-229). 세 행 다 공통된 방향이 하나 있습니다. 사람이 알아채야 하는 것을 코드가 알아채게 옮겼다는 것입니다. 잔여물은 사람이 발견할 때까지 기다리는 게 아니라 다음 실행이 이어받고 보고 채널의 실패는 건수가 아니라 유실된 내용으로 알립니다.

검증 방법도 함께 바뀌었습니다. 이 글의 마지막 절에서 다루지만 "고쳤다"고 믿었던 09-08 의 수정이 사흘 뒤에 또 조용해진 이유가 리허설이 밟지 않은 경우였기 때문에, 지금은 가짜 bun 으로 채널을 죽여 경우를 전부 밟는 검사가 붙어 있습니다.

완성된 초안이 나흘 동안 트리에 잠겨 있었다

09-08 오전 10시 10분 커밋 245f472의 메시지는 잔여물을 실측 목록으로 남깁니다. politics-left 이주노동자 글 14,998자에 SVG 4장과 커버, politics-right 예산안 글 11,522자, note 헬스체크 글 10,492자, dev Polars 글 14,753자 — 네 편 전부 "draft: true 로 멈춰 있었다"고 적습니다. 같은 메시지가 적는 "넷이 나흘간 한 편도 못 냈다"는 politics-left 를 포함한 과장입니다 — git 발행 기록 기준으로 0편이 이어진 것은 politics-right·note·dev 세 사이트이고 politics-left 는 09-05 07:48 과 09-06 07:19 에 발행돼 있습니다(아래 일자별 표와 같습니다). 나중에 만들어진 carry 프롬프트의 표현을 빌리면 "본문·삽화까지 끝나 있고 발행만 안 된 초안"이었습니다.

생성 시각과 발행 시각을 이으면 잠긴 시간이 나옵니다. 생성은 글 frontmatter 의 date(UTC 로 저장되므로 KST 로 환산), 발행은 main 브랜치 발행 커밋의 시각입니다. 이 글의 커밋 시각은 전부 author date, 그러니까 작업 트리에서 에이전트가 커밋을 만든 시각 기준입니다. cherry-pick 으로 main 에 얹히는 committer 시각은 별도로 존재합니다 — 09-04 wave1 세 편은 셋 다 13:18:22 에, 09-08 이어받기 네 편은 wt_integrate 일괄로 13:41:43 에 얹혔습니다. 앞선 헬스체크 글이 같은 커밋을 "13:18"로 적어 둔 것은 committer 기준이라 이 글과 표기가 갈립니다. 이 글의 "발행"은 이후로도 author date 기준이고 push·배포 시각은 커밋 메타데이터에 남지 않으므로 실제 공개는 이보다 늦을 수 있습니다.

초안 생성 (KST) 발행 (KST) 잠긴 시간
politics-left 이주노동자 09-07 05:30 09-08 11:09 약 29시간 39분
politics-right 예산안 09-04 12:46 09-08 12:34 약 96시간
note 헬스체크 09-04 12:58 09-08 13:07 약 96시간
dev Polars 09-04 12:57 09-08 13:36 약 96시간

09-04 실행의 구조도 봐야 합니다. 그날은 wave1 세 편(money 12:07, politics-left 12:27, travel 12:36)이 커밋된 뒤 wave2 세 사이트의 초안이 12:4612:58 에 생성됐고 wave2 는 커밋되지 않은 채 남았습니다. wave1 이 main 에 얹어진 committer 시각이 13:18:22 이므로 실행은 적어도 그때까지는 살아 있었고 종료는 그 이후의 일이지만 정확한 종료 시각은 남아 있지 않습니다. 멈춘 기제는 기록에 없습니다. 러너 로그는 저장소 밖에 남는 자리라 왜 그 시점에 끝났는지 확인할 수 없고 이 글은 그 한계를 받아들이고 기록이 있는 것 — 커밋, frontmatter, 남은 코드 — 로만 움직입니다. 참고로 네 초안은 모두 GLM 폴백으로 생성됐습니다. 09-08 13:4041 에 네 사이트의 chore(<site>): GLM 폴백으로 생성됨 표시 커밋이 붙었고 note 헬스체크 글 frontmatter 에 generatedBy: glm 이 실제로 들어가 있습니다.

2026-09-03 부터 09-12 까지 여섯 사이트의 일자별 발행 편수를 표로 나타낸 도표. politics-right·note·dev 세 열은 09-04 부터 09-07 까지 0이 넉 날 이어지다 09-08 에 각각 1편이 찍히고, 전체 합계는 09-08 에 아침 2편과 낮 회수 6편을 합쳐 8편으로 가장 높으며, 각 칸에는 발행 커밋 시각이 함께 적혀 있다

politics-right·note·dev 의 0은 09-04 부터 09-07 까지 넉 날 이어졌다. 사건 기간(09-03~08) 안에서 politics-left 의 0편일은 09-07 하루뿐이다.

일자별로 세어 보면 이렇게 됩니다(발행 = main 발행 커밋 기준, KST).

일자(요일) money travel politics-left politics-right note dev 계
09-03(목) 1 (01:04) 0 2 (18:42) 0 0 1 (01:04) 4
09-04(금) 1 (12:07) 1 (12:36) 1 (12:27) 0 0 0 3
09-05(토) 1 (06:41) 1 (06:55) 1 (07:48) 0 0 0 3
09-06(일) 1 (07:05) 1 (07:19) 1 (07:19) 0 0 0 3
09-07(월) 1 (07:26) 1 (07:19) 0 0 0 0 2
09-08(화) 2 (07:12·12:59) 2 (06:24·12:16) 1 (11:09) 1 (12:34) 1 (13:07) 1 (13:36) 8
09-09(수) 1 (07:19) 0 0 1 (10:05) 1 (09:35) 1 (09:33) 4
09-10(목) 1 (07:47) 1 (05:53) 1 (05:54) 0 0 1 (09:50) 4
09-11(금) 1 (07:43) 1 (07:21) 1 (07:06) 1 (08:18) 1 (08:12) 0 5
09-12(토) 1 (06:51) 1 (07:00) 1 (07:32) 0 0 1 (08:22) 4

09-08 의 8편은 아침 정기 실행의 2편(travel 06:24, money 07:12)과 낮 회수 세션의 6편(11:09~13:36)을 합친 것입니다. 회수 6편 중 travel 12:16 과 money 12:59 는 그날 새로 쓴 글이고 나머지 넷이 위 표의 이어받은 글입니다. money 큐에는 "2026-09-08 planner (2회차)"로 기록이 남습니다(같은 날의 다른 사이트 큐 기록은 형태가 다릅니다). 스킵 쪽도 세면 13건입니다 — 09-05 세 건, 09-06 세 건, 09-07 세 건, 09-08 아침 네 건(politics-left 가 합류한 날). 이 3·3·3·4 분해는 보드 원기록이 남아 있지 않아 트리 상태와 당시 설계, git 발행 기록으로부터 환산한 값이라 집계 원본이 아니라 유도 근거입니다.

09-03 행에도 각주를 하나 붙입니다. 이날의 네 편(money 1·politics-left 2·dev 1)은 전부 9/19/2 실행이 남긴 잔여물의 수동 회수이고 정기 실행의 산출이 아닙니다. 같은 시각 01:04 에 남은 큐 기록 커밋 세 개(c786835·2fd8462·646fdec)의 메시지가 "9/19/2 실행분"이라고 적고, 회수된 커밋의 메시지도 겹쳐 말합니다 — money 글(b1f642e)은 9/1 에 본문·삽화까지 다 써놓고 삽화 한 장이 비어 발행이 막혔다고, dev 글(29f9fbe)은 9/2 실행에서 이 글을 쓰다 2시간 타임아웃(exit 124)으로 죽었다고, politics-left 의 wip 커밋(6a06562)은 금액 규칙에 막힌 완성 2편을 보관합니다.

2026-09-04 부터 09-11 까지의 사건 타임라인을 그린 그림. 09-04 낮 12시 46분부터 58분 사이에 초안 세 편이 생성된 뒤 커밋 없이 실행이 끝나고, 09-05 부터 09-08 아침까지 네 번의 정기 실행이 세 사이트를 스킵하며, 09-08 오전 10시 10분에 이어받기 설계가 들어가고 10시 32분에 보드 404 원인이 제거되며, 낮에는 네 편이 이어받아 발행되고 오후에 relay 가 추가된 뒤, 09-11 저녁에 가짜 bun 검사가 붙는 흐름

잠긴 시간은 09-04 낮부터 09-08 낮까지 96시간이다. 멈춘 기제는 기록에 없다.

정직한 참고가 하나 필요합니다. politics-right·note 는 09-03 에도 0편이었고 money·dev 의 1편은 앞서 말한 9/19/2 실행분 수동 회수뿐이었습니다. 이 0들은 09-0304 의 침묵 실행과 09-02 의 잘린 실행이 만든 것입니다(앞선 헬스체크 글의 영역입니다) note 는 08-26~09-03 에 초안 자체가 없었습니다 — 큐 공백입니다. politics-right 는 08-26 이후, dev 는 09-01 이후에도 공백이 있었습니다. 그러니 이 사건의 실체는 "매일 발행되던 사이트가 끊겼다"가 아닙니다. 완성된 초안의 감금, 네 번의 반복 스킵, 그리고 그 소식을 전하지 못한 통지의 단절이 사건입니다.

거른 것의 정체와 예전 설계 — "건드리지 않는다"의 대가

예전 설계에서 wt_prepare 는 잔여물을 발견하면 종료 코드 2를 내고 그 사이트를 오늘의 대상에서 뺐습니다. 당시 문서와 코드는 이렇게 생겼 있습니다(커밋 245f472 의 삭제 측).

0  준비됨
1  준비 실패 (그 사이트는 오늘 거른다)
2  잔여물 있음 — 사람이 볼 상태다. 건드리지 않는다

루프는 rc=2 를 받으면 "작업 트리에 잔여물이 있다. 건너뛴다"를 출력하고 다음 사이트로 넘어갔습니다. 보드로 나가는 통지 문구는 "지난 실행이 그 트리에서 멈춘 흔적이다. 사람이 확인해야 다시 돈다"로 끝났습니다. 게다가 에이전트를 부르는 프롬프트는 항상 /daily-post <site> 하나뿐이어서 설령 실행이 이어진다고 해도 에이전트는 트리에 남은 초안을 보라는 말을 듣지 못했습니다.

이 설계의 전제는 "잔여물은 사람이 봐야 할 것"이었습니다. 그 전제가 틀린 이유는 잔여물의 실체에 있습니다. 잔여물의 대표적인 형태는 다 써 놓고 발행만 안 된 글입니다. 이번의 네 편이 그랬습니다. 그런 것을 통째로 거르면 그 트리는 사람이 매번 실행 로그를 뒤져 발견해 주지 않는 한 영영 안 풀립니다. 넉 날 동안 네 번의 실행이 정확히 그 행동을 반복했습니다.

사실 같은 수작업의 전례가 바로 전날 있었습니다. 09-03 01:04 커밋 6a06562 는 politics-left 의 완성 2편을 draft 유지 wip 로 커밋했고 같은 날 18:42 커밋 9c02b6e 에서 규칙을 수정한 뒤 발행됐습니다. 잔여물을 사람이 손으로 회수해 본 적이 있었던 것입니다. 245f472 는 그 수작업을 코드로 옮기고 "건드리지 않는다"는 종료 코드 2를 지운 커밋입니다.

wt_prepare 종료 코드의 예전과 지금을 나란히 비교한 도표. 예전에는 0 준비됨, 1 준비 실패, 2 잔여물 있음 건드리지 않는다의 세 갈래였고, 지금은 0과 1이 같은데 2가 폐기된 자리에 3 잔여물을 이어받았다가 추가돼 부르는 쪽에 그 사실을 넘기는 화살표가 이어진 그림

숫자 하나가 사라지고 숫자의 뜻이 건너뛰기에서 이어받기로 바뀌었다.

건너뛰기에서 이어받기로 — 종료 코드 3

지금의 wt_prepare 는 세 갈래입니다(pipeline/worktree.sh:52-58).

0  준비됨 (깨끗한 트리)
1  준비 실패 (그 사이트는 오늘 거른다)
3  준비됨 — 지난 실행의 잔여물을 **이어받았다**. 부르는 쪽이 그 사실을 넘긴다

rc=3 이 나오면 daily.sh 는 wt_dirt 로 잔여물 목록을 꺼내 에이전트 프롬프트에 붙입니다(pipeline/daily.sh:531-545).

"이 작업 트리에는 지난 실행이 남긴 것이 있다:"
"**오늘 글을 새로 시작하기 전에 이것부터 본다.**"
"- 본문·삽화까지 끝나 있고 발행만 안 된 초안이면, 그 글을 남은 단계(윤문·검수·발행)로 마저 몰고 간다. 오늘의 새 글은 쓰지 않는다."
"- 쓰다 만 조각이라 살릴 게 없으면 지우고 새로 시작한다. **무엇을 왜 지웠는지 마지막 보고에 적는다.**"
"- 어느 쪽이든 트리를 깨끗하게 만들고 끝낸다. 그대로 두면 다음 실행이 같은 것을 또 이어받는다."

이어받은 실행이 새 글을 쓰게 되면 그 글도 발행까지 못 가고 잔여물이 두 겹이 되므로, "오늘의 새 글은 쓰지 않는다"가 프롬프트의 중심입니다. 끝난 초안이면 남은 단계로 몰고 가고 살릴 게 없으면 지우되 그 이유를 보고하게 합니다. 어느 쪽이든 트리를 깨끗하게 만드는 것으로 끝냅니다.

한 가지 위험한 순간이 있습니다. 이어받은 브랜치의 base 를 오늘 것으로 옮기는 부분입니다. 옮기는 이유는 코드 주석에 있습니다 — "나흘 묵은 파이프라인 코드로 돌면 그 사이 고친 검사 규칙이 안 걸린다". 다만 실패하면 옛 base 그대로 둡니다(pipeline/worktree.sh:94-95 의 || true).

실패하면 옛 base 그대로 둔다. 추적 파일이 양쪽에서 갈리면 git 이 거부하는데, 그때 억지로 옮기는 것은 잔여물을 버리는 것과 같다. 미추적 파일(새 글 디렉터리)은 checkout 이 건드리지 않으므로 대개 그냥 넘어간다. 옛 base 로 돌아도 회수는 된다 — wt_integrate 는 base..br 만 집는다.

(worktree.sh 주석, pipeline/worktree.sh:88-94)

회수 쪽이 그 약속을 받습니다. wt_integrate 는 git rev-list --count "$base..$br" 로 개수를 세고 git cherry-pick "$base..$br" 로 가져오는데, 범위가 base..br 이라 브랜치가 옛 base 에 앉아 있어도 회수됩니다(pipeline/worktree.sh:121-134). 충돌하면 cherry-pick 을 abort 하고 1을 돌려 사람이 보게 합니다.

루프는 두 결과를 갈라 보드에 남깁니다(pipeline/daily.sh:707-744). rc=3 이면 WT_CARRIED("지난 실행의 잔여물을 이어받은 사이트:…"), 준비 실패면 WT_SKIPPED("작업 트리 준비에 실패해 건너뛴 사이트:…"). 검사도 교체됐습니다 — worktree.test.sh 에 "잔여물 → rc=3 (이어받음)", "base 를 옮겨도 잔여물이 산다", "막힌 경우 잔여물이 그대로다"가 들어갔습니다. 이 설계가 처음 작동한 날이 09-08 낮이고 결과가 앞 절의 8편입니다.

숫자만 간 알림, 고친 뒤에도 두 번 더 조용했다

첫 번째 침묵은 통지의 경로가 하나뿐이었다는 것입니다. rc=2 시절 스킵 통지는 track_note → Paperclip 보드 한 곳에만 걸렸는데, 그 보드가 09-04 이후 기록을 통째로 남기지 못했습니다. 커밋 3f38077 이 확인한 것은 실행마다 API error 404: Issue not found 였고 키 만료도 아니었습니다 — 그때 만료일 2027-09-04 까지 361일이 남아 있었고 last_used_at 은 09-04 에 동결돼 있었습니다. 원인은 발행 루틴이 러너 자식 프로세스에 주입한 PAPERCLIP_API_KEY 로, 주입된 키로는 이슈가 보이지 않았고 서버는 404 로 답했습니다. 이 이야기의 전체 경위는 앞선 헬스체크 글의 영역이라 여기서는 여기까지만 옮깁니다.

보고 채널이 둘인 구조에서는 한쪽이 죽으면 다른 쪽에 실패 건수를 붙여 주는데, 그래서 디스코드에는 "보드 기록 실패 6건"이라는 숫자만 갔습니다. 이 숫자의 저장소 내 출처는 딱 두 곳입니다 — FLOW.md:217 과 pipeline/report.sh:53 주석, 둘 다 09-08 수정 세션의 산출입니다. 당시 디스코드 메시지 원문은 저장소에 없으므로 이 글도 "문서에 남은 기록으로는 6건" 이상으로 말하지 않습니다. 어느 쪽이든 구조는 같습니다. 숫자는 "보고 채널이 아프다"를 말하고 내용은 "무슨 일이 났다"를 말하는데, 전해진 것은 숫자뿐이었고 무엇이 스킵됐는지는 어디에도 없었습니다. report.sh:49 의 주석이 이 상태를 한 문장으로 적습니다. "보고 채널이 하나면 그 하나가 죽는 순간 조용한 정지가 된다." GA4 글이 다룬 것은 관측 기기(gtag)의 죽음이고 이번은 보고 채널의 죽음으로 축은 다르지만, "프로세스는 초록불인데 소식이 없다"는 모양은 같았습니다.

두 번째 침묵은 고친 뒤에 왔습니다. 09-08 13:05 커밋 becf005 가 "보드에 못 남긴 내용을 디스코드로 옮기기"를 넣었습니다. 상한이 있습니다 — BLOG_BOARD_RELAY_MAX 기본 3. 보드가 하루 종일 죽은 날 track_note 는 열 번 가까이 실패하는데 그걸 다 옮기면 디스코드가 같은 사고로 열 줄이 되고 그러면 사람이 알림을 끕니다. 그래서 앞의 3건만 옮기고 마지막 한 건에는 "(보드가 계속 죽어 있어 이후 기록은 여기로 옮기지 않습니다…)"를 붙입니다. RELAYING 플래그는 보드 실패 → 디스코드, 그 디스코드 실패 → 보드의 맞재귀를 끊습니다(pipeline/report.sh:58-87).

그런데 이 옮기기는 09-09~11 사흘간 한 건도 작동하지 않았습니다(커밋 1728c3c 가 밝힙니다). 카드 열기부터 실패하면 이슈 ID 가 비는데, 당시 track_note 의 첫 줄은 if [ -z "$ISSUE_ID" ]; then return 0; fi — 카드가 없으면 조용히 돌아가는 입구가 함수 맨 앞에 있었습니다. 그 사흘 보드는 404 에서 401 로만 바뀌어 있었습니다. 09-08 의 수정이 주입 키 한 겹만 벗긴 것입니다. 런타임이 PAPERCLIP_API_URL 도 같이 넣어 자격증명 조회가 깨졌고 last_used_at 은 09-08 에서 76시간 멈춰 있었습니다.

보고 채널의 조용한 정지 구조와 고친 뒤의 흐름을 그린 다이어그램. 스킵 통지가 보드로만 가던 구간에서는 보드가 404 로 죽자 아무것도 남지 않고 디스코드에는 실패 건수만 갔고, 지금은 보드 기록 실패 시 못 남긴 내용을 디스코드로 옮기며, 실행 카드를 못 잡은 날도 BOARD_DOWN 으로 구분해 내용을 옮기고, 상한 3건과 재귀 방지 플래그, 가짜 bun 검사가 일곱 경우를 밟는 흐름까지 표시한 그림

숫자는 "보고 채널이 아프다"를 말하고 내용은 "무슨 일이 났다"를 말한다. 둘은 다른 정보다.

그 사흘의 발행 자체는 매일 있었습니다 — 09-09 네 편, 09-10 네 편, 09-11 다섯 편. 다만 전 사이트는 아니었고 여섯 사이트 모두 최소 하루씩 한도 초과로 GLM 폴백으로 발행됐습니다(GLM 표시 커밋: money 09-09·10·11, note 09-09·11, politics-right 09-09·11, dev 09-10, politics-left 09-11, travel 09-11 — "매일 여섯 사이트 전부"라는 강한 독해는 근거가 없습니다). 주어를 정확히 해 둡니다. 이 기록이 안 남은 것은 보고 채널이지 git 이 아니었습니다. GLM 표시 커밋은 날짜별로 남아 있습니다. 남지 않은 것은 "카드 열기부터 실패한 날의 실행 내용"이었고 relay 가 그것을 받도록 만들어져 있었는데 입구에서 조용히 돌아간 것입니다.

구멍을 막은 것이 09-11 17:51 커밋 1728c3c 입니다. 한 커밋에 네 건이 들어갔습니다.

  • track.ts 가 PAPERCLIP_API_URL 까지 unset
  • 보고 함수를 pipeline/report.sh 로 분리하고 BOARD_DOWN 과 report.test.sh 추가
  • 스레드 제안이 러너 잠금에 걸려 사흘 연속 생략된 부분의 무시
  • 티스토리 지연 상한 480초

뒤의 둘은 각각 스레드·티스토리 이야기라 티스토리 글의 영역이고 여기서는 두 번째를 봅니다.

BOARD_DOWN 은 "보드에 적을 곳이 없는 날"을 이름으로 만든 것입니다. daily.sh 가 오늘 실행 카드를 시도했는데 못 잡으면 BOARD_DOWN=1 을 세우고(pipeline/daily.sh:376-389) track_note 는 이슈 ID 가 빈 날에도 이 깃발이 서 있으면 내용을 디스코드로 옮깁니다(pipeline/report.sh:89-101). "보드를 아예 안 쓴 실행"(0)과는 구분하는데 — 안 쓴 실행은 옮길 이유가 없으므로 — 그 구분이 테스트 4번으로 고정돼 있습니다.

마지막 조각은 검증 방법입니다. 이 함수가 예전엔 daily.sh 안에 있어서 커밋의 말대로 "나머지 경우를 돌려볼 방법이 없었습니다". report.test.sh 는 PATH 앞에 가짜 bun 스텁을 끼워 네트워크 없이 보드와 디스코드를 마음대로 죽이고 일곱 경우를 밟습니다.

  • 보드 정상이면 옮기지 않는다
  • 코멘트 실패면 옮긴다
  • 카드 없는 날도 옮긴다
  • 보드를 시도하지 않은 실행은 조용하다
  • 5회 실패면 3건만 옮기고 3번째에 중단 고지를 붙이며, 실패는 5건 그대로 집계한다
  • 양채널 동시 사망이면 재귀 없이 각 1회
  • 디스코드 알림이 실패하면 반대로 보드에 옮긴다

커밋은 "bun test 452 통과 · report.test.sh 7경우 통과"를 남깁니다. 8월 21일 구조적 가드레일 글이 "게이트가 자기 경로만 지켰다"를 다뤘는데 이번도 같은 방향입니다. 09-08 의 리허설은 보드를 없는 이슈로 죽이고 track_note 를 다섯 번 돌리는 방식이어서, 카드는 잡았는데 코멘트가 실패하는 경우와 양채널을 다 죽인 경우까지는 밟았습니다. 밟지 않은 것은 카드 열기부터 실패하는 경우이고 그때 검증되지 않은 것이 바로 그 경우였습니다. FLOW.md:229 의 결론이 이 글의 결론이기도 합니다. 리허설이 밟지 않은 경우는 검증된 게 아니다.

근거와 참고

  • 커밋 245f472 (2026-09-08 10:10 KST) — rc=3 설계 본체, 잔여물 실측 목록(글자 수·SVG·draft 상태), worktree.test.sh 교체, "조용한 정지" 표현
  • 커밋 3f38077 (2026-09-08 10:32 KST) — 보드 404 원인(주입 키), 키 만료 361일 남음·last_used_at 동결 확인, 감시 전환
  • 커밋 becf005 (2026-09-08 13:05 KST) — 보드→디스코드 relay, BLOG_BOARD_RELAY_MAX=3, RELAYING
  • 커밋 1728c3c (2026-09-11 17:51 KST) — relay 0건 원인, PAPERCLIP_API_URL unset, report.sh 분리, BOARD_DOWN, report.test.sh 7경우, bun test 452 통과
  • 커밋 80d284d·803d945·e8092af·727d655 (2026-09-08 11:09~13:36 KST) — 이어받아 발행된 네 편
  • 커밋 6a06562·9c02b6e (2026-09-03 01:04·18:42 KST) — 잔여물 수동 회수의 전례. 같은 시각의 큐 기록 커밋 c786835·2fd8462·646fdec 은 메시지에 "9/1~9/2 실행분"이라고 남기고, 회수된 b1f642e·29f9fbe 의 커밋 메시지도 9/1·9/2 실행의 잔여물임을 적는다
  • 현재 코드 — pipeline/worktree.sh:52-134(종료 코드·base 보존·wt_integrate), pipeline/daily.sh:531-545(carry 프롬프트)·:707-744(루프·WT_CARRIED/WT_SKIPPED)·:376-389(BOARD_DOWN 설정), pipeline/report.sh:28-101(BOARD_DOWN 정의·relay·track_note), pipeline/report.test.sh(가짜 bun 7경우)
  • FLOW.md:205-229 — 채널 이중화 설계 문단. :217·report.sh:53 의 "보드 기록 실패 6건", :229 의 "리허설이 밟지 않은 경우는 검증된 게 아니다"
  • 발행 시각의 원천 — git 커밋 메타데이터(+0900)와 글 frontmatter date(UTC 저장, KST 로 환산). 이 글의 모든 시각은 author date(작업 트리에서 에이전트가 커밋한 시각) 기준이고 cherry-pick 으로 main 에 얹어진 committer 시각은 별도다 — 09-04 wave1 셋(b7796dd·5de1eb3·f4c1eac)은 셋 다 13:18:22, 09-08 이어받기 네 편(80d284d·803d945·e8092af·727d655)은 wt_integrate 일괄 13:41:43. push·배포 시각은 이 기준에 없다
  • 앞선 글 — 셸 헬스체크 빈 응답 exit 124·티스토리 미러링 제거·GA4 조회수 0·구조적 가드레일
이 글 공유하기xXfacebook페이스북